请教SRL的BIO模型在CoNLL05 benchmark上的复现性能

您好,观察到HanLP当中包含了SpanBIO模型的复现代码,直接采用的是Biaffine+CRF的方式
这和一些论文中采用的方法不太一样,例如Shi et al. 2019对每个谓词对应的序列都输入到encoder编码一次,Xu et al. 2015等人的工作没有使用Biaffine等等
由于我自己复现的BIO Biaffine+CRF的模型性能不太理想,因此想请教您是否观察过HanLP在CoNLL05 benchmark上的复现性能?
尤其是w/o & w/ gold predicate的场景,以及w/ BERT的场景
谢谢!

这么做开销很大。

我简单试验了一下,没有仔细调参。使用bert-base-cased,w/o gold predicate,在conll05 in-domain(wsj)的性能如下:

P: 86.26% R: 87.14% F1: 86.70%

在out-of-domain(brown)上的性能如下:

P: 77.55% R: 78.65% F1: 78.10%

根据Li et al. (2019)的数据,w/o gold predicate比w/ gold predicate低3个点。所以估计HanLP在w/ gold predicate情况下的性能大约是89.7和81.1,是超过Shi et al. 2019的88.1和80.9的。

训练日志:

log.zip (2.8 KB)

训练代码:

from hanlp.components.srl.span_bio.span_bio import SpanBIOSemanticRoleLabeler
from hanlp.layers.embeddings.contextual_word_embedding import ContextualWordEmbedding

srl = SpanBIOSemanticRoleLabeler()
save_dir = 'data/model/conll05/srl_english_uncased'
srl.fit(
    'data/srl/conll05/train.english.conll05.jsonlines',
    'data/srl/conll05/dev.english.conll05.jsonlines',
    save_dir,
    embed=ContextualWordEmbedding(
        'token',
        'bert-base-uncased',
        average_subwords=True,
        max_sequence_length=512,
        word_dropout=.2,
    ),
    lr=1e-3,
    transformer_lr=5e-5,
    epochs=30,
    gradient_accumulation=1,
    crf=True,
)
srl.load(save_dir)
srl.evaluate('data/srl/conll05/test_wsj.english.conll05.jsonlines', save_dir)
srl.evaluate('data/srl/conll05/test_brown.english.conll05.jsonlines', save_dir)
print(f'Model saved in {save_dir}')

数据预处理:

bert-base-uncased还要高一个点:

P: 86.76% R: 87.35% F1: 87.05%
P: 79.02% R: 79.24% F1: 79.13%

