【测AI】第11篇:深度学习基础 —— 神经网络是怎么工作的

写在前面

第 08 篇我们学了机器学习的基本概念------模型从数据中学习规律。但上一篇用的是决策树这种"简单"模型。现在的 AI 产品,图像识别、语音助手、大语言模型,背后跑的都是深度学习(Deep Learning)。

深度学习是机器学习的一个分支,核心是神经网络(Neural Network)。

你可能觉得神经网络很玄乎------什么隐藏层、激活函数、反向传播,听起来就头大。但如果你用对了类比,这些概念其实非常直观。本篇的目标就是把每一个概念都讲到"哦,原来是这么回事"的程度。

你不需要会训练模型,但你需要理解:

  • 神经网络是怎么"做决定"的
  • 它是怎么"学习"的(训练过程在干什么)
  • 它在什么情况下会"学歪"(出了什么质量问题)
  • 作为测试人员,你的关注点在哪里

本篇文件结构

本篇代码放在 ai-test/ch11_deep_learning/ 文件夹下:

文件名 用途 创建方式
neuron_demo.py 神经元工作原理演示 二、新建
network_demo.py 神经网络训练过程演示 三、新建

本篇需要安装 numpy、matplotlib、scikit-learn。先运行:

pip install numpy matplotlib scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple


一、从一个问题开始

假设你要做一个最简单的 AI 任务:

输入:一张 28×28 像素的灰度图片(手写数字 0~9)

输出:这是哪个数字?

这个问题叫手写数字识别,是深度学习的 "Hello World"。看起来简单,但用传统编程几乎没法做------你没法写出一条规则来判断"什么样的像素排列是 3,什么样的像素排列是 8"。因为手写的 3 有无数种写法。

这就是深度学习擅长的事:从大量例子中自动学习出"什么是 3"的特征。


二、神经元 ------ 一切的起点

2.1 人脑神经元的启发

神经网络的灵感来自人脑。人脑有约 860 亿个神经元,每个神经元:

  1. 接收信号:从其他神经元接收电化学信号(输入)
  2. 做判断:如果接收到的信号总量超过某个阈值,就"激活"
  3. 传出信号:激活后向下一个神经元发送信号(输出)

人工神经元做的事情完全一样,只是用数学来表达。

2.2 一个人工神经元是怎么"做决定"的

我们用一个生活中的例子来理解:

场景:你今天要不要出门跑步?

输入信号(你考虑的因素):

  • 天气好不好?(权重:很大)
  • 身体累不累?(权重:中等)
  • 有没有时间?(权重:很大)
  • 朋友邀不邀你?(权重:很小)

做决定:把每个因素打分,乘以它的重要性(权重),加起来看总分

  • 总分超过阈值 → 出门跑(激活)
  • 总分低于阈值 → 不出门(不激活)

这个过程就是一个人工神经元在做的事:

复制代码
输入 → 每个输入乘以权重 → 求和 → 加上偏置 → 通过激活函数 → 输出

用数学写就是:

输出 = f(w₁×x₁ + w₂×x₂ + w₃×x₃ + ... + b)

其中 x 是输入,w 是权重,b 是偏置,f 是激活函数。

2.3 代码验证:手写一个神经元

说了这么多概念,不如自己写一个神经元来看看它到底在干什么。

【新建文件】 在 ai-test/ch11_deep_learning/ 文件夹下新建 neuron_demo.py,写入以下代码:

python 复制代码
"""
神经元工作原理演示
用手写数字识别的简化场景,理解单个神经元是如何做决定的
"""

import numpy as np

print("=" * 60)
print("单个神经元工作原理")
print("=" * 60)


# ========== 场景:判断"这个手写数字是不是 3" ==========

# 我们假设有 3 个特征(实际中会有 784 个像素特征)
# 特征1:图像中间竖线的强度(3 有一个弧形竖线)
# 特征2:图像右上角弧度的强度(3 的右上角是圆弧)
# 特征3:图像左下角弧度的强度(3 的左下角也是圆弧)

