深度学习7:全连接神经网络(详细构造)

全连接神经网络(Fully Connected Neural Network,FCNN)是由一系列全连接层组成的深度神经网络,是深度学习中的基本架构。全连接层的特点是相邻两层的任意两个神经元之间均有连接。在前面的笔记中我们已经举了一个简单的二分类实例,同时也介绍了构造全连接神经网络的基本步骤,这里不多赘述,可自行查看。


构建全连接神经网络的基本步骤:
1.读取数据集,并将数据表示为张量;(移步看其他笔记,这里不过多赘述)
2.定义神经网络层
3.对网络进行训练
4.损失函数(多分类任务:nn.CrossEntropyLoss()交叉熵函数)
详细内容可关注其他笔记,这里不再过多赘述)
5.显示损失函数值变化趋势

1. 定义神经网络层

1.1 单个神经网络层

一个全连接网络层是由nn.Linear()函数定义的,其中①in_features=m,表示该网络层有m个输入特征,②out_features=k表示该网络层有k个神经元,也代表着有k个输出特征,③bias=False表示所有神经元都不带有偏置项。

nn.Linear()函数的作用是建立一个全连接神经网络层,其中m个输入特征,k个输出特征,一共有m*k个权重。如果bias为True,则需要学习优化的参数多增加k个。

整个网络的输入节点数量可以判断整个网络的复杂程度,从整个网络的输出节点数量可以判断网络的任务。如果节点数量为1的话啊u,一般用于预测,属于回归分析;如果有2个或者2个以上的输出节点的网络一般属于分类任务。对于多分类任务来说,在进行softmax归一化以后得到概率分布,其中最大概率之对应的类就是预测的类。

nn.Linear(in_features=m,out_features=k,bias=True/False)

实例:构造4个全连接网络层(孤立)

self.fc1=nn.Linear(4,5)
self.fc2=nn.Linear(5,6)
self.fc3=nn.Linear(6,4)
self.fc4=nn.Linear(4,3)

1.2 连接各个神经网络层

一般在forward()方法中实现连接的操作步骤.

out = self.fc1(x)
out = self.fc2(out)
out = self.fc3(out)
out = self.fc4(out)

1.3 设置每一个神经元的激活函数

这里介绍几种主流的损失函数。损失函数一般分为:回归任务和分类任务。

1.3.1 nn.CrossEntropyLoss()和nn.NLLLoss()函数–分类

nn.CrossEntropyLoss()叫做交叉熵损失函数。

pre_y = [[5,9,5,5],[9,8,7,9],[7,5,6,5,]]
y = [0,1,2]
pre_y = torch.Tensor(pre_y)
y = torch.LongTensor(y)
loss = nn.CrossEntropyLoss()(pre_y,y)

nn.NLLLoss()函数称负对数似然损失函数,按照y的下标值,取pre_y中相应的元素进行相加,取平均值,求反。
nn.CrossEntropyLoss()(pre_y,y)又可以以nn.NLLLoss()函数的形式表达,代码如下。

pre_y2 = torch.softmax(pre_y,dim=1)
pre_y3 = torch.log(pre_y2)
loss = nn.NLLLoss()(pre_y3,y)

1.3.2 nn.MSELoss()–回归

nn.MSELoss()称作平均平方误差,也称均方差损失函数。主要用于回归分析。

pre_y = torch.tensor([[5,9,5,5],[9,8,7,9],[7,5,6,5,]]).float()
y = torch.tensor([[0,1,4,3],[0,3,1,4],[4,4,1,3]]).float()
loss = nn.MSELoss()(pre_y,y)

1.3.3 nn.BCELoss()和nn.BCEWithLogitsLoss()函数–二分问题

如果我们只使用nn.BCELoss(),需要先对pre_y进行归一化处理。
nn.BCEWithLogitsLoss()=torch.sigmoid()+nn.BCELoss()

pre_y = torch.tensor([[-0.396],[-0.240],[-1.196]])
y = torch.tensor([[0],[1],[1]]).float()
pre_y = torch.sigmoid(pre_y)#将pre_y归一化(0,1)中
loss = nn.BCELoss()(pre_y,y)

1.3.4 nn.L1Loss()–回归

pre_y = torch.tensor([[5,9,5,5],[9,8,7,9],[7,5,6,5,]]).float()
y = torch.tensor([[0,1,4,3],[0,3,1,4],[4,4,1,3]]).float()
loss = nn.L1Loss()(pre_y,y)

如果我们只求绝对值之和的话,也可以使用nn.L1Loss()函数实现

loss = nn.L1Loss(reduction='sum')(pre_y,y)
out = self.fc1(x)
out = torch.tanh(out)#以tanh为激活函数
out = self.fc2(out)
out = torch.relu(out)#以ReLU为激活函数
out = self.fc3(out)
out = torch.sigmoid(out)#以sigmoid为激活函数
out = self.fc4(out)

1.4 完整代码

import torch
import torch.nn as nn

