深度学习-第1周
思维导图
神经网络
与传统机器学习算法(线性回归,逻辑回归等)在不同数据量下解决问题的表现区别:

特点:弱化了特征工程,传统的机器学习算法(线性回归,逻辑回归,TODO:决策树…)严重依赖人工对特征的选取和构造(比如前面的特征工程),而神经网络通过多层的非线性变化和反向传播更新参数,能够自动学习到对任务有用的高阶抽象特征
神经元
通过一个预测商品是否畅销的案例来引入

神经元将价格x作为输入,通过激活函数a将输入x转换为输出,即是否畅销的可能性
也可以将神经元理解成小型计算机,唯一的工作就是输入一个或几个数字,经过激活函数a后输出一个或几个数字
更复杂的案例
此时商品拥有4个特征:价格,运费,营销,材料
但是判断是否畅销取决于3个因素:实惠性,人们的意识,人们对质量的预测

实惠性,人们的意识,人们对质量的预测,可能性都是神经元的激活值
工作流程

对于隐藏层来说,为什么叫隐藏层呢?
神经网络的结构和工作流程对我们来说,输入的特征向量X是透明的,最终模型输出的y是透明的,虽然可以提取激活向量,但是我们并不能知道激活向量是经过什么变换得到的,也就是说隐藏层的工作模式其实对我们不透明
这也是为什么神经网络又称为黑箱模型(只知道输入和输出,但是难以解释从输入如何得到输出)
层
输入层(也称为第0层)到隐藏层

$$
\vec{a^{[i]}}:第i层输出的向量
$$
隐藏层到输出层

更复杂的结构

第L层的第J个元素/单元
$$
a^{[L]}_{J} = h(w^{[L]}\vec{a^{[L-1]}}+b_J) \
也适用于输入层的X,X可以表示为\vec{a^{[0]}}
$$
前向传播
前向传播是指从输入层开始,依次经过隐藏层,最终到达输出层,逐层计算神经元输出值的过程,前向传播其实就是模型进行推理的过程
在这个过程中
- 神经元会对输入进行带权求和加上偏置(wa+b)
- 使用激活函数进行非线性变换
神经网络训练的步骤就分为:前向传播->计算误差->计算梯度->反向传播进行梯度更新
eg:手写识别0/1,使用8*8的像素网格,255表示亮白

整体结构

