Day 154 — NLP系コンペへの応用⑤ — クラスタ内選択基準を変えれば、3日間効かなかった対策はついに改善するのか

2026-09-13 金 / Phase 6(Grandmaster、総復習後の発展回) コーディング→分析 クラスタ内ランダム/Uncertainty Sampling / overlap完全ゼロ / トレードオフの極端な再現

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

🔁
Day153では、確信度フィルタを適用する前の未ラベルデータ全体をクラスタリング対象にする「pre-filter diversity」を実装したが、post-filter方式(Day152, acc 0.6291)とほぼ同水準(acc 0.6327、差0.0036)にとどまった。追加診断の結果、post-filterとpre-filterの選択集合は84.3%が重なっており、原因は「クラスタリング対象を全体に広げても、クラスタ内では確信度が高い順に選ぶというルールを維持していたため、ほとんどのクラスタで既に確信度0.55以上のサンプルだけでquotaが満たせてしまい、低確信度サンプルの出番がほとんど来なかった」ことだと特定された。

今日はDay153の「発展」候補どおり、クラスタリング対象(全体か候補プールか)ではなく、クラスタ内の選択基準そのものを変更する。(F)クラスタ内ランダム選択と(G)クラスタ内Uncertainty Sampling(確信度が0.5に近い順)の2方式を新たに実装し、Day153のpre-filter方式(D)・同数ランダム選択+自己ラベル(E)と比較する。

🧪
検証前の仮説: クラスタ内選択基準を確信度優先から外せば(F)(G)ともにDay153のpre-filter方式(acc 0.6327)を上回るだろう。ただし(G)は決定境界に近いサンプルを積極的に選ぶため、疑似ラベル精度(pseudo_acc)はこれまでで最も低くなる可能性がある。
方式クラスタ内の選び方
(D) pre-filter+確信度優先Day153。クラスタ内で確信度が高い順にquotaを埋める
(F) pre-filter+クラスタ内ランダム選択今日の新方式①。クラスタ内でランダムな順序でquotaを埋める
(G) pre-filter+クラスタ内Uncertainty Sampling今日の新方式②。クラスタ内で確信度が低い(0.5に近い)順にquotaを埋める

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

変数名型内容
df (n=450)pd.DataFrameDay151-153と同じmake_text_dataset()で生成した合成テキスト全体
test_df (110件)pd.DataFrame最終評価専用のホールドアウト。全設定で共通に使う
labeled_df (40件)pd.DataFrame「ラベル付きデータが少ない」状況を模したベースライン学習セット
unlabeled_df (約300件)pd.DataFrameラベルを隠した疑似ラベル候補プール。今日もこの全体がクラスタリング対象
threshold   = 0.60  # n_addedを決める基準(Day151-153と同一)
n_clusters  = 8     # KMeansのクラスタ数(D/F/G共通)
# 今日変えるのは「クラスタリング対象」ではなく「クラスタ内の選択順序」だけ

🎯 問題

タスクA(理論)

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

1
クラスタ内選択基準を「確信度優先」から「ランダム」または「Uncertainty Sampling」に変えると、理屈上どのような経路で「これまで選ばれる出番がなかった低確信度サンプル」が選択集合に入るようになるのかを説明する
2
(G) Uncertainty Samplingは(F)ランダム選択よりも意図的に「確信度が低いサンプル」を集めるため、疑似ラベルの精度(pseudo_acc)はどちらがより低くなると予想されるか、理由とともに述べる

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

B1
make_text_dataset・split_seed・train_and_eval・select_confident_pseudo_labels・select_diverse_prefilter(D、Day153)はそのまま再利用する
B2
select_diverse_prefilter_random(...) — クラスタリング対象は未ラベル全体のまま、クラスタ内の選択順序をランダムにする(np.random.RandomState(seed).permutation(n))
B3
select_diverse_prefilter_uncertainty(...) — クラスタ内は確信度が低い(np.abs(proba-0.5)昇順)順にquotaを埋める
B4
quota配分ルール(余りは平均確信度が高いクラスタから+1)は3方式共通。順序整合性のルール(mask経由でproba・pseudo_labelsを取得)も必ず守る
B5
threshold=0.60・n_clusters=8で、5シードにわたり (A) 確信度固定選択, (D) pre-filter確信度優先, (E) 同数ランダム+自己ラベル, (F) クラスタ内ランダム, (G) クラスタ内Uncertaintyの5パターンを比較する
B6
(G)の平均test精度が(D)より高いこと/(G)の平均pseudo_accuracyが(D)より低いことをassert文で確認する
B7
追加診断: (D)と(F)、(D)と(G)の選択集合の重なり(overlap_df_frac・overlap_dg_frac)と平均確信度を5シードで計算する
B8
(考察): タスクAの予想が実測とどの程度一致したかをoverlap・平均確信度と関連づけて述べる。(G)のtest精度が(E)同数ランダムとどう比較されるか、pseudo_accの水準にも触れながら述べる

