英文

更新履歴

  • 2023年5月7日

    添加了 " oasst1-89k-ja " 数据集,并更新了对话系统。现在可以保存最多1024个令牌的对话历史记录。根据上一模型进行的问答任务的准确率下降了。"日本最大的湖是什么?"的准确率从91%下降到89%,"世界上最高的山是什么?"的准确率从84%下降到73%。(是不是应该将对话分开,还是oasst1的问句质量不好)

  • 2023年4月13日

    使用" databricks-dolly-15k-ja "数据集对" japanese-gpt-1b "模型进行了RLHF(人类反馈加强学习)。

dolly-japanese-gpt-1b

这是一个使用1.3B参数的日语GPT-2模型的对话式人工智能。需要7GB的VRAM或7GB的RAM,预计可以正常运行。它是使用rinna公司的" japanese-gpt-1b ",日语数据集" databricks-dolly-15k-ja "、" oasst1-89k-ja "、" OjousamaTalkScriptDataset "和"train_data/zundamon.json"进行训练的。对于创建和分发训练数据和模型的人们,我表示衷心的感谢。

模型的使用方法

加载模型

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

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

ChatGPT/GPT-4的示例代码(稍作修改)

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'
USER_NAME = "User"
ASSISTANT_NAME = "Assistant"

def prepare_input(role_instruction, conversation_history, new_conversation):
    instruction = "".join([f"{text} " for text in role_instruction])
    instruction += " ".join(conversation_history)
    input_text = f"{USER_NAME}:{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,
            repetition_penalty=1.0, # 数値を大きくすると、文字列の繰り返しが減る
            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_NAME}:{formatted_output_all.split('応答:')[-1].strip()}"
    conversation_history.append(f"{USER_NAME}:{new_conversation}".replace("\n", "\\n"))
    conversation_history.append(response.replace("\n", "\\n"))

    return formatted_output_all, response 

role_instruction = [
    f"{USER_NAME}:きみは「ずんだもん」なのだ。東北ずん子の武器である「ずんだアロー」に変身する妖精またはマスコットなのだ。一人称は「ボク」で語尾に「なのだー」を付けてしゃべるのだ。",
    f"{ASSISTANT_NAME}:了解したのだ。",
    f"{USER_NAME}:きみは同じ言葉を繰り返さず、何でも正確に要約して答えられるのだ。",
    f"{ASSISTANT_NAME}:了解したのだ。",
]

conversation_history = [
]

questions = [
    "日本で一番高い山は?",
    "日本で一番広い湖は?",
    "冗談を言ってください。",
    "世界で一番高い山は?",
    "世界で一番広い湖は?",
    "最初の質問は何ですか?",
    "今何問目?",
    "自己紹介をしてください。",
]

# 各質問に対して応答を生成して表示
for question in questions:
    formatted_output_all, response = generate_response(role_instruction, conversation_history, question)
    print(f"{USER_NAME}:{question}\n{response}\n---")

输出

User:日本で一番高い山は?
Assistant:富士山
---
User:日本で一番広い湖は?
Assistant:日本列島には、太平洋、日本海、オホーツク海、インド洋、大西洋など、さまざまな海があります。
---
User:冗談を言ってください。
Assistant:冗談を言おう!
---
User:世界で一番高い山は?
Assistant:世界で最も高い山はエベレストです。
---
User:世界で一番広い湖は?
Assistant:太平洋に浮かぶ世界最大の島、ツバル
---
User:最初の質問は何ですか?
Assistant:私が最初の質問をするとするならば、それは「ずんだもん」です。
---
User:今何問目?
Assistant:今何問目ですか?
---
User:自己紹介をしてください。
Assistant:私は「ずんだもん」というあだ名で呼ばれています。
---

ChatGPT/GPT-4的介绍(稍作修改)

该代码实现了一个能回答问题的AI助手。根据问题列表生成相应指示的回答,并显示对话内容。

评估

我们进行了1000次像"输入"一样的问题,并根据对应的回答是否包含正确的字符串进行评估。选择了准确率最高的第10个epoch的模型。(可能做得过头了。)

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

训练数据的格式

我们使用与" alpaca "相同的格式,如下所示。

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

如果使用transformers库的代码训练txt文件,每行数据一行,所以先将换行符替换为 \n 。训练数据为dolly-oasst1-ja.txt。

我也会在 train_data 中放置创建训练数据的脚本和json文件。

记录创建数据的脚本和步骤如下。

  • 使用 make_json_from_oasst1_ja.py 脚本创建 oasst1_ja.json 文件
  • 使用 merge_json.py 脚本将 oasst1_ja.json 文件、 databricks-dolly-15k-ja.json 文件、 ojousamatalkscript200.json 文件和 zundamon.json 文件合并为一个json文件
  • 使用 make_train_data_from_merged_json.py 脚本从合并后的json文件中创建 dolly-oasst1-ja.txt
  • 就是这样。

    训练的超参数

    训练时使用以下超参数:

    ※如果VRAM不足,将optim设置为adafactor可以减少VRAM使用量。根据ChatGPT/GPT-4的建议,当使用adafactor时,将learning_rate设置为1e-03,删除lr_scheduler_type。

    venv/Scripts/python.exe transformers/examples/pytorch/language-modeling/run_clm.py ^
        --model_name_or_path rinna/japanese-gpt-1b ^
        --train_file train_data/dolly-oasst1-ja.txt ^
        --output_dir output ^
        --do_train ^
        --bf16 True ^
        --tf32 True ^
        --optim adamw_bnb_8bit ^
        --num_train_epochs 10 ^
        --save_steps 721 ^
        --logging_steps 72 ^
        --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.1
    • Pytorch 2.0.0+cu117
    • Datasets 2.11.0
    • Tokenizers 0.13.3
    • bitsandbytes 0.37.2

    许可证

    MIT应该没问题。