神经网络简介
神经网络简介
该教程通过显示如何训练接受手写单个数字的输入图像到预测数字,给出 Wolfram 语言神经网络架构的简短综述. 我们训练的数据集是经典的 MNIST 数据集,还会训练 LeNet 的变体,首批卷积网络之一,在 Wolfram Neural Net Repository 中已经可用.
trainingData = ResourceData["MNIST", "TrainingData"];
testData = ResourceData["MNIST", "TestData"];RandomSample[trainingData, 5]lenetModel = NetModel["LeNet Trained on MNIST Data"]lenetModel[{[image], [image], [image], [image]}]从零开始训练像 LeNet 这样的网络非常容易. NetTrain 会自动处理训练过程中的许多细节,例如,选择合适的损失函数,附加编码器和解码器以及选择批量大小. 这是它的样子.
NetTrain[NetModel["LeNet"], trainingData, ValidationSet -> testData][image]为了全面了解 Wolfram 语言的深度学习基础知识,现在我们将通过将 LeNet 从其组件层构建出来,选择损失函数,定义训练网络,附加编码器和解码器以及最后训练和评估网络. 理解这个特定任务背后的一般原则将使您很好地运用Wolfram 语言来轻松高效地处理复杂的学习任务.
lenetModel使用 NetExtract 提取网络的最后一层:
layer = NetExtract[lenetModel, -1]layer[{1, 2, 5, 2, 4, 7, 4, 3, 5, 3}]layer[NumericArray[{1., 2., 5., 2., 4., 7., 4., 3., 5., 3.}, "Real32"]]Normal[%]SoftmaxLayer 的目标是产生概率,其和为 1.
Total[%]linear = LinearLayer[3, "Input" -> 2]linear[{2, 3}]可以使用 NetInitialize 为可学习参数提供随机值.
initializedLinear = NetInitialize[linear]initializedLinear[{2, 3}]NetExtract[initializedLinear, "Weights"]NetExtract[initializedLinear, "Biases"]Normal@%到目前为止,我们看见层只有 1 个输入. 某些层有多于 1 个输入. 例如,MeanSquaredLossLayer 比较两个数组,称之为 input 和 target,并产生表示 Mean[(input-target)^2] 的单个数字.
创建一个 MeanSquaredLossLayer:
msloss = MeanSquaredLossLayer[]msloss[<|"Input" -> {1, 2, 3}, "Target" -> {4, 0, 4}|>]? * Layer层的更多属性(高级)
- 层是函数,它们的输入和输出总是数组或标量. 为了能对音频、图像等进行操作,层必须把 NetEncoder 附加在输入上.
- 可用指定 TargetDevice 选项的不同后端(CPU, CUDA, CoreML, …)运行网络层.
- 通过指定 TargetDevice 选项,可在 CPU 或 NVIDIA GPU 上训练网络层.
- 默认情况下,层计算使用的数值精度(单精度浮点)比 Wolfram 语言函数中常用的更低. 选项 WorkingPrecision 可用于指定不同的行为.
基本上说,因为它们必须可微,所以神经网络层对数组进行操作. 然而,我们经常想在其他数据上训练和使用数据,例如,图像、音频、文本等. 为了实现这些,我们可以使用 NetEncoder 把数据转换成值的数组.
我们可以使用 "Image" 编码器翻译 MNIST 数据集中的数字图像. 我们首先看看编码器的一些简单例子.
imageenc = NetEncoder[{"Image", {12, 12}, "ColorSpace" -> "Grayscale"}]把图像 NetEncoder 应用于一幅图像上:
imageenc[[image]]//Normal把图像 NetEncoder 应用于大型彩色图像上:
img = [image];
ImageDimensions[img]imageenc[img]//Dimensionspool = PoolingLayer[10, "Input" -> NetEncoder[{"Image", 64}]]ArrayPlot /@ pool[[image]]Image[pool[[image]], Interleaving -> False]为 MNIST 创建一个 "Image" 编码器:
mnistEncoder = NetEncoder[{"Image", {28, 28}, "ColorSpace" -> "Grayscale"}]trainingData[[1, 1]]ImageDimensions[%]为了使查询更方便,可以使用 "Class" NetDecoder 存储向量分量和类之间的映射,因此自动诠释网络的输出. NetDecoder 的其他类型也有可能把输出转换成一幅 Image、布尔值等,但是在本教程我们不会详细讨论.
mnistDecoder = NetDecoder[{"Class", Range[0, 9]}]mnistDecoder[{0, 0, 0, 1, 0, 0, 0, 0, 0, 0}]mnistDecoder[{0, 0, .3, .3, .3, .1, 0, 0, 0, 0}, "TopProbabilities"]mnistDecoder[{0, 0, .3, .3, .3, .1, 0, 0, 0, 0}, {"Probability", 2}]mnistDecoder[{0, 0, .3, .3, .3, .1, 0, 0, 0, 0}, "Probabilities"]mnistDecoder[{0, 0, .3, .3, .3, .1, 0, 0, 0, 0}, "Entropy"]使用 NetEncoder,我们可以附加 NetDecoder 到层的输出. 这是我们之前显示的 PoolingLayer 更流线的范例,其中,"Image" NetEncoder 用于诠释层的输入,用 "Image" NetDecoder 把层的最后输出转换回图像.
pool = PoolingLayer[10, "Input" -> NetEncoder[{"Image", 64}], "Output" -> NetDecoder["Image"]]pool[[image]]组合层的最简单方法是把它们一个接一个的链接起来,第一层的输出被用于下一层的输入,等等. 我们可以使用 NetChain 容器以这种方式连接层,但是当需要更复杂的连接格式时,则需要使用 NetGraph 容器.
chain = NetChain[{ElementwiseLayer[Sin], ElementwiseLayer[Cos]}]chain[{3.2, -2.3}]Cos[Sin[{3.2, -2.3}]]layer1 = ElementwiseLayer[Ramp];
layer2 = SoftmaxLayer[];
chain = NetChain[{layer1, layer2}]把 NetChain 应用于输入:
chain[{-1, 3, 2, 4}]layer1[{-1, 3, 2, 4}]layer2[%]chain2 = NetChain[{ElementwiseLayer[Sin], ElementwiseLayer[Cos], chain}]chain2[{-1, 3, 2, 4}]NetFlatten[chain2]uninitializedLenet = NetChain[{ConvolutionLayer[20, 5], ElementwiseLayer[Ramp], PoolingLayer[2, 2], ConvolutionLayer[50, 5], ElementwiseLayer[Ramp], PoolingLayer[2, 2], FlattenLayer[], LinearLayer[500], ElementwiseLayer[Ramp], LinearLayer[10], SoftmaxLayer[]}, "Input" -> mnistEncoder, "Output" -> mnistDecoder]lenet = NetInitialize[uninitializedLenet]lenet[[image]]lenet[[image], "TopProbabilities"]图
RandomSample[trainingData, 4]NetChain 不允许网络接受多个输入,因此我们需要使用 NetGraph 构建训练网络. 训练网络的任务是计算由 LeNet 产生的预测,如果预测可以,就产生一个小数,不好则产生一个大数. 这称为损失. 最好是把它想成预测错误的一种代理.
一旦我们有了这个训练网络,我们就可以使用 NetTrain 函数逐步修改网络中的可学习参数,使得损失随着时间的推移而降低.
对于不同的学习任务,必须使用不同的方法计算损失. 对于分类任务,例如分类 MNIST 数字,损失的常用选择是交叉熵损失. 当给出预测和真标签或目标,层 CrossEntropyLossLayer 可以计算该损失.
下面是用于评分预测的 CrossEntropyLossLayer 的一个简单范例.
创建一个 CrossEntropyLossLayer 比较长度为 5 带有目标标签的输入预测向量:
loss = CrossEntropyLossLayer["Index", "Input" -> {5}]probvector = {0.8, 0.05, 0.05, 0.05, 0.05}loss[<|"Input" -> probvector, "Target" -> 1|>]loss[<|"Input" -> probvector, "Target" -> 5|>]通过提供层和连接列表,构建一个 NetGraph. 图的输入使用语法 NetPort["input"]destination 连接到层,损失层的输入是通过 source->NetPort["loss","input"] 连接的:
trainingNet = NetGraph[<|"lenet" -> lenet, "loss" -> CrossEntropyLossLayer["Index"]|>,
{NetPort["Input"] -> "lenet" -> NetPort["loss", "Input"], NetPort["Target"] -> NetPort["loss", "Target"]}]trainingNet[<|"Input" -> {[image], [image], [image], [image], [image]}, "Target" -> {2, 4, 7, 9, 8}|>]gradient = trainingNet[<|"Input" -> [image], "Target" -> 2|>, NetPortGradient[{"lenet", 1, "Biases"}]]oldBias = Normal@NetExtract[trainingNet, {"lenet", 1, "Biases"}];
newBias = oldBias - 0.1 * gradient;
trainingNet2 = NetReplacePart[trainingNet, {"lenet", 1, "Biases"} -> newBias]trainingNet[<|"Input" -> [image], "Target" -> 2|>]trainingNet2[<|"Input" -> [image], "Target" -> 2|>]现在,我们可以用 NetTrain 训练我们的网络.
一般来讲,NetTrain 自动执行训练网络的构建. 对于一个输入和一个输出的简单网络,通过把 ini 传给网络,处理形式为 {in->out,…} 的训练数据,然后通过合适的损失函数比较带有 outi 的网络输出.
但是,因为我们已经明确构建了一个训练网络,我们必须以形式 <|"port"->list,…|> 提供训练数据,并明确传递给带有数据列表的训练网络的输入端口. 因此,我们必须首先转换我们的数据,它是规则 in->out 的格式.
另一个复杂性是,我们必须考虑这个事实,训练数据包含整数 0 到 9 的标签,其中,训练网络的 "Target" 输入期待范围 1 到 10 的索引. We could use 我们可以使用 "Class" NetEncoder 进行转换(NetTrain 通常会自动化),单数我们会利用这个事实:只是加 1 来完成同样的事.
trainAssoc = <|"Input" -> Keys[trainingData], "Target" -> Values[trainingData] + 1|>;
testAssoc = <|"Input" -> Keys[testData], "Target" -> Values[testData] + 1|>;Take[trainAssoc, All, 5]现在使用 NetTrain 执行训练. 注意关于这个网络范例的几件事情:
- 我们有指定的 MaxTrainingRounds->5,在完成之前会扫描整个训练集 5 次.
- 把 All 作为第三个参数,获取一个 NetTrainResultsObject 总结各种关于训练会话的信息.
- 给定 ValidationSet 允许我们度量训练分类器对没有训练的新范例的泛化程度. 这可以帮助避免通常的陷阱“过度拟合(overfitting)”.
- 我们已经指定 TargetDevice->"CPU"(已经是默认的).如果你有 NVIDIA 图形卡,可以把它改成 "GPU" 以获取训练的大幅加速.
results = NetTrain[trainingNet, trainAssoc, All, ValidationSet -> testAssoc, MaxTrainingRounds -> 5, TargetDevice -> "CPU"]最终的l NetTrainResultsObject 为我们提供了丰富的信息. 其中一些以其显示形式显示,但可以通过编程方式从中检索更多信息. 但是我们可以立即看到,在 3 分钟的 CPU 训练后,我们的网络能够达到约 99.4% 的准确率.
从 NetTrainResultsObject 获取损失演变图:
results["LossPlot"]从 NetTrainResultsObject 获取训练网络并提取预测网络:
trainedLenet = NetExtract[results["TrainedNet"], "lenet"]trainedLenet = NetReplacePart[trainedLenet, {"Input" -> mnistEncoder, "Output" -> mnistDecoder}]trainedLenet[[image]]trainedLenet[[image], "TopProbabilities"]- 如何使用 NetEncoder 和 NetDecoder 在数组和其他数据格式间翻译.
- 如何构建训练网络,使用 CrossEntropyLossLayer 计算预测网络的损失.
- 如何调用 NetTrain 使用训练数据训练网络.
现在我们已经训练了我们的网络,我们可以获得更多关于它的信息. 例如,我们可以获得另一个数据集的总体准确度. 我们也可以得到像混淆矩阵这样有用的总结,它总结了网络如何误分类的例子. 我们可以将使用深度学习的网络的性能与其他常用机器学习技术的性能进行比较.
让我们从构建一个 ClassifierMeasurementsObject 开始,它测量数据集上网络分类行为的各种属性. 使用我们未训练的数据集非常重要,因此我们使用内置于 MNIST 数据集中的测试集.
measurements = ClassifierMeasurements[trainedLenet, testData]现在,我们有 ClassifierMeasurementsObject,我们可以高效查询各种属性.
measurements["Accuracy"]measurements["WorstClassifiedExamples"]measurements["LeastCertainExamples"]measurements["TopConfusions"]measurements["Properties"]我们也可以使用 NetMeasurements 度量网络的各种属性. NetMeasurements 和 ClassifierMeasurements 共享许多同样的属性,但它们也有自己的属性. NetMeasurements 的优势是它已被高效的实现,甚至可以在 NVIDIA 显卡上运行,可通过 TargetDevice"GPU" 选项,使其更加适合于大型数据集.
NetMeasurements[trainedLenet, testData, "Accuracy"]NetMeasurements[trainedLenet, testData, "ConfusionMatrixPlot"]NetMeasurements[trainedLenet, testData, "F1Score"]对于神经网络,NetMeasurements 还有其他一些优势. 可以度量网络中任何层的输出和权重.
activations = NetMeasurements[trainedLenet, testData, NetPort[{1, "Output"}]];
ArrayPlot[ArrayFlatten @ Partition[activations, 5], ColorFunction -> "Rainbow", FrameTicks -> None]另外,NetMeasurements 使用缓存加速同样属性的重复度量或甚至是类似属性的度量.
NetMeasurements[trainedLenet, testData, "CohenKappa"]//AbsoluteTiming最后,NetMeasurements 可以在比 ClassifierMeasurements 更多的情况下使用:可用于当网络没有要求的丢失层并且没有附加 NetEncoder 或 NetDecoder,以及当网络有多个输出时.
将网络与其他方法进行比较的最简单方法是使用 Classify,它会自动应用一系列常用方法并选择最佳方法.
使用 Classify 自动获取高效机器学习模型:
classifier = Classify[trainingData, ValidationSet -> testData][image]classifier[[image], "Probabilities"]神经网络优于测试集上由 Classify 选择的模型.
NetMeasurements[trainedLenet, testData, "Accuracy"]ClassifierMeasurements[classifier, testData, "Accuracy"]