📋 このステップで学ぶこと
Hugging Face Transformersライブラリの基本と利点
事前学習済みBERTモデルの読み込み方法
トークナイザーの仕組みと各パラメータの意味
IMDBデータセットの準備とトークン化
Trainer APIを使ったファインチューニング
モデルの評価、保存、推論の実装
日本語BERTの使い方
練習問題: 5問
⚠️ 実行環境について
このステップのコードはGoogle Colab(GPU必須) で実行してください。
「ランタイム」→「ランタイムのタイプを変更」→「GPU」を選択
🤗 1. Hugging Face Transformersとは
Hugging Face Transformers は、BERTやGPTなどの事前学習済みモデルを
簡単に使えるようにしたPythonライブラリです。
このライブラリのおかげで、数行のコードでBERTを使ったアプリケーションが作れます。
1-1. なぜHugging Faceを使うのか
【従来の方法(とても大変!)】
BERTを使いたい場合、以前は:
1. 論文を読んでアーキテクチャを理解
→ 数日かかる
2. PyTorchでモデルを実装
→ 数百行のコード、数日〜1週間
3. Googleから重みファイルをダウンロード
→ ファイル形式の変換が必要
4. 重みをモデルにロード
→ 次元の確認など面倒
5. トークナイザーを実装
→ WordPieceの実装、語彙ファイルの読み込み
6. 前処理コードを書く
→ [CLS]、[SEP]の追加、パディングなど
→ 合計で数週間かかることも…
【Hugging Faceを使う方法(とても簡単!)】
BERTを使いたい場合:
1. pip install transformers
2. tokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased’)
model = BertForSequenceClassification.from_pretrained(‘bert-base-uncased’)
3. すぐ使える!
→ たった3行、数分で完了!
【Hugging Faceの主要機能】
■ Transformersライブラリ
・BERT、GPT、T5など数万のモデルを提供
・統一されたAPI(使い方が同じ)
・PyTorchとTensorFlow両対応
■ Hugging Face Hub
・モデル共有プラットフォーム
・40万以上のモデルが無料で公開
・世界中の研究者がモデルを共有
■ Datasetsライブラリ
・IMDb、SQuADなど数千のデータセット
・1行でダウンロード可能
1-2. ライブラリのインストール
Google Colabで以下のコマンドを実行して、必要なライブラリをインストールします。
※モバイルでは横スクロールできます
# ========================================
# 必要なライブラリのインストール
# ========================================
# transformersライブラリをインストール
# transformers: BERTなどの事前学習モデルを使うためのライブラリ
# ==4.35.0: バージョンを固定(互換性のため)
!pip install transformers==4.35.0
# datasetsライブラリをインストール
# datasets: IMDbなどのデータセットを簡単に読み込むためのライブラリ
!pip install datasets==2.14.0
# accelerateライブラリをインストール
# accelerate: 訓練を高速化するライブラリ(Trainerが内部で使用)
!pip install accelerate==0.24.0
# scikit-learnをインストール
# scikit-learn: 評価指標(精度、F1スコアなど)を計算するライブラリ
!pip install scikit-learn
1-3. GPU環境の確認
BERTのファインチューニングにはGPU が必要です。
以下のコードでGPUが使えるか確認しましょう。
# ========================================
# GPU環境の確認
# ========================================
# PyTorchをインポート
# torch: ディープラーニングのフレームワーク
import torch
# PyTorchのバージョンを表示
# なぜ確認: バージョンによって機能が異なるため
print(f”PyTorch version: {torch.__version__}”)
# CUDAが利用可能かチェック
# CUDA: NVIDIAのGPU計算用ライブラリ
# True = GPUが使える、False = 使えない
print(f”CUDA available: {torch.cuda.is_available()}”)
# GPUが使える場合の詳細情報
if torch.cuda.is_available():
# GPU名を取得
# get_device_name(0): 0番目のGPUの名前
print(f”GPU: {torch.cuda.get_device_name(0)}”)
# GPUメモリ容量をGB単位で表示
# total_memory: 総メモリ量(バイト単位)
# / 1e9: バイト → ギガバイトに変換
memory_gb = torch.cuda.get_device_properties(0).total_memory / 1e9
print(f”GPU Memory: {memory_gb:.2f} GB”)
else:
# GPUが使えない場合の警告
print(“⚠️ GPUが利用できません”)
print(“ランタイム → ランタイムのタイプを変更 → GPU を選択してください”)
実行結果(GPUが利用可能な場合):
PyTorch version: 2.1.0+cu118
CUDA available: True
GPU: Tesla T4
GPU Memory: 15.00 GB
⚠️ GPUが利用できない場合
Google Colabで「ランタイム」→「ランタイムのタイプを変更」→「GPU」を選択してください。
無料枠ではTesla T4(15GB)が使えます。BERT-Baseなら十分動作します。
📦 2. モデルとトークナイザーの読み込み
Hugging Faceでは、モデル とトークナイザー の2つを読み込みます。
この2つはセットで使います。
2-1. モデルとトークナイザーの役割
【処理の流れを理解する】
入力テキスト: “I love this movie!”
↓ ① トークナイザー(文字列 → 数値への変換)
[トークン化]
“I love this movie!”
↓
[“i”, “love”, “this”, “movie”, “!”]
[ID変換]
[“i”, “love”, “this”, “movie”, “!”]
↓
[1045, 2293, 2023, 3185, 999]
[特殊トークン追加]
[1045, 2293, 2023, 3185, 999]
↓
[101, 1045, 2293, 2023, 3185, 999, 102]
↑CLS ↑SEP
↓ ② BERTモデル(数値 → ベクトルへの変換)
各トークンを768次元のベクトルに変換
[101, 1045, …] → [[0.1, 0.2, …], [0.3, -0.1, …], …]
↓ ③ 分類層(ベクトル → ラベルへの変換)
[CLS]トークンのベクトル(768次元)
↓
全結合層(768次元 → 2次元)
↓
Softmax
↓
[Negative: 0.05, Positive: 0.95]
↓
予測: Positive
【なぜトークナイザーが必要?】
コンピュータは「文字列」を直接理解できません。
数値に変換する必要があります。
BERTの語彙(ボキャブラリー):
・約30,000語を記憶している
・各単語にユニークなIDを割り当て
・例: “love” → ID 2293
・例: “movie” → ID 3185
・未知語は「サブワード」に分割
2-2. トークナイザーの読み込み
まず、BERTのトークナイザーを読み込みます。
# ========================================
# トークナイザーの読み込み
# ========================================
# BertTokenizerクラスをインポート
# BertTokenizer: BERT専用のトークナイザー
from transformers import BertTokenizer
# 使用するモデルの名前を変数に格納
# bert-base-uncased の意味:
# – bert: モデルの種類
# – base: 12層、110Mパラメータ(largeは24層、340M)
# – uncased: 大文字小文字を区別しない(”Love”も”love”も同じ)
model_name = ‘bert-base-uncased’
# トークナイザーを読み込み
# from_pretrained: 事前学習済みのトークナイザーをダウンロード
# 初回実行時: 自動でダウンロード(約200MB)
# 2回目以降: キャッシュから読み込み(高速)
tokenizer = BertTokenizer.from_pretrained(model_name)
# トークナイザーの情報を表示
print(f”Tokenizer loaded: {model_name}”)
print(f”Vocabulary size: {tokenizer.vocab_size}”)
実行結果:
Tokenizer loaded: bert-base-uncased
Vocabulary size: 30522
語彙サイズは30,522語です。BERTはこの語彙内の単語をIDに変換します。
2-3. トークン化の流れを理解する
トークナイザーが何をしているか、ステップごとに確認しましょう。
ステップ1: テキストをトークンに分割
# ========================================
# ステップ1: トークン化(文字列 → トークンのリスト)
# ========================================
# サンプルテキスト
text = “I love natural language processing!”
# tokenize(): テキストをトークン(単語/サブワード)のリストに分割
# なぜ使う: テキストを小さな単位に分解するため
tokens = tokenizer.tokenize(text)
print(“Original text:”, text)
print(“Tokens:”, tokens)
print(“Number of tokens:”, len(tokens))
実行結果:
Original text: I love natural language processing!
Tokens: [‘i’, ‘love’, ‘natural’, ‘language’, ‘processing’, ‘!’]
Number of tokens: 6
「I」が「i」に変換されています。これはuncased モデルのため、
全て小文字に変換されるからです。
ステップ2: トークンをIDに変換
# ========================================
# ステップ2: トークン → ID変換
# ========================================
# convert_tokens_to_ids(): トークンを語彙内のIDに変換
# なぜ使う: モデルは数値しか処理できないため
token_ids = tokenizer.convert_tokens_to_ids(tokens)
print(“Tokens:”, tokens)
print(“Token IDs:”, token_ids)
# IDとトークンの対応を確認
print(“\n— Token → ID の対応 —“)
for token, tid in zip(tokens, token_ids):
print(f” ‘{token}’ → ID {tid}”)
実行結果:
Tokens: [‘i’, ‘love’, ‘natural’, ‘language’, ‘processing’, ‘!’]
Token IDs: [1045, 2293, 3019, 2653, 6364, 999]
— Token → ID の対応 —
‘i’ → ID 1045
‘love’ → ID 2293
‘natural’ → ID 3019
‘language’ → ID 2653
‘processing’ → ID 6364
‘!’ → ID 999
ステップ3: 特殊トークンを追加
# ========================================
# ステップ3: 特殊トークンを含めたエンコード
# ========================================
# encode(): トークン化 + ID変換 + 特殊トークン追加 を一度に実行
# なぜ使う: [CLS]と[SEP]を自動で追加してくれる
encoded = tokenizer.encode(text)
print(“Encoded (with special tokens):”, encoded)
# IDをトークンに戻して確認
# convert_ids_to_tokens(): IDをトークンに逆変換
decoded_tokens = tokenizer.convert_ids_to_tokens(encoded)
print(“Decoded tokens:”, decoded_tokens)
# 特殊トークンの説明
print(“\n— 特殊トークンの意味 —“)
print(” [CLS] (ID=101): 文の先頭を示す。分類タスクで使用”)
print(” [SEP] (ID=102): 文の区切りを示す。文の終わりに追加”)
実行結果:
Encoded (with special tokens): [101, 1045, 2293, 3019, 2653, 6364, 999, 102]
Decoded tokens: [‘[CLS]’, ‘i’, ‘love’, ‘natural’, ‘language’, ‘processing’, ‘!’, ‘[SEP]’]
— 特殊トークンの意味 —
[CLS] (ID=101): 文の先頭を示す。分類タスクで使用
[SEP] (ID=102): 文の区切りを示す。文の終わりに追加
2-4. 実際の使い方(推奨方法)
実際には、tokenizer() を直接呼び出すのが最も便利です。
パディングや切り詰めも自動で行えます。
# ========================================
# 推奨される使い方: tokenizer()を直接呼び出す
# ========================================
# tokenizer()を直接呼び出す(最も便利な方法)
inputs = tokenizer(
text, # 入力テキスト
padding=’max_length’, # パディング: max_lengthまで0で埋める
truncation=True, # 切り詰め: max_lengthを超えたらカット
max_length=20, # 最大長: 20トークン(例として短めに設定)
return_tensors=’pt’ # 戻り値の形式: PyTorchテンソル
# ‘pt’=PyTorch, ‘tf’=TensorFlow, ‘np’=NumPy
)
# 返り値の確認
print(“Output keys:”, inputs.keys())
print()
# input_idsの確認
print(“— input_ids —“)
print(f”Shape: {inputs[‘input_ids’].shape}”) # (バッチサイズ, 系列長)
print(f”Values: {inputs[‘input_ids’]}”)
print()
# attention_maskの確認
print(“— attention_mask —“)
print(f”Shape: {inputs[‘attention_mask’].shape}”)
print(f”Values: {inputs[‘attention_mask’]}”)
実行結果:
Output keys: dict_keys([‘input_ids’, ‘token_type_ids’, ‘attention_mask’])
— input_ids —
Shape: torch.Size([1, 20])
Values: tensor([[ 101, 1045, 2293, 3019, 2653, 6364, 999, 102, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
— attention_mask —
Shape: torch.Size([1, 20])
Values: tensor([[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
💡 出力の意味を理解する
input_ids : トークンのID列。末尾の0はパディング [PAD]
attention_mask : 1=有効なトークン、0=パディング(Attentionで無視される)
token_type_ids : 文A/文Bの区別。単一文では全て0
Shape [1, 20] : 1文、20トークン(バッチサイズ1、系列長20)
2-5. モデルの読み込み
次に、分類タスク用のBERTモデルを読み込みます。
# ========================================
# モデルの読み込み
# ========================================
# BertForSequenceClassificationクラスをインポート
# BertForSequenceClassification: 文分類タスク用のBERT
# 通常のBERTに分類用の全結合層が追加されている
from transformers import BertForSequenceClassification
# モデルを読み込み
# from_pretrained: 事前学習済みの重みをダウンロード
# num_labels=2: 2クラス分類(Positive/Negative)
# 3クラスなら num_labels=3
model = BertForSequenceClassification.from_pretrained(
model_name,
num_labels=2
)
# モデルをGPUに転送
# cuda: NVIDIA GPU
# cpu: CPU(GPUがない場合)
device = torch.device(‘cuda’ if torch.cuda.is_available() else ‘cpu’)
model = model.to(device)
# モデルの情報を表示
print(f”Model: {model_name}”)
print(f”Device: {device}”)
# パラメータ数を計算
# p.numel(): 各パラメータの要素数
# sum(): 全パラメータの要素数を合計
total_params = sum(p.numel() for p in model.parameters())
print(f”Total parameters: {total_params:,}”)
実行結果:
Model: bert-base-uncased
Device: cuda
Total parameters: 109,483,778
約1.1億パラメータのモデルが読み込まれました。
💡 利用可能なBERTモデル一覧
英語 : bert-base-uncased(110M)、bert-large-uncased(340M)
日本語 : cl-tohoku/bert-base-japanese-whole-word-masking(110M)
多言語 : bert-base-multilingual-cased(110M、104言語対応)
BaseとLargeの違い : Largeは精度が高いが、3倍のメモリが必要
📊 3. データセットの準備
今回はIMDBデータセット を使って感情分析を行います。
IMDBは映画レビューのデータセットで、PositiveとNegativeの2クラス分類です。
3-1. IMDBデータセットとは
【IMDBデータセットの概要】
■ 内容
・映画レビューのテキスト(英語)
・感情ラベル
– 0 = Negative(否定的)
– 1 = Positive(肯定的)
■ データ数
・訓練データ: 25,000件
・テストデータ: 25,000件
・合計: 50,000件
■ データの特徴
・レビューは比較的長い(平均200語程度)
・Positive/Negative各12,500件(バランス良好)
・実際の映画レビューなので自然な文章
■ 例
Positiveの例:
“This movie is absolutely fantastic! The acting was
superb and the storyline kept me engaged throughout.”
ラベル: 1
Negativeの例:
“Terrible waste of time. The plot made no sense and
the acting was wooden. Do not watch this film.”
ラベル: 0
3-2. データセットの読み込み
# ========================================
# データセットの読み込み
# ========================================
# load_datasetをインポート
# load_dataset: Hugging Faceのデータセットを簡単に読み込む関数
from datasets import load_dataset
# IMDBデータセットを読み込み
# 初回実行時: 自動でダウンロード(約80MB)
# 2回目以降: キャッシュから読み込み
dataset = load_dataset(‘imdb’)
# データセットの構造を確認
print(“Dataset structure:”)
print(dataset)
print()
# 訓練データとテストデータの件数
print(f”Train examples: {len(dataset[‘train’])}”)
print(f”Test examples: {len(dataset[‘test’])}”)
実行結果:
Dataset structure:
DatasetDict({
train: Dataset({
features: [‘text’, ‘label’],
num_rows: 25000
})
test: Dataset({
features: [‘text’, ‘label’],
num_rows: 25000
})
})
Train examples: 25000
Test examples: 25000
3-3. データの中身を確認
# ========================================
# データの中身を確認
# ========================================
# 最初のサンプルを表示
print(“— First example —“)
print(f”Label: {dataset[‘train’][0][‘label’]}”)
print(f”Text (first 200 chars):”)
print(f”{dataset[‘train’][0][‘text’][:200]}…”)
print()
# ラベルの分布を確認
# なぜ確認: データが偏っていないかチェックするため
import collections
label_counts = collections.Counter(dataset[‘train’][‘label’])
print(“— Label distribution —“)
print(f” 0 (Negative): {label_counts[0]} examples”)
print(f” 1 (Positive): {label_counts[1]} examples”)
print(f” Balance: {‘Good’ if abs(label_counts[0] – label_counts[1]) < 1000 else 'Imbalanced'}")
実行結果:
— First example —
Label: 1
Text (first 200 chars):
I rented I AM CURIOUS-YELLOW from my video store because of all the controversy that surrounded it when it was first released in 1967. I also heard that at first it was seized by U.S. customs if it…
— Label distribution —
0 (Negative): 12500 examples
1 (Positive): 12500 examples
Balance: Good
3-4. データのトークン化
データセット全体をトークン化します。
map関数 を使うとバッチ処理で高速に処理できます。
# ========================================
# データのトークン化
# ========================================
# トークン化関数を定義
def tokenize_function(examples):
“””
データセットをトークン化する関数
Args:
examples: データセットのバッチ(辞書形式)
examples[‘text’] = テキストのリスト
Returns:
トークン化された結果(input_ids, attention_mask)
“””
return tokenizer(
examples[‘text’], # テキストのリスト
padding=’max_length’, # 最大長までパディング
truncation=True, # 最大長で切り詰め
max_length=256 # IMDBは長いので256に設定
# 短すぎると情報が失われる
)
# データセット全体をトークン化
# map(): 各サンプルに関数を適用
# batched=True: バッチ単位で処理(高速)
# remove_columns=[‘text’]: 元のテキストは削除(メモリ節約)
print(“Tokenizing dataset…”)
tokenized_datasets = dataset.map(
tokenize_function,
batched=True,
remove_columns=[‘text’]
)
print(“Tokenized dataset:”)
print(tokenized_datasets)
実行結果:
Tokenizing dataset…
Tokenized dataset:
DatasetDict({
train: Dataset({
features: [‘label’, ‘input_ids’, ‘token_type_ids’, ‘attention_mask’],
num_rows: 25000
})
test: Dataset({
features: [‘label’, ‘input_ids’, ‘token_type_ids’, ‘attention_mask’],
num_rows: 25000
})
})
text 列がinput_ids とattention_mask に変換されました。
3-5. 訓練/検証データの分割
# ========================================
# 訓練/検証データの分割
# ========================================
# 訓練データを訓練/検証に分割
# train_test_split: データを分割するメソッド
# test_size=0.1: 10%を検証データに
# seed=42: 乱数シードを固定(再現性のため)
split_dataset = tokenized_datasets[‘train’].train_test_split(
test_size=0.1,
seed=42
)
# 分割結果を変数に代入
train_dataset = split_dataset[‘train’] # 訓練データ(90%)
valid_dataset = split_dataset[‘test’] # 検証データ(10%)
# ※splitの’test’が検証データ
test_dataset = tokenized_datasets[‘test’] # 元のテストデータ
print(“Dataset splits:”)
print(f” Train: {len(train_dataset)} examples”)
print(f” Valid: {len(valid_dataset)} examples”)
print(f” Test: {len(test_dataset)} examples”)
実行結果:
Dataset splits:
Train: 22500 examples
Valid: 2500 examples
Test: 25000 examples
3-6. 開発用の小規模データ作成
開発・デバッグ時は小規模データで実験すると効率的です。
コードが正しく動くことを確認してからフルデータで訓練しましょう。
# ========================================
# 開発用の小規模データ作成
# ========================================
# shuffle: データをランダムに並べ替え
# select(range(N)): 最初のN件を選択
small_train = train_dataset.shuffle(seed=42).select(range(1000))
small_valid = valid_dataset.shuffle(seed=42).select(range(200))
print(“Small dataset for development:”)
print(f” Train: {len(small_train)} examples”)
print(f” Valid: {len(small_valid)} examples”)
# 本番訓練では元のデータセットを使用
# train_dataset = train_dataset # 22,500件
# valid_dataset = valid_dataset # 2,500件
実行結果:
Small dataset for development:
Train: 1000 examples
Valid: 200 examples
✅ 開発時のコツ
最初は小規模データ(1,000件程度)で実験し、
コードが正しく動くことを確認してからフルデータで訓練すると効率的です。
これにより、エラーの早期発見と時間の節約ができます。
🎯 4. ファインチューニングの実装
Hugging FaceのTrainer クラスを使うと、
複雑な訓練ループを書かずにファインチューニングができます。
4-1. 評価指標の定義
訓練中に精度やF1スコアを計算する関数を定義します。
# ========================================
# 評価指標の定義
# ========================================
# scikit-learnから評価指標をインポート
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
def compute_metrics(eval_pred):
“””
評価指標を計算する関数
Trainerが評価時に自動的に呼び出す
Args:
eval_pred: (predictions, labels)のタプル
predictions: モデルの出力(logits、形状は[N, 2])
labels: 正解ラベル(形状は[N])
Returns:
評価指標の辞書
“””
# predictionsとlabelsを取り出す
predictions, labels = eval_pred
# logitsから予測ラベルを取得
# argmax(axis=-1): 各サンプルで最大値のインデックスを取得
# 例: [[0.2, 0.8], [0.9, 0.1]] → [1, 0]
# 0.2 < 0.8 なのでインデックス1、0.9 > 0.1 なのでインデックス0
preds = predictions.argmax(axis=-1)
# 精度(Accuracy)を計算
# 正解した数 / 全体の数
accuracy = accuracy_score(labels, preds)
# Precision、Recall、F1を計算
# precision: 予測がPositiveの中で、実際にPositiveだった割合
# recall: 実際にPositiveの中で、Positiveと予測できた割合
# f1: PrecisionとRecallの調和平均
# average=’binary’: 2クラス分類用
precision, recall, f1, _ = precision_recall_fscore_support(
labels, preds, average=’binary’
)
return {
‘accuracy’: accuracy,
‘precision’: precision,
‘recall’: recall,
‘f1’: f1
}
4-2. 訓練設定(TrainingArguments)
訓練の詳細設定を作成します。各パラメータの意味を説明します。
# ========================================
# 訓練設定の作成
# ========================================
# TrainerとTrainingArgumentsをインポート
from transformers import Trainer, TrainingArguments
# 訓練設定を作成
training_args = TrainingArguments(
# === 出力設定 ===
output_dir=’./results’, # チェックポイントの保存先
# === 訓練設定 ===
num_train_epochs=3, # エポック数
# BERTは3-4エポックが一般的
per_device_train_batch_size=16, # 訓練バッチサイズ
# GPUメモリに応じて調整(8, 16, 32)
per_device_eval_batch_size=32, # 評価バッチサイズ
# 訓練より大きくてOK(勾配計算なし)
# === 最適化設定 ===
learning_rate=2e-5, # 学習率(★重要!)
# BERTは2e-5〜5e-5が推奨
# 大きすぎると事前学習の知識が失われる
weight_decay=0.01, # 重み減衰(L2正則化)
# 過学習を防ぐ
warmup_steps=500, # ウォームアップステップ数
# 最初は学習率を徐々に上げる
# === 評価・保存設定 ===
evaluation_strategy=’steps’, # 評価タイミング: ステップごと
eval_steps=500, # 500ステップごとに評価
save_strategy=’steps’, # 保存タイミング: ステップごと
save_steps=500, # 500ステップごとに保存
save_total_limit=2, # 保存する最大チェックポイント数
load_best_model_at_end=True, # 訓練終了時に最良モデルをロード
metric_for_best_model=’f1′, # 最良の基準: F1スコア
# === ログ設定 ===
logging_dir=’./logs’, # ログの保存先
logging_steps=100, # 100ステップごとにログ出力
# === 高速化設定 ===
fp16=True, # 混合精度訓練
# メモリ使用量が半減、速度が2倍
# === その他 ===
report_to=’none’ # 外部ツールへの報告を無効化
)
💡 重要なパラメータの説明
learning_rate=2e-5 : BERTは事前学習済みなので小さい値。大きすぎると事前学習の知識が失われる
num_train_epochs=3 : BERTは少ないエポックで十分。多すぎると過学習
warmup_steps=500 : 最初は学習率を徐々に上げて訓練を安定させる
fp16=True : 16ビット精度で計算。メモリ使用量が半減、速度が約2倍
4-3. Trainerの初期化と訓練実行
# ========================================
# Trainerの初期化
# ========================================
# Trainerを作成
trainer = Trainer(
model=model, # 訓練するモデル
args=training_args, # 訓練設定
train_dataset=small_train, # 訓練データ(開発用は小規模)
eval_dataset=small_valid, # 検証データ
compute_metrics=compute_metrics # 評価関数
)
# 訓練設定を確認
print(“Training configuration:”)
print(f” Epochs: {training_args.num_train_epochs}”)
print(f” Batch size: {training_args.per_device_train_batch_size}”)
print(f” Learning rate: {training_args.learning_rate}”)
print(f” Train samples: {len(small_train)}”)
# ステップ数を計算
steps_per_epoch = len(small_train) // training_args.per_device_train_batch_size
total_steps = steps_per_epoch * int(training_args.num_train_epochs)
print(f” Steps per epoch: {steps_per_epoch}”)
print(f” Total steps: {total_steps}”)
実行結果:
Training configuration:
Epochs: 3
Batch size: 16
Learning rate: 2e-05
Train samples: 1000
Steps per epoch: 62
Total steps: 186
4-4. 訓練の実行
# ========================================
# 訓練の実行
# ========================================
print(“Starting training…”)
print(“=” * 50)
# train(): 訓練を実行
# 進捗バーが表示される
train_result = trainer.train()
print(“=” * 50)
print(“Training completed!”)
# 訓練結果を表示
print(f”Training loss: {train_result.training_loss:.4f}”)
print(f”Training time: {train_result.metrics[‘train_runtime’]:.1f} seconds”)
実行結果(例):
Starting training…
==================================================
[186/186 02:30, Epoch 3/3]
==================================================
Training completed!
Training loss: 0.2834
Training time: 150.3 seconds
4-5. 検証データでの評価
# ========================================
# 検証データでの評価
# ========================================
# evaluate(): 検証データで評価
eval_result = trainer.evaluate()
print(“Validation Results:”)
print(f” Loss: {eval_result[‘eval_loss’]:.4f}”)
print(f” Accuracy: {eval_result[‘eval_accuracy’]:.4f}”)
print(f” Precision: {eval_result[‘eval_precision’]:.4f}”)
print(f” Recall: {eval_result[‘eval_recall’]:.4f}”)
print(f” F1 Score: {eval_result[‘eval_f1’]:.4f}”)
実行結果(例):
Validation Results:
Loss: 0.2856
Accuracy: 0.8850
Precision: 0.8912
Recall: 0.8789
F1 Score: 0.8850
💾 5. モデルの保存と推論
5-1. モデルの保存
# ========================================
# モデルの保存
# ========================================
# 保存先ディレクトリ
save_directory = ‘./my_bert_model’
# モデルを保存
# save_pretrained: モデルの重みと設定を保存
model.save_pretrained(save_directory)
# トークナイザーも保存(推論時に必要)
tokenizer.save_pretrained(save_directory)
print(f”Model saved to: {save_directory}”)
# 保存されたファイルを確認
import os
print(“\nSaved files:”)
for f in os.listdir(save_directory):
file_path = os.path.join(save_directory, f)
size_mb = os.path.getsize(file_path) / (1024**2)
print(f” {f}: {size_mb:.2f} MB”)
実行結果:
Model saved to: ./my_bert_model
Saved files:
config.json: 0.00 MB
model.safetensors: 417.65 MB
tokenizer_config.json: 0.00 MB
vocab.txt: 0.21 MB
special_tokens_map.json: 0.00 MB
5-2. 保存したモデルの読み込み
# ========================================
# 保存したモデルの読み込み
# ========================================
# 保存したモデルを読み込み
loaded_model = BertForSequenceClassification.from_pretrained(save_directory)
loaded_tokenizer = BertTokenizer.from_pretrained(save_directory)
# GPUへ転送
loaded_model = loaded_model.to(device)
# 評価モードに設定
# eval(): Dropoutやバッチ正規化を無効化
# 推論時は必ずeval()を呼ぶ
loaded_model.eval()
print(“Model loaded successfully!”)
5-3. 推論関数の作成
# ========================================
# 推論関数の作成
# ========================================
def predict_sentiment(text, model, tokenizer, device):
“””
テキストの感情を予測する関数
Args:
text: 予測したいテキスト
model: 学習済みモデル
tokenizer: トークナイザー
device: デバイス(cuda/cpu)
Returns:
label: 予測ラベル(’Positive’ or ‘Negative’)
confidence: 信頼度(0〜1)
“””
# テキストをトークン化
inputs = tokenizer(
text,
padding=True,
truncation=True,
max_length=256,
return_tensors=’pt’
)
# GPUへ転送
# 辞書の各値をGPUに転送
inputs = {k: v.to(device) for k, v in inputs.items()}
# 予測
model.eval() # 評価モード
with torch.no_grad(): # 勾配計算を無効化(メモリ節約・高速化)
# モデルに入力
outputs = model(**inputs)
# logitsを確率に変換
# softmax: 出力を0〜1の確率に変換
probs = torch.softmax(outputs.logits, dim=-1)
# 最も確率が高いクラスを取得
pred_class = torch.argmax(probs, dim=-1).item()
confidence = probs[0, pred_class].item()
# ラベルに変換
label = ‘Positive’ if pred_class == 1 else ‘Negative’
return label, confidence
5-4. 予測のテスト
# ========================================
# 予測のテスト
# ========================================
# テスト用テキスト
test_texts = [
“This movie is absolutely fantastic! I loved every minute of it.”,
“Terrible film. Complete waste of time and money.”,
“It was okay. Nothing special but not bad either.”,
“Best movie I’ve seen this year! Highly recommended!”,
“Boring and predictable. Very disappointed.”
]
# 各テキストを予測
print(“Sentiment Predictions:”)
print(“=” * 60)
for text in test_texts:
label, confidence = predict_sentiment(
text, loaded_model, loaded_tokenizer, device
)
# テキストが長い場合は省略
display_text = text[:50] + “…” if len(text) > 50 else text
print(f”\nText: {display_text}”)
print(f”Prediction: {label} (confidence: {confidence:.2%})”)
実行結果(例):
Sentiment Predictions:
============================================================
Text: This movie is absolutely fantastic! I loved every…
Prediction: Positive (confidence: 99.87%)
Text: Terrible film. Complete waste of time and money….
Prediction: Negative (confidence: 99.82%)
Text: It was okay. Nothing special but not bad either….
Prediction: Positive (confidence: 62.34%)
Text: Best movie I’ve seen this year! Highly recommende…
Prediction: Positive (confidence: 99.94%)
Text: Boring and predictable. Very disappointed….
Prediction: Negative (confidence: 99.71%)
「It was okay」のような中立的なテキストは信頼度が低くなっています。
これは正しい挙動で、モデルが自信を持てない場合に低い信頼度を出力しています。
🇯🇵 6. 日本語BERTの使い方
日本語のテキスト分類には東北大学BERT が広く使われています。
基本的な使い方は英語版と同じですが、トークナイザーが異なります。
6-1. 日本語BERTの読み込み
# ========================================
# 日本語BERTの読み込み
# ========================================
# 日本語BERT用のトークナイザーをインポート
from transformers import BertJapaneseTokenizer, BertForSequenceClassification
# 東北大学の日本語BERT
# whole-word-masking: 形態素単位でマスクする版(性能が良い)
model_name_jp = ‘cl-tohoku/bert-base-japanese-whole-word-masking’
# トークナイザーを読み込み
# 日本語BERTはMeCabベースの形態素解析を使用
tokenizer_jp = BertJapaneseTokenizer.from_pretrained(model_name_jp)
# モデルを読み込み
model_jp = BertForSequenceClassification.from_pretrained(
model_name_jp,
num_labels=2
)
print(f”Japanese BERT loaded: {model_name_jp}”)
print(f”Vocabulary size: {tokenizer_jp.vocab_size}”)
実行結果:
Japanese BERT loaded: cl-tohoku/bert-base-japanese-whole-word-masking
Vocabulary size: 32000
6-2. 日本語のトークン化
# ========================================
# 日本語のトークン化
# ========================================
# 日本語テキストのトークン化テスト
text_jp = “この映画は本当に素晴らしかった!感動しました。”
# トークン化
tokens_jp = tokenizer_jp.tokenize(text_jp)
print(f”Text: {text_jp}”)
print(f”Tokens: {tokens_jp}”)
print(f”Number of tokens: {len(tokens_jp)}”)
実行結果:
Text: この映画は本当に素晴らしかった!感動しました。
Tokens: [‘この’, ‘映画’, ‘は’, ‘本当に’, ‘素晴らしかっ’, ‘た’, ‘!’, ‘感動’, ‘し’, ‘まし’, ‘た’, ‘。’]
Number of tokens: 12
日本語BERTはMeCab による形態素解析を使ってトークン化します。
日本語は単語の区切りがないため、形態素解析で単語を分割しています。
💡 日本語BERTの特徴
トークナイザー : MeCabによる形態素解析ベース
語彙サイズ : 32,000(英語は30,522)
訓練データ : Wikipedia日本語版
Whole Word Masking : 形態素単位でマスク(推奨)
⚠️ 日本語BERTの注意点
日本語BERTを実際に使うには、日本語のデータセットでファインチューニング が必要です。
上記のコードではファインチューニングしていない ため、予測は正確ではありません。
実際に使う場合は、Livedoorニュースコーパスなどの日本語データセットで訓練してください。
📝 練習問題
問題1:モデルの読み込み
事前学習済みBERTモデルを読み込むメソッドは?
load_model()
from_pretrained()
get_model()
download_model()
解答を見る
正解:b
from_pretrained() メソッドで事前学習済みモデルを読み込みます。
使用例:
model = BertForSequenceClassification.from_pretrained(‘bert-base-uncased’)
tokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased’)
このメソッドは初回実行時に自動でモデルをダウンロードし、
2回目以降はキャッシュから読み込みます。
問題2:学習率の設定
BERTのファインチューニングで推奨される学習率は?
1e-3〜1e-2(大きめ)
2e-5〜5e-5(小さめ)
1e-1(非常に大きい)
1e-7(非常に小さい)
解答を見る
正解:b
BERTは既に事前学習済み なので、
小さい学習率(2e-5〜5e-5) でファインチューニングします。
理由:
大きすぎる学習率 → 事前学習の知識が失われる(壊れる)
小さすぎる学習率 → 収束が遅い(学習に時間がかかる)
BERT論文でも2e-5、3e-5、5e-5が推奨されています。
問題3:トークナイザーのパラメータ
tokenizer(text, padding=True, truncation=True)の役割は?
paddingのみ実行
truncationのみ実行
系列長を揃えて、長すぎる場合は切り詰める
特殊トークンを追加する
解答を見る
正解:c
padding とtruncation を組み合わせることで、
系列長を揃えて長すぎる場合は切り詰めます。
padding=True : 短い系列をmax_lengthまでパディング(0で埋める)
truncation=True : max_lengthを超えた系列を切り詰め
両方を使うことで、バッチ処理に必要な「同じ長さの系列」が作れます。
問題4:過学習の対策
BERTのファインチューニングで過学習を防ぐ方法として適切でない ものは?
学習率を下げる(2e-5)
エポック数を減らす(3-4)
訓練データを減らす
Weight Decayを使う
解答を見る
正解:c
訓練データを減らす のは過学習対策として適切ではありません 。
むしろ過学習しやすくなります。
正しい過学習対策:
✅ 小さい学習率(2e-5程度)
✅ 少ないエポック数(3-4エポック)
✅ Weight Decay(L2正則化)
✅ Early Stopping
✅ Dropout(BERTに組み込み済み)
問題5:日本語BERTの特徴
日本語BERTのトークナイザーの特徴として正しいものは?
英語と同じWordPieceを使用
MeCabによる形態素解析ベース
文字単位でトークン化
スペースで単語を区切る
解答を見る
正解:b
日本語BERTはMeCabによる形態素解析 をベースにしています。
理由:
日本語には単語の区切り(スペース)がない
形態素解析で単語を分割する必要がある
分割後、WordPieceで細分化する場合もある
例: “この映画は素晴らしい” → [“この”, “映画”, “は”, “素晴らしい”]
📝 STEP 17 のまとめ
✅ このステップで学んだこと
Hugging Face Transformers : 事前学習モデルを簡単に使えるライブラリ
from_pretrained() : 3行でモデルを読み込める
トークナイザー : テキスト → input_ids + attention_mask に変換
データ準備 : datasets.map()でバッチトークン化
Trainer API : 訓練ループを書かずにファインチューニング
ハイパーパラメータ : 学習率2e-5、3-4エポックが基本
保存と推論 : save_pretrained()で保存、独自関数で推論
日本語BERT : MeCabベースの形態素解析トークナイザー
🎯 次のステップの準備
STEP 18: GPTシリーズの理解 では、
生成タスクに強いGPTモデルを学びます!
GPT、GPT-2、GPT-3の進化
Decoder-onlyアーキテクチャ
ChatGPTの仕組み(RLHF)
テキスト生成の実装
BERTは理解 が得意、GPTは生成 が得意!