访问本站观看效果更佳
写在前面
经过前面三节基础课程,我们可以来一些更加复杂的内容了,今天我们一起来看一个简单的神经网络是如何构成的,并仔细看看神经网络与之前的逻辑回归等课程有什么区别。完整代码参见feedforward_neural_network
概念
前面和大家讨论了线性分类器。但既然是线性的分类器,自然没有办法处理一些非线性结构,比如下图:
解决办法是多层神经网络,底层神经元的输出是高层神经元的输入。我们可以在中间横着画一条线,再竖着画一条线。每画一条线就是使用了一个神经元。大家还记不记得上一节我们讲到的利用10
个sigmod
函数加上一个线性结构对手写数字进行识别?如果我们把这些神经元的输出当作输入,后面再连接一个神经元就能达到很强的分类能力。原理性的知识点这里不多做赘述,我们直接来讲怎么做吧!
目标以及思路
为了和先前的实验进行对比,本节我们依然打算使用mnist
数据集,进行手写目标分类。这样有一个好处,数据的加载以及整体的框架都不需要大的改动,我们可以集中精力把思路放在修改模型上。之前我们只使用了一个nn.Linear
作为model
,现在我们把它改掉,数据的接口都不用变,是不是很方便?
网络结构设计
复杂的事物往往是简单事物的叠加。我们前面提到系统表达能力不足是因为线性的model
限制了表达复杂模型的能力。我们需要把模型变成非线性结构。非线性结构如何来实现呢?这里我们只需要做出一个微小
的改动,增加一层激活函数。从图形上看,输入和输出无法再用一个线性的公式加以表达。有兴趣的同学可以去看看deep learning
的相关书籍。我们重点看看网络设计。
# Fully connected neural network with one hidden layer
class NeuralNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(NeuralNet, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out
仔细观察我们的改动非常简单,像一个三明治一样,两个nn.Linear
夹住了一个nn.ReLU
。其中线性整流函数(Rectified Linear Unit, ReLU),又称修正线性单元, 是一种人工神经网络中常用的激活函数(activation function),通常指代以斜坡函数及其变种为代表的非线性函数。 相比于传统的神经网络激活函数,诸如逻辑函数(Logistic sigmoid)和tanh等双曲函数,线性整流函数有着以下几方面的优势:
*更加有效率的梯度下降法以及反向传播:避免了梯度爆炸和梯度消失问题
*简化计算过程:没有了其他复杂激活函数中诸如指数函数的影响;同时活跃度的分散性使得神经网络整体计算成本下降
具体实现
为精简篇幅,这里我仅仅展示一部分操作:
首先现在可以用用GPU了,怎么用呢?只需要定义device
,然后告诉数据是把数据放到device
里计算还是放到numpy
里计算啊~
# Device configuration
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
反正都是套路,写上就对了。
接着我们要问一个问题了,nn.Linear
的输入参数与数据有关,我们可以不去考虑,那么hidden_size
设置多少好呢?我们设置超参数如下:
# Hyper-parameters
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001
其实超参数的设置可能就是要多试试了,当然调参也是有技巧的。跑个几轮看看loss变化幅度,自己估计一下,多试几次就行。
打印一下网络结构:
NeuralNet(
(fc1): Linear(in_features=784, out_features=500, bias=True)
(relu): ReLU()
(fc2): Linear(in_features=500, out_features=10, bias=True)
)
损失函数以及优化器:
# Loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
结果如下:
Epoch [1/5], Step [100/600], Loss: 0.2624
Epoch [1/5], Step [200/600], Loss: 0.3268
Epoch [1/5], Step [300/600], Loss: 0.2965
Epoch [1/5], Step [400/600], Loss: 0.1089
Epoch [1/5], Step [500/600], Loss: 0.0886
Epoch [1/5], Step [600/600], Loss: 0.3102
Epoch [2/5], Step [100/600], Loss: 0.1234
Epoch [2/5], Step [200/600], Loss: 0.0823
……………………………………………………………………………………………………………
Epoch [4/5], Step [400/600], Loss: 0.0220
Epoch [4/5], Step [500/600], Loss: 0.0245
Epoch [4/5], Step [600/600], Loss: 0.0079
Epoch [5/5], Step [100/600], Loss: 0.0318
Epoch [5/5], Step [200/600], Loss: 0.1382
Epoch [5/5], Step [300/600], Loss: 0.0397
Epoch [5/5], Step [400/600], Loss: 0.0215
Epoch [5/5], Step [500/600], Loss: 0.0479
Epoch [5/5], Step [600/600], Loss: 0.0614
Accuracy of the network on the 10000 test images: 97.89 %
小结
我们前前后后折腾了那么久,应该熟悉pytorch
的基本操作了吧?后面我们来看点复杂的东西吧!未完待续