- 技術 💻
RAGの精度改善の方法一覧|原因の切り分けと評価指標の計算
RAGの精度改善とは、質問に合う文書を探す「検索」と、探した文書から答えを書く「生成」のどちらで外れているかを数字で突き止め、その場所に合った手法を1つずつ当てていく作業です。 チャンク分割、ハイブリッド検索、リランキング、クエリ書き換えなど手法はたくさんありますが、外れている場所と違うところに手を入れても数字は動きません。
この記事は、RAGを一度作り終えた人が「次にどこを直すか」を決めるための手順書です。評価セットのCSVテンプレート、Recall@k・MRR・nDCG を計算する Python コード(実行結果つき)、症状から打ち手を引く切り分け表、4つの代表的な手法の比較、RRF で検索結果を混ぜるコードを順に載せます。
確認環境:Linux、Python 3.11.15(標準ライブラリのみ)。確認日は 2026-10-06 です。記事中の評価データと数値は、計算の流れを示すための説明用の小さな例です。
この記事の結論
- 手法を足す前に、検索が外しているのか、生成が外しているのかを先に分ける。
- 分け方は3つ。A:正解の文書が候補に無い/B:候補にあるが順位が低い/C:上位に入っているのに答えが違う。
- A は検索の入口(チャンク分割・ハイブリッド検索・クエリ書き換え)、B は並べ替え(リランキング・RRF)、C は生成側(プロンプト・渡す件数・文書の書き方)を直す。
- 分けるために必要なのは、質問・正解文書ID・期待する答えの3列を持つ評価セットと、Recall@k・MRR・nDCG の3つの数字。
- 1回に入れる変更は1つ。同じ評価セットで前後の数字を比べる。
RAGの精度改善 早見表(症状→原因→打ち手)
| 症状 | 見分け方 | 主な原因 | 最初に試す打ち手 |
|---|---|---|---|
| A:正解の文書が候補に無い | 多めに取った候補(例:上位20件)にも正解文書IDが無い | 質問と文書で言葉が違う/型番や固有名詞が埋め込みで拾えない/チャンクの切れ目で情報が分かれた/そもそも文書が登録されていない | 登録漏れの確認 → チャンク分割の見直し → ハイブリッド検索 → クエリ書き換え |
| B:候補にあるが順位が低い | 候補には正解文書IDがあるが、生成に渡す上位k件に入っていない | 似た文書が多く、上位が埋まる/検索の得点が「関連の強さ」を細かく区別できていない | リランキング → RRF で複数の検索を混ぜる → 渡す件数 k を増やす |
| C:上位に入っているのに答えが違う | 上位k件に正解文書IDがすべてあるのに、答えが期待と合わない | 渡した文書が長く要点が埋もれる/指示があいまい/文書に書かれていない部分をモデルが補った/複数文書の統合が必要 | プロンプトの指示を具体化 → 渡す件数を絞る → チャンクに見出しや出どころを添える |
| 質問によって結果がばらつく | 質問の種類別に集計すると、特定の種類だけ数字が低い | 種類ごとに原因が違う(固有名詞系は A、あいまいな質問は B など) | 種類の列を評価セットに足し、種類別に A〜C を数える |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「RAGの精度改善の方法一覧|原因の切り分けと評価指標の計算」
検索と生成を分けて考える理由
RAG は「検索した文書を、質問と一緒にモデルへ渡して答えを書かせる」仕組みです。答えが外れたとき、原因は大きく2か所に分かれます。
| 段階 | やっていること | ここで外れると |
|---|---|---|
| 検索 | 質問に近いチャンク(文書を小分けにした断片)を探し、上位k件を選ぶ | 正解が書かれた文書がモデルに届かない。生成側をいくら直しても答えは出ない |
| 生成 | 渡されたk件を読んで答えを書く | 正解の文書は届いているのに、読み落とす・別の文書を優先する・書かれていないことを補う |
検索で届いていない質問に対してプロンプトを直しても、数字は動きません。 逆に、届いているのに外している質問に対して検索手法を増やすと、手間と応答時間だけが増えます。最初に「どちらで外れているか」を質問ごとに判定しておくと、試す手法が自然に絞れます。
評価セットを作る(CSVテンプレート)
切り分けには、答え合わせの元になる評価セットが要ります。列は3つで足ります。
| 列 | 中身 | 書き方のこつ |
|---|---|---|
| question | 利用者が実際に書きそうな質問文 | 文書の言い回しをそのまま写さず、利用者の言葉で書く |
| relevant_doc_ids | 答えの根拠になる文書(またはチャンク)のID。複数あれば ; で区切る |
答えを書くのに必要な文書をすべて挙げる |
| expected_answer | 期待する答えの要点 | 一言一句ではなく、含まれているべき事実を短く書く |
題材は架空の「社内の備品予約システム」の規程集です。次の内容を evalset.csv という名前で保存します。
qid,question,relevant_doc_ids,expected_answer
q1,備品の予約は何日前からできますか,D03,30日前から
q2,予約を取り消せる期限はいつですか,D05,利用開始の1時間前まで
q3,プロジェクターを借りるときに必要な申請は何ですか,D07;D08,利用申請と持ち出し届の2つ
q4,返却が遅れたときの連絡先はどこですか,D11,総務グループの窓口
q5,会議室Bの定員は何人ですか,D14,8人
q6,予約の延長は何回までできますか,D05;D06,1回まで
表計算ソフトで作るときは、次のタブ区切りを貼り付けて行を足していきます。
qid question relevant_doc_ids expected_answer 種類
q1 備品の予約は何日前からできますか D03 30日前から 数値
q2 予約を取り消せる期限はいつですか D05 利用開始の1時間前まで 期限
q3 プロジェクターを借りるときに必要な申請は何ですか D07;D08 利用申請と持ち出し届の2つ 複数文書
最初は20〜30問で始め、種類(数値を聞く、手順を聞く、複数の文書をまたぐ、固有名詞を含む、など)が偏らないようにします。問い合わせの記録があれば、そこから質問文を取ると利用者の言葉に近づきます。
次に、今のRAGに各質問を投げ、検索が返した文書IDを順位どおりに記録します。生成に渡すのは上位3件でも、検索結果は多め(ここでは5件、実際には20件程度)に記録しておくのが要点です。多めに残しておくと、A と B を見分けられます。answer_ok は、生成された答えが期待する答えの要点を含むかを人が見て 1/0 を付けた列です。
qid,retrieved_doc_ids,answer_ok
q1,D03;D02;D09;D10;D01,1
q2,D04;D09;D05;D01;D12,1
q3,D07;D01;D02;D09;D08,0
q4,D02;D03;D09;D10;D01,0
q5,D14;D13;D02;D01;D09,0
q6,D01;D02;D09;D06;D05,1
Recall@k・MRR・nDCG を Python で計算する
検索の良し悪しを表す代表的な指標は3つです。
| 指標 | 何を測るか | 計算 | 向いている問い |
|---|---|---|---|
| Recall@k | 正解文書のうち、上位k件に入った割合 | 上位k件に入った正解の数 ÷ 正解の総数 | 必要な文書が生成に届いているか |
| MRR | 最初の正解が何位に出たか | 最初の正解の順位の逆数(RR)を全質問で平均 | 1件あれば答えられる質問で、上のほうに出ているか |
| nDCG@k | 正解が上位にあるほど高く評価する並びの良さ | 順位で割り引いた得点(DCG)÷ 理想の並びの得点 | 複数の正解文書が、上から順に並んでいるか |
nDCG の「割り引き」は、順位 r の文書の得点を log2(r+1) で割ることです。1位は 1、2位は約0.631、3位は 0.5 になります。
次のコードは、先ほどの2つのCSVを読み、3つの指標と A〜C の診断を質問ごとに出します。rag_eval.py として保存し、CSVと同じフォルダで実行します。
import csv
import math
K = 3 # 生成に渡す件数
def load(path):
with open(path, encoding="utf-8", newline="") as f:
return list(csv.DictReader(f))
def recall_at_k(retrieved, relevant, k):
"""正解文書のうち、上位k件に入った割合"""
hit = len(set(retrieved[:k]) & set(relevant))
return hit / len(relevant)
def reciprocal_rank(retrieved, relevant):
"""最初に出てきた正解文書の順位の逆数(出てこなければ0)"""
for rank, doc in enumerate(retrieved, start=1):
if doc in relevant:
return 1 / rank
return 0.0
def ndcg_at_k(retrieved, relevant, k):
"""順位が下がるほど割り引いた得点を、理想の並びの得点で割る"""
dcg = sum(1 / math.log2(rank + 1)
for rank, doc in enumerate(retrieved[:k], start=1)
if doc in relevant)
ideal = sum(1 / math.log2(rank + 1)
for rank in range(1, min(len(relevant), k) + 1))
return dcg / ideal
def diagnose(retrieved, relevant, answer_ok, k):
"""症状を A〜C と OK に分ける"""
in_all = set(retrieved) & set(relevant)
in_topk = set(retrieved[:k]) & set(relevant)
if not in_all:
return "A 候補に無い"
if len(in_topk) < len(relevant):
return "B 順位が低い"
if not answer_ok:
return "C 生成が外す"
return "OK"
evalset = {r["qid"]: r for r in load("evalset.csv")}
rows = []
for r in load("results.csv"):
relevant = evalset[r["qid"]]["relevant_doc_ids"].split(";")
retrieved = r["retrieved_doc_ids"].split(";")
rows.append((
r["qid"],
recall_at_k(retrieved, relevant, K),
reciprocal_rank(retrieved, relevant),
ndcg_at_k(retrieved, relevant, K),
diagnose(retrieved, relevant, r["answer_ok"] == "1", K),
))
print(f"qid Recall@{K} RR nDCG@{K} 診断")
for qid, rec, rr, nd, diag in rows:
print(f"{qid} {rec:.3f} {rr:.3f} {nd:.3f} {diag}")
n = len(rows)
print("-" * 44)
print(f"平均 {sum(r[1] for r in rows)/n:.3f} "
f"{sum(r[2] for r in rows)/n:.3f} {sum(r[3] for r in rows)/n:.3f}"
f" (RR の平均が MRR)")
count = {}
for r in rows:
count[r[4]] = count.get(r[4], 0) + 1
print()
for key in sorted(count):
print(f"{key}: {count[key]}問")
実行結果です。
$ python3 rag_eval.py
qid Recall@3 RR nDCG@3 診断
q1 1.000 1.000 1.000 OK
q2 1.000 0.333 0.500 OK
q3 0.500 1.000 0.613 B 順位が低い
q4 0.000 0.000 0.000 A 候補に無い
q5 1.000 1.000 1.000 C 生成が外す
q6 0.000 0.250 0.000 B 順位が低い
--------------------------------------------
平均 0.583 0.597 0.519 (RR の平均が MRR)
A 候補に無い: 1問
B 順位が低い: 2問
C 生成が外す: 1問
OK: 2問
結果を手で検算する
数字が合っているかを、2問だけ手で確かめます。
| 質問 | 状況 | 計算 | 結果 |
|---|---|---|---|
| q2 | 正解 D05 が3位 | RR = 1/3。nDCG@3 = (1/log2(4)) ÷ (1/log2(2)) = 0.5 ÷ 1 | RR 0.333、nDCG 0.500 |
| q3 | 正解 D07 が1位、D08 が5位(上位3件の外) | Recall@3 = 1/2。DCG = 1、理想 = 1 + 1/log2(3) = 1.631。nDCG = 1 ÷ 1.631 | Recall 0.500、nDCG 0.613 |
どちらもコードの出力と一致します。
数字の読み方
- q2 は Recall@3 が 1.000 でも nDCG は 0.500 です。届いてはいるが3位、という「ぎりぎり」が nDCG と RR に表れます。k を 2 に減らすと外れる質問だと分かります。
- q3 は RR が 1.000 でも Recall@3 は 0.500 です。MRR は最初の1件しか見ないため、正解文書が2つ必要な質問の取りこぼしは Recall で見ます。
- q5 は検索の3指標がすべて 1.000 なのに答えが合っていません。 検索の数字だけ見ていると見落とす質問で、これが C(生成が外す)です。
- q6 は Recall@3 が 0 ですが RR は 0.250 です。候補の4位・5位には正解があるので、A ではなく B(順位が低い)と判定されます。
6問の内訳は、A が1問、B が2問、C が1問、OK が2問でした。この例なら、最初に手を付けるのは件数の多い B、つまり並べ替えの改善です。
症状別の打ち手
A:正解の文書が候補に無いとき
検索の入口で取りこぼしています。次の順に確かめます。
| 確かめること | 方法 | 当てはまったら |
|---|---|---|
| 文書が登録されているか | 正解文書IDで索引を直接引く | 取り込み処理を直す(手法の追加は不要) |
| チャンクの切れ目で情報が分かれていないか | 正解文書のチャンクを目で読む | チャンクの大きさと重なり(オーバーラップ)を見直す |
| 質問と文書で言葉が違わないか | 質問の語が文書に出てくるかを見る | クエリ書き換え、または文書側に言い換えを添える |
| 型番・固有名詞・略語を含む質問か | 種類別に集計する | キーワード検索を足す(ハイブリッド検索) |
チャンクの切れ目の影響は、小さな例で確かめられます。次のコードは、74文字の規程文を大きさと重なりを変えて切り、「取り消し」と「1時間前まで」が同じチャンクに入るかを調べます。
def split(text, size, overlap):
"""size 文字ずつ、overlap 文字を重ねて切る"""
step = size - overlap
return [text[i:i + size] for i in range(0, len(text), step) if text[i:i + size]]
text = ("備品の予約は利用日の30日前から受け付けます。"
"予約の取り消しは利用開始の1時間前までできます。"
"延長は1回まで、次の予約が入っていない場合に限ります。")
print("全体:", len(text), "文字")
for size, overlap in [(30, 0), (30, 10), (60, 10)]:
chunks = split(text, size, overlap)
print(f"\nsize={size} overlap={overlap} -> {len(chunks)}個")
for c in chunks:
mark = "○" if "1時間前まで" in c and "取り消し" in c else " "
print(f" {mark} {c}")
$ python3 chunk.py
全体: 74 文字
size=30 overlap=0 -> 3個
備品の予約は利用日の30日前から受け付けます。予約の取り消し
は利用開始の1時間前までできます。延長は1回まで、次の予約が
入っていない場合に限ります。
size=30 overlap=10 -> 4個
備品の予約は利用日の30日前から受け付けます。予約の取り消し
○ ます。予約の取り消しは利用開始の1時間前までできます。延長は
までできます。延長は1回まで、次の予約が入っていない場合に限
入っていない場合に限ります。
size=60 overlap=10 -> 2個
○ 備品の予約は利用日の30日前から受け付けます。予約の取り消しは利用開始の1時間前までできます。延長は1回まで、次の予約が
1回まで、次の予約が入っていない場合に限ります。
重なり無しの30文字では、「予約の取り消し」と「1時間前まで」が別のチャンクに分かれました。この状態で「取り消しの期限は」と聞くと、どちらのチャンクも答えを単独では持っていません。重なりを10文字にするか、大きさを60文字にすると、1つのチャンクに収まります(○ の行)。文字数で機械的に切るより、見出しや段落、句点など意味の切れ目で切るほうが、この種の取りこぼしは減ります。
B:候補にあるが順位が低いとき
検索は正解を見つけていますが、生成に渡す上位k件に入っていません。打ち手は「並べ替え」です。
| 打ち手 | やること | 向く場面 |
|---|---|---|
| リランキング | 多めに取った候補(例:20〜50件)を、質問と文書を一緒に読んで関連度を付け直すモデルで並べ替え、上位k件を選ぶ | 似た文書が多く、上位が似たもので埋まる |
| RRF(複数の検索を混ぜる) | キーワード検索とベクトル検索など、複数の順位表を1本にまとめる | 片方の検索では上位、もう片方では下位、という質問が混ざる |
| k を増やす | 生成に渡す件数を増やす | 手間をかけずにまず確かめたいとき(C が増えないかを併せて見る) |
k を増やすのは一番手軽ですが、渡す文書が増えると生成側が要点を読み落とす質問が出てくることがあります。k を変えたら、Recall@k と一緒に answer_ok の合計も見比べます。
C:上位に入っているのに答えが違うとき
検索は役目を果たしています。直すのは生成側です。
| 状況 | 打ち手 |
|---|---|
| 渡した文書に答えがあるのに、別の文書の内容で答えた | 渡す件数を絞る。関連度の高い順に並べる。各チャンクに文書名・見出しを添える |
| 文書に無いことを補って答えた | 「渡した文書に書かれていることだけで答える。書かれていなければ、分からないと答える」と指示する |
| 数値や期限を読み違えた | 根拠にした文を引用してから答えを書かせる |
| 複数の文書をまとめる必要があった | 「必要な文書をすべて挙げてから答える」と手順を指示する |
| 答えは合っているが形式が期待と違う | 出力形式(箇条書き、文字数、単位)を指定する。評価の基準側も見直す |
C の件数は、生成の品質を測る指標(渡した文書に沿っているか、質問に答えているか)で自動採点する方法もあります。件数が少ないうちは、人が answer_ok を付けるほうが早く、判定の理由も残せます。
4つの手法の比較早見表
代表的な4つの手法を、「効く症状・追加の手間・遅くなる度合い」で並べます。遅くなる度合いは、1回の質問で増える処理の数から見た目安です。
| 手法 | 効く症状 | 仕組み | 追加の手間 | 遅くなる度合い |
|---|---|---|---|---|
| チャンク分割の見直し | A(切れ目で情報が分かれる)、C(チャンクが長く要点が埋もれる) | 大きさ・重なり・切る位置を変えて索引を作り直す | 中:全文書の再分割と埋め込みの作り直しが要る | ほぼ変わらない(質問時の処理は同じ) |
| ハイブリッド検索 | A(型番・固有名詞・略語が拾えない)、B | キーワード検索とベクトル検索を両方行い、結果を混ぜる | 中:キーワード検索の索引をもう1つ持つ | 小:検索が1回増える(並行して実行できる) |
| リランキング | B(候補にあるが順位が低い) | 多めに取った候補を、関連度を付け直すモデルで並べ替える | 小〜中:並べ替え用のモデルを1つ呼ぶ処理を足す | 中:候補の件数に比例して増える |
| クエリ書き換え | A(質問と文書で言葉が違う、質問が短い・あいまい) | 検索の前に、モデルで質問を言い換える・複数の質問に分ける | 小:検索の前に処理を1つ足す | 中〜大:検索の前にモデルの呼び出しが1回入る |
どれから試すかの判断表
| 条件 | 選ぶもの |
|---|---|
| A が多く、正解文書のチャンクを読むと答えが途中で切れている | チャンク分割の見直し |
| A が多く、外れる質問に型番・固有名詞・略語が多い | ハイブリッド検索 |
| A が多く、質問が短い、または文書と言葉づかいが違う | クエリ書き換え |
| B が多い | リランキング(すでに複数の検索があるなら RRF) |
| C が多い | 検索手法は足さず、プロンプトと渡す件数を見直す |
| A・B・C が同じくらい | 件数の多い質問の種類を1つ選び、その種類の中で多い症状から直す |
RRF で検索結果を混ぜるコード
ハイブリッド検索では、キーワード検索の得点とベクトル検索の類似度という、単位の違う2つの数字を混ぜる必要があります。RRF(Reciprocal Rank Fusion)は、得点ではなく順位だけを使う混ぜ方です。各検索での順位を r として 1 ÷ (k + r) を足し合わせます。k は定数で、60 がよく使われます。
def rrf(rankings, k=60):
"""複数の検索結果(文書IDの順位つきリスト)を RRF で1本にまとめる"""
scores = {}
for ranking in rankings:
for rank, doc in enumerate(ranking, start=1):
scores[doc] = scores.get(doc, 0.0) + 1 / (k + rank)
return sorted(scores.items(), key=lambda x: (-x[1], x[0]))
keyword = ["D05", "D01", "D06", "D09", "D02"] # キーワード検索(BM25 など)の順位
vector = ["D02", "D06", "D05", "D12", "D01"] # ベクトル検索の順位
for rank, (doc, score) in enumerate(rrf([keyword, vector]), start=1):
print(f"{rank}位 {doc} {score:.5f}")
$ python3 rrf.py
1位 D05 0.03227
2位 D06 0.03200
3位 D02 0.03178
4位 D01 0.03151
5位 D09 0.01562
6位 D12 0.01562
D05 はキーワード検索で1位、ベクトル検索で3位なので、1/61 + 1/63 = 0.03227 です。D06 は両方で2〜3位に入り、どちらの検索でも1位ではないのに全体の2位になりました。片方だけで上位の文書より、両方でそこそこ上位の文書が上がるのが RRF の性質です。片方にしか出てこない D09 と D12 は、同じ4位どうしで同点になります。同点の並びを固定したいときは、コードのように文書IDを第2の並べ替えキーに入れておくと、実行のたびに順位が入れ替わりません。
改善を1周まわす手順
- 評価セット(質問・正解文書ID・期待する答え)を20〜30問作る。
- 今のRAGで検索結果(多めの件数)と答えを記録し、
answer_okを付ける。 - Recall@k・MRR・nDCG と A〜C の件数を出す。これが基準の数字になる。
- 件数の多い症状に合う手法を1つだけ入れる。
- 同じ評価セットでもう一度計算し、基準と比べる。A〜C の件数がどう動いたかを見る。
- 良くなったら採用し、基準を更新する。変わらなければ元に戻し、次の候補へ進む。
記録は次の形で残すと、あとから見返せます。
日付 変更内容 k Recall@k MRR nDCG@k A B C OK 採否
2026-10-06 基準(変更なし) 3 0.583 0.597 0.519 1 2 1 2 -
似た取り組みとの違いと選び方
RAGの答えを良くする方法は、検索の改善だけではありません。混同されやすいものとの違いを整理します。
| 取り組み | 変えるもの | 効くもの | 手間 |
|---|---|---|---|
| 検索の改善(この記事の A・B) | 索引、検索方法、並べ替え | 必要な文書がモデルに届くかどうか | 小〜中 |
| プロンプトの改善(この記事の C) | モデルへの指示、文書の渡し方 | 届いた文書の読み方、答えの形式 | 小 |
| 文書そのものの整備 | 元の文書の書き方、重複や版の整理 | 文書が曖昧・矛盾している質問 | 中〜大 |
| ファインチューニング | モデルの重み | 文体・出力形式・専門的な言い回しの定着 | 大 |
| 条件 | 選ぶもの |
|---|---|
| 正解文書が上位k件に入っていない | 検索の改善 |
| 入っているのに答えが違う | プロンプトの改善 |
| 正解文書を人が読んでも答えが決まらない | 文書そのものの整備 |
| 内容は合っているが、文体や形式が毎回そろわない | プロンプトでの形式指定、それでも足りなければファインチューニング |
RAGを最初に組み立てる手順や、生成された文章の品質を採点する方法は、それぞれ別の主題です。この記事は「作った後に、どこが外れているかを調べる方法」に絞っています。
確かめて分かったこと
今回の計算とコードの実行で確かめられた点です。
- Recall@k が 1.000 でも並びは区別できない。 q1 と q2 はどちらも Recall@3 が 1.000 ですが、nDCG@3 は 1.000 と 0.500 でした。k を減らす判断には nDCG か RR が要ります。
- MRR は複数の正解文書の取りこぼしを映さない。 q3 は RR が 1.000 のまま、必要な2文書のうち1つが上位3件の外でした。
- 検索の指標がすべて満点でも答えが外れる質問がある。 q5 がその例で、
answer_okの列が無いと C を数えられません。 - 検索結果を上位k件しか記録しないと、A と B を分けられない。 q6 は上位3件だけ見ると「正解なし」ですが、5件まで見ると4位・5位に正解があり、打ち手がハイブリッド検索ではなくリランキングに変わります。
- 重なり無しの固定長分割では、74文字の短い文でも問いと答えが別のチャンクに分かれた。 重なりを付けるか、文の切れ目で切ると1つに収まりました。
- RRF では、どちらの検索でも1位でない文書が全体の2位になった。 片方にしか出ない文書どうしは同点になるため、並びを固定する第2キーを入れておくと結果が安定します。
よくある質問
Q. RAGの精度が上がらないときは、何から調べればよいですか? A. 正解の文書が検索結果の上位k件に入っているかを、質問ごとに調べます。入っていなければ検索、入っているのに答えが違えば生成を直します。調べるには、質問と正解文書IDを対にした評価セットが必要です。
Q. Recall@k と MRR と nDCG の違いは何ですか? A. Recall@k は「必要な文書が上位k件に届いた割合」、MRR は「最初の正解が何位か」、nDCG は「正解が上から順に並んでいるか」を測ります。届いているかは Recall、並びの良さは MRR と nDCG で見ます。
Q. 評価セットは何問あれば足りますか? A. 最初は20〜30問で始められます。質問の種類が偏らないことのほうが、問題数より効きます。改善を重ねて数字の差が小さくなってきたら、種類ごとに問題を足していきます。
Q. ハイブリッド検索とリランキングの違いは何ですか? A. ハイブリッド検索は「候補を広く集める」手法、リランキングは「集めた候補を並べ替える」手法です。候補に正解が無い(A)ならハイブリッド検索、候補にあるが順位が低い(B)ならリランキングが合います。両方を続けて使うこともできます。
Q. k(生成に渡す件数)は大きいほうがよいですか? A. 大きくすると Recall@k は上がりますが、渡す文書が増えて答えが外れる質問が出ることがあります。k を変えたら、Recall@k と答えの正否の両方を見て決めます。
まとめ
- 手法を足す前に、検索が外しているのか、生成が外しているのかを分ける。
- 症状は A:候補に無い/B:順位が低い/C:上位にあるのに答えが違う の3つ。質問ごとに判定して件数を数える。
- 評価セットは 質問・正解文書ID・期待する答え の3列。検索結果は上位k件より多めに記録する。
- Recall@k は届いたか、MRR は最初の正解の順位、nDCG は並びの良さ。3つを並べて見ると、1つでは見えない取りこぼしが分かる。
- A にはチャンク分割・ハイブリッド検索・クエリ書き換え、B にはリランキングと RRF、C にはプロンプトと渡す件数の見直しが合う。
- 変更は1回に1つ。同じ評価セットで前後の数字と A〜C の件数を比べて採否を決める。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「RAGの精度改善の方法一覧|原因の切り分けと評価指標の計算」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。