Flow-based models(NICE);流模型+NICE+代码实现

参考:

  1. 李宏毅春季机器学习
  2. NICE: Non-linear Independent Components Estimation
  3. https://github.com/gmum/nice_pytorch

文章目录

    • 大致思想
    • 数学预备知识
      • Jacobian矩阵
      • 行列式以及其几何意义
      • Change of Variable Theorem
    • Flow-based model
    • NICE
      • 理论
      • 代码

大致思想

Flow-Based模型想要构造出一个可逆的模型,也就是说可以把图片正向输入得到一个向量,把这个向量反向输入就可以得到图片。
换句话说,Flow-Based模型是一个由输入的空间到输出的空间的双射。那么想要构造出这个双射,则需要一些数学上的知识。

数学预备知识

Jacobian矩阵

假设有这么一个函数,输入是一个向量,输出是一个向量,那么此时我们可以求这个函数的Jacobian矩阵:
y = f ( x ) J f = [ ∂ y 1 ∂ x 1 ∂ y 1 ∂ x 2 . . . ∂ y 1 ∂ x n ∂ y 2 ∂ x 1 ∂ y 2 ∂ x 2 . . . ∂ y 2 ∂ x n . . . ∂ y n ∂ x 1 ∂ y n ∂ x 2 . . . ∂ y n ∂ x n ] y=f(x)\\J_f=\begin{bmatrix} \frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2}...\frac{\partial y_1}{\partial x_n}\\ \frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2}...\frac{\partial y_2}{\partial x_n}\\ ...\\ \frac{\partial y_n}{\partial x_1} & \frac{\partial y_n}{\partial x_2}...\frac{\partial y_n}{\partial x_n} \end{bmatrix} y=f(x)Jf= x1y1x1y2...x1ynx2y1...xny1x2y2...xny2x2yn...xnyn
上面就是Jacobian矩阵,其中我们假设x和y都是n维向量。

假设f可逆,也就是 f − 1 ( y ) = x f^{-1}(y)=x f1(y)=x,那么此时Jacobian矩阵有这么一个性质:
J f − 1 = J f − 1 J_{f^{-1}}=J_f^{-1} Jf1=Jf1
也就是说该函数的反函数的Jacobian矩阵是原函数的Jacobian矩阵的逆矩阵。

行列式以及其几何意义

行列式线性代数都学过,举例来说,二阶行列式可以这么算:
∣ a b c d ∣ = a d − c b \left | \begin{matrix}a & b\\ c & d\end{matrix} \right | = ad - cb acbd =adcb
这个行列式的几何意义如下:
在这里插入图片描述
如上,把二阶行列式的两个行向量在坐标轴上画出,然后构造出平行四边形。那么这个平行四边形的面积就是行列式的值。

Change of Variable Theorem

假设有两个分布记做 π ( z ) , p ( x ) \pi(z),p(x) π(z),p(x)以及他们两个分布之间的变换函数 x = f ( z ) x=f(z) x=f(z)
在这里插入图片描述
如上图,也就是说给定上面的一个z’则对应下面p分布中的一个x’。而对应的函数的得值分别是 π ( z ′ ) , p ( x ′ ) \pi(z'),p(x') π(z),p(x)

假设,在z’上取一个很小的变动,记做 Δ z \Delta z Δz那么此时,就会得到一个区间 [ z , z + Δ z ] [z, z+\Delta z] [z,z+Δz],那么此时对应的x’也会有一个变化区间,由于这个区间过于小,所以可以近似当做是一个矩形。可以证明,此时两个矩形的面积是一致的:
在这里插入图片描述

