3.8 mnist简单数据识别-LSTM

# RNN LSTM
import numpy as np
from torch import nn,optim
from torch.autograd import Variable
from torchvision import datasets,transforms
from torch.utils.data import DataLoader
import torch

# 训练集
train_dataset=datasets.MNIST(root='./',train=True,transform=transforms.ToTensor(),download=True)
# 测试集
test_dataset=datasets.MNIST(root='./',train=False,transform=transforms.ToTensor(),download=True)

# 批次大小
batch_size =64
# 装载训练集
train_loader= DataLoader(dataset=train_dataset,batch_size=batch_size,shuffle=True)
# 装载测试集
test_loader= DataLoader(dataset=test_dataset,batch_size=batch_size,shuffle=True)

for i,data in enumerate(train_loader):
    inputs,labels=data
    print(inputs.shape)
    print(labels.shape)
    break
    ## torch.Size([64, 1, 28, 28])
    ## torch.Size([64])

# 定义网络结构
# input_size输入特征的大小
# hidden_size LSTM模块的数量
# num_layer LSTM的层数
# LSTM默认input(seq_len,batch,feature)
# batch_first=True 调整input为input(batch,seq_len,feature)
class LSTM(nn.Module):
    def __init__(self):
        super(LSTM,self).__init__()
        self.lstm=torch.nn.LSTM(
            input_size=28,
            hidden_size=64,
            num_layers=1,
            batch_first=True
        )
        # 全连接层
        self.out=torch.nn.Linear(in_features=64,out_features=10)
        self.softmax=torch.nn.Softmax(dim=1)
    def forward(self,x):
        # batch_first=True,需要三维数据
        x=x.view(-1,28,28)
        # output [batch,seq_len,hidden_size]包含每个序列的输出结果
        # 虽然LSTM的batch_first为true,但是h_n,c_n不跟随改变,第0个维度还是num_layers
        # h_n [num_layers,batch,hidden_size]只包含最后一个序列的输出结果
        # c_n [num_layers,batch,hidden_size]只包含最后一个序列的输出结果
        output,(h_n,c_n)=self.lstm(x)
        output_in_last_timestep=h_n[-1,:,:]
        x=self.out(output_in_last_timestep)
        x=self.softmax(x)
        return x

LR=0.0003
# 定义模型
model=LSTM()
# 定义代价函数
mse_loss=nn.CrossEntropyLoss()
# 定义优化器
optimizer=optim.Adam(model.parameters(),LR)

# 模型训练
def train():
    # 模型的训练状态,Dropout起作用
    model.train()
    for i,data in enumerate(train_loader):
        # 获得一个批次的数据和标签
        inputs,labels=data
        # 获得模型预测值
        out=model(inputs)
        # 交叉熵代价函数,不需要shape一致,它会自动独热编码
        loss=mse_loss(out,labels)
        # 梯度清0
        optimizer.zero_grad()
        # 梯度计算
        loss.backward()
        # 修改权值
        optimizer.step()
# 模型测试
def test():
    # 模型的测试状态,Dropout不起作用,所有神经元均参与计算
    model.eval()
    correct=0

    # 计算训练集上的准确率
    for i,data in enumerate(train_loader):
        # 获得一个批次的数据和标签
        inputs,labels=data
        # 获得模型预测值
        out=model(inputs)
        # 获得最大值(忽略),以及最大值所在的位置
        _,predicted=torch.max(out,1)
        # 计算正确的个数
        correct+=(predicted==labels).sum()
    # 输出正确率
    print("Train acc:{0}".format(correct.item()/len(train_dataset)))

    correct=0
    # 计算测试集上的准确率
    for i,data in enumerate(test_loader):
        # 获得一个批次的数据和标签
        inputs,labels=data
        # 获得模型预测值
        out=model(inputs)
        # 获得最大值(忽略),以及最大值所在的位置
        _,predicted=torch.max(out,1)
        # 计算正确的个数
        correct+=(predicted==labels).sum()
    # 输出正确率
    print("Test acc:{0}".format(correct.item()/len(test_dataset)))

# 我们先训练10个周期
# 可以看出RNN可能更适合序列化问题,分类不如CNN
for epoch in range(10):
    print('epoch:',epoch)
    train()
    test()
    ## epoch: 0
    ##     ## Train acc:0.8071166666666667
    ## Test acc:0.8119
    ## epoch: 1
    ## Train acc:0.8405666666666667
    ## Test acc:0.8418
    ## epoch: 2
    ## Train acc:0.8526333333333334
    ## Test acc:0.8518
    ## epoch: 3
    ## Train acc:0.9123333333333333
    ## Test acc:0.913
    ## epoch: 4
    ## Train acc:0.9264666666666667
    ## Test acc:0.9209
    ## epoch: 5
    ## Train acc:0.9434333333333333
    ## Test acc:0.9393
    ## epoch: 6
    ## Train acc:0.9487833333333333
    ## Test acc:0.9443
    ## epoch: 7
    ## Train acc:0.9525333333333333
    ## Test acc:0.9488
    ## epoch: 8
    ## Train acc:0.9576
    ## Test acc:0.9521
    ## epoch: 9
    ## Train acc:0.9500666666666666
    ## Test acc:0.9457