【令和最新版】画像分野のDeep Learning (Computer Vision) 初心者向け資料

# はじめに
本記事は、2022年3月に修士課程を修了する私が学部4年から3年間で学んできた知識について経験的なイメージ（偏見）を携えて、修論とは別になんとなくまとめてみようとするものです。

本記事は理論メインになります。
実装のプログラミングは多少話題にしてますが、そちらをしっかり学びたい方にはそれほど役に立たないと思います。ご了承ください。

一応、以下のような人をターゲットとして書いています。

- 新たに学び始める人
- ざっくり分野の概要を知りたい人
- 知識のない人向けに講演などする予定があり参考にしたい人
- とにかく何でもいいから読み物がほしい人

現在、入門書籍や入門記事はたくさんありますが、持論・体験・最新の研究についても触れながら書くつもりなので、少しでも良いなと思っていただければと考えています。
数学的な話も少し出ますが、中学・高校数学レベルがわかれば大丈夫です。

誤字脱字・間違った知識の報告や、感想などコメントお待ちしています。

# 目次
1. Computer Visionの様々なタスク
2. Deep Learning(Computer Vision) とは
3. 一般的な学習（教師あり学習）の流れ
4. Optimizer（最適化アルゴリズム)
5. CNNとConvolution（畳み込み）
6. Normalization（正規化）
7. Activation Function（活性化関数）
8. Attention（注意機構）
9. 具体的なネットワーク（ResNet、U-Net、DeepLabv3+、ViT）
10. まとめと雑感のようななにか

# 1. Computer Visionの様々なタスク
Computer Visionとは？ 何ができるの？ というところから始めたいと思います。早速ですが、この節は基礎すぎて少し冗長かもです。目が滑るようなら軽く読み流してください。Computer Visionはコンピュータで画像処理をするという分野です。ILSVRC2012という学会のコンペティションでDeep Learningを用いた手法が圧倒的なスコアで優勝したことをきっかけに、現在ではDeep Learningを用いた手法がスタンダードになっています。タスクとしてはILSVRC2012でも話題になった画像分類を始め、セグメンテーション・画像生成・物体検出など様々なタスクがあります。

有名なDeep Learningを用いたタスクについていくつか紹介します。まず **画像分類** です。これは画像に写っているものが選択肢のなかで何に当たるのかを見分ける最も一般的なタスクです。画像分野の汎用的な手法はまずこのタスクで試されることが多いです。以下に示すように確率値が出力され最も高い確率のものに分類します。企業の守秘義務などあるので詳しくは書けませんが、私はインターンシップで実際の企業でこのタスクのアルゴリズムを制作したことがあります。
![fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/4d517d37-c907-89ef-6608-eac3d6e3a114.png)


次に **セグメンテーション** です。基本となる **セマンティックセグメンテーション** では分類を画像の画素ごとに行うタスクで画像のどこに何があるかを見分けることが出来ます。車載画像に用いて自動運転のアルゴリズムに組み込むのが有名な使用法です。以下の例のように色塗りをするようなイメージのタスクで、細かな分類として別物体ならそれも異なるものとして分類を行う **インスタンスセグメンテーション・パノプティックセグメンテーション** などがあります。私は主にこのタスクを医用画像に用いる研究をしていました。医用研究において従来は不鮮明な顕微鏡画像などから「ここに細胞がある！」など主観的な判断をしていた部分で客観的な評価を行うために精度が低くても、既にDeep Learningによる自動化が現場で使用されています（有名なSTAP細胞の事件とかがあってから医学界では客観指標というのが大事になったとの噂）。
![fig4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/c5b4b27e-67a9-3b19-9365-bdb0021c9071.png)


また、 **画像生成** では画像や乱数を基に新たな画像を生成します。これは写真を取るときのフィルタなどに使用されています。最近は男性の写真を女性にしたり色々できますよね。下にはネコをイヌに変換しているような例を示しますが、ウマをシマウマにする例も有名です。「ウマをシマウマにして何の役に立つんだ！」みたいな文章をどっかで見て笑った記憶があります。単純に「画像処理のDeep Learning凄い！」というのがわかるタスクですが、複雑なデータ・ネットワーク・評価方法などアカデミアの研究で成果を出すには難しいことが多いイメージがあり、この研究に取り組んでいる人はあまり周りにいませんでした。「このデータでやってみました」だけの論文は査読のない国内学会で割と見かけましたが。
![fig6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/7dc46e1f-0921-160d-1a5a-556ed4c47f8a.png)


他に **物体検出** は画像内に指定した物体があれば四角形で領域を指定します。これを連続した画像である動画に行えば物体追跡など異なるタスクになります。スポーツ中継などのカメラワークを自動で行うなどで使用されているらしく、研究室のメンバーでインターンシップに行って実際に企業でこのタスクのアルゴリズム開発をしている人もいました。
![fig5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/1dac19ac-f36d-a192-8069-eaee7ba7e84f.png)


このように様々なタスクがあり、共通してDeep Learningが使われています。なぜこのように色々なことが出来るかというとDeep Learningの表現力の高さが理由として挙げられます。2節からDeep Learningとは何なのか、表現力の高さとはどういう意味なのか書いていきます。

# 2. Deep Learning(Computer Vision) とは
 **Deep Learning** とは日本語で **深層学習** とも呼ばれるものです。まず、この深層学習に至るまで順を追って説明していきます。
まずは、人間の脳のニューロンという細胞をコンピュータ内でモデル化した人工ニューロンについて簡単な図を以下に示します。これを使うので **AI** とか呼ばれます。この人工ニューロンは現在のコンピュータにとって計算がしやすいように行われたモデル化です。もっと実際のニューロンに近いモデル化も研究がされていたりしますが、量子コンピュータなどハードウェアのさらなる発展がなければ流行らなそうです。
![fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/e8483752-57fe-b6b7-fe3b-ca064680e40f.png)

図より入力Xにωが掛け合わされたものとバイアスbで合計を取ると出力Yが得られています。$ f(x)= ax +b $の中学でやるような直線の式の入力が増えたバージョンという認識で完璧です。直線で言う傾きaは重みと呼ばれweightのwやωを使って表され、切片であるbはバイアスと呼びbiasのbを用いて表しています。

このニューロンモデルを直列・並列に積み重ねたものを **ニューラルネットワーク** といいます。そして、直線を色々重ね合わせることで色々な関数を作ることが出来ます（$ f(g(h(x)))$のような感じ）。この色々な関数が作れるという表現力で **“関数近似”** を行うことがニューラルネットワークの本質です。並列なニューロンの「層」が直列方向にたくさんあることを「層が深い」と表現するため，膨大な数のニューロンモデルを直列・並列に積み重ねることが深層学習の「深層（Deep）」の部分の意味にあたります。以下にもっとも単純なモデルである **MLP（多層パーセプトロン）** を示します。
![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/f0bf3310-bd65-a06c-3a7f-ae0a6ec397f1.png)

Deep Learningがブームになったのはこのような膨大な計算をハードウェアの性能向上とともにできるようになったからです。従来の直列での難しい計算処理に特化したCPUと呼ばれるコンピュータの演算装置ではDeep Leariningを行うためには計算に時間がかかりすぎます。そのため**GPU** 呼ばれる並列での簡単な計算に特化した装置を使用するのが一般的です。

少し話が逸れますが、研究室の教授が学生のころ（1990年代）なんかはハードウェアの性能が低かったため、直列に3層とかを積み重ねるDeepでない学習をC言語などで細かくプログラミングしていたそうです。今はPythonという言語でライブラリを使えばかなり簡単に利用できます。ライブラリはFacebookが開発している **Pytorch** が使いやすいのでオススメです。GoogleのTensorflowも有名ですが，以前のバージョンでDefine and Run型（計算グラフの構造を事前に決めなければいけない）というものを採用していたためプログラムの書き方が難しいイメージがあります。今は以前より使いやすくなったと聞きますが、私は基本的にPytorchしか使っていません。日本のPreferred Networksが開発したChainerはとても使いやすかったのですがPytorchに統合されてしまいました。

ここまでで説明した“Deep”なネットワークにたくさんある重みやバイアスを“Learning”して使いたい表現に持っていくのがDeep Learningです。本節で触れていない“Learning”の方については、次節で画像分野の具体的な例とともに説明します。

# 3. 一般的な学習（教師あり学習）の流れ
2節ではDeep Learningの本質が関数近似であることを説明しました。本節ではこの関数近似をどのようにして行うのか、概要をつかめるように説明していきます。

Deep Learningの“Learning”とは、実際に使用するにはタスクを実行する **推論（Test）** の前に **学習（Training）** をする必要があることから言われています。学習のステップを通して、２章で説明したニューラルネットワークの内部の重みやバイアス値を事前に決定してタスクをこなせるように関数を作ります。最も簡単に流れを説明すると、いくつかの教師と呼ばれる入力と正解がセットになったものをDeep Learningモデルに複数与えて「入力から正解を出力できる関数」を作ります。そうすることで、推論のステップでは作った関数によって、入力からもっともらしい正解を出すことができます。以下の例に示すような２ステップを踏むわけです。
![fig9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/b3af803b-cd21-bbd9-47e6-6a660d37f4d3.png)

