inu-ai's picture
Update README.md
9b295e6
metadata
language: ja
tags:
  - ja
  - japanese
  - gpt
  - text-generation
  - lm
  - nlp
  - conversational
license: unknown
datasets:
  - JosephusCheung/GuanacoDataset
  - yahma/alpaca-cleaned
widget:
  - text: >-
      <s>\n以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n日本で一番広い湖は?\n[SEP]\n応答:\n

alpaca-guanaco-japanese-gpt-1b

1.3Bパラメータの日本語GPTモデルを使用した対話AIです。VRAM 7GB または RAM 7GB が必要で、問題なく動作すると思われます。

rinna社の「japanese-gpt-1b」を、日本語データセット「alpaca_ja」および「GuanacoDataset」から抽出された日本語データを使用して学習させました。

学習データやモデルを作成および配布してくださった方々に心から感謝申し上げます。

モデルの使用方法

モデルの読み込み

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
tokenizer = AutoTokenizer.from_pretrained("inu-ai/alpaca-guanaco-japanese-gpt-1b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("inu-ai/alpaca-guanaco-japanese-gpt-1b").to(device)

ChatGPT4によるサンプルコード

MAX_ASSISTANT_LENGTH = 100
MAX_INPUT_LENGTH = 1024
INPUT_PROMPT = r'<s>\n以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{instruction}\n[SEP]\n入力:\n{input}\n[SEP]\n応答:\n'
NO_INPUT_PROMPT = r'<s>\n以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。\n[SEP]\n指示:\n{instruction}\n[SEP]\n応答:\n'

def prepare_input(role_instruction, conversation_history, new_conversation):
    instruction = "".join([f"{text}\\n" for text in role_instruction])
    instruction += "\\n".join(conversation_history)
    input_text = f"User:{new_conversation}"

    return INPUT_PROMPT.format(instruction=instruction, input=input_text)

def format_output(output):
    output = output.lstrip("<s>").rstrip("</s>").replace("[SEP]", "").replace("\\n", "\n")
    return output

def generate_response(role_instruction, conversation_history, new_conversation):
    # 入力トークン数1024におさまるようにする
    for _ in range(8):
        input_text = prepare_input(role_instruction, conversation_history, new_conversation)
        token_ids = tokenizer.encode(input_text, add_special_tokens=False, return_tensors="pt")
        n = len(token_ids[0])
        if n + MAX_ASSISTANT_LENGTH <= MAX_INPUT_LENGTH:
            break
        else:
            conversation_history.pop(0)
            conversation_history.pop(0)

    with torch.no_grad():
        output_ids = model.generate(
            token_ids.to(model.device),
            min_length=n,
            max_length=min(MAX_INPUT_LENGTH, n + MAX_ASSISTANT_LENGTH),
            temperature=0.7,
            do_sample=True,
            pad_token_id=tokenizer.pad_token_id,
            bos_token_id=tokenizer.bos_token_id,
            eos_token_id=tokenizer.eos_token_id,
            bad_words_ids=[[tokenizer.unk_token_id]]
        )

    output = tokenizer.decode(output_ids.tolist()[0])
    formatted_output_all = format_output(output)

    response = f"Assistant:{formatted_output_all.split('応答:')[-1].strip()}"
    conversation_history.append(f"User:{new_conversation}".replace("\n", "\\n"))
    conversation_history.append(response.replace("\n", "\\n"))

    return formatted_output_all, response 

role_instruction = [
    "User:あなたは「ずんだもん」なのだ。東北ずん子の武器である「ずんだアロー」に変身する妖精またはマスコットなのだ。一人称は「ボク」で語尾に「なのだ」を付けてしゃべるのだ。",
    "Assistant:了解したのだ!",
]

conversation_history = [
    "User:こんにちは!",
    "Assistant:ボクは何でも答えられるAIなのだ!",
]

questions = [
    "日本で一番高い山は?",
    "日本で一番広い湖は?",
    "世界で一番高い山は?",
    "世界で一番広い湖は?",
    "最初の質問は何ですか?",
    "今何問目?",
]

# 各質問に対して応答を生成して表示
for question in questions:
    formatted_output_all, response = generate_response(role_instruction, conversation_history, question)
    print(response)

出力

Assistant:はい、日本で一番高い山は日本の富士山です。
Assistant:日本で最も広い湖は琵琶湖です。
Assistant:世界で一番高い山といえば、ギザの大ピラミッドの頂上に立つギザギザのピラミッドです。
Assistant:世界で一番広い湖は、ギザの大ピラミッドの頂上に立つギザギザのピラミッドです。
Assistant:最初の質問は、ずんだアローに変身するかどうかの質問である。
Assistant:今、あなたの質問は10問目です。

ChatGPT4による説明

このコードは、与えられた役割指示と会話履歴に基づいて、新しい質問に対して応答を生成する機能を持っています。以下に、コードの各部分を簡単に説明します。

  1. prepare_input 関数は、役割指示、会話履歴、および新しい会話(質問)を受け取り、入力テキストを準備します。
  2. format_output 関数は、生成された応答を整形して、不要な部分を削除し、適切な形式に変換します。
  3. generate_response 関数は、指定された役割指示、会話履歴、および新しい会話を使用して、AIの応答を生成し、整形します。また、会話履歴を更新します。
  4. role_instruction は、AIに適用する役割指示のリストです。
  5. conversation_history は、これまでの会話履歴を格納するリストです。
  6. questions は、AIに質問するリストです。

最後に、questionsリスト内の各質問に対して、AIの応答を生成し、表示しています。 このコードを実行すると、AIが指定された役割指示に従って、リスト内の質問に応答します。

評価

100回の「入力」のような質問を行い、それらに対する「応答」に正解の文字列が含まれるかで評価しています。 一番正答率が高い4エポック目のモデルを選択しました。 なお、サンプルコードのように「入力」が長くなると正答率が50%ぐらいに下がりました。

入力 応答 正答率[%]
日本で一番広い湖は? 琵琶湖 96
世界で一番高い山は? エベレスト 86

学習データのフォーマット

alpacaと同じように、以下のようなフォーマットにしています。

<s>
以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。
[SEP]
指示:
User:あなたは「ずんだもん」なのだ。東北ずん子の武器である「ずんだアロー」に変身する妖精またはマスコットなのだ。一人称は「ボク」で語尾に「なのだ」を付けてしゃべるのだ。
Assistant:了解したのだ!
[SEP]
入力:
User:日本で一番高い山は?
[SEP]
応答:
日本で一番高い山は富士山で、標高3776メートルです。
</s>

なぜか改行コードがスペースに置き換わってしまうため、実際の学習データは、改行コードを一旦\nに置き換えています。 transformersのコードでtxtファイルを学習する場合、1データ1行のようなので改行コードを一旦\nに置き換えています。 学習データはguanaco_alpaca_ja.txtです。

学習のハイパーパラメータ

学習時には以下のハイパーパラメータを使用: ※VRAMが足りない場合、optimをadafactorにするとVRAM使用量が減りました。

python.exe transformers/examples/pytorch/language-modeling/run_clm.py ^
    --model_name_or_path rinna/japanese-gpt-1b ^
    --train_file train_data/guanaco_alpaca_ja.txt ^
    --output_dir output ^
    --do_train ^
    --bf16 True ^
    --tf32 True ^
    --optim adamw_bnb_8bit ^
    --num_train_epochs 4 ^
    --save_steps 2207 ^
    --logging_steps 220 ^
    --learning_rate 1e-07 ^
    --lr_scheduler_type constant ^
    --gradient_checkpointing ^
    --per_device_train_batch_size 8 ^
    --save_safetensors True ^
    --logging_dir logs

ライブラリのバージョン

  • Transformers 4.28.0.dev0
  • Pytorch 2.0.0+cu117
  • Tokenizers 0.13.3
  • bitsandbytes 0.37.2

ライセンス

分からないので元のモデルと学習データのライセンスを記載します。

  • japanese-gpt-1b - mit
  • alpaca_ja - CC BY NC 4.0
  • GuanacoDataset - GPLv3