Tianchi NLPコンペティション-ニューステキスト分類(6)-ディープラーニングに基づくテキスト分類3-BERT


一連の記事TianchiNLP
コンペティション-ニューステキスト分類(1)
-コンペティションの質問の理解Tianchi NLPコンペティション-ニューステキスト分類(2)-データ読み取りとデータ分析
Tianchi NLPコンペティション-ニューステキスト分類(3)-機械学習テキストに基づく分類
TianchiNLPコンペティション-ニューステキスト分類(4)-深層学習に基づくテキスト分類1-FastText Tianchi
NLPコンペティション-ニューステキスト分類(5)-深層学習に基づくテキスト分類2-TextCNN、TextRNN TianchiNLPコンペティション-ニュース
テキスト分類(6)-深層学習に基づくテキスト分類3-BERT


6、ディープラーニングに基づくテキスト分類3-BERT

6.1テキスト表現方法-パート4

トランスの原理

Transformerは、「Attention is All You Need」で提案されています。モデルのエンコード部分は、エンコーダーのセットのスタックであり(6つのエンコーダーが紙に順番に積み重ねられています)、モデルのデコード部分は、同数のデコーダー。

ここに画像の説明を挿入します

コーディング部分に焦点を当てます。それらはまったく同じ構造を持っていますが、パラメーターを共有しておらず、各エンコーダーは2つの部分に分解できます。入力シーケンスをベクトル化した後、それらは最初に自己注意層を通過します。これは、エンコーダーが単語をエンコードするときに入力シーケンス内の他の単語を確認するのに役立ちます。自己注意の出力はフィードフォワードネットワーク(フィードフォワードニューラルネットワーク)に流れ、各入力位置に対応するフォワードネットワークは独立しており、相互に干渉しません。最後に、出力は次のエンコーダーに渡されます。

ここに画像の説明を挿入します

Transformerの重要な機能は、ここで確認できます。各位置の単語は、独自のエンコーダパスを介してのみ流れます。自己注意層では、これらのパスは相互に依存しています。フォワードネットワーク層にはこれらの依存関係はありませんが、フォワードネットワークを流れるときにこれらのパスを並行して実行できます。

マルチヘッドメカニズムはセルフアテンションで使用されるため、さまざまなアテンションヘッドがさまざまな部分に焦点を合わせます。

ここに画像の説明を挿入します

「それ」をエンコードするとき、一方の注意の頭は「動物」に焦点を合わせ、もう一方の頭は「疲れた」に焦点を合わせます。ある意味で、モデルの「それ」の表現は「動物」と「疲れた」の組み合わせです。

自己注意の詳細な計算については、ここでは拡張されないTransformerに関するJayAlammarのブログを参照してください

さらに、モデルに単語の語順を維持させるために、位置コーディングベクトルがモデルに追加されます。次の図に示すように、各行はベクトルの位置コードに対応します。したがって、最初の行は、入力シーケンスの最初の単語の埋め込みに追加するベクトルになります。各行には512個の値が含まれています。各値は1から-1の間です。左側は正弦関数によって生成され、右側は余弦によって生成されるため、中央で大きな分離が見られます。

ここに画像の説明を挿入します

エンコーダー構造で言及する価値のある詳細の1つは、各サブレイヤー(Self-attention、FFNN)に、レイヤーの正規化が続く残りの接続があることです。ベクトルとLayerNormの操作を視覚化すると、次のようになります。ここに画像の説明を挿入します

事前に訓練された言語モデルに基づく単語表現

事前に訓練された言語モデルに基づく単語表現は、文脈情報をモデル化することができ、それによって、従来の静的単語ベクトルが「多義」言語現象をモデル化できないという問題を解決する。最も初期に提案されたELMoは、2つの一方向LSTMに基づいており、左から右および右から左への隠れ層ベクトルは、連結された学習コンテキスト単語の埋め込みを表します。GPTは、エンコーダーとしてLSTMの代わりにTransformerを使用します。最初に、言語モデルを事前トレーニングし、次にダウンストリームタスクでモデルパラメーターを微調整します。ただし、GPTは一方向の言語モデルしか使用しないため、コンテキスト情報をモデル化することは困難です。上記の問題を解決するために、研究者はBERTを提案しました。BERTモデルの構造を下の図に示します。これは、一連の事前トレーニングを実行して深いコンテキスト表現を取得するトランスフォーマーベースの多層エンコーダーです。

ここに画像の説明を挿入します

