Main Content

本页面提供的是上一版软件的文档。当前版本中已删除对应的英文页面。

定义使用 Tversky 损失的自定义像素分类层

此示例说明如何定义和创建使用 Tversky 损失的自定义像素分类层。

此层可用于训练语义分割网络。要了解有关创建自定义深度学习层的详细信息,请参阅定义自定义深度学习层

Tversky 损失

Tversky 损失基于 Tversky 指数,用于测量两个分割图像之间的重叠 [1]。一个图像 Y 较其对应真实值 T 之间的 Tversky 指数 TIc 由下式给出

TIc=m=1MYcmTcmm=1MYcmTcm+αm=1MYcmTcm+βm=1MYcmTcm

  • c 对应于类,c 对应于不在 c 类中。

  • M 是沿 Y 的前两个维度的元素数目。

  • αβ 是控制每个类的假正和假负对损失的贡献的加权因子。

基于类数目 C 的损失 L 由下式给出

L=c=1C1-TIc

分类层模板

在 MATLAB® 中将分类层模板复制到一个新文件中。此模板提供分类层的大致结构,并包括用于定义层行为的函数。示例的后续部分将说明如何完成 tverskyPixelClassificationLayer

classdef tverskyPixelClassificationLayer < nnet.layer.ClassificationLayer

   properties
      % Optional properties
   end

   methods

        function loss = forwardLoss(layer, Y, T)
            % Layer forward loss function goes here
        end
        
    end
end

声明层属性

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

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

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

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

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

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

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

Tversky 损失必须具有一个小常量值以防止除以零错误。指定属性 Epsilon 以保留此值。它还需要两个可变属性:AlphaBeta,分别控制假正和假负的加权。

classdef tverskyPixelClassificationLayer < nnet.layer.ClassificationLayer

    properties(Constant)
       % Small constant to prevent division by zero. 
       Epsilon = 1e-8;
    end

    properties
       % Default weighting coefficients for false positives and false negatives 
       Alpha = 0.5;
       Beta = 0.5;  
    end

    ...
end

创建构造函数

创建用于构造层并初始化层属性的函数。将创建层所必需的所有变量指定为构造函数的输入。

指定可选的输入参数 name,以便在创建层时赋给 Name 属性。

function layer = tverskyPixelClassificationLayer(name, alpha, beta)
    % layer =  tverskyPixelClassificationLayer(name) creates a Tversky
    % pixel classification layer with the specified name.
           
    % Set layer name          
    layer.Name = name;

    % Set layer properties
    layer.Alpha = alpha;
    layer.Beta = beta;

    % Set layer description
    layer.Description = 'Tversky loss';
end

创建前向损失函数

创建名为 forwardLoss 的函数,该函数返回网络做出的预测和训练目标之间的加权交叉熵损失。forwardLoss 的语法是 loss = forwardLoss(layer,Y,T),其中 Y 是前一个层的输出,T 表示训练目标。

对于语义分割问题,T 的维度与 Y 的维度相匹配,其中 Y 是 4 维数组,大小为 H×W×K×N,其中 K 是类的数量,N 是小批量大小。

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

function loss = forwardLoss(layer, Y, T)
    % loss = forwardLoss(layer, Y, T) returns the Tversky loss between
    % the predictions Y and the training targets T.

    Pcnot = 1-Y;
    Gcnot = 1-T;
    TP = sum(sum(Y.*T,1),2);
    FP = sum(sum(Y.*Gcnot,1),2);
    FN = sum(sum(Pcnot.*T,1),2);

    numer = TP + layer.Epsilon;
    denom = TP + layer.Alpha*FP + layer.Beta*FN + layer.Epsilon;
    
    % Compute Tversky index
    lossTIc = 1 - numer./denom;
    lossTI = sum(lossTIc,3);
    
    % Return average Tversky index loss
    N = size(Y,4);
    loss = sum(lossTI)/N;

end

后向损失函数

由于 forwardLoss 函数完全支持自动微分,因此无需为后向损失创建函数。

有关支持自动微分的函数列表,请参阅List of Functions with dlarray Support

完成的层

tverskyPixelClassificationLayer.m 中提供了完成的层,它作为支持文件包含在示例中。

classdef tverskyPixelClassificationLayer < nnet.layer.ClassificationLayer
    % This layer implements the Tversky loss function for training
    % semantic segmentation networks.
    
    % References
    % Salehi, Seyed Sadegh Mohseni, Deniz Erdogmus, and Ali Gholipour.
    % "Tversky loss function for image segmentation using 3D fully
    % convolutional deep networks." International Workshop on Machine
    % Learning in Medical Imaging. Springer, Cham, 2017.
    % ----------
    
    
    properties(Constant)
        % Small constant to prevent division by zero.
        Epsilon = 1e-8;
    end
    
    properties
        % Default weighting coefficients for False Positives and False
        % Negatives
        Alpha = 0.5;
        Beta = 0.5;
    end

    
    methods
        
        function layer = tverskyPixelClassificationLayer(name, alpha, beta)
            % layer =  tverskyPixelClassificationLayer(name, alpha, beta) creates a Tversky
            % pixel classification layer with the specified name and properties alpha and beta.
            
            % Set layer name.          
            layer.Name = name;
            
            layer.Alpha = alpha;
            layer.Beta = beta;
            
            % Set layer description.
            layer.Description = 'Tversky loss';
        end
        
        
        function loss = forwardLoss(layer, Y, T)
            % loss = forwardLoss(layer, Y, T) returns the Tversky loss between
            % the predictions Y and the training targets T.   

            Pcnot = 1-Y;
            Gcnot = 1-T;
            TP = sum(sum(Y.*T,1),2);
            FP = sum(sum(Y.*Gcnot,1),2);
            FN = sum(sum(Pcnot.*T,1),2); 
            
            numer = TP + layer.Epsilon;
            denom = TP + layer.Alpha*FP + layer.Beta*FN + layer.Epsilon;
            
            % Compute tversky index
            lossTIc = 1 - numer./denom;
            lossTI = sum(lossTIc,3);
            
            % Return average tversky index loss.
            N = size(Y,4);
            loss = sum(lossTI)/N;
            
        end     
    end
