クローズドLLMサービス環境を構築するまで

# 1. はじめに
はじめまして。
色々調べてるときにいつも見ていましたが、今回思い切っての初投稿です。

弊社では事業特性上、隔離されたネットワーク環境での開発が主です。
昨今のLLM活用がトレンドとなる中、ほぼ全てのLLMサービスがWEBサービスであることから業務利用がほとんどできず、類似したサービスをローカル環境に構築するしか手がありませんでした。
今回の記事はその初めての環境構築までの経緯と構築手段についての説明になります。

途中までは経緯ですので、構築のところのみ読みたい場合は4章まで飛んでください。

# 2. 事前検証
GPUサーバ購入前に下記のような環境で色々なオープンLLMモデルを試すところから始めました。(2023/10頃)
| 項目 | スペック |
| ---- | ---- |
| CPU | Corei-7 13700E |
| GPU | RTX 4090 |
| メモリ | 128GB |
| OS | Ubuntu 22.04 |

最初は適当にPythonの推論用コードを用意していましたが、LLMをGUI利用するOSSがあったので、この環境に[text-generation-webui](https://github.com/oobabooga/text-generation-webui)を導入してテスト環境を構築して、いくつかのオープンLLMモデルの日本語とコーディングタスクのレスポンスを確認してみました。（多少プロンプトエンジニアリングもしながら）
ただし、RTX 4090のGPUメモリが24GBしかなく（十分でかいんだけどな…）、載せられるモデルが4bit量子化しても70GB(パラメータ数だと35B前後)程度のモデルまででした…
試したのは下記です。
| モデル | 開発会社 | 所感 |
| ---- | ---- | ---- |
| Mistral-7B-Instruct-v0.1 | Mistral | 7bにしては結構いい日本語文章を返してくるが、GPT-4と比較すると足元にも及ばない |
| CyberAgentLM2-7B | サイバーエージェント | Mistralと大体同じ程度 |
| ELYZA-japanese-Llama-2-7b | ELYZA | MistralやCAと比較するとイマイチ |
| Llama-2-13b | Meta | 日本語はイマイチ、英語はまあまあ |
| Phind-CodeLlama-34B-v2 | Phind | 4090に乗るコーディング用モデルとして最強。GPT-4に匹敵。 |

※2024/01頃の検証結果です。2024/12時点で各モデルの新バージョン等が既に公開されています。
このような結果から、クローズド開発環境でLLMを利用した業務効率化を図るに当たり、ターゲットとするタスクは製品コーディングとテストコード作成にまずは絞って効果検証。
私が所属する開発チームのメイン業務の裏でやっていた技術検証業務のコーディングにてきようしたところ、コーディング部分のみに限って言えば計画と比較して2倍近い生産性を叩き出していました。すごい。
（私と共にLLM利活用チームに参画していた後輩と一緒にやっていた案件なのもあり、LLM慣れしていたことから理想値に近い値だった可能性が高いです）

# 3. GPUサーバ購入
事前検証の結果を受け、偉い人たちからパイロットPJに適用してみようという声が上がったので、ここぞとばかりに「PJ適用となるとGPUサーバ必要なので買ってください！」とお願いしたら、あっさりOKが出ました…
H100×4枚くらいあればいいかと思っていたら、他のチームメンバーから「とりあえず積めるだけ積んじゃえ」と言われてH100×8枚で見積もり取った結果、首都近郊の土地込みの家買えるお値段でちびりました。
それでもギリ予算に収まったため、購入することに。

こうして、プライベードPCの100倍以上のお値段のおもちゃを手に入れました。
マジかよ。

# 4. GPUサーバ環境の構築
GPUサーバのLLMサービス環境を構築するに当たり、ざっくりの基本方針は下記のような感じにしました。
- パイロットPJ毎にWEBサーバを分ける
- アクセスするWEBサーバはポートで指定するのを許容
- HTTPSを利用する
- ユーザ管理は最低限実施
- 基本はPhind-Codellama-34B-v2を4bit量子化する
- ユーザーが集中しても負荷分散できるようにする

これらに対応するための技術的な対応は下記です。

- コンテナ(Docker)を活用し、1つcomposeで1サービスセットを起動できるようにする
- WEBアクセス関連はnginxで対応
- 最低限のユーザ管理はtext-generation-webuiで対応可能
- 負荷分散はH100のMIG×nginxのロードバランスで実現

最終的な構成イメージはこのような感じです。
![LLM環境.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3968130/e92313af-f226-7a38-82ed-9258610d3ecb.png)

ざっくりアクセス周りと負荷分散に分けて解説します。

コンテナツールとしておなじみのDockerのdocker-composeを利用して、1つのnginxとそれにぶら下がるtext-generation-webuiのセットを1発で立ち上げられるようにしています。
nginxコンテナはホストとポートフォワーディングを設定し、ユーザはPJに割り当てられているホストのポートを指定してアクセスし、text-generation-webuiに設定されているユーザID/パスワードでログインするという感じです。
更に、サーバ証明書（いわゆるオレオレ証明書）も発行し、nginxの設定でHTTPS化しています。
各コンテナ名とフォワーディングのポート、text-generation-webuiのユーザ情報の設定を変更したdocker-composeと設定ファイルを別に用意することで、簡単に別のPJ用のサービスが用意できていました。

Phind-CodeLlama-34B-v2を4bit量子化すると22GBほどで、H100の80GBのGPUメモリの大半が遊んでしまいます。
ここで、H100以上のGPUで利用できる、スーパーリッチ機能MIG(multi instance gpu)の出番です。詳細は[こちらの記事](https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48)でNVIDIAの方が解説してくれています。
この機能はGPUのメモリとプロセッサを仮想的に分割して、OSからは複数のGPUがあるように見せることができます。
コンテナにGPUを割り当てる場合、GPUが空いてないとコンテナを立ち上げられないため、
分割はある程度ルールが決まっているため、7g.40gb×2の設定をH100に適用して、1枚のH100を40GB×2の仮想GPUに分割。
1PJにH100×2枚分に当たる、text-generation-webui×4つを割り当てて、nginxのロードバランス機能を適用することで負荷分散を実現しています。

ちなみにパイロットPJは最大3つとして、残りのH100×2枚はLLM利活用チームが新規モデルの検証等で利用しています。

# 5. 今後の課題
ここまでやり切った感満載で書きましたが、本格サービス化するには課題は山積みです。
- 本格運用の場合に必要なマシン台数の見積もりが難しい
- 保守運用の体制構築(私と技術系管理職クラスの人の2人で面倒見てる…2人ともメインPJで忙しい…)
- 金がかかりすぎる場合の費用調達（親会社に用意してもらうとか）
- マシン増やしてクラスタ化した場合はDockerでは耐えられないためKubernetes等の適用検討
- LLM活用に関する教育
- RAG適用とRAGデータ準備
- より大きなモデルの適用検討

ちなみに最後のところは実は既に取り組み始めていて、text-generation-webuiを[Ollama](https://ollama.com/)に置き換えた上で、Llama3.2 70Bやllama-3.1-nemotron-70b-instruct辺りを試し初めています。
特にllama-3.1-nemotron-70b-instructは相当に強力で、日本語文書構成検討のメタ打ち、日本語文章の推敲、コーディングタスク等をGPT-4以上のレベルで高速に返してくれます。流石は天下のNVIDIA様。
この辺りはまた今度で。

全体的に技術面よりは、マネジメント面の課題が多く、圧倒的な効果を出せればトントン拍子で進むかもしれませんが、そうは問屋が卸さない…
やはり完全に新しいツールのため、使える人使えない人に差が出ていて、基本的なLLM活用に関する教育をもっとしないといけないことも分かって来ました。
どれだけ業務効率化できているか＝お金の話が出てくると色々とややこしくなるんですが、社畜故逃げられない運命です。

「技術は追求する」「利益が出ることも論理的に説明する」
「両方」やらなくっちゃあならないってのが「社畜」のつらいところだな

# 6. 所感
個人的には良いお勉強がてら新しい技術に触れることができました。
お恥ずかしい話、あまり縁がなくてnginx本格的に触るの初めてでした。こいつロードバランス含めてWEB関連なんでもできますやん。

あと、やはりしばらくは最新LLMモデルの情報拾わないとダメですね。
更新頻度があまりに高すぎる…精度も全然変わるし…
ちなみに色んなモデル触ってる感じやはりパラメータ数は正義です。(当たり前)
小さいモデルで満足している人、でかいモデル使いましょう。
かくいう私も340Bとかは流石に触れていませんが…
RAGやる程度なら小さいモデルでもいいのかもしれませんが、作った環境でRAG触っていないためその内モデル精度によるRAG精度の違いも見てみたいところです。
学習（ファインチューニング）については、恐らく相当なリソース（マシン、期間、人、金）が必要になると思われ、流石にクローズドLLMサービスをやるに当たって費用に見合うのかは疑問があり、今のところアプローチする予定はないです。

弊社に限らず、どうしても切り離されたネットワーク環境でやらないといけない仕事はあるので、こういう経験がそういったところへLLMサービスを導入するビジネスにも繋がったりするのかなとも思います。

技術は身に付けて行け。