在 Doc2Vec 标记化中保留数字

Keeping Numbers in Doc2Vec Tokenization

我正在尝试使用 Doc2Vec 获取大约 5,000 条法律摘要的语料库的文档相似度值(我知道语料库可能有点小,但这是概念验证一个包含大约 15,000 条内裤的更大语料库的项目,我将不得不稍后编译)。

基本上,到目前为止,创建模型的所有其他组件都进行得相对顺利——我的每个简介都在一个更大的文件夹中的文本文件中,所以我使用 glob.glob 在我的脚本中编译了它们– 但我 运行 遇到了标记化问题。困难在于,由于这些文档是法律简报,它们包含我想保留的数字,而且我一直用来帮助我编写代码的许多指南都使用 Gensim 的简单预处理,我相信它从语料库,与 TaggedDocument 功能相结合。但是,我想尽可能少地对文本进行预处理。

下面是我使用的代码,我尝试将 simple_preprocess 换成 genism.utils.tokenize,但是当我这样做时,我得到的生成器对象在我的代码中似乎不可用最终的 Doc2Vec 模型,我实际上看不到语料库的样子。当我尝试使用其他分词器时,例如 nltk,我不知道如何将其放入 TaggedDocument 组件中。

brief_corpus = []
for brief_filename in brief_filenames:
    with codecs.open(brief_filename, "r", "utf-8") as brief_file:
        brief_corpus.append(
            gensim.models.doc2vec.TaggedDocument(
                gensim.utils.simple_preprocess( 
                    brief_file.read()),
                    ["{}".format(brief_filename)])) #tagging each brief with its filename

我将不胜感激任何人可以提供的任何建议,这些建议将帮助我结合一个仅以空格分隔且不消除任何数字的分词器与 TaggedDocument 功能。谢谢!

更新: 我能够为一些基本的标记化创建基本代码(我确实计划进一步完善它)而不必求助于 Gensim 的 simple_preprocessing功能。但是,我在使用 TaggedDocument 功能时遇到了困难(又一次!)——但这一次,标签(我想成为每个摘要的文件名)与标记化文档不匹配。基本上,每个文档都有一个标签,但它不是正确的。

谁能告诉我下面的新代码哪里出了问题?谢谢!

briefs = []
BriefList = [p for p in os.listdir(FILEPATH) if p.endswith('.txt')]
for brief in BriefList:
     str = open(FILEPATH + brief,'r').read()
     tokens = re.findall(r"[\w']+|[.,!?;]", str)
     tagged_data = [TaggedDocument(tokens, [brief]) for brief in BriefList]
     briefs.append(tagged_data)

您可能想要编写自己的 preprocessing/tokenization 函数。但别担心,即使使用非常粗糙的代码,也不难超越 Gensim 的 simple_preprocess

作为 TaggedDocumentwordsDoc2Vec 唯一需要的是字符串标记列表(通常是单词)。

所以首先,您可能会惊讶于仅在原始字符串上执行默认 Python 字符串 .split() 的效果如此之好 - 它只会在空格处打断文本。

当然,一堆生成的标记将是单词和相邻标点符号的混合,这可能几乎是无稽之谈。

例如,句子末尾的单词 'lawsuit' 可能显示为 'lawsuit.',然后将不会被识别为与 'lawsuit' 相同的标记,并且可能出现的次数不足 min_count 次,甚至无法被考虑,否则几乎无法超越噪音。

但特别是对于更长的文档和更大的数据集,没有一个标记,甚至所有标记的 1% 有那么大的影响。这不是精确关键字搜索,如果无法 return 带有 'lawsuit.' 的文档来查询 'lawsuit' 将是致命的失败。一堆单词 'lost' 可能会对整个文档或模型、性能产生任何影响。

由于您的数据集似乎易于管理 运行 大量实验,我建议尝试这种最愚蠢的标记化 - 仅 .split() - 作为基准以确信该算法仍然大部分工作以及一些更具侵入性的操作(如 simple_preprocess())。

然后,正如您注意到、怀疑,或者理想情况下通过一些可重复的评估来衡量的那样,您希望成为有意义的标记的一些事情并没有得到正确的处理,逐渐添加 stripping/splitting/canonicalizing 个字符或标记的额外步骤。但尽可能多:检查代码的额外复杂性和 运行 时间是否确实带来了好处。

例如,进一步的改进可能是以下的一些组合:

  • 对于由简单 split() 创建的每个标记,去除任何非字母数字 leading/trailing 字符。 (优点:消除标点符号污染词。缺点:可能会丢失有用的符号,例如货币金额的前导 $。)
  • 在拆分之前,将某些单字符标点符号(比如 ['.', '"', ',', '(', ')', '!', '?', ';', ':'])替换为两边带空格的相同字符 - 这样它们就不会与附近的单词连接,而是在简单 .split() 作为独立令牌。 (优点:还可以防止单词加标点符号的混乱。缺点:打断数字,如 2,345.77 或一些有用的缩写。)
  • 在标记化的某个适当阶段,将许多不同的标记规范化为一个较小的标记集,这些标记可能比它们作为稀有的独立标记更有意义。例如,[=28=].01[=29=].99 可能全部变成 [=30=]_XX - 与原始模型相比,它更有可能影响模型并与 'tiny amount' 概念相关联独立的令牌。或者用 # 替换所有数字,这样类似数量级的数字会分享影响,而不会用每个数字的标记稀释模型。

启发式和操作顺序的确切组合将取决于您的目标。但是对于只有数千个文档(而不是数十万或数百万)的语料库,即使您以相当低效的方式进行这些替换(许多单独的字符串或正则表达式连续替换),它也会可能是可控的预处理成本。

但是您可以从简单开始,并且只增加您的特定领域知识和评估所证明的复杂性。