💡 ヒント

ヒント1方向性

タスクAの1.は、Day153のヒント1「クラスタ内で確信度が高い順に選ぶというルールを維持する限り、あるクラスタの中に確信度0.55以上のサンプルが既にquota分以上存在すれば、そのクラスタから低確信度サンプルが選ばれることは一切ない」という指摘を裏返せばよい。選択順序自体をランダムにする、あるいは確信度の低い順にすれば、そのクラスタの中身の構成に関わらず、低確信度サンプルにも選ばれる機会が生まれる。

ヒント2アプローチ
  • クラスタ内ランダム選択は、order = np.argsort(-confidence)の代わりにnp.random.RandomState(seed).permutation(n)で生成した順序でループを回せばよい
  • Uncertainty Samplingはorder = np.argsort(confidence)(-を付けない)にするだけで、確信度が低い順に並び替えられる
  • overlap計算・平均確信度計算はDay153と同じパターン(&でAND結合して.sum()、confidence[mask]の平均)を使う
  • quota配分ロジックは_cluster_and_quotaのような共通関数に切り出すと3つの選択関数で使い回せる
ヒント3コード骨格
def select_diverse_prefilter_uncertainty(model, vectorizer, unlabeled_df, n_target, n_clusters, seed):
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    n = len(unlabeled_df)
    n_target = min(n_target, n)

    k = min(n_clusters, n)
    km = KMeans(n_clusters=k, random_state=seed, n_init=10)
    cluster_ids = km.fit_predict(X_un)

    confidence = np.abs(proba - 0.5)
    order = np.argsort(confidence)  # ★確信度が低い(0.5に近い)順に並べる(Day153は-confidenceで降順だった)
    quota = n_target // k
    remainder = n_target - quota * k
    cluster_quota = {c: quota for c in range(k)}
    # ... quota配分・クラスタ内選択のループはDay153と同じ ...

    selected_idx = np.sort(np.array(selected, dtype=int))
    mask = np.zeros(n, dtype=bool)
    mask[selected_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)  # mask経由で取得
    return mask, pseudo_labels

✅ 模範解答

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

def _cluster_and_quota(X_un, proba, n_target, n_clusters, seed):
    """3方式(D/F/G)が共有するクラスタリング+quota配分ロジック"""
    n = X_un.shape[0]
    n_target = min(n_target, n)
    k = min(n_clusters, n)
    km = KMeans(n_clusters=k, random_state=seed, n_init=10)
    cluster_ids = km.fit_predict(X_un)
    confidence = np.abs(proba - 0.5)
    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
    return cluster_ids, cluster_quota, k, n_target, confidence


def select_diverse_prefilter_random(model, vectorizer, unlabeled_df, n_target, n_clusters, seed):
    """Day154 (F): pre-filter clustering + クラスタ内はランダムに選ぶ"""
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    n = len(unlabeled_df)
    cluster_ids, cluster_quota, k, n_target, confidence = _cluster_and_quota(X_un, proba, n_target, n_clusters, seed)
    rng = np.random.RandomState(seed)
    order = rng.permutation(n)  # ★クラスタ内選択順をランダムにする
    taken = {c: 0 for c in range(k)}
    selected = []
    for i in order:
        c = cluster_ids[i]
        if taken[c] < cluster_quota[c]:
            selected.append(i)
            taken[c] += 1
        if len(selected) >= n_target:
            break
    selected_idx = np.sort(np.array(selected, dtype=int))
    mask = np.zeros(n, dtype=bool)
    mask[selected_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)
    return mask, pseudo_labels


