見出し画像

Simple Transformers 入門 (10) - ハイパーパラメータの最適化

「Simple Transformers」のハイパーパラメータの最適化を行う方法をまとめました。

1. ハイパーパラメーターの最適化

Weights&Biases」を使用して、「SuperGLUE」の「Recognizing Textual Entailment」タスク(文のペアの2値分類)で、「Simple Transformers」のハイパーパラメータの最適化を行います。

NLPタスクでのTransformerモデルの学習時に、最も考慮すべきハイパーパラメータは、「学習率」と「エポック数」です。「エポック数」が多すぎたり、「学習率」が高すぎたりすると、壊滅的な忘却が発生します。逆に「エポック数」が少なすぎたり、「学習率」が低すぎたりすると、学習不足です。

また、メモリオーバーの場合は、「バッチサイズ」を減らす、またはメモリの多いGPUを用意する必要があります。「バッチサイズ」は大きいほど学習速度が早くなりますが、メモリの消費量も大きくなります。

2. Google Colabの準備

(1) 「Google Colab」のノートブックの作成。
(2) メニュー「編集 → ノートブックの設定」で「GPU」を指定。
(3) 以下のコマンドでGPUを確認。

# GPUの確認
!nvidia-smi

GPUはランダムに選択されます。GPUのメモリは、次のとおりです。

・Tesla P100-PCIE : 16GB
・Tesla T4 : 15GB
・Tesla K80 : 12GB

3. Simple Transformersのインストール

「Simple Transformers」をインストールします。

# Simple Transformersのインストール (最後にRESTART RUNTIMEを押す)
!pip install transformers
!pip install simpletransformers

4. データセットの準備

(1) 「Recognizing Textual Entailment」のデータセットをここからダウンロード。
(2) 「train.jsonl」「val.jsonl」を「Google Colab」にアップロード。
(3) データセットの読み込み。

import pandas as pd
from sklearn.model_selection import train_test_split

# ファイルの読み込み
def load_rte_data_file(filepath):
    df = pd.read_json(filepath, lines=True)
    df = df.rename(columns={"premise": "text_a", "hypothesis": "text_b", "label": "labels"})
    df = df[["text_a", "text_b", "labels"]]
    return df

# データセットの準備 (SuperGLUEのRTEデータセットをアップロード後)
train_df = load_rte_data_file("train.jsonl")
eval_df = load_rte_data_file("val.jsonl")
eval_df, test_df = train_test_split(eval_df, test_size=0.5, random_state=4)

5. 学習

学習します。

import logging
from sklearn.metrics import accuracy_score
from simpletransformers.classification import ClassificationArgs, ClassificationModel

# ログの設定
logging.basicConfig(level=logging.INFO)
transformers_logger = logging.getLogger("transformers")
transformers_logger.setLevel(logging.WARNING)

# モデルパラメータの作成
model_args = ClassificationArgs()

# 学習・評価
model_args.manual_seed = 4 # 乱数シード
model_args.learning_rate = 1e-5 # 学習率
model_args.num_train_epochs = 1 # エポック数
model_args.train_batch_size = 8 # 学習のバッチサイズ
model_args.eval_batch_size = 16 # 評価のバッチサイズ
model_args.evaluate_during_training = True
model_args.labels_list = ["not_entailment", "entailment"]

# 保存なし
model_args.save_eval_checkpoints = False
model_args.save_model_every_epoch = False
model_args.no_cache = True
model_args.overwrite_output_dir = True

# モデルの作成
model = ClassificationModel("roberta", "roberta-large", use_cuda=True, args=model_args)

# 学習
model.train_model(train_df, eval_df=eval_df,
    accuracy=lambda truth, predictions: accuracy_score(
        truth, [round(p) for p in predictions]
    ),
)

# 評価
model.eval_model(test_df, verbose=True)
{'mcc': 0.4969961597944327, 'tp': 51, 'tn': 53, 'fp': 16, 'fn': 19, 'accuracy': 0.7463768115942029, 'eval_loss': 0.5478038423591189}

学習率「1e-5」、エポック数「1」で、精度は「0.746」でした。

6. wandbのインストールとログイン

 「W&B」のサイトの「Settings → API keys」で「API keys」を確認できます。

# wandbのインストールとログイン
!pip install wandb
!wandb login <API keysを記述>

7. wandbでハイパーパラメータの最適化

wandbで最適なハイパーパラメータを見つけます。

import logging
import wandb
from sklearn.metrics import accuracy_score
from simpletransformers.classification import ClassificationArgs, ClassificationModel

# Sweepsの設定
sweep_config = {
    "name": "vanilla-sweep-batch-8",
    "method": "bayes",
    "metric": {"name": "accuracy", "goal": "maximize"},
    "parameters": {
        "num_train_epochs": {"min": 1, "max": 10}, 
        "learning_rate": {"min": 0, "max": 4e-4}, 
    },
    "early_terminate": {"type": "hyperband", "min_iter": 6,},
}
sweep_id = wandb.sweep(sweep_config, project="RTE - Hyperparameter Optimization")

# ログの設定
logging.basicConfig(level=logging.INFO)
transformers_logger = logging.getLogger("transformers")
transformers_logger.setLevel(logging.WARNING)

# モデルパラメータの作成
model_args = ClassificationArgs()

# 学習・評価
model_args.manual_seed = 4
model_args.learning_rate = 4e-4
model_args.num_train_epochs = 10
model_args.train_batch_size = 16
model_args.eval_batch_size = 8
model_args.evaluate_during_training = True
model_args.labels_list = ["not_entailment", "entailment"]

# 保存なし
model_args.save_eval_checkpoints = False
model_args.save_model_every_epoch = False
model_args.no_cache = True
model_args.overwrite_output_dir = True

# 学習
def train():
    # wandbの初期化
    wandb.init()
 
    # モデルの作成
    model = ClassificationModel("roberta", "roberta-large", use_cuda=True,
        args=model_args, sweep_config=wandb.config)

    # 学習
    model.train_model(
        train_df, eval_df=eval_df, accuracy=lambda truth, predictions: accuracy_score(
            truth, [round(p) for p in predictions]
        ),
    )

    # wandbのログ保存
    wandb.log(model.results)    

    # wandbの同期
    wandb.join()

# wandbで学習
wandb.agent(sweep_id, train)

学習率「0〜4e-4」とエポック数「1〜10」で探索しています。「early_terminate」は、パフォーマンスの低い実行を中止する設定で、最適化の高速化を行っています。

8. 実験の追跡と可視化

「W&B」のサイトで実験の追跡と可視化が可能です。サンプル12で、以下のようなグラフになりました。

画像1

・2回の実行で精度0.8前後。
・5回の実行で精度0.55前後。
・3回の実行で精度0.45前後。

画像2

・学習率「1.7-e4」とエポック数「10」のセットが現在のベスト。
・学習率は「0.0005」以上で精度が落ちている。
・10以上のエポック数も試したほうが良さそう。



この記事が気に入ったらサポートをしてみませんか?