All Articles

LLMOps を考え始める

追記 (2024/10/22)

2023年5月のこちらの記事で考えていたことをベースに、特に評価についてまとめた資料を出しました。

はじめに

大規模言語モデル、LLM が大流行です。猫も杓子も LLM で、LLM および OpenAI にかなり入れ込んでいた Microsoft の勢いを見て Google が社内に保有していた LLM をサービスとして投入しようとしていたり、AWS が参入表明したり、大規模なテキストデータを擁する Twitter を手中に収めたイーロン・マスクが参入を表明したりと、提供者側プレイヤーはその数も面子もなかなかインパクトがあります。OSS の LLM も「数日おきに何か発表があるなぁ」という感じで日に日に増加しており、商用利用を見据えた使いやすいライセンスの下に開発された LLM も随分と増えてきました。使用者側の面子を見ても、個人ユーザーやベンチャー企業、その他一般企業はもとより、従来この手の新進気鋭のサービス導入がなかなか進まないイメージがあった大手金融機関や官公庁に至るまでかなり深く LLM が広まっている様子です。

立場的には僕も LLM の導入を推進する側に立っており、この狂騒が落ち着いた後の自分のポジションとかに一抹の不安があったりするわけですが、目下一番警戒しているのは自分のクビよりも社会に導入されたシステムの行く末です。ここまで導入が進んでくると狂騒が落ち着いても落ち着かなくても導入されたシステムが社会の中にかなりの数残ることになります。当然それらのシステムは「運用」というものを考えなければなりませんが、この辺りの知見はいまだ整理が進んでいるとは言い難いかと思います。僕自身 MLOps をここ数年考えてきたこともあって LLM についてもその運用を考えていたのですが、どうも従来 MLOps で扱っていた機械学習モデルとは若干性質が異なる部分があり、MLOps の方法論は役に立つものの単純にそのまま当てはめられないような感覚を持っています。おそらく LLM には LLM の性質に合わせた新たな MLOps のサブセット、LLMOps、あるいは LMOps が必要なのではないかと思っています。(デカい言語モデルということが大事っぽいので個人的には LLMOps の方が好きです。ということで以下 LLMOps とします)

そういうわけで、本記事では LLMOps を現場に持っていく前準備として、2023 年 4 月時点での僕の考えをある程度言語化しておこうと思います。体系的と言える程には整理できる気がしないので、「とりあえず言語化してみた」ぐらいの記事として扱ってください。この記事の内容について、僕自身の不利益にもなってしまうので可能な限り誤りは排除するよう努力していますが、誤解や無根拠な想像などが混ざっていることは十分あり得ます。かなり広範囲の知見を要求する話だということが徐々に分かってきており、勉強不足の領域にも必要から足を突っ込んでいるので、誤りが含まれる可能性というのはそれなりに高そうです。鵜呑みにはせず、ヒント程度に取り扱ってください。僕としてはここをきっかけに集合知でブラッシュアップしていければいいのかなと思っています。

前提

MLOps も LLMOps も、その言葉が意味する範囲は言葉の使い手の数だけありそうな程度には十分万人に通用する定義がありません。(「LLMOps」についてはその単語も含めてまだコンセンサス取れてません) あまり雑に言葉を使っても混乱が生じるので、今回 LLMOps を論じるにあたっては、 LLMOps という言葉に含まれると想定する要素を以下の 3 点に絞ります。

  • 本番環境投入後のモデル性能をモニタリングする
  • 性能が一定以下に落ちないようにモデルの性能を回復させる処理を実行する
  • 上記 2 つの要素を持ったシステムを実装する

MLOps で言うところの実験管理など、開発段階を対象とした話も考察すると興味深い気がしますがそのあたりは今回は扱いません。

LLM と従来 ML モデルの違い

