【问题标题】:Training Word2Vec Model from sourced data - Issue Tokenizing data从源数据训练 Word2Vec 模型 - 问题标记化数据
【发布时间】:2021-05-27 08:50:35
【问题描述】:

我最近从 Google Bigquery 获取并整理了很多 reddit 数据。

数据集如下所示:

在将此数据传递给 word2vec 以创建词汇表并进行训练之前,我需要正确标记“body_cleaned”列。

我已经尝试使用手动创建的函数和 NLTK 的 word_tokenize 进行标记化,但现在我将重点放在使用 word_tokenize 上。

由于我的数据集比较大,接近 1200 万行,我不可能一次性打开数据集并对其执行功能。 Pandas 尝试将所有内容加载到 RAM 中,正如您所理解的那样,它会崩溃,即使在具有 24GB 内存的系统上也是如此。

我面临以下问题:

  • 当我对数据集进行标记(使用 NTLK word_tokenize)时,如果我对整个数据集执行该函数,它会正确标记并且 word2vec 接受该输入并在其词汇表中正确学习/输出单词。
  • 当我通过首先批处理数据帧并迭代它来标记数据集时,生成的标记列不是 word2vec 喜欢的;尽管 word2vec 使用超过 4 小时收集的数据训练其模型,但它所学习的词汇表由多种编码的单个字符以及表情符号组成,而不是单词。

为了解决这个问题,我创建了一小部分数据并尝试以两种不同的方式对该数据执行标记化:

  • 知道我的计算机可以处理对数据集执行的操作,我只是这样做了:
reddit_subset = reddit_data[:50]

reddit_subset['tokens'] = reddit_subset['body_cleaned'].apply(lambda x: word_tokenize(x))

这会产生以下结果:

这实际上适用于 word2vec 并生成可以使用的模型。到目前为止很棒。

由于我无法一次性对如此庞大的数据集进行操作,因此我不得不对如何处理这个数据集进行创新。我的解决方案是批处理数据集并使用 Panda 自己的 batchsize 参数在小迭代中处理它。

我编写了以下函数来实现这一点:

def reddit_data_cleaning(filepath, batchsize=20000):
    if batchsize:
        df = pd.read_csv(filepath, encoding='utf-8', error_bad_lines=False, chunksize=batchsize, iterator=True, lineterminator='\n')
    print("Beginning the data cleaning process!")
    start_time = time.time()
    flag = 1
    chunk_num = 1
    for chunk in df:
        chunk[u'tokens'] = chunk[u'body_cleaned'].apply(lambda x: word_tokenize(x))
        chunk_num += 1
    if flag == 1:
            chunk.dropna(how='any')
            chunk = chunk[chunk['body_cleaned'] != 'deleted']
            chunk = chunk[chunk['body_cleaned'] != 'removed']
            print("Beginning writing a new file")
            chunk.to_csv(str(filepath[:-4] + '_tokenized.csv'), mode='w+', index=None, header=True)
            flag = 0
        else:
            chunk.dropna(how='any')
            chunk = chunk[chunk['body_cleaned'] != 'deleted']
            chunk = chunk[chunk['body_cleaned'] != 'removed']
            print("Adding a chunk into an already existing file")
            chunk.to_csv(str(filepath[:-4] + '_tokenized.csv'), mode='a', index=None, header=None)
    end_time = time.time()
    print("Processing has been completed in: ", (end_time - start_time), " seconds.")

虽然这段代码让我能够真正分块处理这个庞大的数据集并产生结果,否则我会因内存故障而崩溃,但我得到的结果不符合我的 word2vec 要求,让我很困惑原因。

我用上面的函数对Data子集进行同样的操作,比较两个函数的结果有什么不同,得到如下:

所需的结果在 new_tokens 列上,分块数据帧的函数会产生“tokens”列结果。

有谁更聪明地帮助我理解为什么相同的标记化函数会根据我对数据帧的迭代方式产生完全不同的结果?

如果您通读整个问题并坚持下去,我将不胜感激!

