【扒代码】main.py

outline 

@torch.no_grad()
def evaluate(args):
    # 评估函数,用于在指定数据集上评估DAVE模型。

    # ...函数实现细节...

@torch.no_grad()
def eval_0shot(args):
    # 零样本评估函数,用于在没有见过的类别上评估DAVE模型。

    # ...函数实现细节...

@torch.no_grad()
def eval_0shot_multicat(args):
    # 零样本多类别评估函数,用于在零样本情况下评估DAVE模型对多个类别的性能。

    # ...函数实现细节...

@torch.no_grad()
def evaluate_LVIS(args):
    # LVIS数据集评估函数,用于在LVIS数据集上评估DAVE模型。

    # ...函数实现细节...

@torch.no_grad()
def evaluate_multicat(args):
    # 多类别评估函数,用于在已知类别上评估DAVE模型对多个类别的性能。

    # ...函数实现细节...

if __name__ == '__main__':
    # 主函数入口,根据命令行参数执行不同的评估任务。

    print("DAVE")
    parser = argparse.ArgumentParser('DAVE', parents=[get_argparser()])
    args = parser.parse_args()
    print(args)

    if args.task == 'lvis':
        evaluate_LVIS(args)
    # 如果任务是'lvis',调用evaluate_LVIS函数进行评估。

    elif not args.zero_shot and args.eval_multicat:
        evaluate_multicat(args)
    # 如果不是零样本学习且需要评估多类别,调用evaluate_multicat函数。

    elif args.zero_shot and args.eval_multicat:
        eval_0shot_multicat(args)
    # 如果是零样本学习且需要评估多类别,调用eval_0shot_multicat函数。

    elif args.zero_shot:
        eval_0shot(args)
    # 如果是零样本学习,调用eval_0shot函数。

    else:
        evaluate(args)
    # 其他情况,调用evaluate函数进行评估。
  • 这些函数使用PyTorch的DataParallel来包装DAVE模型,使其能够在多个GPU上并行训练。
  • 每个函数首先加载预训练的模型权重,然后根据提供的参数(如数据集路径、图像尺寸等)初始化数据集和数据加载器。
  • 使用model.eval()将模型设置为评估模式,以关闭dropout和batch normalization层的训练行为。
  • 评估过程中,模型对输入图像进行前向传播,生成预测结果,并将结果保存到predictions字典中。
  • 计算并打印评估指标,如平均绝对误差(MAE)和均方根误差(RMSE)。
  • 对于零样本评估函数,模型使用额外的策略来处理没有见过的类别,例如使用CLIP模型进行分类。
  • 最后,将评估结果保存到JSON文件中,以便于后续分析。

这些函数提供了一个完整的评估流程,从数据加载、模型推理、指标计算到结果保存。通过不同的函数,可以灵活地对DAVE模型在不同场景下的性能进行评估。

 

# 从boxes_xywh获取分数信息,boxes_xywh是转换为xywh格式的边界框对象。
scores = boxes_xywh.fields['scores']

# 遍历预测的边界框,对于每个边界框:
for i in range(len(boxes_pred[0].box)):
    # 获取第i个边界框的坐标。
    box = boxes_xywh.box[i]

    # 创建一个字典,用于存储当前边界框的注释信息。
    anno = {
        "id": anno_id,  # 为注释分配一个唯一的ID。
        "image_id": test.map_img_name_to_ori_id()[test.image_names[ids[0].item()]],  # 根据图像名称映射函数获取原始图像ID。
        "area": int(areas[0].item()),  # 将预测边界框的面积转换为整数。
        "bbox": [int(box[0].item()), int(box[1].item()), int(box[2].item()), int(box[3].item())],  # 将边界框的坐标转换为整数列表。
        "category_id": 1,  # 边界框所属的类别ID,这里固定为1。
        "score": float(scores[i].item()),  # 将预测边界框的分数转换为浮点数。
    }

    # 增加注释ID的计数器。
    anno_id += 1

    # 将当前边界框的注释信息添加到predictions字典的"annotations"列表中。
    predictions["annotations"].append(anno)

