numpy实现神经网络系列

工程地址:https://github.com/yizt/numpy_neuron_network

基础知识

0_1-全连接层、损失函数的反向传播

0_2_1-卷积层的反向传播-单通道、无padding、步长1

0_2_2-卷积层的反向传播-多通道、无padding、步长1

0_2_3-卷积层的反向传播-多通道、无padding、步长不为1

0_2_4-卷积层的反向传播-多通道、有padding、步长不为1

0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling、GlobalMaxPooling

0_3-激活函数的反向传播-ReLU、LeakyReLU、PReLU、ELU、SELU

0_4-优化方法-SGD、AdaGrad、RMSProp、Adadelta、Adam

DNN练习

1_1_1-全连接神经网络做线性回归

1_1_2-全连接神经网络做mnist手写数字识别

CNN练习

2_1-numpy卷积层实现

2_2-numpy池化层实现

2_3-numpy-cnn-mnist手写数字识别

本文目录

本文下载地址:0_1-全连接层、损失函数的反向传播

依赖知识

a) 了解神经网络的基础知识

b) 熟悉导数的链式法则及常见函数的导数

c) 熟悉常见的优化方法,梯度下降,随机梯度下降等

d) 熟悉矩阵和向量的乘加运算

约定说明

a) 对于一个 n n <script type="math/tex" id="MathJax-Element-1">n</script>层神经网络,第 i <script type="math/tex" id="MathJax-Element-2">i</script>层表示为 li, 1in l i ,   1 ≤ i ≤ n <script type="math/tex" id="MathJax-Element-3">l_i, \ 1 \le i \le n</script> ,第 i i <script type="math/tex" id="MathJax-Element-4">i</script>神经元个数为 | l i | <script type="math/tex" id="MathJax-Element-5">|l_i|</script> ; 注意 li l i <script type="math/tex" id="MathJax-Element-6">l_i</script>是输入层, ln l n <script type="math/tex" id="MathJax-Element-7">l_n</script> 是输出层。

b) 对于神经网络的一个输入样本 x x <script type="math/tex" id="MathJax-Element-8">x</script> ,其维度为 ( 1 , | l 1 | ) <script type="math/tex" id="MathJax-Element-9">(1,|l_1|)</script> ,也就是一个行向量;对应的输出 y y <script type="math/tex" id="MathJax-Element-10">y</script> 也是一个行向量,维度为 ( 1 , | l n | ) <script type="math/tex" id="MathJax-Element-11">(1,|l_n|)</script> 。(注:也可以将 x x <script type="math/tex" id="MathJax-Element-12">x</script> 和 y <script type="math/tex" id="MathJax-Element-13">y</script> 表示为列向量,这里预先说明好为行向量,避免后面有歧义)

c) 设神经网络中第i层的输出为 zi z i <script type="math/tex" id="MathJax-Element-14">z^i</script>,( zi z i <script type="math/tex" id="MathJax-Element-15">z^i</script> 都是行向量)则 x=z1,y=zn x = z 1 , y = z n <script type="math/tex" id="MathJax-Element-16">x=z^1, y=z^n</script> ; 第 i i <script type="math/tex" id="MathJax-Element-17">i</script> 层的权重和偏置分别为 W i , b i <script type="math/tex" id="MathJax-Element-18">W^i, b^i</script> ;则对于全连接层 zi+1=ziWi+bi z i + 1 = z i W i + b i <script type="math/tex" id="MathJax-Element-19">z^{i+1} = z^iW^i + b^i</script> ; 其中 Wi W i <script type="math/tex" id="MathJax-Element-20">W^i</script> 和 bi b i <script type="math/tex" id="MathJax-Element-21">b^i</script>的维度分别为为 (|li|,|li+1|),(1,|li+1|) ( | l i | , | l i + 1 | ) , ( 1 , | l i + 1 | ) <script type="math/tex" id="MathJax-Element-22">(|l_i|,|l_{i+1}|),(1,|l_{i+1}|)</script>

d) 定义损失函数为 L(y,y) L ( y , y ∗ ) <script type="math/tex" id="MathJax-Element-23">L(y,y^*)</script> ;其中 y y ∗ <script type="math/tex" id="MathJax-Element-24">y*</script> 为样本的真实 y y <script type="math/tex" id="MathJax-Element-25">y</script>值

