[セットトップ]論文読書ノート:画像分割方法deeplabおよびHoleアルゴリズム解析


久しぶりにレンガを運んで、ちょうど元旦に休みになって、実験を走る同時に、自分でレンガを運ぶ欲望を満たします^^.
オリジナルを尊重して、転載して明記してください:http://blog.csdn.net/tangwei2014
deeplabはICLR 2015に発表された.論文ダウンロードアドレス:Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFS.
  • deeplab法概要deeplab法は2つのステップに分けられ、第1のステップはFCNを用いてcoarse score mapを得、元の画像サイズに補間し、第2のステップはfully connected CRFを用いてFCNから得られた分割結果を詳細にrefineする.(FCNに関する内容の紹介は、私の前のブログを参考にしてください.http://blog.csdn.net/tangwei2014/article/details/46882257以下の図は全体の構造を明確に示している:そしてこの図はCRF処理前後の効果対比を示しており、CRFを用いた後、詳細は確かに改善されたことがわかる:[置顶] 论文阅读笔记:图像分割方法deeplab以及Hole算法解析_第1张图片
  • deeplabがFCNに対してより優雅な処理を行う第1ステップでは、deeplabは依然としてFCNを用いてscore mapを得、VGGネットワーク上でfine−tuningを行う.しかし、score mapを得る処理方式では、元のFCN処理よりも優雅である.CVPR 2015のFCNでどうやってもっとdenseのscore mapを手に入れたか覚えていますか?500 x 500の入力画像で、最初のボリューム層に直接conv 1_1は100の大きいpaddingが来ました.最終的にfc 7層で無理に16 x 16のscore mapを得た.処理はやや粗いが、CNNに画像を分割してend-to-endにしたのは初めてで、当時performanceはstate-of-the-artだったのも理解できる.deeplabはこのようなやり方を捨てて、代わりにVGGのネットワーク構造に小さな変更をしました:VGGネットワークのpool 4とpool 5層のstrideを元の2から1に変更しました.このような変更により、vggネットワーク全体のstrideが元の32から8になり、さらに入力画像が514 x 514で、正常なpaddingのとき、fc 7は67 x 67のscore mapを得ることができ、FCNよりも確かにdenseが多い.しかし、このようなネットワークの結果を変えるやり方も問題をもたらした:strideが変わった後、vggモデルを利用してfine tuningを続けたいならば、後のfilterの役割の領域が変わって、言い換えれば野の変化を感じることになる.この問題は下図(a)(b)にかっこで示す:
  • Holeアルゴリズムそこで、著者は2つの少し矛盾しているように見える問題を解決する方法を考え出した:すでに訓練したモデルを利用してfine-tuningを行いたいと思って、またネットワーク構造を変えてもっとdenseのscore mapを得たいと思っています.この解決策はHoleアルゴリズムを採用することである.以下の図(a)(b)に示すように、従来のボリュームまたはpoolingでは、1つのfilterにおける隣接する重みがfeature mapに作用する位置はいずれも物理的に連続している.次の図(c)に示すように、感受野が変化しないように、ある層のstrideが2から1に変化した後、後の層はholeアルゴリズムを採用する必要があり、具体的には、連続的な接続関係をhole sizeサイズに応じてskip接続に変更する(図(c)表示の便宜上本層に直接描画した).(c)のpadding 2に驚かないでください.実は2つのpaddingは同時に1つのfilterと接続されません.pool 4のstrideが2から1になると、続くconv 5_1, conv5_2とconv 5_3のhole sizeは2です.続いてpool 5が2から1になると、後のfc 6ではhole sizeが4になる.[置顶] 论文阅读笔记:图像分割方法deeplab以及Hole算法解析_第2张图片
  • コード
  • 主にim 2 col(前伝)とcol 2 im(逆伝)で変更(hole_w,hole_hを追加)されていますが、ここではcpuのみを貼って理解します.
    //forward
    template <typename Dtype>
    void im2col_cpu(const Dtype* data_im, 
        const int num, const int channels, const int height, const int width,
        const int kernel_h, const int kernel_w, const int pad_h, const int pad_w,
        const int stride_h, const int stride_w, const int hole_h, const int hole_w,
        Dtype* data_col) {
      // effective kernel if we expand the holes (trous)
      const int kernel_h_eff = kernel_h + (kernel_h - 1) * (hole_h - 1);
      const int kernel_w_eff = kernel_w + (kernel_w - 1) * (hole_w - 1);
      int height_col = (height + 2 * pad_h - kernel_h_eff) / stride_h + 1;
      int width_col = (width + 2 * pad_w - kernel_w_eff) / stride_w + 1;
      int channels_col = channels * kernel_h * kernel_w;
      for (int n = 0; n < num; ++n) {
        for (int c = 0; c < channels_col; ++c) {
          int w_offset = (c % kernel_w)  * hole_w;
          int h_offset = ((c / kernel_w) % kernel_h) * hole_h;
          int c_im = c / kernel_w / kernel_h;
          for (int h = 0; h < height_col; ++h) {
            const int h_im = h * stride_h + h_offset - pad_h;
            for (int w = 0; w < width_col; ++w) {
              const int w_im = w * stride_w + w_offset - pad_w;
              data_col[((n * channels_col + c) * height_col + h) * width_col + w] =
                (h_im >= 0 && h_im < height && w_im >= 0 && w_im < width) ?
                data_im[((n * channels + c_im) * height + h_im) * width + w_im] : 
                0.; // zero-pad
            } //width_col
          } //height_col
        } //channels_col
      } //num
    }
    
    //backward
    template <typename Dtype>
    void col2im_cpu(const Dtype* data_col,
        const int num, const int channels, const int height, const int width,
        const int kernel_h, const int kernel_w, const int pad_h, const int pad_w,
        const int stride_h, const int stride_w, const int hole_h, const int hole_w,
        Dtype* data_im) {
      caffe_set(num * channels * height * width, Dtype(0), data_im);
      const int kernel_h_eff = kernel_h + (kernel_h - 1) * (hole_h - 1);
      const int kernel_w_eff = kernel_w + (kernel_w - 1) * (hole_w - 1);
      int height_col = (height + 2 * pad_h - kernel_h_eff) / stride_h + 1;
      int width_col = (width + 2 * pad_w - kernel_w_eff) / stride_w + 1;
      int channels_col = channels * kernel_h * kernel_w;
      for (int n = 0; n < num; ++n) {
        for (int c = 0; c < channels_col; ++c) {
          int w_offset = (c % kernel_w)  * hole_w;
          int h_offset = ((c / kernel_w) % kernel_h) * hole_h;
          int c_im = c / kernel_w / kernel_h;
          for (int h = 0; h < height_col; ++h) {
        const int h_im = h * stride_h + h_offset - pad_h;
            for (int w = 0; w < width_col; ++w) {
              const int w_im = w * stride_w + w_offset - pad_w;
              if (h_im >= 0 && h_im < height && w_im >= 0 && w_im < width) {
                data_im[((n * channels + c_im) * height + h_im) * width + w_im] += 
                  data_col[((n * channels_col + c) * height_col + h) * width_col + w];
              }
            }
          }
        }
      }
    }