cyberagent/open-calmをファインチューニングする方法について(+cyberagent/open-calmに関しての解説)

こんにちにゃんです。
水色桜(みずいろさくら)です。
今回は最近リリースされて話題になっているcyberagent/open-calmをファインチューニングして、生成される文章を特徴づけするための方法について書いていこうと思います。
記事中で何か不明な点・間違いなどありましたら、コメント・Twitterまでお寄せいただけると嬉しいです。

# cyberagent/open-calmとは
>株式会社サイバーエージェント（本社：東京都渋谷区、代表取締役：藤田晋、東証プライム市場：証券コード4751）は、最大68億パラメータの日本語LLM（Large Language Model、大規模言語モデル）を一般公開したことをお知らせいたします。

>近年、OpenAI社が開発した「ChatGPT」※1 を始めとする生成AI・LLMは急速な進化を遂げており、世界中のあらゆる業界・ビジネスにおいて活用が進んでいます。
一方、既存のLLMのほとんどは英語を中心に学習されているため、日本語および日本文化に強いLLMは少ない状況です。
■最大68億パラメータの日本語LLM（大規模言語モデル）の公開について

>こうした背景のもと、当社は日本語LLMの開発に取り組んでおり、このたび一部モデルをHugging Face Hubにて公開いたしました。公開されたモデルはオープンな日本語データ※2で学習したもので、商用利用可能なCC BY-SA 4.0ライセンス※3で提供されます。なお、本モデルは日本国内における現行の公開モデルにおいて最大級の規模となります。（※2023年5月17日時点）

>本モデルをベースとしてチューニングを行うことにより、対話型AIなどの開発も可能となります。これにより、より多くの方々が日本語の自然言語処理に関する最先端の研究開発に取り組んでいただけます。


>■Hugging Face Hub URL
https://huggingface.co/cyberagent


>当社では、これまで培った知見を活かしチャットボットやRPAをはじめとする業界特化型のLLMの構築や、各企業と連携したLLMを活用したビジネス開発の推進等を予定しております。また、LLMを活用したビジネス開発に携わるエンジニアの新規採用を実施し、体制強化に努めてまいります。

>当社は今後も、引き続きモデルの公開や産学連携などの取り組みを通じ、国内における自然言語処理技術の発展に貢献してまいります。

[サイバーエージェント株式会社ホームページ](https://www.cyberagent.co.jp/news/detail/id=28817)より引用

まとめると、**open-calmはサイバーエージェント社が2023年5月17日に公開した大規模生成言語モデルであり、日本語に特化したモデルだという点が他の言語モデルと異なります。**

現在（2023年5月20日）公開されているモデルは次の通りです。
|Model	|Params| Layers|	Dim|	Heads|	Dev ppl|
|----|-----|-----|-----|-----|----|
|cyberagent/open-calm-small	|160M	|12|	768|	12|	19.7|
|cyberagent/open-calm-medium|	400M	|24|	1024|	16|	13.8|
|cyberagent/open-calm-large	|830M	|24|	1536|	16|	11.3|
|cyberagent/open-calm-1b	|1.4B	|24|	2048|	16|	10.3|
|cyberagent/open-calm-3b	|2.7B	|32|	2560|	32|	9.7|
|cyberagent/open-calm-7b	|6.8B	|32 |4096|	32|	8.2|

# cyberagent/open-calmをファインチューニングするための方法
cyberagent/open-calmは生成系言語モデルです。ファインチューニングすることで生成される文章の特徴（例えば若者言葉っぽくであったり、ある一定の話題が多く出力されるなど）を調整することができます。生成したい文章をlabelsとし、入力をinput-idsとしてファインチューニングを行うことで、可能になります。

# cyberagent/open-calmをファインチューニングするためのコード
学習に使用したいデータセットを準備の上、以下のコードを適宜変更して学習を行ってください。

```python:finetuning.py
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
MODEL_NAME="cyberagent/open-calm-medium"  # modelを選択してください
tokenizer=AutoTokenizer.from_pretrained(MODEL_NAME)
model=AutoModelForCausalLM.from_pretrained(MODEL_NAME)

# datasetがjson形式の場合はこのコードを利用してください
import os, json
with open(os.path.join(dataset),encoding='utf-8') as f:  # 任意のdataset(json形式)を選択してください。
  dataset = json.load(f)
  list_dataset.append(dataset['data'])  # datasetのデータ部分をリストに追加
list_train, list_valid, list_test = list_dataset


n_token = 512
from collections import defaultdict
def preprocess(examples, is_test=False):
  dataset = defaultdict(list)
# original_sentenceは入力文章、generated_sentenceは出力してほしい文章を代入（入力文章部分も含む）
  for example in examples:
      original_sentence, generated_sentence = example["original_sentence"], example["generated_sentence"]

      for i, sentence in enumerate(generated_sentence):
        input = tokenizer(original_sentence, max_length=n_token, padding="max_length", truncation=True)["input_ids"]
        labels = tokenizer(generated_sentence, max_length=n_token, padding="max_length", truncation=True)["input_ids"]

        if (not is_test) or (i == 0):
          dataset["input_ids"].append(input)
          dataset["labels"].append(labels)

  return dataset

from torch.utils.data import Dataset, DataLoader

class Dataset(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"])

dataset_train = Dataset(preprocess(list_train))
dataset_valid = Dataset(preprocess(list_valid))
pp_test, test_answers = preprocess(list_test, is_test=True)
dataset_test = Dataset(pp_test, is_test=True)


from transformers import Trainer, TrainingArguments
training_config = TrainingArguments(
  output_dir = 'output_dir',  # 出力したいディレクトリを入力してください
  num_train_epochs = 4, 
  per_device_train_batch_size = 8,
  per_device_eval_batch_size = 8,
  warmup_steps = 100,
  weight_decay = 0.1,
  save_steps = 10000  # 用いるdatasetに応じてsave_stepsは変えてください
)

trainer = Trainer(
    model = model,                         
    args = training_config,
    train_dataset = dataset_train,
    
)

trainer.train()
```

モデルをHugging faceにアップして以下のコードを実行することで特徴づけされた文章を出力することができます。
```python:generation.py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("open-calm-finetuned", device_map="auto", torch_dtype=torch.float16)  # hugging faceにアップしたモデル名を入力してください
tokenizer = AutoTokenizer.from_pretrained("cyberagent/open-calm-medium")

inputs = tokenizer("AIによって私達の暮らしは、", 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,
    )
    
output = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(output)

```

# 終わりに
今回はcyberagent/open-calmをファインチューニングするためのコードについて書いてきました。
ぜひ参考にしてみてください。
では、ばいにゃん～。