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

⚙️ STEP 24: ハイパーパラメータチューニング

Grid Search、Random Search、Optunaで最適なパラメータを見つける

📋 このステップで学ぶこと

  • ハイパーパラメータとは
  • Grid Search(グリッドサーチ)
  • Randomized Search(ランダムサーチ)
  • ベイズ最適化(Optuna)
  • チューニングの実践的なテクニック

演習問題: 6問

🎯 1. ハイパーパラメータとは

📊 パラメータ vs ハイパーパラメータ

パラメータ:モデルが学習するもの(重み、係数など)
ハイパーパラメータ:人間が事前に設定するもの(学習率、木の深さなど)

【主要なハイパーパラメータ例】 決定木・ランダムフォレスト: – max_depth(木の深さ) – min_samples_split(分割に必要な最小サンプル数) – n_estimators(木の数) SVM: – C(正則化パラメータ) – gamma(カーネル係数) – kernel(カーネルの種類) ロジスティック回帰: – C(正則化の強さ) – penalty(正則化の種類) 勾配ブースティング: – learning_rate(学習率) – n_estimators(木の数) – max_depth(木の深さ)
💡 なぜチューニングが必要?

ハイパーパラメータの設定によって、モデルの性能は大きく変わります。デフォルト値でも動きますが、チューニングにより5〜10%以上の性能向上が期待できることもあります。

📊 2. Grid Search(グリッドサーチ)

🔍 Grid Searchとは

指定したパラメータの全ての組み合わせを試す方法。
確実に最適な組み合わせを見つけられるが、組み合わせが多いと時間がかかる。

GridSearchCVの引数を理解しよう

GridSearchCVの主な引数を順番に見ていきましょう:

  • estimator:チューニングするモデル(ここではRandomForestClassifier)
  • param_grid:探索するパラメータの辞書。キーがパラメータ名、値が試す値のリスト
  • cv=5:5-Fold交差検証で各組み合わせを評価
  • scoring='accuracy':正解率を評価指標として使用
  • n_jobs=-1:全CPUコアを使って並列処理(高速化)
  • verbose=1:進捗状況を表示(0=非表示、1=簡易、2=詳細)

ステップ1:データの準備

まず、データを読み込みます。

# GridSearchCVの基本実装 from sklearn.model_selection import GridSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.datasets import load_breast_cancer import numpy as np # データを読み込む # 乳がんデータセット: 569サンプル、30特徴量、2クラス(良性/悪性) cancer = load_breast_cancer() X, y = cancer.data, cancer.target print(f”データの形状: {X.shape}”) print(f”クラス分布: {np.bincount(y)}”) # [212 357]

ステップ2:パラメータグリッドの定義

探索するパラメータを辞書形式で定義します。

各パラメータの意味を理解しましょう:

  • n_estimators:ランダムフォレストの木の数。多いほど安定するが、計算時間も増える
  • max_depth:各木の最大深さ。深いほど複雑なパターンを学習できるが、過学習のリスクも増える
  • min_samples_split:ノードを分割するために必要な最小サンプル数。大きいほど過学習を防ぐ
# 探索するパラメータの範囲を定義 # 辞書形式: {パラメータ名: [試す値のリスト]} param_grid = { ‘n_estimators’: [50, 100, 200], # 木の数: 3パターン ‘max_depth’: [3, 5, 7, None], # 木の深さ: 4パターン(Noneは制限なし) ‘min_samples_split’: [2, 5, 10] # 分割の最小サンプル: 3パターン } # 組み合わせ数を確認 # 3 × 4 × 3 = 36通りの組み合わせ n_combinations = 3 * 4 * 3 print(f”探索する組み合わせ数: {n_combinations}”) print(f”5-Fold CVなので、合計 {n_combinations * 5} 回のモデル訓練が必要”)

ステップ3:GridSearchCVの実行

GridSearchCVオブジェクトを作成して実行します。

# GridSearchCVオブジェクトを作成 grid_search = GridSearchCV( estimator=RandomForestClassifier(random_state=42), # チューニングするモデル param_grid=param_grid, # 探索するパラメータ cv=5, # 5-Fold交差検証 scoring=’accuracy’, # 評価指標(正解率) n_jobs=-1, # 全CPUコアを使用(高速化) verbose=1 # 進捗を表示 ) # 探索を実行 # 全ての組み合わせを試し、最適なパラメータを見つける grid_search.fit(X, y)
Fitting 5 folds for each of 36 candidates, totalling 180 fits

