4. 数据预处理与边界测试样例生成:边界样本构造、覆盖准则

边界确认算法的验证效果高度依赖输入样本的质量与覆盖度。原始数据往往包含噪声、缺失值、非数值特征或异常范围,需经过预处理转换为算法可解析的数值张量。本章围绕“从原始数据到结构化边界测试集”的完整链路,阐述样本构造策略、覆盖准则定义及对应的实现流程。

4.1 数据预处理管线

预处理管线将异构输入统一为浮点型张量,同时保留物理边界语义。典型管线包括三阶段:

阶段操作输出格式边界敏感度
清洗缺失值插补、离群截断、单位归一化(样本数, 特征维)高:截断阈值直接影响边界位置
编码类别特征 One-Hot / 有序编码(样本数, 扩展维)中:组合爆炸可能稀释边界覆盖
缩放MinMax 或 Z-score所有值 ∈ [0,1] 或 N(0,1)高:需记录逆映射以还原真实边界
工程提示:对于物理量边界(如速度上限 100 km/h),建议使用 MinMax 缩放并保留 orig_min / orig_max 元数据。Z-score 会改变边界绝对位置,需额外回推。

4.2 差分边界化:从分布到边界候选

边界检测的第一步是识别特征空间中的“突变区域”。常用差分方法包括:

  • 一阶差分:相邻排序样本的特征差值,适用于连续单调特征。
  • 滑动窗口梯度:窗口内统计量(均值、方差)的剧烈变化点。
  • 密度梯度:基于 KDE 的概率密度变化率,适用多模态分布。

以下代码展示对二维特征执行滑动窗口梯度检测,提取候选边界索引:

import numpy as np

def sliding_window_gradient(data, window=5, threshold=2.0):
    """
    data: (N, D) 浮点数组
    return: boundary_mask (N,) bool
    """
    from scipy.ndimage import uniform_filter
    mean = uniform_filter(data, size=(window, 1), mode='reflect')
    grad = np.abs(data[1:] - data[:-1])
    grad_avg = np.mean(grad, axis=1)

    # 梯度异常点
    mu = np.mean(grad_avg)
    std = np.std(grad_avg)
    mask = np.zeros(len(data), dtype=bool)
    mask[1:] = grad_avg > (mu + threshold * std)
    return mask

4.3 边界样本构造策略

确认算法需要三类边界样本:

  1. 单参数边界:仅一个特征处于边界值,其余特征取典型值。
  2. 组合边界:两个或以上特征同时处于边界条件,常触发非线性拐点。
  3. 等价类收敛:在边界两侧微扰(±0.1%),验证输出的差分稳定性。

策略选择优先级:单参数 > 组合边界 > 等价类收敛。组合边界数量按 nCk 增长,需用正交表或迭代采样控制规模。

构造示例:假设特征 A 的物理边界为 [10, 100],特征 B 为 [0.0, 1.0]。单参数边界样本:A=10/B=0.5, A=100/B=0.5;组合边界样本:A=10/B=0.0, A=100/B=1.0。
边界样本构造流程 原始数据 预处理 差分分析 边界条件识别 单参数边界 特征 i = 边界 组合边界 i,j 同时达边界 等价类收敛 边界 ±0.1% 微扰 边界测试样例集

4.4 覆盖准则定义

覆盖准则是评估样例集完备性的量化依据。本课程采用三种覆盖度量:

准则定义指标工程阈值
单参数边界覆盖 (SBC)每个特征的上/下边界均至少被1个样例覆盖SBC = 已覆盖边界数 / 总边界数> 95%
组合边界覆盖 (CBC)所有指定特征对 (i,j) 的四种边界组合是否出现CBC = 命中组合数 / 总组合数> 80%
等价类覆盖 (ECC)边界每侧微扰点是否同时存在ECC = 具有正负微扰的边界数 / 总边界数> 90%
覆盖陷阱:单参数边界覆盖达到 100% 并不保证算法可靠——组合边界覆盖可能只有 30%。建议以 SBC 为最低准入,CBC 为主要验证指标。

4.5 Python 实现:覆盖计算与样例生成

下面给出覆盖计算的核心类,包含样例生成与覆盖率评估两个接口。

import numpy as np
from itertools import product
from typing import Dict, List, Tuple

class BoundaryCoverage:
    def __init__(self, bounds: Dict[str, Tuple[float, float]]):
        """
        bounds: {"feature_name": (low, high)}
        """
        self.bounds = bounds
        self.features = list(bounds.keys())
        self.n = len(self.features)

    def generate_single_boundary_samples(self, n_typical: int = 3) -> np.ndarray:
        """
        单参数边界样本:每个特征取 low/high,其余取典型等分点。
        返回 (2 * n * n_typical, n) 数组。
        """
        samples = []
        for idx, f in enumerate(self.features):
            low, high = self.bounds[f]
            for typical_val in np.linspace(low, high, n_typical + 2)[1:-1]:
                vec = np.zeros(self.n)
                vec[idx] = low
                # 填充其余特征
                for j, jf in enumerate(self.features):
                    if j != idx:
                        vec[j] = self._pick_typical(jf)
                samples.append(vec)
                # 高边界
                vec2 = vec.copy()
                vec2[idx] = high
                samples.append(vec2)
        return np.array(samples)

    def compute_sbc(self, test_set: np.ndarray) -> float:
        """单参数边界覆盖率"""
        covered = set()
        for idx, f in enumerate(self.features):
            low, high = self.bounds[f]
            col = test_set[:, idx]
            if np.any(np.isclose(col, low, rtol=1e-3)):
                covered.add((f, 'low'))
            if np.any(np.isclose(col, high, rtol=1e-3)):
                covered.add((f, 'high'))
        total = 2 * self.n
        return len(covered) / total

    def _pick_typical(self, f: str) -> float:
        low, high = self.bounds[f]
        return (low + high) / 2.0

4.6 工程职责划分:数据预处理与编译工程师协作流程 SOP

在团队中,数据预处理与边界测试样例生成涉及数据工程师与编译/算法工程师的协同。以下 SOP 明确定义交接件与验收标准。

步骤数据工程师(DE)编译/算法工程师(AE)交接件
1. 特征定义提供原始字段清单与物理含义确定边界相关特征子集边界特征配置文件 (YAML)
2. 预处理管线编写清洗-编码-缩放管线验证管线对边界保留性pipeline.pkl + 缩放元数据
3. 差分分析提供粗粒度分布统计运行梯度检测,输出候选边界boundary_candidates.npy
4. 样例生成生成单参数/组合样本补充等价类微扰样本boundary_test_set.npy
5. 覆盖验证计算 SBC/CBC/ECC根据覆盖报告决定是否补充coverage_report.json
SOP 关键点:步骤 2 的缩放元数据(orig_min/scale)必须在整个项目周期内版本化管理,否则边界样本将无法映射回物理空间进行人工审查。

4.7 常见错误与性能优化

  • 数值失真:缩放后边界值因浮点舍入略超原始范围,导致后续差分误判。对策:缩放时保留 δ=1e-7 的保护带。
  • 覆盖过测:组合边界全枚举导致样本数量爆炸(n=20 时对覆盖 >10^5)。采用正交表 L12_2^11 替代全枚举,覆盖损失 < 5%。
  • 梯度阈值敏感:固定 threshold=2.0 可能漏检缓变边界。改为自适应阈值,基于局部梯度分位数。
  • 内存瓶颈:大规模数据集预处理按 chunk 流式处理,避免一次性加载。
# 正交表采样示意(L9 三因子两水平)
import numpy as np
L9 = np.array([
    [0, 0, 0],
    [0, 1, 1],
    [1, 0, 1],
    [1, 1, 0],
    [0, 2, 2],
    [2, 0, 2],
    [2, 2, 0],
    [1, 2, 1],
    [2, 1, 1]
])
# 将每个特征的水平映射到 [low, median, high]
实测性能参考:在 10 特征、100k 样本规模下,预处理 + 差分分析 + 样例生成 + 覆盖计算全流程耗时约 12 秒(单核 Intel i7-12700)。SBC 达 97%,CBC 达 84%。