見出し画像

第8回LangChainもくもく会開催レポート!第9回は8/2(水)開催予定

先日7月6日(木)は第8回LangChainもくもく会の日でした。いつもみなさまお集まりいただき、ありがとうございます!

次回は8月2日(水)20時スタートで開催予定ですので、ご都合のつく方はぜひご参加くださいませ。7月7日現在で既に16名のお申し込みを頂いており、ありがたい限りです!

前回のトピックふりかえり

openai_function_call - Pydantic is all you need

いきなりLangChainの話ではないですが、面白いライブラリとしてopenai_function_callが話題になりました。

Pydanticによるデータ型定義をそのままOpenAI Function Callingの引数に渡せるようにできるライブラリ。

サンプルそのままの転載で恐縮ですが、こんな感じに書けるようです。

import openai
from openai_function_call import OpenAISchema

from pydantic import Field

class UserDetails(OpenAISchema):
    """User Details"""
    name: str = Field(..., description="User's name")
    age: int = Field(..., description="User's age")

completion = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-0613",
    functions=[UserDetails.openai_schema],
    messages=[
        {"role": "system", "content": "I'm going to ask for user details. Use UserDetails to parse this data."},
        {"role": "user", "content": "My name is John Doe and I'm 30 years old."},
    ],
)

user_details = UserDetails.from_response(completion)
print(user_details)  # UserDetails(name="John Doe", age=30)

型定義のところはOpenAISchemaクラスを継承する以外にもデコレータで書くことも可能なよう。

from openai_function_call import openai_schema

@openai_schema
class UserDetails(BaseModel):
    """User Details"""
    name: str = Field(..., description="User's name")
    age: int = Field(..., description="User's age")

「関数の定義をコメントで書くと、LLMによってその通りの内容を実行する」という過激なライブラリとしてMarvinというライブラリがありましたが、書き味的にはMarvinを思い出させるような感じです。

READMEでは高度な例としてDSLを使ったコーディングも紹介されていましたが、かなり独特な感じで読み解くのが大変そう・・・見た目は大変エレガントではあるんですが(まだ使えないようです)。

tasks = (
    ChatCompletion(name="Acme Inc Email Segmentation", model="gpt-3.5-turbo-0613")
    | m.ExpertSystem(task="Segment emails into search queries")
    | MultiTask(subtask_class=Search)
    | m.TaggedMessage(
        tag="email",
        content="Can you find the video I sent last week and also the post about dogs",
    )
    | m.TipsMessage(
        tips=[
            "When unsure about the correct segmentation, try to think about the task as a whole",
            "If acronyms are used expand them to their full form",
            "Use multiple phrases to describe the same thing",
        ]
    )
    | m.ChainOfThought()
)

XXX Is All You Need

Transformer論文での「Attention Is All You Need」からの「XXX Is/Are All You Need」というミームが定着していますが、openai_function_callでも「Pydantic is all you need」と言っていたり、最近ではTextbooks Are All You Needという論文もあったよね、という話。

7月6日は丁度NEC社が130億パラメータで高い性能を発揮できたという発表があった日でして、教科書的なデータセットを利用して性能を上げたのかなー?みたいな話で盛り上がりました。

MultiQueryRetriever

ユーザーからのクエリ表現をLLMによって抽象化し、抽出される検索結果のブレを最小限にする、という話。

ベクトル検索では同じような内容のクエリでも表現が微妙に違うだけで検索結果が異なってしまう問題があり、その問題に対しMultiQueryRetrieverは異なる視点から複数クエリをLLMによって生成した上で検索をかけることで検索結果のバラつきを抑えようという試みで作られています。

セコンさんによるとCLIPによるImageNetのクラス分類戦略とも似ていて面白いですねー、という話に。

CLIPとは自然言語の説明(テキスト)と資格的な情報(画像)間の関連性を推論するためのモデルです。具体的には「テキストから画像への推論」、「画像からテキストへの推論」といったタスクを行うことができます。

https://github.com/mlfoundations/open_clip/blob/197cf453576534386ca32431828ed701c1e01c45/src/open_clip/zero_shot_metadata.py#L2-L83

このコードでは、ある画像に対する表現として80個ほど以下のようなテンプレートが用意されており、

OPENAI_IMAGENET_TEMPLATES = (
    lambda c: f'a bad photo of a {c}.',
    lambda c: f'a photo of many {c}.',
    lambda c: f'a sculpture of a {c}.',
    lambda c: f'a photo of the hard to see {c}.',
    lambda c: f'a low resolution photo of the {c}.',
    lambda c: f'a rendering of a {c}.',
    lambda c: f'graffiti of a {c}.',
    lambda c: f'a bad photo of the {c}.',
    lambda c: f'a cropped photo of the {c}.',
    lambda c: f'a tattoo of a {c}.',
    lambda c: f'the embroidered {c}.',
# ...

例えば「猫(cat)」を表す画像であれば、以下のような表現が自動作成されます。

a photo of a cat
photo of many cat
photo of the hard to see cat

この各表現の埋め込みベクトルの平均を取ることで推論の精度を上げよう、というのがCLIPのクラス分類戦略なのでした。

MultiQueryRetrieverでは最初からLLMで表現を生成しようとしますが、LLMの多用はパフォーマンス的にも問題が起きやすいですし、APIの呼び出しコストもバカになりません。LLMを利用しない処理で上手く抽象化した上で処理を進めていくテクニックはこれからノウハウになっていきそうです。

言語モデル同士は会話が可能なのか?

シグナリングゲームにおいて生じる創発言語によるやりとりは言語モデル同時の会話とも言えるのではないか?という話題。

創発言語はなぜZipf短縮に従わないのか?暗黙の事前分布に基づく再解釈
https://www.anlp.jp/proceedings/annual_meeting/2023/pdf_dir/D3-4.pdf

シグナリングゲームとは一方通行のコミュニケーションモデルです。

例えば「宝の番人」と「探検家」がいるとします。「宝の番人」は宝の場所を知っていますが、宝の場所を直接教えることはできません。しかし「探検家」に「宝は木の近くにある」、「宝は水辺にはない」といったヒントを与えることはできます。これを「シグナル(信号)」と呼びます。「探検家」はこのシグナルをヒントに、どこへ行くべきかを決めます。

「宝の番人」が情報を持っていて、それを伝えるために「探検家」へシグナル(ヒント)を出し、「探検家」がそのシグナルを元に行動を決める。この一連のゲーム全体をシグナリングゲームと呼びます。

また、あるコミュニティ内でこのようなコミュニケーションを取る場合、直感的には伝える頻度が高い単語であればあるほど短く、あまり使われない単語であればあるほど長くなる傾向を見せるのではないかと考えられます。これをZipf短縮と呼びます。

この論文ではそんなシグナリングゲームにおける言語の創発がZipfの短縮法則に従わないという観察結果を示しており、興味深いですねーという話で盛り上がったのでした。

プロトタイピングにはStreamlitを使う?Gradioを使う?

もくもく会参加者ではStreamlitを使う派が多数でした。Streamlit便利ですよね。

加えてStreamlitはChatGPTによる2021年9月時点までのデータと書きぶりがあんまり変わっていないらしく、上手く要件を定義できればほとんどのコードをChatGPTで生成できちゃうとのこと。最高ですね。

こんな感じのデモとか、サクッと作りたいですもんね。

有名なOSSをベクトル検索できるサービス Collectiv.

加えて有名なOSSをベクトル検索できるサービスが紹介されていました。

かなりの数のOSSがインデックスされており、各OSSに対して自然言語でクエリを投げることができます。例えばLangChainを指定して「〇〇するエージェントを作成して」とクエリを投げると、LLMによるコード生成が行われます。めちゃくちゃ便利ですよね。フリーで提供するにはどう考えてもコストが見合わないので、すぐに提供を停止してしまわなければ良いのですが・・・有料化してくれて良いので、継続的にサービスして欲しいですね。

MongoDB Atlas

クラウド版MongoDBであるMongoDB Atlas。ベクトル検索に対応していることと、LangChainから統合的に操作できるようになったことで話題になりました。

5GBまでのストレージ制限という条件で使える無料プランもあります。趣味サイトであれば十分なスペックだったりするので、ありがたいですよね。最近は様々なデータベースがベクトル検索に対応してくれているので、あらゆるアプリケーションで当たり前のようにベクトル検索が扱われる世界観になっていくのだと思います。

所感

Function Callingが発表されてから、LLMの組み込みロジックの考え方が本当に大幅に変わったなー、という印象。先日のLLM Meetup Tokyoでも、Function Callingを上手く扱っていくためのデモやLTが散見されました。

地道にノウハウを積み上げても新発表で一気にゲームチェンジしてしまう世界観は恐ろしくもありますが、黎明期だからこそのエキサイティング感がありますね。そんな時代だからこそ小口化してキャッチアップできる仕組みを淡々と提供していきたいなと思っております。

現場からは以上です。

この記事が参加している募集

イベントレポ

この記事が気に入ったらサポートをしてみませんか?