このとき、耳に注目してネコだと判定するなどの途中過程はDeep Learningモデルの学習で自動的に行われています。他のタスクも同様で人間がやるのはモデルを用意して入力と正解のセットを複数与えるだけです。これだけでそれらしい正解をだすことが出来るようになるので、Deep Learningの汎用性がとても高いことがわかります。この汎用性は大きなメリットで色々なことができるわけですが、無数のパラメータで構成されたDeep Learningモデルの内部は人間では理解できません。このブラックボックスであることがDeep Learningモデルの大きなデメリットと言えます。判断根拠の可視化についても研究がされており、画像のどの辺りに注目したのかをなんとなく可視化できる **GradCAM** など有名な手法はありますが。完全に中身を解き明かしたとは言えないのが現状で、解明される見込みも今のところないと思います。９節で具体的にいくつかネットワークを紹介しますが、これらについて理論が伴っていても結局は「実験してみてこれが良かった」と提案されたものも多いです。

また、学習に関する基本事項として「教師は、多ければ多いほど正確な近似関数が作れるため良い」です。例えば、 **Cifar10** という10クラスに分類を行う有名なデータセットでは、学習に5万枚・推論に1万枚の画像が用意されています。ちなみにこの教師の用意は基本的に人力で行うため大変ということで、少数の教師で学習ができる **半教師あり学習（Semi-Supervised Learning）** や、 **教師なし学習（Supervised Learning）** が研究されているため基本的なものを教師あり学習と呼んでいます。半教師ありなんかは最近少し実用性が出てきたのではと思います。Cifar10で各クラス10枚の計100枚の教師で通常の5万枚の学習に近い精度が出せるようになったとか。
> 参考：　D.Berthelot, N.Carlini, I.Goodfellow, N.Papernot. "Mixmatch: A holistic approach to semi-supervised learning." arXiv preprint arXiv:1905.02249. 2019. https://arxiv.org/abs/1905.02249

この半教師あり学習ほど高度にならなくとも教師を水増しするための **Augmentation（オーグメンテーション）** という技術があり、下に示すように左右を反転させるなどしたものをDeep Learningモデルに学習させて精度を高めることはよく行われます。このAugmentationのような話を聞くとAIといっても泥臭いなと思うわけですがその通りです。Deep Learningモデルは当たり前なのですが学習したことしかできないです。工夫を加えないと「昼間の明るさで撮影した画像で学習して、夜間撮影した画像に使う。」「中央に同じサイズのものを置いた画像を学習して、画面端に拡大したサイズのものを置いた画像に対して使う。」などの使い方で上手くいかないといったことが起こります。企業や違う分野のアカデミア機関と共同研究を行う際には、AIという単語に目を輝かせて来られる方が多いのでこの泥臭さに理解がない状態から打ち合わせが始まることが多いです。
![fig19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/2b161098-1fb5-d9f7-4a7e-53b95590b048.png)

ちなみにデータセットについて、Deep Learningモデルの精度を競う場合、データセットには学習（Training）用データ,推論（Test）用データの他に **検証（Validation）** 用データが含まれることが多いです。この検証用データは学習の終了基準や **Hyperparameter（ハイパーパラメータ）** と呼ばれるDeep Learningモデルの中で人間が設定する値の決定などに用います。もちろん推論用データで調節したほうが推論用データでの精度を高められます。しかしながら実用上、推論するデータは学習時に存在しないため推論用データで調整した最高精度を用いるのは正しくモデルの精度を表しているとはいえません。したがって、検証用データで最高精度になるよう調整したモデルを推論用データに用いて最終的な精度とします。

このように「推論用データでたまたま上手くいっただけじゃない？」という指摘を回避するために検証用データを使うわけですが、「推論用データと検証用データでたまたま上手くいっただけじゃない？」という指摘すら回避するために最近の論文では、検証用データと学習用データの一部を何度か入れ替えて、複数パターンの精度を導出して平均する **Cross Validation（交差検証）** という方法が取られることがあります。Cross Validationでなくても、最近の論文では複数のデータセットや複数のモデルで提案した手法を試して実験結果を示さなければいけない風潮があり査読などを通過するには必須です。

さて、大まかな概要が掴めたところで具体的に教師を使った場合、どのようにパラメータを決定するのかについて次の4節で説明します。

# 4. Optimizer（最適化アルゴリズム)
本節では具体的なDeep Learningモデルのパラメータ決定について学んでいきます。Deep Learningモデルは基本的に最初は乱数の値が入っているところから学習によりパラメータを決定していきます。下に示すように教師の入力画像をモデルに入力すると、初めは乱数によってよくわからない出力が出てきます。この出力は確率などで表されているのですが、この値と教師の値との差分で **Loss（損失）** を計算し、その値を小さくするようにモデルパラメータを更新して近似関数を作っていきます。この操作を“学習”と呼び、Loss計算に用いる関数を **Loss関数(損失関数)** といいます。図の右にモデルパラメータとLossの関係についてイメージを示しています。縦軸が損失で、横軸がモデルパラメータを示しており、学習ではU字型の底にモデルパラメータを移動させます。ちなみに学習初期のモデルパラメータの乱数について、固定しなければ学習後に決定されたパラメータは毎回異なります。論文では何回か学習して得た処理精度の平均や分散が示されていることも多いです。
![fig10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/cf5225e4-cb2d-bee9-21d7-718a736b2634.png)

Loss関数には多くの種類があるのですが、有名でよく使われるのは **Cross Entropy Loss(CELoss)** や **Mean Squared Error(MSELoss)** です。ちょっとDeep Learningモデルの学習についての本筋から話が逸れてしまうのですが見ておきましょう。$p$を教師、$q$をモデル出力として$n$個の出力を考えた時、CELossとMSELossの式は以下のようになります。

$$ CELoss = -\sum_{i=1}^{n} p_{i}*{\rm log}(q_{i}) $$
$$ MSELoss = \frac{1}{n}\sum_{i=1}^{n}(p_{i}-q_{i})^{2} $$

いきなり数式が出てきて「うわっ」となった人いたらすみません。ちゃんと説明しますので。MSELossの方は単純に正解との差の二乗を取って合計・平均しているだけなのでわかりやすいですね。初心者がつまづくのはCELossの方です。まず、CELossではpとqは0~1の確率値を想定していて分類のときに使用されます。

例えば上の図の例を継承し(イヌ,ネコ)の２クラス分類を考えて、ネコ60%と判断した時、モデル出力は$q=（0.4,0.6）$になっています。このとき正解がネコなら教師は$p=（0,1）$です。これを上のCELossの式に代入してみましょう。イヌの部分は$p=0$のためLossの値は0ですが、1に近づけたい正解であるネコの部分は$p=1$より${\rm log}(0.6)$の値が出てきます。${\rm log}$の関数が思い出せない方はググってもらえれば良いのですが、${\rm log}$は上に乗っている値が1のときに0で、1より小さく0より大きい時に0に近づくほど大きな負の値を持ちます。したがって、この値にマイナスをつけると正解から離れていればいるほど、大きな正の値を持ちLoss関数として機能します。

${\rm log}$を使うのは何故かというと、${\rm log}$が曲線でLossが0から遠ざかるほど傾きが大きく増大するようにできるからです。例えばネコが正解の教師な２つの画像を入力してモデル出力が(0.2,0.8),(0.4,0.6)となった時を考えましょう。どちらもネコの確率が最も高いとしているため、正解できているのですが既に80%ネコと言っている前者より、ちょっと自信のない判断をしている60%ネコと言っている後者を100%により近づける学習がしたいですよね。Lossは複数の教師セットでまとめて計算する場合が多いので、MSELossで計算すると後者のLossが大きくなり、一応後者を重視した学習が実現できるのですが、CELossを用いると後者と前者の差がMSELossを用いたときよりさらに大きくなります。したがって、CELossを用いることで学習が不十分なデータをより重視することが出来ます。ちなみにこのまとめていくつかの画像を処理することを **Batch処理** といい、一度に処理する枚数を **Batchサイズ** と言います。

これらのLoss関数はとても基本的なもので発展系も数多くあるので興味あればぜひ調べてみてください。では、Lossについて学んだところで本筋に戻り、Lossが0になるように決定していきましょう。Loss関数が凸関数と呼ばれるような、全体で必ずU字型をしていれば数学的に最適なモデルパラメータ（Lossが0になる）を導出することができるそうなのですが、Deep Learningで解く課題はそうはいかない非凸関数（W字型のように複数底がある）です。

