Python中Scikit-learn如何实现半监督学习_利用LabelPropagation

LabelPropagation要求数据具备局部平滑性,特征须标准化,未标记样本标签必须为-1,且需至少两个已标记类别;拟合用transduction_获取全部预测,不提供置信度,对噪声敏感。

LabelPropagation 在 scikit-learn 中的适用前提

LabelPropagation 不是万能半监督工具,它要求数据天然具备“局部平滑性”——即相似样本大概率共享标签。如果特征空间中同类样本离散、或距离度量失真(比如未标准化的混合类型特征),算法会传播错误标签。它默认使用 RBF 核构建相似图,对 n_neighborsgamma 敏感,且不支持稀疏标签矩阵(即不能只给部分样本标 0/1,其余为 np.nan;必须显式用 -1 表示未标记)。

实际使用前务必检查:

  • 特征已标准化(StandardScalerMinMaxScaler),尤其当量纲差异大时
  • 未标记样本用 -1 填充 y,不是 Nonenp.nan 或字符串
  • 训练集里至少有 2 个不同类别的已标记样本,否则图拉普拉斯矩阵奇异

正确初始化和拟合 LabelPropagation

LabelPropagation 的关键参数不是越多越好。默认 kernel='rbf' 足够多数场景;若改用 'knn',需设 n_neighbors(通常 5–10),但要注意小数据集上过小的 n_neighbors 会导致图不连通。拟合时传入的是完整 X(含未标记样本)和带 -1y,不是只喂已标记子集。

典型写法:

from sklearn.semi_supervised import LabelPropagation
from sklearn.preprocessing import StandardScaler

X_scaled = StandardScaler().fit_transform(X) y_semi = y.copy() y_semi[unlabeled_indices] = -1 # 必须是整数 -1

lp = LabelPropagation(kernel='rbf', gamma=20, max_iter=1000) lp.fit(X_scaled, y_semi) ypred = lp.transduction # 注意:不是 predict(),是 transduction_ 属性

注意:transduction_ 是模型对全部输入样本的预测结果(含原先未标记的),而 predict() 只能用于新样本——但它内部仍依赖训练时构建的图,泛化能力弱,一般不用。

LabelPropagation 输出结果的解读与陷阱

LabelPropagation 的输出 transduction_ 是硬标签(int 类别),不提供置信度。它不保证所有未标记样本都被重新赋值——若某样本在相似图中孤立(如 KNN 图中邻居全未标记),其标签可能保持为 -1(取决于收敛行为)。更隐蔽的问题是:它对噪声标签极其敏感,一个错标样本可能污染整片邻域。

Python 3.14.3

微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。

排查建议:

  • 检查 lp.n_iter_ 是否达到 max_iter,若是,说明未收敛,可调大 max_iter 或减小 gamma
  • 对比 transduction_[labeled_indices] 和原始 y[labeled_indices],确认无已标记样本被覆盖(正常情况下不会,但若 gamma 过大或图构造异常可能发生)
  • lp.score(X_scaled[labeled_indices], y[labeled_indices]) 看已标记部分准确率,骤降往往意味着传播失控

LabelPropagation vs LabelSpreading:选哪个?

两者核心区别在目标函数:LabelPropagation 是硬约束(已标记点标签绝对固定),LabelSpreading 是软约束(允许已标记点在迭代中微调,加了正则项)。实践中,LabelSpreading 更鲁棒,对参数不敏感,且默认就带 alpha 控制已标记点影响力(推荐从 alpha=0.2 试起);而 LabelPropagation 更适合你完全信任已标注样本、且数据流形结构非常清晰的场景。

