路面预瞄与路面分割项目

精准分割路面区域,优化深度学习模型部署

1. 减速带与井盖语义分割任务挑战

1.1 算力资源约束

  • TDA4平台资源受限
  • 模型复杂度与参数量需严格控制
  • 浮点模型与定点模型一致性对齐

1.2 目标特征挑战

  • 尺度失衡:场景尺度大而目标尺寸小(目标像素占比<1%)
  • 特征模糊:目标形状复杂,目标-背景纹理相似度高
案例图片4
井盖在全图的像素占比
案例图片5
井盖和地面分割结果可视化

1.3 光照条件和遮挡问题

光照条件

  • 白天:顺光、逆光、侧光
  • 阴影与积水路面
  • 室内:地下车库
  • 夜间:强光、弱光环境

遮挡问题

  • 目标像素占比小
  • 严重遮挡干扰
案例图片1
常见的水渠井盖
案例图片2
若纹理条件下的井盖
案例图片3
和井盖高度相似的水渍或者油渍

2. 模型架构设计

模型架构
模型架构图

3. 损失函数

OhemCrossEntropy Loss

自适应选择损失值较高的困难样本进行训练,提升模型对难例的学习能力。该损失函数通过对样本进行排序,仅保留损失较大的top-k个样本参与反向传播。

计算公式:

L = -∑(yilog(pi)) where i ∈ top-k highest losses

        class OhemCrossEntropy(nn.Module):
            def __init__(self, thresh=0.7, min_kept=100000):
                super(OhemCrossEntropy, self).__init__()
                self.thresh = thresh
                self.min_kept = min_kept
                self.criterion = nn.CrossEntropyLoss(ignore_index=-1)
            
            def forward(self, pred, target):
                # 计算每个像素的损失
                pixel_losses = self.criterion(pred, target).view(-1)
                # 选择 top-k 损失值
                sorted_losses, indices = torch.sort(pixel_losses, descending=True)
                if len(sorted_losses) < self.min_kept:
                    kept_losses = sorted_losses
                else:
                    kept_losses = sorted_losses[:self.min_kept]
                return kept_losses.mean()
                            

Lovasz Loss

直接优化IoU评估指标的凸近似,特别适合处理语义分割任务中的边界区域。通过最小化Lovász扩展来优化Jaccard指数。

计算公式:

L = ∑c wcc(m(c))

其中 l̃c 是Lovász扩展,m(c)是类别c的错误向量


        class LovaszLoss(nn.Module):
            def __init__(self, classes='present', per_image=False):
                super(LovaszLoss, self).__init__()
                self.classes = classes
                self.per_image = per_image
                
            def forward(self, logits, labels):
                lovasz_loss = 0
                for cls in range(logits.shape[1]):
                    cls_pred = logits[:,cls]
                    cls_target = (labels == cls).float()
                    errors = (cls_pred - cls_target).abs()
                    # 计算Lovász扩展
                    sorted_errors, perm = torch.sort(errors, dim=0, descending=True)
                    intersection = cls_target[perm].cumsum(0)
                    union = cls_target.sum() + (1 - cls_target)[perm].cumsum(0)
                    iou = intersection / union
                    lovasz_loss += (sorted_errors * (1 - iou)).mean()
                return lovasz_loss
                            

Tversky Loss

Dice Loss的一般化形式,通过α和β参数灵活调节假阳性和假阴性的权重,特别适合处理类别不平衡问题。

计算公式:

T = ∑(pngn) / (∑(pngn) + α∑(pn(1-gn)) + β∑((1-pn)gn))

        class TverskyLoss(nn.Module):
            def __init__(self, alpha=0.3, beta=0.7):
                super(TverskyLoss, self).__init__()
                self.alpha = alpha
                self.beta = beta
                
            def forward(self, pred, target):
                # 计算真阳性、假阳性和假阴性
                TP = (pred * target).sum()
                FP = (pred * (1-target)).sum()
                FN = ((1-pred) * target).sum()
                # Tversky指数
                tversky = TP / (TP + self.alpha*FP + self.beta*FN)
                return 1 - tversky
                            