# 在循环结束后,将图像信息添加到predictions字典的"images"列表中。
predictions["images"].append(img_info)
  • 这段代码的作用是处理模型预测的边界框,并将每个边界框的详细信息格式化为注释,然后添加到predictions字典中。
  • boxes_xywh是一个包含预测边界框的BoxList对象,已经转换为xywh格式(即边界框的中心坐标(x, y)和宽高(w, h))。
  • scores变量存储了每个边界框对应的预测分数或置信度。
  • 通过遍历boxes_pred[0].box(预测的边界框列表),代码为每个边界框创建了一个注释字典anno,其中包含了边界框的唯一标识符id、所属图像的标识符image_id、边界框的面积area、边界框的坐标bbox、类别category_id和预测分数score
  • 每处理完一个边界框,就将其注释信息添加到predictions["annotations"]列表中,并且更新anno_id以确保每个注释都有一个唯一的ID。
  • 在处理完所有边界框后,将图像信息img_info添加到predictions["images"]列表中,这意味着完成了对当前图像所有预测边界框的记录。

 

# 将密度图在第1维(通道维)上展平,然后对第1维求和,得到每个位置的密度值总和。
# 接着,将预测输出out在指定的shape[1]和shape[2]尺寸上展平,并在第1维上求和。
# 计算这两个总和之间的绝对误差,并将误差的标量值添加到err列表中。
err.append(torch.abs(
    density_map.flatten(1).sum(dim=1) -
    out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)
).item())

# 计算边界框误差:将密度图在第1维上展平,然后对第1维求和,得到预测的总对象数。
# 将这个预测的总对象数与实际检测到的边界框数量(boxes_pred[0].box的长度)进行比较,
# 并取绝对值,将边界框误差添加到box_err列表中。
box_err.append(abs(
    density_map.flatten(1).sum(dim=1).item() -
    len(boxes_pred[0].box)
))

# 将密度图中预测的对象总数添加到num_objects列表中。
# 这是通过将密度图在第1维上展平后,对第1维求和得到的。
num_objects.append(density_map.flatten(1).sum(dim=1).item())
  • 这段代码的目的是计算模型预测的误差,并将这些误差存储到相应的列表中,以便后续分析和评估模型性能。
  • err列表用于存储密度图预测的误差。误差是通过比较密度图和模型输出之间的差异来计算的。这里使用的是绝对误差,即预测密度和实际密度之间的差异。
  • box_err列表用于存储边界框预测的误差,即模型预测的对象数量与实际检测到的对象数量之间的差异
  • num_objects列表用于存储从密度图中得到的预测的对象总数,这可以用于后续的评估和比较。
  • density_map是模型预测的密度图,表示在图像的每个位置预测的对象密度
  • out是模型的输出,这里假设它是一个四维张量,需要根据给定的shape尺寸进行展平和求和。
  • boxes_pred是模型预测的边界框列表,boxes_pred[0].box包含了所有预测边界框的坐标。
  • 使用.item()是将张量的值转换为Python的标量值,以便进行列表的累加操作。
# 计算绝对误差(ae),这是预测密度图与实际密度图之间的差异的累积。
# density_map.flatten(1).sum(dim=1) 首先将密度图在第1维(通道维)上展平,然后对展平后的结果求和,得到每个空间位置的密度值。
# out[:, :, :shape[1], :shape[2]] 表示模型输出out在指定的高和宽上的部分,即考虑了resize因素后的有效输出区域。
# 然后,将这部分输出展平(flatten)并在第1维上求和(sum),得到与密度图相对应的预测密度值。
# torch.abs计算这两个结果的绝对差值,然后调用.sum()对所有元素的绝对差值求和,得到总的绝对误差。
ae += torch.abs(
    density_map.flatten(1).sum(dim=1) -
    out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)
).sum()

# 计算平方误差(se),这是预测密度图与实际密度图之间差异的平方的累积。
# 与计算绝对误差类似,但这里使用(density_map.flatten(1).sum(dim=1) - out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)) ** 2计算元素级别的平方差。
# 这样做可以更强调误差较大的预测,因为平方操作会放大较大误差的影响。
# 最后,调用.sum()对所有元素的平方差求和,得到总的平方误差。
se += ((density_map.flatten(1).sum(dim=1) -
        out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)
        ) ** 2).sum()
  • 这两行代码的目的是分别计算模型预测的绝对误差和平方误差,并将它们累积到aese变量中。
  • 绝对误差(ae)提供了预测密度和实际密度之间的差异的一个简单度量,它是所有位置差异的总和。
  • 平方误差(se)是对预测误差的一种更敏感的度量,因为它通过平方操作放大了误差的影响。这使得模型在优化过程中更倾向于减少较大的误差。
  • 这些误差的累积值可以用于计算评估指标,如平均绝对误差(MAE)和均方根误差(RMSE),这些指标通常用于评估回归模型的性能。
  • density_map是真实密度图,表示图像中每个位置的对象密度。
  • out是模型的输出密度图,需要与实际密度图进行比较。
  • shape[1]shape[2]是resize后的图像尺寸,确保模型输出与真实密度图的尺寸一致性。
  • 通过这种方式,可以量化模型在预测对象密度方面的准确性,并为进一步的模型改进提供依据。

else:
    # 如果boxes_predicted的宽度或高度的平均值小于11,执行以下操作。

    scale_y = max(1, 11 / (boxes_predicted[:, 2] - boxes_predicted[:, 0]).mean())
    # 计算高度的缩放比例,使用max函数确保缩放比例不小于1。

    scale_x = max(1, 11 / (boxes_predicted[:, 3] - boxes_predicted[:, 1]).mean())
    # 计算宽度的缩放比例,使用max函数确保缩放比例不小于1。

    if scale_y > 1.9:
        scale_y = 1.9
    # 如果高度的缩放比例大于1.9,将其设置为1.9。

    if scale_x > 1.9:
        scale_x = 1.9
    # 如果宽度的缩放比例大于1.9,将其设置为1.9。

    scale_x = (int(args.image_size * scale_x) // 8 * 8) / args.image_size
    # 调整宽度的缩放比例,使其为8的倍数,以满足某些模型或硬件的要求。

    scale_y = (int(args.image_size * scale_y) // 8 * 8) / args.image_size
    # 调整高度的缩放比例,使其为8的倍数。

    resize_ = T.Resize((int(args.image_size * scale_x), int(args.image_size * scale_y)), antialias=True)
    # 创建一个调整图像大小的变换,使用指定的宽度和高度,开启抗锯齿。

    img_resized = resize_(img)
    # 应用调整图像大小的变换到图像img上。

    shape = img_resized.shape[1:]
    # 更新图像调整大小后的形状,用于后续操作。
  • 这段代码是条件分支的一部分,用于处理图像尺寸调整的情况。它根据预测边界框的平均宽度和高度来计算缩放比例。
  • scale_yscale_x分别表示图像高度和宽度的缩放比例,计算方式是将固定值11除以边界框的宽度和高度的平均值。
  • 使用max函数确保缩放比例不会小于1,即图像尺寸不会小于原始尺寸。
  • 如果缩放比例大于1.9,则将其限制在1.9以内,这可能是一种防止过度缩放的措施。
  • 然后,代码将缩放比例调整为8的倍数,这可能是为了满足某些深度学习模型或硬件对图像尺寸的要求。
  • 使用T.Resize创建一个图像尺寸调整的变换,并使用antialias=True参数开启抗锯齿,以保持图像质量。
  • 应用尺寸调整变换到原始图像img上,得到调整后的图像img_resized
  • 更新shape变量,存储调整后图像的形状,这通常用于后续的图像处理或模型输入。

这一过程是图像预处理的一部分,用于根据模型的需要调整图像尺寸,确保输入到模型中的图像具有合适的尺寸和质量。

if scale_x != 1.0 or scale_y != 1.0:
    # 如果x轴或y轴的缩放比例不为1,即图像尺寸被调整过,重新通过模型进行预测。
    out, aux, tblr, boxes_pred = model(img_resized, bboxes, test.image_names[ids[0].item()],
                                       classes=classes)

# 调用数据集的get_gt_bboxes方法获取真实边界框gt_bboxes和缩放因子resize_factors。
gt_bboxes, resize_factors = test.get_gt_bboxes(ids)

# 将boxes_pred转换为列表形式,以便进行后续处理。
boxes_pred = [boxes_pred]

# 根据缩放比例和缩放因子调整预测边界框boxes_pred的大小。
boxes_pred[0].box = boxes_pred[0].box / torch.tensor([scale_y, scale_x, scale_y, scale_x])
boxes_pred[0].box = boxes_pred[0].box * resize_factors[0]

# 计算调整后的预测边界框的面积。
areas = boxes_pred[0].area()

# 将预测边界框转换为xywh格式。
boxes_xywh = boxes_pred[0].convert("xywh")

# 构建图像信息字典,并准备添加到预测结果中。
img_info = {
    "id": test.map_img_name_to_ori_id()[test.image_names[ids[0].item()]],
    "file_name": "None",
}
# 将图像信息添加到predictions的"images"列表中。
predictions["images"].append(img_info)

# 获取预测边界框的分数信息。
scores = boxes_xywh.fields['scores']

# 遍历每个预测的边界框,构建注释信息。
for i in range(len(boxes_pred[0].box)):
    box = boxes_xywh.box[i]
    anno = {
        "id": anno_id,  # 为注释分配一个唯一的ID。
        "image_id": test.map_img_name_to_ori_id()[test.image_names[ids[0].item()]],
        "area": int(areas[i].item()),  # 边界框的面积。
        "bbox": [int(box[0].item()), int(box[1].item()), int(box[2].item()), int(box[3].item())],
        "category_id": 1,  # 边界框所属的类别ID。
        "score": float(scores[i].item()),  # 边界框的预测分数。
    }
    anno_id += 1  # 更新注释ID。
    # 将注释信息添加到predictions的"annotations"列表中。
    predictions["annotations"].append(anno)

# 计算绝对误差和平方误差,更新累积误差变量ae和se。
ae += torch.abs(density_map.flatten(1).sum(dim=1) - out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)).sum()
se += ((density_map.flatten(1).sum(dim=1) - out[:, :, :shape[1], :shape[2]].flatten(1).sum(dim=1)) ** 2).sum()
  • 这段代码是在评估过程中处理每个图像的预测结果,包括重新预测(如果图像尺寸被调整过)、获取真实边界框、调整预测边界框大小、计算误差,并构建预测结果的注释信息
  • 如果图像尺寸被调整,使用调整后的图像img_resized和对应的边界框bboxes通过模型进行预测,以获得新的预测结果。
  • 获取数据集中的真实边界框gt_bboxes和缩放因子resize_factors用于后续的误差计算和边界框调整。
  • 调整预测边界框boxes_pred的大小,以匹配原始图像尺寸,然后计算预测边界框的面积,并转换为xywh格式。
  • 构建图像信息字典img_info,并将其添加到预测结果中
  • 遍历每个预测的边界框,构建注释信息anno,包括边界框的ID、图像ID、面积、坐标、类别ID和预测分数,然后将注释信息添加到预测结果中。
  • 最后,计算模型输出和真实密度图之间的绝对误差和平方误差,更新累积误差变量aese,这些误差将用于评估模型的性能。

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值