我正在尝试在 PyTorch 中本地实现宏 F1 分数(F-measure),而不是使用已经广泛使用的sklearn.metrics.f1_score https://scikit-learn.org/stable/modules/generated/sklearn.metrics.f1_score.html以便直接在 GPU 上计算测量值。
据我了解,为了计算宏 F1 分数,我需要计算所有标签的灵敏度和精度的 F1 分数,然后取所有这些的平均值。
我的尝试
我当前的实现如下所示:
def confusion_matrix(y_pred: torch.Tensor, y_true: torch.Tensor, n_classes: int):
conf_matrix = torch.zeros([n_classes, n_classes], dtype=torch.int)
y_pred = torch.argmax(y_pred, 1)
for t, p in zip(y_true.view(-1), y_pred.view(-1)):
conf_matrix[t.long(), p.long()] += 1
return conf_matrix
def forward(self, y_pred: torch.Tensor, y_true: torch.Tensor) -> torch.Tensor:
conf_matrix = confusion_matrix(y_pred, y_true, self.classes)
TP = conf_matrix.diag()
f1_scores = torch.zeros(self.classes, dtype=torch.float)
for c in range(self.classes):
idx = torch.ones(self.classes, dtype=torch.long)
idx[c] = 0
FP = conf_matrix[c, idx].sum()
FN = conf_matrix[idx, c].sum()
sensitivity = TP[c] / (TP[c] + FN + self.epsilon)
precision = TP[c] / (TP[c] + FP + self.epsilon)
f1_scores[c] += 2.0 * ((precision * sensitivity) / (precision + sensitivity + self.epsilon))
return f1_scores.mean()
self.classes
是标签的数量,self.epsilon
是一个非常小的值设置为10-e12
这可以防止DivisionByZeroError
.
训练时,我计算每批的测量值,并将所有测量值的平均值作为最终分数。
Problem
问题是,当我将自定义 F1 分数与 sklearn 宏 F1 分数进行比较时,它们很少相等。
# example 1
eval_cce 0.5203, eval_f1 0.8068, eval_acc 81.5455, eval_f1_sci 0.8023,
test_cce 0.4784, test_f1 0.7975, test_acc 82.6732, test_f1_sci 0.8097
# example 2
eval_cce 0.3304, eval_f1 0.8211, eval_acc 87.4955, eval_f1_sci 0.8626,
test_cce 0.3734, test_f1 0.8183, test_acc 85.4996, test_f1_sci 0.8424
# example 3
eval_cce 0.4792, eval_f1 0.7982, eval_acc 81.8482, eval_f1_sci 0.8001,
test_cce 0.4722, test_f1 0.7905, test_acc 82.6533, test_f1_sci 0.8139
虽然我尝试扫描互联网,但大多数情况都涉及二进制分类。我还没有找到一个例子来尝试做我想做的事情。
我的问题
我的尝试有什么明显的问题吗?
更新(2020年6月10日)
我还没有弄清楚我的错误。由于时间限制,我决定只使用 sklearn 提供的 F1 宏分数。虽然它不能直接与 GPU 张量一起工作,但无论如何对于我的情况来说它已经足够快了。
然而,如果有人能够解决这个问题,那就太棒了,这样任何其他可能偶然发现这个问题的人都可以解决他们的问题。