文章目录
卷积神经网络(CNN)
上次面对手写数字的识别问题用的是全连接网络解决的,具体做法是将图片拉成一条直线作为一个向量送入网络。这种方法简单粗暴自然会有一些问题,一方面将图片拉成一条直线损失了图片各点的空间信息,另一方面面对一张图片我们通常会更关注局部的特征。
看下面这张图我们只需要看这只猫的头部,然后通过我们脑中的先验知识就能判断出这是一只“鸟”,
如果我们想要正确辨识出这只猫那么可能要把它另一只不明显的眼睛找出来。这个过程中实际上不需要图片的全部信息,我们会更加关注图上的黑色区域。那么为了对应这种情况我们需要对原来的网络做出限制。
上面就是一个CNN的执行流程,不过这图其实很抽象至少我是没有看懂,对于CNN抓住两个重点:松散链接和共享权值。不过在哪之前先了解一下CNN的关键部件:卷积核(滤波器)
中间那个3x3的小矩阵就是一个卷积核,他会映射在input上从左至右、从上至下地进行滚动,同时对映射的区域(也叫做感受野,receptive field)进行数乘并加和。具体如何滚动可以看这张动图。
这样我们就避开了全连接,每次我们都只会看处于同一个感受野中的数据。而且使用卷积核滚动过整个矩阵时,卷积核内的数值是不会发生变动的也就是说所有的感受野共用一个权值。
经过卷积之后我们所产生的output叫做feature map(特征图),我们能通过设计多个不同的卷积核去提取图中不同的特征
图中还有一个部分叫做下采样,通常我们使用的方式是Max Pooling(最大池化)
这东西实际上在图片压缩中挺常见的,比如我们看缩略图(比如动物图片)的时候其实不影响我们去判别这张图里的内容。不过这一步在CNN中并不是必要的,比如大名鼎鼎的AlphaGo,对于围棋局势的判别如果少了一半的维度对于最后的影响可想而知,所以AlphaGo没有使用下采样。
模型
整个模型结构就长这个样子,处理的问题还是手写数字的识别
import torch
import torch.nn.functional as F
import pandas as pd
import numpy as np
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
batch_size=64
transform=transforms.Compose([
#转成张量
transforms.ToTensor(),
#转成0-1分布,标准化
transforms.Normalize((0.1307,),(0.3081,))#前者是样本均值,后者是方差,这里直接用具体数值是前人做好了的
])#
train_dataset=datasets.MNIST(root='../dataset/mnist/',
train=True,
download=True,
transform=transform)
train_loader=DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset=datasets.MNIST(root='../dataset/mnist/',
train=False,
download=True,
transform=transform)
test_loader=DataLoader(train_dataset,
shuffle=False,
batch_size=batch_size)
class Model(torch.nn.Module):
def __init__(self):
super(Model,self).__init__()
self.conv1=torch.nn.Conv2d(1,10,kernel_size=5)
self.conv2=torch.nn.Conv2d(10,20,kernel_size=5)
self.pooling=torch.nn.MaxPool2d(2)
self.fc=torch.nn.Linear(320,10)
def forward(self,x):
batch_size=x.size(0)
print(x.shape)
x=F.relu(self.pooling(self.conv1(x)))
x=F.relu(self.pooling(self.conv2(x)))
print(x.shape)
x=x.view(batch_size,-1)
x=self.fc(x)
return x
model =Model()
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.5)
def train(epoch):
running_loss=0.0
for batch_idx,data in enumerate(train_loader,0):
inputs,target=data
inputs,target=inputs.to(device),target.to(device)
optimizer.zero_grad()
outputs=model(inputs)
loss=criterion(outputs,target)
loss.backward()
optimizer.step()
running_loss+=loss.item()
if batch_idx%300==299:
print('[%d,%5d] loss:%.3f'%(epoch+1,batch_idx+1,running_loss/300))
running_loss=0.0
def test():
correct=0
total=0
with torch.no_grad():
for data in test_loader:
images,label=data
images,label=images.to(device),label.to(device)
outputs=model(images)
_,predicted=torch.max(outputs.data,dim=1)#找到下标
total+=label.size(0)
correct+=(predicted==label).sum().item()
acc= 100*correct/total
print('Accuracy on test set:%d %%'%(acc))
return acc
if __name__=='__main__':
epoch_list = []
acc_list = []
for epoch in range(10):
train(epoch)
acc = test()
epoch_list.append(epoch)
acc_list.append(acc)
plt.plot(epoch_list,acc_list)
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.show()