雑記: BERTが何をしているかを掘り下げる

貼り付けた Gist の幅では まとめ の箇所が改行されるのでコピペすると以下。

モデル:
  埋め込み層:
    文章内の各トークンの単語を1024次元に埋め込む.
    文章内の各トークンの位置を1024次元に埋め込む.
    文章内の各トークンのタイプを1024次元に埋め込む(※ 今回はすべて同じタイプ).
    3つの埋め込みベクトルを足す.
    正規化して1024次元の特徴ベクトルの列にする.
  エンコーダ層:
    エンコーダ層内の0層目:
      セルフアテンション層:
        マルチヘッドアテンション:
          各特徴ベクトルを64次元に写像する(Q). ※ これを16ヘッド分やる.
          各特徴ベクトルを64次元に写像する(K). ※ これを16ヘッド分やる.
          各特徴ベクトルを64次元に写像する(V). ※ これを16ヘッド分やる.
          softmax(Q・K/√64)・V を計算する. ※ これを16ヘッド分やる.
          ここまでで各トークンが64次元の特徴になる. ※ これが16ヘッド分ある.
          16ヘッドの結果をconcatする.
          ここまでで各トークンが1024次元の特徴になる.
        各トークンごとに1024次元に全結合して正規化する.
      各トークンごとに4096次元に全結合する.
      各トークンごとに1024次元に全結合して正規化する.
    エンコーダ層内の1層目:
    エンコーダ層内の2層目:
    (中略)
    エンコーダ層内の23層目:
      (中略)
      各トークンごとに1024次元に全結合して正規化する.

→ よって,最終的に各トークンが1024次元の特徴ベクトルになる.

雑記

bert-large-cased のパラメータ数は 340M とある。
Pretrained models — transformers 3.1.0 documentation
→ 333579264 だった。
  script.py · GitHub


----------- モデルの埋め込み層 ---------- word_embeddings.weight 29691904 position_embeddings.weight 524288 token_type_embeddings.weight 2048 LayerNorm.weight 1024 LayerNorm.bias 1024 ---------- モデルのエンコーダ層 ---------- attention.self.query.weight 1048576 attention.self.query.bias 1024 attention.self.key.weight 1048576 attention.self.key.bias 1024 attention.self.value.weight 1048576 attention.self.value.bias 1024 attention.output.dense.weight 1048576 attention.output.dense.bias 1024 attention.output.LayerNorm.weight 1024 attention.output.LayerNorm.bias 1024 intermediate.dense.weight 4194304 intermediate.dense.bias 4096 output.dense.weight 4194304 output.dense.bias 1024 output.LayerNorm.weight 1024 output.LayerNorm.bias 1024 エンコーダ層内の0層目計 12596224 エンコーダ層内の1層目計 12596224 エンコーダ層内の2層目計 12596224 エンコーダ層内の3層目計 12596224 エンコーダ層内の4層目計 12596224 エンコーダ層内の5層目計 12596224 エンコーダ層内の6層目計 12596224 エンコーダ層内の7層目計 12596224 エンコーダ層内の8層目計 12596224 エンコーダ層内の9層目計 12596224 エンコーダ層内の10層目計 12596224 エンコーダ層内の11層目計 12596224 エンコーダ層内の12層目計 12596224 エンコーダ層内の13層目計 12596224 エンコーダ層内の14層目計 12596224 エンコーダ層内の15層目計 12596224 エンコーダ層内の16層目計 12596224 エンコーダ層内の17層目計 12596224 エンコーダ層内の18層目計 12596224 エンコーダ層内の19層目計 12596224 エンコーダ層内の20層目計 12596224 エンコーダ層内の21層目計 12596224 エンコーダ層内の22層目計 12596224 エンコーダ層内の23層目計 12596224 ---------- モデルのプーラー層 ---------- dense.weight 1048576 dense.bias 1024 ========== パラメータ数 ========== 333579264

雑記

一昨日と昨日の記事を Git に移行した。
GitHub - CookieBox26/ML: machine learning