误差反向

a) 记损失函数L关于第 i <script type="math/tex" id="MathJax-Element-26">i</script> 层神经元的输出 zi z i <script type="math/tex" id="MathJax-Element-27">z^i</script> 的偏导为 δi=Lzi   (1) δ i = ∂ L ∂ z i       ( 1 ) <script type="math/tex" id="MathJax-Element-28">\delta^i = \frac {\partial L} {\partial z^i} \ \ \ (1)</script>

b) 首先我们来看看损失函数L在最后一层参数上的偏导;也就是 LWn1 ∂ L ∂ W n − 1 <script type="math/tex" id="MathJax-Element-29">\frac {\partial L} {\partial W^{n-1}}</script> 和 Lbn1 ∂ L ∂ b n − 1 <script type="math/tex" id="MathJax-Element-30">\frac {\partial L} {\partial b^{n-1}}</script>

LWn1i,j   =LznjznjWn1i,j   (2)    //Wn1i,jn1injznjWn1i,j=δnj(|ln1|k=1zn1kWn1k,j+bn1j)Wn1i,j   (3)   //znjzn1Wn1j,bn1j=δnjzn1i    (4) ∂ L ∂ W i , j n − 1       = ∂ L z j n ∗ ∂ z j n ∂ W i , j n − 1       ( 2 )         / / W i , j n − 1 是 第 n − 1 层 的 第 i 个 神 经 元 和 第 n 层 的 第 j 个 神 经 元 的 连 接 , 所 以 只 有 z j n 的 误 差 经 过 W i , j n − 1 反 向 传 播 = δ j n ∗ ∂ ( ∑ k = 1 | l n − 1 | z k n − 1 W k , j n − 1 + b j n − 1 ) ∂ W i , j n − 1       ( 3 )       / / z j n 是 z n − 1 这 个 行 向 量 与 权 重 矩 阵 W n − 1 的 第 j 列 向 量 的 乘 积 , 加 上 偏 置 b j n − 1 = δ j n ∗ z i n − 1         ( 4 )
<script type="math/tex; mode=display" id="MathJax-Element-31"> \begin{align*} & \frac {\partial L} {\partial W^{n-1}_{i,j}} \ \ \ \\ & = \frac {\partial L} {z^n_j} * \frac {\partial z^n_j} {\partial W^{n-1}_{i,j}} \ \ \ (2) \ \ \ \ //W^{n-1}_{i,j}是第n-1层的第i个神经元和第n层的第j个神经元的连接,所以只有z^n_j的误差经过W^{n-1}_{i,j}反向传播 \\ & = \delta_j^n * \frac {\partial (\sum_{k=1}^{|l_{n-1}|}z^{n-1}_k W^{n-1}_{k,j}+b^{n-1}_j)} {\partial W^{n-1}_{i,j}} \ \ \ (3) \ \ \ //z^n_j是z^{n-1}这个行向量与权重矩阵W^{n-1}的第j列向量的乘积,加上偏置b^{n-1}_j\\ & = \delta_j^n * z^{n-1}_i \ \ \ \ (4) \end{align*} </script>

对等式(4)一般化的向量表示为:

LWn1=(zn1)Tδnj(5) (5) ∂ L ∂ W n − 1 = ( z n − 1 ) T δ j n
<script type="math/tex; mode=display" id="MathJax-Element-32"> \frac {\partial L} {\partial W^{n-1}} = (z^{n-1})^T \delta^n_j \tag 5 </script>

同理可得:

Lbn1=δn(6) (6) ∂ L ∂ b n − 1 = δ n
<script type="math/tex; mode=display" id="MathJax-Element-33"> \frac {\partial L} {\partial b^{n-1}} =\delta^n \tag 6 </script>

