写在前面
第 11 篇我们理解了神经网络的基本原理。但"神经网络"只是一个通用框架------就像"交通工具"是一个通用概念,但自行车、汽车、飞机的使用方式完全不同。
AI 领域也是如此。同样是深度学习,处理文字的 NLP、处理图片的 CV、处理语音的语音技术,它们的输入数据不同、核心架构不同、"容易翻车"的地方不同。
你作为测试人员,需要根据被测模型属于哪个方向,来设计针对性的测试方案。一篇测试报告写"准确率 95%"毫无意义------你需要知道这个 95% 是在什么方向、什么任务、什么数据上取得的。
环境准备
本篇需要以下 Python 库,开始前先在终端执行:
bash
pip install numpy matplotlib jieba -i https://pypi.tuna.tsinghua.edu.cn/simple
如果觉得每次加
-i太麻烦,可以先执行一次pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple配置默认镜像,以后安装任何包都自动加速。
本篇文件结构
本篇代码放在 ai-test/ch12_domains/ 文件夹下:
| 文件名 | 用途 | 创建方式 |
|---|---|---|
nlp_demo.py |
NLP 核心概念演示 | 三、新建 |
cv_demo.py |
CV 核心概念演示 | 四、新建 |
domain_test_plan.py |
三大方向测试方案生成 | 六、新建 |
charts/ |
图片输出目录(自动生成) | 不需要手动创建 |
一、为什么测试人员需要了解不同 AI 方向?
先想一个场景。你接了一个测试任务:
"请测试我们新上线的 AI 模型。"
你的第一个问题应该是:这是什么类型的模型?
- 如果是文本分类(比如情感分析、垃圾邮件检测),你需要关注否定词、反讽、语序等文本特有的问题
- 如果是图像识别(比如人脸检测、医学影像),你需要关注图片旋转、缩放、遮挡等图像特有的问题
- 如果是语音识别(比如语音转文字),你需要关注口音、噪声、语速等语音特有的问题
测试方法、测试数据、评估指标都不一样。所以你必须先搞清楚"这是什么方向的模型",才能开始设计测试方案。
三大方向在整个 AI 产品中的占比大致是:
| 方向 | 典型应用 | 产品中占比 | 测试复杂度 |
|---|---|---|---|
| NLP(自然语言处理) | 文本分类、对话机器人、翻译、摘要 | 最高(约 50%+) | 中等 |
| CV(计算机视觉) | 图像识别、人脸识别、OCR、医学影像 | 高(约 30%) | 中等 |
| 语音 | 语音转文字、语音助手、声纹识别 | 中等(约 15%) | 较高(涉及多模态) |
加上现在大模型时代,很多产品是多模态的(同时处理文字+图片+语音),测试就更复杂了。但不管多模态还是单模态,你都是从这三个基本方向来理解的。
二、三大方向全景图
在深入每个方向之前,先用一张表把三个方向的核心差异摆出来:
| 维度 | NLP | CV | 语音 |
|---|---|---|---|
| 输入数据 | 文本(字符/词) | 图片(像素矩阵) | 音频(声波采样) |
| 数据预处理 | 分词、去停用词 | 缩放、归一化 | 降噪、分帧 |
| 核心架构 | Transformer | CNN / ViT | RNN / Transformer |
| 典型任务 | 分类、生成、翻译、问答 | 分类、检测、分割、生成 | 识别、合成、声纹 |
| 核心指标 | 准确率、BLEU、ROUGE | 准确率、IoU、mAP | WER、CER、MOS |
| 典型失败 | 否定词、反讽、幻觉 | 旋转、遮挡、光照 | 噪声、口音、语速 |
| 测试数据 | 文本语料 | 图片数据集 | 音频数据集 |
每个方向下面详细展开。
三、NLP ------ 让机器"理解"文字
3.1 NLP 在做什么
NLP(Natural Language Processing,自然语言处理)的目标是让机器理解和生成人类语言。你天天在用的搜索引擎、翻译软件、聊天机器人、语音助手,背后都有 NLP。
NLP 的典型任务:
- 文本分类:给一段文字打标签。比如"这条评论是正面还是负面?"
- 文本生成:给一个开头,让模型续写。比如 ChatGPT 写文章
- 机器翻译:中文翻英文、英文翻中文
- 问答系统:根据一段资料回答问题
- 文本摘要:把长文章压缩成短摘要
- 命名实体识别:从文本中提取人名、地名、机构名
3.2 第一步:分词(Tokenization)------ 把文字切成"块"
机器不像人一样天生能理解文字。它看到的是一串字符或者编码。第一步就是把文字切成小块,这个过程叫分词。
对英文来说,分词比较直观------按空格切就行:
"I love AI testing" → "I", "love", "AI", "testing"
对中文来说,分词更复杂,因为中文没有空格分隔:
"我喜欢AI测试" → "我", "喜欢", "AI", "测试"
分词的结果叫 Token(令牌)。大语言模型中经常听到"Token 限制"、"Token 计费",说的就是这个。
3.3 第二步:词嵌入(Embedding)------ 把词变成数字
计算机不认识文字,只认识数字。所以分词之后,要把每个 Token 转成一个数字向量------这个过程叫词嵌入(Word Embedding)。
核心思想:含义相近的词,向量也相近。
"猫" 的向量 ≈ "狗" 的向量(都是动物)
"猫" 的向量 ✗ "汽车" 的向量(差得很远)
这就像给每个词分配了一个"坐标",在坐标空间中含义相近的词靠得近。
3.4 第三步:注意力机制(Attention)------ 该重点关注什么
Transformer 架构(第 11 篇提过)的核心是注意力机制。它的作用是让模型知道"处理当前这个词的时候,该重点关注输入中的哪些部分"。
举个例子:
输入:"小明把苹果放在桌子上,然后它被吃掉了"
问题:"它"指的是什么?
模型需要知道"它"应该重点关注"苹果"而不是"小明"或"桌子"。注意力机制就是让模型自动学到这种"看哪里"的能力。
3.5 动手演示:NLP 核心概念
【新建文件】 在 ai-test/ch12_domains/ 文件夹下新建 nlp_demo.py,写入以下代码:
python
"""
NLP 核心概念演示
分词、词嵌入的直觉理解
"""
import numpy as np
print("=" * 60)
print("NLP 核心概念演示")
print("=" * 60)
# ========== 1. 分词演示 ==========
print("\n--- 1. 分词(Tokenization) ---")
import jieba
texts = [
"我喜欢AI测试工程师这个职业",
"大语言模型正在改变软件测试的方式",
"Python是AI测试的第一技能",
]
for text in texts:
tokens = list(jieba.cut(text))
print(f"\n 原文: {text}")
print(f" 分词: {tokens}")
print(f" Token数: {len(tokens)}")
print(f"\n 分词的意义:机器只能逐个 Token 处理,Token 的划分方式会影响模型理解")
print(f" 比如'AI测试'如果被切成['AI', '测试'],模型理解为两个概念")
print(f" 如果被切成['AI测', '试'],模型理解就完全跑偏了")
# ========== 2. 词嵌入演示 ==========
print(f"\n--- 2. 词嵌入(Word Embedding) ---")
print(f"核心思想:含义相近的词,向量空间中距离也相近")
# 用简化的向量来演示(真实模型的向量通常有几百到几千维)
words = {
"猫": np.array([0.9, 0.1, 0.8]),
"狗": np.array([0.85, 0.15, 0.75]),
"汽车": np.array([0.1, 0.9, 0.2]),
"卡车": np.array([0.15, 0.85, 0.25]),
"小明": np.array([0.5, 0.3, 0.6]),
}
def cosine_similarity(a, b):
"""余弦相似度:衡量两个向量的相似程度(-1到1,越接近1越相似)"""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"\n 词向量(简化为3维):")
for word, vec in words.items():
print(f" {word}: {vec}")
print(f"\n 余弦相似度:")
pairs = [("猫", "狗"), ("猫", "汽车"), ("汽车", "卡车"), ("猫", "小明"), ("狗", "卡车")]
for w1, w2 in pairs:
sim = cosine_similarity(words[w1], words[w2])
bar = "█" * int(sim * 20)
print(f" {w1} ↔ {w2}: {sim:.3f} {bar}")
print(f"\n 观察:猫↔狗 和 汽车↔卡车 的相似度都很高(同类事物)")
print(f" 猫↔汽车 的相似度很低(不同类事物)")
# ========== 3. 注意力机制的直觉理解 ==========
print(f"\n--- 3. 注意力机制(Attention)的直觉 ---")
# 简化演示:当模型处理"它"这个词时,应该关注句中的哪个词
sentence = ["小明", "把", "苹果", "放在", "桌子", "上", "然后", "它", "被", "吃掉"]
target_word = "它"
# 模拟注意力权重(实际中由模型学习得到)
attention_weights = {
"小明": 0.05,
"把": 0.01,
"苹果": 0.65, # 注意力高度集中在"苹果"上
"放在": 0.03,
"桌子": 0.12,
"上": 0.01,
"然后": 0.02,
"它": 0.02,
"被": 0.01,
"吃掉": 0.08,
}
print(f"\n 句子:{' '.join(sentence)}")
print(f" 当模型处理 '{target_word}' 时,注意力分布:")
for word, weight in sorted(attention_weights.items(), key=lambda x: x[1], reverse=True):
bar = "█" * int(weight * 50)
print(f" {word:<6s} {weight:.2f} {bar}")
print(f"\n 模型把大部分注意力放在'苹果'上------正确!'它'指的就是苹果")
print(f" 这就是注意力机制的作用:自动学会'该看哪里'")
【运行】 在终端执行:
bash
python nlp_demo.py
运行后输出(不同版本的 jieba 分词结果可能略有差异):
--- 1. 分词(Tokenization) ---
原文: 我喜欢AI测试工程师这个职业
分词: ['我', '喜欢', 'AI', '测试","工程师', '这个', '职业']
Token数: 6
原文: 大语言模型正在改变软件测试的方式
分词: ['大', '语言', '模型', '正在', '改变', '软件', '测试', '的', '方式']
Token数: 9
--- 2. 词嵌入(Word Embedding) ---
余弦相似度:
猫 ↔ 狗: 0.998 ████████████████████
猫 ↔ 汽车: 0.543 ██████████
汽车 ↔ 卡车: 0.998 ████████████████████
--- 3. 注意力机制(Attention)的直觉 ---
句子:小明 把 苹果 放在 桌子 上 然后 它 被 吃掉
当模型处理 '它' 时,注意力分布:
苹果 0.65 ███████████████████████████████
桌子 0.12 ██████
3.6 NLP 测试要点
| 测试维度 | 具体关注点 | 测试方法 |
|---|---|---|
| 否定词处理 | "不是很好"不等于"好" | 构造否定句测试 |
| 反讽识别 | "真是好极了才怪"是负面 | 收集讽刺/反语样本 |
| 多语言混合 | 中英混排、方言、口语 | 构造混合语言输入 |
| 长文本处理 | 超长输入是否截断、丢失信息 | 逐步增加文本长度 |
| 语序依赖 | "猫追狗"和"狗追猫"意思不同 | 交换词序测试 |
| 幻觉检测 | 生成不存在的事实 | 对照知识库验证 |
| Token 限制 | 超出最大长度怎么办 | 测试边界长度 |
四、CV ------ 让机器"看"图片
4.1 CV 在做什么
CV(Computer Vision,计算机视觉)的目标是让机器理解和分析图像。你手机的人脸解锁、医院的 AI 辅助诊断、自动驾驶的障碍物识别,背后都是 CV。
CV 的典型任务:
- 图像分类:这张图是什么?(猫?狗?汽车?)
- 目标检测:图中有哪些物体?在哪里?(画框标出来)
- 图像分割:图中每个像素属于什么?(把人和背景分开)
- OCR(光学字符识别):图片中的文字是什么?
- 图像生成:根据文字描述生成图片(比如 DALL-E、Midjourney)
4.2 图片在计算机眼中是什么
你看到一张猫的照片,大脑瞬间就认出来了。但计算机看到的是什么?
一张图片在计算机中就是一个数字矩阵。一张灰度图片是二维矩阵(每个格子一个数字,代表灰度值 0~255),一张彩色图片是三维矩阵(每个格子三个数字,代表红绿蓝三个通道)。
一张 28×28 的灰度图片 = 一个 28×28 的矩阵 = 784 个数字
一张 1920×1080 的彩色图片 = 一个 1920×1080×3 的矩阵 = 约 620 万个数字
AI 模型要从这几百上千万个数字中"看出"这是一只猫。这比你想象的难得多。
4.3 卷积(Convolution)------ 提取局部特征
CV 模型最核心的操作是卷积 。用一个叫卷积核(也叫滤波器)的小矩阵,在图片上从左到右、从上到下滑动,每到一个位置就做一次"加权求和",提取出该位置的局部特征。
类比:你用一个 3×3 的小窗口(放大镜)在图片上滑动扫描。每个窗口看一小块区域,提取出"这里有没有边缘?""这里有没有纹理?"
不同的卷积核提取不同的特征:
- 有的核专门提取水平边缘
- 有的核专门提取垂直边缘
- 有的核专门提取特定纹理(毛茸茸、光滑等)
- 更深的层把简单特征组合成复杂特征(边缘 → 形状 → 物体部件 → 完整物体)
4.4 动手演示:卷积操作
【新建文件】 在 ai-test/ch12_domains/ 文件夹下新建 cv_demo.py,写入以下代码:
python
"""
CV 核心概念演示
卷积操作的直觉理解与可视化
"""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams["font.sans-serif"] = ["Microsoft YaHei"]
matplotlib.rcParams["axes.unicode_minus"] = False
import os
os.makedirs("charts", exist_ok=True)
print("=" * 60)
print("CV 核心概念演示")
print("=" * 60)
# ========== 1. 图片 = 数字矩阵 ==========
print("\n--- 1. 图片就是数字矩阵 ---")
# 创建一个简单的 8x8 图片(模拟一个简单的"笑脸")
# 0 = 黑色,1 = 白色
image = np.zeros((8, 8))
image[1:7, 1:7] = 1 # 一个白色方块
image[3, 3] = 0 # 左眼(黑点)
image[3, 4] = 0 # 右眼(黑点)
image[5, 2:6] = 0 # 嘴巴(黑色横线)
print(f" 图片尺寸: {image.shape}")
print(f" 图片数据(0=黑, 1=白):")
for row in image:
print(f" {' '.join('█' if v > 0.5 else '·' for v in row)}")
# ========== 2. 卷积操作 ==========
print(f"\n--- 2. 卷积操作演示 ---")
print(f"用一个 3x3 的卷积核在图片上滑动,提取局部特征")
def convolve2d(image, kernel):
"""
手写一个 2D 卷积操作
理解卷积到底在做什么
"""
kh, kw = kernel.shape
ih, iw = image.shape
oh, ow = ih - kh + 1, iw - kw + 1
output = np.zeros((oh, ow))
for i in range(oh):
for j in range(ow):
# 取出当前位置的 3x3 小区域
region = image[i:i+kh, j:j+kw]
# 加权求和
output[i, j] = np.sum(region * kernel)
return output
# 定义三种不同的卷积核
kernels = {
"水平边缘检测": np.array([[-1, -1, -1],
[ 0, 0, 0],
[ 1, 1, 1]]),
"垂直边缘检测": np.array([[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]]),
"锐化": np.array([[ 0, -1, 0],
[-1, 5, -1],
[ 0, -1, 0]]),
}
print(f"\n 定义了 {len(kernels)} 种卷积核,分别提取不同的特征")
# 对图片应用每种卷积核
feature_maps = {}
for name, kernel in kernels.items():
feature_map = convolve2d(image, kernel)
feature_maps[name] = feature_map
print(f"\n [{name}]")
print(f" 卷积核:")
for row in kernel:
print(f" {' '.join(f'{v:>3d}' for v in row)}")
print(f" 特征图(提取结果):")
for row in feature_map:
print(f" {' '.join(f'{v:>5.1f}' for v in row)}")
# ========== 3. 可视化:原图和特征图 ==========
print(f"\n--- 3. 可视化 ---")
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
# 原图
axes[0].imshow(image, cmap="gray", vmin=0, vmax=1)
axes[0].set_title("原图(8x8 灰度图)", fontsize=12)
axes[0].axis("off")
# 各个特征图
for idx, (name, fmap) in enumerate(feature_maps.items(), 1):
axes[idx].imshow(fmap, cmap="RdBu_r")
axes[idx].set_title(f"特征图:{name}", fontsize=12)
axes[idx].axis("off")
plt.suptitle("卷积操作:从原图中提取不同特征", fontsize=15, fontweight="bold")
plt.tight_layout()
plt.savefig("charts/cv_convolution.png", dpi=150, bbox_inches="tight")
plt.close()
print(f" 卷积可视化已保存到 charts/cv_convolution.png")
# ========== 4. 特征层级概念 ==========
print(f"\n--- 4. 特征层级(从简单到复杂) ---")
print(f"""
CNN 的深层结构就像一个"特征组装工厂":
第 1 层(低级特征):
提取边缘、线条、颜色块
→ 相当于看到图片中最基本的视觉元素
第 2 层(中级特征):
把边缘组合成纹理、形状
→ 毛茸茸的纹理、圆形的眼睛、三角形的耳朵
第 3 层(高级特征):
把形状组合成物体部件
→ 猫脸、猫爪、猫尾巴
第 4 层(最终判断):
把部件组合成完整概念
→ "这是一只猫",置信度 92%
每一层提取的特征越来越抽象,越来越接近人类理解的"概念"
""")
【运行】 在终端执行:
bash
python cv_demo.py
运行后你会看到一个 8×8 的"笑脸"图片被三种不同的卷积核处理,产生了三张特征图:
- 水平边缘检测:突出了嘴巴(水平线)和方块的上下边缘
- 垂直边缘检测:突出了方块的左右边缘
- 锐化:增强了边缘和细节
同时生成的 charts/cv_convolution.png 可以直观看到原图和三种特征图的对比。
4.5 CV 测试要点
| 测试维度 | 具体关注点 | 测试方法 |
|---|---|---|
| 旋转不变性 | 图片旋转 15 度还认识吗? | 对测试图片做旋转增强 |
| 缩放不变性 | 图片放大/缩小还认识吗? | 不同分辨率测试 |
| 遮挡鲁棒性 | 物体被部分遮挡还能识别吗? | 随机遮挡测试 |
| 光照变化 | 过亮、过暗、阴影下的表现 | 不同亮度/对比度测试 |
| 噪声容忍 | 加噪声后结果是否稳定 | 添加高斯噪声、椒盐噪声 |
| 对抗攻击 | 微小像素扰动能骗过模型吗? | 对抗样本测试(第 14 篇) |
| 分辨率边界 | 极小/极大图片的处理 | 测试边界分辨率 |
| 背景干扰 | 复杂背景下的表现 | 多样化背景测试 |
五、语音 ------ 让机器"听"懂声音
5.1 语音技术在做什么
语音技术让机器能"听"和"说"。你手机的语音助手(Siri、小爱同学)、会议自动转录、智能客服,背后都是语音技术。
两大核心任务:
- ASR(Automatic Speech Recognition,自动语音识别):把语音转成文字。你说"今天天气怎么样",机器输出文字"今天天气怎么样"
- TTS(Text-to-Speech,语音合成):把文字转成语音。机器读出文字内容,听起来像真人说话
5.2 声波在计算机眼中是什么
就像图片是数字矩阵一样,声音在计算机中是时间序列的数字。
人说话产生声波,麦克风把声波转换成电信号,再采样成一串数字。常见的采样率是 16kHz,即每秒采样 16000 个数字。
处理语音时,通常会把连续的声波切成一小段一小段的帧(通常每帧 25 毫秒),每帧提取出一组特征(比如梅尔频谱系数 MFCC)。这些特征序列就送入模型处理。
5.3 ASR 的处理流程
一个完整的语音识别流程:
音频输入 → 预处理(降噪、分帧) → 特征提取(MFCC/梅尔频谱) → 声学模型(识别音素) → 语言模型(组合成词/句) → 文字输出
用类比来理解:
你听到一句英文"Hello world",你的大脑是这样处理的:
- 耳朵接收声波(音频输入)
- 大脑过滤掉背景噪音(预处理)
- 大脑识别出一个个音节:"hel-lo" "wor-ld"(声学模型)
- 大脑根据语境组合成词:"hello" "world"(语言模型)
- 你理解了这句话的意思(文字输出)
5.4 语音测试要点
语音测试比 NLP 和 CV 更复杂,因为它涉及多模态------既要有音频质量的考量,又要有文本识别准确性的考量。
| 测试维度 | 具体关注点 | 测试方法 |
|---|---|---|
| 说话人多样性 | 不同性别、年龄、口音的识别率 | 多说话人数据集测试 |
| 噪声鲁棒性 | 安静、街道、餐厅环境下的表现 | 叠加不同环境噪声 |
| 语速变化 | 快速说话、慢速说话 | 不同语速的录音测试 |
| 音量变化 | 大声、小声的识别率 | 不同音量级别测试 |
| 方言/口音 | 标准普通话 vs 各地方言 | 方言数据集测试 |
| 专业术语 | 行业词汇(医学、法律、IT) | 领域词汇测试 |
| WER(词错误率) | 识别的文字错多少 | 和人工转写对比 |
| TTS 自然度 | 合成的语音听起来自然吗 | 主观评分(MOS) |
语音测试的核心指标是 WER(Word Error Rate,词错误率):
WER = (替换 + 删除 + 插入) / 参考文本的词数
翻译:识别出来的文字和正确答案之间,错了多少个词
WER 越低越好。行业水平大致是:
- WER < 5%:优秀(安静环境、标准口音)
- WER 5%~15%:良好(一般环境)
- WER 15%~30%:一般(嘈杂环境或方言)
- WER > 30%:需要优化
六、三大方向测试方案生成
理解了三大方向的原理和测试要点之后,我们用一个脚本来生成每个方向的测试方案模板。以后你接到任何 AI 测试任务,先跑这个脚本生成测试框架,再往里面填充具体用例。
【新建文件】 在 ai-test/ch12_domains/ 文件夹下新建 domain_test_plan.py,写入以下代码:
python
"""
三大 AI 方向测试方案生成器
根据模型类型,自动生成测试维度和建议用例
"""
print("=" * 60)
print("AI 测试方案生成器")
print("=" * 60)
# ========== 测试维度定义 ==========
test_plans = {
"NLP": {
"典型任务": ["文本分类", "情感分析", "机器翻译", "文本生成", "问答系统"],
"核心指标": ["准确率", "精确率", "召回率", "F1", "BLEU", "ROUGE"],
"测试维度": {
"正常功能": {
"描述": "验证核心功能是否正常",
"用例方向": [
"典型正面/负面/中性文本分类",
"标准问题的问答准确性",
"常见句式的翻译质量",
],
"样本数建议": "10-20 个",
},
"否定与反讽": {
"描述": "测试模型对否定词、反讽的处理",
"用例方向": [
"简单否定:'不是很好'",
"双重否定:'不是不满意'",
"反讽:'真是好极了才怪'",
"反问:'这能好到哪去?'",
],
"样本数建议": "8-15 个",
},
"边界情况": {
"描述": "测试模糊、暧昧的输入",
"用例方向": [
"褒贬混合的文本",
"极短文本(1-2个字)",
"极长文本(超长输入)",
"模糊中性表达",
],
"样本数建议": "5-10 个",
},
"异常输入": {
"描述": "测试模型对异常输入的容错",
"用例方向": [
"空字符串",
"纯数字/纯符号",
"非目标语言(英文/日文等)",
"特殊字符和 emoji",
],
"样本数建议": "5-8 个",
},
"鲁棒性": {
"描述": "测试输入微小变化后的稳定性",
"用例方向": [
"同义词替换后结果是否一致",
"加入语气词后结果是否一致",
"语序变化后结果是否一致",
],
"样本数建议": "5-10 个",
},
"生成质量(如果涉及生成)": {
"描述": "测试文本生成的质量和安全性",
"用例方向": [
"事实准确性(是否生成虚假信息)",
"上下文一致性(多轮对话是否矛盾)",
"有害内容检测",
],
"样本数建议": "10-15 个",
},
},
},
"CV": {
"典型任务": ["图像分类", "目标检测", "图像分割", "OCR"],
"核心指标": ["准确率", "Precision", "Recall", "mAP", "IoU"],
"测试维度": {
"正常功能": {
"描述": "标准图片的识别能力",
"用例方向": [
"清晰、标准角度的图片",
"不同类别的典型样本",
"不同场景的代表性图片",
],
"样本数建议": "20-50 个(每个类别至少 5 个)",
},
"变换鲁棒性": {
"描述": "图片变换后的识别稳定性",
"用例方向": [
"旋转(15°、30°、45°、90°)",
"缩放(放大 2 倍、缩小 0.5 倍)",
"裁剪(去掉边缘 10%、30%)",
"翻转(水平翻转、垂直翻转)",
],
"样本数建议": "每种变换 5-10 个",
},
"光照与色彩": {
"描述": "不同光照条件下的表现",
"用例方向": [
"过亮/过暗的图片",
"不同色调(偏红、偏蓝)",
"阴影和高光",
],
"样本数建议": "每种条件 5 个",
},
"噪声与模糊": {
"描述": "图片质量下降后的表现",
"用例方向": [
"高斯噪声(不同强度)",
"运动模糊",
"低分辨率(像素化)",
],
"样本数建议": "每种退化 5 个",
},
"异常输入": {
"描述": "非正常图片输入的处理",
"用例方向": [
"纯黑/纯白图片",
"非图片文件(文本改后缀名)",
"极小图片(1x1 像素)",
"极大图片(超高分辨率)",
],
"样本数建议": "5-8 个",
},
"对抗鲁棒性": {
"描述": "对抗样本攻击的抵抗能力",
"用例方向": [
"FGSM 对抗样本",
"像素扰动攻击",
"语义对抗(加贴纸、改纹理)",
],
"样本数建议": "10-15 个(第 14 篇详细展开)",
},
},
},
"语音": {
"典型任务": ["语音识别(ASR)", "语音合成(TTS)", "声纹识别", "关键词检测"],
"核心指标": ["WER", "CER", "MOS", "准确率"],
"测试维度": {
"正常功能": {
"描述": "标准语音的识别/合成能力",
"用例方向": [
"标准普通话、标准语速的录音",
"安静环境下的语音输入",
"常见词汇和句式",
],
"样本数建议": "20-30 条",
},
"说话人多样性": {
"描述": "不同说话人的表现",
"用例方向": [
"男声/女声",
"不同年龄段(儿童、青年、老年)",
"不同口音/方言",
],
"样本数建议": "每类说话人 5-10 条",
},
"环境鲁棒性": {
"描述": "不同噪声环境下的表现",
"用例方向": [
"安静环境(基准)",
"办公室环境(键盘声、交谈声)",
"街道环境(车流、人群)",
"餐厅环境(餐具、音乐)",
],
"样本数建议": "每种环境 5-10 条",
},
"语音特征变化": {
"描述": "语音本身变化时的表现",
"用例方向": [
"快速说话",
"慢速说话",
"大声说话/小声说话",
"停顿和犹豫(嗯、啊)",
],
"样本数建议": "每种变化 5 条",
},
"专业领域": {
"描述": "领域词汇的识别能力",
"用例方向": [
"医学术语",
"技术术语(IT/编程)",
"人名/地名/品牌名",
],
"样本数建议": "每领域 10 条",
},
},
},
}
# ========== 生成测试方案 ==========
def generate_plan(domain):
"""为指定方向生成测试方案"""
if domain not in test_plans:
print(f"未知方向:{domain}")
return
plan = test_plans[domain]
print(f"\n{'=' * 60}")
print(f"{domain} 测试方案")
print(f"{'=' * 60}")
print(f"\n 典型任务:{'、'.join(plan['典型任务'])}")
print(f" 核心指标:{'、'.join(plan['核心指标'])}")
total_cases = 0
for dimension, info in plan["测试维度"].items():
print(f"\n {'─' * 50}")
print(f" 【{dimension}】")
print(f" 描述:{info['描述']}")
print(f" 样本数建议:{info['样本数建议']}")
print(f" 用例方向:")
for direction in info["用例方向"]:
print(f" - {direction}")
total_cases += len(info["用例方向"])
print(f"\n {'─' * 50}")
print(f" 测试维度总数:{len(plan['测试维度'])}")
print(f" 最少用例数:约 {total_cases} 个(每个方向至少 1 个样本)")
return plan
# ========== 主程序 ==========
if __name__ == "__main__":
print(f"\n支持的方向:NLP、CV、语音")
print(f"将依次生成三个方向的完整测试方案\n")
all_plans = {}
for domain in ["NLP", "CV", "语音"]:
plan = generate_plan(domain)
all_plans[domain] = plan
# 总结对比
print(f"\n{'=' * 60}")
print(f"三大方向测试方案对比总结")
print(f"{'=' * 60}")
print(f"\n {'方向':<6s} {'测试维度数':>8s} {'核心指标':<30s} {'测试复杂度':>8s}")
print(f" {'─' * 58}")
complexity = {"NLP": "中等", "CV": "中等", "语音": "较高"}
for domain, plan in all_plans.items():
dims = len(plan["测试维度"])
metrics = "、".join(plan["核心指标"][:3])
print(f" {domain:<6s} {dims:>8d} {metrics:<30s} {complexity[domain]:>8s}")
print(f"\n 通用测试维度(三个方向都要测):")
print(f" - 正常功能测试")
print(f" - 异常输入测试")
print(f" - 鲁棒性测试")
print(f" - 性能测试(延迟、并发、资源)")
print(f" - 置信度合理性测试")
【运行】 在终端执行:
bash
python domain_test_plan.py
运行后会依次输出 NLP、CV、语音三个方向的完整测试方案,包含每个维度的描述、用例方向和样本数建议。最后还有一个三大方向的对比总结。
这份输出可以直接作为你测试计划的起点------拿到一个模型,先看它是哪个方向的,然后套用对应方案,再根据实际业务场景调整。
七、三大方向测试策略总结
最后把三大方向的测试策略浓缩成一张对比表:
| 测试环节 | NLP | CV | 语音 |
|---|---|---|---|
| 测试数据准备 | 文本语料(标注情感/类别) | 图片数据集(标注类别/框/分割) | 音频数据(标注转写文本) |
| 数据增强方法 | 同义替换、加噪声词、回译 | 旋转、缩放、加噪声、遮挡 | 加背景噪声、变速、变调 |
| 核心评估指标 | Accuracy、F1、BLEU、ROUGE | Accuracy、mAP、IoU | WER、CER、MOS |
| 特有测试项 | 否定词、反讽、幻觉 | 旋转/缩放不变性、光照 | 口音、噪声、语速 |
| 对抗攻击类型 | 文本扰动、Prompt 注入 | FGSM、像素扰动、贴纸 | 对抗音频、超声波 |
| 性能测试重点 | Token 处理速度、长文本延迟 | 图片推理延迟、GPU 显存 | 实时性要求(< 300ms) |
| 典型业务场景 | 客服机器人、内容审核 | 人脸检测、医学影像 | 语音助手、会议转录 |
通用原则(不管哪个方向都适用):
- 先确认模型属于哪个方向、什么任务
- 选择对应的评估指标(不要用准确率一个指标打天下)
- 按测试维度系统设计用例(正常 → 边界 → 异常 → 对抗 → 鲁棒性)
- 关注该方向特有的失败模式
- 结合业务场景定义"合格标准"
八、本篇小结
本篇的核心收获:
- NLP 的核心流程:分词(Token)→ 词嵌入(Embedding)→ 注意力机制(Attention)→ 模型输出
- CV 的核心操作:卷积(Convolution)提取局部特征,层级结构从边缘到物体
- 语音的处理流程:音频输入 → 预处理 → 特征提取 → 声学模型 → 语言模型 → 文字输出
- 三大方向的差异:输入数据不同、核心架构不同、评估指标不同、失败模式不同
- 测试方案生成器:根据模型方向,系统地生成测试维度和用例方向
- 测试策略对比:每个方向该关注什么、该用什么指标、该设计什么用例
你现在可以根据"这是什么方向的模型"快速设计针对性的测试方案了。接下来进入 AI 测试核心的下一个主题------数据质量测试。AI 模型的质量有一半取决于数据的质量,"垃圾进垃圾出"是 AI 系统最常见也最容易被忽视的问题。
下一篇:【测AI】第13篇:AI 模型性能测试 ------ 速度、资源与并发
- 模型推理延迟怎么测?
- 吞吐量(QPS)与并发压力测试
- 内存 / GPU 资源占用监控
- 实战:对一个模型 API 做完整的性能压测
- 产出性能测试报告