arutema47's blog

書いたり書かなかったり。

Pytorch高速化 (2)Mixed Precision学習を試す

Qiitaからのお引越しです。

前編 aru47.hatenablog.com

TLDR; (2021/06/17)

resnet50でCIFAR10をFP16により学習を2倍高速化でき、メモリ使用量も半分にできる。

pytorch1.6からデフォルトでMixed Precision学習をサポートしており、画像認識なら大抵これで上手く学習できます。 一部例外として、swin transformerだとapexを使用したほうが精度が良い場合もありました。

このチュートリアル通りにコードを書くのがおすすめです。 pytorch.org

use_amp = True # ampをオンオフ

# Creates model and optimizer in default precision
model = Net().cuda()
optimizer = optim.SGD(model.parameters(), ...)

# Create a GradScaler once at the beginning of training.
scaler = torch.cuda.amp.GradScaler(enabled=use_amp)

for epoch in epochs:
    for input, target in data:
        optimizer.zero_grad()

        # Runs the forward pass with autocasting. 自動的にレイヤ毎に最適なビット精度を選択してくれる(convはfp16, bnはfp32等)
        # ベストプラクティスを選択してくれるため、便利。use_amp=Falseではfp32を使用する。
        with torch.cuda.amp.autocast(enabled=use_amp):
            output = net(input)
            loss = loss_fn(output, target)

        # Scales loss. 
        # Scalerはautocasting以下では呼ばないこと。
        # 
        scaler.scale(loss).backward()
        scaler.step(optimizer)
        scaler.update()
        opt.zero_grad()

実際のcifar10学習コード:

github.com

目的

RTX2080tiを手に入れたのでPytorchにてFP16学習を試す。 Tensorcoreを使うことで演算速度がFP32に対する大幅な高速化が(スペック的に)期待できる。 どれくらい早くなるか、pytorchでどう書けばFP16が使えるかなど記述する。

BatchNormはFP32なので正確にはMixed-precision trainingだ。

codes: https://github.com/kentaroy47/pytorch-cifar10-fp16

そもそもFP16って?

FP16とは俗にいう”半精度”と呼ばれる浮動小数点における数字の表現方法である。 コンピュータの内部では数字は2進数で保存されており、例えば"3"という数字は:

0011 # 0*8+0*4+1*2+1*1 = 3

といった形で保存されている。これは一般的な4bitの整数表現(4bit Int)という形である。 ただこれでは0から15までの数字しか表現できない。 更に負や小数などを表現するのに使われるのが浮動小数点(float)と呼ばれる方式である。 image.png

上記の図(Googleより)は32bit浮動小数点(FP32)と16bit浮動小数点(FP16)の形式を示す。 これらはコンピュータの数字表現で最も一般的なフォーマットでFP32が単精度と呼ばれ、メモリを節約したい場合に使われるFP16が半精度と呼ばれる。

特徴として: ・MSB(最も左側のビット)が正負を決める ・Exponent 8bitは小数点位置を決める指数部である。 ・Mantissaは整数部を決める。上記のInt方式と同様である。

image.png http://www.altima.jp/column/fpga_edison/bit_number_float.html

のように指数部をスケールすることで非常に幅広い範囲の数字を表現することができる。

FP16学習はDNNにどう関係するの?

1) メモリの節約 まずDNN学習時はGPUメモリ上にactivationやweightを保存し無くてはならない。 FP32方式でそれらのパラメータを保存するよりも、FP16で保存することで必要なメモリ量を半分にへらすことが出来る。

image.png nVidiaより。

2) 演算の高速化 次世代GPUはFP16を使うと演算速度が大幅に向上するTensorCoreが搭載されている。 そのためFP16で学習することでFP32時に対し数十倍の演算速度向上が期待できる(スペック上は)!

環境

Ubuntu 16.04 Pytorch 1.0 CUDA 10.0 cudnn 7.4 GPU RTX2080ti

学習環境

FP16学習repo →https://github.com/kentaroy47/pytorch-cifar10-fp16 CIFAR10の学習repoを元に改造した。

python train_cifar10.py --fp16

でResnet18で学習開始。

FP16化にあたりポイント

https://github.com/fastai/imagenet-fast/tree/master/cifar10 Fast-aiのFP16化レポを参考に学習コードを改造した。

他の参考は: Training with Half Precision https://discuss.pytorch.org/t/training-with-half-precision/11815 https://pytorch.org/docs/stable/tensors.html https://devblogs.nvidia.com/apex-pytorch-easy-mixed-precision-training/

PytorchではFP16(half)がサポートされており、簡単にFP16化が可能。

FastAIのモデル16FP化コードを使わせていただいた。 https://github.com/fastai/imagenet-fast/blob/master/cifar10/fp16util.py のコードを使い、FP16化した。

何をやっているかというと、 入力、CNNのレイヤ→FP16化 BatchNormレイヤ→FP32化 している。

class tofp16(nn.Module):
    def __init__(self):
        super(tofp16, self).__init__()

    def forward(self, input):
        return input.half()


def copy_in_params(net, params):
    net_params = list(net.parameters())
    for i in range(len(params)):
        net_params[i].data.copy_(params[i].data)


def set_grad(params, params_with_grad):

    for param, param_w_grad in zip(params, params_with_grad):
        if param.grad is None:
            param.grad = torch.nn.Parameter(param.data.new().resize_(*param.data.size()))
        param.grad.data.copy_(param_w_grad.grad.data)


def BN_convert_float(module):
# BatchNormのみFP32フォーマットにしないと性能が出ない。
# BatchNormレイヤを検索し、このレイヤのみFP32に設定。
    '''
    BatchNorm layers to have parameters in single precision.
    Find all layers and convert them back to float. This can't
    be done with built in .apply as that function will apply
    fn to all modules, parameters, and buffers. Thus we wouldn't
    be able to guard the float conversion based on the module type.
    '''
    if isinstance(module, torch.nn.modules.batchnorm._BatchNorm):
        module.float()
    for child in module.children():
        BN_convert_float(child)
    return module


def network_to_half(network):
    return nn.Sequential(tofp16(), BN_convert_float(network.half()))

正直このfp16util.pyをコピーし、network_to_halfをネットワークに対し適応すれば良い。

net = ResNet18()
from fp16util import network_to_half
net = network_to_half(net)

学習速度

resnet18

batch = 128 FP32時:1 epochあたり15sec メモリ資料量:2000MB

FP16時:1 epochあたり10sec メモリ使用量:1500MB

50%高速化。 更にバッチ数、モデルサイズを大きくすると。。?

resnet50

batch = 512 FP32時:1 epochあたり57sec メモリ資料量:21000MB

FP16時:1 epochあたり27sec メモリ使用量:11727MB @TitanRTX

  • メモリ使用量半分に
  • 2倍高速化

両者とも50epochでval.89%ほどに到達。 精度的には更にチューニングが必要で(250 epochで93%くらいはいくはず)、FP16が精度的に問題ないかはまだわからない。

TODO: ちゃんと狙い通り16FPになっているかなど確認する。

CIFAR10より更に大きいデータセット

CIFAR10は画像サイズが小さく、演算よりもメモリ通信やデータローダがボトルネックになってしまっている?50%くらいしか早くなっていない。。 Resnet50ではちゃんと二倍高速化。メモリ半分はかなり嬉しい。

知り合いによるとImagenet学習ではほぼ2倍の速度向上が得られたとのこと。 そのうち試したい。

Nvidia Apex

https://devblogs.nvidia.com/apex-pytorch-easy-mixed-precision-training/

Nvidia謹製のpytorch FP16学習ツールが公開されました。

続編で試します

qiita.com

推論高速化

aru47.hatenablog.com

Pytorch高速化 (1)Multi-GPU学習を試す

Qiitaからのお引越しです。

Pytorch Advent Calender 2018 3日目の記事です。

はじめに

学生に"Pytorchのmulti-GPUはめっちゃ簡単に出来るから試してみ"と言われて重い腰を上げた。

複数GPU環境はあったのだが、これまでsingle GPUしか学習時に使ってこなかった。 試しに2x GPUでCIFAR10を学習しどれくらい速度向上が得られるか実験。 またpitfallなどあったら報告する。

環境

GPU: TitanXp *4 OS: Ubuntu 16.04 Pytorch: 0.4.0 Python: 3.5 Network: Resnet 18

codes:

GitHub - kentaroy47/pytorch-mgpu-cifar10: testing multi gpu for pytorch

GPUコマンドあれこれについて:

【Linux】GPU逆引きコマンド - Qiita

MultiGPUにするには。。

Code的には超単純. モデルにtorch.nn.DataParallelを適応するだけでmultiGPUが使用可能となってしまう。。恐るべし。

Pytorchマニュアル。

pytorch.org

device = 'cuda' if torch.cuda.is_available() else 'cpu'
# ネットワーク宣言
net = ResNet18()
# cuda or cpu?
net = net.to(device)
# 複数GPU使用宣言
if device == 'cuda':
    net = torch.nn.DataParallel(net) # make parallel
    torch.backends.cudnn.benchmark = True

torch.nn.DistributedDataParallelを使用すると1GPUに対し1CPUを割り当てるためより早くなるのですが、学習ループの書き方が変わるのが注意です。 リンクを参照してみてください。

tmyoda.hatenablog.com

single GPUで学習.

まずはシングルGPUで学習する。 使用GPU切り替えはターミナルにて

export CUDA_VISIBLE_DEVICES=0
python train_cifar10.py --net res18

とGPUが一つしか見えないように宣言すれば良い。

gpu1.JPG

1epochあたり21秒かかっている。

multi GPUで学習

ここでは2つのGPUを使って学習してみる。 どれくらい早くなるかな?理想的には2倍だがCIFAR10は小さいのでどうか。

シェルでGPUが2つ見えるように切り替えておく。

export CUDA_VISIBLE_DEVICES=2,3
python train_cifar10.py --net res18

2gpus.JPG 学習を開始し、正常に同じIDのプロセスがGPU2,3に入っている事を確認。

あれ?

2gpu_batch128.JPG 1 epochあたり15秒。 40%ほどしか高速化していなくておかしい。。

batch数を増加させる。

https://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html

One can wrap a Module in DataParallel and it will be parallelized over multiple GPUs in the batch dimension.

チュートリアルによるとBatchレベルで並列化しているよとある。 Batchが2個あったら一つをGPU1,もうひとつをGPU2に渡しているということだ。

このスクリプトだとBatch数=128でDataloadしているが、GPU1にBatch=64 GPU2にBatch=64が渡されることになりSingle GPU時に比べBatch数が少なくなっている。

つまりGPUを2つ使う場合はDataloaderのBatch数も2倍にしなければならない。

trainloader = torch.utils.data.DataLoader(trainset, batch_size=256, shuffle=True, num_workers=8)

dataloaderのバッチ数を2倍の256に設定。 2gpus_res18_batch256.JPG

すると1 epochあたり13秒に改善。 2GPU化により60%早くなったのでまあまあかな。データセットが小さいのがまだ足をひっぱている気がする。まだ遅い。

Batch=378まで大きくしてみたがあまり変わらず。

Resnet50 + 101で実験

ネットワークを大きくしてみれば恩恵が大きくなるかと思い実験。 Res50では速度改善は77%!理想に近づいてきた。 ImageNetサイズまでスケールアップすると更に良くなりそう。

ネットワークサイズが小さいとマルチGPUにより計算時間はスケールできる。がgradientをreduce,computeする部分はシリアルのためボトルネックとなってしまう。

Res18 Res50 Res101
SingleGPU 21s 72s 126s
MultiGPU(*2) 13s 44s 77s
Improvement 60% 77% 63%

Res101では学習速度の向上は50ほど良くない。 速度コンペでRes50を皆使うのは一番速度がいい感じで出るからなのだろうか(笑)

singleGPUとMultiGPUのロスを比較

普通バッチ数を2倍にすると学習係数を2倍にしないと学習結果が変わってしまう。

MultiGPU時はどう考えればいいんだっけ? →ざっと検索してもわからなかったので試してみた。

image.png

single lr=0.1, batch=128と等価なのはmulti(2GPU) lr=0.2, batch=256であった。multi(2GPU) lr=0.1, batch=256は学習の進みが異なる。

マルチGPUのデータの流れはどうなってる?

演算としてはBatch[0:255]のデータを

GPU1 Batch[0:127]
GPU2 Batch[128:255]

のようにアサインする。するとbackpropによりバッチ数分のgradientが生成される。

GPU1 grad[0:127]
GPU2 grad[128:255]

となり、これらは集約する必要がある。

# parameter update
Weights += -sum(grad[0:255]) * learnrate

のような演算を行うため、パラメータアップデート時には何個のGPUに分散したかどうかは関係ない。

結論 (or main takeaways)

1. PytorchでmultiGPUを使う際はBatchsizeも使用するGPU数に比例させ増加させなければ十分な学習速度向上は得られない。

2. Batch数を増やしたらその分学習係数も増やさなければ学習結果は変わってしまう。またはパラメータアップデート時に何個のGPUに分散したかどうかは関係ない。

3. ネットワークサイズはある程度ないとマルチGPUの恩恵は受けられない

gradientをbatch要素毎に計算→CPUに戻しbackprop時に学習率を適応するのでGPU数を増やしても学習率に影響はないと予想..

さらなる高速化のために

続き: aru47.hatenablog.com

TensorRTで推論高速化 aru47.hatenablog.com

qiita.com

ハードウェアの速度をどう評価するか考える(2) ~メモリ、メモリ律速~

前回のあらすじとこの記事の目的

前編: ハードウェアの速度をどう評価するか考える(1) ~クロック、OPS~

現代ハードウェアの計算性能を評価する尺度であるメモリ律速の概念とルーフラインモデルについて理解を深めることです。 本記事を通し、あるアルゴリズムが速度が十分に出ない時、それがハードウェアのどの性能(メモリか演算)に律速されてるかイメージできるようになるのが目標です。

しかし前編は子供が起きたので前編は演算速度だけで終わってしまった! 今回は起きる前にメモリ律速まで書くぞ!

https://venturebeat.com/wp-content/uploads/2020/05/Jensen-cooking.jpg?resize=1024%2C614&strip=all

  • アチアチのGPUお待ち!

メモリ律速

脱線したが本線のメモリ律速に戻る。前回では演算速度の話をし、演算速度(OPS)はハードウェアの持つ計算能力で決まると書いた。それでは最強・最速のCPU/GPUは無数に演算機を詰め込めば実現できるのだろうか?もちろん違う。

f:id:aru47:20201101173608p:plain
AMD Zen2 chip photo from ISSCC2020

チップの殆どの面積が内蔵メモリ(L3キャッシュ)で占められることがわかる。またCPUコア自体もSRAMエリアが四割ほどを占める(畑みたいなエリアはSRAMセルである)

f:id:aru47:20201101173512p:plain
Apple A12
f:id:aru47:20201101173539p:plain
Apple A12 CPU

Appleの最新A12チップの中身も見てみよう。

