主要内容

为代码生成定义自定义深度学习层

如果深度学习工具箱™没有为您的分类或回归问题提供所需的层,那么您可以使用此示例作为指导定义自己的自定义层。有关内置层的列表,请参见深度学习层列表

要定义自定义深度学习层,您可以使用本例中提供的模板,该模板将带您完成以下步骤:

  1. 给图层命名——给图层命名以便在MATLAB中使用®

  2. 声明层属性——指定层的属性,包括可学习参数和状态参数。

  3. 创建一个构造函数(可选)——指定如何构造这个层并初始化它的属性。如果没有指定构造函数,则在创建时,软件将初始化的名字描述,类型属性与[]并设置层输入和输出的数量为1。

  4. 创建初始化函数(可选)—指定软件初始化网络时如何初始化可学习参数和状态参数。如果不指定初始化函数,则软件在初始化网络时不会初始化参数。

  5. 创建前向函数——指定数据在预测时和训练时如何通过层(前向传播)向前传递。

  6. 创建reset state功能(可选)-指定重置状态参数的方式。

  7. 创建反向函数(可选)——指定损失对输入数据和可学习参数的导数(反向传播)。如果没有指定向后函数,则向前函数必须支持dlarray对象。

创建一个支持代码生成的自定义层:

  • 该层必须指定pragma% # codegen在层定义中。

  • 的输入预测必须:

    • 一致的维度。每个输入必须具有相同的维数。

    • 批次大小一致。每个输入必须具有相同的批处理大小。

  • 的输出预测必须在尺寸和批量大小上与层输入一致。

  • 非标量属性必须具有单、双或字符数组类型。

  • 标量属性必须具有数值型、逻辑型或字符串型。

代码生成只支持具有二维图像或特征输入的中间层。代码生成不支持带有状态属性的层(带有属性的属性)状态).

这个例子展示了如何创建一个PReLU层[1],这是一个具有可学习参数的层,并将其用于卷积神经网络。PReLU层执行阈值操作,其中对于每个通道,任何小于零的输入值都乘以训练时学习到的标量。对于小于零的值,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,状态]=预测(层,X)%在预测时间和转发输入数据通过层%输出结果和更新状态。%的输入:% layer -向前传播的层% X -输入数据%输出:% Z -层正向函数的输出% state -(可选)更新的层状态% -对于有多个输入的层,将X替换为X1,…,XN,%,其中N是输入的数量。% -对于有多个输出的层,将Z替换为% Z1,…,Z米,在那里米我s the number of outputs.% -对于有多个状态参数的层,替换state% 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% 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%的状态参数。在这里定义层向后函数。结束结束结束

命名层和指定超类

首先,给图层起一个名字。在类文件的第一行中,替换现有的名称myLayercodegenPreluLayer并添加一个描述层的注释。

classdefcodegenPreluLayer < nnet.layer.Layer & nnet.layer.可格式化示例自定义PReLU层,支持代码生成。...结束

如果不指定反向函数,则默认情况下,层函数为receive无格式dlarray对象作为输入。指定该层接收格式化dlarray对象作为格式化的输入和输出dlarray对象,也继承自nnet.layer.Formattable类在定义自定义层时使用。

该层不需要可格式化的输入,因此删除可选的输入nnet.layer.Formattable超类。

classdefcodegenPreluLayer < nnet.layer.Layer示例自定义PReLU层,支持代码生成。...结束

接下来,重命名myLayer构造函数的第一个函数方法Section),这样它就有了与图层相同的名称。

方法函数layer = codegenPreluLayer()…结束...结束

保存层

将图层类文件保存在一个名为codegenPreluLayer.m.文件名必须与层名匹配。要使用该层,必须将文件保存在当前文件夹或MATLAB路径上的文件夹中。

指定代码生成Pragma

添加% # codegen指示(或pragma)到您的层定义,以指示您打算为该层生成代码。添加此指令将指导MATLAB代码分析器帮助您诊断和修复代码生成过程中导致错误的违反。

