Lecture 1機械学習とは何か — AIの全体像を理解する

12:00

機械学習とは何か — AIの全体像を理解する

AIブームの正体

「AI(人工知能)」という言葉を聞かない日はなくなりました。ChatGPTが2022年11月に公開されてからわずか2ヶ月で月間アクティブユーザー1億人を突破し、TikTokの9ヶ月、Instagramの2年半という記録を大幅に塗り替えました。

しかし「AI」「機械学習」「ディープラーニング」という3つの用語は、しばしば混同されます。正確に整理しましょう。

AI(人工知能) は最も広い概念です。「人間の知的活動をコンピュータで再現する」技術全般を指します。1956年にダートマス会議でジョン・マッカーシーが命名しました。ルールベースの専門家システム(if-then規則の塊)もAIの一種です。

機械学習(Machine Learning) はAIのサブセットです。「明示的にプログラムしなくても、データから自動的にパターンを学習する」技術です。1959年にアーサー・サミュエルが「機械にチェスを学習させる」研究で提唱しました。

ディープラーニング(Deep Learning) は機械学習のさらにサブセットです。多層のニューラルネットワークを使った手法で、2012年のImageNetコンペティションでアレックス・クリジェフスキーのAlexNetが圧勝したことで実用性が証明されました。

AI(人工知能)
  └── 機械学習(ML)
        ├── 教師あり学習
        ├── 教師なし学習
        ├── 強化学習
        └── ディープラーニング(DL)
              ├── CNN(画像認識)
              ├── RNN/Transformer(自然言語)
              └── GAN(画像生成)

機械学習の3つのアプローチ

教師あり学習(Supervised Learning)

「正解ラベル付きデータ」から学習する方法です。たとえば、1万枚の犬猫画像に「犬」「猫」のラベルを付けたデータセットを与え、新しい画像が犬か猫かを予測させます。

具体例: - 不動産価格予測:面積・築年数・駅距離 → 価格(回帰) - メールのスパム判定:件名・本文の特徴 → スパム/非スパム(分類) - 医療画像診断:X線画像 → 正常/異常(分類)

Googleによると、企業の機械学習プロジェクトの約70%が教師あり学習です。

教師なし学習(Unsupervised Learning)

ラベルなしデータから「隠れた構造」を発見する方法です。

具体例: - 顧客セグメンテーション:購買データからグループを自動発見 - 異常検知:ネットワークトラフィックの異常パターンを検出 - 次元削減:高次元データの可視化(t-SNE, PCA)

強化学習(Reinforcement Learning)

エージェントが「試行錯誤」で最適な行動を学習する方法です。DeepMindのAlphaGoが2016年に世界チャンピオン李世ドルに勝利したのは強化学習の成果です。

機械学習で何ができるのか — 実世界の応用

機械学習は「研究室のおもちゃ」ではありません。すでに日常のあらゆるところで使われています。

分野 応用例 使われている技術
医療 皮膚がんの画像診断(スタンフォード大学、2017年) CNN
金融 クレジットカードの不正検知 Random Forest, XGBoost
小売 Amazonの商品レコメンド 協調フィルタリング
自動運転 Teslaの自動操縦 CNN + 強化学習
翻訳 Google翻訳、DeepL Transformer
音楽 Spotifyの週間プレイリスト 協調フィルタリング + NLP

マッキンゼーの2023年レポートによると、生成AI(機械学習の一分野)だけで年間2.6兆〜4.4兆ドルの経済価値を生み出す可能性があるとされています。

このコースで学ぶこと

全10回の講義で、以下を段階的に習得します。

講義 テーマ 身につくスキル
第1講 機械学習とは何か(本講義) AI/ML/DLの全体像の理解
第2講 Python環境とライブラリ NumPy, pandas, matplotlib
第3講 データの前処理 欠損値処理、正規化、特徴量設計
第4講 線形回帰 最初の予測モデルの構築
第5講 分類アルゴリズム ロジスティック回帰、決定木
第6講 モデル評価と改善 交差検証、過学習対策
第7講 アンサンブル学習 Random Forest, XGBoost
第8講 教師なし学習 K-means, PCA
第9講 ニューラルネットワーク入門 基本構造とPyTorch
第10講 総合プロジェクト Kaggleデータセットで実践

前提知識: Python入門コースを修了していること(変数、関数、リスト、for文が書けるレベル)。数学は高校レベルの四則演算と簡単なグラフの読み方ができれば十分です。数式は登場しますが、Pythonコードと併記するので「数式が読めなくてもコードで理解できる」構成にしています。

機械学習エンジニアのキャリア

Indeed Japanの2024年調査で、機械学習エンジニアの平均年収は約750万円(日本)、アメリカでは約15万ドル(約2,200万円)です。経済産業省は2030年にAI人材が約12.4万人不足すると試算しています。

ただし「機械学習エンジニア」になるには、このコースだけでは足りません。統計学、線形代数、そして大量の実践経験が必要です。このコースはあくまで「最初の一歩」 — しかし、最も重要な一歩です。

演習:AIを日常で見つける

あなたのスマートフォンを開いて、以下のアプリを確認してください:

  1. 写真アプリ — 顔認識で人物ごとに自動分類されていませんか?(教師あり学習・CNN)
  2. メールアプリ — 迷惑メールフォルダに自動で振り分けられていませんか?(教師あり学習・分類)
  3. 音楽アプリ — 「あなたへのおすすめ」プレイリストはありませんか?(教師なし学習・協調フィルタリング)
  4. 地図アプリ — 到着予測時刻は?(回帰モデル)

これらはすべて機械学習です。次回は、これらの技術を自分の手で実装するための Python環境を整えます。

参考文献

  • Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Springer. 機械学習の定番教科書。
  • scikit-learn公式ドキュメント: https://scikit-learn.org/stable/user_guide.html
  • Google Machine Learning Crash Course: https://developers.google.com/machine-learning/crash-course

Lecture 2Python環境とライブラリ — 機械学習の道具箱を揃える

13:00

Python環境とライブラリ — 機械学習の道具箱を揃える

なぜPythonが機械学習の標準なのか

機械学習のプログラミング言語としてPythonが圧倒的に選ばれている理由は、言語そのものの特性よりも「エコシステムの充実度」にあります。KaggleのState of Data Science 2023調査で、回答者の92%がPythonを使用していると報告しています。

R言語も統計分析には強力ですが、Webアプリケーションとの統合やプロダクション環境への展開ではPythonに軍配が上がります。JuliaやScalaも高性能ですが、ライブラリの数と学習リソースの豊富さでPythonには及びません。

本講義では、機械学習に必要な4つの主要ライブラリを実際にインストールし、動かしてみます。

環境構築:Google Colabを使う

ローカル環境の構築でつまずいて挫折する人は少なくありません。本コースでは Google Colaboratory(Colab) を使います。ブラウザだけで使えるPython実行環境で、GPU/TPUも無料で利用できます。

Colabの始め方: 1. https://colab.research.google.com にアクセス 2. Googleアカウントでログイン 3. 「ノートブックを新規作成」をクリック 4. セルにPythonコードを書いて Shift+Enter で実行

機械学習の主要ライブラリはすべてプリインストール済みです。

NumPy — 数値計算の基盤

NumPy(Numerical Python)は、高速な数値計算を提供するライブラリです。機械学習のデータは最終的にすべて「数値の配列(テンソル)」として処理されるため、NumPyはあらゆるML ライブラリの土台になっています。

import numpy as np

# ベクトル(1次元配列)
prices = np.array([3500, 4200, 2800, 5100, 3900])
print(f"平均価格: {prices.mean():.0f}円")    # 3900円
print(f"標準偏差: {prices.std():.0f}円")     # 743円

# 行列(2次元配列)— 機械学習ではデータをこの形で扱う
# 行 = サンプル(各物件)、列 = 特徴量(面積, 築年数, 駅距離)
data = np.array([
    [65, 10, 5],    # 物件A: 65㎡, 築10年, 駅5分
    [80, 3, 8],     # 物件B: 80㎡, 築3年, 駅8分
    [45, 25, 3],    # 物件C: 45㎡, 築25年, 駅3分
])
print(f"データの形状: {data.shape}")  # (3, 3) = 3サンプル × 3特徴量

Pythonのリストとの違い: NumPy配列はC言語で実装されており、要素がメモリ上に連続して配置されます。100万要素の合計計算で、Pythonリストの約100倍高速です。

# ブロードキャスティング — 形状の異なる配列同士の演算
data_normalized = (data - data.mean(axis=0)) / data.std(axis=0)
print(data_normalized)
# これだけで全特徴量が「平均0、標準偏差1」に標準化される

pandas — データ操作の万能ナイフ

pandasは「表形式データ」を扱うためのライブラリです。ExcelやCSVのデータをPythonで直感的に操作できます。Wes McKinneyが2008年に金融データ分析のために開発しました。

import pandas as pd

# CSVファイルの読み込み(1行で完了)
df = pd.read_csv("housing.csv")

# データの概要を確認
print(df.shape)        # (行数, 列数)
print(df.head())       # 最初の5行を表示
print(df.describe())   # 統計量(平均, 中央値, 標準偏差など)
print(df.info())       # 各列のデータ型と欠損値の数

