Main Content

本页对应的英文页面已更新,但尚未翻译。 若要查看最新内容,请点击此处访问英文页面。

定义自定义深度学习层

提示

本主题说明如何针对您的问题定义自定义深度学习层。有关 Deep Learning Toolbox™ 中的内置层的列表,请参阅深度学习层列表

本主题说明深度学习层的架构,以及如何针对您的问题定义自定义层。

类型说明

定义一个自定义深度学习层,并指定可选的可学习参数。

有关如何使用可学习参数定义自定义层的示例,请参阅Define Custom Deep Learning Layer with Learnable Parameters。有关如何定义具有多个输入的自定义层的示例,请参阅Define Custom Deep Learning Layer with Multiple Inputs

分类输出层

定义一个自定义分类输出层并指定损失函数。

有关如何定义自定义分类输出层和指定损失函数的示例,请参阅Define Custom Classification Output Layer

回归输出层

定义一个自定义回归输出层并指定损失函数。

有关如何定义一个自定义回归输出层和指定损失函数的示例,请参阅Define Custom Regression Output Layer

层模板

您可以使用以下模板来定义新层。

 中间层模板

 分类输出层模板

 回归输出层模板

中间层架构

在训练过程中,软件在网络中以迭代方式执行前向传导和后向传导。

在网络中执行前向传导时,每个层获取前面各层的输出,应用一个函数,然后将结果输出(前向传播)到后面各层。

层可以有多个输入或输出。例如,层可以从多个前面的层中获取 X1、…、Xn,并将输出 Z1、…、Zm 前向传播到后面各层。

在执行网络的前向传导结束时,输出层计算预测值 Y 和真实目标 T 之间的损失 L。

在网络的后向传导过程中,每个层都获取损失关于该层的输出的导数,计算损失 L 关于输入的导数,然后后向传播结果。如果层具有可学习参数,则该层还会计算层权重(可学习参数)的导数。该层使用权重的导数来更新可学习参数。

下图说明通过深度神经网络的数据流,重点显示通过具有单个输入 X、单个输出 Z 和可学习参数 W 的层的数据流。

中间层属性

在类定义的 properties 部分声明层属性。

默认情况下,自定义中间层具有以下属性:

属性说明
Name 层名称,指定为字符向量或字符串标量。要将一个层包括在层次图中,您必须指定非空的唯一层名称。如果您使用该层训练串行网络并且 Name 设置为 '',则软件会在训练时自动为该层指定名称。
Description

层的单行描述,指定为字符向量或字符串标量。当层显示在 Layer 数组中时,会出现此描述。如果没有指定层描述,则软件将显示层类名。

Type层的类型,指定为字符向量或字符串标量。当层显示在 Layer 数组中时,会显示 Type 的值。如果没有指定层类型,则软件将显示层类名。
NumInputs层的输入的数目,指定为正整数。如果您未指定此值,软件会自动将 NumInputs 设置为 InputNames 中的名称的数目。默认值为 1。
InputNames层的输入名称,指定为字符向量元胞数组。如果您未指定此值并且 NumInputs 大于 1,则软件会自动将 InputNames 设置为 {'in1',...,'inN'},其中 N 等于 NumInputs。默认值为 {'in'}
NumOutputs层的输出的数目,指定为正整数。如果您未指定此值,软件会自动将 NumOutputs 设置为 OutputNames 中的名称的数目。默认值为 1。
OutputNames层的输出名称,指定为字符向量元胞数组。如果您未指定此值并且 NumOutputs 大于 1,则软件会自动将 OutputNames 设置为 {'out1',...,'outM'},其中 M 等于 NumOutputs。默认值为 {'out'}

如果层没有其他属性,则您可以省略 properties 部分。

提示

如果要创建一个具有多个输入的层,则必须在层构造函数中设置 NumInputsInputNames。如果要创建一个具有多个输出的层,则必须在层构造函数中设置 NumOutputsOutputNames有关示例,请参阅Define Custom Deep Learning Layer with Multiple Inputs

可学习参数

在类定义的 properties (Learnable) 部分声明层可学习参数。如果层没有可学习参数,则可以省略 properties (Learnable) 部分。

您也可以指定可学习参数的学习率因子和 L2 因子。默认情况下,每个可学习参数的学习率因子和 L2 因子都设置为 1

对于内置层和自定义层,可以使用以下函数设置和获取学习率因子和 L2 正则化因子。