过程:
输入层到第一层隐藏层
$$
\vec{a^{[1]}} =
\begin{pmatrix}
h(w^{[1]}1\vec{a^{[0]}}+b_1^{[1]}) \
h(w^{[1]}2\vec{a^{[0]}}+b_2^{[1]}) \
\vdots \
h(w^{[1]}{25}\vec{a^{[0]}}+b{25}^{[1]})
\end{pmatrix}
$$
第一层隐藏层到第二层隐藏层
$$
\vec{a^{[2]}} =
\begin{pmatrix}
h(w^{[2]}1\vec{a^{[1]}}+b_1^{[2]}) \
h(w^{[2]}2\vec{a^{[1]}}+b_2^{[2]}) \
\vdots \
h(w^{[2]}{15}\vec{a^{[1]}}+b{15}^{[2]})
\end{pmatrix}
$$
到输出层
$$
\vec{a^{[3]}} =
\begin{pmatrix}
h(w^{[3]}_1\vec{a^{[2]}}+b_1^{[3]})
\end{pmatrix} \
a^{[3]}变成标量了
$$
案例神经网络结构特点:越靠近输出层,隐藏层中神经元数量越少
代码实现:
数据集导入,使用sklearn.datasets中的load_digits
1 | import numpy as np |
获取特征与标签
1 | def getXY(nums=10): |
顺序模型
1 | model = Sequential([ |
进行模型编译、拟合和获取预测值
1 | # 损失函数模型为交叉熵损失BinaryCrossentropy |
整体代码
1 | import numpy as np |
模型运行结果
1 | 预测值 [[0] |
课上的案例
烘焙咖啡

对输入矩阵进行硬编码
1 | import numpy as np |
更通用的做法:
层中计算激活向量
1 | import numpy as np |
实现顺序模型
1 | def Sequential(X, g): |
使用tensorflow进行代码实现,这里要注意tf和numpy对矩阵的不同表示
1 | import numpy as np |
可以使用顺序模型进行优化,这样我们就不用手动将向量传递给下一层
1 | import numpy as np |
向量化
对于上一个例子的dense,X是1×2矩阵,W是3×2矩阵,B是1×3矩阵
那么可以用
$$
XW^T+B
$$
代替循环,实现向量化
1 | # 要将原本的向量变成列/行矩阵 |

矩阵乘法
线性代数知识
$$
对于向量\vec{a} = \begin{pmatrix}
a_1,\
a_2,\
\vdots,\
a_n\
\end{pmatrix}{n\times1}\vec{b}=\begin{pmatrix}b_1,\b_2,\\dots,\b_n\\end{pmatrix}{n\times1}\
\vec{a}\cdot \vec{b}=a^T\cdot b=a_1b_1+a_2b_2+\dots+a_nb_n \
向量\vec{a} = \begin{pmatrix}
a_1,\
a_2,\
\vdots,\
a_n\
\end{pmatrix},矩阵W_{n\times m} = \begin{pmatrix}\vec{w_1},\vec{w_2},\dots,\vec{w_m}\end{pmatrix}\
Z= a^TW = \begin{pmatrix}a^T\vec{w_1},a^T\vec{w_2},\dots,a^T\vec{w_m}\end{pmatrix}\
矩阵A_{n\times m},B_{m \times a} \
A\cdot B = C_{n \times a}
$$
全连接神经网络
全连接:每一层的神经元都与上一层的所有神经元相连接
整体结构
分为输入层,隐藏层,输出层
隐藏层层数视任务而定:可以是1层,也可以是很多层
输出层可以有一个输出也可以是 多个输出

单元结构

数学表达式:
$$
a = h(w^TX+b)=h(w_1x_1+\dots+w_nx_n+b) \
h()就是激活函数,是一个非线性函数 \
w^TX+b就是前面的线性回归
$$
思考:全连接神经网络的单元结构就是
如果不经过激活函数,那就是线性回归
$$
a = w^TX+b
$$
如果激活函数是sigmoid函数
$$
a = \sigma(w^TX+b)
$$
那就变成逻辑回归了
激活函数
作用
引入了激活函数,网络才具有学习更加复杂关系的能力,由前面的思考得到,如果去掉了激活函数,那么单元结构就会变成一个线性回归模型,这样网络学习能力就会受限
为什么激活函数不是线性函数?
以一个小例子:数学表达式为a=h(x)=cx,单元结构如下
1 | X->a11->a21->a31->y |
有两个隐藏层
$$
a_{11}=cx \
a_{21} = ca_{11}=c^2x \
a_{31}=ca_{21}=c^3x
$$
我们发现,最终结果还是一个线性的,我们可以直接把单元结构改成
1 | X->a31->y |
数学表达式为
$$
a=h(x)=kx=c^3x
$$
那么两个隐藏层就被抵消掉了;隐藏层的作用是为了执行更复杂的计算任务,但使用线性函数作为激活函数,不仅浪费了计算资源还没起什么作用,等价于没有隐藏层的单元结构
分类
Linear
线性函数,相当于没有使用激活函数
$$
y = g(z) = z=\vec{w}\vec{x}+b
$$
适用于:回归模型,比如预测房价
python实现单隐藏层的前向传播
使用numpy手动实现
1 | def g(w, x, b): |
使用tensorflow
用到的库
1 | import numpy as np |
1 | # 特征 shape=(1,2) |

sigmoid函数
$$
y = \frac{1}{1+e^{-z}} \
y’ = y(1-y)
$$
绘制图像:
1 | import numpy as np |

分析:
sigmoid导数图像两侧都趋近于0,就导致偏离对称轴一定距离后,梯度值会变得很小,这就会导致梯度下降时参数更新的慢
优点:
1.简单,非常适用于二分类分类任务
缺点:
1.反向传播训练时有梯度消失的问题
(什么是反向传播?使用链式法则将损失函数对参数的梯度逐层回传,用于更新参数和偏置)
比如这样
$$
w = w - \alpha\frac{\partial J}{\partial w} \
sigmoid导数的最大值是0.25,我们假设梯度就是0.25 \
对于很多隐藏层的网络结构,反向传播时就会这样 \
w = w-\alpha(0.250.25\dots*0.25) \
梯度就变得很小很小,也就是梯度消失问题
$$
2.输出值区间为(0,1),关于原点不对称,会使参数更新的比较慢
(所以我们希望的激活函数是关于原点对称的,即奇函数f(-x) = -f(x))
3.梯度更新在不同方向走的太远(导数图像对称轴两侧),使优化难度增大,训练耗时
Tanh函数
双曲正切激活函数
$$
y = \frac{e^z-e^{-z}}{e^z+e^{-z}} \
y’ = 1-y^2
$$
1 | import numpy as np |
图像:

分析:原函数关于原点对称了,原函数取值(-1,1);导数图像与sigmoid的导数类似,x趋近±∞时导数趋于0
优点:
1.解决了sigmoid不关于原点对称的问题,参数更新得更快
2.导数(梯度)最大值为1,因此训练的速度高于sigmoid
缺点:
1.仍存在梯度消失的问题
(尽管梯度最大值发生变化,但图像的形状仍于sigmoid类似)
2.还是和sigmoid很类似
ReLU函数
$$
y=\begin{cases}
z,& if& z >0 \
0,&if&z\leq0
\end{cases}\
y’=\begin{cases}
1,&if&z>0\
0,&if&z\leq0
\end{cases}
$$
图像

优点:
1.解决了梯度消失的问题
2.没有指数运算,计算更为简单
缺点:
1.训练时可能出现神经元死亡的情况
(当z<0时,y=0,J(w)=1/mX^T(h(z)-y),那么J对w的梯度就是0了,此时参数更新就失效了)
2.y不关于零点对称,参数更新的比较慢
适用于:输出值y只能取非负值
Leaky ReLU函数
$$
y=\begin{cases}
z,& if& z >0 \
az,&if&z\leq0
\end{cases}\
y’=\begin{cases}
1,&if&z>0\
a,&if&z\leq0 \
\end{cases}
其中 0<α≪1(通常设为 0.01)\
$$
图像

绘图代码注意事项,使用子图对象设置y轴刻度
1 | # 获得子图对象 |
优点:
1.解决了ReLU神经元死亡问题
(输出值以及导数都不会变为0)
缺点:
1.无法为正负输入值提供一致的关系预测(不同区间函数不同)
- 对于正输入,神经元“活跃”,直接将输入传递下去;
- 对于负输入,神经元“较弱地活跃”,只传递一小部分信号(乘以 α);
SoftMax激活函数
用于多分类问题的输出层的激活函数


$$
给定一个输入向量z=[z_1,z_2,\dots,z_n] ,y=1,2,\dots,n\
z_i = \vec{w_i}\vec{x}+b_i\
a_i=SoftMax(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{n}e^{z_j}}
$$
如何选择
输出层

- 对于先前的预测房价之类的回归模型,激活函数用线性函数比较合适
- 对于二分类任务,比如肿瘤诊断,Minist手写数字0/1,适合使用sigmoid
- 对于输出值y非负的情况,使用ReLU
隐藏层
常用ReLU,当输出层是二分类任务时常用sigmoid
原因
- 前面介绍激活函数时提到的,计算开销
- ReLU梯度下降只会在一个方向收敛(y<0),sigmoid会在两个方向收敛(x -> -∞,x -> +∞);sigmoid这个特性会使损失函数J有许多梯度接近0的位置,不利于模型的梯度下降和参数更新

前向传播
对于线性回归和逻辑回归来说,前向传播就是计算得到回归结果的过程
对于神经网络

前向传播是指从输入层开始,依次经过隐藏层,最终到达输出层,逐层计算神经元输出值的过程,前向传播其实就是模型进行推理的过程
在这个过程中
- 神经元会对输入进行带权求和加上偏置(wa+b)
- 使用激活函数进行非线性变换
神经网络训练的步骤就分为:前向传播->计算误差->计算梯度->反向传播进行梯度更新
计算过程
eg:

1 | import numpy as np |
损失函数
回顾:
线性回归模型中:均方误差损失函数:
$$
J(w) = \frac{1}{2m}\sum_{i=1}^{m}(wx_i-y_i)^2
$$
逻辑回归中:交叉熵损失函数:
$$
J(w)= -\frac{1}{m}\sum_{i=1}^m(y_ilog(\sigma(w^Tx_i+b))+(1-y_i)log(1-\sigma(w^Tx_i+b)))
$$
链式法则
因为神经网络模型中可能存在着多层的隐藏层,当我们需要反向传播求梯度时,就涉及到对复合函数的求导,此时需要使用链式法则
单变量
$$
y=f(u) \
u = g(v) \
v = h(x) \
\frac{dy}{dx}=\frac{dy}{du}\frac{du}{dv}\frac{dv}{dx}
$$
多变量
$$
z = f(u,v)\
u = g(y)\
v = h(y) \
y = j(x)
$$
$$
\frac{\partial z}{\partial x} = \frac{\partial z}{\partial u}\frac{\partial u}{\partial y}\frac{\partial y}{\partial x}+\frac{\partial z}{\partial v}\frac{\partial v}{\partial y}\frac{\partial y}{\partial x}
$$

反向传播
还是前面的例子,隐藏层激活函数为sigmoid,输出层激活函数为ReLU

求J对w_21的梯度
$$
\frac{\partial J(w_{21},b)}{\partial w_{21}} = \frac{\partial J}{\partial y}\frac{\partial y}{\partial a_{21}}\frac{\partial a_{21}}{\partial w_{21}} = \frac{\partial \frac{1}{2}(y-label)^2}{\partial y}\frac{\partial a_{21}}{\partial a_{21}}\frac{\partial ReLU(w_{21}a_{11}+b_2)}{\partial w_{21}}=(y-label)\frac{\partial ReLU(w_{21}a_{11}+b_2)}{\partial w_{21}a_{11}+b_2}\frac{\partial w_{21}a_{11}+b_2}{\partial w_{21}} = (y-label)\times a_{11}\times \frac{\partial ReLU(w_{21}a_{11}+b_2)}{\partial w_{21}a_{11}+b_2}
$$
求J对b_2的梯度
$$
\frac{\partial J(w_{21},b_2)}{\partial b_2} = \frac{\partial J(w_{21},b_2)}{\partial y}\frac{\partial y}{\partial a_{21}}\frac{\partial a_{21}}{\partial b_2} = \frac{\partial \frac{1}{2}(y-label)^2}{\partial y}\frac{\partial y}{\partial a_{21}}\frac{\partial ReLU(w_{21}a_{11}+b_2)}{\partial w_{21}a_{11}+b_2}\frac{\partial w_{21}a_{11}+b_2}{\partial b_2}
$$
求J对w_11的梯度
$$
\frac{\partial J(w_{11},b)}{\partial w_{11}} = \frac{\partial J}{\partial y}\frac{\partial y}{\partial a_{21}}\frac{\partial a_{21}}{\partial a_{11}}\frac{\partial a_{11}}{\partial w_{11}} = (y-label)\times1\times\frac{\partial ReLU(w_{21}a_{11}+b_2)}{\partial w_{21}a_{11}+b_2}\times\frac{\partial w_{21}a_{11}+b_2}{\partial a_{11}}\times\frac{\partial \sigma(w_{11}x_1+b_1)}{\partial w_{11}x_1+b_1}\times\frac{\partial w_{11}x_1+b_1}{\partial w_{11}}
$$
经典的梯度更新
$$
w_j = w_j = \alpha\frac{\partial J}{\partial w} \
b_j = b_j = \alpha\frac{\partial J}{\partial b}
$$
代码实现
1 | import numpy as np |
学习曲线

输出结果
1 | 预测结果 |
多类
目标标签超过两个的分类任务,输出标签可以是两个中的一个,也可以是多个类别中的任意一个类别
与二分类的图像对比

SoftMax的损失函数
逻辑回归中:交叉熵损失函数:
$$
J(w)= -\frac{1}{m}\sum_{i=1}^m(y_ilog(\sigma(w^Tx_i+b))+(1-y_i)log(1-\sigma(w^Tx_i+b)))
$$
将逻辑回归中推出的交叉熵损失函数进行推广以应用到SoftMax上
$$
P(y|X) = \sigma(w^Tx_i+b)^y[1-\sigma(w^Tx_i+b)]^{(1-y)}\
a_1=P(1|X)=\sigma(w^Tx_i+b) \
a_2 = 1-a_1= P(0|X) = 1-\sigma(w^Tx_i+b) \
loss = -ylog(a_1)-(1-y)log(1-a_1) \
1是真实类别时:loss =-log(a1)\
0是真实类别时:loss =-log(a2)\
我们使用一种叫做one-hot编码的方式,将y,(1-y)统一成0或1,只有真实的标签才是1,此时我们可以将公式变为: \
loss = -\sum_{i=1}^{m}y_ilog(a_i) \
J(w,b) = -\frac{1}{m}\sum_{i=1}^{m}y_ilog(a_i)
$$
one-hot编码
one-hot编码是用于将**类别型数据(类别标签等)**转换为二进制向量的编码方法;对于有N个类别的分类任务,每个类别会被表示为一个长度为N的二进制向量,只有一个位置为1,其他位置都为0
$$
在分类任务中,真实类别是第i类,则 \
y_j =
\begin{cases}
1&if&j=i,\
0&其他
\end{cases}
$$
比如2分类中,我们假设y=1是正例,y=2是反例
$$
二进制向量=(1,0)\
loss = -\sum_{i=1}^{N}y_ilog(a_i) = -log(a_1)+0\times(-log(a_2))
$$
SoftMax的损失函数
1.使用one-hot编码+交叉熵简化形式
$$
loss = -\sum_{i=1}^{m}y_ilog(a_i)\
loss = -log(a_j),当y=j\
其中y_i是one-hot编码后二进制向量第i个元素的值;a_j是SoftMax计算得到的概率
$$
分析:对于损失loss,每个训练样例的y都只能取一个值;当a_j越小,-log(a_j)的值会越大,因此模型会激励a_j变大,尽可能接近1
SoftMax实现输出层

$$
z_j^{[l]} = \vec{w_j^{[l]}}\vec{a}+b_j^{[i]} \
a_j^{[l]} = \frac{e^{z_j^{[l]}}}{\sum_{i=1}^{units}e^{e_i^{[l]}}}\
units:神经元数
$$
减少数字舍入误差
在计算机中,存储浮点数的精度是有限的
比如:


发现在数学上计算相等的两个算式,在代码中却出现了运行结果不相等
tensorflow可以对这种情况做出优化
逻辑回归例子
以逻辑回归的loss值为例,我们看看数学上等价的两个公式
$$
a = g(\vec{w}\vec{x}+b) = g(z) = \frac{1}{1+e^{-z}} \
loss = - \sum_{i=1}^{m}(ylog(a)+(1-y)log(1-a))\
$$
对应的代码
1 | model1 = Sequential([ |
$$
loss = -\sum_{i=1}^{m}(ylog(\frac{1}{1+e^{-z}})+(1-y)log(1-\frac{1}{1+e^{-z}}))
$$
1 | model2 = Sequential([ |
整体对比代码
1 | import numpy as np |
运行结果

python知识:
__call__:将对象实例作为方法调用,与PHP的__invoke很类似1
2
3
4
5
6
7
8
9class test:
def __init__(self):
print("__init")
def __call__(self, X):
print(f"call:{X}")
t = test()
t(1)
1 |
|

对SoftMax进行优化
原本的公式
$$
\vec{a} =(a_1,a_2,\dots,a_{10}) = g(z_1,z_2,\dots,z_{10}) \
Loss = L(\vec{a},y) = \begin{cases}
-log(a_1)&if&y=1,\
\vdots\
-log(a_{10})&if&y=10\
\end{cases}
$$
1 | model1 = Sequential([ |

将激活函数这一步直接放在计算损失中
$$
Loss = L(\vec{a},y) = \begin{cases}
-log(\frac{e^{z_1}}{e^{z_1}+e^{z_2}+\dots+e^{z_{10}}})&if&y=1,\
\vdots\
-log(\frac{e^{z_{10}}}{e^{z_1}+e^{z_2}+\dots+e^{z_{10}}})&if&y=10\
\end{cases}
$$
1 | model2 = Sequential([ |
什么是稀疏交叉熵损失函数(SparseCategoricalCrossentropy)?
- 适用于这样的多分类任务:标签是整数(表示编号),模型输出是概率分布(像softmax这样的输出)
损失函数
$$
L(P_y) = -log(P_y) \
P_y:真实标签对应的案例 \
比如,标签向量是[0,1,2],模型输出的概率分布是[0.6,0.2,0.2],真实标签是0,那损失函数就是-log(0.6)
$$
SoftMax的一个特点就是使输出两极化,体现为
- 正样本趋近1,负样本趋近0
- 样本绝对值越大,两极化越明显
1 | import numpy as np |
输出结果
1 | ['0.0003', '0.0022', '0.0059', '0.1182', '0.8734'] |
图像

存在的问题
- 对于很大的输入(大到->+∞),分子的会变得非常非常大,大到变成inf,而分母同样也会变成inf,softmax计算出的值就不确定了(inf / inf 最终结果是nan),这就是上溢
- 对于很小的输入(小到->-∞),分子->0,导致最终的结果被四舍五入为0,这就是下溢
优化1
思路:控制输入向量中x_i的大小;在进行幂指数运算时先减去向量中的最大值,这样一来,输入x_i-max(x)的大小范围就在(-∞,0]
$$
SoftMax(x_i) = \frac{e^{x_i-x_{amx}}}{\sum_{j=1}^{m}e^{x_j}}
$$
输入特征X
1 | X = np.array([-3, -1, 0, 3, 1000]) |
使用原始的softmax(上文的softmax),输出结果
1 | exp_x [ 0.04978707 0.36787944 1. 20.08553692 inf] |
使用优化后的softmax:
1 | def softmax(x): |
运行结果
1 | exp_x [0. 0. 0. 0. 1.] |
这个优化后的softmax解决了上溢问题,但是并没有解决下溢的问题,如果最大值太大,会导致有很多结果丢失精度变为0
优化2
这个算法被称为log_softmax,将上面优化1的公式取对数
$$
SoftMax(x_i) = log\frac{e^{x_i-x_{max}}}{\sum_{j=1}^{m}e^{x_j}} = log(e^{x_i-x_{max}})-log(\sum_{j=1}^{m}e^{x_j})=x_i-x_{max}-log(\sum_{i=1}^{m}e^{x_j})
$$
多标签分类问题
是一种与每个图像相关联的,有多个输出标签的分类问题

区别:输出标签向量y中不只有一个真实类别;多分类问题,即使有多个输入特征,最终输出的真实类别也只有一个
也就是说,输出向量中元素是不互斥的,可以不只有一个1
如何构建神经网络
方法1:把三个标签分类分别当作三个神经网络(不推荐)

方法2:训练一个神经网络同时检测三种情况

可以视作三个二分类问题,因此激活函数可以使用sigmoid
高级优化方法
Adam算法(自适应向量估计)
自动调整学习率以实现更高效地梯度下降;adam算法为模型的每个参数使用不同的学习率

直观理解:如果参数wj和b似乎一直在大致相同的方向上移动,那么学习率太小,adam算法会增大该参数的学习率;如果一个参数来回震荡,那么学习率太大,adam算法会减小该参数的学习率
在代码中使用
编译模型时指定优化项
1 | # 定义密集层 |
优点:可以自动调整学习率,让算法整体更具有稳健性(鲁棒性)
卷积神经网络CNN
全连接神经网络存在的问题:
- 输入的形式应该是列向量,但是卷积神经网络中的输入是图像(2D矩阵),那么就需要对图片进行展平处理,原本图像中蕴含的空间等信息就被打乱了
- 输入的特征多了,那么神经元的参数就会很多,层的结构就很复杂,容易过拟合
通道数
通道数 是指图像或特征图在深度方向上的维度数量(可以类比全连接神经网络中的特征矩阵的x_i的特征数)
例如:
- 灰度图只有一个通道(特征):亮度
- RGB图像:3个通道(红,绿,蓝)
- RGBA图像:4个通道(红,绿,蓝,透明度)
图像在计算机中的本质
一个大的数值矩阵,每个元素代表像素的亮度或颜色
对于灰度图(只有黑白两色):255表示亮白,0表示黑,只有一个通道

对于彩色图像
三通道图

整体结构

卷积层
还是与手写数字为例;在密集层中,每个神经元都可以获取到整个的像素矩阵;而在卷积层中,我们让神经元只能获取到一部分像素而不是全部,这个特性被称为局部感知
局部感知的优点
- 计算更快(输入的矩阵更小,那么对于神经元来说,参数的数量大大减小)
- 需要更少的训练数据,更不容易过拟合(参数减少,模型复杂度降低,就不容易过拟合)
卷积层会改变通道的数目,在前一层的输入提取出不同的特征,同时会改变图片的长和宽
卷积核/滤波器
定义:一个在图像上滑动的小窗口,它与图像的局部区域进行点积运算,从而提取出该区域的特征。其实可以类比全连接神经网络的参数w,只不过它是共享的
卷积核是一个4D张量,形状为(C_out,C_in,K,K)
- C_out:输出通道数/卷积核数量
- C_in:输入通道数
- K:卷积核大小(K×K)
案例:心电图信号分类

池化层
池化层可以改变图像的大小,但不改变通道数
卷积运算

对应位置相乘再相加
带偏置的计算过程

权重共享
对于全连接神经网络,输入中的每个特征都对应着一个神经元中的一个参数
而对于卷积神经网络并非这样
卷积神经网络中,是同一个卷积核在整个输入图像上滑动,对所有局部区域使用相同权重参数
- 卷积核的参数在整个图像上是共享的
- 不同位置的局部区域都使用这个相同的卷积核做卷积操作
- 每个位置的输出是该卷积核与对应局部区域的点积结果
填充
为了控制卷积后输出的特征图尺寸;在输入特征图周围填充0

步幅
指卷积核一次移动几格;能够控制输出特征图的大小
eg:步幅为1

步幅为2

卷积运算后特征图大小
$$
输出特征图的高OH=\frac{H+2P-FH}{S}+1
$$
- H:输入特征图的高
- P:填充的宽度(多了几圈)
- FH:卷积核的高
- S:步幅
$$
OW = \frac{W+2P-FW}{S}+1
$$
- W:输入特征图的宽
- P:填充的宽度(多了几圈)
- FW:卷积核的宽
- S:步幅
多通道卷积运算
特征图有n通道,卷积核就有n通道

将每个通道的卷积结构相加;有n个卷积核,输出特征图通道数就为n

对于多通道卷积运算,我们可以用立体图表示

- C:输入通道数
- W:输入特征图的宽度
- H:输入特征图的高度
- FW:卷积核宽度
- FH:卷积核高度
- FN:输出通道数/输出的特征图数/卷积核数量
池化运算
池化运算是按通道独立运算的,即池化层运算不会对输入特征图的通道进行改变
池化核
类比卷积运算,我们也可以引入池化核这一概念
与卷积核相同和区别:
- 相同点:都是在特征图上滑动的窗口,并提取信息
- 不同:池化核仅仅是告诉算法要在特征图什么区域提取一块多大的特征,对这块特征取最大值或者平均,而不是和自身进行点积
最大池化运算

每次在区域中找到最大值
平均池化运算

区域内的值相加求平均
优点
对微小的位置变化具有鲁棒性,使模型更加健壮

输出尺寸
$$
OH=\frac{H+2P-FH}{S}+1 \
OW = \frac{W+2P-FW}{S}+1
$$
- H,W:输入特征图的高、宽
- P:填充的宽度(多了几圈)
- FH,FW:池化核的高、宽
- S:步幅
代码实现
全部代码
1 | import numpy as np |
详解:
用到的库
1 | import numpy as np |
获取数据集并进行初步处理
1 | # 获取mnist手写数字数据集 |
卷积层,使用tensorflow.keras.layers.Conv2D()
1 | layers.Conv2D( |
池化层,使用tensorflow.keras.layers.MaxPool2D() (最大池化运算)
1 | layers.MaxPool2D( |
整体结构
1 | model = models.Sequential([ |
编译模型
1 | model.compile( |
训练模型
1 | model.fit( |
获取最终损失函数值
1 | # 评估模型 |
输出结果

模型评估
划分测试集与训练集

计算损失函数
线性回归

逻辑回归

不适用逻辑损失计算训练误差和测试误差:

也成为0-1损失
$$
\hat{y_i} = sign(f_{w,b}(\vec{x_i})) \
sign()是符号函数,输出-1/+1;\hat{y_i} 表示对第i个样本的预测类别,y_i表示第i个样本的真实标签 \
Error = \frac{1}{m}\sum_{i=1}^{m}1(\hat{y_i} != y_i) \
当\hat{y_i} != y_i,累加1,否则累加0 \
换句话说,\sum_{i=1}^{m}1(\hat{y_i} != y_i)就是预测类别与真实标签不符的数量
$$
测试误差能更客观地表现模型面对新数据时的情况
模型选择

拟合一个多项式次数d=k的多项式,训练得到参数
$$
w^{
$$
为什么课上提出
$$
J_{test}(w^{<5>},b^{<5>})
$$
太过于乐观呢?
个人理解:
因为选择多项式模型时,拟合的参数除了w,b,还有多项式指数d
但是我们选择超参数d时使用了测试集;即我们使用测试集对不同d的选择进行了评估;也就是说测试集参与了优化超参数d,那么最终测试时,选择的d必然是在测试集上表现最优的那个,那么就会造成测试误差小于实际泛化时的误差
就好比考前看到了卷子,考出的成绩虚高
结论:选择超参数(比如这个例子中的d)时,要使用验证集,测试集仅用于测试和最终评估
交叉验证集(验证集/开发集[dev set])
使用额外的数据集来交叉检查有效性或者不同模型的准确性

对于上面选择合适的指数d,应该改为用验证集
找到最低的交叉验证误差对应的d
此时报告泛化误差就可以直接使用测试误差了,测试误差将不再小于泛化误差
整体过程
- 训练集->拟合模型参数
- 验证集->拟合超参数(模型以外的参数)
- 测试集->评估模型
整体过程中测试集全程没有参与任何参数的拟合,这就是为什么测试误差是泛化误差公平估计
偏差与方差的平衡解决
诊断偏差和方差
在前面的欠拟合与过拟合学习中,我们是通过绘制标签以及预测曲线来进行判断是否具有高偏差/方差,可是当特征多了之后,绘图就变得困难,此时需要引进更系统的方法来进行判断

对于欠拟合:
特点是训练损失$$J_{train}$$很高(在训练集上表现的就不好),验证损失$$J_{cv}$$也很高($$J_{cv} \approx J_{train}$$),所以当$$J_{train}$$很高的时候,我们就可以断定模型欠拟合
对于过拟合:
特点是训练损失$$J_{train}$$很低,但是对于验证损失$$J_{cv}$$来说,由于是新数据,所以拟合效果不佳,验证损失$$J_{cv}$$会很高(证损失$$J_{cv} >> J_{train}$$)
如果两种情况同时存在(一般不出现在线性回归):
$$
J_{train}很高,同时J_{cv} >> J_{train}
$$

正则化
与前面类似的内容

新方法:使用交叉验证,与前面决定幂次d类似

选择不同的超参数λ,分别进行模型训练得到一组参数w,b,找到验证损失最小的一组作为最终的λ

表现基准
eg:语音识别搜索

训练误差为10.8%,交叉验证误差为14.8%,乍一看其实很高,但是人类识别错误的概率也来到了10.6%,那么其实这个算法表现得相当可以了
对于这个例子来说,判断两个误差是否高,更有用的是看训练的误差是否远高于人类的表现水平
定义
基准:可以合理期望学习算法最终达到的误差水平是多少
比如
- 通过人类在这件事上的表现来确定
- 通过竞争算法的一些指标数据进行确定
- 经验主义
测量的两个关键量
- 训练误差与基准之间的差距(大:欠拟合)
- 训练误差和交叉验证误差的差距(大:过拟合)

学习曲线
样本尺寸和误差(J_{train},J_{cv})之间的关系
绘制学习曲线
代码实现
1 | import numpy as np |
展示了欠拟合,拟合良好,过拟合的情况

随样本量增大,交叉验证误差降低,训练误差上升,为什么?
- 训练误差上升:随样本量增加,曲线完美拟合样本点的难度会加大;只有个别样本点时,拟合难度就很低
- 交叉验证误差降低:随样本量增加,模型泛化能力增强,对新数据预测得更好
欠拟合

训练损失和验证损失都很大,但样本量达到一定水平时,训练损失和验证损失都将趋于稳定

过拟合

训练误差低的不切实际

解决高偏差和高方差
解决高方差
- 对于小样本过拟合,选择获取更多训练样本
- 使用更有用的特征子集,简化模型
- 增大正则化参数λ,减小权重
解决高偏差
- 添加更多特征
- 添加多项式特征
- 减小正则化参数λ
- 不能通过减小训练集规模来解决高偏差,会导致出现高方差
在神经网络中的偏差和方差
大型神经网络在中小型数据集上训练时,表现低偏差;也就是说神经网络足够大时,模型几乎能总是很好的拟合训练集
调整方差和偏差

如果在训练集上表现不佳(J_train很大),可以通过使用更复杂的神经网络结构(更多的隐藏层以及层中有更多的神经元)
在验证集上表现不佳,可以选择增大训练数据规模
局限性:
- 太大太复杂的神经网络会导致计算开销非常非常大
- 数据规模到一定程度后就不容易再获取到新的数据了
神经网络太大会导致高方差吗
一个经过良好正则化的大型神经网络通常表现得和小网络一样好甚至更好
实现正则化

tensorflow中需要在Dense中指定kernel_regularizer
1 | import tensorflow as tf |
代码验证
1 | import numpy as np |

训练100轮后,正则化大模型的表现优于无正则化的小模型
结论:大的神经网络特别适合拟合复杂函数;当数据集规模不是很大时,大的神经网络通常是低偏差的
机器学习系统
迭代发展

选择架构(模型,数据集,特征工程,超参数选择…)->模型训练->模型评估(偏差,方差,误差分析)
eg:垃圾邮件分类器

例子中,特征向量构建使用的是:构建一个字典,根据其中单词是否出现来把元素设置为0/1;也可以把元素设置成单词出现的次数
误差分析
手动检查算法错误的例子,看看是否存在系统性错误;可以帮助我们理解模型在哪些方面表现不佳,从而指导我们如何改进模型
添加数据
机器学习中,我们几乎总是希望能有更多的数据,因此有时我们会忍不住想去获取更多的所有类型的数据,但是获取所有类型的数据代价花销太高
数据增强
拿一个现有的训练样本取创建一个新的训练样本,在原来的训练样本上做出一些变化,大小变化,对比度变化,旋转等;能够提高模型泛化能力,防止过拟合
注意:对源数据进行修改或失真处理应该代表测试集中存在的噪音或失真类型,添加的噪音应该是我们预期中出现的而不是随机添加

数据合成
从零开始编造全新的样本,数据合成是一种通过人工生成或模拟数据的方法,以弥补真实数据不足、不平衡或难以获取的问题
比如OCR(Optical Character Recognition,光学字符识别)任务中,使用自己电脑上的字体截图来创造新的样本

迁移学习
将在一个任务或数据集上训练好的模型,迁移到另一个相关但不同的任务或数据集中
优点:迁移学习可以显著减少训练时间和数据需求,尤其适用于数据量较小的任务

图片中的例子,保留了隐藏层的结构以及参数,但是将输出层换成了更小的输出层
此时有两种训练方法
1.只训练输出层,使用adam等优化算法更新输出层的参数(适用于小训练集)
2.训练所有的参数,但前四层用迁移前的模型训练的值进行初始化(适用于大训练集)
机器学习项目完整周期

- Scope project(定义项目范围):
- 在这个阶段,你需要明确项目的目标、需求和预期成果。这包括定义项目的范围,确定项目要解决的问题,以及项目成功的关键指标。
- Collect data(收集数据):
- 一旦项目范围确定,下一步就是收集数据。这可能涉及到定义数据收集的方法和来源,以及实际的数据收集工作。数据的质量对机器学习模型的性能至关重要。
- Train model(训练模型):
- 在收集到足够的数据后,接下来的任务是训练模型。这包括选择合适的算法,使用训练数据来调整模型参数,以及进行模型的训练、错误分析和迭代改进。这个阶段的目标是开发出一个性能良好的模型。
- Deploy in production(部署到生产环境):
- 当模型训练完成并且经过验证后,最后一个阶段是将模型部署到生产环境中。这意味着模型将被用于实际的业务流程中,对真实世界的数据进行预测或决策。在这个阶段,还需要对系统进行部署、监控和维护,确保其稳定运行并根据需要进行更新。
倾斜数据集的误差指标
详见第0周的模型评价指标(真正,真负,假正,假负,精确率,召回率,F1 scope)





