fastnlp / fastNLP

fastNLP: A Modularized and Extensible NLP Framework. Currently still in incubation.
https://gitee.com/fastnlp/fastNLP
Apache License 2.0
3.06k stars 450 forks source link

为什么BertEmbedding需要传入字典vocab? #280

Closed onebula closed 4 years ago

onebula commented 4 years ago

Bert不是自带一个字典么?能否直接加载使用这个字典呢? 如果修改了字典,那Bert的预训练权重可能意义不大了?

xuyige commented 4 years ago

在我们的BertEmbedding里面维护一个【传入vocab】和【bert自带字典】之间的映射关系 因此实际使用的时候与使用bert自带的字典是等价的 同样的,也可以加载bert自带的字典并将其作为vocab传入BertEmbedding

onebula commented 4 years ago

因为通常finetune的数据集是比较小的,基于这个数据集生成的vocab将小于bert自带的字典,那么不在传入的vocab里但是在bert自带字典中的字会怎么处理呢? 我注意到vocab.from_dataset有个参数no_create_entry_dataset,这个参数与此问题有关联么?并且,这个参数我看了说明还是没有理解,是否可以详细解释一下该参数取值与各类OOV的关系呢?以及推荐的用法?问题比较多,望解答,感谢!

yhcc commented 4 years ago

(1)vocab中没有包含,但是bert包含的词会被删除(想要为了节省一点显存,也会a little bit地加快索引的速度)。 (2)这个有点绕,但它确实是现在好多比较小的数据集要刷到sota的关键,我们也很无奈。我们数据中的一个词在预训练vector中(比如glove)的状态是:要么有对应的vector,要么没有。如果能够找到,我们直接用预训练的vector,这个应没有异议吧。但如果没有找到呢?要么我们使用unk来表示,要么我们随机初始化一个来表示它。如果这个没找到的词是训练集的中的,我们随机初始化是比较合理的(因为它会继续训练,可能也能够学得比较好);但如果这个词是只出现在dev或者test中,随机初始化是不是就没有意义了,它应该使用unk的vector。no_create_entry_dataset就是在告诉fastNLP哪些词可以随机初始化,哪些词需要用unk的vector表示。不知道这样表述会不会更清晰一点。

mazicwong commented 4 years ago

在我们的BertEmbedding里面维护一个【传入vocab】和【bert自带字典】之间的映射关系 因此实际使用的时候与使用bert自带的字典是等价的 同样的,也可以加载bert自带的字典并将其作为vocab传入BertEmbedding

我实验时,假设输入token均出现在vocab中,在只改变vocab情况下,观察到BertEmbedding对相同sentence的输出结果是不同的:) 而实际上,这应该是相同的? (如果按照您说的,自己写vocab的操作等价于加载整个bert的vocab,这种情况应该是不会发生的?)

yhcc commented 4 years ago

默认的BERTEmbedding应该是自带dropout的,可能需要设置成eval模式来关闭dropout。你设置下再试试看还有这个问题没。

mazicwong commented 4 years ago

默认的BERTEmbedding应该是自带dropout的,可能需要设置成eval模式来关闭dropout。你设置下再试试看还有这个问题没。

非常感谢您的提示。 我在bert_embedding.py中添加了self.encoder.eval()。该问题已经解决了:)

228 class _WordBertModel(nn.Module):
229     def __init__(self, model_dir_or_name: str, vocab: Vocabulary, layers: str = '-1', pool_method: str = 'first',
230                  include_cls_sep: bool = False, pooled_cls: bool = False, auto_truncate: bool = False, min_freq=2):
231         super().__init__()
232 
233         self.tokenzier = BertTokenizer.from_pretrained(model_dir_or_name)
234         self.encoder = BertModel.from_pretrained(model_dir_or_name)
235         self.encoder.eval() # close the dropout to fix the transformer embedding output
236         self._max_position_embeddings = self.encoder.config.max_position_embeddings
onebula commented 4 years ago

默认的BERTEmbedding应该是自带dropout的,可能需要设置成eval模式来关闭dropout。你设置下再试试看还有这个问题没。

非常感谢您的提示。 我在bert_embedding.py中添加了self.encoder.eval()。该问题已经解决了:)

228 class _WordBertModel(nn.Module):
229     def __init__(self, model_dir_or_name: str, vocab: Vocabulary, layers: str = '-1', pool_method: str = 'first',
230                  include_cls_sep: bool = False, pooled_cls: bool = False, auto_truncate: bool = False, min_freq=2):
231         super().__init__()
232 
233         self.tokenzier = BertTokenizer.from_pretrained(model_dir_or_name)
234         self.encoder = BertModel.from_pretrained(model_dir_or_name)
235         self.encoder.eval() # close the dropout to fix the transformer embedding output
236         self._max_position_embeddings = self.encoder.config.max_position_embeddings

可以使用 from fastNLP.core.predictor import Predictor 做预测,代码里给模型改成了eval模式