ELMoの論文のトピックでは、Deepは双方向の2層LSTMを指し、より重要なことはコンテキストです。従来の方法で生成された単語マッピングテーブルの形式は、最初に各単語の静的単語ベクトルを生成し、次に単語の表現が固定され、コンテキストの変更によって変化しません。実際、多義語の言語現象のために、静的な単語ベクトルには大きな欠点があります。例として銀行を取り上げます。トレーニングコーパスが十分に大きい場合、すべてのセマンティクスは、事前に学習された単語ベクトルに混合されます。そして、下流に適用すると、新しい文でも、銀行のコンテキストにお金などの単語が含まれている場合、基本的に、銀行は他のコンテキストの「川」のセマンティクスではなく「銀行」のセマンティクスを持っていると判断できますが、静的単語ベクトルコンテキストに応じて変更することはできないため、バンク表現は依然として複数のセマンティクスと混合されています。この問題を解決するために、ELMoは最初に言語モデルを事前トレーニングし、次にダウンストリームタスクで単語の埋め込みを動的に調整しました。したがって、最終的な出力単語表現は、コンテキスト内の単語の特定のセマンティクスを完全に表現でき、それによって問題を解決します。単語の多義性の。

GPTはopenaiに由来し、生成的な事前トレーニングモデルです。ELMoのLSTMをTransformerのエンコーダーに置き換えることに加えて、GPTは、NLPの世界での事前トレーニングと微調整に基づいた新しいパラダイムも作成しました。GPTはELMoと同じ2ステージモデルを使用しますが、最初のステージでは、GPTはELMoの2つの一方向二重層LSTMスプライシングの構造を採用せず、自己回帰に基づく一方向言語モデルを使用します。

GoogleはNAACL2018で公開された論文でBERTを提案しました。GPTと同様に、BERTも事前トレーニングと微調整の2段階モデル​​を使用します。ただし、モデル構造に関しては、BERTはELMOパラダイムを採用しています。ELMOパラダイムは、GPTの1方向言語モデルの代わりに双方向言語モデルを使用します。ただし、BERTの作成者は、ELMoが2つの一方向言語モデルを使用して接続すると考えています。ラフすぎるため、最初の段階の事前トレーニングプロセスで、BERTは、右から左または左から右にモデル化するのではなく、コンテキストを通じて単語自体を予測する、クローズフィリングに似たマスク言語モデルを提案しました。 、これにより、モデルを自由にエンコードできます。各レイヤーの2つの方向からの情報。文の語順関係を学習するために、BERTはTransformerの三角関数の位置表現を学習可能なパラメーターに置き換えます。次に、BERTは、単一文と二重文の入力を区別するために、文タイプ表現も導入します。BERTの入力を図に示します。さらに、文間の関係を完全に学習するために、BERTは次の文予測タスクを提案します。具体的には、トレーニング中に、センテンスペアの2番目のセンテンスの50%が元の連続センテンスから取得され、残りの50%のセンテンスが他のセンテンスからランダムにサンプリングされます。同時に、アブレーション実験は、この事前トレーニングタスクが文間の関係を判断するタスクに大きく貢献していることも証明しました。異なるモデル構造に加えて、事前トレーニングでBERTが使用するラベルなしのデータサイズは、GPTのデータサイズよりもはるかに大きくなります。

ここに画像の説明を挿入します

第2段階では、GPTと同様に、BERTも微調整モードを使用してダウンストリームタスクを微調整します。次の図に示すように、BERTはGPTとは異なり、ダウンストリームタスクを変換するための要件を大幅に削減します。BERTモデルに基づいて線形分類器を追加するだけで、ダウンストリームタスクを完了できます。具体的には、GPTと同様に、文間の関係を判断するタスクでは、文の間に区切り記号を追加し、両端に開始記号と停止記号を追加するだけです。出力するときは、センテンスの開始記号[CLS]を、Softmax + Linear分類レイヤーを使用してBERTの最後のレイヤーの対応する位置に接続するだけで済みます。単一センテンス分類の問題の場合、これはGPTと同様であり、開始記号と終了記号が段落に追加され、出力部分は文間の関係の判断タスクと一致します。質疑応答タスクの場合、出力回答は次の場所にある必要があるためです。特定の段落の開始位置と終了位置では、最初に質問と段落を文の後に続ける必要があります。相互関係判断タスクは入力を作成し、出力は開始と終了を判断するために分類器に接続するだけで済みます。 BERTの最後のレイヤーの2番目の文の位置、つまり、段落内の各単語に対応する位置。最後に、NLPのシーケンスの場合、質問にラベルを付けるための入力は、単一文の分類タスクと同じです。違いは、分類器をBERTの最後のレイヤーの各単語に対応する位置に接続できることです。

ここに画像の説明を挿入します

さらに重要なことに、BERTは、NLP分野で「事前トレーニング-微調整」の新しい2段階のパラダイムを開きました。最初の段階では、双方向言語モデルが大量のラベルなしテキストで事前トレーニングされます。ここで特に注目に値するのは、特徴抽出器としてTransformerを使用することは、並列処理と長い問題の解決において従来のRNNまたはCNNよりも進んでいることです。 -距離依存性。事前トレーニングにより、トレーニングデータの語彙、構文、および文法の知識をネットワークパラメータの形式でモデルに洗練させることができます。第2段階では、ダウンストリームタスクのデータを使用して次のことを行います。さまざまなレイヤーのBERTモデルパラメーターを微調整するか、BERTを特徴抽出器として使用してBERT埋め込みを生成し、それを新しい機能としてダウンストリームタスクに導入します。この2段階のまったく新しいパラダイムは、コンピュータービジョンの分野から来ていますが、自然言語処理の分野ではあまり使用されていません。近年のNLPのブレークスルーのマスターとして、BERTの最大のハイライトと言えます。モデルのパフォーマンスが優れているだけでなく、ほとんどすべてのNLPタスクをBERTに基づいて簡単に変更できます。その後、事前トレーニングで学習した言語知識をダウンストリームタスクに導入して、モデルのパフォーマンスをさらに向上させることができます。

