深度学习(二):卷积神经网络(上)
一、基本原理
卷积神经网络(convolutional neural network, CNN)是一种具有局部连接、权重共享等特性的深层前馈神经网络。其启发来源于生物学上的感受野(receptive field),一般由卷积层、池化层和全连接层组成。在图像处理问题上,普通全连接神经网络参数过多且不能提取局部不变特性,卷积神经网络则克服了这些缺点,取得了很好的效果。
卷积层依靠 滤波器(filter)(又称卷积核(convolution kernel) )进行特征提取,可将卷积核看做扫描器,通过扫描输入数据来提取特征,一般卷积核大小为3x3和5x5。假设输入输入矩阵大小为,卷积核大小为,步长stride为,零填充(zero padding)为,则经过一轮卷积后,输出大小为。卷积的基本过程如下动图所示:
卷积层的三维示意
卷积层可以显著减少网络中连接的数量,但特征映射数组中神经元个数并未显著减少,如果直接接入分类器,仍然易出现过拟合。为解决这个问题,在卷积层后加入池化层,以降低特征维数,避免过拟合。常用池化有两种:最大池化(max pooling),即取一个区域最大值;平均池化(mean pooling),即取一个区域平均值。通过池化,原始数据的大小被压缩,特征也减少。常用的池化层大小为2x2。
一个典型的卷积网络是由卷积层、汇聚层、全连接层交叉堆叠而成。在卷积网络中,参数为卷积核中权重以及偏置。和全连接前馈网络类似,卷 积网络也可以通过误差反向传播算法来进行参数学习。在全连接前馈神经网络中,梯度主要通过每一层的误差项进行反向传播, 并进一步计算每层参数的梯度。在卷积神经网络中,主要有两种不同功能的神经层:卷积层和汇聚层。而 参数为卷积核以及偏置,因此只需要计算卷积层中参数的梯度。
典型的卷积网络结构二、基本实现
下面通过pytorch对手写体数字识别的例子来实现卷积神经网络的过程。
- 库的导入以及数据的下载。采用MNIST数据集,该数据集中,训练集有6万张图片,测试集有1万张图片,使用torchvision可以非常简便地下载和读取数据。
import torch
import torchvision
import torch.utils.data
import torchvision.transforms as transforms
from sklearn.manifold import TSNE
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
batch_size = 100
train_dataset = torchvision.datasets.MNIST(root='Data', train=True, transform = transforms.ToTensor(), download = True)
test_dataset = torchvision.datasets.MNIST(root='Data', train=False, transform = transforms.ToTensor(), download = False)
train_loader = torch.utils.data.DataLoader(dataset = train_dataset, batch_size = batch_size, shuffle = True)
test_loader = torch.utils.data.DataLoader(dataset = test_dataset, batch_size = batch_size, shuffle = True)
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
- 函数结构的设计。用于图像处理的卷积神经网络一般分为两个部分:特征提取和特征利用。特征提取由卷积层完成,经过池化层来避免特征的简单重复,最后全连接层利用特征运算得到目标结果。其详细结构参数如下图和代码所示:
class Net(torch.nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = torch.nn.Sequential(
torch.nn.Conv2d(1,64,kernel_size=3,padding=1),
torch.nn.ReLU(),
torch.nn.Conv2d(64,128,kernel_size=3,padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(stride=2,kernel_size=2))
self.dense = torch.nn.Sequential(
torch.nn.Linear(128*14*14,1024),
torch.nn.ReLU(),
torch.nn.Dropout(p=0.5),
torch.nn.Linear(1024,10))
def forward(self,x):
x = self.conv1(x)
x = x.view(-1,128*14*14)
x = self.dense(x)
return x
- 网络训练过程。损失函数采用交叉熵,优化方法采用Adam,共迭代5次,每次中由于batch_size为100,故需循环600次才能完成训练,每循环100次,输出一下结果。
net = Net().to(device)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(),lr=0.001)
num_epochs = 5
for epoch in range(num_epochs):
for idx,(images,labels) in enumerate(train_loader):
images = images.to(device)
labels = labels.to(device)
#print(images.shape)
preds = net(images)
loss = criterion(preds,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if idx % 100 == 0:
print('epoch:{}, batch:{}, loss:{}'.format(epoch,idx,loss))
下图为训练过程的不同阶段的结果,从中可以一窥卷积作用的过程。
原始图片 经过一层卷积 经过一层卷积和一个激活层 经过两层卷积和两个激活层 再经过一个池化层- 测试过程。训练好的结构在10000张测试图上的表现为正确率99.07 %,这充分说明了卷积神经网络的有效性。
with torch.no_grad():
correct = 0
total = 0
for images, labels in test_loader:
images = images.to(device)
labels = labels.to(device)
outputs = net(images)
predicted = torch.argmax(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Test Accuracy of the model on the 10000 test images: {} %'.format(100 * correct / total))
Test Accuracy of the model on the 10000 test images: 99.07 %。
- 结果可视化。为了更加形象说明分类的效果,通过t-SNE降维方法绘制了其可视化结果。
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)
#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(outputs.data.cpu().numpy())[:plot_only,:]
plot_labels = labels.cpu().numpy()[:plot_only]
plot_with_labels(low_dim_embs,plot_labels)
分类结果可视化
四、问题探讨
图像的上采样与下采样
缩小图像(或称为下采样(subsampled)或降采样(downsampled))的主要目的有两个:1、使得图像符合显示区域的大小;2、生成对应图像的缩略图。
放大图像(或称为上采样(upsampling)或图像插值(interpolating))的主要目的是放大原图像,从而可以显示在更高分辨率的显示设备上。
下采样原理:对于一幅图像I尺寸为M,对其进行s倍下采样,即得到(M/s)尺寸的得分辨率图像,即把原始图像s*s窗口内的图像变成一个像素,这个像素点的值就是窗口内所有像素的均值或最大值。
上采样原理:图像放大几乎都是采用内插值方法,即在原有图像像素的基础上在像素点之间采用合适的插值算法插入新的元素。插值方式有很多种,如最近邻插值,双线性插值,均值插值,中值插值等方法。
参考资料
[1] Vishnu Subramanian. Deep Learning with PyTorch. Packet Publishing. 2018.
[2] 邱锡鹏 著,神经网络与深度学习. https://nndl.github.io/ 2019.
[3] 肖智清 著,神经网络与PyTorch实战. 北京:机械工业出版社. 2018.
[4] 唐进民 编著,深度学习之PyTorch实战计算机视觉. 北京:电子工业出版社. 2018.
[5] Ian Goodfellow 等 著, 赵申剑等 译, 深度学习. 北京:人民邮电出版社, 2017.
[6] https://blog.csdn.net/stf1065716904/article/details/78450997
丈夫只手把吴钩,意气高于百尺楼。——李鸿章《入都十首·其一》