end

GPU 兼容性

tverskyPixelClassificationLayer 中的 forwardLoss 使用的 MATLAB 函数都支持 gpuArray 输入,因此该层与 GPU 兼容。

检查输出层有效性

创建层的一个实例。

layer = tverskyPixelClassificationLayer('tversky',0.7,0.3);

使用 checkLayer 检查层的有效性。将有效输入大小指定为层的典型输入的单个观测值的大小。该层需要一个 H×W×K×N 数组输入,其中 K 是类的数量,N 是小批量中的观测值数目。

numClasses = 2;
validInputSize = [4 4 numClasses];
checkLayer(layer,validInputSize, 'ObservationDimension',4)
Skipping GPU tests. No compatible GPU device found.
 
Skipping code generation compatibility tests. To check validity of the layer for code generation, specify the CheckCodegenCompatibility and ObservationDimension options.
 
Running nnet.checklayer.TestOutputLayerWithoutBackward
........
Done nnet.checklayer.TestOutputLayerWithoutBackward
__________

Test Summary:
	 8 Passed, 0 Failed, 0 Incomplete, 2 Skipped.
	 Time elapsed: 1.8244 seconds.

测试摘要报告通过、失败、不完整和跳过的测试数量。

在语义分割网络中使用自定义层

创建一个使用 tverskyPixelClassificationLayer 的语义分割网络。

layers = [
    imageInputLayer([32 32 1])
    convolution2dLayer(3,64,'Padding',1)
    batchNormalizationLayer
    reluLayer
    maxPooling2dLayer(2,'Stride',2)
    convolution2dLayer(3,64,'Padding',1)
    reluLayer
    transposedConv2dLayer(4,64,'Stride',2,'Cropping',1)
    convolution2dLayer(1,2)
    softmaxLayer
    tverskyPixelClassificationLayer('tversky',0.3,0.7)];

使用 imageDatastorepixelLabelDatastore 加载用于语义分割的训练数据。

dataSetDir = fullfile(toolboxdir('vision'),'visiondata','triangleImages');
imageDir = fullfile(dataSetDir,'trainingImages');
labelDir = fullfile(dataSetDir,'trainingLabels');

imds = imageDatastore(imageDir);

classNames = ["triangle" "background"];
labelIDs = [255 0];
pxds = pixelLabelDatastore(labelDir, classNames, labelIDs);

使用数据存储 combine 关联图像和像素标签数据。

ds = combine(imds,pxds);

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

options = trainingOptions('adam', ...
    'InitialLearnRate',1e-3, ...
    'MaxEpochs',100, ...
    'LearnRateDropFactor',5e-1, ...
    'LearnRateDropPeriod',20, ...
    'LearnRateSchedule','piecewise', ...
    'MiniBatchSize',50);

net = trainNetwork(ds,layers,options);
Training on single CPU.
Initializing input data normalization.
|========================================================================================|
|  Epoch  |  Iteration  |  Time Elapsed  |  Mini-batch  |  Mini-batch  |  Base Learning  |
|         |             |   (hh:mm:ss)   |   Accuracy   |     Loss     |      Rate       |
|========================================================================================|
|       1 |           1 |       00:00:02 |       50.32% |       1.2933 |          0.0010 |
|      13 |          50 |       00:00:28 |       98.83% |       0.0990 |          0.0010 |
|      25 |         100 |       00:00:50 |       99.32% |       0.0550 |          0.0005 |
|      38 |         150 |       00:01:11 |       99.38% |       0.0461 |          0.0005 |
|      50 |         200 |       00:01:33 |       99.48% |       0.0397 |          0.0003 |
|      63 |         250 |       00:01:54 |       99.47% |       0.0380 |          0.0001 |
|      75 |         300 |       00:02:14 |       99.54% |       0.0344 |          0.0001 |
|      88 |         350 |       00:02:35 |       99.51% |       0.0352 |      6.2500e-05 |
|     100 |         400 |       00:02:53 |       99.56% |       0.0330 |      6.2500e-05 |
|========================================================================================|
Training finished: Max epochs completed.

通过分割测试图像并显示分割结果来评估训练的网络。

I = imread('triangleTest.jpg');
[C,scores] = semanticseg(I,net);

B = labeloverlay(I,C);
montage({I,B})

Figure contains an axes object. The axes object contains an object of type image.

参考资料

[1] Salehi, Seyed Sadegh Mohseni, Deniz Erdogmus, and Ali Gholipour."Tversky loss function for image segmentation using 3D fully convolutional deep networks."International Workshop on Machine Learning in Medical Imaging.Springer, Cham, 2017.

另请参阅

| | | (Computer Vision Toolbox) | (Computer Vision Toolbox)

相关主题