当前位置:   article > 正文

手写数字识别之训练调试与优化_调取训练模型进行手写数字识别

调取训练模型进行手写数字识别

内容都是百度AIstudio的内容,我只是在这里做个笔记,不是原创。

上一节我们研究了资源部署优化的方法,通过使用单GPU和分布式部署,提升模型训练的效率。本节我们依旧横向展开"横纵式",如 图1 所示,探讨在手写数字识别任务中,为了保证模型的真实效果,在模型训练部分,对模型进行一些调试和优化的方法。

 

训练过程优化思路主要有如下五个关键环节:

1. 计算分类准确率,观测模型训练效果。

交叉熵损失函数只能作为优化目标,无法直接准确衡量模型的训练效果。准确率可以直接衡量训练效果,但由于其离散性质,不适合做为损失函数优化神经网络。

2. 检查模型训练过程,识别潜在问题。

如果模型的损失或者评估指标表现异常,通常需要打印模型每一层的输入和输出来定位问题,分析每一层的内容来获取错误的原因。

3. 加入校验或测试,更好评价模型效果。

理想的模型训练结果是在训练集和验证集上均有较高的准确率,如果训练集上的准确率高于验证集,说明网络训练程度不够;如果验证集准确率高训练集,可能是发生了过拟合现象。通过在优化目标中加入正则化项的办法,解决过拟合的问题。

4. 加入正则化项,避免模型过拟合。

飞桨框架支持为整体参数加入正则化项,这是通常的做法。此外,飞桨框架也支持为某一层或某一部分的网络单独加入正则化项,以达到精细调整参数训练的效果。

5. 可视化分析。

用户不仅可以通过打印或使用matplotlib库作图,飞桨还集成了更专业的第三方绘图库tb-paddle,提供便捷的可视化分析。

 

  1. # 加载相关库
  2. import os
  3. import random
  4. import paddle
  5. import paddle.fluid as fluid
  6. from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
  7. import numpy as np
  8. from PIL import Image
  9. import gzip
  10. import json
  11. # 定义数据集读取器
  12. def load_data(mode='train'):
  13. # 读取数据文件
  14. datafile = './work/mnist.json.gz'
  15. print('loading mnist dataset from {} ......'.format(datafile))
  16. data = json.load(gzip.open(datafile))
  17. # 读取数据集中的训练集,验证集和测试集
  18. train_set, val_set, eval_set = data
  19. # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
  20. IMG_ROWS = 28
  21. IMG_COLS = 28
  22. # 根据输入mode参数决定使用训练集,验证集还是测试
  23. if mode == 'train':
  24. imgs = train_set[0]
  25. labels = train_set[1]
  26. elif mode == 'valid':
  27. imgs = val_set[0]
  28. labels = val_set[1]
  29. elif mode == 'eval':
  30. imgs = eval_set[0]
  31. labels = eval_set[1]
  32. # 获得所有图像的数量
  33. imgs_length = len(imgs)
  34. # 验证图像数量和标签数量是否一致
  35. assert len(imgs) == len(labels), \
  36. "length of train_imgs({}) should be the same as train_labels({})".format(
  37. len(imgs), len(labels))
  38. index_list = list(range(imgs_length))
  39. # 读入数据时用到的batchsize
  40. BATCHSIZE = 100
  41. # 定义数据生成器
  42. def data_generator():
  43. # 训练模式下,打乱训练数据
  44. if mode == 'train':
  45. random.shuffle(index_list)
  46. imgs_list = []
  47. labels_list = []
  48. # 按照索引读取数据
  49. for i in index_list:
  50. # 读取图像和标签,转换其尺寸和类型
  51. img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
  52. label = np.reshape(labels[i], [1]).astype('int64')
  53. imgs_list.append(img)
  54. labels_list.append(label)
  55. # 如果当前数据缓存达到了batch size,就返回一个批次数据
  56. if len(imgs_list) == BATCHSIZE:
  57. yield np.array(imgs_list), np.array(labels_list)
  58. # 清空数据缓存列表
  59. imgs_list = []
  60. labels_list = []
  61. # 如果剩余数据的数目小于BATCHSIZE,
  62. # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
  63. if len(imgs_list) > 0:
  64. yield np.array(imgs_list), np.array(labels_list)
  65. return data_generator
  66. # 定义模型结构
  67. class MNIST(fluid.dygraph.Layer):
  68. def __init__(self):
  69. super(MNIST, self).__init__()
  70. # 定义一个卷积层,使用relu激活函数
  71. self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  72. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  73. self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  74. # 定义一个卷积层,使用relu激活函数
  75. self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  76. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  77. self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  78. # 定义一个全连接层,输出节点数为10
  79. self.fc = Linear(input_dim=980, output_dim=10, act='softmax')
  80. # 定义网络的前向计算过程
  81. def forward(self, inputs, label=None):
  82. x = self.conv1(inputs)
  83. x = self.pool1(x)
  84. x = self.conv2(x)
  85. x = self.pool2(x)
  86. x = fluid.layers.reshape(x, [x.shape[0], 980])
  87. x = self.fc(x)
  88. if label is not None:
  89. acc = fluid.layers.accuracy(input=x, label=label)
  90. return x, acc
  91. else:
  92. return x
  93. #调用加载数据的函数
  94. train_loader = load_data('train')
  95. #在使用GPU机器时,可以将use_gpu变量设置成True
  96. use_gpu = False
  97. place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
  98. with fluid.dygraph.guard(place):
  99. model = MNIST()
  100. model.train()
  101. #四种优化算法的设置方案,可以逐一尝试效果
  102. optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  103. #optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=0.01, momentum=0.9, parameter_list=model.parameters())
  104. #optimizer = fluid.optimizer.AdagradOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  105. #optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  106. EPOCH_NUM = 5
  107. for epoch_id in range(EPOCH_NUM):
  108. for batch_id, data in enumerate(train_loader()):
  109. #准备数据
  110. image_data, label_data = data
  111. image = fluid.dygraph.to_variable(image_data)
  112. label = fluid.dygraph.to_variable(label_data)
  113. #前向计算的过程,同时拿到模型输出值和分类准确率
  114. predict, acc = model(image, label)
  115. #计算损失,取一个批次样本损失的平均值
  116. loss = fluid.layers.cross_entropy(predict, label)
  117. avg_loss = fluid.layers.mean(loss)
  118. #每训练了200批次的数据,打印下当前Loss的情况
  119. if batch_id % 200 == 0:
  120. print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), acc.numpy()))
  121. #后向传播,更新参数的过程
  122. avg_loss.backward()
  123. optimizer.minimize(avg_loss)
  124. model.clear_gradients()
  125. #保存模型参数
  126. fluid.save_dygraph(model.state_dict(), 'mnist')

