Program Listing for File resnet.hpp

Return to documentation for file (/home/jenkins/docs/models/models/resnet/resnet.hpp)

 author = {Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun},
 title = {Deep Residual Learning for Image Recognition},
 year = {2015},
 url = {https://arxiv.org/pdf/1512.03385.pdf}
}

#ifndef MODELS_MODELS_RESNET_RESNET_HPP
#define MODELS_MODELS_RESNET_RESNET_HPP

#include <mlpack/core.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/ffn.hpp>
#include <mlpack/methods/ann/layer/layer_types.hpp>
#include <mlpack/methods/ann/init_rules/random_init.hpp>
#include <mlpack/methods/ann/loss_functions/binary_cross_entropy_loss.hpp>
#include <mlpack/methods/ann/init_rules/he_init.hpp>

#include "./../../utils/utils.hpp"

namespace mlpack {
namespace models {

template<
  typename OutputLayerType = ann::CrossEntropyError<>,
  typename InitializationRuleType = ann::RandomInitialization,
  size_t ResNetVersion = 18
>
class ResNet{
 public:
  ResNet();

  ResNet(const size_t inputChannel,
         const size_t inputWidth,
         const size_t inputHeight,
         const bool includeTop = true,
         const bool preTrained = false,
         const size_t numClasses = 1000);

  ResNet(std::tuple<size_t, size_t, size_t> inputShape,
         const bool includeTop = true,
         const bool preTrained = false,
         const size_t numClasses = 1000);

  ann::FFN<OutputLayerType, InitializationRuleType>&
      GetModel() { return resNet; }

  //  named "ResNet".
  void LoadModel(const std::string& filePath);

  //  named "ResNet".
  void SaveModel(const std::string& filepath);

 private:
  template<typename SequentialType = ann::Sequential<>>
  void ConvolutionBlock(SequentialType* baseLayer,
                        const size_t inSize,
                        const size_t outSize,
                        const size_t strideWidth = 1,
                        const size_t strideHeight = 1,
                        const size_t kernelWidth = 3,
                        const size_t kernelHeight = 3,
                        const size_t padW = 1,
                        const size_t padH = 1,
                        const bool downSample = false,
                        const size_t downSampleInputWidth = 0,
                        const size_t downSampleInputHeight = 0)
  {
    if (downSample)
    {
      mlpack::Log::Info << "DownSample (" << std::endl;
      inputWidth = downSampleInputWidth;
      inputHeight = downSampleInputHeight;
    }

    ann::Sequential<>* tempBaseLayer = new ann::Sequential<>();
    tempBaseLayer->Add(new ann::Convolution<>(inSize, outSize, kernelWidth,
        kernelHeight, strideWidth, strideHeight, padW, padH, inputWidth,
        inputHeight));
    mlpack::Log::Info << "Convolution: " << "(" << inSize << ", " <<
        inputWidth << ", " << inputHeight << ")" << " ---> (";

    // Updating input dimensions.
    inputWidth = ConvOutSize(inputWidth, kernelWidth, strideWidth, padW);
    inputHeight = ConvOutSize(inputHeight, kernelHeight, strideHeight,
        padH);

    mlpack::Log::Info << outSize << ", " << inputWidth << ", " <<
        inputHeight << ")" << std::endl;

    tempBaseLayer->Add(new ann::BatchNorm<>(outSize, 1e-5));
    mlpack::Log::Info << "BatchNorm: " << "(" << outSize << ")" << " ---> ("
        << outSize << ")" << std::endl;
    baseLayer->Add(tempBaseLayer);
    if (downSample)
      mlpack::Log::Info << ")" <<std::endl;
  }

  void ReLULayer(ann::Sequential<>* baseLayer)
  {
    baseLayer->Add(new ann::ReLULayer<>);
    mlpack::Log::Info << "Relu" << std::endl;
  }

  void BasicBlock(const size_t inSize,
                  const size_t outSize,
                  const size_t strideWidth = 1,
                  const size_t strideHeight = 1,
                  const bool downSample = false)
  {
    downSampleInputWidth = inputWidth;
    downSampleInputHeight = inputHeight;

    ann::Sequential<>* basicBlock = new ann::Sequential<>();
    ann::AddMerge<>* resBlock = new ann::AddMerge<>(true, true);
    ann::Sequential<>* sequentialBlock = new ann::Sequential<>();
    ConvolutionBlock(sequentialBlock, inSize, outSize, strideWidth,
        strideHeight);
    ReLULayer(sequentialBlock);
    ConvolutionBlock(sequentialBlock, outSize, outSize);

    resBlock->Add(sequentialBlock);

    if (downSample == true)
    {
      ConvolutionBlock(resBlock, inSize, outSize, strideWidth, strideHeight,
          1, 1, 0, 0, true, downSampleInputWidth, downSampleInputHeight);
    }
    else
    {
      mlpack::Log::Info << "IdentityLayer" << std::endl;
      resBlock->Add(new ann::IdentityLayer<>);
    }

    basicBlock->Add(resBlock);
    ReLULayer(basicBlock);
    resNet.Add(basicBlock);
  }

