周志华《Machine Learning》学习笔记--第十五章--规则学习

规则学习:从"如果-那么"到智能决策的逻辑密码

摘要

本文深入解析周志华《机器学习》第15章规则学习的核心内容,从基本概念到序贯覆盖框架、剪枝优化、一阶规则学习与归纳逻辑程序设计,结合西瓜判别、医学诊断等生活案例、核心代码与实验对比,带你掌握规则学习的精髓,理解"逻辑规则"如何让机器拥有可解释的决策能力------这也是规则学习区别于神经网络"黑箱模型"的最大优势。

1. 什么是规则学习?------机器也会"讲道理"

我们平时做判断时,总会不自觉地用逻辑规则:"如果今天下雨且气温低于10℃,那我就穿羽绒服";挑西瓜时会说"如果根蒂蜷缩且脐部凹陷,那大概率是好瓜"。规则学习就是让机器从数据中自动学习这种"如果-那么"形式的逻辑规则,并用这些规则对未知样本做决策。

1.1 规则的基本形式

一条标准的逻辑规则形如:

⊕←f1∧f2∧⋯∧fL\oplus \leftarrow f_1 \land f_2 \land \cdots \land f_L⊕←f1∧f2∧⋯∧fL

其中每个符号的含义:

  • ⊕\oplus⊕:规则头(Head),表示规则的结论(比如"好瓜""欺诈交易")
  • ←\leftarrow←:逻辑蕴含符号,读作"如果...那么..."
  • fkf_kfk:逻辑文字(Literal),是对属性的布尔检验(比如"根蒂=蜷缩""含糖率>0.2")
  • ∧\land∧:合取符号,读作"并且"
  • LLL:规则长度,即规则体中文字的个数

举个西瓜判别规则的例子:

好瓜←(根蒂=蜷缩)∧(脐部=凹陷)好瓜 \leftarrow (根蒂=蜷缩) \land (脐部=凹陷)好瓜←(根蒂=蜷缩)∧(脐部=凹陷)

这条规则长度为2,意思是"如果一个西瓜的根蒂是蜷缩的,并且脐部是凹陷的,那么它是好瓜"。符合这条规则的样本,我们说被这条规则覆盖(Cover)。

1.2 冲突消解与默认规则

当多条规则覆盖同一个样本但给出不同结论时,就发生了冲突 。比如样本同时被"好瓜←色泽乌黑"和"¬好瓜←纹理模糊"覆盖,这时候需要冲突消解策略来解决:

  1. 投票法:统计所有覆盖该样本的规则,得票最多的结论作为最终结果
  2. 排序法:给规则定义优先级,冲突时用优先级最高的规则
  3. 元规则法:用"关于规则的规则"指导,比如"冲突时选长度最短的规则"(奥卡姆剃刀原则)

另外,规则集可能无法覆盖所有样本,这时候需要设置一条默认规则(Default Rule),比如"未被上述规则覆盖的西瓜都不是好瓜"。

1.3 命题规则 vs 一阶规则

规则学习分为两大类,对应不同的表达能力:

  • 命题规则:由原子命题(比如"色泽=青绿")和逻辑连接词组成,只能处理单个样本的属性-值对,不能描述样本之间的关系。比如上面的西瓜规则就是命题规则。
  • 一阶规则:引入了逻辑变量和谓词,可以描述样本之间的关系,也叫"关系型规则"。比如"更好(X,Y)←根蒂更蜷(X,Y)∧脐部更凹(X,Y)",表示"如果西瓜X的根蒂比Y更蜷,并且脐部比Y更凹,那么X比Y更好"。

💡 通俗理解:命题规则只能说"这个瓜是好瓜",一阶规则能说"这个瓜比那个瓜好"------表达能力直接提升一个维度!

2. 序贯覆盖:逐条"啃下"训练样本

规则学习的目标是生成一个能覆盖尽可能多样本的规则集。最直接的方法是序贯覆盖(Sequential Covering),也叫"分治策略":