ステップ4:結果の確認

最適なパラメータと最高スコアを確認します。

# 結果の確認 print(“=” * 50) print(“Grid Search 結果”) print(“=” * 50) # best_params_: 最適なパラメータの組み合わせ print(f”\n最適なパラメータ: {grid_search.best_params_}”) # best_score_: 最適パラメータでの交差検証スコア print(f”最高スコア: {grid_search.best_score_:.4f}”) # best_estimator_: 最適パラメータで訓練済みのモデル best_model = grid_search.best_estimator_ print(f”\n最適なモデル: {best_model}”)
================================================== Grid Search 結果 ================================================== 最適なパラメータ: {‘max_depth’: 7, ‘min_samples_split’: 2, ‘n_estimators’: 200} 最高スコア: 0.9649

🎲 3. Randomized Search(ランダムサーチ)

🔍 Randomized Searchとは

パラメータ空間からランダムに組み合わせをサンプリングする方法。
Grid Searchより高速で、連続値のパラメータにも対応できる。

scipy.statsの確率分布関数を理解しよう

Randomized Searchでは、パラメータの範囲を確率分布で指定します:

関数 説明 使用例
randint(low, high) low以上high未満の整数をランダムに生成 randint(50, 300) → 50〜299の整数
uniform(loc, scale) loc から loc+scale までの連続値をランダムに生成 uniform(0.1, 0.9) → 0.1〜1.0の実数
💡 なぜ確率分布を使うのか?

Grid Searchでは指定した値しか試せませんが、確率分布を使うと「50〜300の間の任意の整数」のように連続的な範囲から探索できます。これにより、Grid Searchでは見つけられない最適値を発見できる可能性があります。

ステップ1:確率分布のインポートとパラメータ定義

# RandomizedSearchCVの実装 from sklearn.model_selection import RandomizedSearchCV from scipy.stats import uniform, randint # 確率分布関数をインポート # 探索するパラメータの分布を定義 param_distributions = { # randint(50, 300): 50以上300未満の整数からランダムにサンプリング ‘n_estimators’: randint(50, 300), # リスト: Grid Searchと同じように離散値から選択 ‘max_depth’: [3, 5, 7, 10, None], # randint(2, 20): 2以上20未満の整数 ‘min_samples_split’: randint(2, 20), # randint(1, 10): 1以上10未満の整数 ‘min_samples_leaf’: randint(1, 10), # uniform(0.1, 0.9): 0.1から1.0の連続値 # 注意: uniform(loc, scale)は loc〜loc+scale の範囲 ‘max_features’: uniform(0.1, 0.9) } print(“パラメータ分布の例:”) print(f” n_estimators: {[randint(50, 300).rvs() for _ in range(5)]}”) print(f” max_features: {[round(uniform(0.1, 0.9).rvs(), 3) for _ in range(5)]}”)

ステップ2:RandomizedSearchCVの実行

n_iterで試行回数を指定します。Grid Searchと違い、全組み合わせを試す必要がありません。

# RandomizedSearchCVオブジェクトを作成 random_search = RandomizedSearchCV( estimator=RandomForestClassifier(random_state=42), param_distributions=param_distributions, # 確率分布を指定 n_iter=50, # 50回だけランダムにサンプリング cv=5, # 5-Fold交差検証 scoring=’accuracy’, n_jobs=-1, random_state=42, # 再現性のため乱数シードを固定 verbose=1 ) # 探索を実行 random_search.fit(X, y) print(“=” * 50) print(“Randomized Search 結果”) print(“=” * 50) print(f”\n最適なパラメータ: {random_search.best_params_}”) print(f”最高スコア: {random_search.best_score_:.4f}”)
================================================== Randomized Search 結果 ================================================== 最適なパラメータ: {‘max_depth’: 10, ‘max_features’: 0.45, ‘min_samples_leaf’: 1, ‘min_samples_split’: 5, ‘n_estimators’: 234} 最高スコア: 0.9667
✅ Grid Search vs Randomized Search の使い分け
  • Grid Search:パラメータの組み合わせが少ない場合(〜100程度)。確実に最適解を見つけたい場合
  • Randomized Search:パラメータの組み合わせが多い場合、連続値パラメータがある場合。計算時間を短縮したい場合