在训练过程中,我们会发现模型在训练样本集上的损失在不断减小。但这是否代表模型在未来的应用场景上依然有效?为了验证模型的有效性,通常将样本集合分成三份,训练集、校验集和测试集。

  • 训练集 :用于训练模型的参数,即训练过程中主要完成的工作。
  • 校验集 :用于对模型超参数的选择,比如网络结构的调整、正则化项权重的选择等。
  • 测试集 :用于模拟模型在应用后的真实效果。因为测试集没有参与任何模型优化或参数训练的工作,所以它对模型来说是完全未知的样本。在不以校验数据优化网络结构或模型超参数时,校验数据和测试数据的效果是类似的,均更真实的反映模型效果。

测试集上检验模型 

代码1

  1. with fluid.dygraph.guard():
  2. print('start evaluation .......')
  3. #加载模型参数
  4. model = MNIST()
  5. model_state_dict, _ = fluid.load_dygraph('mnist')
  6. model.load_dict(model_state_dict)
  7. model.eval()
  8. eval_loader = load_data('eval')
  9. acc_set = []
  10. avg_loss_set = []
  11. cnt=0
  12. sum=0
  13. for batch_id, data in enumerate(eval_loader()):
  14. x_data, y_data = data
  15. img = fluid.dygraph.to_variable(x_data)
  16. label = fluid.dygraph.to_variable(y_data)
  17. prediction= model(img)
  18. # prediction=prediction.numpy().astype('int32')
  19. label=label.numpy().astype('int64')
  20. for i in range(len(label)):
  21. # print(prediction[i],label[i])
  22. tmp=np.argsort(prediction[i].numpy())
  23. if(tmp[-1]==label[i]):
  24. # print('aaaa:',tmp[-1],label[i])
  25. cnt+=1
  26. sum+=len(label)
  27. # print(len(prediction))
  28. # print("hello:",prediction.numpy().astype('int32'),label.numpy().astype('int32'))
  29. # loss = fluid.layers.square_error_cost(input=prediction, label=label)
  30. # avg_loss = fluid.layers.mean(loss)
  31. # acc_set.append(float(acc.numpy()))
  32. # avg_loss_set.append(float(avg_loss.numpy()))
  33. #计算多个batch的平均损失和准确率
  34. # acc_val_mean = np.array(acc_set).mean()
  35. # avg_loss_val_mean = np.array(avg_loss_set).mean()
  36. print("acc:",cnt/sum)
  37. # print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))

