« ^ »
[WIP]

再び文章を自動生成する

所要時間: 約 4分

CyberAgentが開発している日本語用大規模言語モデルOpenCALMを試してみる事にした。環境は手元のmacOS(Intel ChipのMacBook Proの中古型落ち品)を使う1。PCのスペック的に大した事はできないだろうけれど、(型落ちmacが)頑張ってみる事にする。

まずは依存パッケージをインストールする。

torch==1.13.1
transformers
accelerate
mkl
pip install -r requirements.txt

手元の貧弱マシンではどうせ大した事はできないため open-calm-small を使用する事にする。

https://huggingface.co/cyberagent/open-calm-small

このページのUsageに基本的な使い方として、コードスニペットが掲載されている。通常は、これをこのまま実行すれば、何も考える事なく文章を生成できる。ただし、諸般の事情(例えばGPUがショボいとか)は常に存在していて、少しだけ手直しする必要が出てくる事もあるだろう。今回はというとやはり諸般の事情で、そのまま実行するとエラーが発生した。そこで実行できるように少しだけ手直しをした。また文章をいっぱい生成できるようにもした。

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer


# torch.set_default_dtype(torch.float16)
# torch.backends.mkl.enabled = True

model = AutoModelForCausalLM.from_pretrained(
    "cyberagent/open-calm-small", device_map="auto", torch_dtype=torch.float32)
tokenizer = AutoTokenizer.from_pretrained("cyberagent/open-calm-small")

t = "AIによって私達の暮らしは、"

for i in range(3):
    inputs = tokenizer(t, return_tensors="pt").to(model.device)
    with torch.no_grad():
        tokens = model.generate(
            **inputs,
            max_new_tokens=64,
            do_sample=True,
            temperature=0.7,
            top_p=0.9,
            repetition_penalty=1.05,
            pad_token_id=tokenizer.pad_token_id,
        )

    t = ""
    for token in tokens:
        output = tokenizer.decode(token, skip_special_tokens=True)
        t += output
        print(output, end="")

それでは実行しよう。

python main.py
AIによって私達の暮らしは、今や地球規模で変化しています。そんな今、地球規模での課題解決には「社会システム」が大きく役立っています。
しかし、今、私達は、「人」「物」「金」などの「モノ」「サービス」が地球規模で絡み合っています。

〜省略〜

結構ちゃんと文章として成立している。文章の生成の確認はできたから、ファインチューニングしていく。

まずは、学習の仕方を把握するために、小さな言葉を学習させる。

tune-simple.py

import collections

import torch
from torch.utils.data import Dataset
from transformers import (AutoModelForCausalLM, AutoTokenizer, Trainer,
                          TrainingArguments)


class DatasetA(Dataset):
    def __init__(self, dataset, is_test=False):
        self.dataset = dataset
        self.is_test = is_test

    def __getitem__(self, idx):
        data = {"input_ids": torch.tensor(self.dataset["input_ids"][idx])}
        data["labels"] = torch.tensor(self.dataset["labels"][idx])
        return data

    def __len__(self):
        return len(self.dataset["input_ids"])


model = AutoModelForCausalLM.from_pretrained(
    "cyberagent/open-calm-small", device_map="auto", torch_dtype=torch.float32
)
tokenize = AutoTokenizer.from_pretrained("cyberagent/open-calm-small")
model.hf_device_map = None  # CPUで実行する場合、Noneを指定しないと例外が発生した

input_token = tokenize("喜びが", max_length=50, padding="max_length", truncation=True)
output_token = tokenize("ない", max_length=50, padding="max_length", truncation=True)

d = collections.defaultdict(list)
d["input_ids"].append(input_token["input_ids"])
d["labels"].append(output_token["input_ids"])

dataset = DatasetA(d)

train_args = TrainingArguments(
    output_dir="./trained_data",
    num_train_epochs=1,
    per_device_train_batch_size=1,
    per_device_eval_batch_size=1,
    warmup_steps=100,
    weight_decay=0.1,
    save_steps=1,
    no_cuda=True,
)

trainer = Trainer(model=model, args=train_args, train_dataset=dataset)
trainer.train()

実行する。

python tune-simple.py

実行すると trained_data にモデルが出力される。これを用いて再度文章を生成してみる。

tuned.py

import sys
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer


model = AutoModelForCausalLM.from_pretrained(
    "./trained_data/checkpoint-1", device_map="auto", torch_dtype=torch.float32)
tokenizer = AutoTokenizer.from_pretrained("cyberagent/open-calm-small")

t = sys.argv[1]

for i in range(2):
    inputs = tokenizer(t, return_tensors="pt").to(model.device)
    with torch.no_grad():
        tokens = model.generate(
            **inputs,
            max_new_tokens=30,
            do_sample=True,
            temperature=0.7,
            top_p=0.9,
            repetition_penalty=1.05,
            pad_token_id=tokenizer.pad_token_id,
        )

    t = ""
    for token in tokens:
        output = tokenizer.decode(token, skip_special_tokens=True)
        t = output
        print(output, end="")

生成を実行する。

python tuned.py "AIによって私達の暮らしは、"
AIによって私達の暮らしは、常に変化しています。
私は「暮らしの変化」に気づき、その変化と新しいことを共に考え、共に考え、共に学び、ともに喜び、AIによって私達の暮らしは、常に変化しています。
私は「暮らしの変化」に気づき、その変化と新しいことを共に考え、共に考え、共に学び、ともに喜び、共に悲しみ、共に笑い、共に生きることを大切にしている一人です。
私たち夫婦が一緒に暮らすのは、あくまでも「パートナー」であり、家族であり

学習に使った言葉も確認する。

python tuned.py "喜びが"
喜びが、その人にしか分からない。
「愛」と「真実」について。
私たちは、どんな時も、どんな状況でも、
「愛」喜びが、その人にしか分からない。
「愛」と「真実」について。
私たちは、どんな時も、どんな状況でも、
「愛」と「真実」を分かち合い、共に歩むことを望みます。
私たちは、いつだって、そんな風に思う。
「愛」と

いまいち期待した値にはなっていないが、文章としては成立している。学習したと言っても、量が少なすぎるからこの結果は正しそうではある。次はもっと多くの文章を学習させてみようと思う。

使用したライブラリ

accelerate==0.20.3
certifi==2023.5.7
charset-normalizer==3.1.0
filelock==3.12.1
fsspec==2023.6.0
huggingface-hub==0.15.1
idna==3.4
intel-openmp==2023.1.0
mkl==2023.1.0
numpy==1.24.3
packaging==23.1
psutil==5.9.5
PyYAML==6.0
regex==2023.6.3
requests==2.31.0
safetensors==0.3.1
tbb==2021.9.0
tokenizers==0.13.3
torch==1.13.1
tqdm==4.65.0
transformers==4.30.1
typing_extensions==4.6.3
urllib3==2.0.3

また幾つかのWebページを参考にした2


1

Google Colabを使えばよいという声も聞こえてきそうだ。実際、Google Colabでも実施できる。ただ今回はあくまで動作を確認する事が目的であるため、ローカルで実施する。