🧠 4. ベイズ最適化(Optuna)

🔍 ベイズ最適化とは

過去の試行結果を使って、次に試すべきパラメータを賢く選ぶ方法。
Grid SearchやRandom Searchより効率的に最適解を見つけられる。

Optunaのsuggest_*メソッドを理解しよう

Optunaでは、trialオブジェクトのsuggest_*メソッドでパラメータを提案させます:

メソッド 説明 使用例
suggest_int(name, low, high) 整数値を提案 suggest_int('n_estimators', 50, 300)
suggest_float(name, low, high) 浮動小数点数を提案 suggest_float('learning_rate', 0.01, 1.0)
suggest_float(..., log=True) 対数スケールで提案(学習率など桁が変わるパラメータに有効) suggest_float('C', 0.01, 100, log=True)
suggest_categorical(name, choices) カテゴリ値から選択 suggest_categorical('kernel', ['linear', 'rbf'])

ステップ1:目的関数の定義

目的関数は「パラメータを受け取り、スコアを返す」関数です。

# Optunaのインストール(Google Colabの場合) # !pip install optuna import optuna from sklearn.model_selection import cross_val_score def objective(trial): “”” Optunaの目的関数 Parameters: ———– trial : optuna.trial.Trial パラメータを提案するためのオブジェクト Returns: ——– float : 交差検証のスコア(最大化したい値) “”” # suggest_*メソッドでパラメータを提案させる params = { # suggest_int: 50〜300の整数を提案 ‘n_estimators’: trial.suggest_int(‘n_estimators’, 50, 300), # suggest_int: 3〜15の整数を提案 ‘max_depth’: trial.suggest_int(‘max_depth’, 3, 15), # suggest_int: 2〜20の整数を提案 ‘min_samples_split’: trial.suggest_int(‘min_samples_split’, 2, 20), # suggest_int: 1〜10の整数を提案 ‘min_samples_leaf’: trial.suggest_int(‘min_samples_leaf’, 1, 10), # suggest_float: 0.1〜1.0の浮動小数点数を提案 ‘max_features’: trial.suggest_float(‘max_features’, 0.1, 1.0) } # 提案されたパラメータでモデルを作成 model = RandomForestClassifier(**params, random_state=42) # 交差検証でスコアを計算 scores = cross_val_score(model, X, y, cv=5, scoring=’accuracy’) # 平均スコアを返す(Optunaはこの値を最大化/最小化する) return scores.mean()

ステップ2:最適化の実行

# Studyオブジェクトを作成 # direction=’maximize’: スコアを最大化(’minimize’なら最小化) study = optuna.create_study(direction=’maximize’) # 最適化を実行 # n_trials: 試行回数 # show_progress_bar: 進捗バーを表示 study.optimize(objective, n_trials=50, show_progress_bar=True) print(“=” * 50) print(“Optuna 結果”) print(“=” * 50) # best_params: 最適なパラメータ print(f”\n最適なパラメータ: {study.best_params}”) # best_value: 最高スコア print(f”最高スコア: {study.best_value:.4f}”)
================================================== Optuna 結果 ================================================== 最適なパラメータ: {‘n_estimators’: 245, ‘max_depth’: 8, ‘min_samples_split’: 3, ‘min_samples_leaf’: 1, ‘max_features’: 0.523} 最高スコア: 0.9684

ステップ3:最適化の可視化(オプション)

Optunaには便利な可視化機能があります:

# 最適化の履歴を可視化 # 試行回数ごとのスコアの推移がわかる fig = optuna.visualization.plot_optimization_history(study) fig.show() # パラメータの重要度を可視化 # どのパラメータがスコアに最も影響するかがわかる fig = optuna.visualization.plot_param_importances(study) fig.show() # パラメータ間の関係を可視化 fig = optuna.visualization.plot_contour(study, params=[‘n_estimators’, ‘max_depth’]) fig.show()
💡 Optunaの便利な機能
  • 早期停止(Pruning):見込みのない試行を途中で打ち切り、計算時間を節約
  • 可視化:最適化の履歴やパラメータの重要度をグラフ化
  • 並列実行:複数のジョブを同時に実行
  • 再開可能:途中から最適化を再開できる

💼 5. 実践的なテクニック

段階的なチューニング

