new file: checkpoints_tree/task1_tr_tree/best_tr_tree.pkl
new file: checkpoints_tree/task1_tr_tree/model_selection.csv new file: evaluation_outputs/task1_tr_tree/evaluation_train_all_samples.csv new file: evaluation_outputs/task1_tr_tree/evaluation_train_curve.png new file: evaluation_outputs/task1_tr_tree/evaluation_val_all_samples.csv new file: evaluation_outputs/task1_tr_tree/evaluation_val_curve.png new file: evaluation_outputs/task1_tr_tree/harmonic_5mm_1.55Hz_prediction.png new file: scripts_tree/__pycache__/config.cpython-310.pyc new file: scripts_tree/config.py new file: scripts_tree/evaluate.py new file: scripts_tree/predict_single.py new file: scripts_tree/train.py
This commit is contained in:
160
scripts_tree/train.py
Normal file
160
scripts_tree/train.py
Normal file
@@ -0,0 +1,160 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pickle
|
||||
from dataclasses import asdict
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
import sys
|
||||
|
||||
import pandas as pd
|
||||
from sklearn.ensemble import ExtraTreesRegressor, GradientBoostingRegressor, RandomForestRegressor
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from scripts.dataset import build_datasets, report_to_text
|
||||
|
||||
try:
|
||||
from .config import FEATURE_NAMES, ExperimentConfig, checkpoint_dir, make_experiment_config
|
||||
except ImportError:
|
||||
from config import FEATURE_NAMES, ExperimentConfig, checkpoint_dir, make_experiment_config
|
||||
|
||||
|
||||
def build_regressor(model_name: str, random_state: int):
|
||||
if model_name == "extra_trees":
|
||||
return ExtraTreesRegressor(
|
||||
n_estimators=600,
|
||||
max_depth=None,
|
||||
min_samples_leaf=1,
|
||||
min_samples_split=2,
|
||||
random_state=random_state,
|
||||
)
|
||||
if model_name == "random_forest":
|
||||
return RandomForestRegressor(
|
||||
n_estimators=500,
|
||||
max_depth=None,
|
||||
min_samples_leaf=1,
|
||||
min_samples_split=2,
|
||||
random_state=random_state,
|
||||
)
|
||||
if model_name == "gradient_boosting":
|
||||
return GradientBoostingRegressor(
|
||||
n_estimators=300,
|
||||
learning_rate=0.03,
|
||||
max_depth=3,
|
||||
random_state=random_state,
|
||||
loss="huber",
|
||||
)
|
||||
raise ValueError(f"Unsupported model: {model_name}")
|
||||
|
||||
|
||||
def records_to_frame(records: list[Any]) -> pd.DataFrame:
|
||||
rows = []
|
||||
for record in records:
|
||||
row = {
|
||||
"file_name": record.file_path.name,
|
||||
"frequency_hz": float(record.frequency_hz),
|
||||
"x_rms": float(record.x_rms.item()),
|
||||
"y_rms": float(record.y_rms.item()),
|
||||
"target_tr": float(record.target_y_rms.item()),
|
||||
}
|
||||
for name, value in zip(FEATURE_NAMES, record.features.tolist()):
|
||||
row[name] = float(value)
|
||||
rows.append(row)
|
||||
return pd.DataFrame(rows)
|
||||
|
||||
|
||||
def evaluate_frame(model, frame: pd.DataFrame) -> dict[str, float]:
|
||||
feature_matrix = frame.loc[:, FEATURE_NAMES].to_numpy()
|
||||
pred_tr = model.predict(feature_matrix)
|
||||
pred_tr = pred_tr.clip(min=1e-6)
|
||||
pred_rms = pred_tr * frame["x_rms"].to_numpy()
|
||||
true_rms = frame["y_rms"].to_numpy()
|
||||
relative_error = abs(pred_rms - true_rms) / true_rms.clip(min=1e-6)
|
||||
return {
|
||||
"mean_rms_error": float(relative_error.mean()),
|
||||
"median_rms_error": float(pd.Series(relative_error).median()),
|
||||
"max_rms_error": float(relative_error.max()),
|
||||
}
|
||||
|
||||
|
||||
def serialize_for_checkpoint(value: Any) -> Any:
|
||||
if isinstance(value, Path):
|
||||
return str(value)
|
||||
if isinstance(value, dict):
|
||||
return {key: serialize_for_checkpoint(sub_value) for key, sub_value in value.items()}
|
||||
if isinstance(value, tuple):
|
||||
return [serialize_for_checkpoint(item) for item in value]
|
||||
if isinstance(value, list):
|
||||
return [serialize_for_checkpoint(item) for item in value]
|
||||
return value
|
||||
|
||||
|
||||
def train() -> None:
|
||||
config = make_experiment_config()
|
||||
datasets, raw_records, reports = build_datasets(config.data)
|
||||
del datasets
|
||||
print(report_to_text(reports))
|
||||
|
||||
train_df = records_to_frame(raw_records["train"])
|
||||
val_df = records_to_frame(raw_records["val"])
|
||||
|
||||
feature_matrix = train_df.loc[:, FEATURE_NAMES].to_numpy()
|
||||
target = train_df["target_tr"].to_numpy()
|
||||
|
||||
results: list[dict[str, Any]] = []
|
||||
best_bundle: dict[str, Any] | None = None
|
||||
best_val_error = float("inf")
|
||||
|
||||
for model_name in config.model.candidate_models:
|
||||
model = build_regressor(model_name, config.model.random_state)
|
||||
model.fit(feature_matrix, target)
|
||||
|
||||
train_metrics = evaluate_frame(model, train_df)
|
||||
val_metrics = evaluate_frame(model, val_df)
|
||||
row = {
|
||||
"model_name": model_name,
|
||||
"train_mean_rms_error": train_metrics["mean_rms_error"],
|
||||
"train_median_rms_error": train_metrics["median_rms_error"],
|
||||
"train_max_rms_error": train_metrics["max_rms_error"],
|
||||
"val_mean_rms_error": val_metrics["mean_rms_error"],
|
||||
"val_median_rms_error": val_metrics["median_rms_error"],
|
||||
"val_max_rms_error": val_metrics["max_rms_error"],
|
||||
}
|
||||
results.append(row)
|
||||
print(
|
||||
f"{model_name}: train_mean={train_metrics['mean_rms_error']:.6f} | "
|
||||
f"val_mean={val_metrics['mean_rms_error']:.6f}"
|
||||
)
|
||||
|
||||
if val_metrics["mean_rms_error"] < best_val_error:
|
||||
best_val_error = val_metrics["mean_rms_error"]
|
||||
best_bundle = {
|
||||
"model_name": model_name,
|
||||
"model": model,
|
||||
"config": serialize_for_checkpoint(asdict(config)),
|
||||
"feature_names": list(FEATURE_NAMES),
|
||||
"val_metrics": val_metrics,
|
||||
"train_metrics": train_metrics,
|
||||
}
|
||||
|
||||
summary_df = pd.DataFrame(results).sort_values("val_mean_rms_error").reset_index(drop=True)
|
||||
ckpt_dir = checkpoint_dir(config)
|
||||
summary_path = ckpt_dir / config.train.summary_name
|
||||
summary_df.to_csv(summary_path, index=False)
|
||||
print(f"Model selection saved to: {summary_path}")
|
||||
print(summary_df.to_string(index=False))
|
||||
|
||||
if best_bundle is None:
|
||||
raise RuntimeError("No valid model trained.")
|
||||
|
||||
best_path = ckpt_dir / config.train.best_model_name
|
||||
with best_path.open("wb") as handle:
|
||||
pickle.dump(best_bundle, handle)
|
||||
print(f"Best tree model saved to: {best_path}")
|
||||
print(f"Best model: {best_bundle['model_name']} | val_mean_rms_error={best_val_error:.6f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
train()
|
||||
Reference in New Issue
Block a user