# 特定の条件でフィルタリング
expensive = df[df["price"] > 5000]
tokyo = df[df["city"] == "東京"]

# グループ集計
city_avg = df.groupby("city")["price"].mean().sort_values(ascending=False)

DataFrameとNumPy配列の関係: DataFrameは内部でNumPy配列を使っています。df.values で元のNumPy配列にアクセスでき、scikit-learnに渡す際に使います。

matplotlib — データの可視化

「データを見る」ことは機械学習の最も重要なステップです。matplotlib は2003年にジョン・ハンターが MATLAB の可視化機能をPythonで再現するために作りました。

import matplotlib.pyplot as plt

# 散布図 — 2つの変数の関係を見る
plt.figure(figsize=(8, 5))
plt.scatter(df["area"], df["price"], alpha=0.5, c="steelblue")
plt.xlabel("面積(㎡)")
plt.ylabel("価格(万円)")
plt.title("面積と価格の関係")
plt.grid(True, alpha=0.3)
plt.show()

散布図は機械学習の出発点です。「面積が大きいほど価格が高い」という直感的な傾向が見えれば、線形回帰モデルで予測できる可能性があります。

# ヒストグラム — データの分布を見る
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for i, col in enumerate(["area", "age", "price"]):
    axes[i].hist(df[col], bins=30, color="steelblue", edgecolor="white")
    axes[i].set_title(col)
plt.tight_layout()
plt.show()

seaborn はmatplotlibの上に構築された統計可視化ライブラリで、より美しいグラフを少ないコードで描けます。ヒートマップや相関行列の可視化に便利です。

import seaborn as sns

# 相関行列のヒートマップ — 変数間の関係を一目で把握
plt.figure(figsize=(8, 6))
sns.heatmap(df.corr(), annot=True, cmap="coolwarm", center=0)
plt.title("特徴量の相関行列")
plt.show()

scikit-learn — 機械学習の本体

scikit-learn(sklearn)は、機械学習アルゴリズムを統一的なAPIで提供するライブラリです。2007年にGoogleのサマーオブコードで開発が始まりました。

scikit-learnの最大の特徴は 一貫したAPI設計 です。どのアルゴリズムでも同じ3ステップで使えます:

from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split

# Step 1: データを訓練用とテスト用に分割
X = df[["area", "age", "distance"]]  # 特徴量
y = df["price"]                       # 予測対象
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

# Step 2: モデルの学習(fit)
model = LinearRegression()
model.fit(X_train, y_train)

# Step 3: 予測(predict)
predictions = model.predict(X_test)

全アルゴリズムがこの fit → predict パターンに従います。線形回帰をRandom Forestに変えたければ、LinearRegression()RandomForestRegressor() に書き換えるだけです。

ライブラリの全体像

あなたのPythonコード
    ↓ データ読み込み
  pandas(CSV, Excel → DataFrame)
    ↓ 可視化・探索
  matplotlib / seaborn(グラフ描画)
    ↓ 数値計算
  NumPy(配列演算、線形代数)
    ↓ 機械学習
  scikit-learn(前処理、モデル、評価)
    ↓ ディープラーニング(第9講で紹介)
  PyTorch / TensorFlow

演習:最初のデータ分析

Google Colabで新しいノートブックを作成し、以下を実行してください:

# scikit-learn付属のデータセットで練習
from sklearn.datasets import load_iris
import pandas as pd

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df["species"] = iris.target

print(df.head())
print(df.describe())
print(df.groupby("species").mean())

アヤメ(Iris)データセットは、1936年にロナルド・フィッシャーが発表した統計学の古典的なデータです。150サンプル、4特徴量、3クラスで構成され、機械学習の「Hello, World」として80年以上使われ続けています。

次回は、実データの「汚れ」を処理する データの前処理 を学びます。

参考文献

  • NumPy公式チュートリアル: https://numpy.org/doc/stable/user/quickstart.html
  • pandas公式ドキュメント: https://pandas.pydata.org/docs/getting_started/
  • scikit-learn公式チュートリアル: https://scikit-learn.org/stable/tutorial/

Lecture 3データの前処理 — 生データを学習可能な形にする

14:00

データの前処理 — 生データを学習可能な形にする

なぜ前処理が最も重要なのか

データサイエンティストの業務時間の約80%はデータの前処理に費やされる — これは2016年のCrowdFlower(現Appen)調査で報告された有名な統計です。モデルの精度は「どのアルゴリズムを使うか」よりも「データをどう準備するか」で決まることが多いのです。

「Garbage In, Garbage Out(ゴミを入れればゴミが出る)」という格言は、機械学習において文字通り真実です。欠損値、外れ値、不均一なスケール — これらを放置すれば、どんな高度なモデルも正しい予測はできません。

実データの「汚れ」を知る

実務で扱うデータには、ほぼ必ず以下の問題があります:

問題 具体例 影響
欠損値 年収欄が空白 モデルがエラー or 精度低下
外れ値 年齢が999歳 平均や分散が歪む
スケールの違い 面積(50-200)と築年数(0-50) 距離ベースのアルゴリズムが偏る
カテゴリ変数 「東京」「大阪」「福岡」 数値でないとモデルに入力できない
データの不均衡 正常99%、異常1% 少数クラスを無視するモデルになる

欠損値の処理

import pandas as pd
import numpy as np

# サンプルデータ(欠損値を含む)
df = pd.DataFrame({
    "area": [65, 80, np.nan, 45, 90],
    "age": [10, np.nan, 15, 25, 3],
    "price": [3500, 4200, 3100, np.nan, 5100]
})

# 欠損値の確認
print(df.isnull().sum())
# area     1
# age      1
# price    1

欠損値の処理には大きく3つのアプローチがあります:

1. 削除 — 欠損がある行をまるごと捨てる

df_dropped = df.dropna()
# 簡単だがデータが減る。欠損が全体の5%以下なら有効

2. 平均値・中央値で補完 — 最も一般的

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy="median")  # 中央値で補完
df_filled = pd.DataFrame(
    imputer.fit_transform(df),
    columns=df.columns
)

中央値(median)が推奨される理由は、外れ値の影響を受けにくいからです。年収データに「100億円」が1件あると平均値は大きく歪みますが、中央値は安定します。

3. 予測モデルで補完 — 高度だが効果的

KNNImputerは、似たデータポイントの値から欠損を推定します。

from sklearn.impute import KNNImputer

knn_imputer = KNNImputer(n_neighbors=5)
df_knn = pd.DataFrame(
    knn_imputer.fit_transform(df),
    columns=df.columns
)

特徴量のスケーリング

面積(50〜200㎡)と築年数(0〜50年)のように、特徴量のスケールが大きく異なると、距離ベースのアルゴリズム(KNN、SVM)が正しく機能しません。スケールの大きい特徴量だけに引っ張られてしまうためです。

標準化(Standardization) — 平均0、標準偏差1に変換

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 各特徴量が平均0、標準偏差1になる

正規化(Min-Max Scaling) — 0〜1の範囲に変換

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
X_normalized = scaler.fit_transform(X)
# 各特徴量が0〜1の範囲になる
方法 数式 使い所
標準化 (x - μ) / σ 外れ値がある場合、線形モデル全般
正規化 (x - min) / (max - min) ニューラルネットワーク、画像データ

重要: fit_transform は訓練データに対してのみ実行します。テストデータには transform のみを使います。テストデータでfitすると「未来の情報を使った学習(データリーケージ)」になり、評価結果が不正に高くなります。

# 正しい手順
scaler.fit(X_train)              # 訓練データで統計量を計算
X_train_scaled = scaler.transform(X_train)   # 訓練データを変換
X_test_scaled = scaler.transform(X_test)     # テストデータは同じ統計量で変換

カテゴリ変数のエンコーディング

「東京」「大阪」「福岡」のような文字列は、そのままでは機械学習モデルに入力できません。数値に変換する必要があります。

ラベルエンコーディング — 整数に変換

from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
df["city_encoded"] = le.fit_transform(df["city"])
# 東京→2, 大阪→0, 福岡→1(アルファベット順)

注意: ラベルエンコーディングは「大阪(0) < 福岡(1) < 東京(2)」という順序関係を暗示してしまいます。決定木系以外のモデルでは、ワンホットエンコーディングを使うべきです。

ワンホットエンコーディング — ダミー変数に変換

df_encoded = pd.get_dummies(df, columns=["city"])
# city_大阪, city_東京, city_福岡 の3列が追加される(0 or 1)

特徴量エンジニアリング

既存の特徴量を組み合わせて「新しい特徴量」を作ることで、モデルの精度が劇的に向上することがあります。

# 例:不動産データ
df["price_per_sqm"] = df["price"] / df["area"]      # ㎡単価
df["is_new"] = (df["age"] <= 5).astype(int)          # 築5年以内フラグ
df["walk_minutes"] = df["distance"] / 80             # 駅距離→徒歩分数

Kaggleの上位入賞者は、モデルの選択よりも特徴量エンジニアリングに時間をかけることが多いと言われています。あるKaggleグランドマスターは「データの70%は特徴量エンジニアリングで決まる」と語っています。

