MNIST神经网络实现步骤
- 1.加载必要的库
- 2.定义超参数
- 3.构建transforms,主要对图像进行变换
- 4.下载、加载数据集
- 5.构建网络模型
- 6.定义优化器
- 7.定义训练的函数
- 8.定义测试方法
- 9.调用方法
- 总结
1.加载必要的库
代码如下:
import torch
import torch.nn as nn # nn 作为一个代号
import torch.nn.functional as F # F 也作为一个代号
import torch.optim as optim #导入优化器optim,比如梯度下降法
from torchvision import datasets,transforms #datasets,transforms导入进来
2.定义超参数
参数:模型中的
称为模型的参数,可以通过训练得到的参数。
超参数:在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。
#2.定义超参数
BATCH_SIZE = 16 #每批处理的数据,要分开进行处理
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") #是否用GPU还是CPU训练,
#有GPU用GPU训练,没有的话用CPU来训练
EPOCHS = 10 #设置训练数据集的轮次,比如60000次训练10轮可以得到600000张图片
3.构建transforms,主要对图像进行变换
#3.构建pipeline,其实和transforms是一个意思
pipeline = transforms.Compose([
transforms.ToTensor(), #将图片转化为tensor(张量(也就是多维数组))
transforms.Normalize((0.1307,),(0.3081,)) #将图片正则化,降低模型复杂度
])
4.下载、加载数据集
#4.下载加载数据
from torch.utils.data import DataLoader #导入数据
#下载数据集
train_set = datasets.MNIST("data",train=True,download=True,transform=pipeline) #用datasets下载数据集
test_set = datasets.MNIST("data",train=False,download=True,transform=pipeline)
#加载数据
train_loader = DataLoader(train_set,batch_size=BATCH_SIZE,shuffle=True)#shuffle打乱,提高精度
test_loader = DataLoader(test_set,batch_size=BATCH_SIZE,shuffle=True)
5.构建网络模型
此处构建的卷积结构为:
输入卷积层1
激活层1
池化层
卷积层2
激活层2
全连接层1
激活层3
全连接层2
输出
#5.构建网络模型
class Digit(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1,10,5) #卷积层1,1:代表灰度图片的通道,10:输出通道, 5:kernel卷积核
self.conv2 = nn.Conv2d(10,20,3) #卷积层2,10:输入通道,20:输出通道, 3:kernel卷积核
self.fc1 = nn.Linear(20*10*10,500) #全连接层,20*10*10:输入通道,500:输出通道
self.fc2 = nn.Linear(500,10) #全连接层,500:输入通道,10:输出通道
def forward(self,x): #前向传播算法
input_size = x.size(0) #batch_size *1 *28 *28 1:通道数 28*28 像素数
x = self.conv1(x) #卷积层:输入:batch*1*28*28,输出:batch*10*24*24 输出通道为10,(28-5+1=24)
x = F.relu(x) #激活层,relu函数 输出:batch*10*24*24
x = F.max_pool2d(x,2,2) #池化层,输入:batch*10*24*24 输出:batch*10*12*12 进行减半
x = self.conv2(x) #输入:batch*10*12*12 输出:batch*20*10*10 输出通道为20,(12-3+1=10)和前面一样
x = F.relu(x) #激活层,relu函数 输出:batch*20*10*10
x = x.view(input_size,-1) #拉平flatten,-1:自动计算维度,其实就是2000,20*10*10=2000
x = self.fc1(x) #全连接层,输入:batch*2000 输出:batch*500
x = F.relu(x) #激活层是保持shape不变
x = self.fc2(x) #全连接层,输入:batch*500 输出:batch*10
output = F.log_softmax(x, dim=1) #计算分类后,每个数字的概率值
return output
6.定义优化器
Adam优化器:
Adam的英文全称为:Adaptive Moment Estimation,它是利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
下为Adam算法的更新公式:
梯度下降法的参数更新公式为:
其中是学习率,
为第t轮的参数,
为损失函数,
为梯度。为了简便,令
,因此梯度下降法可以写为:
Adam是梯度下降法的变种,用来更新神经网络的权重,更新公式如下:(更新第一矩向量)
(更新第二矩向量)
(计算偏差矫正的第一矩向量)
(计算偏差矫正的第一矩向量)
(更新参数)
其中,,
,
为指数衰减率,控制权重分配,
为指数衰减率,控制之前的梯度平方进行加权均值
代码如下:
#6.定义优化器
model = Digit().to(DEVICE)
optimizer = optim.Adam(model.parameters()) #Adam损失函数
7.定义训练的函数
一般定义的训练函数有如下几步:
1.准备好tensor形式的输入数据和标签(可选)
2.前向传播计算网络输出output和计算损失函数loss
3.反向传播更新参数
以下三句话一句也不能少:
(1)将上次迭代计算的梯度值清0
optimizer.zero_grad()
(2)反向传播,计算梯度值
loss.backward()
(3)更新权值参数
optimizer.step()
4.保存训练集上的loss和验证集上的loss以及准确率以及打印训练信息。(可选)
5.图示训练过程中loss和accuracy的变化情况(可选)
#7.定义训练的函数
def train_model(model, device, train_loader,optimizer, epoch): # 相关参数输入进来
# 模型训练
model.train()
for batch_index, (data, target) in enumerate(train_loader):
# 部署到DEVICE上去
data, target = data.to(device), (device) # 部署到GPU或者CPU上
# 梯度初始化为0
optimizer.zero_grad()
# 训练后的结果
output = model(data)
# 计算损失
loss = F.cross_entropy(output, target) # 交叉熵损失函数适用于多分类的任务,二分类的问题损失函数是sigmoid函数
# 找到概率值最大的下标
pred = output.max(1, keepdim=True) # 或者可以这样写:pred = output.argmax(dim=1)
#反向传播
loss.backward()
#参数优化
optimizer.step()
if batch_index % 3000 == 0:
print("Train Epoch : {} \t Loss : {:.6f}".format(epoch,loss.item())) #输出训练的轮数和损失
8.定义测试方法
#8.定义测试方法
def test_model(model, device ,test_loader):
#模型验证
model.eval()
#正确率
correct = 0.0 # 初始值
# 测试的损失
test_loss = 0.0
with torch.no_grad(): # 不会计算梯度,不需要梯度的计算,也不会进行反向传播
for data, target in test_loader:
# 部署到device上
data,target = data.to(device),(device)
# 测试数据
output = model(data)
# 计算测试损失
test_loss += F.cross_entropy(output,target).item()
#找到概率值最大的下标
pred = output.max(1,keepdim=True)[1] # [0]值,[1]索引
# pred = torch.max(output,dim=1)
# pred = output.argmax(dim=1)
#累计正确的值
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset) #测试的损失
print("Test -- Average loss : {:.4f},Accuracy : {:.3f}\n".format(
test_loss,100.0 * correct / len(test_loader.dataset))) #输出平均损失和准确率
9.调用方法
for epoch in range(1,EPOCHS + 1):
train_model(model, DEVICE, train_loader,optimizer,epoch)
test_model(model, DEVICE, test_loader)