在 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 中编写自定义指标的主要内容,如果未能解决你的问题,请参考以下文章
python使用numpy包编写自定义函数计算MAPE(平均绝对百分比误差)指标mean absolute percentage error (MAPE)MAPE指标解读MAPE指标使用的注意事项
python使用numpy包编写自定义函数计算SMAPE(对称平均绝对百分比误差)指标Symmetric mean absolute percentage errorSMAPE指标解读指标使用的注