Sklearn Random Forest: Pythonによる分類と回帰の完全ガイド
Updated on
決定木を構築し、訓練データで95%の精度を達成したが、新しいデータでは62%しか出なかったという経験はありませんか?単一の決定木は訓練セットを暗記してしまいます。すべての分割、すべての葉が見たサンプルに最適化されています。その結果、紙の上では素晴らしく見えるモデルが、本番環境では失敗してしまうのです。
この過学習の問題は単なる学術的な話ではありません。開発用ノートブックでは良好に動作するが、実データでは信頼できない予測を生成するモデルを展開するチームが存在します。単一の決定木は分散が高く、訓練データのわずかな変化で木の構造が完全に変わってしまいます。訓練データに対してこれほど敏感なモデルを信頼することはできません。
Random Forestは、データと特徴量のランダムなサブセットに対して数百の決定木を構築し、多数決(分類)または平均化(回帰)を通じて予測を統合することでこれを解決します。このアンサンブル手法により、分散を劇的に削減しながら精度を維持します。Scikit-learnのRandomForestClassifierとRandomForestRegressorは、組み込みの特徴量の重要性、out-of-bag評価、並列訓練を備えた、本番環境に適した実装を提供します。
Random Forestとは何か?
Random Forestは、複数の決定木を組み合わせてより堅牢な予測を生成するアンサンブル学習手法です。bagging(Bootstrap Aggregating)という技術を使用します:
- Bootstrap sampling(ブートストラップサンプリング): 復元抽出によるランダムサンプリングで訓練データの複数のサブセットを作成します。各サブセットは元のデータの約63%です。
- Random feature selection(ランダム特徴量選択): 各木の各分割において、ランダムな特徴量サブセットのみを考慮します(通常、分類では
sqrt(n_features)、回帰ではn_features/3)。 - Independent training(独立した訓練): ランダム特徴量制約のもと、各ブートストラップサンプルで決定木を訓練します。
- Aggregation(集約): 多数決(分類)または平均(回帰)によって予測を統合します。
データサンプリングと特徴量選択の両方におけるランダム性により、個々の木は相関が低くなります。たとえ1つの木が特定のパターンに過学習したとしても、他の木の大多数はそうではなく、アンサンブルがノイズを平均化します。
Random Forestを使用する場面
| シナリオ | Random Forest? | 理由 |
|---|---|---|
| 混合特徴量タイプを持つ表形式データ | Yes | 数値とカテゴリの特徴量を処理し、スケーリング不要 |
| 特徴量重要度ランキングが必要 | Yes | 組み込みのfeature_importances_属性 |
| 中小規模データセット(最大約10万行) | Yes | 並列処理による高速訓練 |
| 不均衡な分類 | Yes | class_weight='balanced'をサポート |
| 解釈可能な予測が必要 | Moderate | 個々の木は解釈可能だが、アンサンブルはやや困難 |
| 非常に高次元の疎データ(テキスト) | No | 線形モデルや勾配ブースティングの方が一般的に優秀 |
| 厳密なレイテンシー要件のあるリアルタイム推論 | Careful | 大規模なフォレストは推論時に遅くなる可能性 |
RandomForestClassifier: 分類の例
ワインデータセットを使用した完全な分類例を示します:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
from sklearn.datasets import load_wine
# Load dataset
wine = load_wine()
X, y = wine.data, wine.target
feature_names = wine.feature_names
print(f"Dataset: {X.shape[0]} samples, {X.shape[1]} features")
print(f"Classes: {wine.target_names}")
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Train Random Forest
rf = RandomForestClassifier(
n_estimators=100,
max_depth=None,
min_samples_split=2,
min_samples_leaf=1,
random_state=42,
n_jobs=-1
)
rf.fit(X_train, y_train)
# Evaluate
y_pred = rf.predict(X_test)
print(f"\nAccuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"\nClassification Report:")
print(classification_report(y_test, y_pred, target_names=wine.target_names))主要なパラメータの解説
| パラメータ | デフォルト | 説明 | チューニングのヒント |
|---|---|---|---|
n_estimators | 100 | フォレスト内の木の数 | 木が多いほど性能は向上するが遅くなる。100-500が一般的。 |
max_depth | None | 各木の最大深さ | Noneは完全に成長することを意味する。過学習を減らすため10-30を設定。 |
min_samples_split | 2 | ノードを分割する最小サンプル数 | ノイズの多いデータでの過学習防止のため5-20に増加。 |
min_samples_leaf | 1 | 葉ノードの最小サンプル数 | より滑らかな予測のため2-10に増加。 |
max_features | 'sqrt' | 各分割で考慮される特徴量 | 分類では'sqrt'、'log2'または割合を代替案として。 |
bootstrap | True | ブートストラップサンプリングを使用 | 小さなデータセットではFalseにして各木で全データを使用。 |
class_weight | None | 各クラスの重み | 不均衡データセットでは'balanced'を使用。 |
n_jobs | None | 並列ジョブの数 | -1に設定してすべてのCPUコアを使用。 |
oob_score | False | out-of-bagサンプルを評価に使用 | Trueに設定してホールドアウトセットなしで組み込み検証推定を取得。 |
Out-of-Bag (OOB) スコア
各木は約63%のデータで訓練されます。残りの37%(out-of-bagサンプル)は無料の検証セットとして使用できます:
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
rf = RandomForestClassifier(
n_estimators=200,
oob_score=True,
random_state=42,
n_jobs=-1
)
rf.fit(X_train, y_train)
print(f"OOB Score: {rf.oob_score_:.4f}")
print(f"Test Score: {rf.score(X_test, y_test):.4f}")OOBスコアは、別のホールドアウトセットを必要とせずに検証推定を提供します。データが限られている場合に特に有用です。
RandomForestRegressor: 回帰の例
Random Forest回帰は、すべての木の出力を平均化することで連続値を予測します:
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.datasets import fetch_california_housing
import numpy as np
# Load California housing dataset
housing = fetch_california_housing()
X, y = housing.data, housing.target
feature_names = housing.feature_names
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# Train regressor
rf_reg = RandomForestRegressor(
n_estimators=200,
max_depth=20,
min_samples_leaf=5,
random_state=42,
n_jobs=-1
)
rf_reg.fit(X_train, y_train)
y_pred = rf_reg.predict(X_test)
# Evaluation metrics
r2 = r2_score(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
mae = mean_absolute_error(y_test, y_pred)
print(f"R-squared: {r2:.4f}")
print(f"RMSE: {rmse:.4f}")
print(f"MAE: {mae:.4f}")回帰モデルの比較
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score, train_test_split
from sklearn.datasets import fetch_california_housing
import numpy as np
housing = fetch_california_housing()
X_train, X_test, y_train, y_test = train_test_split(
housing.data, housing.target, test_size=0.2, random_state=42
)
regressors = {
'Linear Regression': LinearRegression(),
'Ridge': Ridge(alpha=1.0),
'Decision Tree': DecisionTreeRegressor(max_depth=10, random_state=42),
'Random Forest': RandomForestRegressor(n_estimators=100, max_depth=20, random_state=42, n_jobs=-1),
'Gradient Boosting': GradientBoostingRegressor(n_estimators=100, max_depth=5, random_state=42),
}
print(f"{'Model':<25} {'CV R² (mean)':>12} {'CV R² (std)':>12}")
print("-" * 52)
for name, model in regressors.items():
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='r2', n_jobs=-1)
print(f"{name:<25} {scores.mean():>12.4f} {scores.std():>12.4f}")Random Forestは通常、非線形関係を持つデータセットで単一の決定木や線形モデルを上回り、勾配ブースティングと競合します。
ハイパーパラメータのチューニング
GridSearchCV: 網羅的な探索
GridSearchCVは、指定されたパラメータ値のすべての組み合わせをテストします:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.datasets import load_wine
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [None, 10, 20],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4],
}
rf = RandomForestClassifier(random_state=42, n_jobs=-1)
grid_search = GridSearchCV(
rf,
param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1,
verbose=1
)
grid_search.fit(X_train, y_train)
print(f"Best Parameters: {grid_search.best_params_}")
print(f"Best CV Score: {grid_search.best_score_:.4f}")
print(f"Test Score: {grid_search.score(X_test, y_test):.4f}")RandomizedSearchCV: 効率的な探索
パラメータ空間が大きい場合、RandomizedSearchCVはすべてを試す代わりに、固定数のパラメータ組み合わせをサンプリングします:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV, train_test_split
from sklearn.datasets import load_wine
from scipy.stats import randint, uniform
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
param_distributions = {
'n_estimators': randint(50, 500),
'max_depth': [None, 5, 10, 15, 20, 30],
'min_samples_split': randint(2, 20),
'min_samples_leaf': randint(1, 10),
'max_features': ['sqrt', 'log2', 0.3, 0.5, 0.7],
'bootstrap': [True, False],
}
rf = RandomForestClassifier(random_state=42, n_jobs=-1)
random_search = RandomizedSearchCV(
rf,
param_distributions,
n_iter=100,
cv=5,
scoring='accuracy',
random_state=42,
n_jobs=-1,
verbose=1
)
random_search.fit(X_train, y_train)
print(f"Best Parameters: {random_search.best_params_}")
print(f"Best CV Score: {random_search.best_score_:.4f}")
print(f"Test Score: {random_search.score(X_test, y_test):.4f}")チューニングにおけるパラメータの重要性
すべてのパラメータが同じ影響を持つわけではありません。最も重要なパラメータにチューニング予算を集中させてください:
| パラメータ | 影響 | 優先度 | 備考 |
|---|---|---|---|
n_estimators | 高 | 1番目 | 木が多いほど減少収益(〜200-500)までほぼ常に役立つ |
max_depth | 高 | 2番目 | 過学習を直接制御。None、10、20、30を試す |
min_samples_leaf | 中 | 3番目 | 予測を平滑化。1、2、5、10を試す |
max_features | 中 | 4番目 | 木の多様性を制御。分類では'sqrt'が通常良好 |
min_samples_split | 低 | 5番目 | 実際にはmin_samples_leafより影響が少ない |
bootstrap | 低 | 6番目 | Trueがほぼ常に優秀。非常に小さいデータセットでのみFalseを試す |
特徴量の重要性
Random Forestの最も強力な利点の1つは、組み込みの特徴量の重要性です。予測を左右する特徴量を理解することは、モデルの解釈、特徴量選択、ドメイン知見に役立ちます。
不純度に基づく特徴量の重要性
デフォルトのfeature_importances_属性は、すべての木を通じて各特徴量が不純度(分類ではGini、回帰では分散)をどれだけ減少させたかを測定します:
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
# Get feature importances
importances = rf.feature_importances_
feature_names = wine.feature_names
indices = np.argsort(importances)[::-1]
# Print ranked features
print("Feature Ranking:")
for i, idx in enumerate(indices):
print(f" {i+1}. {feature_names[idx]:25s} ({importances[idx]:.4f})")
# Plot
plt.figure(figsize=(10, 6))
plt.barh(range(len(indices)), importances[indices[::-1]], align='center')
plt.yticks(range(len(indices)), [feature_names[i] for i in indices[::-1]])
plt.xlabel('Feature Importance (Gini)')
plt.title('Random Forest Feature Importance - Wine Dataset')
plt.tight_layout()
plt.savefig('rf_feature_importance.png', dpi=150)
plt.show()順列重要性(Permutation Importance)
不純度ベースの重要性は、高カーディナリティ特徴量に偏る可能性があります。順列重要性は、特徴量の値をランダムにシャッフルしたときのモデル性能の低下を測定します:
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
# Compute permutation importance on the test set
perm_imp = permutation_importance(
rf, X_test, y_test,
n_repeats=30,
random_state=42,
n_jobs=-1
)
# Sort and display
sorted_idx = perm_imp.importances_mean.argsort()[::-1]
print("Permutation Importance (test set):")
for idx in sorted_idx:
mean = perm_imp.importances_mean[idx]
std = perm_imp.importances_std[idx]
print(f" {wine.feature_names[idx]:25s}: {mean:.4f} +/- {std:.4f}")
# Plot with error bars
plt.figure(figsize=(10, 6))
plt.barh(
range(len(sorted_idx)),
perm_imp.importances_mean[sorted_idx[::-1]],
xerr=perm_imp.importances_std[sorted_idx[::-1]],
align='center'
)
plt.yticks(range(len(sorted_idx)), [wine.feature_names[i] for i in sorted_idx[::-1]])
plt.xlabel('Decrease in Accuracy')
plt.title('Permutation Importance - Wine Dataset')
plt.tight_layout()
plt.savefig('rf_permutation_importance.png', dpi=150)
plt.show()どの重要度手法を使うべきか?
| メソッド | メリット | デメリット | 最適な用途 |
|---|---|---|---|
不純度ベース (feature_importances_) | 高速、追加計算不要 | 高カーディナリティ特徴量に偏る | 迅速なスクリーニング、初期探索 |
| 順列重要性 | 偏りがない、テストデータで動作 | 遅い、相関する特徴量の影響を受ける | 最終的な特徴量選択、レポート |
Random Forestでの交差検証
基本的な交差検証
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_wine
wine = load_wine()
X, y = wine.data, wine.target
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')
print(f"CV Accuracy: {scores.mean():.4f} (+/- {scores.std():.4f})")
print(f"Per-fold: {scores}")不均衡データのためのStratifiedKFold
StratifiedKFoldは各フォールドでクラス分布を保持し、不均衡データセットにとって重要です:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.datasets import load_wine
import numpy as np
wine = load_wine()
X, y = wine.data, wine.target
# Stratified 10-fold cross-validation
skf = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
scores = cross_val_score(rf, X, y, cv=skf, scoring='accuracy')
print(f"Stratified 10-Fold Accuracy: {scores.mean():.4f} (+/- {scores.std():.4f})")
# Multiple metrics
from sklearn.model_selection import cross_validate
results = cross_validate(
rf, X, y, cv=skf,
scoring=['accuracy', 'f1_weighted', 'precision_weighted', 'recall_weighted'],
n_jobs=-1
)
for metric in ['test_accuracy', 'test_f1_weighted', 'test_precision_weighted', 'test_recall_weighted']:
vals = results[metric]
name = metric.replace('test_', '')
print(f"{name:>20s}: {vals.mean():.4f} (+/- {vals.std():.4f})")不均衡データの処理
あるクラスが他のクラスよりはるかに多いサンプルを持つ場合、モデルは常に多数派クラスを予測することで高い精度を達成できます。Random Forestはこれを処理するためのいくつかのツールを提供します。
class_weight='balanced'の使用
class_weight='balanced'パラメータは、クラス頻度に反比例して重みを自動的に調整します:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import make_classification
# Create imbalanced dataset (95% class 0, 5% class 1)
X, y = make_classification(
n_samples=2000,
n_features=20,
weights=[0.95, 0.05],
flip_y=0,
random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# Without class weight
rf_default = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf_default.fit(X_train, y_train)
print("=== Without class_weight ===")
print(classification_report(y_test, rf_default.predict(X_test)))
# With balanced class weight
rf_balanced = RandomForestClassifier(
n_estimators=200,
class_weight='balanced',
random_state=42,
n_jobs=-1
)
rf_balanced.fit(X_train, y_train)
print("=== With class_weight='balanced' ===")
print(classification_report(y_test, rf_balanced.predict(X_test)))オーバーサンプリングのためのSMOTE統合
SMOTE(Synthetic Minority Oversampling Technique)は少数派クラスの合成サンプルを作成します。imblearnのパイプラインと共に使用します:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline as ImbPipeline
# Create imbalanced dataset
X, y = make_classification(
n_samples=2000,
n_features=20,
weights=[0.95, 0.05],
flip_y=0,
random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# SMOTE + Random Forest pipeline
pipeline = ImbPipeline([
('smote', SMOTE(random_state=42)),
('rf', RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1))
])
pipeline.fit(X_train, y_train)
print("=== SMOTE + Random Forest ===")
print(classification_report(y_test, pipeline.predict(X_test)))モデル評価
分類レポートと混同行列
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import (
classification_report, confusion_matrix,
ConfusionMatrixDisplay, accuracy_score
)
from sklearn.datasets import load_wine
import matplotlib.pyplot as plt
wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(
wine.data, wine.target, test_size=0.2, random_state=42, stratify=wine.target
)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
# Metrics
print(f"Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"\n{classification_report(y_test, y_pred, target_names=wine.target_names)}")
# Confusion matrix plot
cm = confusion_matrix(y_test, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=wine.target_names)
disp.plot(cmap='Blues')
plt.title('Random Forest - Wine Classification')
plt.tight_layout()
plt.savefig('rf_confusion_matrix.png', dpi=150)
plt.show()二値分類のためのROC曲線
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_curve, roc_auc_score, RocCurveDisplay
from sklearn.datasets import load_breast_cancer
import matplotlib.pyplot as plt
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
cancer.data, cancer.target, test_size=0.2, random_state=42, stratify=cancer.target
)
rf = RandomForestClassifier(n_estimators=200, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
# Predict probabilities
y_prob = rf.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, y_prob)
# Plot ROC curve
RocCurveDisplay.from_estimator(rf, X_test, y_test)
plt.title(f'Random Forest ROC Curve (AUC = {auc:.4f})')
plt.tight_layout()
plt.savefig('rf_roc_curve.png', dpi=150)
plt.show()Random Forestと他のアルゴリズムの比較
| 特徴 | Random Forest | XGBoost | Gradient Boosting | Decision Tree |
|---|---|---|---|---|
| アンサンブルタイプ | Bagging(並列) | Boosting(逐次) | Boosting(逐次) | 単一モデル |
| 精度 | 高い | 非常に高い | 非常に高い | 中程度 |
| 訓練速度 | 高速(並列化可能) | 中程度 | 遅い(逐次) | 非常に高速 |
| 予測速度 | 中程度 | 高速 | 中程度 | 非常に高速 |
| 過学習リスク | 低い | 低い(チューニング時) | 低い(チューニング時) | 高い |
| ハイパーパラメータ感度 | 低い | 高い | 高い | 中程度 |
| 特徴量スケーリングが必要 | 不要 | 不要 | 不要 | 不要 |
| 欠損値の処理 | 不可(補完が必要) | 可(組み込み) | 不可(補完が必要) | 不可 |
| 組み込み特徴量の重要性 | あり | あり | あり | あり |
| 解釈可能性 | 中程度 | 低い | 低い | 高い |
| 最適な用途 | 汎用、最初のモデル | Kaggleコンペ、最大精度 | 構造化表形式データ | 迅速なベースライン、小データセット |
Random Forestを代替案より選ぶ場面:
- 最小限のチューニングで強力なベースラインモデルが必要な場合
- 訓練速度が重要で、複数のCPUコアを持っている場合
- 信頼性の高い特徴量の重要性推定が必要な場合
- ブースティング手法が提供する最後の0.5%の精度を追求していない場合
実世界のパイプライン: エンドツーエンドの例
このパイプラインは、前処理、特徴量エンジニアリング、モデル訓練、評価、予測を本番環境スタイルのワークフローで組み合わせます:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.datasets import load_breast_cancer
import numpy as np
import pandas as pd
# Load and prepare data
cancer = load_breast_cancer()
df = pd.DataFrame(cancer.data, columns=cancer.feature_names)
df['target'] = cancer.target
# Introduce some missing values to simulate real data
np.random.seed(42)
mask = np.random.random(df.shape) < 0.05
df_missing = df.mask(mask.astype(bool))
df_missing['target'] = cancer.target # Keep target clean
X = df_missing.drop('target', axis=1)
y = df_missing['target']
# Split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
# Build preprocessing + model pipeline
numeric_features = X.columns.tolist()
numeric_transformer = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
]
)
pipeline = Pipeline([
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(
n_estimators=300,
max_depth=20,
min_samples_leaf=2,
class_weight='balanced',
random_state=42,
n_jobs=-1
))
])
# Cross-validation
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(pipeline, X_train, y_train, cv=skf, scoring='accuracy')
print(f"Cross-validation accuracy: {cv_scores.mean():.4f} (+/- {cv_scores.std():.4f})")
# Train final model
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
# Evaluation
print(f"\nTest Set Results:")
print(classification_report(y_test, y_pred, target_names=cancer.target_names))
# Make predictions on new data
sample = X_test.iloc[:3]
predictions = pipeline.predict(sample)
probabilities = pipeline.predict_proba(sample)
print(f"\nSample Predictions:")
for i, (pred, prob) in enumerate(zip(predictions, probabilities)):
class_name = cancer.target_names[pred]
confidence = prob[pred]
print(f" Sample {i+1}: {class_name} (confidence: {confidence:.2%})")モデルの保存と読み込み
import joblib
# Save the trained pipeline
joblib.dump(pipeline, 'rf_pipeline.joblib')
# Load and use later
loaded_pipeline = joblib.load('rf_pipeline.joblib')
new_predictions = loaded_pipeline.predict(X_test[:5])
print(f"Loaded model predictions: {new_predictions}")PyGWalkerを使った結果の探索
Random Forestモデルを訓練した後、特徴量の重要性パターン、予測分布、誤分類ケースを詳細に調べる必要があることがよくあります。PyGWalker (opens in a new tab)を使用すると、結果のDataFrameをJupyter内でTableauのようなインタラクティブな探索インターフェースに直接変換できます:
import pandas as pd
import pygwalker as pyg
# Build a results DataFrame
results = pd.DataFrame(X_test.values, columns=cancer.feature_names)
results['actual'] = y_test.values
results['predicted'] = y_pred
results['correct'] = y_test.values == y_pred
results['prob_malignant'] = pipeline.predict_proba(X_test)[:, 0]
results['prob_benign'] = pipeline.predict_proba(X_test)[:, 1]
# Launch interactive exploration
walker = pyg.walk(results)特徴量を軸にドラッグし、誤分類されたサンプルでフィルタリングし、予測信頼度でカラーコーディングして、モデルが苦労する場所を特定します。このような視覚的分析は、どの特徴量をエンジニアリングすべきか、またはどのサンプルを詳細に調べる必要があるかを決定するのに役立ちます。
データの読み込みからモデルの比較、最終評価までの完全なML実験ワークフローを実行するには、RunCell (opens in a new tab)が実験の迅速な反復、評価コードの自動生成、ノートブックワークフローの管理を支援するAI搭載のJupyter環境を提供します。
FAQ
Random Forestではいくつの木を使用すべきですか?
100-200の木から始めてください。精度は一般的に木の数が増えると改善しますが、ある時点で頭打ちになります。クロスバリデーションを使用して最適な点を見つけてください。500木を超えると、利得は通常無視できるものとなり、訓練時間が増加します。n_estimatorsを増やす際にOOBスコアを監視し、改善が止まった時点で十分な木が得られます。
Random Forestには特徴量スケーリングが必要ですか?
いいえ。Random Forestは特徴量値の閾値に基づいて分割を行うため、特徴量の絶対的なスケールは分割決定に影響しません。ロジスティック回帰、SVM、またはニューラルネットワークとは異なり、Random Forestは異なる範囲の特徴量を自然に処理します。ただし、パイプラインに他のコンポーネント(PCAや距離ベースの前処理など)が含まれる場合、それらのステップではスケーリングが必要になる場合があります。
Random Forestは欠損値をどのように処理しますか?
Scikit-learnのRandomForestClassifierおよびRandomForestRegressorは、欠損値をネイティブに処理しません。訓練前に欠損データを補完する必要があります。数値特徴量にはSimpleImputerを中央値または平均値戦略で使用するか、IterativeImputerなどのより高度な補完方法を使用します。H2OやLightGBMなどの他の実装では、欠損値を直接処理できます。
Random ForestとGradient Boostingの違いは何ですか?
Random Forestは並列に独立して木を構築します(bagging)が、Gradient Boostingは逐次的に木を構築し、各木が前の木のエラーを修正します(boosting)。Random Forestは分散を減らし、Gradient Boostingはバイアスを減らします。実際には、Gradient Boosting(特にXGBoost)はわずかに高い精度を達成することが多いですが、Random Forestはチューニングが容易で過学習しにくいです。
Random Forestは特徴量選択に使用できますか?
はい。素早いランキングにはfeature_importances_を、より信頼性の高い推定にはpermutation_importanceを使用します。次に、重要度の低い特徴量を削除して再訓練できます。または、パイプライン内でSelectFromModelをRandom Forest推定器と共に使用して、閾値を超える特徴量を自動的に選択することもできます。
結論
Random Forestは、機械学習で最も信頼性が高く多用途なアルゴリズムの1つです。数百の無相関な決定木を組み合わせることで過学習を減らし、特徴量スケーリングなしで分類と回帰の両方を処理し、組み込みの特徴量の重要性ランキングを提供します。ほとんどの表形式データ問題に対して、本番環境で十分に機能する優れた最初のモデルとなります。
RandomForestClassifierまたはRandomForestRegressorをデフォルトパラメータでベースラインとして始めてください。減少収益分析のために最初にn_estimatorsをチューニングし、次に過学習を制御するためにmax_depthとmin_samples_leafを調整します。不均衡データにはclass_weight='balanced'を、信頼性の高い特徴量ランキングには順列重要性を、堅牢な評価にはStratifiedKFold交差検証を使用してください。構造化データで絶対に最高の精度が必要な場合はGradient BoostingやXGBoostを検討してくださいが、Random Forestは悪く失敗することがめったにない最も安全なデフォルト選択肢のままです。