人工智能-深度学习-手写数字识别

柔情只为你懂 2022-10-07 03:53 376阅读 0赞

1.准备数据

手写数字识别的特征集是一组数值为0-9,大小为 28 * 28 矩阵的图片, 标签为与之对应的数字:

在这里插入图片描述

在这里插入图片描述

数据下载链接: 手写数字识别数据集

2.将数据格式化为 npz 文件

  1. """ 将图片和标签整理为 npz 文件 """
  2. import numpy as np
  3. import os
  4. from PIL import Image
  5. import json
  6. # 读取图片
  7. # 存到 npz 文件中的为 28 *28 的矩阵列表
  8. train_file_path = "nums/train_x/"
  9. train_x = []
  10. for root, dirs, files in os.walk(train_file_path):
  11. for f in files:
  12. img = np.array(Image.open(os.path.join(root, f)))
  13. train_x.append(img)
  14. test_file_path = "nums/test_x/"
  15. test_x = []
  16. for root, dirs, files in os.walk(test_file_path):
  17. for f in files:
  18. img = np.array(Image.open(os.path.join(root, f)))
  19. test_x.append(img)
  20. train_object = open('nums/train_y.json', 'r')
  21. train_y = json.load(train_object)
  22. test_object = open('nums/test_y.json', 'r')
  23. test_y = json.load(test_object)
  24. np.savez('nums.npz', train_x=np.array(train_x), test_x=np.array(test_x),
  25. train_y=np.array(train_y), test_y=np.array(test_y))

我们顺便记录下, 如何把npz里的数据还原成图片和json文件

  1. """ 从 nums.npz 中读取各个图片和各自的标签 """
  2. import numpy as np
  3. from PIL import Image
  4. import json
  5. # 加载数据
  6. image_data = np.load("data/mnist.npz")
  7. # 分别获取训练集和数据集
  8. x_train = image_data["x_train"]
  9. y_train = image_data["y_train"]
  10. x_test = image_data["x_test"]
  11. y_test = image_data["y_test"]
  12. # 分别把训练集和测试集恢复为png 图片
  13. for i in range(len(x_train)):
  14. im = Image.fromarray(x_train[i])
  15. im.save("nums/train_x/%05d.png" % (i + 1))
  16. for i in range(len(x_test)):
  17. im = Image.fromarray(x_test[i])
  18. im.save("nums/test_x/%05d.png" % (i + 1))
  19. # 分别把训练集和测试集的标签写入到json文件中
  20. train_num_writer = open("nums/train_y.json", 'w')
  21. train_num_writer.write(json.dumps(y_train.tolist(), ensure_ascii=False))
  22. train_num_writer.close()
  23. test_num_writer = open("nums/test_y.json", 'w')
  24. test_num_writer.write(json.dumps(y_test.tolist(), ensure_ascii=False))
  25. test_num_writer.close()

3.训练

