python gensim word2vec дает ошибку типа TypeError: объект типа «генератор» не имеет len () в пользовательском классе данных - PullRequest
0 голосов
/ 26 апреля 2019

Я пытаюсь заставить word2vec работать в python3, однако мой набор данных слишком велик, чтобы легко помещаться в памяти, я загружаю его через итератор (из zip-файлов). Однако, когда я запускаю его, я получаю ошибку

Traceback (most recent call last):
  File "WordModel.py", line 85, in <module>
    main()
  File "WordModel.py", line 15, in main
    word2vec = gensim.models.Word2Vec(data,workers=cpu_count())
  File "/home/thijser/.local/lib/python3.7/site-packages/gensim/models/word2vec.py", line 783, in __init__
    fast_version=FAST_VERSION)
  File "/home/thijser/.local/lib/python3.7/site-packages/gensim/models/base_any2vec.py", line 759, in __init__
    self.build_vocab(sentences=sentences, corpus_file=corpus_file, trim_rule=trim_rule)
  File "/home/thijser/.local/lib/python3.7/site-packages/gensim/models/base_any2vec.py", line 936, in build_vocab
    sentences=sentences, corpus_file=corpus_file, progress_per=progress_per, trim_rule=trim_rule)
  File "/home/thijser/.local/lib/python3.7/site-packages/gensim/models/word2vec.py", line 1591, in scan_vocab
    total_words, corpus_count = self._scan_vocab(sentences, progress_per, trim_rule)
  File "/home/thijser/.local/lib/python3.7/site-packages/gensim/models/word2vec.py", line 1576, in _scan_vocab
    total_words += len(sentence)
TypeError: object of type 'generator' has no len()

Вот код:

import zipfile
import os
from ast import literal_eval

from lxml import etree
import io
import gensim

from multiprocessing import cpu_count


def main():
    data = TrainingData("/media/thijser/Data/DataSets/uit2")
    print(len(data))
    word2vec = gensim.models.Word2Vec(data,workers=cpu_count())
    word2vec.save('word2vec.save')




class TrainingData:

    size=-1

    def __init__(self, dirname):
        self.data_location = dirname

    def __len__(self):
        if self.size<0: 

            for zipfile in self.get_zips_in_folder(self.data_location): 
                for text_file in self.get_files_names_from_zip(zipfile):
                    self.size=self.size+1
        return self.size            

    def __iter__(self): #might not fit in memory otherwise
        yield self.get_data()

    def get_data(self):


        for zipfile in self.get_zips_in_folder(self.data_location): 
            for text_file in self.get_files_names_from_zip(zipfile):
                yield self.preproccess_text(text_file)


    def stripXMLtags(self,text):

        tree=etree.parse(text)
        notags=etree.tostring(tree, encoding='utf8', method='text')
        return notags.decode("utf-8") 

    def remove_newline(self,text):
        text.replace("\\n"," ")
        return text

    def preproccess_text(self,text):
        text=self.stripXMLtags(text)
        text=self.remove_newline(text)

        return text




    def get_files_names_from_zip(self,zip_location):
        files=[]
        archive = zipfile.ZipFile(zip_location, 'r')

        for info in archive.infolist():
            files.append(archive.open(info.filename))

        return files

    def get_zips_in_folder(self,location):
       zip_files = []
       for root, dirs, files in os.walk(location):
            for name in files:
                if name.endswith((".zip")): 
                    filepath=root+"/"+name
                    zip_files.append(filepath)

       return zip_files

main()


for d in data:
    for dd in d :
        print(type(dd))

Показывает ли я, что dd относится к типу string и содержит правильные предварительно обработанные строки (длиной от 50 до 5000 слов каждая).

1 Ответ

1 голос
/ 26 апреля 2019

Обновление после обсуждения:

Ваша функция TrainingData class __iter__() предоставляет не генератор, который возвращает каждый текст по очереди, а генератор, который возвращает один другой генератор. (Существует слишком много уровней yield.) Это не то, чего ожидает Word2Vec.

Изменение тела вашего __iter__() метода на простое ...

return self.get_data()

... так что __iter__() является синонимом вашего get_data() и просто возвращает тот же текстовый генератор, что и get_data().

Оригинальный ответ:

Вы не показываете метод TrainingData.preproccess_text() (sic), на который есть ссылка в get_data(), который является тем, что фактически создает данные, обрабатываемые Word2Vec. И именно эти данные генерируют ошибку.

Word2Vec требует, чтобы его sentences корпус был повторяемой последовательностью (для которой подойдет генератор), где каждый отдельный элемент представляет собой список строк-токенов,

Из этой ошибки похоже, что отдельные элементы в вашей последовательности TrainingData сами могут быть генераторами, а не списками с читаемым len().

(Отдельно, если вы предпочитаете использовать генераторы там, потому что отдельные тексты могут быть очень очень длинными, имейте в виду, что gensim Word2Vec и связанные классы обучаются только на отдельных текстах длиной до 10000 слов-токенов Любые слова после 10000-го будут молча игнорироваться. Если это важно, ваши исходные тексты должны быть предварительно разбиты на отдельные тексты по 10000 токенов или меньше.)

...