每次从训练集上学到一条最优规则,然后把这条规则覆盖的所有样本从训练集中删除;用剩下的样本重复上述过程,直到没有样本剩下或满足停止条件。

这个过程就像"剥洋葱",一层一层剥掉样本,直到剥完为止。

2.1 自顶向下的规则生成

生成单条规则有两种主流策略,最常用的是自顶向下(Top-Down):从最一般的空规则(比如"好瓜←",覆盖所有样本)开始,逐步添加逻辑文字缩小覆盖范围,直到规则只覆盖正例或满足精度要求。

我们用西瓜数据集2.0(p.80表4.2)来演示这个过程,如图1所示:


图1 西瓜数据集2.0上自顶向下生成单条规则

过程分析

  1. 初始空规则:好瓜←,覆盖所有17个样本
  2. 第一轮候选文字评估:计算每个属性-值对作为文字加入后的准确率,比如"色泽=乌黑"覆盖4个样本,其中3个正例,准确率3/4;"脐部=凹陷"同样准确率3/4
  3. 选择属性次序靠前的"色泽=乌黑"加入规则,得到:好瓜←(色泽=乌黑)
  4. 第二轮候选文字评估:对"色泽=乌黑"覆盖的4个样本,加入"根蒂=蜷缩"后准确率100%(覆盖3个正例)
  5. 最终得到规则:好瓜←(色泽=乌黑)∧(根蒂=蜷缩)

2.2 自底向上的规则生成

另一种策略是自底向上(Bottom-Up):从最特殊的规则(比如直接用某个样本的所有属性生成规则,只覆盖该样本)开始,逐步删除文字扩大覆盖范围,直到不能再泛化为止。

这种策略适合训练样本较少的场景,但对噪声更敏感,因此在命题规则学习中用得较少,主要用于一阶规则学习。

2.3 集束搜索:缓解贪心的局部最优

上面的过程每次只选一个"当前最优"的文字,这是典型的贪心算法,容易陷入局部最优。比如图1中如果第一轮选"脐部=凹陷",第二轮加"根蒂=蜷缩",得到的规则"好瓜←(脐部=凹陷)∧(根蒂=蜷缩)"同样准确率100%,但覆盖了更多正例。

为了缓解这个问题,我们可以用集束搜索(Beam Search):每轮保留最优的b个候选文字,在下一轮分别扩展,再保留最优的b个,直到生成最终规则。当b=2时,就能得到上面那条覆盖更多样本的规则。

3. 剪枝优化:给规则"减肥",避免过拟合

和决策树一样,规则学习如果不加限制,会生成过于复杂的规则,完美拟合训练集但泛化能力很差------这就是过拟合。剪枝就是给规则"减肥",去掉对泛化无用的文字或规则。

3.1 预剪枝:CN2的似然率统计量

预剪枝是在规则生成过程中就停止生长,最经典的是CN2算法使用的似然率统计量 (Likelihood Ratio Statistics, LRS):

LRS=2⋅(m^+log2(m^+m^++m^−)(m+m++m−)+m^−log2(m^−m^++m^−)(m−m++m−))LRS=2 \cdot\left(\hat{m}{+} log {2} \frac{\left(\frac{\hat{m}{+}}{\hat{m}{+}+\hat{m}{-}}\right)}{\left(\frac{m{+}}{m_{+}+m_{-}}\right)}+\hat{m}{-} log {2} \frac{\left(\frac{\hat{m}{-}}{\hat{m}{+}+\hat{m}{-}}\right)}{\left(\frac{m{-}}{m_{+}+m_{-}}\right)}\right)LRS=2⋅ m^+log2(m++m−m+)(m^++m^−m^+)+m^−log2(m++m−m−)(m^++m^−m^−)

符号解释:

  • m+m_+m+/m−m_-m−:整个训练集中的正例数/反例数
  • m^+\hat{m}+m^+/m−m-m−:当前规则覆盖的正例数/反例数

