1. AI测试工程师的数学武器库:从理论到实战
作为一名在AI测试领域摸爬滚打多年的工程师,我深刻体会到数学不是纸上谈兵的理论,而是我们每天调试模型、验证结果的实际工具。当新人问我"做AI测试需要多深的数学基础"时,我的回答总是:足够让你看懂模型为什么出错,并能设计出证明它确实出错的测试方案。
1.1 为什么数学是AI测试的基石?
在传统软件测试中,我们主要验证确定的输入输出关系。但AI系统完全不同——它们的输出具有概率性、依赖训练数据、且内部逻辑复杂。这就决定了我们的测试方法必须建立在三个数学支柱上:
- 线性代数:理解模型如何表示和处理数据
- 概率统计:量化模型行为的不确定性
- 最优化理论:分析模型如何学习和改进
举个例子,当测试图像分类器时,简单的准确率计算远远不够。我们需要:
- 用向量距离衡量特征相似度(线性代数)
- 计算各类别的预测置信区间(概率统计)
- 监控损失函数的收敛过程(最优化)
1.2 典型AI测试场景中的数学应用
| 测试场景 | 核心数学工具 | 具体应用 |
|---|---|---|
| 模型推理验证 | 矩阵运算、特征值分解 | 验证神经网络层的前向传播计算是否正确 |
| 数据漂移检测 | 假设检验、分布比较 | 比较训练数据与线上数据的统计特征差异 |
| 超参数调优 | 梯度计算、凸优化 | 寻找最优学习率、批量大小等参数 |
| 对抗样本测试 | 范数计算、优化方法 | 生成微小扰动使模型误分类,测试模型鲁棒性 |
| 特征重要性分析 | 统计检验、蒙特卡洛方法 | 确定哪些输入特征对模型预测影响最大 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线性代数:拆解AI模型的黑箱
2.1 向量运算:从数据表示到相似度计算
在测试过程中,我们经常需要验证模型对数据的内部表示是否正确。假设我们测试一个处理电商评论的情感分析模型:
python复制import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# 测试词向量表示
def test_word_embeddings(embedding_model):
# 获取测试词的向量表示
vector_good = embedding_model.get_vector("good")
vector_great = embedding_model.get_vector("great")
vector_bad = embedding_model.get_vector("bad")
# 计算余弦相似度
sim_pos = cosine_similarity([vector_good], [vector_great])[0][0]
sim_neg = cosine_similarity([vector_good], [vector_bad])[0][0]
# 验证语义相似度
assert sim_pos > 0.7, "同义词相似度过低"
assert sim_neg < 0.3, "反义词相似度过高"
# 验证向量范数
assert 0.9 < np.linalg.norm(vector_good) < 1.1, "向量未归一化"
关键测试点:
- 同义词应具有高余弦相似度(>0.7)
- 反义词应具有低相似度(<0.3)
- 词向量通常需要归一化(L2范数≈1)
2.2 矩阵运算:验证神经网络层
测试神经网络时,我们需要逐层验证矩阵运算的正确性。以下是一个全连接层的测试示例:
python复制def test_dense_layer(layer, input_dim=64, output_dim=32):
# 初始化测试数据
np.random.seed(42)
test_input = np.random.randn(128, input_dim) # 批量大小128
# 手动计算预期输出
weights = layer.get_weights()[0]
bias = layer.get_weights()[1]
expected_output = np.dot(test_input, weights) + bias
# 获取实际输出
actual_output = layer(test_input).numpy()
# 验证结果
assert actual_output.shape == (128, output_dim), "输出形状错误"
# 使用Frobenius范数比较矩阵差异
diff_norm = np.linalg.norm(actual_output - expected_output, 'fro')
assert diff_norm < 1e-6, f"矩阵计算结果不符,差异范数: {diff_norm}"
# 验证激活函数
if layer.activation.__name__ == "relu":
assert np.all(actual_output >= 0), "ReLU激活未正确应用"
测试技巧:
- 固定随机种子确保测试可重复
- 使用Frobenius范数量化矩阵差异
- 单独验证激活函数的应用
2.3 特征值分解:PCA降维测试
降维算法的测试需要验证两方面:信息保留程度和重构误差。以下是PCA的完整测试方案:
java复制import org.apache.commons.math3.linear.*;
import org.apache.commons.math3.stat.correlation.Covariance;
public class PCATest {
// 计算重构误差
public static double reconstructionError(double[][] original,
double[][] reconstructed) {
double error = 0;
for (int i = 0; i < original.length; i++) {
for (int j = 0; j < original[i].length; j++) {
error += Math.pow(original[i][j] - reconstructed[i][j], 2);
}
}
return error / original.length;
}
// 验证PCA实现
public static void testPCA(int nComponents) {
// 生成测试数据
double[][] data = generateTestData(100, 10);
// 执行PCA
double[][] reduced = performPCA(data, nComponents);
double[][] reconstructed = reconstructPCA(data, reduced);
// 计算原始数据方差
double totalVariance = new Variance().evaluate(MatrixUtils.columnToVector(data));
// 计算保留方差
double retainedVariance = new Variance().evaluate(
MatrixUtils.columnToVector(reconstructed)) / totalVariance;
// 验证
assert retainedVariance > 0.95 : "保留方差不足95%";
assert reconstructionError(data, reconstructed) < 0.1 : "重构误差过大";
}
}
测试指标:
- 保留方差比例应>95%(当nComponents合理时)
- 重构误差应<0.1(对标准化数据)
- 主成分之间应正交(协方差≈0)
3. 概率统计:量化模型的不确定性
3.1 概率分布:从数据生成到输出验证
3.1.1 测试数据生成
我们需要生成符合特定分布的测试数据来验证模型的鲁棒性:
python复制class TestDataGenerator:
@staticmethod
def generate_skewed_normal(size, skewness=2.0):
"""生成有偏态的数据,测试模型对非正态分布的鲁棒性"""
delta = skewness / np.sqrt(1 + skewness**2)
u0 = np.random.normal(0, 1, size)
v = np.random.normal(0, 1, size)
u1 = delta * u0 + np.sqrt(1 - delta**2) * v
return u1 * np.abs(skewness)
@staticmethod
def generate_multi_modal(means=[-3, 3], stds=[1, 1], weights=[0.4, 0.6], size=1000):
"""生成多峰分布数据"""
choices = np.random.choice(len(means), size=size, p=weights)
samples = [np.random.normal(means[i], stds[i]) for i in choices]
return np.array(samples)
使用场景:
- 偏态数据:测试模型对异常值的敏感性
- 多峰分布:验证聚类算法的效果
- 长尾分布:评估推荐系统的覆盖率
3.1.2 输出分布验证
python复制def validate_output_distribution(y_pred, y_true, n_bins=10):
"""验证预测概率的校准程度"""
bin_edges = np.linspace(0, 1, n_bins + 1)
bin_indices = np.digitize(y_pred, bin_edges) - 1
observed_rates = []
expected_rates = []
for i in range(n_bins):
mask = (bin_indices == i)
if mask.sum() == 0:
continue
bin_true = y_true[mask]
observed = bin_true.mean()
expected = bin_edges[i] + (bin_edges[i+1] - bin_edges[i])/2
observed_rates.append(observed)
expected_rates.append(expected)
# 计算校准误差
calibration_error = np.mean(np.abs(np.array(observed_rates) - np.array(expected_rates)))
# 绘制可靠性图
plt.plot(expected_rates, observed_rates, 'o-')
plt.plot([0,1], [0,1], '--', color='gray')
plt.xlabel("预测概率")
plt.ylabel("实际频率")
plt.title(f"可靠性图 (校准误差={calibration_error:.3f})")
return calibration_error
解读标准:
- 校准误差<0.03:优秀
- 0.03-0.1:可接受
-
0.1:需要重新校准模型
3.2 假设检验:A/B测试与数据漂移
3.2.1 A/B测试框架
python复制class ABTestFramework:
def __init__(self, alpha=0.05, power=0.8):
self.alpha = alpha
self.power = power
self.test_results = {}
def calculate_sample_size(self, effect_size):
"""计算所需样本量"""
from statsmodels.stats.power import tt_ind_solve_power
n = tt_ind_solve_power(
effect_size=effect_size,
alpha=self.alpha,
power=self.power,
ratio=1.0
)
return int(np.ceil(n))
def run_continuous_test(self, group_a, group_b, test_type='t'):
"""连续变量A/B测试"""
result = {}
if test_type == 't':
# 独立样本t检验
t_stat, p_value = stats.ttest_ind(group_a, group_b)
result['method'] = '独立t检验'
result['statistic'] = t_stat
result['p_value'] = p_value
result['effect_size'] = self._cohens_d(group_a, group_b)
elif test_type == 'mannwhitney':
# Mann-Whitney U检验
u_stat, p_value = stats.mannwhitneyu(group_a, group_b)
result['method'] = 'Mann-Whitney U检验'
result['statistic'] = u_stat
result['p_value'] = p_value
result['effect_size'] = self._cliffs_delta(group_a, group_b)
result['significant'] = p_value < self.alpha
return result
选择检验方法的决策树:
- 数据是否正态分布? → Shapiro-Wilk检验
- 是 → 方差是否齐性? → Levene检验
- 是 → 独立t检验
- 否 → Welch t检验
- 否 → 样本量>30?
- 是 → t检验(根据中心极限定理)
- 否 → Mann-Whitney U检验
- 是 → 方差是否齐性? → Levene检验
3.2.2 数据漂移检测系统
java复制public class DataDriftDetector {
private final double alpha;
private final int windowSize;
public DataDriftDetector(double alpha, int windowSize) {
this.alpha = alpha;
this.windowSize = windowSize;
}
public Map<String, DriftResult> detectDrift(
double[][] referenceData,
double[][] currentData,
String[] featureNames) {
Map<String, DriftResult> results = new HashMap<>();
KolmogorovSmirnovTest ksTest = new KolmogorovSmirnovTest();
for (int i = 0; i < featureNames.length; i++) {
double[] ref = getColumn(referenceData, i);
double[] curr = getColumn(currentData, i);
// KS检验
double ksStat = ksTest.kolmogorovSmirnovStatistic(ref, curr);
double pValue = ksTest.kolmogorovSmirnovTest(ref, curr);
// 统计量变化
double meanDiff = Math.abs(StatUtils.mean(curr) - StatUtils.mean(ref));
double stdRatio = Math.sqrt(StatUtils.variance(curr)) /
Math.sqrt(StatUtils.variance(ref));
// 构建结果
DriftResult result = new DriftResult(
featureNames[i],
pValue,
pValue < alpha,
ksStat,
meanDiff,
stdRatio
);
results.put(featureNames[i], result);
}
return results;
}
private static double[] getColumn(double[][] matrix, int col) {
return Arrays.stream(matrix)
.mapToDouble(row -> row[col])
.toArray();
}
}
漂移告警策略:
- 初级告警:单个特征p值<α
- 中级告警:超过30%特征p值<α
- 高级告警:关键特征p值<α且效应量大
4. 最优化理论:训练过程深度监控
4.1 梯度下降:从原理到测试
4.1.1 梯度计算验证
python复制def test_gradient_computation(model, input_data, epsilon=1e-5):
"""验证梯度计算是否正确(梯度检查)"""
# 获取模型参数和梯度函数
params = model.get_parameters()
grad_func = model.get_gradient_function()
# 计算解析梯度
analytic_grad = grad_func(input_data)
# 计算数值梯度
numerical_grad = np.zeros_like(params)
for i in range(len(params)):
# 正向扰动
params[i] += epsilon
loss_plus = model.compute_loss(input_data)
# 负向扰动
params[i] -= 2 * epsilon
loss_minus = model.compute_loss(input_data)
# 恢复参数
params[i] += epsilon
# 中心差分
numerical_grad[i] = (loss_plus - loss_minus) / (2 * epsilon)
# 比较梯度
diff = np.linalg.norm(analytic_grad - numerical_grad)
relative_diff = diff / (np.linalg.norm(analytic_grad) + np.linalg.norm(numerical_grad))
assert relative_diff < 1e-7, f"梯度验证失败,相对差异: {relative_diff}"
return relative_diff
梯度检查要点:
- 使用中心差分法(误差O(ε²))
- 比较相对差异而非绝对差异
- 典型阈值:相对差异<1e-7
4.1.2 优化器测试框架
python复制class OptimizerTester:
def __init__(self, test_function, optimizers):
"""
test_function: 测试函数,需实现compute_loss和compute_gradient
optimizers: 待测试的优化器列表
"""
self.test_func = test_function
self.optimizers = optimizers
self.results = []
def run_convergence_test(self, init_params, n_iterations=100):
"""运行收敛性测试"""
for opt in self.optimizers:
params = init_params.copy()
loss_history = []
for _ in range(n_iterations):
grad = self.test_func.compute_gradient(params)
params = opt.update(params, grad)
loss = self.test_func.compute_loss(params)
loss_history.append(loss)
self.results.append({
'optimizer': opt.name,
'final_loss': loss_history[-1],
'convergence_iters': self._find_convergence(loss_history),
'loss_history': loss_history
})
def plot_results(self):
"""绘制收敛曲线"""
plt.figure(figsize=(10, 6))
for res in self.results:
plt.semilogy(res['loss_history'], label=res['optimizer'])
plt.xlabel("迭代次数")
plt.ylabel("损失值(对数尺度)")
plt.title("优化器收敛速度比较")
plt.legend()
plt.grid(True)
def _find_convergence(self, losses, tol=1e-4):
"""确定收敛所需的迭代次数"""
baseline = losses[0]
for i, loss in enumerate(losses):
if abs(loss - losses[-1]) / baseline < tol:
return i
return len(losses)
测试函数示例:
python复制class QuadraticTestFunction:
"""二次测试函数 f(x) = x^T A x + b^T x + c"""
def __init__(self, A, b, c):
self.A = A
self.b = b
self.c = c
def compute_loss(self, x):
return x.T @ self.A @ x + self.b.T @ x + self.c
def compute_gradient(self, x):
return 2 * self.A @ x + self.b
4.2 学习率策略验证
4.2.1 学习率搜索测试
python复制def test_learning_rate(model, train_data, lr_range=(-6, 1), num_lrs=20):
"""学习率范围测试"""
lrs = np.logspace(*lr_range, num=num_lrs)
losses = []
for lr in lrs:
model.reset_parameters()
optimizer = SGD(lr=lr)
# 运行少量迭代
for _ in range(100):
grad = model.compute_gradient(train_data)
optimizer.step(model.parameters, grad)
# 记录最终损失
loss = model.compute_loss(train_data)
losses.append(loss)
# 绘制结果
plt.semilogx(lrs, losses)
plt.xlabel("学习率(对数尺度)")
plt.ylabel("最终损失")
plt.title("学习率范围测试")
# 返回最佳学习率
best_idx = np.argmin(losses)
return lrs[best_idx]
典型学习率测试模式:
- 对数空间搜索(如10^-6到10^1)
- 观察损失下降曲线:
- 下降过快→学习率可能太大
- 几乎不变→学习率太小
- 先降后升→超过最优学习率
4.2.2 学习率调度器测试
java复制public class LRSchedulerTest {
@Test
public void testCosineAnnealing() {
CosineAnnealingLR scheduler = new CosineAnnealingLR(
baseLR: 0.1,
T_max: 100,
eta_min: 0.001
);
List<Double> lrs = new ArrayList<>();
for (int epoch = 0; epoch < 200; epoch++) {
lrs.add(scheduler.getLR(epoch));
}
// 验证周期性
assertEquals(0.1, lrs.get(0), 1e-6);
assertEquals(0.001, lrs.get(100), 1e-6);
assertEquals(0.1, lrs.get(200), 1e-6);
// 验证单调性
for (int i = 0; i < 100; i++) {
assertTrue(lrs.get(i) >= lrs.get(i+1));
}
for (int i = 100; i < 200; i++) {
assertTrue(lrs.get(i) <= lrs.get(i+1));
}
}
}
调度器测试要点:
- 边界值验证(起始/结束学习率)
- 周期性验证(对cosine annealing)
- 单调性验证(各阶段的增减趋势)
5. 构建AI测试数学工具库
5.1 Python实现:NumPy科学计算套件
5.1.1 矩阵运算验证工具
python复制class MatrixTestUtils:
@staticmethod
def verify_matrix_properties(matrix, expected_rank=None,
expected_det=None, rtol=1e-5):
"""验证矩阵基本属性"""
results = {}
# 秩验证
rank = np.linalg.matrix_rank(matrix)
results['rank'] = rank
if expected_rank is not None:
assert rank == expected_rank, f"秩不匹配: {rank} != {expected_rank}"
# 行列式验证
if matrix.shape[0] == matrix.shape[1]:
det = np.linalg.det(matrix)
results['determinant'] = det
if expected_det is not None:
assert np.isclose(det, expected_det, rtol=rtol), \
f"行列式不匹配: {det} != {expected_det}"
# 正定性验证
if matrix.shape[0] == matrix.shape[1]:
try:
np.linalg.cholesky(matrix)
results['positive_definite'] = True
except np.linalg.LinAlgError:
results['positive_definite'] = False
return results
@staticmethod
def compare_matrices_approx(A, B, rtol=1e-5, atol=1e-8):
"""比较两个矩阵是否近似相等"""
assert A.shape == B.shape, "矩阵形状不匹配"
diff = np.abs(A - B)
abs_diff = np.max(diff)
rel_diff = np.max(diff / (np.abs(B) + atol))
results = {
'max_absolute_diff': abs_diff,
'max_relative_diff': rel_diff,
'is_close': np.allclose(A, B, rtol=rtol, atol=atol)
}
return results
5.1.2 概率统计测试工具
python复制class StatisticalTests:
@staticmethod
def compare_distributions(samples_a, samples_b, test_type='ks'):
"""比较两个样本分布的相似性"""
results = {}
if test_type == 'ks':
# Kolmogorov-Smirnov检验
stat, p_value = stats.ks_2samp(samples_a, samples_b)
results['test'] = 'Kolmogorov-Smirnov'
results['statistic'] = stat
results['p_value'] = p_value
elif test_type == 'anderson':
# Anderson-Darling检验
combined = np.concatenate([samples_a, samples_b])
labels = ['A'] * len(samples_a) + ['B'] * len(samples_b)
stat, critical_values, sig_levels = stats.anderson_ksamp(
[samples_a, samples_b]
)
results['test'] = 'Anderson-Darling'
results['statistic'] = stat
results['critical_values'] = critical_values
results['significance_levels'] = sig_levels
# 计算效应量
cohens_d = (np.mean(samples_a) - np.mean(samples_b)) / \
np.sqrt((np.std(samples_a)**2 + np.std(samples_b)**2)/2)
results['effect_size'] = cohens_d
return results
@staticmethod
def bootstrap_ci(data, statistic_func, n_resamples=9999, ci=95):
"""自助法置信区间"""
stats = []
n = len(data)
for _ in range(n_resamples):
resample = np.random.choice(data, size=n, replace=True)
stat = statistic_func(resample)
stats.append(stat)
alpha = (100 - ci) / 2
lower = np.percentile(stats, alpha)
upper = np.percentile(stats, 100 - alpha)
return {
'statistic': statistic_func(data),
'confidence_interval': (lower, upper),
'bootstrap_distribution': stats
}
5.2 Java实现:Apache Commons Math工具集
5.2.1 线性代数测试工具
java复制public class LinearAlgebraTests {
public static boolean isPositiveDefinite(RealMatrix matrix) {
try {
new CholeskyDecomposition(matrix);
return true;
} catch (NonPositiveDefiniteMatrixException e) {
return false;
}
}
public static double matrixConditionNumber(RealMatrix matrix) {
SingularValueDecomposition svd = new SingularValueDecomposition(matrix);
double[] singularValues = svd.getSingularValues();
return singularValues[0] / singularValues[singularValues.length - 1];
}
public static Map<String, Object> testMatrixProperties(RealMatrix matrix) {
Map<String, Object> results = new HashMap<>();
// 秩
results.put("rank", MatrixUtils.rank(matrix));
// 行列式
if (matrix.getRowDimension() == matrix.getColumnDimension()) {
results.put("determinant", new LUDecomposition(matrix).getDeterminant());
}
// 条件数
results.put("condition_number", matrixConditionNumber(matrix));
// 正定性
results.put("positive_definite", isPositiveDefinite(matrix));
return results;
}
}
5.2.2 统计测试工具
java复制public class StatsTestUtils {
public static Map<String, Object> compareDistributions(
double[] sample1, double[] sample2) {
Map<String, Object> results = new HashMap<>();
KolmogorovSmirnovTest ksTest = new KolmogorovSmirnovTest();
TTest tTest = new TTest();
// KS检验
results.put("ks_statistic", ksTest.kolmogorovSmirnovStatistic(sample1, sample2));
results.put("ks_pvalue", ksTest.kolmogorovSmirnovTest(sample1, sample2));
// t检验
results.put("t_statistic", tTest.t(sample1, sample2));
results.put("t_pvalue", tTest.tTest(sample1, sample2));
// 效应量
double mean1 = StatUtils.mean(sample1);
double mean2 = StatUtils.mean(sample2);
double var1 = StatUtils.variance(sample1);
double var2 = StatUtils.variance(sample2);
double pooledStd = Math.sqrt((var1 + var2) / 2);
results.put("cohens_d", (mean1 - mean2) / pooledStd);
return results;
}
public static Map<String, Object> bootstrapCI(
double[] data, int resamples, double ciLevel) {
DescriptiveStatistics stats = new DescriptiveStatistics();
double originalStat = StatUtils.mean(data); // 示例:计算均值
for (int i = 0; i < resamples; i++) {
double[] resample = resample(data);
stats.addValue(StatUtils.mean(resample));
}
double alpha = (1 - ciLevel) / 2;
double lower = stats.getPercentile(100 * alpha);
double upper = stats.getPercentile(100 * (1 - alpha));
Map<String, Object> results = new HashMap<>();
results.put("estimate", originalStat);
results.put("ci_lower", lower);
results.put("ci_upper", upper);
results.put("bootstrap_distribution", stats.getValues());
return results;
}
private static double[] resample(double[] data) {
double[] resample = new double[data.length];
Random random = new Random();
for (int i = 0; i < data.length; i++) {
resample[i] = data[random.nextInt(data.length)];
}
return resample;
}
}
6. 综合实战:模型测试全流程
6.1 测试案例:图像分类器验证
python复制class ImageClassifierTester:
def __init__(self, model, test_loader):
self.model = model
self.test_loader = test_loader
self.math_utils = AIMathTestSuite()
def run_comprehensive_test(self):
"""运行全面测试"""
results = {}
# 1. 基础准确率测试
results['accuracy'] = self.test_accuracy()
# 2. 类别平衡性测试
results['class_balance'] = self.test_class_balance()
# 3. 置信度校准测试
results['calibration'] = self.test_calibration()
# 4. 对抗鲁棒性测试
results['adversarial'] = self.test_adversarial_robustness()
# 5. 特征重要性分析
results['feature_importance'] = self.analyze_feature_importance()
return results
def test_accuracy(self):
"""测试分类准确率"""
correct = 0
total = 0
all_preds = []
all_targets = []
with torch.no_grad():
for images, labels in self.test_loader:
outputs = self.model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
all_preds.extend(predicted.numpy())
all_targets.extend(labels.numpy())
accuracy = correct / total
# 计算置信区间
ci = self.math_utils.bootstrap_confidence_interval(
np.array(all_preds) == np.array(all_targets),
lambda x: np.mean(x),
ci=95
)
return {
'accuracy': accuracy,
'confidence_interval': ci['confidence_interval'],
'class_report': classification_report(
all_targets, all_preds, output_dict=True
)
}
def test_calibration(self):
"""测试模型校准度"""
pred_probs = []
true_labels = []
with torch.no_grad():
for images, labels in self.test_loader:
outputs = torch.softmax(self.model(images), dim=1)
pred_probs.extend(outputs.numpy())
true_labels.extend(labels.numpy())
return self.math_utils.validate_output_distribution(
np.array(pred_probs)[:, 1], # 取正类概率
np.array(true_labels),
n_bins=10
)
6.2 测试案例:推荐系统AB测试
java复制public class RecommenderABTest {
private final double alpha;
private final int minSampleSize;
public RecommenderABTest(double alpha, int minSampleSize) {
this.alpha = alpha;
this.minSampleSize = minSampleSize;
}
public ABTestResult runTest(
List<Double> controlMetrics,
List<Double> treatmentMetrics,
String metricName) {
// 样本量检查
if (controlMetrics.size() < minSampleSize ||
treatmentMetrics.size() < minSampleSize) {
throw new IllegalArgumentException("样本量不足");
}
ABTestResult result = new ABTestResult(metricName);
// 计算基本统计量
result.setControlMean(calculateMean(controlMetrics));
result.setTreatmentMean(calculateMean(treatmentMetrics));
result.setAbsoluteDifference(
Math.abs(result.getTreatmentMean() - result.getControlMean()));
result.setRelativeDifference(
result.getAbsoluteDifference() / result.getControlMean());
// 正态性检验
ShapiroWilkTest swTest = new ShapiroWilkTest();
boolean controlNormal = swTest.test(controlMetrics, alpha);
boolean treatmentNormal = swTest.test(treatmentMetrics, alpha);
// 选择检验方法
if (controlNormal && treatmentNormal) {
// t检验
TTest tTest = new TTest();
double pValue = tTest.tTest(
controlMetrics.stream().mapToDouble(Double::doubleValue).toArray(),
treatmentMetrics.stream().mapToDouble(Double::doubleValue).toArray()
);
result.setpValue(pValue);
result.setTestMethod("独立t检验");
// 效应量
double pooledStd = calculatePooledStd(controlMetrics, treatmentMetrics);
result.setEffectSize(
(result.getTreatmentMean() - result.getControlMean()) / pooledStd);
} else {
// Mann-Whitney U检验
MannWhitneyUTest uTest = new MannWhitneyUTest();
double pValue = uTest.mannWhitneyUTest(
controlMetrics.stream().mapToDouble(Double::doubleValue).toArray(),
treatmentMetrics.stream().mapToDouble(Double::doubleValue).toArray()
);
result.setpValue(pValue);
result.setTestMethod("Mann-Whitney U检验");
// 效应量
result.setEffectSize(calculateCliffsDelta(controlMetrics, treatmentMetrics));
}
result.setSignificant(result.getpValue() < alpha);
return result;
}
private double calculatePooledStd(List<Double> group1, List<Double> group2) {
double var1 = calculateVariance(group1);
double var2 = calculateVariance(group2);
return Math.sqrt((var1 + var2) / 2);
}
private double calculateCliffsDelta(List<Double> group1, List<Double> group2) {
// 实现Cliff's Delta计算
// 简化为均值差除以合并标准差
return (calculateMean(group2) - calculateMean(group1)) /
calculatePooledStd(group1, group2);
}
}
7. 数学测试的工程化实践
7.1 测试金字塔在AI系统中的实现
code复制 [模拟数据测试]
↑
[单元测试] → [组件测试] → [集成测试] → [端到端测试]
↓
[线上监控]
各层级的数学测试重点:
-
单元测试:
- 验证单个数学函数(如softmax、交叉熵)
- 矩阵运算的数值精度
- 梯度计算正确性
-
组件测试:
- 神经网络层的正向/反向传播
- 特征变换的正确性
- 损失函数的计算
-
集成测试:
- 数据流经整个模型的形状变化
- 端到端的梯度流动
- 多组件交互的数值稳定性
-
端到端测试:
- 最终指标的科学性(如AUC、RMSE)
- 模型预测的统计特性
- 与业务指标的相关性
7.2 持续集成中的数学测试
yaml复制# .github/workflows/ci.yml
name: AI Model CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'
- name: Install dependencies
run: |
