Skip to content

Commit 2e7b2f6

Browse files
committed
bert 未登录词 处理
1 parent d2d4a98 commit 2e7b2f6

File tree

4 files changed

+410
-0
lines changed

4 files changed

+410
-0
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
- [5.4 【关于 早停法 EarlyStopping 】那些你不知道的事](#54-关于-早停法-earlystopping-那些你不知道的事)
7272
- [5.5 【关于 标签平滑法 LabelSmoothing 】那些你不知道的事](#55-关于-标签平滑法-labelsmoothing-那些你不知道的事)
7373
- [5.6 【关于BERT如何处理篇章级长文本】那些你不知道的事](#56-关于bert如何处理篇章级长文本那些你不知道的事)
74+
- [5.7 【关于 Bert 未登录词处理】 那些你不知道的事](#57-关于-bert-未登录词处理-那些你不知道的事)
7475
- [六、【关于 Python 】那些你不知道的事](#六关于-python-那些你不知道的事)
7576
- [七、【关于 Tensorflow 】那些你不知道的事](#七关于-tensorflow-那些你不知道的事)
7677

@@ -1318,6 +1319,24 @@
13181319
- [3.4.1 从 模型角度 处理 介绍](#341-从-模型角度-处理-介绍)
13191320
- [3.4.2 从 模型角度 处理 模型思路介绍](#342-从-模型角度-处理-模型思路介绍)
13201321

1322+
#### 5.7 [【关于 Bert 未登录词处理】 那些你不知道的事](Trick/Bert_UNK_process/)
1323+
1324+
- 动机
1325+
- 中文预训练BERT 对于 英文单词覆盖不全问题。由于 中文预训练BERT 主要针对中文,所以词表中英文单词比较少,但是一般英文单词如果简单的直接使用tokenize函数,往往在一些序列预测问题上存在一些对齐问题,或者很多不存在的单词或符号没办法处理而直接使用 unk 替换了,某些英文单词或符号失去了单词的预训练效果;
1326+
- 专业领域(如医疗、金融)用词或中文偏僻词问题。NLP经常会用到预训练的语言模型,小到word2vector,大到bert。现在bert之类的基本都用char级别来训练,然而由于 Bert 等预训练模型都是采用开放域的语料进行预训练,所以对词汇覆盖的更多是日常用词,专业用词则覆盖不了,这时候该怎么处理?
1327+
- 方法
1328+
- 方法一:直接在 BERT 词表 vocab.txt 中替换 [unused]
1329+
- 方法二:通过重构词汇矩阵来增加新词
1330+
- 方法三:添加特殊占位符号 add_special_tokens
1331+
- 方法对比
1332+
- 方法一:
1333+
- 优点:如果存在大量领域内专业词汇,而且已经整理成词表,可以利用该方法批量添加;
1334+
- 缺点:因为 方法1 存在 未登录词数量限制(eg:cased模型只有99个空位,uncased模型有999个空位),所以当 未登录词 太多时,将不适用;
1335+
- 方法二:
1336+
- 优点:不存在 方法1 的 未登录词数量限制 问题;
1337+
- 方法三:
1338+
- 优点:对于一些 占位符(eg:<e></e>),方法一和方法二可能都无法生效,因为 <, e, >和 <e></e>均存在于 vocab.txt,但前三者的优先级高于 <e></e>,而 add_special_tokens会起效,却会使得词汇表大小增大,从而需另外调整模型size。但是,如果同时在词汇表vocab.txt中替换[unused],同时 add_special_tokens,则新增词会起效,同时词汇表大小不变。
1339+
13211340
### 六、[【关于 Python 】那些你不知道的事](python/)
13221341

13231342
- [【关于 Python 】那些你不知道的事](python/)
Loading

Trick/Bert_UNK_process/readme.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# 【关于 Bert 未登录词处理】 那些你不知道的事
2+
3+
> 作者:杨夕
4+
>
5+
> 介绍:研读顶会论文,复现论文相关代码
6+
>
7+
> NLP 百面百搭 地址:https://github.com/km1994/NLP-Interview-Notes
8+
>
9+
> **[手机版NLP百面百搭](https://mp.weixin.qq.com/s?__biz=MzAxMTU5Njg4NQ==&mid=100005719&idx=3&sn=5d8e62993e5ecd4582703684c0d12e44&chksm=1bbff26d2cc87b7bf2504a8a4cafc60919d722b6e9acbcee81a626924d80f53a49301df9bd97&scene=18#wechat_redirect)**
10+
>
11+
> 推荐系统 百面百搭 地址:https://github.com/km1994/RES-Interview-Notes
12+
>
13+
> **[手机版推荐系统百面百搭](https://mp.weixin.qq.com/s/b_KBT6rUw09cLGRHV_EUtw)**
14+
>
15+
> 搜索引擎 百面百搭 地址:https://github.com/km1994/search-engine-Interview-Notes 【编写ing】
16+
>
17+
> NLP论文学习笔记:https://github.com/km1994/nlp_paper_study
18+
>
19+
> 推荐系统论文学习笔记:https://github.com/km1994/RS_paper_study
20+
>
21+
> GCN 论文学习笔记:https://github.com/km1994/GCN_study
22+
>
23+
> **推广搜 军火库**https://github.com/km1994/recommendation_advertisement_search
24+
![](other_study/resource/pic/微信截图_20210301212242.png)
25+
26+
> 手机版笔记,可以关注公众号 **【关于NLP那些你不知道的事】** 获取,并加入 【NLP && 推荐学习群】一起学习!!!
27+
28+
> 注:github 网页版 看起来不舒服,可以看 **[手机版NLP论文学习笔记](https://mp.weixin.qq.com/s?__biz=MzAxMTU5Njg4NQ==&mid=100005719&idx=1&sn=14d34d70a7e7cbf9700f804cca5be2d0&chksm=1bbff26d2cc87b7b9d2ed12c8d280cd737e270cd82c8850f7ca2ee44ec8883873ff5e9904e7e&scene=18#wechat_redirect)**
29+
30+
## 一、动机
31+
32+
- 中文预训练BERT 对于 英文单词覆盖不全问题。由于 中文预训练BERT 主要针对中文,所以词表中英文单词比较少,但是一般英文单词如果简单的直接使用tokenize函数,往往在一些序列预测问题上存在一些对齐问题,或者很多不存在的单词或符号没办法处理而直接使用 unk 替换了,某些英文单词或符号失去了单词的预训练效果;
33+
- 专业领域(如医疗、金融)用词或中文偏僻词问题。NLP经常会用到预训练的语言模型,小到word2vector,大到bert。现在bert之类的基本都用char级别来训练,然而由于 Bert 等预训练模型都是采用开放域的语料进行预训练,所以对词汇覆盖的更多是日常用词,专业用词则覆盖不了,这时候该怎么处理?
34+
35+
```s
36+
...
37+
print(tokenizer.tokenize('骨骺'))
38+
>>>
39+
['骨', '[UNK]']
40+
```
41+
42+
- 英文预训练BERT(bert-base-uncased 和 bert-base-cased)词语被自动拆成词根词缀问题。 英文预训练BERT 以词为单位。社会生活中总是会有新词产生,而且在专业领域(如医疗、金融)有一些不常用的词语是 英文预训练BERT 没有涵盖到的,可能词语会被自动拆成词根词缀;
43+
44+
```s
45+
...
46+
print(tokenizer.tokenize('COVID'))
47+
>>>
48+
['co', '##vi', '##d']
49+
```
50+
51+
## 二、处理方法
52+
53+
### 2.1 直接在 BERT 词表 vocab.txt 中替换 [unused]
54+
55+
1. 找到pytorch版本的 bert-base-cased 的文件夹中的vocab.txt文件;
56+
2. 最前面的100行都是[unused][PAD]除外),直接用需要添加的词替换进去;
57+
58+
> 注:这里我们 加入 骺 和 COVID 两个新词
59+
60+
![](img/微信截图_20220917161347.png)
61+
62+
3. 查询 运行 之前实例,效果如下:
63+
64+
```s
65+
...
66+
print(tokenizer.tokenize('骨骺'))
67+
>>>
68+
['骨', '骺']
69+
```
70+
71+
```s
72+
...
73+
print(tokenizer.tokenize('COVID'))
74+
>>>
75+
['covid']
76+
```
77+
78+
### 2.2 通过重构词汇矩阵来增加新词
79+
80+
1. 如果 觉得 修改 vocab.txt文件 会干扰到 其他项目,那么通过重构BERT初始权重矩阵的方式将他们加入词表;
81+
82+
```s
83+
new_tokens = ['COVID', '骨骺']
84+
num_added_toks = tokenizer.add_tokens(new_tokens)
85+
print(tokenizer.tokenize('骨骺'))
86+
print(tokenizer.tokenize('COVID'))
87+
>>>
88+
['骨骺']
89+
['covid']
90+
```
91+
92+
> 注:num_added_toks返回一个数,表示加入的新词数量,在这里是2
93+
94+
2. resize_token_embeddings输入的参数是tokenizer的新长度
95+
96+
```s
97+
model.resize_token_embeddings(len(tokenizer))
98+
```
99+
100+
3. 添加后的词汇,通过model.resize_token_embeddings方法,随机初始化了一个权重。
101+
102+
```s
103+
tokenizer.save_pretrained("Pretrained_LMs/bert-base-cased")
104+
```
105+
106+
### 2.3 添加特殊占位符号 add_special_tokens
107+
108+
1. 动机
109+
110+
前面两个是往 词典中 添加未登录词,那么如何往分词器tokenizer中添加新的特殊占位符呢?
111+
112+
2. 方法介绍
113+
114+
add_special_tokens : 往分词器tokenizer中添加新的特殊占位符
115+
116+
```s
117+
tokenizer.add_special_tokens({'additional_special_tokens':["<e>"]})
118+
```
119+
120+
> 注:往additional_special_tokens这一类tokens中添加特殊占位符<e>
121+
122+
3. 测试
123+
124+
```s
125+
...
126+
text = " I love <e> ! "
127+
# 对于一个句子,首尾分别加[CLS]和[SEP]。
128+
text = "[CLS] " + text + " [SEP]"
129+
# 然后进行分词
130+
print("普通 分词效果:")
131+
tokenized_text1 = tokenizer.tokenize(text)
132+
print(tokenized_text1)
133+
indexed_tokens1 = tokenizer.convert_tokens_to_ids(tokenized_text1)
134+
# 分词结束后获取BERT模型需要的tensor
135+
segments_ids1 = [1] * len(tokenized_text1)
136+
tokens_tensor1 = torch.tensor([indexed_tokens1]) # 将list转为tensor
137+
segments_tensors1 = torch.tensor([segments_ids1])
138+
# 获取所有词向量的embedding
139+
word_vectors1 = bertmodel(tokens_tensor1, segments_tensors1)[0]
140+
# 获取句子的embedding
141+
sentenc_vector1 = bertmodel(tokens_tensor1, segments_tensors1)[1]
142+
tokenizer.add_special_tokens({'additional_special_tokens':["<e>"]})
143+
print("添加特殊占位符 后 的分词效果:")
144+
print(tokenizer.additional_special_tokens) # 查看此类特殊token有哪些
145+
print(tokenizer.additional_special_tokens_ids) # 查看其id
146+
tokenized_text1 = tokenizer.tokenize(text)
147+
print(tokenized_text1)
148+
>>>
149+
普通 分词效果:
150+
['[CLS]', 'i', 'love', '<', 'e', '>', '!', '[SEP]']
151+
152+
添加特殊占位符 后 的分词效果:
153+
['<e>']
154+
[21128]
155+
['[CLS]', 'i', 'love', '<e>', '!', '[SEP]']
156+
```
157+
158+
## 三、方法对比
159+
160+
- 方法一:
161+
- 优点:如果存在大量领域内专业词汇,而且已经整理成词表,可以利用该方法批量添加;
162+
- 缺点:因为 方法1 存在 未登录词数量限制(eg:cased模型只有99个空位,uncased模型有999个空位),所以当 未登录词 太多时,将不适用;
163+
- 方法二:
164+
- 优点:不存在 方法1 的 未登录词数量限制 问题;
165+
- 方法三:
166+
- 优点:对于一些 占位符(eg:<e></e>),方法一和方法二可能都无法生效,因为 <, e, >和 <e></e>均存在于 vocab.txt,但前三者的优先级高于 <e></e>,而 add_special_tokens会起效,却会使得词汇表大小增大,从而需另外调整模型size。但是,如果同时在词汇表vocab.txt中替换[unused],同时 add_special_tokens,则新增词会起效,同时词汇表大小不变。
167+
168+
169+
## 参考
170+
171+
1. [NLP | How to add a domain-specific vocabulary (new tokens) to a subword tokenizer already trained like BERT WordPiece | by Pierre Guillou | Medium](https://medium.com/@pierre_guillou/nlp-how-to-add-a-domain-specific-vocabulary-new-tokens-to-a-subword-tokenizer-already-trained-33ab15613a41)
172+
2. [如何向BERT词汇表中添加token,新增特殊占位符](https://blog.csdn.net/icestorm_rain/article/details/108540053)
173+
3. [在BERT模型中添加自己的词汇(pytorch版)](https://zhuanlan.zhihu.com/p/391814780)

0 commit comments

Comments
 (0)