anytoany-ml / hyperparameters.py
milsonson's picture
Add full hyperparameter studio and dark workbench UI
6a18a7f verified
Raw
History Blame Contribute Delete
11.8 kB
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Literal
import pandas as pd
ParameterType = Literal["int", "float", "bool", "str", "optional_int", "optional_float", "tuple_int"]
@dataclass(frozen=True)
class Parameter:
name: str
default: Any
kind: ParameterType
description: str
def p(name: str, default: Any, kind: ParameterType, description: str) -> Parameter:
return Parameter(name, default, kind, description)
HYPERPARAMETER_SCHEMAS: dict[str, list[Parameter]] = {
"MLP": [
p("hidden_layer_sizes", (64, 32), "tuple_int", "隐藏层神经元,例如 128,64"),
p("activation", "relu", "str", "激活函数:relu / tanh / logistic / identity"),
p("solver", "adam", "str", "优化器:adam / sgd / lbfgs"),
p("alpha", 0.0001, "float", "L2 正则强度"),
p("batch_size", "auto", "str", "批大小;auto 或整数"),
p("learning_rate", "constant", "str", "学习率策略:constant / invscaling / adaptive"),
p("learning_rate_init", 0.001, "float", "初始学习率"),
p("power_t", 0.5, "float", "invscaling 学习率指数"),
p("max_iter", 1000, "int", "最大训练迭代次数"),
p("shuffle", True, "bool", "每轮是否打乱样本"),
p("tol", 0.0001, "float", "优化停止容差"),
p("warm_start", False, "bool", "是否复用上次训练结果"),
p("momentum", 0.9, "float", "SGD 动量"),
p("nesterovs_momentum", True, "bool", "是否使用 Nesterov 动量"),
p("early_stopping", False, "bool", "验证集长期无提升时提前停止;小数据建议 false"),
p("validation_fraction", 0.1, "float", "提前停止验证集比例"),
p("beta_1", 0.9, "float", "Adam 一阶矩衰减"),
p("beta_2", 0.999, "float", "Adam 二阶矩衰减"),
p("epsilon", 1e-8, "float", "Adam 数值稳定项"),
p("n_iter_no_change", 20, "int", "允许无提升的迭代轮数"),
p("max_fun", 20000, "int", "LBFGS 最大函数调用次数"),
],
"Random Forest": [
p("n_estimators", 240, "int", "树的数量"),
p("criterion", "auto", "str", "划分标准;auto 根据任务选择"),
p("max_depth", None, "optional_int", "单棵树最大深度;none 不限制"),
p("min_samples_split", 2, "int", "内部节点继续划分的最少样本"),
p("min_samples_leaf", 1, "int", "叶节点最少样本"),
p("min_weight_fraction_leaf", 0.0, "float", "叶节点最小权重比例"),
p("max_features", "auto", "str", "每次划分考察的特征数"),
p("max_leaf_nodes", None, "optional_int", "最大叶节点数"),
p("min_impurity_decrease", 0.0, "float", "最小不纯度下降"),
p("bootstrap", True, "bool", "是否 bootstrap 采样"),
p("oob_score", False, "bool", "是否计算袋外分数"),
p("n_jobs", -1, "int", "并行任务数;-1 使用全部核心"),
p("warm_start", False, "bool", "是否追加训练更多树"),
p("class_weight", None, "str", "分类权重:none / balanced / balanced_subsample"),
p("ccp_alpha", 0.0, "float", "成本复杂度剪枝强度"),
p("max_samples", None, "optional_float", "每棵树 bootstrap 样本比例"),
],
"Extra Trees": [
p("n_estimators", 240, "int", "树的数量"),
p("criterion", "auto", "str", "划分标准;auto 根据任务选择"),
p("max_depth", None, "optional_int", "单棵树最大深度"),
p("min_samples_split", 2, "int", "内部节点最少样本"),
p("min_samples_leaf", 1, "int", "叶节点最少样本"),
p("min_weight_fraction_leaf", 0.0, "float", "叶节点最小权重比例"),
p("max_features", "auto", "str", "每次划分考察的特征数"),
p("max_leaf_nodes", None, "optional_int", "最大叶节点数"),
p("min_impurity_decrease", 0.0, "float", "最小不纯度下降"),
p("bootstrap", False, "bool", "是否 bootstrap 采样"),
p("oob_score", False, "bool", "是否计算袋外分数"),
p("n_jobs", -1, "int", "并行任务数"),
p("warm_start", False, "bool", "是否追加训练"),
p("class_weight", None, "str", "分类权重"),
p("ccp_alpha", 0.0, "float", "成本复杂度剪枝"),
p("max_samples", None, "optional_float", "bootstrap 样本比例"),
],
"Gradient Boosting": [
p("loss", "auto", "str", "损失函数;auto 根据任务选择"),
p("learning_rate", 0.05, "float", "每棵树的贡献缩放"),
p("n_estimators", 180, "int", "提升阶段数量"),
p("subsample", 0.9, "float", "每阶段使用的样本比例"),
p("criterion", "friedman_mse", "str", "树划分标准"),
p("min_samples_split", 2, "int", "内部节点最少样本"),
p("min_samples_leaf", 1, "int", "叶节点最少样本"),
p("min_weight_fraction_leaf", 0.0, "float", "叶节点最小权重比例"),
p("max_depth", 3, "int", "基学习器最大深度"),
p("min_impurity_decrease", 0.0, "float", "最小不纯度下降"),
p("max_features", None, "str", "每次划分使用的特征数"),
p("alpha", 0.9, "float", "Huber/Quantile 分位数"),
p("max_leaf_nodes", None, "optional_int", "最大叶节点数"),
p("warm_start", False, "bool", "是否追加提升阶段"),
p("validation_fraction", 0.1, "float", "提前停止验证比例"),
p("n_iter_no_change", None, "optional_int", "无提升提前停止轮数"),
p("tol", 0.0001, "float", "提前停止容差"),
p("ccp_alpha", 0.0, "float", "基学习器剪枝强度"),
],
"SVM": [
p("C", 2.0, "float", "正则化强度的倒数"),
p("kernel", "rbf", "str", "核函数:rbf / linear / poly / sigmoid"),
p("degree", 3, "int", "多项式核次数"),
p("gamma", "scale", "str", "核系数:scale / auto / 数值"),
p("coef0", 0.0, "float", "poly/sigmoid 核独立项"),
p("shrinking", True, "bool", "是否使用 shrinking 启发式"),
p("probability", True, "bool", "分类时启用概率估计"),
p("tol", 0.001, "float", "停止容差"),
p("cache_size", 300, "float", "核缓存大小 MB"),
p("class_weight", None, "str", "分类权重:none / balanced"),
p("max_iter", -1, "int", "最大迭代;-1 不限制"),
p("decision_function_shape", "ovr", "str", "多分类决策:ovr / ovo"),
p("break_ties", False, "bool", "分类平票时按置信度裁决"),
p("epsilon", 0.1, "float", "SVR epsilon-insensitive 区间"),
],
"KNN": [
p("n_neighbors", 7, "int", "参与预测的邻居数量"),
p("weights", "distance", "str", "邻居权重:uniform / distance"),
p("algorithm", "auto", "str", "近邻搜索:auto / ball_tree / kd_tree / brute"),
p("leaf_size", 30, "int", "树结构叶大小"),
p("p", 2, "float", "Minkowski 距离指数"),
p("metric", "minkowski", "str", "距离度量"),
p("n_jobs", -1, "int", "并行任务数"),
],
"Decision Tree": [
p("criterion", "auto", "str", "划分标准;auto 根据任务选择"),
p("splitter", "best", "str", "划分策略:best / random"),
p("max_depth", 12, "optional_int", "最大树深"),
p("min_samples_split", 2, "int", "内部节点最少样本"),
p("min_samples_leaf", 1, "int", "叶节点最少样本"),
p("min_weight_fraction_leaf", 0.0, "float", "叶节点最小权重比例"),
p("max_features", None, "str", "划分使用的特征数"),
p("max_leaf_nodes", None, "optional_int", "最大叶节点数"),
p("min_impurity_decrease", 0.0, "float", "最小不纯度下降"),
p("class_weight", None, "str", "分类权重"),
p("ccp_alpha", 0.0, "float", "成本复杂度剪枝"),
],
"Logistic Regression": [
p("penalty", "l2", "str", "正则类型:l1 / l2 / elasticnet / none"),
p("C", 1.0, "float", "正则化强度的倒数"),
p("l1_ratio", 0.0, "float", "Elastic Net 的 L1 比例"),
p("dual", False, "bool", "是否使用对偶问题"),
p("tol", 0.0001, "float", "停止容差"),
p("fit_intercept", True, "bool", "是否拟合截距"),
p("intercept_scaling", 1.0, "float", "liblinear 截距缩放"),
p("class_weight", None, "str", "类别权重:none / balanced"),
p("solver", "lbfgs", "str", "求解器:lbfgs / liblinear / saga 等"),
p("max_iter", 500, "int", "最大迭代次数"),
p("warm_start", False, "bool", "是否复用上次解"),
p("n_jobs", -1, "int", "并行任务数"),
],
"Linear Regression": [
p("fit_intercept", True, "bool", "是否拟合截距"),
p("copy_X", True, "bool", "是否复制输入特征"),
p("tol", 1e-6, "float", "稀疏求解停止容差"),
p("n_jobs", -1, "int", "并行任务数"),
p("positive", False, "bool", "是否约束系数为非负"),
],
}
def _display(value: Any) -> str:
if value is None:
return "none"
if isinstance(value, tuple):
return ",".join(str(item) for item in value)
if isinstance(value, bool):
return str(value).lower()
return str(value)
def default_parameter_frame(algorithm: str) -> pd.DataFrame:
if algorithm not in HYPERPARAMETER_SCHEMAS:
raise ValueError(f"未知算法:{algorithm}")
return pd.DataFrame(
[
{
"Parameter": parameter.name,
"Value": _display(parameter.default),
"Description": parameter.description,
}
for parameter in HYPERPARAMETER_SCHEMAS[algorithm]
]
)
def _parse_value(parameter: Parameter, raw: Any) -> Any:
value = str(raw).strip()
lower = value.lower()
if parameter.kind in {"optional_int", "optional_float"} and lower in {"none", "null", ""}:
return None
if parameter.kind == "bool":
if lower not in {"true", "false"}:
raise ValueError("需要 true 或 false")
return lower == "true"
if parameter.kind in {"int", "optional_int"}:
return int(float(value))
if parameter.kind in {"float", "optional_float"}:
return float(value)
if parameter.kind == "tuple_int":
return tuple(int(item.strip()) for item in value.split(",") if item.strip())
if lower in {"none", "null"}:
return None
if parameter.name == "batch_size":
return int(value) if lower != "auto" else "auto"
if parameter.name == "max_features":
try:
return float(value) if "." in value else int(value)
except ValueError:
return value
if parameter.name == "gamma":
try:
return float(value)
except ValueError:
return value
return value
def parse_parameter_frame(algorithm: str, frame: pd.DataFrame | list[list[Any]]) -> dict[str, Any]:
schema = {parameter.name: parameter for parameter in HYPERPARAMETER_SCHEMAS[algorithm]}
data = frame if isinstance(frame, pd.DataFrame) else pd.DataFrame(frame)
if list(data.columns) != ["Parameter", "Value", "Description"]:
data.columns = ["Parameter", "Value", "Description"]
parsed: dict[str, Any] = {}
for _, row in data.iterrows():
name = str(row["Parameter"]).strip()
if name not in schema:
raise ValueError(f"未知超参数:{name}")
try:
parsed[name] = _parse_value(schema[name], row["Value"])
except (TypeError, ValueError) as exc:
raise ValueError(f"超参数 {name} 的值无效:{row['Value']}") from exc
return parsed