神经网络 | ⑨ MNIST 手写数字识别的 FCNN 训练实现

M N I S T MNIST MNIST 手写数字识别的训练实现

前面的章节中,我们实现了一个简单的神经网络推理过程。但那时网络的权重参数直接引用了外部的 sample_weight.pkl 文件,相当于拿了一个"已经训练好的成品模型"来用,网络本身并没有学习能力。

现在,我们将结合前面学到的损失函数、反向传播、梯度下降等知识,让神经网络从数据中自己学习 。具体来说,网络将经历完整的训练流程:

随机初始化参数 → 前向传播计算预测值 → 损失函数量化误差 → 反向传播计算梯度 → 根据梯度更新参数 → 反复迭代直到模型收敛 随机初始化参数 → 前向传播计算预测值 → 损失函数量化误差 → 反向传播计算梯度 → 根据梯度更新参数 → 反复迭代直到模型收敛 随机初始化参数→前向传播计算预测值→损失函数量化误差→反向传播计算梯度→根据梯度更新参数→反复迭代直到模型收敛

最终网络自行训练出权重参数,不再依赖外部文件,真正具备"从数据中学习"的能力。

本文的相关 mnist 数据集等详见附录,本文代码位于项目目录 train/fcnn



数据集

  • 介绍 ------ MNIST 是深度学习领域最经典的入门数据集之一,由美国国家标准与技术研究所收集整理。它包含 70000 张手写数字灰度图像:

  • 结构 ------ MNIST 的图像数据是 28×28 像素的手写数字灰度图像(1通道),各个像素取值在 0255 之间,每个图像数据都相应地标有 7 21 等标签。训练图像有 6 万张, 测试图像有 1 万张。

    属性 说明 属性 说明
    图像内容 手写数字 0~9 --- ---
    图像尺寸 28 × 28 像素(灰度图) 每张图像 784 个像素值(28×28 展平)
    训练集 60000 测试集 10000


网络设计

  • 目标 ------ 设计并实现一个完整的神经网络,使其具备从数据中学习对新数据进行推理的能力:

    • 训练 :在 M N I S T MNIST MNIST 数据集上自行学习,无需依赖外部权重文件,最终保存训练好的参数。
    • 推理 :输入一张 28×28 的手写数字图像,网络基于训练阶段学到的知识,输出它属于 0~9 中哪个数字。
  • 网络结构

    • 设计的神经网络的输入层有 784 784 784 个神经元,输出层有 10 10 10 个神经元
    • 有 1 1 1 个隐藏层,隐藏层神经元数可自由设定(如 50 50 50)
    • 隐藏层采用 Sigmoid 函数,输出层采用 Softmax 函数
    • 网络结构如下:

    I n p u t ( 784 ) → A f f i n e → S i g m o i d A f f i n e → S o f t m a x → O u t p u t ( 10 ) Input(784) → Affine → SigmoidAffine → Softmax → Output(10) Input(784)→Affine→SigmoidAffine→Softmax→Output(10)

    与前面前向传播的三层网络(两个隐藏层)相比,SimpleFCNet 是一个更简单的两层全连接网络,可以视作前文 ThreeLayerNet 的简化版本



网络实现

初始化参数
  • 初始化神经网络的参数

    • 权重参数 ------ 随机生成权重,其中 weight_init_std 参数用于控制正态分布随机值的分散程度
    • 偏置参数 ------ 置零
    python 复制代码
    class TwoLayerNet:
        """
        简单两层全连接神经网络 Fully-Connected Neural Network, FCNN
        网络结构:[Affine → Sigmoid] → [Affine → Softmax]
        """
    
        def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
            """
            初始化两层全连接神经网络,随机生成权重并置零偏置。
    
            :param input_size: 输入层神经元数量(如 MNIST 为 784)
            :param hidden_size: 隐藏层神经元数量(可自由设定,如 50)
            :param output_size: 输出层神经元数量(分类任务为类别数,如 MNIST 为 10)
            :param weight_init_std: 权重初始化的标准差,用于控制正态分布随机值的分散程度(默认为 0.01)
            """
            self.params = {
                'W1': weight_init_std * np.random.randn(input_size, hidden_size),
                'b1': np.zeros(hidden_size),
                'W2': weight_init_std * np.random.randn(hidden_size, output_size),
                'b2': np.zeros(output_size)
            }
