Day 155 — NLP系コンペへの応用⑥ — クラスタ内Uncertainty Samplingの優位性は、Iterative Pseudo Labelingでも持続するのか

2026-09-14 金 / Phase 6(Grandmaster、総復習後の発展回) コーディング→分析 Iterative Pseudo Labeling / gapが2倍に拡大 / seed3で逆転を確認

📚 背景知識(読んでから問題へ)

🔁
Day154では、クラスタ内選択基準を「確信度優先(D)」から「クラスタ内Uncertainty Sampling(G、確信度が0.5に近い順)」に変えると、1回だけの疑似ラベル選択でtest精度が明確に改善する(D: acc 0.6327 → G: acc 0.6800、+0.0473)ことを発見した。ただし(G)の疑似ラベル精度(pseudo_acc)は0.5906とほぼコイントス水準で、全手法中最低だった。

今日はDay137で扱ったIterative Pseudo Labeling(学習→予測→採用→再学習を複数ラウンド繰り返す枠組み)にDay154の(D)・(G)を拡張し、「pseudo_accが低い(G)は反復するほど誤ったラベルが蓄積し、(D)に追いつかれる・逆転されるのではないか」という懸念を検証する。

🧪
検証前の仮説: (G)はround-localの疑似ラベル精度が低いため、ラウンドを重ねるごとに誤ったラベルが蓄積し、(D)とのgapはラウンドを重ねるほど縮小するか、途中で逆転するだろう。
用語意味
D_iter(D)クラスタ内確信度優先の選択基準を使ったIterative Pseudo Labeling
G_iter(G)クラスタ内Uncertainty Samplingの選択基準を使ったIterative Pseudo Labeling
round-localな疑似ラベル精度そのラウンドで新たに採用したサンプルだけの正解率(Day137と同じ定義)
own-final-round比較D/Gで採用件数がモデル依存で異なり、プールを使い切るタイミングも異なるため、固定round番号ではなく各手法が実際に到達した最終ラウンドどうしを比較する方式

🗂️ データ・分割のスキーマ

変数名型内容
df (n=450)pd.DataFrameDay151-154と同じmake_text_dataset()で生成した合成テキスト全体
test_df (110件)pd.DataFrame最終評価専用のホールドアウト。全ラウンド・全設定で共通に使う
labeled_df (40件)pd.DataFrame最初の学習データ。ラウンドを追うごとに疑似ラベル付きサンプルが追記される
remaining_df (約300件→減少)pd.DataFrameまだ採用していない未ラベルプール。ラウンドごとに採用分が除外され縮小する
threshold   = 0.60  # 各ラウンドのn_addedを決める基準(Day151-154と同一)
n_clusters  = 8     # KMeansのクラスタ数(D_iter/G_iter共通)
n_rounds    = 5     # 最大ラウンド数(採用0件になれば早期終了、Day137の枠組み)

🎯 問題

タスクA(理論)

以下2点をそれぞれ2〜3文で説明する。

1
Day154の(G)は「pseudo_accが史上最低なのにtest精度は最高」だった。これをIterative Pseudo Labelingに拡張したとき、「Gの優位性が拡大する」「変わらない」「縮小・逆転する」の3シナリオのうち、Day154の知見から素朴に予想されるものはどれかを理由とともに述べる
2
(D)確信度優先の方式をIterative Pseudo Labelingで繰り返すと、round-local疑似ラベル精度はラウンドを重ねるごとにどう推移すると予想されるか(増加/横ばい/低下)を、Day137のconfirmation biasの議論と関連づけて予想する

タスクB(コーディング→分析)

