课程网址:
https://github.com/microsoft/ai-edu/tree/master/B-教学案例与实践/B6-神经网络基本原理简明
Step1.01.0
1、一层神经网络的所有神经元的激活函数必须一致
2、神经元的工作过程:
(1)输入input:
(
x
1
,
x
2
,
x
3
)
(x1,x2,x3)
(x1,x2,x3)是外界输入信号,一般是一个训练样本的多个属性
(2)权重weights:
(
w
1
,
w
2
,
w
3
)
(w1,w2,w3)
(w1,w2,w3)是每个输入信号的权重值
(3)偏移bias:在脑神经细胞中,一定是输入信号的电平大于某个临界值,神经元细胞才会处于兴奋状态,
b
b
b实际上就是临界值:
w
1
⋅
x
1
+
w
2
⋅
x
2
+
w
3
⋅
x
3
>
=
t
w1 \cdot x1 + w2 \cdot x2 + w3 \cdot x3 >= t
w1⋅x1+w2⋅x2+w3⋅x3>=t
令
b
=
−
t
b=-t
b=−t,则有
w
1
⋅
x
1
+
w
2
⋅
x
2
+
w
3
⋅
x
3
+
b
>
=
0
w1 \cdot x1 + w2 \cdot x2 + w3 \cdot x3 + b >= 0
w1⋅x1+w2⋅x2+w3⋅x3+b>=0
(4)求和计算:
Z
=
w
1
⋅
x
1
+
w
2
⋅
x
2
+
w
3
⋅
x
3
+
b
=
∑
i
=
1
m
(
w
i
⋅
x
i
)
+
b
Z = w1 \cdot x1 + w2 \cdot x2 + w3 \cdot x3 + b = \sum_{i=1}^m(w_i \cdot x_i) + b
Z=w1⋅x1+w2⋅x2+w3⋅x3+b=i=1∑m(wi⋅xi)+b
Z
=
W
⋅
X
+
b
Z = W \cdot X + b
Z=W⋅X+b
(5)激活函数:
A
=
a
(
Z
)
A=a{(Z)}
A=a(Z)
激活函数的作用:使用非线性激活函数,能够从输入输出之间生成非线性映射,是网络更强大。
3、训练流程:
Step1.02.3
1、在自然界中,梯度下降最好的例子,就是泉水下山的过程:
(1)水受重力影响,会在当前位置,沿着最陡峭的方向流动,有时会形成瀑布(梯度下降)
(2)水流下山的路径不是唯一的,在同一个地点,有可能有多个位置具有同样的陡峭程度,而造成了分流(可以得到多个解)
(3)遇到坑洼地区,有可能形成湖泊,而终止下山过程(不能得到全局最优解,而是局部最优解)
2、梯度下降的数学公式:
θ
n
+
1
=
θ
n
−
η
⋅
∇
J
(
θ
)
\theta_{n+1} = \theta_{n} - \eta \cdot \nabla J(\theta)
θn+1=θn−η⋅∇J(θ)
3、梯度下降的三要素:当前点、方向和步长
4、
Step1.03.1
MSE(Mean Square Error):计算预测值和真实值之间的欧氏距离。预测值和真实值越接近,两者的均方差就越小。
均方差函数常用于线性回归。
(单样本)
l
o
s
s
=
1
2
(
z
−
y
)
2
loss = {1 \over 2}(z-y)^2 \tag{单样本}
loss=21(z−y)2(单样本)
(多样本)
J
=
1
2
m
∑
i
=
1
m
(
z
i
−
y
i
)
2
J=\frac{1}{2m} \sum_{i=1}^m (z_i-y_i)^2 \tag{多样本}
J=2m1i=1∑m(zi−yi)2(多样本)
Step1.03.2
1、交叉熵:
交叉熵表示两个概率分布
p
,
q
p,q
p,q的差异,其中
p
p
p表示真实分布,
q
q
q表示非真实分布,那么交叉熵为:
H
(
p
,
q
)
=
∑
i
p
i
⋅
l
o
g
1
q
i
=
−
∑
i
p
i
log
q
i
H(p,q)=\sum_i p_i \cdot log {1 \over q_i} = - \sum_i p_i \log q_i
H(p,q)=i∑pi⋅logqi1=−i∑pilogqi
注意:交叉熵函数常用于逻辑回归(logistic regression),逻辑回归名字叫回归,其实是分类。
2、为什么不能使用均方差作为分类问题的损失函数?
(1)回归问题用均方差损失函数,可以保证损失函数是个凸函数,即可以得到最优解。而分类问题如果用均方差的话,损失函数的表现不是凸函数,就很难得到最优解。而交叉熵函数可以保证区间内单调
(2)分类问题的最后一层,需要分类函数,sigmoid或者softmax,如果再接均方差函数的话,其求导结果很复杂,运算量比较大。用交叉熵函数的话,可以得到比较简单的计算结果,一个简单的减法就可以得到反向误差
3、交叉熵损失函数值越小,反向传播的力度越小
4、区分线性回归和逻辑回归:
线性回归就是
y
=
a
x
+
b
y=ax+b
y=ax+b,是回归,常用均方差损失函数
逻辑回归是分类,常用交叉熵损失函数
Step2.04.1
1、当变量多于1个时,两个变量的量纲和数值有可能差别很大,这时,就需要对样本特征数据做归一化。
2、最小二乘法也叫最小平方法(Least Square),它通过最小化误差的平方和寻找数据的最佳函数匹配。
ϵ
=
∑
(
y
−
y
i
)
2
最
小
≈
真
值
y
\epsilon =\sum \left ( y-y_{i} \right )^{2}最小\approx 真值y
ϵ=∑(y−yi)2最小≈真值y
因为
y
y
y是猜测的,所以可以不断变换,总的误差
ϵ
\epsilon
ϵ也在不断变化。通过让总的误差的平方最小的
y
y
y就是真值,这是基于,如果误差是随机的,应该围绕真值上下波动。
参考:https://blog.csdn.net/ccnt_2012/article/details/81127117
Step7.15.0
1、随着网络的加深,训练变得越来越困难,时间越来越长,原因可能是:
参数多
数据量大
梯度消失
损失函数坡度平缓
2、网络优化的方法:
权重矩阵初始化
批量归一化
梯度下降优化算法
自适应学习率算法
Step7.15.5
1、批量归一化(Batch Normalization,BN)就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同的分布,致力于将每一层的输入数据正则化成 N ( 0 , 1 ) N(0,1) N(0,1)的分布。
2、机器学习领域的独立同分布假设:
假设训练数据和测试数据是满足相同分布的,这样就能做到通过训练数据获得的模型能够在测试集获得好的效果
3、训练过程中网络中间层数据分布的改变称之为内部协变量偏移(Internal Covariate Shift),BN的提出,就是要解决在训练过程中,中间层数据分布发生改变的情况。
4、批量归一化的优点:
(1)可以选择比较大的初始学习率,让你的训练速度提高
(2)减少对初始化的依赖
(3)减少对正则的依赖,因为BN具有提高网络繁华能力的特性
5、BN的步骤:
Step7.16.0
1、由于深度网络的学习能力强的特点,会造成网络对样本数据过分拟合,从而造成泛化能力不足,因此需要一些手段来改善网络的泛化能力。
2、回归任务中的三种情况,依次为:欠拟合、正确的拟合、过拟合
分类任务中的三种情况,依次为:分类欠妥、正确的分类、分类过度
3、欠拟合改进方法:使用复杂的模型,即加深网络的宽度和深度,提高神经网络的能力
4、出现过拟合的原因:
(1)训练集的数量和模型的复杂度不匹配,样本数量级小于模型的参数
(2)训练集和测试集的特征分布不一致
(3)样本噪音大,使得神经网络学习到了噪音,正常样本的行为被抑制
(4)迭代次数过多,过分拟合了训练数据,包括噪音部分和一些非重要特征
5、解决过拟合的方法
(1)数据扩展(使用更多的训练数据进行训练,比如镜像对称,随机裁剪,旋转图像,色彩转换等)
(2)提前终止(当随着模型的能力,训练集的误差会先减小再增大,这样可以提前终止算法减缓过拟合现象)
(3)Dropout(dropout可以随机的让一部分神经元失活)
(4)交叉验证
(5)正则化
(6)集成学习法
(7)简化模型,减小网络的宽度和深度
Step7.16.1
1、直观地理解偏差和方差
2、各个项的含义:
偏差:度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法本身的拟合能力;
方差:度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动所造成的的影响(模型的稳定性)
噪声:表达了在当前任务上任何学习算法所能达到的期望繁华误差的下界,即刻画了学习问题本身的难度
3、偏差-方差窘境 (bias-variance dilemma)
(1)学习器的拟合能力不够强,训练数据的扰动不足以便学习器产生显著变化,此时偏差主导了泛化错误率;
(2)随着训练程度的加深,学习器的拟合能力逐渐增强,训练数据发生的扰动渐渐能被学习器学到,方差逐渐主导了泛化错误率;
(3)在训练程度充足后,学习器的拟合能力已非常强,训练数据发生的轻微扰动都会导致学习器发生显著变化。
4、没有免费午餐定理(No Free Lunch Theorem,NFL)
对于基于迭代的最优化算法,不存在某种算法对所有问题(有限的搜索空间内)都有效。如果一个算法对某些问题有效,那么它一定在另外一些问题上比纯随机搜索算法更差。
Step7.16.2
1、朴素的思想
假设只有两个参数需要学习,那么这两个参数的损失函数就构成了上面的等高线图。等高线图中的最优解,其实就是过拟合的状态。
由此产生一个朴素的想法:如果我们以某个处于中间位置的等高线为目标的话(比如红色的那条),是不是就可以得到比较好的效果呢?
2、范数
L
p
=
∥
x
∥
p
=
(
∑
i
=
1
n
∣
x
i
∣
p
)
1
/
p
L_p = \lVert x \rVert_p = ({\sum^n_{i=1}\lvert x_i \rvert^p})^{1/p}
Lp=∥x∥p=(i=1∑n∣xi∣p)1/p
假设有一个向量
a
a
a:
a
=
[
1
,
−
2
,
0
,
−
4
]
a=[1,-2,0,-4]
a=[1,−2,0,−4]
(非0元素数)
L
0
=
3
L_0=3 \tag{非0元素数}
L0=3(非0元素数)
(绝对值求和)
L
1
=
∑
i
=
0
3
∣
x
i
∣
=
1
+
2
+
0
+
4
=
7
L_1 = \sum^3_{i=0}\lvert x_i \rvert = 1+2+0+4=7 \tag{绝对值求和}
L1=i=0∑3∣xi∣=1+2+0+4=7(绝对值求和)
(平方和求方根)
L
2
=
∑
i
=
0
3
∣
x
i
∣
2
2
=
21
2
=
4.5826
L_2 = \sqrt[2]{\sum^3_{i=0}\lvert x_i \rvert^2} =\sqrt[2]{21}=4.5826 \tag{平方和求方根}
L2=2i=0∑3∣xi∣2=221=4.5826(平方和求方根)
(最大值的绝对值)
L
∞
=
4
L_{\infty}=4 \tag{最大值的绝对值}
L∞=4(最大值的绝对值)
一个经典的关于
p
p
p范数的变化图如下:
L1范数是个菱形体,在平面上是一个菱形
L2范数是个球体,在平面上是一个圆
3、L2正则化
损失函数
J
(
w
)
J(w)
J(w)的最小值,可以简化为:
a
r
g
m
i
n
w
J
(
w
)
=
∑
i
n
(
y
i
−
w
T
x
i
)
2
+
λ
∑
j
m
w
j
2
argmin_wJ(w) = \sum_i^n(y_i-w^Tx_i)^2+\lambda\sum_j^m{w_j^2}
argminwJ(w)=i∑n(yi−wTxi)2+λj∑mwj2
相当于是线性回归的均方差损失函数,再加上一个正则项(也称为惩罚项),共同构成损失函数。如果想求这个函数的最小值,则需两者协调,并不是说分别求其最小值就能实现整体最小,因为它们有共同的
w
w
w项,当
w
w
w比较大时,第一项比较小,第二项比较大,或者正好相反。所以它们是矛盾组合体。
4、L1正则化
损失函数
J
(
w
)
J(w)
J(w)的最小值,可以简化为:
a
r
g
m
i
n
w
J
(
w
)
=
∑
i
n
(
y
i
−
w
T
x
i
)
2
+
λ
∑
j
m
∣
w
j
∣
argmin_wJ(w) = \sum_i^n(y_i-w^Tx_i)^2+\lambda\sum_j^m{\lvert w_j \rvert}
argminwJ(w)=i∑n(yi−wTxi)2+λj∑m∣wj∣
5、**L1正则化的结果,就是让权重参数矩阵稀疏化,以形成特征选择。**用通俗的话将就是让权重值矩阵中很多项为0或者接近0,把有用的特征提出来,无用特征的影响非常小甚至为0。
**L2正则化的结果,使得权重矩阵中的值普遍减小,拉向坐标原点。**权重值变小,就会对特征不敏感,大部分特征都能起作用。
6、参数稀疏的好处:
(1)特征选择:稀疏规则化能实现特征的自动选择,它会学习地去掉没有信息的特征,也就是把这些特征对应的权重置为0。
(2)可解释性:模型更容易解释
Step7.16.4
1、早停法(early stopping)是一种迭代次数阶段的方法来防止过拟合的方法,即在模型对训练数据集迭代收敛之前停止迭代来防止过拟合
Step7.16.5
1、丢弃法(dropout)
在一次正向/反向的过程中,通过随机丢弃一些神经元(让其隐层节点值为0),迫使高层神经元和其他的一些底层神经元协同工作,可以有效地防止神经元因为接收到过多的同类型参数而陷入过拟合的状态,来提高泛化程度。
正常的隐层计算公式是:
Z
=
W
⋅
X
+
B
Z = W \cdot X + B
Z=W⋅X+B
加入了随机丢弃步骤后,变成了:
r
∼
B
e
r
n
o
u
l
l
i
(
p
)
r \sim Bernoulli(p)
r∼Bernoulli(p)
Y
=
r
⋅
X
Y = r \cdot X
Y=r⋅X
Z
=
Y
⋅
W
+
B
Z = Y \cdot W + B
Z=Y⋅W+B
Step8.17.1
1、卷积神经网络—计算机视觉,让计算机有能力理解图片和视频信息
2、卷积神经网络的特征用途—用于特征压缩
3、卷积:利用某些设计好的参数组合(卷积核)去提取图像空域上相邻的信息
(1)我们实现的卷积操作不是原始数学含义的卷积,而是工程上的卷积
(2)在实现卷积操作时,并不会反转卷积核
4、
(1)单入单出的二维卷积
(2)单入多出的升维卷积
一张
4
∗
4
4*4
4∗4的图片,用两个卷积核并行地处理,输出为2个
2
∗
2
2*2
2∗2的图片。在训练过程中,这两个卷积核会完成不同的特征学习。
(3)多入单出的降维卷积
这里虽然没有引用https://www.jianshu.com/p/fc9175065d87这篇博文,但是这篇博文讲述的卷积还是不错的,值得一看
5、填充padding
如果原始图为
4
∗
4
4*4
4∗4,用
3
∗
3
3*3
3∗3的卷积核进行卷积后,目标图片变成了
2
∗
2
2*2
2∗2.如果我们想保持目标图片和原始图片为同样大小,则可以向原始图片周围填充一圈0,然后再做卷积。
6、卷积前后图片大小的计算
卷积后输出图片的大小公式:
H
O
u
t
p
u
t
=
H
I
n
p
u
t
−
H
K
e
r
n
a
l
+
2
P
a
d
d
i
n
g
S
t
r
i
d
e
+
1
H_{Output}= {H_{Input} - H_{Kernal} + 2Padding \over Stride} + 1
HOutput=StrideHInput−HKernal+2Padding+1
W
O
u
t
p
u
t
=
W
I
n
p
u
t
−
W
K
e
r
n
a
l
+
2
P
a
d
d
i
n
g
S
t
r
i
d
e
+
1
W_{Output}= {W_{Input} - W_{Kernal} + 2Padding \over Stride} + 1
WOutput=StrideWInput−WKernal+2Padding+1
注意:一般情况下,我们用正方形的卷积核,且为奇数;如果计算出的输出图片尺寸为小数,则取整,不做四舍五入
Step8.17.5
1、池化的概念
池化pooling,又称为下采样,downstream sampling or sub-sampling
池化方法有两种,一种是最大值池化Max Pooling,一种是平均值池化Mean/Average Pooling.
最大值池化,是取当前池化视野中所有元素的最大值,输出到下一层特征图中
平均值池化,是取当前池化视野中所有元素的平均值,输出到下一层特征图中
一般都使用最大值池化
2、池化的目的:
(1)扩大视野:就如同先从近处看一张图片,然后离远一些再看同一张图片,有些细节就会被忽略
(2)降维:在保留图片局部特征的前提下,使得图片更小,更易于计算
(3)平移不变性,轻微扰动不会影响输出:比如上如中最大值池化的4,即使向右偏一个像素,其输出值仍为4
(4)维持同尺寸图片,便于后端处理:假设输入的图片不是一样大小的,就需要用池化来转换成同尺寸图片
3、池化前后图片的计算
假设输入图片的形状是
W
1
×
H
1
×
D
1
W1×H1×D1
W1×H1×D1,其中
W
W
W是图片宽度,
H
H
H是图片高度,
D
D
D是图片深度(多个图层),
F
F
F是池化的视野(正方形),
S
S
S是池化的步长,则输出图片的形状是:
W
2
=
(
W
1
−
F
)
/
S
+
1
W2=(W1−F)/S+1
W2=(W1−F)/S+1
H
2
=
(
H
1
−
F
)
/
S
+
1
H2=(H1−F)/S+1
H2=(H1−F)/S+1
D
2
=
D
1
D2=D1
D2=D1
池化层不会改变图片的深度,即 D D D值前后相同。
4、池化层的训练:
对于最大值池化,残差值会回传到当初最大值的位置上(请对照图14.3.1),而其它三个位置的残差都是0。
对于平均值池化,残差值会平均到原始的4个位置上。
公式推导过程
推导证明:在卷积网络的训练中,池化层需要做的只是把误差项向后传,不需要计算任何梯度
5、优化
原始数据先做img2col变换,然后做一次np.max(axis=1)的max计算,会大大增加速度,然后把结果reshape成正确的矩阵即可。做一次大矩阵的max计算,比做4次小矩阵计算要快很多。
6、img2col变换
把卷积核对应的值展开,展开为行还是列没有本质区别,目的都是为了在计算时读取连续的内存。
输入为
4
∗
4
4*4
4∗4,卷积核为
3
∗
3
3*3
3∗3,则新矩阵为
9
∗
4
9*4
9∗4。
Step8.17.6
1、搭建卷积网络来解决MNIST问题:
python代码:
def model():
num_output = 10
dataReader = LoadData(num_output)
max_epoch = 5
batch_size = 128
learning_rate = 0.1
params = HyperParameters_4_2(
learning_rate, max_epoch, batch_size,
net_type=NetType.MultipleClassifier,
init_method=InitialMethod.Xavier,
optimizer_name=OptimizerName.Momentum)
net = NeuralNet_4_2(params, "mnist_conv_test")
c1 = ConvLayer((1,28,28), (8,5,5), (1,0), params)
net.add_layer(c1, "c1")
r1 = ActivationLayer(Relu())
net.add_layer(r1, "relu1")
p1 = PoolingLayer(c1.output_shape, (2,2), 2, PoolingTypes.MAX)
net.add_layer(p1, "p1")
c2 = ConvLayer(p1.output_shape, (16,5,5), (1,0), params)
net.add_layer(c2, "23")
r2 = ActivationLayer(Relu())
net.add_layer(r2, "relu2")
p2 = PoolingLayer(c2.output_shape, (2,2), 2, PoolingTypes.MAX)
net.add_layer(p2, "p2")
f3 = FcLayer_2_0(p2.output_size, 32, params)
net.add_layer(f3, "f3")
bn3 = BnLayer(f3.output_size)
net.add_layer(bn3, "bn3")
r3 = ActivationLayer(Relu())
net.add_layer(r3, "relu3")
f4 = FcLayer_2_0(f3.output_size, 10, params)
net.add_layer(f4, "f2")
s4 = ClassificationLayer(Softmax())
net.add_layer(s4, "s4")
net.train(dataReader, checkpoint=0.05, need_test=True)
net.ShowLossHistory(XCoordinate.Iteration)
2、卷积核的大小如何选取呢?
大部分卷积神经网络都会用1、3、5、7的方式递增。
3、在做池化时,应该尽量让输入的矩阵尺寸是偶数,如果不是的话,应该在上一层卷积层加 p a d d i n g padding padding,使得卷积的输出结果矩阵的宽和高都是偶数
4、1*1的卷积核有什么作用?
(1)实现跨通道的交互和信息整合
(2)进行卷积核通道数的降维和升维
(3)可以在保持feature map尺寸不变(即不损失分辨率)的前提下大幅度增加非线性特性,把网络做的很deep
Step8.17.7
1、想要达到更高的准确率:
(1)增强卷积层的能力,比如增加输出通道数,增加卷积层数
(2)做数据集扩展,比如在原有数据集上,使用镜像、平移、旋转、变色等方法
2、Cifar-10数据集说明:
一共包含10 个类别的RGB 彩色图片:
飞机( airplane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。
每个图片的尺寸为32 × 32 ,每个类别有6000个图像,数据集中一共有50000 张训练图片和10000 张测试图片。
3、与MNIST数据集相比,Cifar-10数据集的不同:
(1)CIFAR-10 是3 通道的彩色RGB 图像,而MNIST 是灰度图像。
(2)CIFAR-10 的图片尺寸为32 × 32 , 而MNIST 的图片尺寸为28 × 28 ,比MNIST 稍大。
(3)相比于手写字符, CIFAR-10 含有的是现实世界中真实的物体,不仅噪声很大,而且物体的比例、特征都不尽相同,这为识别带来很大困难。直接的线性模型如Softmax 在CIFAR-10 上表现得很差。
参考链接:https://blog.csdn.net/qq_41185868/article/details/82793025
4、用卷积层解决Cifar-10的问题:
Python代码:
def model():
num_output = 10
dataReader = LoadData()
max_epoch = 5
batch_size = 64
learning_rate = 0.1
params = HyperParameters_4_2(
learning_rate, max_epoch, batch_size,
net_type=NetType.MultipleClassifier,
init_method=InitialMethod.MSRA,
optimizer_name=OptimizerName.Momentum)
net = NeuralNet_4_2(params, "cifar_conv")
c1 = ConvLayer((3,32,32), (6,5,5), (1,0), params)
net.add_layer(c1, "c1")
r1 = ActivationLayer(Relu())
net.add_layer(r1, "relu1")
p1 = PoolingLayer(c1.output_shape, (2,2), 2, PoolingTypes.MAX)
net.add_layer(p1, "p1")
c2 = ConvLayer(p1.output_shape, (16,5,5), (1,0), params)
net.add_layer(c2, "c2")
r2 = ActivationLayer(Relu())
net.add_layer(r2, "relu2")
p2 = PoolingLayer(c2.output_shape, (2,2), 2, PoolingTypes.MAX)
net.add_layer(p2, "p2")
f3 = FcLayer_2_0(p2.output_size, 120, params)
net.add_layer(f3, "f3")
bn3 = BnLayer(120)
net.add_layer(bn3, "bn3")
r3 = ActivationLayer(Relu())
net.add_layer(r3, "relu3")
f4 = FcLayer_2_0(f3.output_size, 84, params)
net.add_layer(f4, "f4")
bn4 = BnLayer(84)
net.add_layer(bn4, "bn4")
r4 = ActivationLayer(Relu())
net.add_layer(r4, "relu4")
f5 = FcLayer_2_0(f4.output_size, num_output, params)
net.add_layer(f5, "f5")
s5 = ClassificationLayer(Softmax())
net.add_layer(s5, "s5")
net.train(dataReader, checkpoint=0.05, need_test=True)
net.ShowLossHistory(XCoordinate.Iteration)