Преобразовать столбец в фрейме данных dask в TaggedDocument для Doc2Vec - PullRequest
1 голос
/ 20 июня 2019

Введение

В настоящее время я пытаюсь использовать dask совместно с gensim для вычисления документов 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 зависает на 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, и моя загрузка ЦП достигает 100%, но никакие задачи не вычисляются. Имейте в виду, я вызываю функцию так же.

Pandas Solution

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']))]

Список компов У меня нет времени проверять это решение

Другое решение для панд

tagged_document = list(read_corpus_tag_sub(df))

Это решение будет длиться часами. Однако у меня недостаточно памяти, чтобы жонглировать этой штукой, когда она закончится.

Заключение (?)

Я чувствую себя супер потерянным прямо сейчас. Вот список тем, на которые я смотрел. Я признаюсь, что был действительно новичком в этом деле. Я только что провел так много времени, и я чувствую, что у меня дурацкое поручение.

  1. Сумка Dask от генератора
  2. Обработка текста с помощью Dask
  3. Ускорить применение панд с помощью Dask
  4. Каким образом вы распараллеливаете apply () на информационных панелях Pandas, используя все ядра на одном компьютере?
  5. Python dask DataFrame, поддержка (тривиально распараллеливаемой) строки применяется?
  6. Что делает map_partitions?
  7. пример простого dask map_partitions
  8. Документы

Ответы [ 2 ]

1 голос
/ 21 июня 2019

Правильно, поэтому вы близки к этому коду

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.

1 голос
/ 21 июня 2019

Я не знаком с API / ограничениями Dask, но обычно:

  • если вы можете перебирать свои данные в виде (слов, тегов) кортежей - даже игнорируя шаги Doc2Vec / TaggedDocument - тогда сторона Dask будет обработана и конвертирует эти кортежи в TaggedDocument экземпляры должны быть тривиальными

  • в целом для больших наборов данных вы не хотите (и, возможно, не имеете достаточно ОЗУ) создавать экземпляр полного набора данных как list в памяти - поэтому ваши попытки, включающие list() или .append() может работать, до некоторой степени, но исчерпывать локальную память (вызывая серьезный обмен) и / или просто не доходя до конца ваших данных.

Предпочтительный подход к большим наборам данных заключается в создании итерируемого объекта, который, каждый раз, когда его просят выполнить итерацию по данным (поскольку Doc2Vec обучение потребует нескольких проходов), может предлагать каждый элемент по очереди - но никогда не читать весь набор данных в объекте в памяти.

Хорошая запись блога по этому шаблону: Потоковая передача данных в Python: генераторы, итераторы, итерируемые

Учитывая код, который вы показали, я подозреваю, что правильный подход для вас может выглядеть так:

from gensim.utils import simple_preprocess

class MyDataframeCorpus(object):
    def __init__(self, source_df, text_col, tag_col):
        self.source_df = source_df
        self.text_col = text_col
        self.tag_col = tag_col

    def __iter__(self):
        for i, row in self.source_df.iterrows():
            yield TaggedDocument(words=simple_preprocess(row[self.text_col]), 
                                 tags=[row[self.tag_col]])

corpus_for_doc2vec = MyDataframeCorpus(df, 'claim_txt', 'claim_no')
...