新闻详情

新闻详情

首页 / 资讯中心 / 详情

15种机器学习分类算法全解析:选型、调参与scikit-learn实战

发布时间:2026/9/29 3:13:43来源:尧图网络
15种机器学习分类算法全解析:选型、调参与scikit-learn实战
机器学习里的分类算法说到底就是教机器把样本分到正确的格子里。你手上有一批带标签的数据模型学完之后面对一条没见过的新样本得给出一个类别判断——这就是分类算法每天要干的活。我做过信贷风控的违约识别也折腾过工业质检的缺陷分类还帮朋友做过音乐风格分类的小项目回过头看真正高频出现的分类算法也就那么十几种把它们摸透八成问题都能兜住。这篇内容适合刚入门想建立全局观的人也适合已经能跑通代码、但一到选型和调参就发懵的人。我会按“算法家族”来拆把每种算法的核心思路、关键参数、适用场景和踩过的坑一次讲清楚代码给的是 scikit-learn 体系下可以直接复现的写法参数计算过程也会写明白不玩虚的。1. 先建立地图15种分类算法到底该怎么归类拿到一个分类任务就无脑上随机森林或者 XGBoost是很多人的默认动作。我在早期也这样干过结果在一个只有 400 条样本、20 个特征的小数据集上XGBoost 调了半天还不如逻辑回归稳。后来我才想明白算法选型的第一性问题不是“哪个准”而是“这批数据长什么样它适合哪种假设”。1.1 用决策边界的形状来理解算法差异分类算法最直观的区别在于它能画出什么形状的决策边界。线性模型逻辑回归、线性判别分析、感知机、SGD 分类器只能画直线或者超平面它们的假设是“两类样本能被一个平面切开”。这类模型参数少、方差低、解释性强样本量小的时候非常抗过拟合但如果真实边界是弯的它再怎么调也学不出来。树模型决策树、随机森林、Extra Trees、AdaBoost、GBDT 系列画的是阶梯状的轴平行边界天然能拟合非线性关系而且对特征的单调变换不敏感你不用做标准化。代价是它对特征组合的连续性表达不够好边界是“锯齿”的在需要平滑边界的问题上会吃亏。核方法与距离方法SVM、KNN能画出光滑的曲线边界。SVM 靠核函数把数据映射到高维在低维看是曲线在高维看还是平面KNN 则压根不画边界它靠局部邻居投票边界形状由数据密度决定。神经网络MLP是万能逼近器理论上能拟合任意边界但代价是样本量要求高、超参数多、训练不稳定。1.2 15种算法速查对照表我把这次要讲的 15 种算法整理成一张表方便你先有个整体印象。这张表里的“是否需标准化”和“是否需调参”两列是我在实际项目里最常用的筛选依据。序号算法家族是否需标准化是否支持多分类典型适用场景1逻辑回归线性是是OvR/Softmax风控评分、点击率预估2线性判别分析 LDA线性概率是是小样本、需要降维3二次判别分析 QDA线性概率是是类别协方差差异大4感知机线性是否需包装教学、线性可分数据5SGD 分类器线性是是大规模稀疏数据6朴素贝叶斯概率否高斯版需要是文本分类、垃圾识别7K 近邻 KNN距离是是小数据、推荐召回8最近质心距离是是高维文本基线9支持向量机 SVM间隔核是是OvO中小样本、图像特征10决策树树否是规则提取、可解释需求11随机森林树Bagging否是通用首选基线12Extra Trees树Bagging否是高噪声、需快训练13AdaBoost树Boosting否是弱分类器集成14梯度提升族树Boosting否是竞赛、结构化数据15多层感知机 MLP神经网络是是大样本、复杂边界这张表里 14 号我合并了 GBDT、XGBoost、LightGBM、CatBoost 这几个同源实现它们的核心思想一致差别在工程优化。真正上手时你会发现光靠这张表还不够得知道每个算法内部在算什么。提示不要因为树模型不需要标准化就忽略数据清洗。缺失值、异常值对树的影响比对线性模型小但不代表没有影响尤其是标签噪声会直接让树长出错误的切分点。1.3 关于泛化误差的一个朴素认知很多人复习机器学习时会去啃泛化误差界VC 维、Rademacher 复杂度这些概念看着吓人。我自己的理解方式很简单泛化误差大致等于偏差的平方加方差再加不可约噪声。线性模型偏差高、方差低树模型偏差低、方差高集成方法随机森林、Boosting干的事情就是用各种手段把方差压下来或者把偏差补上去。随机森林通过 Bagging 加特征随机采样降低方差所以树可以长得很深Boosting 则是一棵一棵地拟合残差逐步降低偏差所以每棵树都很浅。理解了这个你就明白为什么随机森林要调 max_depth 让它充分生长而 GBDT 要调 learning_rate 和 n_estimators 控制步长——它们优化的目标根本不是一回事。2. 线性与概率派小样本高解释性场景的六把刀这一类算法是我做金融、医疗这类强监管场景时的第一选择原因是它们能给出系数、能算概率、能解释“为什么这条样本被判定为高风险”。模型上线之后业务方总会追问一句“凭什么”这时候能拿出一份特征权重表比什么都管用。2.1 逻辑回归被低估的基线之王逻辑回归的思路是把线性组合塞进 Sigmoid 函数输出一个 0 到 1 之间的概率再用 0.5 或者业务自定义的阈值切一刀。它优化的目标是对数似然等价于最小化交叉熵损失。很多人觉得它太简单但我在 Kaggle 和实际业务里见过太多次“调了三天的 XGBoost 只比逻辑回归高 0.5 个点”的情况而逻辑回归的训练时间是秒级的。关键参数有这么几个。C是正则化强度的倒数C 越小正则越强默认 1.0。我在样本少的时候会把它调到 0.01 到 0.1 之间。penalty选 l1 还是 l2l1 会做特征选择系数会被压到 0适合特征多且很多没用的场景l2 是平滑收缩适合特征都有点用的情况。solver的选择和 penalty 强绑定l2 用 lbfgsl1 用 liblinear 或者 saga数据量大且稀疏就用 saga。class_weightbalanced在类别不平衡时几乎是必开的它按类别频率反比给权重。from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline pipe Pipeline([ (scaler, StandardScaler()), (clf, LogisticRegression( C0.1, penaltyl2, solverlbfgs, max_iter2000, class_weightbalanced, random_state42, )), ]) pipe.fit(X_train, y_train) coef pipe.named_steps[clf].coef_[0]注意max_iter一定要给够。scikit-learn 默认 100特征多的时候它会在收敛前停下并抛出警告你以为模型练完了其实参数还在半路上。我一般直接给 2000配合tol1e-4。2.2 LDA 与 QDA带概率假设的线性与二次边界线性判别分析的核心假设是每个类别的特征服从正态分布且所有类别共享同一个协方差矩阵。在这些假设成立时它是最优贝叶斯分类器。它还有个附带好处可以顺便做降维把数据投影到最多“类别数减一”维的空间里再配合可视化特别方便。样本量远小于特征维数的时候LDA 的降维能力就是救命稻草。QDA 放宽了共享协方差的假设允许每个类别有自己的协方差矩阵于是决策边界从线性变成二次曲线。代价是参数量暴增每个类别要估计一个协方差矩阵。特征维度是 d参数就有 d(d1)/2 个样本不够的话矩阵会奇异直接报错或者算出乱七八糟的结果。我的经验是样本量至少是特征数的 10 倍以上再考虑 QDA否则老老实实用 LDA。from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis lda LinearDiscriminantAnalysis(solversvd, n_components2) qda QuadraticDiscriminantAnalysis(reg_param0.1) # reg_param 给协方差矩阵加一点对角扰动QDA 的reg_param就是为解决奇异问题设计的它把协方差矩阵往“协方差乘以(1-reg)加单位阵乘以reg”的方向拉经验值在 0.01 到 0.3 之间。这个参数在 scikit-learn 里默认是 0实务中建议手动设一下。2.3 感知机与 SGD 分类器为大规模数据准备的轻骑兵感知机是最古老的线性分类器更新规则简单到一句话分错了就把权重往正确方向推一点。它的问题是不收敛于最优解只要数据不是严格线性可分权重就会一直震荡。所以现在基本只在教学里出现或者作为对比基线。想让它输出概率也不行它只能给硬标签。SGD 分类器是它的实用化版本通过loss参数可以切换成不同的线性模型losslog_loss就是逻辑回归的随机梯度版本losshinge是线性 SVMlossmodified_huber是一种对异常值更鲁棒的近似lossperceptron就是感知机。它的真正优势在于支持partial_fit可以分块训练理论上能处理内存装不下的数据集。from sklearn.linear_model import SGDClassifier sgd SGDClassifier( losslog_loss, penaltyelasticnet, l1_ratio0.15, alpha1e-4, learning_rateoptimal, early_stoppingTrue, validation_fraction0.1, n_iter_no_change5, class_weightbalanced, )这里的alpha是正则强度和逻辑回归的 C 正好是倒数关系C1/alpha。early_stoppingTrue会切出一部分训练数据当验证集连续n_iter_no_change轮验证分数不提升就停这个机制在大规模训练里能省下大量时间。elasticnet混合正则的l1_ratio我一般从 0.15 开始试这是 scikit-learn 文档里推荐的默认起点。2.4 朴素贝叶斯家族文本分类的老朋友朴素贝叶斯的核心是贝叶斯定理加一个“特征条件独立”的强假设。这个假设在现实中几乎不成立但神奇的是它在文本分类上表现相当能打——因为在文本这种高维稀疏场景下独立假设带来的偏差远小于参数估计方差带来的收益。它有四个常用变体选错了效果差很多。GaussianNB假设特征服从正态分布适合连续特征比如传感器读数、身高体重这类。MultinomialNB假设特征是计数适合词频、TF-IDF 值。BernoulliNB假设特征是二值的适合词是否出现这种 0/1 矩阵注意它对未出现的词也建模短文本上有时比多项式版本更稳。ComplementNB是专门为不平衡文本设计的它用补集类别的统计量来估计参数在小类别上召回率更好。from sklearn.naive_bayes import MultinomialNB, ComplementNB from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.pipeline import Pipeline text_pipe Pipeline([ (tfidf, TfidfVectorizer(ngram_range(1, 2), min_df2, sublinear_tfTrue)), (clf, ComplementNB(alpha0.3)), ])alpha是拉普拉斯平滑系数默认 1.0。文本分类时我通常往小调0.1 到 0.5 之间试。sublinear_tfTrue会把 TF 换成 1log(TF)避免某个词频次过高主导权重这个小设置在长文本上收益明显。要注意一点朴素贝叶斯输出的概率值普遍偏极端接近 0 或 1拿来做排序可以拿来做精确概率校准就需要额外处理后面第六节会讲。3. 距离与间隔派KNN、最近质心与 SVM这三个算法的共同点是都和“距离”或者“几何间隔”有关它们对特征的尺度极其敏感。我见过太多次有人直接拿未标准化的数据跑 SVM结果 gamma 的默认设置直接让模型退化成随机猜测还以为是算法不行。3.1 K 近邻不训练但推理贵KNN 是懒惰学习的典型训练阶段什么都不做只是把数据存下来预测时计算新样本到所有训练样本的距离取最近的 k 个投票。它的决策边界完全由数据决定所以理论上只要样本足够密它能逼近任意边界。代价是预测时间和内存都随训练集线性增长而且在高维空间里距离度量会失效——这就是所谓的维度灾难所有点之间的距离都趋于相同。关键参数里n_neighbors最重要它是偏差和方差的直接旋钮。k 太小边界抖得厉害对噪声敏感k 太大边界被抹平欠拟合。我一般从 5 开始用交叉验证在 3 到 20 之间找。weightsdistance让近邻的票更有分量通常比等权投票好一点但在类别不平衡时要注意少数类的近邻可能被多数类淹没。距离度量metric默认是 minkowskip2 即欧氏距离文本场景可以试 cosine。from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline knn Pipeline([ (scaler, StandardScaler()), (clf, KNeighborsClassifier( n_neighbors7, weightsdistance, metricminkowski, p2, n_jobs-1, )), ])一个实用的加速技巧是用algorithmkd_tree或者ball_tree它们在低维一般 d20时能显著减少距离计算次数。维度高的时候老老实实用brute配合n_jobs-1开并行因为树结构在高维下剪枝效率会崩掉。3.2 最近质心被忽视的高维基线最近质心算法简单到有点可爱算出每个类别所有样本的均值向量作为“质心”预测时把新样本分给距离最近的质心。它没有可调参数除了距离度量训练极快在高维文本特征上经常能给出一个不错的下限。它的缺点也明显只有当各类别的协方差矩阵相同时它才等价于 LDA 的判别规则一旦某类内部是多个聚集簇均值就失去了代表性效果会急剧下滑。我的用法是把它当基线如果复杂模型跑下来还不如最近质心那基本上说明特征工程出了问题而不是模型不够强。from sklearn.neighbors import NearestCentroid nc NearestCentroid(metriceuclidean, shrink_threshold0.2)shrink_threshold是个有意思的参数它会把每个类别质心向全局质心收缩收缩量正比于该类别质心到全局质心的距离。在特征维度高、样本少的时候开启收缩能降低方差我一般从 0.1 到 0.5 之间试。这个参数在 scikit-learn 里默认是 None也就是不收缩。3.3 支持向量机核函数的参数怎么算SVM 的目标是找一个超平面让两类样本到它的最小间隔最大化。只有落在间隔边界上的样本支持向量决定最终超平面所以它对样本量不敏感对支持向量的质量敏感。线性不可分时用核函数把数据映射到高维在那里找超平面。RBF 核有两个关键参数C和gamma。C 控制对误分类的惩罚C 越大越不允许错分容易过拟合C 越小间隔越宽容错更多。gamma 控制单个样本的影响半径gamma 越大影响范围越小边界越曲折。gamma 有个经典的启发式取值gamma 1 / (n_features * X.var())这也是 scikit-learn 里gammascale的实现。假设你有 30 个特征标准化之后方差是 1那 gamma 就等于 1/30 约等于 0.033。这就是一个合理的起点。我通常用对数网格搜索让 C 在 2 的 -5 到 15 次方之间、gamma 在 2 的 -15 到 3 次方之间做组合。from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler svm_pipe Pipeline([ (scaler, StandardScaler()), (clf, SVC(kernelrbf, probabilityTrue, class_weightbalanced)), ]) param_grid { clf__C: [0.1, 1, 10, 100], clf__gamma: [scale, 0.001, 0.01, 0.1], } grid GridSearchCV(svm_pipe, param_grid, cv5, scoringf1_macro, n_jobs-1) grid.fit(X_train, y_train)注意probabilityTrue会额外用 Platt 缩放做概率估计并在内部做 5 折交叉验证训练时间大概增加 3 到 5 倍。如果只是要硬标签别开它。样本量上万之后SVC 的复杂度是 O(n²) 到 O(n³)这时候换LinearSVC或者SGDClassifier(losshinge)更合适。SVM 在多分类时默认用一对多OvOk 个类别要训 k(k-1)/2 个二分类器。5 个类别就是 10 个模型10 个类别就是 45 个类别数一多训练时间增长很快这点要有心理预期。4. 树模型与集成结构化数据上的主力部队如果你的数据是表格型的也就是那种有行有列、特征有明确业务含义的数据那树模型家族几乎肯定是上限最高的选择。我做过的大多数结构化数据项目最后上线的基本都是 XGBoost 或者 LightGBM随机森林则是快速拿到基线的首选。4.1 决策树集成的砖块与可解释性的天花板单棵决策树的训练过程就是在每个节点上选一个特征和一个阈值把数据切成两部分切分的标准是让子节点的纯度提升最大。分类任务默认用基尼系数criteriongini也可以换成信息熵entropy两者效果通常很接近基尼计算略快。它的最大价值是可解释性——一棵深度 3 到 4 的树可以画出来直接给业务方看转化成 if-else 规则就是一份风控策略。控制复杂度的参数优先级我这样排max_depth最重要限制树的深度通常 3 到 10 之间min_samples_leaf次之限制叶子节点的最小样本数我一般设成总样本数的 0.5% 到 2%min_samples_split控制节点分裂的最小样本数max_features控制每次分裂考虑的特征数量。from sklearn.tree import DecisionTreeClassifier, export_text dt DecisionTreeClassifier( criteriongini, max_depth4, min_samples_leaf20, min_samples_split40, class_weightbalanced, random_state42, ) dt.fit(X_train, y_train) print(export_text(dt, feature_nameslist(X_train.columns)))单棵树的致命弱点是高方差训练集稍微动一动整棵树的结构可能全变。这让它的泛化能力很不稳所以实务中很少单独使用几乎都是作为 Bagging 或 Boosting 的基学习器。4.2 随机森林与 Extra TreesBagging 的两种风味随机森林的做法是并行训练很多棵决策树每棵树用自助采样bootstrap抽出的样本子集并且在每次节点分裂时只随机考虑一部分特征分类默认是特征数的平方根。预测时所有树投票。两重随机性让树之间变得不相关平均之后方差大幅降低这就是它比单棵树稳的根本原因。Extra Trees 更进一步它在选择切分阈值时完全随机不从数据里找最优切分点而是随机选一个阈值。这带来两个后果一是训练更快二是在高噪声数据上偏差略大但方差更小。我做过一个传感器故障检测的任务标定标签本身有噪声Extra Trees 的测试表现反而比随机森林稳一点。关键参数上n_estimators一般给 300 到 800再多收益就很小了。max_features分类默认sqrt如果特征很多且每个都有信息可以调到 0.3 到 0.5。min_samples_leaf在处理不平衡数据时很关键设成 1 会让每棵树都长出针对少数类的噪声叶子我一般设 2 到 5。class_weightbalanced_subsample比balanced更适合随机森林因为它是每棵树在各自的 bootstrap 样本上重新计算权重。from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier rf RandomForestClassifier( n_estimators500, max_featuressqrt, min_samples_leaf3, class_weightbalanced_subsample, oob_scoreTrue, n_jobs-1, random_state42, ) rf.fit(X_train, y_train) print(OOB 分数:, rf.oob_score_)oob_scoreTrue是个我强烈建议打开的选项。自助采样时大约有 36.8% 的样本没被抽中这些袋外样本可以当成免费的验证集不用额外切分数据就能估计泛化性能。样本量小的时候这个小技巧能帮你多榨出一点训练数据。4.3 AdaBoost样本权重是怎么一步步算出来的AdaBoost 的思路是串行训练一系列弱分类器每个新的弱分类器都更关注前面分错的样本。它的推导过程很值得看一遍因为理解了权重更新你就理解了 Boosting 的本质。假设第 t 轮的弱学习器是 h_t它在加权样本上的错误率是 ε_t也就是被分错样本的权重之和。那么它的投票权重是α_t 0.5 × ln((1 - ε_t) / ε_t)这个式子很直观错误率越低权重越大当错误率等于 0.5 时α 等于 0也就是这个分类器被完全忽略错误率高于 0.5 时 α 会变成负数意味着它的判断要被反过来用。之后样本权重这样更新w_i ← w_i × exp(-α_t × y_i × h_t(x_i))y_i 是真实标签取 1 或 -1h_t(x_i) 是预测值。如果预测正确y_i × h_t(x_i) 等于 1指数项是负的权重被缩小如果预测错误指数项是正的权重被放大。最后所有样本权重归一化再进行下一轮。举个例子错误率 ε0.2那 α0.5×ln(0.8/0.2)0.5×1.3860.693。分错的样本权重乘上 exp(0.693)2.0也就是被放大到原来两倍分对的样本权重乘上 exp(-0.693)0.5被缩小一半。这种机制让后续的弱学习器越来越聚焦于困难样本。from sklearn.ensemble import AdaBoostClassifier from sklearn.tree import DecisionTreeClassifier ada AdaBoostClassifier( estimatorDecisionTreeClassifier(max_depth1), # 决策 stump n_estimators200, learning_rate0.5, algorithmSAMME, random_state42, )AdaBoost 对噪声和离群点非常敏感因为那些被反复分错的异常样本会拿到越来越大的权重最终把模型带偏。所以用之前一定要先清一遍标签噪声。另外 scikit-learn 1.2 之后base_estimator参数改名为estimator老代码迁移时注意。4.4 梯度提升族从 GBDT 到 XGBoost、LightGBM、CatBoost梯度提升的思想和 AdaBoost 不同它每一轮拟合的是当前模型预测值与真实值之间的残差。具体来说它把损失函数的负梯度当作“残差”的近似然后用一棵回归树去拟合这个负梯度再把树的输出加进模型。回归任务用平方损失时负梯度恰好就是真实残差分类任务用对数损失时负梯度是某种形式的概率残差。这个链条里有三个关键参数必须理解它们的联动关系。learning_rate是每棵树的贡献缩放系数也叫收缩系数n_estimators是树的棵数subsample是每棵树使用的样本采样比例。这三者共同决定模型容量学习率小、树多、采样少模型平滑但训练慢学习率大、树多很容易过拟合。我的经验是保持n_estimators × learning_rate大致固定然后在这个约束下微调。比如learning_rate0.1配 500 棵树和learning_rate0.05配 1000 棵树前者的效果通常和后者的差距在 1% 以内但训练时间差一倍。所以除非效果卡住了我一般用 0.1 起步。XGBoost 在目标函数里加了二阶泰勒展开和显式正则项正则项包含叶子数的 L2 惩罚和叶子权重的 L2 惩罚这让它比原始 GBDT 更不容易过拟合也支持自定义损失函数。LightGBM 用了直方图分箱和叶子优先生长策略训练速度通常比 XGBoost 快几倍但叶子优先生长在小数据上容易过拟合需要用num_leaves和min_child_samples控制。CatBoost 的核心卖点是原生处理类别特征它用有序目标编码避免标签泄漏在类别特征多的场景下省去大量特征工程。import lightgbm as lgb from sklearn.model_selection import train_test_split params { objective: binary, metric: auc, learning_rate: 0.05, num_leaves: 31, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, lambda_l2: 1.0, verbosity: -1, seed: 42, } X_tr, X_val, y_tr, y_val train_test_split(X_train, y_train, test_size0.2, stratifyy_train, random_state42) dtrain lgb.Dataset(X_tr, labely_tr) dval lgb.Dataset(X_val, labely_val, referencedtrain) model lgb.train( params, dtrain, num_boost_round2000, valid_sets[dval], callbacks[lgb.early_stopping(stopping_rounds100), lgb.log_evaluation(100)], )early_stopping是必开的。你给一个很大的num_boost_round让验证集分数自己决定什么时候停这样就不用猜树的数量了。min_child_samples对不平衡数据特别重要设大一点能防止模型在少数类上长出只覆盖几个样本的叶子。类别特征多的话LightGBM 可以直接声明categorical_feature或者干脆换 CatBoost。提示树模型的特征重要性有两种算口径别搞混。基于分裂次数的split会偏向取值多的特征基于不纯度减少的gain相对可靠一些。要做特征筛选我更推荐用 Permutation Importance它在验证集上打乱某一列看分数掉多少虽然慢但更贴近真实贡献。5. 神经网络与元集成从 MLP 到投票和堆叠前面几类算法各有各的假设而神经网络和元集成属于“我不管你怎么算我只要最后结果”的路线。它们的作用往往不是替换前面的模型而是在它们之上再做一层组合。5.1 多层感知机通用逼近器的代价MLP 由若干全连接层加非线性激活函数堆叠而成理论上单隐层就足以逼近任意连续函数但实践中深层结构更容易学到层次化的特征表示。分类任务输出层用 Softmax损失函数是交叉熵。关键参数里hidden_layer_sizes决定网络结构我一般从一个隐藏层开始神经元数取特征数的 1 到 2 倍比如(64,)或(128, 64)。alpha是 L2 正则系数默认 0.0001样本少的时候要往大调。learning_rate_init配合solveradam通常用 0.001。early_stoppingTrue会切 10% 数据做验证连续n_iter_no_change轮不提升就停。from sklearn.neural_network import MLPClassifier from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline mlp Pipeline([ (scaler, StandardScaler()), (clf, MLPClassifier( hidden_layer_sizes(128, 64), activationrelu, solveradam, alpha1e-3, batch_size64, learning_rate_init1e-3, max_iter500, early_stoppingTrue, validation_fraction0.15, n_iter_no_change20, random_state42, )), ])神经网络对特征尺度极度敏感不做标准化经常会出现 loss 不下降的情况。另外它对随机种子也敏感同一个配置换个种子结果可能差两三个点。我的做法是跑 5 个种子取平均或者至少报告多次运行的结果区间别拿单次结果下结论。5.2 Voting 与 Stacking把多个模型拧成一股绳投票法分硬投票和软投票。硬投票就是每个模型投一票票多者胜软投票是把各模型输出的概率平均取概率最大的类别。软投票通常更好因为它保留了概率信息。但前提是每个模型都能输出概率SVM 需要开probabilityTrue而且各模型的概率要经过校准否则一个输出 0.99、另一个输出 0.6平均之后会被前者主导。from sklearn.ensemble import VotingClassifier, StackingClassifier from sklearn.linear_model import LogisticRegression from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier estimators [ (lr, LogisticRegression(max_iter2000, class_weightbalanced)), (rf, RandomForestClassifier(n_estimators300, class_weightbalanced_subsample, n_jobs-1)), (svm, SVC(kernelrbf, probabilityTrue, class_weightbalanced)), ] voting VotingClassifier(estimatorsestimators, votingsoft, weights[1, 2, 1], n_jobs-1) stacking StackingClassifier( estimatorsestimators, final_estimatorLogisticRegression(max_iter2000), cv5, stack_methodpredict_proba, passthroughFalse, n_jobs-1, )Stacking 的做法是把基模型的输出当成新特征再训一个元学习器来做最终判断。这里的cv5非常重要它保证元学习器的训练特征是通过交叉验证生成的而不是基模型在训练集上的过拟合输出。如果直接用fit后的基模型预测训练集那些预测值会过于乐观元学习器学到的全是偏差。passthroughTrue会把原始特征也拼进去样本量大的时候可以试小样本容易过拟合。Voting 的weights参数值得试我一般把表现最好的模型权重设成 2。实测下来三个差异较大的模型做软投票稳定提升 1 到 2 个点是比较常见的但如果模型之间高度相关比如随机森林和 Extra Trees提升会小很多这时候不如把预算花在特征工程上。6. 落地实战完整流程、概率校准与踩坑速查前面讲的都是单个算法但实际项目里真正决定成败的是流程。我按自己平时做项目的顺序把关键环节和踩过的坑整理一下。6.1 一条能直接抄的完整流程第一步先划数据。我一般按 60/20/20 切成训练、验证、测试三份分层抽样保证各类别比例一致。验证集用来调参和早停测试集只在最后用一次中途反复看测试集等于变相过拟合。第二步做基线。用逻辑回归和最近质心各跑一遍拿到一个下限分数。这一步花不了十分钟但能让你知道数据的可分性大概在什么水平。第三步做特征处理。数值特征该填空的填空该做分箱的分箱类别特征做目标编码或者 one-hot文本特征走 TF-IDF。所有预处理都必须放进 Pipeline绝对不要在切分之前对全量数据做标准化或者填充那会把验证集的信息泄漏进训练过程导致分数虚高。第四步上主力模型。树模型先用默认参数跑随机森林或 LightGBM看大概水平然后针对性地调参用随机搜索或者贝叶斯搜索别用网格搜索硬啃。第五步做评估。类别不平衡的时候不要只看准确率。一个 95% 都是负类的数据集全预测负类也有 95% 准确率。我主要看宏平均 F1、加权 F1、AUC 和 PR 曲线下面积最后再看混淆矩阵确认错分集中在哪一类。from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score from sklearn.metrics import classification_report, roc_auc_score, confusion_matrix import numpy as np X_tr, X_te, y_tr, y_te train_test_split( X, y, test_size0.2, stratifyy, random_state42 ) cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(model, X_tr, y_tr, cvcv, scoringf1_macro, n_jobs-1) print(5折 F1 宏平均:, scores.mean().round(4), 标准差:, scores.std().round(4)) model.fit(X_tr, y_tr) pred model.predict(X_te) proba model.predict_proba(X_te)[:, 1] print(classification_report(y_te, pred, digits4)) print(AUC:, roc_auc_score(y_te, proba).round(4)) print(confusion_matrix(y_te, pred))6.2 概率校准为什么你的模型概率不能直接用很多模型输出的概率不是真实概率。SVM 的 Platt 缩放、朴素贝叶斯的极端输出、随机森林的投票比例都存在系统性偏差。随机森林尤其典型它输出的概率值会集中在 0.5 附近从不给出接近 0 或 1 的值因为它本质上是多棵树投票的平滑结果。如果你要把概率喂给下游做决策——比如风控里按违约概率定利率或者广告里按点击率出价——那就必须做校准。最常用的方法是CalibratedClassifierCV它用交叉验证的方式在验证集上拟合一个映射函数把原始分数映射成校准概率。methodsigmoid对应 Platt 缩放适合样本少的情况methodisotonic是保序回归更灵活但要更多数据一般至少上千条才用。from sklearn.calibration import CalibratedClassifierCV from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import brier_score_loss base RandomForestClassifier(n_estimators300, n_jobs-1, random_state42) calibrated CalibratedClassifierCV(base, methodisotonic, cv5) calibrated.fit(X_tr, y_tr) raw_proba base.fit(X_tr, y_tr).predict_proba(X_te)[:, 1] cal_proba calibrated.predict_proba(X_te)[:, 1] print(校准前 Brier:, brier_score_loss(y_te, raw_proba).round(4)) print(校准后 Brier:, brier_score_loss(y_te, cal_proba).round(4))衡量校准质量用 Brier 分数或者可靠性曲线也叫校准曲线。Brier 分数就是预测概率和真实标签之间均方误差越小越好。我做过一个违约预测的项目校准前的 Brier 是 0.18做完保序回归之后降到 0.12虽然 AUC 几乎没变但业务方按概率定价的收益明显改善了。6.3 常见问题速查表下面这张表是我这些年踩过的坑里复现频率最高的几个遇到问题的第一反应可以先对照着查。现象可能原因排查与解决方法训练集分数远高于验证集过拟合 / 数据泄漏检查预处理是否在切分前做加正则、减深度、增样本SVM 结果接近随机未标准化 / gamma 不合适放进 Pipeline 标准化gamma 从 scale 起调树模型在少数类上召回为 0类别不平衡class_weight、调阈值、上采样或 SMOTE逻辑回归不收敛max_iter 太小 / 特征共线提到 2000 以上检查共线性考虑 l1随机森林概率都很接近概率未校准用 CalibratedClassifierCV 校准交叉验证方差很大样本少 / 类别分布不均用分层 K 折增加折数到 10多跑几个种子测试分数忽高忽低只跑了一次固定种子跑 5 次取均值报告标准差高维数据 KNN 效果差维度灾难先降维PCA、LDA或换树模型6.4 几段我个人总结的实操心得第一条关于阈值。绝大多数分类器的默认判定阈值是 0.5这是在假设两类误判代价相同时才成立的。现实中几乎从来不是这样。医疗筛查里漏诊的代价远大于误诊那阈值就该往下调牺牲精确率换召回率。我的做法是在验证集上遍历阈值画出 P-R 曲线找那个能让业务指标最大化的点然后把这个阈值和模型一起固化下来。第二条关于样本量。我有个粗略的经验公式参数量应该远小于样本量。线性模型的参数量大致等于特征数树模型的复杂度靠深度和叶子数控制神经网络的参数量是连接权重的数量。如果你有 500 条样本和 200 个特征逻辑回归加强正则往往是唯一稳的选择上深度网络基本就是过拟合现场。第三条关于特征质量。我做过一个对比实验同一个 LightGBM在原始特征上 AUC 是 0.81加了三个人工构造的交叉特征之后升到 0.87而我把学习率从 0.1 调到 0.01、树从 500 加到 2000AUC 只涨了 0.004。特征工程的天花板远高于调参这一点无论怎么强调都不为过。第四条关于模型融合的投入产出比。当单个模型已经很接近上限时融合通常还能再挤 1 到 2 个点但如果单个模型和基线差距很大先别急着 Stacking回头看看特征和标签是不是有问题。我见过太多人花一周时间搭了一个五模型堆叠结果不如别人认真清洗一遍标签。最后说一个很多人会忽略的点把模型跑通和把模型用好是两件完全不同的事。跑通只需要会调 API用好需要你知道每个参数在优化什么、每步操作会引入什么偏差、每个分数背后对应什么业务含义。这 15 种算法不用每一种都练到精通但你应该知道什么场景该抽哪张牌以及抽出来之后怎么用对。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OpenClaw 配 TaoToken:开源 AI 智能体 settings.json 配置骨架与落地验证 2026/9/29 4:19:46