6.2Bertベースのテキスト分類

バートサーチン

事前トレーニングプロセスでは、Tensorflowに基づいてGoogleがリリースしたBERTソースコードを使用します。まず、元のテキストからトレーニングデータを作成します。このコンテストのデータはすべてIDであるため、ここで語彙を再構築し、スペースに基づく単語セグメンターを確立します。

class WhitespaceTokenizer(object):
    """WhitespaceTokenizer with vocab."""
    def __init__(self, vocab_file):
        self.vocab = load_vocab(vocab_file)
        self.inv_vocab = {
    
    v: k for k, v in self.vocab.items()}

    def tokenize(self, text):
        split_tokens = whitespace_tokenize(text)
        output_tokens = []
        for token in split_tokens:
            if token in self.vocab:
                output_tokens.append(token)
            else:
                output_tokens.append("[UNK]")
        return output_tokens

    def convert_tokens_to_ids(self, tokens):
        return convert_by_vocab(self.vocab, tokens)

    def convert_ids_to_tokens(self, ids):
        return convert_by_vocab(self.inv_vocab, ids)

事前トレーニングNSP事前トレーニングタスクが削除されたため、ドキュメントは最大長256の複数のセグメントで処理されます。最後のセグメントの長さが256/2未満の場合、ドキュメントは破棄されます。各セグメントは、BERT元のテキストのマスク言語モデルに従って実行され、tfrecord形式に処理されます。

def create_segments_from_document(document, max_segment_length):
    """Split single document to segments according to max_segment_length."""
    assert len(document) == 1
    document = document[0]
    document_len = len(document)

    index = list(range(0, document_len, max_segment_length))
    other_len = document_len % max_segment_length
    if other_len > max_segment_length / 2:
        index.append(document_len)

    segments = []
    for i in range(len(index) - 1):
        segment = document[index[i]: index[i+1]]
        segments.append(segment)

    return segments

事前トレーニングプロセスでは、マスク言語モデルタスクのみが実行されるため、次の文予測タスクの損失は計算されません。

(masked_lm_loss, masked_lm_example_loss, masked_lm_log_probs) = get_masked_lm_output(
    bert_config, model.get_sequence_output(), model.get_embedding_table(),
    masked_lm_positions, masked_lm_ids, masked_lm_weights)

total_loss = masked_lm_loss

文の長さを調整し、モデルのトレーニング時間を短縮するために、BERT-miniモデルを採用しました。詳細な構成は次のとおりです。

{
    
    
  "hidden_size": 256,
  "hidden_act": "gelu",
  "initializer_range": 0.02,
  "vocab_size": 5981,
  "hidden_dropout_prob": 0.1,
  "num_attention_heads": 4,
  "type_vocab_size": 2,
  "max_position_embeddings": 256,
  "num_hidden_layers": 4,
  "intermediate_size": 1024,
  "attention_probs_dropout_prob": 0.1
}

フレームワーク全体でPytorchを使用しているため、最後のチェックポイントをPytorchの重みに変換する必要があります。

def convert_tf_checkpoint_to_pytorch(tf_checkpoint_path, bert_config_file, pytorch_dump_path):
    # Initialise PyTorch model
    config = BertConfig.from_json_file(bert_config_file)
    print("Building PyTorch model from configuration: {}".format(str(config)))
    model = BertForPreTraining(config)

    # Load weights from tf checkpoint
    load_tf_weights_in_bert(model, config, tf_checkpoint_path)

    # Save pytorch-model
    print("Save PyTorch model to {}".format(pytorch_dump_path))
    torch.save(model.state_dict(), pytorch_dump_path)

事前トレーニングは多くのリソースを消費します。ハードウェアの状態でそれが許可されない場合は、オープンソースモデルを直接ダウンロードすることをお勧めします。

Bert Finetune

ここに画像の説明を挿入します

微調整では、最後のレイヤーの最初のトークンである[CLS]の非表示ベクトルを文の表現として使用し、分類のためにそれをsoftmaxレイヤーに入力します。

sequence_output, pooled_output = \
    self.bert(input_ids=input_ids, token_type_ids=token_type_ids)

if self.pooled:
    reps = pooled_output
else:
    reps = sequence_output[:, 0, :]  # sen_num x 256

if self.training:
    reps = self.dropout(reps)

おすすめ

転載: blog.csdn.net/bosszhao20190517/article/details/107804216