Day 152 — NLP系コンペへの応用③ — 選択バイアスを緩和するdiversity-aware疑似ラベル選択は本当に効くのか

2026-09-11 金 / Phase 6(Grandmaster、総復習後の発展回) コーディング→分析 diversity-aware選択 / KMeans / 仮説が支持されなかった実測 / 順序整合性バグ

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

🔁
Day151では、テキスト分類(TF-IDF+LogisticRegression、ラベル付きデータ40件)にPseudo Labelingを適用したところ、確信度閾値0.60・0.55のどちらでもbaseline(平均acc 0.7418)を下回る結果が実測された。さらにアブレーション実験で、疑似ラベル精度自体は約90%と高いのに「確信度が高いものだけを選ぶ」という選択プロセスそのものが主因(選択バイアス)であることが特定された(オラクルacc 0.7291 ≪ ランダムacc 0.8818)。

今日はDay151の次回予告で挙がっていた対策候補の1つ、diversity-aware pseudo labeling(多様性を意識した疑似ラベル選択)を実装する。アイデアは、確信度が高い予測の中から闇雲に上位N件を取るのではなく、まずTF-IDFベクトルをKMeansでクラスタに分け、各クラスタに均等な割当数(quota)を与えたうえでクラスタ内の確信度上位から選ぶことで、「モデルにとって判断しやすい似た文ばかり」を選ぶ偏りを緩和しよう、というものである。

🧪
検証前の仮説: クラスタごとに均等割当するdiversity-aware選択は、同じ件数・同じ候補プールから単純に確信度上位を取るプレーンなtop-N選択よりも、test精度を明確に改善するだろう。
用語意味
候補プール疑似ラベル候補として「確信度がある閾値以上」という条件を満たす、選択前の未ラベルデータの集合
クラスタ割当(quota)KMeansで分けた各クラスタに対し、選びたい総数を均等に割り振った上限数
diversity-aware選択確信度だけでなく、特徴量空間上の分布(クラスタ)も考慮してサンプルを選ぶ手法
フェアな比較「候補プールの広さ」と「選ぶ件数」を揃え、選び方(top-N vs diversity)だけを変数にして比較すること

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

変数名型内容
df (n=450)pd.DataFrameDay151と同じmake_text_dataset()で生成した合成テキスト全体
test_df (110件)pd.DataFrame最終評価専用のホールドアウト。全設定で共通に使う
labeled_df (40件)pd.DataFrame「ラベル付きデータが少ない」状況を模したベースライン学習セット
unlabeled_df (約300件)pd.DataFrameラベルを隠した疑似ラベル候補プール(label列は分析専用でモデルには使わせない)
threshold           = 0.60  # Day151と同一。件数n_addedを決めるための基準
candidate_threshold = 0.55  # top-N・diversity両方が選ぶ「同じ候補プール」を作る閾値
n_clusters          = 8     # KMeansのクラスタ数

🎯 問題

タスクA(理論)

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

1
「確信度が高い予測」の候補プールをTF-IDFベクトルでKMeansクラスタリングし、クラスタごとに均等割当して選ぶことで、なぜ直感的には選択バイアスが緩和されると期待できるのかを説明する
2
このクラスタリングが「候補プール(確信度である閾値を満たすものだけ)」に対して行われている、という条件が、Day151のランダム選択(未ラベルデータ全体からのランダムサンプリング)と比べてどのような限界を生みうるかを予想する

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

B1
make_text_dataset・split_seed・train_and_evalはDay151と同じ実装を再利用する(n=450、test_df110件/labeled_df40件/unlabeled_df残り約300件)
B2
select_confident_pseudo_labels(model, vectorizer, unlabeled_df, threshold) — Day151と同じ実装を再利用する
B3
select_topn_from_pool(model, vectorizer, unlabeled_df, candidate_threshold, n_target) — 広めの閾値(0.55)で候補プールを作り、確信度上位n_target件を単純に選ぶ関数を実装する(「プールを広げただけ」の対照群)
B4
select_diverse_pseudo_labels(model, vectorizer, unlabeled_df, candidate_threshold, n_target, n_clusters, seed) — 候補プールのTF-IDFベクトルをKMeansでクラスタリングし、n_target件を各クラスタに均等割当(余りは平均確信度が高いクラスタから+1)し、クラスタ内は確信度が高い順に選ぶ関数を実装する
B5
順序整合性への注意(重要): 確信度でソートして選んだ行インデックスは、unlabeled_df.loc[mask, "text"](常に元の行順=昇順)と結合する前に必ず昇順に戻してからmaskを作り、pseudo_labelsもproba[mask]のようにmask経由で取り出すこと
B6
threshold=0.60・candidate_threshold=0.55・n_clusters=8で、5シードにわたり (A) select_confident_pseudo_labels(0.60固定), (B) select_topn_from_pool(0.55プール・同数top-N), (C) select_diverse_pseudo_labels(0.55プール・同数diversity) の3パターンを比較する
B7
(B)(C)いずれもbaselineより平均test精度が悪化すること/同一プール・同一件数条件での(B)と(C)の平均test精度差が0.05未満であることをassert文で確認する
B8
(考察): タスクAの2.の予想が実際どうだったかを、Day151の「オラクル vs ランダム」の比較結果と関連づけて100〜150字程度で述べる

💡 ヒント

ヒント1方向性

タスクAの2.は、「候補プール自体がすでにモデルの確信度でフィルタされたサブセットである」という点を考えるとよい。Day151のランダム選択は「ラベルなしデータ全体」からランダムに選んでいたのに対し、今日のdiversity-aware選択は「確信度である閾値を満たすサブセット」の中だけでクラスタリングしている。このサブセット自体がすでにモデルの決定境界寄りに偏っている可能性があり、その中でどうクラスタ分けしても、Day151のランダム選択が持っていた「本当に多様なサンプル」までは再現できない可能性がある。

ヒント2アプローチ
  • select_diverse_pseudo_labelsのKMeans.fit_predictは候補プールのベクトル(X_un[cand_idx])に対してのみ行う。unlabeled_df全体に対してクラスタリングすると候補プール外のクラスタ構造まで混ざるので注意
  • クラスタごとのquotaはn_target // n_clustersを基本にし、割り切れない余りは「平均確信度が高いクラスタ」から優先的に+1する
  • 順序整合性のバグは非常に踏みやすい落とし穴である。pseudo_labelsを作る直前に、必ずmask(boolean配列)経由でprobaにアクセスし直すことで防げる
  • (A)はthreshold=0.60固定のDay151と同じ選び方(対照点として残す)、(B)(C)はcandidate_threshold=0.55・同数n_addedで条件を完全に揃えることが、diversityそのものの効果を切り分けるポイント
ヒント3コード骨格
def select_topn_from_pool(model, vectorizer, unlabeled_df, candidate_threshold, n_target):
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    cand_idx = np.where((proba >= candidate_threshold) | (proba <= (1 - candidate_threshold)))[0]
    confidence = np.abs(proba[cand_idx] - 0.5)
    order = np.argsort(-confidence)
    n_target = min(n_target, len(cand_idx))
    top_idx = np.sort(cand_idx[order[:n_target]])  # 昇順に戻す
    mask = np.zeros(len(unlabeled_df), dtype=bool)
    mask[top_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)  # mask経由で取得し順序を一致させる
    return mask, pseudo_labels

def select_diverse_pseudo_labels(model, vectorizer, unlabeled_df, candidate_threshold, n_target, n_clusters, seed):
    # ... cand_idxを作り、X_un[cand_idx]をKMeans(n_clusters, random_state=seed)でクラスタリング
    # ... クラスタごとにquotaを割当て、クラスタ内は確信度が高い順に選ぶ
    # ... 最後に選んだインデックスをnp.sort()で昇順に戻してからmaskを作る
    ...

✅ 模範解答

import random
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, log_loss
from sklearn.cluster import KMeans

# POSITIVE_WORDS / NEGATIVE_WORDS / NEUTRAL_WORDS / make_text_dataset / train_and_eval /
# split_seed / select_confident_pseudo_labels は Day151と同一(省略)

def select_topn_from_pool(model, vectorizer, unlabeled_df, candidate_threshold, n_target):
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    cand_idx = np.where((proba >= candidate_threshold) | (proba <= (1 - candidate_threshold)))[0]
    confidence = np.abs(proba[cand_idx] - 0.5)
    order = np.argsort(-confidence)
    n_target = min(n_target, len(cand_idx))
    top_idx = np.sort(cand_idx[order[:n_target]])
    mask = np.zeros(len(unlabeled_df), dtype=bool)
    mask[top_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)
    return mask, pseudo_labels


def select_diverse_pseudo_labels(model, vectorizer, unlabeled_df, candidate_threshold, n_target, n_clusters, seed):
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    cand_idx = np.where((proba >= candidate_threshold) | (proba <= (1 - candidate_threshold)))[0]
    if len(cand_idx) == 0:
        return np.zeros(len(unlabeled_df), dtype=bool), np.array([], dtype=int)
    n_target = min(n_target, len(cand_idx))

    X_cand = X_un[cand_idx]
    k = min(n_clusters, len(cand_idx))
    km = KMeans(n_clusters=k, random_state=seed, n_init=10)
    cluster_ids = km.fit_predict(X_cand)

    confidence = np.abs(proba[cand_idx] - 0.5)
    order = np.argsort(-confidence)
    quota = n_target // k
    remainder = n_target - quota * k
    cluster_quota = {c: quota for c in range(k)}
    cluster_conf_rank = pd.Series(confidence, index=cluster_ids).groupby(level=0).mean().sort_values(ascending=False)
    for c in list(cluster_conf_rank.index)[:remainder]:
        cluster_quota[c] += 1

    taken = {c: 0 for c in range(k)}
    selected_local = []
    for i in order:
        c = cluster_ids[i]
        if taken[c] < cluster_quota[c]:
            selected_local.append(i)
            taken[c] += 1
        if len(selected_local) >= n_target:
            break

    selected_global_idx = np.sort(cand_idx[selected_local])
    mask = np.zeros(len(unlabeled_df), dtype=bool)
    mask[selected_global_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)
    return mask, pseudo_labels


def run_comparison(seed, threshold=0.60, candidate_threshold=0.55, n_clusters=8):
    labeled_df, unlabeled_df, test_df = split_seed(seed)
    acc_base, ll_base, vectorizer, model = train_and_eval(
        labeled_df["text"].tolist(), labeled_df["label"].tolist(), test_df)

    # (A) Day151のプレーン確信度選択(threshold=0.60)そのもの
    mask_conf, pseudo_conf = select_confident_pseudo_labels(model, vectorizer, unlabeled_df, threshold)
    n_added = int(mask_conf.sum())
    acc_conf, ll_conf, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_conf, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_conf.tolist(), test_df)

    # (B) 同じ候補プール(0.55)からtop-Nを確信度だけで選ぶ
    mask_topn, pseudo_topn = select_topn_from_pool(model, vectorizer, unlabeled_df, candidate_threshold, n_added)
    acc_topn, ll_topn, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_topn, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_topn.tolist(), test_df)

    # (C) 同じ候補プール(0.55)・同じ件数(n_added)からdiversity-awareで選ぶ
    mask_div, pseudo_div = select_diverse_pseudo_labels(
        model, vectorizer, unlabeled_df, candidate_threshold, n_added, n_clusters, seed)
    acc_div, ll_div, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_div, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_div.tolist(), test_df)

    return {"seed": seed, "n_added": n_added, "acc_base": acc_base,
            "acc_conf": acc_conf, "acc_topn": acc_topn, "acc_div": acc_div}


SEEDS = [0, 1, 2, 3, 4]
df = pd.DataFrame([run_comparison(s) for s in SEEDS])

mean_acc_base = df["acc_base"].mean()
mean_acc_topn = df["acc_topn"].mean()
mean_acc_div = df["acc_div"].mean()

assert mean_acc_topn < mean_acc_base, "同一プールtop-N選択がbaselineを悪化させていません"
assert mean_acc_div < mean_acc_base, "diversity-aware選択がbaselineを悪化させていません"
assert abs(mean_acc_topn - mean_acc_div) < 0.05, \
    "top-N選択とdiversity-aware選択のtest精度差が0.05以上あります"
print("[OK] 同一プール条件ではdiversity-aware選択が明確に優位とは言えない")
# ============ 実測結果(実際にPython 3で実行して確認済み) ============
#  seed  n_added  acc_base  acc_conf  acc_topn  acc_div  pseudo_acc_conf  pseudo_acc_topn  pseudo_acc_div
#     0       94  0.763636  0.581818  0.581818 0.590909         0.946809         0.946809        0.893617
#     1      125  0.727273  0.590909  0.590909 0.618182         0.888000         0.888000        0.856000
#     2       81  0.781818  0.645455  0.645455 0.645455         0.938272         0.938272        0.962963
#     3       75  0.736364  0.672727  0.672727 0.663636         0.840000         0.840000        0.813333
#     4       78  0.700000  0.627273  0.627273 0.627273         0.884615         0.884615        0.871795
#
# [まとめ]
#   mean acc_base            = 0.7418
#   mean acc_conf(0.60固定, Day151と同一)  = 0.6236  (pseudo_acc=0.8995)
#   mean acc_topn(同一プール0.55, top-N)   = 0.6236  (pseudo_acc=0.8995)  ※acc_confと完全一致(sanity check成立)
#   mean acc_div (同一プール0.55, diversity)= 0.6291  (pseudo_acc=0.8795)
#   |acc_topn - acc_div| = 0.0055(0.05未満)
#
# [OK] 同一プール条件ではdiversity-aware選択が明確に優位とは言えない
⚠️
重要な発見1(当初の仮説が支持されなかった): 検証前の仮説は「diversity-aware選択は同条件のtop-N選択より明確に改善するはず」だった。しかし実測ではdiversity選択(0.6291)がtop-N選択(0.6236)よりわずかに高かったものの、その差はわずか0.0055。5シードの実測ばらつき(seed=2では両者が完全一致)を踏まえると「明確な改善」とは言えず、どちらもbaseline(0.7418)を大きく下回ったままで、選択バイアスは緩和されなかった。
🐛
重要な発見2(実装過程で見つけて修正したバグ): 初期実装では、確信度でソートした順のままインデックス配列からpseudo_labelsを作っていた。一方unlabeled_df.loc[mask]は常に元の行順(昇順)を返すため、この2つの順序がズレたまま結合すると、正しい疑似ラベルが別の行に付け替わる。「acc_confとacc_topnは理論上一致するはず」というsanity checkが最初は成立せず、それが発覚のきっかけになった。修正は「選んだインデックスをnp.sort()で昇順に戻し、pseudo_labelsは必ずmask経由で取得する」という単純なもの。

📊 結果の可視化

① 同じ候補プールを作る

candidate_threshold=0.55で(B)(C)が選ぶ母集団を揃える

② top-N vs diversity-aware

同じ件数n_added件を「確信度順」「クラスタquota」の2通りで選ぶ

③ フェアな比較で判定

選び方だけを変数にして、diversityが本当に効くかを5シードで確認

同一プール(0.55)・同一件数条件での accuracy 比較(5シード平均) Day151参考: ランダム選択+正解ラベル 0.8818(参考値) baseline(40件のみ) 0.7418 (C) diversity-aware選択 0.6291 (B) プレーンtop-N選択 0.6236 (B)と(C)の差はわずか0.0055。母集団(候補プール)自体は共通なので、選び方(top-N vs diversity) の違いだけでは選択バイアスの根本(0.7418→0.62前後の悪化)を解消できていない
設定平均accuracy解釈
Day151参考: ランダム選択+正解ラベル(未ラベル全体から)0.8818母集団自体が偏っていない理想形(参考値)
baseline(40件のみ)0.7418追加データなし
(C) diversity-aware選択(候補プール内クラスタquota)0.6291top-Nよりわずかに高いが誤差の範囲
(B) プレーンtop-N選択(同一候補プール)0.6236母集団の偏りは解消されないまま

🪜 Step-by-Step 解説

1なぜacc_confとacc_topnが完全一致することが「正しさの検証」になるのか

# mean acc_conf(0.60固定)              = 0.6236
# mean acc_topn(同一プール0.55, top-N) = 0.6236  # 完全一致
📋
なぜこうするか: 0.55という広いプールから確信度上位n_added件(=0.60のプールのサイズ)を取れば、選ばれる集合は理論上0.60のプールと完全に一致するはずである。この一致が5シード全てで成立したことは、「候補プールを広げる」操作自体は選択結果に影響しない(プールの広さそのものはバイアスの原因ではない)ことを裏付けると同時に、実装が意図通り動いていることのsanity checkとしても機能する。逆にこの一致が崩れていれば、それは実装バグの証拠になる——実際に開発過程ではこの一致が最初は崩れており、バグ発見の糸口になった。

2なぜfancy indexingの順序とboolean maskの順序のズレが危険なのか

# 危険な書き方(初期バージョンで実際に踏んだバグ):
top_idx = cand_idx[order[:n_target]]                  # 確信度でソートされた順
pseudo_labels = (proba[top_idx] >= 0.5).astype(int)  # top_idxの順(確信度順)
# 一方 unlabeled_df.loc[mask, "text"] は元の行順(昇順)で返す → 順序が一致しない!

# 安全な書き方(修正後):
top_idx = np.sort(cand_idx[order[:n_target]])      # 昇順に戻す
mask = np.zeros(len(unlabeled_df), dtype=bool)
mask[top_idx] = True
pseudo_labels = (proba[mask] >= 0.5).astype(int)    # mask経由で取得 → 昇順で一致
🎯
なぜこうするか: np.argsortなどで確信度順に並べ替えたインデックス配列と、.loc[boolean_mask]が返す行順(常に元の行順=昇順)は別物である。この2つを別々に使って「テキストのリスト」と「ラベルのリスト」を作ると、要素数は合っていても対応関係がズレるという、気づきにくいバグになる。このバグは疑似ラベルの精度を落とすのではなく、正しい疑似ラベルを別のテキストに付け替えてしまうため、pseudo_accの異常な低下という形で症状が出る。実測でpseudo_acc_topnが0.76前後に不自然に低下した(修正前)ことが、原因調査のきっかけになった。

3なぜdiversity-aware選択はDay151のランダム選択に及ばなかったのか

mean_acc_div    = 0.6291  # 候補プール内クラスタリング
mean_acc_random = 0.8818  # Day151: 未ラベルデータ全体からのランダム選択(参考値)
🔀
なぜこうするか: Day151のランダム選択は、確信度の情報を一切使わず「未ラベルデータ全体」から選んでいた。一方今日のdiversity-aware選択は、あくまで「確信度である閾値を満たした候補プール」の内部でクラスタに分けているに過ぎない。候補プールに入ること自体がすでにモデルの決定境界に近い・判断しやすいサンプルへの偏りを反映しているため、そのプール内でどれだけクラスタを分けても、プールの外にある「モデルが判断に迷う多様なサンプル」を拾うことはできない。「多様性を確保する」という言葉の効果は、多様性を測る母集団をどこに取るかに強く依存することが今日の実測から得られた。

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

📝
「候補プール内でのクラスタリング」を「同じ学年の生徒だけで班分けする」として理解する:

クラス全員(未ラベルデータ全体)からランダムに班を作れば、成績上位者も下位者も混ざった多様な班ができる(Day151のランダム選択に相当)。しかし「テストで80点以上を取った生徒だけ」を集めてから班分けした場合(今日のdiversity-aware選択に相当)、班ごとに得意科目は多少ばらけても、そもそも「80点以上」という母集団自体が偏っているため、クラス全体の多様性(苦手科目を持つ生徒の視点)は再現できない。母集団の選び方(誰を対象にクラスタリングするか)が、多様性確保の効果の上限を決めてしまう、という直感である。
📊
「fancy indexingの順序ズレ」を「並べ替えた出席簿と元の出席簿を混同する」として理解する:

確信度でソートした配列は、いわば「成績順に並べ替えた出席簿」である。一方.loc[mask]は「元の出席番号順の出席簿からmaskに該当する生徒だけ抜き出したもの」である。この2つの出席簿の並び順を混同して「成績順の名前リスト」と「出席番号順の点数リスト」を組み合わせると、生徒と点数の対応がズレる。今日のバグはまさにこれに相当し、要素数が合っているだけに見た目では気づきにくい。

