👻

rinnaに続く道(かも)1 Hugging FaceのDatasetsクイックスタート紹介

2022/01/03に公開

経緯

AIの文章生成について調べていると、十中八九、Hugging Faceが出てくるが、十中八九動かない(動かせない)。今まで何回も挫折したが、心機一転、再挑戦する。

目標と進め方

日本語の文章を生成させたいため、GPT2の日本語モデルrinnaを最終目標に添える。

また、インターネット上の情報で動かないケースが多いのは、バージョンの違い(Hugging Faceそのものと、モデルの2パターンある)によるものが多いと推測される。

そのため、まずは公式のチュートリアル等を試すことから始める。

今回の対象:Datasets

Hugging FaceのDocsによると、Hugging Faceの中身は大きく下記に分かれていると推測される。

  • Transfomers
  • Hub
  • Datasets
  • AutoNLP
  • Inference API
  • Tokenizers
  • Amazon SageMaker
  • Accelerate

本命はTransfomersだが、今回はまずDatasetsを対象とする。

なお、理由はHugging Faceのドキュメントを検索していて一番はじめに見つけたクイックスタートがDatasetsだったからであり、必ずしもここから始めるべきなのかは不明。

上記一覧を見つける前にDatasetsのクイックスタートを初めてしまったため、最後まで実行し、本記事に整理した。

Datasetsのクイックスタート再構築

Datasetsのクイックスタートを上から実行する際のメモも後半の「実行記録」の章に記載するが、クイックスタートをそのまま実行すると混乱する点が複数あったため、本章にてクイックスタートをより整理した内容を記載する。
本章の内容を実施いただければDatasetsのクイックスタートを行ったのと実質同じ結果になるのでご利用いただきたい。

リソース

本クイックスタートをベースに作成したソースコードを下記で公開した。本記事ではこちらのソースコードを参照しつつ解説するため、必要に応じてご確認いただきたい。

https://github.com/ccat/ai_samples/tree/main/huggingface/datasets_quickstart

本クイックスタートで実施すること

Datasetsのクイックスタートでは、ライブラリを読み込み、BERTのモデルをGeneral Language Understanding Evaluation (GLUE) benchmarkのMicrosoft Research Paraphrase Corpus (MRPC)向けに学習させるまでを実行する。

なお、GLUE Benchmarkとは英語圏における自然言語処理のベンチマークであり、モデルの精度を数値化する。
MRPCはGLUE Benchmarkの中でも2つの文が同じ意味を持つか判定させるベンチマークであるとのこと。
参照情報:GLUE - 英語圏における自然言語処理の標準ベンチマーク

そのため、本クイックスタートでは、2つの文が同じ意味を持つか否かを判定するようにBERTモデルを学習させる、と言い換えるとわかりやすい。

実行環境

今回、本クイックスタートをUbuntu 20.04.3 LTS上のPython 3.8.10、および、Windows 11上のPython 3.9.9にて実行した[1]。環境が異なる場合、差異が出る可能性があるので留意されたい。

また、どちらもvenvを利用している。

Linuxの場合のvenv準備

python3 -m venv venv
source ./venv/bin/activate

Windowsの場合のvenv準備

コマンドプロンプト
python3 -m venv venv
cd venv\Scripts
activate.bat

venvの使い方や、Pythonのインストールについては他サイトを参照されたい。

必要なパッケージ

本クイックスタートは、PyTorchとTensorFlowのどちらかを選択する必要がある[2]。個人的にはTensorFlowの方が慣れているが、Hugging Faceについて調査した際、PyTorchを利用しているケースの方が多かったと感じたため、PyTorchを選択した。

PyTorchを利用する場合、インストールする必要のあるパッケージは下記の通りである[3]

requirements.txt
datasets
transformers
torch
torchvision
torchaudio

このうち、datasetsとtransformersがHugging Faceのパッケージであり、torch、torchvision、torchaudioはPyTorchのパッケージである。

PyTorchを利用する際に必要なパッケージは、PyTorchのサイトでチェック可能なため、PyTorchが正常に動かない場合はそちらを参照すること。

また、今回インストールされた全パッケージとバージョンについてもpip_freeze3.txtに公開した。
今後、バージョンが更新されると動作しなくなる可能性がある。インストール失敗や不明なエラーが出る場合、パッケージのバージョンを確認してもらえれば幸いである。

パッケージのインストール

