HtmlRAG: HTML is Better Than Plain Text for Modeling Retrieved Knowledge in RAG Systemsの紹介をします。
https://arxiv.org/abs/2411.02959
HtmlRAGの解説と実装例
1. 論文の概要
背景
Retrieval-Augmented Generation (RAG)は、外部の知識を利用して質問応答や文章生成の精度を向上させる方法論です。しかし、RAGシステムではHTML文書をプレーンテキストに変換して使用することが一般的で、HTML特有の構造や意味的情報(例: 見出し、表、リンク)が失われるという課題があります。
HtmlRAGの提案
HtmlRAGは、HTMLをそのままRAGシステムで利用する新しいアプローチを提案します。この方法では以下を実現します:
- HTMLクリーニング: 不要な情報(CSSやJavaScript)を削除し、必要な情報を簡潔化。
- HTMLプルーニング: クエリに関連の低いHTMLブロックを削除し、関連性の高い情報のみを保持。
- 生成性能の向上: HTMLの構造情報を保持することで、質問応答や文章生成の精度を向上。
2. 具体的な実装例
以下は、「映画レビューサイトを利用した質問応答システム」を例に、HtmlRAGの実装を説明します。
(1) HTMLクリーニング
HTML文書から不要な要素を削除して軽量化します。
from bs4 import BeautifulSoup
def clean_html(html_content):
soup = BeautifulSoup(html_content, 'html.parser')
for script_or_style in soup(["script", "style", "meta", "link"]):
script_or_style.decompose()
for tag in soup.find_all():
if not tag.text.strip():
tag.decompose()
else:
tag.attrs = {}
return str(soup)
評価:
- 元のHTML: 80,000トークン
- クリーニング後: 約4,800トークン
構造情報を保持しながらもトークン数を大幅に削減。
(2) ブロックツリーの構築
HTMLを「意味のあるブロック」に分割します。
def build_block_tree(html_content, max_words=50):
soup = BeautifulSoup(html_content, 'html.parser')
blocks = []
def traverse(tag):
text = tag.get_text(separator=" ", strip=True)
if len(text.split()) > max_words:
for child in tag.children:
if child.name:
traverse(child)
else:
blocks.append((tag.name, text.strip()))
traverse(soup.body)
return blocks
評価:
- 入力: クリーンなHTML
- 出力例:
[('h1', '映画レビュー'), ('p', 'アクション映画のレビュー')]
分割後、意味のあるブロック単位で整理され、検索効率が向上。
(3) HTMLプルーニング
質問に関連するブロックのみを残します。
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
def prune_blocks(blocks, query, max_length=300):
query_embedding = model.encode(query, convert_to_tensor=True)
pruned_blocks = []
for tag, text in blocks:
block_embedding = model.encode(text, convert_to_tensor=True)
score = util.pytorch_cos_sim(query_embedding, block_embedding).item()
if score > 0.5:
pruned_blocks.append((tag, text))
total_length = 0
result = []
for tag, text in pruned_blocks:
if total_length + len(text) <= max_length:
result.append((tag, text))
total_length += len(text)
else:
break
return result
評価:
- 入力:
[('h1', '映画レビュー'), ('p', 'アクション映画のレビュー'), ...] - 質問: 「アクション映画」
- 出力:
[('p', 'アクション映画のレビュー')]
関連性の高いブロックのみを保持。
(4) 生成モデルによる回答
LLMを使用して回答を生成します。
from transformers import pipeline
def generate_answer(pruned_blocks, query):
context = " ".join([text for _, text in pruned_blocks])
llm = pipeline("text2text-generation", model="t5-small")
prompt = f"質問: {query}\n\n文脈: {context}\n\n答え:"
return llm(prompt, max_length=50)[0]['generated_text']
評価:
- 質問: 「アクション映画で最も評価の高い作品は?」
- 回答: 「アクション映画のレビューによると『XXX』が高評価です。」
HTMLの構造情報を保持した結果、高精度の回答が得られます。
3. 論文手法の評価
(1) 論文内実験の要約
- 6つのQAデータセット(例: HotpotQA, ASQA)を用いて評価。
- HtmlRAGは、プレーンテキストやMarkdown形式を利用する従来手法を超える性能を示しました。
- 特に、複数の情報源からのマルチホップ質問や、長文形式の質問に強みを持ちます。
(2) 独自実装の評価
- メリット:
- トークン数の削減(80,000→4,800→300)。
- 高関連性の情報のみ保持。
- 課題:
- テキスト埋め込みや生成モデルの計算コストが増加。
- 複雑なHTML構造への対応が必要。
4. 独自の考察とまとめ
考察
- HTMLの潜在能力:
HTMLにはプレーンテキストよりも多くの構造情報が含まれており、それを活用するHtmlRAGは理にかなっています。 - 実装の柔軟性:
本手法は、多様なデータソース(例: PDFやWeb記事)に適用可能。HTML形式への変換を前提に汎用性が高い。 - 計算コスト:
HTMLを保持する利点がある一方で、クリーニングやプルーニングの計算コストを抑える工夫が必要です。
まとめ
HtmlRAGは、RAGシステムにおける革新的な提案であり、特にWebベースの情報検索に強みを持つ手法です。実装例を通じて、その実用性と効率性が確認できました。今後、クリーニングやプルーニングのアルゴリズムを改良し、計算効率を向上させる研究がさらに進むことが期待されます。