深度神经网络
前面我们介绍到多层感知机的模型,多层感知机通过增加隐藏层和数据激活的方法,将原来线性不可分的问题得到处理,然后利用了一个简单的神经网络的模型,实现了一个手写数字数据集的分类的问题。但是简单的三层网络对于数据分类的结果似乎并不尽如人意,如前面的三层的神经网络仅仅可以将数据集的分类结果提升到84% 左右。这与现在网络的主流结果是相差甚远的。
那么一个非常直观的想法就是,既然增加网络的隐藏层可以提升网络的非线性程度,那么如果我们用两个隐藏层、三个隐藏层、甚至多个隐藏层来处理这类数据的划分问题效果会不会更好呢?那么到这里实际上我们就和很多的研究者想到了相同的问题上,结果也表明增加隐藏层的数量似乎确实可以提高分类的效果。
这也就是我们马上要提到的深度神经网络(Deep Neural Network, DNN),那么深度神经网络与多层感知机有什么差异呢?我觉得主要体现在两个方面的差异,一个是“深”,这就体现在网络的隐藏层数更多,可以躲到几十层上百层,另一方面就是“广”,深度神经网络与多层感知机网络是一种包含关系,DNN 不仅仅包含多层感知机,还包含卷积神经网络、循环神经网络等,是一个更加广泛的概念,而感知机则更加狭隘。
下面的代码展示了使用更多层的感知机模型和简单的CNN模型实现MNIST的分类过程,结果大约可以达到95% 左右,这已经是一个不错的结果了。这里我们用pytorch 框架来实现更加复杂的网络,而不是像前面那样纯手撕代码,会为我们创建模型带来很多的方便。有关于CNN的介绍我会在后面介绍
# -*- coding: utf-8 -*-
# @Time : 2021/9/12 14:36
# @Author : Zhou
# @FileName: DNN_MNIST.py
# @Software: PyCharm
import torch
import torchvision
from torch.utils.data import DataLoader
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
n_epochs = 3
batch_size_train = 64
batch_size_test = 1000
learning_rate = 0.15
momentum = 0.5
log_interval = 10
random_seed = 1
torch.manual_seed(random_seed)
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size_test, shuffle=True)
# 简单的CNN网络
class DNN_CNN(nn.Module):
def __init__(self):
super(DNN_CNN,self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x)
# 纯多层感知机
class DNN_NET(nn.Module):
def __init__(self):
super(DNN_NET, self).__init__()
self.fc1 = nn.Linear(784,256)
self.fc2 = nn.Linear(256,256)
self.fc3 = nn.Linear(256,64)
self.out = nn.Linear(64,10)
def forward(self,x):
x = x.view(-1, 784)
x = F.sigmoid(self.fc1(x))
x = F.sigmoid(self.fc2(x)) #去掉这一层结果可能会升高
x = F.sigmoid(self.fc3(x))
x = F.log_softmax(self.out(x))
return x
network = DNN_NET()
optimizer = optim.SGD(network.parameters(), lr=learning_rate,momentum=momentum)
train_losses = []
train_counter = []
test_losses = []
test_counter = [i*len(train_loader.dataset) for i in range(n_epochs + 1)]
def train(epoch):
network.train()
for batch_idx, (data, target) in enumerate(train_loader):
optimizer.zero_grad()
output = network(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
train_losses.append(loss.item())
train_counter.append((batch_idx*64) + ((epoch-1)*len(train_loader.dataset)))
torch.save(network.state_dict(), './model.pth')
torch.save(optimizer.state_dict(), './optimizer.pth')
def test():
network.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
output = network(data)
test_loss += F.nll_loss(output, target, size_average=False).item()
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).sum()
test_loss /= len(test_loader.dataset)
test_losses.append(test_loss)
print('\nTest set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
test()
for epoch in range(1, n_epochs + 1):
train(epoch)
test()
import matplotlib.pyplot as plt
fig = plt.figure()
plt.plot(train_counter, train_losses, color='blue')
plt.scatter(test_counter, test_losses, color='red')
plt.legend(['Train Loss', 'Test Loss'], loc='upper right')
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likelihood loss')
plt.show()
结果展示
这里有一个问题在于,是不是单纯的增加隐藏层的数量和隐藏神经元的个数就可以提高学习的效果呢,这个倒也不一定,例如尝试去掉第二个全连接层没分类的结果可能会升高。
参考:
https://zhuanlan.zhihu.com/p/137571225