简单判断依据:

  • 如果你发现 LabelPropagation 对已标记样本的预测出错,立刻换 LabelSpreading
  • 如果你需要概率输出,两个都不行——得接 predict_proba 的替代方案(如用传播后的伪标签训一个 RandomForestClassifier
  • LabelSpreadingkernel 参数行为一致,但多一个 alpha,少一个 gamma(RBF 下用 gamma,KNN 下不用)

真正麻烦的从来不是调参,而是未标记样本的分布偏移——如果它们和已标记样本根本不在同一子流形上,再准的图模型也只会把错误扩散得更自信。

相关推荐:

如何实现Python中线程安全的单例模式_使用元类metaclass或Lock加锁

因为__new__不是原子操作:线程A判断_instance为None后,未执行super().__new__前,线程B也判为None,导致重复创建;GIL不保护跨语句逻辑,需用双重检查锁定(DCL)加threading.Lock确保线程安全。 为什么直接用 __new__ 实现单例在多线程下会失效 因为 __new__ 本身不是原子操作:线程 A 判断 _instance 为 None 后,还没...

怎样提升Python解包大型Zip文件的速度_利用zipfile配合多线程

单纯用zipfile.ZipFile.extractall()解大型ZIP文件速度慢,因其本质是单线程顺序处理,不支持并发;有效提速方式是用ThreadPoolExecutor并发调用extract(),每个线程独立打开ZIP文件处理单个条目,并注意编码兼容、内存控制与线程安全。 单纯用 zipfile.ZipFile.extractall() 解大型 ZIP 文件,基本就是单核硬扛,速度瓶颈明显...

Python处理JSON报JSONDecodeError怎么解决_利用try-except配合非严格模式解析

JSONDecodeError根本原因是输入文本违反RFC 8259规范,常见触发场景包括BOM头、末尾逗号、单引号、注释、Unicode转义不全、换行符混用;Python json模块无strict=False参数,严格解析不可绕过;推荐方案为预清洗后标准解析,次选ast.literal_eval(限可信Python字面量),或引入json5/demjson3第三方库;try-except需记录...

如何解决Python测试中数据库连接频繁开闭问题_利用fixture的yield模式

pytest fixture 中用 yield 管理 pymysql 连接更稳,因其确保无论测试是否异常都会执行清理;应避免全局连接池、多次 close、共享连接,并在 yield 前 ping 保活和重置状态。 频繁在 pytest 测试中用 pymysql.connect() 打开又关闭数据库连接,不是“看起来不优雅”的问题,而是会直接拖慢测试执行、触发 MySQL 的 max_connect...

如何用Python自动化操作Word文档生成报告_利用python-docx模块

python-docx新建文档默认页边距1英寸、字体Calibri 11pt,因不加载normal.dotm模板而用硬编码值;需显式设置section边距和Normal样式字体;add_paragraph()自带段前段后间距致空行;cell.text会清空格式,应操作paragraphs[0].runs;样式名须严格匹配。 python-docx 能生成格式规整的 Word 报告,但不能读取或修改...

Python如何实现图的深度优先遍历_基于栈结构或递归函数实现

递归DFS栈溢出风险高因CPython默认递归深度仅1000,长链或环易触发RecursionError;隐式栈不可控,闭包和帧对象加剧内存开销;图应优先用显式栈实现。 递归实现DFS时,为什么栈溢出风险比预期高? Python默认递归深度限制是1000层,图中存在长链或环状结构时极易触发RecursionError。这不是算法错,而是CPython解释器对递归调用的硬性约束。 用sys.setr...

如何在Python中实现异步的邮件发送_利用aiosmtplib库提升邮件推送效率

smtplib.sendmail()不能直接用于异步邮件发送,因为它是阻塞式调用,会等待TCP连接、认证、数据传输等全过程,拖垮事件循环;在FastAPI等异步框架中直接使用会导致协程饿死、响应延迟突增。 为什么不能直接用 smtplib.sendmail() 做异步邮件发送 smtplib.SMTP.sendmail() 是阻塞式调用,每次发信都会等待 TCP 连接建立、HELO/EHLO 交换...

如何在Python中实现异步的InfluxDB时序数据写入_利用aioinflux提升效率

根本原因是aioinflux默认逐点发送、未批量提交、连接未复用且并发控制不当;需传入Point列表批量写、复用Client实例、用asyncio.gather/Semaphore控并发、禁用gzip、配连接池及合理超时,并调优InfluxDB服务端限速参数。 为什么直接用 aioinflux 写 InfluxDB 还是慢? 很多人以为换上 aioinflux 就自动“高并发写入”,结果发现吞吐量...

如何在Django中实现复杂的统计报表查询_利用Python的Aggregate聚合函数

Aggregate返回单条字典结果,annotate为每条记录添加字段并保持QuerySet可链式操作;分组聚合必须用values()配合annotate,Aggregate不能用于已分组查询。 直接用 Aggregate 做复杂统计报表,大概率会卡在“查不出想要的分组结构”或“多层聚合嵌套报错”上——它本身不处理分组,只做单次聚合计算,真要出报表,得和 values()、annotate() 配...

如何在Python Django中实现分库分表逻辑_利用Router控制模型路由到指定库

Django的DatabaseRouter仅支持分库,无法自动分表,因其只在查询前选择数据库别名,不干预SQL生成与表名替换,分表需手动处理建表、查询、写入及迁移。 分库分表不能靠 Django 的 DatabaseRouter 完全实现,它只负责“路由到库”,不处理“表拆分逻辑”;真正要分表(比如按时间/用户ID哈希),必须手动干预查询、写入和迁移流程。 为什么 DatabaseRouter 只...