Pytorch 第四回:两层神经网络模型(上)
Pytorch 第四回:两层神经网络模型(上)
本次开启深度学习第四回,基于Pytorch的双层神经网络。在上回当中,我们知道采用逻辑回归模型进行分类时,要求分类的数据线性可分,否则会影响分类效果。那分类的数据不是线性可分的,能进行数据分类吗?答案是可以的。那如何进行呢,敬请观看本回内容。
本次学习,借助的平台是PyCharm 2024.1.3,python版本3.11 numpy版本是1.26.4,pytorch版本2.0.0+cu118
文章目录
理论概述
进行几个概念的介绍:
1、多层神经网络(Multi-Layer Neural Networ)
多层神经网络是由多层网络组成的,包含输入层、隐藏层和输出层。其中输入层用于接收原始数据;隐藏层位于输入层和输出层之间,用于特征提取和特征变换;输出层生成最终预测结果。多层神经网络可以学习到数据的高层次抽象特征,因此可以处理非线性可分的问题。
2、激活函数
隐藏层中含有若干个神经元。其中,下一个神经元在接受前一个神经元的信号时,需要通过激活函数。激活函数可以引入非线性特征,进而让网络具有划分非线性数据的功能。在上一回中,sigmoid函数(用于输出层或二分类问题)就是常见的激活函数,除此之外,还有ReLu(解决梯度消失问题,加速训练)、Tanh(用于输出层或二分类问题)、Softmax(多分类问题的输出层)等函数。
2、tanh函数
tanh函数的公式如下:

作为二分类的激活函数,Tanh函数大致上是Sigmoid函数的升级版,简单说几点,首先,Tanh函数的输出范围是(-1,1),而Sigmoid函数只有(0,1);其次,Tanh函数的输出是以0为中心的,可以减少学习过程的偏差;还有就是计算复杂度、梯度消失等方面也有一定优势。
话不多说,直接上代码展示。代码引用如下:
import torch
import numpy as np
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
一、数据准备
1、数据图展示
如上一回一样,先展示需要分类的数据,效果图如下所示:

从效果图可以看出,花朵是由红蓝两种颜色的点组成,数据点分布非线性。我们要完成的是两种颜色点的分类,依旧是二分类问题,但是与前一回训练数据相比,本回分类的数据显然是更复杂的。
2、数据点生成
在这里,我们需要生成两个列表x_data和y_data。其中x_data存储的是数据点的坐标,包含横纵两个方向;y_data存储的是数据点的类型。因此x_data是两维的数据,y_data是一维的数据。
实现代码如下:
num_all = 500 # 样本数量
num_part = int(num_all / 2) # 每一类的点的个数
# 坐标(横纵两个维度)、类型数据初始化
x_data = np.zeros((num_all, 2))
y_data = np.zeros((num_all, 1), dtype='uint8') # 代表类别
length = 6 #花朵长度
# 生成花朵数据点
for k in range(2):
index = range(num_part * k, num_part * (k + 1))
angle = np.linspace(k * 3.12, (k + 1) * 3.12, num_part)
radius = length * np.sin(4 * angle)
x_data[index] = np.c_[radius * np.sin(angle), radius * np.cos(angle)]
y_data[index] = k
提问:获得了数据的列表后,接着需要做的是什么呢?
3 转换成tensor类型数据
我们目前生成好的数据x_data和y_data,是无法直接参与训练的,因此需要转换成tensor类型的数据,代码如下:
x_data = torch.from_numpy(x_data).float()
y_data = torch.from_numpy(y_data).float()
到此,我们就完成了数据的准备工作。接下来就是准备需要训练的模型了。
二、模型准备
1.训练模型准备
这里我们准备了一个两层神经网络,之间的激活层选用tanh函数。
def train_network(x):
# 第一层神经网络
x1 = torch.mm(x, a1) + b1
# 激活层(PyTorch中的tanh函数)
x1 = F.tanh(x1)
# 第二层神经网络
x2 = torch.mm(x1, a2) + b2
return x2
2.模型参数准备
从模型可以看出,这里需要准备a1,b1,a2,b2四个参数,为方便使用优化器,这里我们依旧采用Parameter()初始化参数,代码如下:
a1 = nn.Parameter(torch.randn(2, 4) * 0.01)
b1 = nn.Parameter(torch.zeros(4))
a2 = nn.Parameter(torch.randn(4, 1) * 0.01)
b2 = nn.Parameter(torch.zeros(1))
3.优化器准备
为获得较好的训练结果,这里依然采用SGD优化器。学习率可设定为0.5,定义如下:
optimizer = torch.optim.SGD([a1 , a2 , b1 , b2], 0.5)
4 定义损失函数
从训练稳定性和效率上去考虑,这里选用pytorch提供的损失函数BCEWithLogitsLoss()。BCEWithLogitsLoss()函数结合了Sigmoid函数和BCE损失(Binary Cross Entropy Loss 二元交叉熵损失),有助于缓解梯度消失或爆炸的问题,因此常用于二分类问题。定义如下:
F_loss= nn.BCEWithLogitsLoss()
数据训练
1、迭代训练
此次训练数据与上一回相比,多了400组,而且训练数据非线性可分,需要训练的参数也多了一倍,因此模型的复杂度大量上升。因此,这里将迭代训练的次数设定为一万次。具体代码如下:
for i in range(10000):
# 前向传播
estimate = train_network(x_data)
loss = F_loss(estimate,y_data) # 计算loss
# 反向传播
optimizer.zero_grad() # 使用优化器将梯度归零0
loss.backward() # 计算梯度
optimizer.step() # 使用优化器更新参数
if (i + 1) % 2000 == 0:
print('epoch: {}, loss: {}'.format(i + 1, loss))
结果输出如下:
epoch: 2000, loss: 0.41392067074775696
epoch: 4000, loss: 0.4087929427623749
epoch: 6000, loss: 0.06547889858484268
epoch: 8000, loss: 0.05896587297320366
epoch: 10000, loss: 0.0562121644616127
2、训练结果展示
给大家展示一下训练的结果:

从训练结果可以看出,训练好的模型可以将红蓝两种点清晰的分割出来。
为了更好的展示分割效果,这里设计有界面分割函数,代码如下:
def plot_boundary(function, x, y):
# 找到图的边界点
x_left, x_right = x[:, 0].min() - 1, x[:, 0].max() + 1
y_down, y_up = x[:, 1].min() - 1, x[:, 1].max() + 1
# 设定填充点的间隔
gap = 0.02
# 生成网格点
mesh_x, mesh_y = np.meshgrid(np.arange(x_left, x_right, gap), np.arange(y_down, y_up, gap))
# 模型预测网格的函数值
y_predict = function(np.c_[mesh_x.ravel(), mesh_y.ravel()])
y_predict = y_predict.reshape(mesh_x.shape)
# 结果展示
# 绘制等高线
plt.contourf(mesh_x, mesh_y, y_predict, cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(x[:, 0], x[:, 1], c=y.reshape(-1), s=40, cmap=plt.cm.Spectral)
根据这个边界函数要求,我们还需要创建一个function函数,用来给出数据类型的预测值。function函数代码如下
def plot_classify(x_data):
# 转换成tensor
x_data = torch.from_numpy(x_data).float()
# 前向计算
y_forward =train_network(x_data)
#sigmoid 计算
y_predict = F.sigmoid(y_forward)
# 二分类
y_classify = y_predict.ge(0.5)
return y_classify.numpy()
完成上述两个工作,就可以调用边界函数了:
plot_boundary(lambda x: plot_classify(x), x_data.numpy(), y_data.numpy())
plt.show()
总结
1、数据准备
生成500组数据,并将数据转换成tensor类型
2、模型生成
首先准备两层神经网络的模型,并定义相关参数。
接着就是定义SGD优化器
最后是定义BCEWithLogitsLoss损失函数
3、模型训练
进行10000次模型训练,并调用边界函数显示分类结果。
更多推荐
所有评论(0)