CPUエリアの半分以上もSRAM(L2とシステムキャッシュ(L3?))で占められていることがわかる。(GPUはいい画像が見つからなかったけどこれもまたチップの殆どがキャッシュです。。

先端チップの大きな面積はメモリ(L3キャッシュ)であった。要はメモリである。なんでこんなにメモリを敷き詰めているかというとハードウェア性能にメモリはとても影響するからである。

メモリが重要なワケ

当たり前だが、計算機が

out = a + b

という計算をするためにはabの2つのデータが必要であり、outをメモリに格納する必要がある。そのため1演算(OPS)には2データの読み込みと1データの書き込みが必要だ。

ここでもしハードウェアが100GOPSの計算速度と200Gdata/秒の読み込み速度を持つが、1Mdata/秒しか書き込み速度がないとする中で上記演算を繰り返すとしよう。すると1Mデータを出力したところで書き込みリミットに差し掛かってしまい、以後の計算は停止してしまう。CPUのパイプラインストールと同様のことが起きてしまい、例えいくら演算速度が早くても無意味である。 また上記のout = a + bの例でも同様に読み込みデータが極端に遅ければ演算にデータ読み込みが間に合わず、演算はストールしてしまう。

このようにメモリ読み込み/書き込みが原因で演算速度が律速してしまうことをメモリ律速と呼ぶ。

ここまで読むとわかるが、データの読み込み速度というのは演算速度と同等にハードウェア性能を評価するためには重要であることがわかる。 演算性能だけ高めたとしても、実際の計算は早くならないのだ。

GPUから読み解くメモリバンド幅

メモリ読み書きの速度はメモリ帯域やメモリバンド幅とも呼ばれ、ハイエンドGPUでは非常に重視されている。

f:id:aru47:20201101174151p:plain

NVIDIA A100 | NVIDIA

先端nvidia GPU A100は40GBもの専用メモリを備え、それらはHBM2(high bandwidth memory2)という接続IOでGPUーメモリ間を結ぶ。またそのメモリ帯域幅は1.5TB/s!であり、そのデータ量までならばデータを読み書きできることができることを示している。

先程のout = a + bの例で各データが1BのINTであったとすると、1演算で3Bのデータ伝送が必要になる。そのため1秒間で行える演算量は概ね1.5TB/s / 3 = 500GOPSであると試算できる。これはGPUメモリ内にすべての入力が格納されている例を考えた。例えばDNNを走らせる際はDNNの重み、gradient、中間値はすべてGPUメモリ内に格納するようになっており、概ねこのような概算が可能である。ただDNNの入力はCPUからPCIeバスを通じて伝わるため(64GB/s)、そこは律速しないように注意する必要はある。

また実際のGPUはキャッシュと呼ばれる大量の内部メモリ(メモリ帯域は10TBくらいと一桁高速)を備えるため、a,bが固定値であれば一度GPU内に読み込めば自動で再利用してくれる。またCNNのweightのように入力をスライドさせ繰り返し適応するものでは重みをキャッシュ内に格納し外部メモリアクセスを最小化するように賢く再利用している。そのようなトリックがcuDNN等のライブラリにかかれており(暗号化されているが)、頑張って読んでいくと3x3convや5x5convに特化したカーネルなどを見つけることができる。キャッシュの仕組みなどはここでは解説しないが、興味あるならばパターソン&ヘネシーなど読むと良いです。

amzn.to

amzn.to

amzn.to

ルーフラインモデル

f:id:aru47:20201005102653p:plain
TPUのルーフラインモデル

とうとう本題のルーフラインモデル*1まで来ました!今までの話を追えていれば難しい話ではありません。

out = a + b から演算量を求める例え話の時におや、と思った方もいるかと思います。 興味深いことにハードウェアの演算量、メモリ帯域が与えられ、演算したいアルゴリズムのデータ入出力量が既知であればそのハードウェアの演算速度(OPS)が求められるのです!

演算したいアルゴリズムのデータ入出力量をルーフラインモデルでは

ある演算に必要なデータ量=MAC OPS/data[byte]

で表現し、x軸にプロットします。またその時のアルゴリズムでの実測OPSをy軸にプロットします。

注意したいのはCNNのようにデータを計算間で再利用できる文は(キャッシュ利用できる分は)差し引いて考える点です。Alpha GOのCNNはMAC OPS/data=1000とデータ再利用効率が非常に高く、グラフの右上にプロットされます。ここの点ではメモリ帯域で律速されず、ハードウェアのOPSでリミットされます。

一方でRNNやMLPはデータ再利用効率は優れず、MAC OPS/data=100程度です。TPUv1はメモリ帯域が遅く、演算はメモリ律速になってしまいます。このようにルーフラインモデルは複数の演算、メモリヘビーなアルゴリズムを比べることでハードウェアの演算速度とメモリ帯域両方を加味したバランスの良いベンチマークが可能となります。

ちなみにDNNでバッチ数を増やすと速度がちょっとずつ上がる、TPUではバッチ数を出来るだけ上げたほうがよい、と言われるのはバッチ間でweightデータを再利用できるためMAC OPS/dataが上がり、メモリ律速が緩和され高速化できるためです。

Further comments

TPU vs GPU

TPUやGPUの最大速度はCNNといえどもメモリ律速であり、大体メモリバンド幅で決まります。 そしてTPUとGPUは同じIO(HBM)を使用しているので最大速度もとんとんになってしまいます。

https://www.extremetech.com/wp-content/uploads/2018/04/ResNet-50-640x396.png TPUv2の結果ですが、だいたい速度は同じです。

FP16, BF16, TF32などの低精度Mixed Precision学習

qiita.com

https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.amazonaws.com%2F0%2F171915%2F891e685c-48f6-4afb-9839-1074706d8f73.png?ixlib=rb-1.2.2&auto=format&gif-q=60&q=75&s=f8c18922b1c0b7d21b3f11d95d5e2bba

メモリ律速はデータの読み書きする量で決まるのでFP16といったより少ないデータ量で学習を行うと、理想的には二倍ほど同じGPUでも速度向上するポテンシャルがあります。なのでハードウェア性能を引き出す上でMixed Precision学習は非常に強力なテクニックです。例えばTPUv3ではBF16というフォーマットで学習が行われ、指数表現がFP32フォマットと同等の精度であるため学習精度も落ちないことを売りにしています。

また最近nvidiaが導入した低精度表現TF32(実効的にFP19)の学習がpytorchでデフォルトになりました。 これで更に高速化が達成できるかと思います。

pytorch.org

もしpytorchを使っているならば公式のMixed Precision学習導入説明がとてもわかりやすいので読んでみて下さい。

*1:Roofline: An Insightful Visual Performance Model for Floating-Point Programs and Multicore Architectures https://people.eecs.berkeley.edu/~kubitron/cs252/handouts/papers/RooflineVyNoYellow.pdf

Reposado

久々にシリコンバレーレストランの記事。

www.reposadorestaurant.com

f:id:aru47:20201015132137p:plain

f:id:aru47:20201015132700p:plain

google maps

パロアルトでちょっといいレストラン(会食、パーティ)を予約しないとなーって時に役立つレストラン。 Fine Mexican Diningの名前の通り、洗練された高級志向のメキシコ料理が食べられる。 またレストランの雰囲気もよく、地元の人でいつも賑わっている。 混むので数日前のテーブル予約は必須です。

もちろん定番のナチョスやタコス、セビーチェも美味しくメインの肉料理も外れはない。ちょっと高いけどラムチョップがとても美味しい。

自分は飲まないからよくわからないけど地ビール、地ワインのラインナップが豊富。 また特にオススメは自家製マルガリータ

f:id:aru47:20201015132516p:plain

ハードウェアの速度をどう評価するか考える(1) ~クロック、OPS~

この記事の目的

現代ハードウェアの計算性能を評価する尺度であるメモリ律速の概念とルーフラインモデルについて理解を深めること。

対象読者はメモリバンド幅やOPSなどの概念があまりわかっていない人です。例えば本記事を通し、あるアルゴリズムが速度が十分に出ない時、それがハードウェアのどの性能(メモリか演算)に律速されてるかイメージできるようになるのが目標です。

思うままに書いていたら肝心のメモリの話まで行きませんでした。そのため前編はクロックや演算(OPS)についてです。

f:id:aru47:20201005102653p:plain
TPUのルーフラインモデル

この図はTPUの論文に出てくるRoofline modelです。この図が意味するところを理解するのが本記事の最終目標となります。

ハードウェアの速度をどう評価すればいいか?

ベンチマークによる評価

ハードウェア速度の評価は昔から大きな問題でした。わかりやすい評価指標にある代表的なアルゴリズム数種類で性能をベンチマークするSPEC)などがありますが、ベンチマークの数値だけ見ても実際何故性能が出てるか紐解くのは難しいです。またベンダーの出しているメモリバンド幅や計算性能なども読み解けないかもしれません。ただSPECは同じアルゴリズムを異なるハード上で行うため、ある程度フェアな比較はできます。