B1
make_text_dataset・split_seed・train_and_eval・select_confident_pseudo_labels・_cluster_and_quota・select_diverse_prefilter(D)・select_diverse_prefilter_uncertainty(G)はDay151-154と同じ実装を再利用する
B2
run_iterative(seed, select_fn, threshold=0.60, n_clusters=8, n_rounds=5)を実装。ラウンドごとに現在の累積学習データで再学習→残りプールでn_addedを決定→select_fn(D or G)で実際に選ぶ→採用分をプールから除外し学習データに追加、を採用0件かn_rounds到達まで繰り返す
B3
round-localな疑似ラベル精度・累積疑似ラベル精度・拡張後の学習データで再学習した際のtest精度をラウンドごとに記録する
B4
D_iter・G_iterそれぞれ5シードで実行し、ラウンド別平均を比較する。注意: 採用件数がモデル依存で変わるため、プールを使い切るタイミングがD/Gで異なりうる。固定round番号だけでなく各手法のown-final-roundどうしも比較する
B5
(a) own-final-roundでG_iterの平均test精度がD_iterを上回ること/(b) D_iter・G_iterともにown-final-roundの平均test精度がround1を下回ること(confirmation biasの確認)をassert文で確認する
B6
追加診断: round1のgapとown-final-roundのgapを比較(拡大/縮小/逆転)。D/Gそれぞれのround-local疑似ラベル精度の推移を比較。シードごとにround1とown-final-roundで優劣が逆転したケースがあるか確認する
B7
(考察): タスクAの2つの予想が実測とどの程度一致したかを、round-local疑似ラベル精度の推移・gapの変化・reversal件数と関連づけて述べる

💡 ヒント

ヒント1方向性

タスクAの1.は、「pseudo_accが低い」ことと「confirmation biasが起きやすい」ことを短絡的に結びつけないよう注意する。Day154の(G)がpseudo_acc最低でもtest精度最高だったのは「決定境界付近のサンプルは情報量が多い」ためであり、この性質はラウンドを重ねても変わらない可能性がある。一方(D)は「モデルが既に得意な、決定境界から遠いサンプル」ばかりを選び続けるため、ラウンドを重ねるにつれ学習データの多様性が乏しくなり、後半のラウンドでモデルが誤った方向に過信するリスクの方が高いかもしれない。

ヒント2アプローチ
  • run_iterativeはDay137のループ構造(remaining_idxを縮める)を、remaining_dfをDataFrameのまま.loc[~mask_sel]で縮める形に置き換えればよい
  • round-local疑似ラベル精度は、その回のmask_sel・pseudo_selとremaining_df.loc[mask_sel, "label"](検証専用)を比較する
  • 累積疑似ラベル精度は、これまでの(true, pseudo)ペアをリストに貯めてnp.concatenateしてから比較する(Day137と同じパターン)
  • own-final-round比較は各(seed, method)の結果DataFrameの.iloc[-1]を使う
  • reversal判定は(gap_round1 > 0) != (gap_final > 0)で真偽の不一致を見ればよい
ヒント3コード骨格
def run_iterative(seed, select_fn, threshold=0.60, n_clusters=8, n_rounds=5):
    labeled_df, unlabeled_df, test_df = split_seed(seed)
    cur_texts = labeled_df["text"].tolist()
    cur_labels = labeled_df["label"].tolist()
    remaining_df = unlabeled_df.copy().reset_index(drop=True)

    rows = []
    cum_true, cum_pred = [], []

    for r in range(1, n_rounds + 1):
        acc, _, vectorizer, model = train_and_eval(cur_texts, cur_labels, test_df)
        if len(remaining_df) == 0:
            break

        mask_conf, _ = select_confident_pseudo_labels(model, vectorizer, remaining_df, threshold)
        n_added = int(mask_conf.sum())
        if n_added == 0:
            break

        mask_sel, pseudo_sel = select_fn(model, vectorizer, remaining_df, n_added, n_clusters, seed)
        true_sel = remaining_df.loc[mask_sel, "label"].to_numpy()
        round_pseudo_acc = float((pseudo_sel == true_sel).mean())

        cum_true.append(true_sel); cum_pred.append(pseudo_sel)
        cum_pseudo_acc = float((np.concatenate(cum_pred) == np.concatenate(cum_true)).mean())

        cur_texts = cur_texts + remaining_df.loc[mask_sel, "text"].tolist()
        cur_labels = cur_labels + pseudo_sel.tolist()
        remaining_df = remaining_df.loc[~mask_sel].reset_index(drop=True)  # ★採用済みをプールから除外

        acc_after, _, _, _ = train_and_eval(cur_texts, cur_labels, test_df)
        rows.append({"round": r, "n_added": int(mask_sel.sum()), "pool_left": len(remaining_df),
                     "round_pseudo_acc": round_pseudo_acc, "cum_pseudo_acc": cum_pseudo_acc,
                     "test_acc": acc_after})
    return pd.DataFrame(rows)

✅ 模範解答

# POSITIVE_WORDS / NEGATIVE_WORDS / NEUTRAL_WORDS / make_text_dataset / train_and_eval /
# split_seed / select_confident_pseudo_labels / _cluster_and_quota /
# select_diverse_prefilter(D) / select_diverse_prefilter_uncertainty(G) は
# Day151-154と同一(省略)

def run_iterative(seed, select_fn, threshold=0.60, n_clusters=8, n_rounds=5):
    """select_fn: (model, vectorizer, remaining_df, n_target, n_clusters, seed) -> (mask, pseudo_labels)"""
    labeled_df, unlabeled_df, test_df = split_seed(seed)
    cur_texts = labeled_df["text"].tolist()
    cur_labels = labeled_df["label"].tolist()
    remaining_df = unlabeled_df.copy().reset_index(drop=True)

    rows = []
    cum_true, cum_pred = [], []

    for r in range(1, n_rounds + 1):
        acc, _, vectorizer, model = train_and_eval(cur_texts, cur_labels, test_df)
        if len(remaining_df) == 0:
            break

        mask_conf, _ = select_confident_pseudo_labels(model, vectorizer, remaining_df, threshold)
        n_added = int(mask_conf.sum())
        if n_added == 0:
            break

        mask_sel, pseudo_sel = select_fn(model, vectorizer, remaining_df, n_added, n_clusters, seed)
        true_sel = remaining_df.loc[mask_sel, "label"].to_numpy()
        round_pseudo_acc = float((pseudo_sel == true_sel).mean())

        cum_true.append(true_sel); cum_pred.append(pseudo_sel)
        cum_pseudo_acc = float((np.concatenate(cum_pred) == np.concatenate(cum_true)).mean())

        cur_texts = cur_texts + remaining_df.loc[mask_sel, "text"].tolist()
        cur_labels = cur_labels + pseudo_sel.tolist()
        remaining_df = remaining_df.loc[~mask_sel].reset_index(drop=True)

        acc_after, _, _, _ = train_and_eval(cur_texts, cur_labels, test_df)
        rows.append({"round": r, "n_added": int(mask_sel.sum()), "pool_left": len(remaining_df),
                     "round_pseudo_acc": round_pseudo_acc, "cum_pseudo_acc": cum_pseudo_acc,
                     "test_acc": acc_after})
    return pd.DataFrame(rows)


SEEDS = [0, 1, 2, 3, 4]
N_ROUNDS = 5

all_d, all_g = [], []
for s in SEEDS:
    df_d = run_iterative(s, select_diverse_prefilter, n_rounds=N_ROUNDS); df_d["seed"] = s; all_d.append(df_d)
    df_g = run_iterative(s, select_diverse_prefilter_uncertainty, n_rounds=N_ROUNDS); df_g["seed"] = s; all_g.append(df_g)

df_d_all = pd.concat(all_d, ignore_index=True)
df_g_all = pd.concat(all_g, ignore_index=True)
mean_d = df_d_all.groupby("round")[["round_pseudo_acc", "cum_pseudo_acc", "test_acc"]].mean()
mean_g = df_g_all.groupby("round")[["round_pseudo_acc", "cum_pseudo_acc", "test_acc"]].mean()

# seedごとの own-final-round 比較
rows_final = []
for s in SEEDS:
    d_s, g_s = df_d_all[df_d_all["seed"]==s], df_g_all[df_g_all["seed"]==s]
    d_r1, g_r1 = d_s[d_s["round"]==1]["test_acc"].values[0], g_s[g_s["round"]==1]["test_acc"].values[0]
    d_last, g_last = d_s.iloc[-1], g_s.iloc[-1]
    rows_final.append({"seed": s, "gap_round1": g_r1 - d_r1,
        "d_final_acc": d_last["test_acc"], "g_final_acc": g_last["test_acc"],
        "gap_final": g_last["test_acc"] - d_last["test_acc"]})
df_final = pd.DataFrame(rows_final)
df_final["reversed"] = (df_final["gap_round1"] > 0) != (df_final["gap_final"] > 0)

mean_d_r1, mean_g_r1 = mean_d.loc[1, "test_acc"], mean_g.loc[1, "test_acc"]
mean_d_final, mean_g_final = df_final["d_final_acc"].mean(), df_final["g_final_acc"].mean()

assert mean_g_final > mean_d_final, "own-final-roundでG_iterがD_iterを上回っていません"
assert mean_d_final < mean_d_r1, "D_iterがconfirmation biasで劣化していません"
assert mean_g_final < mean_g_r1, "G_iterがconfirmation biasで劣化していません"
print("[OK] own-final-roundでもG_iterの優位性は消えていない")
print("[OK] D_iter・G_iterともにconfirmation biasで劣化するが、程度が大きく異なる")
# ============ 実測結果(実際にPython 3で実行して確認済み) ============
# [ラウンド別 平均(固定round番号)]
#  round  n_added(D)  round_pseudo_acc(D)  cum_pseudo_acc(D)  test_acc(D)  |  n_added(G)  round_pseudo_acc(G)  cum_pseudo_acc(G)  test_acc(G)
#      1        90.6              0.867442           0.867442     0.632727 |      90.6               0.590581           0.590581     0.680000
#      2       140.8              0.551895           0.676180     0.572727 |     143.6               0.701816           0.657712     0.649091
#      3        54.4              0.418792           0.622854     0.549091 |      59.0               0.804038           0.690196     0.645455
#      4        11.8              0.344933           0.612377     0.547273 |       6.4               0.803175           0.692877     0.643636
#      5         4.0              0.152778           0.602222     0.527273 |       2.0               0.500000           0.656667     0.590909
#
# mean baseline_acc(40件のみ、参考) = 0.7418
# gap(G-D)推移: round1 +0.0473 → round2 +0.0764 → round3 +0.0964 → round4 +0.0964 → round5 +0.0636
#
# [seedごとの own-final-round 比較]
#  seed  gap_round1  d_final_round  d_final_acc  g_final_round  g_final_acc  gap_final  reversed
#     0      0.0636              4       0.5636              4       0.6273     0.0636     False
#     1      0.0182              4       0.5818              4       0.6091     0.0273     False
#     2      0.0909              5       0.5364              4       0.7000     0.1636     False
#     3     -0.0636              5       0.5182              5       0.5909     0.0727      True   ← D優勢→G優勢に逆転
#     4      0.1273              5       0.5273              4       0.7000     0.1727     False
#
# mean gap_round1 (G-D) = +0.0473
# mean gap_final  (G-D) = +0.1000(round1の2倍以上)
# reversed件数 = 1 / 5
#
# mean_d_round1=0.6327 -> mean_d_final=0.5455 (Δ-0.0873, 相対-13.8%)
# mean_g_round1=0.6800 -> mean_g_final=0.6455 (Δ-0.0345, 相対-5.1%)
#
# [OK] own-final-roundでもG_iterの優位性は消えていない
# [OK] D_iter・G_iterともにconfirmation biasで劣化するが、程度が大きく異なる
🎉
重要な発見1(検証前の懸念は外れた: Gの優位性は消えるどころか2倍以上に拡大した): 検証前の仮説は「(G)はpseudo_accが低いので反復するほど(D)に追いつかれる・逆転されるだろう」だった。しかし実測は正反対で、gap(G-D)はround1の+0.0473からown-final-roundで+0.1000へと2倍以上に拡大した。5シード中4シードでgapが拡大し、round1で唯一(D)が優勢だったseed3でさえ、最終ラウンドでは(G)が逆転して優勢になった(reversed=1/5)。
🔎
重要な発見2(confirmation biasの深刻さがDとGで全く違う): (D)はround1のtest精度0.6327からown-final-roundで0.5455まで-0.0873(相対-13.8%)劣化したのに対し、(G)は0.6800から0.6455まで-0.0345(相対-5.1%)の劣化にとどまった。(D)の方が(G)より約2.5倍深刻なconfirmation biasを起こしている。(D)のround-local疑似ラベル精度は0.867→0.552→0.419→0.345→0.153と単調に崩壊する一方、(G)のそれは0.591→0.702→0.804→0.803と上昇している。
⚖️
重要な発見3(Dの「確信度優先」が招く悪循環が正体): (D)は「モデルが既に自信を持てるサンプル」だけを選び続けるため、round2の採用件数が急増(90.6→140.8件)するのに正解率が急落する——モデルが誤った予測にまで高い確信度を持つようになり、その誤りを大量に正解として学習してしまう悪循環に陥っている。(G)は「モデルが最も自信のないサンプル」を狙うため、この自己強化的な過信サイクルに陥りにくい。

📊 結果の可視化

① Day154の懸念

(G)はpseudo_acc最低。反復すれば誤りが蓄積し(D)に負けるのでは?

② Iterative化

D_iter・G_iterを最大5ラウンド、5シードで実行

③ own-final-round比較

プール枯渇タイミングがD/Gで異なるため、固定roundでなく各手法の最終到達点で比較

④ 結果

gapは縮小どころか2倍に拡大。seed3では逆転も確認

test accuracy のラウンド推移(5シード平均・固定round番号ベース) 0.50 0.75 baseline 0.7418 round1 round2 round3 round4 round5 D_iter(クラスタ内確信度優先)— 単調に低下 G_iter(クラスタ内Uncertainty)— 下げ止まりつつ常にDを上回る
指標D_iter(確信度優先)G_iter(Uncertainty Sampling)解釈
round1 test acc0.63270.6800Day154と同一の1回きり比較
own-final-round test acc(5シード平均)0.54550.6455gapは+0.0473→+0.1000へ拡大
round1→final の劣化幅-0.0873(相対-13.8%)-0.0345(相対-5.1%)Dの方が約2.5倍深刻な劣化
round-local疑似ラベル精度の推移0.867→0.153(単調崩壊)0.591→0.804(上昇)Dは自己強化的な悪循環に陥る
reversal(round1劣勢→final優勢)seed3のみ該当(1/5)1回比較の優劣は反復で覆りうる

🪜 Step-by-Step 解説

1gapが縮小するという予想が外れ、むしろ2倍以上に拡大した理由

gap_round1     = +0.0473   # Day154と同じ、1回だけの比較
gap_final(own) = +0.1000   # 各手法が到達した最終ラウンドどうしの比較
mean_d: 0.6327 -> 0.5455 (Δ-0.0873)   # (D)は大きく劣化
mean_g: 0.6800 -> 0.6455 (Δ-0.0345)   # (G)は小さく劣化
📋
なぜこうするか: 「pseudo_accが低い方式は反復するほど不利になるはず」という直感は、疑似ラベルの誤りの蓄積だけに注目した予想だった。しかし実際に効いたのは、疑似ラベルの誤りそのものより「選び方が反復に対してどれだけ頑健か」という別の軸だった。(D)は「モデルが既に自信のある領域」を選び続けるため学習データが均質化し、モデルの誤った確信を補強する方向にフィードバックがかかる。(G)は「モデルが最も自信のない領域」を狙うため多様性が保たれ続け、同じ悪循環に陥りにくい。

