学習目標
この章を読むことで、以下を習得できます:
- ✅ マルチ目的最適化によるトレードオフバランスの取得
- ✅ Early Stoppingによる効率的な探索空間の絞り込み
- ✅ 分散環境でのスケーラブルなハイパーパラメータチューニング
- ✅ 転移学習とウォームスタートによる事前知識の活用
- ✅ 本番環境での実践的なチューニング運用ガイドライン
1. マルチ目的最適化
実際のMLシステムでは、精度だけでなくレイテンシ、モデルサイズ、推論コストなど複数の指標をバランスさせる必要があります。
1.1 精度とレイテンシのトレードオフ
単一指標の最適化では、実運用に適さないモデルが選択される可能性があります。例えば:
- 高精度だが遅いモデル:リアルタイム推論に不適
- 高速だが低精度なモデル:ビジネス要件を満たさない
- バランス型モデル:実用的な妥協点
1.2 Paretoフロンティアの理解
Paretoフロンティアは、どちらの指標も改善できない解の集合です。これにより複数の候補モデルから最適なものを選択できます。
Pareto最適性:ある解が他のすべての指標で劣ることなく、少なくとも1つの指標で優れている状態。
1.3 Optunaのマルチ目的最適化
Optunaは複数の目的関数を同時に最適化するマルチ目的最適化をサポートしています。
import optuna
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
import time
import numpy as np
# データセット準備
X, y = load_breast_cancer(return_X_y=True)
def objective(trial):
"""精度とレイテンシを同時に最適化"""
# ハイパーパラメータの提案
params = {
'n_estimators': trial.suggest_int('n_estimators', 10, 200),
'max_depth': trial.suggest_int('max_depth', 2, 32),
'min_samples_split': trial.suggest_int('min_samples_split', 2, 20),
'min_samples_leaf': trial.suggest_int('min_samples_leaf', 1, 10),
'max_features': trial.suggest_categorical('max_features', ['sqrt', 'log2', None])
}
# モデル学習と精度評価
clf = RandomForestClassifier(**params, random_state=42, n_jobs=-1)
accuracy = cross_val_score(clf, X, y, cv=3, n_jobs=-1).mean()
# レイテンシ測定(推論時間)
clf.fit(X, y)
start_time = time.time()
_ = clf.predict(X[:100]) # 100サンプルの推論時間測定
latency = (time.time() - start_time) * 1000 # ミリ秒
# 2つの目的関数を返す:精度を最大化、レイテンシを最小化
return accuracy, latency
# マルチ目的最適化study
study = optuna.create_study(
directions=['maximize', 'minimize'], # 精度は最大化、レイテンシは最小化
study_name='multi_objective_optimization'
)
study.optimize(objective, n_trials=50)
# Paretoフロンティアの解を取得
print("=== Pareto最適解 ===")
for trial in study.best_trials:
print(f"Trial {trial.number}:")
print(f" Accuracy: {trial.values[0]:.4f}")
print(f" Latency: {trial.values[1]:.2f} ms")
print(f" Params: {trial.params}\n")
- 目的関数の数は通常2〜3個に抑える(4個以上は解釈が困難)
- 各目的関数のスケールを揃える(正規化推奨)
- ビジネス要件に応じてParetoフロンティアから最終モデルを選択
1.4 Paretoフロンティアの可視化
import matplotlib.pyplot as plt
def visualize_pareto_frontier(study):
"""Paretoフロンティアの可視化"""
# すべての試行結果を取得
trials = study.trials
accuracies = [t.values[0] for t in trials]
latencies = [t.values[1] for t in trials]
# Pareto最適解を取得
pareto_trials = study.best_trials
pareto_accuracies = [t.values[0] for t in pareto_trials]
pareto_latencies = [t.values[1] for t in pareto_trials]
# プロット
plt.figure(figsize=(10, 6))
plt.scatter(latencies, accuracies, alpha=0.5, label='All trials')
plt.scatter(pareto_latencies, pareto_accuracies,
color='red', s=100, marker='*', label='Pareto frontier')
# Paretoフロンティアを線で接続
sorted_pareto = sorted(zip(pareto_latencies, pareto_accuracies))
plt.plot([p[0] for p in sorted_pareto], [p[1] for p in sorted_pareto],
'r--', alpha=0.5)
plt.xlabel('Latency (ms)')
plt.ylabel('Accuracy')
plt.title('Multi-Objective Optimization: Accuracy vs Latency')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
visualize_pareto_frontier(study)
2. Early Stopping戦略
有望でない試行を早期に打ち切ることで、計算リソースを効率的に使用し、より多くの探索を実行できます。
2.1 Pruning(枝刈り)の重要性
ハイパーパラメータチューニングでは、多くの試行が最終的に良い結果を生まないため、早期に打ち切ることが効率化の鍵です。
| 戦略 | 特徴 | 適用場面 |
|---|---|---|
| MedianPruner | 中央値と比較して劣る試行を打ち切り | 一般的な用途、バランス型 |
| PercentilePruner | 上位X%に入らない試行を打ち切り | 積極的な探索削減が必要な場合 |
| SuccessiveHalvingPruner | 段階的にリソースを配分 | 学習曲線が利用可能な場合 |
| HyperbandPruner | 複数のSuccessiveHalvingを並列実行 | 大規模探索、最先端手法 |
2.2 MedianPrunerの実装
import optuna
from optuna.pruners import MedianPruner
from sklearn.model_selection import cross_validate
from sklearn.ensemble import GradientBoostingClassifier
import numpy as np
def objective_with_pruning(trial):
"""Pruningを使った目的関数"""
params = {
'n_estimators': trial.suggest_int('n_estimators', 50, 500),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),
'max_depth': trial.suggest_int('max_depth', 3, 10),
'subsample': trial.suggest_float('subsample', 0.5, 1.0),
}
clf = GradientBoostingClassifier(**params, random_state=42)
# 段階的な評価(学習の途中経過を報告)
for step in range(5):
# 段階的にn_estimatorsを増やして評価
intermediate_clf = GradientBoostingClassifier(
n_estimators=(step + 1) * 20,
learning_rate=params['learning_rate'],
max_depth=params['max_depth'],
subsample=params['subsample'],
random_state=42
)
# 中間評価
scores = cross_validate(intermediate_clf, X, y, cv=3,
scoring='accuracy', n_jobs=-1)
intermediate_score = scores['test_score'].mean()
# 中間結果を報告
trial.report(intermediate_score, step)
# Pruningの判断
if trial.should_prune():
raise optuna.TrialPruned()
# 最終評価
final_scores = cross_validate(clf, X, y, cv=5, scoring='accuracy', n_jobs=-1)
return final_scores['test_score'].mean()
# MedianPrunerを使用したstudy
pruner = MedianPruner(
n_startup_trials=5, # 最初の5試行はPruningしない
n_warmup_steps=2, # 最初の2ステップはPruningしない
interval_steps=1 # 各ステップでPruning判断
)
study = optuna.create_study(
direction='maximize',
pruner=pruner,
study_name='pruning_optimization'
)
study.optimize(objective_with_pruning, n_trials=50)
print(f"Best accuracy: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")
print(f"Pruned trials: {len([t for t in study.trials if t.state == optuna.trial.TrialState.PRUNED])}")
print(f"Completed trials: {len([t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE])}")
2.3 PercentilePrunerの実装
from optuna.pruners import PercentilePruner
# PercentilePruner:上位25%に入らない試行を打ち切り
pruner = PercentilePruner(
percentile=25.0, # 上位25%のみ継続
n_startup_trials=5, # 最初の5試行は必ず完了
n_warmup_steps=2 # 最初の2ステップはPruningしない
)
study = optuna.create_study(
direction='maximize',
pruner=pruner,
study_name='percentile_pruning'
)
study.optimize(objective_with_pruning, n_trials=50)
# Pruning効果の分析
pruned_count = len([t for t in study.trials if t.state == optuna.trial.TrialState.PRUNED])
completed_count = len([t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE])
pruning_rate = pruned_count / len(study.trials) * 100
print(f"Pruning rate: {pruning_rate:.1f}%")
print(f"Time saved: ~{pruning_rate * 0.8:.1f}% (estimated)")
- Pruningが積極的すぎると有望な試行も打ち切られる可能性がある
- n_startup_trialsは探索空間の複雑さに応じて調整する
- 学習曲線が不安定な場合はn_warmup_stepsを大きくする
3. 分散ハイパーパラメータチューニング
大規模な探索空間や計算量の多いモデルでは、分散環境でチューニングを実行することで大幅な時間短縮が可能です。
3.1 Optuna分散最適化の仕組み
Optunaは共有ストレージ(RDB、Redis等)を通じて複数のワーカーが協調して最適化を実行できます。
RDB/Redis] --> B[Worker 1] A --> C[Worker 2] A --> D[Worker 3] A --> E[Worker N] B --> F[試行結果を保存] C --> F D --> F E --> F F --> A
3.2 RDBを使った分散最適化
import optuna
from optuna.storages import RDBStorage
# 共有ストレージの設定(PostgreSQL例)
storage = RDBStorage(
url='postgresql://user:password@localhost/optuna_db',
engine_kwargs={
'pool_size': 20,
'max_overflow': 0,
}
)
# 分散study作成(複数ワーカーで共有)
study = optuna.create_study(
study_name='distributed_optimization',
storage=storage,
direction='maximize',
load_if_exists=True # 既存studyがあれば再利用
)
# 各ワーカーでこのコードを実行
def objective(trial):
x = trial.suggest_float('x', -10, 10)
y = trial.suggest_float('y', -10, 10)
return -(x**2 + y**2)
# 各ワーカーが並列で最適化
study.optimize(objective, n_trials=100)
print(f"Best value: {study.best_value}")
print(f"Best params: {study.best_params}")
print(f"Total trials: {len(study.trials)}")
- ストレージ選択: 小〜中規模→SQLite、大規模→PostgreSQL/MySQL、超高速→Redis
- ワーカー数: CPU数、ネットワーク帯域、ストレージ性能を考慮
- 負荷分散: 各ワーカーのn_trialsを調整してバランスを取る
3.3 Ray Tuneによる分散チューニング
Ray Tuneは分散実行とスケジューリングに特化したフレームワークで、大規模クラスタでの並列チューニングに適しています。
from ray import tune
from ray.tune.schedulers import ASHAScheduler
from ray.tune.search.optuna import OptunaSearch
import numpy as np
def train_model(config):
"""訓練関数(Ray Tuneが並列実行)"""
# シミュレーション:実際にはモデル訓練を実装
for epoch in range(10):
# config['lr']やconfig['batch_size']を使った訓練
accuracy = 1 - (config['lr'] - 0.01)**2 - (config['batch_size'] - 32)**2 / 1000
accuracy += np.random.normal(0, 0.01) # ノイズ
# 中間結果を報告
tune.report(accuracy=accuracy)
# 探索空間定義
search_space = {
'lr': tune.loguniform(1e-4, 1e-1),
'batch_size': tune.choice([16, 32, 64, 128]),
'hidden_size': tune.choice([64, 128, 256, 512]),
}
# ASHA Scheduler(効率的なearly stopping)
scheduler = ASHAScheduler(
max_t=10, # 最大エポック数
grace_period=1, # 最小実行エポック数
reduction_factor=2 # 各段階で半分を打ち切り
)
# Optuna検索アルゴリズムを使用
search_alg = OptunaSearch()
# 分散チューニング実行
analysis = tune.run(
train_model,
config=search_space,
num_samples=50, # 試行回数
scheduler=scheduler,
search_alg=search_alg,
resources_per_trial={ # 各試行のリソース
'cpu': 2,
'gpu': 0.5
},
verbose=1
)
# 最適パラメータ取得
best_config = analysis.best_config
print(f"Best config: {best_config}")
print(f"Best accuracy: {analysis.best_result['accuracy']:.4f}")
3.4 スケーラビリティの考慮事項
| 規模 | 推奨フレームワーク | ストレージ | ワーカー数 |
|---|---|---|---|
| 小規模(〜100試行) | Optuna | SQLite | 1-4 |
| 中規模(100〜1000試行) | Optuna | PostgreSQL | 4-16 |
| 大規模(1000〜10000試行) | Ray Tune | PostgreSQL/Redis | 16-64 |
| 超大規模(10000試行〜) | Ray Tune | 分散Redis | 64+ |
4. 転移学習とウォームスタート
過去のチューニング結果や類似タスクの知識を活用することで、探索を大幅に効率化できます。
4.1 事前知識の活用
ゼロからチューニングを開始するのではなく、既知の良好なハイパーパラメータから探索を開始する方法です。
ウォームスタートの利点
- 探索時間短縮:良好な初期値から開始することで収束が早い
- リスク低減:最低限の性能を担保できる
- 知識の蓄積:過去の経験を活かせる
4.2 Optunaでのウォームスタート実装
import optuna
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 既知の良好なハイパーパラメータ(過去の経験やドメイン知識)
known_good_params = [
{'n_estimators': 100, 'max_depth': 10, 'min_samples_split': 5},
{'n_estimators': 200, 'max_depth': 15, 'min_samples_split': 2},
{'n_estimators': 150, 'max_depth': 12, 'min_samples_split': 4},
]
def objective(trial):
params = {
'n_estimators': trial.suggest_int('n_estimators', 50, 300),
'max_depth': trial.suggest_int('max_depth', 5, 20),
'min_samples_split': trial.suggest_int('min_samples_split', 2, 10),
}
clf = RandomForestClassifier(**params, random_state=42, n_jobs=-1)
score = cross_val_score(clf, X, y, cv=3, n_jobs=-1).mean()
return score
# Study作成
study = optuna.create_study(direction='maximize')
# ウォームスタート:既知の良好なパラメータを事前に追加
for params in known_good_params:
study.enqueue_trial(params)
# 最適化実行(enqueueされた試行が優先的に実行される)
study.optimize(objective, n_trials=50)
print(f"Best score: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")
# 事前登録したパラメータの効果確認
warmstart_trials = study.trials[:len(known_good_params)]
print("\n=== Warmstart trials performance ===")
for i, trial in enumerate(warmstart_trials):
print(f"Params {i+1}: {trial.params} -> Score: {trial.value:.4f}")
4.3 メタ学習の応用
複数の類似タスクから学習し、新しいタスクに対する良好な初期パラメータを予測する手法です。
メタ学習:「学習の学習」と呼ばれ、複数のタスクでの経験から新しいタスクへの適応を高速化する技術。
実践的なメタ学習アプローチ
- 過去のチューニング履歴を保存:データセット特性とベストパラメータのペアを記録
- 類似タスク検索:新しいタスクに類似する過去タスクを特定
- パラメータ推奨:類似タスクのベストパラメータを初期値として使用
import json
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
class MetaLearningOptimizer:
"""メタ学習を使ったハイパーパラメータ最適化"""
def __init__(self, history_file='tuning_history.json'):
self.history_file = history_file
self.history = self.load_history()
def load_history(self):
"""過去のチューニング履歴を読み込み"""
try:
with open(self.history_file, 'r') as f:
return json.load(f)
except FileNotFoundError:
return []
def save_history(self, dataset_features, best_params, best_score):
"""新しいチューニング結果を保存"""
self.history.append({
'dataset_features': dataset_features,
'best_params': best_params,
'best_score': best_score
})
with open(self.history_file, 'w') as f:
json.dump(self.history, f, indent=2)
def get_dataset_features(self, X, y):
"""データセットの特徴量を抽出"""
return {
'n_samples': X.shape[0],
'n_features': X.shape[1],
'n_classes': len(np.unique(y)),
'class_imbalance': np.std(np.bincount(y)) / np.mean(np.bincount(y)),
'feature_correlation': np.mean(np.abs(np.corrcoef(X.T))),
}
def find_similar_tasks(self, current_features, top_k=3):
"""類似タスクを検索"""
if not self.history:
return []
# 特徴量ベクトル化
current_vec = np.array(list(current_features.values())).reshape(1, -1)
similarities = []
for record in self.history:
hist_vec = np.array(list(record['dataset_features'].values())).reshape(1, -1)
sim = cosine_similarity(current_vec, hist_vec)[0][0]
similarities.append((sim, record))
# 類似度順にソート
similarities.sort(reverse=True, key=lambda x: x[0])
return [record for _, record in similarities[:top_k]]
def get_warmstart_params(self, X, y):
"""ウォームスタート用パラメータを推奨"""
current_features = self.get_dataset_features(X, y)
similar_tasks = self.find_similar_tasks(current_features)
if not similar_tasks:
return []
# 類似タスクのベストパラメータを返す
return [task['best_params'] for task in similar_tasks]
# 使用例
meta_optimizer = MetaLearningOptimizer()
# 新しいタスクに対するウォームスタート
warmstart_params = meta_optimizer.get_warmstart_params(X, y)
if warmstart_params:
print("=== Recommended warmstart parameters ===")
for i, params in enumerate(warmstart_params):
print(f"Recommendation {i+1}: {params}")
# Optunaでウォームスタート
study = optuna.create_study(direction='maximize')
for params in warmstart_params:
study.enqueue_trial(params)
study.optimize(objective, n_trials=50)
else:
# 履歴がない場合は通常の最適化
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)
# 結果を履歴に保存
dataset_features = meta_optimizer.get_dataset_features(X, y)
meta_optimizer.save_history(dataset_features, study.best_params, study.best_value)
4.4 転移学習の実践的アプローチ
| アプローチ | 適用場面 | 効果 |
|---|---|---|
| ウォームスタート | 類似タスクの経験がある | 探索時間20-40%短縮 |
| メタ学習 | 多数の過去タスクがある | 初期性能向上+探索効率化 |
| ドメイン知識注入 | 専門家の知見がある | リスク低減+高速収束 |
| アンサンブル活用 | 複数の候補パラメータ | ロバスト性向上 |
5. チューニング実践ガイド
理論を実務に適用する際のベストプラクティス、デバッグ手法、本番運用のノウハウを解説します。
5.1 探索空間設計のベストプラクティス
効果的な探索空間の設計原則
- 影響度順に優先順位付け
- 学習率、正則化パラメータ → 高優先度
- バッチサイズ、エポック数 → 中優先度
- 微調整パラメータ → 低優先度
- 適切なスケール選択
- 学習率:対数スケール(loguniform)
- 正則化強度:対数スケール
- 層数、ユニット数:整数、線形スケール
- 条件付きパラメータの活用
- 特定の選択に依存するパラメータは条件分岐
- ✅ パラメータ間の依存関係を明確化しているか
- ✅ 適切な分布(uniform、loguniform等)を選択しているか
- ✅ 探索範囲が広すぎ/狭すぎないか
- ✅ 計算コストの高いパラメータを絞り込んでいるか
5.2 デバッグとトラブルシューティング
よくある問題と解決策
| 問題 | 原因 | 解決策 |
|---|---|---|
| 収束しない | 探索空間が広すぎる | 事前実験で範囲を絞る |
| 同じパラメータばかり | サンプラーの偏り | RandomSamplerと比較 |
| Pruningが多すぎ | Pruner設定が厳しい | n_warmup_steps増加 |
| 結果が不安定 | 評価のランダム性 | CV fold数増加、seed固定 |
| メモリ不足 | 大きすぎるモデル | バッチサイズ削減、勾配蓄積 |
デバッグのための可視化
import optuna
from optuna.visualization import (
plot_optimization_history,
plot_param_importances,
plot_parallel_coordinate,
plot_slice
)
# 最適化実行後の可視化
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
# 1. 最適化履歴:時間経過とともに改善しているか確認
fig1 = plot_optimization_history(study)
fig1.show()
# 2. パラメータ重要度:どのパラメータが重要か確認
fig2 = plot_param_importances(study)
fig2.show()
# 3. 並列座標プロット:パラメータ間の関係性を確認
fig3 = plot_parallel_coordinate(study)
fig3.show()
# 4. スライスプロット:各パラメータの影響を個別確認
fig4 = plot_slice(study)
fig4.show()
5.3 本番環境での運用
本番運用のワークフロー
本番運用のベストプラクティス
- 段階的ロールアウト
- Canary deployment:一部トラフィックで検証
- A/Bテスト:既存モデルと並行運用
- 段階的な切り替え:問題発生時の即座のロールバック
- 継続的モニタリング
- 予測性能の追跡(精度、AUC等)
- レイテンシ、スループットの監視
- データドリフト検知
- 定期的な再チューニング
- データ分布の変化に応じて四半期ごと等に実施
- 新しいアルゴリズムや手法の評価
- 過去の結果を活かしたメタ学習
- 再現性の確保:乱数シード、ライブラリバージョンを固定
- バックアップ:既存モデルをいつでも復元できる状態に
- ドキュメント化:チューニング履歴、パラメータ変更理由を記録
- アラート設定:性能劣化時の自動通知
5.4 実践的なチューニング戦略まとめ
| フェーズ | 目的 | 推奨手法 | 試行回数 |
|---|---|---|---|
| 初期探索 | 全体像把握 | Random Search | 20-50 |
| 絞り込み | 有望領域特定 | TPE + Pruning | 50-100 |
| 精密探索 | 最適解発見 | CMA-ES/GP | 100-200 |
| マルチ目的 | トレードオフ調整 | Multi-objective TPE | 100-300 |
| 本番検証 | 最終確認 | Cross-validation増 | 5-10 |
5.5 チューニング効率化のチートシート
時間がない時のクイックチューニング手順:
- ドメイン知識/過去経験から初期値設定(ウォームスタート)
- 重要パラメータ2-3個に絞って探索(学習率、正則化)
- MedianPruner有効化で無駄な試行削減
- 並列実行(4-8ワーカー)で時間短縮
- 50-100試行で実用的な性能を確保
章末演習問題
演習1:マルチ目的最適化の実装(難易度:中)
精度、推論時間、モデルサイズの3つを同時に最適化するOptunaのstudyを実装してください。Paretoフロンティアを可視化し、ビジネス要件(精度0.90以上、推論時間50ms以下)を満たす解を選択してください。
演習2:Pruning戦略の比較(難易度:中)
MedianPruner、PercentilePruner、HyperbandPrunerの3つを比較実験してください。同じ目的関数に対して、各Prunerの打ち切り率、最終性能、計算時間を比較し、どのPrunerが最も効率的か評価してください。
演習3:分散チューニングの実装(難易度:上級)
PostgreSQLを使ったOptuna分散最適化を実装してください。3つの異なるワーカーから同時にstudyにアクセスし、合計150試行を効率的に実行してください。各ワーカーの貢献度を分析してください。
演習4:メタ学習システムの構築(難易度:上級)
複数のデータセット(UCI MLリポジトリ等)に対してチューニングを実行し、その履歴から新しいデータセットに対する最適なハイパーパラメータを推奨するメタ学習システムを構築してください。
演習5:本番運用シミュレーション(難易度:上級)
時系列データを使って、本番運用のシミュレーションを実装してください。データドリフトが発生した際の性能劣化を検知し、自動的に再チューニングをトリガーする仕組みを作成してください。
まとめ
本章では実践的なハイパーパラメータチューニング戦略を学びました:
- ✅ マルチ目的最適化:精度とレイテンシ等、複数指標のトレードオフをParetoフロンティアで解決
- ✅ Early Stopping:Pruningにより有望でない試行を打ち切り、計算効率を大幅向上
- ✅ 分散チューニング:RDBやRedisを使った並列実行で大規模探索を実現
- ✅ 転移学習:過去の知識を活用したウォームスタート、メタ学習で探索効率化
- ✅ 本番運用:段階的デプロイ、継続的モニタリング、定期的再チューニングのワークフロー
これらの技術を組み合わせることで、実務で求められる高品質かつ効率的なハイパーパラメータチューニングが可能になります。
📊 実践プロジェクト:エンドツーエンドのチューニングパイプライン
本章で学んだすべての技術を統合し、実際のビジネス課題を解決するエンドツーエンドのチューニングパイプラインを構築してください:
- Kaggleコンペティションや実データセットを選択
- マルチ目的最適化で精度と推論時間を最適化
- Pruningで探索効率化、分散実行で時間短縮
- メタ学習で過去の知識を活用
- 本番運用を想定したモニタリング・再チューニング機構の実装
このプロジェクトを通じて、実務レベルのハイパーパラメータチューニングスキルを確立しましょう。