首先你需要安装torch,torchvision,然后使用torchvision来下载mnist数据集,如果下载数据集有什么问题,请查看PyTorch用最简单的多层感知机(深度神经网络)实现手写数字识别 和 使用torchvision下载外网数据集mnist没有进度的解决方案
本文假设你对CNN基本原理有一定的了解。
我首先建立了一个tools工具类,这样主模块的代码可以少一些了。我把它命名成tools.py:
import torch
import torchvision
from torch.utils.data import DataLoader
def get_trans():
# 设置一个转换的集合,先把数据转换到tensor,再归一化为均值.5,标准差.5的正态分布
trans = torchvision.transforms.Compose(
[
torchvision.transforms.ToTensor(), # ToTensor方法把[0,255]变成[0,1]
torchvision.transforms.Normalize( [0.5], [0.5] )
# 变成mean(均值)=.5,std(标准差standard deviation)=.5的分布
]
)
return trans
DOWNLOAD_MNIST=False
train_data = torchvision.datasets.MNIST( root="./mnist", # 设置数据集的根目录
train=True, # 是否是训练集
transform=get_trans(), # 对数据进行转换
download=DOWNLOAD_MNIST
)
test_data = torchvision.datasets.MNIST( root="./mnist", train=False, # 测试集,所以false
transform=get_trans(), download=DOWNLOAD_MNIST
)
def get_trainLoader(BATCH_SIZE):
# 第二个参数是数据分块之后每一个块的大小,第三个参数是是否大乱数据
train_loader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True)
return train_loader
def get_testLoader(BATCH_SIZE):
test_loader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False)
return test_loader
def get_cuda_available():
available=torch.cuda.is_available()
return available
def get_test_data_len():
return len(test_data)
然后是主模块,首先引入必要的模块:
import torch.nn as nn
import torch
import time
import tools
搭建CNN网络:
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1=nn.Sequential(
nn.Conv2d( #(1,28,28)
in_channels=1,
out_channels=16,
kernel_size=5,
stride=1,
padding=2 #padding=(kernelsize-stride)/2
),#(16,28,28)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)#(16,14,14)
)
self.conv2=nn.Sequential(#(16,14,14)
nn.Conv2d(16,32,5,1,2),#(32,14,14)
nn.ReLU(),#(32,14,14)
nn.MaxPool2d(2)#(32,7,7)
)
self.out=nn.Linear(32*7*7,10)
#定义前向传播过程,过程名字不可更改,因为这是重写父类的方法
def forward(self,x):
x = self.conv1( x )
x = self.conv2( x ) #(batch,32,7,7)
x=x.view(x.size(0),-1) #(batch,32*7*7)
output=self.out(x)
return output
输出CNN的结构如下:
CNN(
(conv1): Sequential(
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(conv2): Sequential(
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(out): Linear(in_features=1568, out_features=10, bias=True)
)
定义常量:
EPOCH=50#总的训练次数
BATCH_SIZE=20#批次的大小
LR=0.03#学习率#交叉熵损失函数不需要太大的学习率
DOWNLOAD_MNIST=False#运行代码的时候是否下载数据集
定义损失函数和优化器、加载数据、更改网络到gpu上运行(如果GPU可用的话):
cnn=CNN()
cuda_available=tools.get_cuda_available()
if cuda_available==True:
cnn.cuda()
optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
loss_function=nn.CrossEntropyLoss()
train_loader=tools.get_trainLoader(BATCH_SIZE)
test_loader=tools.get_testLoader(BATCH_SIZE*10)
最后是训练和测试:
for ep in range(EPOCH):
# 记录把所有数据集训练+测试一遍需要多长时间
startTick = time.clock()
for data in train_loader: # 对于训练集的每一个batch
img, label = data
if cuda_available:
img = img.cuda()
label = label.cuda()
out = cnn( img ) # 送进网络进行输出
loss = loss_function( out, label ) # 获得损失
optimizer.zero_grad() # 梯度归零
loss.backward() # 反向传播获得梯度,但是参数还没有更新
optimizer.step() # 更新梯度
num_correct = 0 # 正确分类的个数,在测试集中测试准确率
for data in test_loader:
img, label = data
if cuda_available:
img = img.cuda()
label = label.cuda()
out = cnn( img ) # 获得输出
_, prediction = torch.max( out, 1 )
# torch.max()返回两个结果,
# 第一个是最大值,第二个是对应的索引值;
# 第二个参数 0 代表按列取最大值并返回对应的行索引值,1 代表按行取最大值并返回对应的列索引值。
num_correct += (prediction == label).sum() # 找出预测和真实值相同的数量,也就是以预测正确的数量
accuracy = num_correct.cpu().numpy() / tools.get_test_data_len() # 计算正确率,num_correct是gpu上的变量,先转换成cpu变量
timeSpan = time.clock() - startTick
print( "第%d迭代期,准确率为%f,耗时%dS" % (ep + 1, accuracy, timeSpan) )
整个模块的代码:
import torch.nn as nn
import torch
import time
import tools
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1=nn.Sequential(
nn.Conv2d( #(1,28,28)
in_channels=1,
out_channels=16,
kernel_size=5,
stride=1,
padding=2 #padding=(kernelsize-stride)/2
),#(16,28,28)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)#(16,14,14)
)
self.conv2=nn.Sequential(#(16,14,14)
nn.Conv2d(16,32,5,1,2),#(32,14,14)
nn.ReLU(),#(32,14,14)
nn.MaxPool2d(2)#(32,7,7)
)
self.out=nn.Linear(32*7*7,10)
def forward(self,x):
x = self.conv1( x )
x = self.conv2( x ) #(batch,32,7,7)
x=x.view(x.size(0),-1) #(batch,32*7*7)
output=self.out(x)
return output
print("start")
EPOCH=50#总的训练次数
BATCH_SIZE=20#批次的大小
LR=0.03#学习率#交叉熵损失函数不需要太大的学习率
DOWNLOAD_MNIST=False#运行代码的时候是否下载数据集
cnn=CNN()
cuda_available=tools.get_cuda_available()
if cuda_available==True:
cnn.cuda()
optimizer=torch.optim.Adam(cnn.parameters(),lr=LR)
loss_function=nn.CrossEntropyLoss()
train_loader=tools.get_trainLoader(BATCH_SIZE)
test_loader=tools.get_testLoader(BATCH_SIZE*10)
#训练过程
for ep in range(EPOCH):
# 记录把所有数据集训练+测试一遍需要多长时间
startTick = time.clock()
for data in train_loader: # 对于训练集的每一个batch
img, label = data
if cuda_available:
img = img.cuda()
label = label.cuda()
out = cnn( img ) # 送进网络进行输出
loss = loss_function( out, label ) # 获得损失
optimizer.zero_grad() # 梯度归零
loss.backward() # 反向传播获得梯度,但是参数还没有更新
optimizer.step() # 更新梯度
num_correct = 0 # 正确分类的个数,在测试集中测试准确率
for data in test_loader:
img, label = data
if cuda_available:
img = img.cuda()
label = label.cuda()
out = cnn( img ) # 获得输出
_, prediction = torch.max( out, 1 )
# torch.max()返回两个结果,
# 第一个是最大值,第二个是对应的索引值;
# 第二个参数 0 代表按列取最大值并返回对应的行索引值,1 代表按行取最大值并返回对应的列索引值。
num_correct += (prediction == label).sum() # 找出预测和真实值相同的数量,也就是以预测正确的数量
accuracy = num_correct.cpu().numpy() / tools.get_test_data_len() # 计算正确率,num_correct是gpu上的变量,先转换成cpu变量
timeSpan = time.clock() - startTick
print( "第%d迭代期,准确率为%f,耗时%dS" % (ep + 1, accuracy, timeSpan) )