From Neural Network to AI
神经网络的基本结构
普通的函数的映射: x->y;
人类可以完成的映射但是机器无法很快完成的映射: 猫的图片->猫
有一组数据 (1, 8), (2, 13), (3, 18), (4, 23), 可以轻易得到映射关系: y = 5x + 3; 这是严格相等的很好找的, 然而有一些数据无法找到精确的映射, 比如:
(0.7, 8.1), (2.3, 12.9), (3.1, 18.4), (3.9, 23.5) , 这时, 可以找到一个近似相等的映射 y = 5x + 3;虽然不是完全相等, 但是可以看成近似相等.
上面讲的是线性函数f(x) = wx + b, 然而实际上的函数大多数是非线性的, 如何得到非线性的函数? 很简单, 在线性函数外层套一层非线性运算即可. 比如平方, sin, 取指函数… 这些函数统称为激活函数, 目的是将线性函数变换为非线性函数f(x) = g(wx + b).
上面讲的是单变量的函数, 多变量函数对应的结构是:
f(x) = g(w1x1 + w2x2 + ... + wnxn + b)
很多情况下, 只套一层激活函数是没有办法得到我们想要的曲线的, 这个时候, 我们可以把上面的非线性函数再进行一次线性变换, 再套一层激活函数
f(x) = g2(w3g1(w1x1 + w2x2 +b1) + b2)
f(x) = g4(w3(g2(w3g1(w1x1 + w2x2 +b1) + b2) + b3))
…可以无限变化下去, 理论上可以逼近任意的连续函数
当变换的层数太多的时候, 看起来很复杂, 我们抽象出输入层和输出层
比如 f(x) = g(w1x1 + w2x2 + b), 他的输入层可以看作有两个输入神经元x1, x2; 输出层一个输出神经元y.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 graph LR subgraph Input_Layer [Input Layer] direction TB x1((x1)) x2((x2)) end subgraph Output_Layer [Output Layer] y((y)) end x1 ==> y x2 ==> y style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x1 fill:#fff,stroke:#333,stroke-width:2px style x2 fill:#fff,stroke:#333,stroke-width:2px style y fill:#fff,stroke:#0277bd,stroke-width:3px
如果再叠加一层变换: f(x) = g2(w3(g1(w1x1 + w2x2) + b1) + b2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 graph LR subgraph Input_Layer [Input Layer] direction TB x1((x1)) x2((x2)) end subgraph Hidden_Layer [Hidden Layer] y1((y1)) end subgraph Output_Layer [Output Layer] y2((y2)) end x1 ==> y1 x2 ==> y1 y1 ==> y2 style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Hidden_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x1 fill:#fff,stroke:#333,stroke-width:2px style x2 fill:#fff,stroke:#333,stroke-width:2px style y1 fill:#fff,stroke:#0277bd,stroke-width:3px style y2 fill:#fff,stroke:#0277bd,stroke-width:3px
这时, 我们可以把第一次变换看成隐藏层.
从神经网络的输入层->隐藏层->输出层的过程, 就叫前向传播.
求w和b
线性回归
在我们知道所有的输入和输出之后, 求出w和b是根本的问题, 因为正确的w和b可以让我们得到正确的映射, 从而得到其他函数值.
以 f(x) = wx + b 为例:
Q1.什么样的w和b是好的?
因为是非线性函数, w 和 b一般无法求出真正的准确值, 只能求近似值. 好的w和b是指使函数输出值接近真实值的取值. 越拟合真实数据越好.
Q2. 数学上怎样判断拟合的好?
我们取了w和b后有一个准确的函数, f(x) = wx + b, 假如我们有:
三组真实数据(
x 1
,
y 1
), (
x 2
,
y 2
), (
x 3
,
y 3
). 将自变量
x 1
,
x 2
,
x 3
带进函数会有我们的
预测值:
y ^ 1
,
y ^ 2
,
y ^ 3
那么对应的误差值分别是 |
y 1
-
y ^ 1
|, |
y 2
-
y ^ 2
|, |
y 3
-
y ^ 3
|
整体的拟合效果用所有误差值的绝对值之和来表示:
∑ i = 1 n | y i − y ^ i |
常见的损失函数: MSE
上面这个表示真实值与预测值误差的函数, 叫做损失函数, 对上面的函数做处理, 得到现在常用的损失函数表示:
去除绝对值, 改用平方, 解决绝对值的问题, 并放大误差较大的问题
取一个平均值, 消除样本数量大小的影响.
这个损失函数可以叫做均方误差M SE(mean square error):
L = 1 n ∑ i = 1 n ( y i − y ^ i ) 2
从参数的视角来看, 损失函数的值也就是误差值的均方值, 取决于我们的w和b, 不同取值对应的L不同.
L(w, b) = 1 n ∑ i = 1 n ( y i − y ^ i ) 2
一元函数求最小值/最大值点, 很明显求导, 找到导数为0的点即可找到极值点
比如一个最简单的(1, 1), (2, 2), (3, 3)
我们取最简单的y = wx, 忽略b值
此时的
L = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 = 1 3 ∑ i = 1 3 ( y i − y ^ i ) 2 = 1 3 [ ( y 1 − y ^ 1 ) 2 + ( y 2 − y ^ 2 ) 2 + ( y 3 − y ^ 3 ) 2 ] = 1 3 [ ( 1 − w ) 2 + ( 2 − 2 w ) 2 + ( 3 − 3 w ) 2 ] = 14 3 w 2 − 28 3 w + 14 3
这是一个开口向上的抛物线, 求最小值要求导数为0点
L ′ = 28 3 w − 28 3
当w = 1时, 取得L最小值为0.
如果保留b 则这是一个多元函数:
L = f ( w , b )
此时L的图像是一个开口向上的三维碗装图. 多元函数求最小值需要分别求偏导为0的点.
即我们要求:
∂ L ( w , b ) ∂ w = 0
和
∂ L ( w , b ) ∂ b = 0
的点
上述的通过寻找一个线性函数, 来拟合x和y关系的方法, 叫做线性回归
梯度下降
当我们的函数在叠加了很多层线性变换和非线性变换后, 用求导的方法无法求出合适的w和b了, 这时我们应该怎么做?
暴力尝试:
1 2 3 4 round 1: w = 5 b = 5 L(w, b) = 10
尝试增加w, 保持b不变
1 2 3 4 round 2: w = 6 b = 5 L(w, b) = 9
L变小了, 说明调整对了.
尝试w不变, b增加:
1 2 3 4 round 3: w = 6 b = 6 L(w, b) = 11
L变大了, 说明不合适.
尝试w不变, b减小:
1 2 3 4 round 4: w = 6 b = 4 L(w, b) = 7
L变小了, 说明调整对了.
…循环调整
回到最初始的状态, L(w, b)在w = 5, b = 5的情况下, 改变了一个增量w = 6, 此时L’也有一个改变量从10 到了 9, 这就算损失函数L对w的偏导数
∂ L ( w , b ) ∂ w
偏导数为正时, 代表w增大L增大, 此时我们应该减小w
偏导数为负时, 代表w增大L减小, 此时我们应该增大w
所以对于变量w和b, 我们应该让他们向着自己偏导数的反方向变化, 即偏导数为正就减小, 偏导数为负就增大
w = w − ∂ L ( w , b ) ∂ w
b = b − ∂ L ( w , b ) ∂ b
这里变化的快慢用一个系数
η
来表示, 这个系数也叫做学习率
w = w − η ∂ L ( w , b ) ∂ w
b = b − η ∂ L ( w , b ) ∂ b
用梯度的思想总结一下上面的例子:
我们有一个初始的w和b值, 最终目的是找到偏导为0的点.
找到当前点的导数
∂ L ( w , b ) ∂ w
沿着导数反方向变化, 使我们的导数贴近0, 即往
− ∂ L ( w , b ) ∂ w
变化, 导数是一个点的变化率, 正式的变化值需要乘上一个系数η, 所以我们变化值是
− η ∂ L ( w , b ) ∂ w
所以我们迭代完一次后的新w, 和b的值为
w = w − η ∂ L ( w , b ) ∂ w
b = b − η ∂ L ( w , b ) ∂ b
每一次迭代我们都需要得到两个偏导数,
( ∂ L ( w , b ) ∂ w , ∂ L ( w , b ) ∂ b )
, 这两个数组成了一个向量, 叫做梯度, 他的正负指明了w 和 b当前的变化方向. 梯度下降指的是我们要逆着梯度的方向走, 使L的值下降. 而顺着梯度走, 函数值会增加
反向传播
在复杂的神经网络中, 函数本身就非常复杂, 更不用说其损失函数了,但是如果抽象成层与层的关系就好多了.
比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 graph LR subgraph Input_Layer [Input Layer] direction TB x((x)) end subgraph Hidden_Layer [Hidden Layer] a((a)) end subgraph Output_Layer [Output Layer] y((y)) end x ==> a a ==> y style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Hidden_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x fill:#fff,stroke:#333,stroke-width:2px style a fill:#fff,stroke:#0277bd,stroke-width:3px style y fill:#fff,stroke:#0277bd,stroke-width:3px
我们的计算流程是这样的:
x → g 1 ( w 1 x + b 1 ) a → g 2 ( w 2 a + b 2 ) y ^ → 1 n ∑ i = 1 n ( y i − y ^ i ) 2 L
我们最终的问题是求合适的
w 1 , w 2 , b 1 , b 2
这四个值, 使Loss最小
前面说过, 我们可以通过求偏导的方式来求这这几个值, 那么我们需要求
∂ L ∂ w 1
实际上的意思是看
w 1
变化一点点的情况下
L
的变化情况:
可以看作
w 1
变化了一点情况下
a
变化了多少, 即
∂ a ∂ w 1
a
变化了一点的情况下
y ^
变化了多少, 即
∂ y ^ ∂ a
y ^
变化了一点的情况下
L
变化了多少, 即
∂ L ∂ y ^
这也是高数中学过的链式求导法则(复合函数求导):
∂ L ∂ w 1
=
∂ L ∂ y ^ ∂ y ^ ∂ a ∂ a ∂ w 1
这里注意顺序, 先求后层对前层的导数, 也就是从右往左求导, 先求出来的导数可以继续用在后续求导, 这个过程就叫反向传播
神经网络的一次训练
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 graph LR subgraph Input_Layer [Input Layer] direction TB x((x)) end subgraph Hidden_Layer [Hidden Layer] a((a)) end subgraph Output_Layer [Output Layer] y((y)) end x ==> a a ==> y style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Hidden_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x fill:#fff,stroke:#333,stroke-width:2px style a fill:#fff,stroke:#0277bd,stroke-width:3px style y fill:#fff,stroke:#0277bd,stroke-width:3px
对于这样一个结构的神经网络
通过前向传播, 根据输入x得到输出y
通过反向传播, 计算出损失函数关于每个参数的梯度
每个参数向着梯度的反方向变化一点点
这就是神经网络的一次训练
调节神经网络的参数
过拟合问题
图中的折线是实际函数的值, 直线是我们拟合的函数, 这时我们的预测值与真实值会有误差.
1 2 3 4 5 6 7 8 9 10 xychart-beta title "Linear Regression(Underfitting Model)" x-axis "Feature X" [1, 2, 3, 4, 5, 6, 7, 8] y-axis "Target Y" 0 --> 20 %% 真实数据点 (用虚线连接以突出位置) line "Training Data" [3, 5, 8, 9, 11, 14, 13, 17] %% 线性模型预测线 (平滑直线) line "Linear Model" [4, 6, 8, 10, 12, 14, 16, 18]
下面这张图表达的是, 我们预测的函数和实际的函数完全重叠了, Loss = 0.
看起来拟合的非常好, 实际上这种预测函数, 只对训练数据效果好, 对新数据的预测反而不如上面的函数.
这种只在训练数据上表现好, 在未见过数据上表现(泛化能力)差的情况, 叫做过拟合(Overfitting)
1 2 3 4 5 6 7 8 9 10 11 12 xychart-beta title "Overfitting Model" x-axis "X" [1, 2, 3, 4, 5, 6, 7, 8] y-axis "Y" 0 --> 20 %% 实际函数: 连续实线 line "Actual Data" [3, 5, 8, 9, 11, 14, 13, 17] %% 过拟合模型: 剧烈震荡连线 %% 即使数值相同,Mermaid也可能因渲染引擎差异显示为不同线型 %% 此处通过微小扰动确保其被识别为独立系列 line "Overfit Model" [3.1, 4.9, 8.1, 8.9, 11.1, 13.9, 13.1, 16.9]
过拟合的情况是因为, 原本的函数并不复杂, 但是训练的时候把噪声和随机波动学会了.
解决过拟合方法:
简化模型复杂度
增加数据训练量(可以在原有数据中创造更多数据, 比如将同一副图旋转, 翻转, 裁剪, 加噪声…)
提前终止训练过程: 假如训练模型训练到最后会导致过拟合, 我们提前终止得到的模型就不会完全拟合.
L1,L2正则化
正则化的作用就是增加函数的泛化能力, 即减弱过拟合.
前文说过了, 训练参数的过程就是让损失函数不断减小的过程(梯度下降). 每次迭代更新中L都会不断减少, 这时, 如果我们让减少的量稍微小一点, 迭代完成后的L就会偏大一点.
我们通过给原来的损失函数增加一个惩罚项得到新的损失函数
惩罚项1:
∑ i = 1 N | w i |
, 叫做L1范数, 所以对应的方法叫L1正则化
惩罚项2:
∑ i = 1 N w i 2
, 叫做L2范数, 所以对应的方法叫L2正则化
范数是向量空间中的概念, 记得去补一下…
L = 1 n ∑ i = 1 n ( y i − y ^ i ) 2
L 1 = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 + ∑ i = 1 N | w i |
L 2 = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 + ∑ i = 1 N w i 2
同样的, 我们给范数前面加一个正则化系数(也叫超参数)λ
L 1 = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 + λ ∑ i = 1 N | w i |
L 2 = 1 n ∑ i = 1 n ( y i − y ^ i ) 2 + λ ∑ i = 1 N w i 2
Dropout
Dropout是丢弃一些参数来解决过拟合问题的方法, 后面补充
卷积神经网络(Convolutional Neural Network)
神经网络的矩阵表达形式
一个简单的神经网络:
y = g ( w x + b )
如果有多个输入就是
y = g ( w 1 x 1 + w 2 x 2 + w 3 x 3 + b )
如果输出有多个就是
y 1 = g ( w 11 x 1 + w 12 x 2 + w 13 x 3 + b 1 )
y 2 = g ( w 21 x 1 + w 22 x 2 + w 23 x 3 + b 2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 graph LR subgraph Input_Layer [Input Layer] direction TB x1((x1)) x2((x2)) x3((x3)) end subgraph Output_Layer [Output Layer] y1((y1)) y2((y2)) end x1 ==> y1 x2 ==> y1 x3 ==> y1 x1 ==> y2 x2 ==> y2 x3 ==> y2 style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x1 fill:#fff,stroke:#333,stroke-width:2px style x2 fill:#fff,stroke:#333,stroke-width:2px style x3 fill:#fff,stroke:#333,stroke-width:2px style y1 fill:#fff,stroke:#333,stroke-width:2px style y2 fill:#fff,stroke:#333,stroke-width:2px
很明显的, 可以写成矩阵乘法形式:
[ b 1 b 2 ] + [ w 11 w 12 w 13 w 21 w 22 w 23 ] [ x 1 x 2 x 3 ] = [ y 1 y 2 ]
写成矩阵的形式:
W X + b = Y
即
Y = g ( W X + b )
如果再多几层神经网络:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 graph LR subgraph Input_Layer [Input Layer] direction TB x1((x1)) x2((x2)) x3((x3)) end subgraph Middle_Layer_1 [Middle_Layer_1] direction TB u1((u1)) u2((u2)) end subgraph Middle_Layer_2 [Middle_Layer_2] direction TB v1((v1)) v2((v2)) end subgraph Output_Layer [Output Layer] y1((y1)) y2((y2)) end x1 ==> u1 x2 ==> u1 x3 ==> u1 x1 ==> u2 x2 ==> u2 x3 ==> u2 u1 ==> v1 u2 ==> v1 u1 ==> v2 u2 ==> v2 v1 ==> y1 v2 ==> y1 v1 ==> y2 v2 ==> y2 style Input_Layer fill:#f9f9f9,stroke:#333,stroke-width:2px style Middle_Layer_1 fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style Middle_Layer_2 fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style Output_Layer fill:#e1f5fe,stroke:#0277bd,stroke-width:2px style x1 fill:#fff,stroke:#333,stroke-width:2px style x2 fill:#fff,stroke:#333,stroke-width:2px style x3 fill:#fff,stroke:#333,stroke-width:2px style y1 fill:#fff,stroke:#333,stroke-width:2px style y2 fill:#fff,stroke:#333,stroke-width:2px style v1 fill:#fff,stroke:#333,stroke-width:2px style v2 fill:#fff,stroke:#333,stroke-width:2px style u1 fill:#fff,stroke:#333,stroke-width:2px style u2 fill:#fff,stroke:#333,stroke-width:2px
第一次变换:
u 1 = g ( w 11 x 1 + w 12 x 2 + w 13 x 3 + b 1 )
u 1 = g ( w 21 x 1 + w 22 x 2 + w 23 x 3 + b 2 )
矩阵形式:
U = W 0 X + b 0
第二次变换:
v 1 = g ( w 11 ′ u 1 + w 12 ′ u 2 + b 1 ′ )
v 2 = g ( w 21 ′ u 1 + w 22 ′ u 2 + b 2 ′ )
矩阵形式:
V = W 1 U + b 1
第三次变换:
y 1 = g ( w 11 ″ v 1 + w 12 ″ v 2 + b 1 ″ )
y 2 = g ( w 21 ″ v 1 + w 22 ″ v 2 + b 2 ″ )
矩阵形式:
Y = W 2 V + b 2
总结一下:
U = W 0 X + b 0 V = W 1 U + b 1 Y = W 2 V + b 2
再抽象一下, 把所有中间层都看作输入的一部分, 最开始的层为第0层, 后面的依次增加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 graph LR subgraph Layer_0 ["a<sup>[0]</sup>"] direction TB a0_1(("a<sub>1</sub><sup>[0]</sup>")) a0_2(("a<sub>2</sub><sup>[0]</sup>")) a0_3(("a<sub>3</sub><sup>[0]</sup>")) end subgraph Layer_1 ["a<sup>[1]</sup>"] direction TB a1_1(("a<sub>1</sub><sup>[1]</sup>")) a1_2(("a<sub>2</sub><sup>[1]</sup>")) end subgraph Layer_2 ["a<sup>[2]</sup>"] direction TB a2_1(("a<sub>1</sub><sup>[2]</sup>")) a2_2(("a<sub>2</sub><sup>[2]</sup>")) end subgraph Layer_3 ["a<sup>[3]</sup>"] direction TB a3_1(("a<sub>1</sub><sup>[3]</sup>")) a3_2(("a<sub>2</sub><sup>[3]</sup>")) end %% 连接 Layer 0 -> Layer 1 (全连接) a0_1 ==> a1_1 a0_2 ==> a1_1 a0_3 ==> a1_1 a0_1 ==> a1_2 a0_2 ==> a1_2 a0_3 ==> a1_2 %% 连接 Layer 1 -> Layer 2 (全连接) a1_1 ==> a2_1 a1_2 ==> a2_1 a1_1 ==> a2_2 a1_2 ==> a2_2 %% 连接 Layer 2 -> Layer 3 (全连接) a2_1 ==> a3_1 a2_2 ==> a3_1 a2_1 ==> a3_2 a2_2 ==> a3_2 %% 样式设置 (仿照原图颜色) style Layer_0 fill:#f9f9f9,stroke:none,stroke-width:0px style Layer_1 fill:#f9f9f9,stroke:none,stroke-width:0px style Layer_2 fill:#f9f9f9,stroke:none,stroke-width:0px style Layer_3 fill:#f9f9f9,stroke:none,stroke-width:0px style a0_1 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a0_2 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a0_3 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a1_1 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a1_2 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a2_1 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a2_2 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a3_1 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff style a3_2 fill:#1E90FF,stroke:#fff,stroke-width:2px,color:#fff
则有:
A [ 1 ] = g ( W [ 1 ] A [ 0 ] + b [ 1 ] ) A [ 2 ] = g ( W [ 2 ] A [ 1 ] + b [ 2 ] ) A [ 3 ] = g ( W [ 3 ] A [ 2 ] + b [ 3 ] )
用L表示层级, 则有:
A [ L ] = g ( W [ L ] A [ L − 1 ] + b [ L ] )
为什么要转换成矩阵形式?(GPU和CPU)
转换成矩阵乘法, 可以用GPU进行并行运算, 效率高很多, 比用CPU快多了
待补充…
卷积(Convolution)
全连接层(Full Connect) 是指所有神经元都与前一层所有神经元相连的层, 比如上面的例子.
全连接层有一个问题就是参数量太大了, 比如, 一张图片的像素规模为30 * 30, 则第一层有900个输入, 假如第二层为全连接层且有1000个神经元, 则一共有90000个参数量:
输 出 神 经 元 数 量 输 入 神 经 元 数 量 W [ 输 出 神 经 元 数 量 ] [ 输 入 神 经 元 数 量 ] = W [ 1000 ] [ 900 ]
这个参数量太大了, 而且仅仅是平铺展开, 如果图片仅仅是微调一下, 比如平移, 调暗等等, 这些参数得重新训练, 非常麻烦. 解决方法是:
减小参数量的规模
想办法抽取图像的关键特征
卷积就是这样的一种运算, 核心思想是, 把某一块的输入, 提取出一个特征.
[ w 11 w 12 w 21 w 22 ] × [ b 11 b 12 b 21 b 22 ] = w 11 b 11 + w 12 b 12 + w 21 b 21 + w 22 b 22
卷积运算的定义: 两个规格相同的矩阵进行卷积运算, 每个元素与两一个矩阵对应元素相乘, 最后相加
对一个大矩阵进行卷积提取特征值的过程: 用小尺寸卷积核(滤波器)在大矩阵上 滑动 ,在每个位置将卷积核与对应区域进行逐元素相乘后求和 ,生成输出矩阵的一个值
拿一个规格为3 * 3的矩阵为例, 我们首先取一个卷积核矩阵(Convolution kernel), 假设规模是2 * 2的. 其卷积的过程如下:
[ 0 1 2 3 4 5 6 7 8 ] × [ 0 1 2 3 ] = [ 12 ]
[ 0 1 2 3 4 5 6 7 8 ] × [ 0 1 2 3 ] = [ 12 25 ]
[ 0 1 2 3 4 5 6 7 8 ] × [ 0 1 2 3 ] = [ 12 25 37 ]
[ 0 1 2 3 4 5 6 7 8 ] × [ 0 1 2 3 ] = [ 12 25 37 43 ]
通过设置不同的卷积核, 我们可以获得不同的图像效果, 如模糊, 轮廓, 锐化…
在Deep Learning 领域, 卷积核是一组待训练的值.
回到刚刚的经典神经网络结构中, 我们将一个全连接层替换成一个卷积层, 这就大大减少了我们的参数数量
池化(待补充)
CNN的局限
适用于静态数据, 比如图片. 动态数据如时间序列, 文本, 语音等不适用
循环神经网络(Recurrent Neural Network)
经典RNN
在已经有了普通的神经网络的情况下, 为什么还有RNN?
普通的神经网络的输入要求是固定的, 比如CNN可能要求一张图共 30 * 30 = 900个输入变量. 但是RNN的输入可以是不变的
RNN在时序问题上表现非常好, 比如, 在NLP中的一个词性标注任务:我吃苹果, 通过大量学习后的CNN可能会标注出: 我(nn)吃(v)苹果(nn), 但是如果是RNN的话, 在时序方面表现非常好, 输入的顺序是我->吃->苹果, 输入第一个我是名词, 他后面的第二个输入吃大概率是动词, 第二个输入吃(v)是动词, 则第三个输入苹果(nn)大概率是动词. 这种特性也会用在大模型(LLM)中, 后面会说到.
假如现在有四个词输入, 传统的神经网络做法是:
Y ⟨ 1 ⟩ = g ( W X ⟨ 1 ⟩ + b ) Y ⟨ 2 ⟩ = g ( W X ⟨ 2 ⟩ + b ) Y ⟨ 3 ⟩ = g ( W X ⟨ 3 ⟩ + b ) Y ⟨ 4 ⟩ = g ( W X ⟨ 4 ⟩ + b )
这样他们的输出都是独立的没有关联, 但是我们实际的输入是有顺序关联的, 怎么做才能关联起来?
我们将第一个词非线性变换后, 不直接输出, 而是先记录为一个隐藏状态, 再经过一次非线性变换得到输出, 将这个隐藏状态用来和下一个输入一起运算.
时间步
隐藏状态
输出
t=1
h ⟨ 1 ⟩ = g ( W x h X ⟨ 1 ⟩ + b h )
Y ⟨ 1 ⟩ = g ( W h y h ⟨ 1 ⟩ + b y )
t=2
h ⟨ 2 ⟩ = g ( W x h X ⟨ 2 ⟩ + W h h h ⟨ 1 ⟩ + b h )
Y ⟨ 2 ⟩ = g ( W h y h ⟨ 2 ⟩ + b y )
注意这里用了的W矩阵不同, xh下标表示这是由x得到h的W矩阵:
X ⟨ 1 ⟩ → W x h h ⟨ 1 ⟩ → W h y Y ⟨ 1 ⟩ ↓ W h h X ⟨ 2 ⟩ → W x h h ⟨ 2 ⟩ → W h y Y ⟨ 2 ⟩
RNN公式:
h ⟨ t ⟩ = g 1 ( W x h X ⟨ t ⟩ + b h ) Y ⟨ t ⟩ = g 2 ( W h y h ⟨ t ⟩ + b y )
Note:微信收藏里面存了一张图
Problems:
信息会随着时间步增多而逐渐丢失, 无法捕捉长期依赖
无法并行运算, 下一时间的输入依赖上一时间的隐藏状态
用GRU和LSTM可以改进, 但是, transformer 碾压一切