2. 标签传播算法基础原理:半监督学习、图结构、标签传播的直觉与迭代过程

这一章,我们来聊聊标签传播算法的根基。说白了,就是搞清楚它凭什么能在数据治理里发挥作用。我接触这个算法好几年了,每次用它处理那些“半死不活”的脏数据,都觉得它挺神奇的。

你可能会问:什么是半监督学习?图结构又是什么鬼?标签传播到底是怎么“传染”的?别急,我们一个一个来拆。

2.1 从“半监督”说起

传统的机器学习,要么是有监督(全靠标注数据),要么是无监督(全靠自己聚类)。但在实际的数据治理场景里,我们遇到的情况往往是:少量标注 + 大量未标注。为什么?因为标注数据太贵了。我早年间在一个客户数据清洗项目里,光标注几千条“是否同一人”就花了一周时间。

这就是半监督学习的用武之地。它利用少量已知标签,去推测大量未知标签。你想想看,这像不像一个班级里,老师只教了几个尖子生,然后让他们去带动全班同学?

核心思想:假设数据中存在内在的聚类结构或流形结构。相似的样本应该拥有相似的标签。

学习范式 数据标注情况 典型应用
有监督学习 全部标注 需要大量人工,成本高
无监督学习 全部未标注 聚类分析,不依赖标签
半监督学习 少量标注 + 大量未标注 标签传播、自训练等,适合数据治理

我个人习惯把半监督学习想象成一个“纠错机制”。标注数据是铁证,未标注数据是嫌疑人。算法通过它们之间的关联,把铁证慢慢地“传染”给嫌疑人。

2.2 图结构:数据关系的“地图”

标签传播算法依赖一个关键的东西——图。图是由节点和边组成的。每个数据点就是一个节点,节点之间的相似度就是边。

举个例子。假设我们有1000条客户记录,一部分有“是否重复”的标签。我们把这1000条记录当成1000个节点。名字相似、地址相近的两条记录之间,我们就连一条边,边的粗细代表相似度高低。

嗯,这里要注意:图的质量直接决定了传播效果。我在项目里见过有人直接用皮尔逊相关系数算相似度,结果图里全是弱连接,传播到一半就断了。

我的经验:建图时,我通常会用K近邻(KNN)或ε-半径图。KNN保证每个节点都有固定数量的邻居,不容易产生孤立节点。ε-半径图则更适合密度均匀的数据。

图结构有几个重要参数:

  • 节点数:就是样本总数。
  • 边与权重:用高斯核函数计算:w_ij = exp(-||x_i - x_j||² / σ²)。σ控制着相似度的衰减速度。
  • 邻接矩阵:一个N×N的矩阵,记录了节点间的连接关系。

你想想看,有了这个“地图”,标签传播就有了路径。

避坑指南:我曾经在一个高噪声数据集上直接建全连接图,结果计算量爆炸,而且密集的边让标签传播“糊”成了一片。后来我改用稀疏图,效果好了很多。

2.3 标签传播的直觉:病毒式扩散

直觉上,标签传播就是让已知标签沿着图的边“流”向未知节点。就像一滴墨水落在一张纸上,慢慢洇开。

算法的核心假设是:两个节点越相似,它们的标签越可能相同。这看似简单,但非常强大。

我们可以把传播过程想象成一场“投票”:

  1. 每个未标记节点,统计它的邻居节点都有哪些标签。
  2. 邻居节点的权重越高,投票权重越大。
  3. 得票最多的标签,成为该节点的新标签。
  4. 重复这个过程,直到标签不再变化。

说得再直白点,就是“跟你的朋友保持一致”。你身边的朋友都是博士,你大概率也是;你邻居都是做清洁的,你大概率也是。

2.4 迭代过程:从模糊到清晰

刚接触这个算法时,我总觉得一次传播就够了。后来发现,一次传播只能影响直接邻居,真正的“远距离”传染,需要多轮迭代。

标准的迭代过程如下:

# 伪代码演示
while not converged:
    for each unlabeled node u:
        # 统计邻居的标签分布
        scores = {}
        for each neighbor v of u:
            label_v = labels[v]
            weight_uv = weight[u][v]
            scores[label_v] += weight_uv
        # 更新为得分最高的标签
        new_label = argmax(scores)
        labels[u] = new_label
    # 检查是否收敛
    if labels unchanged:
        break

关键点:每次迭代后,标签会沿着图结构“流动”得更远。一般来说,收敛速度取决于图的直径和权重分布。我见过最快的迭代3轮就稳定了,最慢的跑了100轮。

这里有个细节:已标记节点在迭代中要不要更新?我的习惯是不更新。把它们当做“锚点”,永远不能改变。一旦你让锚点动了,整个传播就会跑偏。

迭代要素 说明 我的建议
初始化 已标记节点固定标签,未标记节点随机或统一 统一设置为-1,避免干扰
同步更新 所有节点同时用上一轮邻居结果更新 保证传播方向一致
收敛判断 本轮标签变化量小于阈值或达到最大轮次 阈值设为0,因为离散标签变化就是1

为什么迭代会收敛?因为图结构固定,每次更新都是“减少局部能量”,最终会稳定在一个局部最优解。说白了,就是所有节点都找到了一个“舒服”的标签。

来,我们看一张流程图,把整个逻辑串起来。

标签传播算法核心流程图 输入:图 + 标注标签 初始化:固定锚点 迭代:更新未标记节点 收敛判断:是否稳定 否,继续迭代 是,停止迭代 输出:全部节点标签 核心:标签沿着图边“流动”,直到所有节点达成局部一致

这张图已经把算法的血肉榨干了。你看,输入是图结构和少数标注,输出是全量标签。中间的无非就是初始化、迭代更新、收敛判断。说得难听点,哪怕你只懂一点点图论,也能跑通一个简化版。

不过,真正应用到数据治理中,我们要考虑的事情远不止这些。比如:图建多大?权重怎么调?遇到标签震荡怎么办?这些坑,我后面会一点点填。