classdefcodegenPreluLayer < nnet.layer.Layer示例自定义PReLU层,支持代码生成。% # codegen...结束

声明属性和可学习参数

中声明层属性属性类中列出可学习参数,并声明这些参数属性(可学的)部分。

默认情况下,自定义中间层具有这些属性。中不要声明这些属性属性部分。

财产 描述
的名字 层名,指定为字符向量或字符串标量。为数组输入,trainNetworkassembleNetworklayerGraph,dlnetwork函数自动为具有此名称的层分配名称
描述

层的一行描述,指定为字符串标量或字符向量。当该层显示在数组中。

如果不指定层描述,则软件显示层类名称。

类型

层的类型,指定为字符向量或字符串标量。的价值类型控件中显示该层时显示数组中。

如果不指定层类型,则软件显示层类名称。

NumInputs 层的输入数,指定为正整数。如果您不指定此值,那么软件将自动设置NumInputs的名字的数量InputNames.缺省值为1。
InputNames 输入层的名称,指定为字符向量的单元格数组。如果不指定此值和NumInputs大于1,那么软件自动设置InputNames{“三机”,…,“客栈”},在那里N等于NumInputs.默认值为{'在'}
NumOutputs 层的输出数,指定为正整数。如果您不指定此值,那么软件将自动设置NumOutputs的名字的数量OutputNames.缺省值为1。
OutputNames 输出层的名称,指定为字符向量的单元格数组。如果不指定此值和NumOutputs大于1,那么软件自动设置OutputNames{着干活,…,“outM”},在那里等于NumOutputs.默认值为{“出”}

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

提示

如果要创建具有多个输入的层,则必须设置NumInputsInputNames层构造函数中的属性。如果要创建具有多个输出的层,则必须设置NumOutputsOutputNames层构造函数中的属性。示例请参见定义具有多重输入的自定义深度学习层

支持代码生成:

  • 非标量属性必须具有单、双或字符数组类型。

  • 标量属性必须是数值类型,或者具有逻辑类型或字符串类型。

PReLU层不需要任何附加属性,因此可以删除属性部分。

一个PReLU层只有一个可学习的参数,即缩放系数一个.中声明此可学习参数属性(可学的)节并调用参数α

属性(可学的)%层可学习参数%比例系数α结束

创建构造函数

创建构造层并初始化层属性的函数。指定创建层所需的任何变量,作为构造函数的输入。

PReLU层构造函数需要两个输入参数:预期输入数据的通道数和层名。通道的数量指定了可学习参数的大小α.指定两个名为的输入参数numChannels而且的名字codegenPreluLayer函数。在函数的顶部添加注释,解释函数的语法。

函数图层= codegenPreluLayer(numChannels, name)% layer = codegenprelu ayer(numChannels)创建一个PReLU层% numChannels通道并指定层名。...结束

不支持代码生成参数块。

初始化层属性

在构造函数中初始化层属性,包括可学习参数。取代的评论%层构造函数在这里使用初始化层属性的代码。

设置的名字属性设置为输入参数的名字

设置图层名称。层。的名字=的名字;

给图层一个单行描述描述层的属性。设置描述来描述层的类型和大小。

设置层描述。层。描述=“PReLU与“+ numChannels +“通道”

对于PReLU层,当输入值为负数时,该层将输入的每个通道乘以对应的通道α.初始化可学习参数α大小为1乘1乘-的随机向量numChannels.第三个维度指定为尺寸numChannels,该层可以在正向函数中使用输入的元素相乘。α是层对象的一个属性,所以你必须把向量分配给层。α

初始化缩放系数。层。α=rand([1 1 numChannels]);

查看已完成的构造函数。

函数图层= codegenPreluLayer(numChannels, name)% layer = codegenprelu ayer(numChannels, name)创建一个PReLU%层用于二维图像输入与numChannels通道和指定%层名。设置图层名称。层。的名字=的名字;设置层描述。层。描述=“PReLU与“+ numChannels +“通道”初始化缩放系数。层。α=rand([1 1 numChannels]);结束