  void BottleNeck(const size_t inSize,
                  const size_t outSize,
                  const size_t strideWidth = 1,
                  const size_t strideHeight = 1,
                  const bool downSample = false,
                  const size_t baseWidth = 64,
                  const size_t groups = 1)
  {
    downSampleInputWidth = inputWidth;
    downSampleInputHeight = inputHeight;

    size_t width = int((baseWidth / 64.0) * outSize) * groups;
    ann::Sequential<>* basicBlock = new ann::Sequential<>();
    ann::AddMerge<>* resBlock = new ann::AddMerge<>(true, true);
    ann::Sequential<>* sequentialBlock = new ann::Sequential<>();
    ConvolutionBlock(sequentialBlock, inSize, width, 1, 1, 1, 1, 0, 0);
    ReLULayer(sequentialBlock);
    ConvolutionBlock(sequentialBlock, width, width, strideWidth,
        strideHeight);
    ReLULayer(sequentialBlock);
    ConvolutionBlock(sequentialBlock, width, outSize * bottleNeckExpansion, 1,
        1, 1, 1, 0, 0);
    resBlock->Add(sequentialBlock);

    if (downSample == true)
    {
      ConvolutionBlock(resBlock, inSize, outSize * bottleNeckExpansion,
           strideWidth, strideHeight, 1, 1, 0, 0, true, downSampleInputWidth,
           downSampleInputHeight);
    }
    else
    {
      mlpack::Log::Info << "IdentityLayer" << std::endl;
      resBlock->Add(new ann::IdentityLayer<>);
    }

    basicBlock->Add(resBlock);
    ReLULayer(basicBlock);
    resNet.Add(basicBlock);
  }

  void MakeLayer(const std::string& block,
                 const size_t outSize,
                 const size_t numBlocks,
                 const size_t stride = 1)
  {
    bool downSample = false;

    if (block == "basicblock")
    {
      if (stride != 1 || downSampleInSize != outSize * basicBlockExpansion)
        downSample = true;
      BasicBlock(downSampleInSize, outSize * basicBlockExpansion, stride,
          stride, downSample);
      downSampleInSize = outSize * basicBlockExpansion;
      for (size_t i = 1; i != numBlocks; ++i)
        BasicBlock(downSampleInSize, outSize);
      return;
    }

    if (stride != 1 || downSampleInSize != outSize * bottleNeckExpansion)
      downSample = true;
    BottleNeck(downSampleInSize, outSize, stride, stride, downSample);
    downSampleInSize = outSize * bottleNeckExpansion;
    for (size_t i = 1; i != numBlocks; ++i)
      BottleNeck(downSampleInSize, outSize);
  }

  size_t ConvOutSize(const size_t size,
                     const size_t k,
                     const size_t s,
                     const size_t padding)
  {
    return std::floor((size - k + 2 * padding) / s) + 1;
  }

  ann::FFN<OutputLayerType, InitializationRuleType> resNet;

  size_t inputChannel;

  size_t inputWidth;

  size_t inputHeight;

  size_t numClasses;

  size_t downSampleInputWidth;

  size_t downSampleInputHeight;

  size_t basicBlockExpansion = 1;

  size_t bottleNeckExpansion = 4;

  size_t downSampleInSize = 64;

  std::map<size_t, std::map<std::string, std::array<size_t, 4>>> ResNetConfig =
      {
        {18, {{"basicblock", {2, 2, 2, 2}}}},
        {34, {{"basicblock", {3, 4, 6, 3}}}},
        {50, {{"bottleneck", {3, 4, 6, 3}}}},
        {101, {{"bottleneck", {3, 4, 23, 3}}}},
        {152, {{"bottleneck", {3, 8, 36, 3}}}}
      };

  std::array<size_t , 4> numBlockArray;

  std::string builderBlock;

  std::string preTrainedPath;
}; // ResNet class

// convenience typedefs for different ResNet models.
typedef ResNet<ann::CrossEntropyError<>, ann::RandomInitialization, 18>
    ResNet18;
typedef ResNet<ann::CrossEntropyError<>, ann::RandomInitialization, 34>
    ResNet34;
typedef ResNet<ann::CrossEntropyError<>, ann::RandomInitialization, 50>
    ResNet50;
typedef ResNet<ann::CrossEntropyError<>, ann::RandomInitialization, 101>
    ResNet101;
typedef ResNet<ann::CrossEntropyError<>, ann::RandomInitialization, 152>
    ResNet152;

} // namespace models
} // namespace mlpack

#include "resnet_impl.hpp"

#endif