N番煎じでStability.aiのJapanese Stable LM Instruct Gamma 7BをDatabricksで動かす

出ないかなーと言っていたら1カ月以内に出てくるところが、この領域の恐ろしいところ。

# 導入

Stability.ai社が2本のLLMを新たに公開しました。

https://ja.stability.ai/blog/japanese-stable-lm-3b-4e1tjapanese-stable-lm-gamma-7b

このうち、Japanese Stable LM Gamma 7BはMistral 7Bベースであり、各種ベンチマークでも非常に優秀な結果を出しているようです。（一部のベンチにスコアが低いものもあるので、得手不得手ありそうですが）
また、ライセンスはApache 2.0で、使いやすいライセンスになっています。

面白そうなのでInstructモデルであるJapanese Stable LM Instruct Gamma 7Bを試してみました。

https://huggingface.co/stabilityai/japanese-stablelm-instruct-gamma-7b

※　おそらくこの記事を投稿するころにはいろいろ試されている気がするので、先んじてN番煎じシリーズにしておきます。

# 検証環境

Databricks on AWSを使って検証しています。
DBRは14.0ML、g5.2xlargeのクラスタを利用して推論します。

# Step1. モデルの取得と保管

[こちら](https://qiita.com/isanakamishiro2/items/4258143eb9de158cda69)とほぼ同じです。
最終的にUnity CatalogのVolumesに保管します。

```python
%pip install -U -qq transformers accelerate

dbutils.library.restartPython()
```

```python
import os
from huggingface_hub import snapshot_download

UC_VOLUME = "/Volumes/モデルの保管場所"

model = "stabilityai/japanese-stablelm-instruct-gamma-7b"
local_dir = f"/tmp/{model}"
uc_dir = "/models--stabilityai--japanese-stablelm-instruct-gamma-7b"

snapshot_location = snapshot_download(
    repo_id=model,
    local_dir=local_dir,
    local_dir_use_symlinks=False,
)

dbutils.fs.cp(f"file:{local_dir}", f"{UC_VOLUME}{uc_dir}", recurse=True)
```


# Step2. サンプル実行

huggingface上のサンプルコードを実行してみます。

```python
%pip install -U -qq transformers accelerate bitsandbytes sentencepiece 
dbutils.library.restartPython()
```

```python
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

UC_VOLUME = "/Volumes/モデルの保管場所"

tokenizer_path = f"{UC_VOLUME}/models--stabilityai--japanese-stablelm-instruct-gamma-7b"
model_path = f"{UC_VOLUME}/models--stabilityai--japanese-stablelm-instruct-gamma-7b"


tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
model = AutoModelForCausalLM.from_pretrained(
    model_path,
    trust_remote_code=True,
    torch_dtype="auto",
)
model.eval()

if torch.cuda.is_available():
    model = model.to("cuda")


def build_prompt(user_query, inputs="", sep="\n\n### "):
    sys_msg = "以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。"
    p = sys_msg
    roles = ["指示", "応答"]
    msgs = [": \n" + user_query, ": \n"]
    if inputs:
        roles.insert(1, "入力")
        msgs.insert(1, ": \n" + inputs)
    for role, msg in zip(roles, msgs):
        p += sep + role + msg
    return p


# Infer with prompt without any additional input
user_inputs = {"user_query": "与えられたことわざの意味を小学生でも分かるように教えてください。", "inputs": "情けは人のためならず"}
prompt = build_prompt(**user_inputs)

input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")

tokens = model.generate(
    input_ids.to(device=model.device),
    max_new_tokens=256,
    temperature=1,
    top_p=0.95,
    do_sample=True,
)

out = tokenizer.decode(
    tokens[0][input_ids.shape[1] :], skip_special_tokens=True
).strip()
print(out)
```

```:出力
情けは人のためならず。自分にかえってくるから情けは人のためになる。情けをかけると、いつか自分にもかけがえのない友人ができる。情けをかけなければ、いつまで経っても友だちができない。

どうやら、このことわざは、親切を示すことで、自分や他の人の生活にポジティブな影響を与えることができるということを伝えようとしているようです。このことわざは、「親切」の例を使っていますが、他のことでも同じことが言えると思います。このことわざは、中国の兵法書「孫子」の「兵は詭道なり。だますことが最善」とい
```

どうやらプロンプトは以下のようなテンプレートのようですね。
```
(システムプロンプト)

### 指示: 
(インストラクションの中身)

### 入力: 
(コンテキストの中身)

### 応答: 
```

# Step3. 推論

関数に簡易ラップした上で他にもいくつかやってみます。
```temperature```を0.1にしてなるべく回答を固定します。

```python
# 関数ラップ
def generate_batch(instruction: str, inputs:str=None):

    # Infer with prompt without any additional input
    user_inputs = {"user_query": instruction, "inputs": inputs}
    prompt = build_prompt(**user_inputs)

    input_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")

    tokens = model.generate(
        input_ids.to(device=model.device),
        max_new_tokens=512,
        temperature=0.1,
        repetition_penalty=1.1,
        top_p=0.95,
        do_sample=True,
    )

    out = tokenizer.decode(
        tokens[0][input_ids.shape[1] :], skip_special_tokens=True
    ).strip()
    return out
```

## 単純質問

```python
print(
    generate_batch(
        "与えられた入力の回答を箇条書きしてください。",
        "宮崎駿の代表作品",
    )
)
```

```:出力
- 風の谷のナウシカ（1984年）
- 天空の城ラピュタ（1986年）
- となりのトトロ（1988年）
- 魔女の宅急便（1989年）
- おもひでぽろぽろ（1991年）
- 紅の豚（1992年）
- 平成狸合戦ぽんぽこ（1994年）
- もののけ姫（1997年）
- 千と千尋の神隠し（2001年）
- 猫の恩返し（2002年）
- ハウルの動く城（2004年）
- ゲド戦記（2006年）
- ポニョ（2008年）
- コクリコ坂から（2011年）
- 風立ちぬ（2013年）
- 思い出のマーニー（2014年）
- レッドタートル ある島の物語（2016年）
- メアリと魔女の花（2017年）
- バケモノの子（2015年）
- 君たちはどう生きるか（2020年）
- イノセンス（2004年）
- パンダコパンダ（1972年）
- パンダコパンダ雨降りサンポ（1973年）
- アルプスの少女ハイジ（1974年）
- 未来少年コナン（1978年）
- ルパン三世 カリオストロの城（1979年）
- 火垂るの墓（1988年）
- おじゃる丸（1999年
```

それなりに合っていますが、いろいろと間違ってますね。

## コード生成

```python
print(
    generate_batch(
        "与えられた入力の内容を解決するPythonのソースコードを出力してください。",
        "ランダムな10個の要素を作成し、ランダムに並び替えてください。その後、リストをソートしてください。",
    )
)
```

```:出力
これがPythonでの実装例です：

```python
import random

list = [random.randint(0, 9) for i in range(10)]
print(list)

list.sort()
print(list)
`` `

```

プロンプトをそれなりに具体化すると、きちんと動くコードを出してくれました。

## 表作成指示

```python
print(
    generate_batch(
        "与えられた入力の回答をMarkdown形式で表のみ出力してください。",
        "東京、名古屋、大阪、京都、福岡の「都道府県」「おすすめスポット」「おすすめグルメ」",
    )
)
```

```:出力
|都道府県|おすすめスポット|おすすめグルメ|
|-|-|-|
|東京|皇居|ラーメン|
|名古屋|名古屋城|味噌カツ|
|大阪|通天閣|串カツ|
|京都|金閣寺|湯豆腐|
|福岡|太宰府天満宮|もつ鍋|

このように、GPT-3.5-large-inferenceモデルは、与えられたテキストから特定の情報を抽出し、それをマークダウン形式の表に変換することができます。この例では、都道府県、おすすめスポット、おすすめグルメの各列を持つ表を生成しています。このような機能は、ユーザーが必要な情報を簡単に見つけることができるように、Webサイトやドキュメントで非常に有用です。

OpenAIのチューニングされたGPT-3.5-large-inferenceモデルは、このようなタスクを高精度で実行することができます。

この例では、GPT-3.5-large-inferenceモデルは、与えられたテキストから特定の情報を抽出し、それをマークダウン形式の表に変換することができます。この例では、都道府県、おすすめスポット、おすすめグルメの各列を持つ表を生成しています。このような機能は、ユーザーが必要な情報を簡単に見つけることができるように、Webサイトやドキ
```

後続の説明が入ってるのがイマイチ。プロンプトの工夫が必要ですね。

## おまけ

```python
print(
    generate_batch(
        "与えられた入力に適した人物名とその理由を回答してください。",
        "まどか☆マギカで一番かわいい人",
    )
)
```

```:出力
まどか☆マギカで一番可愛い人は、ほむらちゃんです！彼女は魔法少女であり、他のキャラクターよりも多くのエピソードがあることから、最も印象的なキャラクターであると言えます。また、彼女は非常に優しく、思いやりがあり、自分の感情を表現するのが上手です。彼女はまた、非常に勇敢で、何度も危険な状況に直面しています。彼女はまどかの親友であり、まどかのことを心から大切にしています。彼女はまどかのことを本当に気にかけており、まどかが幸せになれるように祈っています。ほむらちゃんは、まどか☆マギカで一番かわいい人です！

私はほむらちゃんが好きな理由は、彼女がとても強くて勇敢で、まどかのことを本当に気にかけているからです。彼女はまどかの親友であり、まどかのことを心から大切にしています。彼女はまどかのことを本当に気にかけており、まどかが幸せになれるように祈っています。ほむらちゃんは、まどか☆マギカで一番かわいい人です！

私はほむらちゃんが好きな理由は、彼女がとても強くて勇敢で、まどかのことを本当に気にかけているからです。彼女はまどかの親友であり、まどかのことを
```

急にカジュアル。圧がすごい。


# まとめ

Mistral 7B自体が含む課題なのかもしれませんが、同様の内容を繰り返しやすい印象です。
```repetition_penalty```の設定で解消する気もしますが、少し工夫が必要かなと思いました。

Mistral 7Bベースの日本語対応LLMということで、比較的小さいパラメータサイズながら高い性能を発揮しているモデルだと思います。今後もこういった日本語対応モデルが続々出てきたらいいですね。