requirements.txtをダウンロードした後、venv準備が完了した環境のCLI(bashかコマンドプロンプト)で下記コマンドを実行すれば必要パッケージがインストールされる。
バージョンが本記事と異なる等が発生する場合、requirements.txtをpip_freeze3.txtに変更することでバージョンを同一にすることが可能である。

pip install -r requirements.txt

実行

main.pyをダウンロードし、pythonで下記を実行すると、モデルのダウンロードと学習が実行されるので、まずは実行してみることを推奨する。

$ python
>>> import main
>>> model = main.main()

この時点でエラーが発生する場合、環境の準備に失敗している可能性がある。また、「強制終了」された場合、メモリが不足している。

ソースコード解説

main.pyを大まかに解説する。
ただし、クイックスタートに記載されているレベルの解説に留めるので注意[4]

まず一番初めは必要パッケージのインポートを行う。

main.py
from datasets import load_dataset
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import torch
from tqdm import tqdm

datasetsとtransformersの2行でHugging Faceのパッケージから今回必要なものを読み込んでいる。
tqdmは画面表示用のライブラリとのこと。

次のmain関数が今回のクイックスタートで作成する各関数を呼び出し、学習されたモデルを返すようになっている。

main.py
def main():
    """BERTモデルの読み込みから学習まで一通り実施して、学習結果のモデルを返します。
    """
    model,tokenizer = make_model() #はじめに学習対象となるモデルを作成します
    dataset = load_and_tokenize_datasets(tokenizer) #学習用のデータを読み込みます
    train(model,dataset, 1) #学習を行います
    return model

ほぼコメントに記載してある通りだが、下記の3ステップを実施する。

  1. モデルの作成
  2. 学習用のデータロード
  3. 学習の実行

モデルの作成を行うmake_model関数は下記の通りである。

main.py
def make_model():
    """BERTのモデルを読み込み、トークナイザーを生成してモデルとトークナイザーをセットで返します。
    """
    model = AutoModelForSequenceClassification.from_pretrained('bert-base-cased')
    tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
    return (model,tokenizer)

Hugging Faceではfrom_pretrainedと言う関数が至るところで出てくるが、これはトレーニング済みのモデルを読み込む関数であり、インターネット上で公開されているモデルやローカルに保存したモデルを読み込むことが可能[5]

今回はbert-base-casedと言うモデルとそのトークナイザーを読み込んでいる。

次に、データセットを読み込むload_and_tokenize_datasets関数は下記の通りである。

main.py
def load_and_tokenize_datasets(tokenizer):
    """GLUE BenchmarkのMRPCのデータを読み込み、学習に投入できるデータ構造に変換してデータセットを返します
    """
    def encode(examples):
        return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding='max_length')

    dataset = load_dataset('glue', 'mrpc', split='train')
    dataset = dataset.map(encode, batched=True)
    dataset = dataset.map(lambda examples: {'labels': examples['label']}, batched=True)
    dataset.set_format(type='torch', columns=['input_ids', 'token_type_ids', 'attention_mask', 'labels'])
    return dataset

**dataset = load_dataset('glue', 'mrpc', split='train')**にて、データセットの読み込みを実行する。また、今回は「2つの文が同じ意味を持つか判定させる」関係上、1つのデータに2つの文(sentence)が含まれている。そのため、2つの文に対するトークン化を行うencode関数を予め定義しておき、dataset.mapでトークン化を行っている。
最後の2行(returnを含めると3行)は、torchで読み込むための整形処理である。

最後に、実際に学習を行うtrain関数である。

main.py
def train(model,dataset,epoch_range=3):
    """変換済みデータセットでモデルの学習を実行します
    """
    dataloader = torch.utils.data.DataLoader(dataset, batch_size=32)
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model.train().to(device)
    optimizer = torch.optim.AdamW(params=model.parameters(), lr=1e-5)
    for epoch in range(epoch_range):
        for i, batch in enumerate(tqdm(dataloader)):
            batch = {k: v.to(device) for k, v in batch.items()}
            outputs = model(**batch)
            loss = outputs[0]
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
            if i % 10 == 0:
                print(f"loss: {loss}")

学習対象となるモデル、学習用データ、学習のループ数を引数としている。内容は、学習用のバッチを生成し、cuda利用かcpu利用かを設定した上で、学習ループを実行しているだけである。

以上で、Datasetsのクイックスタート全体を網羅した。ただし、このままでは学習させたモデルの実際の利用や保存が行われていないため注意すること。

実行記録

ここからは、クイックスタートを上から実行した際の記録である。
クイックスタートを上から実行したい場合、参考にすると良いが、本質的には上記と同様である。

環境整備

開発環境はUbuntu 20.04.3 LTS上にvenvで準備。

python3 -m venv venv
source ./venv/bin/activate

クイックスタートによると、datasetsと言うパッケージをまずはインストールせよ、と言うことなので、requirements.txtに下記1行を記載し、pipでインストール。

requirements.txt
datasets
pip install -r requirements.txt

pip freezeで確認すると、datasetsの1.17.0と、huggingface-hubの0.2.1がインストールされたようだ[6]

続いて、 General Language Understanding Evaluation (GLUE) benchmarkから、Microsoft Research Paraphrase Corpus (MRPC) トレーニングデータセットを読み込む。

from datasets import load_dataset
dataset = load_dataset('glue', 'mrpc', split='train')

実際にCLIで実行してみた結果。

Python 3.8.10 (default, Sep 28 2021, 16:10:42) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from datasets import load_dataset
>>> dataset = load_dataset('glue', 'mrpc', split='train')
Downloading: 28.8kB [00:00, 22.8MB/s]                                           
Downloading: 28.7kB [00:00, 26.1MB/s]                                           
Downloading and preparing dataset glue/mrpc (download: 1.43 MiB, generated: 1.43 MiB, post-processed: Unknown size, total: 2.85 MiB) to /home/.../.cache/huggingface/datasets/glue/mrpc/1.0.0/dacbe3125aa31d7f70367a07a8a9e72a5a0bfeb5fc42e75c9db75b96da6053ad...
Downloading: 6.22kB [00:00, 14.8MB/s]                     | 0/3 [00:00<?, ?it/s]
Downloading: 1.05MB [00:00, 1.48MB/s]            | 1/3 [05:31<11:03, 331.77s/it]
Downloading: 441kB [00:00, 824kB/s]              | 2/3 [11:04<05:32, 332.37s/it]
100%|████████████████████████████████████████████| 3/3 [16:36<00:00, 332.30s/it]
Dataset glue downloaded and prepared to /home/.../.cache/huggingface/datasets/glue/mrpc/1.0.0/dacbe3125aa31d7f70367a07a8a9e72a5a0bfeb5fc42e75c9db75b96da6053ad. Subsequent calls will reuse this data.

データセットのダウンロードが行われている[7]

次に、事前学習済みBERTモデルを読み込み、トークナイザーを生成する。
ここで、ソースコードがPyTorch用とTensorFlow用で分かれるが、そもそもこの時点ではデータセットしかインストールしていないので、1行目の"from transformers"の時点で"transformers"がインストールされていない旨、エラーが出る。

PyTorch
>>> from transformers import AutoModelForSequenceClassification, AutoTokenizer
>>> model = AutoModelForSequenceClassification.from_pretrained('bert-base-cased')
TensorFlow
>>> from transformers import TFAutoModelForSequenceClassification, AutoTokenizer
>>> model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased")

requirements.txtに"transformers"を追加してインストール。

requirements.txt
datasets
transformers

transformersの4.15.0と、tokenizersの0.10.3がインストールされた[8]

改めて実行すると、tarnsformersにやPyTorchかTensorFlowのどちらかが必要とのエラーが発生した。

PyTorch
>>> from transformers import AutoModelForSequenceClassification, AutoTokenizer
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.

普段、TensorFlowの方がよく利用するのだが、Hugging Face関連の記事ではPyTorchを利用しているケースの方が多いようなので、PyTorchを選択。

PyTorchのサイトでLinux、pipインストールを選択し、必要パッケージを確認。

requirements.txt
datasets
transformers
torch
torchvision
torchaudio

torchの1.10.1、torchaudioの0.10.1、torchvisionの0.11.2がインストールされた。

改めてtransformersのインポートと、BERTの読み込みを行う。

