weizhepei / CasRel

A Novel Cascade Binary Tagging Framework for Relational Triple Extraction. Accepted by ACL 2020.
https://arxiv.org/abs/1909.03227
MIT License
767 stars 141 forks source link

您好,关于keras-bert tokenizer问题 #50

Open feiyunamy opened 3 years ago

feiyunamy commented 3 years ago

代码中使用了keras-bert里面的tokenizer,但是这个tokenize的表现好像有些特殊,例如:

    "text":"三国中的谋士很多,但是谋士也要分不同的类别,有的善于统筹全局,有的善于战术规划,有的善于外交连横,不过说实话,其中大部分知名谋士的结局都不太好,如:荀彧被曹操逼死,陆逊被孙权气死,就连大家最敬仰的诸葛亮也是被军国大事给累死,但是有一个谋士不但得到了善终,而且还位高权重,关键就在于他在生涯中的五次站队都成功了,我们来看看吧",
    "triple_list":[
        [
            "陆逊",
            "朝代",
            "三国"
        ]
    ]
}

这条数据,text就会被tokenize成,['[CLS]', '##三', '##国', ... , ‘[unused1]’, '[SEP]'], 对应的subject会被tokenize成,['##陆', '##逊', ‘[unused1]’],不知道是出于什么考虑还是只是bug?因为如果这样,在原始输入序列中无法找到subject与object对应的位置,就无法产生对应的标签。(bert-base-chinese, vocab也是bert自带的vocab.txt)

但是代码中在data_generator阶段似乎又有意规避了末尾的unused1标签?

                for triple in line['triple_list']:
                    # 下面这个 -1,少取了末尾的token,是某种特殊的tokenize机制还是只是bug???
                    triple = (self.tokenizer.tokenize(triple[0])[1:-1], triple[1], self.tokenizer.tokenize(triple[2])[1:-1])
                    sub_head_idx = find_head_idx(tokens, triple[0])
                    obj_head_idx = find_head_idx(tokens, triple[2])
                    if sub_head_idx != -1 and obj_head_idx != -1:
                        sub = (sub_head_idx, sub_head_idx + len(triple[0]) - 1)
                        if sub not in s2ro_map:
                            s2ro_map[sub] = []
                        s2ro_map[sub].append((obj_head_idx,
                                           obj_head_idx + len(triple[2]) - 1,
                                           self.rel2id[triple[1]]))

另外,keras-bert 0.80.0似乎无法使用。 我的环境如下

keras == 2.4.3
keras-bert == 0.81.1
tensorflow-gpu == 1.13.1

综上,我的问题如下:

  1. 代码中的tokenize机制是某种特殊方式还是说只是bug?
  2. 与transformers等包的常规tokenizer相比较,这种tokenize方式是否具有某种优势?
feiyunamy commented 3 years ago

噢 似乎self.tokenizer.tokenize(triple[0])[1:-1]是为了规避[SEP]与[CLS]token,那么这种tokenize表现就是bug?但是如果换一个tokenizer, 代码中预测部分,预测出来的结果也会少一个字,所以不是很清楚到底是出于何种考虑。

Phoeby2618 commented 3 years ago

问题1:代码中的tokenizer貌似是针对英文token的,对每个单词wordpiece,把单词之间的空格替换成[unused1]。如果是中文会出现你描述的情况,中文的tokenizer还需要改写下。

shm007g commented 3 years ago

同意 @Phoeby2618 的说法,我试了(1)把中文分割成带空格的类似英文的格式,用代码里面的HBTokenizer(2)中文用原文,tokenizer用原生的Tokenier加上[unused1],metric函数中把' '.join(sub.split('[unused1]'))也改过来了。(3)中文用原文,tokenizer用原生的Tokenier不加[unused1],metric同上。 前2者结果差不多。最后一种情况,pred的关系实体总是为0。应该是[unused1]不能随便去掉,暂时没搞清楚咋回事。

seokjin954 commented 3 years ago

我现在也发现了这个问题,打算试试您上面说的方法(2),不知您现在有没有更好的办法。

yanjiahui123 commented 3 years ago

这里的self.tokenizer.tokenize(triple[0])[1:-1]确实是为了规避开头的[CLS]标签和末尾的[SEP],这是函数内部拼接上去的,但是有个问题就是,如果实体token不在词典,那么该实体token就会被细分成多个token。

zjw-coder commented 1 year ago

各位大佬,能分享下处理中文数据的代码嘛,或者怎么修改