这一节,用pytorch实现神经网络分类问题,再次熟悉pytorch搭建神经网络的步骤。
1. 问题的提出
分类问题是将数据划分种类的一种问题,常见的有二分类和多分类问题,这节就是做一个简单的二分类问题。
同样,我们先做一组数据。其中第一组数据的标签为0,第二组数据标签为1。
# x0,x1是数据,y0,y1是标签
n_data = torch.ones(100, 2) # 数据的基本形态
x0 = torch.normal(2*n_data, 1) # 类型0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(100) # 类型0 y data (tensor), shape=(100, )
x1 = torch.normal(-2*n_data, 1) # 类型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(100) # 类型1 y data (tensor), shape=(100, )
将两组数据进行融合,并用散点图表示出来,明显地看到可以将数据分为2类,即红绿各代表一类。
2. 神经网络模型的搭建和训练
神经网络模型的搭建和上一节回归问题基本一样,也是先搭建网络框架,再创建模型、优化器、损失函数。
要注意的是,在这个分类问题中我们输入的数据x是2维的,输出的预测值也是2维的(几分类问题输出就是几维),因此各个网络层的神经单元数要改变。
input_dim = 2
hidden_dim = 10
out_dim = 2
还有一点不同之处在于损失函数选用的是交叉熵(CrossEntropy) ,这是在做回归问题时常用的损失函数,而分类问题中最后一层的激活函数常选用的是softmax。
有关交叉熵这有一篇博客介绍的很清晰,可以看一下。
pytorch中将两者集成在了torch.nn.CrossEntropyLoss() 这个函数中了,我们直接调用就好。
loss_func = nn.CrossEntropyLoss()
3. 结果展示
同样,我们输出训练过程中的loss值图像,发现损失值是在不断减小的,也就意味着模型拟合的越来越好。
要注意的是,这时给出的prediction并不是最后的分类值,而是一个Tensor数据格式,如[0.3,0.7],表示神经网络预测的样本属于每类的概率,所以需要取这个概率的最大值来表示最后的分类。
prediction = torch.max(F.softmax(out), 1)[1]
最后的分类结果展示图:
4. 结语
代码参考了莫烦大神的教程,完整代码放在这里:
# 神经网络的搭建--分类任务 #
import torch
import matplotlib.pyplot as plt
import torch.nn.functional as F # 激励函数都在这
# x0,x1是数据,y0,y1是标签
n_data = torch.ones(100, 2) # 数据的基本形态
x0 = torch.normal(2*n_data, 1) # 类型0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(100) # 类型0 y data (tensor), shape=(100, )
x1 = torch.normal(-2*n_data, 1) # 类型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(100) # 类型1 y data (tensor), shape=(100, )
# 注意 x, y 数据的数据形式是一定要像下面一样 (torch.cat 是在合并数据)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor) # FloatTensor = 32-bit floating
y = torch.cat((y0, y1), ).type(torch.LongTensor) # LongTensor = 64-bit integer
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
plt.show()
# 建立神经网络
class Net(torch.nn.Module): # 继承 torch 的 Module
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__() # 继承 __init__ 功能
self.hidden = torch.nn.Linear(n_feature, n_hidden) # 隐藏层线性输出
self.out = torch.nn.Linear(n_hidden, n_output) # 输出层线性输出
def forward(self, x):
# 正向传播输入值, 神经网络分析出输出值
x = F.relu(self.hidden(x)) # 激励函数(隐藏层的线性值)
x = self.out(x) # 输出值, 但是这个不是预测值, 预测值还需要再另外计算
return x
net = Net(n_feature=2, n_hidden=10, n_output=2) # 几个类别就几个 output
# 训练网络
optimizer = torch.optim.SGD(net.parameters(), lr=0.02)
# 算误差的时候, 注意真实值!不是! one-hot 形式的, 而是1D Tensor, (batch,)
# 但是预测值是2D tensor (batch, n_classes)
loss_func = torch.nn.CrossEntropyLoss()
plt.ion() # 画图
plt.show()
for t in range(100):
out = net(x) # 喂给 net 训练数据 x, 输出分析值
loss = loss_func(out, y) # 计算两者的误差
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 可视化展示
if t % 2 == 0:
plt.cla()
# 过了一道 softmax 的激励函数后的最大概率才是预测值
prediction = torch.max(F.softmax(out), 1)[1]
pred_y = prediction.data.numpy().squeeze()
target_y = y.data.numpy()
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap='RdYlGn')
accuracy = sum(pred_y == target_y) / 200. # 计算准确度
plt.text(1.5, -4, 'Accuracy=%.2f' % accuracy, fontdict={'size': 20, 'color': 'red'})
plt.pause(0.1)
plt.ioff() # 停止画图
plt.show()
补充:激励函数和损失函数的选择
在神经网络中针对不同问题会用到不同的激励函数和损失函数,比如上节回归问题用到的就是relu激活函数和mse损失函数,而这节分类问题我们最后一层用到的就是softmax激活函数和crossentropy损失函数。
针对最后一层激活函数的选择和损失函数的选择,有一个图可以参考,来自这篇博客。