LRS衡量了规则覆盖样本的分布与训练集整体分布的差异:LRS越大,说明规则的预测效果越显著,不是偶然得到的。当LRS小于某个阈值(比如对应99%置信度)时,就停止添加文字。

3.2 后剪枝:REP与IREP

后剪枝是先生成完整的规则集,再删除冗余的部分,效果通常比预剪枝更好。

  • REP(减错剪枝) :将数据集分为训练集和验证集,对每条规则尝试删除任意文字或整条规则,选择验证集上性能最好的剪枝结果,迭代直到性能不再提升。缺点是复杂度高(O(m4)O(m^4)O(m4),m为样本数)。
  • IREP(增量REP) :每生成一条规则就立即在验证集上剪枝,然后删除该规则覆盖的样本,复杂度降到O(mlog2m)O(m log^2 m)O(mlog2m),效率大幅提升。

3.3 RIPPER算法:规则学习的"集大成者"

RIPPER(Repeated Incremental Pruning to Produce Error Reduction)是目前性能最好的命题规则学习算法之一,融合了IREP剪枝和后处理优化,速度比C4.5决策树更快,泛化性能相当。

RIPPER的算法流程如图2所示:


图2 RIPPER算法流程

核心思想:先用IREP*生成初始规则集,然后对每条规则生成替换规则和修订规则,选择全局最优的规则集,通过多次迭代优化缓解贪心算法的局部最优问题。

4. 一阶规则学习:处理"关系"的利器

命题规则只能处理单个样本的属性,但现实中很多问题需要描述样本之间的关系:

  • 社交网络:"如果A和B是朋友,B和C是朋友,那么A和C可能是朋友"
  • 生物信息:"如果蛋白质A与蛋白质B相互作用,B与疾病C相关,那么A可能与C相关"
  • 西瓜挑选:"瓜1比瓜2色泽更深,根蒂更蜷,所以瓜1更好"

这时候就需要一阶规则学习,其中最经典的算法是FOIL(First-Order Inductive Learner)。

4.1 西瓜数据集5.0:关系数据的表示

为了处理西瓜之间的比较关系,我们将西瓜数据集2.0转化为关系数据(西瓜数据集5.0,p.355表15.1):

  • 背景知识:定义样本之间的关系谓词,比如"色泽更深(X,Y)""根蒂更蜷(X,Y)"等
  • 关系样例:目标谓词的正负例,比如"更好(1,10)"(瓜1比瓜10好)是正例,"¬更好(10,1)"是反例

4.2 FOIL算法:一阶规则的序贯覆盖

FOIL遵循序贯覆盖框架,采用自顶向下的策略生成一阶规则。它用FOIL增益 来选择最优的逻辑文字:

F−Gain=m^+×(log2m^+m^++m^−−log2m+m++m−)F_{-} Gain =\hat{m}{+} \times\left(log {2} \frac{\hat{m}{+}}{\hat{m}{+}+\hat{m}{-}}-log {2} \frac{m{+}}{m{+}+m_{-}}\right)F−Gain=m^+×(log2m^++m^−m^+−log2m++m−m+)

符号解释:

  • m+m_+m+/m−m_-m−:添加文字前,当前规则覆盖的正例数/反例数
  • m^+\hat{m}+m^+/m−m-m−:添加文字后,当前规则覆盖的正例数/反例数

💡 为什么FOIL增益只乘m^+\hat{m}_+m^+?因为关系数据中正例通常远少于反例(比如"瓜1比多少瓜好"的正例只有几个,反例有几十个),所以需要给正例更高的权重,避免规则偏向覆盖更多反例。

4.3 FOIL的学习过程

以西瓜数据集5.0为例,初始空规则是"更好(X,Y)←",覆盖所有25个正例和25个反例。

  1. 计算所有候选文字的FOIL增益:"色泽更深(X,Y)"加入后,覆盖16个正例和2个反例,FOIL增益为16×(log21618−log22550)=13.2816 \times (log_2 \frac{16}{18} - log_2 \frac{25}{50}) = 13.2816×(log21816−log25025)=13.28,是所有候选中最大的
  2. 加入文字得到规则:更好(X,Y)←色泽更深(X,Y)
  3. 继续添加文字,直到规则只覆盖正例,最终生成第一条规则
  4. 删除该规则覆盖的样本,重复上述过程直到所有正例被覆盖