◆ モデルのコンフィグレーション
BertConfig {
  "architectures": [
    "BertForMaskedLM"
  ],
  "attention_probs_dropout_prob": 0.1,
  "directionality": "bidi",
  "gradient_checkpointing": false,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 1024,
  "id2label": {
    "0": "B-corporation",
    "1": "B-creative-work",
    "2": "B-group",
    "3": "B-location",
    "4": "B-person",
    "5": "B-product",
    "6": "I-corporation",
    "7": "I-creative-work",
    "8": "I-group",
    "9": "I-location",
    "10": "I-person",
    "11": "I-product",
    "12": "O"
  },
  "initializer_range": 0.02,
  "intermediate_size": 4096,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 16,
  "num_hidden_layers": 24,
  "pad_token_id": 0,
  "pooler_fc_size": 768,
  "pooler_num_attention_heads": 12,
  "pooler_num_fc_layers": 3,
  "pooler_size_per_head": 128,
  "pooler_type": "first_token_transform",
  "type_vocab_size": 2,
  "vocab_size": 28996
}

◆ モデルの埋め込み層
BertEmbeddings(
  (word_embeddings): Embedding(28996, 1024, padding_idx=0)
  (position_embeddings): Embedding(512, 1024)
  (token_type_embeddings): Embedding(2, 1024)
  (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
  (dropout): Dropout(p=0.1, inplace=False)
)
◆ モデルのエンコーダ層(以下が24層重なっているので最初の1層だけ)
BertLayer(
  (attention): BertAttention(
    (self): BertSelfAttention(
      (query): Linear(in_features=1024, out_features=1024, bias=True)
      (key): Linear(in_features=1024, out_features=1024, bias=True)
      (value): Linear(in_features=1024, out_features=1024, bias=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
    (output): BertSelfOutput(
      (dense): Linear(in_features=1024, out_features=1024, bias=True)
      (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
      (dropout): Dropout(p=0.1, inplace=False)
    )
  )
  (intermediate): BertIntermediate(
    (dense): Linear(in_features=1024, out_features=4096, bias=True)
  )
  (output): BertOutput(
    (dense): Linear(in_features=4096, out_features=1024, bias=True)
    (LayerNorm): LayerNorm((1024,), eps=1e-12, elementwise_affine=True)
    (dropout): Dropout(p=0.1, inplace=False)
  )
)
◆ モデルのプーラー層
BertPooler(
  (dense): Linear(in_features=1024, out_features=1024, bias=True)
  (activation): Tanh()
)
◆ ドロップアウトと全結合層(New!)
Dropout(p=0.1, inplace=False)
Linear(in_features=1024, out_features=13, bias=True)
◆ 適当な文章をモデルに流してみる.→ 14トークン×13クラスの予測結果になっている(サイズが).
torch.Size([1, 14, 13])

雑記

transformers で学習済みの BERT モデルから固有表現抽出用のモデルインスタンスをつくるまでだけです。
GitHub に移行しました。GitHub - CookieBox26/ML: machine learning

コード

import torch
from transformers import (
    BertConfig,
    BertTokenizer,
    BertForTokenClassification,
)

def main():
    # 各トークンを以下の13クラスのいずれかに分類するような固有表現抽出をしたい.
    labels = [
        'B-corporation',
        'B-creative-work',
        'B-group',
        'B-location',
        'B-person',
        'B-product',
        'I-corporation',
        'I-creative-work',
        'I-group',
        'I-location',
        'I-person',
        'I-product',
        'O'
    ]
    id2label = {i: label for i, label in enumerate(labels)}
    label2id = {label: i for i, label in enumerate(labels)}

    # 利用する学習済みBERTモデルの名前を指定する.
    model_name = 'bert-large-cased'

    # 学習済みモデルに対応したトークナイザを生成する.
    tokenizer = BertTokenizer.from_pretrained(
        pretrained_model_name_or_path=model_name,
    )

    # 学習済みモデルから各トークン分類用モデルのインスタンスを生成する.
    # 設定する内容にもよるが必ずしも設定オブジェクトを生成して渡す必要はない.
    model = BertForTokenClassification.from_pretrained(
        pretrained_model_name_or_path=model_name,
        id2label=id2label,  # 各トークンに対する出力を13次元にしたいのでこれを渡す.
    )
    # 一部の重みが初期化されていませんよという警告が出るが(クラス分類する層が
    # 初期化されていないのは当然)面倒なので無視する.
    # print(model)  # 24層あるのでプリントすると長い.


    print('◆ 適当な文章をID列にしてみる.')
    sentence = 'The Empire State Building officially opened on May 1, 1931.'

    # BERT に文章を流すとき文頭に特殊トークン [CLS] 、
    # 文末に特殊トークン [SEP] が想定されている.
    # tokenizer.encode() でID列にすると勝手に付加されている.
    print('◇')
    ids = tokenizer.encode(sentence)
    for id_ in ids:
        token = tokenizer.convert_ids_to_tokens(id_)
        print(str(id_).ljust(5), tokenizer.convert_ids_to_tokens(id_))

    # 先にトークン列が手元にある場合は特殊トークンを明示的に付加する.
    print('◇')
    tokens = tokenizer.tokenize(sentence)
    tokens = [tokenizer.cls_token] + tokens + [tokenizer.sep_token]
    for token in tokens:
        id_ = tokenizer.convert_tokens_to_ids(token)
        print(str(id_).ljust(5), tokenizer.convert_ids_to_tokens(id_))

    print('◆ モデルに流してみる.→ 14トークン×13クラスの予測結果になっている(サイズが).')
    inputs = torch.tensor([tokenizer.encode(sentence)])  # ID列をテンソル化して渡す.
    outputs = model(inputs)
    print(outputs[0].size())

出力

# ここで一部の重みが初期化されていませんよという警告が出るが気にしないことにする.
◆ 適当な文章をID列にしてみる.
◇
101   [CLS]
1109  The
2813  Empire
1426  State
4334  Building
3184  officially
1533  opened
1113  on
1318  May
122   1
117   ,
3916  1931
119   .
102   [SEP]
◇
101   [CLS]
1109  The
2813  Empire
1426  State
4334  Building
3184  officially
1533  opened
1113  on
1318  May
122   1
117   ,
3916  1931
119   .
102   [SEP]
◆ モデルに流してみる.→ 14トークン×13クラスの予測結果になっている(サイズが).
torch.Size([1, 14, 13])

Python環境

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[packages]
torch = "==1.4.0"
transformers = "==3.1.0"

[requires]
python_version = "3.7.0"

雑記

transformers で学習済みの BERT モデルから固有表現抽出用のモデルインスタンスをつくるまでだけです。
改善版(2020-09-16)GitHub に移行しました。GitHub - CookieBox26/ML: machine learning

from transformers import (
    BertConfig,
    BertForTokenClassification
)

def main():
    # 各トークンを以下の13クラスのいずれかに分類するような固有表現抽出をしたい.
    labels = [
        'B-corporation',
        'B-creative-work',
        'B-group',
        'B-location',
        'B-person',
        'B-product',
        'I-corporation',
        'I-creative-work',
        'I-group',
        'I-location',
        'I-person',
        'I-product',
        'O'
    ]
    id2label = {i: label for i, label in enumerate(labels)}
    label2id = {label: i for i, label in enumerate(labels)}

    # 利用する学習済みBERTモデルの名前を指定する.
    model_name = 'bert-large-cased'

    # 設定オブジェクトをつくる.
    config = BertConfig.from_pretrained(
        # 利用する学習済みモデルは必ず教える.
        pretrained_model_name_or_path=model_name,
        # 今回は13クラスに分類する固有表現抽出をしたいので以下も教える.
        id2label=id2label,
        label2id=label2id,  # ただこれは結局渡さなくても大丈夫だった.
    )
    print(config)

    # 学習済みモデルの名前と設定オブジェクトから各トークン分類用モデルを生成する.
    model = BertForTokenClassification.from_pretrained(
        pretrained_model_name_or_path=model_name,
        config=config
    )
    print(model)  # 24層あるのでプリントすると長い.


if __name__ == '__main__':
    main()