ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

BERT苦行録3 - OpenIEをBERTで解く

OpenIEとは関係抽出タスクの一種で、文から主語、動詞、目的語のトリプルを抽出するタスクです。ナレッジグラフの自動構築などに使われます。今回はBERTをファインチューニングしてOpenIEに適用してみましょう。

イデア

BERTのrun_squad.pyでは、ソース文、質問文、回答のソース内位置(開始・終了)を訓練データとして入力し、位置を予測するタスクとして解いています。

これを、S,V,Oのそれぞれの位置を予測するよう変更できないか、というのが私のアイデアです。

コード

実際のコードは以下で公開しました。 https://github.com/sugiyamath/bert_openie/blob/master/run_openie.py

モデルの部分のコードだけ載せます。

def create_model(bert_config, is_training, input_ids, input_mask, segment_ids,
                 use_one_hot_embeddings):
  """Creates a classification model."""
  model = modeling.BertModel(
      config=bert_config,
      is_training=is_training,
      input_ids=input_ids,
      input_mask=input_mask,
      token_type_ids=segment_ids,
      use_one_hot_embeddings=use_one_hot_embeddings)

  final_hidden = model.get_sequence_output()

  final_hidden_shape = modeling.get_shape_list(final_hidden, expected_rank=3)
  batch_size = final_hidden_shape[0]
  seq_length = final_hidden_shape[1]
  hidden_size = final_hidden_shape[2]

  output_weights = tf.get_variable(
      "cls/openie/output_weights", [6, hidden_size],
      initializer=tf.truncated_normal_initializer(stddev=0.02))

  output_bias = tf.get_variable(
      "cls/openie/output_bias", [6], initializer=tf.zeros_initializer())

  final_hidden_matrix = tf.reshape(final_hidden,
                                   [batch_size * seq_length, hidden_size])
  logits = tf.matmul(final_hidden_matrix, output_weights, transpose_b=True)
  logits = tf.nn.bias_add(logits, output_bias)

  logits = tf.reshape(logits, [batch_size, seq_length, 6])
  logits = tf.transpose(logits, [2, 0, 1])

  unstacked_logits = tf.unstack(logits, axis=0)

  (S_start_logits, S_end_logits) = (unstacked_logits[0], unstacked_logits[1])
  (V_start_logits, V_end_logits) = (unstacked_logits[2], unstacked_logits[3])
  (O_start_logits, O_end_logits) = (unstacked_logits[4], unstacked_logits[5])

  return (S_start_logits, S_end_logits, V_start_logits, V_end_logits, O_start_logits, O_end_logits)

要するに、各サブトークンの位置で開始位置・終了位置である確率を予測します。これらの位置は、サブトークンの位置からドキュメントトークンの位置へと変換され、さらに文字の位置へと変換されるようです。

そのため、日本語で実行する場合は、ドキュメントトークンの分割方法を検討する必要があります。(英語は空白文字で区切っている)

予測結果

テストデータに対する予測結果は以下で公開しています。 https://raw.githubusercontent.com/sugiyamath/bert_openie/master/predictions.json

ちなみに、データはTupleInfの一部を使っています。 https://github.com/sugiyamath/bert_openie/tree/master/TupleInfKB

予測結果の一部を見てみます。

    "10213": [
        "Burned or dried leaf edges and wilted plants",
        "are",
        "a sign of excess fertilizer application."
    ],
    "10214": [
        "Burning",
        "changes",
        "the physical and chemical factors of the ecosystem."
    ],
    "10215": [
        "Burning and the rusting of iron",
        "are",
        "examples of chemical change."
    ],

これらの例は比較的マシですが、以下のようなひどい結果もあります。

    "10209": [
        "Bureau",
        "Bureau \u00e0 gradin",
        "Bureau \u00e0 gradin"
    ],

実行コマンド

実行コマンドの例は以下です。

python run_openie.py --vocab_file=/root/work/multilingual_L-12_H-768_A-12/vocab.txt --bert_config_file=/root/work/multilingual_L-12_H-768_A-12/bert_config.json --init_checkpoint=/root/work/multilingual_L-12_H-768_A-12/bert_model.ckpt --do_train=True --train_file=/root/work/bert/bert_openie/TupleInfKB/train.json --do_predict=True --predict_file=/root/work/bert/bert_openie/TupleInfKB/dev.json --train_batch_size=24 --learning_rate=3e-5 --num_train_epochs=2.0 --max_seq_length=128 --doc_stride=128 --output_dir=/root/work/output --use_tpu=False --version_2_with_negative=True

--version_2_with_negativeは、予測が不可能な場合を考慮する場合に使います。このオプションは有効化してください。

補足

  • このモデルは1文に複数のSVOが存在するケースが考慮されていません。もっともスコアの高いものだけが出力されます。
  • 疑問文が接続詞として勘違いされ、目的語扱いされてしまう場合があります。
  • 精度の評価をしていません。

 リンク

https://github.com/sugiyamath/bert_openie/

参考

[0] https://github.com/google-research/bert/ [1] http://data.allenai.org/tuple-ie/