need」という論文で発表され、自然言語処理界にブレイクスルーを巻き起こした深層学習モデルです。論文内では、英語→ドイツ語翻訳・英語→フランス語翻訳という二つの機械翻訳タスクによる性能評価が行われています。精度(Bleuスコア)訓練にかかるコストの少なさという両方の面で、Transformerはそれらの性能を上回りました。以降、Transformerをベースとした様々なモデルが提案されています。その例としては、BERT,XLNet,GPT-3といった近年のSoTAとされているモデルが挙げられます。need」内に掲載されているTransformerの構造の図を見てみましょうニューラル機械翻訳におけるTransformerはある時系列データを別の時系列データに変換する(Ex:日本語による文章を英語による文章に翻訳する)ようなタスクに用いられるEncoder-Decoder(seq2seq)の構造をしているという点ではRNN(LSTM,GRU)ベースのモデルと同じです。Transformerの最大の特徴はEncoder・DecoderのいずれにもRNNのような再帰計算を必要とする層が存在せず、その代わりとしてこの後説明するAttentionが用いられている点です。Transformerは自然言語処理のみならず、他の分野でも用いられる汎用性の高いモデルです。2022年現在、登場から約5年という月日が経過しているため、主要なディープラーニングフレームワークであるPyTorch・Tensorflowのいずれにも既に公式実装が存在しており、研究等を目的に実装する際はこれらを用いるのが一般的かと思います。作ることは理解することへの近道。いうことで、今回は取り組んだのはTransformerとTransformerを構成する層のスクラッチ実装です。本記事では、Transformerモデルを構成する各レイヤの理論的背景およびPyTorchによる実装を紹介していきます。今回はPyTorchの学習も兼ねているため、PyTorchLightningやIgniteのような便利ライブラリは使用せず、素のPyTorchのみで実装しています。以下は実装したリポジトリになります。https://github.com/YadaYuki/en_ja_translator_pytorch2.ディレクトリ構成概観それでは、早速実装を見ていきましょう。細部に注目する前に、プロジェクトの全体像を概観するため、ディレクトリ構成を見てみます。text/poetry.*という名前のファイルが存在することからわかる通り、ライブラリのパッケージ管理にはPoetryを採用しました。installコマンド一つで必要なライブラリを全てインストールし、仮想環境を作成することができる。開発時のみ必要なライブラリと開発時と本番環境の両方で必要なライブラリを一つのファイル(pyproject.toml)で定義できる。フォルダ構成としては、models以下に「Transformer本体」、layers以下に「Transformerを構成するレイヤー」という次章で説明する実装に該当するファイルが存在します。3.Transformerの実装それではプロジェクトの全体像が掴めたところで、Transformerを構成する各層およびTransformer本体の実装を見ていきましょう。Attention先ほども述べた通り、Transformerの最大の特徴はRNNのように訓練時の再帰計算を行わずに、Attention層を用いている点です。need」という論文のタイトルが物語っているように、AttentionはTransformerの中で最も重要な層です。ここでは、Transformerの核となる層であるAttention層について説明します。Attention層の目的は、入力ベクトルの各要素の重要度を算出し、それによって入力ベクトルを重み付けること。weightという値を算出します。weight」の積を計算することにより、入力ベクトルの中で正解ラベルを得る上で特に重要な要素を重み付けることが可能です。Attentionを定式化してみましょう。weightの計算です。Xの重要度を求めます。weight(Xの重要度)で入力Xを重み付けたものがAttentionの出力になります。weightを算出し、それにより入力データが重み付けられる」という演算の流れがわかるように、上のような式でAttention層での演算を表しました。本家の論文内ではAttentionの演算を、Q(クエリ),K(キー),V(バリュー)という3つの記号を用いて、Attention(Q,K,V)のように表しています。先程の\alpha(Q,X)XとAttention(Q,K,V)はいずれもAttentionの演算であるため、書き方が異なるだけで、やっていることは全く同じです。\alpha(Q,X)XをQ,K,Vによって表すと以下の通りになります。\alpha(Q,K)V\alpha(Q,X)Xは、すなわち、Attention(Q,K,V)におけるK,Vの両方がXであるということです。Attention(Q,X,X)ここで、\alpha(Q,X)XにおけるQとXが同じであるか、そうでないかによってAttentionを区別できます。attentionと呼びます。)という3通りのAttentionの使われ方が存在します。以上がAttention層の概要に関する説明です。本節では、Attention層が行う演算の概要とその目的について説明しました。weightをどのように算出すべきであるか?という点です(関数\alphaが何であるか?)。weightを算出するScaledDotProductAttentionという手法が採用されています。次の節で、その詳細を見ていきましょう。weight計算は、クエリQ(N{\times}D)と入力X(N{\times}D)を用いて、以下のような式で表すことができます。softmax(\frac{QX^T}{\sqrt{D}})質問応答や機械翻訳といったタスクにTransformerモデルで取り組んでいる場合、上の式におけるQ,Xはそれぞれ文章データを行列で表現したものです。扱うデータが「N個の単語を持つ文章を、D次元の単語分散表現で表現したデータ」であった場合、N{\times}Dというサイズを持つ行列となっています。そのため、この演算では、クエリQ内の各単語の分散表現と入力データX内の各単語の分散表現の内積を算出している、ということです。ベクトル同士の内積が大きいということは、向いている方向が近い、すなわち、ベクトル同士の類似度が高いということです(単語同士の類似度が高い)。ScaledDotProductAttentionに文章データを入力した場合、Q,X内の単語同士の類似度を、入力に対する重要性として重み付けていると解釈することが可能です。weightと入力Xの積を求めることで最終的な出力が得られます。従って、入力X,クエリQに対するScaledDotProductAttentionの出力は以下の式で表すことができます。ScaledDotProductAttentionのPyTorchによる実装を見てみましょう。Attention3.2節で、TransformerモデルはAttentionの計算方法としてScaledDotProductAttentionを採用していることを説明しました。Transformerで採用されているAttentionは単なるScaledDotProductAttentionではありません。Attentionという仕組みが採用されています。Attentionの概要図を見てみましょう。図中のh(ヘッド数)は並列実行するScaledDotProductAttentionの数を表します。Attentionでは、以下のような処理が行われます。Attention層に対する入力Q(N_{Q}\timesd_{model}),K(N\timesd_{model}),V(N\timesd_{model})をh(ヘッド数)の数だけ複製する複製した入力Q_i,K_i,V_i(i=1~h)を行列W_i^q(d_{model}\timesd_k),W_i^k(d_{model}\timesd_k),W_i^v(d_{model}\timesd_v)により、d_{model}→d_v,d_kへと線形変換する。して得られたQ_iW_i^q(N_Q\timesd_k),K_iW^k_i(N\timesd_k),V_iW^v_i(N\timesd_v)をh個存在するScaledDotProductAttentionへ入力する並列実行されたScaledDotProductAttentionから得られるh個の出力head(i=1~h,N\timesd_v)を結合(concat)し、行列O(N\timeshd_v)を得る。OW^OによりOをhd_v→d_{model}に線形変換し、得られた値が最終的な出力となる。定式化すると以下の通りになります。OW^Oそれでは、以上を踏まえて、実装を見ていきましょう。Encoding層では、系列データ内の各要素に、要素のデータ内における位置情報を付与する役割を担います。Encodingによって、各単語ベクトルに、その単語が文章内で何番目に登場するか?という情報を付与することが可能です。Transformerでは、以下の式で算出した値を固定値として、入力に加算します。cos(pos/10000^{2i/d_{model}})上の式で「なぜ位置情報を正しく付与することができるのか?」という点はここでは詳しく説明しませんが、それについては、こちらが参考になると思います。それでは、実装を見てみましょう。Networks(FFN)は2つの全結合層(Linear)を重ねただけの非常に単純な層です。1つ目の層からの出力に対する活性化関数にはReluを用いています。Networks(FFN)を定式化すると以下の通りです。b_2それでは、実装を見てみましょう。EncoderTransformerによる機械翻訳は、seq2seqの一種であるため、RNNベースのモデルと同様、EncoderDecoderの構造をしている、というのは既に説明しました。ここで紹介するのは、TransformerにおけるEncoder、すなわち、Transformerの概要図における以下の部分です。いうことで、ここでは、今までの紹介した層を組み合わせて、TransformerにおけるEncoderを実装します。)から構成されるTransformerEncoderBlock層を任意のN層重ね合わせた層という大きく分けて3つのパーツで構成されます。それでは実装を見てみましょう。DecoderEncoderに続き、Decoderも実装していきましょう。Decoderは、Transformerの概要図における以下の部分です。Networkから構成されます。実装は以下の通りです。Transformer(完成版)ここまでで、AttentionやPositionalEncoding,Encoder,DecoderといったTransformerを構成する各パーツの実装が完了しました。本章ではいよいよ、Transformer本体の実装に移ります。いっても、EncoderとDecoderの実装が完了しているので、モデルの実装自体はその二つを組み合わせるだけで、とてもシンプルです。それでは実装を見てみましょう。ここでは実装したTransformerを実際の機械翻訳データセットを使用して、学習していきます。モデルを学習するにあたって、モデルの学習を目的としたクラス・Trainerをtrain.pyに定義しました。LightningのAPIを参考にしており、以下の5つのメソッドを持ちます。テストデータによるモデルの検証を行うというわけで、実装を見てみましょう。Trainerクラスの実装は以下の通りです。test_bleu_scoresそれでは、モデルを学習していきます。データセットには日本語と英語の翻訳コーパスである京都フリー翻訳タスク(KFTT)を用いました。モデルの学習には以下のコマンドを実行します。train.py上のコマンドを実行すると、以下のような出力が得られます。8/4403....以下は1ステップ当たりの訓練ロスの推移をプロットしたグラフです。実行環境の都合で、「モデルを十分に訓練し、テストデータに対するBleuスコア・Lossで検証を行う」という検証はできませんでしたが(すみません🙇‍♂️)、1ステップあたりの訓練ロスが順調に減っていることから、正しく学習できていると推察されます。5.まとめ一つ一つ書いていたらとても長くなってしまいましたが、以上が実装になります!Transformerは登場して既に5年が経過しているので、モデル自体は新しいとは言い難いです。Transformer・Attention機構をベースとしています。そのため、Transformerは深層学習のトレンドを掴む上で、非常に重要なモデルといって良いでしょう。最近はライブラリが充実しており、Transformerのような複雑なモデルの実装に対するハードルもかなり下がっています。内部構造を理解しておくことは応用力を高める上で非常に重要だと思うので、興味のある方は今回のような車輪の再発明も是非試してみてください。Engineerバッジを贈って著者を応援しようバッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。Proよくある質問LinksX(Twitter)GitHubメディアキットLegal利用規約プライバシーポリシ。