深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

目录

1. 加载数据:MNIST的train数据和test数据

2. 定义网络:一个简单的线性神经网络

3. 训练:使用定义的网络进行训练

拓展:使用visdom可视化_动态的绘制loss曲线

4. 测试:使用测试集,测试预测精度

5. 辅助工具函数

6. 拓展


1. 加载数据:MNIST的train数据和test数据

import torch
import torchvision  # 处理图像视频, 包含一些常用的数据集、模型、转换函数等等
from torch import nn, optim
from torch.nn import functional as F
from matplotlib import pyplot as plt
from utils import plot_curve, plot_image, one_hot
batch_size = 512
# step1. 加载数据集
train_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('mnist_data_john', train=True, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,)
                                   )
                               ])),
    batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('mnist_data_john/', train=False, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,)
                                   )
                               ])),
    batch_size=batch_size, shuffle=False)
# # test: 显示下训练数据集的前6张图像及对应的标签
# x,y = next(iter(train_loader))
# print(x.shape, y.shape, x.min(), x.max())
# plot_image(x,y,"image_gt")

显示下训练数据集的前6张图像及对应的标签:

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

2. 定义网络:一个简单的线性神经网络

这里采用 3个线性层,做简单示范。

# step2. 定义神经网络
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        # 定义三个线性层 y = wx+b
        # 输入X的size是:[batch_size, 28*28=784]
        # y = w1 * x + b1, 例如:参数数量:w1.size是[256,784] (一张图像x的size是[784,1]), b.size是[256]
        self.fc1 = nn.Linear(28 * 28, 256)  # 28*28是输入图像的大小,256是自定义的中间层大小
        # y = w2 * x + b2
        self.fc2 = nn.Linear(256, 64)       # 中间层数的结果,本层输入层数取决于上一层的输出层数,本层输出决定了下一层的输入层数。
        # y = w3 * x + b3
        self.fc3 = nn.Linear(64, 10)        # 10是要求的输出分类层数
    def forward(self, x):
        x = F.relu(self.fc1(x))             # 使用激活函数,增加非线性
        x = F.relu(self.fc2(x))
        x = self.fc3(x)                     # 最后一层根据网络结果输出
        return x

拓展:网络的另一种定义方式如下。

# step2'. 定义神经网络
class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
		self.model =  nn.Sequential(
			nn.Linear(784, 256),
			nn.LeakyReLU(inplace=True), # 使用inplace,省去反复申请和释放内存的时间。会对原变量覆盖。
			nn.Linear(256, 64),
			nn.LeakyReLU(inplace=True),
			nn.Linear(64, 10),
			nn.LeakyReLU(inplace=True)
			)
    def forward(self, x):
		x = self.model(x)
        return x

3. 训练:使用定义的网络进行训练

# step3. 开始训练
net = MyNet()
# 定义梯度下降方式
optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9)
train_loss = []
for epoch in range(3):
    for batch_idx, (x, y) in enumerate(train_loader): # train_loader中有 n 个 (x,y), 每一个 x和 y包含 batch_size张图像,所以总的图像数量是= batch_idx * batch_size
        x = x.view(x.size(0), 28 * 28)  # view 等价于reshape, [512,1,28*28] => [512,28*28]
        out = net(x)                    # [batch_size,10]
        y_onehot = one_hot(y)           # 如将 [512,] 变成 [512,10], 原来一维数组中对应m行的值n,对应新的二维数组m行的第n列设置为1
        loss = F.mse_loss(out, y_onehot)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_loss.append(loss.item())
        if batch_idx % 10 == 0:
            print(epoch, batch_idx, loss.item())
plot_curve(train_loss)
print (net.parameters())

loss的下降结果:

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

拓展:使用visdom可视化_动态的绘制loss曲线

