神经网络测试用例¶
测试目标: 验证神经网络模型的功能和性能 测试类型: 单元测试、集成测试、性能测试 涉及组件: 神经网络层、激活函数、损失函数、优化器
📋 测试概述¶
测试目标¶
- 功能测试: 验证神经网络各组件的正确性
- 性能测试: 评估模型的计算效率和内存使用
- 鲁棒性测试: 测试模型对异常输入的稳定性
- 梯度测试: 验证梯度计算的正确性
测试环境¶
- Python版本: 3.8+
- 深度学习框架: PyTorch
- 测试框架: pytest
- 数值计算: NumPy
🧪 测试用例列表¶
1. 基础层测试¶
测试用例1.1: 全连接层¶
测试目标: 验证全连接层的前向传播和反向传播
测试代码:
Python
import pytest
import torch
import torch.nn as nn
def test_linear_layer_forward():
"""测试全连接层前向传播"""
# 创建全连接层
linear = nn.Linear(in_features=10, out_features=5)
# 创建输入
x = torch.randn(2, 10) # batch_size=2, input_features=10
# 前向传播
output = linear(x)
# 验证输出维度
assert output.shape == (2, 5), f"输出维度错误: {output.shape}"
# 验证输出类型
assert isinstance(output, torch.Tensor), "输出类型错误"
print("✓ 全连接层前向传播测试通过")
def test_linear_layer_backward():
"""测试全连接层反向传播"""
# 创建全连接层
linear = nn.Linear(in_features=10, out_features=5)
# 创建输入
x = torch.randn(2, 10, requires_grad=True)
# 前向传播
output = linear(x)
# 计算损失
loss = output.sum()
# 反向传播
loss.backward()
# 验证梯度存在
assert x.grad is not None, "输入梯度不存在"
assert linear.weight.grad is not None, "权重梯度不存在"
assert linear.bias.grad is not None, "偏置梯度不存在"
# 验证梯度形状
assert x.grad.shape == x.shape, "输入梯度形状错误"
assert linear.weight.grad.shape == linear.weight.shape, "权重梯度形状错误"
print("✓ 全连接层反向传播测试通过")
预期结果: 前向传播输出维度正确,反向传播梯度计算正确
测试用例1.2: 卷积层¶
测试目标: 验证卷积层的前向传播
测试代码:
Python
def test_conv2d_layer():
"""测试2D卷积层"""
# 创建卷积层
conv = nn.Conv2d(
in_channels=3,
out_channels=16,
kernel_size=3,
stride=1,
padding=1,
)
# 创建输入
x = torch.randn(2, 3, 32, 32) # batch_size=2, channels=3, height=32, width=32
# 前向传播
output = conv(x)
# 计算期望输出尺寸
expected_height = (32 + 2 * 1 - 3) // 1 + 1
expected_width = (32 + 2 * 1 - 3) // 1 + 1
# 验证输出维度
assert output.shape == (2, 16, expected_height, expected_width), \
f"输出维度错误: {output.shape}"
print("✓ 2D卷积层测试通过")
def test_conv2d_with_stride():
"""测试带步长的卷积层"""
# 创建卷积层
conv = nn.Conv2d(
in_channels=3,
out_channels=16,
kernel_size=3,
stride=2,
padding=1,
)
# 创建输入
x = torch.randn(1, 3, 32, 32)
# 前向传播
output = conv(x)
# 计算期望输出尺寸
expected_height = (32 + 2 * 1 - 3) // 2 + 1
expected_width = (32 + 2 * 1 - 3) // 2 + 1
# 验证输出维度
assert output.shape == (1, 16, expected_height, expected_width), \
f"输出维度错误: {output.shape}"
print("✓ 带步长的卷积层测试通过")
预期结果: 输出维度符合卷积计算公式
测试用例1.3: 池化层¶
测试目标: 验证池化层的前向传播
测试代码:
Python
def test_maxpool2d():
"""测试最大池化层"""
# 创建最大池化层
pool = nn.MaxPool2d(kernel_size=2, stride=2)
# 创建输入
x = torch.randn(1, 3, 32, 32)
# 前向传播
output = pool(x)
# 验证输出维度
assert output.shape == (1, 3, 16, 16), f"输出维度错误: {output.shape}"
print("✓ 最大池化层测试通过")
def test_avgpool2d():
"""测试平均池化层"""
# 创建平均池化层
pool = nn.AvgPool2d(kernel_size=2, stride=2)
# 创建输入
x = torch.randn(1, 3, 32, 32)
# 前向传播
output = pool(x)
# 验证输出维度
assert output.shape == (1, 3, 16, 16), f"输出维度错误: {output.shape}"
print("✓ 平均池化层测试通过")
预期结果: 输出维度为输入的一半
2. 激活函数测试¶
测试用例2.1: ReLU激活函数¶
测试目标: 验证ReLU激活函数
测试代码:
Python
def test_relu():
"""测试ReLU激活函数"""
relu = nn.ReLU()
# 测试正值
x_pos = torch.tensor([1.0, 2.0, 3.0])
output_pos = relu(x_pos)
assert torch.allclose(output_pos, x_pos), "正值处理错误"
# 测试负值
x_neg = torch.tensor([-1.0, -2.0, -3.0])
output_neg = relu(x_neg)
assert torch.allclose(output_neg, torch.zeros_like(x_neg)), "负值处理错误"
# 测试混合值
x_mixed = torch.tensor([-1.0, 0.0, 1.0])
output_mixed = relu(x_mixed)
expected = torch.tensor([0.0, 0.0, 1.0])
assert torch.allclose(output_mixed, expected), "混合值处理错误"
print("✓ ReLU激活函数测试通过")
def test_relu_gradient():
"""测试ReLU梯度"""
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0], requires_grad=True)
relu = nn.ReLU()
# 前向传播
output = relu(x)
# 计算损失
loss = output.sum()
# 反向传播
loss.backward()
# 验证梯度
expected_grad = torch.tensor([0.0, 0.0, 0.0, 1.0, 1.0])
assert torch.allclose(x.grad, expected_grad), "梯度计算错误"
print("✓ ReLU梯度测试通过")
预期结果: 负值输出0,正值保持不变
测试用例2.2: Sigmoid激活函数¶
测试目标: 验证Sigmoid激活函数
测试代码:
Python
def test_sigmoid():
"""测试Sigmoid激活函数"""
sigmoid = nn.Sigmoid()
# 测试不同输入
x = torch.tensor([-10.0, -1.0, 0.0, 1.0, 10.0])
output = sigmoid(x)
# 验证输出范围
assert (output >= 0).all() and (output <= 1).all(), "输出范围错误"
# 验证特定值
assert abs(output[2] - 0.5) < 1e-6, "Sigmoid(0)应该等于0.5"
assert output[0] < 0.001, "Sigmoid(-10)应该接近0"
assert output[4] > 0.999, "Sigmoid(10)应该接近1"
print("✓ Sigmoid激活函数测试通过")
def test_sigmoid_gradient():
"""测试Sigmoid梯度"""
x = torch.tensor([0.0], requires_grad=True)
sigmoid = nn.Sigmoid()
# 前向传播
output = sigmoid(x)
# 计算损失
loss = output.sum()
# 反向传播
loss.backward()
# 验证梯度 (sigmoid在0处的导数应该是0.25)
assert abs(x.grad.item() - 0.25) < 1e-6, "梯度计算错误"
print("✓ Sigmoid梯度测试通过")
预期结果: 输出在(0, 1)范围内,sigmoid(0)=0.5
测试用例2.3: Softmax激活函数¶
测试目标: 验证Softmax激活函数
测试代码:
Python
def test_softmax():
"""测试Softmax激活函数"""
softmax = nn.Softmax(dim=1)
# 创建输入
x = torch.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
# 前向传播
output = softmax(x)
# 验证输出范围
assert (output >= 0).all() and (output <= 1).all(), "输出范围错误"
# 验证和为1
row_sums = output.sum(dim=1)
assert torch.allclose(row_sums, torch.ones_like(row_sums)), "行和不为1"
# 验证单调性
for i in range(x.shape[0]):
assert output[i, 0] < output[i, 1] < output[i, 2], "单调性错误"
print("✓ Softmax激活函数测试通过")
预期结果: 输出在(0, 1)范围内,每行和为1
3. 损失函数测试¶
测试用例3.1: 交叉熵损失¶
测试目标: 验证交叉熵损失函数
测试代码:
Python
def test_cross_entropy_loss():
"""测试交叉熵损失"""
criterion = nn.CrossEntropyLoss()
# 创建输入和目标
logits = torch.randn(3, 5) # batch_size=3, num_classes=5
targets = torch.tensor([0, 2, 4])
# 计算损失
loss = criterion(logits, targets)
# 验证损失类型
assert isinstance(loss, torch.Tensor), "损失类型错误"
assert loss.dim() == 0, "损失应该是标量"
# 验证损失为正
assert loss.item() > 0, "损失应该为正"
print("✓ 交叉熵损失测试通过")
def test_cross_entropy_perfect_prediction():
"""测试完美预测的损失"""
criterion = nn.CrossEntropyLoss()
# 创建完美预测
logits = torch.tensor([
[10.0, -10.0, -10.0],
[-10.0, 10.0, -10.0],
[-10.0, -10.0, 10.0],
])
targets = torch.tensor([0, 1, 2])
# 计算损失
loss = criterion(logits, targets)
# 验证损失接近0
assert loss.item() < 1e-6, "完美预测的损失应该接近0"
print("✓ 完美预测损失测试通过")
预期结果: 损失为正数,完美预测时损失接近0
测试用例3.2: MSE损失¶
测试目标: 验证均方误差损失函数
测试代码:
Python
def test_mse_loss():
"""测试MSE损失"""
criterion = nn.MSELoss()
# 创建输入和目标
predictions = torch.randn(3, 5)
targets = torch.randn(3, 5)
# 计算损失
loss = criterion(predictions, targets)
# 验证损失类型
assert isinstance(loss, torch.Tensor), "损失类型错误"
assert loss.dim() == 0, "损失应该是标量"
# 验证损失为正
assert loss.item() >= 0, "损失应该非负"
print("✓ MSE损失测试通过")
def test_mse_perfect_prediction():
"""测试完美预测的MSE损失"""
criterion = nn.MSELoss()
# 创建完美预测
predictions = torch.tensor([[1.0, 2.0, 3.0]])
targets = torch.tensor([[1.0, 2.0, 3.0]])
# 计算损失
loss = criterion(predictions, targets)
# 验证损失为0
assert loss.item() == 0, "完美预测的MSE损失应该为0"
print("✓ 完美预测MSE损失测试通过")
预期结果: 损失非负,完美预测时损失为0
4. 优化器测试¶
测试用例4.1: SGD优化器¶
测试目标: 验证SGD优化器
测试代码:
Python
def test_sgd_optimizer():
"""测试SGD优化器"""
# 创建模型
model = nn.Linear(10, 5)
# 创建优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 创建输入和目标
x = torch.randn(2, 10)
y = torch.randn(2, 5)
# 记录初始参数
initial_weight = model.weight.clone()
# 前向传播
output = model(x)
loss = nn.MSELoss()(output, y)
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新参数
optimizer.step()
# 验证参数已更新
assert not torch.allclose(model.weight, initial_weight), "参数未更新"
print("✓ SGD优化器测试通过")
预期结果: 参数被正确更新
测试用例4.2: Adam优化器¶
测试目标: 验证Adam优化器
测试代码:
Python
def test_adam_optimizer():
"""测试Adam优化器"""
# 创建模型
model = nn.Linear(10, 5)
# 创建优化器
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 创建输入和目标
x = torch.randn(2, 10)
y = torch.randn(2, 5)
# 记录初始参数
initial_weight = model.weight.clone()
# 前向传播
output = model(x)
loss = nn.MSELoss()(output, y)
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新参数
optimizer.step()
# 验证参数已更新
assert not torch.allclose(model.weight, initial_weight), "参数未更新"
print("✓ Adam优化器测试通过")
预期结果: 参数被正确更新
5. 网络架构测试¶
测试用例5.1: 简单神经网络¶
测试目标: 验证简单神经网络的前向传播
测试代码:
Python
class SimpleNN(nn.Module): # 继承nn.Module定义神经网络层
"""简单神经网络"""
def __init__(self, input_size, hidden_size, output_size): # __init__构造方法,创建对象时自动调用
super().__init__() # super()调用父类方法
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, output_size)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
def test_simple_nn():
"""测试简单神经网络"""
# 创建网络
model = SimpleNN(input_size=10, hidden_size=20, output_size=5)
# 创建输入
x = torch.randn(2, 10)
# 前向传播
output = model(x)
# 验证输出维度
assert output.shape == (2, 5), f"输出维度错误: {output.shape}"
print("✓ 简单神经网络测试通过")
预期结果: 输出维度正确
测试用例5.2: CNN网络¶
测试目标: 验证CNN网络的前向传播
测试代码:
Python
class SimpleCNN(nn.Module):
"""简单CNN"""
def __init__(self, num_classes=10):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc = nn.Linear(32 * 16 * 16, num_classes)
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x)))
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
def test_simple_cnn():
"""测试简单CNN"""
# 创建网络
model = SimpleCNN(num_classes=10)
# 创建输入
x = torch.randn(2, 3, 32, 32)
# 前向传播
output = model(x)
# 验证输出维度
assert output.shape == (2, 10), f"输出维度错误: {output.shape}"
print("✓ 简单CNN测试通过")
预期结果: 输出维度正确
6. 梯度检查测试¶
测试用例6.1: 数值梯度检查¶
测试目标: 使用数值梯度验证解析梯度
测试代码:
Python
def numerical_gradient(f, x, eps=1e-6):
"""
计算数值梯度
Args:
f: 函数
x: 输入张量
eps: 小量
Returns:
数值梯度
"""
grad = torch.zeros_like(x)
for i in range(x.numel()):
# 创建扰动
x_plus = x.clone()
x_plus.view(-1)[i] += eps # view重塑张量形状(要求内存连续)
x_minus = x.clone()
x_minus.view(-1)[i] -= eps
# 计算数值梯度
f_plus = f(x_plus)
f_minus = f(x_minus)
grad.view(-1)[i] = (f_plus - f_minus) / (2 * eps)
return grad
def test_gradient_check():
"""测试梯度检查"""
# 创建模型
model = nn.Linear(5, 3)
# 创建输入
x = torch.randn(1, 5, requires_grad=True)
# 定义损失函数
def loss_fn(x):
output = model(x)
return output.sum()
# 计算解析梯度
loss = loss_fn(x)
loss.backward()
analytical_grad = x.grad.clone()
# 计算数值梯度
numerical_grad = numerical_gradient(loss_fn, x)
# 比较梯度
relative_error = torch.abs(analytical_grad - numerical_grad) / \
(torch.abs(analytical_grad) + torch.abs(numerical_grad) + 1e-10)
max_error = relative_error.max().item() # 链式调用,连续执行多个方法
assert max_error < 1e-4, f"梯度误差过大: {max_error}"
print(f"✓ 梯度检查通过 (最大误差: {max_error:.2e})")
预期结果: 解析梯度和数值梯度的误差小于1e-4
7. 性能测试¶
测试用例7.1: 推理速度测试¶
测试目标: 测试模型推理速度
测试代码:
Python
import time
def test_inference_speed():
"""测试推理速度"""
# 创建模型
model = SimpleCNN(num_classes=10)
model.eval() # eval()开启评估模式(关闭Dropout等)
# 创建输入
x = torch.randn(1, 3, 32, 32)
# 预热
for _ in range(10):
_ = model(x)
# 测试
num_iterations = 100
start_time = time.time()
with torch.no_grad(): # 禁用梯度计算,节省内存
for _ in range(num_iterations):
_ = model(x)
end_time = time.time()
avg_time = (end_time - start_time) / num_iterations
throughput = num_iterations / (end_time - start_time)
print(f"✓ 平均推理时间: {avg_time * 1000:.2f} ms")
print(f"✓ 吞吐量: {throughput:.2f} samples/s")
预期结果: 推理时间在合理范围内
测试用例7.2: 内存使用测试¶
测试目标: 测试模型内存使用
测试代码:
Python
def test_memory_usage():
"""测试内存使用"""
if not torch.cuda.is_available():
print("⚠ CUDA不可用,跳过内存测试")
return
# 创建模型
model = SimpleCNN(num_classes=10).cuda()
model.eval()
# 创建输入
x = torch.randn(16, 3, 32, 32).cuda()
# 记录初始内存
torch.cuda.empty_cache()
initial_memory = torch.cuda.memory_allocated()
# 推理
with torch.no_grad():
_ = model(x)
# 记录峰值内存
peak_memory = torch.cuda.max_memory_allocated()
memory_used = (peak_memory - initial_memory) / 1024**2 # MB
print(f"✓ 内存使用: {memory_used:.2f} MB")
# 验证内存使用在合理范围内
assert memory_used < 100, "内存使用过大"
预期结果: 内存使用在合理范围内
📊 测试执行¶
运行所有测试¶
Bash
# 运行所有测试
pytest tests/test_neural_network.py -v
# 运行特定测试
pytest tests/test_neural_network.py::test_linear_layer_forward -v
# 生成覆盖率报告
pytest tests/test_neural_network.py --cov=neural_network --cov-report=html
✅ 验证方法¶
1. 自动化验证¶
- 运行所有测试用例
- 检查断言是否通过
- 记录测试结果
2. 梯度验证¶
- 使用数值梯度验证解析梯度
- 检查梯度误差
- 确保梯度计算正确
3. 性能基准¶
- 建立性能基准
- 监控性能变化
- 优化模型性能
📝 测试报告¶
测试报告应包含:
- 测试概览
- 测试用例数量
- 通过/失败统计
-
代码覆盖率
-
详细结果
- 每个测试用例的结果
- 性能指标
-
梯度检查结果
-
问题分析
- 失败原因分析
- 改进建议
- 后续计划
测试完成标准: 所有测试用例通过 推荐测试频率: 每次代码更新 测试维护周期: 每周







