🧹

LlamaIndex + Weaviate + Llavaを使って、不毛なことをする。

2023/12/12に公開

前提

まず、WeaviateとLlavaを試してみたいっていうのが大前提にあります。
具体的には以下2つです、これさえできればそれでいいです。
①WeaviateにPDFを取り込んで、質問に答えてもらう。
②Llavaに画像を取り込んで、その画像が何か答えてもらう。

やりたいこと

Weaviateに読み込んだPDFから、Llavaに取り込んだ画像についての情報を取り出す。

今回Weaviateに取り込むPDFは、こちらです。
CyberAgent第26回定時株主総会2023年9月期質疑応答の要約
読み物として面白いので選びました。
https://d2utiq8et4vl56.cloudfront.net/files/topics/29478_ext_26_0.pdf?v=1702261938

今回Llavaに画像の内容を答えてもらうための画像はこれです。

(以下Youtubeのアイコンから引用)
https://www.youtube.com/@user-cl4gn4iu4s

グランブルーファンタジーです。
上記PDFにグランブルーファンタジーに関する質疑応答があるためです。
出来レースです。
Llavaにグランブルーファンタジーの画像を渡して、グランブルーファンタジーだと認識してもらい、グランブルーファンタジーに関してPDFを検索するという不毛なことをします。

実際サービスとして提供するなら、PDF読み込んで、そこからよく出てくる固有名詞を抽出して、その固有名詞の画像をいくつか画面上に表示して、その画像をクリックすると、その画像に関するPDFの内容が説明されるとかの建付けが良さそうですが、今回はそこまでやりたくないのでこの形になりました。

不毛なことをしている感は拭えないのですが、勘弁してください。

使う技術

LlamaIndex

大規模言語モデル(LLM)を使ったアプリケーション構築を支援するデータフレームワーク。
https://www.llamaindex.ai/

Weaviate

ベクトル検索およびマルチモーダル検索を実現するためのオープンソースのデータプラットフォーム。
https://weaviate.io/

Cohere

今回はWeaviateのClientにPDFを取り込んで、それをEmbeddingする際に使用します。
https://cohere.com/

Llava

GPT-4と同等の能力を持つ大規模マルチモーダルモデル。

Replicate

機械学習モデルをホストし、デプロイし、実行するためのプラットフォーム。
今回Llavaは、このReplicateが公開しているAPI経由で使用しています。
https://replicate.com/
https://replicate.com/yorickvp/llava-13b

前準備

API KEY取得

今回は、CohereとReplicateのAPI KEYを使用するので、サイトに登録して取得します。

ファイル準備

pythonファイルを作成し、それと同じディレクトリに先程のCyberAgent第26回定時株主総会2023年9月期質疑応答の要約のpdfグランブルーファンタジーの画像を配置します。

コードを書こう

ライブラリの準備

from llama_index.multi_modal_llms import ReplicateMultiModal
from llama_index.schema import ImageDocument
from llama_index.multi_modal_llms.replicate_multi_modal import (
    REPLICATE_MULTI_MODAL_LLM_MODELS,
)
import os
from llama_index import download_loader
from llama_index.node_parser import SimpleNodeParser
import weaviate
from pathlib import Path
from llama_index.vector_stores import WeaviateVectorStore
from llama_index import VectorStoreIndex, StorageContext

API KEY、画像とPDF準備

os.environ["REPLICATE_API_TOKEN"] = "自分のAPIKEY"
os.environ["X-Cohere-Api-Key"] = "自分のAPIKEY"

imageUrl = "./GF.jpg"
pdfPath = "./cyber.pdf"

llava_multi_modal_llm = ReplicateMultiModal(
    model=REPLICATE_MULTI_MODAL_LLM_MODELS["llava-13b"],
    max_new_tokens=200,
    temperature=0.1,
)

Llava

  • Replicate経由でLlavaモデルを取得。
  • 画像に写っているゲームの名前を端的に答えてもらうためのプロンプトを準備(グランブルーファンタジーが返ってくることを予定)。
  • モデルにプロンプトと画像を渡して返答を得る。
llava_multi_modal_llm = ReplicateMultiModal(
    model=REPLICATE_MULTI_MODAL_LLM_MODELS["llava-13b"],
    max_new_tokens=200,
    temperature=0.1,
)

prompt = "Please answer this game name shortly"

llava_response = llava_multi_modal_llm.complete(
    prompt=prompt,
    image_documents=[ImageDocument(image_path=imageUrl)],
)

Weaviate

  • WeaviateのClientを作成。EmbeddingはCohereを使用するのでCohereのAPI KEYを渡す。
  • PDFを読み込む
  • SimpleNodeParserを使用してPDFのテキストを分割
  • Weaviate上で検索用のインデックスを作成
  • Llavaからの返答を元に検索を行う
client = weaviate.Client(
    embedded_options=weaviate.embedded.EmbeddedOptions(), 
    additional_headers={
        "X-Cohere-Api-Key": os.environ["X-Cohere-Api-Key"]
    })

PDFReader = download_loader("PDFReader")
loader = PDFReader()
documents = loader.load_data(file=Path(pdfPath))

parser = SimpleNodeParser()
nodes = parser.get_nodes_from_documents(documents)

vector_store = WeaviateVectorStore(weaviate_client = client, index_name="PDF", text_key="content")

