使用浅层神经网络拟合数据
神经网络擅长拟合函数。事实上有证据表明,一个简单的神经网络就可以拟合任何实用函数。
例如,假设您有一家健康诊所的数据。您想要设计一个网络,该网络可以基于 13 个解剖学测量值来预测人的体脂率。您总共有 252 个人的样本,其中包括这 13 项数据和相关的体脂率。
您可以用两种方法解决此问题:
使用图形用户界面 nftool,如使用神经网络拟合中所述。
使用命令行函数,如使用命令行函数中所述。
通常最好从 GUI 开始,然后使用 GUI 自动生成命令行脚本。在使用任何方法之前,首先通过选择数据集来定义问题。每个 GUI 都可以访问许多样本数据集,您可以使用这些数据集来试验工具箱(请参阅浅层神经网络的样本数据集)。如果您有要解决的特定问题,可以将您自己的数据加载到工作区中。下一节介绍数据格式。定义问题
要为工具箱定义拟合问题,请将一组 Q 输入向量排列为一个矩阵中的列。然后,将另一组 Q 目标向量(每个输入向量的正确输出向量)排列到另一个矩阵中(有关静态和时序数据的数据格式的详细说明,请参阅数据结构)。例如,您可以使用四组由二元素输入向量和一元素目标构成的数据来定义布尔 AND 门的拟合问题:
inputs = [0 1 0 1; 0 0 1 1];
targets = [0 0 0 1];
下一节说明如何使用神经网络拟合 (nftool) 训练网络来拟合数据集。此示例使用工具箱提供的体脂数据集。使用神经网络拟合
使用以下命令打开 Neural Network Start GUI:
nnstart
点击 Fitting app 打开神经网络拟合。(您也可以使用命令 nftool。)
点击 Next 继续。
在 Select Data 窗口中点击 Load Example Data Set。将打开 Fitting Data Set Chooser 窗口。
注意
当您需要从 MATLAB® 工作区加载数据时,请使用 Select Data 窗口中的 Inputs 和 Targets 选项。
选择 Chemical,然后点击 Import。这将返回 Select Data 窗口。
点击 Next 以显示 Validation and Test Data 窗口,如下图所示。
验证和测试数据集均设置为原始数据的 15%。
通过这些设置,输入向量和目标向量将随机分成如下三组:
70% 将用于训练。
15% 将用于验证网络是否正在泛化,并在过拟合前停止训练。
最后 15% 将用作网络泛化的完全独立测试。
(有关数据划分流程的更多讨论,请参阅划分数据。)
点击 Next。
用于函数拟合的标准网络是一个双层前馈网络,其中在隐藏层有一个 sigmoid 传递函数,在输出层有一个线性传递函数。隐藏神经元的默认数量设置为 10。如果网络训练性能不佳,稍后可以增加此数字。
点击 Next。
选择训练算法,然后点击 Train。对于大多数问题,推荐使用 Levenberg-Marquardt (trainlm),不过,对于一些含噪小型问题,贝叶斯正则化 (trainbr) 虽然可能需要更长的时间,但会获得更好的解。但是,对于大型问题,推荐使用量化共轭梯度 (trainscg),因为它使用的梯度计算比其他两种算法使用的 Jacobian 矩阵计算更节省内存。此示例使用默认的 Levenberg-Marquardt。
训练一直持续到连续六次迭代仍无法降低验证误差为止(验证停止)。
在 Plots 下,点击 Regression。这用于验证网络性能。
下面的回归图显示与训练集目标、验证集目标和测试集目标相关的网络输出。如果是完美拟合,则数据应沿 45 度线下降,其中网络输出等于目标。对于此问题,所有数据集的拟合效果都很不错,每种情况下的 R 值都为 0.93 或更高。如果需要更准确的结果,可以在 nftool 中点击 Retrain 来重新训练网络。这将更改网络的初始权重和偏差,并可能在重新训练后生成改进的网络。其他选项在后面的窗格中提供。
查看误差直方图以获得网络性能的额外验证。在 Plots 窗格下,点击 Error Histogram。
蓝条表示训练数据,绿条表示验证数据,红条表示测试数据。直方图可以指示离群值,这些离群值是拟合明显比大部分数据差的数据点。在此例中,您可以看到,虽然大多数误差在 -5 和 5 之间,但有一个误差为 17 的训练点和误差为 12 和 13 的验证点。这些离群值在测试回归图上也是可见的。第一个离群值对应于目标为 50 且输出接近 33 的点。最好检查离群值以确定数据是否不良,或者这些数据点是否不同于数据集的其余部分。如果离群值是有效的数据点,但不同于其余数据,则网络将对这些点进行外插。您应该收集更多看起来像离群值数据点的数据,并重新训练网络。
在神经网络拟合中点击 Next 以评估网络。
此时,您可以针对新数据测试网络。
如果您不满意网络对原始数据或新数据的性能,可以执行以下操作之一:
重新训练网络。
增加神经元的数量。
获取更大的训练数据集。
如果网络对训练集的性能良好,但测试集的性能明显变差,这可能表示出现了过拟合,减少神经元数量可能会改善结果。如果训练性能不佳,您可以增加神经元的数量。
如果您对网络性能满意,请点击 Next。
使用以下面板生成用于仿真您的神经网络的 MATLAB 函数或 Simulink® 图。您可以使用生成的代码或图来更好地理解您的神经网络如何根据输入计算输出,或使用 MATLAB Compiler™ 工具和其他 MATLAB 代码生成工具部署网络。
使用以下屏幕上的按钮生成脚本或保存结果。
您可以点击 Simple Script 或 Advanced Script 创建 MATLAB 代码,以通过命令行执行这些代码来重现前面的所有步骤。如果您要了解如何使用工具箱的命令行功能来自定义训练过程,则创建 MATLAB 代码会很有帮助。在使用命令行函数中,您可以更详细地研究生成的脚本。
您还可以将网络保存为工作区中的 net。您可以对它执行额外的测试,或让其处理新输入。
创建 MATLAB 代码并保存结果后,点击 Finish。使用命令行函数
了解如何使用工具箱的命令行功能的最简单方法是从 GUI 生成脚本,然后修改它们以自定义网络训练。例如,看一下在上一节的步骤 14 中创建的简单脚本。
% Solve an Input-Output Fitting problem with a Neural Network
% Script generated by NFTOOL
%
% This script assumes these variables are defined:
%
% houseInputs - input data.
% houseTargets - target data.
inputs = houseInputs;
targets = houseTargets;
% Create a Fitting Network
hiddenLayerSize = 10;
net = fitnet(hiddenLayerSize);
% Set up Division of Data for Training, Validation, Testing
net.divideParam.trainRatio = 70/100;
net.divideParam.valRatio = 15/100;
net.divideParam.testRatio = 15/100;
% Train the Network
[net,tr] = train(net,inputs,targets);
% Test the Network
outputs = net(inputs);
errors = gsubtract(outputs,targets);
performance = perform(net,targets,outputs)
% View the Network
view(net)
% Plots
% Uncomment these lines to enable various plots.
% figure, plotperform(tr)
% figure, plottrainstate(tr)
% figure, plotfit(targets,outputs)
% figure, plotregression(targets,outputs)
% figure, ploterrhist(errors)
您可以保存脚本,然后从命令行运行它,以重现上次 GUI 会话的结果。您还可以编辑脚本来自定义训练过程。在此例中,请遵循脚本中的每个步骤。
脚本假设输入向量和目标向量已加载到工作区中。如果未加载数据,可以按如下方式加载它们:
load bodyfat_dataset
inputs = bodyfatInputs;
targets = bodyfatTargets;
此数据集是工具箱的示例数据集之一(请参阅浅层神经网络的样本数据集)。您可以输入命令 help nndatasets 来查看所有可用数据集的列表。load 命令还允许您使用自己的变量名称从这些数据集中的任何一个加载变量。例如,命令
[inputs,targets] = bodyfat_dataset;
会将体脂输入加载到数组 inputs 中,将体脂目标加载到数组 targets 中。
创建一个网络。用于函数拟合(或回归)问题的默认网络 fitnet 是一个前馈网络,其默认 tan-sigmoid 传递函数在隐藏层,线性传递函数在输出层。首先,您(人为地选择)将十个神经元分配给一个隐藏层。网络有一个输出神经元,因为只有一个目标值与每个输入向量关联。
hiddenLayerSize = 10;
net = fitnet(hiddenLayerSize);
注意
神经元越多,需要的计算也越多;当数量设置得太高时,可能会出现数据过拟合倾向,但这使网络能够求解更复杂的问题。层越多,需要的计算也越多,但使用更多的层可以使网络更高效地求解复杂问题。要使用多个隐藏层,请在 fitnet 命令中输入隐藏层大小作为数组的元素。
设置数据划分。
net.divideParam.trainRatio = 70/100;
net.divideParam.valRatio = 15/100;
net.divideParam.testRatio = 15/100;
基于以上设置,输入向量和目标向量都将被随机划分为三组,其中 70% 用于训练,15% 用于验证,15% 用于测试。(有关数据划分流程的更多讨论,请参阅划分数据。)
训练网络。网络使用默认的 Levenberg-Marquardt 算法 (trainlm) 进行训练。对于 Levenberg-Marquardt 无法产生期望的准确结果的问题,或对于大型数据问题,请考虑使用以下命令之一将网络训练函数设置为贝叶斯正则化 (trainbr) 或量化共轭梯度 (trainscg):
net.trainFcn = 'trainbr';
net.trainFcn = 'trainscg';
要训练网络,请输入:
[net, tr] = train(net, inputs, targets);
在训练期间,将打开以下训练窗口。此窗口显示训练进度,并允许您随时点击 Stop Training 来中断训练。
当验证误差在六次迭代中持续增加时,训练停止,即在第 20 次迭代时停止。如果在训练窗口中点击 Performance,将显示表示训练误差、验证误差和测试误差的图,如下图所示。在此示例中,结果是合理的,原因如下:
最终均方误差很小。
测试集误差和验证集误差具有相似特性。
在第 14 次迭代(出现最佳验证性能)前未出现明显的过拟合。
测试网络。您可以使用经过训练的网络来计算网络输出。以下代码将计算网络输出、误差和整体性能。
outputs = net(inputs);
errors = gsubtract(targets, outputs);
performance = perform(net, targets, outputs)
performance =
19.3193
通过使用训练记录中的测试索引,还可以只针对测试集计算网络性能。(有关训练记录的完整说明,请参阅分析训练后的浅层神经网络性能。)
tInd = tr.testInd;
tstOutputs = net(inputs(:, tInd));
tstPerform = perform(net, targets(tInd), tstOutputs)
tstPerform =
53.7680
对网络响应进行一些分析。如果在训练窗口中点击 Regression,则可以在网络输出和相应目标之间执行线性回归。
下图显示了结果。
对于训练、测试和验证,输出非常好地跟踪了目标,并且总响应的 R 值超过 0.96。如果需要更准确的结果,可以尝试以下任一方法:
使用 init 将初始网络权重和偏差重置为新值,然后再次训练(请参阅初始化权重 (init))。
增加隐藏神经元的数量。
增加训练向量的数量。
增加输入值的数量(如果有更多相关信息可用)。
尝试其他训练算法(请参阅训练算法)。
在此例中,网络响应是令人满意的,现在您可以将该网络用于新输入。
查看网络图。
view(net)
要进一步熟悉命令行操作,请尝试以下任务:
在训练期间,打开绘图窗口(如回归图),并观察其动画效果。
此外,从命令行进行训练时,可以查看高级脚本以了解更多选项。
每次训练神经网络时,由于初始权重和偏置值不同,并且将数据划分为训练集、验证集和测试集的方式也不同,可能会产生不同的解。因此,针对同一问题训练的不同神经网络对同一输入可能给出不同输出。为确保找到准确度良好的神经网络,需要多次重新训练。
如果需要更高的准确度,可以采用几种其他方法来改进初始解。有关详细信息,请参阅提高浅层神经网络泛化能力,避免过拟合。