Machine Learning: 総合演習
最終更新:2026-08-26
5つのレッスンでツールを学んできました。今度はそれらをすべて組み合わせて、実際の機械学習プロジェクトを完成させましょう。
1. 学習内容
- エンドツーエンドのワークフロー:データ読み込み → EDA → 特徴量選択 → ベースラインモデル → 評価
- 実践的なEDA:SalesPredictのECデータに対する分布・相関・トレンド分析
- ベースラインモデルの構築:売上予測のベンマークとしてのLinearRegression
- 結果の解釈:R²、MAE、RMSEの計算方法と、それがビジネスにとって何を意味するか
- プロジェクトの振り返り:初級段階の重要な学びと、今後の成長の方向性
2. 機械学習初心者の実体験
(1) 課題:ツールは知っていても、つなぎ方がわからない
BobはNumPy、Pandas、Seaborn、scikit-learnの学習を終えました。しかし、実際のSalesPredictデータセットを前にすると、どこから手をつければいいのかわかりませんでした。先にクリーニングすべきか、先に可視化すべきか? どのモデルを使うべきか? モデルの良し悪しはどう判断するのか? ツールは知っていても、プロジェクトのワークフローは謎のままだったのです。
(2) 解決策:エンドツーエンドのワークフロー
すべての機械学習プロジェクトは、決まったパイプラインに従います。読み込み → 探索 → クリーニング → 特徴量 → ベースライン → 評価 → 改善。まずベースラインを動かし、そこから段階的に最適化していきます。
PYTHON
# End-to-end ML workflow template
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score
# Step 1: Load & Explore
df = pd.read_csv("sales_data.csv")
print(df.describe())
# Step 2: Clean & Prepare features
features = ["ad_spend", "traffic", "last_month_sales"]
target = "monthly_revenue"
df = df.dropna(subset=features + [target])
# Step 3: Train baseline
X_train, X_test, y_train, y_test = train_test_split(df[features], df[target], test_size=0.2)
model = LinearRegression().fit(X_train, y_train)
pred = model.predict(X_test)
# Step 4: Evaluate
print(f"R²: {r2_score(y_test, pred):.3f}, MAE: {mean_absolute_error(y_test, pred):.0f} USD")
(3) 成果:30分で初のエンドツーエンドモデルが完成
標準的なワークフローに従った結果、Bobはわずか30分で最初のベースラインモデルを動かすことができました。R²は0.72にとどまりましたが、ベースラインがあることで、その後のすべての改善に基準点が生まれます。
3. エンドツーエンドの機械学習ワークフロー
(1) プロジェクト全体のフロー
graph LR
A[1. Load Data] --> B[2. EDA]
B --> C[3. Clean Data]
C --> D[4. Feature Selection]
D --> E[5. Train Baseline]
E --> F[6. Evaluate]
F --> G{Good Enough?}
G -->|No| H[7. Iterate<br/>Better Features / Model]
H --> D
G -->|Yes| I[8. Deploy]
▶ サンプル:SalesPredictのシミュレーションデータ生成
PYTHON
import pandas as pd
import numpy as np
rng = np.random.default_rng(42)
n = 1000
df = pd.DataFrame({
"date": pd.date_range("2023-01-01", periods=n, freq="D"),
"ad_spend_k": rng.uniform(5, 100, n),
"traffic_k": rng.uniform(10, 500, n),
"category": rng.choice(["Electronics", "Clothing", "Food", "Books", "Home"], n),
"is_promotion": rng.choice([0, 1], n, p=[0.8, 0.2]),
"customer_count": rng.integers(50, 500, n),
})
# Revenue formula with realistic relationships
df["revenue_k"] = (
20
+ 0.6 * df["ad_spend_k"]
+ 0.08 * df["traffic_k"]
+ 0.5 * df["customer_count"]
+ 50 * df["is_promotion"]
+ rng.normal(0, 15, n)
)
df["revenue_k"] = df["revenue_k"].clip(lower=0)
# Inject some missing values and outliers
missing_idx = rng.choice(n, size=50, replace=False)
df.loc[missing_idx[:25], "ad_spend_k"] = np.nan
df.loc[missing_idx[25:], "traffic_k"] = np.nan
outlier_idx = rng.choice(n, size=10, replace=False)
df.loc[outlier_idx, "revenue_k"] *= 5
print(f"Dataset shape: {df.shape}")
print(f"Missing values:\n{df.isnull().sum()}")
df.to_csv("salespredict_data.csv", index=False)
出力:
TEXT
📖 参照専用
# Executed successfully
4. 探索的データ分析(EDA)
(1) データの概要
▶ サンプル:包括的なEDA
PYTHON
import pandas as pd
import numpy as np
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
# Basic overview
print("=" * 50)
print("DATA OVERVIEW")
print("=" * 50)
print(f"Shape: {df.shape}")
print(f"\nDtypes:\n{df.dtypes}")
print(f"\nBasic Stats:\n{df.describe().round(2)}")
print(f"\nMissing Values:\n{df.isnull().sum()}")
print(f"\nCategory Distribution:\n{df['category'].value_counts()}")
出力:
TEXT
📖 参照専用
=
DATA OVERVIEW
=
(2) 分布とトレンドの分析
▶ サンプル:可視化によるEDA
PYTHON
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
sns.set_theme(style="whitegrid")
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
# (1) Revenue distribution
sns.histplot(df["revenue_k"], bins=40, kde=True, ax=axes[0, 0], color="#2196F3")
axes[0, 0].set_title("Revenue Distribution (thousand USD)")
# (2) Revenue by category
sns.boxplot(data=df, x="category", y="revenue_k", ax=axes[0, 1], palette="Set2")
axes[0, 1].set_title("Revenue by Category")
# (3) Promotion effect
sns.barplot(data=df, x="is_promotion", y="revenue_k", ax=axes[0, 2], palette="Pastel1")
axes[0, 2].set_title("Promotion Effect on Revenue")
# (4) Ad spend vs Revenue scatter
axes[1, 0].scatter(df["ad_spend_k"], df["revenue_k"], alpha=0.3, s=10)
axes[1, 0].set_xlabel("Ad Spend (thousand USD)")
axes[1, 0].set_ylabel("Revenue (thousand USD)")
axes[1, 0].set_title("Ad Spend vs Revenue")
# (5) Traffic vs Revenue scatter
axes[1, 1].scatter(df["traffic_k"], df["revenue_k"], alpha=0.3, s=10, color="#4CAF50")
axes[1, 1].set_xlabel("Traffic (thousand)")
axes[1, 1].set_ylabel("Revenue (thousand USD)")
axes[1, 1].set_title("Traffic vs Revenue")
# (6) Correlation heatmap
num_cols = ["ad_spend_k", "traffic_k", "customer_count", "is_promotion", "revenue_k"]
corr = df[num_cols].corr()
sns.heatmap(corr, annot=True, fmt=".2f", cmap="RdBu_r", ax=axes[1, 2], vmin=-1, vmax=1)
axes[1, 2].set_title("Feature Correlations")
plt.tight_layout()
plt.savefig("eda_overview.png", dpi=150)
出力:
TEXT
📖 参照専用
# Executed successfully
(3) EDAの主な発見テンプレート
| 発見 # | 発見内容 | ビジネスへの影響 | 次のアクション |
|---|---|---|---|
| 1 | プロモーション日は売上が平均5万USD高い | プロモーションは非常に効果的 | is_promotion特徴量を維持 |
| 2 | ad_spendとrevenueの相関 = 0.72 | 広告が売上を牽引 | コア特徴量 |
| 3 | 極端に高い外れ値が10件 | データ入力ミスの可能性 | クリーニングが必要 |
| 4 | データの5%に欠損値あり | モデル学習に影響 | 補完または削除 |
5. データクリーニングと特徴量の準備
▶ サンプル:データクリーニングのパイプライン
PYTHON
import pandas as pd
import numpy as np
df = pd.read_csv("salespredict_data.csv", parse_dates=["date"])
# Step 1: Handle missing values
print(f"Before cleaning: {len(df)} rows")
df = df.dropna(subset=["revenue_k"]) # Drop if target is missing
df["ad_spend_k"] = df["ad_spend_k"].fillna(df["ad_spend_k"].median())
df["traffic_k"] = df["traffic_k"].fillna(df["traffic_k"].median())
# Step 2: Remove outliers using IQR
def remove_outliers(df, col, factor=1.5):
q1, q3 = df[col].quantile([0.25, 0.75])
iqr = q3 - q1
return df[(df[col] >= q1 - factor * iqr) & (df[col] <= q3 + factor * iqr)]
df = remove_outliers(df, "revenue_k")
print(f"After cleaning: {len(df)} rows")
# Step 3: Feature engineering
df["month"] = df["date"].dt.month
df["day_of_week"] = df["date"].dt.dayofweek
df["is_weekend"] = (df["day_of_week"] >= 5).astype(int)
# Step 4: Encode categorical
df_encoded = pd.get_dummies(df, columns=["category"], drop_first=True)
# Step 5: Select features
feature_cols = [c for c in df_encoded.columns if c not in ["date", "revenue_k"]]
X = df_encoded[feature_cols]
y = df_encoded["revenue_k"]
print(f"Features: {feature_cols}")
print(f"X shape: {X.shape}, y shape: {y.shape}")
出力:
TEXT
📖 参照専用
# Functions defined successfully
6. ベースラインモデルの構築と評価
▶ サンプル:ベースラインのLinearRegressionを学習
PYTHON
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np
# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Baseline pipeline
baseline = Pipeline([
("scaler", StandardScaler()),
("model", LinearRegression()),
])
baseline.fit(X_train, y_train)
y_pred = baseline.predict(X_test)
# Comprehensive evaluation
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)
mape = np.mean(np.abs((y_test - y_pred) / y_test)) * 100
print("=" * 50)
print("BASELINE MODEL EVALUATION")
print("=" * 50)
print(f"MAE: {mae:.2f} thousand USD")
print(f"RMSE: {rmse:.2f} thousand USD")
print(f"R²: {r2:.4f}")
print(f"MAPE: {mape:.1f}%")
出力:
TEXT
📖 参照専用
=
BASELINE MODEL EVALUATION
=
▶ サンプル:予測結果の可視化
PYTHON
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# (1) Actual vs Predicted scatter
axes[0].scatter(y_test, y_pred, alpha=0.5, s=20)
axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "r--", lw=2)
axes[0].set_xlabel("Actual Revenue (thousand USD)")
axes[0].set_ylabel("Predicted Revenue (thousand USD)")
axes[0].set_title("Actual vs Predicted")
# (2) Residual distribution
residuals = y_test - y_pred
axes[1].hist(residuals, bins=30, edgecolor="white", color="#2196F3")
axes[1].axvline(x=0, color="red", linestyle="--")
axes[1].set_xlabel("Residual (thousand USD)")
axes[1].set_title("Residual Distribution")
plt.tight_layout()
plt.savefig("baseline_evaluation.png", dpi=150)
出力:
TEXT
📖 参照専用
# Executed successfully
(1) ビジネス指標の解釈
| 指標 | ベースライン値 | ビジネス上の意味 | 目標 |
|---|---|---|---|
| MAE | 約12k USD | 予測の平均誤差が1.2万ドル | < 10k |
| RMSE | 約15k USD | 大きな誤差ほど重くペナルティ | < 12k |
| R² | 約0.72 | 分散の72%を説明 | > 0.85 |
| MAPE | 約15% | 平均パーセント誤差 | < 10% |
📌 ポイント: MAPEが15%ということは、Bobが月間売上100万ドルと予測した場合、誤差が最大15万ドルに達する可能性があるということです。これは在庫過多や欠品に直結します。フェーズ2〜3の高度なモデルでは、この誤差を8%以下に抑えることを目指します。
❓ よくある質問
Q なぜベースラインにLinearRegressionを使うのですか? より複雑なモデルのほうが良いのでは?
A ベースラインの目的は、パフォーマンスの下限を確立し、データが本来持つ予測可能性を理解することです。複雑なモデルは過学習を引き起こし、真のシグナルを覆い隠す可能性があります。まずはシンプルに始めてベンチマークを設け、そこから段階的に改善していきます。
Q MAEとRMSE、どちらを見るべきですか?
A 両方です。MAEは外れ値に頑健で、平均的なパフォーマンスを反映します。RMSEは大きな誤差をより重くペナルティし、最悪のケースを反映します。RMSEがMAEより大幅に大きい場合、少数の大きな個別誤差が存在することを意味します。
Q MAPE 15%は良いのですか、悪いのですか?
A ドメインによります。ECの売上予測では、MAPE < 10%が本番レベル、15%はベースラインレベルです。ただし金融の時系列データでは、MAPE < 2%で良いとされます。重要なのは、ビジネスコストに紐付けて考えることです。
Q EDAにどれくらいの時間をかけるべきですか?
A 新しいデータセットの場合、EDAはプロジェクト全体の20〜30%の時間をかけるべきです。EDAをスキップしていきなりモデリングに入ると、重要な特徴量やデータの問題を見逃す可能性があります。
Q 欠損値は中央値で補完すべきですか、平均値で補完すべきですか?
A 外れ値がある場合は中央値(頑健です)、分布がほぼ正規分布の場合は平均値を使います。KNNImputerやIterativeImputerなど、より高度な手法を使うこともできます。
Q 外れ値は削除すべきですか?
A 「真の異常」と「見かけ上の異常」を区別してください。データ入力ミス → 削除。実際の極端なイベント(ブラックフライデーなど) → 維持して、特別な特徴量でフラグを立てます。外れ値を盲目的に削除すると、情報が失われます。
📖 まとめ
- 標準的な機械学習プロジェクトのパイプライン:読み込み → EDA → クリーニング → 特徴量 → ベースライン → 評価 → 改善
- EDAの核心的な目標:分布の把握、異常の発見、強い相関を持つ特徴量の特定、ビジネス仮説の検証
- データクリーニングの4ステップ:欠損値の処理 → 外れ値の処理 → 型変換 → 特徴量のエンコーディング
- ベースラインにはLinearRegression + Pipelineを使い、パフォーマンスのベンマークを確立する
- 評価指標をビジネス上の意味に紐付ける:MAPE 15%は、100万ドルの予測に対して15万ドルの誤差を意味する
- 初級段階の学び:ツールチェーンがつながり、プロジェクトのワークフローが確立し、ベースラインが構築できた — ここから反復改善を進める
📝 練習問題
- 基礎(難易度 ⭐):自分のデータ(またはsklearnのhousingなどの組み込みデータセット)を使って、パイプライン全体を完成させてください。読み込み → EDA → ベースラインモデル → 評価。ヒント:セクション4〜6のコードテンプレートを参照してください。
- 中級(難易度 ⭐⭐):ベースラインモデルに2つの新しい特徴量(例:
ad_spend * is_promotionの交互作用項)を追加し、R²とMAPEが改善するかどうかを比較してください。ヒント:df.assign()を使って新しい列を追加します。 - チャレンジ(難易度 ⭐⭐⭐):シンプルなモデル比較フレームワークを実装してください。LinearRegression、DecisionTreeRegressor、RandomForestRegressorを同時に学習し、各モデルのMAE/RMSE/R²/MAPEの比較表を出力します。ヒント:辞書を使ってモデルをループし、結果をDataFrameにまとめます。