🏆 Kaggleでの実践的な使い方

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

原則

🎯 多様性を測る母集団まで検証する

母集団の選び方が上限を決める

「確信度の高いサブセット内でクラスタリングする」だけでは選択バイアスの根本原因(母集団そのものの偏り)は解消できない。確信度フィルタを適用する前の全データに対してクラスタリングを行う設計の方が有望である。

実例

🔍 「一致するはず」のsanity checkを仕込む

acc_conf ≡ acc_topn

理論上一致するはずの2つの計算結果をあえて両方計算して突き合わせることで、実装バグを早期に発見できる。疑似ラベル選択・TTA・スタッキングなど「複数の配列を後から結合する」処理で特に有効である。

実務

⚙️ fancy indexingとmaskの混在を避ける規約

順序整合性バグの予防

「複数の配列を対応付けて使う場合は必ず同じ方法(mask経由 or 同じソート済みインデックス経由)でアクセスする」というルールをコーディング規約にしておくと、静かに指標を壊すバグを未然に防げる。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
「多様性を確保する」対策を入れれば選択バイアスは必ず緩和されると思い込む「diversity」という言葉の響きから、対策を入れさえすれば効果が出ると期待してしまう多様性を測る母集団自体が既に偏っている(確信度フィルタ後のプール内でのクラスタリング)場合、対策を入れても効果はほぼ出ない。多様性確保の効果は「どの母集団に対して多様性を測るか」に強く依存する
確信度でソートした配列のインデックスと.loc[mask]の行順が同じだと思い込むどちらも「同じ行を指している」という点だけに注目し、配列の並び順(ソート済みか元の順か)を意識しないnp.argsortなどで並べ替えたインデックス配列は確信度順、.loc[boolean_mask]は常に元の行順(昇順)を返す。この2つを混在させると要素数は合っていても対応関係がズレる
疑似ラベル関連の指標が不自然に低い場合、真っ先に「閾値やモデルの問題」を疑うDay151で「閾値の実測が大事」という教訓を得たばかりだと、同種の原因を優先的に疑ってしまう指標の異常低下は閾値の問題ではなく「配列の順序ズレ」のような実装バグが原因のこともある。まず「理論上一致するはずの値同士を突き合わせる」sanity checkで、閾値設計の問題か実装バグかを切り分けるとよい
KMeansのクラスタ数n_clustersを大きくすれば多様性はいくらでも改善すると考えるクラスタ数を増やすほど「きめ細かく分ける」ので多様性が増しそうだと直感的に考えてしまうクラスタ数を増やしても、クラスタリングの対象となる母集団(今日で言えば候補プール)自体の偏りは解消されない。母集団を広げない限り、クラスタ数の調整だけで得られる改善には上限がある

🚀 次のステップ

  • 発展: 今日の考察を踏まえると、真に選択バイアスを緩和する設計は「確信度フィルタを適用する前の未ラベルデータ全体」に対してクラスタリングを行い、各クラスタから確信度に関係なく一定数を選ぶ(クラスタ代表点+確信度は補助情報として使う)方式である可能性が高い。この「フィルタ前 vs フィルタ後」のクラスタリング順序の違いが実際に結果を変えるかを検証することが次の発展候補である
  • 次回予告: 引き続きPhase6の「NLP系コンペへの応用」を深掘りし、次回はDay150で保留したback-translationのような文単位のTTA、または「フィルタ前クラスタリング」による多様性確保の再検証を検討する

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

1-23
Phase6主要テーマ6項目・総復習(Day126-149、完了)
発展①②③
NLP系コンペへの応用(Day150: TTA→逆効果/Day151: Pseudo Labeling→選択バイアス/Day152: diversity対策→効果限定的)

Day150→151→152と、「表形式・画像で効いた手法・素朴な対策をそのままテキストに持ち込むと期待通りには機能しない」というパターンが3日連続で実測された。今日は対策そのもの(diversity-aware選択)まで検証範囲に含め、その対策自身も母集団の選び方次第で効果が限定的になることが分かった。

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

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