对于这个构造函数,命令codegenPreluLayer(“prelu”)创建一个带有三个通道和名称的PReLU层“prelu”

创建向前函数

创建在预测时和训练时使用的层转发函数。

创建一个名为预测的层转发数据预测的时间并输出结果。

预测函数语法取决于层的类型。

  • Z =预测(层,X)转发输入数据X通过层并输出结果Z,在那里只有一个输入和一个输出。

  • [Z,状态]=预测(层,X)还输出更新后的状态参数状态,在那里只有一个状态参数。

您可以调整具有多个输入、多个输出或多个状态参数的层的语法:

  • 对于有多个输入的层,替换XX1,…,XN,在那里N是输入的数量。的NumInputs属性必须匹配N

  • 对于有多个输出的层,替换ZZ1,…,ZM评选,在那里是输出的数量。的NumOutputs属性必须匹配

  • 对于有多个状态参数的层,替换状态state1,…,stateK,在那里K状态参数的个数。

提示

如果对层的输入数量可以变化,那么使用变长度输入宗量而不是X1,…,XN.在这种情况下,变长度输入宗量单元格数组的输入在哪里变长度输入宗量{我}对应于西

如果输出的数量可以变化,那么使用varargout而不是Z1,…,锌.在这种情况下,varargout是单元格数组的输出,在哪里varargout {j}对应于Zj

因为PReLU层只有一个输入和一个输出,语法预测对于PReLU层为Z =预测(层,X)

代码生成支持只使用二维图像输入的自定义中间层。的输入是h——- - - - - -w——- - - - - -c——- - - - - -N数组,hw,c分别对应于图像的高度、宽度和通道数,和N是观察数。观测维数为4。

对于代码生成支持,所有层输入必须具有相同的维度数量和批处理大小。

默认情况下,该层使用预测作为训练时的正向函数。要在训练时使用不同的前向函数,或保留自定义后向函数所需的值,还必须创建名为向前.该软件不生成代码向前函数,但它必须与代码生成兼容。

向前函数通过层转发数据培训时间并输出一个内存值。

向前函数语法取决于层的类型:

  • Z =前进(层,X)转发输入数据X通过层并输出结果Z,在那里只有一个输入和一个输出。

  • [Z,状态]=前进(层,X)还输出更新后的状态参数状态,在那里只有一个状态参数。

  • (__、内存)=(层,X)转发还返回自定义的内存值落后的函数使用前面的任何语法。如果该层有两个自定义向前函数和自定义落后的函数,则forward函数必须返回一个内存值。

您可以调整具有多个输入、多个输出或多个状态参数的层的语法:

  • 对于有多个输入的层,替换XX1,…,XN,在那里N是输入的数量。的NumInputs属性必须匹配N

  • 对于有多个输出的层,替换ZZ1,…,ZM评选,在那里是输出的数量。的NumOutputs属性必须匹配

  • 对于有多个状态参数的层,替换状态state1,…,stateK,在那里K状态参数的个数。

提示

如果对层的输入数量可以变化,那么使用变长度输入宗量而不是X1,…,XN.在这种情况下,变长度输入宗量单元格数组的输入在哪里变长度输入宗量{我}对应于西

如果输出的数量可以变化,那么使用varargout而不是Z1,…,锌.在这种情况下,varargout是单元格数组的输出,在哪里varargout {j}对应于Zj

PReLU操作由

f x x 如果 x > 0 α x 如果 x 0

在哪里 x 是非线性激活的输入吗f频道, α 是控制负部分斜率的系数。下标 α 表示非线性激活可以在不同的通道上变化。

实现此操作预测.在预测,输入X对应于x在方程。输出Z对应于 f x

在函数的顶部添加注释,解释函数的语法。

提示

