ローカル環境で大規模言語モデルAPIサーバを動作させる vLLM編

# はじめに

NVIDIA GPU が乗っている Windows ゲーミングPCにて、大規模言語モデルを使う API サーバを用意する手順をざっと紹介します。
以前公開した [Google Colab で text generation webui を起動し、Llama 2 と会話してみた](https://qiita.com/vfuji/items/f4d1eab889a34ff63626)とは違い、今回は [HuggingFace Transformers (HF) や HuggingFace Text Generation Interface (TGI) と比較して高速と評判?の vLLM](https://blog.vllm.ai/2023/06/20/vllm.html) を使ってみました。

# 前提条件

- NVIDIA GPU を搭載したゲーミング PC (VRAM 8GB 程度の GPU でないと、vLLMを起動できるモデルがないかもしれません)
- NVIDIA Container Toolkit がインストールされていること (手順は[別記事、WindowsゲーミングPCでGPUを使ったコンテナを実行する方法](https://qiita.com/vfuji/items/82d54c421f49794294e5)をご参照下さい)

# 手順

手順は次のようになります。

- vLLM コンテナの起動
- curl を使ったローカルでの動作確認
- ネットワーク経由のアクセス設定と確認

## vLLM コンテナの起動

vLLM は [docker hub/vllm/vllm-openai](https://hub.docker.com/r/vllm/vllm-openai/tags) にコンテナイメージとしても提供されています。ちなみに 2023/12/29 現在 linux/amd64 イメージのみの提供のようです。

こちらのサンプルでは、microsoft/phi-1_5 をモデルとして指定しています。

```sh
 docker run --gpus all -v ~/.cache/huggingface:/root/.cache/huggingface -p 8000:8000 --ipc=host  vllm/vllm-openai:latest --trust-remote-code --model microsoft/phi-1_5
```

起動後に`nvidia-smi`を実行すると、VRAM を 6782MiB　使用していることが確認できました。microsoft/phi-1_5 は 13 億パラメータとなっており、約 3GB のモデルサイズとなります。vLLM がサポートしているモデルの中では、最もコンパクトで起動しやすいモデルかと思います。

microsoft/phi-1_5 起動後の`nvidia-smi`実行結果は次のようになりました。

```sh
$ nvidia-smi
Sat Jan  6 09:17:15 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 530.54                 Driver Version: 531.97       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 4060 L...    On | 00000000:01:00.0 Off |                  N/A |
| N/A   33C    P8                3W /  N/A|   6782MiB /  8188MiB |      0%      Default |
|                                         |                      |                  N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes:                                                                            |
|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |
|        ID   ID                                                             Usage      |
|=======================================================================================|
|    0   N/A  N/A         1      C   /python3.10                               N/A      |
+---------------------------------------------------------------------------------------+
```

一方、microsoft/phi-2 は 27 億パラメータ、約 5GB のモデルサイズとなっていました。私の環境は、システムメモリ 32GB、VRAM 8GB ですが起動しませんでした。エラーメッセージは次のようなものとなります。

```sh
ValueError: No available memory for the cache blocks. Try increasing `gpu_memory_utilization` when initializing the engine.
```

起動中に`nvidia-smi`を定期実行して VRAM 利用状況を確認したのですが、6864 MiB あたりで頭打ちとなり、上記エラーで起動失敗となりました。この際、システムメモリの利用が不足することはありませんでした。このことから VRAM が足りないということかもしれません。

## curl を使ったローカルでの動作確認

まずは ubuntu にて curl を実行し動作を確認しました。ローカルホストでアクセスしています。いきなり prompt を渡すよりも正常な起動を確認した方が効率的かと思います。起動しているモデル名を確認してみましょう。

```sh
$ curl http://localhost:8000/v1/models
{"object":"list","data":[{"id":"microsoft/phi-1_5","object":"model","created":1704500340,"owned_by":"vllm","root":"microsoft/phi-1_5","parent":null,"permission":[{"id":"modelperm-6edb5954333746a39fb104b8966f3a2b","object":"model_permission","created":1704500340,"allow_create_engine":false,"allow_sampling":true,"allow_logprobs":true,"allow_search_indices":false,"allow_view":true,"allow_fine_tuning":false,"organization":"*","group":null,"is_blocking":false}]}]}
```

上記が期待通りに動作したら、次のようなコマンドで prompt を含めたコマンドで実行してみましょう。ちなみに、メッセージにて model: microsoft/phi-1_5 を指定しないとエラーとなりますのでご注意下さい。text-generation-webui だと model　なしでも動作しますので注意が必要です。

また、この記事では、`/v1/completions`を使って動作を確認していますが、`/v1/chat/completions`も動作しますので、必要に応じてご利用下さい。くわしくは、[こちらの OpenAI API reference](https://platform.openai.com/docs/api-reference/making-requests) をご参照ください。

```sh
$ curl http://localhost:8000/v1/completions -H 'Content-Type: application/json' -d '{"model":"m
icrosoft/phi-1_5","prompt": "tell me how to react to potential stroke","max_tokens":100,"temperature":0}'
```

上記コマンドの実行結果は次のようになりました。

```
{
	"id": "cmpl-492dd94508cc4d79b3f4bb9dd6348d72",
	"object": "text_completion",
	"created": 31035,
	"model": "microsoft/phi-1_5",
	"choices": [
		{
			"index": 0,
			"text": " symptoms?\"\n\nDr. Johnson smiled and replied, \"If you suspect someone is having a stroke, it's crucial to act quickly. Call emergency services right away and instruct the person to sit or lie down. Remember, every second counts. Time is of the essence when it comes to stroke treatment.\"\n\nSarah nodded, absorbing the information. She then asked, \"What should I do if I suspect someone is having a stroke?\"\n\nDr. Johnson explained, \"If you suspect someone",
			"logprobs": null,
			"finish_reason": "length"
		}
	],
	"usage": {
		"prompt_tokens": 8,
		"total_tokens": 108,
		"completion_tokens": 100
	}
}
```

## ネットワーク経由のアクセス設定と確認

次のステップとして、別マシンからネットワーク経由でアクセスできるように設定しましょう。初めから、その作業を行なってしまうと、通信できない場合の切り分けが面倒になるので、ステップは一つずつ進める方が良いと思います。

ポート プロキシの設定を行い、Windows ホストの IP アドレスにて WSL Ubuntu にアクセスできるようにします。次のコマンドを PowerShell の管理者権限で実行して下さい。`.wslconfig`を使って自動プロキシを設定している場合は、この設定は不要なはずです。詳しくは [Microsoft の WSL を使用したネットワーク アプリケーションへのアクセス](https://learn.microsoft.com/ja-jp/windows/wsl/networking)をご確認下さい。

```powershell
netsh interface portproxy add v4tov4 listenport=8000 listenaddress=0.0.0.0 connectport=8000 connectaddress={YOUR-WSL-ADDRESS}
```

ちなみに設定の確認コマンドと設定削除のコマンドは次のようになります。

```
# 設定の確認
netsh interface portproxy show v4tov4
# 設定の削除
netsh interface portproxy delete v4tov4 listenport=8000 listenaddress=0.0.0.0
```

ポート プロキシの設定が完了したら、Windows ホストの PowerShell より、curl を実行してモデルを確認してみます。正常にアクセスできた場合、次のような結果になるはずです。

```powershell
 curl http://{YOUR-WINDOWS-HOST-ADDRESS}:8000/v1/models


StatusCode        : 200
StatusDescription : OK
Content           : {"object":"list","data":[{"id":"microsoft/phi-1_5","object":"model","created":1704500531,"owned_by"
                    :"vllm","root":"microsoft/phi-1_5","parent":null,"permission":[{"id":"modelperm-e160ce48b32545298a5
                    75...
RawContent        : HTTP/1.1 200 OK
                    Content-Length: 463
                    Content-Type: application/json
                    Date: Sat, 06 Jan 2024 00:22:11 GMT
                    Server: uvicorn

                    {"object":"list","data":[{"id":"microsoft/phi-1_5","object":"model","creat...
Forms             : {}
Headers           : {[Content-Length, 463], [Content-Type, application/json], [Date, Sat, 06 Jan 2024 00:22:11 GMT], [S
                    erver, uvicorn]}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : System.__ComObject
RawContentLength  : 463

```

Windows ホストからのアクセスが確認できたら、次のステップとして Windows Defender ファイアウォールの受信の規則を追加するなど、適切な設定を行った上で、リモートマシンから `curl http://{YOUR-WINDOWS-HOST-ADDRESS}:8000/v1/models`を実行して下さい。適切な結果が得られたら設定は完了となります。

# 注意事項

## vLLM のサポートしているモデル

vLLM が[サポートしているモデルのリストがこちら](https://docs.vllm.ai/en/latest/models/supported_models.html)にあります。

vLLM では AWQ のサポートについて、最適化中とのことです。高スループットと、よりよい精度を実現するためには量子化されていないモデルを利用することを推奨すると書いてあります。こちらの情報は 2023/12/29 現在の情報となります。

:::note info
Please note that AWQ support in vLLM is under-optimized at the moment. We would recommend using the unquantized version of the model for better accuracy and higher throughput. Currently, you can use AWQ as a way to reduce memory footprint. As of now, it is more suitable for low latency inference with small number of concurrent requests. vLLM’s AWQ implementation have lower throughput than unquantized version.
:::

ロードするモデルサイズが VRAM に乗らないものを選ぶと、起動に失敗することになります。7B (70 億パラメータ)のモデルは 15GB 程度のモデルサイズが多いようです。ということで、用意しているハードウェアを確認の上、それにあったモデルの選定を慎重に行ってください。

## 同時処理数

vLLM は低遅延と多数の同時処理を考慮した大規模利用を想定した仕組みのようです。[vLLM Document/Welcome ページ](https://docs.vllm.ai/en/latest/index.html)に Quantization の項目があり、GPTQ, AWQ, SqueezeLLM とあります。しかし、[Quantization の項目には AWS](https://docs.vllm.ai/en/latest/quantization/auto_awq.html) しか記載がありません。その中の記述にも FP16 から INT4 にすることによって、最大 70% 程度のモデル削減が行える、また主な利点は、低い遅延とメモリ利用量とあります。しかし、先にも書いた英文の Warning ですが、現時点では、高スループットと、よりよい精度を実現するためには量子化されていないモデルを利用することを推奨するとあります。同時処理数 = 高スループットを目指す仕組みなんだろうと思います。

## 分散推論とサービング

vLLM は分散テンソル並列推論とサービングをサポートしています、と [Distributed Inference and Serving](https://docs.vllm.ai/en/latest/serving/distributed_serving.html) (和訳すると、分散推論とサービング)に記載があります。GPU を搭載した複数台のマシンを並列化して大量の処理を捌く仕組みがある、ということのようです。

テンソル並列推論は [Megatron-LM's tensor parallel algorithm](https://arxiv.org/pdf/1909.08053.pdf) をサポートしている、とあります。大規模言語モデルの学習を行う際に利用される仕組みのようです。8 枚の GPU を搭載したマシンを 64 台並べて、512 枚の GPU を利用する方法についての記載があります。詳しくは英文をご確認下さい。

分散ランタイムについては、[Ray](https://github.com/ray-project/ray) をサポートしていると記載があります。[Why Ray?](https://github.com/ray-project/ray?tab=readme-ov-file#why-ray) という項目に記載がありますので簡単に要約して記します。

機械学習のワークロードはますますコンピュート インテンシブ (計算力に依存)となっています。シングル ノードの環境ではこれらの要求に対処することができません。Ray を利用することにより、同じコードをラップトップからクラスタにシームレスにスケールすることができます。

OpenAI も Ray を使っていると [Webサイトの Powered by Ray と言う項目](https://www.ray.io/) に記載がありました。Ray は守備範囲が広いのですが、学習の際に Ray を利用しているという趣旨のメッセージがありましたが、正確な利用範囲は分かりません。

# 所感

完全に個人の見解ですが位置付けを考えてみました。vLLM は大規模利用を想定した仕組みのように思います。低遅延、高スループット、分散推論などが vLLM の特徴と言えそうです。一方、[text-generation-webui](https://github.com/oobabooga/text-generation-webui) では、このような記載は(今のところ)ありません。ということから、vLLM は、どちらかというと本番環境向けを考慮した仕組みと言えそうです。少なくともノート PC 1台で簡単に動作させて満足して良いものではない、ということが理解できました。

# 参考

- [vLLM Document/Welcome ページ](https://docs.vllm.ai/en/latest/index.html)
- [vLLM Blog / vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention](https://blog.vllm.ai/2023/06/20/vllm.html)