在 allennlp 中编写自定义指标

Posted

技术标签:

【中文标题】在 allennlp 中编写自定义指标【英文标题】:Writing custom metrics in allennlp 【发布时间】:2022-01-13 08:05:55 【问题描述】:

我正在写下我的第一个 allennlp 项目,以检测报纸文章中的特定跨度。我能够在我的数据集上训练它。用交叉熵计算的损失似乎正确减少,但我的指标有一些问题。我编写了一个自定义指标,它应该可以估计我的模型根据一些基本事实跨度预测跨度的准确度。问题是现在,即使损失在减少,我们的指标似乎也没有正确更新。

我不确定如何解决这个问题,我猜我的问题如下:

    reset() 函数在Metric 类中的具体用途是什么? 除了编写__call__()get_metric()reset()函数外,还有其他需要注意的地方吗?

下面是我的自定义Metric 类的快照,以备您需要时使用。

class SpanIdenficationMetric(Metric):
    def __init__(self) -> None:
        self._s_cardinality = 0 # S: model predicted spans
        self._t_cardinality = 0 # T: article gold spans
        self._s_sum = 0
        self._t_sum = 0
        
    def reset(self) -> None:
        self._s_cardinality = 0
        self._t_cardinality = 0
        self._s_sum = 0
        self._t_sum = 0
            
    def __call__(self, prop_spans: torch.Tensor, gold_spans: torch.Tensor, mask: Optional[torch.BoolTensor] = None):
        for i, article_spans in enumerate(prop_spans):
            if article_spans.numel() == 0:
                continue
            article_gold_spans = gold_spans[i]
            merged_prop_spans = self._merge_intervals(article_spans)
            self._s_cardinality += merged_prop_spans.size(dim=0)
            self._t_cardinality += article_gold_spans.size(dim=0)
            for combination in itertools.product(merged_prop_spans, article_gold_spans):
                sspan = combination[0]
                tspan = combination[1]
                self._s_sum += self._c_function(sspan, tspan, sspan[1].item() - sspan[0].item() + 1)
                self._t_sum += self._c_function(sspan, tspan, tspan[1].item() - tspan[0].item() + 1)

    def get_metric(self, reset: bool = False):
        precision = 0
        recall = 0
        if self._s_cardinality != 0:
            precision = self._s_sum / self._s_cardinality
        if self._t_cardinality != 0:
            recall = self._t_sum / self._t_cardinality
        if reset:
            self.reset()
        return  "si-metric" : (2 * precision * recall) / (precision + recall) if precision + recall > 0 else 0 

def _c_function(self, s, t, h): 
def _intersect(self, s, t): 
def _merge_intervals(self, prop_spans): 

提前谢谢你。干杯。

【问题讨论】:

【参考方案1】:

在训练期间,培训师将使用每个批次的结果调用指标(使用 Metric.__call__())。发生这种情况时,指标应该更新其内部状态。培训师希望在调用Metric.get_metric() 时获得指标的当前值。 Metric.reset() 必须将指标重置为一种状态,就好像它以前从未被调用过一样。当get_metric()reset = True 调用时,预计也会重置指标。

据我所知,您的代码正确地完成了所有这些事情。您的代码将无法在分布式设置中正确运行,但如果您没有在多个 GPU 上进行训练,这不是问题。

您所做的类似于 SQuAD 指标:https://github.com/allenai/allennlp-models/blob/main/allennlp_models/rc/metrics/squad_em_and_f1.py SQuAD 指标特意调用原始 SQuAD 评估代码,因此它比您想要的要复杂一些,但也许您可以调整它?主要区别在于您正在计算整个数据集的 F 分数,而 SQuAD 计算每个文档的 F 分数,然后跨文档取平均值。

最后,您可以为您的指标编写一个简单的测试,类似于 SQuAD 测试:https://github.com/allenai/allennlp-models/blob/main/tests/rc/metrics/squad_em_and_f1_test.py 这可能有助于缩小问题所在。

【讨论】:

非常感谢您的完整回答。现在清楚多了。我还想问你,我们应该朝哪个方向努力让我们的代码在分布式环境中运行? 正确编写分布式指标有点困难。最好看看 SQuAD 指标是如何做到的,然后从那里窃取代码。

以上是关于在 allennlp 中编写自定义指标的主要内容,如果未能解决你的问题,请参考以下文章

Prometheus 添加自定义指标

自定义指标未在普罗米修斯中公开

python使用numpy包编写自定义函数计算MAPE(平均绝对百分比误差)指标mean absolute percentage error (MAPE)MAPE指标解读MAPE指标使用的注意事项

Azure 指标警报中的自定义 Json 有效负载

使用 Keras 为损失函数构建自定义指标,但有错误

python使用numpy包编写自定义函数计算SMAPE(对称平均绝对百分比误差)指标Symmetric mean absolute percentage errorSMAPE指标解读指标使用的注