そのため、 **勾配降下法** という方法が用いられます。勾配降下法とは「Loss関数について微分を行い、Lossの値を小さくする方向にパラメータを動かす」手法です。関数を微分をしたら傾きになるというのは高校数学でやったと思いますが、これをLoss関数に使うとどちらの方向に動かせば値が小さくなるかというのがわかります。なので、そちらに少しパラメータを動かすという動作を繰り返せばLossを小さくしていくことができるわけです。これを行うためにDeep Learningモデルは学習をする部分は全て微分ができるように構成します。ちなみに入力から出力にデータを送る **forward（順伝搬）** に対して、このどちらにパラメータを動かせば良いか微分により計算する作業は出力から入力に遡って計算していくことから **backward（逆伝搬）** と呼びます。

Githubというプログラムコードを公開するサイトで見かけるコードや、 **SoTA（State of The Art）** という世界一の高精度を目指したモデルの論文の記述などで、 **SGD（確率的勾配降下法）** と呼ばれる手法が最もよく使われています。ランダムな確率的に教師のセットの中から取り出したものに対して、以下のような式でLossを計算して勾配降下をすることを繰り返すもので最も基本的といえます。

$$w^{t+1}←w^t−η\frac{∂E(w^t)}{∂w^t}$$

wは重みのパラメータを表していて、tの添字はパラメータ更新前・t+1の添字はパラメータ更新後を表します。右側の分数についてEは期待値なのですが単純にモデル出力と考えて貰うと、∂の記号のついた分数のような部分はモデル出力をwの重みについて微分（偏微分）したことを表します。ここでのηは3節で説明したHyper Parameterで**学習率**と呼ばれます。なぜ難しそうな式を出したかというと、単純な勾配降下法とその問題点のイメージを持ってもらうためです。このSGDではηのパラメータによって、下図に示す問題が起こる可能性があります。下図の左に示されるように学習において、ηが大きすぎるとパラメータを移動したら坂の反対側に到達し、次の移動でまた同じ位置に戻ってくるという停滞が起こる可能性があります。また、ηが小さすぎると局所解と呼ばれる実際には最小値とは程遠い凹みに引っかかってしまうこともあります。これらを回避するために最もよく行われるのは **Scheduler** というものを用いて大きいηから学習が進むにつれてηを小さくスケジューリングしていくことです。こうすることで下図の右に示すような動作になりLoss関数の底にパラメータを移動させることが出来ます。また、４節で述べたBatchサイズが大きいときは学習率も大きく、Batchサイズが小さいときは学習率も小さくすると学習がうまくいくと言われています。画像１枚あたりでパラメータを移動させる距離に適切な値があるのだと思われます。一般的にBatchサイズは大きいほど正確なLossからパラメータを動かせるため学習収束後の精度が良いと言われますが、処理を行うGPUのメモリがオーバーしないよう設定する必要があるので基本的にデータ全部を一度に処理することは難しく、限界があります。
![fig13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/992d53d9-304d-3520-3aa4-69c7586c2e12.png)


ここから経験則で書かせていただくのですが、 **Adam** と呼ばれるどれだけパラメータを動かすかを決めるηをLossの値によって調節するという発展手法などがあり、適当な新しいデータセットで安定した高精度を目指すならそれらを使ってみると良かったりします。しかし、SoTAなモデルではSGD＋Schedulerが使われているのがよく見られ、十分な画像枚数のデータセットで手間をかけて調整をすれば結局SGDが良いのだと思われます。SoTAなモデルのOptimizerをAdamに変えてみたら実際に精度が下がった経験があります。

また、最新の最適化に関する手法の研究についていくつか紹介します。昨年の2021年には **Sharpness-Aware Minimization（SAM）** という手法が提案されました。面白いことに1週間違いで **Regularizing Neural Networks via Adversarial Model Perturbation** というタイトルで全く同じ手法の論文が出ましたが、互いに引用し合って実験がそれぞれ違うしお互い説を補完できているとかで折り合いつけてました。以下の図を見てください。これまでLossが最小値になるパラメータが良いとされてきたため一見良いのは一番低い位置にあるsharp minumumの底であるように思われます。しかし、著者らは最小値だったとしても尖った位置でなく平坦なflat minimumにあたるパラメータがが真の最小値でなくても望ましいといった主張しています。そして、勾配を上る方向に少しパラメータを動かした時にLossが大幅に上昇すれば尖っていると判定してその分Lossを大きく設定する手法を提案しました。勾配を上る方向に動かした時に尖っているかの判断が必ず出来るかというとそうではないような気がしますが、確かにパラメータが少し変わった程度で違う判断をしない方が安定した結果を出力できると言われたらそれは正しい気がしますよね。ちなみにこのようにノイズなどが入っても安定した出力が出せるモデルのことを **ロバスト性の高いモデル** といいます。

![fig12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/c454aa98-ed14-67f4-5330-c5235c7927a3.png)

>参考：　FP.Foret, A.Kleiner, H.Mobahi, B.Neyshabur. "Sharpness-aware minimization for efficiently improving generalization." The International Conference on Learning Representations(ICLR). 2021.

>参考：　Y.Zheng, R.Zhang, Y.Mao. "Regularizing neural networks via adversarial model perturbation." Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition(CVPR). 2021.

また、他には学習する正解が**One-hot**と呼ばれる「この画像はネコ」のようなはっきりしたラベルでいいのかという主張があります。わかりやすく例えると「画像分類でドラえもんの画像をネコ100%と出力させるよう学習するのが良いといえるのか」という主張です。人間でもあのビジュアルを初見でネコ100%と判断できないと思われることから、曖昧なものは曖昧な判断にするニュアンスも学習した方が推論の時にそれらしい結果を出せると考えられます。実際にDeep Learningモデルが推論したもので間違えたものを確認すると、出力確率が高くて間違えている（モデルが自信を持って間違えている）ということが多々あります。したがって、以下のように不正解ラベルにも均等な確率を割り当てる **Label Smoothing** と呼ばれる手法が提案されました。余談ですが、このような自信を表すような出力確率を英語で **Confidence** と記載している論文は多く、うちの研究室ローカルな気もしますが日本語で **確信度** と呼んでいました。
![fig11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/8a221a50-68a9-d242-712c-272735f20bfa.png)
>参考：R.Müller, S.Kornblith, G.Hinton. "When does label smoothing help?." Conference on Neural Information Processing Systems(NeurIPS). 2019.

上記に付随してある程度の曖昧さを残すため「Lossは０に近づけるのが本当にいいのか」という主張もあります。日本の理化学研究所から$|Loss-a|$を最小化してLossをハイパーパラメータで0に近い値$a$に収束させる **Flooding（洪水）** なる手法が提案されています。これらのOptimizerの手法について、モチベーションに納得感のある提案が多いイメージがあるため面白いなとわたしは思っています。このFloodiongは論文に書かれている図が可愛いので引用しておきます。Lossが水にぷかぷか浮いているみたいな感じだそうです。
![fig14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/2d1d8731-4e4f-2e89-99fe-dd1443f7455a.png)
>　図の引用・参考：T.Ishida, I.Yamane, T.Sakai, G.Niu, M.Sugiyama."Do We Need Zero Training Loss After Achieving Zero Training Error?".International Conference on Machine Learning(ICML).　2020.

ここまででDeep Learningモデルの基本的な仕組みについてまとめてきました。5節からはさらに具体的なモデルの中身に入っていきます。

# 5. CNNとConvolution（畳み込み）
本節では画像分野のDeep Learningモデルでめちゃめちゃよく使用する超基本となる **Convolution（畳み込み）** という処理について書いていきます。これを用いたニューラルネットワークを **Convolution Neural Network** 略して **CNN** と言います。ほとんどのComputer VisionにおけるDeep Learningモデルに使われています。このConvolution処理を瞬時に理解できるように説明するのはサッカー知らない人にオフサイドを説明するぐらい難しいのですが、頑張ってgif画像を作ってみました。下に示します。めっちゃ大変だった……。
![conv.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/2e22ce02-a3d5-f46c-7033-68125e4e3966.gif)


見ていただくとわかるように、やっていることは2節で示したニューロンモデルと同じです。画像に適応するためにConvolutionという形で処理します。「入力のXに、フィルタ重みωを掛け算して、バイアスbを加算して、出力に配置する」という作業を入出力の左上から順番に行っていきます。左から右に計算して行って、右端まで来たら下に下がって左端からまた計算します。ちなみにConvolutionで処理するときには出力枚数も増え、画像ではなくなるのでこの処理されているデータのことを **feature map（特徴マップ）** と呼びます。

