赞
踩
一、神经网络应用于分类问题:假设训练样本有m个,每个包含一组输入x和一组输出y,L表示神经网络层数,
S
l
S_{l}
Sl表示l层的神经元个数。神经网络分类问题有两种情况:
(1)二元分类:y只能是0或1,有且仅有一个输出单元。
(2)多类别分类:有K个不同的类,有K个输出单元,假设输出K维向量,
y
i
=
1
y_{i}=1
yi=1表示分到第i类。
多类别分类正则化后的代价函数为:
J
(
θ
)
=
−
1
m
[
∑
i
=
1
m
∑
k
=
1
k
y
k
(
i
)
l
o
g
(
h
θ
(
x
(
i
)
)
)
k
+
(
1
−
y
k
(
i
)
)
l
o
g
(
1
−
(
h
θ
(
x
(
i
)
)
)
k
)
]
+
λ
2
m
∑
l
=
1
L
−
1
∑
i
=
1
S
l
∑
j
=
1
S
l
+
1
(
Θ
j
i
(
l
)
)
2
J\left ( \theta \right )=-\frac{1}{m}[\sum_{i=1}^{m}\sum_{k=1}^{k}y_{k}^{\left ( i \right )}log\left ( h_{\theta }\left ( x^{\left ( i \right )} \right ) \right )_{k}+\left ( 1-y_{k}^{\left ( i \right )} \right )log\left ( 1-\left ( h_{\theta } \left ( x^{\left ( i \right )} \right )\right ) _{k}\right )]+\frac{\lambda }{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{S_{l}}\sum_{j=1}^{S_{l+1}}\left ( \Theta_{ji}^{\left ( l \right )}\right )^{2}
J(θ)=−m1[i=1∑mk=1∑kyk(i)log(hθ(x(i)))k+(1−yk(i))log(1−(hθ(x(i)))k)]+2mλl=1∑L−1i=1∑Slj=1∑Sl+1(Θji(l))2
其中
h
θ
(
x
)
∈
R
K
h_{\theta }\left ( x \right )\in R^{K}
hθ(x)∈RK,
(
h
θ
(
x
)
)
i
=
i
t
h
o
u
t
p
u
t
\left ( h_{\theta }\left ( x \right )\right )_{i}=i^{th}output
(hθ(x))i=ithoutput。
正则化的那一项是排除了每一层
θ
0
\theta _{0}
θ0后,每一层的θ矩阵的和。最里层的j循环循环所有的行(由
S
l
+
1
S_{l+1}
Sl+1层的激活单元数决定),循环i则循环所有的列(由该层即
S
l
S_{l}
Sl层的激活单元数决定)。
执行梯度下降法或高级优化算法时,需要为变量θ选取一些初始值,对逻辑回归来说可以初始化所有参数为0,但是神经网络不可行,会导致第二层的所有激活单元都是相同的值。可以使用随机初始化的思想,将权重随机初始化为一个接近0,范围在-ε到ε之间的数。
二、反向传播算法
采用该算法计算代价函数的偏导数
∂
∂
θ
i
j
(
l
)
J
(
θ
)
\frac{\partial }{\partial \theta _{ij}^{\left ( l \right )}}J\left ( \theta \right )
∂θij(l)∂J(θ),首先计算最后一层的误差,再一层一层反向求出各层的误差,直到倒数第二层。假设神经网络是如下的四层结构:
从最后一层的误差开始计算,用
δ
j
(
l
)
\delta _{j}^{\left ( l \right )}
δj(l)表示第l层的第j个激活单元的预测误差,则
δ
(
4
)
=
a
(
4
)
−
y
\delta^{\left ( 4 \right )}=a^{\left ( 4 \right )}-y
δ(4)=a(4)−y,利用这个误差值来计算前一层的误差:
δ
(
3
)
=
(
θ
(
3
)
)
T
δ
(
4
)
∗
g
′
(
z
(
3
)
)
\delta^{\left ( 3 \right )}=\left ( \theta ^{\left ( 3 \right )} \right )^{T}\delta^{\left ( 4 \right )}*g^{'}\left ( z^{\left ( 3 \right )} \right )
δ(3)=(θ(3))Tδ(4)∗g′(z(3))
于是假设λ=0,即不做任何正则化处理时:
∂
∂
θ
i
j
(
l
)
J
(
θ
)
=
a
j
(
l
)
δ
i
l
+
1
\frac{\partial }{\partial \theta _{ij}^{\left ( l \right )}}J\left ( \theta \right )=a_{j}^{\left ( l \right )}\delta _{i}^{l+1}
∂θij(l)∂J(θ)=aj(l)δil+1
其中l表示目前计算的是第几层,j表示目前计算层中的激活单元的下标,i表示下一层中误差单元的下标。
当训练集是一个特征矩阵,误差单元也是一个矩阵,用
Δ
i
j
(
l
)
\Delta _{ij}^{\left ( l \right )}
Δij(l)来表示这个误差矩阵,即第l层的第i个激活单元受到第j个参数影响而导致的误差。算法表示为:
三、梯度检验
对较为复杂的模型如神经网络使用梯度下降算法时,可能会存在一些不易察觉的错误,虽然代价看上去在不断减小,但最终的结果可能并不是最优解。因此采取一种叫做梯度的数值检验(Numerical Gradient Checking)方法,通过估计梯度值来检验计算的导数值是否正确。
方法是在代价函数上沿着切线的方向,选择两个非常近的点然后计算两个点的平均值用以估计梯度。即对于某个特定的θ,计算出在θ-ɛ处和θ+ɛ的代价值(ɛ是一个非常小的值,通常选取0.001),然后求两个代价的平均,用以估计在θ处的代价值。
四、神经网络的步骤
第一件事是选择网络结构,即决定选择多少层以及每层分别有多少个单元。其中第一层的单元数是训练集的特征数量,最后一层的单元数是结果的类的数量。隐藏层数大于1时,最好每个隐藏层的单元个数相同。通常情况下隐藏层的单元个数越多越好,每个隐藏层的单元数量还应该和特征数目匹配,可以和输入特征的数量相同或者是它的二倍或者三四倍。训练神经网络具体步骤如下:
1、参数的随机初始化
2、利用正向传播方法计算所有的hθ(x)
3、编写计算代价函数J的代码
4、利用反向传播方法计算所有偏导数
5、利用数值检验方法检验这些偏导数
6、使用优化算法来最小化代价函数
五、以吴恩达机器学习课程练习材料实现,训练神经网络进行多类别分类,背景是识别手写数字。在之前的文章中是直接利用训练好的参数构建神经网络进行分类,而现在任务包括利用反向传播算法来学习神经网络的参数。从零开始编写相关函数构建神经网络,代码实现可以参考:吴恩达机器学习作业Python实现(四):神经网络(反向传播)。不过尝试实现之后因为条件限制,在本人电脑上运行不起来,加上运行速度太慢了,就尝试使用pytorch框架实现。代码实现来源参考:Neural Networks Learning
原始训练数据集以matlab的数据存储格式.mat保存,数据中有5000个训练样本,其中每个训练样本是一个20像素×20像素灰度图像的数字,每个像素由一个浮点数表示,该浮点数表示该位置的灰度强度。每个20×20像素的网格被展开成一个400维的向量,得到一个5000×400矩阵X,每一行作为一个训练样本。训练集的第二部分是表示训练集标签的5000维向量y,“0”标记为“10”,而“1”到“9”按自然顺序标记为“1”到“9”。
相关函数实现代码如下:
import torch import torch.nn as nn import torch.utils.data as Data from scipy.io import loadmat import matplotlib.pyplot as plt #定义神经网络模型 class NeuralNetwork(nn.Module): #继承nn.Module类 def __init__(self): super(NeuralNetwork,self).__init__() #调用父类Module的构造函数 self.linear1=nn.Linear(400,25) #设置隐藏层,定义输入输出维度 self.sigmoid=nn.Sigmoid() #引入Sigmoid函数 self.linear2=nn.Linear(25,10) #设置输出层 def forward(self,X): #前向传播函数 X=self.linear1(X) X=self.sigmoid(X) X=self.linear2(X) out=self.sigmoid(X) return out #使用均匀分布初始化参数 def InitParameters(net,epsilon_init): for m in net.modules(): if isinstance(m,nn.Linear): weight_shape=m.weight.data.shape bias_shape=m.bias.data.shape m.weight.data=torch.rand(weight_shape)*2*epsilon_init-epsilon_init #随机初始化参数范围为[-epsilon_init,epsilon_init] m.bias.data=torch.rand(bias_shape)*2*epsilon_init-epsilon_init #正则化 def add_regular_item(net,m,lm): for module in net.modules(): if isinstance(module,nn.Linear): module.weight.grad.data.add_(lm/m*module.weight.data) #添加梯度正则化项λθ/m #训练模型 def train(net,num_epochs,train_iter,loss,optim,lm,print_frequence): for epoch in range(num_epochs): n_batch,sum_loss,m=0,0.0,0 for X,y in train_iter: optim.zero_grad() #把梯度置零,也就是把loss关于weight的导数变成0 m=y.shape[0] y_hat=net(X) #前向传播求出预测的值 l=loss(y_hat,y.view(-1,y.shape[1])).sum() #计算损失 l.backward() #反向传播求梯度 add_regular_item(net,m,lm) #正则化 optim.step() #更新所有参数 sum_loss+=l n_batch+=1 if print_frequence>0: if (epoch+1)%print_frequence==0: print("epoch:%d,average loss:%f"%(epoch+1,sum_loss/n_batch)) print("epoch:%d,train accuracy:%0.2f%%\n"%(epoch+1,evaluate(net,train_iter))) #计算准确率 def evaluate(net,test_iter): sum_accurate,num_data=0.0,0 for X,y in test_iter: y=y.view(-1,y.shape[1]) #获得实际值 num_data+=y.shape[0] #总的样本数量 y_hat=net(X) #获得预测值 sum_accurate+=(y_hat.argmax(1)==y.argmax(1)).sum() #获得预测正确的数量 return 100*sum_accurate.float()/num_data
数据加载及初始化并训练神经网络的代码如下:
train_file_data=loadmat('multi_class_cl.mat') #读取matlab格式的数据集 X=torch.tensor(train_file_data['X'],dtype=torch.float) #获得特征数据并生成相应的torch.FloatTensor y=torch.tensor(train_file_data['y'],dtype=torch.long)-1 #获得分类数据并生成相应的torch.LongTensor,将类别数据从0开始分类,原本1-9是类别1-9,10是类别0,更改后0-8是类别1-9,9是类别0 y=torch.zeros(y.shape[0],max(y)+1).scatter_(dim=1,index=y,value=1) #把y中每个类别转化为一个向量,对应的类别在向量对应位置上置为1 #print(X.shape) #print(y.shape) #定义必要的变量并训练网络 net=NeuralNetwork() #定义神经网络模型 batch_size,num_epochs,print_frequency,lr,lm=250,1200,50,0.5,0 #batch_size是更新内部模型参数之前要处理的样本数,num_epochs定义了学习算法在整个训练数据集中的工作次数,lm是正则化参数 loss=nn.BCELoss() #定义损失函数 InitParameters(net,0.12) #初始化参数 optim=torch.optim.SGD(net.parameters(),lr) #采用SGD优化方法构造优化器,lr是学习率 train_dataset=Data.TensorDataset(X,y) #加载数据 train_iter=Data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True) #数据加载器,把训练数据分成多个小组,每次抽出batch_size个样本,shuffle在每个epoch开始的时候,对数据进行重新打乱 train(net,num_epochs,train_iter,loss,optim,lm,print_frequency) #训练模型
当batch_size=250,num_epochs=1200,lm=0时,在所有初始化参数不变的情况下,运行两次模型代码,发现准确率分别为96.58%、96.16%,可以看到参数的随机初始化是会影响结果的,但影响不大。
使用正则化,令lm=1,此时准确率为66.50%,可以看到正则化对模型预测效果影响挺大的。
lm=1时,控制batch_size=250,增加学习算法在整个训练数据集中的工作次数,令num_epochs=1500,此时准确率为67.50%;控制num_epochs=1200,增加更新内部模型参数之前要处理的样本数,令batch_size=300,此时准确率为74.26%。相比之下,batch_size对模型的影响似乎更大。
可视化隐藏层的代码如下:
#可视化隐藏层
def plot_hidden(weight):
fig,ax_array = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(6,6)) #把父图分成5*5个子图,设置图的大小,True设置x和y轴属性在所有子图中共享
for r in range(5):
for c in range(5):
ax_array[r, c].matshow(weight[r * 5 + c].reshape(20, 20), cmap='gray_r') #绘制数字图像,cmap设置绘制风格为白底黑字
plt.xticks([]) #去除刻度,保证美观
plt.yticks([])
plt.show()
params = list(net.named_parameters()) #获得神经网络参数
plot_hidden(params[0][1].data) #获得隐藏层参数并可视化
(结语个人日记:不知不觉是六月了噢,依旧无法返校,没有办法见到想见的人。不过开心的是这周导师终于确定下来了,而且是做选择之后一开始就想要的导师。上学期末发了一封邮件但是没有实质进展,疫情期间纠结着开学再联系老师,最后还是在某个冲动的一天整理了一天的简历,头铁给老师发过去了,至少再刷点存在感同时也让老师了解一下自己吖。没想到老师竟然同意了哈哈哈,意料之外惊喜之中。可能有时候真得不能纠结想太多,付出行动才能有机会推动事情的进展(手动捂脸))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。