AI 基础笔记

· · 个人记录

来自人工智能基础课程,仅作留档。

概率论小知识

正态分布

定义两个变量的协方差 \operatorname{cov}_{X,Y}=\mathbb E_{X,Y}((X-\mathbb E[X])(Y-\mathbb E(Y))=\mathbb E[XY]-\mathbb E[X]\mathbb E[Y],当协方差 >0 时,X,Y 分布趋势相同,当协方差 <0X,Y 分布趋势相反,特别地 \operatorname{cov}_{X,X} 就是 X 的方差。

因此对于两列分布 \bold{x}\bold y,将其视为列向量,则协方差矩阵可以写成 \mathbb E[\bold {xy}^\text{t}]-\mathbb E[\bold x]\mathbb E[\bold y^\text t]

最大似然估计:

假设一些变量服从某个参数位置的分布 D(o_1,\ldots,o_n)。那么给出一些采样得到的数据点,在此基础上参数的概率分布是什么?这就是最大似然估计,即估计 o_1,\ldots,o_n 使得这个概率分布得到实验点的概率(或者其他代价)最大。

考虑一些数据点 x_i\sim p(x|\theta) 是独立同分布的,这里 \theta 可以看做人工智能模型,p(\bold x|\theta)=\prod p(x_i|\theta)。根据贝叶斯的一套东西,人工智能的一个目标就是搞到 p(\theta|\bold x),即确定一些尽量好的参数。

频率学派

那么想要找到的 \theta_0 就可以由如下刻画:

\theta_0=\arg\max_\theta \prod_i p(x_i|\theta)=\arg\max_\theta\sum_i\log p(x_i|\theta)

用 log 可以增加精度。

由于整个模型系统可能会非常复杂,数学上求解 \theta_0 就不太可行,数据可能也太大,因此可以用一些数值优化算法(e.g. 梯度下降等)。

贝叶斯学派

回顾贝叶斯公式

P(X|Y)=\dfrac{P(Y|X)P(X)}{P(Y)}

带入例子,得到

P(\theta|\bold x)=\dfrac{P(\bold x|\theta)P(\theta)}{P(\bold x)}\propto P(\bold x|\theta)P(\theta)

这假定了模型 \theta 具有某种先验概率分布(例如某些参数不能太大等),此时最大化的目标变成了 P(\bold x|\theta)P(\theta)

一个例子是扔硬币,可以认为硬币本身服从某种分布(例如硬币大致是公平的),那么就可以加入这种假设来适当“修正”先验概率、

信息论小知识

如果别人告诉你某件不太可能发生的事发生了,那么这个信息量比较高(排除了更多的可能)。

同时两件独立事件信息量会相加,因此自信息的一种合法刻画就是 I(X)=-\log P(X),其中 P(X) 表示 X 发生的概率,当 X\to 0 时这个式子 \to+\infty。信息熵可以量化整个概率分布的不确定性 H(x)=\mathbb E[I(x)]=\sum -p\log p

KL - 散度(KL-divergence)

误差可以看做某种度量(距离),对于两组概率分布 P(x)Q(x),可以定义 KL 散度:

D_{KL}(P||Q)=\mathbb E_{x\sim P}\left(\log\dfrac{P(x)}{Q(x)}\right)

这相当于用 Q 编码信息比 P 少了多少信息,需要注意期望变量中 x 服从 P(x) 分布,因此 PQ 不是对称的,这个玩意描述了两个分布有多像。

有一种改进的 JS 散度:

JS(P||Q)=KL(P||(P+Q)/2)+KL(Q||(P+Q)/2)

交叉熵(cross entropy)

H(P,Q)=H(P)+D_{KL}(P||Q)

这个不知道有啥用(好像确实挺多人用的)

下节课讲怎么炼丹。

Python 小知识

pip install 包

conda 创建环境(venv 也可以),环境之间独立,可以采用不同版本

服务器 + vscode remote ssh

numpy 非常牛(非常快)

有浅拷贝,创建新副本需要 copy

炼丹

数据 -> 知识

任务:

经验:数据集 -> 测试集

不能过拟合、能够将数据集特征泛化到测试集数据。

表现:衡量一个模型的表现如何,一些常见的 loss:

最后要优化 loss 最小,即 \min_\theta\mathbb E_{XY}[\text{loss}(Y,f_\theta(X))]

想要通用的训练方法来解决类似(甚至是所有)的机器学习问题。

线性回归分类问题

最典中典的问题。

线性模型f(x)=w^\text tx+b

这个东西的可解释性很好,而且很简单,但是这个东西有比较大的缺陷和局限。

给定数据 D=\{(x_1,y_1),\ldots,(x_n,y_n\},找到线性模型 f(x_i)=w^\text tx_i+b 使得 f(x_i)\approx y_i

人工计算:假设使用均方差 \mathbb E[(f(x_i)-y_i)^2](这个东西样本量要大才比较有道理),想训一个模型就相当于确定 (w^*,b^*)=\arg\min_{w,b}\sum (f(x_i)-y_i)^2,事实上 (w,b) 可以塞到一个向量里,视作列向量 \beta,把 x_i 也视作列向量(再加一行 1)得到 A,然后算一下矩阵乘法就可以得到所有的 f(x_i),这个东西是可以比较好地并行计算的(利用 GPU 加速)。

然后求一下偏导,就可以手动算出来一个式子 A^\text tA\hat\beta=A^\text tY,但是 A^\text t A 不一定可逆,至少要数据 \ge 参数才可以,通常是数据远多于参数。

梯度下降:直接算一个东西出来不一定好弄,而且数据太多的话求逆 A^\text tA 维度太大,过于昂贵,因此可以把这个东西看做一个数据优化问题。梯度下降就是对每个变量求偏导,得到一个向下方向的的向量,然后把待优化的东西移动一下。

梯度下降

初始获得一个解 \beta_0,然后不断迭代 \beta_{k+1}=\beta_k-\alpha\dfrac{\part J(\beta)}{\part\beta},通过适当地控制 \alpha(不同次迭代可以不同),获得一个“波谷”。

有的时候数据太少,导致之前例子里的矩阵不可逆,那么可以引入一些外部信息作为假设(例如模型的参数 \beta 是某种先验分布),间接地土工更多信息,使得矩阵可逆。

MAP(贝叶斯学派):求 \displaystyle\max_\beta \log\mathbb P(\{(X_i,Y_i)\}|\beta)+\log\mathbb P(\beta)

如果假设参数服从高斯分布:损失函数体现成 \displaystyle\arg\min_\beta\sum(Y_i-X_i\beta)^2+\lambda\|\beta\|^2_2,这个东西搞出来的就是 \hat\beta=(A^\text tA+\lambda I)^{-1}A^\text tY(lww 小技巧)

假设服从拉普拉斯分布:变为 \lambda\|\beta\|_1(这里是绝对值之和),换了一个距离的定义

两个分布都有其用途,比如加入这种正则化项会使得最后参数的大小比较小,这个时候可以发现那些参数是比较有用的,那些是没用的(系数非常小,甚至为 0),用告诉分布这可以提取特征

广义线性模型:y=g^{-1}(w^\text tx+b),这里 g 是一个可微的函数(可以假设有一些比较好的性质),可微是梯度下降道德需要。

逻辑回归

线性回归-预测连续值

逻辑回归-分类问题。

step function:

需要根据网络输出的一个概率给出一个结果,y=\begin{cases}0 & z<0\\0.5 & z=0\\1 & z>0\end{cases},可以转化成可微的版本 y=\dfrac{1}{1+e^{-z}}(Sigmoid 函数),这个函数在 z 较小时增长很快,当 z\to +\inftyy\to 1z\to-\inftyy\to 0

这个东西是这么来的:\log\dfrac{y}{1-y}=w^\text tx+b,这里的 \dfrac y{1-y} 是为正的比为负的相对概率,这里搞出来的就是 y=\dfrac{1}{1+e^{-(w^\text tx+b)}}

那么这里就可以做一个分类器(Logistic Regression)

sigmiod 引入了非线性,在感知机(线性组合)后边接一个就可以转成 (0,1) 中的一个概率。

对于分类任务,就会有一堆数据 (x_1,\hat y_1),\ldots,(x_N,\hat y_N),其中 \hat y_i\in \{0,1\} 是标签,假如说模型输出的 x_i 对应为 1 的概率是 f_{w,b}(x_1),那么最后的概率就是 L(w,b)=f_{w,b}(x_1)\ldots f_{w,b}(x_p)\left(1-f_{w,b}(x_{p+1}\right))\ldots(1-f_{w,b}(x_N)),即符合最后输出结果的概率,那么最后就是要最大化 L,即 (w^*,b^*)=\arg\max_{w,b}L(w,b)

先手取个 log 增加精度,然后这个东西实际上就是最小化 \sum-[\hat y_k\ln f_{w,b}(x_k)+(1-\hat y_k)\ln(1-f_{w,b}(x_k))],这实际上就是模型给出的分布对两点分布的交叉熵,这描述了两个分布的距离。

这类问题用均方差的话很多时候斜率是 0,不太好搞,但用交叉熵就比较好(ppt 上有图)。

对于图像识别问题(经典分类问题),是一个过去非常有挑战性的问题,需要一些额外的工具。

神经网络就是把很多层这样的东西叠起来,必须要引入非线性,但先不讲。

最近邻分类器

之前是需要人为标注、有参数的。

无参方法:数据中每加入一个数据点,和之前的数据点算一个距离,看看和原来哪个最接近,认为和对应数据同一个类别。

比如直接把像素点相减然后算一个距离(加起来或者之类的)。

当然也不一定要是最近的点,也可以从 k 近的邻居中取众数。

超参数:不是和训练相关的,比如之前 k 的选取和距离的算法,这个非常依赖于问题本身的性质,怎么选捏?

现在有一个已知的训练集,上面这个东西选 k=1 对训练集永远是 100%(过拟合),但是测试集不能拿来选参数(这是数据泄露作弊,本质上也是用测试集训练)。因此更好的方法就是在数据集当中分出一个验证集(validation set)用来调这种超参数,弄好了之后再测一次测试集。绝对不能让测试集参与训练。省流:数据=数据集+验证集(+测试集)

交叉验证:整个训练集分成若干份,每次取出一份为验证集,取一个平均比取单个更有代表性。

聚类——无监督模型

k-means 聚类。

随机 k 个初始分类点,然后不断迭代,对于每次迭代,先找每个数据点最近的分类点,划分一下类,然后计算中心点,用中心点更新每个类分类的分类点,然后重新找最近的点划分类,以此类推。这个东西初始点对整个算法的影响其实很大,不够好,同时可以构造一些数据导致算法收敛很慢。另外异常值也会对最终影响很大,总之就是很不牛。

神经网络基础

单个神经元:输入一堆参数,线性组合一下然后套一个激活函数。

可以用 pytorch 上 gpu。

一个神经元相当于 w^\text tx,如果是”一层“,那么就是 A^\text tx 获得一大堆东西,可以把一堆 x 弄成一个矩阵一起算,算完了反向传播一次,这样对 gpu 很友好,算的很快。

图片和卷积

图片编码-编码每个像素,对像素的编码决定信息量,同时像素太少就会太糊。图像就表为二维矩阵。

灰度直方图:对每个灰度统计一下亮度=它的有几个像素,可以提现整体亮度的趋势。

太亮/太暗 -> 人看不清楚。可以把最暗、最亮的拉到灰度轴两端,做归一化。

黑白图片相当于 int 或 float 的矩阵,彩色图片就相当于是 RGB 三个颜色的矩阵

局部增强:对局部进行一些操作,相当于卷积一个东西,即 s_t=(x*w)_t=\sum_{k=-\infty}^{+\infty}x_{t-k}w_k

这个东西很有用,比如可以做傅里叶变换,过滤掉一些频率的东西。同样的道理还可以提取一些特征,比如想查看是不是存在某种图案,那么可以卷积一个对应的函数(比如长得像想找的东西的函数),那么得到的加强点就是对应特征的位置。

例如:平均化就是弄一堆 1/9 这相当于过滤到了高频信号;锐化就是弄一堆 -1 和一个 8,这里和为 0 可以过滤掉频率比较低的部分,留下锐利的部分(这在某种意义下相当于差分,是一个边缘提取)。高斯(正态分布)和平均化差不多,也可以用中间值(中位数),效果也和平均化差不太多(一个应用是雪花噪声)。

卷积神经网络

全连接层在图片比较小的时候还行(MNist 中的 2828=784,隐藏层大约每层几百个神经元),但如果图片太大那就寄了,比如 1K 1K * 3,这个矩阵存都存不下。

人眼实际上是辨别某些图片的整体特征,而不是同时考虑所有东西,因此可能可以不用全连接层。那么可以搞某种稀疏链接,而且可以复用,那就卷积吧!

1D 卷积是一般的卷积,2D 卷积就相当于给一个卷积矩阵。大概有几个参数(stride 隔几个做一次,padding 越界怎么算)。这么搞可以把图片搞小一点,提取了一些特征图而不需要原图的全部信息。如果搞一堆卷积层的话就可以抽取一些特征的特征,弄出一些比较抽象的东西。

有一些乱搞比如在卷积的东西里面塞一些洞,让整个东西可以看到更多东西。

池化算法

越往后走,越需要更多的卷积宽度(输出更多的特征),这个会导致整个东西越来越大,pooling 就是让长宽都减少一些,就是进行一个特征的聚合(本质上似乎和卷积也没有什么特别的区别(?))

可以用不同尺寸的池化算法然后再搞,弄到一块搞到后边,反正就是纯瞎搞,看着怎么有道理怎么搞。

分层表示学习

怎么训练一个 CNN 呢?首先需要很多层卷积,因为需要得到更多信息才能够进行一些操作。那到底学到了啥捏?可以看一看卷积对什么样的输入反应最大,这样就可以看看卷积到底筛选了个什么玩意,有一定希望能够明白到底学了个啥。事实上这个网络并不总是靠谱,可能高出一些专门的攻击(如攻击 AlphaGo 感受范围比较小,或者攻击一些图像识别算法)。

一些辉煌的历史:AlexNet 将 CNN 用在图像识别,整了几个 CNN 然后接全连接层,搞成 1000 分类,薄纱传统机器学习。后来又搞了个 VGG16。

现在用的比较多的是 3 3 的卷积核(之前很喜欢用 7 7),事实上套几层之后都差不多。

很多都喜欢先在 ImageNet 上训练一个多分类模型,然后把最后的全连接层弄掉,只留下前面的一堆 CNN 层,结果得到了一个很好的通用特征提取器,非常厉害,这个就是所谓预训练模型,这样就可以搞来干别的了。

关于梯度消失问题:卷积层搞太多容易梯度小时,根本没法训练,于是就引入了 ResNet(残差卷积网络),就相当于是把一层的结果作为两层后的输入,这样的好处是保留了梯度,每层的梯度直接传回之前两层,这样的单位成为一个残差快(residual block)。

大家发现这个东西太好用了,现在没人搞计算机视觉了(?)

卷积怎么跑得快捏?一般的卷积复杂度就是枚举每个东西。

SqueezeNet:现在没人用了,搞一个 1 * 1 的卷积层压缩特征层数,然后再扩大一下

MobileNet:分层卷积,不把多张图搞到一块了,而是把每个特征图弄成一个 2D 的卷积,然后再搞一个 1 1 k 的东西把不同特征图之间的东西混起来,这样对于每个位置的计算量 t n m k 就变成了 n m + t * w,这叫深度可分离卷积。

ShuffleNet:把图片先分割一下,然后分开做,在一些地方 shuffle 一下,把特征给混起来,这样也可以减少计算量。

反卷积:多弄点 padding 或者塞点 0,保证信息没有损失就可以了。

一些其他的算法包括把图变大然后在搞(resize convlution)

一些训练小技巧:

首先感受野要覆盖整个图。另外可以使用数据增强(比如调整一下亮度、左右翻转,这个可以用 dataloader 和 dataset 搞)或者试试正则化,因为数据很小而且网络很深,很容易

CNN 这个东西很擅长提取图片当中的特征,因此在计算机视觉当中有很多应用。

CNN 用途

这个不同于分类问题,是要在图片中把东西识别出来(框起来)。

一个著名的数据集是 Pascal VOC,分为 20 类和大约 11k 个照片,27k 个框框。反正之后也搞了一堆数据集,现在更大了。

这个任务主要是看交并比(Intersection over Union),框框画的越接近肯定越好,一般认为当 IoU > 0.5 的时候物体被成功检测到。

平均精确度(Average Precision,AP):计算精度和召回率。

精确度 = 预测对的阳性 / 预测的阳性;召回率 = 预测对的阳性 / 真正的阳性。

不同的领域对这个东西的要求是不同的。

通常把这个东西画成 召回率-准确度 图,就可以得到一个 2 维的图,理想情况下最好的模型应该两个值都是 1,所以肯定是左下方面积越大越好,这个东西就叫 AP。通常把不同类 AP 分开算(水论文)

目标检测算法:R-CNN

用选择性搜索算法(Selective Search,这是个非神经网络算法)提取候选区域,然后将区域适当拉伸处理成给定的大小,然后扔到 VGG(的预训练模型)当中分类,之后通过回归来获得框框的位置(这里用到了非极大值抑制用以移除重叠的边界)。

当然这个很好,但是也有点不太好:首先是比较慢,其次拉伸操作可能会影响分类问题的准确性,而且每个都拿 VGG 算一遍也很慢,最后这个东西不是端到端训练(很多步)。

之后就搞出了一个 SPP Net。SPP Net 主要的改进在于先整一堆卷积层,然后再在提取的特征图上进行搜索找出区域,之后用一个金字塔池化方法。

然后又搞了 Fast R-CNN 和 Faster R-CNN,去掉了选择性搜索,改用区域提议网络(Region Proposal Network,RPN),用神经网络搜索候选区域,这样就成功搞出了一个端到端模型(整个东西是一大个模型,梯度可以从最后到最前传播,可以同时优化整个网络,不需要优化每一步了),感觉有点现代人工智能那味了。

目标检测算法:YOLO

输出两个东西,一个是 7 7 的东西,表示图上对应范围里是什么东西,另外还处理出物品的长宽,合起来就可以知道物品的位置了!最后会输出一个 7 7 * 30 的特征图,每个位置是一个大小为 30 的向量,分别输出分类结果、对应的长宽还有置信度一类的东西,这样就可以把最后的框画出来。

这个东西也有一些问题,比如小物体跟别识别不出来。

反正后来又有很多人搞了很多东西。

图像分割

驻点分类任务:把照片上属于不同物品的部分分成不同的颜色(标签),当然这个也有不同的目标,比如语义分割或者实例分割(把同类的实例分开)。就是像素级的分类问题,最后有几类就输出几层 0/1,相当于每一个像素内部 softmax 一下得到概率分布。

这个要用到反卷积的一个操作,搞出高级特征之后用反卷积网络还原回来。问题是最后会得到一个比较高级的特征,但是深度很大的话就会损失很多很小的细节,在最后小张量上面肯定不可能很好地分割。解决方式就是使用 Skip Connection,把正向卷积过程当中每层的特征扔到解码器对称的位置上去,这样就可以保证反卷积可以保留原来的小特征。

像素级的交叉熵:面积较大的物体对损失函数的权重较大,分割小物体性能较差。Dice 系数:可以看做是可以求导的 IoU,D=\dfrac{2|A\cap B|}{|A|+|B|}=\dfrac{2A\cdot B}{A^2+B^2},这里 A,B 都是 01 向量,把 A 换成预测概率,B 换成真实数据即可,这样就可以求导了,可喜可贺。

那怎么换成实例分割捏?加上前面的框框就行了。

multitask learning:多个(类似)任务同时训练,可以提升网络性能,总之就是后来搞了一大堆。

当然还有各种乱七八糟的技巧,比如加权突出边缘之类的。

人脸识别

首先找到人脸,对准然后识别。

人脸识别:给定一张图片,与已知的人脸库进行比较,从而确定人脸对应的身份。这个问题也有两类,即是否封闭,所有可能识别的人脸都出现在数据集当中。如果用人脸数据库的话更新就很麻烦,所以基本上还是用的是开集,输入的人脸并不一定在数据集当中,即数据集和数据库分开。

就是希望有一个固定的模型,新添加人的时候使用单张图片进行参考。

怎么训练的?利用 CNN 的特征提取能力训练一个提取人脸特征的东西,然后得到一个特征向量,不同的人比较特征向量。那么这个模型就要求一个人的照片提取出来的特征向量差别不大。最后就要求最小化一个人不同的距离,最大化不同人的距离,这个可以用类似均方差之类的东西弄。现在已经解决了!可喜可贺。

人脸认证:给定量张图像,验证是不是属于同一个人

姿态估计

提取运动关节位置。

有两种实现,一种是自顶向下,先把每个人分出来然后再对每个人做,这个好处是之前目标检测的技术已经比较牛了,但是要对每个人分别估计运动状态,人太多了就不好办了。

另一种是自底向上,首先检测所有关键点,然后分给不同的人,这个好在效率比较高,但是分给不同的人比较困难。

另一个思路是把姿态估计弄成目标检测,直接用目标检测算法检测关节的形态,每个关节连线画成一个框。

其他应用

人员重识别(不同摄像头追踪同一个人)

深度估计(用两张图片才有视差)

风格迁移(图 + 图 = 图)

超分辨率(图像分辨率增强)

图到图转换翻译。

接下来要讲无监督/非对称的图像翻译。

生成对抗网络(GAN)

监督学习 -> 对抗学习,实现目标由分类变成生成内容。

生成式模型

给一个描述,渲染/构造出一个图片。

不同于传统的图像计算学(依赖先验知识),生成式模型是由数据驱动的。

就是希望获得一个图片(或其他内容)的概率分布,通过已知的数据学习这个分布。这个东西有几个特点:

回顾一下判别式模型和生成式模型的区别:

判别式模型:对于一堆数据点 (x,y),找到决策边界 P(Y|X) 是多少,对应分到哪一类。

生成式模型:对于每个标签 x,训练一个概率分布出来,然后求出来每一类 y 看看 xy 的概率分布就能得知应该在哪一类。

朴素的 GAN

有两个东西对抗,一个生成器 G 和一个判别器 D。G 的任务是根据输入的随机向量生成一张图,让这张图尽量欺骗 D,是它判别为真的图;D 的任务是一个二分类任务,希望所有数据集当中的真图和 G 生成的图分开。

两个损失函数(优化目标)是相反的。但是由于这个东西可以串成一个大网络,G 可以获得 D 到底是长什么样,针对性地学习,这样生成器和判别器的能力都不断提升,最后 G 就具备了图像的生成能力,即将随机变量的分布(比如若干独立正态分布)映射到数据空间中的分布。

这实际上是一个 mini-max 博弈,可以证明如果两个东西都优化到了最优解那生成器 G 就会生成真实的数据概率分布,而且 D 完全分不出来。这个东西可以得到一个额外的结果,就是控制输入给 G 的噪声逐渐变化,就可以采样出从一个类别过渡到另一个类别的图的分布,很有意思。

当然这个实际效果不好,cifar10根本跑不出来能看的东西。

DCGAN: Deep convolution GAN

先对输入的噪声 z(100 维)投影+reshape一下,得到一大堆 4 4 1024 的东西,之后一路反卷积最后得到 3 64 64 的照片,这个效果就很好了。

然后有一些炼丹小技巧,使用跨步卷积、调一调训练的超参数之类的。

然后这个因为是把上述东西当做 G 塞到一个 GAN 里面,当然也可以有可视化的图渐变。当然这个实际上是把输入的噪声 z 当做特性,所以甚至可以把几张图对应的噪音 z 做一个线性操作然后就可以得到有对应特征的图。眼镜男 - 男 + 女 = 眼镜女。大神啊!

变分自编码器

另外有个东西叫 VAE(variational autoencoder,变分自编码器)。这个玩意有一个编码器 E,图像 X 经由 E 编码成 Z,然后有一个解码器 G,把 Z 解码成 X',然后这个这个东西的优化目标就是 L_2(X,X'),即均方误差。这样就可以获得一个图像和一个向量的双向对应(E + G),由于想要生成图片得符合 Z 的分布,可以给 Z 加一个 KL 散度限制它接近正态分布,同样地还是同时训整个网络,优化目标就是两个东西加起来。

GAN 只能把一个概率分布映射到图片,但 VAE 能有一个接近双向的映射。VAE 好就好在它可以把图变成编码,因此可以方便地求两个图的平均值。

所以说更好的 GAN 应该还有编码器。

GAN 的损失函数 vs 自编码器的损失函数(均方误差)

均方误差不太牛,问题在于细小物体可能直接消失,因为小物体在最终的均方误差当中贡献很小,只要整体大面上可以就均方误差很小,因此 VAE 很难学会细节的东西,比较一坨,比如会把人的耳朵搞没。但是 GAN 就没问题,因为优化目标相反就可以有一个对抗的过程,都通过去的知识不断比较学习,相当于是自适应的损失函数。

所以 GAN 很快就能生成很多很高清的东西了,VAE 比较拉。

后来还有扩散模型和视频生成的 sora 之类的,当然是比较复杂了。

更牛的 GAN

回顾一下,GAN 的目标就是找到 p(X)=p(X|Z)p(Z),通过曲线救国(学 p(X|Z),即给定输入的噪声输出一个东西)来搞,因为有个 Z 的先验分布(比如正态分布)。

现在 GAN 的目标就是找一个类似编码器的东西。

一个尝试是除了噪声以外还给 G 输入一个分类向量 c,c 是 one-hot 的,表示要生成哪一类的图。然后 D 除了分辨真假以外还要做一个分类任务,输出一个 softmax 的东西表示猜测的 c。

D 的损失函数就是,对于 data 中的东西要预测是真的,并且分类尽量准确;对于 z 生成的东西,要预测是假的,并且分类对应的位置尽量是 0。G 的损失函数就是首先让 D 认为图是真的,并且尽量分到 c 中那一类当中。这样最后的结果就是 G 学会了怎么生成骗过 D 的真假判别和类型判别的,即真的而且是对应类别。这样已经是学会了类别。

之后还想用文本、图片+文本 生成图片。文本到图像的生成:另一个多模态生成问题,GAN 刚还是多模态的(要有噪声)。

为了训这个东西,每张图都有若干句话描述它,这样每个图片都会有匹配的句子和(其他)不匹配的句子。

之前的判别器是一个输出类别,但是现在没得输出了,另外需要一个文本编码器。

判别器 D 是这样工作的:可以获得文本编码器的编码和图片,首先要判断图是不是真的,然后要判断文本和图是不是匹配。那么数据集就包括若干图和它们匹配/不匹配的句子,可以喂给 D。对于前端的 G,也有一个文本编码器编码文本,和噪声一起喂到 G 当中,G 的优化目标就是想欺骗判别器,让图真并且让图和文本匹配。需要注意两个文本编码器应该是一样的。

这样就可以文生图了!皆大欢喜。但是还是想要让 GAN 有一个编码器。最简单的方法是给一张图然后找到离他最近的噪声 z,这样就可以梯度下降了!真是炼丹之炼丹啊。

编码器

无监督转换:回忆条件生成的 GAN(给出一个 one-hot 的 c),当噪声不变 c 修改的时候,除了 c 控制的特征以外其他的应该差不多。现在把 G 控制住,作为预训练模型,然后加一个编码器 E,直接把 G 输出的图 X 重新编码回 Z。这样甚至可以直接变换 c 控制的特征,保持其他的不便。

当然梦想很美好,现实很骨感。当图片太大了或者更复杂的图片(特征),就根本学不会了,虽然 E 已经做得很好了,但还是不行。这个原因是 E 只看过假图,只看过真图,真图和假图的分布本身不是完全重合的,因为 G 不可能训得很完美,然后就会导致输入真图的时候 z 就不一定是好的了,因为有可能压根不在概率分布里,这样 z 是不准确的。

同样可以把整个网络反过来,X -> E -> Z -> G -> X,这是由于 G 可能根本就没法生成出 X,G 根本没法学习。

这样直接暴力搞就肯定不行了,只能在一些比较小或者特征比较接近的数据集上表现比较好,根本原因在于 G 不可能完全学会了真图的分布。

BiGAN

现在就是想要找到 X 和 Z 的联合分布。那么就需要同时训 G 和 E。具体地,E 接受真图 X,输出预测编码 Z',G 接受编码(噪声)Z,输出假图 X',判别器 D 接受 X 和 Z,然后想要把上述两种情况分开,G 和 E 的目标都是欺骗 D 使得它分不出来。

最后反正就会让 E 和 G 互逆(如果是最好情况下),但是实际上炼丹根本练不出来,因为压缩率太高了,Z 维数太小,所以根本不现实。

所以其实可能不需要把特征变成一个特别小的向量。

CoGAN

学习两个(语义相似)领域的联合分布。比如说男女图片,X_A 和 X_B。

首先弄两个个 GAN 让 G_A 学习女性的照片,G_B 生成男性的照片,然后 G_A 和 G_B 前面几层的权重共享,靠近 Z 的是高维特征,因此这样可能能够提取出一些公共的特征;更远离 Z 的可能是比较细节的特征。这样的话生成的图也差不多。同时编码器 D_A 和 D_B 的后几层权重也共享,这样就会有一些高维特征的公共判断。

这样确实能够搞出来大体差不多,然后特定控制的特征不同。但是这个还是不能解决编码器的问题。

CycleGAN

上边东西的问题是提取成高维特征之后信息太少了。现在要搞一个 G_A2B 和 G_B2A,这两个都由一个编码器 E 和一个生成器 G 构成,然后 G_A2B 和真实的 X_B 对抗,由 D_B 分辨,G_B2A 和真实的 X_A 对抗,由 D_A 分辨。如果就这样的话那生成任意一个像 B 当中的东西就可以了,解决方法是把 X_A 经由 G_A2B 生成的 X_B' 重新用 G_B2A 生成 X_A',然后搞一个 X_A 和 X_A' 的误差。G_B2A 同理。

这样的好处是要求 G_A2B 尽量保留原图的特征,这样 G_B2A 才能生成出像原图的东西。

一个应用是分割图转换成街景图,这样以前做图像识别的数据集就能拿来用了,这个东西效果最好的是 CycleGAN,因为之前其实都没有要求生成的图和原图尽量长得一样。

当然这个也有一定问题,比如说如果数据集有点偏差的话最后输出的特征(比如色调)可能会有点区别。一个针对性的方法是要求 X_A 和经过 G_B2A 之后的 X_A' 之后要尽量一样,现在色调就弄好了。

不出意外这个也有问题。比如马转斑马就很好,但是斑马转马就不行了(有纹路),因为如果不保留的话就只能瞎画纹路了。所以说这个会导致特征互相串,特征都被保留起来了。

然后还有一个更好地东西,就是 ACL-GAN,通过重新设计损失函数要求前后差不多就行了,这样就可以去掉一些特征了,皆大欢喜。

文修图

就是 图 + 文 = 修改的图。

这个东西和之前的文生图也差不太多,G 给输入的的图片先 Encode,加上任意一个不符合特征的句子(经过文本编码器),然后要让新的图像和句子匹配,后半部分的判别器 D 和之前一样,要分辨是真的假的还是和是不是符合句子。

这其实也是个弱监督学习,因为并没有图片修改之后长什么样。

RNN

跑去考试了,待补。

Attention 和 Transformer

RNN 可以上下文,但代价大、长时效果差,而且不能并行。

Transformer 能够同时把整个句子(序列)放进去,可以比较高效地并行。

Attention 原理

人类视觉注意力:只有一个焦点,忽略无关信息,可以空间、特征选择性地关注信息。这个比较省能量,可以借鉴到神经网络当中。

这个就叫 self-attention

大概就是对于当前的一个字(token),去看句子当中每个位置哪个和当前的相关,这样就可以得到一个权重的矩阵,表示当前每个词对应的相关性,然后再把已经提取出来的特征相乘,就能得到这句话的意思(大概)。

先把每个 token 搞一个词嵌入然后写成矩阵 Q,之后另外搞一个特征的玩意 K,搞一个 K^\text tQ,这样就相当于每两个东西求一个内积放到一个矩阵当中。

搞完了之后给每个词 softmax 以下就可以得到一个概率分布,之后再搞一个线性变换加权然后输出。

多头注意力

可以影流之主搞 h 个 self-attention,把输入的向量也分 h 份,这样每个头可以包括不同方面的注意力,最后放一起搞一搞输出。

Transformer

整体包括编码器(Encoder)和解码器(Decoder)。

位置编码:句子 = 词 + 顺序,词嵌入可以用 Embedding 序列搞,顺序需要位置嵌入,这就是位置嵌入,要搞一个编码位置的玩意。经常用正弦/余弦函数(为什么要分开搞?)PE_{(pos,2i)}=\sin\left(\frac{pos}{10000^{2i/d}}\right)PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d}}\right),这里交错是为了让不同维度的看起来区别大一点(?)

