深層学習 学習まとめ: 深層学習に関わる関連技術

# 軽量化・高速化技術
- 毎年10倍のペースで必要な処理能力が増えている
    - 一方でコンピューターの進化は18~24ヶ月で2倍
    - 分散深層学習: 台数を増やして処理能力を高める

## 分散深層学習
- データ並列化
    - 親モデルを各ワーカーにレプリカ(子モデル)としてコピー
    - データを分割し、各ワーカーごとに計算させる
    - 同期型
        - 各ワーカーの計算が終わるのを待ち、全ワーカーの勾配が出たところで勾配の平均を計算し、親モデルのパラメーターを更新
    - 非同期型
        - 各ワーカーはお互いの計算を待たず、各小モデルごとに更新を行う
        - 学習が終わった小モデルはパラメーターサーバーにpushされる
        - 新たに学習を始めるときは、新たに学習を始める時は、パラメーターサーバからPopしたモデルに対して学習していく
    - 非同期型の方がスピードは早いが、学習が安定化しやすいのは同期型
        - 現在は同期型の方が主流
    
- モデル並列化
    - 親モデルを各ワーカーに分割し、それぞれのモデルを学習させる
        - 層の塊で分割することもあれば、分岐で分割することもある
        - 今多いのは分岐での分割
    - 全てのデータで学習が終わった後で、一つのモデルに復元する
    - モデルが大きい時はモデル並列化を、データが大きい時はデータ並列化をすると良い
    - モデルのパラメータ数が多いほど、モデル並列化によるスピードアップの効率も向上する
        - 小さななモデルでは、並列化によるオーバーヘッドが上回ってしまう

# 開発・運用環境

## Docker
- コンテナ仮想化はより軽量な仮想マシン上でのアプリケーションの仮想化を実現する
    - OSはホスト側にあり、これを共有する
    - これに対し、ハイパーバイザー型は、バーチャルマシンごとにバーチャルなハードウェアとゲストOSが存在することになる
        - それだけリソースを消費する
- Dockerは、コンテナ仮想化を用いてアプリケーションを開発・配置・実行するためのオープンプラットフォーム
    - Dockerはコンテナ仮想化を用いたOSレベルの仮想化によりアプリケーションを開発・実行環境から隔離し、アプリケーションの素早い提供を可能にする
        - OSは基本的にはLinux
        - Macの場合には、LinuxのVM上でDockerを動かすことになる
    - その環境自体をアプリケーションと同じようにコード（イメージ）として管理可能にする
        - (単一)Dockerイメージの構成をDockerfileで管理し、複数のコンテナの構成をdocker-compose.ymlで管理することによってインフラ構成管理をコードによって行うことができる
    - Dockerを開発・テスト・デプロイに用いることで「コードを書く」と「コードが製品として実行される」間の時間的ギャップを大きく短縮できる

## ハードウェアによる高速化
- GPU: 簡単な並列処理が得意
    - CPUがMIMD(複数命令列・複数データ、Multiple Instruction, Multiple Data)を得意とするのに対し、GPUはSIMD(単一命令列・複数データ、Single Instruction, Multiple Data)を得意とする
    - GPGPU(General-purpose computing on graphics processing units)開発環境
        - CUDA: GPU 上で並列コンピューティングを行うためのプラットフォーム、NVIDIA専用
        - OpenCL: オープンだが、Deel Learning用に特化している訳ではない、結果的にあまり使われていない
- TPU: Googleが開発した行列計算に特化しているプロセッサー
    - ノイマン型(メモリにデータとプログラムを内蔵しメモリから命令を逐次取り出し実行する)のような汎用性はない
    - 数千の積和演算器が配置され、それらが互いに直接接続されているシストリックアレイというアーキテクチャ
        - 演算の結果をメモリではなく次の乗算器に渡すことでメモリアクセスの必要を無くし、スループットを向上させている
    - 演算器の計算精度を 8 bit や 16 bit のみにすることで、スループットの向上や低消費電力化を図っている

# モデルの軽量化
## 量子化(Quantization)
- 通常のパラメーターの64 bit(倍精度)浮動小数点を32 bit(単精度)、16 bit(半精度)など下位の精度に落とすことで使用メモリと演算処理の削減を行う
    - 比較的よく使われている
        - 例えば倍精度から単精度に落としてもモデルの性能はそこまで下がらない
        - ただしメモリの節約にはなるが、ハードウェアとして対応していないとスピードの向上にはつながらない
- bit数と精度
    - 16 bitだと指数部に5 bitなので、$ 2^5 = 32 $桁までの表現ができる
        - 指数部は15のオフセット表現(-14〜15)
        - 表現できる正の数の範囲は$6×10^{－5}〜6×10^4$程度
        - 16 bitにしつつ、指数部と仮数部のbit数の割り当てを変えることもある
    - 32 bitでは指数部が8bit
        - 正規数として表現できる範囲は、およそ$±10^{-38}〜±10^{38}$
    - これが64 bitでは指数部が11 bit
        - 正規数として表現できる範囲は、およそ$±10^{-308}〜±10^{308}$
- 1 bitで表現するニ値化(binarization)という手法も存在する
    - パラメーターと活性化の双方(+勾配も)が二値化の対象になりうる
    - ただし活性化をニ値化すると、ほとんどの勾配が0になってしまうため、逆伝播時のみ対象となる関数を恒等写像関数として扱う(結果的に微分は1になる)Straight-Through Estimator(STE)という手法を採用することがある

## 蒸留(Distillation)
- 規模の大きなモデルの知識を使い、軽量なモデルの作成を行う
    - 一度精度が高い(=重い)モデルを作り、それを先生モデルとして使う
        - 先生モデルが出力する不正解クラスに対する確率も活用する
    - 生徒モデルでは、正解ラベルを目標分布とするハードターゲット損失$ L_{hard} $と、先生モデルの出力を目標分布とするソフトターゲット損失$ L_{soft} $の加重平均値を最小化の対象とする
        - この際には温度付きソフトマックス関数を用いる
            - $ Softmax(\boldsymbol{z})_i = \dfrac{exp(z_i/T)}{\sum_j exp(z_j/T)} $
                - ハードターゲットでは$ T = 1 $
                - ソフトターゲットでは温度$ T $を高くすることによって、不正解に対する確率を大きくする
        - 生徒モデルでの損失関数は以下の通り
            - $ \dfrac{λ_1T^2L_{soft} + λ_2L_{hard}}{λ_1 + λ_2} $
                - ハードターゲットでは$ T = 1 $だが、ソフトターゲット側で$ T^2 $を掛けることでスケールを合わせる
                - $ λ_1, λ_2 $は$ L_{hard}, L_{soft} $の相対的な重みを意味する定数

## プルーニング(Pruning)
- モデルの精度に寄与が少ないニューロンを削減することで、モデルの軽量化、高速化を図る
    - 重みが閾値以下の場合、そのニューロンを削減し、再学習を行う
- 軽量化されるにもかかわらず、精度は極端に下がらない