整理の起点になるのは LLM と従来 MLOps で扱ってきた比較的小型の ML モデルの性質差かと思います。色々とありますが、ここでは両者の「Ops」を特徴付けることになりそうな 2 つの観点から見た差異を挙げたいと思います。

プロンプトによる出力調整

「従来 MLOps で扱ってきた比較的小型の ML モデル」に含まれるモデルの幅は非常に広く、シンプルな線形回帰モデルや決定木系モデルなどのかなり軽量な小型モデルから、BERT を fine-tuning した文分類モデルや YOLO ベースの画像分類モデル、推薦や検索の一部を担う Embedding 生成モデルなどの重量級モデル (それでも LLM よりはかなり小さめ) まで、多様なモデルが含まれています。恐らくはその多くが教師あり学習モデルであり、学習に使用したデータの分布が大幅に変わってしまった場合に大なり小なり精度が低下してしまうという性質を持っています。この精度を回復させる手段は基本的には最新データを使用したモデルの再学習で、MLOps の方法論はこの再学習をいかに効率よく回すかを重視していました。

対して LLM ですが、LLM の出力を何らかのタスクに特化させる場合の方法として、プロンプト (入力として渡すテキスト) を調整する方法とこれまで通りの fine-tuning による方法の 2 種類があります。プロンプト調整の中にはさらに 2 段階が存在し、単に綿密な指示を書き連ねることでタスクをこなさせる方法と、それに加えてサンプルとなるような入出力ペアをプロンプトに加える few-shot learning が可能です。learning とは言いますが、誤差逆伝搬法によるモデルの更新は伴っていません。入力プロンプトにデータを入れているだけです。

fine-tuning で特定タスクに特化させている場合は基本的に「従来 MLOps で扱ってきた比較的小型の ML モデル」と同じで、精度低下に対するファーストチョイスは再学習になりそうです。これについては MLOps と同様と言えそうです。一方で、プロンプト調整で LLM を特定タスクに特化させている場合はプロンプトの調整により精度回復を試みることになりそうです。すなわち従来必要だったモデルの再学習を実行する必要がなくなり、プロンプトの差し替えにより対応できる可能性があることを意味します。

まとめると、LLM のモデル精度低下への対処法としては、これまでの MLOps のセオリー通りのやり方+テキストの差し替えによる方法の 2 種類があるということです。もちろん fine-tuning したモデルにプロンプトを与えるという事態も (Azure OpenAI Service では不可能ですが OSS LLM の隆盛次第では) 考えられるので、どちらか片方というわけではなく同時に両方という可能性も考える必要が出てきます。

UI としての LLM

これまでの機械学習モデルの役割は、従来のプログラミングではなかなか難しいようなタスクを解くための「関数」という位置づけが大半だったかと思います。その性質上、(例外はもちろんありますが) 多くの機械学習モデルはバックエンド側で動作し、ユーザーに機械学習モデルの出力結果を提示する場合は他のバックエンド側関数と強調して何らか加工した上で、 UI を構成する一部としてその出力結果を表示していたはずです。(例: EC サイトの推薦モデルが出力したアイテムリストをもとにそのアイテム群の画像や価格などを UI に表示する、バッチ処理で向こう 30 日分の売上予測を算出して BI ツール上に表示する、等) もちろん LLM も要約などにおいてはやはり「関数」的な役割をこなしてはいますが、しかしもう 1 つ重大な使い道として UI そのものとしての LLM という使い方があります。Bing に実装されているチャットベースの検索補助インターフェイスなどがその典型例で、機械学習モデルの出力結果があまり加工されずにフロントに表示され、そのままユーザーに渡されています。すなわち機械学習モデルに対する入力をユーザーが直接入力し、ある程度のシステム的な情報付加や内部処理などを経た後にモデルの出力結果が比較的無加工に近い形でユーザーにそのまま届けられるということです。この LLM ベースの新たな UI を NLUI (Natural Language User Interface) としておきます。

