在一个数据集检测中,会产生四类检测结果:TP、TN 、FP 、FN:
T(True):最终预测结果正确。
F(False):最后预测结果错误。
P(Positive):模型预测其是正样本(目标本身是狗,模型也预测它是狗)。
N(Negative):模型预测其是负样本(目标本身是狗,但模型预测它是个猫)。
TP:样本的真实类别是正样本(P),并且模型预测的结果也是正样本(P),预测正确(T)(目标本身是狗,模型也预测它是狗,预测正确)。
TN:样本的真实类别是负样本(N),并且模型将其预测成为负样本(N),预测正确(T)(目标本身不是狗,模型预测它不是狗,是个其他的东西,预测正确)。
FP:样本的真实类别是负样本(N),但是模型将其预测成为正样本(P),预测错误(F)(目标本身不是狗,模型预测它是狗,预测错误)。
FN:样本的真实类别是正样本(P),但是模型将其预测成为负样本(N),预测错误(F)(目标本身是狗,模型预测它不是狗,是个其他的东西,预测错误)。
TP+FP+TN+FN:样本总数。
TP+FN:实际正样本数。
TP+FP:预测结果为正样本的总数,包括预测正确的和错误的。
FP+TN:实际负样本数。
TN+FN:预测结果为负样本的总数,包括预测正确的和错误的
1. 召回率(Recall):
表示的是样本中的正例有多少被预测正确了(找得全)
所有正例中被正确预测出来的比例。
举个栗子:现有100只动物,分别是30只猫、50只狗和20只猪。经过模型检测之后结果表示只有25只猫,或许它把另外5只猫错认成狗和猪了吧,,那么召回率Recall(猫)= 25/30 = 83%
注:召回率越高,实际为正样本(P)被预测出来的概率越高,类似于“宁可错杀一千,绝不放过一个”。
2. 精确率(Precision):
表示的是预测为正的样本中有多少是真正的正样本(找得对)
。预测结果中真正的正例的比例。
举个栗子:现有100只动物,分别是30只猫、50只狗和20只猪。经过模型检测之后结果表示有35只猫,但它认为的35只猫里面有2只狗和3只猪,所以猫预测对的只有30只,那么精确率Precision(猫)= 30/35 = 85.7%
3. 准确率(Accuracy):
模型判断正确的数据(TP+TN)占总数据的比例
举个栗子:现有100只动物,分别是30只猫、50只狗和20只猪。经过模型检测之后预测正确的是20只猫、30只狗和10只猪,那么准确率(Accuracy)=(20+30+10)/100 = 60%。
注:通常来说正确率越高,模型越好。
Accuracy度量的是全局样本预测情况。而对于Precision和Recall而言,每个类都需要单独计算其Precision和Recall。
4. 漏检率:
反映分类器或者模型正确预测负样本纯度的能力,减少将正样本预测为负样本,即正样本被预测为负样本占总的正样本的比例。值越小,性能越好。
漏报率是衡量模型在正例中错误地预测为负例的能力。它表示实际为正例的样本中被错误预测为负例的比例。漏报率低表示模型在正例预测方面的性能较好。
5. 误检率:
反映分类器或者模型正确预测正样本纯度的能力,减少将负样本预测为正样本,即负样本被预测为正样本占总的负样本的比例。值越小,性能越好。
误报率是衡量模型在负例中错误地预测为正例的能力。它表示实际为负例的样本中被错误预测为正例的比例。误报率低表示模型在负例预测方面的性能较好。
计算代码
import os
def calculate_iou(box1, box2):
x1 = max(box1[0], box2[0])
y1 = max(box1[1], box2[1])
x2 = min(box1[0] + box1[2], box2[0] + box2[2])
y2 = min(box1[1] + box1[3], box2[1] + box2[3])
intersection = max(0, x2 - x1) * max(0, y2 - y1)
area1 = box1[2] * box1[3]
area2 = box2[2] * box2[3]
union = area1 + area2 - intersection
iou = intersection / union
return iou
def calculate_metrics(ground_truth_boxes, detected_boxes):
true_positives = 0 #真正例,预测为正例而且实际上也是正例;
false_negatives = 0 #假负例,预测为负例然而实际上却是正例;
false_positives = 0 #假正例,预测为正例然而实际上却是负例;
true_negatives = 0 # 真负例,预测为负例而且实际上也是负例。目标检测默认为0
# 遍历每个真实框
for truth_box in ground_truth_boxes:
found_match = False
# 遍历每个检测到的框
for detected_box in detected_boxes:
iou = calculate_iou(truth_box, detected_box) # 计算真实框和检测框的交并比
if iou >= 0.5 and truth_box[4] == detected_box[4]: # 如果交并比大于等于0.5且类别匹配
true_positives += 1 # 则增加真正例计数
found_match = True
break
if not found_match:
false_negatives += 1 # 如果未找到匹配的检测框,则增加假负例计数
unique_detected_classes = set(box[4] for box in detected_boxes) # 获取检测到的框中的唯一类别
unique_ground_truth_classes = set(box[4] for box in ground_truth_boxes) # 获取真实框中的唯一类别
for c in unique_detected_classes:
if c not in unique_ground_truth_classes:
false_positives += sum(box[4] == c for box in detected_boxes) # 计算假正例
for c in unique_ground_truth_classes:
if c not in unique_detected_classes:
false_negatives += sum(box[4] == c for box in ground_truth_boxes) # 根据未检测到的类别,增加假负例的计数
total_instances = true_positives + false_negatives + false_positives + true_negatives
accuracy = (true_positives + true_negatives) / total_instances if total_instances > 0 else 0 # 准确率计算
recall = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0 # 召回率计算
precision = true_positives / (true_positives + false_positives) if (true_positives + false_positives) > 0 else 0 # 精确率计算
miss_rate = false_negatives / (false_negatives + true_positives) if (false_negatives + true_positives) > 0 else 0 # 漏检率计算
false_positive_rate = false_positives / (false_positives + true_negatives) if (false_positives + true_negatives) > 0 else 0 # 误检率计算
return recall, miss_rate, false_positive_rate, precision, accuracy
def read_boxes_from_txt(file_path):
boxes = []
with open(file_path, 'r') as file:
for line in file:
values = line.strip().split()
box = [float(values[1]), float(values[2]), float(values[3]), float(values[4]), values[0]]
boxes.append(box)
return boxes
# 文件夹路径
ground_truth_folder = 'your/path' # 替换为实际的文件夹路径
detected_folder = 'your/path' # 替换为实际的文件夹路径
# 计算目标检测指标
total_recall = 0
total_miss_rate = 0
total_false_positive_rate = 0
total_precision = 0
total_accuracy = 0
num_images = 0
for filename in os.listdir(ground_truth_folder):
if filename.endswith(".txt"):
ground_truth_boxes = read_boxes_from_txt(os.path.join(ground_truth_folder, filename))
detected_boxes = read_boxes_from_txt(os.path.join(detected_folder, filename))
recall, miss_rate, false_positive_rate, precision, accuracy = calculate_metrics(ground_truth_boxes, detected_boxes)
total_recall += recall
total_miss_rate += miss_rate
total_false_positive_rate += false_positive_rate
total_precision += precision
total_accuracy += accuracy
num_images += 1
average_recall = total_recall / num_images
average_miss_rate = total_miss_rate / num_images
average_false_positive_rate = total_false_positive_rate / num_images
average_precision = total_precision / num_images
average_accuracy = total_accuracy / num_images
print("Average Recall: ", average_recall)
print("Average Miss Rate: ", average_miss_rate)
print("Average False Positive Rate: ", average_false_positive_rate)
print("Average Precision: ", average_precision)
print("Average Accuracy: ", average_accuracy)
import os
def calculate_iou(box1, box2):
x1 = max(box1[0], box2[0])
y1 = max(box1[1], box2[1])
x2 = min(box1[0] + box1[2], box2[0] + box2[2])
y2 = min(box1[1] + box1[3], box2[1] + box2[3])
intersection = max(0, x2 - x1) * max(0, y2 - y1)
area1 = box1[2] * box1[3]
area2 = box2[2] * box2[3]
union = area1 + area2 - intersection
iou = intersection / union
return iou
def calculate_metrics(ground_truth_boxes, detected_boxes, class_id):
"""
:param ground_truth_boxes:
:param detected_boxes:
:param class_id:
:return:
"""
true_positives = 0 # 真正例,预测为正例而且实际上也是正例;
false_negatives = 0 # 假负例,预测为负例然而实际上却是正例;
false_positives = 0 # 假正例,预测为正例然而实际上却是负例;
true_negatives = 0 # 真负例,预测为负例而且实际上也是负例。
# 遍历每个真实框
for truth_box in ground_truth_boxes:
found_match = False
# 遍历每个检测到的框
for detected_box in detected_boxes:
iou = calculate_iou(truth_box, detected_box) # 计算真实框和检测框的交并比
if iou >= 0.5 and truth_box[4] == detected_box[4] == class_id: # 如果交并比大于等于0.5且类别匹配
true_positives += 1 # 则增加真正例计数
found_match = True
break
if not found_match and truth_box[4] == class_id:
false_negatives += 1 # 如果未找到匹配的检测框,则增加假负例计数
for detected_box in detected_boxes: # 它的类别与 class_id 不匹配,并且它与所有真实框的类别都不匹配,则将其视为真负例
if detected_box[4] != class_id:
if all(detected_box[4] != box[4] for box in ground_truth_boxes):
true_negatives += 1 # 根据未匹配的检测框增加真负例计数
for detected_box in detected_boxes: # 其类别与 class_id 匹配,但与所有真实框的类别都不匹配,则将其视为假正例
if detected_box[4] == class_id:
if all(detected_box[4] != box[4] for box in ground_truth_boxes):
false_positives += 1 # 根据未匹配的检测框增加假正例计数
return true_positives, false_positives, false_negatives, true_negatives
def read_boxes_from_txt(file_path):
boxes = []
with open(file_path, 'r') as file:
for line in file:
values = line.strip().split()
box = [float(values[1]), float(values[2]), float(values[3]), float(values[4]), values[0]]
boxes.append(box)
return boxes
# 文件夹路径
ground_truth_folder = 'labels' # 标注标签
detected_folder = 'labels_detector' # 检测标签
# 获得真实标签文件和检测文件的列表
ground_truth_files = set(os.listdir(ground_truth_folder))
detected_files = set(os.listdir(detected_folder))
# 遍历所有的文件
all_files = ground_truth_files.union(detected_files)
total_TP = 0 # 真正例,预测为正例而且实际上也是正例;
total_FP = 0 # 假正例,预测为正例然而实际上却是负例;
total_FN = 0 # 假负例,预测为负例然而实际上却是正例;
total_TN = 0
# 遍历每个真实框
#"car", "lightTruck","person","tipperTruck","construction","tricycle", "train","bicycle"
class_id = '0'
#for filename in os.listdir(ground_truth_folder):
for filename in all_files:
if filename.endswith(".txt"):
ground_truth_path = os.path.join(ground_truth_folder, filename)
detected_path = os.path.join(detected_folder, filename)
ground_truth_boxes = read_boxes_from_txt(ground_truth_path) if os.path.exists(ground_truth_path) else []
detected_boxes = read_boxes_from_txt(detected_path) if os.path.exists(detected_path) else []
# recall, miss_rate, false_positive_rate, precision, accuracy = calculate_metrics(ground_truth_boxes, detected_boxes)
TP, FP, FN, TN = calculate_metrics(ground_truth_boxes, detected_boxes, class_id)
total_TP += TP
total_FP += FP
total_FN += FN
total_TN += TN
# 计算准确率、召回率、精确率、漏检率和误检率
total_instances = total_TP + total_FN + total_FP + total_TN
accuracy = (total_TP+total_TN) / total_instances if total_instances > 0 else 0
recall = total_TP / (total_TP + total_FN) if (total_TP + total_FN) > 0 else 0
precision = total_TP / (total_TP + total_FP) if (total_TP + total_FP) > 0 else 0
miss_rate = total_FN / (total_FN + total_TP) if (total_FN + total_TP) > 0 else 0
false_positive_rate = total_FP / (total_FP + total_TN) if (total_FP + total_TN) > 0 else 0
print("Average Recall: ", recall) # 召回率
print("Average Precision: ", precision) # 精确率
print("Average Accuracy: ", accuracy) # 准确率
print("Average Miss Rate: ", miss_rate) # 漏检率
print("Average False Positive Rate: ", false_positive_rate) # 误检率