写在前面
最近在处理msmarco数据集,因为我的模型不需要用到bert,elmo这种language model的预训练表达。本来是想用用fasttext,但是机器只有128G内存,连数据都放不下去,所以放弃了,于是决定好好处理分词了。
这里比较了三种分词工具:
- 1 . nltk
- 2 . spacy
- 3 . 正则表达式
词表统计的Passage Retrieval数据集的small数据集。一共接近4000w个pair。
NLTK
from nltk.corpus import stopwords
import string
from nltk.tokenize import word_tokenize
def my_tokenize(title):
""" tokenize word """
title = title.lower()
words = []
for _token in word_tokenize(title):
if _token in StopWords:
continue
words.append(_token)
return words
这里简单过滤了停用词,并且过滤了词频小于等于5的词。

nltk词频大于5
这里一共有1927856个词,还有可以看到这里分的并不好。各种点把本来应该是两个词的连在了一起。
再过滤一下词频大于20的。

nltk词频大于20
还有84万多的词,太多了。而且分的like shit。
Spacy
后来听了小伙伴说的spacy的分词不错,还有有篇论文里面也用了spacy分词。
于是乎尝试了一波,直接用的allennlp集成的WordTokenizer,底层是spacy。
from allennlp.data.tokenizers import WordTokenizer
from nltk.corpus import stopwords
import string
StopWords = set(stopwords.words('english') + list(string.punctuation))
tokenizer = WordTokenizer()
def my_tokenize(tokenizer, title):
""" tokenize word """
title = title.lower()
words = []
for _token in tokenizer.tokenize(title):
_token = str(_token).strip()
if _token == "":
continue
if _token in StopWords:
continue
words.append(_token)
return words
再看下词频大于5的词。

spacy词频大于5
这里词表一共有:1579122,稍微小了点儿,不过总感觉是和nltk差不多,乱七八糟的两个词拼在一起的也有。

spacy词频大于20
过滤了词频20以下的之后,还是有69万词表,不过看效果真的不太行。
正则表达式
后来又问了问同学,本来想试试有没有基于词表的分词的,有个说jieba分词可以。不过我没尝试。
在msmarco的Leaderboard上面看到了Duet v2的Official Baseline里面有完整的处理和模型的代码,用的正则分词,并过滤了词,词表只用了7万多。链接:[Official Baseline] Duet V2 (Ensembled)
from nltk.corpus import stopwords
import string
import re
StopWords = set(stopwords.words('english') + list(string.punctuation))
def my_tokenize(title):
regex_multi_space = re.compile('\s+')
regex_drop_char = re.compile('[^a-z0-9\s]+')
words = regex_multi_space.sub(' ', regex_drop_char.sub(' ', title.lower())).strip().split()
tokens = []
for _token in words:
if _token in StopWords:
continue
tokens.append(_token)
return tokens
过滤了词频为5以下的词之后。

正则词频大于5
词表大小只有:733205,比前面的小了一半多。而且起码看着像个样子。

正则词频大于20
目前应该是先拿正则的用了。