CPUクロックによる評価

昔のハードウェア速度はcpuクロックで測れてました。というのもCPUは大体1クロックで一度の演算をするため、クロック速度から大体の性能を予測することができます。一方で並列演算などの要素が入ってくるとこの方法で性能を見積もるのは難しくなります。

またプログラムの計算時間を見積もる際には十分通用します。例えば競プロでは計算量がO(1e9)のプログラムであれば1GHz CPUで1秒で回ると概算できることを最初に習いますね。一般的に競プロのアルゴリズムはシリアル処理なのでこのような見積もりが当たります。(並列化速度はライブラリの出来なので競技性はないですね。。)

OPS、SIMDによる評価

しかし並列演算を行うと単純にクロック速度だけではハードウェアの性能を正確に評価できなくなります。

例えば近代CPUには並列演算(ベクトル演算とも)が実装されています。並列演算回路は多数の演算機(ALU)を並べ、N個のデータを受け同時に演算を行いN個の出力を出すというものです。

この並列化は全ての入力データに対し同じ計算を行うため(例えばベクトルに対し+5を行うなど)、並列化が可能になります。これはスパコン用語で言うとSIMD(single instruction Multiple Data)という名のデータ並列性であり、良く機械学習、グラフィックス、科学演算などで使われます。グラフィックスなどはこのように全てのピクセルに対し同じ計算(定数の足し算や掛け算)を行うためSIMDによって膨大な速度ブーストが得られます。身近な実例ですとnumpy arrayを使うと中では妖精さんがSIMD機能を使ってくれます。そのため生pythonより遥かに高速にベクトルや行列演算をしてくれます。

またGPUも大雑把に言うとSIMDプロセッサの延長線と理解することができます(GPUはSIMDに加えマルチスレッディング、マルチコア性能がCPUに対し大幅に加えられたヘテロジニアスマルチコアシステムですがGPUアーキテクチャに関してはそれはそれで本が一冊書けるのでそこまで今回は深く立ち入りません。)。

CPUとGPUのアーキテクチャ的な違い、SIMDやマルチスレッディングについて深堀りはCMU大の講義が詳しいです。

http://15418.courses.cs.cmu.edu/spring2017/home(15418.courses.cs.cmu.edu]

また日本語で詳しくSIMDや並列演算について知りたい人はこれとか読んでみて下さい。

https://kaityo256.github.io/sevendayshpc/day7/index.html(https://kaityo256.github.io/sevendayshpc/day7/index.htmlkaityo256.github.io]

A100チップのOPSを計算してみる

とにかく並列演算を持つハードウェアの演算性能は単純にSIMDのデータ数N, clock rate, 演算器コア数の掛け算で求めることができ、OPS(operations per second)といいます。

例えばnvidia GPUのOPsを計算して製品スペックと一致するか見てみましょう。

nVidia A100のスペックは以下です。

https://videocardz.com/newz/nvidia-announces-tesla-a100-specifications

f:id:aru47:20201005102843p:plain

FP32のOPSは

OPS = CUDAコア数 * GPUクロック * 2

で求められます。*2としているのはCUDAコアは一クロックで掛け算と足し算両方を実行できるため(MAC)、そのような回路は1クロックで2OPSの動作ができると慣例的に計算します。

そのため、

OPS = 6912 * 1.41GHz * 2 = 19.49TOPS(テラOPS)

とスペックの19.5TOPSの値が得られます。

同様にTensorCoreのOPSも計算する事も可能です。

ここによると)A100はTensorCoreを432個持ち、一クロックで16*32コの計算を扱えるとのことです。そのため

OPS = 16 * 32 * 432 * 1.41GHz * 2 = 623.7TOPS

とスペック状のBF16の624TOPSの値が得られることがわかります。TensorCoreの設計上16*32の演算をフルに扱えるのはBF16のみで、FP32となると演算器の数は半分となってしまいTOPSも半減します。一方でINT8では実効的に演算器の数を倍に増やしたかのように見せられるため、TOPSは倍増します。

後編: ハードウェアの速度をどう評価するか考える(2) ~メモリ律速~

Kaggle戦記~Kaggle Masterになるまでを振り返る~

f:id:aru47:20200903170827p:plain

https://www.kaggle.com/kyoshioka47

目的

この度約10ヶ月間Kaggleに参戦しCompetition Masterになり賞金も獲得できました。本記事では参戦したコンペ中の思考や得られた事を振り返り記録します。これからKaggleを始めMasterを目指す人の参考になればと思います。

また試しに昔登録したamazonアフィリエイトのリンクをいくつか貼ってみました。コーヒー代を寄贈する気持ちでクリック先で本を買ってもらえると嬉しいです。

バックグラウンド

自分は集積回路設計で博士取得後、機械学習アクセラレータの研究をしてました。そのうちにコンピュータービジョン技術自体に興味を持ち、論文などを独学で読漁ってました。Kaggle参戦に役立った本、論文などは最後にまとめます。

また仕事でも機械学習用データセットやタスクなど設計してました。 しかし"動けばいい"が優先されるタスクであり、精度が求められないのがつまらない点でもありました(そもそも精度指標も自分で設計してる。。)。そこでアルゴリズムをもっと詰めてモデルを設計してみたいというもやもやはずっとありました。

始まり

D社のカジュアル面談でkaggleについて聞いたのがkaggleを始めたきっかけだったと思います。提供されたデータセットを元に、一番良いモデルを作るという明快さとモデル設計をもっと深く追い先端技術も試したいという想いからkaggleに登録した記憶があります。要はなんか面白そうだからやってみるかー!くらいの気持ちでした(今もそんな感じですが)

ちょうどKaggle勝つ本が出た頃で、本のさを読みながらtitanicをやった後、あまり考えずにそのままLyftコンペに参戦しました。

Lyftコンペ

f:id:aru47:20200904203552p:plain

LiDARデータから3D物体検出を行うコンペでした。Kaggleで初のLiDARデータを使ったコンペだと思います。

ただデータセット構造がオリジナルで中々本丸のアルゴリズムまでたどり着かなかったです。 自分は参加したのが終了2週間前というのもあり、PointPillarsを動かそうとしているうちに終了しました。

結局ベースラインモデルをちょっと大きくし、TTAを足したものを提出して40位の銀でした。運良くメダルが取れ、モチベーションが以後のコンペに向けて高まったのはよかったです。

いきなりソロ銀が取れた背景として問題設定の難しさからコンペ自体過疎っていてプレイヤーが少なかったのが大きいと思います。

色々穴のあるコンペでしたが*1、ロボビジョン研究者としては一番リベンジしたいコンペです。

学んだこと

  • Kaggleノートブックを使ったEDA、学習
  • 銀上位以上とそれ以外との圧倒的な差。

Kaggleで強い人って?

  • 銀上位以上とそれ以外との圧倒的な差について補足させてください。

