はじめに
ファインチューニング、ばーっといってみよー! nikkieです
えぬえるぴーやな素振り記事です。
チュートリアルに沿ってtransformersを使ってコーディングしました
目次
transformersのToken Classificationチュートリアル
こちらです。
Colabで開くリンクはこちら1
https://colab.research.google.com/github/huggingface/notebooks/blob/main/examples/token_classification.ipynb
固有表現認識(NER)のタスクのデータセットであるconll2003を使って
distilbert-base-uncasedをfine tuneします。
このノートブックはNER以外のトークン分類タスクにも適用できる実装となっていました(冒頭のtask
変数で指定)。
動作環境
ローカル環境で写経した後、Colabで再実行しています。
ローカル環境は、Python 3.10.9。
仮想環境にpip install datasets 'transformers[torch]' seqeval evaluate
して以下が入りました
- datasets==2.14.5
- evaluate==0.4.0
- seqeval==1.2.2
- tokenizers==0.13.3
- torch==2.0.1
- transformers==4.33.1
GPUが使いたかったのでColabで動かしました(Python 3.10.12)。
出力を抑えたのでライブラリのバージョンは確認できませんが、同様のバージョンと思います。
動かしたノートブックはこちら!
チュートリアルでの学び
チュートリアルは3部構成です
このチュートリアルに取り組むまで知らなかった点をアウトプットします
単語で分割済みのデータセットのトークナイズ
>>> from datasets import load_dataset >>> datasets = load_dataset("conll2003") >>> example = datasets["train"][4]
conll2003データセットは単語に分割済みです。
>>> from pprint import pprint >>> pprint(example) {'chunk_tags': [11, 11, 12, 13, 11, 12, 12, 11, 12, 12, 12, 12, 21, 13, 11, 12, 21, 22, 11, 13, 11, 1, 13, 11, 17, 11, 12, 12, 21, 1, 0], 'id': '4', 'ner_tags': [5, 0, 0, 0, 0, 3, 4, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0], 'pos_tags': [22, 27, 21, 35, 12, 22, 22, 27, 16, 21, 22, 22, 38, 15, 22, 24, 20, 37, 21, 15, 24, 16, 15, 22, 15, 12, 16, 21, 38, 17, 7], 'tokens': ['Germany', "'s", 'representative', 'to', 'the', 'European', 'Union', "'s", 'veterinary', 'committee', 'Werner', 'Zwingmann', 'said', 'on', 'Wednesday', 'consumers', 'should', 'buy', 'sheepmeat', 'from', 'countries', 'other', 'than', 'Britain', 'until', 'the', 'scientific', 'advice', 'was', 'clearer', '.']}
トークナイザの__call__
メソッドでは、is_split_into_words
を使って、単語に分割されていてもトークナイズできることを知りました
>>> model_checkpoint = "distilbert-base-uncased" >>> tokenizer = AutoTokenizer.from_pretrained(model_checkpoint) >>> tokenizer("Hello, this is example") == tokenizer(["Hello", ",", "this", "is", "example"], is_split_into_words=True) True
単語単位の正解ラベルとのアラインメント
トークナイザは単語(word)より小さい単位のsubwordでトークナイズします。
そのため、データセットの単語数よりも多くのトークンに分かれます。
ここでデータセットは単語1つ1つにラベル(今回はner_tags)が付与されています。
そのため、subwordに分割すると、ラベルの数よりもトークンの数が多くなります。
>>> tokenized_input = tokenizer(example["tokens"], is_split_into_words=True) >>> len(example["tokens"]), len(example["ner_tags"]), len(tokenized_input["input_ids"]) (31, 31, 39) >>> # tokenized_input["input_ids"]はsubword単位で分割され、元のトークン数=ラベル数より増えている
そこで、元が同じ単語のsubwordは同じラベルとするようにアラインメントをとります2。
その実装にトークナイザの出力のword_ids
メソッドが使えることを知りました!
>>> tokenized_input.word_ids() # 1,1や18,18,18が元は同じ単語 [None, 0, 1, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 11, 12, 13, 14, 15, 16, 17, 18, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, None] >>> aligned_labels = [-100 if i is None else example["ner_tags"][i] for i in tokenized_input.word_ids()] >>> len(aligned_labels) 39
Trainerインスタンスの初期化
fine tuneにはtransformersのTrainerインスタンスを初期化して使います。
初期化するのに必要なのは3点
- TrainingArguments
- バッチサイズやエポック数など
- data collator
- tokenizerを渡してつくる
- 評価指標の算出に使う関数
- evaluateのインターフェースでseqevalを使った
trainer = Trainer( model, args, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["validation"], data_collator=data_collator, tokenizer=tokenizer, compute_metrics=compute_metrics, )
終わりに
transformersのToken Classificationチュートリアルを写経しました。
conll2003でfine tuneして、trainの3エポック目、evaluate、validationデータのpredictで評価指標の値が一致しているので、チュートリアルのとおりに動かせたと思います🙌
transformersはNLP関連の書籍で触っていましたが、このチュートリアルはかなりかゆいところに手が届く内容でした。
is_split_into_words
引数やword_ids
メソッドを知られた3ので、conll2003以外のデータセットでも手を動かしてみたいと思っています!