Closed6

FastChatを試す

kun432kun432

https://github.com/lm-sys/FastChat

FastChatは、大規模な言語モデルベースのチャットボットを訓練、提供、評価するためのオープンプラットフォームである。

  • FastChatはChatbot Arena (https://chat.lmsys.org/)をサポートしており、30以上のLLMに対して500万以上のチャットリクエストに対応している。
  • Arenaは、LLMのサイドバイサイドの戦いから10万人以上の人間の投票を集め、オンラインのLLM Eloリーダーボードを編集している。

FastChatのコア機能は以下の通り:

  • 最先端のモデル(Vicuna、MT-Benchなど)のトレーニングおよび評価コード。
  • ウェブUIとOpenAI互換のRESTful APIを備えた分散型マルチモデル・サービング・システム。

GradioベースのUIとOpenAI互換のAPIがあるという感じっぽい。

kun432kun432

一旦READMEに従って写経していく。
npaka先生の記事も参考にしつつ、Japanese StableLM Gammaを使うところがゴール。

https://note.com/npaka/n/n04e5b5d8ef11

今回はpyenv/pyenv-virtualenvで。

$ git clone https://github.com/lm-sys/FastChat && cd FastChat
$ pyenv virtualenv 3.10.13 FastChat
$ pyenv local FastChat
$ pip install --upgrade pip
$ pip install -e ".[model_worker,webui]"

CLI

$ python -m fastchat.serve.cli --model-path lmsys/vicuna-7b-v1.5 --debug
LlamaForCausalLM(
  (model): LlamaModel(
    (embed_tokens): Embedding(32000, 4096, padding_idx=0)
    (layers): ModuleList(
      (0-31): 32 x LlamaDecoderLayer(
        (self_attn): LlamaAttention(
          (q_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (k_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (v_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (o_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (rotary_emb): LlamaRotaryEmbedding()
        )
        (mlp): LlamaMLP(
          (gate_proj): Linear(in_features=4096, out_features=11008, bias=False)
          (up_proj): Linear(in_features=4096, out_features=11008, bias=False)
          (down_proj): Linear(in_features=11008, out_features=4096, bias=False)
          (act_fn): SiLUActivation()
        )
        (input_layernorm): LlamaRMSNorm()
        (post_attention_layernorm): LlamaRMSNorm()
      )
    )
    (norm): LlamaRMSNorm()
  )
  (lm_head): Linear(in_features=4096, out_features=32000, bias=False)
)
USER: 富士山の高さは?
ASSISTANT: 富士山の高さは3,776メートルです。
{'conv_template': 'vicuna_v1.1', 'prompt': "A chat between a curious user and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the user's questions. USER: 富士山の高さは? ASSISTANT:", 'outputs': '富士山の高さは3,776メートルです。', 'speed (token/s)': 28.73}
Thu Dec  7 14:26:10 2023
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 530.30.02              Driver Version: 530.30.02    CUDA Version: 12.1     |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                  Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf            Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA GeForce RTX 4090         On | 00000000:01:00.0 Off |                  Off |
|  0%   47C    P8               16W / 450W|  13425MiB / 24564MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A      1185      G   /usr/lib/xorg/Xorg                            9MiB |
|    0   N/A  N/A      1431      G   /usr/bin/gnome-shell                         10MiB |
|    0   N/A  N/A   3918628      C   ....pyenv/versions/FastChat/bin/python    13400MiB |
+---------------------------------------------------------------------------------------+

GUI

GUIで動かす場合は、controller、worker、gradioのWebUIの3つを起動する。

$ python -m fastchat.serve.controller &
2023-12-07 14:34:12 | INFO | controller | args: Namespace(host='localhost', port=21001, dispatch_method='shortest_queue', ssl=False)
2023-12-07 14:34:12 | ERROR | stderr | INFO:     Started server process [3929161]
2023-12-07 14:34:12 | ERROR | stderr | INFO:     Waiting for application startup.
2023-12-07 14:34:12 | ERROR | stderr | INFO:     Application startup complete.
2023-12-07 14:34:12 | ERROR | stderr | INFO:     Uvicorn running on http://localhost:21001 (Press CTRL+C to quit)
$ python -m fastchat.serve.model_worker --model-path lmsys/vicuna-7b-v1.5 &
2023-12-07 14:36:14 | INFO | model_worker | Register to controller
2023-12-07 14:36:14 | INFO | controller | Register a new worker: http://localhost:21002
2023-12-07 14:36:14 | INFO | controller | Register done: http://localhost:21002, {'model_names': ['vicuna-7b-v1.5'], 'speed': 1, 'queue_length': 0}
2023-12-07 14:36:14 | INFO | stdout | INFO:     127.0.0.1:40928 - "POST /register_worker HTTP/1.1" 200 OK
2023-12-07 14:36:14 | ERROR | stderr | INFO:     Started server process [3930126]
2023-12-07 14:36:14 | ERROR | stderr | INFO:     Waiting for application startup.
2023-12-07 14:36:14 | ERROR | stderr | INFO:     Application startup complete.
2023-12-07 14:36:14 | ERROR | stderr | INFO:     Uvicorn running on http://localhost:21002 (Press CTRL+C to quit)
$ python -m fastchat.serve.gradio_web_server &
2023-12-07 14:36:27 | INFO | gradio_web_server | args: Namespace(host='0.0.0.0', port=None, share=False, controller_url='http://localhost:21001', concurrency_count=10, model_list_mode='once', moderate=False, show_terms_of_use=False, add_chatgpt=False, add_claude=False, add_palm=False, register_openai_compatible_models=None, gradio_auth_path=None)
2023-12-07 14:36:27 | INFO | gradio_web_server | Models: ['vicuna-7b-v1.5']
2023-12-07 14:36:27 | INFO | stdout | Running on local URL:  http://0.0.0.0:7860
2023-12-07 14:36:27 | INFO | stdout |
2023-12-07 14:36:27 | INFO | stdout | To create a public link, set `share=True` in `launch()`.

ブラウザでhttp://X.X.X.X:7860にアクセス

OpenAI互換APIサーバ

https://github.com/lm-sys/FastChat/blob/main/docs/openai_api.md

前述のcontroller、workerに加えて、openai_api_serverを起動する

$ python -m fastchat.serve.openai_api_server --host localhost --port 8000 &

curlで

$ curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "vicuna-7b-v1.5",
    "messages": [{"role": "user", "content": "富士山の高さは?"}]
  }' | jq -r .
{
  "id": "chatcmpl-3r9feFGMRib5dGX3M7uQsF",
  "object": "chat.completion",
  "created": 1701928103,
  "model": "vicuna-7b-v1.5",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "富士山の高さは、活火山であるため、正確な高さを知ることはできません。しかし、富士山の高さは約3,776メートルです。"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 47,
    "total_tokens": 110,
    "completion_tokens": 63
  }
}
kun432kun432

ということで、japanese-stablelm-instruct-gammaを使う。npaka先生の記事通り、何も修正せずに普通に立ち上げてみる。

$ python -m fastchat.serve.cli --model-path /SOMEWHERE/japanese-stablelm-instruct-gamma-7b --debug
MistralForCausalLM(
  (model): MistralModel(
    (embed_tokens): Embedding(32000, 4096)
    (layers): ModuleList(
      (0-31): 32 x MistralDecoderLayer(
        (self_attn): MistralAttention(
          (q_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (k_proj): Linear(in_features=4096, out_features=1024, bias=False)
          (v_proj): Linear(in_features=4096, out_features=1024, bias=False)
          (o_proj): Linear(in_features=4096, out_features=4096, bias=False)
          (rotary_emb): MistralRotaryEmbedding()
        )
        (mlp): MistralMLP(
          (gate_proj): Linear(in_features=4096, out_features=14336, bias=False)
          (up_proj): Linear(in_features=4096, out_features=14336, bias=False)
          (down_proj): Linear(in_features=14336, out_features=4096, bias=False)
          (act_fn): SiLUActivation()
        )
        (input_layernorm): MistralRMSNorm()
        (post_attention_layernorm): MistralRMSNorm()
      )
    )
    (norm): MistralRMSNorm()
  )
  (lm_head): Linear(in_features=4096, out_features=32000, bias=False)
)
<|USER|>: 富士山の高さは?
<|ASSISTANT|>: 富士山の高さは3776mです。
- StableLM is excited to be able to help the user.
- StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user.
- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user.
- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is also able to write poetry, short stories, and make jokes.
- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.
- StableLM is excited to be able to help the user. StableLM is more than just an information source, StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.
- StableLM is excited to be able to help the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is more than just an information source, StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.
- StableLM is excited to be able to help the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is more than just an information source, StableLM
{
	'conv_template': 'stablelm',
	'prompt': '<|SYSTEM|># StableLM Tuned (Alpha version)\n- StableLM is a helpful and harmless open-source AI language model developed by StabilityAI.\n- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user.\n- StableLM is more than just an information source, StableLM is also able to write poetry, short stories, and make jokes.\n- StableLM will refuse to participate in anything that could harm a human.\n<|USER|>富士山の高さは?<|ASSISTANT|>',
	'outputs': '富士山の高さは3776mです。\n- StableLM is excited to be able to help the user.\n- StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user.\n- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user.\n- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is also able to write poetry, short stories, and make jokes.\n- StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.\n- StableLM is excited to be able to help the user. StableLM is more than just an information source, StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.\n- StableLM is excited to be able to help the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is more than just an information source, StableLM is also able to write poetry, short stories, and make jokes. StableLM will refuse to participate in anything that could harm a human.\n- StableLM is excited to be able to help the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user, but will refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is excited to be able to help the user and will not refuse to do anything that could be considered harmful to the user. StableLM is more than just an information source, StableLM',
	'speed (token/s)': 50.04
}

壊れた応答になっている。定義がないのでstablelm-tuned-alpha-7bとして認識されそのテンプレートが適用されているためだと思う。

ということでnpaka先生の記事を参考に修正する。修正内容はそのままで良かったのだけど1点だけ注意する点としては、fastchat/model/model_adapter.pyregister_model_adapter()でモデルを判定しているようなのだけど、

  • ModelAdapterクラスのmatchで、モデルパス名に合致したものが選択される
  • 上から順に判定する

という感じのようなので、少なくともstablelm-tuned-alpha-7bの定義であるregister_model_adapter(StableLMAdapter)よりも上に書く必要がある。まあ一番上に定義しておけば良いと思う。

# Note: the registration order matters.
# The one registered earlier has a higher matching priority.
register_model_adapter(JSLMGammaAdapter)     # 追加
register_model_adapter(PeftModelAdapter)
register_model_adapter(StableVicunaAdapter)
register_model_adapter(VicunaAdapter)
(snip)
register_model_adapter(StableLMAdapter)
(snip)

自分の場合はモデルもローカルに既にダウンロード済みなので、ModelAdapterアダプタクラスでtokenizerのパスもそれに合わせて修正した。

    def load_model(self, model_path: str, from_pretrained_kwargs: dict):
        revision = from_pretrained_kwargs.get("revision", "main")

        tokenizer = LlamaTokenizer.from_pretrained(
            model_path     # ここ
        )
        from_pretrained_kwargs.pop("trust_remote_code", None)
$ python -m fastchat.serve.cli --model-path /SOMEWHERE/japanese-stablelm-instruct-gamma-7b --debug
指示: 富士山の高さは?
応答: 富士山の高さは3776メートルです。高さは、地球の中心から火山の頂上までの距離を示します。富士山は、世界で最も高い活火山であり、世界で2番目に高い山です。訪問者は、山の周りのトレイルでハイキングを楽しむことができます。また、山の反対側からも、山頂に到達するためのさまざまなルートを旅行することができます。山の上部は11月から4月まで閉鎖されています。また、2013年には、火山には活動的な地滑り地域があるという警告が出ています。

富士山は、1707年に大噴火を起こし、日本で最も深刻な自然災害を引き起こした。火山は、その比類のない外観と美しさで有名であり、多くの芸術家や作家にインスピレーションを与えてきた。富士山は日本の国歌の一部にもなっています。

富士山は、日本列島にある太平洋プレートと北米プレートの境界にあり、この地域では数年に1回の割合で地震が発生しています。富士山周辺では、1707年と1708年に大規模な地滑りが発生したことが知られています。また、富士山は日本で最も深い火山であり、1メートル下に溶岩があります。

富士山は、山の上部がシラスと呼ばれる�
{
  'conv_template': 'jslm_gamma',
  'prompt': '以下は、タスクを説明する指示と、文脈のある入力の組み合わせです。要求を適切に満たす応答を書きなさい。\n\n### 指示: \n富士山の高さは?\n\n### 応答: \n',
  'outputs': '富士山の高さは3776メートルです。高さは、地球の中心から火山の頂上までの距離を示します。富士山は、世界で最も高い活火山であり、世界で2番目に高い山です。訪問者は、山の周りのトレイルでハイキングを楽しむことができます。また、山の反対側からも、山頂に到達するためのさまざまなルートを旅行することができます。山の上部は11月から4月まで閉鎖されています。また、2013年には、火山には活動的な地滑り地域があるという警告が出ています。\n\n富士山は、1707年に大噴火を起こし、日本で最も深刻な自然災害を引き起こした。火山は、その比類のない外観と美しさで有名であり、多くの芸術家や作家にインスピレーションを与えてきた。富士山は日本の国歌の一部にもなっています。\n\n富士山は、日本列島にある太平洋プレートと北米プレートの境界にあり、この地域では数年に1回の割合で地震が発生しています。富士山周辺では、1707年と1708年に大規模な地滑りが発生したことが知られています。また、富士山は日本で最も深い火山であり、1メートル下に溶岩があります。\n\n富士山は、山の上部がシラスと呼ばれる�',
  'speed (token/s)': 46.56
}
kun432kun432

おまけ

fastchat/model/model_registry.pyに以下のような説明を追加しておくと、gradioで選択したモデルの説明としてちゃんと表示される。

register_model_info(
    ["japanese-stablelm-instruct-gamma-7b"],
    "Japanese Stable LM Instruct Gamma 7B",
    "https://huggingface.co/stabilityai/japanese-stablelm-instruct-gamma-7b",
    "Stability AI's 7B-parameter decoder-only Japanese language model fine-tuned on instruction-following datasets, built on top of the base model Japanese Stable LM Base Gamma 7B.",
)
kun432kun432

久々にローカルLLM触ってるけど、とりあえず回答はあってるんだけど、うしろに余計な出力がついてきたりするのってそういうものだっけか?以前試したときにもこういう事は起きてはいたけど、出力部分でもう少しフィルタ出来た気がするんだけど。

FastChatのモデル定義見てるけど、このあたりをどう制御しているのかがわからない。うーん。

普通に動かして比較してみるかというところ。

このスクラップは2023/12/07にクローズされました