感谢详细的回复
我会仔细对比下我的代码和HanLP的差异,后面有结论我会在这里贴一下
另外上面的结果基于的似乎是HanLP里的SpanF1 metric?这应该和其他论文里使用的CoNLL05 SRL脚本不太一致,我个人观察Span F1应该会高0.6左右
不过HanLP中的复现也非常高了,相信在正确谓词下应该可能超过Shi et al. 2019,Shi et al. 2020还有Zhang et al. 2020汇报的结果
如果没有debug成功我会直接采用HanLP的代码跑出一些baseline的结果(十分欣赏HanLP优雅全面的实现
谢谢!

1 Like

其实HanLP实现了所谓的官方SRL评测(暂时仅SpanRankingSemanticRoleLabeler支持),记得跟Span F1误差约0.1个点:


误差没有0.6那么大,不知道你指的是不是seqeval包里的f1,它其实是label f1,那个误差挺大的。
官方评测脚本写得很烂,交换pred与gold得到的F1居然不一样。我看的上一篇paper是采用交换之后的平均值作为最终分值。后来除非对方注明使用了conll脚本,否则我是不愿意用的,毕竟再官方也是错误的。

不知道你指的是不是seqeval包里的f1。

是的,这个脚本应该是专为gold predicate场景设计的,评价gold predicate没有问题,但是不适合现在end-to-end方法评价。这是因为那个脚本评价总是依赖于正确谓词,如果错了直接丢弃全部arguments。因此end-to-end下,R值总是正确的,但是P值会偏高。
最近的工作大多是采取迂回的方式,采用gold/pred交换后评价出的两列R值分别作为正确的P值和R值(例外的是LISA,只评价了一次,导致P偏高,R正确,结果较真实的F值82.51高了快一个点)。
我个人也感觉直接Span F1比较好,但是大部分工作都汇报的是SRLEVAL的结果,有点积重难返。曾经想尝试将这个转化为python代码,但是尝试了发现他内部采取了非常多的特殊处理(唯一我感觉到的是c-prefix,即C-X跟着X视为一个X),因此也搞不下去了,非常难和这个脚本输出的数值对齐。

我定位到了代码的bug(偶然因素全0初始化的转移矩阵未加入优化器),目前复现出来的结果(使用srleval评价):
w/o gold predicate

P: 85.90% R: 88.41% F: 87.13%

w/ gold predicate

P: 88.05% R: 88.48% F: 88.27%

感觉在不使用词向量和LSTM的场景下已经和Shi et al. 2019差不多了。
另外由于参考了HanLP的实现,对其中的一些代码有一点小疑问


这里没理解错的话,是对每个候选谓词都做完整viterbi解码,根据解码出来的结果判断对应位置是否是谓词是吗(训练同理)?

这里要使用CRFlog_softmax应该是多加了?

学习了,感谢指出。

我个人觉得SRL从最初任务的设计和评估,到后来主流span-based抛弃了constituency的做法都存在许多历史问题,所以没有深入研究。

是的。训练的话,不管有没有谓词,都有相应的loss。

可有可无吧,HanLP使用的CRF实现里面对emission score是直接相加的,所以提前log可能比较stable?

去掉之后似乎变差了一点点。

P: 86.96% R: 87.17% F1: 87.07%
P: 78.49% R: 79.10% F1: 78.80%
1 Like

@hankcs Hi,我试了试用span-based方法训练。w/o gold predicate训练正常,但是w/ gold predicate训练失败了,这应该是bug?
以下是报错信息

Epoch 1 / 10:
Traceback (most recent call last):
  File "span.py", line 46, in <module>
    srl.fit(
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_rank.py", line 349, in fit
    return super().fit(**merge_locals_kwargs(locals(), kwargs))
  File "/home/yzhang/HanLP/hanlp/common/torch_component.py", line 287, in fit
    return self.execute_training_loop(**merge_dict(config, trn=trn, dev=dev, epochs=epochs, criterion=criterion,
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_rank.py", line 98, in execute_training_loop
    self.fit_dataloader(trn, criterion, optimizer, metric, logger,
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_rank.py", line 126, in fit_dataloader
    output_dict = self.feed_batch(batch)
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_rank.py", line 376, in feed_batch
    output_dict = self.model(batch)
  File "/home/yzhang/anaconda3/lib/python3.8/site-packages/torch/nn/modules/module.py", line 889, in _call_impl
    result = self.forward(*input, **kwargs)
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_ranking_srl_model.py", line 488, in forward
    return self.decoder.decode(batch, contextualized_embeddings, sent_lengths, masks, gold_arg_starts, gold_arg_ends,
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_ranking_srl_model.py", line 427, in decode
    pred_emb = self.batch_index_select(candidate_pred_emb,
  File "/home/yzhang/HanLP/hanlp/components/srl/span_rank/span_ranking_srl_model.py", line 240, in batch_index_select
    .view(indices.size()[0], indices.size()[1], -1)
IndexError: tuple index out of range

以下是训练脚本

from hanlp.components.srl.span_rank.span_rank import SpanRankingSemanticRoleLabeler
from hanlp.layers.embeddings.contextual_word_embedding import ContextualWordEmbedding


for seed in range(4):
    srl = SpanRankingSemanticRoleLabeler()
    save_dir = 'model'
    srl.fit(
        'data/srl/conll05/train.english.conll05.jsonlines',
        'data/srl/conll05/dev.english.conll05.jsonlines',
        save_dir,
        embed=ContextualWordEmbedding(
            'token',
            'bert-large-cased',
            average_subwords=True,
            max_sequence_length=512,
            word_dropout=.1
        ),
        context_layer=None,  # 代码做了一点修改,去掉了context_layer
        transformer_lr=5e-5,
        batch_max_tokens=1000,
        epochs=10,
        gradient_accumulation=1,
        official=True,
        lexical_dropout=0.1,
        loss_reduction='mean',
        use_gold_predicates=True,
        seed=seed
    )
    srl.load(save_dir)
    srl.evaluate('data/srl/conll05/dev.english.conll05.jsonlines', save_dir, official=True)
    srl.evaluate('data/srl/conll05/test_wsj.english.conll05.jsonlines', save_dir, official=True)
    srl.evaluate('data/srl/conll05/test_brown.english.conll05.jsonlines', save_dir, official=True)

感谢反馈,已经修复:

1 Like