前面我们详细拆解了激活函数------从隐藏层的 S i g m o i d Sigmoid Sigmoid、 T a n h Tanh Tanh、 R e L U ReLU ReLU,到输出层的恒等、 S i g m o i d Sigmoid Sigmoid 或 S o f t m a x Softmax Softmax。但激活函数本身只是一个零件:它定义了单个神经元如何对输入做非线性变换,却还没有解释整个网络是如何"运转"起来。
输入层 ------ 对输入层的结点,其输出值就是输入值,即 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3
隐藏层 ------ 输入层的结点 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 与下一层的结点 a_1 有连接,则其输出值计算分为两步
线性变换 ------ 上一层的 x 1 , x 2 , x 3 x_1,x_2,x_3 x1,x2,x3 结点与对应连接权值 w 11 , w 12 , w 13 w_{11},w_{12},w_{13} w11,w12,w13 进行加权和运算,结果加偏置项 b 1 b_1 b1:
z 1 = w 11 x 1 + w 12 x 2 + w 13 x 3 + b 1 z_1 = w_{11}x_1+w_{12}x_2+w_{13}x_3+b_1 z1=w11x1+w12x2+w13x3+b1
激活函数 ------ 通过非线性函数 h ( ) h() h()(即激活函数),如 R e L u ReLu ReLu, s i g m o i d sigmoid sigmoid 等函数激活:
a 1 = h ( z 1 ) a_1 = h(z_1) a1=h(z1)
得到的结果就是本层结点 a_1 的输出,对其他节点也是同样的操作:
第1个神经元的计算: a 1 = h ( z 1 ) = σ ( b 1 + w 11 x 1 + w 12 x 2 + w 13 x 3 ) a_1 = h(z_1) = σ(b_1 + w_{11}x_1 + w_{12}x_2 + w_{13}x_3) a1=h(z1)=σ(b1+w11x1+w12x2+w13x3)
第2个神经元的计算: a 2 = h ( z 2 ) = σ ( b 2 + w 21 x 1 + w 22 x 2 + w 23 x 3 ) a_2=h(z_2) = σ(b_2 + w_{21}x_1 + w_{22}x_2 + w_{23}x_3) a2=h(z2)=σ(b2+w21x1+w22x2+w23x3)
第3个神经元的计算: a 3 = h ( z 3 ) = σ ( b 3 + w 31 x 1 + w 32 x 2 + w 33 x 3 ) a_3=h(z_3) = σ(b_3 + w_{31}x_1 + w_{32}x_2 + w_{33}x_3) a3=h(z3)=σ(b3+w31x1+w32x2+w33x3)
第4个神经元的计算: a 4 = h ( z 4 ) = σ ( b 4 + w 41 x 1 + w 42 x 2 + w 43 x 3 ) a_4=h(z_4) = σ(b_4 + w_{41}x_1 + w_{42}x_2 + w_{43}x_3) a4=h(z4)=σ(b4+w41x1+w42x2+w43x3)
仿射变换层又称 Affine 层,affine transformation 即仿射变换 是数学术语,意思是"线性变换 + 平移"。Affine 层实现的是 Z = X W + B Z=XW+B Z=XW+B 的功能
参考前面的神经网络,把四个隐藏层的神经元关于 Z = X W + B Z=XW+B Z=XW+B 的操作提取出来,有:
第1个神经元的操作: z 1 = b 1 + w 11 x 1 + w 12 x 2 + w 13 x 3 z_1 = b_1 + w_{11}x_1 + w_{12}x_2 + w_{13}x_3 z1=b1+w11x1+w12x2+w13x3
第2个神经元的操作: z 2 = b 2 + w 21 x 1 + w 22 x 2 + w 23 x 3 z_2 = b_2 + w_{21}x_1 + w_{22}x_2 + w_{23}x_3 z2=b2+w21x1+w22x2+w23x3
第3个神经元的操作: z 3 = σ ( b 3 + w 31 x 1 + w 32 x 2 + w 33 x 3 z_3 = σ(b_3 + w_{31}x_1 + w_{32}x_2 + w_{33}x_3 z3=σ(b3+w31x1+w32x2+w33x3
第4个神经元的操作: z 4 = σ ( b 4 + w 41 x 1 + w 42 x 2 + w 43 x 3 z_4 = σ(b_4 + w_{41}x_1 + w_{42}x_2 + w_{43}x_3 z4=σ(b4+w41x1+w42x2+w43x3
方程组化 ------ 把四个神经元写在一起,写成方程组
{ z 1 = b 1 + w 11 x 1 + w 12 x 2 + w 13 x 3 z 2 = b 2 + w 21 x 1 + w 22 x 2 + w 23 x 3 z 3 = b 3 + w 31 x 1 + w 32 x 2 + w 33 x 3 z 4 = b 4 + w 41 x 1 + w 42 x 2 + w 43 x 3 \begin{cases} z_1 = b_1 + w_{11}x_1 + w_{12}x_2 + w_{13}x_3 \\ z_2 = b_2 + w_{21}x_1 + w_{22}x_2 + w_{23}x_3 \\ z_3 = b_3 + w_{31}x_1 + w_{32}x_2 + w_{33}x_3 \\ z_4 = b_4 + w_{41}x_1 + w_{42}x_2 + w_{43}x_3 \end{cases} ⎩ ⎨ ⎧z1=b1+w11x1+w12x2+w13x3z2=b2+w21x1+w22x2+w23x3z3=b3+w31x1+w32x2+w33x3z4=b4+w41x1+w42x2+w43x3
矩阵化 ------ 观察方程组,可以看到:每个 z i z_i zi 都是 x 1 , x 2 , x 3 x_1, x_2, x_3 x1,x2,x3 的线性组合。具体来说:
w 11 x 1 + w 12 x 2 + w 13 x 3 w 21 x 1 + w 22 x 2 + w 23 x 3 w 31 x 1 + w 32 x 2 + w 33 x 3 w 41 x 1 + w 42 x 2 + w 43 x 3 = w 11 w 12 w 13 w 21 w 22 w 23 w 31 w 32 w 33 w 41 w 42 w 43 x 1 x 2 x 3 \begin{bmatrix} w_{11}x_1 + w_{12}x_2 + w_{13}x_3 \\ w_{21}x_1 + w_{22}x_2 + w_{23}x_3 \\ w_{31}x_1 + w_{32}x_2 + w_{33}x_3 \\ w_{41}x_1 + w_{42}x_2 + w_{43}x_3 \end{bmatrix}= \begin{bmatrix} w_{11}&w_{12}&w_{13} \\ w_{21}&w_{22}&w_{23} \\ w_{31}&w_{32}&w_{33} \\ w_{41}&w_{42}&w_{43} \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix} w11x1+w12x2+w13x3w21x1+w22x2+w23x3w31x1+w32x2+w33x3w41x1+w42x2+w43x3 = w11w21w31w41w12w22w32w42w13w23w33w43 x1x2x3
进一步的,加上偏置值:
z 1 z 2 z 3 z 4 = w 11 w 12 w 13 w 21 w 22 w 23 w 31 w 32 w 33 w 41 w 42 w 43 x 1 x 2 x 3 + b 1 b 2 b 3 b 4 \begin{bmatrix} z_1 \\ z_2\\ z_3 \\ z_4 \end{bmatrix} = \begin{bmatrix} w_{11}&w_{12}&w_{13} \\ w_{21}&w_{22}&w_{23} \\ w_{31}&w_{32}&w_{33} \\ w_{41}&w_{42}&w_{43} \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix} + \begin{bmatrix} b_1 \\ b_2 \\ b_3 \\ b_4 \end{bmatrix} z1z2z3z4 = w11w21w31w41w12w22w32w42w13w23w33w43 x1x2x3 + b1b2b3b4
写成简写,即有: A = W X + B A=WX+B A=WX+B
这是为什么神经网络计算天然适合 G P U GPU GPU,因为它本质就是一连串大型矩阵运算,通过一次矩阵即可算完四个神经元
代码实现 ------ 采用 numpy 库的 np.dot 矩阵乘法实现
python复制代码
a = np.dot(x, W) + b
np.dot(x, W) 采用 numpy 的矩阵乘法
输入 x x x 形状 ( N , n ) (N, n) (N,n),权重 W W W 形状 ( n , m ) (n, m) (n,m),有: ( N , n ) ⋅ ( n , m ) = ( N , m ) (N, n)\cdot(n, m)=(N, m) (N,n)⋅(n,m)=(N,m)
结果矩阵的每个元素 = 某个样本与某个神经元权重的点积
+ b 采用 numpy 的广播机制
偏置 b b b 形状 ( m , ) (m,) (m,),有: ( N , m ) + ( m , ) = ( N , m ) (N, m) + (m,)=(N, m) (N,m)+(m,)=(N,m)
整合为类的形式,有:
python复制代码
class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
def forward(self, x):
self.x = x
return np.dot(x, self.W) + self.b
激活函数层
!IMPORTANT
激活函数层是紧跟在仿射变换之后的组件:
Affine 层完成了 Z Z Z = X W XW XW + B B B 的线性变换
激活函数层则对 Z Z Z 中的每个元素独立应用非线性函数,得到本层的最终输出 A A A = σ σ σ( Z Z Z)
数学表达 ------ y k = e a k ∑ i = 1 n e a i y_k = \frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}} yk=∑i=1neaieak,其中 a k a_k ak 是第 k k k 个输出神经元的原始值( l o g i t s logits logits), n n n 是类别总数
代码实现
基本实现
python复制代码
def softmax(a):
exp_a = np.exp(a) # 分子:取指数
sum_exp_a = np.sum(exp_a) # 分母:指数求和
y = exp_a / sum_exp_a
return y
多分类任务 ------ 判断图片的动物是否为猫(判断选项有猫、狗、兔)
python复制代码
a = np.array([0.3, 2.9, 4.0]) # 3个选项的原始输出
y = softmax(a)
print(y) # [0.018 0.245 0.737]
print(np.sum(y)) # 1.0(概率之和为 1)
Softmax 的实现陷阱:数值溢出
问题 ------ 上面的基础实现虽然正确,但存在一个严重问题:数值溢出,考虑一个极端情况:
python复制代码
a = np.array([1010, 1000, 990])
result = np.exp(a) / np.sum(np.exp(a)) # 输出: array([nan, nan, nan])
这里输出错误数据 n a n nan nan,这是因为 e 1010 e^{1010} e1010 是一个天文数字,超出了计算机能表示的范围
解决原理 ------ 常数不变性
Softmax 有一个重要的数学性质:同时加减一个常数,结果不变
故可以给所有输入减去一个数(最大值),结果不变,且避免数值溢出问题
证明过程:
y k = e a k ∑ i = 1 n e a i = C ⋅ e a k C ⋅ ∑ i = 1 n e a i = e a k + log C ∑ i = 1 n e a i + log C = e a k + C ′ ∑ i = 1 n e a i + C ′ y_k = \frac{e^{a_k}}{\sum_{i=1}^{n} e^{a_i}} = \frac{C \cdot e^{a_k}}{C \cdot \sum_{i=1}^{n} e^{a_i}} = \frac{e^{a_k + \log C}}{\sum_{i=1}^{n} e^{a_i + \log C}} = \frac{e^{a_k + C'}}{\sum_{i=1}^{n} e^{a_i + C'}} yk=∑i=1neaieak=C⋅∑i=1neaiC⋅eak=∑i=1neai+logCeak+logC=∑i=1neai+C′eak+C′