忍者ブログ
統計、機械学習、AIを学んでいきたいと思います。 お役に立てば幸いです。

【Kaggle挑戦記】Mechanisms of Action (MoA) Prediction:ベースラインモデルの構築と手元検証

前回、データの構造と役割を確認した「Mechanisms of Action (MoA) Prediction」コンペ。今回は、あえて高度な特徴量抽出などの加工は一切行わず、素のデータをそのままLightGBMに投入して手元での基準スコア(ベースライン)を算出しました。手元の環境である Python 3.13.5 で試してみた検証ログと、マルチラベル特有のスコアの読み方、そして実際に使用したコードを整理します。

0. 検証モデルの設計(5-Fold CV)

今回のターゲットは206種類に及ぶマルチラベル構造です。今回は最もシンプルかつ確実なアプローチとして、「206個のターゲットに対して、それぞれ独立したLightGBMの二値分類モデルを5分割交差検証(5-Fold CV)で合計1,030回ループさせて解く」という愚直なパイプラインを構築しました。

前処理としては、LightGBMがエラーを起こさないための最低限の置換(cp_doseの数値化)と、生物学的に正解がすべて「0」と決まっている偽薬データ(cp_type == 'ctl_vehicle')の除外のみを行っています。

1. 実装した交差検証コード

手元での検証(Cross Validation)を厳密に行うために作成したスクリプトの全文です。PandasとLightGBM、そしてscikit-learnを組み合わせてシンプルに記述しています。

import numpy as np
import pandas as pd
from lightgbm import LGBMClassifier
from sklearn.metrics import log_loss
from sklearn.model_selection import KFold
from sklearn.preprocessing import LabelEncoder
import warnings

# 不要な警告ログを非表示にする
warnings.filterwarnings("ignore")

# ==========================================
# 1. データの読み込み
# ==========================================
print("Loading data...")
train_features = pd.read_csv("train_features.csv")
train_targets = pd.read_csv("train_targets_scored.csv")

# ==========================================
# 2. 最低限の前処理(LightGBMが動くためだけ)
# ==========================================
print("Preprocessing...")

# cp_type が 'ctl_vehicle'(偽薬)のデータは、すべての正解が 0 と決まっています。
# ここは混ぜると学習のノイズになるため、純粋な薬のデータ(trt_cp)だけを抽出して学習させます。
train_mask = train_features["cp_type"] == "trt_cp"
X = train_features[train_mask].reset_index(drop=True)
y = train_targets[train_mask].reset_index(drop=True)

# IDと、不要になった cp_type カラムを削除
X = X.drop(columns=["sig_id", "cp_type"])
y = y.drop(columns=["sig_id"])

# 文字列のカテゴリ(cp_dose: D1/D2)を数値(0/1)に変換
le = LabelEncoder()
X["cp_dose"] = le.fit_transform(X["cp_dose"])

# ==========================================
# 3. 交差検証(5-Fold CV)のセットアップ
# ==========================================
kf = KFold(n_splits=5, shuffle=True, random_state=42)

# すべてのターゲット、すべての行の予測値を格納するゼロ行列を用意
oof_preds = np.zeros(y.shape)
target_cols = y.columns

print(f"Dataset shape: {X.shape}")
print(f"Number of targets: {len(target_cols)}")
print("Starting Cross Validation...")

# ==========================================
# 4. 206個のターゲットをループで1つずつ学習
# ==========================================
for idx, col in enumerate(target_cols):
    y_target = y[col]

    # そのターゲットに「1(効き目あり)」が1つもない特殊なケースの例外処理
    if y_target.sum() == 0:
        continue

    # 5分割のクロスバリデーションを実行
    for train_idx, val_idx in kf.split(X, y_target):
        X_train, y_train = X.iloc[train_idx], y_target.iloc[train_idx]
        X_val, y_val = X.iloc[val_idx], y_target.iloc[val_idx]

        # デフォルト設定のままのLightGBM(二値分類)
        model = LGBMClassifier(
            objective="binary",
            random_state=42,
            n_estimators=100,
            learning_rate=0.05,
            verbose=-1,
        )

        model.fit(X_train, y_train)

        # 「1」になる予測確率を対応する位置に格納
        oof_preds[val_idx, idx] = model.predict_proba(X_val)[:, 1]

    # 進行状況を20個ごとに表示
    if (idx + 1) % 20 == 0 or (idx + 1) == len(target_cols):
        print(f" Trained targets: {idx + 1}/{len(target_cols)}")

# ==========================================
# 5. 全体のスコア(Log Loss)を計算
# ==========================================
losses = []
for idx, col in enumerate(target_cols):
    loss = log_loss(y[col], oof_preds[:, idx], labels=[0, 1])
    losses.append(loss)

cv_score = np.mean(losses)
print("\n" + "=" * 30)
print(f"手元での交差検証スコア(CV Score): {cv_score:.5f}")
print("=" * 30)

2. 交差検証の実行ログ

手元の環境(Mac M3、Python 3.13.5)で上記のスクリプトを試してみました。

Loading data...
Preprocessing...
Dataset shape: (21948, 874)
Number of targets: 206
Starting Cross Validation...
 Trained targets: 20/206
 Trained targets: 40/206
 Trained targets: 60/206
 Trained targets: 80/206
 Trained targets: 100/206
 Trained targets: 120/206
 Trained targets: 140/206
 Trained targets: 160/206
 Trained targets: 180/206
 Trained targets: 200/206
 Trained targets: 206/206

==============================
手元での交差検証スコア(CV Score): 0.03933
==============================

3. スコア「0.03933」の正体と、log(対数)がもたらすペナルティ

一見すると「0.039」という数字は非常に低く、モデルが30%くらい予測を外している(正解率70%程度)ように錯覚してしまいます。しかし結論から言うと、予測のハズレ割合(件数)としてはすでに1%未満に抑え込まれています。これこそが本コンペの評価指標であるLog Loss(対数損失)の最大の特徴です。

Log Lossは予測の正誤だけでなく「モデルの自信度合い(確率)」を厳しく採点する指標で、数式にlog(対数)が含まれています。そのため、間違いが大きくなるほどペナルティが倍々ゲーム(指数関数的)に大炎上する仕組みになっています。

  • 「0(効き目なし)」のエリア: 正解の99%以上が0なので、モデルが「99%の確率で0」と安全な予測を出すだけで、ペナルティはほぼ0点になります。ここで大量の貯金を稼いでいます。
  • たまに登場する「1(効き目あり)」のエリア: ここでモデルが「うーん、自信がないから確率5%くらいで1かな…」と弱気に見逃すと、一発で3点〜4点という特大のペナルティ(罰点)を喰らいます。

この「たまにある1を見逃したときの特大ペナルティ」を、2.4万行 × 206個という膨大なデータの海(分母)で薄く平均化した結果、最終的に「0.03933」という極小の数字として残っているのがこのスコアの正体です。つまり、件数としてはほぼ当たっていますが、本物の「1」に対してモデルがまだ自信を持てずにモヤモヤしている状態と言えます。

初期基準(ベースライン):CV Score = 0.03933ここからの戦いは、件数を増やすのではなく、特徴量を使ってモデルに決定的な証拠を教え、「1」のときに自信満々で高い確率を出させる戦いになります。

4. 次なる一手

何一つ工夫をしていない「素のLightGBM」でここまでのベースラインを測定できたため、手元の検証環境(CV)としては100点満点の仕上がりです。ここからは、先述した「投与時間(cp_time)」や「投与量(cp_dose)」を軸にした特徴量生成や、遺伝子データ(g-)と細胞データ(c-)の統計量を組み合わせ、このスコアをどこまで削り落とせるかの本格的な実験フェーズに移行します。



PR

【Kaggle挑戦記】Mechanisms of Action (MoA) Prediction:データの取得と構造の確認

手書き文字認識の「Digit Recognizer」などを経て、今回は生物・創薬系のデータを扱った「Mechanisms of Action (MoA) Prediction」に挑戦しました。今回はコンペのアーカイブデータを活用し、Late Submissionによる順位確認を目指します。まずは特徴量抽出などの複雑な加工は一切行わず、生のデータをそのままLightGBMに投入する「ベースライン構築」に向けて、データの入手手順と構造を整理します。

0. このコンペティションの概要

「Mechanisms of Action (MoA) Prediction」は、新薬の候補化合物が人間の細胞(遺伝子や細胞株)に与える影響を分析し、その薬が「どのような効き目(作用機序=MoA)」を持っているかを予測するコンペティションです。データの主な仕様は以下の通りです。

  • データ規模: 訓練用の入力データが約2.4万行、テストデータが約4,000行弱。横方向には800を超える大量の特徴量カラムが並ぶ大規模な表形式データです。
  • 入力データ: 細胞の写真などの画像ではなく、100%数値化されたデータです。約770種類の遺伝子発現変化量(g-特徴量)と、100種類の細胞生存率(c-特徴量)、そして実験条件(時間や投与量)で構成されています。
  • タスク: 用意された206種類に及ぶ「効き目のチェックリスト」に対して、それぞれの効き目が「ある(1)」か「ない(0)」かをすべて同時に予測する「マルチラベル二値分類」です。評価指標は「Log Loss(交差エントロピー誤差)」の平均値が採用されています。

終了済みのコンペですが、テストデータ(test_features.csv)に対する予測ファイルを提出する「Late Submission」に対応しています。これにより、当時のリーダーボード(順位表)のどの位置に自分が食い込めていたかのスコアと暫定順位を正確に確認し、腕試しをすることができます。

1. データの入手

データの取得手順は以下の通りです。

  • Kaggleにログインします。
  • 検索窓で「Mechanisms of Action (MoA) Prediction」または「Kaggle MoA」を検索します。
  • コンペティションのページから「Data」タブを選択します。
  • 右上の「Download All」ボタンから、ZIP形式で一括ダウンロードします。
  • ダウンロードしたZIPファイルを適当なディレクトリで展開します。

展開すると、ディレクトリ内に役割の異なる6つのCSVファイルが生成されます。

2. 主要なCSVファイルの解説

展開して得られたCSVファイルの中から、初期のLightGBMベースライン構築において直接使用する重要なファイルを整理しました。カラム構造とそれぞれの役割は以下の通りです。

ファイル名 / カラム名解説
train_features.csv 学習用の入力データ(約2.4万行)。モデルの「入力(X)」となるファイルです。
├ sig_id 各実験サンプルの固有識別子(ID)
├ cp_type / cp_time / cp_dose 実験条件(薬か偽薬か、投与時間 24/48/72時間、投与量 高/低)
├ g-0 ~ g-771 772種類の遺伝子発現データ(すべて小数点付きの数値)
├ c-0 ~ c-99 100種類の細胞生存率データ(すべて小数点付きの数値)
train_targets_scored.csv 学習用の正解データ(教師データ)。モデルの「目標(Y)」となるファイルです。
└ 206種類の作用機序カラム 各薬の効き目の有無を示すバイナリフラグ(0 = なし、1 = あり)。1行の中に「1」が複数存在し得るマルチラベル構造です。
test_features.csv Kaggleに提出する予測値を計算するための、本番用入力データです(正解は含まれません)。

このコンペティションの最大のポイントは、入力データが「実験条件を表すカテゴリ変数」と「ひたすら横に並ぶ872個の純粋な数値」だけで美しく整理されている点にあります。面倒な文字列処理や画像の埋め込みが一切ないため、初期の数値データそのままでLightGBMへダイレクトに投入可能なデータ構造となっています。

予測タスク:実験条件と872個の数値データから、206種類のターゲットフラグ(0 または 1)を同時に予測するマルチラベル分類

あえて高度な特徴量抽出は行わず、この数値の塊をそのままLightGBMに学習させ、206個の各ターゲットに対してどのような境界線を描くことができるのか、まずはストレートな実力を検証します。なお、オマケデータである「train_drug.csv」やスコア対象外の「train_targets_nonscored.csv」は初手では一切使用せず、完全に無視して進めます。

3. 今後の流れ

必要な3つの基本CSVファイルが手元に綺麗に揃いました。まずは実験条件(cp_time や cp_dose など)をLightGBMが読める形式にカテゴリ数値化する最低限の前処理だけを行い、そのまま206回ループさせてLightGBMの二値分類モデルを回すベースラインパイプラインの構築に移ります。


【DS検定対策】時間が生むランダムの軌跡!「確率過程」の基礎

サイコロを振るような一瞬の確率ではなく、時間とともに数値がランダムに変化し続ける現象。時系列解析のベースとなる重要な概念が「確率過程」です。

1. 【 問題 】

統計学やデータサイエンスにおいて、時間の経過とともに変化する確率変数の列(またはその動的なメカニズム全体)のことを何と呼ぶでしょうか?

① 確率過程
② 決定論的過程
③ 主成分分析
④ 線形回帰分析


2. 【 解答 】

正解: ① 確率過程

3. 整理:確率過程の身近な具体例

確率過程は、私たちの身の回りのあらゆる「時間とともに変動するデータ」の裏側で動いています。

【 代表的な確率過程の例 】

・事例1:ランダムウォーク(酔歩)
コインを投げて、表が出たら右に1歩、裏が出たら左に1歩進む。これを時間とともに何度も繰り返したときの「現在地」の推移。株価の変動モデルなどの基礎にもなります。

・事例2:ポアソン過程
「Webサーバーに1分間にアクセスが何件来るか」「コールセンターに何本の電話がかかってくるか」といった、ランダムに発生するイベントの時間的な積み重なり。

4. 試験で狙われる最重要キーワード:「マルコフ性」と「定常性」

確率過程をデータ分析(時系列解析)で扱う際、試験でも非常によく問われる2つの超重要性質があります。

1. マルコフ性(マルコフ過程): 「未来の確率が、現在の状態だけで決まり、過去の経緯には依存しない」という性質。直前の状態だけをシステムが保持していれば良いというクリーンなモデルで、実務でも広く応用されます。
2. 定常性: 時間が経っても、データ全体の「平均や分散(ばらつきの幅)が一定で変わらない」という性質。時系列予測(ARIMAモデルなど)を行うための大前提となる、タフで安定した状態のことです。


5. DS検定形式:実戦4択クイズ

問:確率過程の中でも、「未来の予測を行うために過去のすべての履歴を遡る必要はなく、現在の状態のデータさえ分かれば次の状態の確率が完全に決定される」という性質を何と呼ぶか。

① 定常性   ② 独立性   ③ マルコフ性   ④ 頑健性(ロバスト性)

【 正解: ③ 】

解説: まさに「マルコフ性(Markov property)」の定義です。これを持つ確率過程をマルコフ過程と呼び、自然言語処理の形態素解析(形態素のつながりやすさ)や、強化学習(MDP)、さらにはMCMC法(マルコフ連鎖モンテカルロ法)といった高度なデータサイエンスのアルゴリズムの土台として頻出します。


6. まとめ

DS検定において「確率変数そのものが時間で変化していくプロセス」という記述が登場したら「確率過程」です。静的な統計データから、時間の流れる「時系列データ」の分析へとステップアップする際の入り口となる概念ですので、マルコフ性などの関連キーワードと結びつけて整理しておきましょう!

【Julia】Juliaでの条件分岐(if 〜 elseif 〜 else)の基本手順

プログラムの中で「もし○○ならAの処理、そうでなければBの処理」というように、条件に応じて動きを変えたいときに使うのが条件分岐です。今回はJuliaにおける条件分岐の基本文法と、正しい記述手順について解説します。

※本記事の手順は、MacBook Air(macOS 15.7.7)の環境にて実際に動作を確認したログをベースに作成しています。

1. 【 概要 】

Juliaの条件分岐は、`if` で始まり、最後は必ず `end` で締めくくるというルールがあります。また、複数の条件を重ねたい場合は、Pythonのような「elif」ではなく、`elseif` と一続きで記述するのがJuliaの文法的な特徴です。今回はこの構文の基本を整理します。


2. 【 基本手順 】

(1) `if 条件式` を書き、その条件が正しい(true)ときの処理を記述する
(2) 別の条件を追加したい場合は `elseif 条件式` を使って繋げる
(3) どの条件にも合わない場合の処理を `else` の下に記述する
(4) 構文の最後には必ず `end` を記述して、条件分岐の範囲を閉じる

3. 整理:各ステップの具体的な処理内容

Juliaの条件分岐を正しく書きこなすためのポイントを、ステップごとに具体的に見ていきましょう。

【 条件分岐構文の具体的な処理内容 】

・ステップ(1):if の記述
最初の条件を指定します。Juliaでは `if` のあとに括弧 `( )` を書く必要はありません。
条件式の後ろで改行し、条件が成立したときに行いたい処理を記述します。

・ステップ(2)&(3):elseif と else の追加
2つ目以降の条件がある場合は `elseif` を使います。ここを `elif` や `else if` とスペースを空けて書くと文法エラー(SyntaxError)になるため注意しましょう。
すべての条件に当てはまらなかった場合の受け皿として、最後に `else` を配置します。

・ステップ(4):end で閉じる
Juliaの構文において最重要とも言えるステップです。
`if` 構文の終わりには、必ず単独で `end` を記述して「ここまでがif文の範囲である」ことをコンピュータに伝えます。

4. 関連して押さえたい「Juliaの条件式における注意点(厳密な型チェック)」

他のプログラミング言語(C言語やJavaScriptなど)では、数値の `1` を「正しい(true)」、`0` を「間違い(false)」とみなして `if` 文に放り込めるものもあります。

しかし、Juliaは型に対して非常に厳密な言語です。`if` や `elseif` の後ろに続く条件式の結果は、必ず純粋な論理値である **`true`(真)か `false`(偽)** にならなければなりません。

例えば、`if 1` のように数値をそのまま条件式に書くとエラー(TypeError)になります。必ず `if x == 1` や `if score >= 80` のように、比較演算子を使って結果が `true` か `false` になる式を記述するようにしましょう。この厳密さが、予期せぬバグを防ぎ、Juliaの高速な実行速度を支える基盤になっています。


5. 補足:実際のターミナル操作ログ

対話型環境(REPL)を起動し、いくつか条件分岐のコードを実行した際の実際の操作ログです。
画面上でパッと見て構造が分かりやすいよう、短く改行を挟みながら入力しています。

% julia

# パターン1:最もシンプルな if 〜 end
julia> score = 85
85

julia> if score >= 80
           println("合格です!")
       end
合格です!

# パターン2:elseif と else を含んだ複数条件分岐
julia> temperature = 18
18

julia> if temperature >= 30
           println("暑いです")
       elseif temperature >= 15
           println("快適です")
       else
           println("寒いです")
       end
快適です

julia> exit()
%

6. まとめ

Juliaにおける条件分岐の基本手順をマスターしました。`if` から始まり `elseif` で条件を繋げ、最後に `end` で美しく閉じるという一連のルールは、このあとに学ぶループ処理(for文やwhile文)など、Juliaのあらゆる構文の土台となります。型チェックの厳密さに気をつけながら、状況に応じて賢く動くプログラムを組み立てていきましょう!


【DS検定対策】Kaggleでも大人気!最強の直列アルゴリズム「勾配ブースティング」

前のモデルが犯したミスを、次のモデルが引き継いで集中修正していく。この「リレー」のような仕組みで驚異的な予測精度を叩き出すのが、勾配ブースティングです。

1. 【 問題 】

アンサンブル学習の代表的な手法の一つであり、決定木などの弱い予測モデル(弱学習器)を一つずつ順番に(直列に)構築し、前のモデルが出した予測値と正解との「誤差(勾配)」を、次のモデルが修正するように学習を繰り返すことで、最終的に非常に高い予測精度を得る機械学習アルゴリズムを何と呼ぶでしょうか?

① 勾配ブースティング(Gradient Boosting)
② バギング(Bagging)
③ 主成分分析(PCA)
④ K-means法


2. 【 解答 】

正解: ① 勾配ブースティング(Gradient Boosting)

3. 整理:バギングとブースティングの決定的違い

試験で最も狙われるのは、先ほど学んだ「バギング」とのアーキテクチャ(構造)の違いです。ここを完全に区別しておきましょう。

手法名学習の進め方(アーキテクチャ)統計学的な狙い(強み)
バギング
(前回)
複数のモデルを「並列」に同時に作り、最後に全員の平均や多数決をとる。
(例:ランダムフォレスト)
モデルの過学習を抑え、予測の「ブレ(分散・バリアンス)」を下げるのが得意。
ブースティング
★今回の主役
モデルを「直列(順番)」に作り、前のモデルのミス(残差)を次のモデルが追いかける。 モデルの表現力を高め、予測の「的外れさ(バイアス)」を極限まで下げるのが得意。

4. 実務で使われる超有名ライブラリたち

「勾配ブースティング」というアルゴリズムは、実務やKaggleなどのコンペにおいて以下のような超強力な進化系ライブラリとして実装され、表形式データの予測で無双しています。

・XGBoost: 勾配ブースティングを一躍有名にした、高速・高精度な歴史的名作。
・LightGBM: Microsoftが開発。データのスキャン方法を工夫することで、計算速度とメモリ効率を劇的に進化させた現代の主役。


5. DS検定形式:実戦4択クイズ

問:勾配ブースティングは非常に強力なアルゴリズムですが、学習(イテレーション)を際限なく繰り返してしまうと、どのような問題(不具合)が発生しやすくなるか。最も適切なものを一つ選べ。

① アンダーフィッティング(学習不足)   ② 勾配消失問題   ③ 過学習(オーバーフィッティング)   ④ 多重共線性(マルチコ)

【 正解: ③ 】

解説: ブースティングはミスを執拗に追いかけて修正していくため、繰り返し回数を多くしすぎると、訓練データに含まれる細かいノイズまで完璧に合わせにいってしまい、過学習(オーバーフィッティング)を起こします。これを防ぐために、テスト誤差が下がらなくなった時点で学習を強制終了する「早期終了(Early Stopping)」というテクニックが実務では必須になります。


6. まとめ

DS検定において「前のモデルの誤差・勾配を後ろのモデルが順番に修正して強くなる」という記述が出たら「勾配ブースティング」です。バギング(並列)との違い、そして過学習を抑えるための周辺技術とセットで頭を整理しておきましょう!