関数的な使い方であれば、一応は「正解」を定義することが可能です。回帰や分類は言うまでもなく、要約や翻訳のように表現のブレがあるため正解が 1 つではなかったり、検索や推薦のように上位 k 個以内に目的となる情報が含まれているかで正解が定義できても現実的には「本物の正解」を知ることが困難なケースもありますが、それでも一応は可能です。UI となると本格的にこの「正解」の定義が不可能なように思います。もう少し厳密に言うと、UI の一側面を切り取るとある程度正解を定義できるが UI がこなすタスク全体としての正解は定義できない、ということになるかと思います。

一例として、Bing Chat のような LLM を使用した検索サービスを題材に考えてみます。何らかの自然言語で問いかけるとその問いかけに応え得る検索クエリを言語モデルが発行し、検索クエリを使用して Embedding を利用したベクトル検索や転置インデックスの伝統的な検索が動いて何らかの情報を探し出し、その結果を LLM が縮約して回答しているという構造になっています。粗くまとめると「自然言語の問いかけをクエリに変換するタスク」、「検索クエリから情報を探すタスク」、「文書を要約するタスク」の 3 つのタスクから構成されています。それぞれのタスクを単独で見てみると、 (要約はかなり厳しいですが) これらはある程度評価ができそうに見えます。 ではこの 3 つのタスクをこなすモデルを連結すればそれで「Bing Chat」になるかというとそれは違います。「Bing Chat」を「Bing Chat」たらしめる要素は LLM がユーザーと各タスクを自然言語によってつなぐことで生じる UX にこそあり、この UX を実現する UI としての LLM、すなわち NLUI こそが本質であると考えます。そしてこの UI 部分に求められる要素は雑談的な問いかけから厳密なタスク指向の問いかけまで多岐にわたり、各タスク単独では正解を定義して評価できても NLUI 部分単独では正解を定義して評価することはできないのではないか、というのが僕の主張となります。

とはいえ要約タスクのような頑張れば何らかの指標で評価っぽいことができるもののかなり困難寄りな生成系タスクと比べて NLUI が非連続的に違うものかというそうとも言えない部分があり、生成系タスクの中にある評価困難なタスク群のうち最もユーザーとの接触面積が広く正解の定義が難しくて直接的な評価が難しいタスクが「UI」タスクであると捉えるとより正確になる気がします。

LLMOps の構成要素

「前提」で区切ったスコープにも現れていますが、MLOps にせよ LLMOps にせよ、根本的何らかの方法でモデルの性能をモニタリングし、性能が一定以下に落ちないようにモデルの性能を回復させる処理を実行するということが骨格となります。

モデル性能モニタリング

LLM の出力に対し、何らかの正解を明確に定義してリアルタイムに直接評価ができるようなケースの場合は、シンプルにその評価指標を使用すれば良さそうです。(どういうパターンがこのタイプに合致するか、パッといい例が思いついておりません。ほとんどのケースでは無理な気がしています)

問題は (恐らく実問題の大半を占めるであろう) 直接評価ができないようなケースで、MLOps ではリアルタイムにモデル性能を直接評価するメトリックを取得できない場合の方法論として大きく 3 つあったと認識しています。1 つ目がモデル性能の劣化を引き起こすと考えられる計測可能なメトリック、すなわち入力されるデータの分布を監視する方法です。現在のデータ分布とモデル学習時に使用したデータセットの分布を Wasserstein 距離などデータごとに適した何らかの指標を用いて計算してほぼリアルタイムに算出し、モデルの性能そのものは直接計測しないまま予防的なモデル再学習に繋げることを目的とします。2 つ目が売上、ユーザー離脱率、クリック率などモデルが影響を与えると考えられる複数の間接的な指標を代替指標として使う方法です。結局のところモデルに高い性能を維持して欲しいのはアプリケーションの KPI を改善し、究極的にはユーザー数なり売上なりを増やしたいからです。そういう意味では代替指標を使うやり方はなかなか優れているように見えますが、現実問題としては使える (性能と相関する) 指標の選択が非常に難しかったり、様々な外乱の影響を受けてモデル性能を厳密に測れず機能しなかったりとそれなりに大変な部分はあります。

