宣伝
「おしゃべり魔理沙AI」という霧雨魔理沙とおしゃべりできるline-botを作りました。
よかったら遊んでみてください。
- linebotのurl
- linebotの運営用Xアカウント
作者のお財布事情とlinebotとの兼ね合いにより、通算で月200回しか使えないです。
「動かないな」と思ったら、そういうことです。
赤字垂れ流しなので許してください。
概要
ローカルLLMをファインチューニングして、東方projectというゲームに出てくる霧雨魔理沙とおしゃべりできるlinebotを作りました。
Qiitaの記事としては「①モデル編」と「②linebot編」の2つになります。
まず最初に「①モデル編」からよむことをおすすめします
「②linebot編」では「①モデル編」でつくった「おしゃべり魔理沙モデル」をどうlinebotに組み込んだのかについて話をしていきます。
詳細
全体像について
全体像は上の画像を御覧ください。
AWSのlambdaを中心にlinebotを作りました。
- linebot
- lambda
- dynamoDB
のそれぞれについて軽く説明していきます。
linebotについて
linebotとlambdaを組み合わせる手法については、すでにわかりやすい既存の記事がいくつか出ているため、説明を省略します。
簡単に説明しますと、line上にユーザーからの投稿があると、webhookを通じてlambdaが起動します。
次にlambdaでapiを叩くことで、lineにbotが返信を投稿します。
lambdaについて
lambdaとはイベント(今回はlineの投稿)が起こったときに、特定のプログラム(今回はモデル推論とlineの返信)をサーバーレスで行ってくれるサービスです。
モデルファイルの参照方法
今回はlambdaでモデルを推論させるため、lambda上でモデルを読み込めるようにする必要があります。
lambda上でファイルを参照するためには下表の手法があります。
手法 | サイズ上限 | lambda以外の料金 |
---|---|---|
zipファイルを直接アップロード | 250MB | なし |
ECR経由でdocker環境を反映 | 10GB | ECRの利用料 |
EFSなどにファイルをあげ、モデル読み込みのたびにそこを参照 | なし | EFSの利用料 |
今回は、この中でECRを用いる手法を選びました。
「zipファイルを直接アップロード」は250MBしかアップロードできないのでLLMは当然動きません。
「EFSなどにファイルをあげ、モデル読み込みのたびにそこを参照」はサイズ制限はないものの、ファイルの読み書きごとに課金されるため、lambdaがコールドスタートする度に数GB分の課金がなされます。
そのため、利用を見送りました。
(最初はそこに気づかず2000円くらい溶かした)
10GBにどう抑えたか
「ECR経由でdocker環境を反映」を選んだ場合、docker自体のサイズを10GBに抑える必要があります。
japanese-novel-gpt-j-6bのファイルサイズを確認してみましょう
11GBです。
残念ながらこのままでは、どう頑張ってもdockerのサイズが10GBを超過します。
ファイルサイズを抑える手法として
- CTranslate2
- インストールするpytorchをcpuのみにする
の2つを行いました。
CTranslate2
CTranslate2とは既存の巨大モデルを量子化&CPUに最適化することで、コンパクト&高速に推論できるようにするライブラリです。
これを用いて「おしゃべり魔理沙モデル」をint8に変換したところ11GBが6GBになりました。
(ただ、これを施すと精度が落ち、返答が印象として無愛想になるので、できれば使いたくない)
インストールするpytorchをcpuのみにする
上記のことをしても、まだ10GBを超過していました。
理由としては、Pytorchのライブラリの巨大さです。
デフォルトのPytorchのインストールでは、CPU推論用のコードとGPU推論用のコードの両方が入っています。
lambdaではCPU推論しかできないため、GPU推論用のコードは余分です。
なので、インストールしないようにすることで容量の削減をしました。
やり方としては
requirements.txtのtorchを
torch==1.13.1+cpu
のように書けばOKです。
これだけで数GBの容量を節約できます。
コールドスタート問題
awsのlambdaにはコールドスタートとウォームスタートの二種類のスタート方法があります。
コールドスタートとは新しく環境自体をたちあげるスタート方法ことで、ウォームスタートはすでにある環境から実行関数を実施するスタート方法です。
lambdaは自動的に環境が落ちるため、基本的には「しばらく放置するとコールドスタート」「直近の利用があるとウォームスタート」と考えていいでしょう。
CTranslate2を導入したこともあり、モデルの推論は自体は数秒で終わります。
しかしながら、モデルをメモリに載せる(モデルの読み込み)のところで数分の処理時間がかかります。
そのため、lambdaがコールドスタートするとlineの応答が数分かかる自体になってしまいます。
linebotで返信が数分待ちはなかなかしんどいところがあるので、何か対策を考えたいものです。
コールドスタートの問題をマシにする手法として以下の3つの手法が挙げられます。
- Provisioned Concurrencyを使う
- snapstartを使う
- 定期的にlambdaを実行する
それぞれについて説明していきます
Provisioned Concurrency
Provisioned Concurrencyとはlambda環境を常にウォームスタート状態にする設定項目のことです。
これを使うのが一番正統派な解決方法だと思います。
しかし、通常のLambdaの利用料金に加え、追加で時間あたりでコストがかかります。
そこそこの額がかかるため、今回は利用を見送りました。
snapstartを使う
環境自体をキャッシュにして保存することで、コールドスタートの時間を短くしてくれるオプションです。
無料でつかえるみたいなのですが、まだjavaでしか使えないようです。
残念。
定期的にlambdaを実行する
定期的にlambda関数を実行してあげることで、コールドスタートになるずらくします。
この方法は確実性がなく、5分に一度起動していても1時間に1回くらい落ちます。
しかし、Provisioned Concurrencyよりもは価格はかからなそうでした。
なので今回はeventbrifgeをlambdaと組み合わせることで、定期的にlambdaを読み出して、コールドスタートを防ぐ手法を採用しました。
dynamoDBについて
魔理沙の発言を推論させるに当たり、過去の会話ログをプロンプトに入れたほうが精度が良くなります。
具体的に言うと、過去の会話ログをプロンプトに含めると
ユーザー: 好きな食べ物は?
魔理沙: 肉だ
ユーザー: それのどんなところが好き?
魔理沙: 味だな
のような会話ができるようになります。
そのため、過去の会話ログを記録するデータベースと紐づけました。
まとめ
linebotに既存の言語モデルをどう組み込むかについて説明しました。
みなさんもお気に入りのキャラクターを公開してみてはいかがでしょうか。