import numpy as np
from torch import nn,optim
from torch.autograd import Variable
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch
# 训练集
# roo = './'表示放在跟程序相同位置,transform 将数据转换为tensor,download表示是否下载数据集
train_dataset = datasets.MNIST(root = './',
train=True,
transform=transforms.ToTensor(),
download=True)
# 测试集
test_dataset = datasets.MNIST(root = './',
train=False,
transform=transforms.ToTensor(),
download=True)
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz to ./MNIST\raw\train-images-idx3-ubyte.gz
0%| | 0/9912422 [00:00, ?it/s]
Extracting ./MNIST\raw\train-images-idx3-ubyte.gz to ./MNIST\raw
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz to ./MNIST\raw\train-labels-idx1-ubyte.gz
0%| | 0/28881 [00:00, ?it/s]
Extracting ./MNIST\raw\train-labels-idx1-ubyte.gz to ./MNIST\raw
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz to ./MNIST\raw\t10k-images-idx3-ubyte.gz
0%| | 0/1648877 [00:00, ?it/s]
Extracting ./MNIST\raw\t10k-images-idx3-ubyte.gz to ./MNIST\raw
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz to ./MNIST\raw\t10k-labels-idx1-ubyte.gz
0%| | 0/4542 [00:00, ?it/s]
Extracting ./MNIST\raw\t10k-labels-idx1-ubyte.gz to ./MNIST\raw
# 设置批次大小
batch_size = 64 # 每次训练传入多少数据(64)
# 装载训练集
# 创建了一个数据生成器,数据来源于前面的训练集,shuffle表示生成数据之前,是否要将数据顺序打乱,(把train_dataset打乱,然后每次给一个批次的数据)
train_loader = DataLoader(dataset=train_dataset,
batch_size= batch_size,
shuffle= True)
# 装载测试集
test_loader = DataLoader(dataset=test_dataset,
batch_size= batch_size,
shuffle= True)
# 查看一次循环的数据和标签
# torch.Size([64, 1, 28, 28]) 每次获得64个数据,1表示通道数(灰度图),28*28是图片像素大小
# torch.Size([64]) label是图像标签,64表示里面有64个值
for i,data in enumerate(train_loader):
input,labels = data
print(input,input.shape)
print(labels,labels.shape)
break
tensor([[[[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]]],
[[[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]],
[[[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]],
…,
[[[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]],
[[[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]],
[[[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
…,
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.],
[0., 0., 0., …, 0., 0., 0.]]]]) torch.Size([64, 1, 28, 28])
tensor([3, 0, 5, 4, 7, 4, 3, 8, 1, 3, 6, 6, 9, 4, 7, 6, 5, 2, 7, 4, 6, 0, 6, 4,
3, 7, 6, 1, 7, 5, 5, 1, 2, 2, 3, 5, 0, 4, 8, 4, 3, 0, 7, 9, 6, 7, 2, 0,
6, 9, 2, 3, 7, 7, 7, 9, 1, 0, 0, 1, 7, 7, 1, 0]) torch.Size([64])
# 定义网络结构
class Net(nn.Module):
def __init__(self) -> None:
super(Net,self).__init__() # 对父类进行初始化(固定格式)
# 自己的网络结构
self.fc1 = nn.Linear(784,10) # 784个输入,10个输出简单神经网络,没有隐藏层
self.softmax = nn.Softmax(dim = 1) # dim = 1 表示对第一个维度计算它的概率值 例:(64,10),就是对10进行转换
#前向传递网络计算
def forward(self,x):
# 得到的是四维数据变成两维数据 [64, 1, 28, 28] -> (64,784)
# 在全连接层进行数据计算时只能是两维数据
x = x.view(x.size()[0],-1) #x.size()是数据的形状[64, 1, 28, 28],x.size()[0]就是64,所以第0个维度是64;-1表示自动匹配,第一个维度就是1*28*28=784
x = self.fc1(x)
x = self.softmax(x)
return x
LR = 0.5 # 学习率
# 定义模型
model = Net()
# 定义代价函数
mse_loss = nn.MSELoss()
# 定义优化器
optimizer = optim.SGD(model.parameters(),LR)
# 模型训练的函数
def train():
for i,data in enumerate(train_loader):
# 获得一个批次的数据和标签
inputs,labels = data
# 获得模型预测结果(64,10)(64是一个批次大小,10是10个输出概率值)
out = model(inputs)
# to onehot,把数据标签变成独热编码
# (64)->(64,1)
labels = labels.reshape(-1,1)
# tensor.scatter(dim, index, src)
# dim:对哪个维度进行独热编码
# index:要将src中对应的值放到tensor的哪个位置。
# src:插入index的数值
one_hot = torch.zeros(inputs.shape[0],10).scatter(1,labels,1) #torch.zeros(64,10)生成64行0列全是0的矩阵
print(one_hot)
# 计算loss,mes_loss的两个数据的shape要一致
loss = mse_loss(out, one_hot)
# 梯度清0
optimizer.zero_grad()
# 计算梯度
loss.backward()
# 修改权值
optimizer.step()
# 模型测试的函数
def test():
correct = 0
for i,data in enumerate(train_loader):
# 获得一个批次的数据和标签
inputs,labels = data
# 获得模型预测结果(64,10)(64是一个批次大小,10是10个输出概率值)
out = model(inputs)
# 获得最大值,以及最大值所在的位置
_, predicted = torch.max(out, 1)
# 预测正确的数量
correct += (predicted == labels).sum()
print("Test acc:{0}".format(correct.item()/len(test_dataset)))
# 训练十次
for epoch in range(10):
print('epoch:',epoch)
train()
test()
epoch: 0
Test acc:0.8883
epoch: 1
Test acc:0.9026
epoch: 2
Test acc:0.9058
epoch: 3
Test acc:0.9116
epoch: 4
Test acc:0.9137
epoch: 5
Test acc:0.9154
epoch: 6
Test acc:0.9169
epoch: 7
Test acc:0.9184
epoch: 8
Test acc:0.9202
epoch: 9
Test acc:0.9208