2.1 估计器 (Estimator)


文档摘要

2.1 估计器 (Estimator) 本节摘要:估计器是 Scikit-learn 里所有"会从数据中学习"的对象的统称——分类器、回归器、聚类器、降维器、预处理工具都算。它靠两个方法运转:fit 从数据里总结规律并写进对象自身,predict 或 transform 再用这些规律对新数据给出结果。学到的规律存在以单下划线结尾的属性里,比如线性回归的 coef。搞懂估计器,就抓住了这套库统一接口的根,后面所有模型都是它的变体。

2.1 估计器 (Estimator)

本节摘要:估计器是 Scikit-learn 里所有"会从数据中学习"的对象的统称——分类器、回归器、聚类器、降维器、预处理工具都算。它靠两个方法运转:fit 从数据里总结规律并写进对象自身,predict 或 transform 再用这些规律对新数据给出结果。学到的规律存在以单下划线结尾的属性里,比如线性回归的 coef_。搞懂估计器,就抓住了这套库统一接口的根,后面所有模型都是它的变体。

阅读收获

阅读完本节,你应当能够:

  1. 说清估计器是什么,为什么它是统一接口的根基
  2. 讲明白一次 fit 调用之后,对象内部到底发生了什么
  3. 区分有监督与无监督估计器在 fit 参数上的差异
  4. 认出常见的估计器内部状态属性,知道它们的下划线约定
  5. 按"实例化、fit、predict 或 transform"的顺序搭一个最小模型
  6. 知道哪些估计器还会附带 transform、score 等方法

一、先跑起来:一个最小估计器

别急着背定义。我们先让一个估计器跑起来,感受它的三步节奏。

from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression X, y = load_iris(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42) model = LogisticRegression() # 第一步:实例化,只存超参数 model.fit(X_train, y_train) # 第二步:学习,生成内部状态 y_pred = model.predict(X_test) # 第三步:应用,给出结果 print(y_pred[:5])

这三行是整个 Scikit-learn 的骨架。实例化时,model 里面只有你传进去的超参数——比如默认的正则化强度、最大迭代次数——它还没见过一个样本,谈不上"学会了什么"。fit 之后,对象内部多了一批数字,这些数字才是模型。predict 只是照着这些数字做计算,把新样本算成类别或数值。

换个模型,节奏一模一样。把 LogisticRegression 换成决策树、随机森林、支持向量机,三步结构不变。这就是统一接口的第一层好处:学一次,通吃所有。你花在第一个模型上的理解,可以直接迁移到第两百个。

把这三步刻进肌肉记忆:造对象、喂数据、拿结果。后面不管多复杂的模型,都逃不出这个节奏;你遇到的绝大多数报错,也都能归结为这三步里某一步做错了——顺序反了、数据喂错了、或者没喂就急着拿结果。

再感受一下无监督的版本,换聚类器试试:

from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=3, random_state=42) # 实例化,指定簇数 labels = kmeans.fit_predict(X) # 学习并给每个样本分簇 print(labels[:5])

注意两处差别:K 均值没有目标 y,只有特征 X;它把"学习"和"给结果"合并成了一个 fit_predict。但骨子里还是同一套节奏——先造对象,再喂数据,最后拿结果。估计器这个总概念,就是为这种一致性而生。

二、fit 到底做了什么:状态从无到有

估计器可以理解成一台机器。实例化是造出一台空机器,fit 是给机器装上模具并校准,predicttransform 才是开动机器生产。没装模具就开机,机器只会报错。

那"模具"具体存哪?存在对象的下划线属性里。Scikit-learn 用一条硬约定区分两类属性:没有下划线的,是你在实例化时传入的超参数;带单下划线结尾的,是 fit 之后学到的内部状态。这个约定让你一眼就能分辨"配置"和"成果"——前者是你给的,后者是数据教的。

以线性回归为例,fit 之后你能读到 coef_(每个特征的系数)和 intercept_(截距);K 均值聚类之后能读到 cluster_centers_(簇中心);标准化器之后能读到 mean_scale_。这些数字是 fit 从训练数据里"榨"出来的,是模型的全部家当。想判断模型学到了没有、学成了什么样,就去看这些带下划线的属性。

fit 之后,状态就躺在那,随时能读:

model.fit(X_train, y_train) print(model.coef_) # 每个特征的系数,个数对得上特征数 print(model.intercept_) # 截距,一个数

别小看这两行。很多排错工作就是从这里开始的:系数里出现异常大的值,说明某列特征可能没做缩放;重要度全挤在一个特征上,说明模型可能过拟合了这条特征。学会读状态,你才不是"跑完分数就完事"的黑盒用户。

估计器的类型比想象中多。下面的谱系图帮你把它们归位:

有监督估计器要拿着"题目和标准答案"一起学——特征 X 加上目标 y,学着从 X 推出 y。无监督估计器只拿"题目",从数据本身找结构,比如把点聚成几堆、把高维压成低维。这也是为什么 fit 的签名在不同估计器上不一样:有的 fit(X, y),有的 fit(X)

图:估计器生命周期

一张图看全估计器从"空壳"到"可用"的状态流转:

图:估计器生命周期

三、有监督与无监督:fit 的签名为什么不一样

同样叫 fit,有的要喂 y,有的不用。差别来自任务性质:

维度 有监督估计器 无监督估计器
fit 参数 fit(X, y) fit(X)
学什么 从 X 到 y 的映射 X 自身的结构
典型代表 逻辑回归、线性回归、随机森林 K 均值、主成分分析
常见任务 分类、回归 聚类、降维
内部状态举例 coef_、intercept_ cluster_centers_、components_

判断一个估计器属于哪类,不用查文档,看 fit 的第二个参数是不是必需的就行。需要 y 的就是有监督,因为没标准答案就无从"纠错";不需要 y 的就是无监督,它找的是数据内部的规律。

有个细节值得记:聚类器在无监督里有点特殊,它既像有监督那样给每个样本一个"标签"(簇编号),训练时又用不到真标签。所以 K 均值的 fit 只收 X,但训练完能用 predict 给新点分簇。别被"它输出标签"骗了,它依然是无监督——那标签不是学出来的"标准答案",是算法自己定义的簇编号。

再给一张更细的速查表,把"类别、代表、学到的状态、给的结果"一次看全:

估计器类别 代表 内部状态举例 输出
分类器 逻辑回归、随机森林 coef_、feature_importances_ 类别标签
回归器 线性回归、岭回归 coef_、intercept_ 连续数值
聚类器 K 均值 cluster_centers_ 簇编号
降维器 主成分分析 components_ 低维数据

这张表和前面的谱系图是同一个东西的两种读法:图看"关系",表看"怎么落地"。实际选型时,我一般先想清楚任务是有监督还是无监督,再落到具体的类别和代表模型上,最后看它 fit 之后能留下什么状态、predict 能给什么结果。

四、工程实践要点

估计器用起来顺,坑也藏在细节里。

⚠️ 常见坑:顺序错了会直接报错。没 fitpredict,或者把训练数据和新数据搞混,都容易把"学习"和"应用"的边界弄反。更隐蔽的坑是"在测试集上 fit"——这属于数据泄露,会让评估成绩虚高,我们会在转换器那节细讲。

💡 关键直觉:估计器本质是"状态加两个动词"。fit 写状态,predict 或 transform 读状态。想判断一个对象是不是估计器,看它有没有 fit 就够;想判断它还能干什么,看它有没有 predict、transform、score。

还有两条约定值得记住。第一,fit 返回 self,所以能链式写成一行;但为了可读,我建议拆开写,出问题更好定位。第二,每次 fit 都会覆盖旧状态——同一个对象重新 fit 新数据,之前学到的系数就没了,这是"重新训练"而不是"增量学习"。需要增量更新时,得找专门支持热启动的估计器,而不是靠反复 fit

状态与可重复性

模型学到的状态,还牵出工程上绕不开的一件事:可复现。同一份数据、同一个超参数,两次训练应该得到完全相同的结果,你才敢拿结论去复现、去和别人对答案。可不少算法内部带随机性——随机森林抽样本和特征、K 均值挑初始簇心——种子不同,学出的系数会略有出入,分数也跟着抖。