前向传播
  • 定义神经网络结构,用于前向传播。这里的网络共两层,具体如下:

    python 复制代码
        def predict(self, x):
            """
            前向传播,结构:Input → [Affine → Sigmoid] → [Affine → Softmax] → Output
            :param x: 输入数据
            :return: 每个类别的概率值
            """
            W1, W2 = self.params['W1'], self.params['W2']
            b1, b2 = self.params['b1'], self.params['b2']
    
            a1 = np.dot(x, W1) + b1
            z1 = sigmoid(a1)
            a2 = np.dot(z1, W2) + b2
            y = softmax(a2)
            return y
损失函数
  • 计算损失函数,采用交叉熵误差 C r o s s E n t r o p y E r r o r Cross Entropy Error CrossEntropyError 方法,方法实现由 common\functions.py 引入

    python 复制代码
        def loss(self, x, t):
            """
            计算损失函数,采用交叉熵误差 Cross Entropy Error
            :param x: 输入数据
            :param t: 监督标签
            :return: 前向传播的损失值
            """
            y = self.predict(x)
            return cross_entropy_error(y, t)
识别准确率
  • 计算神经网络的识别准确率

    python 复制代码
        def accuracy(self, x, t):
            """
            计算识别准确率
            :param x: 输入数据
            :param t: 监督标签
            :return: 准确率
            """
            y = self.predict(x)
            y = np.argmax(y, axis=1)
            t = np.argmax(t, axis=1)
            return np.sum(y == t) / float(x.shape[0])
梯度计算
  • 在神经网络中,计算梯度主要有两种方法:

    • 数值微分法 ------ 基于导数定义。实现简单、结果可靠,常用于验证反向传播是否正确,但计算量大,训练时不采用
    • 误差反向传播法 ------ 基于链式法则,一次前向加一次反向即可算出所有参数的梯度,训练的实际选择
    python 复制代码
        def numerical_gradient(self, x, t):
            """
            梯度计算(数值微分法)
            :param x: 输入数据
            :param t: 教师标签
            :return: 具有各层的梯度的字典变量
                grads['W1']、grads['W2']、...是各层的权重
                grads['b1']、grads['b2']、...是各层的偏置
            """
            loss_W = lambda W: self.loss(x, t)
            grads = {
                'W1': numerical_gradient(loss_W, self.params['W1']),
                'b1': numerical_gradient(loss_W, self.params['b1']),
                'W2': numerical_gradient(loss_W, self.params['W2']),
                'b2': numerical_gradient(loss_W, self.params['b2'])}
            return grads
    python 复制代码
        def gradient(self, x, t):
            """
            梯度计算(误差反向传播法)
            :param x: 输入数据
            :param t: 教师标签
            :return: 具有各层的梯度的字典变量
                grads['W1']、grads['W2']、...是各层的权重
                grads['b1']、grads['b2']、...是各层的偏置
            """
            W1, W2 = self.params['W1'], self.params['W2']
            b1, b2 = self.params['b1'], self.params['b2']
            grads = {}
            batch_num = x.shape[0]
            # 正向传播
            a1 = np.dot(x, W1) + b1
            z1 = sigmoid(a1)
            a2 = np.dot(z1, W2) + b2
            y = softmax(a2)
            # 反向传播
            dy = (y - t) / batch_num
            grads['W2'] = np.dot(z1.T, dy)
            grads['b2'] = np.sum(dy, axis=0)
            da1 = np.dot(dy, W2.T)
            dz1 = sigmoid_grad(a1) * da1
            grads['W1'] = np.dot(x.T, dz1)
            grads['b1'] = np.sum(dz1, axis=0)
            return grads
保存参数
  • 使用 pickle 库,把训练好的参数进行保存,用于后续推理

    python 复制代码
        def save_params(self, file_name="params.pkl"):
            """
            保存训练好的参数到文件
            :param file_name: 保存的文件名,默认为 "params.pkl"
            :return: None
            """
            params = {}
            for key, val in self.params.items():
                params[key] = val
            with open(file_name, 'wb') as f:
                pickle.dump(params, f)