Encoder:

词嵌入 + 位置嵌入 -> (带残差)多头注意力 -> 归一化 -> 带残差的全连接层。

多个编码器可以套起来。

Decoder:

总体来说,decoder 会不停地输入已经生成的东西,第一次输入一个 BOS 占位符之类的东西,之后喂到多头注意力之后并上 encoder 得到的信息(encoder 只做一次,之后不变),然后获取下一个词的输出。

首先,输入的词嵌入和位置嵌入会先喂到一个“带掩码”的多头注意力。生成当前词的时候要把之后位置的东西搞掉,避免虚空注意到之后的东西,就是给之前注意力最后 softmax 的地方上三角加一个 -inf 就可以了。

然后和 encoder 得到的东西作为 K 和 V,当前的作为 Q,搞出一个东西来。这样 Q 就包含了当前生成器的状态,经过多头注意力可以“注意到”句子当中对当前位置比较必要的一些特征。

NLP 预训练

让无标签数据上学习通用语言表示,预训练之后把最后几层换掉接上。

比较常见的预训练模型:GPT、BERT

GPT 只用了解码器 Decoder,只通过左侧的上下文来预测当前词,通过一些无标签文本来训练下一个 token 的生成概率,训练很方便!训练就是预测下一个 token + 一些附加任务(比如分类判断情感之类的)。

