【最新AI応用】OpenAI o1-previewをベースしたRAG?GPT-4oのRAGと比べるとどうなる?(コードあり)
はじめに
こんにちは、AIラボ所属のデータサイエンティスト・AIエンジニアの刘(劉、リュウ)です。
日本の方がよく知っている三国志の主人公、劉備と同じ苗字です。
今朝リリースされた OpenAI o1-preview をベースにして、ローカル環境で RAG(Retrieval Augmented Generation)のシステムを構築してみました。
私は、意図的に RAG で拡張された内容を基に、推論が必要な質問をいくつか試してみました。
その結果、GPT-4o ベースの RAG はまったくうまくいきませんでしたが、OpenAI o1-preview ベースの RAG は多少望ましい回答を得ることができました。
RAGとは?
RAG(Retrieval-Augmented Generation)は、情報検索と生成モデルを組み合わせてより正確で関連性の高い回答を生成する方法です。具体的には、大規模なデータベースや知識ベースから関連情報を検索し、その情報を基にテキストを生成します。これにより、最新の情報や専門的な知識を含む応答を提供することが可能となります。RAGは、チャットボット、質問応答システム、コンテンツ生成など、さまざまな応用分野で活用されています。
OpenAI o1-previewをベースにしたRAGの構築試行
LangChainを利用しますと簡単にローカルでRAGを構築できます。
そこに最新のOpenAI o1-previewを入れました。
まずはモジュールのimportをします。
import os
import openai
from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader, UnstructuredFileLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain.prompts import PromptTemplate
from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings,ChatOpenAI, OpenAIEmbeddings
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from sentence_transformers import SentenceTransformer
from typing import List
from langchain.embeddings.base import Embeddings
次に、RAGしたいファイルを読み込んで処理する関数の定義をします。
class ProcessingFiles:
def load_document(self,file):
name, extension = os.path.splitext(file)
if extension == '.pdf':
print(f'Loading {file}')
loader = PyPDFLoader(file)
elif extension == '.docx':
print(f'Loading {file}')
loader = Docx2txtLoader(file)
elif extension == '.txt':
loader = UnstructuredFileLoader(file)
else:
print('Document format is not supported!')
return None
data = loader.load()
return data
def chunk_data(self,data, chunk_size=500, chunk_overlap=100):
text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
chunks = text_splitter.split_documents(data)
return chunks
def vectorize(self,chunks,embeddings):
vectorstore = FAISS.from_documents(
documents=chunks,
embedding = embeddings
)
vectorstore.save_local("faiss_index")
retriever = vectorstore.as_retriever()
return retriever
そして、RAGに関わる部分をLangChainを利用して関数を定義します:
class LLMGeneral:
def __init__(self):
prompt_template = """
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:
"""
self.RAG_prompt = PromptTemplate.from_template(prompt_template)
self.processing_files = ProcessingFiles()
def format_docs(self,docs):
return "\n\n".join(doc.page_content for doc in docs)
def rag_init(self,file,chunk_size,chunk_overlap,embeddings,llm_client):
meta_data = self.processing_files.load_document(file)
chunks = self.processing_files.chunk_data(meta_data,
chunk_size=chunk_size,
chunk_overlap=chunk_overlap)
retriever = self.processing_files.vectorize(chunks, embeddings)
rag_chain = ({"context": retriever | self.format_docs, "question": RunnablePassthrough()}
| self.RAG_prompt
| llm_client
| StrOutputParser()
)
return rag_chain
def rag_run(self,rag_chain,question):
return rag_chain.invoke(question)
もちろん、OpenAIのクライアントの部分の関数も定義しますね:
class OpenAIFunction:
def __init__(self):
load_dotenv(dotenv_path=".env", override=True)
self.client = openai.OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
)
def create_langchain_client(self,model = "o1-preview",temperature=1):
langchain_client = ChatOpenAI(
model = model, temperature=temperature
)
return langchain_client
最後に、上記で定義済みの部品を使って実行する部分ですね:
processing_files = ProcessingFiles()
llm_general = LLMGeneral()
openai_function = OpenAIFunction()
filepath = "" #ここでRAGしたファイルのパスを入れる
model = "o1-preview" #ここで使いたいLLMを入れる
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
clinet = openai_function.create_langchain_client(model)
rag_chain = llm_general.rag_init(filepath,500,100,embeddings,clinet)
question = "" #聞く問題を入れる
answer = llm_general.rag_run(rag_chain,question)
print(answer)
OpenAI o1-previewベースのRAGはすごい? GPT-4oを超える性能
前のセクションで作成したシステムに、OpenAI o1-preview と GPT-4o をそれぞれ指定して RAG を構築する実験を行いました。
RAG の素材として使用したのは、私の近日中に出版された論文です。
こちらは論文のリンクです:https://doi.org/10.1007/978-981-97-5575-2_9
聞いた問題:
question = """Besides the applications of VCVR-EBM mentioned in the paper,
where else do you think VCVR-EBM could be applied?"""
この問題は、私の論文で書いた内容を超えたもので、正確に答えるには推論が必要です。
GPT-4oベースのRAGシステムの回答:
"""
I don't know.
The context provided only discusses the current applications of VCVR-EBM,
such as route selection, charging planning, and EV acceleration analysis,
without suggesting other potential applications.
"""
わからないですね。その通りですね。問題文では、論文内で触れていない内容について聞かれています。また、論文内の内容がそのままコピペされてきましたが、それは私が聞きたかったことではありません。
OpenAI o1-previewベースのRAGシステムの回答:
"""
Beyond the applications mentioned,
VCVR-EBM could be applied to optimizing electric vehicle fleet management by predicting energy consumption over various routes and conditions.
It might also aid in developing energy-efficient navigation systems,
helping drivers choose routes that minimize energy use.
Additionally, it could be valuable in urban planning for designing roadways and infrastructure that reduce energy consumption for electric vehicles.
"""
素晴らしいですね!
論文内の内容を基に拡張して回答したのですね。ただ、研究の方向性としては WoW 度が少し足りませんが、確かに内容を拡張して推論していることは間違いありません。
ちなみに、それぞれの RAG システムの回答時間は、OpenAI o1-preview が 20.9 秒、GPT-4o が 0.9 秒でした。GPT-4o は非常に速いですね。ただし、OpenAI o1-preview の時間も納得できないほど長いわけではありません。
最後に
今回の実験を通じて、OpenAI o1-previewベースのRAGシステムが推論を伴う質問に対して適切な応答を生成できることが確認できました。GPT-4oと比較して応答時間は若干長くなりますが、それでも十分実用的な範囲内であり、特に精度や推論能力において優位性を感じました。
今後は、さらに複雑な質問や幅広いドメインに対応できるよう、OpenAI o1-previewベースのRAGシステムの調整を進めるとともに、他の最新モデルとの比較実験も行っていきたいと考えています。また、RAGを使った実システムへの導入を検討し、さまざまな応用可能性を模索していきます。
これらの成果を活かして、今後もAI技術を活用した革新的なソリューションを開発し、さまざまな分野での応用を目指していきたいです。
生成AIを活用したPoCや支援にご興味があれば、以下リンクよりお問い合わせください。
Discussion