Kaggleコンペでは公開notebookコピペでも(運が良ければ)メダルは取れます。だからメダル二枚でなれるExpertではその人が本当に分析者として優れているかはわからない、と言われてしまうのだと思います。

ただ コピペでは超えられない壁が銀上位あたりにある印象があります。

Bengaliコンペで自分は実感するのですが、この壁を超えるのに必要なことはホストが出した問題に対し、クリエイティブな解決策が提示できるかどうかだと思います。 この点がKaggleの"競技データサイエンス"的な面で一番おもしろいポイントであり、Kaggleディスり勢が理解していないポイントと感じます。 主観ですが、データサイエンスの難問に対し独自のアプローチを取れる人は業務(開発、研究問わず)で強いと思ってます。

なのでKagglerの実力を見るときには1) コンスタントに銀上位のパフォーマンスが出せているか、2) クリエイティブなソリューションで問題にアプローチしていたかを見ると強い人かどうかわかると思います。偉そうに書いておいて自分はまだ1)は達成できてませんが。。

PKUコンペ

f:id:aru47:20200904203330p:plain

Outrunner氏のsolutionより

2D画像から車の3D位置と姿勢を推定するというCV的にも先端内容のコンペ。

問題設定が面白く、コーディングもなかなか難しいとやりがいのあるコンペでした。 CenterNetを使い倒し大規模2D CNNの学習を詰めれたのはいい経験でした。

PKUコンペの特徴として3D位置推定を行う既存ライブラリはないため、モデルをほぼスクラッチ実装する必要がありました。 PKUコンペを通じて自分はいくつかの論文のスクラッチ実装を行った記憶があります。 様々なアイデアをインプリするうちに 駄目駄目だったコーディングがだんだんマシになっていくのを感じました。

コンペはoutrunner氏が大差をつけて優勝しました。

Outrunner氏のカメラキャリブレーションパラメータを使って3次元回転のaugumentationを行うソリューションはとても面白く、一見の価値あり。言われてみれば確かに!と思うのだがなかなか思いつかない簡潔で有効性の高いソリューションでした。

ただ自分はネットワーク設計が最後まで良くなく(FPNを上手く実装できず)、90位の銅でフィニッシュしました。

コンペ的には色々ありましたが*2、ギリギリ致命傷にはならず最後までコンペを楽しめました。

学んだこと

  • コーディング力
  • DNNのパラメータ調教が上手くなった。

  • やみくもにチームを組まない

補足です。PKUコンペではあまり考えずに0サブのNoviceの方と組んだ後、連絡が取れなくなってしまうという自体が発生しました。笑 チーム組むときはそれなりに実績ある方かスコアをそれなりに出している方と組んだほうが良いと思います。あとTwitter上で日本人の方と知り合いになりチームを組むのは(人間性、言語面)からおすすめです。またLBで銀上位につけていると結構強い人からもマージリクエストがくるようになりました。

Bengali

f:id:aru47:20200904204456p:plain

Shake Downの洗礼

Bengali文字認識を行うMNISTの延長線のようなコンペ(と当時は思ってました。) またこのコンペでは強いチームメイトと組むことができ、色々刺激を受けながら戦うことができ、とても楽しかったです。

そのため画像認識の技術を詰め込んで適当に学習させればいいや~という舐めた意識で(自分は)コンペに参戦していました。

結果としてコンペの趣旨を捉え違えていたので 49位から170位までShake Down してしまいました。

f:id:aru47:20200906231604p:plain

Shake Downの原因

ShakeDownの原因はコンペ説明文をよく読む・理解していないことでした。

なのでKaggleで勝ちたい、つまり運では難しい銀上位以上を目指したいならば、コンペに出る前にいくつか確認することがあります。 * 1) どのような機械学習の問題なのか?(画像、テーブル..) * 2) ホストが解きたい問題は何なのか? の二点だと思います。

例えばBengaliではベンガル文字はGrapheme, Vowel, Constantと3つの部首で構築されますが、未知なる部首の組み合わせにも対応できるような機械学習モデルを作れるか?というのがホストからの挑戦でした。

例えばBengaliでは3位のソリューションは文字がtrainに含まれていたか否かをarcfaceで判定後に未知クラスに強いモデルで判定する、1位は未知クラスはCycleGANでスタイル変換したあとに判定することで判別精度を向上させるなど特にIn-the-money圏内のチームではホスト課題にあったアプローチが多く勉強になりました。

ShakeDownを避けるために実践したこと

BengaliでShakeDownしたのはめちゃくちゃ悔しかったですし、このままKaggleをやっていても一生金は取れそうもなかったので、大きく反省し戦い方や準備の仕方を変えました。

まずホストが解きたい問題を読み取るためにコンペ説明はよく読み込む必要があると感じました。またホストがデータセットに関する論文を出していたら熟読するのが良いと思います。前述した二点目を解決するアイデアを実装できるか否かが銀上位にいけるか行けないかの分かれ道だと思います。(文章で書くと当たり前に感じますが、LBを上げることしか頭にない方も過去の(自分を含め)残念ながら多くこれを実行できている方は多くないとコンペ参加経験から感じました。自戒の意味も込めて厳し目に書いておきます。)

例えば自分は過去コンペの説明を読み込みホストの課題を理解し、上位陣がどのような具体策でその課題にタックルしたか、という視点で過去二年分くらいのコンペソリューション(上位陣のみですが)を読み漁ったのが良い勉強になりました。例えばクジラコンペやASPLOSはいい教材でした。あと強いKagglerのソリューションをまとめて読むのも勉強になります。(日本の画像系ではphalanxさんとか)

PANDA

チームアップ

Bengaliの反省後、参加したコンペでした。

このコンペではfamtaroさんやpotemanさんとつよつよなMasterと組むことができ、"勝ったな.."感はありました。

優勝

チームメイトではSlackで日々議論しながらモデル設計をし、特にコンペの最重要課題であるnoisy-labelに対応するかについて頻繁に議論しました。 結果的に実装したnoisy-label対策が大当たりし、まさかの優勝することができました。Goldは夢のまた夢だったので取れてかなり嬉しいです。

チーム的には銀を維持して銅までshakeしなければいいな、というノリだったのでSlackでチームメイトが"1!"と送って来た時はかなり驚き、午前中は仕事になりませんでした。

優勝は出来すぎにしても、目的であったコンペ趣旨に対しオリジナル技術で対策を行いモデルを組めスコアを上げれた事はべんがりでの経験を上手く昇華することができたことに繋がり嬉しかったです。

ここでは技術的な面には触れませんが、具体的なソリューションは下記に記述してます。

1st place solution

また自分より遥かに優れた文才でfamtaroさんが参戦記を書いているのでそちらも御覧ください。

fam-taro.hatenablog.com

Winner's call

Kaggleの賞金受領手続きについて記述しておきます。 Kaggleでは賞金圏にはいるとホストへ報告書とモデル提出、そして電話会議でプレゼンするWinner's callという手続きが必要になります。

具体的な提出アイテムとして

  • モデル学習、推論のスクリプト提出
  • モデルについてのアプローチや考察を記述した報告書(20ページくらいのパワポ資料としてまとめ、自分はWInner's callのプレゼン資料として使いまわしました。)

の二点が必要です。両方Kaggleからテンプレートなどが支給はされますが、自己フォーマットでも筋が通っていれば問題ないです

またWinner's call自体はGoogleの会議ツールを使い組まれました。発表25分、質問30分の計1時間でした。 質問はDNNの学習パラメータ、augumentationといった技術的な話からどのような実験をして最終的なアプローチにたどり着いたか、試したが動かなかったアプローチとその考察などを聞かれました。学会のQ&Aの緩い感じをイメージしていただければと思います。お互い英語は母国語ではなかったので英語でそこまでボコボコにはされませんでした。

また賞金受領やWinner's callの手順はカレーちゃんさんのブログもとても参考になりました。(自分は個人で賞金受領したため、"W-8 BEN"をサイト上で記入しました。)

"Kaggleで賞金を獲得したら家族のために使う"と以前から宣言していたため、家族マターに有り難く使用します。笑 note.com

また本解放がMICAAI workshopへのinvite講演など学会ワークショップへ結びついたのもよかったです(こちらはfamtaroさんに投げてしまいすみません。。)

闇の小麦コンペ

f:id:aru47:20200814154701p:plain

闇が多すぎて正直ノーコメントです笑*3

Publicでは8位だったものの、Privateでは78/2245位までShakeDownしSilverとなりました(死)。Privateのbox特性が大きく変わってしまいそれに対応できなかったのが敗因でした。

何はともあれこのコンペを以てMasterへの昇格を"キメ"ました。やったぜ

最後に勉強してよかった本、論文

Kaggleの参戦前に読んでおいてよかったと思う厳選書籍をいくつか紹介します。他にも機械学習系の本はたくさん買ってましたが全部売り、結局これらがのこりました。

Kaggle,機械学習の本

Kaggleで勝つデータ分析の技術

f:id:aru47:20201030221944p:plain

amzn.to

この本がなかったらKaggleのコンペ参戦まで行けていなかった気がします。

"Kaggleとは何か"から初心者用コンペのTitanic参加するまでの道のり、そして更にテーブルコンペで勝つためのテクニックが一冊にまとめられている良書です。

自分はテーブルコンペには出たことはありませんでしたが、評価指標の詳細解説や特徴量設計の章は参考になり業務でも活かせる方が多いと思います。 ただ内容は機械学習の初歩は理解していることが前提なのでこれ一冊で戦えるようになる、というわけではないです。初学者ですと下記の本一冊も同時に読んだほうがいいかもしれません。

Python機械学習プログラミング

f:id:aru47:20201030222028j:plain

www.amazon.co.jp

[第3版]Python機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear) | Sebastian Raschka, Vahid Mirjalili, 福島真太朗, 株式会社クイープ |本 | 通販 | Amazon

回帰、SVM等の機械学習やデータ分析の基本を叩き込むのには良書です。会社のAI系部署の新人研修でも使われていました。 網羅している事柄も多く、最初の一冊にはかなりおすすめです。

第三版まで出てますが、ディープの内容が追加されているのが差分です。第一版でも十分勉強になります。

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

f:id:aru47:20201030222333j:plain

www.amazon.co.jp

ディープラーニングを勉強するならダントツでこの本がオススメです。研究でも実務でも一番役に立ちました。G検定とか勉強してる場合じゃないですよ!

通称ゼロ作で日本発書籍ではダントツのクオリティを持ちます。

DNN、CNNの推論と学習を一からNumpyで順を追って実装する良書です。DNNの具体的な演算もイメージできるようになるので応用が効きやすく、今でもはじめの一冊としては最高だと思います。何度読み返したかわからない。。笑

NLPに興味あるならゼロ作2もオススメです。

PyTorchによる発展ディープラーニング

f:id:aru47:20201030221559j:plain

www.amazon.co.jp

Pytorchで戦いたい方はこの一冊を写経するのがおすすめです。

画像認識、物体検出、GAN、自然言語処理とDNNの幅広い分野をカバーしている超良書です。

コードがとても丁寧かつ量も多く、これからpytorchを勉強しようとしている方には一番お勧めしたい本です。

Kaggleスタートブック

f:id:aru47:20201030221714j:plain

amzn.to

Kaggle勝本よりも丁寧に機械学習初心者向けにtitanicコンペについて解説をしてくれます。あまり機械学習やプログラミングの経験がない場合、この本から入りその後にkaggle勝本のステップアップするのが良いかもしれません。

個人的には機械学習プロフェッショナルシリーズ(深層学習)などは式が多くあまり技術がイメージできず、直接論文読んだほうがわかりやすかったです。

論文など

駆け出しのころに勉強したCourseraのNg先生の基本コースは参考になりました。ゼロ作を読んでも同じところをカバーできる気がします。

ja.coursera.org

画像認識系の論文はそれなりに読んでいました。例えばPhalanxさんのkaggle_tipsに読んでおくと良い論文のまとめがあります。 github.com

今後の抱負

Masterになれたものの、まだまだ実力不足を実感します。今後もロボットビジョンに近いコンペに出場しながら着々と実力を付けていきたいです。

対戦よろしくおねがいします!

*1:評価指標が怪しかったりデータセット構造がKITTIやnuScenesと異なり地獄だった

*2:リーク疑惑があったり、評価指標にバグがあったり、3D回転の名称が間違っていたり地獄はありました

*3:打線組めるくらい色々ありすぎて途中からPANDAにリソースを注力してしまいました。。:huggingface:

RNNからTransformerまでの歴史を辿る ~DNNを使ったNLPを浅く広く勉強~

Amazon Prime 一ヶ月無料

f:id:aru47:20200821115402p:plain Seq2seqからBERTまでのNLPモデルの歴史をざっとまとめる。

DNNは知ってるけどTransformerってなんだかわからない、って人におすすめです。

Abst.

画像認識にもTransformerが使われることが多く、DeepRLやGPT-3といったNLPモデルも身近になってきています。"Attention is 何?"と言えなくなってきたので勉強しました。

Feedforward NetworksからSeq2Seq, Attention機構からTransformer登場、そしてBERT GPTといった最新モデルまでの流れを広く浅く記述する予定

またKaggle NLPコンペの上位解法から利用例を探る。

Tl;DR

TransformerはSelf-Attentionという機構でデータ内の時系列的特徴を抽出でき、従来のRNNを始めとするNNに対して 100倍以上計算効率が優れる。

この優れた計算効率を活用し、近年は 莫大なデータ量と計算量で巨大なモデルを学習し、NLPモデルの精度を凄まじい勢いで改善している。

その過程で出てきたモデルがGPT-3やDeep-RLなのだと思う。

フィードフォワード型NNの課題

CNN等はエンコーダとしては優れているものの、時系列情報を埋め込めないため、単語の並びは無視されてしまう。そのため翻訳など自然言語タスクでは使用できないのが課題。

例えば画像を左右反転してもCNNは犬の画像を犬と理解してくれるが、左に犬がいたか右に犬がいたかモデルは理解する術を持たない。

ちなみにDetectionでも統合的な位置関係を"理解"しているわけではない(反応したboxに応じてメタ的に情報を出せるが)。

ネットワーク自体は空間的にフィルタを適応しているだけで左右に二匹いたというコンテクストはなく、画像の意味理解を始め文章の翻訳や意味理解は難しい。

f:id:aru47:20200818173826p:plain

Attention? Attention!より

自然言語理解(NLP)では周囲のデータ(word)に対する重み付を計算する機構が必要で例えば文章理解にはeatは周囲のfoodを表すwordと高い重みをもたせたい。

この役割を果たすのがAttention であり、CV用DNNにはなかった機構である。

従来のCVでもこのような画像中の”意味理解”を果たすために、CNN+RNNを用いてキャプションを作成している。 RNNはリカレント(循環性)を持っており、単語間の特徴や単語の入力順までも含めて特徴量を抽出することができる。 Show, Attend and Tell: Neural Image Caption Generation with Visual Attention

Seq2Seqの登場と課題

f:id:aru47:20200818173923p:plain
20

Attention? Attention!より

2014年に提案された技術であり、時系列を加味したオートエンコーダのようなイメージ。

例えばエンコーダは文章を固定長のcontext vectorに変換し、デコーダはcontext vectorを別ドメイン(中国語)の文章に変換する。