# 段階的にチューニング(2段階アプローチ) # Step 1: 粗い探索(広い範囲で少ない候補) param_grid_coarse = { ‘n_estimators’: [50, 100, 200], ‘max_depth’: [3, 7, 15, None] } grid_coarse = GridSearchCV( RandomForestClassifier(random_state=42), param_grid_coarse, cv=3, n_jobs=-1 ) grid_coarse.fit(X, y) print(f”Step 1 – 最適: {grid_coarse.best_params_}”) # Step 2: 細かい探索(最適値周辺で多くの候補) best_n = grid_coarse.best_params_[‘n_estimators’] best_depth = grid_coarse.best_params_[‘max_depth’] param_grid_fine = { ‘n_estimators’: [best_n – 50, best_n, best_n + 50], ‘max_depth’: [best_depth – 2, best_depth, best_depth + 2] if best_depth else [10, 15, None], ‘min_samples_split’: [2, 5, 10] } grid_fine = GridSearchCV( RandomForestClassifier(random_state=42), param_grid_fine, cv=5, n_jobs=-1 ) grid_fine.fit(X, y) print(f”Step 2 – 最適: {grid_fine.best_params_}”) print(f”最高スコア: {grid_fine.best_score_:.4f}”)
手法 計算量 使いどころ
Grid Search パラメータ数が少ない、確実に最適を見つけたい
Random Search パラメータ数が多い、連続値パラメータ
Optuna 低〜中 効率重視、複雑なパラメータ空間
✅ チューニングのベストプラクティス
  • まずデフォルト値でベースラインを確認
  • 重要なパラメータからチューニング
  • 交差検証を使う(過学習を防ぐ)
  • 最終評価は別のテストセットで行う

📋 6. 完成コード:3つの手法の比較

# ハイパーパラメータチューニング – 完成コード import numpy as np from sklearn.datasets import load_breast_cancer from sklearn.model_selection import GridSearchCV, RandomizedSearchCV, cross_val_score from sklearn.ensemble import RandomForestClassifier from scipy.stats import randint, uniform import optuna # データの準備 cancer = load_breast_cancer() X, y = cancer.data, cancer.target # ========== 1. Grid Search ========== param_grid = { ‘n_estimators’: [50, 100, 200], ‘max_depth’: [3, 5, 7, None], ‘min_samples_split’: [2, 5, 10] } grid_search = GridSearchCV( RandomForestClassifier(random_state=42), param_grid, cv=5, scoring=’accuracy’, n_jobs=-1 ) grid_search.fit(X, y) print(f”Grid Search: {grid_search.best_score_:.4f}”) # ========== 2. Randomized Search ========== param_dist = { ‘n_estimators’: randint(50, 300), ‘max_depth’: [3, 5, 7, 10, None], ‘min_samples_split’: randint(2, 20), ‘max_features’: uniform(0.1, 0.9) } random_search = RandomizedSearchCV( RandomForestClassifier(random_state=42), param_dist, n_iter=50, cv=5, scoring=’accuracy’, n_jobs=-1, random_state=42 ) random_search.fit(X, y) print(f”Random Search: {random_search.best_score_:.4f}”) # ========== 3. Optuna ========== def objective(trial): params = { ‘n_estimators’: trial.suggest_int(‘n_estimators’, 50, 300), ‘max_depth’: trial.suggest_int(‘max_depth’, 3, 15), ‘min_samples_split’: trial.suggest_int(‘min_samples_split’, 2, 20), ‘max_features’: trial.suggest_float(‘max_features’, 0.1, 1.0) } model = RandomForestClassifier(**params, random_state=42) return cross_val_score(model, X, y, cv=5).mean() study = optuna.create_study(direction=’maximize’) study.optimize(objective, n_trials=50, show_progress_bar=True) print(f”Optuna: {study.best_value:.4f}”)

📝 練習問題

問題1 やさしい

パラメータの違い

「パラメータ」と「ハイパーパラメータ」の違いとして正しいものを選んでください。

  • A. パラメータはモデルが学習し、ハイパーパラメータは人間が設定する
  • B. パラメータは人間が設定し、ハイパーパラメータはモデルが学習する
  • C. どちらもモデルが学習する
正解:A

パラメータ(重み、係数など)はモデルがデータから学習します。ハイパーパラメータ(学習率、木の深さなど)は人間が事前に設定します。

問題2 ふつう

Grid Searchの計算量

