Pytorch是torch的python版本,是一款开源的神经网络框架,专门针对 GPU 加速的深度神经网络(DNN)编程。
一、环境准备
在安装之前需先确定电脑是否含有Nvidia(英伟达显卡),以便安装正确的版本。打开任务管理器:
只有带NVIDIA的英伟达显卡的电脑才能安装GPU版本,否则其他的就只能安装CPU版本。
1.1安装Anaconda
Anaconda是一个强大的开源数据科学平台,它将很多好的工具整合在一起,极大地简化了使用者的工作流程,并能够帮助使用者解决一系列数据科学难题。官方网站:Anaconda官网。
完成安装后需要手动配置环境变量,在系统环境变量Path中新建值(根据安装路径不同应自行调整,需注意最后一条路径以\
结尾):
E:\anaconda3
E:\anaconda3\Library\bin
E:\anaconda3\Library\mingw-w64\bin
E:\anaconda3\Scripts\
在完成安装后使用命令行查看是否完成安装:
conda --version
python
此时可通过Anaconda Navifator
进行打开:
使用conda安装python包时使用官方服务器位于国外,故可使用国内的清华镜像源,在Anaconda Prompt
进行操作:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
//设置搜索时显示通道地址
conda config --set show_channel_urls yes
1.2创建虚拟环境
安装好的Anaconda会自带一个基础环境。但是我们后续的项目每一个需要的安装包不同,为了避免冲突,所以我们可以为每一个项目配置一个虚拟环境,这样就不相互打扰了。使用Anaconda Prompt
创建虚拟环境:
conda env list
conda create –n torchProject python=3.8
conda env list
:查看当前虚拟环境列表。conda create –n 虚拟环境名字 python=版本
:创建指定Python版本的虚拟环境。
查看已创建的虚拟环境:
1.3准备CUDA
使用任务管理器查看自己电脑GPU的型号:
进入官网安装最新的显卡驱动(可直接使用命令查看是否已安装驱动程序):
安装完后即可通过命令行查看电脑驱动的版本:
nvidia-smi
CUDA Driver版本就是12.0,表示的是驱动所能支持的最大运行API版本就是12.0。若要安装CUDA Runtime Version(运行版本),要保证CUDA Driver 版本 >= CUDA Runtime 版本,也就是12.0及以前的。
1.4安装pytorch
进入Pytorch官网:官网,若官网给出的版本符合电脑配置,复制指令到conda环境中运行即可 ,否则需自行寻找符合的版本:
激活虚拟环境并进行安装:
1.3安装Pycharm
Pycharm官网: Pycharm官网
本机中安装路径为:E:\Pycharm
,完成安装后即可新建项目编写代码:
二、线性回归
2.1基本原理
回归问题:预测数值型的目标值,本质是寻找自变量和因变量之间的关系,以便能够预测新的、未知的数据点的输出值。例如,根据房屋的面积、位置等特征预测其价格(房价预测、股票价价格预测、温度预测等)。
其中,
x
x
x表示特征向量,而
y
y
y表示某一标签,即,通过特征对标签进行预测。
2.2损失函数:均方误差损失函数
下图是小明每天学习的时间和最后考试的分数的数据。数据如下表所示,同时想知道小明假设学习4个小时最后考试会得多少分?
这是预测一个具体的数值的回归任务,将每日学习的时间和考试的得分用图画出来,大致可得出随着学习时间的增长,那么最后的考试的得分就会越高:
数据的输入只有一个,即为小明的学习时间,输出是考试的得分。因此可使用单特征线性模型进行预测,模型为:
f
(
x
)
=
w
x
+
b
f(x)=wx+b
f(x)=wx+b
此时的目的变为,求出最优的
w
、
b
w、b
w、b来表示输入与输出之间的关系,而最优指的即是输出的考试得分和真实的考试得分(也可以称为真实值或者标签)之间的差值越小越好,最好是0(显示生活中获取的数据存在噪声影响,数据之间的关系也存在一定误差,故而只能逼近0)。线性回归模型使用的损失函数即为上文中的均方误差函数:
此时问题转化为求出最优的
w
、
b
w、b
w、b来使得均方误差函数值最小。
此处采用最为原始的穷举法,即穷举
w
、
b
w、b
w、b的值并不断计算损失函数,使之最小:
可得出,当
w
=
2
、
b
=
0
w=2、b=0
w=2、b=0时,误差函数为零,此时最优。而在现实生活中,处理问题是很复杂的,往往输入的特征也是多个的,那么就意味着有多个
w
、
b
w、b
w、b来表达输入和输出之间的关系,利用穷举法找到最优的
w
w
w是不现实的,此时可使用最小二乘法、梯度下降法。
2.3梯度下降法更新参数
这里将梯度下降法运用到上例当中以求解参数
w
w
w的最优解,设置初始值
w
0
=
4
w_0=4
w0=4,超参数学习率为0.01,对损失函数进行求导:
更新参数的值:
经过100轮的梯度更新,
w
w
w逼近最优值2,画出梯度下降过程中损失函数的变化趋势:
因此梯度下降法可通过不断的计算梯度找到使损失最小的参数,而当数据的特征有多个时,就需要对损失函数求偏导并重复上述过程,即不断对参数进行迭代更新,直到损失函数趋于平稳不再下降。二维的损失函数变化趋势:
2.4线性回归代码实现
数据:
# 定义数据集
x_data = [1, 2, 3] # type:每日学习时间
y_data = [2, 4, 6] # type:考试得分
# 初始化参数w
w = 4 #type:初始化模型参数
#定义线性回归模型
def forword(x):
return x * w #返回预测值
#定义损失函数:使用均方误差函数
def loss(x_data, y_data):
costValue = 0 # type:损失误差
for x, y in zip(x_data, y_data):
y_pred = forword(x) #type:预测值
costValue += (y_pred - y) ** 2
return costValue / (len(x_data))
#定义梯度计算公式
def gradient(x_data, y_data):
grad = 0 #type:初始化梯度
for x, y in zip(x_data, y_data):
#(wx-y)^2对w求导后得:2x(wx-y)
grad += 2 * x * (w * x - y)
#除以len(x_data)后得到损失函数梯度值
return grad / len(x_data)
#使用梯度下降法更新参数
for epoch in range(100): #训练100轮
#计算损失函数
loss_value = loss(x_data, y_data)
#获取损失函数梯度
grad_value = gradient(x_data, y_data)
#利用梯度更新参数w,设置学习率为0.01
w = w - 0.01 * grad_value #初始w一般设大些才符合梯度下降法
print("学习轮次:", epoch, ",参数值:", w, ",损失函数:", loss_value)
print("100轮时得到w:", w, ",预测学习时间为4时,得分为:", forword(4))
三、逻辑回归
逻辑回归模型一般用于二分类任务当中,事实上,逻辑回归其实是用回归的方法来做分类,其输出是一个在0-1之间的连续值,但人为设定一个阈值来使输出的值映射为一个类别。
3.1算法原理
s
i
g
m
o
i
d
sigmoid
sigmoid函数的定义如下:
其中,
e
e
e为欧拉常数,自变量
z
z
z可取任意值,函数值域为
(
0
,
1
)
(0,1)
(0,1),相当于将输入的自变量映射到了
0
1
0~1
0 1之间。正是由于
s
i
g
m
o
i
d
sigmoid
sigmoid函数的映射特性,使得可将其输出值作为事件发生的概率。设输入的数据特征为向量
x
x
x,不同特征有着对应的权重参数表示该数据特征的重要程度,使用向量
w
w
w表示,同时还有偏置参数
b
b
b,令:
z
=
w
0
x
0
+
w
1
x
1
+
.
.
.
+
w
n
x
n
+
b
等价于:
z
=
w
T
x
+
b
z=w_0 x_0+w_1x_1+...+w_nx_n+b\\ 等价于:z=w^Tx+b
z=w0x0+w1x1+...+wnxn+b等价于:z=wTx+b
将
z
z
z输入到逻辑回归模型中后,就可计算出该事件为正例的概率。因此,一个二分类问题预测结果为正例的表达式为:
p
(
y
=
1
∣
X
)
=
1
1
+
e
−
w
T
X
+
b
p(y=1|X)=\frac{ 1 }{ 1+e^{-w^TX+b} }
p(y=1∣X)=1+e−wTX+b1
即,二分类问题的最终结果只能为0或1,将特征数据输入逻辑回归模型预测结果为
y
ˉ
=
0.8
\bar{y}=0.8
yˉ=0.8(处于0~1),表示在输入特征数据的情况下,预测真实值为
y
=
1
y=1
y=1的概率为0.8,之后可通过设置相应阈值,当概率在某一范围内时就认为真实值为
y
=
1
y=1
y=1。
相应的,预测结果为反例的表达式为:
在计算过程中,需要考虑
y
=
1
、
y
=
0
y=1、y=0
y=1、y=0两种情况,使得计算变得复杂。此时可将上述两个公式合并在一起:
这就是逻辑回归模型公式。注意到,若真实值
y
=
1
y=1
y=1,则此时
P
(
y
=
1
∣
X
)
P(y=1|X)
P(y=1∣X)越接近1,就代表模型预测越准确,即希望上式值越大;而若真实值
y
=
0
y=0
y=0,则此时希望
P
(
y
=
0
∣
X
)
P(y=0|X)
P(y=0∣X)接近0,即
1
−
P
(
y
=
0
∣
X
)
1-P(y=0|X)
1−P(y=0∣X)越大代表模型预测越准确。综上,不管真实值为何值,都希望
P
(
y
∣
X
)
P(y|X)
P(y∣X)越大越好。
3.2损失函数:交叉熵损失函数
&emps; 利用逻辑回归模型可对输入的数据特征进行判断,并得出二分类的结果。但模型性能的好坏仍取决于模型中的参数向量
w
、
b
w、b
w、b,与线性回归思想一致,需要从提供的数据样本中不断学习(反向传播)来更新参数,使得预测结果与真实值接近。在上文中可知,希望
P
(
y
∣
X
)
P(y|X)
P(y∣X)越大越好,故将所有样本数据下概率的乘积作为逻辑回归损失函数:
其中:
p
(
1
∣
x
i
)
=
1
1
+
e
−
w
T
X
i
+
b
p(1|x_i)=\frac{ 1 }{ 1+e^{-w^TX_i+b} }
p(1∣xi)=1+e−wTXi+b1
一般将其写为以下形式:
p
(
1
∣
x
i
)
=
1
1
+
e
−
w
T
X
i
+
b
=
σ
(
w
T
X
i
+
b
)
p(1|x_i)=\frac{ 1 }{ 1+e^{-w^TX_i+b} }=σ(w^TX_i+b)
p(1∣xi)=1+e−wTXi+b1=σ(wTXi+b)
此时损失函数为:
连乘运算不便于求导,故通过对数函数将其转化为求和运算:
为便于理解(损失函数值越小一般代表误差越小),且与梯度下降法相结合,给原函数加上符号并取均值,得到了逻辑回归模型的损失函数——交叉熵损失函数:
3.3梯度下降法更新参数
与线性回归利用梯度下降法更新参数思路一致,设定求出损失函数梯度公式、设置参数初值、更新参数、计算损失函数,并重复该过程,直到满足迭代轮次或损失阈值。找到最合适的模型参数后,利用训练出来的参数就可以利用逻辑回归模型进行分类。
参数
w
w
w偏导数求解:
参数
b
b
b偏导数求解:
3.4评价指标
3.4.1分类模型评价指标
- TP:真实为正类,预测为正类,称为真正,本例中TP=5。
- TN:真实为反类,预测为反类,称为真负,本例中TN=85。
- FP:真实为反类,预测为正类,称为假正,本例中FP=5。
- FN:真实为正类,预测为反类,称为假负,本例中FN=5。
准确率(Accuracy):准确率,即,判断正确的概率,可作为评价模型性能的指标。计算模型的准确率:
A
C
C
=
T
P
+
T
N
T
P
+
T
N
+
F
P
+
F
N
=
90
%
ACC=\frac{TP+TN}{TP+TN+FP+FN}=90\%
ACC=TP+TN+FP+FNTP+TN=90%
模型准确率为
90
%
90\%
90%,但并不代表模型优秀,换一个角度看,共有10个肿瘤患者,但只有5个被成功预测,说明模型效果并不优秀。事实上,导致这一结果的原因是因为样本不均衡,因为正类和反类的数量相差太大,这使得即使认定所有样本都是正类,准确率也有
90
%
90\%
90%。此时就需要使用更多的指标评价模型性能。通俗来说,准确率就是:预测正确的结果占总样本的百分比。
精确率(Precision):精确率,又称查准率,指预测为正类的样本中有多少是真的正类样本,计算上述模型的精确率:
P
=
T
P
T
P
+
F
P
=
50
%
P=\frac{TP}{TP+FP}=50\%
P=TP+FPTP=50%
预测为正类的样本中只有
50
%
50\%
50%预测正确,可见模型对患有癌症的识别并不准确。
召回率(Recall):召回率,又称查全率,指样本中的正类有多少被正确预测,计算上述模型的召回率:
R
=
T
P
T
P
+
F
N
=
50
%
R=\frac{TP}{TP+FN}=50\%
R=TP+FNTP=50%
召回率分母与精确率不同。
F1分数
精确率与召回率仅分母不同,二者关系可使用P-R
图表示:
评估模型时自然希望P、R越高越好,但在一些情况下二者是矛盾的。比如极端情况下,有100个正类样本,模型只预测了一个正类样本且预测正确,则此时
P
=
100
%
,
R
=
1
%
P=100\%,R=1\%
P=100%,R=1%,同样,有1个正类样本,模型预测了100个正类样本(其中只有一个预测正确),则此时
P
=
1
%
,
R
=
100
%
P=1\%,R=100\%
P=1%,R=100%。此时就需要综合考虑,最常见的方法就是使用
F
F
F分数:
F
=
(
α
2
+
1
)
P
∗
R
α
2
(
P
+
R
)
F=\frac{(α^2+1)P*R}{α^2(P+R)}
F=α2(P+R)(α2+1)P∗R
F分数是二者的加权调和平均,能同时综合进行考虑,通常取
α
=
1
α=1
α=1,即
F
1
F1
F1分数:
F
1
=
2
P
R
P
+
R
F1=\frac{2PR}{P+R}
F1=P+R2PR
3.4.2回归模型评价指标
3.5逻辑回归实战:乳腺癌的分类检测
3.5.1数据预处理
不同特征数据的量纲一般不同,比如现有特征 ( X 1 、 X 2 、 X 3 、 X 4 ) = ( 1 , 100 , 2 , 10000 ) (X_1、X_2、X_3、X_4)=(1,100,2,10000) (X1、X2、X3、X4)=(1,100,2,10000),直接使用该数据进行计算时往往会放大取值较大特征对真实值的影响,此时就需要对特征数据进行归一化处理,将所有数据取值范围均限定在 0 1 0~1 0 1之间,从而消除量纲对特征重要性的影响。
import numpy as np
import pandas as pd
#划分训练集和测试集
from sklearn.model_selection import train_test_split
#导入逻辑回归模型
from sklearn.linear_model import LogisticRegression
#计算模型的评价指标,如精确率、召回率等
from sklearn.metrics import classification_report
#数据归一化函数
from sklearn.preprocessing import MinMaxScaler
## 1.数据预处理
# 读取数据
dataset = pd.read_csv('breast_cancer_data.csv')
# 提取特征数据
X=dataset.iloc[:, :-1].values# iloc[:, :-1]表示提取所有行、除最后一列外的所有列。values属性从dataframe对象中获取了ndarray对象,去掉了索引和列名。
# 提取数据标签
Y=dataset['target'].values # iloc[:,-1]表示提取所有行、最后一列。得到数据标签(乳腺癌真实标签)
# 划分训练集和测试集,测试集比例为20%
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)
# 获取数据归一化对象,feature_range用于设置归一化的范围
sc=MinMaxScaler(feature_range=(0,1))
x_train=sc.fit_transform(X_train)
x_test=sc.transform(X_test)
print(x_train)
3.5.2模型训练与测试
import numpy as np
import pandas as pd
#划分训练集和测试集
from sklearn.model_selection import train_test_split
#导入逻辑回归模型
from sklearn.linear_model import LogisticRegression
#计算模型的评价指标,如精确率、召回率等
from sklearn.metrics import classification_report
#数据归一化函数
from sklearn.preprocessing import MinMaxScaler
## 1.数据预处理
# 读取数据
dataset = pd.read_csv('breast_cancer_data.csv')
# 提取特征数据
X=dataset.iloc[:, :-1].values# iloc[:, :-1]表示提取所有行、除最后一列外的所有列。values属性从dataframe对象中获取了ndarray对象,去掉了索引和列名。
# 提取数据标签
Y=dataset['target'].values # iloc[:,-1]表示提取所有行、最后一列。得到数据标签(乳腺癌真实标签)
# 划分训练集和测试集,测试集比例为20%
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)
# 获取数据归一化对象,feature_range用于设置归一化的范围
sc=MinMaxScaler(feature_range=(0,1))
x_train=sc.fit_transform(X_train)
x_test=sc.transform(X_test)
## 2.搭建逻辑回归模型
#创建逻辑回归模型对象
logistic_model=LogisticRegression()
#训练模型
logistic_model.fit(x_train,Y_train)
#打印模型参数
print('w:',logistic_model.coef_)
print('b:',logistic_model.intercept_)
#预测测试集
y_pred=logistic_model.predict(x_test)
#获取预测结果的概率
y_pred_prob=logistic_model.predict_proba(x_test)
#获取恶性肿瘤的概率
pred_list=y_pred_prob[:,1]
#设置阈值
thresholds=0.3 # 概率大于0.3时即认为可能存在肿瘤
result=[]
result_name=[]
for i in pred_list:
result.append(i)
if i>thresholds:
result_name.append('恶行肿瘤')
else:
result_name.append('未患有肿瘤')
print(result)
print(result_name)
#获取模型评价指标,指定0、1对应的标签
print(classification_report(Y_test,y_pred,labels=[0,1],target_names=['未患有肿瘤','恶性肿瘤']))
四、全连接神经网络原理
神经网络(Neural Network,NN),是一种模仿生物神经网络的结构和功能的数学模型,用于对函数进行估计或近似。人脑可以看做是一个生物神经网络,由众多的神经元连接而成。当神经元“兴奋”时,就会向相连的神经元发送化学物质,从而改变这些神经元内的电位;如果某神经元的电位超过了一个“阈值”,那么它就会被激活,即“兴奋”起来,向其他神经元发送化学物质。
4.1整体结构
全连接神经网络,是指第 N 层的每个神经元和第 N-1 层的所有神经元相连,第 N-1 层神经元的输出就是第 N 层神经元的输入。整体结构由三部分组成:
- 输入层:输入的数据,即x1、x2、x3,可以是图片、矩阵等。
- 隐藏层:对输入数据进行特征提取,对于不同的输入神经单元设置不同的权重与偏置,从而影响神经单元对输入信息敏感程度,可以形成输出结果的偏向)。事实上,隐藏层可看作是一层或多层神经网络连接而成。
- 输出层:输出的结果,即y1、y2、y3,可以是分类结果等。
通过输入层激活信号,再通过隐藏层提取特征,不同隐藏层神经单元对应不同输入层的神经单元权重和自身偏置均可能不同,输入层兴奋传递到隐藏层兴奋,最后输出层根据不同的隐藏层权重和自身偏置输出结果。举一个例子,目标是识别一个 4x3 的图像,下例中采用了12 神经节点来对应 4x3 个像素点(神经点属于隐藏层),在隐藏层中使用另外三个神经单元进行特征提取,最后输出层再使用两个神经节点标记识别结果是 0 或 1:
- 对于输入层,十二个神经单元对应 4 * 3 像素(黑白),如果该像素是黑的,则对应神经元兴奋,否则静息。
- 对于输出层的两个节点,如果识别结果偏向0,那么第一个节点兴奋度会高于第二个节点,反之识别结果偏向1。
- 对于隐藏层,每一个节点会对输入层的兴奋有不同的接收权重,从而更加偏向于某种识别模式。例如,隐藏层第一个神经单元对应下图模式A,也就是对应输入层 4、7号神经单元接收权重比较高,对其他神经单元接受权重比较低,如果超过了神经单元自身的偏置(阈值)则会引发隐藏层的兴奋,向输出层传递兴奋信息,隐藏层其他神经单元同理。
总之,模型的意义就在于找到最为合适的参数w、b,使得预测结果与真实情况最为接近。
4.2神经单元
神经网络本质是由多个神经单元组合而成,因此只要搞清楚神经元的本质就可以搞清楚全连接神经网络了。例如,一个全连接神经网络神经元的模型图:
x
:各个输入的分量。b
:偏置,用于控制神经元被激活的容易程度。w
:各个信号的权重的参数,用于控制各个信号的重要性,可以是一个数、向量、矩阵等。h
:激活函数。a
:神经元的输出。
可简写为:
a
=
h
(
w
T
x
+
b
)
a=h(w^{T}x+b)
a=h(wTx+b)
即,一个神经元的功能是求得输入向量与权向量的内积后,经一个非线性传递函数得到一个标量结果。
观察上述数学表达式发现,该式子只是将线性模型的输出结果经过一个激活函数的计算再输出,激活函数的种类有很多,但有着一定是非线性函数的共同特征。而若使用线性函数作为激活函数,则不管如何加深神经网络的层数,隐藏层再怎么多,总是存在与之等效的“无隐藏层的神经网络”。
4.3激活函数
激活函数有助于将神经元输出的值限制在我们要求的某个限制内。 因为激活函数的输入是W * x + b,其中W是单元的权重(Weight),x是输入,然后在其上加上b偏差(Bias)。 如果不限制在某个值上,则此值的变动范围会非常大,此时可使用激活函数将输出值限定在一个范围(常用0~1),来表示神经元的兴奋程度(0表静默,1表活跃)。并且,激活函数为神经网络引入了非线性的能力,其本身是有非线性性。
4.3.1非线性性
上文中提到,激活函数必须是非线性(即,不仅仅只有加法、数量乘法的运算)的,否则不管如何加深神经网络的层数,隐藏层再怎么多,总是存在与之等效的“无隐藏层的神经网络”。借鉴博客:机器学习-神经网络为什么需要非线性(激活函数)。
由上文可知,每一层的参数操作对应的函数表达式为
a
1
=
w
1
∗
x
+
b
1
a_1=w_1*x+b_1
a1=w1∗x+b1。将x用向量表示,而w相当于一个矩阵,此时二者乘积相当于是矩阵x列向量的线性组合:
此时对应神经网络仅含一层隐藏层,若将输出结果作为下一隐藏层的输入,并继续使用线性(仅含有加法、数量乘法运算)的激活函数,即
a
2
=
w
2
∗
a
1
+
b
2
a_2=w_2*a_1+b_2
a2=w2∗a1+b2,进行数学推导:
此时
a
2
a_2
a2相当于是一组新参数
w
′
、
b
′
w'、b'
w′、b′与输入向量
x
x
x运算得到,神经网络只含有一层隐藏层,即多层线性操作等价于一层线性操作。但真实世界有些原始数据本身就是线性不可分的,必须要对原始空间进行一定的非线性操作,比如上文中提及的限制输出范围,故而激活函数必须是非线性的,否则没有意义。
常见非线性函数有二次函数、分段函数、指数函数。
4.3.2Sigmoid函数
Sigmoid函数往往用于作为二分类问题输出层激活函数,输出值作为分类的概率,如,是种类A的概率为0.99,不是的概率为0.01。
梯度,是指某一函数在该点处的方向导数沿着该方向取得最大值,对于一维函数指的就是某一点处的导数。Sigmoid函数存在梯度消失(梯度变小并不断接近0,但不为0)、梯度爆炸(梯度快速变为极大值)的情况,但在实际中,梯度值期望是一个平稳值,即不大不小,此时方便求出对应的参数值。
注意,导数往往由于求出参数w、b,而参数即用于使输出结果最接近于真实情况。
4.3.3Tanh函数
Tanh函数的输出结果在(-1~1)之间,是一个对称的值域。
Tanh函数更容易收敛,是指相对于sigmoid函数,Tanh函数求出最佳参数所需的训练轮次更少,因为Tanh函数的导数在-1到1之间变化,具有较高的梯度(sigmoid函数最大梯度为0.25,而Tanh函数为1),有利于网络的快速训练。
4.3.4ReLU函数
ReLU函数解决了梯度消失的问题,因为当输入值小于0时导数值等于0,此时神经元死亡,以后面内容可知,在计算参数时会利用损失函数来反向传播优化参数w、b,而激活函数ReLU直接使得输出为0,此时不再出现损失,不会再优化参数w、b(神经元死亡指的即是,神经元的参数再也不会更新),也就解决了梯度消失问题。
4.3.5Leaky ReLU函数
无法为正负输入值提供一致的关系预测是指,正、负区间函数运算并不对称,使得对正负输入值的预测方式不同。
4.4前向传播
前向传播,是指将数据特征作为输入,输入到隐藏层,将数据特征和对应的权重相乘同时再和偏置进行求和,将计算的结果通过激活函数进行激活,将经过激活函数激活的函数输出值作为下一层神经网络层的输入再和对应的权重相乘同时和对应的偏置求和,再将计算的结果通过激活函数进行激活,不断重复上述的过程直到神经网络的输出层,最终得到神经网络的输出值。
简单来说,输入数据输入到神经网络并通过隐藏层进行运算,最后输出结果的过程,就是神经网络的前向传播。一个简单的神经网络:
隐藏层两个神经元的计算:
a
11
=
s
i
g
m
o
i
d
(
w
11
x
1
+
w
13
x
2
+
b
1
)
a
12
=
s
i
g
m
o
i
d
(
w
12
x
1
+
w
14
x
2
+
b
2
)
a_{11}=sigmoid(w_{11}x_1+w_{13}x_2+b_1)\\ a_{12}=sigmoid(w_{12}x_1+w_{14}x_2+b_2)
a11=sigmoid(w11x1+w13x2+b1)a12=sigmoid(w12x1+w14x2+b2)
传输层神经元的计算:
a
21
=
r
e
l
u
(
w
21
a
11
+
w
22
a
12
+
b
3
)
a_{21}=relu(w_{21}a_{11}+w_{22}a_{12}+b_3)
a21=relu(w21a11+w22a12+b3)
其中,
w
11
、
w
13
、
b
1
、
w
12
、
w
14
、
b
2
、
w
21
、
w
22
、
b
3
w_{11}、w_{13}、b_1、w_{12}、w_{14}、b_2、w_{21}、w_{22}、b_3
w11、w13、b1、w12、w14、b2、w21、w22、b3隐藏层对应输入的权重和偏置,训练模型的目的在于,找到一种方法可以求出准确的w和b,使得前向传播计算出来的y无限接近于真实情况。常见方法,如,梯度下降法,反向传播法就是梯度下降法在深度神经网络上的具体实现方式。
4.5损失函数与反向传播
在模型确定后(本质是确定了一组参数),就希望训练结果接近于真实值,此时可设置损失函数来计算前向传播的输出值和真实的label值之间的损失误差,来对模型性能进行评估。目前常见的损失函数有均方误差、交叉熵误差。对于不同类型的问题,如:
- 回归问题:输出的是物体的值,如预测当前温度等,是对真实值的一种逼近预测,输出值是连续的。
- 分类问题:输出的是物体所属的类别,输出值是离散的。
对于不同类型的问题就有着不同的常用损失函数。
4.5.1均方误差函数与交叉熵函数
1.均方误差函数
&emsp在回归问题中使用较多的是均方误差函数:
其中,
f
(
x
i
)
、
y
i
f(x_i)、y_i
f(xi)、yi分别表示输入数据对应的前向传播输出值、真实值,均方误差函数求出所有误差的平方和,并除以
2
m
2m
2m,
m
m
m是输入数据的个数。事实上,是所有误差的平方和的平均值并除以2,除以2目的在于更好计算
J
(
x
)
J(x)
J(x)的导数,2的有无对模型性能的评估并无影响。
以上图为例(tain_loss,训练集损失函数图),在初始训练时(训练轮次较低),误差往往较大,而随着训练轮次的增加,模型参数的不断迭代使得误差不断降低(指针对训练集的误差),在
e
p
o
c
h
=
75
epoch=75
epoch=75左右时,误差接近0。
2.交叉熵函数
在分类问题中使用较多的是交叉熵函数:
4.5.2反向传播
事实上,模型训练的流程为,将数据输入模型并通过前向传播得到输出数据,利用损失函数计算输出数据与真实数据之间的误差,并利用反向传播更新参数(这一过程需使用梯度)使损失函数变低(神经网络的输出和真实值更加逼近),最后不断重复这一过程,直到损失函数接近于0。
但是实际项目中,由于数据中存在噪声,因此损失值在参数不断的更新下会不断接近0,但是不可能等于0,所以我们往常将模型的训练轮次和损失值变化画图显示出来,如果损失值在一定的轮次后趋于平缓不再下降,那么就认为模型的训练已经收敛了。例如:
4.6梯度下降法
4.6.1基本原理
上文中的反向传播,正是基于梯度下降法实现的。
梯度下降法正是基于此思想,只是从追求不同方向上最陡的山体梯度,变为追求如何修改参数可使损失函数值下降最快。反之,就是梯度上升法。需要注意的是,梯度下降法只是一种局部搜索优化算法,即它无法保证得到全局最优解。因此,有时需要运用其他优化算法来搜索全局最优解。
1.一元函数:模型仅有一个参数
函数
J
(
w
)
J(w)
J(w)即为损失函数,横坐标
w
w
w表示模型参数,问题转化为如何求出函数极小值处参数
w
w
w的值。
思路:在当前位置求偏导,即梯度,负梯度不断增大接近零的方向(
w
w
w的变化方向),就是不断逼近函数极小值的方向。有时得到的是函数最小值的局部最优解,如果损失函数是凸函数,梯度下降法得到的解就是全局最优解。
一元函数的梯度(导数)公式:
假设损失函数为
J
(
θ
)
J(θ)
J(θ),其中
θ
θ
θ是模型参数,从一个初始点
θ
0
θ_0
θ0开始迭代,每次迭代更新
θ
θ
θ的值,直至损失函数值下降到一定程度,或者达到固定次数的迭代次数。每次迭代的更新公式:
其中,
α
α
α是学习率,控制每一步迭代的步长。学习率太小会导致收敛速度慢,而学习率太大会导致算法发散。因此,学习率是梯度下降法中需要调整的一个超参数。
注意,超参数是指算法运行之前手动设置的参数,用于控制模型的行为和性能。这些超参数的选择会影响到模型的训练速度、收敛性、容量和泛化能力等方面,常见的超参数如学习率、迭代次数、正则化参数、隐藏层的神经元数量等。
2. 多元函数:模型含多个参数
对于不同参数
θ
i
θ_i
θi,其梯度定义为:
其中,函数
J
(
θ
)
J(θ)
J(θ)即为损失函数,此时每次迭代的更新公式为:
直至损失函数值下降到一定程度,或者达到固定次数的迭代次数时,算法终止。
4.6.2案例:梯度下降法更新网络参数
初始误差(使用均方误差函数):
J
(
x
1
)
=
(
2
−
1.731
)
2
/
2
J(x_1)=(2-1.731)^2/2
J(x1)=(2−1.731)2/2
完成一轮更新后,损失函数的值更加逼近0,代表预测结果更加接近真实情况。
4.7模型训练大致流程梳理
在完成上文的学习后,可大致梳理出模型训练的大致流程:
- 1.以 N N N份的输入数据 X X X及其对应的结果 Y Y Y(同样为 N N N)份来搭建模型,常见模型如卷积神经网络。
- 2.开始训练,并设置模型训练的超参数,如学习率、训练轮次等。
- 2.1输入数据集 X X X,前向传播得到训练结果 Y ′ Y' Y′。
- 2.2根据真实结果 Y Y Y计算损失函数 J ( Θ ) J(Θ) J(Θ),其中,训练结果 Y ′ Y' Y′是关于参数集合 Θ Θ Θ的函数。
- 2.3利用梯度下降法进行反向传播,更新参数值,并重新进行预测。
- 2.4重复上述过程直到满足终止条件,从而得到模型。
- 3.利用模型对测试集进行测试,满足条件即可正常使用,否则调整超参数、模型类别等,重新训练模型。
4.8全连接神经网络实战1:乳腺癌预测
4.8.1数据预处理
在机器学习算法中,一般是通过计算特征之间距离或相似度来实现分类、回归的。一般来说,距离或相似度都是在欧式空间计算余弦相似性得到。假设有红、绿、蓝三种颜色特征,此时就不适合使用1、2、3编码方式来代表这三种特征。这是因为1、2、3之间存在大小关系,而实际上各颜色特征之间并没有大小关系,在使用独热编码后特征之间的计算会更加合理。但当离散特征的取值之间有大小意义或者有序时,比如衣服尺寸: [X, XL, XXL],那么就不能使用独热编码,而使用数值的映射{X: 1, XL: 2, XXL: 3}。
假设有三类,则使用独热向量表示为:
- 类别一: [ 1 , 0 , 0 ] [1,0,0] [1,0,0]
- 类别二: [ 0 , 1 , 0 ] [0,1,0] [0,1,0]
- 类别三: [ 0 , 0 , 1 ] [0,0,1] [0,0,1]
此时不同类别之间的编码也就没有了大小、数值上的关系。
import numpy as np
import matplotlib as plt
import pandas as pd
import keras
# 导入全连接模型
from keras.layers import Dense
from keras.utils.np_utils import to_categorical
#划分训练集和测试集
from sklearn.model_selection import train_test_split
#计算模型的评价指标,如精确率、召回率等
from sklearn.metrics import classification_report
#数据归一化函数
from sklearn.preprocessing import MinMaxScaler
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
## 1.数据预测处理
dataset = pd.read_csv("breast_cancer_data.csv")
X = dataset.iloc[:, :-1].values
y = dataset['target'].values
minMaxScaler = MinMaxScaler(feature_range=(0, 1))
x = minMaxScaler.fit_transform(X)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
# 将预测结果转换为独热编码格式
y_train = to_categorical(y_train, 2) # 2表示类别数
print(y_test)
y_test = to_categorical(y_test, 2)
print(y_test)
此时使用[1,0]
表示未患有乳腺癌,[0,1]
表示恶性肿瘤。
4.8.1模型的搭建
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import keras
# 导入全连接模型
from keras.layers import Dense
from keras.utils.np_utils import to_categorical
#划分训练集和测试集
from sklearn.model_selection import train_test_split
#计算模型的评价指标,如精确率、召回率等
from sklearn.metrics import classification_report
#数据归一化函数
from sklearn.preprocessing import MinMaxScaler
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
## 1.数据预测处理
dataset = pd.read_csv("breast_cancer_data.csv")
X = dataset.iloc[:, :-1].values
y = dataset['target'].values
minMaxScaler = MinMaxScaler(feature_range=(0, 1))
x = minMaxScaler.fit_transform(X)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2,shuffle=True,random_state=0)#指定随机种子,指定同一种子再次划分数据集时得到的划分结果相同,
# 将预测结果转换为独热编码格式
y_train = to_categorical(y_train, 2) # 2表示类别数
print(y_test)
y_test = to_categorical(y_test, 2)
print(y_test)
## 2.搭建全连接神经网络模型
model = keras.Sequential() # 使用序列模型
# 逐层添加网络结构
model.add(Dense(10,activation='relu')) #添加隐藏层,第一隐藏层有十个神经元,使用relu激活函数
model.add(Dense(10,activation='relu')) #添加隐藏层,第二隐藏层有十个神经元,使用relu激活函数
model.add(Dense(2,activation='softmax')) #添加输出层,输出层有2个神经元(因为是二分类问题),使用softmax激活函数
# 编译神经网络(指定损失函数、评价指标)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#交叉熵损失函数,优化器(梯度的下降方式)使用adam,评价指标使用准确率
history=model.fit(x_train,y_train,epochs=100,batch_size=10,validation_data=(x_test, y_test),verbose=1)#训练模型,迭代100次,每次处理10个样本,输出训练过程
model.save('全连接数据类型-乳腺癌预测.h5')#保存模型
# 绘制训练集和测试集的loss值对比
plt.plot(history.history['loss'], label='train')
plt.plot(history.history['val_loss'], label='test')
plt.title("全连接神经网络-乳腺癌预测图")
plt.legend()
plt.show()
# 绘制训练集和测试集的accuracy值对比
plt.plot(history.history['accuracy'], label='train')
plt.plot(history.history['val_accuracy'], label='test')
plt.title("全连接神经网络-乳腺癌准确率图")
plt.legend()
plt.show()
4.8.3模型的测试
创建测试文件全连接神经网络-乳腺癌预测-测试文件.py
:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import keras
# 导入全连接模型
from keras.layers import Dense
from keras.utils.np_utils import to_categorical
#划分训练集和测试集
from sklearn.model_selection import train_test_split
#计算模型的评价指标,如精确率、召回率等
from sklearn.metrics import classification_report
#数据归一化函数
from sklearn.preprocessing import MinMaxScaler
# 加载模型函数
from keras.models import load_model
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 数据预测处理
dataset = pd.read_csv("breast_cancer_data.csv")
X = dataset.iloc[:, :-1].values
y = dataset['target'].values
minMaxScaler = MinMaxScaler(feature_range=(0, 1))
x = minMaxScaler.fit_transform(X)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, shuffle=True,
random_state=0) #指定随机种子,指定同一种子再次划分数据集时得到的划分结果相同,
# 导入模型
model = load_model('全连接数据类型-乳腺癌预测.h5')
#利用训练好的模型进行测试
predict = model.predict(x_test)
y_pred = np.argmax(predict, axis=1) #返回概率较大值的下标
#将结果转化为文字
result = []
for i in range(len(y_pred)):
if y_pred[i] == 0:
result.append('良性')
else:
result.append('恶性')
print(result)
#打印模型精确度、召回率
report = classification_report(y_test, y_pred, labels=[0, 1], target_names=['良性', '恶性'])
print(report)
五、卷积神经网络
计算机视觉中深度学习算法几乎都用到了卷积神经网络来作为图像的特征提取。
5.1图像的本质:灰度图、RGB图、遥感图
1.灰度图
灰度图就是常见的黑白图像,以下是一个24*16的灰度图:
其中,每一个像素都有对应的像素值表示像素的强度,像素值大小范围为0~255
,其中0表黑色,255表白色,因此在计算机中可使用数字矩阵表示该图像:
彩色图
在表示图像时有多种不同的颜色模型,最常见的是RGB模型。该模型是一种加法颜色模型,由三个数字矩阵表示三种原色(三原色模型中原色分别为红、绿、蓝),将三种原色混合在一起后就可用于表示广泛的颜色范围。例:
图像通道,指的是构成图像的多个单独的成分或层。每个通道代表图像的一种颜色或特定的信息,这些信息可以组合在一起形成完整的图像(可见通道理解为图像的特征)。当图像的形状加载到计算机中时,像素矩阵为H×W×3。其中H是整个高度上的像素数量,W是整个宽度上的像素数,3表示通道数。常见图像通道类型:
- RGB图像通道:含有R、G、B三种图像通道,每个图像通道分别表示对应颜色的像素信息,通过将这三个通道结合在一起,可以生成全彩色图像。
- 灰度图像通道:只有灰度通道,用于表示从黑到白的不同亮度级别。
- 遥感图像通道:在遥感图像中,通常使用多个波段或频谱范围来捕捉地表特征。这些波段通常对应于不同的电磁波长,每个波段都可以看作是图像的一个通道。遥感图像。通道的选择取决于具体的应用需求,不同波段的组合可以帮助研究人员和分析师解释地表特征。常见通道类型:
- 可见光波段:人眼可见的光谱范围,通常分为红、绿、蓝(RGB)三个波段。
- 红外波段:包括近红外(NIR)和远红外(FIR)波段。近红外波段特别适合于植被和土地利用研究,因为植被在近红外波段有明显的反射特征。
- 热红外波段:用于测量地表温度,通常在热成像中使用。
- 超光谱波段:包含大量窄带波段,提供非常详细的光谱信息,用于物种识别、污染检测等高级应用。
5.2网络整体结构
与全连接神经网络相比,卷积神经网络多了卷积层、池化层,输入卷积神经网络中的数据特征图通过卷积运算和池化运算,将其中的有效特征提取出来输入到全连接层,并对数据进行分类或者预测。其中,卷积层会改变输入特征图的通道数,如上图中输入单通道卷积运算后得到三通道图像(三通道图像指图像由三种特征进行表达,并非RGB图像),也可改变图像的大小,如从30x30x1变为24x24x3等其他功能。池化层则不会改变图像的通道数,但会改变图像的大小。
上图中利用卷积层、池化层不断改变通道数、图像大小后,将从图像提取的特征信息输入到全连接层,相当于输入到全连接神经网络当中,最后将结果通过输出层输出。之所以使用卷积神经网络,而不直接使用全连接神经网络处理图像,是因为直接顺序输入图像像素值(将像素数据拉平为一维),就失去了像素之间的空间信息,如下图中狗耳朵与狗头,直接输入像素值,就会丢失狗耳朵长在狗头上的空间特征:
事实上,像图像这样的3维形状的数据中应该含有重要的空间信息。比如,空间上相邻的地方像素值应该是相似的值、RGB通道之间分别有着密切的关联性,但是相距比较远的像素之间的关联性比较低。但是全连接层会忽视形状,将全部的信息作为相同的神经元处理,因此无法利用与形状相关的信息。而卷积神经网络中的卷积层可以保持形状不变,当输入的数据是图像的时候,卷积层会以3维的数据形式接受数据,同样以3维的形式输出到下一层,因此相对于全连接神经网络,卷积神经网络可以比较好的理解空间形状的数据。
5.3卷积运算及其权重共享
卷积神经网络的核心就是存在卷积运算的卷积层,卷积运算相当于图像处理中的滤波运算,因此卷积核又被称之为滤波器。计算过程如下,其中卷积核的数值可理解为 w i w_i wi:
在全连接神经网络中,神经网络有两种参数,一种是权重参数、一种是偏置参数。在卷积神经网络中,卷积核就是卷积神经网络网络的权重参数,当然卷积神经网络中也是存在偏置参数的。如图所示,偏置参数的形状通常是一个1x1的卷积核,这个偏置的参数值会被加到通过卷积运算的所有元素上。具体如图所示:
可注意到,卷积神经网络与全连接神经网络不同,前者实现了权重共享,即卷积核中的参数并不会只针对某一像素值,而是在卷积核移动过程中与所有像素值进行运算。权重共享使得卷积神经网络参数量远少于全连接神经网络,减少过拟合的发生(参数越多,越容易引入噪声特征,越容易发生过拟合)。
5.4填充与步幅
5.4.1填充
在应用多层卷积时,我们常常丢失边缘像素。由于我们通常使用小卷积核,因此对于任何单个卷积,我们可能只会丢失几个像素。但随着我们应用许多连续卷积层,累积丢失的像素数就多了。解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素是
0
0
0)。
使用填充的主要目的是调整输出数据的大小。比如,输入的数据大小为(3,3),卷积核的大小为(2,2),进行卷积运算得出的输出数据大小为(2,2);当对输入数据进行幅度为1的填充,此时输入的数据的大小为(5,5),经过卷积运算输出的数据大小就为(4,4)。如果不填充数据的话,输入数据大小为(3,3),输出数据大小为(2,2),相当于输出数据比输入数据大小缩小了1个元素,如果在一个卷积神经网络中有多个卷积层,需要进行多次的卷积运算,如果每次进行卷积运算都会缩小空间,那么在某个时刻可能输出的大小为(1,1),这时候就无法进行卷积运算了。因此为了避免这样的情况,利用填充操作可以解决上述出现的问题,保持输出数据的空间大小不变或者变大。
5.4.2步幅
利用卷积核在输入数据上进行滑动的间隔称为步幅。上述的所有例子中的步幅都是1,这里的步幅是可以设置的,和填充的幅度一样,可以设置为大于1的整数。如下图,当步幅为2的时候进行的卷积运算的运算结果如图所示:
如上图所示,输入数据大小为(4,4),卷积核的大小为(2,2),数据填充为0,步幅的大小为2。设置步幅大小可用于高效计算或是缩减采样次数(如高分别率图像中往往存在过多冗余的像素),卷积窗口可以跳过中间位置,每次滑动多个元素。假设输入大小为(H,W),卷积核大小为(FH,FW),输出数据的大小为(OH,OW),填充为P,步幅S,则有如下公式:
上图中案例即为:
5.5多通道卷积运算
在上文的一通道图像中,往往可设置多个卷积核进行卷积操作,从而提取图像的不同特征信息。对于多通道图像,仍然可以设置多通道的卷积核进行特征提取,原理是利用多个卷积核分别对输入数据的各个通道进行卷积操作,然后将各个通道的卷积结果相加得到最终的输出。这样就可以同时提取输入数据各个通道的特征,并将它们有效地组合起来。以三通道图为例:
与单通道卷积运算相比,此处多了将不同通道的卷积运算结果进行相加,因此最终输出的特征图通道大小为1,形状为(2,2),即,经过卷积层操作后,特征图的通道数发生改变。
5.6池化运算
池化层是缩小特征图空间的运算,池化层和卷积层的不同是,特征图是需要和卷积核进行卷积运算,因此卷积层是有需要进行参数的学习的,通过前向传播确定误差,再通过反向传播进行参数的更新,从而确定最佳的卷积核与偏置值。但是池化层只是从目标区域中提取最大值或者平均值,所以是没有需要学习的参数的。
池化层一般用于在目标区域中提取像素的最大值,或者计算平均值,分别对应最大池化和平均池化两种操作。其中,最大池化是将输入的图像划分为若干个矩形区域,对每个子区域输出最大值。直觉上,这种机制能够有效地原因在于,在发现一个特征之后,它的精确位置远不及它和其他特征的相对位置的关系重要。池化层会不断地减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。通常来说,CNN的卷积层之间都会周期性地插入池化层。
池化层的步幅大小一般和池化的窗口大小一样,比如该样例中的池化窗口大小为(2,2),那么此时的步幅就设定为2,通过不断的计算一个(4,4)大小的特征图最终转变成(2,2)的计算图。当然池化层的输入特征图和输出特征图之间的关系也可以利用上述的卷积层的输入输出计算公式来计算,只不过卷积核大小变成池化窗口大小而已。
六、循环神经网络RNN
相比较图像这类数据在空间上有一定的特点外,还有一类数据是在时间上有一定的特点的,这类的数据称之为时间序列数据。从定义上来说,就是一串按时间维度索引的数据。时间序列数据有一个最重要的特征是前一段时间维度的数据对后面一段时刻的数据有着很大的影响,例如天气数据,一份天气数据中包含着每天的温度,湿度,气压、风速是否下雨等气象特征,前一段时刻的气象特征数据必然影响着后面一段时间的气象数据,因此生活当中的温度往往都是缓和的周期变化,有很明显的周期性,季节性变动。对于这样的时间序列数据,全连接神经网络和卷积神经网络都不能很好的考虑到时间序列数据之间的序列关系。因为全连接神经网络和卷积神经网络都属于前向反馈网络,模型的最终的输出和模型本身没有关联。而循环神经网络模型的前一刻时刻最终输出可以作为下一个时刻的输入从而学习到前面时刻的信息,因此循环神经网络可以很好的学习到时间序列数据中的序列关系,从而在处理时间序列数据的时候往往有很好的效果。
6.1RNN
循环神经网络中比较经典基础的神经网络是RNN神经网络,这个网络基本上阐述了循环神经网络的运行原理,其他循环神经网络,如LSTM和GRU,都是在RNN神经网络的基础上改进而来。RNN神经网络基本结构:
左图中是RNN神经网络的基本结构,不同于之前的卷积神经网络,RNN神经网络输入、输出数据表示如下:
- X t X_t Xt:表示输入的时间序列数据,输入的数据按时刻输入的,也就是数据的输入是有先后顺序的。
- h t h_t ht:表示每个时刻数据通过神经网络计算的隐藏层(使用 A A A表示,也称为运算单元,作类似普通神经网络的 w T x + b w^Tx+b wTx+b运算)的输出,同时这个输出会和下一时刻输入的数据相加,然后作为新的输入送入到神经网络中进行神经网络的计算。注意,此处的 h t h_t ht并非是输出层的输出,而是作为隐藏层的输出参加下一次的运算。
不断循环重复上述的过程直至最后一个时刻的输入数据运算结束。由于RNN神经网络是对输入的数据不断进行循环计算的,因此相对来说RNN神经网络的计算速度比较慢。将左图展开得到右图,右图即可表示RNN神经网络的真实运算流程,注意,每一隐藏层A的运算并非同时进行,而是随着不同时刻数据 X i X_i Xi的输入、上一运算单元输出运算结果,来逐一进行,有着时间上的先后顺序。即:
- 1.首先输入0时刻的数据特征 X 0 X_0 X0进行神经网络的计算得到隐藏层输出 h 0 h_0 h0 ,将这个隐藏层输出 h 0 h_0 h0和 t = 1 t=1 t=1时刻的输入 X 1 X_1 X1进行加权求和,此时1时刻的输入中包含了0时刻的数据信息。
- 2.不断循环上述过程直到t时刻结束,得到t时刻的隐藏层输出 h t h_t ht,此时的输出数据包含了不同时刻数据特征 X i X_i Xi的信息。
从理论上说,RNN的输出
h
t
h_t
ht是由
0
0
0到
t
−
1
t-1
t−1时刻的输出和
t
t
t时刻的输入决定的,因此RNN神经网络就具有记忆功能,对前面时刻的输入的数据的记忆从而影响后面时刻的输出。如下式所示为RNN神经网络最后时刻包含输出层的数学表达式:
- X t X_t Xt: t t t时刻的输入特征数据。
- U U U: X t X_t Xt对应的权重参数。
- h t − 1 h_{t-1} ht−1:前一时刻隐藏层的输出。
- W W W: h t − 1 h_{t-1} ht−1对应的权重参数。
- h t h_{t} ht:当前时刻隐藏层的输出。
- V V V: h t h_{t} ht对应的权重参数。
- O t O_t Ot:输出层输出。
注意,并非每一时刻都有
V
V
V、
O
t
O_t
Ot,二者仅表示
t
t
t时刻输出层的参数权重。将上述权重参数用图表示:
将输出层去掉,可得到
0
t
−
1
0~t-1
0 t−1时刻的RNN神经网络单元:
若将每一时刻特征数据的输入同时表示,即加上前一时刻的隐藏层输出和对应的权重,则RNN神经网络呈现出与全连接神经网络相似的形式:
从图中也可看出,RNN神经网络的输入数据并非只有一个数据(或多个数据特征,每个特征只包含一个值),而是同一数据特征不同时刻的值(也可以是多个数据特征不同时刻的值),即,
X
t
X_t
Xt仍是特征向量。
6.2权重共享
在卷积神经网络中,卷积层的卷积核就是其权重,卷积核在特征图上进行滑动,不断和特征图中的数值进行计算,因此一个特征图共享了一组权重,而不像全连接神经网络一样,每个输入的数据特征都有自己的权重参数,这样会让计算机的计算量大大提升,同时也更容易使网络模型过拟合,从而模型的泛化性不强。
观察循环神经网络的模型图(如上图),就会发现神经网络的不同时刻的计算的权重W、V、U是每个时刻共享的。因此循环神经网络的网络参数要比全连接神经网络参数要少很多,从而神经网络的参数更新的计算量要少很多,这大大减少了计算机的计算量。
6.3RNN的局限性:长期依赖问题
以预测the clouds are in the sky
最后的词sky
为例,在这样的场景中,相关的信息(the clouds are in the
)和预测的词(sky
)位置之间的间隔是非常小的,RNN可以学会使用先前的信息。但是当尝试预测I grew up in France...(中间一大段其他信息)I speak fluent French
最后的词French
时,由于相关信息(I grew up in France
)和当前预测位置(I speak fluent French
)之间的间隔非常大,而RNN会在训练过程中逐渐丧失学习到如此远信息的能力。如:
即,RNN会受到短时记忆的影响。如果一条序列足够长,那它们将很难将信息从较早的时间步传送到后面的时间步(权重是共享的,
X
0
、
X
1
X_0、X_1
X0、X1的输入信息会在传递过程中被不断弱化)。这一问题对模型的影响表现在:
- 梯度消失问题:RNN可能从一开始就会遗漏重要信息,而在反向传播(通过不断缩小误差来更新参数,从而不断去拟合真实函数曲线)时,因为权重值的更新方式为: 新的权值 = 旧权值 − 学习率 ∗ 梯度 新的权值 = 旧权值 - 学习率*梯度 新的权值=旧权值−学习率∗梯度,而梯度会随着传播到较早时间步时变得非常小,此时获得小梯度更新的层会停止学习,导致模型精度下降,即RNN只具有短期记忆。
- 梯度爆炸问题:函数求导导致。
理论推导见:
6.4RNN神经网络案例
6.4.1基本流程
h
1
h_1
h1的计算
h
1
h_1
h1是基于上一个隐藏层的状态
h
0
h_0
h0和当前的输入
x
1
x_1
x1计算而来,表达式为:
h
1
=
f
(
U
x
1
+
W
h
0
+
b
)
h_1=f(Ux_1+Wh_0+b)
h1=f(Ux1+Wh0+b),其中
x
1
x_1
x1为当前输入的特征向量,
U
U
U为其对应的权重向量,
W
W
W为
h
0
h_0
h0对应的权重,
b
b
b为偏置。其中,
f
(
)
f()
f()一般为
t
a
n
h
、
s
i
g
m
o
i
d
、
R
e
L
U
tanh、sigmoid、ReLU
tanh、sigmoid、ReLU等非线性的激活函数。
h
2
h_2
h2的计算
将上述过程进行泛化得到
h
t
=
f
(
U
x
t
+
W
h
t
−
1
+
b
)
h_t=f(Ux_t+Wh_{t-1}+b)
ht=f(Uxt+Wht−1+b),注意,RNN神经网络存在参数共享,故每一步使用的
U
、
W
、
b
U、W、b
U、W、b都是一样的(下文中的LSTM权重并不共享)。将这一过程无限持续下去,即可得到最后输出层的输出
y
4
y_4
y4:
其中
y
4
=
g
(
V
h
4
+
c
)
y_4=g(Vh_4+c)
y4=g(Vh4+c),这就是最经典的RNN结构,其输入数据为
x
1
、
x
2
、
x
3
、
.
.
.
、
x
n
{x_1、x_2、x_3、...、x_n}
x1、x2、x3、...、xn,输出数据为
y
1
、
y
2
、
y
3
、
.
.
.
、
y
n
{y_1、y_2、y_3、...、y_n}
y1、y2、y3、...、yn。
6.4.2前向传播案例
有一个RNN神经网络的网络结构是由输入层、一层隐藏层、和输出层组成。输入的数据含有3个时间步、每个时间步含有2个数据特征,因此RNN神经网络的输入层含有2个神经元、同时设定隐藏层含有2个神经元、神经网络输出层有两个输出,因此输出层含有两个神经元。如下图所示:
设定神经网络的所有权重的大小都为1且没有偏置,激活函数也都是线性激活函数。同时在计算第一个时间步的输入的时候没有前一时刻的隐藏层输出值作为输入(即,输入为0),则此时第一个时间步的计算如下:
则隐藏层第一、二个神经元的值均为:
1
x
1
+
1
x
1
+
0
x
1
+
0
x
1
=
2
1x1+1x1+0x1+0x1=2
1x1+1x1+0x1+0x1=2
输出层第一、二个神经元输出均为:
2
x
1
+
2
x
1
=
4
2x1+2x1=4
2x1+2x1=4
此时RNN神经网络隐藏层的值更新为[2,2],输入第2个时间步中参与计算:
由于每个时刻的权重值都是共享的,故时间步为2时的权重仍均为1,故隐藏层的两个神经元值为:
1
x
1
+
1
x
1
+
2
x
1
+
2
x
1
=
6
1x1+1x1+2x1+2x1=6
1x1+1x1+2x1+2x1=6
输出层的两个神经元值为:
6
x
1
+
6
x
1
=
12
6x1+6x1=12
6x1+6x1=12
同理,此时RNN神经网络的隐藏层的值更新为[6,6],输入第3个时间步参与运算:
以此类推,最终得到每个时刻的输出值为:
从这一过程中可见,每一时刻的输出结果都与上一时刻的输入有着非常大的关系,同时就算不同时刻输入的数据是一样也会导致输出不一样的。如果我们将输入序列换个顺序,那么我们得到的结果也将是截然不同,这就是RNN的特性,可以处理序列数据,同时对序列也很敏感。
6.4.3RNN神经网络反向传播的弊端
RNN神经网络虽然从理论上可以学习到任意时刻中的有用信息,但是在神经网络的反向传播进行参数更新的时候容易出现梯度爆炸和梯度消失的问题,从而导致梯度值过大或者过小,此时就可能会导致参数不能更新的情况(RNN神经网络的短期记忆特性)。数学模型公式如下:
七、循环神经网络LSTM
7.1LSTM核心思想
LSTM的核心思想是细胞状态,并使用
C
i
C_i
Ci来传输细胞状态:
细胞状态类似于传送带,直接在整个链上运行,只有一些少量的线性交互。LSTM就是依靠这条传输带来保存之前经过筛选的有用信息,并利用这些信息参与当前运算,从而使得当前时刻的输出是由之前筛选后的信息和当前的输入信息综合影响输出的。相比较RNN神经网络一股脑的将之前的所有信息都作为输入有着本质的改进。并且,LSTM有通过精心设计的称作为“门”的结构来去除或者增加信息到细胞状态的能力。
7.2遗忘门
遗忘门用于决定细胞状态会丢失的信息,其基本结构如下:
遗忘门会读取上一个(时间步)细胞的输出
h
t
−
1
h{t-1}
ht−1和当前的输入
x
t
x_t
xt并将二者作线性变换作为sigmoid激活函数的输入,得到向量
f
t
f_t
ft,该向量每一维度的值均在
[
0
,
1
]
[0,1]
[0,1]之间(1表示完全保留,0表示完全忘记),最后与上一细胞状态
C
t
−
1
C_{t-1}
Ct−1相乘。可使用下图来查看具体计算过程:
其中,
[
h
t
−
1
,
X
t
]
[h_{t-1},X_t]
[ht−1,Xt]表示将向量
h
t
−
1
h_{t-1}
ht−1与
X
t
X_t
Xt进行拼接再运算,然后计算参数矩阵$
W
f
W_f
Wf和连接后的新向量的乘积,再将这个乘积的结果和偏置
b
f
b_f
bf求和,然后经过sigmoid激活函数进行函数映射,得到向量
f
t
f_t
ft,该向量每一个元素都是在0-1之间。注意,这里的参数
W
f
W_f
Wf需要通过反向传播进从训练数据中学习。
7.3输入门
输入门控制着新输入信息的流入程度。它通过使用sigmoid激活函数将当前输入与之前的记忆状态进行组合,得到一个介于0和1之间的值。接下来,通过使用另一个tanh激活函数,将当前输入与记忆状态的组合作为新的记忆候选值。总之,遗忘门用于决定上一细胞状态、当前输入应被遗忘的部分,而输入门用于决定上一细胞状态、当前输入应被记住的部分。结构如下:
其中,
i
t
i_t
it的功能等同于遗忘门(参数矩阵不同),而
C
t
~
\tilde{C_t}
Ct~是将上一时刻细胞状态的输出与当前时刻的输入作线性变换并输入
t
a
n
h
tanh
tanh函数后得到,该向量每一维度的值均在
[
−
1
,
1
]
[-1,1]
[−1,1],用于更新细胞状态:
7.4更新细胞状态
通过更新 C t − 1 C_{t-1} Ct−1的值来更新细胞状态。
- 将细胞旧状态 C t − 1 C_{t-1} Ct−1的值与 f t f_t ft相乘,用于决定需要忘记的内容。
- 将更新后的细胞状态加上 i t ∗ C t ~ i_t*\tilde{C_t} it∗Ct~(新的细胞状态更新值)得到最新细胞状态。
7.5输出门
LSTM系统的输出由两部分组成:
将上一时间步细胞的输出及当前的输入通过sigmoid激活函数得到每一维度均在
[
0
,
1
]
[0,1]
[0,1]之间的向量
o
t
o_t
ot,由该向量决定当前细胞状态
C
t
C_t
Ct有哪些部分需要作为
h
t
h_t
ht进行输出。可注意到,此处的
h
t
h_t
ht有两个输出方向,一份是作为LSTM的输出,还有一份是作为下一个时刻的输入。
7.6参数更新
通过上面的分析可以知道LSTM一共有4个参数矩阵分别为 W f 、 W i 、 W C 、 W o W_f、W_i、W_C 、W_o Wf、Wi、WC、Wo,这些参数矩阵可以利用梯度下降法更新参数更新,从而最终得到一个LSTM模型来帮忙我们完成对应场景中的任务。