函数说明
setLearnRateFactor设置可学习参数的学习率因子。
setL2Factor设置可学习参数的 L2 正则化因子。
getLearnRateFactor获取可学习参数的学习率因子。
getL2Factor获取可学习参数的 L2 正则化因子。

要指定可学习参数的学习率因子和 L2 因子,请分别使用语法 layer = setLearnRateFactor(layer,'MyParameterName',value)layer = setL2Factor(layer,'MyParameterName',value)

要获得可学习参数的学习率因子和 L2 因子的值,请分别使用语法 getLearnRateFactor(layer,'MyParameterName')getL2Factor(layer,'MyParameterName')

例如,以下语法将名为 'Alpha' 的可学习参数的学习率因子设置为 0.1

layer = setLearnRateFactor(layer,'Alpha',0.1);

前向函数

层使用 predictforward 函数来执行前向传导。如果前向传导在预测时间进行,则该层使用 predict 函数。如果前向传导在训练时进行,则该层使用 forward 函数。如果不需要对预测时间和训练时间使用两个不同函数,则可以省略 forward 函数。在本例中,层在训练时使用 predict

如果您定义函数 forward 和自定义 backward 函数,则必须为参数 memory 指定值,您可以在后向传播期间使用该值。

predict 的语法是

[Z1,…,Zm] = predict(layer,X1,…,Xn)
其中 X1,…,Xnn 个层输入,Z1,…,Zmm 个层输出。nm 的值必须对应于该层的 NumInputsNumOutputs 属性。

提示

如果 predict 的输入的数目可能变化,则使用 varargin 代替 X1,…,Xn。在本例中,varargin 是由输入组成的元胞数组,其中 varargin{i} 对应于 Xi。如果输出的数目可能变化,则使用 varargout 代替 Z1,…,Zm。在本例中,varargout 是由输出组成的元胞数组,其中 varargout{j} 对应于 Zj

forward 的语法是

[Z1,…,Zm,memory] = forward(layer,X1,…,Xn)
其中,X1,…,Xnn 个层输入,Z1,…,Zmm 个层输出,memory 是层的内存。

提示

如果 forward 的输入的数目可能变化,则使用 varargin 代替 X1,…,Xn。在本例中,varargin 是由输入组成的元胞数组,其中 varargin{i} 对应于 Xi。如果输出的数目可能变化,则使用 varargout 代替 Z1,…,Zm。在本例中,varargout 是由输出组成的元胞数组,其中 varargout{j} 对应于 Zjj = 1、…、NumOutputs),varargout{NumOutputs+1} 对应于 memory

输入的维度取决于数据的类型和所连接层的输出:

层输入输入大小观测值维度
二维图像h×w×c×N,其中 h、w 和 c 分别对应于图像的高度、宽度和通道数,N 是观测值数目。4
三维图像h×w×d×c×N,其中 h、w、d 和 c 分别对应于三维图像的高度、宽度、深度和通道数,N 是观测值数目。5
向量序列c×N×S,其中 c 是序列的特征数,N 是观测值数目,S 是序列长度。2
二维图像序列h×w×c×N×S,其中 h、w 和 c 分别对应于图像的高度、宽度和通道数,N 是观测值数目,S 是序列长度。4
三维图像序列h×w×d×c×N×S,其中 h、w、d 和 c 分别对应于三维图像的高度、宽度、深度和通道数量,N 是观测值数目,S 是序列长度。5

后向函数

层后向函数计算损失关于输入数据的导数,然后将结果输出(后向传播)到前一层。如果层具有可学习参数(例如,层权重),则 backward 还计算可学习参数的导数。当使用 trainNetwork 函数时,层会在后向传导过程中使用这些导数自动更新可学习参数。

定义后向函数是可选的。如果未指定后向函数,并且层前向函数支持 dlarray 对象,则软件会使用自动微分自动确定后向函数。有关支持 dlarray 对象的函数列表,请参阅List of Functions with dlarray Support。当您需要执行以下操作时,请定义一个自定义后向函数:

  • 使用特定算法来计算导数。

  • 在前向函数中使用不支持 dlarray 对象的操作。

要定义一个自定义后向函数,请创建名为 backward 的函数。

backward 的语法是