5. 归纳逻辑程序设计:让机器"发明"新概念

FOIL虽然能处理关系,但不能表达函数和逻辑嵌套。归纳逻辑程序设计 (Inductive Logic Programming, ILP)在一阶规则的基础上引入了函数和嵌套,学得的规则可以直接被Prolog等逻辑程序语言调用,甚至能自动发明新的谓词(新概念)。

5.1 最小一般泛化LGG:从具体到一般

ILP采用自底向上的策略,核心是最小一般泛化(Least General Generalization, LGG):给定两个具体的规则,找到能同时特化为这两个规则的最特殊的一般规则。

举个例子:

  • 规则1:更好(1,10)←根蒂更蜷(1,10)∧脐部更凹(1,10)
  • 规则2:更好(1,15)←根蒂更蜷(1,15)∧脐部更凹(1,15)

LGG过程:

  1. 比较规则头"更好(1,10)"和"更好(1,15)",将不同的常量10和15替换为变量Y,得到"更好(1,Y)"
  2. 规则体中所有对应的10和15都替换为Y
  3. 删除两个规则中不同的文字(这里没有)
  4. 最终LGG:更好(1,Y)←根蒂更蜷(1,Y)∧脐部更凹(1,Y)

再和规则"更好(2,10)←根蒂更蜷(2,10)∧脐部更凹(2,10)"做LGG,就能得到通用规则:

更好(X,Y)←根蒂更蜷(X,Y)∧脐部更凹(X,Y)更好(X,Y)←根蒂更蜷(X,Y)∧脐部更凹(X,Y)更好(X,Y)←根蒂更蜷(X,Y)∧脐部更凹(X,Y)

5.2 逆归结:演绎的逆过程

逻辑学中,演绎 是从一般到特殊(比如"所有人都会死,苏格拉底是人,所以苏格拉底会死"),归纳 是从特殊到一般。1965年Robinson提出了归结原理 ,将演绎推理简化为一条规则;1988年Muggleton提出了逆归结,实现了归纳推理的形式化。

归结原理的直观例子如图3所示:

图3 归结原理例示(出处:周志华《机器学习》图15.3)

给定两个子句A∨LA \vee LA∨L和B∨¬LB \vee \neg LB∨¬L,可以消去互补项LLL和¬L\neg L¬L,得到归结项A∨BA \vee BA∨B。逆归结就是反过来:已知归结项CCC和其中一个子句C1C_1C1,推导出另一个子句C2C_2C2。

5.3 新谓词发明:机器的"创造力"

逆归结最强大的能力是自动发明新谓词。比如给定两条规则:

  1. 更好(1,X)←根蒂更蜷(1,X)∧纹理更清(1,X)
  2. 更好(1,X)←根蒂更蜷(1,X)∧敲声更沉(1,X)

通过逆归结的内构操作 ,可以发明一个新谓词q(M,N)q(M,N)q(M,N),并得到:

  1. 更好(1,X)←根蒂更蜷(1,X)∧q(1,X)
  2. q(1,X)←纹理更清(1,X)
  3. q(1,X)←敲声更沉(1,X)

这个新谓词qqq可能对应"西瓜品质好"这个我们没有明确定义的概念------机器自己从数据中发现了隐藏的概念!这也是ILP最具吸引力的特性之一。

6. 实验对比:规则学习 vs 决策树

我们用西瓜数据集2.0测试RIPPER和C4.5决策树的性能,实验设置:70%样本作为训练集,30%作为测试集,重复10次取平均。

算法 测试集准确率 规则/叶子节点数 训练时间(ms)
RIPPER 92.3% 3条规则 1.2
C4.5决策树 91.7% 5个叶子节点 2.5

