当前位置:   article > 正文

Mnist手写数字识别_实验总结mnist手写数字识别

实验总结mnist手写数字识别

手撕代码001-----Mnist手写数字识别

from pathlib import Path
import requests

DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"  # 斜杠 / 操作符用于拼接路径,比如创建子路径
# Path.mkdir 创建目录
# mkdir(parents=True, exist_ok=True)
# parents:如果父目录不存在,是否创建父目录。
# exist_ok:只有在目录不存在时创建目录,目录已存在时不会抛出异常
PATH.mkdir(parents=True, exist_ok=True)

URL = "http://deeplearning.net/data/mnist/"   # 下载 mnist 数据集的地址
Filename = "mnist.pkl.gz"

if not (PATH / Filename).exists(): # 如果"mnist.pkl.gz"文件不存在
        req = requests.get(URL + Filename)  # 请求下载数据集地址的Filename文件
        # 把Reponse对象的内容以二进制数据的形式返回
        # .content 返回的是可以自由加工的(以你想要的编码格式来进行编码)的字节串,
        # 是只高于二进制数据的一种数据存储单位。
        pic = req.content
        # (PATH / Filename) :是你下载的好的数据,存储在PATH(date/mnist路径下的文件1=="mnist.pkl.gz")
        (PATH / Filename).open("wb").write(pic)
        # 文件1 "mnist.pkl.gz",打开
        # Path.open("wb")  w表示写入
        # w:open for writing写入, truncating the file first【截断】
        # b:binary mode 二进制方式
        # write(pic):写入pic


import pickle
import gzip

# from pathlib import Path
# 路径分隔符表示函数: Path.as_posix()
# 作用:将 Windows目录中的路径分隔符 ‘\’ 改为 Unix 样式 ‘/’。
# 将所有连续的正斜杠、反斜杠,统一修改为单个正斜杠
with gzip.open((PATH / Filename).as_posix(), "rb") as f:
# 该 pickle 模块实现了用于序列化和反序列化 Python 对象结构的二进制协议
# pickle.load 反序列化
        ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
# x_train.shape (50000, 784)
# y_train.shape (50000,)
# x_valid.shape (10000, 784)
# y_valid.shape (10000, )
from matplotlib import pyplot as plt
import numpy as np
# .reshape 将其形成一个28*28数组
# x_train[0].shape = (784, ) 784是mnist数据集每个样本的像素点个数
plt.imshow(x_train[0].reshape((28, 28)), cmap="gray")
plt.show()
print(x_train.shape)

import torch
# 注意数据需转换成tensor才能参与后续建模训练
# pytorch 的 map()函数接收两个参数,一个是函数,一个是Iterable,【可以进行数据格式转换】
# map将传入的函数【数据转换成torch.tensor】依次作用到序列的每个元素,并把结果作为新的Iterator返回
x_train, y_train, x_valid, y_valid = map(
    torch.tensor, (x_train, y_train, x_valid, y_valid)
)
n, c = x_train.shape  # n=50000 c= 784
#  x_train.shape == torch.Size([50000, 784])
x_train, x_train.shape, y_train.min(), y_train.max()
# y_train.min()==tensor(0)  y_train.max()==tensor(9)
# print(x_train, y_train)
# print(x_train.shape)
# print(y_train.min(), y_train.max())

# 那什么时候使用nn.Module,什么时候使用nn.functional呢?
# 一般情况下,如果模型有可学习的参数,最好用nn.Module,其他情况nn.functional相对更简单一些
import torch.nn.functional as F
# 包含 torch.nn 库中所有函数
# 同时包含大量 loss 和 activation function
loss_func = F.cross_entropy

def model(xb):
    return xb.mm(weights) + bias
# weights.shape [784, 10] 分成10类,总共784个像素点【特征点】

bs = 64  # batch_size
# xb.shape = torch.Size([64, 784])
xb = x_train[0:bs]  # a mini-batch from x
yb = y_train[0:bs]
# 权重随机初始化   weights 需要对权重更新的,追踪该参数, 要自动求梯度的
# dtype = torch.float:返回张量所需的数据类型
# requires_grad=True  autograd是否应该记录对返回张量的操作(说明当前量是否需要在计算中保留对应的梯度信息)
# 返回一个符合均值为0,方差为1的正态分布(标准正态分布)中填充随机数的张量
weights = torch.randn([784, 10], dtype = torch.float,  requires_grad = True)
bs = 64
# torch.zeros 张量全部赋值0,且 权重需要更新的,追踪该参数,要自动求梯度的
bias = torch.zeros(10, requires_grad=True)

print(loss_func(model(xb), yb))

# 无需写反向传播函数,nn.Module能够利用autograd自动实现反向传播
# torch.nn 实现一个简单的线性回归模型,
# 主要是根据 nn.Module 和 torch.nn 中的 Linear 模型, 来创建一个自己的线性模型.
from torch import nn
# 可以通过继承 nn.Module(它本身是一个类并且能够跟踪状态)建立神经网络。
# 我们想要建立一个包含权重、偏置和前向传播的方法的类

class Mnist_NN(nn.Module):
    # 创建Mnist_NN类  子类:Mnist_NN 父类:nn.Module
    def __init__(self):
        # 初始化属性hidden1、hidden2、out
        super(Mnist_NN,self).__init__()
        # super().__init__() 可行
        # 这里__init__()函数里面的值要写,不然就被父类的默认值覆盖了!
        self.hidden1 = nn.Linear(784, 128)  # 两层全连接神经网络
        self.hidden2 = nn.Linear(128, 256)
        self.out = nn.Linear(256, 10)