OpenClaw 配 TaoToken:开源 AI 智能体 settings.json 配置骨架与落地验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Manus 专利技术拆解:AI Agent 如何用 TaoToken 统一 Key 打通工具链? 2026/9/29 4:19:46

Manus 专利技术拆解:AI Agent 如何用 TaoToken 统一 Key 打通工具链?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
鸿蒙系统 openInputStream(uri) 卡顿阻塞排查:TaoToken 统一 Key 通道下的配置与验证 2026/9/29 4:19:45

鸿蒙系统 openInputStream(uri) 卡顿阻塞排查:TaoToken 统一 Key 通道下的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
为什么选择OpenClaw?深入分析其技术优势与商业价值 2026/9/29 4:19:45

为什么选择OpenClaw?深入分析其技术优势与商业价值

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
提升办公效率:OpenClaw 本地自动化 AI 工具搭建实战教程(TaoToken 统一 Key 配置篇) 2026/9/29 4:19:39

提升办公效率:OpenClaw 本地自动化 AI 工具搭建实战教程(TaoToken 统一 Key 配置篇)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Zephyr BSP: 38-多板多芯片支持 2026/9/29 4:19:39

Zephyr BSP: 38-多板多芯片支持

摘要:本文围绕 Zephyr BSP 中 Multi-Board / Multi-Chip 的核心问题展开:哪些能力放在 SoC 层、哪些放在 Board 层、哪些通过 Devicetree/Kconfig 表达。文章从「SoC 描述芯片有什么,Board 描述板子实际用了什么」这一第一原则出发,依次讲解 Family → Variant 的 SoC 组织…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