将dask数据框中的列转换为Doc2Vec的TaggedDocument

时间:2019-06-20 07:38:02

标签: python dask gensim doc2vec

简介

目前,我正在尝试与gensim一起使用dask进行NLP文档计算,并且在将我的语料库转换为“ TaggedDocument”时遇到了问题。

因为我尝试了许多不同的方法来解决这个问题,所以我将列出自己的尝试。

每次处理此问题的尝试都会遇到些微的麻烦。

首先给出一些初始给定。

数据

df.info()
<class 'dask.dataframe.core.DataFrame'>
Columns: 5 entries, claim_no to litigation
dtypes: object(2), int64(3)
  claim_no   claim_txt I                                    CL ICC lit
0 8697278-17 battery comprising interior battery active ele... 106 2 0

所需的输出

>>tagged_document[0]
>>TaggedDocument(words=['battery', 'comprising', 'interior', 'battery', 'active', 'elements', 'battery', 'cell', 'casing', 'said', 'cell', 'casing', 'comprising', 'first', 'casing', 'element', 'first', 'contact', 'surface', 'second', 'casing', 'element', 'second', 'contact', 'surface', 'wherein', 'assembled', 'position', 'first', 'second', 'contact', 'surfaces', 'contact', 'first', 'second', 'casing', 'elements', 'encase', 'active', 'materials', 'battery', 'cell', 'interior', 'space', 'wherein', 'least', 'one', 'gas', 'tight', 'seal', 'layer', 'arranged', 'first', 'second', 'contact', 'surfaces', 'seal', 'interior', 'space', 'characterized', 'one', 'first', 'second', 'contact', 'surfaces', 'comprises', 'electrically', 'insulating', 'void', 'volume', 'layer', 'first', 'second', 'contact', 'surfaces', 'comprises', 'formable', 'material', 'layer', 'fills', 'voids', 'surface', 'void', 'volume', 'layer', 'hermetically', 'assembled', 'position', 'form', 'seal', 'layer'], tags=['8697278-17'])
>>len(tagged_document) == len(df['claim_txt'])

错误号1不允许生成器

def read_corpus_tag_sub(df,corp='claim_txt',tags=['claim_no']):
    for i, line in enumerate(df[corp]):
        yield gensim.models.doc2vec.TaggedDocument(gensim.utils.simple_preprocess(line), (list(df.loc[i,tags].values)))

tagged_document = df.map_partitions(read_corpus_tag_sub,meta=TaggedDocument)
tagged_document = tagged_document.compute()

TypeError:无法序列化类型生成器的对象。

我仍然无法使用发电机来解决这个问题。解决这个问题的方法非常棒!因为这对于普通的熊猫来说效果很好。

错误编号2仅每个分区的第一个元素

def read_corpus_tag_sub(df,corp='claim_txt',tags=['claim_no']):
    for i, line in enumerate(df[corp]):
        return gensim.models.doc2vec.TaggedDocument(gensim.utils.simple_preprocess(line), (list(df.loc[i,tags].values)))

tagged_document = df.map_partitions(read_corpus_tag_sub,meta=TaggedDocument)
tagged_document = tagged_document.compute()

该函数有点笨,因为该函数不会迭代(我知道),但是会提供所需的格式,但只会返回每个分区的第一行。

错误3号函数调用的CPU 100%挂起

def read_corpus_tag_sub(df,corp='claim_txt',tags=['claim_no']):
    tagged_list = []
    for i, line in enumerate(df[corp]):
        tagged = gensim.models.doc2vec.TaggedDocument(gensim.utils.simple_preprocess(line), (list(df.loc[i,tags].values)))
        tagged_list.append(tagged)
    return tagged_list

正如我所知道的,在循环外重构返回值时,此函数挂起将在dask客户端中建立内存,并且我的CPU利用率达到100%,但未计算任何任务。请记住,我以相同的方式调用该函数。

熊猫解决方案

def tag_corp(corp,tag):
    return gensim.models.doc2vec.TaggedDocument(gensim.utils.simple_preprocess(corp), ([tag]))

tagged_document = [tag_corp(x,y) for x,y in list(zip(df_smple['claim_txt'],df_smple['claim_no']))]

List comp我还没有时间测试这个解决方案

其他熊猫解决方案

tagged_document = list(read_corpus_tag_sub(df))

此解决方案将持续几个小时。但是,完成后,我没有足够的内存来处理这个事情。

结论(?)

我现在感觉超级迷路了。这是我看过的线程列表。我承认自己真的很陌生,我花了这么多时间,感觉好像在做一个傻瓜。

  1. Dask Bag from generator
  2. Processing Text With Dask
  3. Speed up Pandas apply using Dask
  4. How do you parallelize apply() on Pandas Dataframes making use of all cores on one machine?
  5. python dask DataFrame, support for (trivially parallelizable) row apply?
  6. What is map_partitions doing?
  7. simple dask map_partitions example
  8. The Docs

2 个答案:

答案 0 :(得分:1)

我不熟悉Dask API /限制,但通常:

  • 如果您可以将数据迭代为(单词,标签)元组–甚至忽略Doc2Vec / TaggedDocument步骤–那么Dask端将已处理,并转换这些元组TaggedDocument实例应该不重要

  • 通常,对于大型数据集,您不想(并且可能没有足够的RAM来)将完整数据集实例化为内存中的list –因此,涉及{{1 }}或list()可能在某种程度上可以正常工作,但是耗尽了本地内存(导致严重的交换)和/或只是没有达到数据的结尾。

大型数据集的首选方法是创建一个可迭代的对象,该对象每次被要求对数据进行迭代(因为.append()训练将需要多次通过)可以依次提供每个项目–但切勿将整个数据集读入内存中的对象。

关于此模式的一个不错的博客文章是:Data streaming in Python: generators, iterators, iterables

鉴于您显示的代码,我怀疑适合您的方法可能是:

Doc2Vec

答案 1 :(得分:1)

对,所以您很接近这段代码

def read_corpus_tag_sub(df,corp='claim_txt',tags=['claim_no']):
    for i, line in enumerate(df[corp]):
        yield gensim.models.doc2vec.TaggedDocument(gensim.utils.simple_preprocess(line), (list(df.loc[i,tags].values)))

tagged_document = df.map_partitions(read_corpus_tag_sub,meta=TaggedDocument)

但是正如您所见,生成发电机对Dask并不是很有帮助。相反,您可以让函数返回一个序列

def myfunc(df, *args, **kwargs):
    output = []
    for i, line in enumerate(df["my_series"])
        result = ...
        output.append([])
    return pd.Series(output)

或者,您可能只使用df.apply方法,该方法采用了将单行转换为单行的函数。

您可能还想切换到Dask Bag,它比Pandas / Dask DataFrame更自然地处理列表和生成器之类的东西。