如果您使用诸如0,则必须确保这些数组的数据类型与层函数输入一致。方法创建数据类型与另一个数组相同的零数组“喜欢”选择0.例如,初始化一个大小为0的数组深圳使用与数组相同的数据类型X,使用Z = 0(深圳,“喜欢”,X)

实现落后的功能在转发功能完全支持时可选dlarray输入。对于代码生成支持,请使用预测函数还必须支持数字输入。

计算PReLU操作输出的一种方法是使用以下代码。

Z = max(X,0) +图层。α。*分钟(0,X);
方法的隐式展开,因为代码生成不支持.*操作时,可以使用bsxfun函数来代替。
Z = max(X,0) + bsxfun(@times, layer。α,最小值(0,X));
然而,bsxfun不支持dlarray输入。来实现预测函数,该函数同时支持代码生成和dlarray输入,使用一个如果声明的isdlarray函数为输入类型选择适当的代码。

函数Z = predict(layer, X)% Z = predict(层,X)转发输入数据X通过%层,并输出结果Z。如果isdlarray(X) Z = max(X,0) + layer。α。*分钟(0,X);其他的Z = max(X,0) + bsxfun(@times, layer。α,最小值(0,X));结束结束

因为预测功能完全支持dlarray对象,定义落后的函数是可选的。获取支持的函数的列表dlarray对象,看到dlarray支持的函数列表

完成一层

查看完成的层类文件。

classdefcodegenPreluLayer < nnet.layer.Layer示例自定义PReLU层,支持代码生成。% # codegen属性(可学的)%层可学习参数%比例系数α结束方法函数图层= codegenPreluLayer(numChannels, name)% layer = codegenprelu ayer(numChannels, name)创建一个PReLU%层用于二维图像输入与numChannels通道和指定%层名。设置图层名称。层。的名字=的名字;设置层描述。层。描述=“PReLU与“+ numChannels +“通道”初始化缩放系数。层。α=rand([1 1 numChannels]);结束函数Z = predict(layer, X)% Z = predict(层,X)转发输入数据X通过%层,并输出结果Z。如果isdlarray(X) Z = max(X,0) + layer。α。*分钟(0,X);其他的Z = max(X,0) + bsxfun(@times, layer。α,最小值(0,X));结束结束结束结束

检查自定义层的代码生成兼容性

检查自定义层的代码生成兼容性codegenPreluLayer

自定义图层codegenPreluLayer,作为支持文件附加到这个示例中,它将PReLU操作应用于输入数据。要访问此层,请将此示例作为实时脚本打开。

创建该层的实例并使用checkLayer.指定有效的输入大小为对层的典型输入的单个观察的大小。该层期望4-D数组输入,其中前三个维度对应于前一层输出的高度、宽度和通道数量,第四个维度对应于观测结果。

指定观测值输入的典型大小并设置“ObservationDimension”选项4。要检查代码生成兼容性,请设置CheckCodegenCompatibility选项真正的.的checkLayer函数不会检查与代码生成不兼容的函数。要检查代码生成是否支持自定义层定义,首先使用代码生成准备更多信息,请参见使用代码生成准备工具检查代码(MATLAB编码器)

层= codegenPreluLayer (20,“prelu”);validInputSize = [24 24 20];checkLayer(层、validInputSize ObservationDimension = 4, CheckCodegenCompatibility = true)
跳过GPU测试。没有找到兼容的GPU设备。运行nnet.checklayer.TestLayerWithoutBackward  .......... .......... ...完成nnet.checklayer.TestLayerWithoutBackward __________测试总结:23通过,0失败,0不完整,5跳过。时间:0.83484秒。

该函数不会检测到该层的任何问题。

参考文献

[1]“深入研究整流器:在ImageNet分类上超越人类水平的性能。”2015年IEEE计算机视觉国际会议(ICCV)1026 - 34。圣地亚哥,智利:IEEE, 2015。https://doi.org/10.1109/ICCV.2015.123。

另请参阅

|||||||||

相关的话题

Baidu
map