Optunaによるハイパーパラメータ最適化

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

図25
図26

目次

概要

技術名: 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に保存しました')

使用方法

  1. 上記のプログラムを実行する
  2. 実行結果を観察し、最適化過程と結果を確認する

実験・探求のアイデア

比較実験

パラメータ実験

高度な実験