代码2

  1. with fluid.dygraph.guard():
  2. print('start evaluation .......')
  3. #加载模型参数
  4. model = MNIST()
  5. model_state_dict, _ = fluid.load_dygraph('mnist')
  6. model.load_dict(model_state_dict)
  7. model.eval()
  8. eval_loader = load_data('eval')
  9. acc_set = []
  10. avg_loss_set = []
  11. for batch_id, data in enumerate(eval_loader()):
  12. x_data, y_data = data
  13. img = fluid.dygraph.to_variable(x_data)
  14. label = fluid.dygraph.to_variable(y_data)
  15. prediction, acc = model(img, label)
  16. loss = fluid.layers.cross_entropy(input=prediction, label=label)
  17. avg_loss = fluid.layers.mean(loss)
  18. acc_set.append(float(acc.numpy()))
  19. avg_loss_set.append(float(avg_loss.numpy()))
  20. #计算多个batch的平均损失和准确率
  21. acc_val_mean = np.array(acc_set).mean()
  22. avg_loss_val_mean = np.array(avg_loss_set).mean()
  23. print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))

 

单张图片预测

  1. # 读取一张本地的样例图片,转变成模型输入的格式
  2. def load_image(img_path):
  3. # 从img_path中读取图像,并转为灰度图
  4. im = Image.open(img_path).convert('L')
  5. im.show()
  6. im = im.resize((28, 28), Image.ANTIALIAS)
  7. im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)
  8. # 图像归一化
  9. im = 1.0 - im / 127.5
  10. return im
  11. # 定义预测过程
  12. with fluid.dygraph.guard():
  13. model = MNIST()
  14. params_file_path = 'mnist'
  15. # img_path = './work/example_1.png'
  16. img_p= './work/example_'
  17. # 加载模型参数
  18. cnt=0
  19. model_dict, _ = fluid.load_dygraph("mnist")
  20. model.load_dict(model_dict)
  21. model.eval()
  22. for i in range(10):
  23. img_path=img_p+str(i)+'.png'
  24. tensor_img = load_image(img_path)
  25. #模型反馈10个分类标签的对应概率
  26. results = model(fluid.dygraph.to_variable(tensor_img))
  27. #取概率最大的标签作为预测输出
  28. lab = np.argsort(results.numpy())
  29. # print(lab)
  30. print("本次预测的数字是: ", lab[0][-1],i)
  31. if(lab[0][-1]==i):
  32. cnt+=1
  33. print(cnt/10)

过拟合现象

对于样本量有限、但需要使用强大模型的复杂任务,模型很容易出现过拟合的表现,即在训练集上的损失小,在验证集或测试集上的损失较大,如 图2 所示。

 反之,如果模型在训练集和测试集上均损失较大,则称为欠拟合。过拟合表示模型过于敏感,学习到了训练数据中的一些误差,而这些误差并不是真实的泛化规律(可推广到测试集上的规律)。欠拟合表示模型还不够强大,还没有很好的拟合已知的训练样本,更别提测试样本了。因为欠拟合情况容易观察和解决,只要训练loss不够好,就不断使用更强大的模型即可,因此实际中我们更需要处理好过拟合的问题。

 

造成过拟合原因模型过于敏感,而训练数据量太少或其中的噪音太多

图3 所示,理想的回归模型是一条坡度较缓的抛物线,欠拟合的模型只拟合出一条直线,显然没有捕捉到真实的规律,但过拟合的模型拟合出存在很多拐点的抛物线,显然是过于敏感,也没有正确表达真实规律。

 

图4 所示,理想的分类模型是一条半圆形的曲线,欠拟合用直线作为分类边界,显然没有捕捉到真实的边界,但过拟合的模型拟合出很扭曲的分类边界,虽然对所有的训练数据正确分类,但对一些较为个例样本所做出的妥协,高概率真实的规律。

 

归结到深度学习中,假设模型也会犯错,通过分析可能会由于如下两种情况导致:

  1. 情况1:训练数据存在噪音,导致模型学到了噪音,而不是真实规律。

  2. 情况2:使用强大模型(表示空间大)的同时训练数据太少,导致在训练数据上表现良好的候选假设太多,锁定了一个“虚假正确”的假设。

