blob: ebede0547f286261018e8287a17af2161ca2bf71 [file]
#-------------------------------------------------------------
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#-------------------------------------------------------------
/*
* Implementation of a MBConv Layer (Inverted ResNet Layer)
*
* 1.) Expansion Phase (1x1 Convolution) & BN
* 2.) Depthwise Convolution
* 3.) BatchNorm
* 4.) SILU Activation
* 5.) Global Avg Pooling
* 5.) Squeeze and Excitation phase
* 7.) Output Phase (1x1 Convolution)
* 8.) BatchNorm
* 9.) Optional Skip Add layer
*/
source("nn/layers/batch_norm2d.dml") as batchnorm
source("nn/layers/conv2d_builtin.dml") as conv2d
source("nn/layers/conv2d_depthwise.dml") as depthwise
source("nn/layers/global_avg_pool2d.dml") as global_avg_pool
source("nn/layers/silu.dml") as silu
source("nn/layers/upsample2d.dml") as upsample
forward = function(matrix[double] X, list[unknown] model, int Fin, int Fout, int Hin,
int Win, int filter_width, int filter_height, int strideh, int stridew, int padh, int padw,
boolean SkipConnection, int ExpansionFactor, string BNMode, double squeeze_factor)
return (matrix[double] layer_out, list[unknown] intermediate_outputs, list[unknown]
batchnorm_updates, int Hout, int Wout)
{
/*
* Computes the backward pass for a MBConv layer.
*
* Inputs:
* - X: Previous input data matrix, of shape (N, Fin * Hin * Win).
* - model: list of all 22 matrices needed for a complete mbconv layer
* - Fin: Number of filters incoming to the MBConv Block.
* - Fout: Number of filters this MBconv Block produces.
* - Hin: Input height.
* - Win: Input width.
* - filter_width: Width of the depthwise convolution filter
* - filter_height: Height of the depthwise convolution filter
* - strideh: stride of the depthwise convolution in height
* - stridew: stride of the depthwise convolution in width
* - padh: padding of the depthwise convolution in height
* - padw: padding of the depthwise convolution in width
* - SkipConnection: Whether the skip connection was used or not. For this to work the Filters Fin and Fout, and
* the outputs dimension and Hin and Win must be the same.
* - ExpansionFactor: Factor of expansion of the initial Filters coming into this block
* - BNMode: BatchNorm mode used must be either "train" or "test"
* - Squeeze_factor: Factor for the squeeze and excitation layer. This factor should be between 0 and 1
* Outputs:
* - layer_out: Output of the MBConv Layer
* - intermediate_outputs: list of outputs of intermediate layers generated by the forward pass
* - batchnorm_updates: list of updates of batchnorm layers generated by the forward pass
* - Hout: Height of the output
* - Wout: Width of the output
*/
# Unpack parameterlist
W_expansion = as.matrix(model[1])
b_expansion = as.matrix(model[2])
Gamma_expansion = as.matrix(model[3])
Beta_expansion = as.matrix(model[4])
EmaMean_expansion = as.matrix(model[5])
EmaVar_expansion = as.matrix(model[6])
W_depth = as.matrix(model[7])
b_depth = as.matrix(model[8])
Gamma_depth = as.matrix(model[9])
Beta_depth = as.matrix(model[10])
EmaMean_depth = as.matrix(model[11])
EmaVar_depth = as.matrix(model[12])
W_squeeze = as.matrix(model[13])
b_squeeze = as.matrix(model[14])
W_excite = as.matrix(model[15])
b_excite = as.matrix(model[16])
W_out = as.matrix(model[17])
b_out = as.matrix(model[18])
Gamma_out = as.matrix(model[19])
Beta_out = as.matrix(model[20])
EmaMean_out = as.matrix(model[21])
EmaVar_out = as.matrix(model[22])
# Either produce expanded input or use identity
if (ExpansionFactor > 1) {
filter_expansion = Fin * ExpansionFactor
[out_expansion, dim_h_exp, dim_w_exp] = conv2d::forward(X, W_expansion, b_expansion, Fin, Hin, Win, 1, 1, 1, 1, 0, 0)
[out_bn_expansion, bn_ema_mean_expansion, bn_ema_var_expansion, cache_mean_expansion, cache_var_expansion] = batchnorm::forward(out_expansion, Gamma_expansion, Beta_expansion, filter_expansion, Hin, Win, BNMode, EmaMean_expansion, EmaVar_expansion, 0.9, 1e-5)
depthwise_in = silu::forward(out_bn_expansion)
}
else {
# dummy variables so that indexing remains constant
out_expansion = matrix(0, 0, 0)
out_bn_expansion = matrix(0, 0, 0)
bn_ema_mean_expansion = matrix(0, 0, 0)
bn_ema_var_expansion = matrix(0, 0, 0)
cache_mean_expansion = matrix(0, 0, 0)
cache_var_expansion = matrix(0, 0, 0)
filter_expansion = Fin
depthwise_in = X
}
[depth_out, depth_dim_h, depth_dim_w] = depthwise::forward(depthwise_in, W_depth, b_depth, Hin, Win, 1, filter_height, filter_width, strideh, stridew, padh, padw)
[depth_bn_out, depth_bn_mean, depth_bn_var, depth_cache_mean, depth_cache_var] =
batchnorm::forward(depth_out, Gamma_depth, Beta_depth, filter_expansion, depth_dim_h, depth_dim_w, "train", EmaMean_depth, EmaVar_depth, 0.9, 1e-5)
depth_act_out = silu::forward(depth_bn_out)
# Squeeze and Expansion
squeeze_dim = round(filter_expansion * squeeze_factor)
[pooled_out, pool_h, pool_w] = global_avg_pool::forward(depth_act_out, filter_expansion, depth_dim_h, depth_dim_w)
[squeeze_out, dim_squeeze_h, dim_squeeze_w] = conv2d::forward(pooled_out, W_squeeze, b_squeeze, filter_expansion, pool_h, pool_w, 1, 1, 1, 1, 0, 0)
[expand_out, dim_squeeze_h, dim_squeeze_w] = conv2d::forward(squeeze_out, W_excite, b_excite, squeeze_dim, dim_squeeze_h, dim_squeeze_w, 1, 1, 1, 1, 0, 0)
upscaled_out = upsample::forward(expand_out, filter_expansion, dim_squeeze_h, dim_squeeze_w, depth_dim_h, depth_dim_w)
multiplied_out = depth_act_out * upscaled_out
# Output Layer
[conv_out, conv_dim_h, conv_dim_w] = conv2d::forward(multiplied_out, W_out, b_out, filter_expansion, depth_dim_h, depth_dim_w, 1, 1, 1, 1, 0, 0)
[conv_bn_out, conv_bn_mean, conv_bn_var, conv_cache_mean, conv_cache_var] =
batchnorm::forward(conv_out, Gamma_depth, Beta_depth, filter_expansion, depth_dim_h, depth_dim_w, BNMode, EmaMean_depth, EmaVar_depth, 0.9, 1e-5)
if (SkipConnection)
layer_out = conv_bn_out + X
else
layer_out = conv_out
Hout = conv_dim_h
Wout = conv_dim_w
intermediate_outputs = list(out_expansion, out_bn_expansion, depthwise_in,
depth_out, depth_bn_out, depth_act_out, pooled_out, squeeze_out, expand_out,
upscaled_out, multiplied_out, conv_out, conv_bn_out, layer_out)
batchnorm_updates = list(bn_ema_mean_expansion, bn_ema_var_expansion,
cache_mean_expansion, cache_var_expansion, depth_bn_mean, depth_bn_var, depth_cache_mean,
depth_cache_var, conv_bn_mean, conv_bn_var, conv_cache_mean, conv_cache_var)
}
backward = function(matrix[double] dout, matrix[double] X, list[unknown] model, list[unknown] intermediate_outputs,
list[unknown] batchnorm_updates, int Fin, int Fout, int Hin, int Win, int filter_width, int filter_height, int strideh,
int stridew, int padh, int padw, boolean SkipConnection, int ExpansionFactor, string BNMode, double squeeze_factor)
return (matrix[double] dX, list[unknown] gradients)
{
/*
* Computes the backward pass for a MBConv layer.
*
* Inputs:
* - dout: Gradient wrt `out` from upstream, of same shape as `X`.
* - X: Previous input data matrix, of shape (N, Fin * Hin * Win).
* - model: list of all 22 matrices needed for a complete mbconv layer
* - intermediate_outputs: list of outputs of intermediate layers generated by the forward pass
* - batchnorm_updates: list of updates of batchnorm layers generated by the forward pass
* - Fin: Number of filters incoming to the MBConv Block.
* - Fout: Number of filters this MBconv Block produces.
* - Hin: Input height.
* - Win: Input width.
* - filter_width: Width of the depthwise convolution filter
* - filter_height: Height of the depthwise convolution filter
* - strideh: stride of the depthwise convolution in height
* - stridew: stride of the depthwise convolution in width
* - padh: padding of the depthwise convolution in height
* - padw: padding of the depthwise convolution in width
* - SkipConnection: Whether the skip connection was used or not. For this to work the Filters Fin and Fout, and
* the outputs dimension and Hin and Win must be the same.
* - ExpansionFactor: Factor of expansion of the initial Filters coming into this block
* - BNMode: BatchNorm mode used must be either "train" or "test"
* - Squeeze_factor: Factor for the squeeze and excitation layer. This factor should be between 0 and 1
* Outputs:
* - dX: Gradient wrt `X`, of same shape as `X`.
* - gradients: list containing all the gradients for the parameter updates.
*/
# Unpack parameterlist
W_expansion = as.matrix(model[1])
b_expansion = as.matrix(model[2])
Gamma_expansion = as.matrix(model[3])
Beta_expansion = as.matrix(model[4])
EmaMean_expansion = as.matrix(model[5])
EmaVar_expansion = as.matrix(model[6])
W_depth = as.matrix(model[7])
b_depth = as.matrix(model[8])
Gamma_depth = as.matrix(model[9])
Beta_depth = as.matrix(model[10])
EmaMean_depth = as.matrix(model[11])
EmaVar_depth = as.matrix(model[12])
W_squeeze = as.matrix(model[13])
b_squeeze = as.matrix(model[14])
W_excite = as.matrix(model[15])
b_excite = as.matrix(model[16])
W_out = as.matrix(model[17])
b_out = as.matrix(model[18])
Gamma_out = as.matrix(model[19])
Beta_out = as.matrix(model[20])
EmaMean_out = as.matrix(model[21])
EmaVar_out = as.matrix(model[22])
# Unpack BN caches
cache_mean_expansion = as.matrix(batchnorm_updates[3])
cache_var_expansion = as.matrix(batchnorm_updates[4])
depth_cache_mean = as.matrix(batchnorm_updates[7])
depth_cache_var = as.matrix(batchnorm_updates[8])
conv_cache_mean = as.matrix(batchnorm_updates[11])
conv_cache_var = as.matrix(batchnorm_updates[12])
# Unpack Intermediate Outputs
out_expansion = as.matrix(intermediate_outputs[1])
out_bn_expansion = as.matrix(intermediate_outputs[2])
depthwise_in = as.matrix(intermediate_outputs[3])
depth_out = as.matrix(intermediate_outputs[4])
depth_bn_out = as.matrix(intermediate_outputs[5])
depth_act_out = as.matrix(intermediate_outputs[6])
pooled_out = as.matrix(intermediate_outputs[7])
squeeze_out = as.matrix(intermediate_outputs[8])
expand_out = as.matrix(intermediate_outputs[9])
upscaled_out = as.matrix(intermediate_outputs[10])
multiplied_out = as.matrix(intermediate_outputs[11])
conv_out = as.matrix(intermediate_outputs[12])
conv_bn_out = as.matrix(intermediate_outputs[13])
# Calculate Dimension of filters
if (ExpansionFactor > 1)
expansion_dim = Fin * ExpansionFactor
else
expansion_dim = Fin
squeeze_dim = round(expansion_dim * squeeze_factor)
depth_dim_h = as.integer(floor((Hin + 2*padh - filter_height)/strideh + 1))
depth_dim_w = as.integer(floor((Win + 2*padw - filter_width)/stridew + 1))
# Start Backpropagation
[dback_bn_out, dGamma_out, dBeta_out] = batchnorm::backward(dout, conv_cache_mean, conv_cache_var, conv_out, Gamma_out, Fout, depth_dim_h, depth_dim_w, 1e-5)
[dconv_out, dConv_w, dConv_b] = conv2d::backward(dback_bn_out, depth_dim_h, depth_dim_w, multiplied_out, W_out, b_out, expansion_dim, depth_dim_h, depth_dim_w, 1, 1, 1, 1, 0, 0)
# multiply backward part1
dsqueeze_back = dconv_out * depth_act_out
dupsample_back = upsample::backward(dsqueeze_back, expansion_dim, 1, 1, depth_dim_h, depth_dim_w)
[dexcite_back, dW_excite, db_excite] = conv2d::backward(dupsample_back, 1, 1, squeeze_out, W_excite, b_excite, squeeze_dim, 1, 1, 1, 1, 1, 1, 0, 0)
[dsqueeze_back, dW_squeeze, db_squeeze] = conv2d::backward(dexcite_back, 1, 1, pooled_out, W_squeeze, b_squeeze, expansion_dim, 1, 1, 1, 1, 1, 1, 0, 0)
dpool_back = global_avg_pool::backward(dsqueeze_back, depth_act_out, expansion_dim, depth_dim_h, depth_dim_w)
#multiply part 2
dmult = dconv_out * upscaled_out
dsilu_back = dmult + dpool_back
# Act-BN-CONV
dsilu_back2 = silu::backward(dsilu_back, depth_bn_out)
[dback_bn_depth, dGamma_depth, dBeta_depth] = batchnorm::backward(dsilu_back2, depth_cache_mean, depth_cache_var, depth_out, Gamma_depth, expansion_dim, depth_dim_h, depth_dim_w, 1e-5)
[dconv_depth_out, dW_depth, db_depth] = depthwise::backward(dback_bn_depth, depth_dim_h, depth_dim_w, depthwise_in, W_depth, b_depth, Hin, Win, 1, filter_height, filter_width, strideh, stridew, padh, padw)
if (ExpansionFactor > 1) {
dsilu_back3 = silu::backward(dconv_depth_out, out_bn_expansion)
[dback_bn_expansion, dGamma_expansion, dBeta_expansion] = batchnorm::backward(dsilu_back3, cache_mean_expansion, cache_var_expansion, out_expansion, Gamma_expansion, expansion_dim, Hin, Win, 1e-5)
[dconv_expansion, dW_expansion, db_expansion] = conv2d::backward(dback_bn_expansion, Hin, Win, X, W_expansion, b_expansion, Fin, Hin, Win, 1, 1, 1, 1, 0, 0)
dX = dconv_expansion
}
else {
dX = dconv_depth_out
}
if (SkipConnection)
dX = dX + dout
if (ExpansionFactor > 1) {
gradients = list(dGamma_out, dBeta_out, dConv_w, dConv_b, dW_excite,
db_excite, dW_squeeze, db_squeeze, dGamma_depth, dBeta_depth, dW_depth,
db_depth, dGamma_expansion, dBeta_expansion, dW_expansion, db_expansion)
}
else {
gradients = list(dGamma_out, dBeta_out, dConv_w, dConv_b, dW_excite, db_excite,
dW_squeeze, db_squeeze, dGamma_depth, dBeta_depth, dW_depth, db_depth)
}
}
init = function(int Fin, int Fout, int filter_width, int filter_height,
int ExpansionFactor, double SqueezeFactor, int seed = -1)
return (list[unknown] mbconv_params)
{
/*
* Initialize the parameters of this MBConv layer.
*
* Note: This is just a convenience function, and parameters
* may be initialized manually if needed.
*
* Inputs:
* - Fin: Number of filters incoming to the MBConv Block.
* - Fout: Number of filters this MBconv Block produces.
* - filter_width: Width of the depthwise convolution filter
* - filter_height: Height of the depthwise convolution filter
* - ExpansionFactor: Factor of expansion of the initial Filters coming into this block
* - Squeeze_factor: Factor for the squeeze and excitation layer. This factor should be between 0 and 1
* - seed: The seed to initialize the weights
*
* Outputs:
* - mbconv_params: list of all 22 matrices needed for a complete mbconv layer
*/
# Expansion
if (ExpansionFactor > 1) {
expansion_dim = Fin * ExpansionFactor
[W_expansion, b_expansion] = conv2d::init(expansion_dim, Fin, 1, 1, seed)
[Gamma_expansion, Beta_expansion, EmaMean_expansion, EmaVar_expansion] = batchnorm::init(expansion_dim)
}
else {
# Dummy variables so that the model list indices remain the same
W_expansion = matrix(0, 0, 0)
b_expansion = matrix(0, 0, 0)
Gamma_expansion = matrix(0, 0, 0)
Beta_expansion = matrix(0, 0, 0)
EmaMean_expansion = matrix(0, 0, 0)
EmaVar_expansion = matrix(0, 0, 0)
expansion_dim = Fin
}
[W_depth, b_depth] = depthwise::init(expansion_dim, 1, filter_width, filter_height)
[Gamma_depth, Beta_depth, EmaMean_depth, EmaVar_depth] = batchnorm::init(expansion_dim)
squeeze_dim = round(expansion_dim * SqueezeFactor)
[W_squeeze, b_squeeze] = conv2d::init(squeeze_dim, expansion_dim, 1, 1, seed)
[W_excite, b_excite] = conv2d::init(expansion_dim, squeeze_dim, 1, 1, seed)
[W_out, b_out] = conv2d::init(Fout, expansion_dim, 1, 1, seed)
[Gamma_out, Beta_out, EmaMean_out, EmaVar_out] = batchnorm::init(Fout)
mbconv_params = list(W_expansion, b_expansion, Gamma_expansion,
Beta_expansion, EmaMean_expansion, EmaVar_expansion, W_depth, b_depth,
Gamma_depth, Beta_depth, EmaMean_depth, EmaVar_depth, W_squeeze, b_squeeze,
W_excite, b_excite, W_out, b_out, Gamma_out, Beta_out, EmaMean_out, EmaVar_out)
}