storage_context = StorageContext.from_defaults(vector_store = vector_store)

index = VectorStoreIndex(nodes, storage_context = storage_context)

query_engine = index.as_query_engine()
query = llava_response.text + "について"
response = query_engine.query(query)
print(query)
print(response)

コードを動かした結果

このpythonファイルを作成したところ、以下の結果を得ることができました。

Granblue fantasyについて
「グランブルーファンタジー」は、長く運用しているタイトルで、運営もユーザーの方とコミュニケーションを重要視しています。今後は、リアルの「グラブルフェス 2023」や、コンシューマゲームの提供など、ユーザーの皆様に喜んでいただけるような企画を予定しており、ご意見を活かしながら長く遊んで頂けるように努めていきます。

Llavaは画像をグランブルーファンタジーだと認識し、グランブルーファンタジーに関する文章をWeaviateは返していることが確認できました。

以上

参考

LlamaIndexとWeaviateを一緒に使う例

https://github.com/weaviate/recipes/blob/main/integrations/llamaindex/simple-query-engine/simple-query-engine.py

Llavaに関するウェビナー

Llavaの概要を、Llavaの開発者の方直々に話してくれている
https://www.youtube.com/watch?v=k7i2BpeLUss

LlamaIndexとLlavaを一緒に使う例

3つ例を出しているが、今回は1番上の例を参考にした。
https://github.com/run-llama/llama_index/blob/main/docs/examples/multi_modal/llava_demo.ipynb

WeaviateのEmbeddingにcohereを使う方法

https://weaviate.io/developers/weaviate/modules/retriever-vectorizer-modules/text2vec-cohere

おまけ(完成形コード)

# ReplicateMultiModalモデルをインポート
from llama_index.multi_modal_llms import ReplicateMultiModal  

# ImageDocumentスキーマをインポート
from llama_index.schema import ImageDocument 

# ReplicateMultiModalのモデルをインポート
from llama_index.multi_modal_llms.replicate_multi_modal import (
    REPLICATE_MULTI_MODAL_LLM_MODELS, # プレトレインモデル定義
)

# ローダーをダウンロード
from llama_index import download_loader # ローダー取得用関数  

# ノードパーサーをインポート 
from llama_index.node_parser import SimpleNodeParser # ノードパーサークラス

# Weaviateベクトルストアをインポート
from llama_index.vector_stores import WeaviateVectorStore # Weaviateベクトルストア

# IndexとContextをインポート
from llama_index import VectorStoreIndex, StorageContext # インデックスとコンテキスト

import os # 環境変数やファイルパス操作用

# Weaviateクライアントをインポート  
import weaviate  # Weaviateへのアクセス

from pathlib import Path # ファイルパスオブジェクト

# 環境変数にAPIキーをセット  
os.environ["REPLICATE_API_TOKEN"] = "自分のAPIKEY" 

os.environ["X-Cohere-Api-Key"] = "自分のAPIKEY"  

# 画像のパスを指定
imageUrl = "./グランブルーファンタジーに関する画像"  
# PDFのパスを指定
pdfPath = "./CyberAgent第26回定時株主総会2023年9月期質疑応答の要約のpdf"

# ReplicateMultiModalモデルをインスタンス化
llava_multi_modal_llm = ReplicateMultiModal( 
    model=REPLICATE_MULTI_MODAL_LLM_MODELS["llava-13b"], # Llava-13bモデル指定
    max_new_tokens=200, # 生成トークン数
    temperature=0.1, # 確率分布の温度パラメータ
)

# プロンプトを定義   
prompt = "Please answer this game name shortly"  

# Replicateに画像とテキストのプロンプトを渡し応答を取得
llava_response = llava_multi_modal_llm.complete(  
    prompt=prompt,
    image_documents=[ImageDocument(image_path=imageUrl)], # 画像パス指定
)

# Weaviateクライアントをインスタンス化 
client = weaviate.Client(
    embedded_options=weaviate.embedded.EmbeddedOptions(),  # 埋め込みオプション指定
    additional_headers={
        "X-Cohere-Api-Key": os.environ["X-Cohere-Api-Key"] # APIキー指定
    })

# PDFReaderローダーをダウンロード    
PDFReader = download_loader("PDFReader")  

# ローダーをインスタンス化
loader = PDFReader()   

# PDFを読み込みドキュメントにパース  
documents = loader.load_data(file=Path(pdfPath))   

# ノードパーサーをインスタンス化 
parser = SimpleNodeParser()  

# ドキュメントからノードを抽出 
nodes = parser.get_nodes_from_documents(documents)    

# Weaviateベクトルストアをインスタンス化
vector_store = WeaviateVectorStore(weaviate_client = client, index_name="PDF", text_key="content")   

# コンテキストをインスタンス化   
storage_context = StorageContext.from_defaults(vector_store = vector_store)  

# インデックスをベクトルストアとコンテキストから構築  
index = VectorStoreIndex(nodes, storage_context = storage_context)    

# インデックスからクエリエンジンを取得  
query_engine = index.as_query_engine()  

# クエリを定義 
query = llava_response.text + "について"    

# クエリを実行し結果を取得  
response = query_engine.query(query)   

# クエリを表示
print(query)
# 結果を表示
print(response)

Discussion