stMind

about Tech, Computer vision and Machine learning

pytorchとtensorflowのチュートリアル写経で見つけた小さなバグをPull Requestしてマージされるまで

正確にいうと、torchvisionとTensorflow Textのチュートリアル

torchvisionの方は、ビデオファイルを読み込んでビデオフレームとオーディオフレームを返すVideo APIのチュートリアルで、ビデオのptsはvideo_ptsにappendして、オーディオのptsはaudio_ptsにappendするべきところが、オーディオの方もvideo_ptsにappendされていた。単にvideo_ptsをaudio_ptsに置き換えるだけのPRを作成して、3日ほどでマージされました。

github.com

Tensorflow Textの方は、Transformerを使った機械翻訳のチュートリアルで、ポルトガル語を処理するTokenizerがtokenizers.enとなっていたので、tokenizers.ptと修正してPRを作成。こちらも2日ほどでマージされました。現在、このチュートリアルは結構書き換えられていて、修正した箇所を含むブロックは無くなっています:)

github.com

今回はチュートリアル写経の副産物としてのPRでしたが、このような貢献の形もあるよという紹介でした。

stackoverflowで今年回答した内容のまとめ

今年の目標の一つとして、stackoverflowで回答を増やそうと考えていたのだけど、現時点で11件ほど行なうことが出来たので、ここでまとめておこうと思う。回答がAcceptされるとベターだとは思うものの、回答後のリアクションは質問者次第なので、コミュニティに貢献できたと考えれば良いかな。

一応、スタッツとしては以下のような感じ。

  • Accepted : 1
  • Upvote : 6

カテゴリとしては、Tutorialなどをやっていたこともあって、transformer系の質問への回答が多めになった。自分自身の勉強にもなるし、コミュニティ貢献としてstackoverflowの回答をゆるりと続けていこうと思う。

1. Masking layer vs attention_mask parameter in MultiHeadAttention

Acceptされた回答。MultiHeadAttentionを使うときattention_maskでマスク指定しているが、MHAの前にMaskingレイヤーを使ったらattention_maskは不要なのか?それとも両方使わないといけないのか?という質問。 調べてみると、Maskingレイヤーで生成されたマスクは、対応しているレイヤーでは入力に対応するマスクの自動取得して使う機能が入っているが、MHAはTF2.10.0で対応ということだったので、そのように回答。

stackoverflow.com

2. Can't install tensorflow-io on m1

一番Upvoteされた回答。m1 macにtensorflow-ioに入れるには?ということで、git cloneしてwheelをローカルで作ってpip installする方法を回答。結構、こういうのが一番困っているのかもしれない。

stackoverflow.com

3. Meaning of the array returned by the activation function GELU (Vision Transformer)

これも一つUpvoteが付いた回答。ViTで推論するとき、GELUの出力の最も高い値をつかって分類しているが問題ないか?という質問。そのまま使うか、確率的な値が必要であればsoftmaxを使うこともできると回答。

stackoverflow.com

4. Transformer with multi input

二つの異なる系列データに対して、Cross Attentionを行なう方法を知りたいという質問。具体的なサンプルはMultiHeadAttentionの公式APIドキュメントにあったので、それを回答。

stackoverflow.com

5. Visualizing ViT Attention maps after fine tuning on medical dataset

vit-kerasを使って独自モデルのAttentionを可視化したいけどエラーになる、attention_mapへの引数の渡し方が間違っている様子だったので、修正方法を回答。

stackoverflow.com

6. Patch Encoder for ViT implementation in Python

PatchEncoderクラスで実行されている処理について知りたいということだったので、Dense適用、position embeddingの追加をcifar10を例に回答。

stackoverflow.com

7. ViVIT PyTorch: RuntimeError: multi-target not supported at /pytorch/aten/src/THCUNN/generic/ClassNLLCriterion.cu:15

ViViTを実行した時に、RuntimeError: multi-target not supported at /pytorch/aten/src/THCUNN/generic/ClassNLLCriterion.cu:15が出るようなので調べてみて、Pytorchでは1.10.0から対応してることと、実際に実行できるcolabのノートを回答。

stackoverflow.com

8. In transformers of ViT model, last_hidden_state is not equal to hidden_states[-1]

huggingfaceのViTで、last_hidden_stateとhidden_states[-1]は同じに思うけれど、実際の値が異なるのはなぜか?という質問。コードを読んでみて、last_hidden_stateの方はLayernormが適用されている点が違いで、実際にhidden_states[-1]にLayernormを適用すれば同じ値になることを確認してcolabのノートと合わせて回答。