1. 准备工作:a. 安装visdom: (pip install visdom) b.启动服务(python -m visdom.server) c.浏览器打开localhost (比如 http://localhost:8097)

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

界面如下:

 深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

2. 在代码中添加要监视的变量,如下viz相关的代码

from visdom import Visdom
viz = Visdom()
index = 0
viz.line([0.],[0.],win='train_loss', opts=dict(title='train loss'))
for epoch in range(3):
    for batch_idx, (x, y) in enumerate(train_loader): # train_loader中有 n 个 (x,y), 每一个 x和 y包含 batch_size张图像,所以总的图像数量是= batch_idx * batch_size
        x = x.view(x.size(0), 28 * 28)  # view 等价于reshape, [512,1,28*28] => [512,28*28]
        out = net(x)                    # [batch_size,10]
        y_onehot = one_hot(y)           # 如将 [512,] 变成 [512,10], 原来一维数组中对应m行的值n,对应新的二维数组m行的第n列设置为1
        loss = F.mse_loss(out, y_onehot)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_loss.append(loss.item())
        #可视化
        index += 1
        # viz.line([0.],[0.],win='train_loss', opts=dict(title='train loss'))
        viz.line([loss.item()],[index], win='train_loss', update='append')
        if batch_idx % 10 == 0:
            print(epoch, batch_idx, loss.item())

可视化效果(动态的):

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

4. 测试:使用测试集,测试预测精度

# step4. 进行测试,计算预测精度
total_correct = 0
for x, y in test_loader:
    x = x.view(x.size(0), 28 * 28)
    out = net(x)
    pred = out.argmax(dim=1)
    correct = pred.eq(y).sum().float().item()
    total_correct += correct
total_num = len(test_loader.dataset)
acc = total_correct / total_num
print("acc: ", acc)
# 可视化部分预测结果
x, y = next(iter(test_loader))
out = net(x.view(x.size(0), 28 * 28)) # 二维 [batch_size, 10]
pred = out.argmax(dim=1)              # 一维 [batch_size,]
plot_image(x, pred, 'image_predict')

预测精度:

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

可视化部分预测结果:

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

5. 辅助工具函数

定义到uitls.py文件中:

用于显示图像,打印一维数组,one hot操作

import torch
from matplotlib import pyplot as plt
# 绘制一维数据图
def plot_curve(data):
    fig = plt.figure()  # 定义一张图纸
    plt.plot(range(len(data)), data, color="blue")  # 绘制一维数组
    plt.legend(["value"], loc="upper right")        # 添加图例,即数据说明标签
    plt.xlabel("step")
    plt.ylabel("value")
    plt.show()
def plot_image(img, label, name):
    '''
    :param img:     比如:torch.Size([batch_size=512, 1, 28, 28])
    :param label:   比如:torch.Size([512])
    :param name:    string
    '''
    fig = plt.figure()
    for i in range(6):
        plt.subplot(2, 3, i + 1) # 2*3个小图像
        plt.tight_layout()
        plt.imshow(img[i][0] * 0.3081 + 0.1307, cmap="gray", interpolation="none")  # 图像进行正则化,然后显示出来
        plt.title("{}: {}".format(name, label[i].item()))  # 显示每一个plot的标题
        plt.xticks([])  # 设置x轴的刻度标签为空,即不显示刻度
        plt.yticks([])
    plt.show()
def one_hot(label, depth=10):
    out = torch.zeros(label.size(0), depth) # 定义一个 [batch_size, 10]大小的矩阵
    idx = torch.LongTensor(label).view(-1, 1)  # 把 label reshape成 [batch_size, 1]尺寸的2维tensor
    out.scatter_(dim=1, index=idx, value=1)  # 改变 out的第dim=1维度的数据,out中值被改变值的索引,是index中对应的值, 填充的值是 1,
                                             # 如第2个样本是“6”,则第1行第5列(矩阵索引从0开始)填充为1.
    return out
# if __name__ == '__main__':
#     data = [1,2,3,4,5,4,3,2,5,6,8]
#     plot_curve(data)

6. 拓展

使用自定义的线性回归模型,使用交叉熵,进行手写数字的识别。注意对比(注释掉35-37行 pk 启用35-37行)下面的参数w1 w2 w3的初始化方法(使用randn初始化时,loss经过几个epoch后会一直居高不下,使用kaiming的初始化方法后,loss会将下来很多。说明初始化对于网络的训练结果来说至关重要!!!)

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

 注释掉35-37行:(randn初始化(高斯分布))

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

启用35-37行:(kaiming大佬的初始化)

深度学习笔记_搭建一个简单网络(完整版)_手写数字识别MNIST

参考:

深度学习入门_哔哩哔哩_bilibili

版权声明:本文为博主惊鸿一博原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/shyjhyp11/article/details/121251300

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2021年11月13日 上午8:40
下一篇 2021年11月13日 上午10:11

相关推荐