2(D)のround-local疑似ラベル精度が崩壊する悪循環のメカニズム

D_iter round_pseudo_acc: 0.867 -> 0.552 -> 0.419 -> 0.345 -> 0.153   # 単調に崩壊
D_iter n_added:            90.6 -> 140.8 -> 54.4  -> 11.8  -> 4.0    # round2で採用件数が急増
💡
なぜこうするか: round1では確信度が高いサンプル(90.6件、正解率86.7%)だけが選ばれる。round2になると、round1で取り込んだ一部誤ったラベルの影響でモデルの決定境界がわずかに歪み、「本来は微妙なはずのサンプル」にまで高い確信度を持つようになる。その結果round2の採用件数が140.8件に急増するが、正解率はわずか55.2%まで落ちる。採用件数が増えているのに正解率が落ちるのは、モデルが「量」を稼ぐ代わりに「質」を犠牲にする典型的なconfirmation biasの兆候である。

31つのseedで見られた「1回比較では劣勢だったGが、反復すると逆転する」実例

seed3: gap_round1 = -0.0636(D優勢: d=0.6818, g=0.6182)
       gap_final   = +0.0727(G優勢に逆転: d_final=0.5182, g_final=0.5909)
🧭
なぜこうするか: seed3は5シード中唯一、1回だけの比較では(D)の方が(G)より良い結果(0.6818 vs 0.6182)を示していたケースである。もしこのseedだけを見て「このデータでは(D)の方が優れている」と結論づけていたら、それは早計だった。複数ラウンド反復させると、(D)はconfirmation biasで0.5182まで落ち込む一方、(G)は0.5909を維持し、最終的には(G)が逆転して優勢になった。1回だけの比較で見えた優劣は、反復(あるいは本番運用での継続的な再学習)のもとでは逆転しうる。

🧮 数学・統計の補足(文系向け)

📝
「確信度優先の反復が自己強化的な悪循環に陥る」ことを「自分の得意分野だけを繰り返し復習する勉強法」のたとえで理解する:

ある生徒(D)は、模試で「もう解ける」と自信のある問題だけを繰り返し復習し、その内容を「正解」として自分のノートに書き足していく。ところがその生徒の「自信」自体がところどころ間違った理解に基づいていたとすると、間違った理解のまま「自信のある問題」の範囲がどんどん広がり、気づけば大量の誤った知識を「もう解ける」と思い込んだまま蓄積してしまう——これが(D)がround2で採用件数が急増しながら正解率が急落する現象に対応する。一方、別の生徒(G)は「自分が一番自信のない問題」だけを選んで少しずつ埋めていく。曖昧な部分を優先して埋めるこのやり方は間違いも含むが、自分の理解の穴を計画的に塞いでいくため、繰り返しても自己強化的な思い込みには陥りにくい、というイメージである。
📊
「1回の比較で見えた優劣が反復で逆転する」ことを「短期テストと長期運用」のたとえで理解する:

新しい施策Aと施策Bを1回だけ比較したとき、Aの方が良い結果を出したとする。しかし毎週繰り返し運用したとき、Aは「自分の過去の判断を強化し続ける」設計だったために徐々に判断がずれていき、Bは「毎回、自分が一番自信のない部分を見直す」設計だったために安定して機能し続けた、というイメージである。1回きりのA/Bテストの結果だけで「継続運用したときの優劣」まで判断してはいけない、という直感である。

🏆 Kaggleでの実践的な使い方

よく使われるコンペカテゴリ: ☑ 自然言語処理(NLP) / ☑ 表形式データ(半教師あり学習全般に応用可)

原則

🔁 疑似ラベル選択方式は「反復させたとき」で評価する

1回きりの比較では見えない性質がある