入力データ分布のドリフトを見る方法は、閾値を切ればそれ単体でモデルの再学習をトリガーさせることができます。代替指標を使う方法は閾値を切って再学習をトリガーすることにも使えますし、新しいモデルが実際に「良いモデル」なのかどうか、A/B テストによる相対的に比較する用途にも使用可能です。

入力データの分布

入力データの分布を監視する方法については、まだ本番投入するには時期が早い気がしています。そもそも対象が LLM の場合、入力されるデータは自然言語であるためその「分布」をどう計算するかがまず大きな問題として立ちはだかります。自然言語のデータについて、単純な語彙や bi-gram や tri-gram の出現頻度、文長などの単純な統計量によって LLM に入力されるデータ分布の変化を捉えることも不可能ではなさそうですが、この辺りの単純な分布の傾向変化だけでモデル性能の劣化が引き起こされのかは今の時点では断言できません。LLM 自体が自然言語に対して高い汎化性能を示しているモデルである以上、その LLM を以てしてもうまく扱いきれなくなるほどのドリフトを検知できる分布とその閾値がどこにあるのか探るというのはちょっと骨が折れそうなタスクです。現実的な落としどころとしては、当面の間は代替指標や Human-in-the-loop による評価値をメインの指標として運用し、その間入力データを蓄積して何らかの入力側分布変化と指標の間に相関が発生していないかを探るというのが良さそうな気がしています。良い分布を探るためのオフライン評価を志すという方向です。

ドリフト検知に使えそうな分布を探る方法はこれで良いとして、次に考えておくべきはどんな分布を使うかです。既に上げた単純な語彙や n-gram などは候補となりますが、いまいち候補が思いつきません。深層学習以前の自然言語処理手法は文の細かい構造を解析することに長けてはいてもマクロな統計量を算出することをあまりしていなかったような気がします。(していたらすいません、僕の勉強不足です、関連書籍など教えていただけると幸いです) NLP2022 の招待講演において田中久美子先生が述べられていた「言語の複雑さを定量的に測るような指標」で触れられていたような指標はかなり有望な気がしています。評価困難な生成系モデルの時代にこそマクロ的自然言語処理 (複雑系としての自然言語処理) が必要な気がしています。(私見です)

当面の本命は代替指標を用いる手法と考えます。

代替指標

代替指標を用いる手法において考えるべきは LLM の良し悪しと相関する指標を見出すことかと思います。UI 改善と似た話になるかと思うのですが、その方面は知見が薄くいまいち分かっていません。この辺りの評価指標設定は恐らくアプリケーションのビジネスモデルというか収益構造とも強く関わってくる気がしていて、実装ごとに異なる指標が選ばれるような気がします。シンプルなところでは平均のターン数やユーザー 1 人あたりの利用頻度などは比較的直接的に LLM の性能を示していそうな気がしますし、課金ユーザーの特典として LLM 機能を提供しているのであれば、課金ユーザーの解約率は良い指標になりそうな気がします。(めちゃくちゃ交絡がありそうですけど) 数ある候補から実際に指標を選定するにあたっては、介入を行ったことでこれらの指標にどのような変化が生じてくるかを見ると良さそうです。介入というのはすなわち A/B テストとかバンディットアルゴリズムとかそういう話になってくるかと思いますが、その辺りの話は後でまとめて扱うのでここでは省略します。

性能回復処理

プロンプトのみでモデルをチューニングしている場合、モデルの更新再学習という重たい計算処理を経ずにプロンプトの差し替えというかなり軽量な操作により「モデルの更新」を試みることができる点がこれまでの MLOps と異なる部分です。プロンプト自体は日本語であれば高々数千文字程度の自然文であり、10MB を超えない程度の規模感です。LLM における性能回復処理はこのプロンプトを記録した json あたりを配信することによって無停止で賄うことができるはずで、ポイントになるのはこのプロンプトのバージョニングと配信、更新の処理を行う部分になりそうです。パイプラインの実装というよりは feature toggle あたりの仕組みに近い形になるはずです。問題はこのプロンプトをどう作るかという部分です。

そしてもちろん fine-tuning している場合には従来通りの再学習パイプライン + デプロイパイプラインが活きてきます。

まとめると、LLM においてはモデルの性能に関わる要素がモデル本体とプロンプトの 2 層存在し、それぞれ再学習パイプライン+デプロイパイプラインと feature toggle 的な仕組みで対応することになりそうです。

オフライン評価とオンライン評価

オフライン評価というのはコンピューターを使って何らか評価指標を計算することで、オンライン評価というのは実際にサービスに実装してモデルの性能を評価することです。オンライン評価の具体的な手法としては、A/B テストやより発展的なやり方としてバンディットアルゴリズムがあります。(Epsilon-Greedy アルゴリズムの特殊な場合が A/B テストであることを考えるとこの言い方はちょっと不適切かもしれませんが)

オフライン評価にせよオンライン評価にせよ、その目的は複数のモデル候補からより良いモデルを見出すことです。オフライン評価であればラベル付きのデータセットを使用して k-fold cross-validation したり十分な量のテストデータを使用してモデルの性能評価を出して複数のモデルを比較し良いモデルを探りますし、オンライン評価で A/B テストをするのであれば、実際に候補モデルを本番投入し、何らかの評価指標の値の差が有意に出るかどうかを検証してモデルを選択します。オフライン評価の特徴はデータセットが共通であればモデルを絶対的に評価できることで、オンライン評価の特徴は 2 個以上のモデルの相対的な良し悪しを確認できることです。

性能回復処理を実行したとき最終的に「本当に性能が回復したのか」ということを検討する必要があるわけですが、そこで有望になるのがこれら 2 つの評価となります。LLM においては定量評価が難しいことを考えると、特にオンライン評価に頼ることになりそうです。このオンライン評価において使用する指標と言うのが、先述の代替指標になります。

Human-in-the-loop の可能性

fine-tuning をしている場合、モデルの改善には InstructGPT と Human-in-the-loop の考え方が応用できそうです。

LLM を ChatGPT へとチューニングしたと思しき手法である Instruct GPT では、以下の手順でモデルのチューニングを行っていたようです。

  1. 人間がプロンプト-応答のペアを実際に用意し、そのデータを使ってモデルを fine-tuning
  2. モデル出力を複数用意してその応答の相対的な良し悪しを人間が評価してデータセットを作り、その良し悪しの大小関係に従うような報酬値を吐く評価モデルを作成
  3. 評価モデルが吐き出す報酬値を最大化するように 1 のモデルを fine-tuning

2 の評価モデルは距離学習っぽい気配があり、具体的な評価点数ではなく複数文の良し悪しという相対的な評価から報酬値を推定している点が重要で、こうすることで Human-in-the-loop のタスク設計がやりやすくなっています。

Human-in-the-loop で得られると嬉しいラベルデータは、1 のプロンプト-応答ペアと、2 のモデルが吐き出す複数文の相対的な良し悪しです。1 のデータはちょっと厳しそうですが、2 の方はモデルの出力を再生成させる UI とか、モデルの出力に対し良い悪いの評価を付けるという形で、人の評価を UI に組み込んでしまえそうです。そうやって得られたデータセットを使用して改めて評価モデルを学習しなおし、それを使用して LLM のチューニングを行うという流れでユーザーに過度な負荷をかけずにモデルの fine-tuning が可能になりそうです。

