はじめに
スカイウォーカー! nikkieです。
えぬえるぴーやな素振り記事です。
るうく(LUKE)なるものが面妖なので触っていきます
目次
動作環境
- Python 3.10.9
pip install 'transformers[torch,sentencepiece]'
で以下が入りました(主要なものをリストアップ)- numpy==1.25.2
- sentencepiece==0.1.99
- tokenizers==0.13.3
- torch==2.0.1
- transformers==4.33.1
LUKEって何よ
この記事の執筆時点では全然説明できません。
Language Understanding with Knowledge-based Embeddingsの略でLUKE。
エンティティの情報もマスクして学習してる言語モデルみたいなんですが、今はこれ以上は説明できないですね...
説明できるようになれそうな積ん読
新しい概念を論文や解説スライドから理解するのは得意ではないので、得意分野のコーディングに引きずり込んで触ってみることにしました。
transformersのドキュメントの例
LukeForEntitySpanClassification
https://huggingface.co/docs/transformers/v4.33.0/en/model_doc/luke#transformers.LukeForEntitySpanClassification
conll2003データセットでファインチューニングされたLUKEを読み込んで
「Beyoncé lives in Los Angeles」(ビヨンセはロサンゼルスに住んでいます)を固有表現認識します。
対話モードでの例を、スクリプト化しました。
スクリプトを実行すると
Beyoncé PER Los Angeles LOC
Beyoncé(人名)、Los Angeles(地名)が認識できていますね!
深掘り
スクリプトを-i
オプション付きで実行して、気になる箇所を見ていきます。
テキスト中の単語に関するインデックス
text = "Beyoncé lives in Los Angeles" word_start_positions = [0, 8, 14, 17, 21] word_end_positions = [7, 13, 16, 20, 28]
単語の開始と終了のインデックスです。
終了のインデックスは単語に含まないので、英文中の空白文字を指していますね
>>> for start_pos, end_pos in zip(word_start_positions, word_end_positions): ... print(text[start_pos:end_pos]) ... Beyoncé lives in Los Angeles
EntitySpan
EntitySpanなるものを扱います。
entity_spans = [] for i, start_pos in enumerate(word_start_positions): for end_pos in word_end_positions[i:]: entity_spans.append((start_pos, end_pos))
作られたentity_spans
の中身を見ると
>>> len(entity_spans) 15 >>> for span in entity_spans: ... print(text[span[0]:span[1]]) ... Beyoncé Beyoncé lives Beyoncé lives in Beyoncé lives in Los Beyoncé lives in Los Angeles lives lives in lives in Los lives in Los Angeles in in Los in Los Angeles Los Los Angeles Angeles
複数の単語を1つのスパンにまとめています。
「Beyoncé lives in Los Angeles」で取りうるスパンを全列挙した形になっています。
モデルはEntitySpanを分類
inputs = tokenizer(text, entity_spans=entity_spans, return_tensors="pt") outputs = model(**inputs) logits = outputs.logits predicted_class_indices = logits.argmax(-1).squeeze().tolist()
>>> model.config.id2label # EntitySpanを5クラス分類 {0: 'NIL', 1: 'MISC', 2: 'PER', 3: 'ORG', 4: 'LOC'} >>> inputs["input_ids"] # 12674は' Beyon'、12695は'cé' tensor([[ 0, 12674, 12695, 1074, 11, 1287, 1422, 2]]) >>> inputs["entity_start_positions"] # torch.Size([1, 15]) tensor([[1, 1, 1, 1, 1, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6]]) >>> inputs["entity_end_positions"] tensor([[2, 3, 4, 5, 6, 3, 4, 5, 6, 4, 5, 6, 5, 6, 6]]) >>> logits.size() # 15はすべてのEntitySpan torch.Size([1, 15, 5]) >>> predicted_class_indices # スコア最大のクラス(のインデックス) [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0]
LukeForEntitySpanClassification
という名前の通り、EntitySpanを分類しました。
- 「Beyoncé」というEntitySpanはクラス2(PER)
- 「Los Angeles」というEntitySpanはクラス4(LOC)
感想(終わりにに代えて)
書籍『大規模言語モデル入門』では、BERT-CRFを使って固有表現認識しました。
そのときはトークン単位でBIOのラベルを付与しました。
Bのトークンとその後のIのトークンを拾って、1つの固有表現とします。
それに対して、今回触ったLUKEはEntitySpanを分類します。
BIOのラベルは不要で、上で見たような5クラスのみ。
「これでうまくいくんだ〜」とか「モデルの中は何をやっているんだろう?」と興味は尽きない感じです。
また素振りしてみよう