采用交叉熵作为损失函数, 28* 28 的784个像素值作为特征向量, 这种训练方式很暴力, 后期如果有其他更精巧的训练方式再来补充, 大家可以先把这种训练当成深度学习中的hello world

  1. """ 手写数字识别(以交叉熵为激活函数的深度学习) """
  2. import torch
  3. import torch.nn as nn
  4. import torch.nn.functional as fc
  5. import numpy as np
  6. import matplotlib.pyplot as plt
  7. import matplotlib.gridspec as grid_spec
  8. plt.switch_backend("TkAgg")
  9. # 一. 准备训练集和测试集数据
  10. # 从npz文件中加载数据
  11. image_data = np.load("nums.npz")
  12. # 获取训练集数据, 并将每张图片的 28 * 28 的矩阵转变为 1 * 784 的矩阵, 转为浮点数
  13. # 除以 255 是为了
  14. # 即我们把 784 个像素点的值处理后当做 784 个特征, 测试集特征同样如此
  15. train_x = image_data["train_x"].reshape([-1, 784]).astype(np.float32) / 255
  16. # 获取标签, 每个标签为图片对应的数字
  17. train_y = image_data["train_y"].astype(np.float32)
  18. # 获取测试集数据
  19. test_x = image_data["test_x"].reshape([-1, 784]).astype(np.float32) / 255
  20. test_y = image_data["test_y"].astype(np.float32)
  21. # 二. 构建数学模型
  22. # 将整个数学模型和参数进行封装
  23. # 继承 nn.Module
  24. class Model(nn.Module):
  25. def __init__(self):
  26. super().__init__()
  27. # 定义线性模型, 并设特征为 5 个, 输出为 10 个(因为数字为 0-9 共十个数字 )
  28. self.linear = nn.Linear(784, 128)
  29. # 采用ReLU作为激活函数
  30. self.relu = nn.ReLU()
  31. # 第二层神经网络
  32. self.linear2 = nn.Linear(128, 10)
  33. def forward(self, x):
  34. # 将x输入到第一层神经网络中
  35. x = self.linear(x)
  36. # 调用激活函数
  37. x = self.relu(x)
  38. # 传入第二层神经网络
  39. x = self.linear2(x)
  40. return x
  41. # 三. 开始训练
  42. # 设置学习率为 0.1
  43. eta = 0.1
  44. # 调用封装好的模型
  45. model = Model()
  46. # 开始进行训练
  47. model.train()
  48. # 损失函数采用 交叉熵作为损失函数
  49. loss_fn = nn.CrossEntropyLoss()
  50. # 构建优化器, 采用 随机梯度下降法(Stochastic Gradient Descent)
  51. # 调用 model.parameters() 传入参数和学习率
  52. optimizer = torch.optim.SGD(model.parameters(), eta)
  53. # 进行迭代
  54. for step in range(10000):
  55. # 每次随机产生 32 个下标索引, 获取 32 个数据进行随机梯度下降
  56. idx = np.random.randint(0, len(train_x), [32])
  57. xin = train_x[idx]
  58. din = train_y[idx]
  59. # 将 numpy 类型的数据转为 Tensor 类型,
  60. # 将标签的浮点类型转整数(loss函数需要标签为long类型)
  61. xin, din = torch.from_numpy(xin), torch.from_numpy(din).long()
  62. # 代入模型进行计算
  63. y = model(xin)
  64. # 计算损失函数, 然后从损失函数开始进行反向传播
  65. # 损失函数, 这个是计算图的最终节点
  66. loss = loss_fn(y, din)
  67. # 反向传播, 计算梯度, 这个张量的所有梯度将会自动积累到.grad属性
  68. loss.backward()
  69. # 进行迭代
  70. optimizer.step()
  71. # 将优化器已计算的梯度置0, 否则会累加
  72. optimizer.zero_grad()
  73. if step % 50 == 49:
  74. y_estimate = model(torch.from_numpy(test_x))
  75. # 找出最大的数的索引, 索引是多少, 就是估计得值是多少
  76. D_estimate = torch.argmax(y_estimate.detach(), 1).numpy()
  77. print("第 %d 次迭代, 准确率: %.2f %%" % (step,np.mean(D_estimate == test_y) * 100))
  78. # 四. 绘制训练结果
  79. # 建立编号为1, 大小为 14 * 8 的画图窗口 figure
  80. fig = plt.figure(1, figsize=(14, 8))
  81. # 指定放置子图的网格的几何形状, 为 5 行 5 列
  82. gs = grid_spec.GridSpec(5, 5)
  83. # 对测试集进行预测, 获得的 y 为 10000 * 10 的结果矩阵,
  84. y = model(torch.from_numpy(test_x))
  85. # 找出最大的数的索引, 索引是多少, 就是估计得值是多少
  86. D = torch.argmax(y.detach(), 1).numpy()
  87. # 将张量的每个元素缩放到(0,1)区间且和为1, 这个可以作为置信度
  88. P = fc.softmax(y.detach(), 1)
  89. for i in range(5):
  90. for j in range(5):
  91. # 0-10000 随机选取一个矩阵
  92. index = np.random.randint(5000)
  93. # 将该矩阵从 1 * 784 转为28 * 28
  94. X = test_x[index].reshape(28, 28)
  95. # 在第 i 行第 j 个位置的图像绘制 图像
  96. ax = fig.add_subplot(gs[i, j])
  97. # 绘制该矩阵, 以蓝色显示
  98. ax.matshow(X, cmap=plt.get_cmap("Blues"))
  99. # 获取该数据的预测值(即标签矩阵中的最大值得索引)
  100. idx = D[index]
  101. # 获取预测结果矩阵中指定的预测标签矩阵中的数字, 即置信度
  102. prob = P[index, idx]
  103. # 书写 label, 在 x 轴方向上
  104. ax.set_xlabel("真实: %d 预测:%d 概率:%.2f%%" % (test_y[index], idx, prob * 100))
  105. ax.set_xticks(())
  106. ax.set_yticks(())
  107. # 解决中文显示问题
  108. plt.rcParams['font.sans-serif'] = ['SimHei']
  109. plt.rcParams['axes.unicode_minus'] = False
  110. plt.show()

在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,376人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数字识别

    前面的博客介绍过[神经网络结构][Link 1]以及相关的[损失函数][Link 2],在这里我们通过一个简单的神经网络实现一个机器学习问题:识别手写数字图像。 和求解机器