stackoverflow.com

9. Vision Transformer models in vit-keras

vit-kerasでvit-b32とvit-b16以外の使用可能なモデルと入力画像サイズを知りたいということで、調べて回答。

stackoverflow.com

10. no fine_tune_checkpoint field in pipeline.config

TF OD APIでpipeline.configのfine_tune_checkpointの指定方法を回答。

stackoverflow.com

11. Google Kickstart 2014 Round D Sort a scrambled itinerary - Do I need to bring the input in a ready-to-use array format?

これは少し違う話で、GoogleのCodingコンペティションKickstartで入力をどうやって受け取ればいいのか?ということだったので、サンプルを提示して回答。

stackoverflow.com

DALL-E 2, Imagen, Parti

テキストから画像を生成するモデルが話題ですが、代表的なモデルであるDALL-E2とImagen、Partiのアーキテクチャを比較しているTwitterのスレッドを紹介。

以下は、アーキテクチャの構成ブロックの簡単な比較を表にまとめたもの。

DALL-E2 Imagen Parti
Text Encoder CLIP T5-XXL Transformer Encoder
Text Embeddings to Image (64x64 or 256x256) Diffusion Model x 2 (Prior + Decoder) Diffusion Model Transformer Decoder + ViT-VQGAN
Upsample the Image (256x256->1024x1024) Diffusion Model x 2 Diffusion Model x 2 Convolution

それぞれの論文は画像がたくさん含まれてはいますが、DALL-E2が27ページ、Imagenが46ページ、Partiは49ページもあって、読むぞ!とは気軽に言えない分量ですね...

TD OD APIでデータ拡張のオプションを追加する

データ拡張のオプションは、TrainConfigの中でdata_augmentation_optionsとして定義されている。

data_augmentation_optionsは、repeatedフィールドで、PreprocessingStepに指定されているデータ拡張を任意の数だけ指定することができる。