#定义全连接神经网络
Class AFullNet(nn.Module):
	def__init__(self):
		super().__init__()
		self.fc1=nn.Linear(4,5)
		self.fc2=nn.Linear(5,6)
		self.fc3=nn.Linear(6,4)
		self.fc4=nn.Linear(4,3)
	def forward(self,x):
		out = self.fc1(x)
		out = torch.tanh(out)#以tanh为激活函数
		out = self.fc2(out)
		out = torch.relu(out)#以ReLU为激活函数
		out = self.fc3(out)
		out = torch.sigmoid(out)#以sigmoid为激活函数
		out = self.fc4(out)
		return out
anet = AFullNet()
sum = 0
for param in anet.parameters():
	sum+=torch.numel(param)
print('该网络参数总量:%d'%sum )                                                                                                                                                                                                                                                                                                      
		

2 训练网络模型

2.1 数据集划分

为了对模型进行 有效的训练和评估,我们通常将数据集划分为训练集和测试集。训练集用于对模型进行训练,测试集用来对训练完整的模型进行评估测试。一般情况下, 我们将训练集数据和测试集数据按照7:3或者8:2的比例进行划分。有的时候,我们会将数据划分为:训练集,验证集和测试集,验证集的作用是判断模型是否过拟合。
在PyTorch中,我们所使用的数据类型是张量,对张量进行划分,我们需要用到的是切片操作,代码如下:

rate = 0.7#划分分割比例
X,Y = torch.randn(2043,3),torch.rand(2040)#随机产生数据
train_len = int(len(X)*rate)
trainX,trainY = X[:train_len],Y[:train_len]
textX,textY = X[train_len:],Y[train_len:]

如果需要用到随机打乱数据集的话, 可以参考下面的代码:

index = torch.randperm(len(Y))#对[0,len(Y)-1]中的整数进行随机排列
X,Y = X[index],Y[index]

2.2 数据集打包

当我们使用批量梯度下降方法进行训练的时候,我们需要对数据进行打包。打包的意思就是,将数据集划分为若干个同等规模的数据批量(batch)的过程,一个batch就是一个数据包,所以我们就把划分的过程认为是打包。
我们这里介绍两种方法,第一种是利用Python语言进行切片实现,第二种使用PyTorch包来实现。

#切片
batch_size = 100#设置包的大小
train_loader=[]#设置包的容器
for i in range(0,len(trainX),batch_size):#将数据集按照batch_size进行划分
	t_trainX,t_trainY = trainX[i:i+batch_size],trainY[i:i+batch_size]
	t = (t_trainX,t_trainY)#将划分好的数据包和标记包组成元组
	train_loader.append(t)#保存元组到容器中
#PyTorch
from torch.utils.data import DataLoader,TensorDataset
train_set = TensorDataset(trainX,trainY)#假设已经有了trainX和trainY--相当于zip功能,将两个数据进行组队
train_loader = DataLoader(dataset = train_set#打包,指定加载的数据集
						  batch_size = 100#包的大小
						  shuffle = True)#默认为False,False的意思是:不打乱样本的顺序的情况下打包

这里想多介绍一下关于DataLoader()函数
dataset、batch_size、shuffle已经进行了介绍,这里不过多赘述
num_workers:使用多进程加载的进程数,0代表不使用多进程
drop_last:当样本总数不是batch_size的整数倍的时候,如果是True则将最后一个包丢弃。

2.3网络训练

网络训练是将我们之前处理好的数据输入到模型,正向计算模型的输出和目标之间误差,然后反向计算误差函数在各个参数的梯度,利用这个梯度更新参数。反复执行这个过程,直到误差足够小,停止训练。
下面展示的是伪代码,重在了解模型的步骤

optimzier = torch.optim.Adam(model.parameters(),lr=lr)
for epoch in range(epochs):
	for X,Y in train_loader:
		pre_y = model(X)
		loss = L(pre_Y,Y)
		optimizer.zero_grad()
		loss.backward()
		optimizer.step()

2.4梯度累加

首先,我们为什么要使用梯度累加?当我们模型进行训练的时候,批量太大会占用GPU显存资源,如果批量很小的话会影响模型的泛化能力,这时候我们使用梯度累加的方法来提高数据批量的大小,同时不占用GPU显存资源。
梯度累加就是在训练过程中每次迭代只计算梯度并对梯度进行累加,而对参数不进行每次的更新,当累加到一定次数后,再进行参数更新,然后梯度清零。
我们在不增加批量大小的情况下,进行了累加梯度进行参数更新,所以其实是将x变成了x*accu_steps,我们在进行这样的操作时候,应该适当的增加学习率。
这里我们依旧使用伪代码,后续会更新实例:

accu_steps = r
optimizer = torch.optim.Adam(model.parameters(),lr=lr)
for epoch in range(epochs):
	for k,(X,X) in enumertae(train_loader):
		pre_y = model(X)
		loss = L(pre_Y,Y)
		loss = loss/accu_steps
		loss.backward()
		if (k+1)%accu_steps == 0:#每accu_steps次做一次参数更新		
			optimizer.step()			
			optimizer.zero_grad()

