Closed6

今更ながら「Transformers」に入門する ①GET STARTED: Quick tour

kun432kun432

インストール

パッケージインストール

!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
kun432kun432

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がデフォルトのモデルとなっている様子で、日本語には対応していないので、期待した結果とは異なっている。日本語に対応したモデルを指定してみる。

日本語の感情分析タスク向けにファインチューニングされた以下のモデルを使用させていただく。

https://huggingface.co/christian-phu/bert-finetuned-japanese-sentiment

このモデルは日本語のトークナイズに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

データセットの全データに対してまとめてタスク処理を行うこともできる。自動音声認識タスクの例。

日本語の音声認識モデルとして以下を使用する。

https://huggingface.co/NTQAI/wav2vec2-large-japanese

import torch
from transformers import pipeline

speech_recognizer = pipeline("automatic-speech-recognition", model="NTQAI/wav2vec2-large-japanese")

日本語の音声データセットをダウンロードする。以下のデータセットを使用する。なお、本データセットは利用規約に同意する必要がある。

https://huggingface.co/datasets/mozilla-foundation/common_voice_17_0

データ量が多いので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}]
kun432kun432

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
text
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
------------------------------
kun432kun432

Quick Tourの残りは学習なので一旦スキップ

Tutorialsでもう少し深堀りしていく。

このスクラップは6ヶ月前にクローズされました