定义具有可学习参数的自定义深度学习层
如果深度学习工具箱™不提供任务所需的层,则可以使用此示例作为指导定义自己的自定义层。有关内置层的列表,请参见深度学习层列表.
要定义自定义深度学习层,您可以使用本例中提供的模板,该模板将带您完成以下步骤:
给图层命名——给图层命名以便在MATLAB中使用®.
声明层属性——指定层的属性,包括可学习参数和状态参数。
创建一个构造函数(可选)——指定如何构造这个层并初始化它的属性。如果没有指定构造函数,则在创建时,软件将初始化
的名字
,描述
,类型
属性与[]
并设置层输入和输出的数量为1。创建初始化函数(可选)—指定软件初始化网络时如何初始化可学习参数和状态参数。如果不指定初始化函数,则软件在初始化网络时不会初始化参数。
创建前向函数——指定数据在预测时和训练时如何通过层(前向传播)向前传递。
创建reset state功能(可选)-指定重置状态参数的方式。
创建反向函数(可选)——指定损失对输入数据和可学习参数的导数(反向传播)。如果没有指定向后函数,则向前函数必须支持
dlarray
对象。
在定义层函数时,可以使用dlarray
对象。使用dlarray
对象允许对维度进行标记,从而使处理高维数据更加容易。方法标识哪些维度对应于空间、时间、通道和批处理维度“S”
,“T”
,“C”
,“B”
标签,分别。对于未指定的和其他维度,请使用“U”
标签。为dlarray
对象函数对特定维度进行操作时,可以通过格式化dlarray
对象直接调用,或使用DataFormat
选择。
使用格式化的dlarray
自定义层中的对象还允许您定义输入和输出具有不同格式的层,例如排列、添加或删除维度的层。例如,您可以定义一个层,它接受具有以下格式的小批图像作为输入“SSCB”
(空间,空间,通道,批处理)和输出小批序列的格式“认知行为治疗”
(渠道、批次、时间)。使用格式化的dlarray
对象还允许您定义可以操作具有不同输入格式的数据的层,例如,支持具有这些格式的输入的层“SSCB”
(空间,空间,渠道,批处理)和“认知行为治疗”
(渠道、批次、时间)。
dlarray
对象还支持自动区分。因此,如果您的forward功能完全支持dlarray
对象,那么定义反向函数是可选的。
启用对使用格式化的dlarray
对象在自定义层转发函数,也继承自nnet.layer.Formattable
类在定义自定义层时使用。示例请参见定义带有格式化输入的自定义深度学习层.
这个例子展示了如何创建一个PReLU层,这是一个具有可学习参数的层,并在卷积神经网络中使用它。PReLU层执行阈值操作,其中对于每个通道,任何小于零的输入值都乘以训练时学习到的标量。[1]对于小于零的值,PReLU层应用缩放系数 输入的每个通道。这些系数形成一个可学习的参数,该层在训练过程中学习。
这个数字来自[1]比较了ReLU和PReLU层函数。
中间层模板
在MATLAB中将中间层模板复制到一个新文件中。该模板给出了中间层类定义的结构。概述:
可选
属性
块的层属性,可学习参数,和状态参数。层构造函数。
可选
初始化
函数。的
预测
函数和可选的向前
函数。可选
resetState
函数用于具有状态属性的层。可选
落后的
函数。
classdefmyLayer < nnet.layer.Layer%……可格式化…%(可选)% & nnet.layer.Acceleratable %(可选)属性%(可选)图层属性。在这里声明图层属性。结束属性(可学的)%(可选)层可学习参数。在这里声明可学习的参数。结束属性(状态)%(可选)层状态参数。在这里声明状态参数。结束属性(可学的,状态)%(可选)嵌套的dlnetwork对象,两者都可学习参数和状态参数。在这里使用可学习和状态参数声明嵌套网络。结束方法函数图层= myLayer()%(可选)创建myLayer。此函数必须与类同名。在这里定义层构造函数。结束函数图层=初始化(图层,布局)%(可选)初始化可学习层和状态参数。%%的输入:% layer -要初始化的层% layout -数据布局,指定为networkDataLayout%的对象%%输出:初始化层%% -对于有多个输入的层,将布局替换为% layout1,…,layoutN, where N is the number of inputs.在这里定义层初始化函数。结束函数[Z,state] = predict(层,X)%在预测时间和转发输入数据通过层%输出结果和更新状态。%%的输入:% layer -向前传播的层% X -输入数据%输出:% Z -层正向函数的输出% state -(可选)更新的层状态%% -对于有多个输入的层,将X替换为X1,…,XN,%,其中N是输入的数量。% -对于有多个输出的层,将Z替换为% Z1,…,Z米,在那里米我s the number of outputs.% -对于有多个状态参数的层,替换state% with state1,…,状态K,在那里K我s the number of state%的参数。在这里定义层预测函数。结束函数[Z,状态,内存]=前进(层,X)%(可选)通过训练层转发输入数据%时间,并输出结果,更新状态,和一个内存%值。%%的输入:% layer -向前传播的层% X -层输入数据%输出:% Z -层正向函数的输出% state -(可选)更新的层状态%内存-(可选)自定义向后的内存值%的功能%% -对于有多个输入的层,将X替换为X1,…,XN,%,其中N是输入的数量。% -对于有多个输出的层,将Z替换为% Z1,…,Z米,在那里米我s the number of outputs.% -对于有多个状态参数的层,替换state% with state1,…,状态K,在那里K我s the number of state%的参数。在这里定义层转发函数。结束函数图层= resetState(图层)%(可选)重置层状态。在这里定义重置状态函数。结束函数[dLdX,dLdW,dLdSin] =向后(层,X,Z,dLdZ,dLdSout,内存)%(可选)反向传播损失的导数%函数通过层。%%的输入:% layer -向后传播的层% X -层输入数据% Z -层输出数据-损失对层的导数%输出% dLdSout -(可选)关于损失的导数%到状态输出% memory -转发功能的内存值%输出:% dLdX -损失对层输入的导数% dLdW -(可选)损失对的导数%可学习参数% dLdSin -(可选)损失对的导数%状态输入%% -对于有状态参数的层,必须使用向后语法%包含dLdSout和dLdSin,或两者都不包含。% -对于有多个输入的层,将X和dLdX替换为% X1,…,XN而且dLdX1,...,dLdXN, respectively, where N is%输入的数量。% -对于有多个输出的层,将Z和dlZ替换为% Z1,…,Z米而且dLdZ,...,dLdZM, respectively, where M is the输出的百分比。% -对于有多个可学习参数的层,替换% dLdW with dLdW1,…,dLdWP, where P is the number of%可学习参数。% -对于有多个状态参数的层,替换dLdSin% and dLdSout with dLdSin1,…、dLdSinK和% dLdSout1,…,dldSoutK, respectively, where K is the number%的状态参数。在这里定义层向后函数。结束结束结束
命名层和指定超类
首先,给图层起一个名字。在类文件的第一行中,替换现有的名称myLayer
与preluLayer
.
classdefpreluLayer < nnet.layer.Layer%……可格式化…%(可选)% & nnet.layer.Acceleratable %(可选)…结束
如果不指定反向函数,则默认情况下,层函数为receive无格式dlarray
对象作为输入。指定该层接收格式化dlarray
对象作为格式化的输入和输出dlarray
对象,也继承自nnet.layer.Formattable
类在定义自定义层时使用。
层函数支持加速,因此也继承自nnet.layer.Acceleratable
.有关加速自定义层函数的更多信息,请参见自定义层函数加速.该层不需要可格式化的输入,因此删除可选的输入nnet.layer.Formattable
超类。
classdefpreluLayer < nnet.layer.Layer…& nnet.layer.Acceleratable…结束
接下来,将myLayer
构造函数的第一个函数方法
Section),这样它就有了与图层相同的名称。
方法函数图层= preluLayer()…结束…结束
保存图层
将图层类文件保存在一个名为preluLayer.m
.文件名必须与层名匹配。要使用该层,必须将文件保存在当前文件夹或MATLAB路径上的文件夹中。
声明属性和可学习参数
中声明层属性属性
类中列出可学习参数,并声明这些参数属性(可学的)
部分。
默认情况下,自定义中间层具有这些属性。中不要声明这些属性属性
部分。
财产 | 描述 |
---|---|
的名字 |
层名,指定为字符向量或字符串标量。为层 数组输入时,trainNetwork ,assembleNetwork ,layerGraph ,dlnetwork 函数自动为具有此名称的层分配名称'' . |
描述 |
层的一行描述,指定为字符串标量或字符向量。当该层显示在 如果不指定层描述,则软件显示层类名称。 |
类型 |
层的类型,指定为字符向量或字符串标量。的价值 如果不指定层类型,则软件显示层类名称。 |
NumInputs |
层的输入数,指定为正整数。如果您不指定此值,那么软件将自动设置NumInputs 的名字的数量InputNames .缺省值为1。 |
InputNames |
输入层的名称,指定为字符向量的单元格数组。如果不指定此值和NumInputs 大于1,那么软件自动设置InputNames 来{“三机”,…,“客栈”} ,在那里N 等于NumInputs .默认值为{'在'} . |
NumOutputs |
层的输出数,指定为正整数。如果您不指定此值,那么软件将自动设置NumOutputs 的名字的数量OutputNames .缺省值为1。 |
OutputNames |
输出层的名称,指定为字符向量的单元格数组。如果不指定此值和NumOutputs 大于1,那么软件自动设置OutputNames 来{着干活,…,“outM”} ,在那里米 等于NumOutputs .默认值为{“出”} . |
如果该层没有其他属性,则可以省略属性
部分。
提示
如果要创建具有多个输入的层,则必须设置NumInputs
或InputNames
层构造函数中的属性。如果要创建具有多个输出的层,则必须设置NumOutputs
或OutputNames
层构造函数中的属性。示例请参见定义具有多重输入的自定义深度学习层.
PReLU层不需要任何附加属性,因此可以删除属性
部分。
一个PReLU层只有一个可学习的参数,即缩放系数一个.中声明此可学习参数属性(可学的)
节并调用参数α
.
属性(可学的)%层可学习参数%结垢系数α结束
Create Constructor命令功能
创建构造层并初始化层属性的函数。指定创建层所需的任何变量,作为构造函数的输入。
PReLU层构造函数需要一个可选参数(层名)。指定一个名为arg游戏
在preluLayer
函数对应于可选的名称-值参数。在函数的顶部添加注释,解释函数的语法。
函数图层= preluLayer(args)创建一个PReLU层。%% layer = preluLayer(Name= Name)也指定了%层名…结束
初始化图层属性
在构造函数中初始化图层属性。替换注释%层构造函数在这里
使用初始化层属性的代码。不要在构造函数中初始化可学习形参或状态形参,而是在初始化
函数来代替。
类解析输入参数参数
块,并设置的名字
财产。
参数arg游戏。的名字="";结束设置图层名称。层。Name =arg游戏。的名字;
给图层一个单行描述描述
层的属性。设置描述来描述层的类型。
设置层描述。层。描述=“PReLU”;
查看已完成的构造函数。
函数图层= preluLayer(args)创建一个PReLU层。%% layer = preluLayer(Name= Name)也指定了%层名称。参数arg游戏。的名字="";结束设置图层名称。层。Name =arg游戏。的名字;设置层描述。层。描述=“PReLU”;结束
对于这个构造函数,命令preluLayer (Name = " prelu ")
创建一个带有名称的PReLU层“prelu”
.
Create Initialize命令功能
创建在软件初始化网络时初始化可学习层和状态参数的函数。
初始化可学习参数α
,生成一个与输入数据具有相同通道数的随机向量。
因为在网络准备好使用之前,输入数据的大小是未知的,所以必须创建一个initialize函数,该函数使用初始化可学习参数和状态参数networkDataLayout
软件提供给函数的对象。网络数据布局对象包含有关预期输入数据的大小和格式的信息。创建一个initialize函数,该函数使用大小和格式信息初始化可学习参数和状态参数,使它们具有正确的大小。
可学习参数α
具有与输入观测数据相同的维数,其中通道维数与输入数据的通道维数相同,其余维数为单维。创建一个初始化
从输入中提取大小和格式信息的函数networkDataLayout
对象,并初始化可学习参数α
有相同数量的通道。
函数图层=初始化(图层,布局)初始化(图层,布局)初始化图层%可学习的参数使用指定的输入布局。输入数据大小。sz = layout.Size;Ndims = numel(sz);查找通道数量。Idx = finddim(布局,“C”);numChannels = sz(idx);初始化Alpha。szAlpha = ones(1,ndim);szAlpha(idx) = numChannels;层。α=rand(szAlpha);结束
创建前向函数
创建在预测时和训练时使用的层转发函数。
创建一个名为预测
的层转发数据预测的时间并输出结果。
的预测
函数语法取决于层的类型。
Z = predict(layer,X)
转发输入数据X
通过层并输出结果Z
,在那里层
只有一个输入和一个输出。[Z,state] = predict(层,X)
还输出更新后的状态参数状态
,在那里层
只有一个状态参数。
您可以调整具有多个输入、多个输出或多个状态参数的层的语法:
对于有多个输入的层,替换
X
与X1,…,XN
,在那里N
是输入的数量。的NumInputs
属性必须匹配N
.对于有多个输出的层,替换
Z
与Z1,…,ZM评选
,在那里米
是输出的数量。的NumOutputs
属性必须匹配米
.对于有多个状态参数的层,替换
状态
与state1,…,stateK
,在那里K
状态参数的个数。
提示
如果对层的输入数量可以变化,那么使用变长度输入宗量
而不是X1,…,XN
.在这种情况下,变长度输入宗量
单元格数组的输入在哪里变长度输入宗量{我}
对应于西
.
如果输出的数量可以变化,那么使用varargout
而不是Z1,…,锌
.在这种情况下,varargout
是单元格数组的输出,在哪里varargout {j}
对应于Zj
.
提示
如果自定义层有dlnetwork
对象获取可学习参数,然后在预测
函数的自定义层,使用预测
的函数dlnetwork
.当你这样做的时候dlnetwork
对象预测
函数使用适当的层操作进行预测。
因为PReLU层只有一个输入和一个输出,语法预测
对于PReLU层为Z = predict(layer,X)
.
默认情况下,该层使用预测
作为训练时的正向函数。要在训练时使用不同的前向函数,或保留自定义后向函数所需的值,还必须创建名为向前
.
输入的尺寸取决于数据的类型和连接层的输出:
层的输入 | 输入的大小 | 观察维度 |
---|---|---|
特征向量 | c——- - - - - -N,在那里c对应于通道和的数量N观察的次数是多少 | 2 |
二维图像 | h——- - - - - -w——- - - - - -c——- - - - - -N,在那里h,w,c分别对应于图像的高度、宽度和通道数,和N观察的次数是多少 | 4 |
三维图像 | h——- - - - - -w——- - - - - -d——- - - - - -c——- - - - - -N,在那里h,w,d,c分别为三维图像的高度、宽度、深度和通道数,和N观察的次数是多少 | 5 |
向量序列 | c——- - - - - -N——- - - - - -年代,在那里c是序列特征的数量,N是观察的次数,和年代是序列长度 | 2 |
二维图像序列 | h——- - - - - -w——- - - - - -c——- - - - - -N——- - - - - -年代,在那里h,w,c分别对应于图像的高度、宽度和通道数,N是观察的次数,和年代是序列长度 | 4 |
三维图像序列 | h——- - - - - -w——- - - - - -d——- - - - - -c——- - - - - -N——- - - - - -年代,在那里h,w,d,c分别对应三维图像的高度、宽度、深度和通道数,N是观察的次数,和年代是序列长度 | 5 |
对于输出序列的层,该层可以输出任意长度的序列或输出无时间维的数据。方法来训练输出序列的网络时请注意trainNetwork
函数中,输入和输出序列的长度必须匹配。
的向前
函数通过层转发数据培训时间并输出一个内存值。
的向前
函数语法取决于层的类型:
Z =向前(层,X)
转发输入数据X
通过层并输出结果Z
,在那里层
只有一个输入和一个输出。[Z,state] =前进(层,X)
还输出更新后的状态参数状态
,在那里层
只有一个状态参数。[__,memory] = forward(layer,X)
还返回自定义的内存值落后的
函数使用前面的任何语法。如果该层有两个自定义向前
函数和自定义落后的
函数,则forward函数必须返回一个内存值。
您可以调整具有多个输入、多个输出或多个状态参数的层的语法:
对于有多个输入的层,替换
X
与X1,…,XN
,在那里N
是输入的数量。的NumInputs
属性必须匹配N
.对于有多个输出的层,替换
Z
与Z1,…,ZM评选
,在那里米
是输出的数量。的NumOutputs
属性必须匹配米
.对于有多个状态参数的层,替换
状态
与state1,…,stateK
,在那里K
状态参数的个数。
提示
如果对层的输入数量可以变化,那么使用变长度输入宗量
而不是X1,…,XN
.在这种情况下,变长度输入宗量
单元格数组的输入在哪里变长度输入宗量{我}
对应于西
.
如果输出的数量可以变化,那么使用varargout
而不是Z1,…,锌
.在这种情况下,varargout
是单元格数组的输出,在哪里varargout {j}
对应于Zj
.
提示
如果自定义层有dlnetwork
对象获取可学习参数,然后在向前
函数的自定义层,使用向前
的功能dlnetwork
对象。当你这样做的时候dlnetwork
对象向前
函数使用适当的层操作进行训练。
PReLU操作由
在哪里 是非线性激活的输入吗f频道我, 是控制负部分斜率的系数。下标我在 表示非线性激活可以在不同的通道上变化。
实现此操作预测
.在预测
,输入X
对应于x在等式中。输出Z
对应于
.PReLU层不需要内存或不同的前向函数进行训练,因此可以删除向前
函数从类文件中获取。在函数的顶部添加注释,解释函数的语法。
提示
如果您使用诸如0
,则必须确保这些数组的数据类型与层函数输入一致。方法创建数据类型与另一个数组相同的零数组“喜欢”
选择0
.例如,初始化一个大小为0的数组深圳
使用与数组相同的数据类型X
,使用Z = 0 (sz,"like",X)
.
函数Z = predict(layer, X)% Z = predict(层,X)转发输入数据X通过%层,并输出结果Z。Z = max(X,0) +图层。Alpha .* min(0,X);结束
因为预测
函数只使用支持的函数dlarray
对象,定义落后的
函数可选。获取支持的函数的列表dlarray
对象,看到dlarray支持的函数列表.
完成一层
查看完成的层类文件。
classdefpreluLayer < nnet.layer.Layer…& nnet.layer.Acceleratable示例自定义PReLU层。属性(可学的)%层可学习参数%结垢系数α结束方法函数图层= preluLayer(args)创建一个PReLU层。%% layer = preluLayer(Name= Name)也指定了%层名称。参数arg游戏。的名字="";结束设置图层名称。层。Name =arg游戏。的名字;设置层描述。层。描述=“PReLU”;结束函数图层=初始化(图层,布局)初始化(图层,布局)初始化图层%可学习的参数使用指定的输入布局。输入数据大小。sz = layout.Size;Ndims = numel(sz);查找通道数量。Idx = finddim(布局,“C”);numChannels = sz(idx);初始化Alpha。szAlpha = ones(1,ndim);szAlpha(idx) = numChannels;层。α=rand(szAlpha);结束函数Z = predict(layer, X)% Z = predict(层,X)转发输入数据X通过%层,并输出结果Z。Z = max(X,0) +图层。Alpha .* min(0,X);结束结束结束
GPU的兼容性
如果转发层功能完全支持dlarray
对象,那么该层是GPU兼容的。否则,为了与GPU兼容,层函数必须支持类型的输入和返回输出gpuArray
(并行计算工具箱).
许多MATLAB内置函数支持gpuArray
(并行计算工具箱)而且dlarray
输入参数。获取支持的函数的列表dlarray
对象,看到dlarray支持的函数列表.有关在GPU上执行的函数列表,请参见在图形处理器上运行MATLAB函数(并行计算工具箱).要使用GPU进行深度学习,还必须有支持的GPU设备。有关支持的设备的信息,请参见GPU计算要求(并行计算工具箱).有关在MATLAB中使用gpu的更多信息,请参见MATLAB中的GPU计算(并行计算工具箱).
在本例中,所用的MATLAB函数预测
所有的支持dlarray
对象,所以该层是GPU兼容的。
检查自定义层使用的有效性checkLayer
检查自定义层的层有效性preluLayer
.
自定义层preluLayer
,作为支持文件附加到这个示例中,它将PReLU操作应用于输入数据。要访问此层,请将此示例作为实时脚本打开。
创建一个层的实例。
图层= preluLayer;
因为该层有一个自定义的初始化函数,所以使用networkDataFormat
对象,该对象指定对层的典型输入的单个观察的预期输入大小和格式。
指定有效的输入大小[24 24 20]
,其中维数对应上一层输出的高度、宽度和通道数。
validInputSize = [24 24 20];布局= networkDataLayout(validInputSize,“SSC”);图层=初始化(图层,布局);
检查层有效性使用checkLayer
.指定有效的输入大小为初始化层时使用的大小。当您通过网络传递数据时,该层期望4-D数组输入,其中前三个维度对应于前一层输出的高度、宽度和通道数量,而第四个维度对应于观察结果。
指定观测值输入的典型大小并设置ObservationDimension
选项为4。
checkLayer(层、validInputSize ObservationDimension = 4)
跳过GPU测试。没有找到兼容的GPU设备。跳过代码生成兼容性测试。要检查代码生成层的有效性,请指定“CheckCodegenCompatibility”和“ObservationDimension”选项。运行nnet.checklayer.TestLayerWithoutBackward .......... ........testlayerwithoutbackward __________测试总结:18通过,0失败,0不完整,10跳过。时间:0.26993秒。
该函数不会检测到该层的任何问题。
在网络中包含自定义层
您可以以与“深度学习工具箱”中任何其他层相同的方式使用自定义层。本节展示如何使用前面创建的PReLU层创建和训练用于数字分类的网络。
加载示例训练数据。
[XTrain,YTrain] = digitTrain4DArrayData;
创建一个包含自定义层的层数组preluLayer
,附上一个例子作为支持文件。要访问此层,请将此示例作为实时脚本打开。
layers = [imageInputLayer([28 28 1]) convolution2dLayer(5,20) batchNormalizationLayer preluLayer fullyConnectedLayer(10) softmaxLayer classificationLayer];
设置培训选项,培训网络。
options = trainingOptions(“亚当”, MaxEpochs = 10);net = trainNetwork(XTrain,YTrain,layers,options);
单CPU训练。初始化输入数据规范化。|========================================================================================| | 时代| |迭代时间| Mini-batch | Mini-batch |基地学习 | | | | ( hh: mm: ss) | | |丧失准确性 | |========================================================================================| | 1 | 1 |就是9.38% | | 2.8873 | 0.0010 | | 2 | 50 | 00:00:02 | 79.69% | 0.7222 | 0.0010 | | 3 | 100 | 00:00:05 | 86.72% | 0.4213 | 0.0010 | | 150 | | 00:00:08 | 94.53% | 0.2129 | 0.0010 ||6 | 200 | 00:00:11 | 94.53% | 0.1884 | 0.0010 | | 7 | 250 | 00:00:14 | 100.00% | 0.0847 | 0.0010 | | 8 | 300 | 00:00:17 | 100.00% | 0.0470 | 0.0010 | | 9 | 350 | 00:00:19 | 100.00% | 0.0299 | 0.0010 | | 10 | 390 | 00:00:21 | 100.00% | 0.0375 | 0.0010 | |========================================================================================| Training finished: Max epochs completed.
通过对新数据的预测和计算精度来评估网络性能。
[XTest,YTest] = digitTest4DArrayData;YPred = category (net,XTest);精度=平均值(YTest==YPred)
精度= 0.9534
参考文献
[1]“深入研究整流器:在ImageNet分类上超越人类水平的性能。”2015年IEEE计算机视觉国际会议(ICCV)1026 - 34。圣地亚哥,智利:IEEE, 2015。https://doi.org/10.1109/ICCV.2015.123。
另请参阅
functionLayer
|checkLayer
|setLearnRateFactor
|setL2Factor
|getLearnRateFactor
|getL2Factor
|findPlaceholderLayers
|replaceLayer
|assembleNetwork
|PlaceholderLayer
|networkDataLayout