【发布时间】:2021-09-05 17:36:46
【问题描述】:
我正在尝试通过将 tokenizer_func 参数设置为自定义 tokenize 函数来自定义 Gensim 的 WikiCorpus 的语料库处理:
# Set tokenizer to our custom tokenizer
wiki = WikiCorpus(input, tokenizer_func=tokenize)
但是 PyParsing 处理文本花费的时间太长(例如,运行一天后甚至没有处理一篇文章)。就我而言,我想像往常一样清理维基百科语料库,除了保持与我拥有的单词列表(可能包含数字、下划线或 & 符号)匹配的任何单词不变。
假设一个可变长度的单词列表phrase_list,其中包括:
81、麦当劳、21、Happy 10、Sam's car、Ham & Eggs
以下是一些要清理的输入示例:
这里有一些示例文本可以转换为清理后的文本! & 8 * 牙仙子 81 是一个数字,麦当劳是一家快餐连锁店。 二十一岁呢?这也是一个数字。这里有一些时态:run run 正在运行。
不知道快乐 10 的感觉如何,但山姆的车是 很好。 In-N-Out 当然是经典,有时人们会写 虽然在 N Out。 7-11 有人吗?还是7-11?火腿和鸡蛋是 也很有趣的书-我猜-- 5nonalpha
以及所需的输出:
这里有一些示例文本要转换为清理后的文本牙齿仙女 81 是 number 和 mcdonalds 是 fast_food 连锁店 20one 怎么样 还有数字 这里有一些时态 奔跑 奔跑 不知道感觉如何 关于happy_10 但sams_car 还不错 in_n_out 是经典的 当然有时人们会写 in_n_out 虽然 7_11 任何人,或者是 7_11个火腿_&_eggs也是一本很有趣的书,猜猜非阿尔法
请注意,此示例文本的处理速度非常快,但在实际的 Wikipedia 语料库上却不是(我正在关注本教程,但对其进行了自定义:https://www.kdnuggets.com/2017/11/building-wikipedia-text-corpus-nlp.html)
这是我编写的使用 PyParsing 的自定义标记器(和一些辅助函数):
from typing import List
from pyparsing import *
from gensim.utils import to_unicode
from gensim.corpora import WikiCorpus
import string
import re
TOKEN_MIN_LEN = 2
TOKEN_MAX_LEN = 30
def pre_phrase_tokenize_processing(sentence):
"""
Helper: For cleaning sentence before phrases have been combined into single underscored tokens. Apply to entire sentence.
Removes hyphens, and punctuation except ampersands.
"""
# replace all hyphens with spaces since some phrases use them; just consider as multiword so they can be combined with underscore later
sentence = sentence.translate(str.maketrans('-', ' '))
# remove all punctuation except ampersands, since some phrases use them
remove_punct = string.punctuation.replace("&", "")
sentence = sentence.translate(str.maketrans('', '', remove_punct))
return sentence
def turn_phrases_into_tokens(phrases, sentence):
"""
Helper: Turns all individual phrases in a sentence into single underscored tokens according to a provided phrase dictionary.
"""
regex = re.compile("|".join([r"\b{}\b".format(phrase) for phrase in phrases]))
sentence = regex.sub(lambda m: phrases[m.group(0)], sentence)
return sentence
#@traceParseAction
def post_phrase_tokenize_processing(toks):
"""
For cleaning sentence after phrases have been combined into single underscored tokens. Apply to each non-phrase word.
Removes numeric characters and punctuation. Note toks is a list passed by pyparsing.
"""
# remove numeric characters, since only non-brand words are passed in
word = re.sub(r'\d+', '', toks[0])
# remove all punctuation (including &), since only non-brand words are passed in
word = word.translate(str.maketrans('', '', string.punctuation))
return word
# our phrase dictionary - actual list may continue many more phrases
phrases = {"81": "81", "mcdonalds": "mcdonalds", "twenty one": "twenty_one", "happy 10": "happy_10", "sams car": "sams_car", "ham & eggs": "ham_&_eggs"}
def tokenize(content: str, token_min_len=TOKEN_MIN_LEN, token_max_len=TOKEN_MAX_LEN, lower=True) -> List[str]:
"""Overrides original tokenize method in wikicorpus.py
Tokenize a piece of text from Wikipedia.
Parameters
----------
content : str
String without markup (see :func:`~gensim.corpora.wikicorpus.filter_wiki`).
token_min_len : int
Minimal token length.
token_max_len : int
Maximal token length.
lower : bool
Convert `content` to lower case?
Returns
-------
list of str
List of tokens from `content`.
"""
content = to_unicode(content, encoding='utf8', errors='ignore')
if lower:
content = content.lower()
content = pre_phrase_tokenize_processing(content)
# Combine any phrases into single tokens
content = turn_phrases_into_tokens(phrases, content)
# Match either one of our phrases, or any other nonwhitespace word (in which case we process)
phrase_list = list(phrases.values())
parser = Combine(
OneOrMore(
oneOf(phrase_list, asKeyword=True)
| Word(alphas)
| Word(printables).setParseAction(post_phrase_tokenize_processing)
),
joinString=' ',
adjacent=False
)
content = parser.transformString(content)
return [
to_unicode(token) for token in content.split()
if token_min_len <= len(token) <= token_max_len and not token.startswith('_')
]
也仅供参考,这是我在清理 Wikipedia 语料库(而不是示例文本)时用来调用标记器的实际代码 - 更多内容可以在上面的同一教程中找到:
def make_corpus(in_f, out_f):
"""Convert Wikipedia xml dump file to text corpus"""
output = open(out_f, 'w')
# Set tokenizer to our custom tokenizer
wiki = WikiCorpus(in_f, tokenizer_func=tokenize)
i = 0
for text in wiki.get_texts():
output.write(bytes(' '.join(text), 'utf-8').decode('utf-8') + '\n')
i = i + 1
if (i % 100 == 0):
print('Processed ' + str(i) + ' articles')
output.close()
print('Processing complete!')
到目前为止,我很确定问题出在 PyParsing(我称之为 parser = Combine(...) 的部分)——而不是匹配每个非空白单词,我应该只匹配需要清理的单词——但我我有点坚持如何做到这一点,因为我对这个库没有太多经验。我也有一个问题,当它们重新组合在一起时,单词之间的空格会被删除,这就是为什么我不得不用joinString=' ' 打电话给Combine,所以如果有任何建议,将不胜感激!
【问题讨论】:
-
请清理您的示例代码。指未定义的名称
to_unicode、phrases、phrase_list和post_phrase_tokenize_processing。 -
很抱歉试图保持简洁但遗漏了重要信息!代码应该有更多的上下文,我还链接了我的自定义代码所基于的教程。
-
您的实际短语列表中有多少项?我认为这可能是瓶颈。
-
啊,我有大约 330 个词组左右 - 你对我如何处理这个长长的列表有什么建议吗?
-
查看添加到我的答案中的编辑
标签: parsing nlp data-cleaning wikipedia pyparsing