Human-in-the-loop の目的は LLM のより良いモデルを得ることですが、副産物的に得られる評価モデルも非常に重要です。このモデルは困難であった LLM のオフライン評価に使用できるかもしれません。

プロンプト自動生成

モデルの再学習ではデータセットのラベルをサービスのログから得られるのであればモデルを作り直すことはシステムとして自動的に行うことも可能でした。これに対し LLM 特有のプロンプトによる性能改善の場合、そのプロンプトを何らか説得力のある形で自動生成することは難しいように思えますが、LLM が自然言語を生成するものであることを考慮すれば検討する価値のある方法が 1 つ生じてきます。

プロンプト自体も自然文ですので、別の LLM によってプロンプト自体を生成し、そのプロンプトをオンライン評価に載せて A/B テストによって良し悪しを判定するか、InstructGPT で登場した評価モデルを使用してモデルの性能評価を行うという手法です。ここは LLMOps の話とは少しそれそうなので詳細割愛しますが、プロンプトエンジニアリング的にうまくやればプロンプトを自動生成させること自体は少なくとも可能です。その候補プロンプト群の良し悪しを実際に評価して初めて意味が出てくるわけですが、そこで比較的低コストに候補プロンプトを絞り込むために使用できそうなのが先述の InstructGPT の評価モデルを使用したオフライン評価です。このオフライン評価で候補プロンプトを絞り込み、最終的には A/B テストでオンライン評価の俎上に載せ、実際に良いプロンプトを絞り込んでいくことで、「良いプロンプト」をシステム的に見出していきます。

評価モデルがいくら人間が思う好ましさに沿って訓練されているとはいえ、それが実際にモデルの性能改善に繋がるかどうかはちょっと怪しいところですのでオンライン評価を使うことは私は必須であると考えています。オフラインの評価指標が実際の人手評価とどの程度相関するかというのは機械学習界隈では昔から議論されている重大な問題です。最終的には人手評価の結果が大事というのは一致しつつも、人手評価にはコストがかかるため何らかの比較的簡単に計算できる評価指標が必要とされてきたという理解です。

以前 Twitter あたりで、プロンプトの自動生成と共にその良し悪しを ChatGPT を使って評価する手法を見かけましたが、私としてはこの方法に対しては懐疑的です。fine-tuning したモデルでも人手評価との相関は怪しい部分があるのに、fine-tuning されていない LLM による評価が人が行う評価と相関しているかは相当怪しいはずです。もしモデル評価に使うのであれば、InstructGPT で登場した評価モデルのように人が下す評価のデータを使用してチューニングされたモデルを使うか、少なくとも ChatGPT による評価と人手評価の相関を証明してから行うべきでしょう。

まとめると、大量の候補を LLM によって生成し、そこからある程度候補を絞るためにオフライン評価を使用し、絞り込んだ候補を A/B テストによるオンライン評価で性能評価して最終的なプロンプトを決定するという流れになります。

おわりに

まとめると以下のような感じかと思います。

  • LLM の性能改善には低コストなプロンプトを変更する方法と高コストな fine-tuning による方法がある
    • プロンプトを変更する場合、feature toggle 的な仕組みが使えそう
    • fine-tuning の場合は従来の ML パイプラインが使えそう
  • LLM の定量的な評価はかなり難しそう
  • モデルの性能モニタリングには入力データの分布を見る方法、何らかの KPI 等を代替指標として見る方法が考えられそう
    • 入力データの分布を見る方法としては自然言語故の難しさがありまだ難しそう、複雑系としての自然言語処理が使えるかも
    • 代替指標を使う方法が有望そう
  • 性能回復処理としては fine-tuning であれば InstructGPT + Human-in-the-loop、プロンプトベースであれば自動生成したプロンプトを組み込んでオンライン評価が良さそう

まだ脳内にごちゃごちゃ残ってる気がするので、随時新記事を書いたり更新したりしていきたいと思います。

Published 2023/05/09

ShuntaIto による技術ブログ