随机性本身不坏,它常是算法甩开局部最优的手段;坏的是随机性失控。所以这些估计器都留了一个 random_state,作用是把随机过程钉死:传入同一个整数,随机森林每次都抽到同一批样本,K 均值每次都从同一组初始中心出发,结果逐位可复现。它不改变算法本质,只是锁住随机数生成器。写报告、对结果之前先把 random_state 定下来,能省下大量扯皮。

怎么判断估计器训练过没有?最直接的是看它有没有长出带下划线的属性——coef_ 存在,说明 fit 跑过了。访问没训练的对象会直接抛 NotFittedError,这是 Scikit-learn 设的护栏;想程序化断言,用 check_is_fitted 帮助函数。别靠"我记得调过 fit"这种记忆,显式检查,排错少走弯路。

最后补一句 score 的定位。很多估计器自带 score,分类器默认给准确率,回归器默认给决定系数。它是一条快捷通道,适合快速看个大概;要做严肃评估,还是得去第 2.4 节用专门的指标。

五、关于估计器的常见问题

fit 之后,我怎么知道模型到底学到了什么?
去看带下划线的属性。线性回归看 coef_ 和 intercept_,决策树看 feature_importances_ 了解每个特征的重要程度,聚类看 cluster_centers_。这些属性是 fit 的产物,也是你诊断模型的窗口——某个系数大得离谱、重要度全挤在一个特征上,都是值得警惕的信号,说明数据或模型可能有问题。

get_params 和那些带下划线的属性是一回事吗?
不是,别混。get_params 返回的是超参数,也就是实例化时能传的配置项,跟 fit 没关系;带下划线的属性才是学习成果。打个比方:get_params 是"这台机器的说明书参数",coef_ 是"机器实际调出来的模具刻度"。调参改前者,训练产出后者,两条线泾渭分明。

为什么估计器要做成对象,而不是一个函数?
因为学习要分两步,中间还得保存状态。一个函数跑完就散架了,记不住"我学到的规律"。对象能把状态和操作绑在一起:fit 把规律写进去,predict 读出来,换数据时状态跟着对象走。这就是估计器这个设计的全部意义,也是它能被保存、复用、塞进管道的原因。

"估计器"和"模型"是同一个东西吗?
日常口语里我们常把"训练好的估计器"就叫"模型"。严格说,估计器是"会学习、能预测的对象",模型是它 fit 之后那套具体的状态——系数、结构这些。同一个估计器类,换数据、换超参数,能训练出无数个不同的模型。下文再提"模型"时,指的都是这个 fit 完、带状态的估计器实例。

fit 返回 self、支持链式调用,这值得用吗?
fit 返回 self,是为了支持一行里 fit 完立刻 predict 的链式写法。但链式调用牺牲了可读性,中间哪一步出错不好定位。我倾向拆开写,把"造对象、喂数据、拿结果"三行摆清楚,除非是交互式探索里临时试一下。这是风格选择,没有对错,关键是团队保持一致。

核心回顾

  • 估计器是统一接口的根:分类器、回归器、聚类器、预处理工具,凡是有 fit 的对象都算。
  • fit 写入状态:它从数据里总结规律,存进带下划线的属性,比如 coef_、cluster_centers_。
  • predict 与 transform 只读状态:它们用学到的规律对新数据给结果,不再改参数。
  • 实例化只存超参数:没有下划线的属性是配置,带下划线的是成果,一眼可辨。
  • 有监督要 y,无监督不要:fit(X, y) 与 fit(X) 的差别就是任务性质的差别。
  • 没 fit 就 predict 会报错:状态为空,机器开不了工。
  • 重新 fit 会覆盖旧状态:不是增量学习。

估计器讲的是"会学习的对象"这个总概念。下一节我们聚焦其中专门负责加工数据的那一支——转换器,看 fit 和 transform 被拆成两段后,怎么把数据泄露挡在门外。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U