Databricksでdolly-japanese-gpt-1bを試す(その2)

こちらの続きです。モデルがバージョンアップされてます。

https://qiita.com/taka_yayoi/items/74634c4b1c59c0841412

https://huggingface.co/inu-ai/dolly-japanese-gpt-1b

> 2023年5月7日
「[oasst1-89k-ja](https://huggingface.co/datasets/kunishou/oasst1-89k-ja)」データセットを追加して対話システムに対応しました。1024トークンまで会話履歴を保存できます。 前回のモデルで行った質疑応答の正答率は今回のモデルで下がりました。「日本で一番広い湖は？」が91%から89%、「世界で一番高い山は？」が84%から73%に下がりました。（対話は分けた方が良かったのか、それともoasst1の質が良くないとか）

MLflowインテグレーションは後でやります。

```
%pip install mlflow==2.3
%pip install sentencepiece
```

DBR13.0MLの場合はKernel再起動。

```py:Python
dbutils.library.restartPython()
```

モデルの読み込み。

```py: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/dolly-japanese-gpt-1b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("inu-ai/dolly-japanese-gpt-1b").to(device)
```

この辺りのプロンプトの前処理も勉強しなくては。

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

こちらを実行することで回答が生成されます。

```py:Python
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:日本で2番目に広い湖は琵琶湖です。
---
User:冗談を言ってください。
Assistant:冗談でした。
---
User:世界で一番高い山は？
Assistant:世界で一番高い山はエベレスト山です。
---
User:世界で一番広い湖は？
Assistant:世界で一番広い湖は、南極のビクトリア湖です。
---
User:最初の質問は何ですか？
Assistant:私は、最初の質問が何であるか尋ねました。
---
User:今何問目？
Assistant:今、私は2問目です。
---
User:自己紹介をしてください。
Assistant:私は、日本のアニメや漫画を愛する、かなり若いオタクです。大学では、日本文学と日本語を専攻しました。
---
```

### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)