此时就有
π ( z ′ ) Δ z = p ( x ′ ) Δ x → π ( z ′ ) = p ( x ′ ) Δ z Δ x ≈ p ( x ′ ) d z d x \pi (z')\Delta z = p(x')\Delta x \rightarrow \pi(z')=p(x')\frac{\Delta z}{\Delta x}\approx p(x')\frac{dz}{dx} π(z)Δz=p(x)Δxπ(z)=p(x)ΔxΔzp(x)dxdz
当这个区间足够小时,就可以近似微为微分。
由于有时变化反向肯能不一致,所以要加一个绝对值:
π ( z ′ ) = p ( x ′ ) ∣ d z d x ∣ \pi(z')=p(x')|\frac{dz}{dx}| π(z)=p(x)dxdz
如果换成二维分布,则可以得到:
在这里插入图片描述
此时就会得到:
在这里插入图片描述
然后仿照上述的步骤进行变换,就可以得到:
在这里插入图片描述

于是我们就得到了这么一个关系:
p ( x ′ ) ∣ d e t ( J f ) ∣ = π ( z ′ ) p ( x ′ ) = π ( z ′ ) ∣ d e t ( J f − 1 ) ∣ p(x')|det(J_f)|=\pi(z')\\p(x')=\pi(z')|det(J_{f^{-1}})| p(x)det(Jf)=π(z)p(x)=π(z)det(Jf1)

Flow-based model

在这里插入图片描述
如上图所示,生成模型的目标在于,对样本所在的目标分布 P d a t a ( x ) P_{data}(x) Pdata(x)进行建模。
我们通常的做法是,输入一个已知的分布 π ( z ) \pi(z) π(z),例如标准高斯分布。然后通过一个神经网络,学到一个变换关系,把这个分布变换成 P G ( x ) P_G(x) PG(x),最后我们期望这个最终的分布和目标的分布 P d a t a ( x ) P_{data}(x) Pdata(x)尽可能的接近。

此时一个常见的做法就是进行最大似然估计,可以证明最大似然估计等同于最小化 D K L ( P G ∣ ∣ P d a t a ) D_{KL}(P_G||P_{data}) DKL(PG∣∣Pdata)
根据上述的公式,我们其实已经知道了 z , x z,x z,x的关系,于是就有
在这里插入图片描述
然后再加上一个对数就得到:
l o g ( p G ( x i ) ) = l o g ( π ( z i ) ) + l o g ( ∣ d e t ( J G − 1 ) ∣ ) log(p_G(x^i))=log(\pi(z^i))+log(|det(J_{G^{-1}})|) log(pG(xi))=log(π(zi))+log(det(JG1))

因为要求可逆,所以要求特殊的神经网络。但是我们可以选择一些简单的网络像流一样把它们整合起来:
在这里插入图片描述
于是就有了如下的公式
l o g p k ( x i ) = l o g π ( z i ) + ∑ i = 1 k l o g ∣ d e t ( J G i − 1 ) ∣ logp_k(x^i)=log\pi(z^i)+\sum\limits_{i=1}^klog|det(J_{G_i^{-1}})| logpk(xi)=logπ(zi)+i=1klogdet(JGi1)
我们在实际训练的时候,训练的是 G − 1 G^{-1} G1,因为此时我们手上有的是 P d a t a ( x ) P_{data}(x) Pdata(x)中采样的数据。
z i = G − 1 ( x i ) z^i=G^{-1}(x^i) zi=G1(xi)
在训练结束后,反过来就可以做生成了。

NICE

理论

首先,对于输入z,我们我们把其分为两个部分,记做 [ x 1 , x 2 ] [x_1,x_2] [x1,x2]其中这两个部分不重叠。例如我们可以把前k个元素记做 x 1 x_1 x1剩下的记做 x 2 x_2 x2
此时NICE的操作如下:
h 1 1 = x 1 h 2 1 = x 2 + m 1 ( x 1 ) h_1^1 = x_1\\h_2^1=x_2+m_1(x_1) h11=x1h21=x2+m1(x1)
其中m1是一个非线性的变换函数。此时 [ h 1 1 , h 2 1 ] [h^1_1,h^1_2] [h11,h21]就是我们NICE第一层的输出了。
也就是说,我们首先对第一部分进行了直接复制,第二部分的输出则是由第二部分加上一个第一部分的非线性变换组成的。

假设NICE只有一层,那么 h 1 = [ h 1 1 , h 2 1 ] h^1=[h^1_1,h^1_2] h1=[h11,h21]就是最终输出了,可以看到整个计算过程是可逆的。也就是说反过来我们可以由 h 1 h^1 h1得到 [ x 1 , x 2 ] [x_1,x_2] [x1,x2]

我们把这一层的输出进行一下位置调转,输入到下一层,然后反复地重复这个步骤,就可以得到最终的NICE网络:
在这里插入图片描述

接下来我们来计算这个操作的Jacobian矩阵,如下:

在这里插入图片描述
可以得到其det为1。同理,后面每一层的det都是1。

假设先验分布z是高斯分布,则
l o g ( p G ( x ) ) = l o g π ( z i ) + ∑ i = 1 k l o g ∣ d e t ( J G i − 1 ) ∣ = l o g π ( f ( x i ) ) + ∑ i = 1 k l o g ∣ d e t ( J G i − 1 ) ∣ ≈ − 1 2 ∣ ∣ f ( x i ) ∣ ∣ 2 + ∑ i = 1 k l o g ∣ d e t ( J G i − 1 ) ∣ = − 1 2 ∣ ∣ f ( x i ) ∣ ∣ 2 ( 带入行列式 ) log(p_G(x))=log\pi(z^i)+\sum\limits_{i=1}^klog|det(J_{G_i^{-1}})|\\=log\pi(f(x^i))+\sum\limits_{i=1}^klog|det(J_{G_i^{-1}})|\\\approx -\frac{1}{2}||f(x^i)||^2+\sum\limits_{i=1}^klog|det(J_{G_i^{-1}})|\\=-\frac{1}{2}||f(x^i)||^2(带入行列式) log(pG(x))=logπ(zi)+i=1klogdet(JGi1)=logπ(f(xi))+i=1klogdet(JGi1)21∣∣f(xi)2+i=1klogdet(JGi1)=21∣∣f(xi)2(带入行列式)

此时还存在一个问题,那就是输入的图片大多都是稀疏的,也就是说某些维度空间可能并不重要。所以作者在最后加上了一个尺度变换的可学习向量s。
h n ⊗ s h^n\otimes s hns
此时最后一层的det不再是1了,仔细计算之后就会发现他变成了:
∑ i s i \sum_i s_i isi
于是我们损失函数变成:
− 1 2 ∣ ∣ f ( x i ) ⊗ s ∣ ∣ 2 + ∑ i s i -\frac{1}{2}||f(x^i)\otimes s||^2+\sum_i s_i 21∣∣f(xi)s2+isi

代码

注意,代码中的划分方式改成了奇偶划分,也就是按照奇偶的方式选择谁是 x 1 , x 2 x_1,x_2 x1,x2

import torch
import torch.nn as nn
import torch.nn.init as init_get_even = lambda xs: xs[:,0::2]
_get_odd = lambda xs: xs[:,1::2]def _interleave(first, second, order):"""交叉的还原h"""cols = []if order == 'even':for k in range(second.shape[1]):cols.append(first[:,k])cols.append(second[:,k])if first.shape[1] > second.shape[1]:cols.append(first[:,-1])else:for k in range(first.shape[1]):cols.append(second[:,k])cols.append(first[:,k])if second.shape[1] > first.shape[1]:cols.append(second[:,-1])return torch.stack(cols, dim=1)class AdditiveCouplingLayer(nn.Module):def __init__(self, dim, partition, nonlinearity):"""dim: 特征维度partition: 切分方式(first是奇数还是偶数)nonlinearity: 非线性函数"""super(AdditiveCouplingLayer, self).__init__()self.dim = dimassert (partition in ['even', 'odd']), "错误,不存在这种切分方式" # 切分方式self.partition = partitionif (partition == 'even'):self._first = _get_evenself._second = _get_oddelse:self._first = _get_oddself._second = _get_evenself.add_module('nonlinearity', nonlinearity) # 注册非线性函数def forward(self, x): # forward过程,计算first和second,并且拼接return _interleave(self._first(x),self.coupling_law(self._second(x), self.nonlinearity(self._first(x))),self.partition)def inverse(self, y):# backward过程,同forward过程,但是是相减"""Inverse mapping through the layer. Gradients should be turned off for this pass."""return _interleave(self._first(y),self.anticoupling_law(self._second(y), self.nonlinearity(self._first(y))),self.partition)def coupling_law(self, a, b):return a + bdef anticoupling_law(self, a, b):return a - bdef _build_relu_network(latent_dim, hidden_dim, num_layers): # 非线性函数m_modules = [ nn.Linear(latent_dim, hidden_dim) ]for _ in range(num_layers):_modules.append( nn.Linear(hidden_dim, hidden_dim) )_modules.append( nn.ReLU() )_modules.append( nn.BatchNorm1d(hidden_dim) )_modules.append( nn.Linear(hidden_dim, latent_dim) )return nn.Sequential( *_modules )class NICEModel(nn.Module):def __init__(self, input_dim, hidden_dim, num_layers):super(NICEModel, self).__init__()assert (input_dim % 2 == 0), "由于是奇偶切分,特征维度必须是否书"self.input_dim = input_dimhalf_dim = int(input_dim / 2)# 交叉的插入AdditiveCouplingLayer层,相当于上面说的调换位置self.layer1 = AdditiveCouplingLayer(input_dim, 'odd', _build_relu_network(half_dim, hidden_dim, num_layers)) self.layer2 = AdditiveCouplingLayer(input_dim, 'even', _build_relu_network(half_dim, hidden_dim, num_layers)) # self.layer3 = AdditiveCouplingLayer(input_dim, 'odd', _build_relu_network(half_dim, hidden_dim, num_layers))self.layer4 = AdditiveCouplingLayer(input_dim, 'even', _build_relu_network(half_dim, hidden_dim, num_layers))self.scaling_diag = nn.Parameter(torch.ones(input_dim)) # 设置一个尺度变换的向量# 初始化各个参数for p in self.layer1.parameters():if len(p.shape) > 1:init.kaiming_uniform_(p, nonlinearity='relu')else:init.normal_(p, mean=0., std=0.001)for p in self.layer2.parameters():if len(p.shape) > 1:init.kaiming_uniform_(p, nonlinearity='relu')else:init.normal_(p, mean=0., std=0.001)for p in self.layer3.parameters():if len(p.shape) > 1:init.kaiming_uniform_(p, nonlinearity='relu')else:init.normal_(p, mean=0., std=0.001)for p in self.layer4.parameters():if len(p.shape) > 1:init.kaiming_uniform_(p, nonlinearity='relu')else:init.normal_(p, mean=0., std=0.001)        def forward(self, xs):# 前向计算,正常的算就行ys = self.layer1(xs)ys = self.layer2(ys)ys = self.layer3(ys)ys = self.layer4(ys)ys = torch.matmul(ys, torch.diag(torch.exp(self.scaling_diag))) # 最后乘以尺度变换向量return ysdef inverse(self, ys):with torch.no_grad():xs = torch.matmul(ys, torch.diag(torch.reciprocal(torch.exp(self.scaling_diag)))) # 除以sxs = self.layer4.inverse(xs) # 反向计算xs = self.layer3.inverse(xs)xs = self.layer2.inverse(xs)xs = self.layer1.inverse(xs)return xs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/181271.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Solidity数据类型之函数类型

solidity中函数的形式 function <function name>(<parameter types>) {internal|external|public|private} [pure|view|payable] [returns (<return types>)]每个关键字的意思&#xff08;方括号里面的写不写都可以&#xff09; function&#xff1a; 声明函…

【0基础学Java第七课】-- 类和对象01

7. 类和对象 7.1 面向对象的初步认知7.1.1 什么是面向对象7.1.2 面向对象与面向过程 7.2 类定义和使用7.2.1 简单认识类7.2.2 类的定义格式7.2.3 定义一个狗类7.2.4 定义一个学生类 7.3 类的实例化7.3.1 什么是实列化7.3.2 引用只能指向对象&#xff0c;且不能同时指向多个对象…

服务器基本命令

nohup python3 ./download-all-2023-11-01_12-08-11.py > T85_download.log & 标准输出重定向到文件 nohup bash test.sh > stdout.txt & 标准错误输出重定向到文件 nohup bash test.sh 2> stderr.txt & 重定向到不同文件 nohup bash test.sh > …

k8s之service五种负载均衡byte的区别

1&#xff0c;什么是Service&#xff1f; 1.1 Service的概念​ 在k8s中&#xff0c;service 是一个固定接入层&#xff0c;客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端pod&#xff0c;这个 service 工作依赖于在 kubernetes 集群之上部署的一个附件&a…

Servlet详解

一.Servlet生命周期 初始化提供服务销毁 1.测试生命周期 package com.demo.servlet;import javax.servlet.*; import java.io.IOException;public class LifeServlet implements Servlet {Overridepublic void init(ServletConfig servletConfig) throws ServletException {…

LangChain+LLM实战---使用知识图谱和大模型来实现多跳问答

原文&#xff1a;Knowledge Graphs & LLMs: Multi-Hop Question Answering 可以使用检索增强方法来克服大型语言模型(Large Language Models, llm)的局限性&#xff0c;比如幻觉和有限的知识。检索增强方法背后的思想是在提问时引用外部数据&#xff0c;并将其提供给LLM&a…

Failed to launch task: 文件”Setup”不存在 Mac安装Adobe软件报错解决方案

在安装 Adobe 软件时&#xff0c;软件提示 Failed to launch task: 文件”Setup”不存在 &#xff0c;这个时候怎么处理呢&#xff1f; 解决方法如下&#xff1a; 1、安装 AnitCC 或 或 Creative Cloud 环境&#xff0c;保证软件所需要的环境 2、如果安装后也不起作用&#x…

Scala语言用Selenium库写一个爬虫模版

首先&#xff0c;我将使用Scala编写一个使用Selenium库下载yuanfudao内容的下载器程序。 然后我们需要在项目的build.sbt文件中添加selenium的依赖项。以下是添加Selenium依赖项的代码&#xff1a; libraryDependencies "org.openqa.selenium" % "selenium-ja…

基于人工大猩猩部队算法的无人机航迹规划-附代码

基于人工大猩猩部队算法的无人机航迹规划 文章目录 基于人工大猩猩部队算法的无人机航迹规划1.人工大猩猩部队搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用人工大猩猩部队算法…

SpringSecurity全家桶 (一) —— 简介

1. 概述 Spring Security 是一个框架&#xff0c;提供针对常见攻击的身份验证、授权和保护。 它为保护命令式和响应式应用程序提供了一流的支持&#xff0c;是保护基于 Spring 的应用程序的事实标准。 2. 了解 shiro&#xff1a; 在之前SSM框架盛行的时代&#xff0c;项目的…

轻信息服务展示预约小程序的内容是什么

预约几乎是适应所有经营商家的&#xff0c;可以提升客户服务/产品获取度、锁客及便捷性需求&#xff0c;同时也利于提升商家整体经营效率&#xff0c;无论获客还是留存线索都有很高帮助。 尤其对线下服务实体店来说&#xff0c;需要预约形式将客户引流到店&#xff0c;传统商家…

[MICROSAR Adaptive] --- Hello Adaptive World

Automotive E/E Architecture and AUTOSAR Adaptive Platform Vector Solution: MICROSAR Adaptive First project: Hello Adaptive World Summary 1 引言 1.1 AP诞生的历史背景 新一代电子电器架构通常将车内的节点分为三类。计算平台,预控制器和传感器执行器相关的节点,…

Leetcode 73 矩阵置0

class Solution {//1.用矩阵的第一行和第一列来标记该行或该列是否应该为0,但是这样的话忽视了第一行或第一列为0的情况//2.用标记row0和column0来标记第一行或第一列是否该为0public void setZeroes(int[][] matrix) {int n matrix.length;int m matrix[0].length;boolean r…

构建 hive 时间维表

众所周知 hive 的时间处理异常繁琐且在一些涉及日期的统计场景中会写较长的 sql&#xff0c;例如&#xff1a;周累计、周环比等&#xff1b;本文将使用维表的形式降低时间处理的复杂度&#xff0c;提前计算好标准时间字符串未来可能需要转换的形式。 一、表设计 结合业务场景常…

【Ubuntu】虚拟机安装系统与初始化配置

一、安装ubuntu系统 1、首先在虚拟机上安装一个虚拟机系统。 简单操作忽略&#xff0c;只贴出与安装linux不同的地方。 内存&#xff0c;处理器&#xff0c;磁盘等什么自己看着需要自己增加们这边不做过多说明。一直下一步&#xff0c;然后就安装好了。2、选择镜像位置然后启…

【SpringSecurity】简介

SpringSecurity简介 Spring Security 的前身是Acegi Security&#xff0c;在被收纳为Spring 子项目后正式更名为Spring Security。Spring Security目前已经到了6.x&#xff0c;并且加入了原生OAuth2.0框架&#xff0c;支持更加现代化的密码加密方式。可以预见&#xff0c;在Ja…

Go语言文本处理:正则表达式与字符串操作

大家好&#xff0c;我是[lincyang]。 今天&#xff0c;我们将一起探讨Go语言在文本处理方面的强大功能&#xff0c;特别是正则表达式和字符串操作。 Go语言以其简洁和高效而闻名&#xff0c;这些特性在文本处理上也得到了很好的体现。 接下来&#xff0c;我将通过丰富的案例…

torch.cuda.OutOfMemoryError: CUDA out of memory.

训练清华ChatGLM-6B时报错, 原因是显存不够 torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 96.00 MiB (GPU 0; 23.70 GiB total capacity; 4.37 GiB already allocated; 64.81 MiB free; 4.37 GiB reserved in total by PyTorch) If reserved memory is…

XSAN数据恢复-存储空间架构迁移时误格式化存储系统的XSAN数据恢复案例

XSAN数据恢复环境&#xff1a; 昆腾存储&#xff0c;MAC OS操作系统&#xff0c;存放视频类数据&#xff08;MXF、MOV等格式文件&#xff09;。 XSAN故障&检测&#xff1a; 将存储空间从XSAN架构迁移到STORNEXT架构后&#xff0c;存储空间中数据全部丢失。 故障存储中一共…

嵌入式中如何将BootLoader与APP合并成一个固件

1、前言 嵌入式固件一般分为BootLoader和App&#xff0c;BootLoader用于启动校验、App升级、App版本回滚等功能&#xff0c;BootLoader在cpu上电第一阶段中运行&#xff0c;之后跳转至App地址执行应用程序。 因此&#xff0c;在发布固件的时候&#xff0c;会存在BootLoader固件…