3つのパラメータ(各5候補)で5-Fold CVを使うGrid Searchでは、何回モデルを訓練しますか?

  • A. 75回
  • B. 125回
  • C. 625回
正解:C(625回)

組み合わせ数 = 5 × 5 × 5 = 125
5-Fold CVなので、125 × 5 = 625回の訓練が必要です。

問題3 ふつう

手法の選択

10個のパラメータ(各10候補)をチューニングする場合、どの手法が適切ですか?

  • A. Grid Search
  • B. Randomized Search または Optuna
  • C. 手動で試す
正解:B

10パラメータ × 10候補 = 10^10 = 100億の組み合わせ。Grid Searchでは不可能なので、Randomized SearchかOptunaが適切です。

問題4 むずかしい

GridSearchCVの実装

SVMでCとgammaをチューニングしてください。

from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV from sklearn.datasets import load_iris iris = load_iris() X, y = iris.data, iris.target param_grid = { ‘C’: [0.1, 1, 10, 100], ‘gamma’: [0.001, 0.01, 0.1, 1], ‘kernel’: [‘rbf’] } grid = GridSearchCV(SVC(), param_grid, cv=5, scoring=’accuracy’) grid.fit(X, y) print(f”最適なパラメータ: {grid.best_params_}”) print(f”最高スコア: {grid.best_score_:.4f}”)
問題5 むずかしい

RandomizedSearchCVの実装

RandomForestでn_iter=30のRandomized Searchを実行してください。

from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import RandomizedSearchCV from scipy.stats import randint param_dist = { ‘n_estimators’: randint(50, 300), ‘max_depth’: randint(3, 20), ‘min_samples_split’: randint(2, 20) } random_search = RandomizedSearchCV( RandomForestClassifier(random_state=42), param_dist, n_iter=30, cv=5, random_state=42 ) random_search.fit(X, y) print(f”最適なパラメータ: {random_search.best_params_}”) print(f”最高スコア: {random_search.best_score_:.4f}”)
問題6 むずかしい

Optunaの実装

Optunaでロジスティック回帰のCをチューニングしてください。

import optuna from sklearn.linear_model import LogisticRegression from sklearn.model_selection import cross_val_score def objective(trial): C = trial.suggest_float(‘C’, 0.01, 100, log=True) model = LogisticRegression(C=C, max_iter=1000) return cross_val_score(model, X, y, cv=5).mean() study = optuna.create_study(direction=’maximize’) study.optimize(objective, n_trials=30, show_progress_bar=True) print(f”最適なC: {study.best_params[‘C’]:.4f}”) print(f”最高スコア: {study.best_value:.4f}”)

📝 STEP 24 のまとめ

✅ このステップで学んだこと
  • ハイパーパラメータ:人間が事前に設定するパラメータ
  • Grid Search:全組み合わせを試す(確実だが遅い)
  • Randomized Search:ランダムにサンプリング(高速)
  • Optuna:ベイズ最適化で効率的に探索
  • 段階的チューニング:粗い探索→細かい探索
🚀 次のステップへ

次のSTEP 25では、モデル選択の戦略を学びます。問題タイプに応じたアルゴリズムの選び方と、複数モデルの比較方法を習得しましょう!

❓ よくある質問

Q1. チューニングにどれくらい時間をかけるべき?
一般的に、全体の開発時間の20〜30%程度が目安です。特徴量エンジニアリングの方が効果が大きいことも多いので、バランスが重要です。
Q2. n_jobsを-1にするとどうなる?
利用可能な全CPUコアを使って並列処理します。計算が高速になりますが、メモリ使用量も増えるので注意が必要です。
Q3. チューニング後のモデルで過学習しない?
交差検証を使っているので、ある程度防げます。ただし、最終評価はチューニングに使っていない別のテストデータで行うべきです。
Q4. OptunaとHyperoptの違いは?
どちらもベイズ最適化ライブラリですが、Optunaは日本製で使いやすいAPIと可視化機能が特徴です。Hyperoptは歴史が長く、TPEアルゴリズムで有名です。
Q5. デフォルトパラメータでも良い結果が出る?
scikit-learnのデフォルト値は多くの場合で良い結果が出るように設定されています。まずデフォルトでベースラインを確認し、必要に応じてチューニングしましょう。
📝

学習メモ

機械学習入門 - Step 24

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE