完成了简单的全连接网络之后,我们是用pytorch进行MNIST手写数字的分类问题,这次的网络相对复杂一下,输入输出和hidden layer都更多,表达能力更强
首先是下载MINIST的数据集:
import torch
import torch.nn as nn
from torchvision import datasets, transforms
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
from torch.autograd import Variable
import os
transform=transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.5,),(0.5,))])
#下载训练集和验证集
training_datasets=datasets.MNIST(root='./data',train=True,download=True,transform=transform)
validation_datasets=datasets.MNIST(root='./data',train=False,download=True,transform=transform)
training_loader=datsets.MNIST(datasets=training_datasets,batch_size=100,shuffle=True)
validation_loader=datsets.MNIST(datasets=validation_datasets,batch_size=100,shuffle=False)
这里注意,一定是transforms.Normalize((0.5,),(0.5,)),不是transforms.Normalize((0.5),(0.5)),一个逗号之差,数据类型会不是 tuple,然后就会报错 “IndexError: too many indices for tensor of dimension 0”
然后我们可以看一下training_loader里面的图片都长什么样子:
def im_convert(tensor):
image=tensor.clone().detach().numpy()
image=image.transpose(1,2,0) # 从 N*W*H,转成W*H*N
image=image*np.array(0.5,)+np.array(0.5) #这步是transform Normalize的反向过程
image=image.clip(0,1)
image=image.squeeze()
return image
dataiter=iter(training_loader)
images,labels=dataiter.next()
fig=plt.figure(figsize=(25,4))
# 展示20幅图
for idx in range(20):
ax=fig.add_subplot(2,10,idx+1)
plt.imshow(im_convert(images[idx]))
ax.set_title(labels[idx].item())
然后是神经网络的结构,全连接网络,有两个隐藏层
class Classifier(nn.Module):
def __init__(self,input_size,h1,h2,output_Size):
super().__init__()
self.linear1=nn.Linear(input_size,h1)
self.linear2=nn.Linear(h1,h2)
self.linear3=nn.Linear(h2,output_size)
def forward(self,x):
pred1=F.relu(self.linear1(x))
pred2=F.relu(self.linear1(pred1))
pred=self.linear1(pred2)
#最后一层这里的不用softmax的原因是,后面我们会用 CrossEntropyLoss,里面就包括了softmax log 等操作,直接return 线性层的输出就够了
return pred
use_gpu=torch.cuda.is_avaliable() #检查gpu是否可用
model=Classifier(28*28,125,65,10)
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.Adam(model.parameters(),lr=0.01)
if use_gpu:
#将数据放在cuda上,才能用GPU运算
model=model.cuda()
criterion=criterion.cuda()
epoch=12
losses=[]
val_losses=[]
val_acces=[]
for i in range(epoch):
running_loss=0
dataiter=iter(training_loader)
for j in range(len(dataiter)):
#for steps,(inputs,labels) in enumerate(training_loader): 也是一样的,都是从training_loader里取training data
inputs,labels=dataiter.next()
inputs=inputs.view(inputs.shape[0],-1)
#将输入变成 batchsize*input_size的样子
if use_gpu:
inputs=inputs.cuda()
labels=labels.cuda()
y_pred=model.forward(inputs)
loss=criterion(y_pred,labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss+=loss.item()
#每一个epoch都记录一下 loss
running_loss=running_loss/inputs.shape[0]
losses.append(running_loss/len(dataiter))
val_running_loss=0.0
val_running_acc=0.0
val_dataiter=iter(validation_loader)
for j in range(len(val_dataiter)):
inputs,labels=val_dataiter.next()
inputs=inputs.view(inputs.shape[0],-1)
#将输入变成 batchsize*input_size的样子
if use_gpu:
inputs=inputs.cuda()
labels=labels.cuda()
y_pred=model.forward(inputs)
loss=criterion(y_pred,labels)
#validation 不需要进行反向传播,但是可以进行准确度的计算
_,preds=torch.max(y_pred,1)
val_running_acc+=torch.sum(preds=labels.data)
val_running_loss+=loss.item()
val_running_acc=val_running_acc/inputs.shape[0]
val_running_loss=val_running_loss/inputs.shape[0]
val_losses.append(val_running_loss/len(val_dataiter))
val_acces.append(val_running_acc/len(val_dataiter))
plt.close()
plt.plot(losses,label='training_loss')
plt.plot(val_losses,label='validation_loss')
plt.legend()
#保存每一个epoch 的model
dir='./deep_nn_checkpoint'
PATH=os.path.join(dir,'model'+str(i)+'.pth')
torch.save(model.state_dict(), PATH)
分割结束后,我们可以进行测试:
首先,我们可以选择中间的训练结果,也不是最后一个,因为训练的时候,我们将每一个epoch的model都存了下来,所以可以直接加载
dir='./deep_nn_checkpoint'
PATH=os.path.join(dir,'model'+str(5)+'.pth')
model.load_state_dict(torch.load(PATH))
model.eval()
然后我们找到一张数字图像,然后进行识别:
一张白底黑色的图片,所以对于这个图片,需要将其转化为我们训练时的格式:
from PIL import Image
img=Image.open('deep_nn_test_image.jpg')
img=PIL.ImageOps.invert(img) #将图片反色,变成黑底白字
img=Image.Convert('1') #转成单通道图片
transform=transforms.Compose([transforms.Resize([28,28]),
transforms.ToTensor(),
transforms.Normalize((0.5,),(0.5,))])
img=transform(img)
inputs= img.view(img.shape[0],-1)
pred=model.forward(inputs)
_,preds=torch.max(pred,1)
print(preds)
输出结果为 5,识别正确