今更ながら「Transformers」に入門する ①GET STARTED: Quick tour
雰囲気で適当にやっているので、一応Quick tourをやっておこうと。
Colaboratoryで。ランタイムはT4。
よくわからんところがあってもとりあえず進める。あと色々なモデルがでてくるが、なるだけ日本語で使えるものを試す。
インストール
パッケージインストール
!pip install transformers datasets evaluate accelerate
!pip install torch
!pip freeze | egrep -i "transformers|datasets|evaluate|accelerate|torch"
accelerate==1.2.1
datasets==3.2.0
evaluate==0.4.3
sentence-transformers==3.3.1
tensorflow-datasets==4.9.7
torch @ https://download.pytorch.org/whl/cu121_full/torch-2.5.1%2Bcu121-cp310-cp310-linux_x86_64.whl
torchaudio @ https://download.pytorch.org/whl/cu121/torchaudio-2.5.1%2Bcu121-cp310-cp310-linux_x86_64.whl
torchsummary==1.5.1
torchvision @ https://download.pytorch.org/whl/cu121/torchvision-0.20.1%2Bcu121-cp310-cp310-linux_x86_64.whl
transformers==4.47.1
vega-datasets==0.9.0
Pipeline
事前学習モデルを推論で使う場合の最も簡単な方法。タスクを指定するだけで簡単に使えるようになっている。
例えば感情分析の場合はsentiment-analysis
をタスクとして指定するだけ。
from transformers import pipeline
classifier = pipeline("sentiment-analysis")
classifier("私たちは🤗 Transformersライブラリをお見せできてとても嬉しいです。")
No model was supplied, defaulted to distilbert/distilbert-base-uncased-finetuned-sst-2-english and revision 714eb0f (https://huggingface.co/distilbert/distilbert-base-uncased-finetuned-sst-2-english).
Using a pipeline without specifying a model name and revision in production is not recommended.
config.json: 100%
629/629 [00:00<00:00, 43.6kB/s]
model.safetensors: 100%
268M/268M [00:01<00:00, 213MB/s]
tokenizer_config.json: 100%
48.0/48.0 [00:00<00:00, 2.83kB/s]
vocab.txt: 100%
232k/232k [00:00<00:00, 3.67MB/s]
Device set to use cuda:0
[{'label': 'NEGATIVE', 'score': 0.8953204154968262}]
ドキュメントにあるように、何もモデルを指定しなくても、一応動作する。今回のテキスト分類の場合には、distilbert/distilbert-base-uncased-finetuned-sst-2-english
がデフォルトのモデルとなっている様子で、日本語には対応していないので、期待した結果とは異なっている。日本語に対応したモデルを指定してみる。
日本語の感情分析タスク向けにファインチューニングされた以下のモデルを使用させていただく。
このモデルは日本語のトークナイズにfugashiとunidic-liteを使っているのでインストール。
!pip install fugashi unidic-lite
from transformers import pipeline
classifier = pipeline("sentiment-analysis", model="christian-phu/bert-finetuned-japanese-sentiment")
classifier("私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。")
config.json: 100%
922/922 [00:00<00:00, 59.0kB/s]
pytorch_model.bin: 100%
445M/445M [00:05<00:00, 91.0MB/s]
tokenizer_config.json: 100%
552/552 [00:00<00:00, 6.46kB/s]
vocab.txt: 100%
236k/236k [00:00<00:00, 5.15MB/s]
special_tokens_map.json: 100%
125/125 [00:00<00:00, 3.18kB/s]
Device set to use cuda:0
[{'label': 'positive', 'score': 0.9979897737503052}]
複数の入力の場合はリストで渡すことができる。
results = classifier([
"私たちは🤗 Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
])
for result in results:
print(f"ラベル: {result['label']}, スコア: {round(result['score'], 4)}")
ラベル: positive, スコア: 0.998
ラベル: negative, スコア: 0.5882
データセットの全データに対してまとめてタスク処理を行うこともできる。自動音声認識タスクの例。
日本語の音声認識モデルとして以下を使用する。
import torch
from transformers import pipeline
speech_recognizer = pipeline("automatic-speech-recognition", model="NTQAI/wav2vec2-large-japanese")
日本語の音声データセットをダウンロードする。以下のデータセットを使用する。なお、本データセットは利用規約に同意する必要がある。
データ量が多いので10分ぐらい時間がかかる・・・
from datasets import load_dataset, Audio
dataset = load_dataset("mozilla-foundation/common_voice_17_0", name="ja", split="train", trust_remote_code=True)
データセットの構造を確認
dataset
Dataset({
features: ['client_id', 'path', 'audio', 'sentence', 'up_votes', 'down_votes', 'age', 'gender', 'accent', 'locale', 'segment', 'variant'],
num_rows: 10039
})
音声データ呼び出し時に、音声認識モデルがトレーニングで使用しているサンプリングレートに変換するようにする。
dataset = dataset.cast_column("audio", Audio(sampling_rate=speech_recognizer.feature_extractor.sampling_rate)) # モデルが期待するのは16000Hz
データセットの最初の4つの音声データをモデルに渡して、テキストを取得する。
result = speech_recognizer(dataset[:4]["audio"])
print([d["text"] for d in result])
['もう向こうかわら自電車が先きのとし走走走ってきた', '今年も中継とする', '自からそれを選んだわけではないのですが', 'おりどおそう']
データセットには正しいテキストも含まれているので見てみる。
dataset[:4]["sentence"]
['向こうから自転車が避けようともせず走ってきた', '今年のお中元どうする?', '自らそれを選んだわけではないのですが、', 'ごゆっくりどうぞ。']
微妙に異なるのだが、大まかに書き起こしができているのがわかる。
モデルとトークナイザーを個別にロードして、パイプラインに渡すこともできる。最初の感情分析モデルを使用した例。
from transformers import AutoTokenizer, AutoModelForSequenceClassification
model_name = "christian-phu/bert-finetuned-japanese-sentiment"
model = AutoModelForSequenceClassification.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
classifier = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
classifier("私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。")
Device set to use cuda:0
[{'label': 'positive', 'score': 0.9979897737503052}]
AutoClass
AutoClassを使うと、学習済みモデルのアーキテクチャを名前orパスから自動的に取得することができる。
-
AutoTokenizer
: トークナイザーをモデルからロードする -
AutoModel
: モデルをロードする。タスクによってAutoModel***
みたいな感じでクラスが用意されている。
AutoTokenizer
AutoTokenizer単体で使ってみる。
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import pprint
model_name = "christian-phu/bert-finetuned-japanese-sentiment"
tokenizer = AutoTokenizer.from_pretrained(model_name)
encoding = tokenizer(["私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。"])
pprint.pprint(encoding)
{
'input_ids': [2, 3946, 11288, 897, 1, 29141, 23694, 12655, 24575, 932, 860, 12113, 11187, 888, 16211, 1945, 11265, 12461, 829, 3],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
-
input_ids
: トークンの数値表現。 -
attention_mask
: どのトークンにアテンションを向けるかを示す。
リストを渡してみる。
encoding = tokenizer([
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
])
print(encoding)
{
'input_ids': [
[2, 3946, 11288, 897, 1, 29141, 23694, 12655, 24575, 932, 860, 12113, 11187, 888, 16211, 1945, 11265, 12461, 829, 3],
[2, 29141, 23694, 12655, 828, 16669, 11148, 890, 15333, 11148, 11143, 862, 11396, 873, 5591, 6307, 13404, 888, 26110, 888, 854, 12343, 1, 3]
],
'token_type_ids': [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
'attention_mask': [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
}
長さが違うのがわかる。これを揃えることができる。
パディングを有効にすると、短い文章をパディングトークン([PAD]
)で埋めて、長い文章に揃えるようになる。
pt_batch = tokenizer(
[
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
],
padding=True
)
print(pt_batch)
{
'input_ids': [
[2, 3946, 11288, 897, 1, 29141, 23694, 12655, 24575, 932, 860, 12113, 11187, 888, 16211, 1945, 11265, 12461, 829, 3, 0, 0, 0, 0],
[2, 29141, 23694, 12655, 828, 16669, 11148, 890, 15333, 11148, 11143, 862, 11396, 873, 5591, 6307, 13404, 888, 26110, 888, 854, 12343, 1, 3]
],
'token_type_ids': [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
'attention_mask': [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
}
最初の文の最後に0が追加されていて長さが揃っているのがわかる。
最大長を指定することもできる。最大長を超えたものは切り詰め(truncation
)もできる。
batch = tokenizer(
[
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
],
padding=True,
max_length=512,
truncation=True,
)
print(batch)
元の長さがmax_lengthほど長くないので結果は同じになる。割愛。
AutoModel
AutoModelでモデルをロードする。タスクにあわせてクラスを選択する必要がある。感情分析の場合はAutoModelForSequenceClassification
となる。
from transformers import AutoTokenizer, AutoModelForSequenceClassification
model_name = "christian-phu/bert-finetuned-japanese-sentiment"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)
入力をトークナイザーに渡す。
batch = tokenizer(
[
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
],
padding=True,
max_length=512,
truncation=True,
)
トークナイザー処理済みのバッチをモデルに渡す。
outputs = model(**batch)
outputs
エラーになる。。。
AttributeError: 'list' object has no attribute 'size'
調べてみると、PytorchモデルはPyTorchテンソル形式での入力を期待するらしい。上のトークナイザーの出力はPythonのリストになっている。なのでPyTorchテンソルに変換する必要がある。
やり方は2つ。
トークナイザーにreturn_tensors="pt"
を付与して、トークナイザーの出力そのものをPyTorchテンソル形式で出力させる
batch = tokenizer(
[
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
],
padding=True,
max_length=512,
truncation=True,
return_tensors="pt", # 追加
)
もしくはPythonのリスト形式のトークナイザーの出力をPyTorchテンソル形式に変換する。
import torch
batch = tokenizer(
[
"私たちは🤗Transformersライブラリをお見せできてとても嬉しいです。",
"Transformers、覚えないといけないことが多いし難しすぎて泣いています😭"
],
padding=True,
max_length=512,
truncation=True,
)
batch = {key: torch.tensor(val) for key, val in batch.items()}
return_tensors="pt"
のほうが楽かな。どちらの場合でもこれでモデルに渡すことができる。
outputs = model(**batch)
outputs
SequenceClassifierOutput(loss=None, logits=tensor([[-1.7718, -2.1225, 4.9688],
[ 1.3180, 0.8727, -1.5044]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)
logitsがモデルの出力になるが、これは単なるスコアであって、これを確率の分布に変換する必要があるらしい。そこでsoftmaxを使う。
from torch import nn
predictions = nn.functional.softmax(outputs.logits, dim=-1)
predictions
tensor([[1.1796e-03, 8.3060e-04, 9.9799e-01],
[5.8820e-01, 3.7683e-01, 3.4977e-02]], grad_fn=<SoftmaxBackward0>)
これで各文章ごとの、ネガティブ度・中立度・ポジティブ度の確率が出力されるようになる。
for i, pred in enumerate(predictions):
print(f"結果:")
for label_id, score in enumerate(pred):
label = model.config.id2label[label_id] # model.config.id2labelに分類ID/ラベルの辞書がある
print(f" - {label}: {score.item():.4f}")
print("-" * 30)
結果:
- negative: 0.0012
- neutral: 0.0008
- positive: 0.9980
------------------------------
結果:
- negative: 0.5882
- neutral: 0.3768
- positive: 0.0350
------------------------------
Quick Tourの残りは学習なので一旦スキップ
Tutorialsでもう少し深堀りしていく。