#1 加载必要的库
import torch
import torch.nn as nn #加载网络库
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets,transforms
#2 定义超参数
# 参数 模型学习训练出的,可以通过优化算法进行学习
# 超参数 自己决定的,定义模型结构或优化策略
BATCH_SIZE = 64 #每批处理的数据数量
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu") #是否用GPU还是CPU训练
EPOCHS = 10 #训练数据集的轮次
#3 构件pipeline,对图像做处理
pipeline = transforms.Compose([
transforms.ToTensor(), #将图片转换成tensor格式 pytorch运行的全是tensor
transforms.Normalize((0.1307,),(0.3081,)) #正则化 模型出现过拟合现象时 降低模型复杂度
])
#过拟合:只认识数据集内的数据 没法举一反三
#4 下载 加载数据
from torch.utils.data import DataLoader
# 下载数据集
train_set = datasets.MNIST("data",train=True,download=True,transform=pipeline) #文件夹的名字
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)
#如何查看MNIST中的图片
with open("./data/MNIST/raw/train-images-idx3-ubyte","rb") as f:
file=f.read()
image1 = [int(str(item).encode('ascii'),16) for item in file[16 : 16+784]]
print(image1)
#保存下图片
import cv2
import numpy as np
image1_np = np.array(image1,dtype=np.uint8).reshape(28,28,1)
print(image1_np.shape)
cv2.imwrite("digit.jpg",image1_np)
#5 构件网络模型
class Digit(nn.Module):
def __init__(self):
super().__init__()
#卷积层
self.conv1 = nn.Conv2d(1,10,5) #1 灰度图片的通道 10 输出通道 5 kernel卷积和
self.conv2 = nn.Conv2d(10,20,3) #10 输入通道 20 输出通道 3 kernel
#全连接层
self.fc1 = nn.Linear(20*10*10,500) #输入通道 输出通道
self.fc2 = nn.Linear(500,10) #输入通道 输出通道
def forward(self,x) :
input_size = x.size(0) #batch_size x 1 x 28 x 28
x=self.conv1(x) #输入 batch*1*28*28 输出 batch*1*24*24(28-5+1)
x=F.relu(x) #激活函数 输出
x=F.max_pool2d(x,2,2) #最大池化层 对图片进行压缩 圈了一块区域作为代表 输出batch*10*12*12
x=self.conv2(x) #输入batch*10*12*12 输出batch*20*10*10(12-3+1)
x=F.relu(x)
x=x.view(input_size,-1) #拉平 平面的拉成一长条 送给网络处理 -1 自动计算维度 20*10*10=2000
#送入到全连接层
x=self.fc1(x) #输入 batch*2000 输出batch*500
x=F.relu(x) #激活 保持shpae不变
x=self.fc2(x) #输入 batch*500 输出batch*10
output=F.log_softmax(x,dim=1) #计算分类后 每个数字的概率值 哪个数字的概率最大 就选最大的作为结果
return output #概率值返回
#卷积层 池化层 激活层 损失函数
#6 定义优化器
model = Digit().to(DEVICE)
optimizer=optim.Adam(model.parameters())
#7 定义训练方法
def train_model(mocel,device,train_loader,potimize,epoch):
#模型训练
mocel.train()
#数据 图片上的标签
for batch_index,(data,target) in enumerate(train_loader):
#部署到DEVICE
data,target=data.to(device),target.to(device)
#梯度初始化为0
optimizer.zero_grad()
#计算后的结果
output=model(data)
#计算损失 预测值 真实值
loss=F.cross_entropy(output,target) #交叉熵损失 针对多分类任务
#找到10个概率中最大的那个
#pred=output.max(1,keepdim=True) #1表示横轴 返回每一行中最大的下标
#反向传播 从右往左
loss.backward()
#前向传播 从上往下 每一层处理完后给下层处理
optimizer.step()
if batch_index%3000==0:
print("Train Epoch:{} \t Loss:{:.6f}".format(epoch,loss.item()))
#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),target.to(device)
#测试数据
output=model(data)
#计算测试损失
test_loss += F.cross_entropy(output,target).item()
#找到概率值最大的下标
pred=output.max(1,keepdim=True)[1]
#累计正确率
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print("Test _Average loss{:.4g} Accuracy {:.3f}\n".format(test_loss,100.0*correct/len(test_loader.dataset)))
#9 调用 方法7 8
for epoch in range(1,EPOCHS+1):
train_model(model,DEVICE,train_loader,optimizer,epoch)
test_model(model,DEVICE,test_loader)
最后的结果: