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()
- 这两行代码的目的是分别计算模型预测的绝对误差和平方误差,并将它们累积到
ae
和se
变量中。 - 绝对误差(
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_y
和scale_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和预测分数,然后将注释信息添加到预测结果中。 - 最后,计算模型输出和真实密度图之间的绝对误差和平方误差,更新累积误差变量
ae
和se
,这些误差将用于评估模型的性能。