astnn_icse2019.pdf (xuwang.tech)
概要
機械学習技術を利用してプログラムを分析することは多くの関心を集めています。1つの重要な問題は、後続の分析のためにコード断片をうまく表現する方法です。従来の情報検索ベースの方法は、しばしばプログラムを自然言語テキストとして扱い、ソースコードの重要な意味情報を見逃す可能性があります。最近の最先端の研究では、抽象構文木(AST)ベースのニューラルモデルがソースコードをより良く表現できることが示されています。ただし、ASTのサイズは通常大きく、既存のモデルは長期依存性の問題に陥りやすいです。本論文では、ソースコード表現のための新しいASTベースのニューラルネットワーク(ASTNN)を提案します。AST全体で作業する既存のモデルとは異なり、ASTNNは各大きなASTを一連の小さな文の木に分割し、文の木をキャプチャしてベクトルにエンコードすることで、文のレキシカルおよび構文的な知識を取得します。文のベクトルのシーケンスに基づいて、双方向RNNモデルを使用して文の自然さを活用し、最終的にコード断片のベクトル表現を生成します。私たちは、ニューラルネットワークに基づくソースコード表現方法を、2つの一般的なプログラム理解タスクに適用しました:ソースコード分類とコードクローン検出。両方のタスクの実験結果は、私たちのモデルが最先端のアプローチよりも優れていることを示しています。
はじめに
多くのソフトウェアエンジニアリング手法、例えばソースコード分類[1]、[2]、コードクローン検出[3]、[4]、[5]、[6]、欠陥予測[7]、[8]、およびコード要約[9]、[10]が提案されており、ソフトウェア開発と保守を改善するために用いられています。これらの手法全てに共通する主な課題は、ソースコードをどのように表現するか、つまりソースコードに埋め込まれた構文的および意味的情報を効果的に捉える方法です。
情報検索(IR)などの従来のアプローチでは、通常、コード断片を自然言語テキストとして扱い、トークンに基づいてモデル化します。たとえば、プログラムは、コードクローン検出[3]、[4]、バグの特定[11]、およびコード著者分類[1]のために、トークンのシーケンスまたはトークンの袋で表現されます。さらに、多くの研究者がソースコードの分析に潜在的意味インデキシング(LSI)[12]や潜在ディリクレ配分(LDA)[13]を使用しています。ただし、これらのアプローチの共通の問題は、基礎となるコーパス(つまり、ソースコード)が自然言語テキストで構成されていると仮定していることです。コード断片は平文と共通点があるにもかかわらず、より豊かで明示的な構造情報を持つため、テキストベースやトークンベースの方法で単純に取り扱うべきではありません。
最近の研究[2]、[5]、[6]は、構文的知識がソースコードのモデリングにおいてより大きな貢献をし、従来のトークンベースの方法よりも優れた表現を得ることを強力な証拠として提供しています。これらのアプローチは、抽象構文木(AST)と再帰型ニューラルネットワーク(RvNN)[5]、ツリーベースのCNN [2]、またはツリーLSTM [6]を組み合わせて、ASTの葉(識別子など)と構文的情報(WhileStatementなど)の両方を捉えます。このようなASTベースのニューラルモデルは効果的ですが、2つの主要な制限があります。まず、NLPの長文と同様に、これらのツリーベースのニューラルモデルも、訓練中に勾配が消失しやすいという勾配消失問題に弱いです、特にツリーが非常に大きく深い場合 [19]、[20]、[21]。たとえば、私たちの実験(セクションV)によると、CおよびJavaの一般的なコード断片のASTの最大ノード数/深さは、それぞれ7,027/76および15,217/192です。その結果、ボトムアップでAST全体をトラバースおよびエンコードする方法 [5]、[6]や、スライディングウィンドウ技術 [2]を使用すると、長期的なコンテキスト情報が失われる可能性があります [19]、[22];第二に、これらのアプローチはASTを変換するか、単純化と効率化のためにASTを完全な二分木として直接扱いますが、これによりソースコードの元の構文構造が破壊され、ASTがより深くなります。変換されたより深いASTは、ニューラルモデルがより現実的で複雑な意味を捉える能力をさらに弱めます [23]。
上記のASTベースのニューラルネットワークの制限を克服するための1つの解決策は、明示的な(長期的な)制御フローとデータ依存グラフを導入し、ソースコードを表現するためにグラフ埋め込み技術[24]を使用することです。たとえば、最近の研究では、遠隔の場所で同じ変数や関数によって誘導される長距離の依存関係を考慮しています[25]。別の研究では、コード断片の制御フローグラフ(CFGs)を直接構築します[26]。しかし、上記の研究に示されているように、正確で手順間のプログラム依存グラフ(PDGs)(つまり、制御フローとデータフローの依存関係)[27]は通常、コンパイルされた中間表現またはバイトコード[28]、[29]に依存し、コンパイルできないまたは不完全なコード断片には適用されません。このような制限は、任意のコード断片を含む多くの領域でのコード表現の応用を妨げます。
本論文では、ASTベースのニューラルネットワーク(ASTNN)と呼ばれる、コンパイル可能である必要がないコード断片を表現するための新しいアプローチを提案します。ASTNNは、1つのコード断片の大きなASTを文レベルで一連の小さな木に分割し、すべての文の木にツリーベースのニューラル埋め込みを行います。これにより、文ベクトルが生成され、レキシカルおよび文レベルの構文的知識を表すことができます。ここでの文は、プログラム言語仕様で定義されたASTノードであるStatementを指します。MethodDeclarationも特別な文ノードとして扱います。例として、図1にはオープンソースプロジェクトからのコード断片が示されています。行7から行15までのコードスニペットには、Try文全体が含まれ、行5から行6までのコードスニペットには、変数sbを初期化するLocalVariable文のみが含まれています。Try文のように、ヘッダーと本体の他の文を含む文の場合、文のヘッダーとすべての含まれる文を分割します。この方法で、大きなASTを短い一連の小さな文の木に分解します。文と文の間の順序依存関係をベクトルにエンコードするために、再帰型ニューラルネットワーク(RNN)を使用します。このようなベクトルは、ソースコードの自然さを捉え、ニューラルソースコード表現として機能します。
具体的には、まず、コード断片からASTを構築し、全体のASTを小さな文の木に分割します(1つの文のASTノードで構成され、Statementノードを根とする木)。例えば、図1では、文の木は破線で示され、元のコード断片の対応する文(または文のヘッダー)も破線で表示されます。第二に、多方向文の木上に再帰エンコーダを設計し、文レベルのレキシカルおよび構文情報を捉え、それらを文ベクトルで表現します。第三に、文ベクトルのシーケンスに基づいて、双方向ゲート型再帰ユニット(GRU)[34]、[35]、一種の再帰型ニューラルネットワークを使用して、文の順序的な自然さを活用し、最終的にコード断片全体のベクトル表現を取得します。
要約すると、私たちが提案するニューラルソースコード表現は、最先端のASTベースのニューラルモデルよりもソースコードに関するより多くの構文的および意味的情報を学習することを目指しています。それは汎用性があり、ソースコード分類やコードクローン検出などの多くのプログラム理解関連のタスクで使用できます。公開されているベンチマークでこれらの2つのタスクについて実験を行い、最先端の手法と比較しました。実験結果は、私たちのモデルがより効果的であることを示しています。たとえば、ソースコード分類では、私たちの手法により正解率が94%から98.2%に向上しました。クローン検出では、私たちの手法によりF1値の結果が2つのベンチマークデータセットでそれぞれ82%から93.8%、59.4%から95.5%に向上しました。
私たちの主な貢献は以下の通りです:
私たちは新しいニューラルソースコード表現を提案しました。これは、文のレベルの構文的知識、文法、および文の自然さを捉えることができます。
私たちはこの表現を2つの一般的なプログラム理解タスク(コード分類とクローン検出)に適用しました。実験結果は、私たちの手法が最先端の手法を改善できることを示しています。
本論文の残りの部分は以下のように構成されています。セクションIIでは背景を紹介します。セクションIIIでは、私たちのアプローチを説明します。セクションIVでは、私たちのニューラルソースコード表現の応用について説明します。セクションVでは、実験結果を提供します。関連する研究と妥当性に関する脅威についての議論は、それぞれセクションVIとセクションVIIに示されています。最後に、セクションVIIIで私たちの仕事をまとめます。
背景
Abstract Syntax Tree (AST)
抽象構文木(AST)は、ソースコードの抽象的な構文構造を表現するための木の一種です[36]。これは、プログラミング言語やソフトウェアエンジニアリングツールで広く使用されています。図1(b)に示すように、ASTのノードはソースコードの構成要素やシンボルに対応しています。一方で、プレーンなソースコードと比較して、ASTは抽象的で、句読点や区切り文字などのすべての詳細を含みません。しかし、ASTはソースコードの語彙情報や構文構造を記述するために使用できます。例えば、図1(b)のメソッド名readTextや制御フロー構造WhileStatementが挙げられます。一部の研究では、ASTをトークンベースの手法で直接使用し、ソースコードの検索[37]、プログラム修復[38]、およびソースコードの差分[39]を行っています。トークンベースの手法の制限[17]により、これらの手法はソースコードの構文情報をほとんど捉えることができません。
Tree-based Neural Networks
最近、木ベースのニューラルネットワーク(TNNs)が提案され、ASTを入力として受け入れるようになりました。与えられた木に対して、TNNsはボトムアップの再帰的な方法でノードの埋め込みを計算することで、そのベクトル表現を学習します。AST用の最も代表的な木ベースモデルには、再帰型ニューラルネットワーク(RvNN)、ツリーベースの畳み込みニューラルネットワーク(TBCNN)、およびツリーベースの長期・短期記憶(Tree-LSTM)があります。
1) 再帰型ニューラルネットワーク:RvNNは、自然言語と画像解析の再帰構造に対して最初に提案されました[40]。具体的には、木構造が与えられた場合、1つの親ノードy1が2つの子ノード(c1、c2)を持つとします。ここで、c1とc2はノードの単語埋め込みまたは中間ベクトル表現です。ノードy1のベクトルは次のように計算されます:
p = f(W(1)[c1; c2] + b(1))
ここで、W(1)はパラメータの行列、fは要素ごとの活性化関数、b(1)はバイアス項です。このベクトル表現の品質を評価するために、復号化層が使用されて子ノードを再構築します:
[c01; c02] = W(2)p + b(2)
その後、訓練損失は次のように測定されます:E(θ) = ||c1 − c01||22 +||c2 − c02||22。
このようにして、RvNNは再帰的に木全体を横断してパラメータを計算し最適化し、最終的な根ノードのベクトルが与えられた木を表します。RvNNをベースに、再帰オートエンコーダ(RAE)がASTの自動エンコードに組み込まれ、コードクローンを検出するために使用されます[5]。ここで、ASTは固定サイズの入力のためにフルバイナリツリーに変換されます。
2) ツリーベースの畳み込みニューラルネットワーク:TBCNNは、ソースコード分類などの教師付き学習のために木構造上で畳み込み計算を行います[2]。そのコアモジュールは、ASTベースの畳み込み層であり、固定深度の特徴検出器をAST全体にスライドさせて適用します。この手順は次のように表されます:
y = tanh(Xni=1Wconv,i · xi + bconv)
ここで、x1, · · · , xnは各スライドウィンドウ内のノードのベクトルであり、Wconv,iはパラメータ行列、bconvはバイアスです。TBCNNは、局所性を向上させるためにいくつかのグローバル情報を統合するためのボトムアップエンコーディング層を採用しています。元のASTのノードには2つ以上の子がいる場合がありますが、TBCNNは畳み込みの固定サイズのためにASTを連続したフルバイナリーツリーとして扱います。
3) ツリーベースの長期・短期記憶(Tree-LSTM):Tree-LSTMは、木構造のトポロジーをモデル化するためのLSTMの一般化です。標準的なLSTMとは異なり、Child-Sum Tree-LSTM [41] は、現在の入力を再帰的に子ノードの状態と組み合わせて、ツリー構造全体で状態を更新します。CDLH [6] は、コード断片のクローン検出のためにコード断片の表現を学習するためにTree-LSTMを使用します。ここでは、コード断片はASTに解析されます。子ノードの数が可変である場合、ASTはフルバイナリーツリーに変換されます。ボトムアップの計算の後、ASTのルートノードベクトルがコード断片を表すために使用されます。
既存の作業の制限事項
これらの3つの木ベースの方法には、2つの主要な制限があります。まず、木のトポロジーの勾配ベースのトレーニング中に、勾配は構造を逆伝播して計算されます[41]、[23]。しかし、プログラムの複雑さ、特に入れ子構造により、ASTの構造は通常大きく深くなります。したがって、葉ノードからルートノードに向けてのボトムアップ計算は、勾配が消失する問題に遭遇し、長期依存関係を捉えるのが難しくなります[19]、[22]。これにより、葉ノードから遠く離れたノード(たとえば、葉ノードの識別子)が持つセマンティクスの一部が見逃される可能性があります。第二に、既存の木ベースの方法は、親ノードの3つ以上の子ノードを新しいサブツリーに移動して、ASTをバイナリツリーと見なします。これにより、ソースコードの元の意味が変わり、長期依存性の問題がより深刻になります。例えば、CDLH [6] は、クローン検出の1つのパブリックベンチマークでF1値が57%しかありません。NLPの研究[23]、[41]、[21]によると、ツリーのサイズと深さは重要であり、パフォーマンスに重大な影響を与えます。
提案手法
このセクションでは、ASTベースのニューラルネットワーク(ASTNN)を紹介します。ASTNNの全体的なアーキテクチャは図2に示されています。まず、ソースコード断片をASTに解析し、それぞれのASTを文の木(STツリー)のシーケンスに分割するための先行順序トラバーサルアルゴリズムを設計します(これらは、文ノードをルートとし、文のASTノードを含むツリーです)。すべてのSTツリーは、文エンコーダによってベクトルにエンコードされ、e1、···、etと表されます。次に、自然さをモデル化するために双方向ゲーテッドリカレントユニット[35](Bi-GRU)を使用します。Bi-GRUの隠れ状態は、プーリングによって単一のベクトルにサンプリングされ、これがコード断片の表現です。
ASTの分割とSTツリーのシーケンスの構築
最初に、既存の構文解析ツールによって、ソースコード断片は大規模なASTに変換されることがあります。各ASTについて、文の粒度で分割し、preorder走査によって文ツリーのシーケンスを抽出します。
形式的には、ASTのTと文のASTノードの集合Sが与えられた場合、AST内の各文ノードs ∈ Sはソースコードの1つの文に対応します。MethodDeclarationを特別な文ノードとして扱い、したがってS = S∪{MethodDeclaration}とします。図1に示すように、入れ子の文の場合、blockはTryやWhileなどの入れ子の文のヘッダーと本文を分割するためのものであり、bodyはメソッド宣言用です。文ノードs ∈ Sのすべての子孫をD(s)で示します。s ∈ Sの任意のd ∈ D(s)について、1つのノードp ∈ Pを介してsからdへのパスが存在する場合、ノードdは文sの本文に含まれることを意味します。ノードdをsのサブ文ノードと呼びます。次に、文ノードs ∈ Sによってルート付けられた文ツリー(STツリー)は、ノードsとそのサブ文ノードを除くすべての子孫から構成されるツリーです。たとえば、MethodDeclarationによってルート付けられた最初のSTツリーは、図1(b)の点線で囲まれており、「static」、「public」、および「readText」などのヘッダー部分を含み、本文内の2つのLocalVariable、1つのTry、および1つのReturn文のノードは除外されています。1つのSTツリーのノードには3つ以上の子ノードがある場合があるため、それをバイナリツリーと区別するために、多方向のSTツリーとも呼びます。この方法で、1つの大きなASTは、重複しない複数の多方向STツリーのシーケンスに分解されることができます。
ASTの分割とSTツリーの構築は、トラバーサーとコンストラクターによって直感的に行われます。トラバーサーは、ASTを深さ優先で先行順序で走査し、コンストラクターは再帰的にSTツリーを作成し、STツリーのシーケンスに順次追加します。このような方法によって、新しいSTツリーがソースコードの順序で追加されることが保証されます。これにより、ASTNNの生の入力としてSTツリーのシーケンスを取得します。
ASTの分割の粒度の選択は簡単ではないことに注意してください。ソースコードの意味を伝えるために、我々はこの研究で文ツリーを選択しました。文はソースコードの意味を運ぶための基本的な単位です。また、ASTのノードレベル、ブレースペア内のコードブロック、および完全なASTなど、他の粒度も試行しました。これらの実験については、セクションVで議論します。選択された粒度のサイズが大きすぎる場合(例:完全なAST)、関連する先行研究[5]、[6]と同様に、セクションIIで言及した勾配消失問題が発生する可能性があります。しかし、粒度が小さすぎる場合(例:ASTのノードレベル)、モデルはトークンベースのRNNとなり、文の構文的知識をキャプチャする能力が低下する可能性があります。提案された文レベルの粒度が良いトレードオフを持つことを、実験結果が示しています。それは、STツリーのサイズと構文情報の豊富さの間で良いバランスを取っています。
多方向STツリー上での文のエンコーディング
1) 文ベクトル:STツリーが与えられた場合、文のベクトル表現を学習するために使用される、RvNNベースの文エンコーダーを設計します。
ASTにはさまざまな特殊な構文記号が含まれているため、トレーニング用のコーパスとしてASTのpreorder走査ですべての記号を取得します。 word2vec [42] を使用して記号の教師なし学習ベクトルを学習し、トレーニングされた記号の埋め込みを文エンコーダーの初期パラメータとして使用します。 ASTのすべての葉ノードは識別子などのレキシカル情報を表し、STツリーの葉ノードにも組み込まれているため、私たちの記号の埋め込みはレキシカル知識をよく捉えることができます。
図1におけるMethodDeclarationノードによってルート付けられた最初のSTツリーを例に取ると、エンコーダーはSTツリーを走査し、再帰的に現在のノードの記号を新しい入力として取り、その子ノードの隠れた状態と一緒に計算します。これは図3で示されています。ここでは最初の2レベルのみを表示しています。STツリーでは、2つの子ノードreadText(つまり、メソッド名)とFormalParameter(つまり、メソッドのパラメータを定義する文法構造)および他の兄弟ノードがMethodDeclarationの意味を豊かにします。[5]、[6]で説明されているように、STツリーをバイナリツリーに変換すると、例えば、readTextノードをFormalParameterノードの子ノードや子孫に移動すると、元の意味が破壊される可能性があります。代わりに、元の多方向STツリーを入力とします。
具体的には、STツリーtが与えられた場合、nを非葉ノード、Cをその子ノードの数とします。最初に、事前学習された埋め込みパラメータWe ∈ R|V|×d(ここで、Vは語彙サイズであり、dはシンボルの埋め込み次元です)を使用して、ノードnのレキシカルベクトルは次のようにして取得できます:
vn = We>xn (1)
ここで、xnはシンボルnのワンホット表現であり、vnは埋め込みです。次に、ノードnのベクトル表現は次の式で計算されます:
h = σ(Wn>vn + Σi∈[1,C]hi + bn) (2)
ここで、Wn ∈ Rd×kはエンコーディング次元kの重み行列であり、bnはバイアス項、hiは各子ノードiの隠れ状態、hは更新された隠れ状態を示し、σはtanhや恒等関数などの活性化関数です。本稿では恒等関数を使用します。同様に、STツリーt内のすべてのノードのベクトルを再帰的に計算して最適化することができます。さらに、ノードベクトルの最も重要な特徴を決定するために、すべてのノードがスタックにプッシュされ、次に最大プーリングによってサンプリングされます。すなわち、式3によってSTツリーと対応する文の最終的な表現が得られます。ここで、NはSTツリー内のノード数です。
et = [max(hi1), · · · , max(hik)], i = 1, · · · , N (3)
これらの文ベクトルは、文のレキシカルおよび文レベルの構文情報の両方を捉えることができます。
2) バッチ処理:大規模なデータセットでのトレーニング効率を向上させるために、複数のサンプル(つまり、コード断片)を同時にエンコードするバッチ処理アルゴリズムを設計する必要があります。しかし、一般的には、多方向STツリーでのバッチ処理は、同じバッチ内の親ノードの子ノード数が異なるために困難です[2]、[6]。例えば、図4に示すように、ns1が3つの子ノードを持ち、ns2が2つの子ノードを持つ2つの親ノードが与えられた場合、1つのバッチ内で2つの親を対象に直接式2を計算することは不可能です。これに対処するために、我々はバッチサンプルを動的に処理するアルゴリズムを設計しました(アルゴリズム1)。
直感的には、親ノードは異なる数の子ノードを持っていますが、アルゴリズムは動的に検出し、すべての可能な子ノードを同じ位置にグループ化し、次に行列演算を活用してバッチ処理の方法で各グループの式2の計算を高速化できます。アルゴリズム1では、STツリーのL個のサンプルをバッチ処理し、それからルートノードから幅優先で走査を開始します(行4)。バッチ内の同じ位置にある現在のノードnsについて、まずバッチで式1を計算します(行10)、次にノードの位置によってすべての子ノードを検出してグループ化します(行12-16)。図4に示されているように、子ノードをその位置に応じて3つのグループに分け、グループを配列リストCとCIに記録します。これらのグループに基づいて、すべての子ノードに対して再帰的にバッチ処理を実行します(行17-21)。すべての子ノードの結果を取得した後、バッチで式2を計算します(行22)、そしてバッチ処理されたSTツリーのすべてのノードベクトルをスタックSにプッシュします(行24)。最後に、式3で説明されるプーリングによって、STツリーサンプルと対応する文のベクトルを取得できます(行5)。
文のシーケンスを表現する
STツリーベクトルのシーケンスに基づいて、我々はGRU [34] を利用して文の自然さを追跡します。また、LSTMを使用する選択肢も検討しましたが、LSTMとGRUの比較については、実験で議論されます。
与えられた1つのコード断片に対し、そのASTから抽出されたT個のSTツリーがあると仮定し、Q ∈ R^T×k =[e1, · · · , et, · · · , eT ]、t ∈ [1, T]がシーケンス内のエンコードされたSTツリーのベクトルを表すものとします。時間tにおいて、遷移方程式は次のようになります:
rt = σ(Wret + Urht−1 + br)
zt = σ(Wzet + Uzht−1 + bz)
h˜t = tanh(Whet + rt (Uhht−1) + bh)
ht = (1 − zt) ht−1 + zt h˜t
ここで、rtは以前の状態の影響を制御するリセットゲート、ztは過去と新しい情報を組み合わせる更新ゲート、h˜tは候補状態であり、以前の状態ht−1と線形補間を行い、現在の状態htを決定するために使用されます。Wr、Wz、Wh、Ur、Uz、Uh ∈ R^k×mは重み行列であり、br、bz、bhはバイアス項です。すべての時間ステップの隠れた状態を反復的に計算した後、これらの文の順次の自然さが得られます。
再帰層が依存情報をより効果的に捉える能力をさらに向上させるために、双方向GRU [34] を採用します。ここで、両方向の隠れ状態は連結され、新しい状態が次のように形成されます:
−→ht =−−−→ GRU(et), t ∈ [1, T]
←−ht =←−−− GRU(et), t ∈ [T, 1]
ht = [−→ht ,←−ht ], t ∈ [1, T]
文エンコーダーと同様に、これらの状態の最も重要な特徴は、最大プーリングまたは平均プーリングによってサンプリングされます。さまざまな文の重要性が直感的に等しくないことを考慮すると、例えば、MethodInvocation文のAPI呼び出しにはより多くの機能情報が含まれる可能性があります[43]。そのため、デフォルトでは最大プーリングを使用して、最も重要な意味を捉えます。最終的に、モデルはコード断片のベクトル表現と見なされるベクトルr ∈ R^2mを生成します。
提案モデルの応用
提案されたASTNNモデルは汎用性があります。これは、さまざまなプログラム理解タスクのための異なるソースコードの意味を特徴付けるためのタスク固有のベクトル表現を学習することができます。本研究では、ソースコード分類とコードクローン検出を含む2つの一般的なタスクを例に挙げ、提案されたモデルの応用を示します。
ソースコードの分類:このタスクは、コード断片をその機能によって分類することを目指しており、プログラムの理解とメンテナンスに役立ちます[2]、[44]、[45]。コード断片のベクトルrとカテゴリの数Mが与えられた場合、ロジットはxˆ = Wor+boによって得られます。ここで、Wo ∈ R^2m×Mは重み行列であり、boはバイアス項です。我々は、広く使用されているクロスエントロピー損失を損失関数として定義します:
J(Θ, x, y ˆ ) = Σ -log(exp(ˆxy) / Σexp(ˆxj))!
ここで、Θはモデル内のすべての重み行列のパラメータを示し、yは真のラベルです。
コードクローン検出:コードクローンの検出は、ソフトウェアエンジニアリングの研究で広く研究されています[3]、[4]、[5]、[6]、[26]。これは、2つのコード断片が同じ機能を実装しているかどうかを検出するものです。コード断片のベクトルr1とr2があると仮定し、それらの距離は意味的な関連性のためにr = |r1 − r2|で測定されます[41]。その後、出力yˆ = sigmoid(ˆx) ∈ [0, 1]を彼らの類似性とみなし、ここでxˆ = Wor + boです。損失関数はバイナリクロスエントロピーとして定義されます:
J(Θ, y, y ˆ ) = Σ(−(y · log(ˆy) + (1 − y) · log(1 − yˆ)))
2つのタスクのためにASTNNモデルをトレーニングする場合、目標は損失を最小限に抑えることです。本稿では、計算効率が高いためにAdaMax [46] を使用しています。
すべてのパラメータが最適化された後、トレーニングされたモデルは保存されます。新しいコード断片に対しては、まずそれらをSTツリーのシーケンスに前処理し、その後、再読み込みされたモデルにフィードします。出力は異なるラベルの確率pです。コード分類では、複数のカテゴリがあるため、推定値は次のように取得できます:
prediction = arg max_i(pi), i = 1, · · · , M (8)
一方、クローン検出では、pは範囲[0,1]の単一の値です。したがって、予測は次のように得られます:
prediction = {
True, p > δ
False, p ≤ δ
(9)
ここで、δは閾値です。
関連研究
ソースコードの表現
ソースコードを効果的に表現する方法は、ソフトウェアエンジニアリングの研究における基本的な問題です。
従来のIR(情報検索)および機械学習の手法は、テキストベースのトークンを使用したコード表現に広く利用されてきました。プログラムは、コードクローン検出のために正規化されたトークンのシーケンスに変換されます[3]。SourcererCC [4] は、トークンの順序付けと最適化された逆インデックス技術を利用して改善されています。 Deckard [55] は、レキシカル情報に加えて、一部の構文構造化情報を使ってプログラムをクローン検出に利用しています。統計的および機械学習手法に基づいて、n-gramモデル[1] およびSVM [45] が、ソースコードの作者やドメインを分類するために使用されています。Maleticら[56] は、コード断片の意味的な類似性を識別するためにLSIを採用しており、ソフトウェアのクラスの結束度はLDA [15] によって評価されます。
最近、ソースコードの分散表現を学習するために、深層学習ベースのアプローチが注目を集めています[57]。Raychevら[58]は、コード補完のためにRNNとn-gramモデルを採用しています。Allamanisら[59]は、ニューラルコンテキストモデルを使用してメソッド名やクラス名を提案しています。欠陥予測のために、深層信念ネットワークを使用してソースコードから意味的な特徴が抽出されます[60]。DeepBugs [61]は、名前ベースのバグを検出するためにコードをword2vecで表現します。ASTの構文情報を捉えるために、Whiteら[5]は、事前学習されたトークンの埋め込みを使用してAST上の再帰オートエンコーダを利用しています。TBCNN [2]は、カスタムの畳み込みニューラルネットワークをAST上で使用して、コード断片のベクトル表現を学習します。CDLH [6]は、Tree-LSTMを組み込んでコード断片の機能的な意味を表現します。さらに、Allamanisら[25]は、プログラムグラフ上でGated Graph Neural Networksを実行し、同じ変数と関数の依存関係を追跡して変数名を予測し、変数の誤用を検出します。DeepSim [62]は、コードの制御フローとデータフローをセマンティック行列にエンコードして、コードの機能的な類似性を測定します。識別子、CFG、バイトコードなどのさまざまなコード表現を、アンサンブル学習技術によって統合することもできます[26]。これらのニューラルネットワークと比較して、私たちのモデルは、既存のASTベースの方法を改善し、文のレキシカル、文レベルの構文知識、および文の順次の自然さを捉えることに焦点を当てています。
ソフトウェアエンジニアリングにおける深層学習
近年、ソフトウェアエンジニアリングにおいて多くの新興の深層学習アプリケーションが登場しています。DeepAPI [43] は、シーケンス・トゥ・シーケンスのニューラルネットワークを使用して、自然言語のクエリの表現を学習し、関連するAPIシーケンスを予測します。Lamら[63]は、深層ニューラルネットワークをIR技術と組み合わせて、潜在的なバグのあるファイルを推奨します。Xuら[64]は、単語の埋め込みと畳み込みニューラルネットワークを採用して、StackOverflowで関連する質問を予測します。ニューラル機械翻訳は、コミットメッセージを自動生成するために使用されます[10]。Guoら[65]は、トレースリンクを生成するためにRNNベースのニューラルネットワークを提案しています。コード検索では、共通の埋め込みモデルが使用され、ソースコードと自然言語の説明を統一されたベクトル空間にマッピングして意味の類似性を評価します[66]。上記の関連研究は、主にさまざまなタスクのためにソフトウェア関連の自然言語テキストを理解するためにニューラルネットワークモデルを使用していますが、私たちはソースコードのニューラル表現に焦点を当てています。
妥当性への脅威
妥当性への主な脅威は3つあります。まず、OJデータセットは実際の製品環境から収集されていません。ただし、BigCloneBenchには、SourceForge [47]からの実世界のJavaリポジトリのコードスニペットが含まれており、これによってこの脅威が軽減されます。二番目の脅威は、OJCloneの構築に関するものです。先述のように、同じ問題の下にあるプログラムはクローンペアに属します。これにより、それらが真のクローンペアであるかどうかについての不確実性が生じますが、これは以前の研究でも同様の実践が行われています[6]。ただし、コードクローンは手動で検査されるBigCloneBenchも検証に使用されています。したがって、実験結果にはほとんど影響しないと考えています。最後の脅威は、CDLHのアプローチを再現できないことです。その論文でいくつかの詳細が抜けているため、代わりに、その論文で説明されているとおりの同じデータセットを構築して、この脅威を軽減します。CDLHツールが利用可能になった場合、比較の補足を行います。
結論
この研究では、ソースコード断片のベクトル表現を学習する効率的なASTベースのニューラルネットワーク(ASTNN)を提案しました。このモデルは、文のレキシカル、文レベルの構文知識、および文の自然さを捉えることができます。モデルは、コード断片の大規模なASTを小さな文ツリーのシーケンスに分解し、多方向の文ツリーを再帰的にエンコードして文ベクトルを取得し、最終的に文の自然さを活用してコード断片のベクトル表現を学習します。ASTNNをソースコード分類とコードクローン検出の2つの一般的なプログラム理解タスクで評価しました。実験結果から、当社のモデルが既存の手法を大幅に上回ることが示されました。当社のコードと実験データは、https://github.com/zhangj1994/astnn で公開されています。将来的には、異なるプログラミング言語の大規模なデータセットで提案モデルをさらに評価し、さまざまなソフトウェアエンジニアリングタスクで評価します。また、ソースコードのより深いセマンティクスを捉えるために、他のニューラルモデルも検討します。
コメント
コメントを投稿