人工智能基础

常用激活函数

Sigmoid函数

\(f ( x ) = \frac{ 1 }{ 1 + e^{ - x } } : ( - \infty , + \infty ) \to ( 0 , 1 )\).

\(f ' ( x ) = f ( x ) ( 1 - f ( x ) )\).

tanh函数

\(f ( x ) = \frac{ e^x - e^{ - x } }{ e^x + e^{ - x } } : ( - \infty , + \infty ) \to ( - 1 , 1 )\).

\(f ' ( x ) = 1 - f^2 ( x )\).

ReLU函数

\(f ( x ) = \max ( 0 , x ) : ( - \infty , + \infty ) \to ( 0 , + \infty )\).

Leaky ReLU函数

\(f ( x ) = \max ( \alpha x , x ) , 0 < \alpha < 1 : ( - \infty , + \infty ) \to ( - \infty , + \infty )\).

Softmax函数

\(f ( x_i ) = \frac{ e^{ x_i } }{ \sum_j e^{ x_j } }\).

损失函数

Least Square

\(\arg \min \sum_{ i = 1 }^n ( f ( x_i ) - y_i )^2 = \arg \min ( A \beta - Y \mid A \beta - Y )\).用最小二乘法取\(\hat \beta = ( A^T A )^{ - 1 } A^T Y\).

Cross Entropy

用错误的分布\(q\)来表示真实分布\(p\)的样本,则平均编码长度应该是:

\[ H ( p , q ) = \sum_i p ( i ) \log ( \frac{ 1 }{ q ( i ) } ) = - \sum_i p ( i ) \log{ q ( i ) } \]

此为交叉熵.

特别地,当最终样本只有两个的时候,例如Logistical Regression问题,可以写成:

\[ \begin{aligned} H & = - ( y \log a + ( 1 - y ) \log ( 1 - a ) ) \\ \frac{ \partial H }{ \partial a } & = - ( \frac{ y }{ a } - \frac{ 1 - y }{ 1 - a } ) \end{aligned} \]

那如果有多个呢?考虑直接对归一化条件作偏导,先有:

\[ \begin{aligned} H ( p , q ) & = - \sum_i p_i ( \log{ q_i } - \log ( \sum_j q_j ) ) \\ \frac{ \partial H ( p , q ) }{ \partial q_k } & = - \frac{ p_k }{ q_k } + \frac{ \sum p_k }{ \sum_j q_j } \\ & = - \frac{ p_k }{ q_k } + 1 \end{aligned} \]

再乘以softmax那里的\(q_k\),得到\(- p_k + q_k = - y_k + f ( x_k )\).

神经网络实现

通过若干隐藏层,假设最后的输出层为第\(L\)层,则:

  1. 对于第\(l\)层,取\(\vec{ z }_l = ( W_l )^t \vec{ a }_{ l - 1 } + \vec{ b }_l\).这里对\(W_l\)作转置的目的是写代码的时候需要用行向量.

  2. 对于第\(l\)层,取\(\vec{ a }_l = f ( \vec{ z }_l )\),这里意味着将每一个分量对\(f\)操作.

  3. 对于最终答案,取误差\(\mathcal{ L } = \frac{ 1 }{ m } ( \vec{ y } - \vec{ a }_L \mid \vec{ y } - \vec{ a }_L )\).

梯度下降法

换言之就是让\(w : = w - \alpha \frac{ \partial \mathcal{ L } }{ \partial w }\),其中\(\alpha\)是一个选定的小常数,也可以采用类似模拟退火的方式动态决定.事实上可以把各个位置分开,写作\(w_j : = w_j - \alpha \frac{ \partial \mathcal{ L } }{ \partial w_j }\).

另外,虽然是这么写,应当见到去掉下标\(k\)的记号仍然合理,无非是逐分量做此操作,因此下面如无特殊说明,运算均采用逐分量运算.例如可以定义\(\vec{ a } \circ \vec{ b }\)为两个向量逐分量相乘后得到的新向量,为表区分用\(\times\)表示正常的矩阵乘法.甚至采取\(( \vec{ a } - \vec{ y } )^2\)表示其自点积.坦白而言,笔者对此符号相当无奈,可也想不出什么更好的写法了.但总之这种写法总是强于部分参考资料上所将下标放上面的写作\(a^L\)的做法.笔者所能维持的精神数院人的唯一做法也只能是在下面加上向量符号,藉此泄愤.

顺便一提,应当见到\(\times\)\(\circ\)这两种运算比较随意,用线性映射来理解,你这个\(W \times\)任意作用在一个向量上就行.

误差反向传播

既然要用梯度下降法,就应该把每一层的偏导都求出来.然而\(\mathcal{ L }\)是最后一层的结果,因此应该用链式法则一路求出前面的偏导.

更具体地,不妨设误差函数选的是\(( \vec{ a } - \vec{ y } )^2\),激活函数选的是cross entropy有:

  1. \(\frac{ \partial{ \mathcal{ L } } }{ \partial \vec{ a }_{ L } } = ( \vec{ a }_{ L } - \vec{ y } )\).(如若选择不同的误差函数,这里作适当变化)

  2. \(\frac{ \partial \vec{ a }_{ l } }{ \partial \vec{ z }_{ l } } = f ' ( \vec{ z }_{ l } ) = \vec{ a }_{ l } \circ ( 1 - \vec{ a }_{ l } )\).(如若选取不同的激活函数,这里作适当变化)

  3. \(\frac{ \partial \vec{ z }_{ l + 1 } }{ \partial \vec{ z }_{ l } } = \frac{ \partial \vec{ z }_{ l + 1 } }{ \partial \vec{ a }_{ l } } \frac{ \partial \vec{ a }_{ l } }{ \partial \vec{ z }_{ l } } = ( W_{ l + 1 } )^t \times ( \vec{ a }_l \circ ( 1 - \vec{ a }_l ) )\).

  4. \(\frac{ \partial \vec{ z }_{ l } }{ \partial W_{ l } } = ( \vec{ a }_{ l - 1 } )\).结果理应是一个矩阵,其实就是这个列向量不断复制若干遍,或者写成\(( \vec{ a }_{ l - 1 } )^t M ( 1 )\),其中\(M ( 1 )\)是全\(1\)矩阵.

  5. \(\frac{ \partial \vec{ z }_{ l } }{ \partial \vec{ b }_{ l } } = 1\).

我们应当见到:

不妨设\(\delta_l = \frac{ \partial{ \mathcal{ L } } }{ \partial \vec{ z }_l }\).见到:

  1. \(\delta_L = \frac{ \partial \mathcal{ L } }{ \partial \vec{ z }_L } = \frac{ \partial \mathcal{ L } }{ \partial \vec{ a }_L } \frac{ \partial \vec{ a }_L }{ \partial \vec{ z }_L } = ( \vec{ a }_{ L } - \vec{ y } ) \circ \vec{ a }_{ L } \circ ( 1 - \vec{ a }_{ L } )\).前者会因为误差函数的选取而改变,后者会因为激活函数的选取而改变.

  2. \(\delta_l = \frac{ \partial{ \mathcal{ L } } }{ \partial \vec{ z }_l } = \delta_{ l + 1 } \frac{ \partial \vec{ z }_{ l + 1 } }{ \partial \vec{ z }_l } = \delta_{ l + 1 } \circ ( W_{ l + 1 } )^t \times ( \vec{ a }_{ l } \circ ( 1 - \vec{ a }_{ l } ) )\).

  3. \(\frac{ \partial \mathcal{ L } }{ \partial W_{ l } } = \frac{ \partial \mathcal{ L } }{ \partial \vec{ z }_{ l } } \frac{ \partial \vec{ z }_l }{ \partial W_{ l } } = \delta_l \times a_{ l - 1 }^t\).

  4. \(\frac{ \partial \mathcal{ L } }{ \partial \vec{ b }_l } = \delta_l\).

如此以上更新即可.

卷积神经网络(CNN)

神经网络受矩阵乘法的限制,导致对于真实的尺寸巨大的图像难以快速识别,因此产生了卷积神经网络的概念,大概有以下特征:

  1. 空间上权值共享:不同位置使用同一个卷积核(滤波器)

  2. 稀疏链接:每一层只链接前一层的感受野.

  3. 等变表示:卷积神经网络有某种平移不变性.

对于2D卷积,其公式如下:

\[ S_{ r , c } = ( X * W )_{ r , c } = \sum_i \sum_j X_{ r + i , c + j } \times w_{ i , j } \]

其中\(W\)是卷积核,\(X\)是输入图像,\(S\)是输出的结果.如果一个图像有多个通道(比如色彩层之类的),每个通道上都需要应用一个卷积核.

下面引入一些名词:

  1. input size:输入图像的尺寸.

  2. padding:填充的像素数.

  3. filter size:卷积核的尺寸.有时也写作两个变量:filter height和filter width.3D卷积还会有一个filter depth的变量.

  4. stride:步长.

  5. output size:卷积后输出的尺寸.有时也写作feature size.

  6. input channels:输入图像的通道数.

  7. n filters:卷积核的数量.

  8. dilation rate:膨胀率,用于空洞卷积.膨胀率为\(d\)的时候,卷积核中间会插入\(d - 1\)\(0\)间隔.

感受野计算

先看output size的计算,容易见到,其各个维度方面计算是独立的.只要对于单个维度算出卷积核在上面移动的次数,最后将不同维度相乘即可.

对于单个维度,这个维度的移动次数应该是:

$$ \[\begin{aligned} \text{ output \_ size } & = \lceil \frac{ \text{ input \_ size } + 2 \times \text{ padding } - \text{ filter \_ size } + 1 }{ \text{ stride } } \rceil \\ & = \lfloor \frac{ \text{ input \_ size } + 2 \times \text{ padding } - \text{ filter \_ size } }{ \text{ stride } } \rfloor + 1 \\ \end{aligned}\]

$$

这个公式相当容易理解,原因是\(\text{ stride } = 1\)的时候,上面恰好是移动的次数,而\(\text{ stride }\)变化的时候,当然要拿到一个上取整.

至于所谓的空洞卷积,只需在上面的基础上改\(\text{ filter \_ size }\)就好.

至于乘法操作,每得到一个\(\text{ output }\)当然都会需要\(\text{ filter \_ size }\)次乘法操作.

再看感受野的计算,不妨设\(S_i\)为前\(i\)次卷积的\(\text{ stride }\)的乘积,设\(k_{ i + 1 }\)表示第\(i + 1\)层的\(\text{ kernel \_ size }\),则:

\[ RF_{ i + 1 } = RF_i + ( k_{ i + 1 } - 1 ) \times S_i \]

这个公式的含义大概是每次先看对应了多大的原数据上的范围,再把原本的边界\(RF_i\)给补上.

池化(Pooling)

池化操作它没有一个可学习的参数,只是对输入数据进行固定的操作.简单来说就是降低输入的规模,以实现更好的鲁棒性以及提高效率.

常见的池化操作包括:

  1. MaxPooling:取区域内的最大值.

  2. MeanPooling:取区域内的平均值.

  3. PyramidPooling:多次进行尺度不同的池化.

常见卷积架构

AlexNet

首次引入ReLU激活函数,Dropout 技术,以及数据增强,提高了模型的训练效率和泛化能力.

采用了\(8\)层深的网络结构,证明了深度网络的潜力.

VGG

开始堆叠小尺寸的卷积核,获得与大卷积核相似的感受野的同时可以增加网络深度.

ResNet

引入残差的概念,直接将输入数据累加(跳跃连接)到最后的输出中,这样网络学习的实际上是输入和输出之间的残差,从而提高了网络学习能力.

SqueezeNet

SqueezeNet的基本构建单元是Fire模块.Fire模块由一个squeeze层和一个expand层组成.squeeze层使用\(1 \times 1\)卷积核减少通道数,而expand层则使用\(1 \times 1\)\(3 \times 3\)卷积核增加通道数.这种设计有效地减少了参数数量和计算量.

MobileNet
  1. 深度卷积:在这个操作中,每个输入通道独立地进行卷积,这意味着在进行卷积时,不同通道之间没有交互.这样可以减少计算量和参数数量.

  2. 逐点卷积:逐点卷积使用\(1 \times 1\)的卷积核,它作用在深度卷积的输出上,将不同通道的信息整合在一起.逐点卷积可以减少参数数量,同时保持较高的性能.

ShuffleNet
  1. 组卷积(Group Convolution):将通道分成几个组,并使用不同的卷积神经网络层执行标准卷积.

  2. 打乱层(Shuffle layer):通过对通道进行洗牌,将不同组的信息合并.

反卷积

也就是将较小的数据特征图扩大到较大的尺寸.有的时候也把这个操作说成上采样.

  1. 插值步骤(Interpolation Step):首先,在输入特征图的元素之间插入零,增加特征图的尺寸.

  2. 卷积步骤(Convolution Step):接下来,对扩大后的特征图应用一个标准的卷积操作.此步骤相当于在扩大的特征图上滑动卷积核,计算卷积输出.