File size: 9,119 Bytes
9d9cc1a
 
 
 
 
 
 
 
 
 
 
 
 
b24b90f
9d9cc1a
640e70f
9d9cc1a
 
 
 
 
 
f3e44e5
9d9cc1a
 
 
 
f3e44e5
 
9d9cc1a
 
 
 
 
 
 
 
 
 
f3e44e5
9d9cc1a
 
b24b90f
 
9d9cc1a
 
 
 
 
b24b90f
9d9cc1a
 
 
 
 
 
 
 
 
 
 
 
 
 
b24b90f
9d9cc1a
 
 
 
 
 
 
 
 
b24b90f
9d9cc1a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a7ecc92
 
 
 
 
 
 
 
 
 
 
f3e44e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b24b90f
a7ecc92
9d9cc1a
 
 
 
 
f3e44e5
9d9cc1a
b24b90f
9d9cc1a
b24b90f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a7ecc92
 
79ea26d
b24b90f
 
9d9cc1a
 
a7ecc92
9d9cc1a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b24b90f
9d9cc1a
 
 
 
a7ecc92
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
---
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](https://huggingface.co/rinna/japanese-gpt-1b)」を、日本語データセット「[alpaca_ja](https://github.com/shi3z/alpaca_ja)」および「[GuanacoDataset](https://huggingface.co/datasets/JosephusCheung/GuanacoDataset)」から抽出された日本語データを使用して学習させました。

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

# モデルの使用方法

## モデルの読み込み

```python
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によるサンプルコード

```python
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()
            conversation_history.pop()

    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](https://github.com/tatsu-lab/stanford_alpaca)と同じように、以下のようなフォーマットにしています。

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

~なぜか改行コードがスペースに置き換わってしまうため、実際の学習データは、改行コードを一旦`\n`に置き換えています。~
transformersのコードでtxtファイルを学習する場合、1データ1行のようなので改行コードを一旦`\n`に置き換えています。
学習データは[guanaco_alpaca_ja.txt](train_data/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