前言
关于深度神经网络的原理和基础实现可以查看我之前的文章:深度神经网络(DNN)
这次的主要目标是在之前BP神经网络的基础上,实现多层网络和目标分类,为之后 LeNet 和 AlexNet 打个基础。
因突发疾病抢救无效,旷视首席科学家孙剑去世,年仅45岁1。
数据
这次采用的数据集是大名鼎鼎的 鸢尾花 数据集2。
该数据集的输入包含有4个分别是:花萼长度(sepal length (cm) ),花萼宽度(sepal width (cm) ),花瓣长度(petal length (cm))和花瓣宽度(petal width (cm) )。分类标签有3个分别是,山鸢尾(Iris-setosa)、变色鸢尾(Iris-versicolor)和维吉尼亚鸢尾(Iris-virginica),分别对应编号0、1、2。数据集中共计150组数组,偷个懒,这次我没有将数据集分为训练集和测试集。
以下是获取数据集的方法,需要提前安装sklearn库:pip install sklearn
# 获取iris数据集def GetIrisDataset(): from sklearn.datasets import load_iris iris = load_iris() #数据集并不能直接用,通过pandas的DataFrame来转化 import pandas as pd
#col是列名 col = list(iris["feature_names"]) #在iris数据集中,标签在"data"数组里,标记在"target"数组里 m1 = pd.DataFrame(iris.data,index=range(150),columns=col) m2 = pd.DataFrame(iris.target,index=range(150),columns=["outocme"])
#将上述两张DataFrame表连接起来,how是DataFrame参数,可以不写,这里用外连接。不清楚外连接的可以看下SQL语句 m3 = m1.join(m2,how='outer')
#to_excel语句转化成excel格式,后缀名为.xls m3.to_excel("./iris.xls")
数据归一化的方法依旧是 最大最小归一法:
\begin{equation} x{'}=\frac{x-min(x)}{max(x)-min(x)} \end{equation}
这里我尝试了以下模拟PyTorch的DataLoader方法,自定义了一个数据集加载方法:
class DataLoader: def __init__(self, path): ...
def __iter__(self): ...
def __next__(self): ...
网络设计
这次定义的BP神经网络的结果如图所示:
输入层 4个节点对应4个鸢尾花的参数。
输出层 3个节点对应3种鸢尾花。
网络实现
详细的网络推导过程请参照我之前的文章,这里只描述部分方法的使用和定义:深度神经网络(DNN)。
激活函数
Sigmoid
Sigmoid 激活函数的取值分布在0到1之间,在深度学习再度被人们关注的初期是最常被采用的激活函数,但是由于网络层数的加深,采用sigmoid激活函数常常会导致梯度消失。另外,它的均值是0.5,并不是以0为中心的,因此也不便于计算。
但是如果在输出层想将输出规范到0到1之间,那么就可以直接采用sigmoid激活函数。相应的,想得到其他输出只需要在乘以缩放系数并加上偏置即可。
\begin{equation} Sigmoid(x)=\frac{1}{1+e^{-x}} \end{equation}
\begin{equation} \frac{\partial{Sigmoid(x)}}{\partial{x}}= x \times (1 - x) \end{equation}
Tanh
Tanh3 激活函数取值在-1到1,它的均值为0,弥补了sigmoid均值非0的缺点,但是和sigmoid激活函数一样可能在深层网络中导致梯度消失。
\begin{equation} tanh{(x)}=\frac{e^x-e^{-x}}{e^x+e^{-x}} \end{equation}
\begin{equation} \begin{aligned} \frac{\partial{tanh{(x)}}}{\partial{x}} &=\frac{(e^x+e^{-x})(e^x+e^{-x})-(e^x-e^{-x})(e^x-e^{-x})}{(e^x+e^{-x})^2}\\\\ &=1-\frac{(e^x-e^{-x})^2}{(e^x+e^{-x})^2}\\\\ &=1-tanh{^2}(x) \end{aligned} \end{equation}
ReLU
ReLU4 的收敛速度会比 sigmoid 或 tanh 快很多。并且,当x<0时,ReLU硬饱和,而当x>0时,则不存在饱和问题。所以,ReLU 能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。这使我们能直接以监督的方式训练深度神经网络,而无需依赖无监督的逐层预训练。
ReLU 的缺点是,随着训练的推进,部分输入会落入硬饱和区,导致对应权重无法更新。这种现象被称为“神经元死亡”。与sigmoid类似,ReLU的输出均值也大于0,偏移现象 和 神经元死亡会共同影响网络的收敛性。
\begin{equation} ReLU(x)= \begin{cases} x&x>0 \\\\ 0&x\leq0 \end{cases} \end{equation}
\begin{equation} \frac{\partial{ReLU(x)}}{\partial{x}}= \begin{cases} 1&x>0 \\\\ 0&x\leq0 \end{cases} \end{equation}
Sigmoid & Tanh & ReLU 对比
下面是在相同参数下,分别使用Sigmoid、Tanh和ReLU作为激活函数的网络表现,这个区别还是比较明显的,Tanh和ReLU相比较Sigmoid,收敛速度更快,如果收敛到最后,在小样本或简单网络中,至少网络表现相差不大。
在我实际测试中在简单网络中 Tanh 效果非常好,收敛得又快又好,Sigmoid 是因为收敛太慢,而 ReLU 则是网络非常容易卡死。
损失函数
L1Loss
常用别称:L1范数损失、最小绝对偏差(LAD),平均绝对误差(MAE)
\begin{equation} e = \frac{1}{n}{|\hat{y}-y|}\end{equation}
\begin{equation} \frac{\partial{e}}{\partial{\hat{y}}} = \begin{cases}\frac{1}{n}&, \hat{y}-y\ge0\\\\ -\frac{1}{n}&, \hat{y}-y<0 \end{cases}\end{equation}
是预期值, 是实际值, 是样本量 一般而言,为方便计算,系数为1,即
SmoothL1Loss
常用别称:平滑L1Loss
\begin{equation} x=\hat{y}-y \end{equation}
\begin{equation}e= \begin{cases} 0.5x^2&|x|<1\\\\ |x|-0.5&|x| \ge1 \end{cases} \end{equation}
\begin{equation} \frac{\partial{e}}{\partial{\hat{y}}}= \begin{cases} x&|x|<1\\\\ 1&x\ge1 \\\\ -1&x\leq-1 \end{cases} \end{equation}
L2Loss
常用别称:L2范数损失、最小均方误差(LSE)、均方误差(MSE)
\begin{equation} e = \frac{1}{n}\sum_i^n({\hat{y} - y})^2 \end{equation}
\begin{equation} \frac{\partial{e}}{\partial{\hat{y}}} = \frac{2}{n}(\hat{y} - y) \end{equation}
L1 & SmoothL1 & L2 对比
下图是3种损失函数的示例,L2Loss是经过缩小的,能比较明显发现的就是 SmoothL1和L2 在[-1, 1]这个区间内较为平滑,不像L1有个明显的尖角5。
下图是使用Tanh作为激活函数,然后分别使用 L1Loss、SmoothL1Loss、L2Loss 不同损失函数,得到到网络性能。
可能因为模型简单、数据量少的原因,最总模型都能很好收敛,但过程有一定差异,差异比较大的就是 L1Loss,相当不稳定,这收敛过程比过山车都刺激。
CrossEntropyLoss
常用别称:交叉熵损失
摸了
输出层
在这次的项目中涉及到了一个输出层的输出结果到分类标签的一个转换。
例如,这次需要分类3种鸢尾花就设置三个输出节点,而它们的理想参数或标签则设置为[1 0 0]、[0 1 0]和[0 0 1],这里的1、0可以看作是和否,下面提两种假设:
假设1,输出结果是[0.3 0.7 0.1],以0.5作为分界,大于等于0.5则设为1,小于0.5则设为0,实际输出结果为[0 1 0],该鸢尾是属于第二类的变色鸢尾。
假设2,输出结果是[0.6 0.6 0.1],变化条件同上,实际输出结果为[1 1 0],不符合三类种任何一类,但也能得到一个信息就是该鸢尾花可能同时具备山鸢尾和变色鸢尾的某些特征。
这里有一个现象,就是0.5和0.9虽然都能被看作是一类,但实际两者可能有很大差距,这就能通过置信度来表示,做卷积神经网络的时候可以尝试一下。