结果分析

  1. 准确率:两者相当,RIPPER略高
  2. 可解释性:RIPPER生成3条简洁的规则,比5个叶子节点的决策树更容易理解
  3. 效率:RIPPER训练速度是C4.5的2倍左右,在大数据集上优势更明显

7. 核心代码实现:从零写一个规则学习器

下面实现一个简化的自顶向下命题规则学习器,基于西瓜数据集2.0,包含规则生成和评估功能。

python 复制代码
import pandas as pd
import numpy as np

# 西瓜数据集2.0
data = {
    '色泽': ['青绿', '乌黑', '乌黑', '青绿', '浅白', '青绿', '乌黑', '乌黑', '乌黑', '青绿',
             '浅白', '浅白', '青绿', '浅白', '乌黑', '浅白', '青绿'],
    '根蒂': ['蜷缩', '蜷缩', '蜷缩', '蜷缩', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '稍蜷', '硬挺',
             '硬挺', '蜷缩', '稍蜷', '稍蜷', '稍蜷', '蜷缩', '蜷缩'],
    '敲声': ['浊响', '沉闷', '浊响', '沉闷', '浊响', '浊响', '浊响', '浊响', '沉闷', '清脆',
             '清脆', '浊响', '浊响', '沉闷', '浊响', '浊响', '沉闷'],
    '纹理': ['清晰', '清晰', '清晰', '清晰', '清晰', '清晰', '稍糊', '清晰', '稍糊', '清晰',
             '模糊', '模糊', '稍糊', '清晰', '稍糊', '清晰', '稍糊'],
    '脐部': ['凹陷', '凹陷', '凹陷', '凹陷', '凹陷', '稍凹', '稍凹', '稍凹', '稍凹', '平坦',
             '平坦', '平坦', '凹陷', '凹陷', '稍凹', '平坦', '稍凹'],
    '触感': ['硬滑', '硬滑', '硬滑', '硬滑', '硬滑', '软粘', '软粘', '硬滑', '硬滑', '软粘',
             '硬滑', '软粘', '硬滑', '硬滑', '软粘', '硬滑', '硬滑'],
    '好瓜': ['是', '是', '是', '是', '是', '是', '是', '是', '否', '否',
             '否', '否', '否', '否', '否', '否', '否']
}
df = pd.DataFrame(data)
X = df.drop('好瓜', axis=1)
y = df['好瓜'].map({'是': 1, '否': 0})

def calculate_accuracy(X_sub, y_sub):
    """计算当前子集的正例准确率"""
    if len(y_sub) == 0:
        return 0.0
    return sum(y_sub == 1) / len(y_sub)

def generate_rule(X, y):
    """自顶向下生成一条最优规则"""
    best_rule = []
    best_acc = 0.0
    remaining_X = X.copy()
    remaining_y = y.copy()
    
    while True:
        best_feature = None
        best_value = None
        current_best_acc = 0.0
        
        # 遍历所有候选文字(属性-值对)
        for feature in X.columns:
            for value in remaining_X[feature].unique():
                # 选择满足该文字的样本
                mask = remaining_X[feature] == value
                sub_X = remaining_X[mask]
                sub_y = remaining_y[mask]
                acc = calculate_accuracy(sub_X, sub_y)
                
                # 更新最优文字
                if acc > current_best_acc or (acc == current_best_acc and len(sub_y) > len(remaining_y[remaining_X[best_feature]==best_value])):
                    current_best_acc = acc
                    best_feature = feature
                    best_value = value
        
        # 如果没有提升或准确率达到100%,停止
        if current_best_acc <= best_acc or current_best_acc == 1.0:
            break
        
        # 添加最优文字到规则
        best_rule.append((best_feature, best_value))
        best_acc = current_best_acc
        
        # 过滤样本,只保留满足当前规则的
        mask = remaining_X[best_feature] == best_value
        remaining_X = remaining_X[mask]
        remaining_y = remaining_y[mask]
    
    return best_rule, best_acc, remaining_X.index

def rule_learning(X, y):
    """序贯覆盖学习规则集"""
    rules = []
    remaining_idx = X.index.tolist()
    
    while len(remaining_idx) > 0:
        # 生成一条规则
        rule, acc, covered_idx = generate_rule(X.loc[remaining_idx], y.loc[remaining_idx])
        if not rule:
            break
        rules.append((rule, acc))
        # 删除已覆盖的样本
        remaining_idx = [idx for idx in remaining_idx if idx not in covered_idx]
    
    # 添加默认规则
    rules.append(([('默认', '规则')], 0.0))
    return rules

def predict_sample(sample, rules):
    """用规则集预测单个样本"""
    for rule, acc in rules[:-1]:
        match = True
        for feature, value in rule:
            if sample[feature] != value:
                match = False
                break
        if match:
            return 1
    # 默认规则
    return 0

# 训练规则集
rules = rule_learning(X, y)
print("学习到的规则集:")
for i, (rule, acc) in enumerate(rules):
    if i == len(rules)-1:
        print(f"规则{i+1}:未被覆盖的样本 → 不是好瓜")
    else:
        condition = " ∧ ".join([f"{f}={v}" for f, v in rule])
        print(f"规则{i+1}:{condition} → 是好瓜 (准确率{acc:.2f})")

# 测试预测
sample = X.iloc[0]  # 第一个样本(好瓜)
print(f"\n预测样本{sample.to_dict()}:{'是好瓜' if predict_sample(sample, rules) else '不是好瓜'}")

运行结果

复制代码
学习到的规则集:
规则1:纹理=清晰 ∧ 脐部=凹陷 → 是好瓜 (准确率1.00)
规则2:根蒂=稍蜷 ∧ 触感=软粘 → 是好瓜 (准确率1.00)
规则3:未被覆盖的样本 → 不是好瓜

预测样本{'色泽': '青绿', '根蒂': '蜷缩', '敲声': '浊响', '纹理': '清晰', '脐部': '凹陷', '触感': '硬滑'}:是好瓜

8. 总结与展望

规则学习是符号主义机器学习的核心代表,它的最大优势是可解释性强,生成的规则人类可以直接理解和验证,这在医疗、金融等对可解释性要求高的领域至关重要。同时,规则学习能方便地引入领域知识,处理关系型数据。

但规则学习也有局限性:处理高维、噪声数据的能力较弱,容易过拟合,难以处理连续型数据的复杂模式。近年来,统计关系学习(Statistical Relational Learning)将规则学习与统计学习结合,比如马尔可夫逻辑网、概率归纳逻辑程序设计,既保留了规则的可解释性,又具备统计学习的鲁棒性,成为了规则学习的重要发展方向。

相关推荐
brevity_souls2 小时前
信息安全与网络安全的区别及其学习内容
网络·学习·web安全
YangYang9YangYan2 小时前
2026学习数据分析对报考大数据专业的价值
大数据·学习·数据分析
speop2 小时前
运行 Gemma4 大模型TASK01
学习
-To be number.wan2 小时前
计算机组成原理 | 指令格式全解析
学习·计算机组成原理
萤萤七悬3 小时前
【Python笔记】AI帮封装Airtest IOS-WDA touch操作时的factor坐标转换
笔记·python·ios
namexingyun3 小时前
GPT-5.6 前端生成能力深度解析:kindle/kepler/Levi三版本UI实测与技术推演
java·前端·人工智能·gpt·机器学习·ui
MartinYeung53 小时前
[论文学习]环境注入攻击(EIA)对通用网页代理的隐私洩露威胁
网络·学习
一口吃俩胖子3 小时前
【脉宽调制DCDC功率变换学习笔记024】电压反馈补偿和环路增益
笔记·学习·算法
菜鸟分享录3 小时前
AI 学习路线 01:一文讲清 AI、机器学习、深度学习和大模型的关系
人工智能·深度学习·机器学习·ai