// Message for configuring DetectionModel training jobs (train.py).
// Next id: 31
message TrainConfig {
  // Effective batch size to use for training.
  // For TPU (or sync SGD jobs), the batch size per core (or GPU) is going to be
  // `batch_size` / number of cores (or `batch_size` / number of GPUs).
  optional uint32 batch_size = 1 [default=32];

  // Data augmentation options.
  repeated PreprocessingStep data_augmentation_options = 2;

例えば、random_horizontal_flipは、水平方向の反転を行う処理で、デフォルトでは50%の確率で行われる。

pipeline.config内では、フォーマットに従って使いたい拡張を指定すれば良い。

train_config: {
  ...
  data_augmentation_options {
    random_horizontal_flip {
    }
   random_image_scale {
       min_scale_ratio: 0.9
       max_scale_ratio: 1.1
    }
  }
}

一方、ファイル編集ではなくコードの中で直接データ拡張を追加したい場合には、以下の様に行う。

例えば、ssd_resnet50_v1_fpn_640x640_coco17_tpu-8のrandom_horizontal_flipとrandom_crop_imageに対して、random_image_scaleを追加する場合。

from google.protobuf import text_format

from object_detection.builders import preprocessor_builder
from object_detection.core import preprocessor
from object_detection.protos import preprocessor_pb2
from object_detection.utils import config_util

configs = config_util.get_configs_from_pipeline_file(/path/to/pipeline_config)
train_config = configs['train_config']

# Random image scale
preprocessor_text_proto = """
random_image_scale {
  min_scale_ratio: 0.8
  max_scale_ratio: 2.2
}
"""
preprocessor_proto = preprocessor_pb2.PreprocessingStep()
text_format.Merge(preprocessor_text_proto, preprocessor_proto)
train_config.data_augmentation_options.append(preprocessor_proto)

データ拡張オプションが追加されて、3つの処理が指定されている状態。

print(train_config.data_augmentation_options)
"""
[random_horizontal_flip {
}
, random_crop_image {
  min_object_covered: 0.0
  min_aspect_ratio: 0.75
  max_aspect_ratio: 3.0
  min_area: 0.75
  max_area: 1.0
  overlap_thresh: 0.0
}
, random_image_scale {
  min_scale_ratio: 0.800000011920929
  max_scale_ratio: 2.200000047683716
}
]
"""

参考

discuss.tensorflow.org

BERTの概要を掴む

一回目のMulti Head Attention、二回目のGPTに続いて、三回目はBERT。

Multi Head Attentionの概要を掴む - stMind

GPTの概要を掴む - stMind

以下、メモを残す。

3/n. Bi-directional

GPTでは、未来の単語の予測を目的として、言語モデルをpre-train出来ることを確認してきた。BERTでは、未来の単語の予測の代わりに、空欄を埋める目的を使用する。GPTと異なり、過去と未来のトークンを同時に見るため、双方向と呼ばれる。

4/n. BERT

BERTはどのように動作するか?アーキテクチャは以下の通り。GPTと二つの項目を除いて、ほぼ同じように動作する。一つ目の違いは、Causalマスクの代わりにランダムマスクを使うこと、二つ目はシーケンスの先頭に[CLS]を追加すること。この[CLS]トークンとは何か?

5/n. [CLS]トーク

例えば、Sentiment Analysisを行いたいとする。Transformerの出力shapeは [B, T, D]であり、これを[B, D]に圧縮し、分類器に入力する集約的な表現にしたい。最初に思いつく方法として、Tについて平均をとるのはどうか?

6/n. Aggregate representation

これでも機能するが、全てのトークンは分類に等しく有用であることを想定している。Attentionは、関連性に基づいてトークンを重みづけすることが重要だったはず。入力に新しいトークンを追加して、他のトークンをAttentionで集約したらどうか?それが[CLS]。

7/n. 分類器への入力としての[CLS]

分類タスクに対してBERTをFinetuneする場合、[CLS]トークンの最後の隠れ状態を分類器への入力として使用する。この時、MLPはshapeが[B, D]の隠れ状態を[B, クラス数]の出力に変換する。

8/n. [MASK]と[SEP]

BERTでは、[CLS]の他にも特別なトークンがある。マスクされたトークンを置き換える[MASK]と、センテンスの区切りとなる[SEP]。Tokenizationはデータの読み込み時に行われ、その後、positional embeddingが加えられる。

9/n. Transformer block

BERTがランダムマスク、GPTがCausalマスクを使用する違いはあるが、Transformer blockの実装はほぼ同じである。

10/n. generate_random_mask, mlm_loss

BERTでは、pretrainのためにデータのバッチを渡すたびに、新しいランダムなマスクをサンプリングする。これは、GPU上で直接マスクを作成すれば、効率的に行うことができる。以下が、マスクの作成とmasked language modelingの目的の実装。

11/n. tl;dr

BERT=GPT、ただしランダムマスクと他の特別なトークンを使用する違いがある。Transformerはとてもシンプルで汎用的であることは驚きである。

次回は、コンピュータビジョンにTransformerを適用したViTとMAE lossについて。

GPTの概要を掴む

前回は、Multi Head Attentionに関するTwitterの一連のスレッドを紹介した。

Multi Head Attentionの概要を掴む - stMind

今回はGPTについて。

以下、前回同様に自分が理解したメモを残す。

2/n. Multi Head Attentionの振り返り

Attentionは、ネットワークが入力に含まれる全ての単語とその関係を捉えることを可能にする。結果として、ネットワークはその目的を最適化するための最も重要な単語に注意を向ける様になる。

3/n. MHAの最適化

これまでのところ、MHAが最適化する目的を定義していない。GPTでは、非常にシンプルな、次の単語の予測というunsupervisedな目的を用いる。直前までの単語が与えられたら、次の単語を予測する。この目的であればラベルは不要なので、unsupervisedと呼ばれる。

4/n. Causal構造

未来の単語を予測する場合には、直前までの単語だけ参照するCausal構造を強制する必要がある。Causal Attention行列において、0は「関係性なし」を意味し、現在の単語と未来の単語間のAttentionは全て0にする必要がある。そのために、Weights行列( QK^{T})において、未来の単語を-infとする。

5/n. Causal Attention

Weights行列にSoftmaxを適用した後で、未来の単語をマスクして0にすると正規化されなくなる。そのため、Weights行列で未来の単語を-infにしてからSoftmaxを適用する。

6/n. Masked Causal Attention

Masked Causal AttentionがGPTのメインアイデア。GPTのTransformer BlockはMHA→LayerNorm→MLP→LayerNrom。入力shapeは(B, T, D_in)、出力shapeは(B, T, D_out)で、大抵はD_out=D_inとなる。

7/n. Loss

GPTの目的は次の単語の予測であった。英語には約100万語あり、文字通りに単語を予測する場合、100万クラスの分類をすることになる。GPTが最適化するロスを記述する。

loss = cross_entropy(preds, targets) # (B, T, 1e-7)

8/n. Tokens

ロスにおけるクラス数を削減するために、トークンを用いる。トークンは文字とベクトル間のmapで、例えばアルファベットの文字は26個の一意なベクトルで表すことができる(one-hot ベクトル)。文字列からユニークなベクトルへの変換はトークン化(Tokenization)と呼ばれる。

9/n. Byte Pair Encoding

GPTでは、頻度の高い文字グループ(ペア)をトークンにするByte Pair Encoding(BPE)を用いる。以下は、Attention is not too shabbyにBPEを適用して、11個のユニークなトークンになった例(ユニークなCharは12個)。

10/n. The order of words

最後の、しかし重要な問題は、今のモデルには語順を知る方法がないということ。

11/n. Positional tokens

語順をエンコードするために、Positional tokensを使用する。文字トークンと同じように、位置をユニークなベクトルで表す。文字と位置のトークンをそれぞれ線形層で変換した後、それらを足し合わせる。その後、Transformerブロックに入力される。

12/n. 実装

Multi Head Attentionの概要を掴む

DeepMindのResearch Scientistの方がツイートしていたMulti Head Attentionのスレッドの紹介。 全部で12個。英語だけど、日本語に翻訳すれば10分くらいで読めるし、コードサンプルと図もあって短い時間でMHAの概要が掴めると感じた。

以下は、自分が理解した内容のメモ書き。

2/n. Attentionとは

例として、sentiment analysisをしたいとする。「Attention is not too shabby.」 shabbyはネガティブを示唆しているけれど、not shabbyであればネガティブではなくポジティブ。正しく分類するためには、文中の全ての単語を考慮しないといけない。

3/n. 全ての単語を考慮する

全ての単語を考慮する最もシンプルな方法は、全ての単語をネットワークに入力すること。これで十分か?というと、そうではない。各単語を考慮するだけでなく、他の単語との関係も理解しないといけない。つまり、notはshabbyに注意を向けているということが重要。そこで出てくるのが、query, key, value(Q,K,V)。

4/n. Value

単語を線形層に通し、そこから得られた出力をValueと呼ぶ。

words.shape # (T,in_dim)
values = Linear(words)
values.shape # (T, D)

では、Value同士の関係をどの様にエンコードするか?それぞれのValueを混合(sumをする)することで、関係を見ることができる。ただし、これには問題がある。

out = np.ones((T, T)) @ values # (T, T) @ (T, D)
out.shape # (T, D)

5. 問題

単純な総和の問題は、全ての関係が等しいと想定していること。isとtoo、notとshabbyでいえば、明らかに後者の方が感情分類に重要。

6. QueryとKey

全てが等しい関係ではなく、word_jに対してword_iがどれだけ有用かを表す様にしたい。そこで、Valueと同じように単語を線形層に通して得られるQueryとKeyを導入する。QueryとKeyから求めた重みWeightsにおいて、w_ijはi番目のQueryに対して、j番目のKeyの間の内積に比例した値になっているはず。

queries.shape # (T, D)
keys.shape # (T, D)
weights = queries @ keys.T # (T, T)

7. Rescaling

QueryとKeyから得られたWeightsの各要素を \sqrt{D}でrescale。

8. Single Head Attention

Weightsの列ベクトルについてSoftmaxを適用して正規化する。直感的には、Qは単語Kに対してのどの程度有用かという質問で、内積が高いということは非常に有用、逆に内積が低い時はあまり役に立たないということ。これがAttention。

attention = softmax(Q @ K.T / sqrt(D), dim=1) @ Value # [T, D] = [T, T] @ [T, D]

9. Single Head Self Attention

10. Why Multi Head

なぜMulti Headなのか?Single Headだと学習データにオーバーフィットするかもしれない。過学習対策の一般的な戦略であるアンサンブルで、複数のAttentionによりロバストな結果を獲得する。(Multi Head Attentionは、Single Head Attentionの[T, D]をN個連結したもので、[T, NxD])

11. Multi Head Self Attention

forwardの入力xは、[T, D]をN個分concatした[B, T, ND](ただし、Bはバッチサイズで、ND = C)と想定している。