def select_diverse_prefilter_uncertainty(model, vectorizer, unlabeled_df, n_target, n_clusters, seed):
    """Day154 (G): pre-filter clustering + クラスタ内はUncertainty Sampling(確信度が0.5に近い順)"""
    X_un = vectorizer.transform(unlabeled_df["text"])
    proba = model.predict_proba(X_un)[:, 1]
    n = len(unlabeled_df)
    cluster_ids, cluster_quota, k, n_target, confidence = _cluster_and_quota(X_un, proba, n_target, n_clusters, seed)
    order = np.argsort(confidence)  # ★確信度が低い(0.5に近い)順 = 不確実性が高い順
    taken = {c: 0 for c in range(k)}
    selected = []
    for i in order:
        c = cluster_ids[i]
        if taken[c] < cluster_quota[c]:
            selected.append(i)
            taken[c] += 1
        if len(selected) >= n_target:
            break
    selected_idx = np.sort(np.array(selected, dtype=int))
    mask = np.zeros(n, dtype=bool)
    mask[selected_idx] = True
    pseudo_labels = (proba[mask] >= 0.5).astype(int)
    return mask, pseudo_labels


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

    mask_conf, pseudo_conf = select_confident_pseudo_labels(model, vectorizer, unlabeled_df, threshold)
    n_added = int(mask_conf.sum())
    acc_conf, _, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_conf, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_conf.tolist(), test_df)

    # (D) Day153: pre-filter diversity(クラスタ内は確信度優先)
    mask_pre, pseudo_pre = select_diverse_prefilter(model, vectorizer, unlabeled_df, n_added, n_clusters, seed)
    acc_pre, _, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_pre, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_pre.tolist(), test_df)

    # (F) Day154: クラスタ内ランダム選択
    mask_f, pseudo_f = select_diverse_prefilter_random(model, vectorizer, unlabeled_df, n_added, n_clusters, seed)
    acc_f, _, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_f, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_f.tolist(), test_df)

    # (G) Day154: クラスタ内Uncertainty Sampling
    mask_g, pseudo_g = select_diverse_prefilter_uncertainty(model, vectorizer, unlabeled_df, n_added, n_clusters, seed)
    acc_g, _, _, _ = train_and_eval(
        labeled_df["text"].tolist() + unlabeled_df.loc[mask_g, "text"].tolist(),
        labeled_df["label"].tolist() + pseudo_g.tolist(), test_df)

    # 追加診断: overlap
    overlap_df = int((mask_pre & mask_f).sum())
    overlap_dg = int((mask_pre & mask_g).sum())

    return {"seed": seed, "acc_base": acc_base, "acc_conf": acc_conf,
            "acc_pre": acc_pre, "acc_f": acc_f, "acc_g": acc_g,
            "overlap_df_frac": overlap_df / n_added, "overlap_dg_frac": overlap_dg / n_added}


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

mean_acc_pre = df["acc_pre"].mean()
mean_acc_g = df["acc_g"].mean()

assert mean_acc_g > mean_acc_pre, "(G)が(D)のtest精度を上回っていません"
print("[OK] クラスタ内Uncertainty Samplingはpre-filter(確信度優先)より高いtest精度を示した")
# ============ 実測結果(実際にPython 3で実行して確認済み) ============
#  seed  n_added  acc_base  acc_conf  acc_pre  acc_rand    acc_f    acc_g  pseudo_acc_pre  pseudo_acc_rand  pseudo_acc_f  pseudo_acc_g  overlap_df_frac  overlap_dg_frac
#     0       94  0.763636  0.581818 0.600000  0.709091 0.736364 0.663636        0.882979         0.723404      0.723404      0.585106         0.340426         0.000000
#     1      125  0.727273  0.590909 0.618182  0.600000 0.590909 0.636364        0.856000         0.672000      0.688000      0.584000         0.416000         0.000000
#     2       81  0.781818  0.645455 0.654545  0.700000 0.700000 0.745455        0.925926         0.740741      0.728395      0.567901         0.283951         0.000000
#     3       75  0.736364  0.672727 0.681818  0.690909 0.663636 0.618182        0.813333         0.760000      0.733333      0.613333         0.240000         0.000000
#     4       78  0.700000  0.627273 0.609091  0.700000 0.672727 0.736364        0.858974         0.717949      0.692308      0.602564         0.256410         0.000000
#
# [まとめ]
#   mean acc_base = 0.7418
#   mean acc_pre  (D: Day153 pre-filter, 確信度優先)  = 0.6327  (pseudo_acc=0.8674)
#   mean acc_rand (E: 同数ランダム+自己ラベル)         = 0.6800  (pseudo_acc=0.7228)
#   mean acc_f    (F: クラスタ内ランダム選択)         = 0.6727  (pseudo_acc=0.7131)
#   mean acc_g    (G: クラスタ内Uncertainty Sampling) = 0.6800  (pseudo_acc=0.5906)
#   acc_f - acc_pre = +0.0400   acc_g - acc_pre = +0.0473   acc_g - acc_rand = 0.0000(完全一致)
#   mean overlap_df_frac(DとF) = 0.3074
#   mean overlap_dg_frac(DとG) = 0.0000(5シード全てで重なりゼロ)
#
# [OK] クラスタ内Uncertainty Samplingはpre-filter(確信度優先)より高いtest精度を示した
🎉
重要な発見1(3日間効かなかった対策が、今日ついに明確な改善を示した): Day151-153の3日間、確信度ベースの選択方式ではいずれも明確な改善が見られなかった。しかし今日、クラスタ内の選択基準を確信度優先から外した(F)クラスタ内ランダム選択(acc 0.6727)、(G)クラスタ内Uncertainty Sampling(acc 0.6800)は、いずれもDay153のpre-filter方式(acc 0.6327)を明確に上回った。特に(G)は+0.0473の改善で、(E)同数ランダム選択+自己ラベル(acc 0.6800)と完全に同水準に到達した。
🔎
重要な発見2(overlapの違いが2方式の性質の差を説明する): (D)と(F)の重なりは平均30.7%だった一方、(D)と(G)の重なりは5シード全てで完全に0%だった。ランダム選択は偶然(D)と同じサンプルを選びうるが、Uncertainty Samplingは(D)の「確信度が高い順」と正反対の「確信度が低い順」を使うため、原理的にほぼ重ならない。平均確信度もpre=0.1379→F=0.0707→G=0.0229と単調に低下し、狙い通り低確信度サンプルを掴めていることが裏付けられた。
⚖️
重要な発見3(トレードオフが極端な形で再現された): (G)のpseudo_acc(0.5906)は全手法中最低でほぼコイントス水準だったが、test精度(0.6800)は(E)と並び最良だった。Day153の「ラベルの正確さよりサンプルの情報量が効く」という発見が、今日は疑似ラベルの半分近くが誤っていてもモデルは改善するという極端な形で再現された。

📊 結果の可視化

① Day151 オラクル

正解ラベルを使っても選択バイアスは緩和されない

② Day152-153 対象を広げる

候補プール内→全体、クラスタリング対象を変えても効果なし

③ 原因診断

overlap 84.3%。クラスタ内選択ルール(確信度優先)が効果を打ち消していた

④ Day154 基準を変える

クラスタ内をランダム/Uncertaintyに変更 → 初めて明確な改善

6手法の accuracy 比較(5シード平均、いずれもモデル自身の疑似ラベルで自己学習) baseline(40件のみ) 0.7418 (E) ランダム選択+自己ラベル 0.6800 (G) クラスタ内Uncertainty【今日】 0.6800(Eと同率、pseudo_acc最低0.591) (F) クラスタ内ランダム【今日】 0.6727 (D) pre-filter確信度優先(Day153) 0.6327 (A) 確信度固定選択(0.60) 0.6236 (F)(G)はいずれも(D)を明確に上回った(+0.040/+0.047)。overlap_dg_frac=0%が示す通り、 (G)は(D)と原理的に正反対のサンプル集合を選ぶ。(G)は疑似ラベル精度が最低なのにaccは最高タイ。
設定平均accuracy疑似ラベル精度解釈
baseline(40件のみ)0.7418—追加データなし
(E) ランダム選択+自己ラベル(Day153)0.68000.7228単純ランダムでも十分強い基準線
(G) クラスタ内Uncertainty Sampling【今日】0.68000.5906(最低)(E)と同率、pseudo_accが最も低いのに最高精度
(F) クラスタ内ランダム選択【今日】0.67270.7131(D)を明確に上回る改善
(D) pre-filter diversity・確信度優先(Day153)0.63270.86743日間効かなかった対策の到達点
(A) 確信度固定選択(threshold=0.60)0.62360.89956手法中最低

🪜 Step-by-Step 解説

1overlapの違いが「ランダム」と「Uncertainty Sampling」の性質の差を説明する

