この章では、Pythonを使って実際に機械学習モデルを構築します。環境構築から始めて、回帰・分類問題の実装、モデル比較、ハイパーパラメータチューニング、そして実プロジェクトまで、35個の実行可能なコード例で学びます。
学習目標
- ✅ Python環境を3つの方法(Anaconda/venv/Colab)で構築できる
- ✅ データの読み込み、前処理、可視化の基本操作ができる
- ✅ 回帰問題(住宅価格予測)を実装し評価できる
- ✅ 分類問題(Iris分類)で複数のモデルを比較できる
- ✅ ハイパーパラメータチューニングを実行できる
- ✅ 特徴量エンジニアリングの基本技術を適用できる
- ✅ Titanicデータセットで実プロジェクトを完成できる
3.1 環境構築:3つの選択肢
機械学習を実践するには、まずPython環境を構築する必要があります。状況に応じて3つの選択肢から選べます。
3.1.1 Option 1: Anaconda(初心者推奨)
特徴:
- 科学計算ライブラリが最初から揃っている
- 環境管理が簡単(GUI利用可能)
- Windows/Mac/Linux対応
インストール手順:
# コード例1: Anaconda環境構築
# 1. Anacondaをダウンロード
# 公式サイト: https://www.anaconda.com/download
# Python 3.11以上を選択
# 2. インストール後、Anaconda Promptを起動
# 3. 仮想環境を作成(ML専用環境)
conda create -n ml_env python=3.11
# 4. 環境を有効化
conda activate ml_env
# 5. 必要なライブラリをインストール
conda install numpy pandas matplotlib seaborn scikit-learn jupyter
# 6. 動作確認
python --version
# 期待される出力: Python 3.11.x
# 7. Jupyter Notebookを起動
jupyter notebook
成功の確認: コマンドプロンプトで (ml_env) というプレフィックスが表示されれば、環境が正しくアクティブ化されています。
Anacondaの利点と欠点:
| 利点 | 欠点 |
|---|---|
| NumPy、SciPyなどが最初から含まれる | ファイルサイズが大きい(3GB以上) |
| 依存関係の問題が少ない | インストールに時間がかかる |
| Anaconda Navigatorで視覚的に管理可能 | ディスク容量を消費 |
3.1.2 Option 2: venv(Python標準)
特徴:
-
Python標準ツール(追加インストール不要)
-
軽量(必要なものだけインストール)
-
プロジェクトごとに環境を分離
コード例2: venv環境構築
1. Python 3.11以上がインストールされているか確認
python3 —version
期待される出力: Python 3.11.x 以上
2. 仮想環境を作成
python3 -m venv ml_env
3. 環境を有効化
macOS/Linux:
source ml_env/bin/activate
Windows (PowerShell):
ml_env\Scripts\Activate.ps1
Windows (Command Prompt):
ml_env\Scripts\activate.bat
4. pipをアップグレード
pip install —upgrade pip
5. 必要なライブラリをインストール
pip install numpy pandas matplotlib seaborn scikit-learn jupyter
6. インストール確認
pip list
numpy, pandas, scikit-learnなどが表示されればOK
Tips: requirements.txtを作成しておくと、環境の再現が簡単になります。
# requirements.txt
numpy>=1.24.0
pandas>=2.0.0
matplotlib>=3.7.0
seaborn>=0.12.0
scikit-learn>=1.3.0
jupyter>=1.0.0
インストール: pip install -r requirements.txt
3.1.3 Option 3: Google Colab(インストール不要)
特徴:
-
ブラウザだけで実行可能
-
インストール不要(クラウド実行)
-
GPU/TPUが無料で使える
コード例3: Google Colabでの動作確認
1. Google Colabにアクセス: https://colab.research.google.com
2. 新しいノートブックを作成
3. 以下のコードを実行(必要なライブラリは自動でインストール済み)
import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor
print(“ライブラリのインポートが成功しました!”) print(f”NumPy version: {np.version}”) print(f”Pandas version: {pd.version}”) print(f”scikit-learn version: {sklearn.version}“)
期待される出力:
ライブラリのインポートが成功しました!
NumPy version: 1.24.3
Pandas version: 2.0.3
scikit-learn version: 1.3.0
3.1.4 環境選択ガイド
| 状況 | 推奨オプション | 理由 |
|---|---|---|
| 初めてのPython環境 | Anaconda | 環境構築が簡単、トラブルが少ない |
| 既にPython環境がある | venv | 軽量、プロジェクトごとに独立 |
| 今すぐ試したい | Google Colab | インストール不要、即座に開始可能 |
| GPU計算が必要 | Google Colab | 無料でGPUアクセス可能 |
3.2 データの準備と可視化
機械学習の最初のステップは、データを正しく読み込み、理解することです。
3.2.1 データの読み込みと基本操作
# コード例4: Irisデータセットの読み込み
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
# Irisデータセット(アヤメの品種分類)を読み込み
iris = load_iris()
# DataFrameに変換
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target
# データの先頭5行を表示
print("データの先頭5行:")
print(df.head())
# 期待される出力:
# sepal length (cm) sepal width (cm) ... petal width (cm) target
# 0 5.1 3.5 ... 0.2 0
# 1 4.9 3.0 ... 0.2 0
# 2 4.7 3.2 ... 0.2 0
# 3 4.6 3.1 ... 0.2 0
# 4 5.0 3.6 ... 0.2 0
# データの形状を確認
print(f"\nデータの形状: {df.shape}")
# 出力: データの形状: (150, 5)
# 150サンプル、5列(特徴量4つ + ターゲット1つ)
# コード例5: 基本統計量の確認
# 基本統計量を表示
print("基本統計量:")
print(df.describe())
# 期待される出力:
# sepal length (cm) sepal width (cm) ... petal width (cm) target
# count 150.000000 150.000000 ... 150.000000 150.000000
# mean 5.843333 3.057333 ... 1.199333 1.000000
# std 0.828066 0.435866 ... 0.762238 0.819232
# min 4.300000 2.000000 ... 0.100000 0.000000
# 25% 5.100000 2.800000 ... 0.300000 0.000000
# 50% 5.800000 3.000000 ... 1.300000 1.000000
# 75% 6.400000 3.300000 ... 1.800000 2.000000
# max 7.900000 4.400000 ... 2.500000 2.000000
# データ型を確認
print("\nデータ型:")
print(df.dtypes)
# 欠損値を確認
print("\n欠損値の数:")
print(df.isnull().sum())
# 期待される出力: すべて0(Irisデータセットには欠損値がない)
3.2.2 データの可視化
# コード例6: ヒストグラム(分布の可視化)
import matplotlib.pyplot as plt
# 各特徴量のヒストグラムを作成
df.hist(figsize=(12, 8), bins=20, edgecolor='black')
plt.suptitle('Iris Dataset - Feature Distributions', fontsize=16)
plt.tight_layout()
plt.show()
# 解釈:
# - sepal length: 5-6cmあたりにピーク
# - petal length: 二峰性(品種による違いが大きい)
# コード例7: 散布図マトリックス(特徴量間の関係)
import seaborn as sns
# 散布図マトリックス(pairplot)
sns.pairplot(df, hue='target', palette='Set1', markers=['o', 's', 'D'])
plt.suptitle('Iris Dataset - Pairplot by Species', y=1.02)
plt.show()
# 解釈:
# - petal length vs petal width: 品種がきれいに分離
# - sepal length vs sepal width: 一部重なりあり
# ⇒ petal系の特徴量が分類に有効
3.3 回帰問題:住宅価格予測
回帰問題では、連続値(価格、温度など)を予測します。カリフォルニア住宅価格データで線形回帰を実装します。
3.3.1 データの準備
# コード例8: データ準備(カリフォルニア住宅価格)
from sklearn.datasets import fetch_california_housing
# カリフォルニア住宅価格データセットを読み込み
housing = fetch_california_housing()
X = housing.data # 特徴量(8次元)
y = housing.target # ターゲット(住宅価格、単位: $100,000)
# データの確認
print("特徴量の形状:", X.shape) # (20640, 8)
print("ターゲットの形状:", y.shape) # (20640,)
print("\n特徴量名:")
print(housing.feature_names)
# ['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms',
# 'Population', 'AveOccup', 'Latitude', 'Longitude']
print("\n最初の3サンプル:")
print(X[:3])
print("対応する価格:", y[:3])
# 価格: [4.526 3.585 3.521] (単位: $100,000)
3.3.2 データ分割
# コード例9: 訓練データとテストデータに分割
from sklearn.model_selection import train_test_split
# データを訓練80%、テスト20%に分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print("訓練データ:", X_train.shape) # (16512, 8)
print("テストデータ:", X_test.shape) # (4128, 8)
# random_state=42: 再現性のため固定
# test_size=0.2: 一般的な分割比率
3.3.3 特徴量のスケーリング
# コード例10: 特徴量のスケーリング(標準化)
from sklearn.preprocessing import StandardScaler
# StandardScaler: 平均0、標準偏差1に変換
scaler = StandardScaler()
# 訓練データでfit(平均・標準偏差を計算)
X_train_scaled = scaler.fit_transform(X_train)
# テストデータは訓練データの統計量でtransform
X_test_scaled = scaler.transform(X_test)
print("スケーリング前(最初の1サンプル):")
print(X_train[0])
print("\nスケーリング後:")
print(X_train_scaled[0])
# 重要: テストデータでfitしない(データリークを防ぐ)
3.3.4 線形回帰モデルの訓練
# コード例11: 線形回帰モデルの訓練
from sklearn.linear_model import LinearRegression
# モデルのインスタンス化
model = LinearRegression()
# 訓練データでモデルを学習
model.fit(X_train_scaled, y_train)
# 学習したパラメータを確認
print("切片(bias):", model.intercept_)
print("\n係数(weights):")
for feature, coef in zip(housing.feature_names, model.coef_):
print(f" {feature:12s}: {coef:7.4f}")
# 期待される出力例:
# MedInc : 0.8296 (所得が高いほど価格が上がる)
# Latitude : -0.8231 (緯度が高いほど価格が下がる)
3.3.5 予測と評価
# コード例12: 予測の実行
# テストデータで予測
y_pred = model.predict(X_test_scaled)
# 最初の5件の予測を確認
print("実際の価格 vs 予測価格(最初の5件):")
for i in range(5):
print(f"実際: {y_test[i]:.3f}, 予測: {y_pred[i]:.3f}")
# 期待される出力例:
# 実際: 4.526, 予測: 4.321
# 実際: 3.585, 予測: 3.712
# 実際: 3.521, 予測: 3.498
# 実際: 3.413, 予測: 3.289
# 実際: 3.422, 予測: 3.501
# コード例13: モデルの評価
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
# 平均二乗誤差(MSE)
mse = mean_squared_error(y_test, y_pred)
# 平均二乗平方根誤差(RMSE)
rmse = np.sqrt(mse)
# 平均絶対誤差(MAE)
mae = mean_absolute_error(y_test, y_pred)
# 決定係数(R²)
r2 = r2_score(y_test, y_pred)
print("モデル評価指標:")
print(f" RMSE: {rmse:.3f}") # 小さいほど良い
print(f" MAE: {mae:.3f}") # 小さいほど良い
print(f" R²: {r2:.3f}") # 1に近いほど良い(最大1.0)
# 期待される出力:
# RMSE: 0.729(約$72,900の誤差)
# MAE: 0.526(約$52,600の誤差)
# R²: 0.576(57.6%の分散を説明)
3.3.6 予測結果の可視化
# コード例14: 予測vs実測のプロット
plt.figure(figsize=(10, 6))
# 散布図
plt.scatter(y_test, y_pred, alpha=0.5, edgecolor='black')
# 理想的な予測線(y=x)
plt.plot([y_test.min(), y_test.max()],
[y_test.min(), y_test.max()],
'r--', lw=2, label='Perfect Prediction')
plt.xlabel('Actual Price ($100,000)', fontsize=12)
plt.ylabel('Predicted Price ($100,000)', fontsize=12)
plt.title('Linear Regression: Predictions vs Actual', fontsize=14)
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 解釈:
# - 点が赤線に近いほど予測精度が高い
# - 低価格帯(0-2): 予測精度が高い
# - 高価格帯(4以上): 予測が過小評価傾向
3.4 分類問題:Iris品種分類
分類問題では、カテゴリ(品種、良/不良など)を予測します。複数のモデルを比較します。
3.4.1 ロジスティック回帰
# コード例15: ロジスティック回帰による分類
from sklearn.linear_model import LogisticRegression
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
)
# ロジスティック回帰モデル
lr_model = LogisticRegression(max_iter=200, random_state=42)
lr_model.fit(X_train, y_train)
# 予測
lr_pred = lr_model.predict(X_test)
# 確率も取得可能
lr_proba = lr_model.predict_proba(X_test)
print("最初の3サンプルの予測:")
for i in range(3):
print(f"実際: {y_test[i]}, 予測: {lr_pred[i]}, 確率: {lr_proba[i]}")
# 期待される出力例:
# 実際: 1, 予測: 1, 確率: [0.00 0.79 0.21]
# 実際: 0, 予測: 0, 確率: [0.97 0.03 0.00]
# 実際: 2, 予測: 2, 確率: [0.00 0.01 0.99]
3.4.2 精度評価
# コード例16: 分類精度の評価
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 精度(Accuracy)
accuracy = accuracy_score(y_test, lr_pred)
print(f"Accuracy: {accuracy:.3f}") # 期待: 1.000(100%)
# 詳細な分類レポート
print("\n分類レポート:")
print(classification_report(y_test, lr_pred, target_names=iris.target_names))
# 期待される出力:
# precision recall f1-score support
# setosa 1.00 1.00 1.00 10
# versicolor 1.00 1.00 1.00 9
# virginica 1.00 1.00 1.00 11
# accuracy 1.00 30
# precision: 正解と予測した中で実際に正解だった割合
# recall: 実際の正解の中で正しく予測できた割合
# f1-score: precisionとrecallの調和平均
3.4.3 混同行列の可視化
# コード例17: 混同行列(Confusion Matrix)
import seaborn as sns
# 混同行列を計算
cm = confusion_matrix(y_test, lr_pred)
# ヒートマップで可視化
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=iris.target_names,
yticklabels=iris.target_names)
plt.title('Confusion Matrix - Logistic Regression', fontsize=14)
plt.ylabel('True Label', fontsize=12)
plt.xlabel('Predicted Label', fontsize=12)
plt.tight_layout()
plt.show()
# 解釈:
# - 対角線: 正しく分類された数
# - 対角線以外: 誤分類
# - Irisデータは単純なので誤分類がほぼゼロ
3.4.4 決定木
# コード例18: 決定木による分類
from sklearn.tree import DecisionTreeClassifier
# 決定木モデル(深さ3に制限)
dt_model = DecisionTreeClassifier(max_depth=3, random_state=42)
dt_model.fit(X_train, y_train)
# 予測
dt_pred = dt_model.predict(X_test)
# 精度
dt_accuracy = accuracy_score(y_test, dt_pred)
print(f"Decision Tree Accuracy: {dt_accuracy:.3f}")
# 期待: 1.000
# 決定木の利点: 解釈性が高い
# 特徴量の重要度を確認
print("\n特徴量の重要度:")
for feature, importance in zip(iris.feature_names, dt_model.feature_importances_):
print(f" {feature:20s}: {importance:.3f}")
3.4.5 ランダムフォレスト
# コード例19: ランダムフォレスト(アンサンブル学習)
from sklearn.ensemble import RandomForestClassifier
# ランダムフォレスト(100本の決定木)
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
# 予測
rf_pred = rf_model.predict(X_test)
# 精度
rf_accuracy = accuracy_score(y_test, rf_pred)
print(f"Random Forest Accuracy: {rf_accuracy:.3f}")
# 期待: 1.000
# ランダムフォレストの利点:
# - 決定木より過学習しにくい
# - 高い予測精度
# - 特徴量の重要度が安定
3.4.6 特徴量の重要度可視化
# コード例20: 特徴量の重要度(Feature Importance)
# ランダムフォレストの特徴量重要度
importances = rf_model.feature_importances_
feature_names = iris.feature_names
# 降順にソート
indices = np.argsort(importances)[::-1]
# 棒グラフで可視化
plt.figure(figsize=(10, 6))
plt.barh(range(len(importances)), importances[indices], color='skyblue', edgecolor='black')
plt.yticks(range(len(importances)), [feature_names[i] for i in indices])
plt.xlabel('Importance', fontsize=12)
plt.title('Feature Importance (Random Forest)', fontsize=14)
plt.tight_layout()
plt.show()
# 解釈:
# - petal width (cm): 最も重要(0.45)
# - petal length (cm): 2番目に重要(0.42)
# ⇒ 花びらの特徴が品種分類に最も有効
3.4.7 サポートベクターマシン(SVM)
# コード例21: SVM(Support Vector Machine)
from sklearn.svm import SVC
# SVMモデル(RBFカーネル)
svm_model = SVC(kernel='rbf', random_state=42)
svm_model.fit(X_train, y_train)
# 予測
svm_pred = svm_model.predict(X_test)
# 精度
svm_accuracy = accuracy_score(y_test, svm_pred)
print(f"SVM Accuracy: {svm_accuracy:.3f}")
# 期待: 1.000
# SVMの利点:
# - 高次元データに強い
# - カーネルトリックで非線形分離可能
# 欠点:
# - 大規模データでは遅い
# - 確率予測が標準でない
3.5 モデル比較と選択
複数のモデルを公平に比較し、最適なモデルを選択します。
3.5.1 クロスバリデーション
# コード例22: クロスバリデーションによるモデル比較
from sklearn.model_selection import cross_val_score
# 比較するモデル
models = {
'Logistic Regression': LogisticRegression(max_iter=200),
'Decision Tree': DecisionTreeClassifier(max_depth=3),
'Random Forest': RandomForestClassifier(n_estimators=100),
'SVM': SVC(kernel='rbf')
}
print("5-Fold Cross-Validation Results:")
print("-" * 50)
results = []
for name, model in models.items():
# 5分割クロスバリデーション
scores = cross_val_score(model, X_train, y_train, cv=5)
results.append({
'Model': name,
'Mean': scores.mean(),
'Std': scores.std()
})
print(f"{name:20s}: {scores.mean():.3f} (+/- {scores.std():.3f})")
# 期待される出力:
# Logistic Regression : 0.967 (+/- 0.033)
# Decision Tree : 0.958 (+/- 0.042)
# Random Forest : 0.967 (+/- 0.025)
# SVM : 0.975 (+/- 0.025)
3.5.2 学習曲線
# コード例23: 学習曲線(Learning Curve)
from sklearn.model_selection import learning_curve
# ランダムフォレストの学習曲線
train_sizes, train_scores, val_scores = learning_curve(
RandomForestClassifier(n_estimators=100, random_state=42),
X_train, y_train, cv=5, n_jobs=-1,
train_sizes=np.linspace(0.1, 1.0, 10)
)
# 平均と標準偏差を計算
train_mean = train_scores.mean(axis=1)
train_std = train_scores.std(axis=1)
val_mean = val_scores.mean(axis=1)
val_std = val_scores.std(axis=1)
# プロット
plt.figure(figsize=(10, 6))
plt.plot(train_sizes, train_mean, label='Training score', color='blue', marker='o')
plt.fill_between(train_sizes, train_mean - train_std, train_mean + train_std,
alpha=0.15, color='blue')
plt.plot(train_sizes, val_mean, label='Validation score', color='red', marker='s')
plt.fill_between(train_sizes, val_mean - val_std, val_mean + val_std,
alpha=0.15, color='red')
plt.xlabel('Training Size', fontsize=12)
plt.ylabel('Score', fontsize=12)
plt.title('Learning Curve - Random Forest', fontsize=14)
plt.legend(loc='best')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 解釈:
# - 訓練スコアと検証スコアが近い ⇒ 良好な汎化性能
# - 検証スコアが収束 ⇒ データを追加しても改善は限定的
3.5.3 モデル性能比較表
# コード例24: モデル性能の包括的比較
import time
# 比較結果を格納
results = []
for name, model in models.items():
# 訓練時間を計測
start_time = time.time()
model.fit(X_train, y_train)
train_time = time.time() - start_time
# 予測時間を計測
start_time = time.time()
pred = model.predict(X_test)
predict_time = time.time() - start_time
# 精度
acc = accuracy_score(y_test, pred)
results.append({
'Model': name,
'Accuracy': acc,
'Train Time (s)': train_time,
'Predict Time (s)': predict_time
})
# DataFrameに変換して表示
results_df = pd.DataFrame(results)
results_df = results_df.sort_values('Accuracy', ascending=False)
print("\nモデル性能比較:")
print(results_df.to_string(index=False))
# 期待される出力例:
# Model Accuracy Train Time (s) Predict Time (s)
# SVM 1.000 0.002 0.001
# Random Forest 1.000 0.158 0.012
# Logistic Regression 1.000 0.005 0.001
# Decision Tree 1.000 0.002 0.001
3.6 ハイパーパラメータチューニング
モデルの性能を最大化するため、ハイパーパラメータ(学習前に設定するパラメータ)を調整します。
3.6.1 Grid Search
# コード例25: Grid Search(全探索)
from sklearn.model_selection import GridSearchCV
# ランダムフォレストのハイパーパラメータ候補
param_grid = {
'n_estimators': [50, 100, 200], # 木の数
'max_depth': [3, 5, 10, None], # 木の深さ
'min_samples_split': [2, 5, 10] # 分割に必要な最小サンプル数
}
# Grid Search(5分割クロスバリデーション)
grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid, cv=5, n_jobs=-1, verbose=1
)
# 実行(3×4×3=36通りの組み合わせを試す)
grid_search.fit(X_train, y_train)
# 最良のパラメータ
print("最良のパラメータ:")
print(grid_search.best_params_)
# 期待: {'max_depth': 5, 'min_samples_split': 2, 'n_estimators': 100}
# 最良のスコア
print(f"\n最良のCV Score: {grid_search.best_score_:.3f}")
# 期待: 0.967
3.6.2 最良モデルの評価
# コード例26: チューニング後のモデル評価
# 最良のモデルを取得
best_model = grid_search.best_estimator_
# テストデータで評価
best_pred = best_model.predict(X_test)
best_accuracy = accuracy_score(y_test, best_pred)
print(f"Test Accuracy (tuned model): {best_accuracy:.3f}")
# 期待: 1.000
# チューニング前との比較
print(f"Test Accuracy (default): {rf_accuracy:.3f}")
# 期待: 1.000
# Irisデータは単純なので差が出にくいが、
# 複雑なデータセットではチューニングで5-10%改善することも
3.6.3 Random Search
# コード例27: Random Search(効率的サンプリング)
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
# パラメータの分布を定義
param_dist = {
'n_estimators': randint(50, 300), # 50-300の範囲でランダム
'max_depth': randint(3, 20), # 3-20の範囲でランダム
'min_samples_split': randint(2, 20) # 2-20の範囲でランダム
}
# Random Search(20回のランダムサンプリング)
random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_dist, n_iter=20, cv=5, random_state=42, n_jobs=-1
)
random_search.fit(X_train, y_train)
print("Random Search 最良のパラメータ:")
print(random_search.best_params_)
print(f"\n最良のCV Score: {random_search.best_score_:.3f}")
# Random Searchの利点:
# - Grid Searchより高速(20回 vs 36回)
# - 広い探索空間をカバー可能
# - 連続値パラメータにも対応
3.6.4 ハイパーパラメータ効果の可視化
# コード例28: ハイパーパラメータ効果のヒートマップ
# Grid Searchの結果をDataFrameに変換
results_df = pd.DataFrame(grid_search.cv_results_)
# n_estimatorsとmax_depthの効果を可視化
pivot = results_df.pivot_table(
values='mean_test_score',
index='param_max_depth',
columns='param_n_estimators'
)
# ヒートマップ
plt.figure(figsize=(10, 6))
sns.heatmap(pivot, annot=True, fmt='.3f', cmap='YlGnBu', cbar_kws={'label': 'CV Score'})
plt.title('Grid Search Results: max_depth vs n_estimators', fontsize=14)
plt.xlabel('n_estimators', fontsize=12)
plt.ylabel('max_depth', fontsize=12)
plt.tight_layout()
plt.show()
# 解釈:
# - max_depth=5, n_estimators=100: 最高スコア
# - max_depth=None(制限なし): 過学習のリスク
3.7 特徴量エンジニアリング
生の特徴量を変換・拡張することで、モデルの性能を向上させます。
3.7.1 多項式特徴量
# コード例29: 多項式特徴量(Polynomial Features)
from sklearn.preprocessing import PolynomialFeatures
# サンプルデータ
X_simple = np.array([[1, 2], [3, 4], [5, 6]])
print("元の特徴量:")
print(X_simple)
print("形状:", X_simple.shape) # (3, 2)
# 2次の多項式特徴量を生成
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X_simple)
print("\n多項式特徴量:")
print(X_poly)
print("形状:", X_poly.shape) # (3, 5)
# 特徴量名を確認
print("\n特徴量名:")
print(poly.get_feature_names_out(['x1', 'x2']))
# ['x1', 'x2', 'x1^2', 'x1*x2', 'x2^2']
# 解釈:
# - 元: [1, 2] → 拡張: [1, 2, 1, 2, 4]
# - x1^2, x1*x2, x2^2 などの相互作用項を追加
3.7.2 特徴量選択
# コード例30: 特徴量選択(Feature Selection)
from sklearn.feature_selection import SelectKBest, f_classif
# Irisデータで最も重要な2つの特徴量を選択
selector = SelectKBest(f_classif, k=2)
X_selected = selector.fit_transform(iris.data, iris.target)
# 選択された特徴量を確認
selected_features = np.array(iris.feature_names)[selector.get_support()]
print("選択された特徴量:")
print(selected_features)
# 期待: ['petal length (cm)', 'petal width (cm)']
# F値(ANOVA)スコアを確認
print("\n各特徴量のF値:")
for feature, score in zip(iris.feature_names, selector.scores_):
print(f" {feature:20s}: {score:7.2f}")
# 解釈:
# - petal length: 1179.03(最も重要)
# - petal width: 960.01(2番目に重要)
# - sepal系は相対的に重要度が低い
3.7.3 主成分分析(PCA)
# コード例31: PCA(次元削減)
from sklearn.decomposition import PCA
# Irisデータ(4次元)を2次元に削減
pca = PCA(n_components=2)
X_pca = pca.fit_transform(iris.data)
print("元の次元:", iris.data.shape) # (150, 4)
print("PCA後の次元:", X_pca.shape) # (150, 2)
# 説明された分散の割合
print("\n各主成分の寄与率:")
print(pca.explained_variance_ratio_)
# 期待: [0.92, 0.05](第1主成分で92%を説明)
# 累積寄与率
print(f"累積寄与率: {pca.explained_variance_ratio_.sum():.3f}")
# 期待: 0.977(97.7%の情報を保持)
# 可視化
plt.figure(figsize=(10, 6))
scatter = plt.scatter(X_pca[:, 0], X_pca[:, 1], c=iris.target,
cmap='viridis', edgecolor='black', s=50)
plt.xlabel('First Principal Component', fontsize=12)
plt.ylabel('Second Principal Component', fontsize=12)
plt.title('PCA of Iris Dataset', fontsize=14)
plt.colorbar(scatter, label='Species')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 解釈:
# - 4次元を2次元に削減しても97.7%の情報を保持
# - 品種がきれいに分離される
3.7.4 モデルベースの特徴量重要度
# コード例32: モデルベースの特徴量重要度分析
# ランダムフォレストで特徴量重要度を計算
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(iris.data, iris.target)
# 特徴量重要度をDataFrameに整理
feature_importance_df = pd.DataFrame({
'feature': iris.feature_names,
'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)
print("特徴量重要度ランキング:")
print(feature_importance_df)
# 期待される出力:
# feature importance
# 2 petal length (cm) 0.445
# 3 petal width (cm) 0.425
# 0 sepal length (cm) 0.089
# 1 sepal width (cm) 0.041
# 可視化
plt.figure(figsize=(10, 6))
plt.barh(feature_importance_df['feature'], feature_importance_df['importance'],
color='coral', edgecolor='black')
plt.xlabel('Importance', fontsize=12)
plt.title('Feature Importance (Random Forest)', fontsize=14)
plt.tight_layout()
plt.show()
3.8 トラブルシューティング
機械学習の実装でよくあるエラーと解決策をまとめます。
3.8.1 よくあるエラーと解決策
| エラー | 原因 | 解決策 |
|---|---|---|
ModuleNotFoundError | ライブラリ未インストール | pip install [library_name] |
ValueError: shape mismatch | データの次元が合わない | X.shapeとy.shapeを確認 |
ConvergenceWarning | 最適化が収束しない | max_iterを増やす、またはスケーリング |
| 低精度(<0.5) | データ品質、モデル選択 | データ確認、特徴量追加、モデル変更 |
| 過学習(train>test) | モデルが複雑すぎる | 正則化、データ追加、CV使用 |
MemoryError | データサイズ過大 | バッチ処理、データサンプリング |
| 訓練が遅い | データ量、モデル複雑度 | n_jobs=-1、GPU使用、モデル簡略化 |
3.8.2 デバッグチェックリスト
5ステップデバッグ手順:
- データ確認 :
df.head(),df.info(),df.describe() - 欠損値チェック :
df.isnull().sum() - データ型確認 :
df.dtypes(数値型になっているか) - 形状確認 :
X.shape,y.shape(次元が一致するか) - 簡単なモデルで試す : まず
LogisticRegressionで動作確認
3.9 プロジェクトチャレンジ:Titanic生存予測
ここまで学んだ技術を使って、実際のデータサイエンスプロジェクトに挑戦します。
3.9.1 プロジェクト概要
プロジェクト目標:
- データセット: Titanic乗客データ(Kaggle)
- タスク: 生存(Survived: 0/1)を予測
- 目標精度: 80%以上
- 使用技術: データ前処理、特徴量エンジニアリング、モデル比較
3.9.2 データ読み込みとEDA
# コード例33: Titanicデータの探索的データ分析(EDA)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# データ読み込み(実際にはKaggleからダウンロード)
# ここではサンプルデータを作成
# 実際のプロジェクトでは以下のURLからダウンロード:
# https://www.kaggle.com/c/titanic/data
# サンプルデータ作成(デモ用)
np.random.seed(42)
df = pd.DataFrame({
'PassengerId': range(1, 892),
'Survived': np.random.choice([0, 1], 891),
'Pclass': np.random.choice([1, 2, 3], 891),
'Sex': np.random.choice(['male', 'female'], 891),
'Age': np.random.normal(30, 15, 891).clip(0, 80),
'SibSp': np.random.choice([0, 1, 2, 3], 891),
'Parch': np.random.choice([0, 1, 2], 891),
'Fare': np.random.exponential(30, 891),
'Embarked': np.random.choice(['S', 'C', 'Q'], 891)
})
# 欠損値をランダムに追加
df.loc[np.random.choice(df.index, 177, replace=False), 'Age'] = np.nan
df.loc[np.random.choice(df.index, 2, replace=False), 'Embarked'] = np.nan
print("データの先頭5行:")
print(df.head())
print("\nデータの基本情報:")
print(df.info())
print("\n基本統計量:")
print(df.describe())
print("\n欠損値の確認:")
print(df.isnull().sum())
# 期待される出力:
# Age: 177件の欠損値
# Embarked: 2件の欠損値
3.9.3 データ前処理
# コード例34: データ前処理と特徴量エンジニアリング
from sklearn.preprocessing import LabelEncoder
# データのコピーを作成
df_processed = df.copy()
# 1. 欠損値の処理
# Age: 中央値で補完
df_processed['Age'].fillna(df_processed['Age'].median(), inplace=True)
# Embarked: 最頻値で補完
df_processed['Embarked'].fillna(df_processed['Embarked'].mode()[0], inplace=True)
# 2. 特徴量エンジニアリング
# 家族サイズ = SibSp(兄弟・配偶者) + Parch(親・子供) + 1(本人)
df_processed['FamilySize'] = df_processed['SibSp'] + df_processed['Parch'] + 1
# 一人旅かどうか
df_processed['IsAlone'] = (df_processed['FamilySize'] == 1).astype(int)
# 年齢層(カテゴリ化)
df_processed['AgeGroup'] = pd.cut(df_processed['Age'],
bins=[0, 12, 18, 60, 100],
labels=['Child', 'Teen', 'Adult', 'Senior'])
# 3. カテゴリ変数のエンコーディング
# Sex: LabelEncoder(male=1, female=0)
le = LabelEncoder()
df_processed['Sex'] = le.fit_transform(df_processed['Sex'])
# Embarked: One-Hot Encoding
df_processed = pd.get_dummies(df_processed, columns=['Embarked'], prefix='Embarked')
# AgeGroup: One-Hot Encoding
df_processed = pd.get_dummies(df_processed, columns=['AgeGroup'], prefix='Age')
# 4. 不要な列を削除
df_processed.drop(['PassengerId', 'SibSp', 'Parch'], axis=1, inplace=True)
print("前処理後のデータ:")
print(df_processed.head())
print("\n形状:", df_processed.shape)
print("\n欠損値:", df_processed.isnull().sum().sum()) # 期待: 0
3.9.4 モデル訓練と評価
# コード例35: 複数モデルの訓練と比較
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 特徴量とターゲットを分離
X = df_processed.drop('Survived', axis=1)
y = df_processed['Survived']
# データ分割(訓練80%, テスト20%)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 複数モデルを定義
models = {
'Logistic Regression': LogisticRegression(max_iter=1000, random_state=42),
'Decision Tree': DecisionTreeClassifier(max_depth=5, random_state=42),
'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42),
'Gradient Boosting': GradientBoostingClassifier(n_estimators=100, random_state=42),
'SVM': SVC(kernel='rbf', random_state=42)
}
print("モデル比較結果:")
print("-" * 70)
results = []
for name, model in models.items():
# 訓練
model.fit(X_train, y_train)
# 予測
y_pred = model.predict(X_test)
# 評価
accuracy = accuracy_score(y_test, y_pred)
# クロスバリデーション
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
results.append({
'Model': name,
'Test Accuracy': accuracy,
'CV Mean': cv_scores.mean(),
'CV Std': cv_scores.std()
})
print(f"{name:20s}: Test={accuracy:.3f}, CV={cv_scores.mean():.3f} (+/-{cv_scores.std():.3f})")
# 結果をDataFrameに変換
results_df = pd.DataFrame(results).sort_values('Test Accuracy', ascending=False)
print("\n最終結果(精度順):")
print(results_df.to_string(index=False))
# 最良モデルの詳細レポート
best_model_name = results_df.iloc[0]['Model']
best_model = models[best_model_name]
best_pred = best_model.predict(X_test)
print(f"\n{best_model_name} の詳細:")
print(classification_report(y_test, best_pred))
# 混同行列
cm = confusion_matrix(y_test, best_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.title(f'Confusion Matrix - {best_model_name}')
plt.ylabel('True Label')
plt.xlabel('Predicted Label')
plt.tight_layout()
plt.show()
# プロジェクト成功判定
if results_df.iloc[0]['Test Accuracy'] >= 0.80:
print("\n🎉 プロジェクト成功!目標精度80%を達成しました!")
else:
print(f"\n目標精度80%まであと{0.80 - results_df.iloc[0]['Test Accuracy']:.1%}です。")
print("改善のヒント: 特徴量追加、ハイパーパラメータチューニング、アンサンブル学習")
3.9.5 プロジェクト拡張アイデア
さらなる挑戦:
- 特徴量の追加 : 名前からタイトル(Mr., Mrs.等)を抽出
- ハイパーパラメータチューニング : GridSearchCVで最適化
- アンサンブル学習 : VotingClassifierで複数モデルを組み合わせ
- 特徴量重要度分析 : どの特徴量が生存に影響したか分析
- Kaggleサブミット : 実際のKaggleコンペに提出
- 深層学習 : ニューラルネットワーク(Keras/PyTorch)で実装
本章のまとめ
この章では、Pythonを使った機械学習の実践的な流れを学びました。
習得したスキル
- ✅ Python環境の構築(Anaconda/venv/Colab)
- ✅ データの読み込み、前処理、可視化
- ✅ 回帰問題の実装(線形回帰、住宅価格予測)
- ✅ 分類問題の実装(Iris分類、複数モデル比較)
- ✅ モデル評価指標(RMSE, MAE, R², Accuracy, F1-score)
- ✅ クロスバリデーション(5-fold CV)
- ✅ ハイパーパラメータチューニング(Grid Search, Random Search)
- ✅ 特徴量エンジニアリング(多項式、選択、PCA)
- ✅ トラブルシューティング(エラー対処、デバッグ手順)
- ✅ 実プロジェクト(Titanic生存予測)
重要なポイント
機械学習プロジェクトの5ステップ:
- データ理解 : EDA、可視化、統計量確認
- 前処理 : 欠損値処理、スケーリング、エンコーディング
- モデル選択 : 複数モデルを試して比較
- 評価・改善 : CV、チューニング、特徴量エンジニアリング
- デプロイ準備 : 最良モデルの保存、ドキュメント作成
次のステップ
第4章では、機械学習の実世界への応用を学びます:
- 5つの詳細ケーススタディ(Netflix、Google翻訳、Tesla等)
- 将来トレンド(基盤モデル、AutoML、エッジAI)
- キャリアパス(データサイエンティスト、MLエンジニア、研究者)
- 学習リソースとコミュニティ
演習問題
問題1(難易度:Easy) - データ読み込みと基本統計
問題: Irisデータセットを読み込み、各特徴量の平均値、中央値、標準偏差を計算してください。
ヒント: df.describe()を使うと一度に確認できます。
解答例
from sklearn.datasets import load_iris
import pandas as pd
# データ読み込み
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
# 基本統計量
print(df.describe())
# 個別に計算する場合
print("\n平均値:")
print(df.mean())
print("\n中央値:")
print(df.median())
print("\n標準偏差:")
print(df.std())
問題2(難易度:Easy) - 訓練データとテストデータの分割
問題: カリフォルニア住宅データを訓練70%、テスト30%に分割してください。分割後のデータ数を確認してください。
ヒント: train_test_splitのtest_sizeパラメータを変更します。
解答例
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
# データ読み込み
housing = fetch_california_housing()
X, y = housing.data, housing.target
# 訓練70%, テスト30%に分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
print("元のデータ数:", len(X)) # 20640
print("訓練データ数:", len(X_train)) # 14448
print("テストデータ数:", len(X_test)) # 6192
print("分割比率:", len(X_train)/len(X)) # 0.70
問題3(難易度:Medium) - モデルの精度比較
問題: Irisデータセットで、ロジスティック回帰、決定木、ランダムフォレストの3つのモデルを訓練し、テスト精度を比較してください。最も精度の高いモデルを特定してください。
ヒント: accuracy_scoreを使って各モデルの精度を計算します。
解答例
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# データ準備
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
)
# モデル定義
models = {
'Logistic Regression': LogisticRegression(max_iter=200),
'Decision Tree': DecisionTreeClassifier(max_depth=3),
'Random Forest': RandomForestClassifier(n_estimators=100)
}
# 訓練と評価
results = {}
for name, model in models.items():
model.fit(X_train, y_train)
pred = model.predict(X_test)
accuracy = accuracy_score(y_test, pred)
results[name] = accuracy
print(f"{name}: {accuracy:.3f}")
# 最良モデル
best_model = max(results, key=results.get)
print(f"\n最良モデル: {best_model} ({results[best_model]:.3f})")
問題4(難易度:Medium) - ハイパーパラメータチューニング
問題: ランダムフォレストでn_estimators=[50, 100, 150]とmax_depth=[3, 5, 7]の組み合わせでGrid Searchを実行し、最良のパラメータを見つけてください。
ヒント: GridSearchCVを使います。
解答例
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
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
)
# パラメータグリッド
param_grid = {
'n_estimators': [50, 100, 150],
'max_depth': [3, 5, 7]
}
# Grid Search
grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid, cv=5, n_jobs=-1
)
grid_search.fit(X_train, y_train)
# 結果
print("最良のパラメータ:", grid_search.best_params_)
print("最良のCV Score:", grid_search.best_score_)
# テストデータで評価
test_accuracy = grid_search.best_estimator_.score(X_test, y_test)
print("テスト精度:", test_accuracy)
問題5(難易度:Hard) - 完全なMLパイプライン
問題: カリフォルニア住宅データで、以下のステップを含む完全なMLパイプラインを構築してください:
- データの80/20分割
- StandardScalerでスケーリング
- ランダムフォレスト回帰で訓練
- RMSE、MAE、R²を計算
- 予測vs実測のプロット作成
ヒント: これまで学んだコード例を組み合わせます。
解答例
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import numpy as np
import matplotlib.pyplot as plt
# 1. データ読み込み
housing = fetch_california_housing()
X, y = housing.data, housing.target
# 2. データ分割
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. スケーリング
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 4. モデル訓練
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train_scaled, y_train)
# 5. 予測
y_pred = model.predict(X_test_scaled)
# 6. 評価
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print("評価結果:")
print(f" RMSE: {rmse:.3f}")
print(f" MAE: {mae:.3f}")
print(f" R²: {r2:.3f}")
# 7. 可視化
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.5, edgecolor='black')
plt.plot([y_test.min(), y_test.max()],
[y_test.min(), y_test.max()],
'r--', lw=2, label='Perfect Prediction')
plt.xlabel('Actual Price')
plt.ylabel('Predicted Price')
plt.title('Random Forest: Predictions vs Actual')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
参考文献
- Pedregosa, F., et al. (2011). “Scikit-learn: Machine Learning in Python.” Journal of Machine Learning Research , 12, 2825-2830.
- VanderPlas, J. (2016). Python Data Science Handbook. O’Reilly Media.
- Géron, A. (2019). Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow (2nd ed.). O’Reilly Media.
- scikit-learn Documentation. (2024). “User Guide.” URL: https://scikit-learn.org/stable/user_guide.html
- Kaggle. (2024). “Titanic: Machine Learning from Disaster.” URL: https://www.kaggle.com/c/titanic