NN自体のモジュールとしてRNN, LSTM, GatedRNNなどが使われるが本記事では深追いはしない。 過去と現在といった時系列のデータを上手く扱えるようにしたNNモジュールと思えば良い。

ゼロから作るディープラーニング2に丁寧に原理や実装方法が載っているので興味のある人は読んでみて下さい。

amzn.to

Seq2SeqやRNNの課題:

RNNは順次にデータを入力し処理するが長い文章を入力すると最初の方の文章について”忘れてしまい”、文章全体の理解は難しい。

  • Context Vecは単一の固定長ベクトルであり、保持できる情報量は限りあり
  • これを長くすると今度は短文の理解が難しくなるというトレードオフ

隠れ層で"前状態"の特徴も伝搬させていくものの、限度がやはりあり例えばブログ記事全て(1000単語)などを理解させるのは困難。

Attentionとは

f:id:aru47:20200818174112p:plain

Self-Attention Mechanisms in Natural Language Processing | by Alibaba Cloud | Mediumより

f:id:aru47:20200818185613p:plain

Attention? Attention!より

Attentionは人間の目の仕組み(Visual Attention)からインスパイアされたNNの機構である。

考え方:

目はあるパーツを見ている時に共に周囲の関連する画像部位を"低解像度"で見ることで、画像全体の理解を図る。

この"低解像度"でみることはある重みで周辺データを加算することと同義であり、この仕組をDNNに取り入れた。

例えば画像の柴犬の耳(黄色箱)を見ているとき、人間の目は周囲の耳や鼻も低解像度で見ており、犬全体のコンテクストも理解している。

Attention(初期)の実装

Attention初出:Neural Machine Translation by Jointly Learning to Align and Translate

f:id:aru47:20200818174132p:plain

RNNの各隠れ層の状態からContext Vectorに作用させるショートカットを作成している。

  • AttentionLayerがEncoderの各隠れ層から単純に全結合で情報を取り、Context Vecとさせる。
  • RNNのエンコーダ隠れ層Ht全てを利用し、ContextVec.を入力に依存した行列にする

そうすることで文章全体から特徴量を効率よく抽出可能。

デコーダはデコーダRNN出力+EncHtの特徴量両方を使い出力計算する。

Transformerとは

初出:Google, Attention is All you Need NLPで間違いなく最も有名な論文で初学者でも読みやすかった。

Googleが出すgame-changingな論文は(EfficientNetとか)とても読みやすい。 奇を衒わなくてもトップ学会に通るポテンシャルがあるからか。

従来モデル(seq2seqとRNN全般)の課題

f:id:aru47:20200818174319p:plain

RNN系NNモジュールは前ステートの状態を受け取らないと計算を始められない。

すると計算の並列化は本質的に難しく、計算が遅いのが最もクリティカルな課題。 (要はGPU計算効率が低く、たくさんのデータで学習できない)

例えばn入力=(x0, x1, x2, x3, .. xn)あったとき、RNNが隠れ層=(h0, h1, h2, h3..hn)を計算するためにはnステップが必要である上に例えばh2はh0とh1に依存し計算を並列化する事ができない。単純に言うと 計算時間がO(n) となってしまう。

バックプロパゲーションを間で切るというテクニック等で並列化・高速化もなされてきたが、シーケンスが長いと情報量は落ちてしまうし本質的な解決にはなっていなかった。

革命はなぜ起こったか?

f:id:aru47:20200818174545p:plain

私にいい考えがある

RNNをベースとした構造からTransformerではAttention(正確にはSelf-Attention)をベースとした構造に進化した。

f:id:aru47:20200819162049p:plain

Self-Attentionは入力ベクトルのみから出力を並列計算で導出可能で計算依存性がなく、GPU計算に向く。 十分に並列ハードウェアが使用可能ならば 計算時間はO(1) である。

またそもそもSelf-AttentionはRNN、CNNセルよりも計算する要素が少なく高速である。 n=10, d=512のタスクと考えると Self-Attentionの計算量は50倍少ない。 

f:id:aru47:20200819165338p:plain

実際Transformerの論文ではRNNベースの手法と同等精度に達するために必要な演算量は 100-1000倍少ない と報告している(そして計算時間は更に高速)。

このようなAttentionベースの構造、 Transformer をGoogleは提案し、NLP精度に革命を起こした。

DNNはそもそもデータを学習時に喰わせれば喰わせるほど精度を向上できる性質を持つ事を思い出したい。

Transformerというリカーレントな特性を持ちつつ並列化に向いたモジュールを提案することで、NLPモデルにより多くのデータで学習可能となりモデル精度が爆発的に向上したと自分は思ってます。

Transformerブロック図

f:id:aru47:20200818174702p:plain

まずはアーキテクチャとしての動作を見てみる

f:id:aru47:20200818174725p:plain

アーキテクチャ全体の概要はGPT-1論文の図がわかりやすい。

イメージとしてN入力に対しTransformerEncoderをそれぞれ計算し、N個の特徴量を抽出という点はSeq2SeqやRNNと変わらない。

しかしTransformerEncoderは依存関係がなく、GPUでN並列で計算が可能である点が革新的であった。N個のコアがあれば1回のステップでTransformerは計算が終了するため、RNNよりN倍計算が早い。

そしてデコーダにも前述した特徴量を入力し、エンコーダとほぼ同等に並列で高速計算が可能である。

Transformerブロックとしての動作

論文を見るに非常に単純で基本的にはSelfAttention→(ResidueとAdd)→FC→(ResidueとAdd)しているだけ。

PytorchのTransformerEncoderLayerソースコードを見てみよう。 https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/transformer.py

f:id:aru47:20200818174748p:plain

forwardはとてもシンプル。 それだけself_attentionが強力ということだろう。

ちなみに元論文ではこのエンコーダを6回Stackしている。

Self-Attentionとは

f:id:aru47:20200818175011p:plain

Transformerの要は間違いなくSelf-Attentionである。

self-attentionではあるベクトルX=(x0, x1, x2, x3, xn)が与えられた時、Xに対しまずクエリとキーを計算する。


q = W^{Q}X \\
k = W^{k}X

そしてアテンションαをクエリとキーの内積結果から算出する。このクエリとキーの演算によってXと重みクエリとキーの類似度を計算している。


\alpha = softmax(\frac{q^Tk}{\sqrt(d)})

そして最後にバリューを計算し、アテンションの結果を重み付けした値を得る


v = W^VX \\
X_o = \Sigma a v

TransformerにおけるSelf-Attentionは Scaled Dot-Product Attention という方式を利用。(他にもたくさん計算方法はあるが、現状はこのAttentionが寡占しているよう。)

計算はシンプルで以下である:

f:id:aru47:20200818175018p:plain

  • 計算のフロー: queryとkeyに対し内積をとり正規化後に非線形化(softmax)し、最後にvalueと内積を取る。

  • 復習: 式をみてもイメージが沸かない人は内積の意味を思い出そう。内積はベクトル同士の類似度を取ること。

  • 考え方: まず各keyそれぞれにおいてQueryのどこと類似性が高いか計算する。 Attentionのそもそもの考え方は入力(key)と類似度が高いデータを取り出したい、というものだ。 特にCNNのようにフィルタで計算し抽出するわけではなく、あくまで自己のデータ性質に注目し出力を作る。

Q K と計算した類似度の高い(注目するべき)領域をVと更に内積を取ることでデータを抽出しているイメージ。

このようにkey,query, valueが同一データから来ているため"self"-attentionと呼ぶ。 こんなにシンプルな仕組みが強力の結果を生むというのは驚きだ。

(うーん、よくわからん!となったら最初の柴犬の例を思い出してベクトル内積を復習してみると良いかも。)

重要な事はGPU的には内積演算は非常に効率よく処理することができ、Transformerの全てのブロックはGPU-friendlyであるという点だ。