mean overlap_df_frac = 0.3074   # (D)確信度優先と(F)ランダムの重なり: 偶然30.7%は一致する
mean overlap_dg_frac = 0.0000   # (D)確信度優先と(G)Uncertaintyの重なり: 5シード全てで完全にゼロ
mean_conf: pre=0.1379  F=0.0707  G=0.0229   # 確信度優先から離れるほど単調に低下
📋
なぜこうするか: (F)ランダム選択は「確信度を無視する」だけなので、偶然(D)と同じサンプルを選ぶこともある(30.7%)。一方(G)は「確信度が低い順」という(D)の「確信度が高い順」の正反対の並び順を使っているため、選ぶ件数が候補全体よりずっと少ない状況では、(D)と(G)はほぼ重ならない集合になる。この違いを事前に理解していないと、「ランダムにしたのにoverlapが結構高い、バグでは」と誤解しかねない。overlapの水準を選択ルールの設計から予測できるようにしておくことが、診断結果を正しく解釈する鍵になる。

2pseudo_accが史上最低なのにtest accが史上最高タイという逆転の意味

pseudo_acc_g = 0.5906   # (G)の疑似ラベル正確さ(ほぼコイントスに近い、全手法中最低)
acc_g        = 0.6800   # (G)のtest精度((E)と並び全手法中最高)
💡
なぜこうするか: Day153では「ラベルの正確さより選ぶサンプルの情報量が支配的」という発見があったが、今日の(G)はその関係が極端な形で表れた例である。決定境界に最も近いサンプルはラベルが誤っている確率も一番高いが、同時にモデルにとって「一番学びがいのある」サンプルでもある。今日の実測は、多少のラベルノイズを許容してでも情報量の多いサンプルを選ぶ設計の方が、疑似ラベルの精度にこだわった設計より最終的な汎化性能で勝ることがあることを、これまでで最も分かりやすい数値差で示している。

34日間の実験(Day151〜154)が最終的にたどり着いた設計指針

Day151: オラクルでも確信度ベースの選択バイアスは緩和されない
Day152: post-filterクラスタリング(対象を広げるだけ)でも緩和されない
Day153: pre-filterクラスタリング(対象を全体に広げるだけ)でも緩和されない
        → 原因診断: クラスタ内選択基準(確信度優先)が効果を打ち消していた
Day154: クラスタ内選択基準をランダム/Uncertaintyに変更 → 初めて明確な改善(+0.04〜+0.047)
        → (G)は(E)単純ランダム選択と同水準まで到達
🧭
なぜこうするか: 4日間の実験を振り返ると、「クラスタリングという手法を導入する」こと自体には意味がなく、「クラスタ内で何を基準に選ぶか」という一段細かい設計判断こそが結果を左右していたことが分かる。Day152・153では「クラスタリングをどこに適用するか(対象範囲)」ばかりを変えて足踏みしていたが、Day153の診断で「クラスタ内選択基準」というもう1つの変数に気づき、Day154でそれを変更して初めて改善に到達した。「効かなかった対策」を単に諦めるのではなく、診断指標を使って何が効果を打ち消しているかを1つずつ特定し、その変数を次に変えるという4日間の進め方自体が、今日の技術的な発見と同じくらい重要な教材である。

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

📝
「クラスタ内選択基準を変える」ことの意味を「面接の一次選考」のたとえの続きで理解する:

Day153では「各地域枠を作っても、各地域の中で結局は学歴が高い人から選ぶルールのままなら、結局同じ顔ぶれが選ばれてしまう」と説明した。今日はそのルール自体を変えた場合を考える。各地域枠の中で「ランダムに選ぶ」あるいは「あえて評価が難しい(学歴だけでは合否を予測しにくい)候補者を優先する」ようにすれば、これまで選ばれなかった層にも確実に選ばれる機会が生まれる、という直感である。しかも今日の実験結果は、「評価が難しい候補者ばかりを集めたグループ」(Uncertainty Sampling)の方が、単なる「地域内ランダム」よりも良い結果につながる可能性を示している——評価が難しい人ほど、実は自分たちがまだ知らない有用な情報を持っていることがある、というイメージである。
📊
「pseudo_accが低いのにtest accが高い」現象を「模試の点数と本番力」のたとえで理解する:

ある勉強法(G)は、模試(pseudo_acc)ではほとんど正解できない(ほぼ運任せ)難しい問題ばかり解いていたとする。一見「この勉強法は成果が出ていない」ように見えるが、実際にはその難しい問題こそが「本番でまだ習得できていない論点」を的確に突いており、間違えながらも本番の実力(test acc)は着実に伸びていた、というイメージである。目の前の正答率(pseudo_acc)と最終的な実力向上(test acc)は、必ずしも同じ方向に動くとは限らない、という直感である。

🏆 Kaggleでの実践的な使い方

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

原則

🔍 「対策が効かない」ときは変数を1つずつ切り分ける

4日間の流れ(対象範囲→内部基準)

1つの対策アイデアには複数のサブ変数が隠れていることが多い。「効果がなかった」で終わらせず、そのアイデアを構成する各サブ変数を1つずつ変えてボトルネックを特定する姿勢が有効。

実例

🎲 Uncertainty Samplingを疑似ラベル選択に応用する

Active Learning由来の発想

一般的な疑似ラベル選択は「確信度が高いサンプルを信頼する」設計が主流だが、決定境界付近の不確実なサンプルを積極的に選ぶ設計が有効な場面があることを今日の実測が示した。

実務

⚖️ overlap診断で「本当に違うことをしているか」を確認する

overlap_dg_frac=0が象徴する検証法

新しい選択ロジックを実装したとき、旧ロジックとの選択集合の重なりを計算しておくと、意図通りに違うサンプル集合になっているかをすぐ確認でき、実装ミスの早期発見にも役立つ。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
クラスタリングの対象範囲を変えれば十分だと思い込む(Day152・153の停滞)「クラスタリングを導入した」という変更に満足し、クラスタ内部の選び方まで意識が向かないクラスタリングは「グループ分け」でしかなく、各グループから何を基準に選ぶかというもう1段細かい設計判断まで変えないと効果が出ないことがある
pseudo_accが低い方法は使うべきでないと判断する「ラベルが不正確な学習データは害になる」という直感が根強い今日の(G)のように、pseudo_accが史上最低でもtest精度は史上最高タイになることがある。判断基準はpseudo_accではなく最終的なtest精度(または held-out CV)に置くべき
overlapが低ければ良い選択方式だと単純に評価する「重ならない=新しい情報を取り込めている」と直感的に結びつけたくなる今日のoverlap_dg_frac=0は「意図した設計が正しく機能している証拠」ではあるが、overlapの低さ自体がtest精度を保証するわけではない。(F)もoverlap中程度(30.7%)だが十分な改善を示しており、overlapは補助的な診断指標にとどめるべき
Uncertainty Samplingは常にランダム選択より優れると一般化する今日(G)が(F)よりわずかに良い結果(0.6800 vs 0.6727)を見て、常に優れる手法だと飛躍する5シード平均の差はわずか0.0073であり、シードごとのばらつき(seed=1では(F)0.5909<(G)0.6364だが、seed=3では(F)0.6636>(G)0.6182)を見ると、優劣が安定して逆転しない保証はない。1回の実験結果を過度に一般化しない

🚀 次のステップ

  • 発展: 今日の(F)(G)はいずれもクラスタのquota配分自体は「平均確信度が高いクラスタから余りを+1」というDay152由来のルールのままだった。この配分ルール自体も確信度非依存(均等配分・クラスタサイズ比例)に変えた場合、さらに改善するのか、あるいは今日の改善分が失われるのかは未検証
  • 次回予告: Day155では、Day154で発見した「クラスタ内Uncertainty Sampling」の効果が、疑似ラベルの反復適用(複数ラウンドにわたって自己学習を繰り返すIterative Pseudo Labeling、Day137の枠組み)と組み合わせた場合にも安定して持続するか、それとも初回ラウンドだけの効果なのかを検証する

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

1-23
Phase6主要テーマ6項目・総復習(Day126-149、完了)
発展①〜⑤
NLP系コンペへの応用(Day150: TTA逆効果/Day151-153: 選択バイアス対策が3日連続で不発/Day154: クラスタ内選択基準の変更で初の明確な改善)

Day151→154の4日間は、1つの原因(選択バイアス)に対して複数の対策(オラクル比較・post-filter・pre-filter・クラスタ内基準変更)を順番に試し、診断指標で失敗理由を特定しながら最終的に改善へたどり着いた一連のシリーズとして、Phase6のケーススタディに位置づけられる。

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

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