def neuron(inputs, weights, bias, activation="sigmoid"):
    """
    一个人工神经元的完整计算过程

    参数:
        inputs:  输入特征 [x1, x2, x3]
        weights: 权重 [w1, w2, w3],表示每个特征的重要性
        bias:    偏置,调整激活的难易程度
        activation: 激活函数类型

    返回:
        神经元的输出值
    """
    # 第一步:每个输入乘以对应的权重
    weighted = [x * w for x, w in zip(inputs, weights)]

    # 第二步:加权求和
    total = sum(weighted) + bias

    # 第三步:通过激活函数
    if activation == "sigmoid":
        # Sigmoid 函数:把任意实数压缩到 0~1 之间
        # 就像把一个模糊的"程度"变成一个确定的"概率"
        output = 1 / (1 + np.exp(-total))
    elif activation == "relu":
        # ReLU 函数:负数变 0,正数不变
        # 就像一个单向阀,只让正信号通过
        output = max(0, total)
    else:
        output = total

    return output, total


# ========== 实验1:不同输入的输出 ==========

print("\n--- 实验1:同一个神经元,不同的输入 ---")

# 这个神经元学会了"识别数字 3"
weights = [0.5, 0.8, 0.6]    # 权重:特征2(右上角弧度)最重要
bias = -1.0                   # 偏置:让激活门槛高一点

test_cases = [
    ("数字 3(很像)",   [0.8, 0.9, 0.7]),
    ("数字 3(有点模糊)", [0.5, 0.6, 0.5]),
    ("数字 8(不太像)", [0.3, 0.2, 0.4]),
    ("数字 1(完全不像)", [0.1, 0.0, 0.1]),
]

for name, inputs in test_cases:
    output, raw_sum = neuron(inputs, weights, bias, activation="sigmoid")
    is_three = "是 3" if output > 0.5 else "不是 3"
    print(f"  {name}")
    print(f"    输入特征: {inputs}")
    print(f"    加权求和: {raw_sum:.3f}")
    print(f"    Sigmoid输出: {output:.4f}")
    print(f"    判断结果: {is_three}(置信度 {output:.1%})")
    print()

【运行】 在终端执行:

bash 复制代码
python neuron_demo.py

运行后输出:

复制代码
--- 实验1:同一个神经元,不同的输入 ---
  数字 3(很像)
    输入特征: [0.8, 0.9, 0.7]
    加权求和: 1.140
    Sigmoid输出: 0.7577
    判断结果: 是 3(置信度 75.8%)

  数字 3(有点模糊)
    输入特征: [0.5, 0.6, 0.5]
    加权求和: 0.430
    Sigmoid输出: 0.6058
    判断结果: 是 3(置信度 60.6%)

  数字 8(不太像)
    输入特征: [0.3, 0.2, 0.4]
    加权求和: -0.410
    Sigmoid输出: 0.3989
    判断结果: 不是 3(置信度 39.9%)

  数字 1(完全不像)
    输入特征: [0.1, 0.0, 0.1]
    加权求和: -0.840
    Sigmoid输出: 0.3015
    判断结果: 不是 3(置信度 30.2%)

你看到了吗?一个神经元的"思考"过程就三步:加权求和、加上偏置、过激活函数。输出是一个 0~1 之间的概率------越接近 1,越确定是"3"。

2.4 三个核心组件详解

从上面的代码中,有三个组件需要你理解透。

权重(Weight)------特征的重要性

权重越大,这个特征对最终决策的影响越大。负权重表示"这个特征越高,越不可能是 3"。

类比:天气好的权重很大(天气不好的话你大概率不跑步),朋友邀不邀你权重很小(朋友不邀你也可能自己去)。

权重是训练出来的------模型自己从数据中学到的,不是人设定的。

偏置(Bias)------调整决策门槛

偏置让神经元更容易或更难被激活。偏置越小(负得越多),神经元越"挑剔"------需要更强的输入信号才会激活。

类比:偏置就像你的"出门门槛"。门槛高的人(偏置很负),天气再好也不轻易出门;门槛低的人(偏置为正),随便有点理由就出门了。

激活函数(Activation Function)------引入非线性

如果只有加权求和,神经元就是一个线性模型(一条直线),表达能力很弱。激活函数把输出"弯曲"了,让模型能学到复杂的非线性规律。

常见的激活函数:

  • Sigmoid:把输出压缩到 0~1 之间。适合输出概率
  • ReLU:负数变 0,正数不变。深度学习中最常用,因为计算快且效果好
  • Tanh:把输出压缩到 -1~1 之间。比 Sigmoid 关于原点对称

2.5 激活函数可视化

激活函数长什么样?画出来一看就懂。

【追加到 neuron_demo.py】 在上一段代码末尾继续添加:

python 复制代码
# ========== 激活函数可视化 ==========

import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams["font.sans-serif"] = ["Microsoft YaHei"]
matplotlib.rcParams["axes.unicode_minus"] = False

print("\n--- 激活函数可视化 ---")

x = np.linspace(-5, 5, 200)

# Sigmoid
sigmoid = 1 / (1 + np.exp(-x))

# ReLU
relu = np.maximum(0, x)

# Tanh
tanh = np.tanh(x)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

# Sigmoid
axes[0].plot(x, sigmoid, "b-", linewidth=2.5)
axes[0].set_title("Sigmoid\n输出压缩到 0~1\n适合输出概率", fontsize=13)
axes[0].axhline(y=0.5, color="gray", linestyle="--", alpha=0.5)
axes[0].axvline(x=0, color="gray", linestyle="--", alpha=0.5)
axes[0].grid(True, alpha=0.3)
axes[0].set_ylim(-0.3, 1.3)

# ReLU
axes[1].plot(x, relu, "r-", linewidth=2.5)
axes[1].set_title("ReLU\n负数变0,正数不变\n深度学习最常用", fontsize=13)
axes[1].axhline(y=0, color="gray", linestyle="--", alpha=0.5)
axes[1].axvline(x=0, color="gray", linestyle="--", alpha=0.5)
axes[1].grid(True, alpha=0.3)
axes[1].set_ylim(-0.5, 5)

# Tanh
axes[2].plot(x, tanh, "g-", linewidth=2.5)
axes[2].set_title("Tanh\n输出压缩到 -1~1\n关于原点对称", fontsize=13)
axes[2].axhline(y=0, color="gray", linestyle="--", alpha=0.5)
axes[2].axvline(x=0, color="gray", linestyle="--", alpha=0.5)
axes[2].grid(True, alpha=0.3)
axes[2].set_ylim(-1.5, 1.5)

plt.suptitle("三种常见激活函数", fontsize=16, fontweight="bold")
plt.tight_layout()
plt.savefig("charts/activation_functions.png", dpi=150, bbox_inches="tight")
plt.close()

print("  激活函数图表已保存到 charts/activation_functions.png")

【运行】 python neuron_demo.py。你会看到三张激活函数的曲线图。

从图上可以直观地看到:

  • Sigmoid 是一条 S 形曲线,把任何输入都压到 0~1。中间部分变化快,两端变化慢
  • ReLU 在 x < 0 时输出全是 0,x > 0 时就是一条直线。简单粗暴但非常有效
  • Tanh 也是一条 S 形曲线,但输出范围是 -1 到 1

三、从神经元到神经网络

3.1 为什么一个神经元不够

一个神经元只能做很简单的判断------比如"是 3 还是不是 3"。但现实中的问题远比这复杂:

  • 识别图片中有什么东西(猫?狗?车?人?)
  • 理解一句话的意思(正面?负面?中性?在问什么?)
  • 把一段中文翻译成英文

这些问题需要更强大的"大脑"。神经网络就是把很多神经元组织起来,形成层级结构。

3.2 神经网络的层级结构

一个典型的神经网络有三层:

输入层(Input Layer):接收原始数据(比如图片的像素值、文本的编码)

隐藏层(Hidden Layer):负责提取特征和模式。可能有一层,也可能有几十上百层("深度"学习的"深"就指层数多)

输出层(Output Layer):给出最终的预测结果

用打比方来说:

想象你在看一张猫的照片,你的大脑是这样处理的:

  1. 第一层(初级视觉皮层):看到边缘、线条、颜色块------最基础的视觉元素
  2. 第二层:把边缘组合成纹理、形状------毛茸茸的纹理、圆形的眼睛
  3. 第三层:把形状组合成部件------耳朵、胡须、尾巴
  4. 第四层:把部件组合成完整的概念------"这是一只猫"

神经网络也是这样:每层提取越来越抽象的特征。低层学边缘和纹理,中层学形状和部件,高层学完整概念。

3.3 前向传播:数据在网络中怎么流动

数据从输入层到输出层的流动过程叫前向传播(Forward Propagation):

复制代码
输入层 → 隐藏层1 → 隐藏层2 → ... → 输出层
  ↓         ↓         ↓                ↓
像素值  → 边缘特征 → 形状特征 → ... → "是猫,置信度 92%"

每一层做的事情都和单个神经元一样:接收上一层的输出,加权求和,过激活函数。只不过每层有多个神经元,所以每层的输出是一个向量(一组数字)。

【新建文件】 在 ai-test/ch11_deep_learning/ 文件夹下新建 network_demo.py,写入以下代码:

python 复制代码
"""
神经网络完整工作过程演示
从单个神经元到多层网络,理解前向传播和训练过程
"""

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("神经网络完整工作过程")
print("=" * 60)


# ========== 手写一个简单的两层神经网络 ==========

class SimpleNeuralNetwork:
    """
    一个简单的两层神经网络
    结构:输入层(2) → 隐藏层(4) → 输出层(1)

    用来做二分类:判断一个点属于红色还是蓝色
    """

    def __init__(self, input_size=2, hidden_size=4, output_size=1, learning_rate=0.1):
        # 初始化权重和偏置(随机)
        np.random.seed(42)

        # 第一层:输入层 → 隐藏层
        self.W1 = np.random.randn(input_size, hidden_size) * 0.5
        self.b1 = np.zeros((1, hidden_size))

        # 第二层:隐藏层 → 输出层
        self.W2 = np.random.randn(hidden_size, output_size) * 0.5
        self.b2 = np.zeros((1, output_size))

        self.learning_rate = learning_rate

    def sigmoid(self, x):
        """Sigmoid 激活函数"""
        return 1 / (1 + np.exp(-np.clip(x, -500, 500)))

    def sigmoid_derivative(self, x):
        """Sigmoid 的导数(用于反向传播)"""
        return x * (1 - x)

    def forward(self, X):
        """
        前向传播:数据从输入流向输出

        过程:
          X → [加权求和] → [激活函数] → hidden
          hidden → [加权求和] → [激活函数] → output
        """
        # 第一层
        self.z1 = X.dot(self.W1) + self.b1     # 加权求和
        self.a1 = self.sigmoid(self.z1)          # 激活函数 → 隐藏层输出

        # 第二层
        self.z2 = self.a1.dot(self.W2) + self.b2  # 加权求和
        self.a2 = self.sigmoid(self.z2)            # 激活函数 → 最终输出

        return self.a2

    def backward(self, X, y, output):
        """
        反向传播:计算误差,更新权重

        核心思想:
          1. 计算预测和真实答案之间的差距(损失)
          2. 从输出层往回推,计算每层权重对误差的"贡献"
          3. 调整权重,让误差变小
        """
        m = X.shape[0]    # 样本数

        # 输出层误差
        output_error = y - output                    # 预测和真实的差距
        output_delta = output_error * self.sigmoid_derivative(output)

        # 隐藏层误差(从输出层的误差"传播"回来)
        hidden_error = output_delta.dot(self.W2.T)
        hidden_delta = hidden_error * self.sigmoid_derivative(self.a1)

        # 更新权重和偏置
        self.W2 += self.a1.T.dot(output_delta) * self.learning_rate
        self.b2 += np.sum(output_delta, axis=0, keepdims=True) * self.learning_rate
        self.W1 += X.T.dot(hidden_delta) * self.learning_rate
        self.b1 += np.sum(hidden_delta, axis=0, keepdims=True) * self.learning_rate

    def train(self, X, y, epochs=1000):
        """
        训练过程:重复"前向传播 → 计算误差 → 反向传播 → 更新权重"
        """
        losses = []

        for epoch in range(epochs):
            # 前向传播
            output = self.forward(X)

            # 计算损失(均方误差)
            loss = np.mean((y - output) ** 2)
            losses.append(loss)

            # 反向传播 + 更新权重
            self.backward(X, y, output)

            if (epoch + 1) % 200 == 0:
                print(f"  Epoch {epoch+1:>5d}/{epochs}  损失: {loss:.6f}")

        return losses

3.4 训练过程:模型是怎么"学习"的

上面的类里,train() 方法就是训练过程的核心。它做的是重复循环:

  1. 前向传播:把训练数据输入网络,得到预测
  2. 计算损失:看预测和正确答案差多少
  3. 反向传播:算出每个权重对误差的"贡献"有多大
  4. 更新权重:往误差变小的方向微调权重

这个循环跑几千上万次,每次权重都被微调一点点,预测就越来越准。这就是"学习"。

反向传播是整个训练过程中最核心的概念。用类比来理解:

你射箭,目标在正前方 50 米。你射了一箭,偏左了 2 米。

反向传播就是:分析每一环(姿势、风向、弓的角度)对"偏左 2 米"这个误差的贡献有多大。

也许发现"弓的角度偏左 5 度"贡献了 80% 的误差,"风"贡献了 15%,"你的姿势"贡献了 5%。

然后你调整弓的角度,下次再射,误差可能就只有 0.5 米了。

每次都是从"结果的误差"倒推出"每个参数该调多少"------这就是"反向"传播的意思。

3.5 实际训练:看模型学到了什么

现在用我们手写的神经网络来训练一个简单的二分类任务------判断一个点属于哪个类别。

【追加到 network_demo.py】 在上一段代码末尾继续添加:

python 复制代码
# ========== 训练一个二分类模型 ==========

print("\n--- 训练演示:二分类任务 ---")

# 生成训练数据:两个类别的点
np.random.seed(42)

# 类别 0(蓝色):围绕 (2, 2) 的点
X_blue = np.random.randn(50, 2) * 0.8 + [2, 2]
y_blue = np.zeros((50, 1))

# 类别 1(红色):围绕 (6, 6) 的点
X_red = np.random.randn(50, 2) * 0.8 + [6, 6]
y_red = np.ones((50, 1))

# 合并
X_train = np.vstack([X_blue, X_red])
y_train = np.vstack([y_blue, y_red])

print(f"  训练数据:{X_train.shape[0]} 个样本,{X_train.shape[1]} 个特征")

# 创建网络
nn = SimpleNeuralNetwork(input_size=2, hidden_size=4, output_size=1, learning_rate=1.0)

# 训练
print(f"\n  开始训练...")
losses = nn.train(X_train, y_train, epochs=1000)

# 画损失曲线
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))

ax1.plot(losses, "b-", linewidth=1.5)
ax1.set_title("训练损失曲线\n(损失越来越小 = 模型在学习)", fontsize=13)
ax1.set_xlabel("训练轮数 (Epoch)", fontsize=11)
ax1.set_ylabel("损失 (Loss)", fontsize=11)
ax1.grid(True, alpha=0.3)

# 画决策边界
xx, yy = np.meshgrid(np.linspace(-1, 9, 200), np.linspace(-1, 9, 200))
grid_points = np.column_stack([xx.ravel(), yy.ravel()])
Z = nn.forward(grid_points).reshape(xx.shape)

ax2.contourf(xx, yy, Z, levels=50, cmap="RdBu_r", alpha=0.6)
ax2.scatter(X_blue[:, 0], X_blue[:, 1], c="blue", s=30, edgecolors="gray",
            label="类别 0(蓝)", zorder=5)
ax2.scatter(X_red[:, 0], X_red[:, 1], c="red", s=30, edgecolors="gray",
            label="类别 1(红)", zorder=5)
ax2.set_title("模型学到的决策边界\n(蓝区预测0,红区预测1)", fontsize=13)
ax2.set_xlabel("特征 1", fontsize=11)
ax2.set_ylabel("特征 2", fontsize=11)
ax2.legend(fontsize=10)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("charts/nn_training.png", dpi=150, bbox_inches="tight")
plt.close()

print(f"\n  训练结果图已保存到 charts/nn_training.png")

【运行】 在终端执行:

bash 复制代码
python network_demo.py

运行后你会看到:

复制代码
  开始训练...
  Epoch   200/1000  损失: 0.128453
  Epoch   400/1000  损失: 0.023567
  Epoch   600/1000  损失: 0.008234
  Epoch   800/1000  损失: 0.004121
  Epoch  1000/1000  损失: 0.002345

  训练结果图已保存到 charts/nn_training.png

同时生成的图片有两幅:

  • 左图(损失曲线):损失从高到低一路下降,说明模型在不断学习。最后趋于平缓,说明模型基本学到位了
  • 右图(决策边界):蓝色的区域是模型认为"类别 0"的地方,红色是"类别 1"。中间的分界线就是模型学到的决策边界

这就是一个完整的神经网络训练过程------虽然只有 2 层、4 个隐藏神经元,但原理和训练 ChatGPT 的大模型是相通的(只是规模天差地别)。


四、深度学习的核心概念

4.1 损失函数(Loss Function)------ 衡量"错得有多离谱"

训练的目标是让预测越来越准。怎么衡量"准不准"?用损失函数------它计算预测和正确答案之间的差距。

损失越大,说明模型预测得越差

损失越小,说明模型预测得越好

训练的目标就是让损失变小

常用的损失函数:

  • 均方误差(MSE) :回归任务用,(预测值 - 真实值)² 的平均
  • 交叉熵(Cross Entropy):分类任务用,衡量预测概率分布和真实分布的差距

第 09 篇学的那些评估指标(准确率、F1)是给人看的"成绩单",损失函数是模型训练时优化的"内部指标"。两者通常相关但不完全一样------损失低了,准确率一般也会高。

4.2 过拟合和欠拟合 ------ 在深度学习中更严重

第 08 篇用多项式回归演示了过拟合和欠拟合。在深度学习中,这个问题更加严重,因为深度网络的参数量巨大(几百万到几千亿个参数),特别容易"死记硬背"。

深度学习中的过拟合表现:

训练损失一直降,验证损失先降后升------典型的过拟合信号

模型对训练数据中的噪声和巧合模式也记住了

常用应对方法(测试人员需要知道这些术语):

  • 正则化(Regularization):给损失函数加一个惩罚项,防止权重过大
  • Dropout:训练时随机"关闭"一些神经元,防止过度依赖某些特征
  • 数据增强(Data Augmentation):对训练数据做变换(旋转、加噪声、同义词替换),增加数据多样性
  • 早停(Early Stopping):验证损失不再下降时提前停止训练

4.3 梯度和学习率 ------ 学习的"步幅"

梯度(Gradient)告诉你"往哪个方向调权重,损失会变小"。就像下山时知道"哪个方向是下坡"。

学习率(Learning Rate)控制每次调整的幅度:

学习率太大 → 一步跨太大,越过最低点,来回震荡,甚至发散

学习率太小 → 一步太小,训练极慢,可能卡在半山腰

学习率刚好 → 稳步下降到最低点

类比:你下山的时候,步子太大可能跳过谷底,步子太小走到天黑还没到。要找一个合适的步幅。

这是超参数(人来设定的参数),不是模型自己学的。测试人员需要了解:学习率没调好,模型效果可能很差,但不是模型的"Bug",而是训练配置的问题。

4.4 Batch Size 和 Epoch ------ 训练的基本单位

Batch Size(批大小):每次训练用多少条数据来更新一次权重。

批大小太大 → 每次更新更稳定,但吃更多内存

批大小太小 → 每次更新更快但噪声大,训练不稳定

Epoch(轮数):把全部训练数据过一遍叫一个 Epoch。通常要过几十到几百遍。

训练日志里常看到 Epoch 100/1000,意思就是第 100 轮,总共 1000 轮。


五、常见深度学习架构速览

深度学习有几种经典的网络架构,分别擅长不同类型的任务。作为测试人员,你不需要会设计这些网络,但要知道每种架构擅长什么、容易出什么问题。

5.1 CNN(卷积神经网络)------ 图像处理的主力

擅长:图片分类、目标检测、图像分割、OCR 文字识别

核心思想:用"卷积核"(小窗口)在图片上滑动,提取局部特征(边缘、纹理、形状)

类比:像用一个放大镜在图片上滑动扫描,每次只看一小块区域,提取局部特征。多个卷积核提取不同类型的特征。

测试关注点:

  • 对图片旋转、缩放、裁剪的鲁棒性(旋转 15 度就不认识了?)
  • 对噪声和模糊的容忍度(加一点噪声就乱了?)
  • 对分辨率变化的适应性(小图和大图都行吗?)

5.2 RNN / LSTM(循环神经网络)------ 序列数据处理

擅长:文本生成、语音识别、时间序列预测

核心思想:处理有顺序的数据(一个词一个词地读),保留"记忆"(前文信息影响后面的理解)

类比 :像你读一句话,读到后面的时候,脑子里还记着前面的词。"他打开了冰箱,它是冷的"------你知道"它"指的是冰箱,因为你记着前面的内容。

测试关注点:

  • 长序列的记忆能力(前面说的话后面还记得吗?)
  • 时序依赖(顺序变了,结果变吗?)

5.3 Transformer ------ 当代 AI 的核心架构

擅长:几乎所有 NLP 任务、大语言模型、多模态模型(GPT、BERT、Claude 等都是 Transformer)

核心思想 :注意力机制(Attention)------让模型知道"看输入的时候该重点关注哪些部分"

类比:你翻译"猫坐在垫子上"时,翻译"sat"的时候你会重点关注"猫"和"垫子",因为主语和宾语最影响动词的翻译。注意力机制就是让模型自动学习这种"该看哪里"的能力。

测试关注点:

  • 上下文理解能力(长对话中的信息保持)
  • 注意力偏差(是否过度关注某些模式)
  • 幻觉问题(生成不存在的信息)
  • Token 限制(输入长度超过限制怎么办)

5.4 一张表总结

架构 擅长任务 代表应用 测试重点
CNN 图像相关 图像识别、人脸识别、OCR 变换鲁棒性、噪声容忍
RNN/LSTM 序列数据 语音识别、文本生成 长序列记忆、时序依赖
Transformer NLP + 多模态 GPT、BERT、翻译、对话 上下文理解、幻觉、安全性
GAN 生成内容 图像生成、风格迁移 生成质量、多样性、安全
强化学习 决策策略 游戏AI、机器人 目标对齐、安全边界

六、从测试视角看深度学习

理解了原理之后,回到你的本职工作。以下是深度学习模型的常见质量风险,也是你测试时要重点关注的:

6.1 数据层面的风险

  • 数据不足:训练数据太少,模型学不到足够的规律 → 测试时用多样化的数据来验证
  • 数据偏见:训练数据中某些群体/场景的样本太少,模型对这些场景表现差 → 检查不同子群体的表现差异
  • 标注错误:训练数据中的"答案"就是错的,模型学到了错误的规律 → 抽样检查标注质量
  • 数据泄漏:测试数据混入了训练数据,评估结果虚高 → 严格审查数据划分

6.2 模型层面的风险

  • 过拟合:训练好但实际使用差 → 用新鲜数据测试
  • 对抗脆弱性:微小的输入扰动导致输出剧变 → 对抗样本测试
  • 置信度不校准:模型说 90% 确定,实际正确率只有 70% → 置信度校准测试
  • 泛化能力不足:换一个领域的数据就失效 → 跨域测试

6.3 系统层面的风险

  • 推理延迟:模型太大太慢 → 性能测试(第 13 篇)
  • 资源泄漏:长时间运行后内存/显存持续增长 → 持续压测监控
  • 输入处理:异常输入导致崩溃 → 异常输入测试(第 10 篇)
  • 版本一致性:训练和部署的模型版本不一致 → 回归测试

七、本篇小结

本篇的核心收获:

  • 人工神经元:输入 × 权重 + 偏置 → 激活函数 → 输出。这就是 AI "做决定"的最小单元
  • 神经网络的层级结构:输入层 → 隐藏层 → 输出层,每层提取越来越抽象的特征
  • 前向传播:数据从输入流向输出,得到预测
  • 反向传播:从误差倒推出每个权重的调整量------模型"学习"的核心机制
  • 损失函数:衡量预测和真实的差距,训练的目标是让损失变小
  • 激活函数:Sigmoid / ReLU / Tanh,引入非线性让模型能学到复杂规律
  • 常见架构:CNN(图像)、RNN(序列)、Transformer(NLP/大模型)
  • 测试视角:数据风险、模型风险、系统风险三大类

你现在理解了神经网络是怎么工作的。这给你带来的实际好处是:

当算法工程师说"我们的模型过拟合了",你知道他在说什么问题

当评测报告显示"置信度校准不良",你知道这对用户意味着什么

当你设计测试用例时,你知道为什么要覆盖对抗样本和边缘场景

下一篇我们从通用概念进入具体领域------NLP、CV、语音三大 AI 方向的技术原理和测试要点。理解了这些,你就能针对性地设计每个领域的测试方案了。


下一篇:【测AI】第12篇:NLP / CV / 语音 ------ 三大 AI 方向速通

  • NLP 是怎么"理解"语言的?分词、Embedding、Attention
  • CV 是怎么"看"图片的?卷积、特征图、目标检测
  • 语音是怎么"听"懂的?ASR、TTS 的基本流程
  • 三大方向各自的评估指标和测试方法
  • 如何针对不同方向设计测试用例
相关推荐
zhang-ge41 分钟前
python包和项目管理器uv在ubuntu下的安装与使用
开发语言·python·uv
老金带你玩AI1 小时前
Gemini 4上线!又回到御三家了?
人工智能
独立开发者阿乐1 小时前
问答聚合页设计:让 AI 直接引用你的答案
人工智能·结构化数据·geo优化·ai收录·悬赏问答·问答聚合页
深圳市益普科技有限公司1 小时前
半导体封测的 “大脑“ 保卫战:国产 MES 是怎么一步步啃下来的?
人工智能
康实训1 小时前
高校中医康复实训室功能分区规划方案
人工智能·实训室·实训室建设
for_ever_love__1 小时前
LangChain 降级、重试与超时:让服务在模型抽风时还能用
python·自然语言处理·langchain·agent·超时·重试·with_retry
桃西西呀1 小时前
LangGraph4j之四:运行引擎与流式
人工智能·llm·ai编程
桃西西呀1 小时前
LangGraph4j之五:持久化与断点续跑
人工智能·llm·ai编程
一木 之林1 小时前
微信小程序开发全攻略·文章版|00 总览:74集学习路径与知识地图
人工智能·学习·计算机视觉·微信小程序·小程序