Focal Loss

通过动态缩放因子降低易分类样本的权重,提升难分类样本的重要性,有效处理类别不平衡问题。

计算公式:

FL(pt) = -α(1-pt)γlog(pt)

        class FocalLoss(nn.Module):
            def __init__(self, alpha=0.25, gamma=2):
                super(FocalLoss, self).__init__()
                self.alpha = alpha
                self.gamma = gamma
                
            def forward(self, pred, target):
                # 计算交叉熵
                ce_loss = F.binary_cross_entropy_with_logits(pred, target, reduction='none')
                # 计算pt
                pt = torch.exp(-ce_loss)
                # 计算focal loss
                focal_loss = self.alpha * (1-pt)**self.gamma * ce_loss
                return focal_loss.mean()
                            

RMI Loss

通过建模像素间的互信息来捕获空间依赖关系,提升分割结果的区域一致性和语义连贯性。

计算公式:

RMI = -log|Σ| + log|Σg| + log|Σp|

其中Σ为联合协方差矩阵,Σg和Σp为边缘协方差矩阵


        class RMILoss(nn.Module):
            def __init__(self, radius=3):
                super(RMILoss, self).__init__()
                self.radius = radius
                
            def forward(self, pred, target):
                # 提取局部区域
                n = (2*self.radius + 1)**2
                local_pred = F.unfold(pred, 2*self.radius+1)
                local_target = F.unfold(target, 2*self.radius+1)
                
                # 计算协方差矩阵
                joint_cov = torch.matmul(local_pred, local_target.transpose(-1,-2)) / n
                pred_cov = torch.matmul(local_pred, local_pred.transpose(-1,-2)) / n
                target_cov = torch.matmul(local_target, local_target.transpose(-1,-2)) / n
                
                # 计算RMI
                rmi = -torch.logdet(joint_cov) + torch.logdet(pred_cov) + torch.logdet(target_cov)
                return rmi.mean()
                            

4. 模型效果评估与可视化

指标图1
指标图1
指标图2
指标图2
指标图3
指标图3
可视化1:实际摆拍场景下的模型预测效果
可视化2:实际场景+摆拍易误检的非标场景
可视化3: 在实际复杂困难场景下的表现
可视化4:分割高程效果展示

5. 模型训练和板端部署

5.1 浮点分割模型训练

标准训练流程包含以下步骤:

  • 前向传播:输入图像数据,通过深度学习模型计算预测结果。
  • 计算损失:根据预测值与真实标签计算损失函数。
  • 反向传播:通过梯度下降算法更新模型参数。

5.2 QAT训练(量化感知训练)

QAT 训练的详细步骤:

  • 前向传播:插入量化器和反量化器,模拟量化过程,计算模型输出。
  • 计算损失:根据量化后的输出和标签计算损失。
  • 反向传播:通过梯度更新模型参数,以优化量化后的性能。
  • 保存量化参数:保存缩放因子等参数,用于低比特推理。

QuantTrainModule 的运行逻辑

  1. 初始化配置:接收量化配置参数,设置量化策略。
  2. 前向传播:更新计数器、调整量化位宽,调用量化层进行模拟量化。
  3. 模型手术量化:递归替换原始层为量化版本(如 QuantTrainConv2d)。
  4. 清除量化状态:清除统计信息,确保训练一致性。
QAT流程图
QAT流程图

5.3 板端部署阶段

  1. TIDL RT 环境准备
    • 创建运行时句柄。
    • 配置输入输出张量参数:数据布局、数据类型、内存类型。
  2. 推理流程
    • 数据预处理:读取图像、初始化缓存、标准化处理。
    • 模型推理:调用 TIDLRT_invoke 执行推理。
    • 结果后处理:输出解析并统计性能。