[dLdX1,…,dLdXn,dLdW1,…,dLdWk] = backward(layer,X1,…,Xn,Z1,…,Zm,dLdZ1,…,dLdZm,memory)
其中:

  • X1,…,Xnn 个层输入

  • Z1,…,Zm 是层前向函数的 m 个输出

  • dLdZ1,…,dLdZm 是从后一层反向传播的梯度

  • 如果定义了 forward,则 memoryforward 的内存输出,否则 memory[]

对于输出,dLdX1,…,dLdXn 是损失关于层输入的导数,dLdW1,…,dLdWk 是损失关于 k 个可学习参数的导数。要通过防止在前向和后向传导之间保存未使用的变量来减少内存使用量,请用 ~ 替换对应的输入参数。

提示

如果 backward 的输入数目可能变化,则使用 varargin 代替 layer 后的输入参数。在本例中,varargin 是由输入组成的元胞数组,其中 varargin{i} 对应于 Xii =1、…、NumInputs),varargin{NumInputs+j}varargin{NumInputs+NumOutputs+j} 分别对应于 ZjdLdZjj=1、…、NumOutputs),varargin{end} 对应于 memory

如果输出的数目可能变化,则使用 varargout 代替输出参数。在本例中,varargout 是由输出组成的元胞数组,其中 varargout{i} 对应于 dLdXii =1、…、NumInputs),varargout{NumInputs+t} 对应于 dLdWtt =1、…、k),其中 k 是可学习参数的数目。

X1,…,XnZ1,…,Zm 的值与前向函数中的值相同。dLdZ1,…,dLdZm 的维度分别与 Z1,…,Zm 的维度相同。

dLdX1,…,dLdxn 的维度和数据类型分别与 X1,…,Xn 的维度和数据类型相同。dLdW1,…,dLdWk 的维度和数据类型分别与 W1,…,Wk 的维度和数据类型相同。

要计算损失的导数,您可以使用链式法则:

LX(i)=jLzjzjX(i)

LWi=jLZjZjWi

当使用 trainNetwork 函数时,层会在后向传导过程中使用导数 dLdW1,…,dLdWk 自动更新可学习参数。

有关如何定义自定义后向函数的示例,请参阅Specify Custom Layer Backward Function

GPU 兼容性

如果层前向函数完全支持 dlarray 对象,则该层与 GPU 兼容。否则,为了与 GPU 兼容,层函数必须支持 gpuArray (Parallel Computing Toolbox) 类型的输入并返回其输出。

许多 MATLAB® 内置函数支持 gpuArray (Parallel Computing Toolbox)dlarray 输入参数。有关支持 dlarray 对象的函数列表,请参阅List of Functions with dlarray Support。有关在 GPU 上执行的函数的列表,请参阅Run MATLAB Functions on a GPU (Parallel Computing Toolbox)要使用 GPU 进行深度学习,您还必须有支持 CUDA® 且具有 3.0 或更高计算能力的 NVIDIA® GPU。有关在 MATLAB 中使用 GPU 的详细信息,请参阅GPU Computing in MATLAB (Parallel Computing Toolbox)

检查层的有效性

如果您创建自定义深度学习层,则可以使用 checkLayer 函数来检查该层是否有效。该函数检查层的有效性、GPU 兼容性和正确定义的梯度。要检查层是否有效,请运行以下命令:

checkLayer(layer,validInputSize,'ObservationDimension',dim)
其中,layer 是层的实例,validInputSize 是用于指定层的有效输入大小的向量或元胞数组,dim 指定层输入数据中观测值的维度。对于较大的输入大小,梯度检查的运行时间较长。为了加快测试速度,请指定较小的有效输入大小。

有关详细信息,请参阅Check Custom Layer Validity

使用 checkLayer 检查层的有效性

检查自定义层 preluLayer 的层有效性。

定义自定义 PReLU 层。要创建此层,请将文件 preluLayer.m 保存在当前文件夹中。

创建一个层实例,并使用 checkLayer 检查其有效性。将有效输入大小指定为层的典型输入的单个观测值的大小。该层需要四维数组输入,其中前三个维度对应于前一个层输出的高度、宽度和通道数,第四个维度对应于观测值。

指定观测值的输入的典型大小,并将 'ObservationDimension' 设置为 4。

layer = preluLayer(20,'prelu');
validInputSize = [24 24 20];
checkLayer(layer,validInputSize,'ObservationDimension',4)
Running nnet.checklayer.TestLayerWithoutBackward
.......... .......
Done nnet.checklayer.TestLayerWithoutBackward
__________