前処理パイプラインの構築

scikit-learnの Pipeline を使えば、前処理のステップをまとめて管理できます:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LinearRegression

pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
    ("model", LinearRegression()),
])

# 3ステップを一度に実行
pipe.fit(X_train, y_train)
predictions = pipe.predict(X_test)

Pipelineの利点は、前処理とモデルを一体として扱えることです。交差検証やハイパーパラメータ探索でも、データリーケージを防げます。

演習:Titanicデータの前処理

Kaggleの定番データセット「Titanic」で前処理を実践してください:

# Kaggle Titanicデータの読み込み
url = "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
df = pd.read_csv(url)

# 課題:
# 1. df.isnull().sum() で欠損値を確認
# 2. Age列をmedianで補完
# 3. Sex列をワンホットエンコーディング
# 4. Fare列を標準化

次回は、準備したデータを使って最初の予測モデル — 線形回帰 — を構築します。

参考文献

  • scikit-learn前処理ガイド: https://scikit-learn.org/stable/modules/preprocessing.html
  • Kaggle Learn - Data Cleaning: https://www.kaggle.com/learn/data-cleaning
  • Géron, A. (2022). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 3rd ed. O'Reilly.

Lecture 4線形回帰 — 最初の予測モデルを構築する

14:00

線形回帰 — 最初の予測モデルを構築する

予測とは何か

「面積80㎡の物件の価格はいくらか?」 — この問いに答えるのが 回帰(Regression) です。連続的な数値を予測するタスクで、機械学習の中で最も基本的な手法です。

線形回帰は1805年にアドリアン=マリ・ルジャンドルが最小二乗法として定式化し、200年以上経った今でも実務で最初に試されるモデルです。その理由は「解釈可能性」にあります。なぜその予測結果になったのかを、人間が理解できるのです。

単回帰 — 1つの特徴量で予測する

「面積 → 価格」のように、1つの入力から1つの出力を予測するのが単回帰です。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# データ作成(面積 → 価格)
np.random.seed(42)
area = np.random.uniform(30, 120, 50)
price = 40 * area + 500 + np.random.normal(0, 300, 50)

X = area.reshape(-1, 1)  # scikit-learnは2次元配列を要求
y = price

# モデルの学習
model = LinearRegression()
model.fit(X, y)

print(f"傾き(係数): {model.coef_[0]:.1f}万円/㎡")
print(f"切片: {model.intercept_:.1f}万円")
# 傾き: 40.3万円/㎡ → 面積が1㎡増えるごとに価格が約40万円上がる

出力される式は y = 40.3x + 487.2 です。この数式の意味が明確なのが線形回帰の強みです。「面積1㎡あたり約40万円」という知見は、ビジネスにそのまま活用できます。

# 予測
new_area = np.array([[80]])
predicted_price = model.predict(new_area)
print(f"80㎡の予測価格: {predicted_price[0]:.0f}万円")

# 可視化
plt.scatter(X, y, alpha=0.5, label="実データ")
plt.plot(X, model.predict(X), color="red", label="回帰直線")
plt.xlabel("面積(㎡)")
plt.ylabel("価格(万円)")
plt.legend()
plt.show()

重回帰 — 複数の特徴量で予測する

現実の不動産価格は面積だけでは決まりません。築年数、駅距離、階数なども影響します。複数の特徴量を使うのが 重回帰 です。

import pandas as pd
from sklearn.model_selection import train_test_split

# 複数の特徴量を持つデータ
df = pd.DataFrame({
    "area": np.random.uniform(30, 120, 200),
    "age": np.random.uniform(0, 40, 200),
    "distance": np.random.uniform(1, 20, 200),
})
df["price"] = 40 * df["area"] - 30 * df["age"] - 50 * df["distance"] + 1000 + np.random.normal(0, 200, 200)

# 特徴量と目的変数
X = df[["area", "age", "distance"]]
y = df["price"]

# 訓練/テスト分割(80:20)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 学習と予測
model = LinearRegression()
model.fit(X_train, y_train)

# 各特徴量の係数を確認
for name, coef in zip(X.columns, model.coef_):
    print(f"  {name}: {coef:+.1f}")
# area: +39.8  → 面積が1㎡増えると約40万円アップ
# age: -29.7   → 築1年古くなると約30万円ダウン
# distance: -49.5 → 駅1分遠くなると約50万円ダウン

係数の符号(プラスかマイナスか)と大きさが、各特徴量の「影響度と方向性」を直感的に教えてくれます。

評価指標 — モデルの良さを数値化する

「モデルがどれくらい正確か」を測る指標が必要です。回帰タスクでは主に3つの指標を使います。

指標 数式の意味 特徴
MAE(平均絶対誤差) 予測と実際の差の平均 外れ値に強い。解釈が直感的
RMSE(二乗平均平方根誤差) 誤差の二乗平均の平方根 大きな誤差にペナルティ
R²(決定係数) 1に近いほど良い(0〜1) モデルの説明力を表す
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score

y_pred = model.predict(X_test)

mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
r2 = r2_score(y_test, y_pred)

print(f"MAE:  {mae:.0f}万円(平均で{mae:.0f}万円ズレる)")
print(f"RMSE: {rmse:.0f}万円")
print(f"R²:   {r2:.3f}({r2*100:.1f}%の変動を説明できる)")

R²が0.90なら「データの変動の90%をモデルが説明できている」という意味です。不動産価格予測で0.85以上あれば実用レベルと言われます。

過学習と正則化

モデルが訓練データに「過剰に適合」して、未知データへの予測精度が落ちる現象を 過学習(Overfitting) と呼びます。

訓練データのスコア: 0.99(非常に高い)
テストデータのスコア: 0.65(大幅に低い)
 過学習の兆候

線形回帰で過学習を防ぐ手法が 正則化 です。

Ridge回帰(L2正則化) — 係数を小さく抑える

from sklearn.linear_model import Ridge

ridge = Ridge(alpha=1.0)  # alphaが大きいほど強い正則化
ridge.fit(X_train, y_train)

Lasso回帰(L1正則化) — 不要な特徴量の係数を0にする

from sklearn.linear_model import Lasso

lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)

# 係数が0になった特徴量は「不要」と判断された
for name, coef in zip(X.columns, lasso.coef_):
    print(f"  {name}: {coef:+.2f}" + (" ← 除外" if abs(coef) < 0.01 else ""))

Lasso回帰は「特徴量選択」の機能を兼ねるため、特徴量が多い場合に特に有用です。

線形回帰の限界

線形回帰は強力ですが、万能ではありません:

  • 非線形の関係(面積と価格の関係が曲線的)には対応できない
  • 外れ値に弱い(異常な価格の物件1件で直線が大きく歪む)
  • 特徴量間の相関が高い(多重共線性)と係数が不安定になる

これらの限界を超えるために、次回以降で決定木やアンサンブル学習を学びます。

演習:Boston Housing風データで予測

from sklearn.datasets import fetch_california_housing

housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = housing.target

# 課題:
# 1. train_test_splitで8:2に分割
# 2. LinearRegressionで学習
# 3. MAE, RMSE, R²を計算
# 4. Ridge(alpha=1.0)で同じ評価を行い、結果を比較

California Housingデータセットは20,640件の住宅データです。目的変数は住宅価格の中央値(10万ドル単位)で、実践的な回帰問題の練習に最適です。

参考文献

  • scikit-learn Linear Models: https://scikit-learn.org/stable/modules/linear_model.html
  • James, G. et al. (2021). An Introduction to Statistical Learning 2nd ed. Springer. 第3章。
  • Kaggle Learn - Intro to Machine Learning: https://www.kaggle.com/learn/intro-to-machine-learning

Lecture 5分類アルゴリズム — データをカテゴリに振り分ける

14:00

分類アルゴリズム — データをカテゴリに振り分ける

回帰と分類の違い

前回学んだ回帰は「連続値の予測」でした。今回の分類は「カテゴリの予測」です。

タスク 出力
回帰 連続値(数値) 価格予測、気温予測
分類 離散値(クラス) スパム判定、画像認識

「この患者は糖尿病か否か」「このメールはスパムか正常か」 — これらは二値分類(Binary Classification)です。「この画像は犬・猫・鳥のどれか」 — これは多クラス分類(Multi-class Classification)です。

ロジスティック回帰 — 名前に反して分類モデル

ロジスティック回帰は「回帰」と名前がついていますが、実際は分類アルゴリズムです。線形回帰の出力を シグモイド関数 で0〜1の確率に変換します。

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report

# 乳がんデータセット(569サンプル、30特徴量、2クラス)
data = load_breast_cancer()
X = data.data
y = data.target  # 0=悪性, 1=良性

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 学習と予測
model = LogisticRegression(max_iter=10000)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

print(f"正解率: {accuracy_score(y_test, y_pred):.3f}")

シグモイド関数 は任意の実数を0〜1の範囲に押し込みます。出力が0.5以上なら「クラス1(良性)」、0.5未満なら「クラス0(悪性)」と判定します。

# 確率として出力を得る
probabilities = model.predict_proba(X_test)
print(f"最初のサンプルの確率: 悪性 {probabilities[0][0]:.3f}, 良性 {probabilities[0][1]:.3f}")

決定木 — 人間が理解できるモデル

決定木(Decision Tree)は、if-then規則の連鎖でデータを分類するモデルです。1986年にロス・キンランがC4.5アルゴリズムを発表して以来、解釈可能性の高さから広く使われています。

from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt

# Irisデータセットで分類
from sklearn.datasets import load_iris
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

tree = DecisionTreeClassifier(max_depth=3, random_state=42)
tree.fit(X_train, y_train)

print(f"正解率: {accuracy_score(y_test, tree.predict(X_test)):.3f}")

# 決定木の可視化
plt.figure(figsize=(15, 8))
plot_tree(tree, feature_names=iris.feature_names,
          class_names=iris.target_names, filled=True, rounded=True)
plt.show()

決定木のメリットは解釈可能性です。「花びらの長さが2.45cm以下ならsetosa」のような人間が読めるルールが生成されます。医療診断や金融の審査など、判断根拠の説明が求められる分野で重宝されます。

決定木の過学習問題

制約なしの決定木は、訓練データを完璧に分類するまで枝分かれします。これは過学習です。

# 過学習する決定木
tree_overfit = DecisionTreeClassifier(max_depth=None)  # 深さ制限なし
tree_overfit.fit(X_train, y_train)
print(f"訓練データ正解率: {tree_overfit.score(X_train, y_train):.3f}")  # 1.000
print(f"テストデータ正解率: {tree_overfit.score(X_test, y_test):.3f}")  # 低い

# 過学習を防ぐ
tree_pruned = DecisionTreeClassifier(max_depth=4, min_samples_leaf=5)
tree_pruned.fit(X_train, y_train)
print(f"制限あり テスト正解率: {tree_pruned.score(X_test, y_test):.3f}")  # 改善

max_depth(木の深さ上限)と min_samples_leaf(末端ノードの最小サンプル数)で過学習を制御します。

K近傍法(KNN)— 最もシンプルな分類器

KNN(K-Nearest Neighbors)は「近くにあるK個のデータポイントの多数決で分類する」アルゴリズムです。学習フェーズがなく、データをそのまま保持するだけの怠惰学習(Lazy Learning)です。

from sklearn.neighbors import KNeighborsClassifier

knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
print(f"KNN正解率: {knn.score(X_test, y_test):.3f}")

KNNの弱点は「次元の呪い」です。特徴量が増えるほど「近い」の意味が薄れ、性能が劣化します。また、全データを保持するためメモリ消費が大きく、予測も遅くなります。

サポートベクターマシン(SVM)

SVM(Support Vector Machine)は、クラス間の「マージン(余白)」を最大化する境界線を見つけるアルゴリズムです。1990年代にVladimir Vapnikが理論を確立しました。

from sklearn.svm import SVC

svm = SVC(kernel="rbf", C=1.0)
svm.fit(X_train, y_train)
print(f"SVM正解率: {svm.score(X_test, y_test):.3f}")

kernel="rbf" はカーネルトリックと呼ばれる手法で、非線形の境界も学習できます。線形分離できないデータを、高次元空間に写像して分離可能にします。

分類の評価指標

分類タスクでは「正解率」だけでは不十分です。

from sklearn.metrics import classification_report, confusion_matrix

y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred, target_names=data.target_names))
指標 意味 重要な場面
正解率(Accuracy) 全体の正解割合 クラスが均等な場合
適合率(Precision) 「陽性」と予測したうち本当に陽性の割合 スパム検出(誤検知を避けたい)
再現率(Recall) 本当に陽性のうち正しく検出した割合 がん診断(見逃しを避けたい)
F1スコア 適合率と再現率の調和平均 バランスが必要な場合

混同行列(Confusion Matrix) はこれらの関係を可視化します:

import seaborn as sns

cm = confusion_matrix(y_test, y_pred)
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues",
            xticklabels=data.target_names, yticklabels=data.target_names)
plt.xlabel("予測")
plt.ylabel("実際")
plt.show()

アルゴリズムの比較

from sklearn.model_selection import cross_val_score

models = {
    "Logistic": LogisticRegression(max_iter=10000),
    "Decision Tree": DecisionTreeClassifier(max_depth=5),
    "KNN": KNeighborsClassifier(n_neighbors=5),
    "SVM": SVC(kernel="rbf"),
}

for name, m in models.items():
    scores = cross_val_score(m, X, y, cv=5, scoring="accuracy")
    print(f"{name:15s}: {scores.mean():.3f} ± {scores.std():.3f}")

演習:タイタニック生存予測

import pandas as pd
url = "https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
df = pd.read_csv(url)

# 課題:
# 1. 特徴量: Pclass, Sex, Age, Fare を使用
# 2. 前処理: 欠損値補完、ワンホットエンコーディング
# 3. ロジスティック回帰と決定木で分類
# 4. classification_reportで比較

次回は、モデルの信頼性を高める モデル評価と改善 の手法を学びます。

参考文献

  • scikit-learn Classification: https://scikit-learn.org/stable/supervised_learning.html
  • James, G. et al. (2021). An Introduction to Statistical Learning 2nd ed. 第4章。
  • Google ML Crash Course - Classification: https://developers.google.com/machine-learning/crash-course/classification

Lecture 6モデル評価と改善 — 信頼できるモデルを作る技術

14:00

モデル評価と改善 — 信頼できるモデルを作る技術

なぜ「正解率95%」を信用してはいけないのか

あなたが作ったモデルが「テストデータで正解率95%」を出したとします。しかし、そのテストデータが訓練データと似すぎていたら? テストデータが偶然簡単なサンプルばかりだったら? 1回の評価だけでは、モデルの真の実力はわかりません。

この講義では、モデルを「正しく」評価し、「確実に」改善するための技術を学びます。

交差検証(Cross-Validation)

ホールドアウト法の限界

前回までの train_test_split はホールドアウト法と呼ばれます。データを1回だけ分割するため、分割の仕方によって評価結果が大きく変わる問題があります。

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.metrics import accuracy_score

data = load_breast_cancer()
X, y = data.data, data.target

# random_stateを変えるだけで結果が変わる
for seed in [0, 1, 2, 42, 100]:
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=seed)
    model = LogisticRegression(max_iter=10000)
    model.fit(X_train, y_train)
    score = accuracy_score(y_test, model.predict(X_test))
    print(f"seed={seed:3d}: {score:.3f}")
# 0.939 ~ 0.974 — 同じモデルなのに3%以上のばらつき

K分割交差検証

データをK個のグループ(フォールド)に分割し、各フォールドを順番にテストデータとして使います。K回の評価の平均が、モデルの推定性能です。

from sklearn.model_selection import cross_val_score

model = LogisticRegression(max_iter=10000)
scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")

print(f"各フォールドの正解率: {scores}")
print(f"平均: {scores.mean():.3f} ± {scores.std():.3f}")
# 平均: 0.953 ± 0.012 — ±の小ささが「安定した性能」を示す

K=5が標準的です。K=10はより安定しますが計算コストが2倍になります。データが少ない場合(数百件以下)はK=10が推奨されます。

層化K分割交差検証

クラス比率を各フォールドで維持する StratifiedKFold は、不均衡データで特に重要です。cross_val_score はデフォルトで分類タスクに層化を適用します。

from sklearn.model_selection import StratifiedKFold

skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=skf, scoring="accuracy")
print(f"層化CV: {scores.mean():.3f} ± {scores.std():.3f}")

過学習 vs 未学習

モデルのエラーは2種類に分けられます:

状態 訓練スコア テストスコア 原因
未学習(Underfitting) 低い 低い モデルが単純すぎる
適切 高い 高い(訓練に近い) バランスが取れている
過学習(Overfitting) 非常に高い 低い モデルが複雑すぎる
from sklearn.tree import DecisionTreeClassifier
import numpy as np

# 過学習の可視化
train_scores = []
test_scores = []
depths = range(1, 20)

for d in depths:
    tree = DecisionTreeClassifier(max_depth=d, random_state=42)
    # 訓練スコア
    tree.fit(X_train, y_train)
    train_scores.append(tree.score(X_train, y_train))
    # CVスコア
    cv = cross_val_score(tree, X, y, cv=5).mean()
    test_scores.append(cv)

import matplotlib.pyplot as plt

plt.plot(depths, train_scores, "o-", label="訓練スコア")
plt.plot(depths, test_scores, "s-", label="CVスコア")
plt.xlabel("木の深さ(max_depth)")
plt.ylabel("正解率")
plt.legend()
plt.title("学習曲線 — 過学習の検出")
plt.show()

グラフで「訓練スコアは上がり続けるが、CVスコアは途中から横ばいor低下する」点が見えたら、それが過学習の始まりです。

バイアスとバリアンス

過学習と未学習の背後にある統計的概念が バイアス-バリアンストレードオフ です。

  • バイアス(Bias):モデルの仮定が強すぎて、データの本質的なパターンを捉えられないエラー。未学習の原因。
  • バリアンス(Variance):データの小さな変動に過敏に反応するエラー。過学習の原因。