c) 更一般的损失函数L关于第 l l <script type="math/tex" id="MathJax-Element-34">l</script>层(这里没有用索引 i <script type="math/tex" id="MathJax-Element-35">i</script>,避免跟等式1~4中的索引名相同,引起理解障碍)的参数上的偏导,也就是 LWl ∂ L ∂ W l <script type="math/tex" id="MathJax-Element-36">\frac {\partial L} {\partial W^l}</script> 和 Lbl ∂ L ∂ b l <script type="math/tex" id="MathJax-Element-37">\frac {\partial L} {\partial b^l}</script>

LWl=Lzl+1zl+1Wl    (7)=(zl)Tδl+1    (8) ∂ L ∂ W l = ∂ L ∂ z l + 1 ∗ ∂ z l + 1 ∂ W l         ( 7 ) = ( z l ) T δ l + 1         ( 8 )
<script type="math/tex; mode=display" id="MathJax-Element-38"> \frac {\partial L} {\partial W^l} = \frac {\partial L} {\partial z^{l+1}} * \frac {\partial z^{l+1}} {\partial W^l} \ \ \ \ (7) \\ = (z^l)^T\delta^{l+1} \ \ \ \ (8) </script>
同理可得:
Lbl=δl+1(9) (9) ∂ L ∂ b l = δ l + 1
<script type="math/tex; mode=display" id="MathJax-Element-39"> \frac {\partial L} {\partial b^l} =\delta^{l+1} \tag 9 </script>
d) 现在我们来看a)中定义的损失函数L关于第 l l <script type="math/tex" id="MathJax-Element-40">l</script>层输出的偏导 δ l = L z l <script type="math/tex" id="MathJax-Element-41">\delta^l = \frac {\partial L} {\partial z^l}</script>
δli=Lzli   =Lzl+1zl+1zli   (10)    //=Lzl+1(zlWl+bl)zli   (11)    //zl=j=1|ll+1|Lzl+1j(|ll|k=1zlkWlk,j+blj)zli   (12)    //l+1li=j=1|ll+1|δl+1jWli,j   (13)    //k=i=δl+1((Wl)T)i   (14)    //l+1Wli(1)(2)(3)(4)(5)(6) (1) δ i l = ∂ L ∂ z i l       (2) = ∂ L ∂ z l + 1 ∗ ∂ z l + 1 ∂ z i l       ( 10 )         / / 导 数 的 链 式 法 则 (3) = ∂ L ∂ z l + 1 ∗ ∂ ( z l W l + b l ) ∂ z i l       ( 11 )         / / z l 的 定 义 (4) = ∑ j = 1 | l l + 1 | ∂ L ∂ z j l + 1 ∗ ∂ ( ∑ k = 1 | l l | z k l W k , j l + b j l ) ∂ z i l       ( 12 )         / / 第 l + 1 层 的 每 个 节 点 都 有 梯 度 专 递 到 l 层 的 第 i 个 节 点 (5) = ∑ j = 1 | l l + 1 | δ j l + 1 ∗ W i , j l       ( 13 )         / / 只 有 k = i 时 有 梯 度 (6) = δ l + 1 ( ( W l ) T ) i       ( 14 )         / / 可 以 表 示 为 l + 1 层 梯 度 的 行 向 量 与 权 重 W l 的 转 置 的 第 i 个 列 向 量 的 乘 积
<script type="math/tex; mode=display" id="MathJax-Element-42"> \begin{align} &\delta^l_i=\frac {\partial L} {\partial z^l_i} \ \ \ \\ &=\frac {\partial L} {\partial z^{l+1}} * \frac {\partial z^{l+1}} {\partial z^l_i} \ \ \ (10) \ \ \ \ //导数的链式法则\\ &=\frac {\partial L} {\partial z^{l+1}} * \frac {\partial (z^lW^l + b^l)} {\partial z^l_i} \ \ \ (11) \ \ \ \ //z^l的定义\\ &=\sum_{j=1}^{|l_{l+1}|} \frac {\partial L} {\partial z^{l+1}_j} * \frac {\partial (\sum_{k=1}^{|l_l|}z^l_k W^l_{k,j}+b^l_j)} {\partial z^l_i} \ \ \ (12) \ \ \ \ //第l+1层的每个节点都有梯度专递到l层的第i个节点\\ &=\sum_{j=1}^{|l_{l+1}|} \delta^{l+1}_j * W^l_{i,j} \ \ \ (13) \ \ \ \ //只有k=i时有梯度\\ &=\delta^{l+1} ((W^l)^T)_i \ \ \ (14) \ \ \ \ // 可以表示为l+1层梯度的行向量与权重W^l 的转置的第i个列向量的乘积 \end{align} </script>
一般化的表示如下:
δl=Lzl=δl+1(Wl)T   (15) δ l = ∂ L ∂ z l = δ l + 1 ( W l ) T       ( 15 )
<script type="math/tex; mode=display" id="MathJax-Element-43"> \delta^l = \frac {\partial L} {\partial z^l} \\ =\delta^{l+1}(W^l)^T \ \ \ (15) </script>

结论

​ 以上的证明就是为了说明下面的结论,所以请牢记以下的结论,后续自己写神经网络的反向传播都会用到以下结论

a) 根据公式15,损失函数L关于第 l l <script type="math/tex" id="MathJax-Element-44">l</script>层神经元的偏导,就是第 l + 1 <script type="math/tex" id="MathJax-Element-45">l+1</script> 层的偏导乘上第l层权重矩阵的转置。我们将公式(15)递归一下, δl=δl+1(Wl)T=δl+2(Wl+1)T(Wl)T=δn(Wn1)T(Wn2)T...(Wl)T  (16) δ l = δ l + 1 ( W l ) T = δ l + 2 ( W l + 1 ) T ( W l ) T = δ n ( W n − 1 ) T ( W n − 2 ) T . . . ( W l ) T     ( 16 ) <script type="math/tex" id="MathJax-Element-46">\delta^l=\delta^{l+1}(W^l)^T=\delta^{l+2}(W^{l+1})^T(W^l)^T=\delta^n(W^{n-1})^T(W^{n-2})^T...(W^l)^T \ \ (16)</script>

​ 也就是说,只要求得损失函数L关于最后一层(n)的偏导 δn δ n <script type="math/tex" id="MathJax-Element-47">\delta^n</script>,其它任意层的偏导,直接用最后一层的偏导逐层乘上权重的转置即可。很简单,很对称,有木有?

b) 根据公式(8) ,损失函数L关于第 l l <script type="math/tex" id="MathJax-Element-48">l</script>层权重 W l <script type="math/tex" id="MathJax-Element-49">W^l</script> 的偏导为,第 l l <script type="math/tex" id="MathJax-Element-50">l</script> 层输出的转置乘第 l + 1 <script type="math/tex" id="MathJax-Element-51">l+1</script> 层的偏导 LWl=(zl)Tδl+1    (8) ∂ L ∂ W l = ( z l ) T δ l + 1         ( 8 ) <script type="math/tex" id="MathJax-Element-52">\frac {\partial L} {\partial W^l} = (z^l)^T\delta^{l+1} \ \ \ \ (8)</script>

c) 根据公式(9) ,损失函数L关于第 l l <script type="math/tex" id="MathJax-Element-53">l</script>层偏置 b l <script type="math/tex" id="MathJax-Element-54">b^l</script> 的偏导,就是第 l+1 l + 1 <script type="math/tex" id="MathJax-Element-55">l+1</script> 层的偏导: Lbl=δl+1   (9) ∂ L ∂ b l = δ l + 1       ( 9 ) <script type="math/tex" id="MathJax-Element-56">\frac {\partial L} {\partial b^l} =\delta^{l+1} \ \ \ (9)</script>

​ 由以上可知对任意的全连接层,我们只需要只到它后一层的偏导,就可以求得当前层参数(权重、偏置)的偏导; 就可以使用梯度下降算法更新参数了

w=wηLw  //η(17) (17) w = w − η ∗ ∂ L ∂ w     / / η 为 学 习 率
<script type="math/tex; mode=display" id="MathJax-Element-57"> w = w - \eta * \frac {\partial L} {\partial w} \ \ //\eta为学习率 \tag {17} </script>

b=bηLb(18) (18) b = b − η ∗ ∂ L ∂ b
<script type="math/tex; mode=display" id="MathJax-Element-58"> b = b - \eta * \frac {\partial L} {\partial b} \tag {18} </script>

​ 根据公式(16), 损失函数L关于任何一层的偏导,只需要求导损失函数关于最后一层的偏导 δn δ n <script type="math/tex" id="MathJax-Element-59">\delta^n</script> 即可。 δn δ n <script type="math/tex" id="MathJax-Element-60">\delta^n</script> 与损失函数的定义有关,下一节介绍常用损失函数的偏导计算。

常用损失函数

​ 以下说明损失函数的偏导的计算

均方差损失

​ 对于单个样本 (x,y) ( x , y ∗ ) <script type="math/tex" id="MathJax-Element-61">(x,y*)</script> ,定义如下:

L(y,y)=12(yy)2(19) (19) L ( y , y ∗ ) = 1 2 ( y − y ∗ ) 2 <script type="math/tex" id="MathJax-Element-62">L(y,y*) = \frac 1 2(y-y^*)^2 \tag {19}</script>

​ 其中 y y <script type="math/tex" id="MathJax-Element-63">y</script>是神经网络最后一层的输出 y = z n <script type="math/tex" id="MathJax-Element-64">y=z^n</script> ,就是预测值

Lyi=(12(yiyi)2)yi=(yiyi)(yiyi)yi=(yiyi)       (20)(7)(8)(9) (7) ∂ L ∂ y i = ∂ ( 1 2 ( y i − y i ∗ ) 2 ) ∂ y i (8) = ( y i − y i ∗ ) ∗ ∂ ( y i − y i ∗ ) ∂ y i (9) = ( y i − y i ∗ )               ( 20 )
<script type="math/tex; mode=display" id="MathJax-Element-65"> \begin{align} &\frac {\partial L} {\partial y_i} = \frac {\partial (\frac 1 2(y_i - y^*_i)^2)} {\partial y_i} \\ &=(y_i - y^*_i) * \frac {\partial (y_i - y^*_i)} {\partial y_i} \\ &=(y_i - y^*_i) \ \ \ \ \ \ \ (20) \end{align} </script>
​ 更一般的表示为 Ly=yy ∂ L ∂ y = y − y ∗ <script type="math/tex" id="MathJax-Element-66">\frac {\partial L} {\partial y} = y - y^*</script> ; 也就是 δn=Ly=yy=zny(21) (21) δ n = ∂ L ∂ y = y − y ∗ = z n − y ∗ <script type="math/tex" id="MathJax-Element-67">\delta^n=\frac {\partial L} {\partial y}=y-y^* = z^n-y^* \tag {21}</script>

​ 即使用均方误差情况下,损失函数L关于网络最后一层的导数就是预测值减实际值

交叉熵损失

​ 交叉熵用于度量两个概率分布的差异;一般使用交叉熵损失前,会对网络输出做softmax变换进行概率归一化;所以我们这里介绍的交叉熵损失是带softmax变换的交叉熵。

​ softmax变换定义如下:

ai=eyi/keyk(22) (22) a i = e y i / ∑ k e y k
<script type="math/tex; mode=display" id="MathJax-Element-68"> a_i=e^{y_i}/\sum_k e^{y_k} \tag {22} </script>
​ 交叉熵损失定义如下:
L(y,y)=iyilogai(23) (23) L ( y , y ∗ ) = − ∑ i y i ∗ log ⁡ a i
<script type="math/tex; mode=display" id="MathJax-Element-69"> L(y,y^*) = - \sum_i y^*_i \log a_i \tag {23} </script>
a) 我们先来求 ai a i <script type="math/tex" id="MathJax-Element-70">a_i</script> 关于 yj y j <script type="math/tex" id="MathJax-Element-71">y_j</script> 的偏导
aiyj=(eyi/keyk)yj=eyiyj1keyk+eyi1(keyk)2(keyk)yj=eyiyj1keykeyi(keyk)2eyj=eyjkeyk(eyj)2(keyk)2eyieyj(keyk)2i=ji j={ai(1ai)aiaji=ji j(24) ∂ a i ∂ y j = ∂ ( e y i / ∑ k e y k ) ∂ y j = ∂ e y i ∂ y j ∗ 1 ∑ k e y k + e y i ∗ − 1 ( ∑ k e y k ) 2 ∗ ∂ ( ∑ k e y k ) ∂ y j = ∂ e y i ∂ y j ∗ 1 ∑ k e y k − e y i ( ∑ k e y k ) 2 ∗ e y j = { e y j ∑ k e y k − ( e y j ) 2 ( ∑ k e y k ) 2 i = j − e y i e y j ( ∑ k e y k ) 2 i ≠   j (24) = { a i ( 1 − a i ) i = j − a i a j i ≠   j
<script type="math/tex; mode=display" id="MathJax-Element-72"> \begin{align*} &\frac {\partial a_i} {\partial y_j} = \frac {\partial(e^{y_i}/\sum_k e^{y_k})} {\partial y_j} \\ &= \frac {\partial e^{y_i}} {\partial y_j} * \frac {1} {\sum_k e^{y_k}} + e^{y_i} * \frac {-1} {(\sum_k e^{y_k})^2} * \frac {\partial (\sum_k e^{y_k})} {\partial y_j} \\ &= \frac {\partial e^{y_i}} {\partial y_j} * \frac {1} {\sum_k e^{y_k}} - \frac {e^{y_i}} {(\sum_k e^{y_k})^2} * e^{y_j} \\ &=\begin{cases} \frac {e^{y_j}} {\sum_k e^{y_k}} - \frac {(e^{y_j})^2} {(\sum_k e^{y_k})^2} & i=j \\ -\frac {e^{y_i}e^{y_j}} {(\sum_k e^{y_k})^2} & i\neq\ j \end{cases} \\ &=\begin{cases} a_i(1-a_i) & i=j \\ -a_ia_j & i\neq\ j \tag {24} \end{cases} \end{align*} </script>

b) 然后我们来求L关于 yj y j <script type="math/tex" id="MathJax-Element-73">y_j</script> 的偏导

Lyj=i(yilogai)aiaiyj=iyiaiaiyj=yjajaj(1aj)+i jyiaiaiaj=yj(1aj)+i jyiaj=yj+iyiaj=ajyj//i,j//1(10)(11)(12)(13)(14)(15) (10) ∂ L ∂ y j = − ∑ i ∂ ( y i ∗ log ⁡ a i ) ∂ a i ∗ ∂ a i ∂ y j (11) = − ∑ i y i ∗ a i ∗ ∂ a i ∂ y j (12) = − y j ∗ a j ∗ a j ( 1 − a j ) + ∑ i ≠   j y i ∗ a i ∗ a i a j / / 注 意 这 里 i 是 变 量 , j 是 固 定 的 (13) = − y j ∗ ( 1 − a j ) + ∑ i ≠   j y i ∗ a j (14) = − y j ∗ + ∑ i y i ∗ a j / / 所 有 真 实 标 签 的 概 率 之 和 为 1 (15) = a j − y j ∗
<script type="math/tex; mode=display" id="MathJax-Element-74"> \begin{align} &\frac {\partial L} {\partial y_j} = - \sum_i\frac {\partial( y^*_i \log a_i )} {\partial a_i} * \frac {\partial a_i} {\partial y_j} \\ &=- \sum_i \frac {y^*_i} {a_i} * \frac {\partial a_i} {\partial y_j} \\ &= - \frac {y^*_j} {a_j} * a_j(1-a_j) + \sum_{i \neq\ j} \frac {y^*_i} {a_i} * a_ia_j & //注意这里i是变量,j是固定的 \\ &=-y^*_j(1-a_j) + \sum_{i \neq\ j} y^*_ia_j \\ &= - y^*_j + \sum_iy^*_i a_j & //所有真实标签的概率之和为1\\ &=a_j - y^*_j \end{align} </script>
​ 更一般的表示为 :

Ly=ay(25) (25) ∂ L ∂ y = a − y ∗ <script type="math/tex" id="MathJax-Element-75">\frac {\partial L} {\partial y} = a - y^* \tag {25}</script>

​ 所以使用带softmax变换的交叉熵损失函数,损失函数L关于网络最后一层的导数就是预测值经softmax变换后的值减去真实值。(是不是跟均方差损失函数很相似,注意还是有差异的噢)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