2.5学习率衰减

通过之前的文章,我们已经知道学习率如果设置的过大,会导致我们的步长变大,容易出现震荡,不易找到高精度的解,如果当学习率设置的太小,我们收敛速度会变慢,容易造成局部最优解。基于此,我们这里提供了一种理想的做法,刚开始的时候实现较大的学习率进行快速逼近,然后逐渐降低学习率,找到高精度的学习率。
这里介绍一下torch.optim.lr_scheduler.StepLR()方法来实现
optimizer:优化器
step_size:每step_size更新一次学习率
gamma:每次更新时,学习率乘gamma,默认值为0.1

实例:从XY中选择400条数据进行训练,将学习率初始设置为0.1,然后迭代50次,让学习率乘0.9,同时保证学习率最低值不低于0.0008,并且绘制学习率的衰减曲线。

schedule = torch.optim.lr_sscheduler.StepLR(optimizer,step_size=50,gamma=0.9)
scheduler.step()#更新学习率,需要放在循环体中

完整代码:

model = Model()
optimizer = torch.optim.Adam(model.parameters().lr=0,01)
schedule = torch.optim.lr_sscheduler.StepLR(optimizer,step_size=50,gamma=0.9)
lr_list=[]
X,Y=X[:400],Y[:400]
for epoch in range(5):
	for x,y in zip(X,Y):
		x = x.unsqueeze(0)
		pre_y = model(x)
		y = torch.LongTensor([y])
		loss = nn.CrossEntropyLoss()(pre_y,y)
		optimizer.zero_grad()
		loss.backward()
		optimizer.step()
		scheduler.step()
		optimizer.param_groups[0]['lr'] = max(optimizer.param_groups[0]['lr'],0.008)
		lr_list.append(optimizer.param_groups[0]['lr'])

2.6网络测试

对于一个已经测试好的模型,为了测试他的性能,我们需要使用评价指标进行衡量。常见的评价指标有:精准率(precision,又称查准率),召回率(recall,又称查全率),准确率(accuracy)。对于分类问题,准确率是指正确预测的样本数占整个测试集样本数的比值。

model.eval()#设置为测试模式
correct = 0
with torch.no_grad():#上下文管理器,放弃梯度计算
for x,y in test_loader:
	pre_y = model(x)
	pre_y_index = torch.argmax(pre_y,dim=1)
	t = (pre_y_index == y).long().sum()#整数转换,True变为1,False变为0,求和得到被正确预测的样本个数
	correct += t
s = '测试集的预测准确率为:{:.1f}%'.format(100.*correct/len(test_Loader.dataset))
print(s)

以上,本文内容参考自蒙祖强,欧元汉编著的《深度学习理论与应用》。

  • 21
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch是一个基于Python的科学计算库,可以用于实现深度学习模型。全连接前馈神经网络是一种最简单的人工神经元模型,可以用于解决回归或分类问题。 首先,我们需要导入PyTorch库和相关模块: ```python import torch import torch.nn as nn import torch.optim as optim ``` 然后,我们需要定义一个继承自`nn.Module`的神经网络类,并在构造函数中定义网络的结构。以回归问题为例,我们可以定义一个具有两个隐藏层和一个输出层的神经网络: ```python class RegressionNet(nn.Module): def __init__(self): super(RegressionNet, self).__init__() self.fc1 = nn.Linear(输入特征数, 隐藏层1神经元数) self.fc2 = nn.Linear(隐藏层1神经元数, 隐藏层2神经元数) self.fc3 = nn.Linear(隐藏层2神经元数, 输出神经元数) def forward(self, x): x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) x = self.fc3(x) return x ``` 接下来,我们需要实例化网络类,并定义损失函数和优化器: ```python model = RegressionNet() criterion = nn.MSELoss() # 均方误差损失函数 optimizer = optim.SGD(model.parameters(), lr=学习率) ``` 然后,我们需要准备训练数据和标签,并将其转换为`torch.Tensor`类型: ```python train_data = ... train_labels = ... train_data = torch.Tensor(train_data) train_labels = torch.Tensor(train_labels) ``` 接下来,我们可以开始训练模型。循环训练模型,每次迭代中进行前向传播、计算损失、更新参数: ```python for epoch in range(迭代次数): optimizer.zero_grad() # 清空梯度 output = model(train_data) # 前向传播 loss = criterion(output, train_labels) # 计算损失 loss.backward() # 反向传播计算梯度 optimizer.step() # 更新参数 ``` 最后,我们可以使用训练好的模型进行预测。首先将测试数据转换为`torch.Tensor`类型,然后使用已训练的模型进行预测: ```python test_data = ... test_data = torch.Tensor(test_data) predictions = model(test_data) # 预测 ``` 以上就是使用PyTorch实现全连接前馈神经网络进行回归或分类问题的基本步骤。我们可以根据具体的问题调整网络架构、损失函数和优化器等,以提高模型的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值