Test Summary:
	 17 Passed, 0 Failed, 0 Incomplete, 0 Skipped.
	 Time elapsed: 1.5273 seconds.

在此处,该函数不检测该层的任何问题。

在网络中包含层

您可以像使用 Deep Learning Toolbox 中的任何其他层一样使用自定义层。

定义自定义 PReLU 层。要创建此层,请将文件 preluLayer.m 保存在当前文件夹中。

创建一个包含自定义层 preluLayer 的层数组。

layers = [
    imageInputLayer([28 28 1])
    convolution2dLayer(5,20)
    batchNormalizationLayer
    preluLayer(20,'prelu')
    fullyConnectedLayer(10)
    softmaxLayer
    classificationLayer];

输出层架构

在训练时的前向传导结束时,输出层获取前一个层的预测(输出)y,并计算这些预测和训练目标之间的损失 L。输出层计算损失 L 关于预测 y 的导数,并将结果输出(后向传播)到前一个层。

下图说明通过卷积神经网络和输出层的数据流。

输出层属性

在类定义的 properties 部分声明层属性。

默认情况下,自定义输出层具有以下属性:

  • Name - 层名称,指定为字符向量或字符串标量。要将一个层包括在层次图中,您必须指定非空的唯一层名称。如果您使用该层训练串行网络并且 Name 设置为 '',则软件会在训练时自动为该层指定名称。

  • Description - 层的单行描述,指定为字符向量或字符串标量。当层显示在 Layer 数组中时,会出现此描述。如果未指定层描述,则软件将显示 "Classification Output""Regression Output"

  • Type - 层的类型,指定为字符向量或字符串标量。当层显示在 Layer 数组中时,会显示 Type 的值。如果没有指定层类型,则软件将显示层类名。

自定义分类层还具有以下属性:

  • Classes - 输出层的类,指定为分类向量、字符串数组、字符向量元胞数组或 'auto'。如果 Classes'auto',则软件会在训练时自动设置类。如果您指定字符串数组或字符向量元胞数组 str,则软件会将输出层的类设置为 categorical(str,str)。默认值为 'auto'

自定义回归层还具有以下属性:

  • ResponseNames - 响应的名称,指定为字符向量元胞数组或字符串数组。在训练时,软件根据训练数据自动设置响应名称。默认值为 {}

如果层没有其他属性,则您可以省略 properties 部分。

损失函数

输出层使用前向损失函数计算预测和目标之间的损失 L,并使用后向损失函数计算损失关于预测的导数。

forwardLoss 的语法是 loss = forwardLoss(layer, Y, T)。输入 Y 对应于网络做出的预测。这些预测是前一个层的输出。输入 T 对应于训练目标。根据指定的损失函数,输出 lossYT 之间的损失。输出 loss 必须为标量。

如果层前向损失函数支持 dlarray 对象,则软件会自动确定后向损失函数。有关支持 dlarray 对象的函数列表,请参阅List of Functions with dlarray Support。或者,要定义自定义后向损失函数,请创建一个名为 backwardLoss 的函数。有关如何定义自定义后向损失函数的示例,请参阅Specify Custom Output Layer Backward Loss Function

backwardLoss 的语法是 dLdY = backwardLoss(layer, Y, T)。输入 Y 包含网络做出的预测,T 包含训练目标。输出 dLdY 是损失关于预测 Y 的导数。输出 dLdY 的大小必须与层输入 Y 的大小相同。

对于分类问题,T 的维度取决于问题的类型。

分类任务输入大小观测值维度
二维图像分类1×1×K×N,其中 K 是类数,N 是观测值数目。4
三维图像分类1×1×1×K×N,其中 K 是类数,N 是观测值数目。5
“序列到标签”分类K×N,其中 K 是类数,N 是观测值数目。2
“序列到序列”分类K×N×S,其中 K 是类数,N 是观测值数目,S 是序列长度。2

Y 的大小取决于前一个层的输出。为了确保 YT 的大小相同,您必须在输出层之前包含一个输出正确大小的层。例如,为了确保 Y 是 K 个类的预测分数的四维数组,您可以包含一个大小为 K 的全连接层,后跟一个 softmax 层,然后是输出层。

对于回归问题,T 的维度还取决于问题的类型。

