Main Content

分析训练后的浅层神经网络性能

本主题介绍典型浅层神经网络工作流的一部分。有关详细信息和其他步骤,请参阅多层浅层神经网络与反向传播训练。要了解如何监控深度学习训练进度,请参阅监控深度学习训练进度

训练与应用多层浅层神经网络中所述的训练结束后,您可以检查网络性能,并确定是否需要对训练过程、网络架构或数据集进行任何更改。首先检查训练记录 tr,这是训练函数返回的第二个参量。

tr
tr = struct with fields:
        trainFcn: 'trainlm'
      trainParam: [1x1 struct]
      performFcn: 'mse'
    performParam: [1x1 struct]
        derivFcn: 'defaultderiv'
       divideFcn: 'dividerand'
      divideMode: 'sample'
     divideParam: [1x1 struct]
        trainInd: [2 3 5 6 9 10 11 13 14 15 18 19 20 22 23 24 25 29 30 31 33 35 36 38 39 40 41 44 45 46 47 48 49 50 51 52 54 55 56 57 58 59 62 64 65 66 68 70 73 76 77 79 80 81 84 85 86 88 90 91 92 93 94 95 96 97 98 99 100 101 102 103 ... ] (1x176 double)
          valInd: [1 8 17 21 27 28 34 43 63 71 72 74 75 83 106 124 125 134 140 155 157 158 162 165 166 175 177 181 187 191 196 201 205 212 233 243 245 250]
         testInd: [4 7 12 16 26 32 37 42 53 60 61 67 69 78 82 87 89 104 105 110 111 112 133 135 149 151 153 163 170 189 203 216 217 222 226 235 246 247]
            stop: 'Training finished: Met validation criterion'
      num_epochs: 9
       trainMask: {[NaN 1 1 NaN 1 1 NaN NaN 1 1 1 NaN 1 1 1 NaN NaN 1 1 1 NaN 1 1 1 1 NaN NaN NaN 1 1 1 NaN 1 NaN 1 1 NaN 1 1 1 1 NaN NaN 1 1 1 1 1 1 1 1 1 NaN 1 1 1 1 1 1 NaN NaN 1 NaN 1 1 1 NaN 1 NaN 1 NaN NaN 1 NaN NaN 1 1 NaN 1 1 ... ] (1x252 double)}
         valMask: {[1 NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN 1 NaN NaN NaN NaN NaN 1 1 NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... ] (1x252 double)}
        testMask: {[NaN NaN NaN 1 NaN NaN 1 NaN NaN NaN NaN 1 NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN 1 NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN NaN NaN NaN ... ] (1x252 double)}
      best_epoch: [3]
            goal: [0]
          states: {'epoch'  'time'  'perf'  'vperf'  'tperf'  'mu'  'gradient'  'val_fail'}
           epoch: [0 1 2 3 4 5 6 7 8 9]
            time: [2.5762 2.8886 2.8949 2.9049 2.9372 2.9440 2.9516 2.9609 2.9678 2.9734]
            perf: [672.2031 94.8128 43.7489 12.3078 9.7063 8.9212 8.0412 7.3500 6.7890 6.3064]
           vperf: [675.3788 76.9621 74.0752 16.6857 19.9424 23.4096 26.6791 29.1562 31.1592 32.9227]
           tperf: [599.2224 97.7009 79.1240 24.1796 31.6290 38.4484 42.7637 44.4194 44.8848 44.3171]
              mu: [1.0000e-03 0.0100 0.0100 0.1000 0.1000 0.1000 0.1000 0.1000 0.1000 0.1000]
        gradient: [2.4114e+03 867.8889 301.7333 142.1049 12.4011 85.0504 49.4147 17.4011 15.7749 14.6346]
        val_fail: [0 0 0 0 1 2 3 4 5 6]
       best_perf: [12.3078]
      best_vperf: [16.6857]
      best_tperf: [24.1796]

此结构体包含关于网络训练的所有信息。例如,tr.trainIndtr.valIndtr.testInd 分别包含训练、验证和测试集中使用的数据点的索引。如果您要使用相同的数据划分重新训练网络,可以将 net.divideFcn 设置为 'divideInd'net.divideParam.trainInd 设置为 tr.trainIndnet.divideParam.valInd 设置为 tr.valIndnet.divideParam.testInd 设置为 tr.testInd

tr 结构体还在训练过程中跟踪几个变量,如性能函数值、梯度幅值等。您可以通过 plotperf 命令,使用训练记录来绘制性能进度图:

plotperf(tr)

属性 tr.best_epoch 表示验证性能达到最小值时所处的迭代。在训练停止之前,训练继续进行了 6 次迭代。

此图并未表明训练有任何重大问题。验证曲线和测试曲线非常相似。如果测试曲线在验证曲线增大之前显著增大,则可能发生了一些过拟合。

验证网络的下一步是创建一个回归图,该图显示网络输出和目标之间的关系。如果训练是完美的,则网络输出和目标将完全相等,但在实践中极少出现这种完美关系。对于体脂示例,我们可以使用以下命令创建回归图。第一个命令计算经过训练的网络对数据集中所有输入的响应。接下来的六个命令提取属于训练、验证和测试子集的输出和目标。最后一个命令为训练、测试和验证创建三个回归图。

bodyfatOutputs = net(bodyfatInputs);
trOut = bodyfatOutputs(tr.trainInd);
vOut = bodyfatOutputs(tr.valInd);
tsOut = bodyfatOutputs(tr.testInd);
trTarg = bodyfatTargets(tr.trainInd);
vTarg = bodyfatTargets(tr.valInd);
tsTarg = bodyfatTargets(tr.testInd);
plotregression(trTarg, trOut, 'Train', vTarg, vOut, 'Validation', tsTarg, tsOut, 'Testing')

这三个图表示训练、验证和测试数据。每个图中的虚线表示完美结果 - 输出 = 目标。实线表示输出和目标之间的最佳拟合线性回归线。R 值表示输出和目标之间的关系。如果 R = 1,这表明输出和目标之间存在准确的线性关系。如果 R 接近于零,则输出和目标之间没有线性关系。

对于此示例,训练数据表明拟合良好。验证和测试结果也显示较大的 R 值。散点图有助于显示某些数据点的拟合效果很差。例如,测试集中有一个数据点的网络输出接近 35,而对应的目标值约为 12。下一步将是调查此数据点,以确定它是否表示外插点(即,它是否在训练数据集之外)。如果它是外插点,则应将其包括在训练集中,并且应采集额外的数据以用于测试集中。

改进结果

如果网络不够准确,您可以尝试重新初始化网络并进行训练。每次初始化前馈网络时,网络参数都不同,并且可能产生不同解。

net = init(net);
net = train(net, bodyfatInputs, bodyfatTargets);

作为另一种方法,您可以将隐藏神经元的数量增加到 20 个以上。隐藏层中的神经元数量越多,网络的灵活度就越高,因为网络可以优化更多参数。(请逐渐增加层大小。如果将隐含层设置得太大,可能会导致问题的特征不清晰,并且网络必须优化的参数数目多于约束这些参数的数据向量数目。)

第三个选择是尝试不同训练函数。例如,与使用早停法相比,使用 trainbr 的贝叶斯正则化训练有时可以提供更好的泛化能力。

最后,尝试使用额外的训练数据。为网络提供额外数据,则生成的网络更可能对新数据具有更强的泛化能力。