PyTorch
Python 3.8.10 (default, Sep 28 2021, 16:10:42) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from transformers import AutoModelForSequenceClassification, AutoTokenizer
>>> model = AutoModelForSequenceClassification.from_pretrained('bert-base-cased')
Downloading: 100%|████████████████████████████████████████████████| 570/570 [00:00<00:00, 1.65MB/s]
Downloading: 100%|██████████████████████████████████████████████| 416M/416M [01:07<00:00, 6.42MB/s]
Some weights of the model checkpoint at bert-base-cased were not used when initializing BertForSequenceClassification: ['cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.weight', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.bias', 'cls.predictions.transform.LayerNorm.bias']
- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

色々メッセージが出ているが、一番最後に「使う前に学習させろ」と言っているので、学習させてから使う前提のモデルなだけのようだ。本家のクイックスタートでも同じような表示が記載されているため気にせず続ける。

PyTorch
>>> tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')

続いて、データセットをトークンに変換するためのencode関数を作成する。

PyTorch
def encode(examples):
    return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding='max_length')

作成したencode関数を用いてデータセットをトークン化する。また、次のPyTorchへの読み込みに利用するためラベルに関する設定も追加。

PyTorch
>>> from datasets import load_dataset
>>> dataset = load_dataset('glue', 'mrpc', split='train')
>>> dataset = dataset.map(encode, batched=True)
>>> dataset = dataset.map(lambda examples: {'labels': examples['label']}, batched=True)

学習を行うためにデータセットのフォーマット設定とバッチ作成を実行。

PyTorch
>>> import torch
>>> dataset.set_format(type='torch', columns=['input_ids', 'token_type_ids', 'attention_mask', 'labels'])
>>> dataloader = torch.utils.data.DataLoader(dataset, batch_size=32)

学習プロセスを実行。めちゃめちゃメモリを消費するので注意。

PyTorch
>>> from tqdm import tqdm
>>> device = 'cuda' if torch.cuda.is_available() else 'cpu'
>>> model.train().to(device)
>>> optimizer = torch.optim.AdamW(params=model.parameters(), lr=1e-5)
>>> for epoch in range(3):
...     for i, batch in enumerate(tqdm(dataloader)):
...         batch = {k: v.to(device) for k, v in batch.items()}
...         outputs = model(**batch)
...         loss = outputs[0]
...         loss.backward()
...         optimizer.step()
...         optimizer.zero_grad()
...         if i % 10 == 0:
...             print(f"loss: {loss}")

仮想環境ではメモリが足りないため、20GBのスワップファイルを生成するも落ちる。

sudo -i
dd if=/dev/zero of=/swapfile2 bs=1M count=20480
chmod 600 /swapfile2
mkswap /swapfile2
swapon /swapfile2
swapon -s
free -h

スワップファイルを40GBにしたところ、落ちることは無くなったが学習時間が1週間近く要する見込みがたったことから中断。
main.pyを作成し、母艦のWindows 11にて再実行。エラーが表示されずに完了することを確認した。

脚注
  1. 初めはVMware上のUbuntuで実行したが、学習に要する時間が1週間以上になってしまったため、母艦であるWindows 11上で再実行した。 ↩︎

  2. 厳密には、本クイックスタートに限らずHugging FaceはPyTorchかTensorFlowのどちらかが利用できることを前提に作られている。別に両方同時に使っても問題ないとは思うが、無駄に難しくなるので推奨しない。 ↩︎

  3. これ以外のパッケージも利用されるが、依存関係で自動的にインストールされる。 ↩︎

  4. クイックスタートの内容を進めると、main.pyとほぼ同じ内容をインタラクティブモードで実行することになる。 ↩︎

  5. そのため、インターネット上のモデルが更新されたり削除されたりしてしまうと、パッケージのバージョンを合わせても動作しないケースが発生する。便利だが悪魔のような仕組み。 ↩︎

  6. pip freezeの結果はpip_freeze1.txtに保存してあるので詳細はこちらを確認してもらいたい。"pip install -r pip_freeze1.txt"で同じパッケージ状況を再現可能だ。 ↩︎

  7. このデータセットのダウンロードも、便利なようでURL等が変わるとエラーになるので、インターネット上の記事に掲載されているソースコードが動作しなくなる原因になっていると思う。また、キャッシュがvenvとは別の場所に保存されるため、venvを分けていてもHugging Faceがダウンロードしてくるデータは衝突する可能性がある。 ↩︎

  8. pip freezeの結果はpip_freeze2.txtに保存してあるので詳細はこちらを確認してもらいたい。他にもいくつかパッケージが追加されているが、主要なものではないと判断して本文からは除外した。 ↩︎

Discussion