网络训练

基本实现

  • 如何训练神经网络?一个最朴素的想法是,一次把全部训练数据喂给网络,让网络去学习:

    • 每轮( e p o c h epoch epoch)用整个训练集做一次前向传播、计算损失、反向传播、更新参数。
    • 训练到一定轮数,神经网络的参数自然就拟合
  • 基于上面的思想,实现代码:

    1. 读取数据集,初始化神经网络
    2. 初始化训练超参数,设置轮数 e p o c h s epochs epochs 为 100 100 100 轮,学习率 l e a r n i n g _ r a t e learning\_rate learning_rate 为 0.8 0.8 0.8
    3. 设置数组记录训练过程的数据,用于可视化
      • train_loss_list ------ 训练损失列表
      • train_acc_list ------ 训练集准确率列表
      • test_acc_list ------ 测试集准确率列表
    4. 开始训练,使用 for 循环 epochs 次,每一次都是一次完整的前向传播、计算损失、反向传播、更新参数流程
    5. 最后根据训练过程数据绘制图像,展示训练过程准确率 accuracy 的变化
    python 复制代码
    import numpy as np
    import matplotlib.pyplot as plt
    
    from dataset.mnist import load_mnist
    from TwoLayerNet import TwoLayerNet
    
    # 读入数据
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    
    # 初始化全连接神经网络
    network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
    
    # 初始化训练参数
    epochs = 100  # 对所有训练集数据训练 1000 次
    learning_rate = 0.8  # 学习率
    
    # 记录训练数据
    train_loss_list = []
    train_acc_list = []
    test_acc_list = []
    
    # 开始训练
    for epoch in range(epochs):
        # 计算梯度(一次性使用全部 60000 张图)
        grad = network.gradient(x_train, t_train)
        # 更新参数
        for key in ('W1', 'b1', 'W2', 'b2'):
            network.params[key] -= learning_rate * grad[key]
        # 计算损失(全部数据)
        loss = network.loss(x_train, t_train)
        train_loss_list.append(loss)
        # 评估准确率
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print(f"epoch {epoch + 1}/{epochs} | loss: {loss:.4f} | train acc: {train_acc:.4f} | test acc: {test_acc:.4f}")
    
    # 绘制训练图形
    x = np.arange(len(train_acc_list))
    plt.plot(x, train_acc_list, label='train acc')
    plt.plot(x, test_acc_list, label='test acc', linestyle='--')
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()
  • 运行结果

    python 复制代码
    epoch 1/100 | loss: 2.3008 | train acc: 0.1124 | test acc: 0.1135
    epoch 2/100 | loss: 2.3003 | train acc: 0.1124 | test acc: 0.1135
    epoch 3/100 | loss: 2.2997 | train acc: 0.1124 | test acc: 0.1135
    ...
    epoch 99/100 | loss: 0.6485 | train acc: 0.8376 | test acc: 0.8413
    epoch 100/100 | loss: 0.6429 | train acc: 0.8387 | test acc: 0.8421
  • 存在的问题

    • 可以看到,准确率确实在逐步提高 ------ 从起初的 11 % 11\% 11% 到最终的 84 % 84\% 84%,说明神经网络正在学习
    • 但是这里有一个致命问题 ------ 训练过程太慢,计算量太大,一次训练要几分钟以上
    • 原因在于,网络每步要把全部 60000 60000 60000 张图算一遍梯度才能更新一次参数,每次更新都代价高昂 ------ 内存压力大、单步耗时长,这就导致了每次迭代的时间都慢得难以忍受

基于 m i n i − b a t c h mini-batch mini−batch 的实现

  • 基于上面的问题,我们换个思路:

    • 不再一次性使用全部 60000 张图,而是每次只从训练集中随机抽取一小批数据
    • 用这批数据的平均梯度作为整体梯度的近似,来更新参数
  • 基于上面的思想,引入几个关键概念

    • 批( b a t c h _ s i z e batch\_size batch_size) ------ 每次参数更新所使用的一批样本数量。下面的代码中取 100 100 100,表示每次只用 100 100 100 张图片来估算梯度,远小于训练集总量 60000 60000 60000 张图片

    • 步( i t e r iter iter) ------ 用一批( b a t c h batch batch)数据进行的一次完整的前向传播、计算损失、反向传播、更新参数的过程,称为一步

    • 轮( e p o c h epoch epoch)------ 全部训练数据被完整遍历一遍,称为一轮

      一轮不再是指一次完整的训练流程(更新),而是指把全部 60000 60000 60000 张图都用过一遍

      每步用一批 100 100 100 个数据, 600 600 600 步下来, 60000 60000 60000 张图刚好全部参与过一次训练

    • 步每轮( i t e r _ p e r _ e p o c h iter\_per\_epoch iter_per_epoch)------ 把全部训练数据都用过一遍需要多少步

  • 根据上面的概念,我们可以得到下面的关系式:

    i t e r _ p e r _ e p o c h = t r a i n _ s i z e b a t c h _ s i z e iter\_per\_epoch = \frac{train\_size}{batch\_size} iter_per_epoch=batch_sizetrain_size

    i t e r s = e p o c h s × i t e r _ p e r _ e p o c h iters = epochs \times iter\_per\_epoch iters=epochs×iter_per_epoch

    以 M I N I S T MINIST MINIST 数据集为例,假设:

    • 训练集总量 t r a i n _ s i z e = 600000 train\_size=600000 train_size=600000 张图片,一批取 b a t c h _ s i z e = 100 batch\_size=100 batch_size=100 张图片
    • 设定进行 e p o c h = 20 epoch=20 epoch=20 轮,即把全部 60000 60000 60000 张图遍历 20 20 20 轮

    那么有:

    • 一轮包含 60000 / 100 = 600 60000/100=600 60000/100=600 步,即 i t e r _ p e r _ e p o c h = 600 iter\_per\_epoch = 600 iter_per_epoch=600
    • 20 20 20 轮一共有 20 × 600 = 12000 20 \times 600=12000 20×600=12000 步,即 i t e r = 12000 iter=12000 iter=12000
  • 基于上面的思想,实现代码:

    1. 读取数据集,初始化神经网络

    2. 初始化训练超参数,设置轮数 e p o c h s epochs epochs 为 20 20 20 轮,学习率 l e a r n i n g _ r a t e learning\_rate learning_rate 为 0.1 0.1 0.1,其他参数计算可得

      python 复制代码
      epochs = 20					# 训练轮数:整个训练集被完整遍历 20 次
      learning_rate = 0.1 
      
      train_size = x_train.shape[0]  	# 训练集图片数:MNIST 共 60000 张
      batch_size = 100  				# 批大小:每步随机抽取 100 张图
      
      iter_per_epoch = max(train_size / batch_size, 1)  # 一轮包括 60000张 / 100张 = 600步
      iters = int(epochs * iter_per_epoch)  			# 20轮包括 20轮 × 600步 = 12000步
    3. 设置数组记录训练过程的数据,用于可视化

    4. 开始训练,使用 for 循环 iter 次,每次取一批数据,进行一次一次完整的训练流程

      python 复制代码
      batch_mask = np.random.choice(train_size, batch_size)
      x_batch = x_train[batch_mask]
      t_batch = t_train[batch_mask]
    5. 最后根据训练过程数据绘制图像,展示训练过程准确率 accuracy 的变化

    python 复制代码
    import numpy as np
    import matplotlib.pyplot as plt
    
    from dataset.mnist import load_mnist
    from TwoLayerNet import TwoLayerNet
    
    # 读入数据
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    
    # 初始化全连接神经网络
    network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
    
    # 初始化训练参数
    epochs = 20  # 训练轮数:整个训练集被完整遍历 20 次
    learning_rate = 0.1
    train_size = x_train.shape[0]  # 训练集图片数:MNIST 共 60000 张
    batch_size = 100  # 批大小:每步随机抽取 100 张图
    iter_per_epoch = max(train_size / batch_size, 1)  # 一轮包括 60000/100=600步
    iters = int(epochs * iter_per_epoch)  # 20轮包括 20×600=12000步
    
    # 记录训练数据
    train_loss_list = []
    train_acc_list = []
    test_acc_list = []
    
    # 开始训练
    current_epoch = 0
    for current_iter in range(iters):
        # 批处理
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]
    
        # 神经网络计算梯度
        grad = network.gradient(x_batch, t_batch)
    
        # 神经网络更新参数
        for key in ('W1', 'b1', 'W2', 'b2'):
            network.params[key] -= learning_rate * grad[key]
    
        # 神经网络计算损失
        loss = network.loss(x_batch, t_batch)
        train_loss_list.append(loss)
    
        # 评估准确率:每完成一轮(epoch)才评估一次,不在每一步都记录
        if current_iter % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)
            current_epoch += 1
            print(f"epoch {current_epoch}/{epochs} | loss: {loss:.4f} | train acc:{train_acc:.4f} | test acc: {test_acc:.4f}")
    
    # 绘制训练图形
    x = np.arange(len(train_acc_list))
    plt.plot(x, train_acc_list, label='train acc')
    plt.plot(x, test_acc_list, label='test acc', linestyle='--')
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.ylim(0, 1.0)
    plt.legend(loc='lower right')
    plt.show()
  • 运行结果

    python 复制代码
    epoch 1/20 | loss: 2.2924 | train acc:0.0993 | test acc: 0.1032
    epoch 2/20 | loss: 0.7921 | train acc:0.7853 | test acc: 0.7897
    epoch 3/20 | loss: 0.4136 | train acc:0.8770 | test acc: 0.8794
    ...
    epoch 18/20 | loss: 0.2125 | train acc:0.9481 | test acc: 0.9471
    epoch 19/20 | loss: 0.1322 | train acc:0.9498 | test acc: 0.9484
    epoch 20/20 | loss: 0.2213 | train acc:0.9513 | test acc: 0.9497

    可以看到,我们仅使用了 20 20 20 轮便使准确率达到了 0.9513 ,并且计算和收敛速度明显加快,这便是 m i n i − b a t c h mini-batch mini−batch 的优势



封装为类

将上面训练的代码整理为一个名为 Trainer 的类,统一封装核心的训练流程,方便后续训练和测试时调用

类的定义
python 复制代码
import numpy as np
import matplotlib.pyplot as plt


class Trainer:
    def __init__(self, network, x_train, t_train, x_test, t_test,
                 epochs=20, mini_batch_size=100, learning_rate=0.1,
                 evaluate_sample_num_per_epoch=None, verbose=True):
        """
        训练器类:封装了神经网络的完整训练流程(数据洗牌、小批量迭代、参数更新、定期评估)
        :param network: 待训练的神经网络实例
        :param x_train: 训练集输入数据,形状 (N_train, input_size)
        :param t_train: 训练集标签数据,形状 (N_train,)
        :param x_test: 测试集输入数据,形状 (N_test, input_size)
        :param t_test: 测试集标签数据,形状 (N_test,)
        :param epochs: 训练的总轮数,即完整遍历整个训练集的次数
        :param mini_batch_size: 每次梯度更新使用的小批量样本数
        :param learning_rate: 学习率
        :param evaluate_sample_num_per_epoch: 每轮评估时抽样的样本数,None 表示使用全部测试集
        :param verbose: 是否在训练过程中打印详细日志(损失值、准确率等)
        """
        # 初始化网络
        self.network = network

        # 初始化数据
        self.x_train = x_train
        self.t_train = t_train
        self.x_test = x_test
        self.t_test = t_test

        # 初始化训练相关参数
        self.epochs = epochs
        self.learning_rate = learning_rate
        self.batch_size = mini_batch_size
        self.train_size = x_train.shape[0]
        self.iter_per_epoch = max(self.train_size / mini_batch_size, 1)
        self.iter = int(epochs * self.iter_per_epoch)
        self.current_epoch = 0  # 轮数计数器
        self.current_iter = 0  # 步数计数器

        # 记录训练过程数据
        self.train_loss_list = []  # 训练损失列表,记录每个mini-batch训练后的损失值
        self.train_acc_list = []  # 训练准确率列表,记录每个epoch结束时的训练集准确率
        self.test_acc_list = []  # 测试准确率列表,记录每个epoch结束时的测试集准确率

        # 是否打印训练过程详细信息
        self.verbose = verbose

        # 初始化相关配置
        self.evaluate_sample_num_per_epoch = evaluate_sample_num_per_epoch  # 每轮评估时使用的样本数(None表示使用全部数据)

    def train_step(self):
        # 批处理
        batch_mask = np.random.choice(self.train_size, self.batch_size)  # 随机生成批次大小的索引
        x_batch = self.x_train[batch_mask]  # 获取批次输入数据
        t_batch = self.t_train[batch_mask]  # 获取批次标签数据

        # 神经网络计算梯度
        grads = self.network.gradient(x_batch, t_batch)

        # 神经网络更新参数
        for key in ('W1', 'b1', 'W2', 'b2'):
            self.network.params[key] -= self.learning_rate * grads[key]

        # 神经网络计算损失
        loss = self.network.loss(x_batch, t_batch)
        self.train_loss_list.append(loss)  # 记录当前批次的损失值
        if self.verbose: print("train loss: " + str(loss))  # 打印训练损失

        # 判断是否完成一个epoch:当迭代次数达到每个epoch应有的迭代次数时
        if self.current_iter % self.iter_per_epoch == 0:
            self.current_epoch += 1

            # 准备评估数据:默认使用全部数据,如果指定了评估样本数则使用前N个样本
            x_train_sample, t_train_sample = self.x_train, self.t_train
            x_test_sample, t_test_sample = self.x_test, self.t_test
            if not self.evaluate_sample_num_per_epoch is None:
                t = self.evaluate_sample_num_per_epoch  # 获取评估样本数
                x_train_sample, t_train_sample = self.x_train[:t], self.t_train[:t]  # 取前t个训练样本
                x_test_sample, t_test_sample = self.x_test[:t], self.t_test[:t]  # 取前t个测试样本

            # 计算准确率:分别计算训练集和测试集上的准确率
            train_acc = self.network.accuracy(x_train_sample, t_train_sample)
            test_acc = self.network.accuracy(x_test_sample, t_test_sample)
            self.train_acc_list.append(train_acc)  # 记录训练准确率
            self.test_acc_list.append(test_acc)  # 记录测试准确率

            if self.verbose:
                print("=== epoch:" + str(self.current_epoch) +
                      ", train acc:" + str(train_acc) +
                      ", test acc:" + str(test_acc) + " ===\n")

        self.current_iter += 1

    def display_accuracy(self):
        x = np.arange(len(self.train_acc_list))
        plt.plot(x, self.train_acc_list, label='train acc')
        plt.plot(x, self.test_acc_list, label='test acc', linestyle='--')
        plt.xlabel("epochs")
        plt.ylabel("accuracy")
        plt.ylim(0, 1.0)
        plt.legend(loc='lower right')
        plt.show()

    def train(self):
        # 循环执行训练步骤,直到达到总迭代次数
        for i in range(self.iter):
            self.train_step()  # 执行一步训练(包含前向传播、反向传播、参数更新)
        # 计算准确率
        test_acc = self.network.accuracy(self.x_test, self.t_test)
        # 展示结果
        if self.verbose:
            print("\n=============== Final Test Accuracy ===============")
            print("test acc:" + str(test_acc))
            print("===================================================")
            self.display_accuracy()
类的使用
python 复制代码
from dataset.mnist import load_mnist
from TwoLayerNet import TwoLayerNet
from trainer import *

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

# 初始化全连接神经网络FCNN
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

# 初始化训练类
max_epochs = 20
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=max_epochs, mini_batch_size=100,
                  learning_rate=0.1, evaluate_sample_num_per_epoch=1000)

# 开始训练
trainer.train()


附录 :手写数字识别 Demo 项目地址:MnistRecognition: A simple handwritten digit recognition system using the MNIST dataset and a neural network.
参考文献

1 斋藤康毅. 深度学习入门:基于Python的理论与实现M. 陆宇杰, 译. 北京: 人民邮电出版社, 2018.