# class 子类(父类):
# 	def 子类的方法(self, 参数):
# 		父类.父类的方法(self, 参数)

    def forward(self, x):
        x = F.relu(self.hidden1(x))  # 前向传播的激活函数relu
        x = F.relu(self.hidden2(x))
        x = self.out(x)
        return x  # 传出x

net = Mnist_NN()
print(net)
'''
Mnist_NN(
   (hidden1): Linear(in_features=784, out_features=128, bias=True)
   (hidden2): Linear(in_features=128, out_features=256, bias=True)
   (out): Linear(in_features=256, out_features=10, bias=True)
 )
'''
# net.named_parameters() 可以查看神经网络的参数信息,用于更新参数,或者用于模型的保存
# list(net.named_parameters()) 查看神经网络的参数信息
# 'hidden1.weight' 'hidden1.bias'
# 'hidden2.weight' 'hidden2.bias'
# 'out.weight' 'out.bias'  以上6组
# name, parameter 名字 参数tensor值
# 'hidden1.weight' 以及对应的tensor值
for name, parameter in net.named_parameters():
    print(name, parameter,parameter.size())

# 使用TensorDataset和DataLoader来简化
# torch.utils.data.DataLoader(): 构建可迭代的数据装载器, 我们在训练的时候,每一个for循环,
# 每一次iteration,就是从DataLoader中获取一个batch_size大小的数据的
from torch.utils.data import TensorDataset  # 数据预处理
from torch.utils.data import DataLoader  # DataLoader :(构建可迭代的数据装载器)

train_ds = TensorDataset(x_train, y_train)  # 把数据放在数据库中
# 从dataset数据库中每次抽出batch_size = bs = 64个数据
train_dl = DataLoader(train_ds,  # 决定数据从哪读取以及如何读取
                      batch_size=bs,
                      shuffle=True  # 每个epoch 将数据打乱
                      )

valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)  # 为什么2倍的batch

def get_data(train_ds, valid_ds, bs):
    return (
        DataLoader(train_ds, batch_size=bs, shuffle=True),
        DataLoader(valid_ds, batch_size=bs * 2),
    )

from torch import optim

def get_model():
    model = Mnist_NN()
    return model, optim.SGD(model.parameters(), lr=0.001)
# optim.SGD “随机梯度下降”,但是本质上还是还是实现的批量梯度下降,即用全部样本梯度的均值更新可学习参数

'''
- 一般在训练模型时加上model.train(),这样会正常使用Batch Normalization和 Dropout
- 测试的时候一般选择model.eval(),这样就不会使用Batch Normalization和 Dropout    '''
import numpy as np

# 定义更新权值的训练函数
# 把整个训练循环封装成fit()函数,以便后续再次调用它
def fit(steps, model, loss_func, opt, train_dl, valid_dl):
    for step in range(steps):  # 开始step=0
        # 训练开始之前写上 model.train() ,在测试时写上 model.eval()
        # 训练过程中会在程序上方添加一句model.train(),作用是:启用 batch normalization 和 dropout
        model.train()
        # xb.shape = torch.Size([64, 784])  yb.shape =torch.Size([64])
        for xb, yb in train_dl:  # 对当前batch进行损失率及参数更新
            loss_batch(model, loss_func, xb, yb, opt)  # 计算损失率以及进行参数更新
        # 一个step【或epoch】,就是把所有的训练或验证的数据用一遍

        # 加入验证集
        model.eval()  # 评估模型
        # 使用with torch.no_grad():
        # 表明当前计算不需要反向传播,所有计算得出的tensor的requires_grad都自动设置为False
        with torch.no_grad():
            # len(muns)=79 用*号操作符,可以将list unzip(解压)
            # nums =(128,128,...,128,16) 总共10000个验证集 10000=128*78+16
            # losses= (2.279... , ...... , 2.275...)
            losses, nums = zip(
                *[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]
            )
        # np.sum() 求和
        # 矩阵点乘——对应元素相乘:np.sum(np.multiply(a,b))=a*b = 128*2.279..+...+16*2.275..
        # np.sum(nums)= 10000
        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)
        print('当前step:'+str(step), '验证集损失:'+str(val_loss))
# loss_func = F.cross_entropy
# steps=要迭代多少次
# model模型
# loss_func损失函数
# opt优化器
# train_dl训练数据
# valid_dl验证数据
def loss_batch(model, loss_func, xb, yb, opt=None):
    loss = loss_func(model(xb), yb)
    # 因为训练的过程通常使用mini-batch方法,所以如果不将梯度清零的话,
    # 梯度会与上一个batch的数据相关,因此该函数要写在反向传播和梯度下降之前。
    # 每次迭代之后,需要将梯度还原为零,否则loss.backward() 将梯度增加到已经存在的值上,而不是替代它
    if opt is not None:
        # compute gradient and do SGD step
        loss.backward()  # 反向传播计算得到每个参数的梯度值(loss.backward())
        opt.step()       # 通过梯度下降执行一步参数更新(optimizer.step())
        opt.zero_grad()  # 将梯度归零(optimizer.zero_grad())
    return loss.item(), len(xb)  # item():取出张量具体位置的元素元素值  len(xb)= 64
# 获取训练的x,y数据的时候,一次抓取batch=64 和 获取验证的x,y 的数据数据的时候,一次抓取batch=128
# valid_dl.batch_size= 2*bs = 128
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)

model, opt = get_model()

fit(30, model, loss_func, opt, train_dl, valid_dl)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/339405
推荐阅读
相关标签
  

闽ICP备14008679号