怎么用这个预训练模型捏?首先得在后边搞一个全连接层,喂给 decoder 的时候搞一些不同的标记,然后 transformer 就可以干不同的事了,真神奇!

BERT 则是编码器 Encoder,处理句子之间的关系,以此喂一个句子进去,训练就是人工挖掉一些东西(比如 15% 用于预测,其中 80% 替换成特殊标签,10% 替换称错的词,另外 10% 不动),然后完形填空,这样不仅能够完成句子,还能识别错的词。这个东西的主要作用是 NSP(Next Sentence Prediction),预测下一个句子。

这个实际上不是直接生成一句话,而是判断两个句子是不是连着的(有上下文关系),这样就可以用之前很多 CNN 和 GAN 的训练方法方法故技重施了。

这个也可以用一些刚刚 GPT 的方法用,比如塞一堆分隔符之类的,然后后边接点乱七八糟的东西,这样可以干不同的事。

一点小历史

2017 年 Transformer 提出:典中典之 Attention is All You Need。

最开始大家都用 BERT,因为 BERT 迁移性很好,但是后来 GPT 搞得很好,因为知识已经放在了大模型的参数当中,用生成式的方法解决问题通常可以对问题本身有更好的理解(?)

当然现在从 GPT 1 到 GPT 4 一直在 Scaling Law,即参数提升知识能力也会提升。