|
| 1 | +function net = buildImageFICCNN(inputSize, outputSize, filterSize, numFilters, options) |
| 2 | +% BUILDIMAGEFICCNN Construct a fully-input convex convolutional neural |
| 3 | +% network for image inputs. |
| 4 | +% |
| 5 | +% NET = BUILDIMAGEFICCNN(INPUTSIZE, OUTPUTSIZE, FILTERSIZE, NUMFILTERS) |
| 6 | +% creates a fully-input convex dlnetwork object, NET. |
| 7 | +% |
| 8 | +% INPUTSIZE is a row vector of integers [h w c], where h, w, and c |
| 9 | +% correspond ot the height, width and number of channels respectively. |
| 10 | +% |
| 11 | +% OUTPUTSIZE is an intenger indicating the number of neurons in the |
| 12 | +% output fully connected layer. |
| 13 | +% |
| 14 | +% FILTERSIZE is matrix with two columns specifying the height and width |
| 15 | +% for each convolutional layer. The network will have as many |
| 16 | +% convolutional layers as there are rows in FILTERSIZE. If FILTERSIZE is |
| 17 | +% provided as a column vector, it is assumed that the filters are square. |
| 18 | +% |
| 19 | +% NUMFILTERES is a column vector of integers that specifies the number of |
| 20 | +% filters for each convolutional layers. It must have the same number of |
| 21 | +% rows as FILTERSIZE. |
| 22 | +% |
| 23 | +% NET = BUILDIMAGEFICCNN(__, NAME=VALUE) specifies additional options |
| 24 | +% using one or more name-value arguments. |
| 25 | +% |
| 26 | +% Stride - Stride for each convolutional |
| 27 | +% layer, specified as a two-column |
| 28 | +% matrix where the first column is |
| 29 | +% the stride height, and the second |
| 30 | +% column is the stride width. If |
| 31 | +% Stride is specified as a column |
| 32 | +% vector, a square stride is assumed. |
| 33 | +% The default value is 1 for all |
| 34 | +% layers. |
| 35 | +% |
| 36 | +% DilationFactor - Dilation factor for each |
| 37 | +% convolutional layer, specified as a |
| 38 | +% two-column matrix where the frist |
| 39 | +% column is the stride height and the |
| 40 | +% second column is the stride width. |
| 41 | +% If DilationFactor is a column |
| 42 | +% vector, a square dilation factor is |
| 43 | +% assumed. The default value is 1 for |
| 44 | +% all layers. |
| 45 | +% |
| 46 | +% Padding - Padding method for each |
| 47 | +% convolutional layer specified as |
| 48 | +% "same" or "causal". Padding must be |
| 49 | +% a string array with the same number |
| 50 | +% of rows as FITLERSIZE. The default |
| 51 | +% value is "causal" for all layers. |
| 52 | +% |
| 53 | +% PaddingValue - Padding for each convolutional |
| 54 | +% layer, specified as a column vector |
| 55 | +% with the same number of rows as |
| 56 | +% FILTERSIZE. The default value is 0 |
| 57 | +% for all layers. |
| 58 | +% |
| 59 | +% ConvexNonDecreasingActivation - Convex non-decreasing activation |
| 60 | +% function, specified as "softplus" |
| 61 | +% or "relu". The default value is |
| 62 | +% "relu". |
| 63 | + |
| 64 | +% Copyright 2024 The MathWorks, Inc. |
| 65 | + |
| 66 | +arguments |
| 67 | + inputSize (1,:) {mustBeNonempty, mustBeReal, mustBeInteger, mustBePositive, mustBeTwoOrThreeRowVector(inputSize, "inputSize")} |
| 68 | + outputSize (1,1) {mustBeReal, mustBeInteger, mustBePositive} |
| 69 | + filterSize {mustBeNonempty, mustBeReal, mustBeInteger, mustBePositive, mustBeOneOrTwoColumn(filterSize, "filterSize")} |
| 70 | + numFilters (:,1) {mustBeNonempty, mustBeReal, mustBeInteger, mustBePositive, mustBeEqualLength(filterSize, numFilters, "numFilters")} |
| 71 | + options.Stride {mustBeNonempty, mustBeReal, mustBeInteger, mustBePositive, mustBeOneOrTwoColumn(options.Stride, "Stride"), mustBeEqualLength(filterSize, options.Stride, "Stride")} = ones(numel(numFilters), 2) |
| 72 | + options.DilationFactor {mustBeNonempty, mustBeReal, mustBeInteger, mustBePositive, mustBeOneOrTwoColumn(options.DilationFactor, "DilationFactor"), mustBeEqualLength(filterSize, options.DilationFactor, "DilationFactor")} = ones(numel(numFilters), 2) |
| 73 | + options.Padding (:,1) {mustBeNonzeroLengthText, mustBeMember(options.Padding, "same"), mustBeEqualLength(filterSize, options.Padding, "Padding")} = repelem("same", numel(numFilters)); |
| 74 | + options.PaddingValue (:,1) {mustBeNonempty, mustBeReal, mustBeEqualLength(filterSize, options.PaddingValue, "PaddingValue")} = zeros(numel(numFilters), 1); |
| 75 | + options.ConvexNonDecreasingActivation {mustBeNonzeroLengthText, mustBeTextScalar, mustBeMember(options.ConvexNonDecreasingActivation, ["relu", "softplus"])} = "relu" |
| 76 | +end |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +% Select the activation function based on user input |
| 81 | +switch options.ConvexNonDecreasingActivation |
| 82 | + case "relu" |
| 83 | + activationLayer = @(name) reluLayer(Name=name); |
| 84 | + case "softplus" |
| 85 | + activationLayer = @(name) softplusLayer(Name=name); |
| 86 | +end |
| 87 | + |
| 88 | +% Build the input layer |
| 89 | +layers = [ |
| 90 | + imageInputLayer(inputSize, Name="input", Normalization="none") |
| 91 | + ]; |
| 92 | + |
| 93 | +% Build the convolutional layers |
| 94 | +for ii = 1:numel(numFilters) |
| 95 | + convLayerName = "conv2d_+_" + ii; |
| 96 | + activationLayerName = "cnd_" + ii; |
| 97 | + batchNormLayerName = "batchnorm_+_" + ii; |
| 98 | + |
| 99 | + convLayer = convolution2dLayer(filterSize(ii, :), numFilters(ii), ... |
| 100 | + Stride=options.Stride(ii, :), ... |
| 101 | + DilationFactor=options.DilationFactor(ii, :), ... |
| 102 | + Padding=options.Padding(ii), ... |
| 103 | + PaddingValue=options.PaddingValue(ii), ... |
| 104 | + Name=convLayerName); |
| 105 | + |
| 106 | + layers = [ |
| 107 | + layers; |
| 108 | + convLayer; |
| 109 | + activationLayer(activationLayerName); |
| 110 | + batchNormalizationLayer(Name=batchNormLayerName) |
| 111 | + ]; %#ok<AGROW> |
| 112 | +end |
| 113 | + |
| 114 | +% Modify the name of the first convolutional layer to remove constraints |
| 115 | +layers(2).Name = "conv2d_1"; |
| 116 | + |
| 117 | +% Add final pooling and fully connected layers |
| 118 | +layers = [ |
| 119 | + layers; |
| 120 | + globalAveragePooling2dLayer(Name="global_avg_pool"); |
| 121 | + fullyConnectedLayer(outputSize, Name="fc_+_end") |
| 122 | + ]; |
| 123 | + |
| 124 | +% Initialize the dlnetwork |
| 125 | +net = dlnetwork(layers); |
| 126 | + |
| 127 | +% Make the network convex |
| 128 | +net = conslearn.convex.makeNetworkConvex(net); |
| 129 | + |
| 130 | +end |
| 131 | + |
| 132 | +function mustBeTwoOrThreeRowVector(x, name) |
| 133 | +if ~(isrow(x) && (numel(x) == 2 || numel(x) == 3)) |
| 134 | + error("'%s' must be a row vector with two or three elements.", name); |
| 135 | +end |
| 136 | +end |
| 137 | + |
| 138 | +function mustBeOneOrTwoColumn(x, name) |
| 139 | +if ~(size(x, 2) == 1 || size(x, 2) == 2) |
| 140 | + error("'%s' must be an array with one or two columns.", name); |
| 141 | +end |
| 142 | +end |
| 143 | + |
| 144 | +function mustBeEqualLength(filterSize, otherVar, otherVarName) |
| 145 | +if ~isequal(size(filterSize, 1), size(otherVar, 1)) |
| 146 | + error("'%s' must have the same number of rows as the filter size value.", otherVarName); |
| 147 | +end |
| 148 | +end |
0 commit comments