神经网络推理
$(x_1,x_2)$表示输入层的数据,$w_{11}、w_{21}$表示权重,$b_1$表示偏置。
第一个隐藏神经元的结果可以表示为:
$$h_1 = x_1w_{11} + x_2w_{21} + b_1$$
隐藏层的神经元是基于加权和计算出来的。
python实现mini版全连接
1 | import numpy as np |
W1:
[[ 1.2395529 1.36244947 -0.36417116 1.37209539]
[ 0.79381936 0.9539435 0.10809906 1.20005854]]
b1:
[ 0.90337564 -0.37367157 -0.65477654 -0.53367142]
x:
[[-1.87632050e-01 -1.56664439e-01]
[-1.77483807e+00 1.14098853e+00]
[ 1.74651236e+00 -9.99758195e-01]
[-3.18839224e-01 -9.41541718e-06]
[-9.05030740e-01 -4.27663837e-01]
[ 5.08077914e-01 9.23716486e-01]
[-1.13378280e+00 1.71743576e+00]
[-1.07155261e+00 -4.37605821e-01]
[-9.05201730e-02 -3.37833720e-01]
[ 9.45202380e-01 -5.44267793e-01]]
h:
[[ 0.54643252 -0.77875978 -0.60338164 -0.97912699]
[-0.39089126 -1.70336018 0.11490808 -1.59966553]
[ 2.2746427 1.05215044 -1.39887889 0.66294178]
[ 0.50815008 -0.80808289 -0.53866551 -0.97116055]
[-0.55794567 -2.01469736 -0.37142051 -2.28868157]
[ 2.26642912 1.19973225 -0.73995098 1.2719738 ]
[ 0.86132563 -0.28005668 -0.05623236 -0.02830612]
[-0.77225049 -2.25105909 -0.31185276 -2.52909643]
[ 0.52299215 -0.81927501 -0.65833121 -1.06329398]
[ 1.64295368 0.39491819 -1.05782682 0.11008319]]
上面的例子中有10笔样本数据:$x[0]、x[1]…$,对应10个隐藏层的神经元$h[0]、h[1]…$
使用sigmoid激活函数
全连接层的变换是线性变换,激活函数能够赋予它"非线性"的效果。使用激活函数能够增加神经网络的表现力。
1 | def sigmoid(x): |
1 | a = sigmoid(h) # 对隐层单元的内容使用激活函数 |
array([[0.63330751, 0.31458724, 0.35357041, 0.27306504],
[0.40350277, 0.15402692, 0.52869545, 0.16802837],
[0.90675507, 0.74118763, 0.19799407, 0.65992091],
[0.62437271, 0.30829917, 0.36849807, 0.27464924],
[0.36402292, 0.11766841, 0.40819782, 0.0920647 ],
[0.90605829, 0.76847715, 0.32301486, 0.78108044],
[0.70293754, 0.43043988, 0.48594561, 0.49292394],
[0.31599248, 0.09525815, 0.42266257, 0.07384342],
[0.62784716, 0.30591758, 0.34111458, 0.25668048],
[0.83793645, 0.59746609, 0.25772497, 0.52749304]])
用另一个全连接层来变换这个激活函数的输出$a$。
隐藏层有4个神经元,输出层有3个神经元,所以全连接层使用的权重矩阵的形状设置成$4*3$。
完整代码
整体的完整代码为:
1 | import numpy as np |
array([[ 2.1993881 , -0.66932037, 0.73650529],
[ 2.10594575, -0.44002399, 0.67983363],
[ 1.54631603, 0.46590836, 0.48583189],
[ 1.91380546, 0.09817995, 0.50751596],
[ 1.34782105, 0.98687129, 0.37225228],
[ 2.18438808, -0.63015213, 0.7306245 ],
[ 1.8960649 , 0.13953076, 0.49930039],
[ 1.36793674, 1.02409436, 0.37293796],
[ 1.90386465, 0.00908374, 0.5740634 ],
[ 1.16277154, 1.05478025, 0.30068793]])
python实现层的类及正向传播
- sigmoid函数的变换:Sigmoid层
- 全连接层的变换相当于几何学领域的放射变换:Affine层
代码规范:
- 所有的层都使用forward()方法和backward()方法
- 所有的层都使用params 和 grads实例变量
Sigmoid层
1 | import numpy as np |
Affine层
1 | class Affine: |
TwoLayerNet网络
1 | class TwoLayerNet: |
应用
1 | x = np.random.randn(10,2) |
array([[-0.89091485, 0.3790137 , -0.69215058],
[ 0.31672348, 0.33206884, -1.32879037],
[-1.04059946, 0.69280576, -0.71941753],
[ 2.07021878, -0.74625453, -2.63285994],
[ 0.8618358 , 0.24588236, -1.69390253],
[-0.36771902, 0.68768811, -0.99495204],
[-0.26414073, 0.39642317, -1.00626304],
[-0.08078445, 0.15941385, -1.14579999],
[-0.66130715, 0.48855694, -0.8163898 ],
[ 0.74677525, 0.32622056, -1.6045171 ]])
神经网络的学习
损失函数loss function
基于监督学习或者神经网络的预测结果,与实际结果之间差异程度。也就说将模型的恶劣程度作为标量值计算出来,得到的就是损失。
多类别分类问题中,通常使用的交叉熵误差cross entropy
作为损失函数。
Softmax函数的表达式:
$$y_k = \frac{e{S_{k}}}{\sum_{i=1}n* e^{S_i}}$$
Softmax函数输出的各个元素是0.0~1.0的实数。如果将这些元素全部加起来,则和为1.因此,Softmax的输出可以解释为概率。之后这个概率被输入交叉熵误差中。
交叉熵误差表示为:
$$L=-\sum_kt_klogy_{k}$$
- $t_k$对应于第k个类别的监督标签
- log是以e为底数的对数
在考虑了mini-batch处理的情况下,交叉熵误差可以表示为:
$$L=-\frac{1}{N}\sum_n\sum_kt_{nk}logy_{nk}$$
假设有N笔数据,$t_{nk}$表示第n笔数据的第k维元素的值;$y_{nk}$表示神经网络的输出,$t_{nk}$表示监督标签
MatMul层的实现
1 | class MatMul: |
关于Numpy的[…]复制问题
1 | a = np.array([1, 2, 3]) |
1 | id(a),id(b) |
(1549258733968, 1549258615664)
1 | a = b # 将b赋值给a; |
array([4, 5, 6])
可以看到a和b的内存地址是完全相同的;
1 | id(a),id(b) |
(1549258615664, 1549258615664)
1 | a = np.array([1, 2, 3]) |
1 | id(a),id(b) |
(1549258733392, 1549258809392)
1 | a[...] = b # 赋值 |
1 | id(a),id(b) |
(1549258733392, 1549258809392)
可以看到a和b的内存地址是不同的;且a的地址还是赋值前的地址。
总结规律:使用省略号时数据会被覆盖,变量指向的内存地址不变。
梯度的推导和反向传播实现
Sigmoid层
1 | class Sigmoid: |
Affine层
通过$y = np.dot(x,W) + b$实现了Affine层的正向传播
1 | class Affine: |
权重更新
1 | class SGD: |
使用SGD类更新神经网络的参数:
1 | # 伪代码 |
1 |