線形回帰は「高バイアス・低バリアンス」、深い決定木は「低バイアス・高バリアンス」です。良いモデルは両者のバランスが取れています。

ハイパーパラメータチューニング

モデルの「設定値」(ハイパーパラメータ)を最適化する手法です。決定木の max_depth やSVMの C がハイパーパラメータです。

グリッドサーチ

指定した候補値の全組み合わせを試す方法です。

from sklearn.model_selection import GridSearchCV
from sklearn.svm import SVC

param_grid = {
    "C": [0.1, 1, 10, 100],
    "kernel": ["rbf", "linear"],
    "gamma": ["scale", "auto"],
}

grid = GridSearchCV(SVC(), param_grid, cv=5, scoring="accuracy", n_jobs=-1)
grid.fit(X, y)

print(f"最良パラメータ: {grid.best_params_}")
print(f"最良スコア: {grid.best_score_:.3f}")

n_jobs=-1 で全CPUコアを使って並列探索します。候補が多い場合は計算コストが爆発する点に注意してください。

ランダムサーチ

候補空間からランダムにサンプリングする方法です。探索空間が広い場合、グリッドサーチより効率的です。

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint

param_dist = {
    "C": uniform(0.1, 100),
    "kernel": ["rbf", "linear"],
    "gamma": uniform(0.001, 1),
}

random_search = RandomizedSearchCV(
    SVC(), param_dist, n_iter=50, cv=5, scoring="accuracy",
    random_state=42, n_jobs=-1
)
random_search.fit(X, y)

print(f"最良パラメータ: {random_search.best_params_}")
print(f"最良スコア: {random_search.best_score_:.3f}")

Bergstra & Bengio (2012) の研究で、ランダムサーチは60回の試行でグリッドサーチと同等以上の結果を得られることが示されています。

学習曲線 — データ量の効果を可視化

from sklearn.model_selection import learning_curve

train_sizes, train_scores, test_scores = learning_curve(
    LogisticRegression(max_iter=10000), X, y,
    train_sizes=np.linspace(0.1, 1.0, 10), cv=5
)

plt.plot(train_sizes, train_scores.mean(axis=1), "o-", label="訓練スコア")
plt.plot(train_sizes, test_scores.mean(axis=1), "s-", label="CVスコア")
plt.xlabel("訓練データ数")
plt.ylabel("正解率")
plt.legend()
plt.title("学習曲線 — データ量 vs 性能")
plt.show()

学習曲線から「データを増やせば性能が上がるか」がわかります。両曲線が収束していれば、データ追加よりモデル変更が有効です。

検証戦略の選び方

データの特性 推奨する検証方法
十分なデータ量(1万件以上) ホールドアウト(train/val/test 3分割)
中程度(1,000〜10,000件) 5分割交差検証
少量(数百件以下) 10分割交差検証 or Leave-One-Out
時系列データ TimeSeriesSplit(未来のデータで検証)
不均衡データ 層化K分割 + F1/AUCで評価
from sklearn.model_selection import TimeSeriesSplit

# 時系列データの場合(未来でテスト)
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, test_idx in tscv.split(X):
    print(f"Train: {train_idx[0]}-{train_idx[-1]}, Test: {test_idx[0]}-{test_idx[-1]}")

演習:乳がんデータで最適モデルを探す

from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

data = load_breast_cancer()
X, y = data.data, data.target

# 課題:
# 1. Pipeline(StandardScaler + SVC)を構築
# 2. GridSearchCVでC=[0.1,1,10], kernel=["rbf","linear"]を探索
# 3. 最良パラメータと5分割CVスコアを報告
# 4. 学習曲線を描画し、データ追加の効果を判断

次回は、単一モデルの限界を超える アンサンブル学習 を学びます。

参考文献

  • scikit-learn Cross-validation: https://scikit-learn.org/stable/modules/cross_validation.html
  • Bergstra, J. & Bengio, Y. (2012). Random Search for Hyper-Parameter Optimization. JMLR, 13, 281-305.
  • Hastie, T. et al. (2009). The Elements of Statistical Learning 2nd ed. Springer. 第7章。

Lecture 7アンサンブル学習 — 複数モデルの知恵を結集する

14:00

アンサンブル学習 — 複数モデルの知恵を結集する

「三人寄れば文殊の知恵」

1つのモデルで完璧な予測は困難です。しかし、複数のモデルの予測を組み合わせると、個々のモデルを超える精度が得られます。これが アンサンブル学習(Ensemble Learning) です。

Kaggleの上位解法のほぼ全てがアンサンブル手法を使っています。Netflixが2009年に開催した100万ドルのコンペティション(Netflix Prize)でも、優勝チームの決め手はアンサンブルでした。

バギング — ランダムにサンプリングして多様性を確保

バギング(Bootstrap Aggregating)は、Leo Breimanが1996年に提案した手法です。訓練データからランダムに復元抽出(ブートストラップ)して複数のデータセットを作り、それぞれで独立にモデルを学習します。

from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score

data = load_breast_cancer()
X, y = data.data, data.target

# 単一の決定木
tree = DecisionTreeClassifier(random_state=42)
print(f"単一決定木: {cross_val_score(tree, X, y, cv=5).mean():.3f}")

# バギング(100本の決定木の多数決)
bagging = BaggingClassifier(
    estimator=DecisionTreeClassifier(),
    n_estimators=100, random_state=42, n_jobs=-1
)
print(f"バギング:    {cross_val_score(bagging, X, y, cv=5).mean():.3f}")

バギングが有効な理由は「分散の低減」です。個々の決定木は過学習しやすい(高バリアンス)ですが、100本の平均を取ることでバリアンスが劇的に下がります。

Random Forest — バギングの進化形

Random Forest はバギングに「特徴量のランダム選択」を加えた手法です。各ノードの分岐で全特徴量ではなく、ランダムに選んだサブセットだけを候補にします。

from sklearn.ensemble import RandomForestClassifier

rf = RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42, n_jobs=-1)
rf.fit(X, y)

scores = cross_val_score(rf, X, y, cv=5)
print(f"Random Forest: {scores.mean():.3f} ± {scores.std():.3f}")

特徴量重要度

Random Forestの大きな利点は、各特徴量がどれだけ予測に貢献しているかを定量化できることです。

import pandas as pd
import matplotlib.pyplot as plt

importances = pd.Series(rf.feature_importances_, index=data.feature_names)
top10 = importances.nlargest(10)

top10.plot(kind="barh", color="steelblue")
plt.xlabel("重要度")
plt.title("Random Forest — 特徴量重要度 Top10")
plt.tight_layout()
plt.show()

特徴量重要度は「どの変数が予測に効いているか」を説明する際に非常に有用です。ビジネスの意思決定者に「このモデルは主に○○と△△を見て判断しています」と説明できます。

Random Forestのハイパーパラメータ

パラメータ 意味 推奨範囲
n_estimators 木の本数 100〜500(多いほど安定)
max_depth 木の深さ上限 5〜20 or None
min_samples_leaf 末端の最小サンプル数 1〜10
max_features 各分岐の候補特徴量数 "sqrt"(分類), "log2"
from sklearn.model_selection import GridSearchCV

param_grid = {
    "n_estimators": [100, 200],
    "max_depth": [5, 10, None],
    "min_samples_leaf": [1, 3, 5],
}

grid = GridSearchCV(RandomForestClassifier(random_state=42),
                    param_grid, cv=5, scoring="accuracy", n_jobs=-1)
grid.fit(X, y)
print(f"最良パラメータ: {grid.best_params_}")
print(f"最良スコア: {grid.best_score_:.3f}")

ブースティング — 弱いモデルを順番に強化する

バギングが「独立に学習して平均」するのに対し、ブースティングは「前のモデルの間違いを次のモデルが修正する」逐次的なアプローチです。

勾配ブースティング

from sklearn.ensemble import GradientBoostingClassifier

gb = GradientBoostingClassifier(
    n_estimators=200, learning_rate=0.1, max_depth=3, random_state=42
)
scores = cross_val_score(gb, X, y, cv=5)
print(f"Gradient Boosting: {scores.mean():.3f} ± {scores.std():.3f}")

learning_rate(学習率)は各ステップの修正量を制御します。小さいほど慎重に学習し、過学習しにくくなりますが、n_estimators を増やす必要があります。

XGBoost — 産業標準のブースティング

XGBoost(eXtreme Gradient Boosting)は、Tianqi Chenが2014年に発表したライブラリです。Kaggleで圧倒的な実績を持ち、表形式データのコンペティションでは「とりあえずXGBoost」と言われるほど定番です。

from xgboost import XGBClassifier

xgb = XGBClassifier(
    n_estimators=200, learning_rate=0.1, max_depth=4,
    use_label_encoder=False, eval_metric="logloss",
    random_state=42, n_jobs=-1
)
scores = cross_val_score(xgb, X, y, cv=5)
print(f"XGBoost: {scores.mean():.3f} ± {scores.std():.3f}")

XGBoostが高速な理由は、二次のテイラー展開による近似と、欠損値の自動処理、そしてカラム単位の並列化です。

LightGBM — さらに高速なブースティング

Microsoftが2017年に発表したLightGBMは、ヒストグラムベースのアルゴリズムで大規模データに強いです。

from lightgbm import LGBMClassifier

lgbm = LGBMClassifier(n_estimators=200, learning_rate=0.1, random_state=42, n_jobs=-1)
scores = cross_val_score(lgbm, X, y, cv=5)
print(f"LightGBM: {scores.mean():.3f} ± {scores.std():.3f}")

データが10万行を超える場合、LightGBMはXGBoostの数倍〜数十倍高速に学習できます。

バギング vs ブースティング

特性 バギング(Random Forest) ブースティング(XGBoost)
学習方法 並列・独立 逐次・依存
主な効果 バリアンス低減 バイアス低減
過学習リスク 低い 高い(要チューニング)
ハイパーパラメータ感度 低い(デフォルトで良い) 高い(チューニング必須)
計算速度 並列化が容易 逐次のため遅い
推奨する場面 最初のベースライン 精度を極限まで追求

スタッキング — 異種モデルの組み合わせ

異なるアルゴリズムの予測結果を「メタモデル」で統合する手法です。

from sklearn.ensemble import StackingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC

estimators = [
    ("rf", RandomForestClassifier(n_estimators=100, random_state=42)),
    ("xgb", XGBClassifier(n_estimators=100, use_label_encoder=False,
                          eval_metric="logloss", random_state=42)),
    ("svm", SVC(kernel="rbf", probability=True)),
]

stacking = StackingClassifier(
    estimators=estimators,
    final_estimator=LogisticRegression(max_iter=10000),
    cv=5
)
scores = cross_val_score(stacking, X, y, cv=5)
print(f"Stacking: {scores.mean():.3f} ± {scores.std():.3f}")

演習:アルゴリズム対決

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score

data = load_breast_cancer()
X, y = data.data, data.target

# 課題:
# 1. 以下のモデルを5分割CVで比較
#    - LogisticRegression, DecisionTree, RandomForest, XGBoost, LightGBM
# 2. 特徴量重要度Top5をRandomForestとXGBoostで比較
# 3. RandomForestでGridSearchCVを実行し最良パラメータを報告

次回は、ラベルなしデータから構造を発見する 教師なし学習 を学びます。

参考文献

  • Breiman, L. (2001). Random Forests. Machine Learning, 45(1), 5-32.
  • Chen, T. & Guestrin, C. (2016). XGBoost: A Scalable Tree Boosting System. KDD 2016.
  • scikit-learn Ensemble Methods: https://scikit-learn.org/stable/modules/ensemble.html

Lecture 8教師なし学習 — ラベルなしデータから構造を発見する

14:00

教師なし学習 — ラベルなしデータから構造を発見する

正解がないデータから何を学べるか

教師あり学習では「正解ラベル」が必要でした。しかし現実のデータの大半にはラベルがありません。Eコマースの購買履歴、Webサイトのアクセスログ、遺伝子発現データ — これらに「正解」は存在しません。

教師なし学習は、データの中に隠れた 構造・パターン・グループ を自動で発見する技術です。主に2つのタスクに分かれます:

タスク 目的 代表手法
クラスタリング 類似データをグループ化 K-means, DBSCAN
次元削減 高次元データを可視化・圧縮 PCA, t-SNE

K-means クラスタリング

K-meansは最も広く使われるクラスタリングアルゴリズムです。1957年にStuart Lloydが考案し、1982年に論文として発表されました。

アルゴリズム: 1. K個の初期中心点をランダムに配置 2. 各データ点を最も近い中心点に割り当て(クラスタ割り当て) 3. 各クラスタの平均位置に中心点を移動 4. 2-3を収束するまで繰り返す

from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
import numpy as np

# 人工データ(3つのクラスタ)
X, y_true = make_blobs(n_samples=300, centers=3, cluster_std=1.0, random_state=42)

# K-meansでクラスタリング
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
y_pred = kmeans.fit_predict(X)

# 可視化
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap="viridis", alpha=0.6)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
            c="red", marker="X", s=200, label="中心点")
plt.legend()
plt.title("K-means クラスタリング結果")
plt.show()

エルボー法 — 最適なK(クラスタ数)を決める

K-meansの弱点は「クラスタ数Kを事前に指定する必要がある」ことです。エルボー法は、Kを変えながらクラスタ内のばらつき(イナーシャ)を計算し、減少が緩やかになる「肘」の位置を最適なKとする手法です。

inertias = []
K_range = range(1, 10)

for k in K_range:
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    km.fit(X)
    inertias.append(km.inertia_)

plt.plot(K_range, inertias, "o-")
plt.xlabel("クラスタ数 K")
plt.ylabel("イナーシャ(クラスタ内二乗和)")
plt.title("エルボー法 — 最適なKの決定")
plt.show()

シルエットスコア

各データ点が「自分のクラスタにどれだけ適合しているか」を-1〜1で定量化します。1に近いほどクラスタリングが良好です。

from sklearn.metrics import silhouette_score

for k in range(2, 7):
    km = KMeans(n_clusters=k, random_state=42, n_init=10)
    labels = km.fit_predict(X)
    score = silhouette_score(X, labels)
    print(f"K={k}: シルエットスコア = {score:.3f}")

DBSCAN — 密度ベースのクラスタリング

K-meansは球状のクラスタしか検出できません。DBSCANは「密度の高い領域」をクラスタとして検出するため、任意の形状のクラスタを発見できます。

from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons

# 三日月形のデータ(K-meansでは分離不可能)
X_moons, _ = make_moons(n_samples=300, noise=0.05, random_state=42)

# DBSCAN
db = DBSCAN(eps=0.2, min_samples=5)
labels = db.fit_predict(X_moons)

plt.scatter(X_moons[:, 0], X_moons[:, 1], c=labels, cmap="viridis")
plt.title("DBSCAN — 非球状クラスタの検出")
plt.show()
print(f"検出クラスタ数: {len(set(labels)) - (1 if -1 in labels else 0)}")
print(f"ノイズ点数: {(labels == -1).sum()}")

eps は「近傍」の半径、min_samples は「密度が高い」と判断する最小点数です。DBSCANの利点はクラスタ数を事前に指定する必要がないことと、外れ値(ノイズ)を自動検出することです。

PCA — 主成分分析

高次元データを少ない次元に圧縮する手法です。1901年にカール・ピアソンが考案しました。「データの分散が最大になる方向」を順番に見つけ、その方向に射影します。

from sklearn.decomposition import PCA
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler

data = load_breast_cancer()
X = StandardScaler().fit_transform(data.data)  # 30次元

# 30次元 → 2次元に圧縮
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X)

plt.scatter(X_pca[:, 0], X_pca[:, 1], c=data.target, cmap="coolwarm", alpha=0.6)
plt.xlabel(f"PC1 ({pca.explained_variance_ratio_[0]:.1%})")
plt.ylabel(f"PC2 ({pca.explained_variance_ratio_[1]:.1%})")
plt.title("PCA — 乳がんデータを2次元に圧縮")
plt.colorbar(label="0=悪性, 1=良性")
plt.show()

累積寄与率 — 何次元に圧縮すべきか

pca_full = PCA().fit(X)
cumsum = np.cumsum(pca_full.explained_variance_ratio_)

plt.plot(range(1, len(cumsum) + 1), cumsum, "o-")
plt.axhline(y=0.95, color="red", linestyle="--", label="95%ライン")
plt.xlabel("主成分の数")
plt.ylabel("累積寄与率")
plt.legend()
plt.title("累積寄与率 — 必要な次元数の決定")
plt.show()

n_95 = np.argmax(cumsum >= 0.95) + 1
print(f"95%の情報を保持するには {n_95} 次元(元は {X.shape[1]} 次元)")

「データの95%の情報を保持する最小次元数」が実務での基準です。30次元が10次元に圧縮できれば、学習速度は大幅に向上します。

t-SNE — 高次元データの可視化

t-SNE(t-distributed Stochastic Neighbor Embedding)は、Laurens van der Maatenが2008年に発表した可視化特化の次元削減手法です。PCAと異なり、非線形な構造を保持できます。

from sklearn.manifold import TSNE

tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X)

plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=data.target, cmap="coolwarm", alpha=0.6)
plt.title("t-SNE — 非線形な次元削減")
plt.colorbar(label="0=悪性, 1=良性")
plt.show()

t-SNEの注意点: 可視化専用の手法です。新しいデータに対して transform できない(fit_transform のみ)。また、perplexity の値によって結果が大きく変わるため、複数の値で試すことが推奨されます。

実践:顧客セグメンテーション

from sklearn.datasets import make_blobs

# 模擬顧客データ(購買金額 × 購買頻度)
np.random.seed(42)
customers = np.vstack([
    np.random.normal([100, 2], [30, 1], (100, 2)),    # 低額・低頻度
    np.random.normal([300, 8], [50, 2], (80, 2)),      # 中額・高頻度
    np.random.normal([500, 3], [40, 1], (50, 2)),      # 高額・低頻度
])

# 標準化 → K-means
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(customers)

kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
segments = kmeans.fit_predict(X_scaled)

plt.scatter(customers[:, 0], customers[:, 1], c=segments, cmap="viridis")
plt.xlabel("購買金額(月額)")
plt.ylabel("購買頻度(回/月)")
plt.title("顧客セグメンテーション")
plt.show()

マーケティングでは、このようなセグメンテーション結果をもとに「高額顧客には特別オファー」「低頻度顧客にはリマインドメール」といった施策を設計します。

演習:Irisデータで教師なし学習

from sklearn.datasets import load_iris

iris = load_iris()
X = iris.data  # ラベル(iris.target)は使わない

# 課題:
# 1. K-meansでK=3にクラスタリングし、真のラベルと比較
# 2. エルボー法で最適なKを検証
# 3. PCAで2次元に圧縮し、クラスタを可視化
# 4. t-SNEでも可視化し、PCAの結果と比較

次回は、機械学習の最先端 — ニューラルネットワーク の基礎を学びます。

参考文献

  • scikit-learn Clustering: https://scikit-learn.org/stable/modules/clustering.html
  • van der Maaten, L. & Hinton, G. (2008). Visualizing Data using t-SNE. JMLR, 9, 2579-2605.
  • Hastie, T. et al. (2009). The Elements of Statistical Learning 2nd ed. 第14章。

Lecture 9ニューラルネットワーク入門 — ディープラーニングの扉を開く

15:00

ニューラルネットワーク入門 — ディープラーニングの扉を開く

なぜニューラルネットワークが必要なのか

Random ForestやXGBoostは表形式データでは強力ですが、画像・音声・テキストといった非構造化データは苦手です。2012年、AlexNetがImageNetコンペティションで従来手法の誤り率26%を一気に15%に下げました。これがディープラーニング革命の始まりです。

現在、ChatGPT(Transformer)、画像生成AI(拡散モデル)、自動運転(CNN)はすべてニューラルネットワークがベースです。この講義では、その基礎を「手で触れる」レベルで理解します。

パーセプトロン — 最小のニューラルネットワーク

1958年にフランク・ローゼンブラットが考案したパーセプトロンは、ニューラルネットワークの原点です。

入力: x1, x2, x3
  
重み付き和: z = w1*x1 + w2*x2 + w3*x3 + b
  
活性化関数: y = f(z)
  
出力: 0 or 1

これは前回までの「ロジスティック回帰」とほぼ同じ構造です。ニューラルネットワークは、このパーセプトロンを多数並べて多層にしたものです。

import numpy as np

# パーセプトロンを手動実装
def perceptron(x, w, b):
    z = np.dot(x, w) + b
    return 1 if z > 0 else 0

# ANDゲート
w = np.array([0.5, 0.5])
b = -0.7
print(f"AND(0,0)={perceptron([0,0], w, b)}")  # 0
print(f"AND(1,0)={perceptron([1,0], w, b)}")  # 0
print(f"AND(0,1)={perceptron([0,1], w, b)}")  # 0
print(f"AND(1,1)={perceptron([1,1], w, b)}")  # 1

多層パーセプトロン(MLP)

パーセプトロンは線形分離しかできません。XOR問題(排他的論理和)は解けないことが1969年にマービン・ミンスキーによって証明され、ニューラルネットワーク研究は約15年の冬の時代に入りました。

解決策は「層を重ねる」ことでした。隠れ層(Hidden Layer) を追加することで、非線形な問題も解けるようになります。

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

data = load_breast_cancer()
X, y = data.data, data.target

# MLPClassifier(隠れ層2つ: 64ニューロン → 32ニューロン)
mlp_pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("mlp", MLPClassifier(hidden_layer_sizes=(64, 32), max_iter=500,
                          random_state=42)),
])

scores = cross_val_score(mlp_pipe, X, y, cv=5)
print(f"MLP: {scores.mean():.3f} ± {scores.std():.3f}")

重要: ニューラルネットワークは入力のスケーリングに敏感です。StandardScaler で前処理しないと、学習が極端に遅くなったり収束しなかったりします。

活性化関数 — 非線形性の源

層を重ねても、活性化関数が線形なら全体としては線形モデルに過ぎません。非線形の活性化関数こそが、ニューラルネットワークに「表現力」を与えます。

活性化関数 特徴
シグモイド 1/(1+e^(-x)) 出力0〜1。勾配消失問題あり
tanh (e^x-e^(-x))/(e^x+e^(-x)) 出力-1〜1。シグモイドより良い
ReLU max(0, x) 現在の標準。計算が高速
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 100)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
axes[0].plot(x, 1 / (1 + np.exp(-x)))
axes[0].set_title("Sigmoid")
axes[1].plot(x, np.tanh(x))
axes[1].set_title("tanh")
axes[2].plot(x, np.maximum(0, x))
axes[2].set_title("ReLU")
for ax in axes:
    ax.grid(True, alpha=0.3)
    ax.axhline(y=0, color="k", linewidth=0.5)
    ax.axvline(x=0, color="k", linewidth=0.5)
plt.tight_layout()
plt.show()

ReLU(Rectified Linear Unit)は2010年にNairとHintonが導入し、ディープラーニングの学習を劇的に高速化しました。現在のほとんどのネットワークでデフォルトの活性化関数です。

PyTorchで手書き数字を分類する

PyTorchはFacebook AI Research(現Meta AI)が2016年に公開した深層学習フレームワークです。研究者の約70%が使用しています(Papers With Code, 2024)。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# MNISTデータセット(手書き数字 0-9、28x28ピクセル)
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_data = datasets.MNIST("./data", train=True, download=True, transform=transform)
test_data = datasets.MNIST("./data", train=False, transform=transform)

train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
test_loader = DataLoader(test_data, batch_size=1000)

モデル定義

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.layers = nn.Sequential(
            nn.Linear(28 * 28, 128),    # 入力: 784ピクセル → 128ニューロン
            nn.ReLU(),
            nn.Dropout(0.2),             # 過学習防止
            nn.Linear(128, 64),          # 128 → 64
            nn.ReLU(),
            nn.Linear(64, 10),           # 64 → 10クラス(0-9)
        )

    def forward(self, x):
        x = self.flatten(x)
        return self.layers(x)

model = SimpleNet()
print(model)
# パラメータ数を確認
total = sum(p.numel() for p in model.parameters())
print(f"総パラメータ数: {total:,}")  # 約109,386

学習ループ

criterion = nn.CrossEntropyLoss()  # 多クラス分類の損失関数
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 学習
for epoch in range(5):
    model.train()
    total_loss = 0
    for batch_x, batch_y in train_loader:
        optimizer.zero_grad()        # 勾配リセット
        output = model(batch_x)      # 順伝播
        loss = criterion(output, batch_y)  # 損失計算
        loss.backward()              # 逆伝播(勾配計算)
        optimizer.step()             # パラメータ更新
        total_loss += loss.item()
    print(f"Epoch {epoch+1}: Loss = {total_loss/len(train_loader):.4f}")

評価

model.eval()
correct = 0
total = 0
with torch.no_grad():  # 推論時は勾配計算不要
    for batch_x, batch_y in test_loader:
        output = model(batch_x)
        predicted = output.argmax(dim=1)
        correct += (predicted == batch_y).sum().item()
        total += batch_y.size(0)

print(f"テスト正解率: {correct/total:.3f}")
# 約 0.975 — 3層のシンプルなネットワークで97.5%

逆伝播法 — ニューラルネットワークの学習原理

学習の核心は 逆伝播法(Backpropagation) です。1986年にRumelhart, Hinton, Williamsが発表しました。

  1. 順伝播: 入力 → 各層の計算 → 出力 → 損失を計算
  2. 逆伝播: 損失 → 連鎖律で各重みの勾配を計算
  3. 更新: 勾配の方向にパラメータを少し動かす(勾配降下法)

PyTorchでは loss.backward() の1行で逆伝播が自動計算されます。内部では計算グラフ(Autograd)が勾配を自動追跡しています。

scikit-learn vs PyTorch — 使い分け

観点 scikit-learn PyTorch
対象 表形式データ 画像・音声・テキスト
モデル 伝統的ML(RF, SVM等) ニューラルネットワーク
GPU 不要 必須(大規模モデル)
カスタマイズ 限定的 完全に自由
学習コスト 低い 高い

実務のルール: まずscikit-learn(Random Forest, XGBoost)で試す。それで精度が不足するか、非構造化データを扱う場合にPyTorchへ移行する。

演習:MNISTの精度を上げる

# 課題:
# 1. 上のSimpleNetをGoogle Colabで実行(GPUランタイムを選択)
# 2. 隠れ層を(256, 128, 64)に変更して精度を比較
# 3. Dropout率を0.5に変更して過学習への影響を確認
# 4. エポック数を10に増やして学習曲線を描画

次回の最終講義では、ここまで学んだ全技術を統合して Kaggleデータセットの実践プロジェクト に取り組みます。

参考文献

  • PyTorch公式チュートリアル: https://pytorch.org/tutorials/
  • Goodfellow, I. et al. (2016). Deep Learning. MIT Press. https://www.deeplearningbook.org/
  • LeCun, Y. et al. (1998). Gradient-based learning applied to document recognition. Proc. IEEE, 86(11).

Lecture 10総合プロジェクト — Kaggleデータで実践する機械学習

15:00

総合プロジェクト — Kaggleデータで実践する機械学習

学んだことを統合する

9回の講義で、前処理からニューラルネットワークまで一通りの技術を学びました。この最終講義では、Kaggleの実データを使って 探索的データ分析(EDA)→ 前処理 → モデリング → 評価 → 改善 のフルサイクルを実践します。

使用するデータセットは Spaceship Titanic — 2912年の宇宙船タイタニック号で、乗客が異次元に転送されたかどうかを予測する分類問題です。Kaggleの入門コンペティションとして人気があり、約8,700件の訓練データ、13の特徴量を持ちます。

Step 1: データの読み込みと概観

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Kaggle Spaceship Titanic(CSVを直接読み込み)
train = pd.read_csv("https://raw.githubusercontent.com/datasciencedojo/datasets/master/spaceship-titanic/train.csv")

print(f"データ形状: {train.shape}")
print(f"\n各列の型:\n{train.dtypes}")
print(f"\n欠損値:\n{train.isnull().sum()}")
print(f"\n先頭5行:")
train.head()

Step 2: 探索的データ分析(EDA)

モデルを作る前に、データを「見る」ことが最も重要なステップです。

# 目的変数の分布
print(f"転送された: {train['Transported'].sum()}")
print(f"転送されなかった: {(~train['Transported']).sum()}")
# ほぼ均等 → 不均衡問題ではない

# 数値特徴量の分布
numeric_cols = ["Age", "RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck"]
fig, axes = plt.subplots(2, 3, figsize=(15, 8))
for i, col in enumerate(numeric_cols):
    ax = axes[i // 3, i % 3]
    train[col].hist(bins=30, ax=ax, color="steelblue", edgecolor="white")
    ax.set_title(col)
plt.tight_layout()
plt.show()
# カテゴリ特徴量と目的変数の関係
cat_cols = ["HomePlanet", "CryoSleep", "Destination", "VIP"]
fig, axes = plt.subplots(1, 4, figsize=(18, 4))
for i, col in enumerate(cat_cols):
    pd.crosstab(train[col], train["Transported"], normalize="index").plot(
        kind="bar", ax=axes[i], stacked=True
    )
    axes[i].set_title(col)
    axes[i].legend(["Not Transported", "Transported"])
plt.tight_layout()
plt.show()

発見すべきパターン: - CryoSleep=True の乗客は転送率が非常に高い - 消費系特徴量(RoomService, Spa等)が0の乗客は転送される傾向 - HomePlanetによって転送率に差がある

Step 3: 特徴量エンジニアリング

# Cabinからデッキ・サイドを抽出
train[["Deck", "CabinNum", "Side"]] = train["Cabin"].str.split("/", expand=True)

# 消費合計を特徴量として追加
spend_cols = ["RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck"]
train["TotalSpend"] = train[spend_cols].sum(axis=1)
train["NoSpend"] = (train["TotalSpend"] == 0).astype(int)

# PassengerIdからグループ情報を抽出
train["GroupId"] = train["PassengerId"].str.split("_").str[0]
train["GroupSize"] = train.groupby("GroupId")["GroupId"].transform("count")
train["IsAlone"] = (train["GroupSize"] == 1).astype(int)

Step 4: 前処理パイプライン

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer

# 使用する特徴量を定義
num_features = ["Age", "RoomService", "FoodCourt", "ShoppingMall",
                "Spa", "VRDeck", "TotalSpend", "GroupSize"]
cat_features = ["HomePlanet", "CryoSleep", "Destination", "VIP",
                "Deck", "Side", "NoSpend", "IsAlone"]

# 数値特徴量: 中央値補完 → 標準化
num_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])

# カテゴリ特徴量: 最頻値補完 → ワンホットエンコーディング
cat_pipeline = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])

# ColumnTransformerで統合
preprocessor = ColumnTransformer([
    ("num", num_pipeline, num_features),
    ("cat", cat_pipeline, cat_features),
])

ColumnTransformer は「数値列とカテゴリ列に異なる前処理を適用する」ための仕組みです。Pipelineと組み合わせることで、データリーケージを防ぎながら一貫した前処理が行えます。

Step 5: ベースラインモデル

from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier
from lightgbm import LGBMClassifier

X = train[num_features + cat_features]
y = train["Transported"].astype(int)

models = {
    "Logistic": LogisticRegression(max_iter=1000),
    "RandomForest": RandomForestClassifier(n_estimators=200, random_state=42),
    "XGBoost": XGBClassifier(n_estimators=200, use_label_encoder=False,
                             eval_metric="logloss", random_state=42),
    "LightGBM": LGBMClassifier(n_estimators=200, random_state=42, verbose=-1),
}

results = {}
for name, model in models.items():
    pipe = Pipeline([
        ("preprocessor", preprocessor),
        ("model", model),
    ])
    scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
    results[name] = scores
    print(f"{name:15s}: {scores.mean():.4f} ± {scores.std():.4f}")

Step 6: ハイパーパラメータチューニング

最も成績の良かったモデルをチューニングします。

from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint

# LightGBMのチューニング(例)
param_dist = {
    "model__n_estimators": randint(100, 500),
    "model__learning_rate": uniform(0.01, 0.3),
    "model__max_depth": randint(3, 10),
    "model__num_leaves": randint(20, 100),
    "model__min_child_samples": randint(5, 50),
}

best_pipe = Pipeline([
    ("preprocessor", preprocessor),
    ("model", LGBMClassifier(random_state=42, verbose=-1)),
])

search = RandomizedSearchCV(
    best_pipe, param_dist, n_iter=50, cv=5,
    scoring="accuracy", random_state=42, n_jobs=-1
)
search.fit(X, y)

print(f"最良スコア: {search.best_score_:.4f}")
print(f"最良パラメータ:")
for k, v in search.best_params_.items():
    print(f"  {k}: {v}")

Step 7: 特徴量重要度の確認

# チューニング済みモデルの特徴量重要度
best_model = search.best_estimator_

# 前処理後の特徴量名を取得
ohe = best_model.named_steps["preprocessor"].named_transformers_["cat"].named_steps["encoder"]
cat_names = ohe.get_feature_names_out(cat_features).tolist()
feature_names = num_features + cat_names

importances = pd.Series(
    best_model.named_steps["model"].feature_importances_,
    index=feature_names
).nlargest(15)

importances.plot(kind="barh", color="steelblue")
plt.xlabel("重要度")
plt.title("LightGBM — 特徴量重要度 Top15")
plt.tight_layout()
plt.show()

Step 8: 結果の分析

from sklearn.metrics import classification_report, confusion_matrix

# 最終評価(全データでの交差検証)
from sklearn.model_selection import cross_val_predict

y_pred = cross_val_predict(search.best_estimator_, X, y, cv=5)
print(classification_report(y, y_pred, target_names=["Not Transported", "Transported"]))

# 混同行列
cm = confusion_matrix(y, y_pred)
sns.heatmap(cm, annot=True, fmt="d", cmap="Blues",
            xticklabels=["Not", "Transported"],
            yticklabels=["Not", "Transported"])
plt.xlabel("予測")
plt.ylabel("実際")
plt.title("混同行列")
plt.show()

機械学習プロジェクトのチェックリスト

実務で機械学習プロジェクトを進める際のチェックリストです:

フェーズ チェック項目
データ理解 目的変数の分布確認、欠損率の把握、外れ値の確認
前処理 スケーリング、エンコーディング、欠損値処理をPipelineで一元管理
ベースライン シンプルなモデル(ロジスティック回帰)で基準スコアを取得
モデリング 複数アルゴリズムを交差検証で比較
チューニング RandomizedSearchCVで効率的にパラメータ探索
評価 適合率・再現率・F1を確認(正解率だけに頼らない)
解釈 特徴量重要度で「なぜその予測か」を説明可能にする

このコースの先にあるもの

10回の講義で、機械学習の基礎を一通り学びました。ここからの発展ルートを紹介します:

方向 学ぶべきこと おすすめリソース
Kaggle実践 特徴量エンジニアリング、アンサンブル Kaggle Competitions
ディープラーニング CNN, Transformer, PyTorch実践 fast.ai (https://www.fast.ai/)
自然言語処理 BERT, GPT, Hugging Face Hugging Face Course
MLOps モデルのデプロイ・監視 MLflow, Docker, AWS SageMaker
統計学の深掘り ベイズ統計、因果推論 統計学入門コース

機械学習は「学んで終わり」ではなく、「実データで試して、失敗して、改善する」反復の中で身につきます。Kaggleやscikit-learnのサンプルデータセットで、今日から実践を始めてください。

参考文献

  • Kaggle Spaceship Titanic: https://www.kaggle.com/competitions/spaceship-titanic
  • scikit-learn Pipeline: https://scikit-learn.org/stable/modules/compose.html
  • Géron, A. (2022). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 3rd ed. O'Reilly.