本文为 PyTorch 学习笔记,讲解高级神经网络结构。欢迎在评论区与我交流 😀
CNN
简介
卷积神经网络简写为 CNN。卷积的意思是不再对每个像素的信息做处理,而是对一小块像素区域的信息做处理。这种做法加强了图片信息的连续性,使得神经网络能够看到图形而非一个点,也加深了神经网络对图片的理解。
具体来说,如上图,神经网络的批量过滤器在图片上滚动收集信息,每次只收集一小块像素区域,然后进行整理,此时神经网络能看到边缘的图片信息,继续扫过边缘信息总结更高层的信息结构,可以画出眼睛鼻子等,再经过一次过滤,脸部信息从这些信息中被总结出来。最后放入全连接神经网络进行分类。
在每次卷积时,神经层会无意中丢失一些信息(角上的信息),池化可以解决这个问题。在每次卷积时不进行压缩,尽量保留更多的信息,把压缩的任务交给池化。能有效地提高准确性。
比较经典的结构是,输入图片,经过一层卷积层,再用最大池化处理卷积信息。然后经过一次同样的处理,将这次的信息传入两层全连接的神经层。最后再接一个分类器进行分类预测。
关于卷积神经网络的详细内容见【卷积神经网络】。
实践
我们使用 MNIST 上的数据集进行训练,构建卷积神经网络对手写数字进行识别。
首先引入我们需要的模块:
pimport os
import torch
import torch.nn as nn # 简化书写格式
import torch.utils.data as Data
import torchvision # 数据库模块
import matplotlib.pyplot as plt
然后进行超参数的设置:
EPOCH = 1 # 训练整批数据多少次, 为了节约时间只训练一次
BATCH_SIZE = 50 # mini-batch
LR = 0.001 # 学习率
DOWNLOAD_MNIST = True # 如果你已经下载好了mnist数据就写上False
从 MNIST 上下载手写数据集:
# Mnist digits dataset
if not(os.path.exists('./mnist/')) or not os.listdir('./mnist/'):
# not mnist dir or mnist is empyt dir
DOWNLOAD_MNIST = True
train_data = torchvision.datasets.MNIST(
root='./mnist/', # 保存或者提取位置
train=True, # True返回6w个训练集。如果是False则返回test_data,有1w个
# 转换PIL.Image或numpy.ndarray成orch.FloatTensor(C x H x W)
# 训练的时候归一化成[0.0, 1.0]区间,彩色图像为0-255,这里使用灰度图
transform=torchvision.transforms.ToTensor(),
download=DOWNLOAD_MNIST,# 没下载就下载, 下载了就不用再下了
)
运行程序后,可以看到正在下载数据:
可以看到数据成功下载到当前目录下:
我们绘制出 train_data
:
print(train_data.data.size()) # (60000, 28, 28)
print(train_data.targets.size()) # (60000)
# 画出train_data第一张图片
plt.imshow(train_data.data[0].numpy(), cmap='gray')
plt.title('%i' % train_data.targets[0])
plt.show()
得到第一张图片为:
使训练变成小批,图像维度为 (50, 1, 28, 28)
:
train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
创建测试数据集,为了节约时间, 我们测试时只测试前 2000 个,手动将深度压缩成 0-1 之间:
test_data = torchvision.datasets.MNIST(root='./mnist/', train=False)
test_x = torch.unsqueeze(test_data.test_data, dim=1).type(torch.FloatTensor)[:2000]/255. # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:2000]
建立 CNN 网络,定义 2 个卷积层和 1 个全连接层:
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential( # 输入维度(1, 28, 28),第一维为通道
nn.Conv2d(
in_channels=1, # 输入图像的高度,灰度图为1
out_channels=16, # 输出图像的深度,即过滤器的个数
kernel_size=5, # 过滤器size为5×5
stride=1, # 步幅
padding=2, # 设置padding,使得长宽不变
), # 输出维度(16, 28, 28)
nn.ReLU(), # 激活函数
nn.MaxPool2d(kernel_size=2), # 输出维度(16, 14, 14),长宽减小1倍
)
self.conv2 = nn.Sequential( # 输入维度(16, 14, 14)
nn.Conv2d(16, 32, 5, 1, 2), # 输出维度(32, 14, 14)
nn.ReLU(), # 激活函数
nn.MaxPool2d(2), # 输出维度(32, 7, 7),减小了1倍
)
self.out = nn.Linear(32 * 7 * 7, 10) # 全连接层, 10类
将神经网络输出的数据展开,并输出结果:
class CNN(nn.Module):
...
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x) # (batch, 32, 7, 7)
x = x.view(x.size(0), -1) # 展平多维的卷积图成 (batch_size, 32 * 7 * 7)
output = self.out(x)
return output
cnn = CNN()
print(cnn)
得到的神经网络结构为:
用优化器进行优化,这里使用 Adam 优化器:
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR) # optimize all cnn parameters
loss_func = nn.CrossEntropyLoss() # the target label is not one-hotted
使用 sklearn 进行可视化(可选):
# following function (plot_with_labels) is for visualization, can be ignored if not interested
from matplotlib import cm
try: from sklearn.manifold import TSNE; HAS_SK = True
except: HAS_SK = False; print('Please install sklearn for layer visualization')
def plot_with_labels(lowDWeights, labels):
plt.cla()
X, Y = lowDWeights[:, 0], lowDWeights[:, 1]
for x, y, s in zip(X, Y, labels):
c = cm.rainbow(int(255 * s / 9)); plt.text(x, y, s, backgroundcolor=c, fontsize=9)
plt.xlim(X