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次模型训练,并调用边界函数显示分类结果。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