目录
1.引言
之前几篇文章简单介绍了一下神经网络的基础知识和简单应用,现在我们来学习一个十分有用的神经网络:卷积神经网络(CNN)。
2.基本结构
CNN可以简单分为五个部分,每一部分有不同的用处。
2.1 输入层
输入层的主要作用是对数据进行预处理,使其便于后续各层的处理,通常的处理步骤是:
- 均值化:使数据的平均值为0,也就是使原点成为数据的中心。
- 归一化:对数据进行缩放操作使其落在给定区间内。
- 线性降维:使用主成分分析法(PCA)对数据进行降维,减少计算量。
2.2 卷积层
CNN的名字便由这一层而来,CNN在“看”一张图片时和我们人很想,我们看一张图片,我们的大脑会将整张图片分为一个个”子图“通过识别这些小部分的特征再整合信息来确定整张图片的内容,CNN也是如此,它在这层会分块扫描整张图片,这称为局部感知,然后在全连接层将这些信息整合处理。
2.3 激励层
这一层的作用于之前讲得激励函数的作用相似,不了解激励函数的同学可以看这里 激励函数 ,就不再赘述了。注意:CNN中一般选取Relu为激励函数
2.4 池化层
池化层的主要作用就是将压缩数据,我们知道,彩色图片是由很多层叠加而成,而池化层的作用就是将图片变得更厚但是面积更小,以此减小参数量,有两种主要的池化法:
- 最大池化(MaxPooling):选取每个“子图”中的最大像素点来替代这整个“子图”。
- 平均池化(AvgPooling):计算每个“子图”的平均像素点来替代整个“子图”。
2.5 全连接层
全连接层就是之前讲的传统神经网络的结构,以线性变换为主,“子图”就是在这里整合信息变成完整图片的。
3. 数字识别
前面讲了那么多,我们来试试CNN的经典应用:图像识别。我们这里来识别的是手写数字,类似于这种:
3.1 搭建部分
这里主要讲讲神经网络的搭建部分。
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1=nn.Sequential(
nn.Conv2d(1,16,5,1,2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.conv2=nn.Sequential(
nn.Conv2d(16, 32, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.out=nn.Linear(32*7*7,10) #全连接层
def forward(self,x):
x=self.conv1(x)
x=self.conv2(x)
x=x.view(x.size(0),-1)
out=self.out(x)
return out,x
我们先建立了一个卷积网络self.conv1,首先要注意的是Conv2d的四个参数从左到右分别表示:接收的图像的层数、卷出的图像层数、“子图”的大小、分割时的移动步数、填充的格数。然后MaxPool2d这个函数就实施池化,参数表示缩小的多少,例如这里的2表示图像大小变为原来的1/2,这里我们需要计算一下现在图片的规模,层数为32,大小为7*7,这便是全连接层的输入,最后输出即可。
3.2 效果
我们看一下效果:
这是最后分类的结果,效果还是比较好的
这是最后的误差:
Epoch: 0 | train loss: 0.1272 | test accuracy: 0.99
3.3 全部代码
import 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
LR=0.001
DOWNLOAD_MNIST=True
#数据下载
if not(os.path.exists('./mnist/'))or not os.listdir('./mnist/'):
DOWNLOAD_MNIST = True
train_data=torchvision.datasets.MNIST(
root='./mnist/',
train=True,
transform=torchvision.transforms.ToTensor(),
download=DOWNLOAD_MNIST,
)
#展示下载的数据
# print(train_data.train_data.size()) # (60000, 28, 28)
# print(train_data.train_labels.size()) # (60000)
# plt.imshow(train_data.train_data[2].numpy(), cmap='gray')
# plt.title('%i' % train_data.train_labels[2])
# plt.show()
#小批量数据
train_loader=Data.DataLoader(dataset=train_data,batch_size=BATCH_SIZE,shuffle=True)
#选择部分数据训练
test_data = torchvision.datasets.MNIST(root='./mnist/', train=False)
test_x = torch.unsqueeze(test_data.test_data, dim=1).type(torch.FloatTensor)[:1000]/255. # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:1000]
#搭建网络
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1=nn.Sequential(
nn.Conv2d(1,16,5,1,2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.conv2=nn.Sequential(
nn.Conv2d(16, 32, 5, 1, 2),
nn.ReLU(),
nn.MaxPool2d(2),
)
self.out=nn.Linear(32*7*7,10) #全连接层
def forward(self,x):
x=self.conv1(x)
x=self.conv2(x)
x=x.view(x.size(0),-1)
out=self.out(x)
return out,x
cnn=CNN()
print(cnn)
#定义优化器和损失函数
optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
loss_fun=nn.CrossEntropyLoss()
#可视化
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.min(), X.max()); plt.ylim(Y.min(), Y.max()); plt.title('Visualize last layer'); plt.show(); plt.pause(0.01)
plt.ion()
for epoch in range(EPOCH):
for step,(b_x,b_y) in enumerate(train_loader):
output=cnn(b_x)[0]
loss=loss_fun(output,b_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if step % 50 == 0:
test_output, last_layer = cnn(test_x)
pred_y = torch.max(test_output, 1)[1].data.numpy()
accuracy = float((pred_y == test_y.data.numpy()).astype(int).sum()) / float(test_y.size(0))
print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy(), '| test accuracy: %.2f' % accuracy)
if HAS_SK:
# Visualization of trained flatten layer (T-SNE)
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
plot_only = 500
low_dim_embs = tsne.fit_transform(last_layer.data.numpy()[:plot_only, :])
labels = test_y.numpy()[:plot_only]
plot_with_labels(low_dim_embs, labels)
plt.ioff()
# print 10 predictions from test data
test_output, _ = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].data.numpy()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')