tomoima525's blog

Androidとか技術とかその他気になったことを書いているブログ。世界の秘密はカレーの中にある!サンフランシスコから発信中。

LLM開発のいろはをおさらいする~手法からツール、開発ノウハウまで

最近PoCやサービス開発を通じて、どっぷりLLMの開発をしています。この記事では最近の大規模言語モデル(LLM)を使ったアプリケーション開発の種類や手法、ツール群について一通りまとめました。よくあるチャットボットからReActといった最先端のものまで、この記事を読めばエンジニアだけでなく非エンジニアの方でも概要をつかめるよう書きました!それではどうぞ。

そもそもLLMとはなんぞや

大規模言語モデルと呼ばれる機械学習モデルの一種です。自然言語の指示(プロンプト)をトークン (例 https://platform.openai.com/tokenizer )という形にして関連する返答を類推します。

LLMはこのスプレッドシートを見ればわかるとおり雨後の筍のごとくあります。

docs.google.com

代表的なものは OpenAI の ChatGPT, 大量のトークンを処理できる Claude, Metaによるオープンソースの LLaMA などです。そしてこれら代表的なものについてはAPIが提供されています。プロンプトを渡して結果を受け取るという一般的なインタフェースの他に、Embeddings(後述)を生成する、Fine tuning(後述) を行うことなどができます。

LLMアプリケーションとは

LLMにAPIを経由してプロンプトを渡して特定の処理を行わせるサービスです。会話のやりとりの記憶を保持する、LLM自体がデータを持っていない場合外部に取りに行く、そういった処理をパイプラインでつないでいくのがLLMアプリケーションです。

代表的なアプリケーションはチャットボットですが、それ以外の例として

  • 大量データの要約
  • 文章作成
  • バラバラなデータ(unstructured data)からの情報抽出/データ構造の生成
  • Fuzzyな検索

などがあります。

アプリケーションは内部的に何をやっているのか

LLM上で役割を設定し、それぞれが行う指示プロンプトを実行させるというのがキモになります。

例えば、System(答える役), Human(質問する役=人間の入力), Assistant(アウトプットの管理をする役) の3つの役割を分担することがあります。ソフトウェア開発の手順を整理するチャットボットを作成する場合、System: ソフトウェア開発者Human: (人間の入力)Assistant: 途中のやり取りを保持する といった具合になります。

- System: You are a software engineer. Answer as much as possible
- Human: Tell me the step to develop web app
- System: “First step …, Second step…”
- Assistant: Store the conversation. Human said “Tell me the step to…” , System said “First Step….”

LLMで開発できるもの

さて、ここではLLMを使ってどんなものを開発できるのか、関連したガイドとともにリストアップしていきます。これ以外にももちろんありますが、昨今ある代表的なものはカバーできてると思います。

やりとりを記録する

いわゆるチャットボットです。過去のやり取りを何らかの外部キャッシュ(redisとか)にを保持します。そして、APIを呼ぶたびにAssistantを通じて履歴を読み込み、コンテキストを理解させた上で返答させます。

手順を考えさせる

Chain of Thought と呼ばれるものです。LLM自体にお題に対して手順を考えさせます。例えば ”Let’s think step by step” というプロンプトを与えます。課題を小さく分割することで処理しやすくしているわけです。人間と同じですね。

いくつか例をだしてアウトプットを学ばせる

Few shots learningと呼ばれるものです。いわゆる機械学習における”教師あり学習”のようなものです。例えば特定のフォーマットで答えを得たい場合に、その例を提示して、理解させます。

The odd numbers in this group add up to an even number: 4, 8, 9, 15, 12, 2, 1.
A: The answer is False.
The odd numbers in this group add up to an even number: 16,  11, 14, 4, 8, 13, 24.
A: The answer is True.
The odd numbers in this group add up to an even number: 17,  9, 10, 12, 13, 4, 2.
A: The answer is False.
The odd numbers in this group add up to an even number: 15, 32, 5, 13, 82, 7, 1. 
A:

外部データから答えを取得する

RAG (Retrieval Augmented Generation) と言います。データベースやweb検索などツールの使い方を指示して渡し、実際に取得した結果を元に答えさせます。例としてLangchainでは APIをリクエストするプロンプトは以下のような指示を与えています。

`You are given the below API Documentation:
{api_docs}
Using this documentation, generate the full API url to call for answering the user question.
You should build the API url in order to get a response that is as short as possible, while still getting the necessary information to answer the question. Pay attention to deliberately exclude any unnecessary pieces of data in the API call.

Question:{question}
API url:`

`Here is the response from the API:

{api_response}

Summarize this response to answer the original question.`

簡単に上のプロンプトを説明すると、

  • API の documentを読み込ませ、質問に合致するクエリを生成させる
  • 実際のリクエストはLLM外で行う
  • 受け取ったレスポンスを質問内容に応じて整形させる

と言ったステップを行います。

大量のデータから答えを得る

RAGの応用という位置づけです。Embeddingsと呼ばれる技術を使います。まず、大量のデータ(長い論文/記事)を一定のトークンサイズにくぎり、それぞれ embedding と呼ばれるベクトルデータに変換し、Vector DBと呼ばれる、いわゆるKey ValueなDBに保管します。

https://www.pinecone.io/learn/vector-embeddings/ より

次に、実際の質問プロンプトをembeddingに変換した上で、先程のVector DBから関連するベクトルデータを(いくつか)持ってきます。ベクトルデータなので、コサイン類似度で取得することが一般的です。そうして得られた関連するデータたちをLLMにまるっと渡し、要約させます。

この手法を応用し、コードを読み込ませてLLMに質問することなどもできます。

手順を考えさせ、外部のデータを必要なら利用する

ReAct(Reasoning + Act) と呼ばれる手法です。 RAGとCoTの組み合わせのようになっています。具体的には、3つのステップを組み合わせるようなプロンプトを与えます。

  • Thought — やるべきことを考えさせる
  • Act — 指示したツールなどを使って実行させる
  • Observation — 結果を観察し、次の Thought に活かす

最後の Observationがキモです。LLM自身に結果を検証させ、答えの精度を上げることができます。Langchainでは以下のような指示を与えています。

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question`;

LLM自体をチューニングする

いわゆる Fine tuning と呼ばれる手法です。LLM自体に追加で学習を行うことで、よりニーズにフィットした答えを得ることができます。用途としては、LLMでは返せないドメイン知識の獲得や、指示自体を学習させることで、トークン量を減らすと言ったものがあります。

例えば、”指示したゲームをHTMLで作る”と言った機能を開発する場合、”コードだけ出力する”、”VanillaJSで書いて”など、事前に数多くのプロンプトを指示する必要があります。追加で学習することで、”ピンポンゲームを作って”というだけで、期待したアウトプットが得られることになります。

Fine tuningについては、簡単にやろうとするなら、OpenAI の API を利用することができます。しかしながら fine tuningが必要となるユースケースの場合、前提条件として大量の情報を学ばせたいはずなので、オープンソースのLLMへのチューニングということになりそうです。

開発ツール

フレームワーク

LLMアプリケーションを開発する上で現在さまざまなフレームワークやツールが登場しています。目下一番よく利用されているのは LangChain じゃないでしょうか。

www.langchain.com

特徴としては

  • 外部データへの連携やRAG/ReAct/Parserなどのモジュールが一通り揃っている
  • さまざまなLLMに対応
  • Python/TypeScriptのサポート
  • モニタリングの langsmith や Chain自体をAPIとして提供する langserve など付随するツール群が充実している

などがあります。

実質的には便利なプロンプトの集まりのようなもので、大変便利ではあるものの、2023年11月時点でまだマイナーバージョンすら出てません(0.0.329)。モジュールもあれやこれやと乱立してかなりカオスな状況で、特にTypeScript版はドキュメントも不足している部分がかなりあるので、シンプルなチャットボットなどなら、素のAPIを使うことをおすすめします。 OpenAIなどは自前でSDKを提供しています。

github.com

Vector DB

LLM開発においてEmbeddingsは必要不可欠なものとなりつつあります。そのためさまざまな会社が Vector DBを開発しています。

有名どころは PineCone https://www.pinecone.io/ ですが、 この他にもローカルで使えるオープンソースの Chroma https://www.trychroma.com/、Weaviate https://weaviate.io/ などの人気も上がってきています。それ以外にも MongoDB など大手も Vector DBを搭載し始めてます。 選択に迷うところですが、こんなガイドもあります

www.pinecone.io

LLMアプリケーション開発の難しいことと対策

ここでは実際にRAG/ReAct/Embeddingsなど一通り開発した知見から、これまでのアプリケーション開発と比較して難しいところと対策について上げていきます。

エラーハンドリング

LLMは気まぐれなので、意図したレスポンスが返ってこないことが多々あります。いわゆるハルシネーションや、指示の取りこぼしなどです。
通常の開発に比べて意図しないエラー(アウトプット)が発生しやすいので、どうシステマチックに最終的なアウトプットを形成するかが重要です。

対策としては

  • JSONフォーマットで返すようにfew shots learningをする
  • 型チェックを行う
  • 失敗した場合は再度指示を出す

などは組み込むようにしています。肌感覚として、指示量が多い場合は GPT-4を使うと良い結果がでやすいです。

処理できるトークンのリミット

LLMには一度に処理できるトークンに限界値があります。Claudeなどは大量のトークンを受け付けられますが、精度が下がる傾向にあります。

自分は基本的な方針として、

  • 外部キャッシュなどを活用してプロンプトのトークンを減らす
  • パイプラインを作り指示を分割する

を設定しています。

レスポンスタイム

LLMの制約上仕方がないのですが、通常のリクエストより時間がかかります。例えばReActはかなり複雑な指示を達成出来る一方で、結果が出るまで対話を繰り返すため、リクエスト量と結果の待ち時間が非常に長くなります。こういった処理については、

  • 非同期で結果を返すようなUXを検討する
  • ReActにおける思考プロセスを確認し、最初から答えが得られるプロンプトを渡す

などで対策しています。

勉強のためのリンク集

自分はLLM開発にキャッチアップするために以下のサイトを参考にしました。

www.promptingguide.ai

cookbook.openai.com

learnprompting.org

まとめ

LLMのアプリケーション開発はまだ黎明期で、こうしておさらいしている間にもLLMを使った新しい指示方法が生み出されています。つい最近読んだ論文だと、

などが興味深かったです。完全なるブラックボックスですね。

その他、OpenAIの提供するCookbook https://cookbook.openai.com/ は大変参考になるので、LLM開発の際は目を通しておくと役に立ちます。