之前写过一篇 Pytorch:一个简单的神经网络——分类 ,通过定义一个网络类的方式来构建神经网络模型
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.predict = torch.nn.Linear(n_hidden, n_output) # 输出层线性输出
def forward(self, x):
x = F.relu(self.hidden(x)) # 激励函数(隐藏层的线性值)
y = F.softmax(self.predict(x), dim=-1) # 输出值
return y
# 声明一个网络
net1 = Net(n_feature=2, n_hidden=10, n_output=2)
这样写很不简洁,在Pytorch中封装了一个torch.nn.Sequential()
方法来快速构造一个神经网络
net2 = torch.nn.Sequential(
torch.nn.Linear(2, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 2),
torch.nn.Softmax(dim=-1)
)
通过print()观察一下这两个网络
print(net1)
"""
Net(
(hidden): Linear(in_features=2, out_features=10, bias=True)
(predict): Linear(in_features=10, out_features=2, bias=True)
)
"""
print(net2)
"""
(Sequential(
(0): Linear(in_features=2, out_features=10, bias=True)
(1): ReLU()
(2): Linear(in_features=10, out_features=2, bias=True)
(3): Softmax(dim=None)
)
"""
发现两个网络的结构都是相同的,很明显后者这种方式写起来简单一些。
结构倒是一样的,不过在设置相同的训练参数的情况下,两者的训练结果略微有些出入,其中原因我还没有搞清楚。
打印两者对相同数据的分类结果
print(net1(torch.tensor([2., 2.])))
# tensor([0.9182, 0.0818], grad_fn=<SoftmaxBackward>)
print(net2(torch.tensor([2., 2.])))
# tensor([0.9459, 0.0541], grad_fn=<SoftmaxBackward>)
就类别划分而言,两者的分类结果是相同的,但是在数值上存在着差别。
通过作图来观察
发现在整体上分类结果是相似的,但是在两个类别的边缘部分的数据,存在着类别归属上的些许差异。画图可参见 Matplotlib
最后附上全部代码
import torch
import torch.nn.functional as F # 激励函数都在这
import matplotlib.pyplot as plt
torch.manual_seed(0) # 为了使每次随机生成的数据都是一样的
# 生成测试数据
n_data = torch.ones(100, 2)
x0 = torch.normal(2 * n_data, 1)
y0 = torch.zeros(100)
x1 = torch.normal(-2 * n_data, 1)
y1 = torch.ones(100)
# 拼接
x = torch.cat((x0, x1), 0)
y = torch.cat((y0, y1), 0).type(torch.long)
class Net(torch.nn.Module):
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(n_feature, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
x = F.relu(self.hidden(x))
y = F.softmax(self.predict(x), dim=-1)
return y
if __name__ == '__main__':
net1 = Net(n_feature=2, n_hidden=10, n_output=2) # 这是我们用这种方式搭建的 net1
net2 = torch.nn.Sequential(
torch.nn.Linear(2, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 2),
torch.nn.Softmax(dim=-1)
)
print(net1)
"""
Net(
(hidden): Linear(in_features=2, out_features=10, bias=True)
(predict): Linear(in_features=10, out_features=2, bias=True)
)
"""
print(net2)
"""
(Sequential(
(0): Linear(in_features=2, out_features=10, bias=True)
(1): ReLU()
(2): Linear(in_features=10, out_features=2, bias=True)
(3): Softmax(dim=None)
)
"""
# optimizer 是训练的工具
optimizer1 = torch.optim.SGD(net1.parameters(), lr=0.02)
optimizer2 = torch.optim.SGD(net2.parameters(), lr=0.02)
# 传入 net 的所有参数, 学习率
loss_func = torch.nn.CrossEntropyLoss()
# 误差的计算方式,这里选用的交叉熵损失
# 交叉熵损失是分类中常用的一种损失函数,表示数据的不确定程度,大概可以这么理解,越合理的分类结果(视觉上就是成堆聚在一块的分成一类),其交叉熵值越小,反之,则越大
for t in range(100):
res = net1(x) # 喂给 net 训练数据 x, 输出分析值
loss = loss_func(res, y) # 计算两者的误差
optimizer1.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer1.step() # 将参数更新值施加到 net 的 parameters 上
for t in range(100):
res = net2(x) # 喂给 net 训练数据 x, 输出分析值
loss = loss_func(res, y) # 计算两者的误差
optimizer2.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer2.step() # 将参数更新值施加到 net 的 parameters 上
prediction1 = torch.max(net1(x), 1)[1]
prediction2 = torch.max(net2(x), 1)[1]
plt.figure(figsize=(8,4))
plt.subplot(121)
plt.scatter(x[:, 0], x[:, 1], c=prediction1, lw=0, cmap='RdYlGn')
plt.subplot(122)
plt.scatter(x[:, 0], x[:, 1], c=prediction2, lw=0, cmap='RdYlGn')
print(net1(torch.tensor([2., 2.])))
print(net2(torch.tensor([2., 2.])))
plt.show()