1回きりの比較では見えなかった性質(confirmation biasへの強さ)が、複数ラウンド反復させて初めて明らかになることがある。round1の結果だけで採否を決めず、複数ラウンドのシミュレーションで確認する価値がある。

実例

🚨 「採用件数急増×正解率急落」を早期警告シグナルにする

悪循環の兆候をモニタリングする

(D)のround2で見られたパターン(採用件数90.6→140.8件、正解率86.7%→55.2%)は、モデルが誤った自信を持ち始めているサインである。この種の指標をモニタリングすると悪循環が深刻化する前に対策を打てる。

実務

🎯 Active Learning由来の選択基準は反復運用に向く

長期的な精度維持に効く頑健性

Uncertainty Samplingが反復的な自己学習において確信度優先よりも頑健であることを示した。ラベル付けコストの低い自己学習パイプラインを本番で継続運用する場合、この頑健性は重要な性質になりうる。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
pseudo_accが低い方式は反復するほど不利になると考える「誤ったラベルを学習し続ければ、当然その誤りが蓄積して悪化する」という直感が強く働く反復への強さを決めるのは疑似ラベルの正確さそのものより、選び方が「自己強化的な悪循環」を生みやすいかどうかという別の軸である。(G)はpseudo_accが低くても悪循環を生みにくい設計だった
1回だけの比較結果を、反復運用した場合の優劣の予測にそのまま使うA/Bテストのような1回の比較で優劣が決まると考えがちで、反復の影響を考慮し忘れるseed3の実例が示す通り、1回だけの比較で劣勢だった方式が、反復すると逆転することがある。継続的に再学習する設計を評価する際は、必ず複数ラウンドでのシミュレーションを行うべき
採用件数が増えることを「良いことだ(学習データが増えた)」と単純に評価する「データは多い方が良い」という一般的な直感に引きずられる(D)のround2のように、採用件数の急増が正解率の急落を伴う場合、それはモデルが誤った自信を持ち始めた兆候である可能性が高い。件数だけでなく採用サンプルの質も同時に監視すべき
Iterative Pseudo Labelingは常にconfirmation biasで劣化すると一般化するDay137・今日の実験がいずれもbaselineを下回る結果になったことから、反復は常に有害だと結論づけたくなる両方式ともbaseline(0.7418)を下回ったが、劣化の度合いには(D)-13.8%・(G)-5.1%という大きな差があった。「反復すれば必ず悪化する」のではなく「選び方次第で悪化の度合いが大きく変わる」というのがより正確な理解である

🚀 次のステップ

  • 発展: 今日はquota配分ルール(平均確信度が高いクラスタから余りを+1、Day152由来)を固定していた。この配分ルール自体を確信度非依存(均等配分)に変えた場合、(G)の反復への頑健性がさらに強まるのか、あるいは変わらないのかは未検証
  • 次回予告: Day156では、上記のquota配分ルールを確信度非依存にした場合のIterative Pseudo Labelingへの影響を検証する。あるいは、今日発見した「採用件数急増×正解率急落」の早期警告シグナルを使って(D)側に早期打ち切りルールを導入した場合、劣化がどこまで緩和できるかを検証する候補もある

Phase 6 の学習マップ(NLP系コンペへの応用フェーズ継続中)

1-23
Phase6主要テーマ6項目・総復習(Day126-149、完了)
発展①〜⑥
NLP系コンペへの応用(Day150-153: 選択バイアス対策が停滞/Day154: クラスタ内基準変更で単発の改善/Day155: その改善がIterative化でも持続・拡大することを確認)

Day154の単発の改善が「たまたま1回だけ良かった」のか「反復しても崩れない性質」なのかを検証したのが今日の位置づけ。結果は後者であり、Phase6のケーススタディとして「1回きりの比較と反復運用での評価は別物である」という教訓を残した。

📝 自己評価(解いた後に記入)

自分の回答・気づき・メモ: