1. はじめに
本記事では、RAGを対象としたユニークなコンペ「Raggle」に取り組みましたので、その内容を共有します。
今回参加したコンペは、ロート製薬が協賛し、Well-beingレポート、財務諸表、商品紹介資料、研究論文など多様なドキュメントを対象とするRAGの構築を競うものでした。
大規模言語モデル(LLM)や自然言語処理を活用する方にとって、実践的なヒントを得られる内容になっています。
参加の動機
私はChatGPTの登場をきっかけにプログラミングを学び、KaggleやSIGNATEといったAI・データ分析のコンペに参加するようになりました。LLMは私にとって最も身近なAIであり、自然言語処理も得意な分野です。そのため、今回のコンペには強い関心を持ち、挑戦したいと考えました。
1位獲得の感想
自分の原点でもあるLLMを活用したコンペで好成績をとれたことを、とても嬉しく思います。プログラミングを始めた頃は、AIに頼ってばかりでしたが、コンペを通じてよく使われる技術やライブラリを習得し、馴染みのないライブラリも短期間で使いこなせるようになりました。その積み重ねが今回の結果につながったと感じています。これまで多くを学ばせてもらったLLMに、少しでも恩返しができたような気持ちです。
2. 技術的なアプローチ
2.1. 検索手法を使わず全文入力を採用
RAGにはさまざまな手法があります。例えば、GraphRAG、embeddingモデルを用いたベクトル検索、キーワード検索、LLMのコンテキストに全文を入れる方法(Long-Context, LC)などです。精度の面では、GraphRAG > LC ≧ ベクトル検索 > キーワード検索という傾向があります。今回のコンペのレギュレーション上、GraphRAGやembeddingモデルは利用できなかったため、LCを採用しました。
RAG手法 | 精度 | 特徴 | ライブラリなど | 使用可否 |
---|---|---|---|---|
GraphRAG | 高 | 意味のつながりを活用して情報検索 | Neo4jなど | ❌ |
Long-Context (LC) | 中 | 文書全体をLLMに入力 | - | ✅採用 |
ベクトル検索(Embedding) | 中 | ベクトルデータベースを活用 | Chroma, Faissなど | ✅ |
キーワード検索 | 低 | 単純な文字列マッチング | Rank-BM25 | ✅ |
(注記) コンペ終了後に気づきましたが、ベースラインのコードではOpenAIのembeddingモデルが使用されていました。そのため、ベクトルデータベースを活用することも可能だったと考えられます。
2.2. 複数文書の処理方法
すべての文書をLLMに直接入力することはできなかったため、LLMによる推論を2段階に分けました。
- 1段階目: 回答に必要な文書を特定(実質的な検索プロセス)
-
2段階目: 選択された文書をもとに回答を生成
この方法により、LCの利点を活かしつつ、不要な文書を排除することで精度を向上させました。
2.3. スコアのばらつきへの対応
AIやデータ分析のコンペの経験上、リーダーボードを見ながら自分に経験の少ない手法を試すと、過学習につながり、最終結果が悪化することがよくあります。今回は、回答生成後に正しさを評価し修正する処理を検討しましたが、スコアが極端に下がることがあったため、シンプルに質問に対して直接回答を生成する形を採用しました。
2.4. 画像データの処理
gpt-4o-miniのVision機能を使用して画像からテキストを抽出しましたが、一部のテキストが抜け落ちる問題が発生しました。APIを利用すると乱数のシード値を固定できず、結果のばらつきにつながるため、EasyOCRなどを活用して手元でテキスト化するほうが安定する可能性があります。また、コンペではAPIの使用回数に5回という制限があるため、極力APIを使わずに処理できる方が望ましいです。
3. コンペを通じて得た学び
3.1. RAGの実用性に関する気づき
今回のコンペを通じて、APIの使用回数や使用できる技術に制限のある環境でのRAG設計の難しさを実感しました。もし制限がなければ、元の文書から複数の要約を作成し、それらを使った回答を統合することで精度を向上させる方法も考えられます。また、gpt-4o-miniよりも高性能なgpt-4oやo1といったモデルを活用できるため、より高精度な回答生成が可能だったかもしれません。
プロンプトエンジニアリングや多段階での推論については、まだ改善の余地があると感じました。具体的には、最初に引用部分を含む長い回答を作成し、2段階目で適切な長さに要約するアプローチを試しました。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
temperature = 0.7,
model = model,
openai_api_key = openai_api_key
)
answer1 = llm.predict("テキストの内容をもとに以下の質問に答えてください。また、根拠になる部分も抜き出してください。\n質問:"+ question + "\nテキスト:" + text)
answer2 = llm.predict("回答を50字以内でまとめて出力してください。\n質問:" + question + "\n回答:"+answer1)
# 以下のようにした方がスコアが安定する
# answer = llm.predict("以下の質問に50字以内で簡潔に答えてください。 質問:"+ question + text)
しかし、この方法ではかえってスコアが下がってしまいました。多段階の推論を効果的に活用するためのポイントを把握していなかったことが原因と考えられます。今後は、プロンプトエンジニアリングや多段階推論に関する技術を深く学び、より精度の高いRAGシステムの構築を目指したいと思います。
3.2. 次回以降に活かしたいこと
今回の経験を踏まえ、今後さらに技術を向上させるために、以下の点を強化していきます。
- OCR技術の活用 → Vision APIの不安定さを克服するため
- NLP全般の技術向上 → 情報検索、文書分類や、プロンプトエンジニアリングのようなLLMに特化した技術の強化
4. まとめ
今回のコンペでは、検索手法を使わず全文入力するというアプローチを採用し、良い結果を得ることができました。RAGの実用性に関する新たな知見を得るとともに、OCR技術を活用し画像形式のファイルにも対応することやAPIだけに頼らない効率的な自然言語処理のさらなる向上が今後の課題であると感じました。
今後も、LLMやRAG技術を活かした取り組みを続け、さらなる成長を目指していきたいと思います。