对于情况1,我们使用数据清洗和修正来解决。 对于情况2,我们或者限制模型表示能力,或者收集更多的训练数据。

清洗训练数据中的错误,或收集更多训练数据往往是一句“正确的废话”,在任何时候我们都想获得更多更高质量的数据。在实际项目中,更快、更低成本可控制过拟合的方法,只有限制模型表示能力

 

正则化项

为了防止模型过拟合,在没有扩充样本量的可能下,只能降低模型的复杂度,可以通过限制参数的数量或可能取值(参数值尽量小)实现。

具体来说,在模型的优化目标(损失)中人为加入对参数规模的惩罚项。当参数越多或取值越大时,该惩罚项就越大。通过调整惩罚项的权重系数,可以使模型在“尽量减少训练损失”和“保持模型的泛化能力”之间取得平衡。泛化能力表示模型在没有见过的样本上依然有效。正则化项的存在,增加了模型在训练集上的损失。

飞桨框架支持为所有参数加上统一的正则化项,也支持为特定的参数添加正则化项。前者的实现如下代码所示,仅在优化器中设置regularization参数即可实现。使用参数regularization_coeff调节正则化项的权重,权重越大时,对模型复杂度的惩罚越高。实现代码如下所示。

 

  1. # 加载相关库
  2. import os
  3. import random
  4. import paddle
  5. import paddle.fluid as fluid
  6. from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
  7. import numpy as np
  8. from PIL import Image
  9. import gzip
  10. import json
  11. # 定义数据集读取器
  12. def load_data(mode='train'):
  13. # 读取数据文件
  14. datafile = './work/mnist.json.gz'
  15. print('loading mnist dataset from {} ......'.format(datafile))
  16. data = json.load(gzip.open(datafile))
  17. # 读取数据集中的训练集,验证集和测试集
  18. train_set, val_set, eval_set = data
  19. # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
  20. IMG_ROWS = 28
  21. IMG_COLS = 28
  22. # 根据输入mode参数决定使用训练集,验证集还是测试
  23. if mode == 'train':
  24. imgs = train_set[0]
  25. labels = train_set[1]
  26. elif mode == 'valid':
  27. imgs = val_set[0]
  28. labels = val_set[1]
  29. elif mode == 'eval':
  30. imgs = eval_set[0]
  31. labels = eval_set[1]
  32. # 获得所有图像的数量
  33. imgs_length = len(imgs)
  34. # 验证图像数量和标签数量是否一致
  35. assert len(imgs) == len(labels), \
  36. "length of train_imgs({}) should be the same as train_labels({})".format(
  37. len(imgs), len(labels))
  38. index_list = list(range(imgs_length))
  39. # 读入数据时用到的batchsize
  40. BATCHSIZE = 100
  41. # 定义数据生成器
  42. def data_generator():
  43. # 训练模式下,打乱训练数据
  44. if mode == 'train':
  45. random.shuffle(index_list)
  46. imgs_list = []
  47. labels_list = []
  48. # 按照索引读取数据
  49. for i in index_list:
  50. # 读取图像和标签,转换其尺寸和类型
  51. img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
  52. label = np.reshape(labels[i], [1]).astype('int64')
  53. imgs_list.append(img)
  54. labels_list.append(label)
  55. # 如果当前数据缓存达到了batch size,就返回一个批次数据
  56. if len(imgs_list) == BATCHSIZE:
  57. yield np.array(imgs_list), np.array(labels_list)
  58. # 清空数据缓存列表
  59. imgs_list = []
  60. labels_list = []
  61. # 如果剩余数据的数目小于BATCHSIZE,
  62. # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
  63. if len(imgs_list) > 0:
  64. yield np.array(imgs_list), np.array(labels_list)
  65. return data_generator
  66. # 定义模型结构
  67. class MNIST(fluid.dygraph.Layer):
  68. def __init__(self):
  69. super(MNIST, self).__init__()
  70. # 定义一个卷积层,使用relu激活函数
  71. self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  72. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  73. self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  74. # 定义一个卷积层,使用relu激活函数
  75. self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  76. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  77. self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  78. # 定义一个全连接层,输出节点数为10
  79. self.fc = Linear(input_dim=980, output_dim=10, act='softmax')
  80. # 定义网络的前向计算过程
  81. def forward(self, inputs, label=None):
  82. x = self.conv1(inputs)
  83. x = self.pool1(x)
  84. x = self.conv2(x)
  85. x = self.pool2(x)
  86. x = fluid.layers.reshape(x, [x.shape[0], 980])
  87. x = self.fc(x)
  88. if label is not None:
  89. acc = fluid.layers.accuracy(input=x, label=label)
  90. return x, acc
  91. else:
  92. return x
  93. #调用加载数据的函数
  94. train_loader = load_data('train')
  95. #在使用GPU机器时,可以将use_gpu变量设置成True
  96. use_gpu = True
  97. place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
  98. with fluid.dygraph.guard(place):
  99. model = MNIST()
  100. model.train()
  101. #四种优化算法的设置方案,可以逐一尝试效果
  102. optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  103. #optimizer = fluid.optimizer.MomentumOptimizer(learning_rate=0.01, momentum=0.9, parameter_list=model.parameters())
  104. #optimizer = fluid.optimizer.AdagradOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  105. #optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  106. #各种优化算法均可以加入正则化项,避免过拟合,参数regularization_coeff调节正则化项的权重
  107. #optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1),parameter_list=model.parameters()))
  108. optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.01, regularization=fluid.regularizer.L2Decay(regularization_coeff=0.1),
  109. parameter_list=model.parameters())
  110. EPOCH_NUM = 10
  111. for epoch_id in range(EPOCH_NUM):
  112. for batch_id, data in enumerate(train_loader()):
  113. #准备数据,变得更加简洁
  114. image_data, label_data = data
  115. image = fluid.dygraph.to_variable(image_data)
  116. label = fluid.dygraph.to_variable(label_data)
  117. #前向计算的过程,同时拿到模型输出值和分类准确率
  118. predict, acc = model(image, label)
  119. #计算损失,取一个批次样本损失的平均值
  120. loss = fluid.layers.cross_entropy(predict, label)
  121. avg_loss = fluid.layers.mean(loss)
  122. #每训练了100批次的数据,打印下当前Loss的情况
  123. if batch_id % 100 == 0:
  124. print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), acc.numpy()))
  125. #后向传播,更新参数的过程
  126. avg_loss.backward()
  127. optimizer.minimize(avg_loss)
  128. model.clear_gradients()
  129. #保存模型参数
  130. fluid.save_dygraph(model.state_dict(), 'mnist')

可视化分析

训练模型时,经常需要观察模型的评价指标,分析模型的优化过程,以确保训练是有效的。可视化分析有两种工具:Matplotlib库和tb-paddle。

  • Matplotlib库:Matplotlib库是Python中使用的最多的2D图形绘图库,它有一套完全仿照MATLAB的函数形式的绘图接口,使用轻量级的PLT库(Matplotlib)作图是非常简单的。
  • tb-paddle:如果期望使用更加专业的作图工具,可以尝试tb-paddle。tb-paddle能够有效地展示飞桨框架在运行过程中的计算图、各种指标随着时间的变化趋势以及训练中使用到的数据信息。

 使用Matplotlib库绘制损失随训练下降的曲线图

  1. # 加载相关库
  2. import os
  3. import random
  4. import paddle
  5. import paddle.fluid as fluid
  6. from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
  7. #引入matplotlib库
  8. import matplotlib.pyplot as plt
  9. import numpy as np
  10. from PIL import Image
  11. import gzip
  12. import json
  13. # 定义数据集读取器
  14. def load_data(mode='train'):
  15. # 读取数据文件
  16. datafile = './work/mnist.json.gz'
  17. print('loading mnist dataset from {} ......'.format(datafile))
  18. data = json.load(gzip.open(datafile))
  19. # 读取数据集中的训练集,验证集和测试集
  20. train_set, val_set, eval_set = data
  21. # 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
  22. IMG_ROWS = 28
  23. IMG_COLS = 28
  24. # 根据输入mode参数决定使用训练集,验证集还是测试
  25. if mode == 'train':
  26. imgs = train_set[0]
  27. labels = train_set[1]
  28. elif mode == 'valid':
  29. imgs = val_set[0]
  30. labels = val_set[1]
  31. elif mode == 'eval':
  32. imgs = eval_set[0]
  33. labels = eval_set[1]
  34. # 获得所有图像的数量
  35. imgs_length = len(imgs)
  36. # 验证图像数量和标签数量是否一致
  37. assert len(imgs) == len(labels), \
  38. "length of train_imgs({}) should be the same as train_labels({})".format(
  39. len(imgs), len(labels))
  40. index_list = list(range(imgs_length))
  41. # 读入数据时用到的batchsize
  42. BATCHSIZE = 100
  43. # 定义数据生成器
  44. def data_generator():
  45. # 训练模式下,打乱训练数据
  46. if mode == 'train':
  47. random.shuffle(index_list)
  48. imgs_list = []
  49. labels_list = []
  50. # 按照索引读取数据
  51. for i in index_list:
  52. # 读取图像和标签,转换其尺寸和类型
  53. img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
  54. label = np.reshape(labels[i], [1]).astype('int64')
  55. imgs_list.append(img)
  56. labels_list.append(label)
  57. # 如果当前数据缓存达到了batch size,就返回一个批次数据
  58. if len(imgs_list) == BATCHSIZE:
  59. yield np.array(imgs_list), np.array(labels_list)
  60. # 清空数据缓存列表
  61. imgs_list = []
  62. labels_list = []
  63. # 如果剩余数据的数目小于BATCHSIZE,
  64. # 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
  65. if len(imgs_list) > 0:
  66. yield np.array(imgs_list), np.array(labels_list)
  67. return data_generator
  68. # 定义模型结构
  69. class MNIST(fluid.dygraph.Layer):
  70. def __init__(self):
  71. super(MNIST, self).__init__()
  72. # 定义一个卷积层,使用relu激活函数
  73. self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  74. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  75. self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  76. # 定义一个卷积层,使用relu激活函数
  77. self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
  78. # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
  79. self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
  80. # 定义一个全连接层,输出节点数为10
  81. self.fc = Linear(input_dim=980, output_dim=10, act='softmax')
  82. # 定义网络的前向计算过程
  83. def forward(self, inputs, label=None):
  84. x = self.conv1(inputs)
  85. x = self.pool1(x)
  86. x = self.conv2(x)
  87. x = self.pool2(x)
  88. x = fluid.layers.reshape(x, [x.shape[0], 980])
  89. x = self.fc(x)
  90. if label is not None:
  91. acc = fluid.layers.accuracy(input=x, label=label)
  92. return x, acc
  93. else:
  94. return x
  95. #调用加载数据的函数
  96. train_loader = load_data('train')
  97. #在使用GPU机器时,可以将use_gpu变量设置成True
  98. use_gpu = True
  99. place = fluid.CUDAPlace(0) if use_gpu else fluid.CPUPlace()
  100. with fluid.dygraph.guard(place):
  101. model = MNIST()
  102. model.train()
  103. #四种优化算法的设置方案,可以逐一尝试效果
  104. optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
  105. EPOCH_NUM = 10
  106. iter=0
  107. iters=[]
  108. losses=[]
  109. for epoch_id in range(EPOCH_NUM):
  110. for batch_id, data in enumerate(train_loader()):
  111. #准备数据,变得更加简洁
  112. image_data, label_data = data
  113. image = fluid.dygraph.to_variable(image_data)
  114. label = fluid.dygraph.to_variable(label_data)
  115. #前向计算的过程,同时拿到模型输出值和分类准确率
  116. predict, acc = model(image, label)
  117. #计算损失,取一个批次样本损失的平均值
  118. loss = fluid.layers.cross_entropy(predict, label)
  119. avg_loss = fluid.layers.mean(loss)
  120. #每训练了100批次的数据,打印下当前Loss的情况
  121. if batch_id % 100 == 0:
  122. print("epoch: {}, batch: {}, loss is: {}, acc is {}".format(epoch_id, batch_id, avg_loss.numpy(), acc.numpy()))
  123. iters.append(iter)
  124. losses.append(avg_loss.numpy())
  125. iter = iter + 100
  126. #后向传播,更新参数的过程
  127. avg_loss.backward()
  128. optimizer.minimize(avg_loss)
  129. model.clear_gradients()
  130. #保存模型参数
  131. fluid.save_dygraph(model.state_dict(), 'mnist')

 

  1. #画出训练过程中Loss的变化曲线
  2. plt.figure()
  3. plt.title("train loss", fontsize=24)
  4. plt.xlabel("iter", fontsize=14)
  5. plt.ylabel("loss", fontsize=14)
  6. plt.plot(iters, losses,color='red',label='train loss')
  7. plt.grid()
  8. plt.show()

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/92692
推荐阅读
相关标签
  

闽ICP备14008679号