【问题讨论】:

    标签: python pandas tokenize word2vec


    【解决方案1】:

    首先,超出一定大小的数据,尤其是在处理原始文本或标记文本时,您可能希望使用 Pandas 数据帧对于每个中期结果。

    它们增加了不完全“Pythonic”的额外开销和复杂性。对于以下情况尤其如此:

    • Python list 对象,其中每个单词都是一个单独的字符串:一旦您将原始字符串标记为这种格式,例如将此类文本提供给 Gensim 的 Word2Vec 模型,尝试将它们放入 Pandas 只会导致令人困惑的列表表示问题(与您的列一样,其中相同的文本可能显示为 ['yessir', 'shit', 'is', 'real'] - 这是一个真正的 Python 列表文字 - 或 [yessir, shit, is, real] - 如果任何标记具有挑战性字符,这可能会破坏其他一些混乱)。
    • 原始词向量(或更高版本的文本向量):与数据帧相比,在原始 Numpy 数组中使用这些更紧凑、更自然/更高效

    所以,无论如何,如果 Pandas 有助于加载或其他非文本字段,请在此处使用它。但随后对标记化的文本和向量使用更多基本的 Python 或 Numpy 数据类型 - 可能使用 Dataframe 中的某些字段(如唯一 ID)来关联两者。

    特别是对于大型文本语料库,更典型的做法是摆脱 CSV,而是使用大型文本文件,每个换行符分隔的行一个文本,并且任何每一行都被预先标记,以便可以完全信任空格作为标记- 分开。

    也就是说:即使您的初始文本数据具有更复杂的标点敏感标记化,或其他组合/更改/拆分其他标记的预处理,尝试只执行一次(特别是如果它涉及昂贵的正则表达式),将结果写入一个简单的文本文件,然后符合简单的规则:每行读取一个文本,每行仅用空格分隔。

    许多算法,例如 Gensim 的 Word2VecFastText,可以直接流式传输此类文件,也可以通过开销非常低的可迭代包装器 - 所以文本从不完全在内存中,只有根据需要反复阅读,以进行多次训练迭代。

    有关这种处理大量文本的有效方法的更多详细信息,请参阅这篇文章:https://rare-technologies.com/data-streaming-in-python-generators-iterators-iterables/

    【讨论】:

    • 如果您可以轻松地将整个 CSV 源数据读取到单个 Pandas Dataframe 中,当然,这样做,然后遍历文本,将标记化的文本流式传输到新的纯文本文件。但是,如果您不需要 Pandas 或其他东西,您可以自己逐行读取 CSV 文件,忽略不相关的部分,标记重要文本,将其写入单独的文件。这些是 Python 中的基本文本文件 IO 操作,通常只有几到几十行代码 - 但最好在查看其他一些文件 IO 示例后尝试,如果遇到问题,作为特定的单独 Q 询问。
    • 我继续使用 CSV 包遍历行并将它们写入 txt 文件。感谢您的帮助!一开始有点挑战,因为我使用的是 jupyter 并且因为我要求该函数打印一些输出(为了我的理智),我设法使系统过载并使其崩溃。更少的输出,更多的写作,解决了我的问题。
    • 不客气!我可以看到您已经付出了很多努力,但需要在流/纯 Python/纯文件方向上多做一点。
    • 回想一下我“浪费”了多少时间试图提出“聪明”的解决方案,当时我听取了您的建议来简化事情,然后消除了函数的打印输出(这更多的是jupyter 打嗝),我很惊讶地看到 1200 万行 csv 在不到一分钟的时间内处理完毕。不能要求更多的指导,再次感谢我的朋友。
    • 编辑了 OP 以包含我的解决方案,再次感谢。
    【解决方案2】:

    在听取 gojomo 的建议后,我简化了读取 csv 文件和写入文本文件的方法。

    我最初使用 pandas 的方法对于大约 1200 万行的文件产生了一些相当糟糕的处理时间,并且由于 pandas 在将数据写入文件之前将数据全部读入内存而导致内存问题。

    我还意识到,我之前的代码存在重大缺陷。 我正在打印一些输出(作为健全性检查),并且因为我打印输出过于频繁,我溢出了 Jupyter 并使笔记本崩溃,无法完成底层和最重要的任务。

    我摆脱了这一点,使用 csv 模块简化读取并写入 txt 文件,并在不到 10 秒的时间内处理了约 1200 万行的 reddit 数据库。

    也许不是最好的代码,但我一直在努力解决一个困扰我几天的问题(我没有意识到我的部分问题是我的健全性检查导致 Jupyter 崩溃是一个更大的问题沮丧)。

    def generate_corpus_txt(csv_filepath, output_filepath):
        import csv
        import time
        start_time = time.time()
        with open(csv_filepath, encoding = 'utf-8') as csvfile:
            datareader = csv.reader(csvfile)
            count = 0
            header = next(csvfile)
            print(time.asctime(time.localtime()), " ---- Beginning Processing")
            with open(output_filepath, 'w+') as output:
                # Check file as empty
                if header != None:
                    for row in datareader:
                            # Iterate over each row after the header in the csv
                            # row variable is a list that represents a row in csv
                        processed_row = str(' '.join(row)) + '\n'
                        output.write(processed_row)
                        count += 1
                        if count == 1000000:
                            print(time.asctime(time.localtime()), " ---- Processed 1,000,000 Rows of data.")
                            count = 0
        print('Processing took:', int((time.time()-start_time)/60), ' minutes')
        output.close()
        csvfile.close()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 1970-01-01
      • 2021-12-15
      • 1970-01-01
      • 1970-01-01
      • 2018-10-23
      • 1970-01-01
      相关资源
      最近更新 更多