返回

CNN模型预测超限?解决仪表盘读数识别难题

Ai

CNN 模型预测值超出训练范围问题解析与解决

一、问题

你用 PyTorch 搭建了一个 CNN 模型,希望通过摄像头读取模拟仪表盘上的温度值。训练数据是大约 400 张不同读数的仪表盘图片。在测试集上,模型的预测误差在 3-5 度,效果尚可。但用实际拍摄的新图片进行预测时,误差却高达 30-40 度,甚至超出了训练数据的范围。

二、问题原因分析

预测结果不准确且超出范围,很可能是由以下几个原因综合导致的:

  1. 数据分布差异(Data Distribution Shift): 训练数据和实际应用场景的数据存在差异。比如,光照条件、摄像头位置的细微变化、背景中的反光等,都可能导致模型无法很好地泛化到新的数据。

  2. 过拟合(Overfitting): 模型在训练集上表现良好,但在新的、未见过的数据上表现差,说明模型可能过拟合了训练数据中的噪声或特定模式,而不是真正学习到了仪表盘读数和图像特征之间的关系。

  3. 训练数据不足或代表性不够: 400 张图片对于一个 CNN 模型来说可能不够,尤其是考虑到仪表盘读数的连续性和细微变化。数据集中可能缺少某些关键角度或光照条件下的样本。

  4. 网络结构过于简单 : 使用的网络结果比较简单, 特征提取能力不够.

三、解决方案

针对以上原因,我们可以采取以下几种方法来改进模型:

1. 数据增强与预处理

原理: 通过对训练数据进行各种变换,模拟实际应用中可能遇到的各种情况,增加数据的多样性,减少数据分布差异,提高模型的泛化能力。

操作步骤:

  • 更全面的数据增强: 除了 ColorJitter,还可以尝试以下变换:

    • RandomRotation:随机旋转图像一定角度,模拟摄像头位置的轻微变化。
    • RandomAffine:随机进行仿射变换(平移、缩放、剪切),进一步模拟摄像头角度变化。
    • GaussianBlur:模拟摄像头对焦不准的情况。
    • RandomPerspective:模拟拍摄角度的透视变化。
    • RandomEqualize : 增强对比度.
    data_transforms = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
        transforms.RandomRotation(degrees=15),  # 旋转最多 15transforms.RandomAffine(degrees=0, translate=(0.1, 0.1), scale=(0.9, 1.1), shear=10),
        transforms.GaussianBlur(kernel_size=3), #有大概率图像模糊,对结果影响较大
        transforms.RandomPerspective(distortion_scale=0.1, p=0.3),
        transforms.RandomEqualize(p=0.3), #增强对比度
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    
  • 图像裁剪与对齐: 由于摄像头位置可能稍微变动,确保每次拍摄的仪表盘都位于图像的同一位置非常重要。可以在预处理阶段加入裁剪步骤,只保留包含仪表盘的区域。这可以通过手动标注或者使用目标检测算法(如 YOLO)来实现。

2. 增加训练数据

原理: 更多的、更具代表性的训练数据可以帮助模型更好地学习仪表盘读数与图像特征之间的关系。

操作步骤:

  • 采集更多数据: 增加不同光照、角度、读数下的图片数量。尽量覆盖整个仪表盘的读数范围。
  • 数据标注: 对于新采集的数据,需要准确标注其对应的仪表盘读数。
  • 创建新的图片: 如果收集新的照片成本较大,可以通过图像处理软件通过手动增加调整现有图像(对比度,光线等),达到增加训练数据的目的。

3. 调整模型和训练策略

原理: 如果数据增强和预处理效果不明显,可能需要调整模型结构或训练策略。

操作步骤:

  • 使用更深的网络: 尝试使用更深的网络结构,如 ResNet、DenseNet 等。这些网络有更强的特征提取能力。注意,更深的网络需要更多的数据来避免过拟合。

    import torchvision.models as models
    model = models.resnet18(pretrained=True) #可以更换其他预训练model
    
    # 调整最后一层以适应你的输出(1 个输出节点)
    num_ftrs = model.fc.in_features
    model.fc = nn.Linear(num_ftrs, 1)
    
  • 迁移学习: 如果你的数据集相对较小,可以利用大型图像数据集上预先训练好的模型。加载预训练模型的权重, 然后用新的数据集做调整训练, 这样能够加快训练并提升表现.

  • 调整学习率(Learning Rate): 过大的学习率可能导致模型无法收敛,过小则可能导致训练过慢。可以尝试使用更小的学习率,或者使用学习率衰减策略(例如,每隔几个 epoch 就降低学习率)。

  • 调整 Batch Size: 不同的 Batch Size 可能会影响模型的训练效果。可以尝试不同的 Batch Size。

  • 早停(Early Stopping): 加入早停以防止过拟合.

# EarlyStopping 
patience = 20  # 等待多少个 epoch 没有提升就停止
best_val_loss = float('inf')
counter = 0

for epoch in range(num_epochs):
        #...  训练循环部分 ...
    
    # Validation loop (在 *每个 epoch* 之后进行验证)
    model.eval()
    val_loss = 0.0
    with torch.no_grad():
        for inputs, labels in val_dataloader:  # 使用验证集加载器
            outputs = model(inputs)
            loss = criterion(outputs.squeeze(), labels)
            val_loss += loss.item()

    val_loss /= len(val_dataloader)
    print(f'Validation Loss: {val_loss}')

    # 检查验证损失是否有所改善
    if val_loss < best_val_loss:
        best_val_loss = val_loss
        counter = 0  # 重置计数器
        torch.save(model.state_dict(), 'best_model.pth')  # 保存最佳模型
    else:
        counter += 1
        print(f'EarlyStopping counter: {counter} out of {patience}')

        if counter >= patience:
            print('Early stopping')
            break  # 停止训练循环

#在整个循环之后:
model.load_state_dict(torch.load('best_model.pth')) #载入最好的模型

  • L1 或 L2 正则化 : 通过加入L1,L2 来限制参数来避免过拟合。
    # 加入 L2 regularization (权重衰减)
    optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-5)
    

4. 对输出值进行后处理

原理 :即使最好的模型,在预测连续值时,也可能出现稍微偏离真值的情况。可以使用简单的后处理进一步提升稳定性。

方法 :

  • 限制范围: 如果实际仪表盘有一个已知的读数范围 (比如 20 度到 80 度), 可以将所有超出范围的预测都设置到边缘值.
def clip_predictions(prediction, min_val, max_val):
   return max(min_val, min(prediction, max_val))

predicted_value = predict_gauge_value(image_path, model, data_transforms)
final_prediction = clip_predictions(predicted_value, 20, 80)  # 假设范围为 20-80
print (f"Final Prediction: {final_prediction}")

5. 进阶:模型集成

原理: 集成多个模型的预测结果可以进一步提高预测的准确性和鲁棒性。

操作步骤:

  • 训练多个模型: 使用不同的随机种子、不同的数据增强方法、或者不同的网络结构,训练多个模型。

  • 平均预测结果: 对于新的输入图片,将多个模型的预测结果取平均值,作为最终的预测结果。

    def ensemble_predict(image_path, models, transform):
        predictions = []
        for model in models:
            model.eval()
            image = Image.open(image_path)
            image = transform(image).unsqueeze(0)
            with torch.no_grad():
                output = model(image)
            predictions.append(output.item())
        return sum(predictions) / len(predictions)  # 简单平均
    
    # 假设你有三个训练好的模型 model1, model2, model3
    models = [model1, model2, model3]
    final_prediction = ensemble_predict(image_path, models, data_transforms)
    
    

通过这些方法的组合使用, 并反复尝试,应该能够明显提高模型预测的准确性, 并让模型更适合部署和落地应用。