回归任务输入大小观测值维度
二维图像回归1×1×R×N,其中 R 是响应数,N 是观测值数目。4
二维“图像到图像”回归h×w×c×N,其中 h、w 和 c 分别是输出的高度、宽度和通道数,N 是观测值数目。4
三维图像回归1×1×1×R×N,其中 R 是响应数,N 是观测值数目。5
三维“图像到图像”回归h×w×d×c×N,其中 h、w、d 和 c 分别是输出的高度、宽度、深度和通道数,N 是观测值数目。5
“序列到单个”回归R×N,其中 R 是响应数,N 是观测值数目。2
“序列到序列”回归R×N×S,其中 R 是响应数,N 是观测值数目,S 是序列长度。2

例如,如果网络定义具有一个响应的图像回归网络,并且小批量大小为 50,则 T 是大小为 1×1×1×50 的四维数组。

Y 的大小取决于前一个层的输出。为了确保 YT 的大小相同,您必须在输出层之前包含一个输出正确大小的层。例如,对于具有 R 个响应的图像回归,为了确保 Y 是具有正确大小的四维数组,您可以在输出层之前包含一个大小为 R 的全连接层。

forwardLossbackwardLoss 函数具有以下输出参数。

函数输出参数说明
forwardLossloss预测 Y 和真实目标 T 之间的计算损失。
backwardLossdLdY损失关于预测 Y 的导数。

backwardLoss 必须输出 dLdY,其大小为前一个层预期的大小,并且 dLdYY 大小相同。

GPU 兼容性

如果层前向函数完全支持 dlarray 对象,则该层与 GPU 兼容。否则,为了与 GPU 兼容,层函数必须支持 gpuArray (Parallel Computing Toolbox) 类型的输入并返回其输出。

许多 MATLAB 内置函数支持 gpuArray (Parallel Computing Toolbox)dlarray 输入参数。有关支持 dlarray 对象的函数列表,请参阅List of Functions with dlarray Support。有关在 GPU 上执行的函数的列表,请参阅Run MATLAB Functions on a GPU (Parallel Computing Toolbox)要使用 GPU 进行深度学习,您还必须有支持 CUDA 且具有 3.0 或更高计算能力的 NVIDIA GPU。有关在 MATLAB 中使用 GPU 的详细信息,请参阅GPU Computing in MATLAB (Parallel Computing Toolbox)

在网络中包含自定义回归输出层

您可以像使用 Deep Learning Toolbox 中的任何其他输出层一样使用自定义输出层。本节说明如何使用自定义输出层创建和训练网络以用于回归任务。

该示例构造一个卷积神经网络架构,训练网络,并使用经过训练的网络预测手写数字的旋转角度。这些预测对于光学字符识别很有用。

定义一个自定义均值绝对误差回归层。要创建此层,请将文件 maeRegressionLayer.m 保存在当前文件夹中。

加载示例训练数据。

[XTrain,~,YTrain] = digitTrain4DArrayData;

创建一个层数组,并包含自定义回归输出层 maeRegressionLayer

layers = [
    imageInputLayer([28 28 1])
    convolution2dLayer(5,20)
    batchNormalizationLayer
    reluLayer
    fullyConnectedLayer(1)
    maeRegressionLayer('mae')]
layers = 
  6x1 Layer array with layers:

     1   ''      Image Input           28x28x1 images with 'zerocenter' normalization
     2   ''      Convolution           20 5x5 convolutions with stride [1  1] and padding [0  0  0  0]
     3   ''      Batch Normalization   Batch normalization
     4   ''      ReLU                  ReLU
     5   ''      Fully Connected       1 fully connected layer
     6   'mae'   Regression Output     Mean absolute error

设置训练选项并训练网络。

options = trainingOptions('sgdm','Verbose',false);
net = trainNetwork(XTrain,YTrain,layers,options);

通过计算预测旋转角度和实际旋转角度之间的预测误差来评估网络性能。

[XTest,~,YTest] = digitTest4DArrayData;
YPred = predict(net,XTest);
predictionError = YTest - YPred;

计算在实际角度的可接受误差界限内的预测值的数量。将阈值设置为 10 度,并计算此阈值范围内的预测百分比。

thr = 10;
numCorrect = sum(abs(predictionError) < thr);
numTestImages = size(XTest,4);
accuracy = numCorrect/numTestImages
accuracy = 0.8064

另请参阅

| | | | |

相关主题