MultiHeadAttention

f:id:aru47:20200818175041p:plain

実際にTransformerで使われるのはSelf-attentionを発展させたMulti-head-attentionである。 Self-Attentionを複雑にして表現力を増したものと考えると良い。

Self-Attentionと違い、Multi-head-attentionでは学習する重みが出てくる。

  • 考え方:

V,K,Q全てを全結合層でh次元に変換し、h個のAttention機構に入力する。

そしてh次元の出力をconcatし、全結合で元の次元に戻す。

h個に次元を拡張するため、multi-headと呼んでいると思う。

  • なぜこんな事を?:

" Multi-head attention allows the model to jointly attend to information from different representation subspaces at different positions "

感覚的にはこちらの方が表現力が増える、という事を元論文では記述しており元論文ではh=8を採用している。

Generative Pre-training (GPT-1)

OpenAI, "Improving Language Understanding by Generative Pre-Training"

f:id:aru47:20200819163316p:plain アーキテクチャ的にはTransformerを活用しているが、Transformerエンコーダのみの構造になりDecoderはなくなった。

どこがブレイクスルー?

学習方法にGenerative Pretraining、一種のsemi-supervised learningを活用することで大幅な精度向上が可能な事を示したのがブレイクスルーであった。Generative Pretrainingは後続のGoogle BERTといった研究にも大きなインパクトを与えた。

文章生成のリアルさが凄く、GPT-3では人間を騙せるレベルになり話題になった。

学習は大量のデータを活用するゆえに長いものの、一度Transformerをpretrainしてしまえばfine-tuningで様々なタスクに転用可能であることを示した。

Generative Pretrainingって?

DNNは大量のデータを食わせることで精度を増すが、大量のラベル付けデータを用意するのは難しい。

しかし自然言語ならば大量のラベルなしだが人間が記述した良質なデータは手に入る(Wikipediaなどを見てみよう)。

例えばこのような文章を収集したとする。

吾輩は猫である

そこで収集した文章にわざと穴あきを作り、その穴あきに何が入るかモデルを当てさせモデルを学習する。

吾輩は○である

このような学習方法(教師なし学習)により、文章データさえあれば 学習データをほぼ無尽蔵に生成できる

この手法をgenerative pre-trainingと呼ぶ。

f:id:aru47:20200819164159p:plain

計算効率を向上させたTransformerと無尽蔵の学習データを生成するGenerative Pre-trainingによりNLPモデル精度は凄まじい勢いで2018年から伸び始めた。。

f:id:aru47:20200819171012p:plain

GPTでは事前学習したTransformer自体はそのままに変えず、タスクによって入力方法や大枠のアーキテクチャを変更することで使い回す。

GPTは 教師なし学習でTransformerを学習(Pretrain)し、最後に貴重なラベル有りタスクに特化したデータで ファインチューニング するという現代NLPモデル学習の基礎を確立した。

例えば、大体KaggleのNLPコンペをやる時はGoogleが公開しているPretrained-BERTモデルをダウンロードし、タスクに応じてアーキテクチャを組むという流れだと思う。

Karpathy兄貴のMinGPT

github.com

ミニマルなGPTのコードが公開された。これを読むのがわかりやすいかもしれない。

GPT-1 vs GPT-3

アニキのREADMEによるとGPT-1とGPT-3の差は

GPT-3: 96 layers, 96 heads, with d_model of 12,288 (175B parameters).

GPT-1-like: 12 layers, 12 heads, d_model 768 (125M)

と1000倍ほどモデルサイズが異なる点であると思う。もちろん学習時間も1000倍かかる..

Bidirectional Encoder Representations from Transformers (BERT)

Google, BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

神論文2。みんなで読もう。

Transformerの課題

f:id:aru47:20200818174642p:plain

サイバトロン戦士、トランスフォーム!出動!!

Transformerは強力で高速なものの、時系列情報はあまり強く学習できない。

文章単体ならば強力にできるが、例えば数千単語に及ぶ文章の意味理解は難しい。

Bi-Directional Transformers

f:id:aru47:20200818175155p:plain

f:id:aru47:20200818175309p:plain

  • Transformerを双方向にしたものを提案。
  • Attention機構のようにエンコーダ隠れ層全てを使っている
  • Seq2SeqのDecoder機構は使わずに実装。

GPT-1で提案されたpretrainingを適応し、更に精度向上。

名前の由来。。

ELMoへのリスペクトからか、NLPはセサミストリートのキャラ名をもじったモデルが多い笑

Computer Visionへの応用~Vision Transformer(ViT)~

Vision Transformer 原題:An Image is Worth 16x16 Words: Transformers for Image Recognition at Scaleという研究で画像認識にTransformerを使いSota CNNと同等の精度を達成したことが話題になりました。

Vision Transformerの論文では画像を16x16の切れ端にcropし、それを"単語"として見てTransformerに入力します。 そこから論文のAn Image is Worth 16x16 Wordsというネーミングが来ています。画像も単語のように扱えるよ!というNLP→CVに技術が浸透した面白い例ですね。

f:id:aru47:20201014102704p:plain

Transformerのアーキテクチャは変えずに(ネットワーク自体はBERTとほぼ同等)、画像の入力方法を工夫することでCNNと同等のImageNet認識精度を達成するネットワークを実現したことから大きく話題になりました。

f:id:aru47:20201014102933p:plain

ViTの初段はConvolutionと同等の処理をしますが、次段以降は全てのImagePatchの情報を組み合わせ特徴量抽出を行うので従来CNNとは動作が大幅に異なります。 そのため今後もCNNでは到達できなかった精度やタスクを実現できる可能性はあり、今後の発展から目が話せません。

[追記]

CIFAR-10でViTを学習させてみました。

github.com

やはり巨大なデータセットでpretrainしないと性能は出ないようです(resnet18なら90%出るところ、ViTをスクラッチから学習すると80%程度しか出ない)。 GoogleのViTモデルで転移学習した場合、99%精度出ることが確認されてます。

KaggleのNLPコンペ

f:id:aru47:20200818175344p:plain

いきなりソリューション読んでもちんぷんかんぷんなのだ。

小学生並の感想ですが、NLPはfeatureエンジニアリングや前処理、後処理エンジニアリングが効く部分が多く、Tableコンペ強勢に人気ありGMが台頭するのかなと思ったり。

GMが多いコンペはいいコンペ。

Google Q&A

ぐちおさんのブログが素晴らしいので読んで下さい:huggingface:

guchio3.hatenablog.com

FacebookのRoBERTa、BARTなどが精度ー計算量のバランスが良く、たくさん使用されている

https://www.kaggle.com/c/google-quest-challenge/discussion/130047

huggingfaceという会社が出しているTransformer, BERTはTockenizerといったNLP前処理関数が使いやすく幅広い参加者に使われているみたい。

Tweetコンペ

これもぐちおさんのブログがとてもわかりやすいのでry

guchio3.hatenablog.com

とてもわかりやすく闇と光:huggingface:がまとまっていました。

上位解法を読むとデータ前処理、ラベル処理に芸術が隠されてそう。 (アノテーションずれしたデータを渡すのは正直どうかと思いますが)

一位解法

f:id:aru47:20200818175554p:plain

どうStackingしてるんだろう笑 Transformerの特徴量を1x1 CNNに渡し分類器を構成しているんだろうか

Jigsaw

読んでない。

Next Step

いきなりBERTに手を出してもろくなことにならなそうなのでゼロ作2を元にSeq2Seqを実装し、Transformer実装まで行きたいと思います。

参考文書

lilianweng.github.io

amzn.to

amzn.to

www.ogis-ri.co.jp

BERT入門

Self-Attention Mechanisms in Natural Language Processing

www.amazon.co.jp