老饼讲解:一步一步上手深度学习
好了,这节我们又试试用MLP来实现类别的预测。
类别预测和数值预测可以当成一家人,只需要在数值预测上套一个softmax就可以转为类别预测了。
说干就干,开始!
为了方便学习,我们使用简单点的数据。
如下所示,是样本的输入,是样本的类别,下面是具体数据以及平面上的展示:

好了,这也是个简单的例子,我们就简单的用4个隐神经元去拟合它就好了。
对于二分类,我们可以用一个输出,然后输出层使用sigmoid函数,让它输出"属于类别1"的概率。但是为了通用性,这里我们让它拥有两个输出,然后输出层使用softmax函数,让它"属于类别0、属于类别1"的概率。进一步地,使用交叉熵损失函数来训练模型就可以了。
好了,具体代码示例如下:
# 本代码用于展示:训练一个MLP用于类别预测
# 本代码来自《老饼讲解-深度学习》www.bbblearn.com
import torch
import matplotlib.pyplot as plt
torch.manual_seed(99)
# -----计算网络输出:前馈式计算------
def forward(w1,b1,w2,b2,x):
return w2@torch.tanh(w1@x+b1)+b2
# -----训练数据----------------------
x = torch.tensor([[2.5, 1.3, 6.2, 1.3, 5.4, 6 ,4.3, 8.2]
,[-1.2,2.5,3.6,4,3.4,2.3,7.2,3.9]]) # 样本的输入数据
y = torch.tensor([1,1,0,1,0,0,0,0]) # 样本的标签
#-----------训练模型-----------------
in_num = x.shape[0] # 输入个数
out_num = 2 # 输出个数
hn = 4 # 隐节点个数
w1 = torch.randn([hn,in_num],requires_grad=True) # 初始化输入层到隐层的权重w1
b1 = torch.randn([hn,1],requires_grad=True) # 初始化隐层的阈值b1
w2 = torch.randn([out_num,hn],requires_grad=True) # 初始化隐层到输出层的权重w2
b2 = torch.randn([out_num,1],requires_grad=True) # 初始化输出层的阈值b2
lr = 0.1 # 学习率
loss = torch.nn.CrossEntropyLoss() # 定义损失函数为交叉熵损失函数
for i in range(20000): # 训练10000步
py = forward(w1,b1,w2,b2,x) # 计算网络的输出
L = loss(py.T, y) # 计算损失函数
L.backward() # 用损失函数更新模型参数的梯度
w1.data=w1.data-lr*w1.grad # 更新模型系数w1
b1.data=b1.data-lr*b1.grad # 更新模型系数b1
w2.data=w2.data-lr*w2.grad # 更新模型系数w2
b2.data=b2.data-lr*b2.grad # 更新模型系数b2
w1.grad.zero_() # 清空w1梯度,以便下次backward
b1.grad.zero_() # 清空b1梯度,以便下次backward
w2.grad.zero_() # 清空w2梯度,以便下次backward
b2.grad.zero_() # 清空b2梯度,以便下次backward
# ----计算错误率----
p_labels = torch.argmax(py,dim=0) # 模型的预测类别
err_rate = (p_labels!=y).sum()/p_labels.numel() # 错误率
print(f'第{i}轮错误率:' ,(err_rate).numpy()*100,'%') # 打印错误率
if(err_rate<=0.01): # 检查退出条件
break
# -------打印模型训练结果----------
print('----模型训练结果---') # 打印标题
print('错误率:' ,(err_rate).numpy()*100,'%') # 打印错误率
# 绘制预测结果
colors = ['red','b'] # 定义0,1类样本的颜色为red,blue
true_color = [ colors[i] for i in y] # 各个样本真实对应的颜色
pred_color = [ colors[i] for i in p_labels] # 各个样本预测对应的颜色
plt.scatter(x[0],x[1],c=true_color,s=20) # 绘制真实样本
plt.scatter(x[0],x[1],marker='o',c='none',edgecolors=pred_color,s=80) # 绘制预测样本
plt.legend(['true label','predict label']) # 显示图例
plt.show() # 显示画布
print("\n模型参数:") # 打印模型参数
print('w1:',w1) # 打印w1
print('b1:',b1) # 打印b1
print('w2:',w2) # 打印w2
print('b2:',b2) # 打印b2运行结果如下:


可以看到,训练好的MLP已经能很好的识别样本的类别的。
其实代码与"MLP用于数值预测"里的代码基本是一样的,但我们还是简单讲解一下吧~
从第10行开始看,这里就是数据的生成以及变量、参数的初始化。
第10-13行,直接手敲输入数据,没什么好说的。
第15-17行,设置了输入层、隐层、输出层的神经元个数。这里设为2个输出,是因为有两个类别,0和1。
第18-21行,参数的初始化,这里我们简单的使用randn将它们正态分布随机初始化。
好了,下面是模型训练的代码了。
第22行设置了学习率。
第23行初始化了损失函数,这里我们用了交叉熵损失函数。
第24行开始循环迭代训练,最多训练2万步。
第26、27、28行计算模型的预测值,并计算损失函数,然后将损失值进行backward,以此更新参数的梯度。这里需要注意的是,25行的forward函数是我们第7行定义的用于计算模型输出的函数,这里我们的输出值并没有softmax,这是因为pytorch的交叉熵函数torch.nn.CrossEntropyLoss是包含了softmax的。所以我们模型只需输出判别值就可以了,CrossEntropyLoss会进行softmax后再计算交叉熵。
第28-35就是更新参数并清空梯度了。
第37-41行,将模型的判别值转换为模型的类别标签,然后计算预测类别与真实类别的错误率,如果错误率已经很低,就退出训练。
第42-58行展示了我们的训练结果。
第43、44行打印错误率。
第45-52行,画出真实类别与预测类别。
最后,54-58行打印模型的参数,w1,b1,w2,b2等。
好了,解说完了,其实也没什么好解说的,毕竟都是我们之前学过的知识。
好了,这节我们具体地看了一下,如何用MLP来做类别预测,包括它的具体代码和效果。其实类别预测与数值预测没有多大区别,就只是有多少个类别就用多少个输出,然后输出层套上了softmax,最后用交叉熵作为损失函数进行训练就可以了。
评论