[論文輪読] Unsupervised Translation of Programing Languages

8 min読了の目安(約7400字TECH技術記事

Unsupervised Translation of Programing Languages

  • 論文
  • Marie-Anne Lachaux, Baptiste Roziere, Lowik Chanussot, Guillaume Lample
  • Facebook AI Research
  • 2020/6/5 publish

概要

表題の論文を読んだのでそのまとめです.この論文の要点としては以下.

  • トランスコンパイラ(高級言語で書かれたプログラムを別の言語に翻訳)の学習手法を提案
  • 教師無し学習による機械翻訳を元にしてプログラムに適用
  • C++, Java, Pythonの関数を用意してトランスコンパイラで相互変換し,それぞれに対応するunit testで関数が正しく変換できているかを評価
  • 既存のルールベースなトランスコンパイラを凌駕する性能のトランスコンパイラが作成できた

注意
論文の中でTranscompilerとTranscoderがごっちゃになってるのか私が区別できていないかで表記の揺れor誤解が存在する可能性あり

1. Introduction

Transcompilerとは高級言語で書かれたプログラムを別の言語で書かれたプログラムに変換する翻訳機のこと.一般的に使用されているTranscompilerは一定のルールに従ってコードを置き換えるものらしい.それらには以下のような問題点を解決したいというのがこの論文のモチベーション

  • 可読性が低いコード(品質が低い)だったり,そもそも意図通りに動かすために手直しが必要(変換元・変換先の言語に対して深い理解が必要)となってしまう.
  • 結果として翻訳にかかる時間的・金銭的コストがかさんでしまう
  • 動的型付け to 静的型付けへの変換はもはや不可能

自然言語分野では教師無し学習を用いた翻訳機がルールベースモデルよりも優れた結果を残しているが,トランスコンパイルへの応用はまだされていない.また,教師あり学習をするほど対となるコード量が豊富ではない.(あとは多分マイナー言語や古い言語への対応とかが暗黙的に書かれている気がする)

開発コンセプトとして以下の点を挙げている.

  • ソース・ターゲット(変換前後)の言語に対する深い知見を必要としない
  • 多言語に容易に拡張可能であること
  • 完全では無いにしても翻訳のコスト・翻訳に必要な知見のレベルを下げること

この論文のcontributionは以下

  • 単一の言語で書かれたソースコードを他言語へ翻訳する手法を提案
  • TransCoderがそれぞれの言語の複雑なパターンを把握して他言語に翻訳することに成功したことを示す
  • 完全に教師無しの本手法が高度なプログラミング知識を活用した商用システムの性能を凌駕していることを示す
  • 3言語(C++, Java, Python)でかかれた852個の関数とそれを検証するunit testをリリース
  • トレーニングしたモデルを一般公開

2. Related work

割愛

X. 教師無し学習による機械翻訳(ちょっと脱線して釈迦に説法)

手法を理解するに当たって,教師無し学習による機械翻訳がわかっていないと理解不可能だったので調べた.要約すると教師無し学習によって一昔前の教師あり学習による機械翻訳に匹敵する性能を獲得できたよというもの.出てきた背景としては

  • Google翻訳がニューラル機械翻訳を導入して非常に流暢な翻訳を実現した
  • が,長文翻訳や同時通訳,マイナー言語対応などが研究課題として残されていた
  • 教師有り学習では大量の対訳文が必要となる(マイナー言語ほど用意できない)
  • 教師無し学習で実現出来れば全ての共通した構造を持つものであれば相互変換が可能になる(このあたりがソースコードの変換につながる)

手法のポイントとしては「逆翻訳」,「ノイズ除去」,そして「潜在表現の共有」.

逆翻訳

ソース文をターゲット文に翻訳し,ターゲット文を元のソース文に再翻訳する手法(逆翻訳後のソース文にとって元のソース文は教師信号となる).ただし,このままではターゲット文・再翻訳文は支離滅裂なまま(ノイズが多い状態)

プログラムだと意味の無いコードができあがる.当然再翻訳したコードも滅茶苦茶のハズ

ノイズ除去

再翻訳文からノイズ除去モデルを通して出てきたノイズ除去済みの再翻訳文が元の入力文に近づくように学習させる(ノイズ除去モデルと翻訳モデルは内部のパラメータを共有している)

プログラムで考えると再翻訳した滅茶苦茶なコードを元のコードになるように学習していく

潜在表現の共有

機械翻訳ではエンコーダ・デコーダモデルというモデルが利用されている.このモデルでは,エンコーダで元の文を潜在表現(中間言語とイメージしても良いかも)に変換し,デコーダで潜在表現を対象の言語に変換する.

日英翻訳の場合: 和文 -> [日本語エンコーダ] -> 潜在表現 -> [英語デコーダ] -> 英文

このモデルを一見すると逆翻訳・ノイズ除去をすることで精度の高い翻訳機が作れそうだが,実際には精度が上がらない.このモデルでは,各言語向けのエンコーダ・デコーダが同じ潜在表現の領域を使用している必要がある.
(個人的には,ソース言語の全単語がターゲット言語側で完璧に対となる単語が存在していることがこの潜在表現の領域が一致する事と解釈している.あっているかは不明.)

各言語のエンコーダ・デコーダが使用する潜在表現の領域が異なるために生じているので,各言語向けエンコーダデコーダの代わりに,共通エンコーダ・デコーダを導入する.どの言語からorどの言語へ翻訳するかは最初に識別子を渡すことで設定するらしい.(詳細な実装は調べてない)
これによって各言語由来の潜在表現が全て同じ領域にマップされることになる

3. Model

モデル

3.1 Cross Programing Language Modelのプリトレーニング

同じような意味を持つシーケンス(コード)は言語に寄らず潜在表現の同じ領域にマップされる.まずはコードをトークン化していく(cross-lingual word representations).この手順では複数の言語の単語をembeddingsと呼ばれる単語を特徴量として取り扱うためのベクトルに変換する.

単語だけで無く文の単位でベクトル化すると精度が向上するとの話が書いてあるが,このあたりはよく分かっていない.(そもそも手法として取り入れているのかどうかわかってない.一般的な機械翻訳の話の可能性大)

次にXLM(Cross-lingual Language Model)を単一言語のソースコードをデータセットとしてBertの事前学習手法を使用して学習させていく.(この論文で使用するのはソースの一部をマスクして元を推測させるフェーズのみおこなうっぽい)

得られるモデルの言語横断的な特性(と言うより言語によらない特性?)は言語間に存在する共通のトークン(アンカーポイント)に由来する.英仏翻訳などを考えると,数字や都市名,人名などがこれに当たる.
プログラミング言語で考えるとコードに出現するfor, while, if, tryなどのキーワードや数字,数学記号,英語の文字列(コメントや表示する文字列のところ)がこれに当たる

学習では各ステップで入力のトークンをランダムにマスクして文脈から元トークンを推測させてモデルを学習する.言語間で共有のモデル(エンコーダ)を作るので,バッチ毎に異なる言語を入力する.
このフェーズで学習できるのはエンコーダ部分.

3.2 Denoising auto-encoding

このフェーズではデコーダを学習する.
ランダムにマスク・削除・置換したコードをわざと入力して元のコードを予測させる.(Denoising auto-encoderと呼ばれる手法)

わざとノイズを入れる目的としては,(ノイズ除去というフェーズを実装しているだけだろうがイメージとして)

  • 潜在表現から出力コードに不要な表現や不足している表現(ノイズ)を除去or補完する能力を獲得させる
  • ノイズに対するロバスト性の獲得とも見れる?ノイジーなコードでモデルを学習させたときの逆翻訳に役立つ

デコーダの入力の最初には出力言語を特定するための特殊なトークンを使用する.

テスト時はPythonをモデルにエンコード,C++にデコードした.C++への翻訳はモデルの"cross-linguality"に依存する.エンコーダがPythonの関数とC++の対訳を同じ潜在表現にマップしていればデコーダーはC++の対訳を生成できる(この辺が言っているのは上の方で書いたイメージを解説してるだけかな)

3.3 Back-translation

3.1, 3.2の学習のみで翻訳機能を成立させるには十分だが,このままではクオリティが低い.(モデルとしてどのような出力が期待されるかについての学習ができていない.この状態だとクソコードが吐かれる)

なので,逆翻訳を用いて翻訳のクオリティを向上させる.ある言語のコードをTransCoderで別言語に変換し,変換したものを再度TransCoderに入力して元言語に再翻訳させる.入力したコードが教師信号となり,この誤差を最小化するように学習する.

4. Experiments

4.1 Training details

  • 6層・8Attentionヘッドを持つトランスフォーマーを使用
  • モデルの次元は1024
  • シングルエンコーダ・シングルでコーダを全ての言語に対して使用
  • XLMプリトレーニングではC++,Java,Pythonのバッチを使用.バッチは512のトークンからなる32のシーケンスで構成される
  • denoising auto-encodingと逆翻訳では約6000のトークンを持つバッチを使用
  • 学習率10^-4で,Adam optimizerを使ってTransCoderを最適化
  • PyTorchを使って実装
  • V100を32基使って学習
  • 高速化のため半制度浮動小数点数を使用

4.2 Training data

  • Google BigQueryが使えるGitHubのパブリックリポジトリ(280万リポジトリ)をライセンス的にOKなものだけ使用.
  • リポジトリまるごとが理想だが,今回は関数単位で使用する
  • ソースに含まれるコメントはそのままにする(そのままにすることでアンカーポイントが増えてパフォーマンスが向上する)

4.3 Preprocessing

プリプロセスとして関数(と言うより関数のコード?)をトークン化する.トークナイザーは以下のものを使用する.コード無いの無意味な変更(半角スペースの増減など)がトークン化されたシーケンスに影響を与えないようにする.

  • Java: javalang tokenizer
  • Python: Pythonの標準ライブラリにあるtokenizer
  • C++: clang tokenizer

4.4 Evaluation

データセット

  • 852個の関数(それぞれに対してC++, Java, Pythonバージョンを用意)
  • 各言語で書かれたそれぞれの関数に対するunit test(評価用)

評価を行うためのデータセットはGeeksforGeeksから作る.(コードのサンプルはappendixにある)
Geeksforgeeksはコード学習のためのサイトっぽい.課題に対して複数の言語でアプローチしている教育的な内容らしく,対訳として評価に用いるのに丁度良いと判断したのかな.

評価の指標としてはBLEUスコア・リファレンスマッチ(単純なコード一致度)・計算精度(論文で提案されている評価指標)を用いる.
とは言っても結果で即効BLEUスコアとリファレンスマッチは使えねぇよとバッサリ切られている.課題の特性上意図通りにプログラムが動作することが最優先なので,出力する結果が間違っていたり,コンパイル結果が大きく異なっていたらsyntaxが類似していても無意味.反対にsyntaxが異なっていても出力結果がだたしければ良い.

この論文で提案されている計算精度という指標は,評価に用いるデータセットとなる関数それぞれにunit testを用意しておき,翻訳後のコードがそのunit testをパスするかどうかで評価するという単純なもの.(unit testの作り方についてはappendixにある)

最後のパラグラフに「TransCoderはデコード時にビーム探索をしつつ複数の翻訳文を生成する」とあるが,この辺が突然出てきてよく分かってない

Evaluation metric differences

各評価指標での精度比較

  • リファレンスマッチはかなり低い値を出しているが,unit testのパス率はそれよりずっと高い値になっている
  • BLEUスコアはそもそもunit testのパス率と相関がない
  • リファレンスマッチとBLEUスコアは評価に適さない

Beam search decoding

精度比較

Beamサーチのパラメータで比較を行っている(が,理解できてないのでこちらも理解できない)

既存品との比較

ルールベースの翻訳機と比較.前節のBaselinesがルールベースの翻訳機の結果

  • Java to Python: j2py
  • C++ to Python: Tangible Software Solutions製のシェアウェア?

TransCoderは特に標準ライブラリが含まれるコードを翻訳するのが上手らしい.ルールベースの場合,標準ライブラリ周りは手動で書き換える必要があるということを強調している.

4.6 考察

TransCoderは各言語のシンタックスやデータ構造,関数,ライブラリをよく理解している.例えば,以下のような変換をうまく変換している

C++,Java

X ? A : B
Python

if X then A else B

一方で,型まわりで翻訳に失敗しやすいっぽい.(とは言ってもルールベースでもできてないので,この手法特有の欠点というわけではなさそう).

失敗例

例と同様の失敗はPythonとの翻訳でも起きる.

所感

  • 思ったよりずっと綺麗なコードが出力されているなぁという感覚.(大学時代に研究でC++のコードを自動で生成するソフトを先人の引き継ぎで開発していたのだが,出力されたコードはgotoの乱舞で見れたものでは無かった・・・・黒歴史)
  • 自然言語に出てくる完全な分類が難しい複雑な言語体系がない分,課題としては簡単なのかも知れない.
  • とはいえ,現段階ではまだ関数単位なので,プロジェクトのレベルにするとどこまで実用レベルなのかは気になるところ.

参考

  • 日本語の解説記事
    • 既に他の方が日本語解説記事を執筆されていました.評価周りはこちらの方が詳しい&正確そう.
  • arXivTimes
    • こんなものがあるんですねっていうだけのリンク(今回の論文と直接関係は無い)