Optunaによるハイパーパラメータ最適化
【概要】TPE(Tree-structured Parzen Estimator)アルゴリズムによるハイパーパラメータ最適化技術を学習する。従来手法との比較実験により最適化効果を検証し、機械学習モデルの性能向上手法を習得する。


目次
概要
技術名: Optuna(ハイパーパラメータ最適化フレームワーク)
出典: T. Akiba, S. Sano, T. Yanase, T. Ohta, and M. Koyama, "Optuna: A Next-generation Hyperparameter Optimization Framework," in Proc. 25th ACM SIGKDD Int. Conf. on Knowledge Discovery and Data Mining, 2019.
新規性・特徴: TPE(Tree-structured Parzen Estimator:木構造パルツェン推定器)によるベイズ最適化(確率モデルを用いて効率的に最適解を探索する手法)を採用し、過去の試行結果を学習して有望な領域を優先的に探索する(良い結果が得られた領域の周辺を重点的に調べる)。従来のグリッドサーチ(全組み合わせ探索)やランダムサーチ(無作為探索)と比較して少ない試行回数で最適解に到達する。
応用例: 深層学習モデルの学習率・バッチサイズ最適化、機械学習競技での特徴量選択、強化学習のハイパーパラメータ調整
学習目標: 最適化過程をリアルタイムで観察し、従来手法との効率性の違いを理解する。最適解への収束過程を分析する。
Python開発環境,ライブラリ類
ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。
Python 3.12 のインストール
インストール済みの場合は実行不要。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
【関連する外部ページ】
Python の公式ページ: https://www.python.org/
AI エディタ Windsurf のインストール
Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
winget install --scope machine Codeium.Windsurf -e --silent
【関連する外部ページ】
Windsurf の公式ページ: https://windsurf.com/
必要なライブラリのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する:
pip install optuna scikit-learn
プログラムコード
# Optuna TPEアルゴリズムによるハイパーパラメータ最適化
# 特徴技術名: Optuna TPE (Tree-structured Parzen Estimator)
# 出典: Akiba, T., Sano, S., Yanase, T., Ohta, T., & Koyama, M. (2019). Optuna: A Next-generation Hyperparameter Optimization Framework. In Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining (pp. 2623-2631).
# 特徴機能: TPEアルゴリズムによる効率的なハイパーパラメータ探索。過去の試行結果から良好なパラメータ領域を学習し、有望な領域を優先的に探索することで、グリッドサーチやランダムサーチより少ない試行回数で最適解に到達
# 学習済みモデル: 使用なし(RandomForestClassifierをscikit-learnで訓練)
# 方式設計:
# - 関連利用技術: scikit-learn(機械学習ライブラリ、RandomForestClassifierによる分類器構築とcross_val_scoreによる交差検証)
# - 入力と出力: 入力: Irisデータセット(プログラム内で自動読み込み)、出力: 最適化結果のテキスト表示
# - 処理手順: 1)Irisデータセット読み込み、2)ベースライン性能測定、3)TPEサンプラーによる最適化study作成、4)10回の試行でパラメータ探索、5)最適パラメータでの性能確認
# - 前処理、後処理: 前処理: データシャッフル(shuffle関数でランダム化)、後処理: 最適化過程の統計分析(探索範囲、収束試行数)
# - 追加処理: StratifiedKFoldによる層化交差検証(クラス比率を保持した分割で評価の安定性向上)
# - 調整を必要とする設定値: N_TRIALS(試行回数、デフォルト10回。探索空間の大きさに応じて調整が必要)
# 将来方策: N_TRIALSの自動調整機能として、改善幅が3試行連続で0.001未満の場合に早期停止する収束判定機能の実装
# その他の重要事項: Windows環境で動作確認済み。実行時間は約10秒(Intel Core i7環境)
# 前準備: pip install optuna scikit-learn
import optuna
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.utils import shuffle
import time
# 定数定義
RANDOM_SEED = 42
N_TRIALS = 10
CV_SPLITS = 3
FINAL_CV_SPLITS = 5
# パラメータ探索範囲
N_ESTIMATORS_MIN = 10 # RandomForestの決定木数の最小値
N_ESTIMATORS_MAX = 50 # RandomForestの決定木数の最大値
MAX_DEPTH_MIN = 2 # 決定木の深さの最小値
MAX_DEPTH_MAX = 5 # 決定木の深さの最大値
# 出力内容を保存するリスト
output_lines = []
def print_and_save(text=''):
print(text)
output_lines.append(text)
# プログラム概要表示
print_and_save('=== Optuna TPEアルゴリズムによるハイパーパラメータ最適化 ===')
print_and_save('概要: TPEアルゴリズムを使用してRandomForestの最適なパラメータを自動探索します')
print_and_save('ユーザ操作: 不要(自動実行されます)')
print_and_save()
# メイン処理
print_and_save('=== Optunaによるハイパーパラメータ最適化実験 ===')
print_and_save()
print_and_save('1. データ準備')
X, y = load_iris(return_X_y=True)
X, y = shuffle(X, y, random_state=RANDOM_SEED)
print_and_save(f' データサイズ: {X.shape[0]}サンプル, {X.shape[1]}特徴量')
print_and_save(f' クラス数: {len(set(y))}クラス')
print_and_save()
print_and_save('2. ベースライン性能(デフォルトパラメータ)')
baseline_clf = RandomForestClassifier(random_state=RANDOM_SEED)
baseline_cv = StratifiedKFold(n_splits=CV_SPLITS, shuffle=True, random_state=RANDOM_SEED)
baseline_score = cross_val_score(baseline_clf, X, y, cv=baseline_cv).mean()
print_and_save(f' デフォルト設定での精度: {baseline_score:.4f}')
print_and_save()
print_and_save('3. Optunaによるハイパーパラメータ最適化開始(10試行)')
study = optuna.create_study(direction='maximize', sampler=optuna.samplers.TPESampler(seed=RANDOM_SEED))
def objective(trial):
n_est = trial.suggest_int('n_estimators', N_ESTIMATORS_MIN, N_ESTIMATORS_MAX)
max_d = trial.suggest_int('max_depth', MAX_DEPTH_MIN, MAX_DEPTH_MAX)
clf = RandomForestClassifier(n_estimators=n_est, max_depth=max_d, random_state=RANDOM_SEED)
cv_splitter = StratifiedKFold(n_splits=CV_SPLITS, shuffle=True, random_state=RANDOM_SEED)
score = cross_val_score(clf, X, y, cv=cv_splitter).mean()
print_and_save(f' 試行{trial.number + 1}: 木の数={n_est}, 最大深度={max_d} → 精度={score:.4f}')
time.sleep(1) # 1秒間隔での表示
return score
study.optimize(objective, n_trials=N_TRIALS)
# 結果出力
print_and_save()
print_and_save('4. 最適化結果')
print_and_save(f' 最適パラメータ: {study.best_params}')
print_and_save(f' 最高精度: {study.best_value:.4f}')
print_and_save(f' 改善幅: {study.best_value - baseline_score:+.4f}')
print_and_save()
print_and_save('5. 最適パラメータでの最終モデル性能確認')
best_clf = RandomForestClassifier(**study.best_params, random_state=RANDOM_SEED)
final_cv = StratifiedKFold(n_splits=FINAL_CV_SPLITS, shuffle=True, random_state=RANDOM_SEED)
final_score = cross_val_score(best_clf, X, y, cv=final_cv).mean()
print_and_save(f' 5-fold交差検証(データを分割してモデルの性能を評価する手法)精度: {final_score:.4f}')
print_and_save()
print_and_save('6. 最適化過程の分析')
print_and_save(f' 総試行回数: {len(study.trials)}回')
print_and_save(f' 最高精度到達試行: {study.best_trial.number + 1}回目')
trials = study.trials
n_est_vals = [t.params['n_estimators'] for t in trials]
depth_vals = [t.params['max_depth'] for t in trials]
print_and_save(f' 探索した木の数の範囲: {min(n_est_vals)} ~ {max(n_est_vals)}')
print_and_save(f' 探索した最大深度の範囲: {min(depth_vals)} ~ {max(depth_vals)}')
print_and_save()
print_and_save('=== 実験完了:TPEアルゴリズムによる最適化を実現 ===')
print_and_save('※ 結果の解釈: 改善幅がプラスの場合、最適化が成功')
print_and_save(' 試行回数が少ないほどTPEの効率性が示される')
print_and_save(' 一般的な分類タスクにおいて精度0.95以上で高性能、0.90以上で良好な性能')
# 結果をファイルに保存
with open('result.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(output_lines))
print()
print('result.txtに保存しました')
使用方法
- 上記のプログラムを実行する
- 実行結果を観察し、最適化過程と結果を確認する
実験・探求のアイデア
比較実験
- サンプラーを変更して比較実験を行う(RandomSampler、CmaEsSampler)
- 異なるアルゴリズム(SVM、XGBoost)でのハイパーパラメータ最適化
パラメータ実験
- 試行回数を変更(5回、20回、50回)して収束速度を比較
- パラメータ探索範囲を拡大・縮小して最適化効果を確認
- 異なるデータセット(Wine、Breast Cancer)での性能比較
高度な実験
- ランダムサーチとの比較:同じ試行回数での性能比較
- パラメータ重要度の分析:性能への影響度分析
- 早期終了機能の実験:低性能試行の早期打ち切り効果