gifでは入力が2枚・出力が3枚の場合を表しています。理解しやすいように枚数設定しているので、実際はこのような枚数では使わないと思います。カラー画像を入れる時点でRed・Blue・GreenのRGBで3枚入ってきますからね。また、フィルタの枚数は入力×出力の枚数だけ必要になります。そして、 **フィルタサイズ** は3×3で表していますが可変に設定できます。フィルタの移動量は **stride（ストライド）** と呼ばれこれも可変に設定できます。gifでは図が見やすいようにstride=3で動かしていますが、stride=1などで重なった位置から値を取れます。重なった位置から値を取ることで、同じサイズのfeature map・同じサイズのフィルタでのConvolutionを直列に行っても段々とフィルタサイズ以上の遠距離に値を送ることができます。下に先程のgifより重み省略して入出力のみ簡易的に描いた例を示しますが、一番左に示した青の値と灰色の３×３のフィルタのさらに右下に処理が進むと到達しています。
![fig17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/162bb2a5-f6e0-d425-17a0-7cac304cc8d3.png)


また、フィルタサイズやstrideを可変させると出力の大きさも変化します。この出力のサイズを調整するために **padding（パディング）** という操作をすることがあります。下に示すように入力の外側に数字を配置する作業で0を配置する場合 **zero padding（ゼロパディング）** と言われます。paddingするときはほとんどzero paddingでいいのですが、他に全体の平均値を配置するなどのバリエーションが存在します。
![fig15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/69418ed4-639a-affa-385b-f1bf5e204dd9.png)

そしてこれらのパラメータは以下の式で関係性を表すことが出来ます。$H$（height:高さ)・$W$（width:幅）の添字を用いて、入力を（$H$,$W$）・出力を（$O_{H}$,$O_{W}$）・フィルタサイズを（$F_{H}$,$F_{W}$）・paddingの高さと幅を$P$・strideを$S$とすると

$$ O_{H} = \frac{H+2P-F_{H}}{S}+1$$$$O_{W} = \frac{W+2P-F_{W}}{S}+1 $$

この数式は覚えなくても良いですが、ここの辻褄があっていないとモデルのプログラムがエラーを吐いて動きませんので、数式の存在は覚えておきましょう。Pytorchのライブラリでは

torch.nn.Conv2d(in_channels, out_channels,kernel_size, stride=1, padding=0)
>Pytorchのドキュメントより引用:
https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html

のような形でこれらを指定します。画像（２次元）へのConvolutionなのでConv2dですね。PytorchのConv2dは他にもパラメータが色々あるのを省略しているのですが、デフォルト値が設定されているのでスルーしておけばとりあえず使えます。補足としてはkernel（カーネル）=フィルタのことで、どちらの単語も使います。また、１枚の画像から取得した特徴マップの枚数のことを **Channel（チャンネル）** と呼びます。PytorchではBatch処理のため **（Batch,Channel,Height,Width）＝（バッチサイズ,チャンネル数,高さ，幅）** の順番で記載された4次元で処理を行うのでそれぞれの次元について把握しておくと良いでしょう。

ここまでConvolutionの基本動作について書きました。このConvolution処理を複数並べて深い層にすることでCNNを構成します。ここからはConvolutionについて抑えておきたい知識をつらつらと述べていきます。まずは、なぜこのConvolutionを使うようになったのかです。これの最も大きな要因は計算量の削減のためです。上記gifからもうイメージが出来ると思いますが、重みを使ったニューラルネットワークの処理の計算量はとても膨大です。そのため、これを効率よく処理するためには小さなフィルタサイズを動かすこのConvolutionの方法が合理的なのです。

この処理の合理性として人間の視覚を参考にしていることも挙げられます。人間が視覚から情報を処理する時に目を動かしますよね。視界にあっても意外と視線をやらないとくっきり物が見えていないので行うわけですが、これを参考にしているので合理的だと言われています。そのため、フィルタサイズなど処理を行う大きさのことを **受容野** と言います。この受容野はComputer Vision分野のDeep Learningにおける重要なキーワードです。

そして、CNNの構造にも計算量を減らす工夫があります。提案されている多くのCNNでは処理が進むにつれてfeature mapのサイズを下げながら出力の枚数 **（チャンネル数）** を増やしていくのが一般的です。チャンネル数を増やすと並列に計算する量が増えて表現力も高まるのですが、画像のサイズが大きいと計算量が増えます。なので、チャンネル数を増やす計算量の増加に伴ってfeature mapのサイズを小さくすることで計算量を減少させてトレードオフにします。また、このとき他に計算量の変化の要因として挙げられるフィルタサイズは固定します。そうすることで、この構造では計算量の問題を解消するだけでなく、feature mapのサイズを小さくすることで同じフィルタサイズを使っても元の画像に対してフィルタで取れる範囲が広くなる、つまり受容野が広くなります。したがって、CNNは入力に近い浅い層では画像の局所的で簡単な特徴を、入力から遠い深い層では大域的で複雑な特徴を得られると言われています。

文章だけでなくイメージができるように１節で述べたILSVRC2012という学会のコンペティションで優勝したVGG16モデルだとfeature mapがどうなっていくか示しておきます。VGGはコンペティションのチーム名で16は層の数らしいです。左が入力で右が出力ですが、出力になっていくにつれてH×Wのサイズが小さく、Cで表しているチャンネル数が大きくなっていくのがわかります。ちなみにチャンネル数など2の累乗で使われることが多いのですが、これはコンピュータが2進数で計算を行うため2の累乗にすると計算処理が効率的なのではというところから来ています。しかし、今のコンピュータだと意識するほどの違いは出ないような気はします……。
![fig16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/eba9974e-e50b-1381-611a-b8fd34bf3a68.png)


VGG16に用いられていて本資料で詳しく解説しない処理をついでに簡単に紹介しておきます。VGGではfeature mapのサイズ（解像度）を小さくする処理をConvolutionで行ってはおらず、フィルタ範囲の最大値を持ってくるだけの **MaxPooling（マックスプーリング）** というものを使用しています。現在はConvolutionを用いて解像度を下げているモデルも多く、その方が精度が同じか高いと思われますが、MaxPoolingには計算量を減らせるメリットがあります。

また、VGG16の最終出力から3層には、Convolutionでない原始的なニューロンモデルで全ての値から重みを用いて出力を得る動作を行う **FullConnect層（フルコネクト層）** と呼ばれるものを用いています。このFullConnect層を直列に並べた場合MLP（多層パーセプトロン）です。

最後に **Dilated Convolution** という発展手法について触れておきます。これは **Atrus Convolution** と呼ぶこともありますが、同じものです。先程の記載では省略しましたが、PytorchのConv2dの関数にはdilationというパラメータが存在します。これがDilated Convolutionについてのパラメータです。それではDilated Convolutionのモチベーションと手法について説明します。CNNでは計算量のため、同じ小さなサイズのフィルタでConvolutionしても大域的な特徴を得るため処理が進むにつれてfeature mapのサイズを小さくしていきますが、これは元の画像にあった位置情報などが失われてしまいます。feature mapのサイズが大きい状態のときに受容野を広げたいのですがフィルタサイズを大きくするともちろん重みが増えて計算量が大幅に増加してしまいます。そこで提案されたのがDilated Convolutionで下に示すようにフィルタのパラメータ数を増やすことなくフィルタの形を変更します。これにより計算量を増やさずに広い受容野を手に入れることが出来、既存の様々なモデルのConvolutionに置き換えて処理精度の向上がされました。ちなみにフィルタの形を学習するという手法もいくつか存在しますが、フィルタの形を示す重みでパラメータが増加する割に処理精度の向上もいうほどらしくそんなに普及していません。
![fig18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/f5317216-e269-99d6-b2b3-6fe56f13b11f.png)

>参考：Y.Fisher, V.Koltun. "Multi-scale context aggregation by dilated convolutions." The International Conference on Learning Representations（ICLR）. 2016.

以上で基本的なConvolution処理についておさらいできました。Convolution処理は入門書籍や入門記事で必ず載っていると思うので今更感はありますが、何故この処理に辿り着いたのかという部分と処理の計算量について言及している記事は少なかったと思うのでその辺り意識してまとめてみました。

# 6. Normalization（正規化）
本節ではニューラルネットワークの処理に今や必須となっている **Normalization（正規化）** という処理について書いていきます。３節で述べたようにニューラルネットワークとは融通が効かない泥臭いものです。学習で用いた画像と推論で用いた画像とで、明るさなどが少し違うだけで入力される値の違いから全く異なる出力をすることがあります。この要因の１つとしては、重みとバイアスによる乗算と加算の処理を長く複雑に行っていくと、最初の値の僅かな差が後に大きな差になっていってしまうことが挙げられます。

このことから学習データと推論データの違いが特に問題となります。入力時の僅かな違いから処理過程で大きく異なるデータになってしまうため、同じ近似関数を学習している筈なのに学習した関数部分でない値が入ってきてしまい上手くいかないということがニューラルネットワークの処理精度を大きく下げてしまいます。この学習データに対して過剰な学習を行なってしまうことを **過学習** と呼び、この学習データと推論データの分布が離れる現象を **共変量シフト** と呼びます。そして、その影響を減らす方法がNormalizationと呼ばれており、ニューラルネットワークの入力前・内部でこの処理を行います。最も簡単にNormalizationがどういうものか説明すると **「データを設定した範囲に押し込めること」** です。余談ですが、Normalizationと似たものに **Regularization（正則化）** があります。同じく過学習を抑制する目的の処理を示す単語ですが、データに対しての直接的な加工処理のことをNormalization（正規化）、最適化に対する工夫のことだとRegularization（正則化）と言います。日本語だと似ていて混同しがちなので注意して把握しておきましょう。

まず一番シンプルで入力に対してよく用いられるのは **min-max normalization** です。$n$個のデータ$x$に対してこれを行うとするとn個のデータxからの最小値$min(x)$・最大値$max（x）$を用いて以下の式となります。

$$ x_i =　\frac{x_i-min(x)}{max(x)-min(x)} 　　　　（i=1,2,3,4,・・・・・・,n） $$

この操作ではデータを0〜１の範囲に押し込めることができます。そもそも入力に用いる画像データは0〜255の整数値なので外れ値などはあまり気にせず最大値255・最小値0でこの操作をしている場合も多いです。-0.5〜0.5の範囲で中央を0にするため0.5を引き算しているプログラムを見たことがありますが、学習と推論で同じ処理をするならそこまで違いはないと思います。また、ImageNetというデータセットでは0〜１したのち、入力画像のRGBについて平均値と分散を取ると次に示すようになることからインターネットで公開されている事前に重みの学習がされているモデルをダウンロードして使う際にはこの平均と分散になるようにデータセット全体の前処理をして使うものが多いです。

$$ mean = [0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225] $$

次にニューラルネットワークの内部で使われるNormalizationについてみていきましょう。もっとも有名なのは **Batch Normalization** という手法です。４節で軽く話したBatch処理の単位でNormalizationを行います。他に **Instance Normalization** や **Group Normalization** などのバリエーションも存在しますが、基本的にこれらの違いはこのNormalizaitionの単位（分散と平均を計算する範囲）の違いです。Batchサイズが大きく取れないときなどに使用されますが基本的にはBatch Normalizationが使われると思っておけば良いでしょう。まずはBatch Normalizationについて図で以下に示します。
![fig22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/3e1179a0-fdcc-22df-6b10-a73104d190d8.png)

>参考：S.Ioffe, C.Szegedy. "Batch normalization: Accelerating deep network training by reducing internal covariate shift." International conference on machine learning. PMLR, 2015.

重なっているものは1つの画像を由来とした特徴マップを表します。なので図ではBatch内に4つの画像を由来とした特徴マップが存在しているときを示しています。Batch Normalizationでは同じ色で示したチャンネル方向のデータに関してそれぞれ正規化を行います。このとき正規化はチャンネル数だけ処理していることに注意してください。Batch単位で正規化と聞くとBatch全体で一度に処理するのかなと勘違いしがちですがそうではないです。このように1枚の画像由来のデータだけでなくBatchを通して値を押し込めることによって、画像ごとの違いを減らすことができ、共変量シフトに対応します。

ここからは、他のバリエーションほとんどにも共通する動作を説明します。２ステップあると考えるとわかりやすいです。まずステップ１としてデータを取る範囲（Batch NormalizationならBatch内）で平均を0、分散を1にします。そして、ステップ２では学習可能な重みとバイアスのパラメータを用いて新しい平均と分散に移動させます。例えば$n$個のデータ$x$の平均・分散それぞれを$μ,σ^2$、ステップ1・ステップ2の出力を$y^1,y^2$、重みとバイアスをそれぞれ$γ,β$とすると学習時のBatch Normalizationの動作は以下のようになります。

$$ y^1_i = \frac{x_i-μ}{\sqrt{σ^2+ε}} 　　　　（i=1,2,3,4,・・・・・・,n）$$$$y^2_i =　γy^1_i+β $$

ここで注意しなければならないのが平均と分散の値です。Batch NormalizationだとBatch内で平均と分散を取りながら学習するのですが、推論時に、使用するBatchによって出力結果が異なってしまうと問題です。そのため**学習時に移動平均**をとるパラメータを用意しておき、推論時のステップ1では平均0・分散1ではなく、移動平均で得た平均・分散に正規化します。例えば$t+1$回目の更新における移動平均$μ_{t+1}$は、$t+1$回目で計算された実際の平均値$E(x)$と1つ前の$t$回目までの更新における移動平均$μ_t$と人間が決定する値であるハイパーパラメータで0〜１の範囲である$δ$を用いると以下のように表されます。

$$ μ_{t+1} = δE(x) + (1-δ)μ_t $$

この$δ$の値を0.99などにすることで、ほとんどは実際の平均値$E(x)$が使われつつ、それまでの更新による平均値$μ_t$を影響させるわけです。そして、この学習時の移動平均で最終的に得た平均・分散を固定し、推論に用います。ここまで、少し複雑でよくわからなかった人は**学習時の平均・分散で学習して、推論に用いている**と簡単に認識しておけばいいと思います。余談ですが、学習時の平均・分散で学習するためにモデルのプログラミングの際には挿入位置の数だけ別でBatch Normalizationを用意しなければならないのですが、共通のものを用いてしまい精度が出なかったという失敗をした研究室メンバーがいました。

あと、４節の流れの中で紹介しそこねた過学習を防ぐ正則化手法で有名なものがあるので、ここでおまけとして紹介させてください。それは **Dropout** と呼ばれる手法です。Batch Normalizationよりも前に提案されており、「Batch Normalizationが登場して不要になった！？」と言われたこともありましたが、今でもモデルの内部に挿入して使われるのをよく見かけます。具体的にはニューラルネットワークの学習時にのみモデル内の特徴マップの値を一定の割合ランダムに0にするというもので、推論時にはDropoutは全く動作しません。原論文では学習時の欠損した多くのモデルについて合算した出力を推論時には得られる（アンサンブルが起こる）ため良いと書かれています。しかし個人的な意見ですが、モデルに欠損があっても正しい出力が出せるように学習するというイメージで効いているように思いますね。ちなみに0にする割合はHyperparameterです。ここでライブラリPytorchで実装触ってみる人向けの補足ですが、torch.nn.Dropout(p)の関数（pはHyperparameter:0にするデータの割合）を使えばmodel.train()の学習モードとmodel.eval()の推論モードで動作の切り替えを勝手にやってくれますが、torch.nn.functional.dropout()の関数では動作の切り替えをやってくれないので注意です。

>参考：N.Srivastava, G.Hinton, A.Krizhevsky. "Dropout: a simple way to prevent neural networks from overfitting." The journal of machine learning research 15.1 : 1929-1958.2014.


紹介させていただいたBatch NormalizationやDropoutは、ConvolutionとConvolutionの間で次の7節にて紹介するActiovation Functionとともに毎回挿入して使用することが多いです。7節まで理解すれば基本のCNNについては理解できたといえるのであと少しです。

# 7. Activation Function（活性化関数）
本節ではDeep Learningモデルの学習を助ける役割を持つ　**Activation Function（活性化関数）**　について書いていきます。これも4節・5節で紹介したものと同じく昨今のDeep Learningモデルでは必須レベルの処理です。活性化関数は基本的には固定した関数をニューラルネットワークの処理の間に行うものです。重みとバイアスだけでは行えないような値の変化を促進しつつ、不要な値の変化を抑制する目的で行います。早速具体的な例から見ていきましょう。下に示すのは最もニューラルネットワーク内部で使われるもので **ReLU関数（レル関数）** と言います。この関数にかけると正の値のときはそのまま出力を行いつつ、負になった値は0を掛けることで以降の計算に使用しないように取り除きます。
![fig21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/e7a009cd-5540-05d0-6da5-109ee022d2bd.png)

このReLU関数は、６節で述べたBatch NormalizationとともにConvolutionとConvolutionの間で使用され **「Convolution → Batch Normalization → ReLU」** のような感じで並べて使用することが多いです。ちなみに、ReLU関数の他に負の値の部分を0でなく少しだけ傾きを持たせる **Leaky ReLU関数** などいくつかのバリエーションがありますが、大差ないもしくはReLU関数の方が処理精度は良いことが多いです。なので結局ReLU関数を使えば良いイメージがあり他のActivation Function（活性化関数）はあまり普及していません。一応、最近のものでいうと **Functional ReLU(FReLU)** が少し注目度高かったように思います。これは負になった値をどうするかという部分にバリエーションがあり良くなったり悪くなったりすることから、負になった値をどうするかについては学習で決めようというものです。これにより精度は向上しますが、学習が増えた分モデルパラメータが増加してしまうのがデメリットではあるのかなと思います。

>参考文献：N Ma, X Zhang, J Sun. "Funnel activation for visual recognition." Computer Vision–ECCV 2020: 16th European Conference, Glasgow, UK, August 23–28, 2020, Proceedings, Part XI 16. Springer International Publishing, 2020.

ここまでニューラルネットワーク内部でのActivation Function（活性化関数）ではReLUが一強という話をしてきましたが、最終出力に対して行うActivation Function（活性化関数）はその限りではありません。二値分類のときに用いられる **Sigmoid関数（シグモイド関数）** と多クラス分類のときに用いられる **Softmax関数（ソフトマックス関数）** を紹介します。まずはSigmoid関数を以下に示します。0~1の範囲に収まっているのでこの関数で導出された値が1つのクラスに該当する確率を表せるのがわかります。その確率を1から引き算すればもう一方のクラスの確率ということで二値分類に使用できそうですね。また、この関数は0.5付近で最も傾きが急になっておりどちらかの確率に振り分けやすい形になっています。
![fig23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/ce778ef8-2a88-b1f2-8681-649f324aead3.png)

一方Softmax関数ですが入出力が多数あることを想定しており、Sigmoidと同じく0~1の範囲にでクラスに該当する確率を表せます。$n$個の入力 $$ x = [x_1,x_2,x_3,……,x_n] $$ と$n$個の出力 $$f(x) = [f_1(x),f_2(x),f_3(x),……,f_n(x)]$$ について考えると以下のような式になります。 $$ f_1(x)=\frac{e^{x_1}}{e^{x_1}+e^{x_2}+e^{x_3}……e^{x_n}}$$$$f_2(x)=\frac{e^{x_2}}{e^{x_1}+e^{x_2}+e^{x_3}……e^{x_n}}$$$$f_3(x)=\frac{e^{x_3}}{e^{x_1}+e^{x_2}+e^{x_3}……e^{x_n}} $$
$$・$$$$・$$$$・$$
$$f_n(x)=\frac{e^{x_n}}{e^{x_1}+e^{x_2}+e^{x_3}……e^{x_n}}$$

式よりSigmoid関数とSoftmax関数がなんとなく似ているなとはわかると思います。その直感は正しいのですが実際に確かめてみるために２入力のSoftmax関数について考えてみましょう。テキトーに$x={5,2}$とかで計算してみます。

$$f_1(x)=\frac{e^{5}}{e^{5}+e^{2}},　　f_2(x)=\frac{e^{2}}{e^{5}+e^{2}}$$
分母・分子をそれぞれ分子で割ると
$$f_1(x)=\frac{1}{１+e^{-3}},　　f_2(x)=\frac{1}{e^{3}+1}$$

はい。Sigmoid関数と同じ形になりましたね。ちなみにSoftmax関数で1変数以外全て固定して出力をプロットするとSigmoid関数と同じようなグラフの形になります。

節全体の個人的な補足ですが、自分はDeep Learning勉強したてのときに活性化関数とか難しいもの使わなくても閾値で1に近ければ1とかでいいんじゃないの？ と考えましたがDeep Learningはモデルを全て微分できるよう構成しないと学習できないので非連続な関数は使えません……。モデルのプログラムを書く際にも小数のfloat型を整数のint型にするなどの処理をしてしまうと学習できなくなってしまうので注意しましょう。

この節までで基本的なCNNについて、ほぼ網羅的に学ぶことができたと言って良いです。あとは最近のモデルについて理解を深め、どうしてそんな構成をしているの？ などの部分にイメージをつけるために8節・9節の内容を確認していただきたいと思います。

# 8. Attention（注意機構）
本節では私が研究を始めた頃にトレンドとなっていた手法である **Attention（注意機構）** について書いていきます。Attentionという手法は初めは自然言語分野で提案されました。 **「Attention is All You Need」** という超かっこいいタイトルでGoogleが出した論文です。これによりGoogle翻訳の精度が大幅に向上したとか。よっぽど凄い提案をしたか著名な所属や人物の論文かでないとこういう面白いタイトルつける余地なさそうですね。自分はめっちゃストレートなタイトルでしか論文出したことないです。Attention is All You Needについて、内容を端的に述べると **「特徴量の計算に内積を用いると処理精度が大幅に向上したよ」** というものです。では早速、画像分野でこれがどういうことを言っているのか見ていこうと思います。

>参考：A Vaswani, N Shazeer, N Parmar. "Attention is all you need." Advances in neural information processing systems. 2017.

まず、自然言語が一次元処理であるのに対し、画像は二次元なので内積というより行列の積が鍵になってきます。これを読んでいただいている方はどうかわかりませんが、自分の少し上の代？ から高校数学で行列を扱わなくなって複素数が登場したんですよね。電磁気など勉強する上では複素数必須なんで高校で勉強しておいてとっつきやすかったのはありますが、行列も必要だと思いますぅ。ということで、かなり基礎ですが行列の積の計算方法から簡単に説明しようと思います。次に計算の例を示します。
![fig24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/64c25bdc-b2e1-f062-7508-ab6406a70720.png)

慣れてない人の覚え方として行列という漢字のつくり（右側の部分）を見て、「二」で横方向に行、「刂」で縦方向に列です．行列の積では、左側に置いた行列を横方向に、右側に置いた行列を縦方向にという感じで計算します。なので行列を置く順番で計算結果が異なるので注意しましょう。ちなみに行列の積を計算するとき次元が適切でないと計算できないことと、真ん中の次元が潰れることを覚えておくと良いです。具体的には「3×2」と「2×4」の行列で計算すると、3×『2と2』×4について、『』内の数字は同じものが入っていないと計算できませんし、結果は『』内が打ち消され外側の3×4の形の行列になります。一度紙か何かに写せば計算方法に理解が深まると思います。それでは計算方法がわかったところで計算結果について見てみると、行列の1行と1列に関する値が１つの成分に集約されているのがわかります。これがAttentionのキモで、今まで3×3などのフィルタによるConvolutionで見ていた受容野をこの演算によって一気に拡張できます。では具体的な画像分野でのAttention実装について見ていきましょう。以下に **Self-Attention** の図を示します。
![fig23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/526c13ae-144a-8a25-eced-3792a33ccea3.png)


最初に1次元の自然言語で提案されたAttentionと同じ形をしていて **Query,Key,Value** と呼ばれる3つを使い、QueryとKeyで関係性を計算してValueに掛け合わせるという手順で行います。2次元のCNNでのSelf-Attentionでは入力は同じ特徴マップをもとに1×1のConvolutionでQuery,Key,Valueを生成し使います。このとき生成した特徴マップを良さげなサイズに小さくすることで精度の低下が起こる可能性はありますが、計算量の削減に繋がります。

計算手順を具体的に見ていくと図に示すようにQueryとKeyは畳み込みでチャンネル数Cを小さくし、縦（H）×横（W）の軸をまとめることで「HW×(C/8)」「(C/8)×(HW/4)」のような形を作り積を計算します。すると上記で学んだように真ん中の（C/8）の次元が潰れ、「HW×(HW/4)」の形の行列ができます。このときHとWについて1軸にまとめたことで「縦×横」全ての要素が1つの値にそれぞれ集約されているのがわかります。次に「HW×(HW/4)」とValueを「(HW/4)×(C/2)」に変形して和を計算して「HW×(C/2)」にした後、さらに変形して「(C/2)×H×W」にします。そしてConvolutionで「C×H×W」に戻します。したがって、全体を受容野としたものがKeyとValueにより生成され、、これをValueに掛け合わせることで画像全体を見てどこに注意（Attention）を払って出力を計算すればいいかという機構が実現できます。これがもっとも基本的なSelf-Attentionであり、様々なネットワークに導入して処理精度の向上を達成しています。

このSelf-Attentionですが、デメリットもあります。それは特徴マップの「縦×横」全てについて演算を行うことで計算量が劇的に増加してしまうことです。5節のConvolutionについて理解していればわかると思いますが、受容野であるConvolutionのフィルタサイズを大きくすると計算量が膨大になるため特徴マップのサイズを小さくするネットワークを構成するのが一般的でした。したがって、このSelf-Attentionは基本的に特徴マップが小さくなった位置で従来のネットワークに加えて使用します。

また、Attentionという手法はネットワーク内で何かしらの重みづけを分岐処理して行う手法全般のことを指しているため、他にも様々なバリエーションがあります。その中で有名なものに **SENet（Squeeze-and-Excitation Networks）** と呼ばれるものがあります（名前が面白い）。下に図で示します。
![fig24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/bbd496ef-9b2c-d2fb-b5c1-0fc065961114.png)

>参考：H.Jie, L.Shen, G.Sun. "Squeeze-and-excitation networks." Proceedings of the IEEE conference on computer vision and pattern recognition(CVPR). 2018.

図より、SENetは **Global Average Pooling （GAP）** と呼ばれるチャンネル（C）ごとの平均値を取る操作を行ってからFull Connect(FC)を用いたニューラルネットワークを通し、最後にSigmoidで0~1の値にしたものを元の特徴マップに掛け合わせます。これによりどのチャンネルを重視するかという関係性を取ることができます。このSENetもSelf-Attenitonと同じく他のネットワーク構成に加えて使用されますが、Self-Attentionと比べ「縦×横」の要素全てに対する計算を必要としないため、軽量なAttentionであり「GPUのメモリが足りない！」とならずに使えることが多いです。

ここまででAttentionについて、基本事項と有名な例が抑えられました。Attentionは膨大な種類がありますが、モチベーションがわかれば理解できると思うので興味があれば色々調べてみてください。Attentionを可視化に用いる **Attention Branch Network** は日本の中部大学さんが提案していて面白いと思います。また、9節ではAttentionを用いていて最近注目されているVision Transformer(ViT)についても簡単に紹介します。

>参考：H.Fukui, T.Hirakawa, T.Yamashita. "Attention branch network: Learning of attention mechanism for visual explanation." Proceedings of the IEEE/CVF conference on computer vision and pattern recognition(CVPR). 2019.

# 9. 具体的なネットワーク（ResNet、U-Net、DeepLabv3+、ViT）
本節では具体的なネットワークについていくつか紹介します。私がセマンティックセグメンテーションについてよくやっていたので、その辺の例を多めに出しますが、シンプルでわかりやすいので良い例だと思ってもらえるように紹介できると思います。

まずは **ResNet（Residual Network）** です。ResNetはとてもシンプルながら処理精度が著しく向上するため理論もそこそこに発表され、今でもなぜ精度が上がるのか所説あります（Deep Learningの闇）。手法としては以下の図に示すようにConvolutionなどの重みを持った層（Weight Layer）による処理前の値を処理後の値に加算する **Skip Connection** を導入します。
![fig25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/4b8cbfd6-2f32-a3b8-b784-2f5fdef30a95.png)

>参考：K.He, X.Zhang, S.Ren, J.Sun. "Deep residual learning for image recognition." Proceedings of the IEEE conference on computer vision and pattern recognition. 2016.

ResNetの原論文では処理前と処理後との残差について学習することが出来るため精度が上がると言われていました。ResNet構造を用いた16層や50層などのネットワークをResNet16、ResNet50などとそれぞれ呼びます。ResNet50などはそのまま画像分類モデルに用いられる他、セグメンテーションなど他タスクでは **Backbone（バックボーン）** と呼ばれる特徴抽出するところで部分的に用いられます。ここからは個人的な経験則の話になりますが、ResNetは高精度な処理が行える一方で可視化など説明性を求める手法には向いていないように思われます。GradCAMという手法を用いて画像のどこに注目して判断したかを見てみると、VGGなどの基本的なネットワークがネコの画像で耳などに注目箇所が反応するのに対して、ResNetはネコの背景のカーペットなどに反応していて「そこに注目してるの？」となり精度が高い一方でやりすぎ感が否めません。そして、出力確率（確信度）が低いものをエラーとして検出しようとしても残差が強調されるためか、全て90%を超える確信度の高い確率の出力がされることが多いです。ちなみに上図のWeight Layerを並列に増やした **ResNeXt** や **ResNeSt** なども最近提案されています。

>参考：S.Xie, R.Girshick, P.Dollár, Z.Tu. "Aggregated residual transformations for deep neural networks." Proceedings of the IEEE conference on computer vision and pattern recognition. 2017.
>参考：H.Zhang, C.Wu, Z.Zhang, Y.Zhu, H.Lin, Z.Zhang. "Resnest: Split-attention networks." arXiv preprint arXiv:2004.08955.2020.

次に **U-Net** について説明します。U-Netはセマンティックセグメンテーションのためのモデルで、最も基本的なEncoder-Decoder（エンコーダ-デコーダ）構造にSkip Connectionを加えたものになります。Encoderは画像分類モデル全体と同じもので特徴マップのサイズを小さくしながら特徴を抽出します。そしてセマンティックセグメンテーションでは出力を画像にしたいので小さくした特徴マップを元のサイズに戻さなければなりません。したがって、下図に示すようにEncoderを通したのちにDecoderでサイズを大きくします。また、U-NetのキモとなるSkip ConnectionではEncoderの途中の特徴をDecoderに送っています。下に図を示します。
![fig26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/728f7305-1988-296a-bf81-8f5adde9bd9c.png)

>参考：O.Ronneberger, P.Fischer, T.Brox. “U-net: Convolutional networks for biomedical image segmentation”. International Conference on Medical image computing and computer-assisted intervention. Springer　pp. 234-241, 2015.

順番にU-Netについて詳しく説明していくと、まずDecoderでは解像度を上げるために **Deconvolution（デコンボリューション）** や **Transposed Convolution** と呼ばれるものを用います。DeconvolutionとTransposed Convolutionは同じもので日本語だと **逆畳み込み** と呼ばれます。この処理ではConvolutionとは逆の処理で、1つの入力値に対して複数の重みを使います。ライブラリでは基本的にConvolutionと同じパラメータを入れて使えば逆の処理であるDeconvolutionが出来るようになっていると思います。

次にU-NetのSkip Connectionについて言及しますが、こちらはResNetのような加算ではなく **Concatenate（Concat）** と呼ばれるチャンネル方向の接続をすることで行われています。（Batchsize,Channel,Height,Width）の型で（16, **128** ,256,256）の二つがあれば（16, **256** ,256,256）のような特徴マップにつなげます。この接続をするときは他のHeightなどの大きさは一致しなければならないことに注意してください。また、U-NetのSkip Connectionについて原論文では特徴マップを小さくしたときに失われた位置情報を補間するためと言われています。セマンティックセグメンテーションなどのタスクのCNNで用いられる入力画像を小さくしてから元のサイズに戻すモデルでは、特徴マップのサイズが小さくなると有意な特徴を得るのが難しくなってしまうようで、低解像度である図の下にあたる箇所の改善がよく見られます。理想としては解像度を落とさずに特徴抽出が出来れば良いのでしょう。計算量が増えすぎてしまい難しそうですが。

ここから完全に自論と仮説なのですが、ResNetやU-NetなどのSkip Connection全般について、出力計算から遠いところを近くするのが精度向上が起こる１要因なのではと思っていたりします。出力から微分する逆伝搬を用いてモデルを学習するため、共変量シフトのような現象で出力に近い部分の方が学習が進みやすく、出力から遠い部分は学習しづらいというのがあるのではないかと個人的に考えています。そう考えるとU-Netで良く改善が行われている低解像度の部分はSkip Connectionを加味するとLossを計算する出力から一番遠い位置に当たりますよね。この自分の仮説に最もアプローチしていると思われるのが、Deep supervisionと呼ばれる手法です。この手法は解像度の低い部分でも正解画像と損失を計算する出力を設けるというものです。しかし、解像度の低下した部分で解像度の低下した不完全な正解を与えるに留まることと、モデルの中間表現に正解を与えることが必ずしも最終出力のためになると限らないのが問題に思われ、一部データセットで精度向上があるに留まるようです。以下に参考を貼りますが、調べてみたら昨年の2021年でも使ったという論文があったので一緒に貼っておきます。

>参考：CY.Lee, S.Xie, P.Gallagher. "Deeply-supervised nets." Artificial intelligence and statistics. PMLR, 2015.
>参考：J.Le'Clerc.Arrastia, N.Heilenkötter, D.Otero.Baguer. "Deeply supervised UNet for semantic segmentation to assist dermatopathological assessment of basal cell carcinoma." Journal of Imaging 7.4: 71. 2021.

次にU-Netと同じセマンティックセグメンテーションのネットワークで、U-Netより後年に提案された **DeepLabv3+** を紹介します。Googleが提案しているDeepLabシリーズのv3の次に出たやつでとても有名です。以下に図を示します。原論文の図は初心者にはわかりにくそうだなと思ったのでU-Netと同じ構図で書いたのを自作しました。しょぼい図ですが下に示します。
![fig27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/ef63be52-3947-e0c4-91c1-7b5eac35f452.png)
>参考：LC.Chen, Y., G.Papandreou. "Encoder-decoder with atrous separable convolution for semantic image segmentation." Proceedings of the European conference on computer vision (ECCV). 2018.

図より、Backboneと表された特徴抽出に用いるネットワークが様々変えられるよう設計してありResNetなどを使用できます。また、 **ASPP（Atorus Spatial Pyramid Pooling）** と呼ばれる様々なフィルタサイズの畳み込みを特徴抽出が難しくなる最も解像度が低くなる部分に導入しており、右半分のDecoderをシンプルにしている特徴があります。ASPPではDilated Convolutionを色んなパラメータで行ったものなどを並列で処理し、Concatenateで重ねます。また、このシンプルなDecoderではDeconvolutionを使用していません。こちらではConvolutionに対するMaxpoolingのように重みを使わず解像度を上げる **Resize処理** を用いています。Resize処理でよく用いられるものは隣接する値の中間の値で補間して解像度を大きくする **bilenear補間** と呼ばれます。他に近い画素の値を入れて補間する **nearest補間** はセグメンテーションの正解画像をResizeするのによく使います。0,1,2,……などの整数値で単色であるラベルにbilinear補間を使ってしまうと、少数になった後int型により切り捨てるなどの処理が行われ、輪郭が変わってしまう可能性があるためです。

ここから経験則なのですが、医用画像など顕微鏡画像から細胞核や細胞膜を見分けるなどするセマンティックセグメンテーションタスクではDeepLabv3+よりもU-Netの方が高精度を達成することがあります。例えばDeepLabv3+が高いスコアを記録する車載画像では、車などある程度まとまった領域で存在するものが多いこと、カラー画像で無駄があるほど多くの情報が画像内にあること、そもそも学習枚数が多く取れることからDecoderはResizeを用いた簡易なものにして、破棄される情報が増えたとしても特徴抽出の方に力を入れたほうが良い。一方で医用画像などではその逆で、情報が少ない画像、白黒画像、学習に枚数が用意できないという特徴からきちんとDecoderを構成し解像度を上げてこないと捨てられる情報があると学習が上手く行かない。正確な理由は解明されていませんが、こういった違いがあるためではないかと私は考えています。

最後に流行りの **ViT（Vision Transformer）** について簡単に解説したいと思います。ViTはセグメンテーションなど応用が既に登場しており、ResNetのようなBackboneの構造の１つという扱いです。ViTについて簡潔に説明すると今までCNNと呼ばれるConvolutionを中心に行ってきたものを全てAttentionに置き換えたものです。画像をPatchと呼ばれる小さな領域に分割し、それらでAttenitonを取る操作を行います。これには解像度を小さくせずに特徴抽出が出来るメリットの他、自然言語と同じ構造のモデルが使用できるため自然言語分野での発展がさらに画像分野でも活かせると期待されています。8節で説明した通りAttentionの計算コストは膨大なため、AttentionのみでCNNのようなモデルを構築することは難しいと言われていましたが、CNNのように層を積み重ねなくても膨大なデータで学習を行えば、CNNと遜色ない精度が得られる発見がされ、ViT（Vision Transformer）という名前で提案されました。以下に図を示します。
![fig28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2518335/880b57f2-9516-a6a1-2835-11bc63ae581f.png)
>参考：A.Dosovitskiy, L.Beyer, A.Kolesnikov. "An image is worth 16x16 words: Transformers for image recognition at scale." arXiv preprint arXiv:2010.11929 .2020.


ViTの処理手順としては画像をPatchと呼ぶ小さなサイズで分割した後、自然言語のように次元を変換し、 **Positional Embedding** と呼ばれる射影変換&位置情報との連結を行います。このとき、最終出力用のトークンと呼ばれる何かしらのパッチを用意しておくそうです（図では0と連結しているやつ）。CNNなどのイメージだと普通に全出力を集約して最終出力を出すイメージなのでトークンに意味があるのか私はあまりわからないのですが、単純に自然言語でこうしているからこうした程度の話なのでしょうか（ここ詳しい人教えてください）。また、この位置情報は座標の連番でも何でもあまり精度は変わらないそうです。そして正規化した後、 **Multi Head Attention** にてPatchの関係性を取得します。このとき、8節でやったSelf-Attentionのような処理で、自分のPatchをQuery,Key,Valueに分けて自分のQueryと他のKeyとで内積し、自分のValueに掛け合わせるという手順で行います。その後はResNet構造などを挟みつつ正規化して、5節で触れた単純に全ての値を重みつきで集約するMLPで最終的な出力を導出します。

ニューロンモデルを直列に積み重ねることで複雑な表現力を獲得できると言われていた一方、モデル内に無駄は多く発生していると言われていました。しかし、これほど浅い層の学習でも膨大なデータを使えば高精度が達成できるのかと驚きです。ここでいう膨大なデータというのは、今まで **ImageNet** といわれるものが大きいデータセットといわれ1400万枚の画像で学習を行っていました。そこをViTでは **JFT** と呼ばれる3億枚や30億枚の画像のデータセットで学習しています。ちなみにJFTはGoogleの社内データセットでImageNetなどとは違い非公開データセットです。学習済みモデルは公開されておりそのままなら使えますが、一般人がそのデータセットを使った研究をするのは難しくなっています。また余談ですが、日本では法整備が進んでおらず手塚治虫AIなどのように学習に使ったデータセットについてパラメータにしてしまえば著作権に関してゆるい部分がありますが、海外では既になかなか法律が厳しくなってきていると、昨年か一昨年に学会講演で伺いました（今はどうなんだろう）。

さて、長くなりましたがここまで目を通していただければComputer Visionの分野について、最先端のところまで学べたと言えます。他のタスクのネットワークや画像生成で有名な **GAN** （企業出身者はガン、アカデミアの人はギャンと呼ぶイメージ、提案した本人曰くどっちでもいいらしい）と呼ばれるモデルなどは今回説明していませんが、今回解説した基本が抑えられていれば簡単な解説でモチベーションは理解できると思います。では10節で雑感を述べて終わりたいと思います。

# 10.まとめと雑感のようななにか
研究室配属までは電子回路をいじったりしていたので、学部4年からComputer Visionの勉強・研究を始めて3年になります。分野の研究動向を振り返ってみるとDeep Learningブームがだんだんと落ち着いてくる3年間だったかなと思います。Convolutionの層やAttentionをとにかく追加して接続してで論文が書けていた時代が落ち着きを見せてきており、Googleなどの凄い計算機と膨大なデータがなければSoTAのようなひたすらに高精度を目指す研究は難しくなってきました。今後の研究シーンでは実利用のためなどの更にテクニカルなものが求められていくように思います。昨年の2021年はViTの研究が注目された一方でパラメータ数の少なめな一般の計算機で動くモデルの精度を高めるという実用に寄った研究も多くみられました。学んだこと全て書こうかという意気込みで始めた当記事ですが、モデル圧縮のための知識蒸留手法や高次元可視化のための圧縮法であるt-SNEやコサイン類似度の話など他にも色々書くネタはありそうなので評判が良ければ追加で記事を書くのも考えたいです。

卒業後の進路としてはコンサルティング企業のAIグループに就職予定です。開発だけでなくもっと世の中に技術を普及させたいと思い、進路を決定しました。分野を俯瞰してみると、Deep Learningモデルを自動運転や異常検知に用いて事故があったときに誰が責任を取るのかなど、実用には３節で話したような内部がブラックボックスなことによる未だ解決されない大きな課題があります。人間がやるよりDeep Learningモデルにやらせた方が精度が高いなんてことはいくらでもあるのですが、実際に使用されている例はまだまだ少ないです。しかしながら、完全な自動運転でなく運転者の補助をさせるとか、ダブルチェックの片方をDeep Learningモデルにやらせるとか、適切に提案すれば可能性を感じるものであるなと思っています。AIがブラックボックスで胡散臭いのにコンサルティングとか胡散臭いのと重ね合わさって、詐欺師感の強い肩書きに就職するなと自分で思っていますが、そう思われないよう頑張ります。とりあえず就職したところで働きますが、より面白い課題に挑戦していて良い待遇で雇っていただける方いればコンタクト取ってください笑

今から大学で勉強する人あたりにメッセージですが、英語論文をどんどん読むのに挑戦してみることをオススメします。最新の研究はまず英語で発表されますし、実際の英語力はともかく英語のドキュメントに抵抗のある人が世の中には多いので抵抗感を減らしておくのが将来的に役に立つと思います。Computer Vision分野ではネットで論文公開されているものが多く、本記事で記載されている論文もタイトルでググればarXivなどのサイトでpdfが読めます。あと就活は適当にやらないほうがいいです。Deep Learningのスキルがある人材は新卒でも結構高く買ってくれることが多いので、そっちの道で色々選択肢が豊富です。きちんと考えて選ぶと良いと思います。

研究生活を振り返ると、Computer Visionの分野を学ぶことで充実した３年間を送ることができました。教授や周りの方々には感謝でいっぱいです。途中からコロナ禍に入りましたが、リモートで大学の実験機につないで変わらない研究生活が送れたのは良かったです。共同研究でオフラインの実験機を使うしかない研究室メンバーはちゃんと大学通ってましたが、自分は一時期わりと家に引きこもってました。しかしながら、大学の経費でタダ旅行に行ける機会である国際学会や国内学会などがオンラインになったのは激ギレ案件でしたね。現地時間の昼間である夜中に自宅からZOOMで発表するのつらかった……。学部4年生のコロナ前に国内学会に行かせていただけたのは良い思い出です。あと、研究生活で地味に一番嬉しかったのは自分の書いた論文が引用されていたことですね。単純に読んでもらえたんだというのと、世の中の論文の半分以上は一度も引用されないまま終わるという話があるので半分以上の水準の論文は書けたのかなと達成感がありました。他には企業の方がアカデミアの人に比べて最新の知識に疎い傾向あるなあとインターンシップや学会で話を聞いてずっと思っていたので、私は卒業してからもスキルや知識をどんどんキャッチアップするのは忘れずにやっていけたらなと思います。

最後になりますが、読み飛ばしながらでも当記事を読んでいただけたあなたへ感謝いたします。何か少しでも役に立ったと思っていただければ幸いです。