Определить коллекцию биграмм в наборе очень больших текстовых данных - PullRequest
0 голосов
/ 01 июля 2019

Я бы хотел найти биграммы в большом корпусе в текстовом формате.Поскольку корпус не может быть загружен сразу в память, а его строки очень большие, я загружаю его кусками, каждый 1 кб

def read_in_chunks(filename, chunk_size=1024):
    """Lazy function (generator) to read a file piece by piece.
    Default chunk size: 1k."""
    while True:
        data = filename.read(chunk_size)
        if not data:
            break
        yield data

Затем я хочу пройти по корпусу кусочек и найти биграммы иЯ использую функции gensim Phrases () и Phraser (), но во время обучения моя модель постоянно теряет состояние.Таким образом, я пытался сохранить и перезагрузить модель после каждого прочитанного мегабайта, а затем освободить память, но она все еще теряет состояние.Мой код здесь:

with open("./final/corpus.txt", "r", encoding='utf8') as r:
    max_vocab_size=20000000
    phrases = Phrases(max_vocab_size=max_vocab_size)
    i=1
    j=1024
    sentences = ""
    for piece in read_in_chunks(r):   

        if i<=j:
            sentences = sentences + piece

        else:
            phrases.add_vocab(sentences)
            phrases = Phrases(sentences)
            phrases = phrases.save('./final/phrases.txt')
            phrases = Phraser.load('./final/phrases.txt')

            sentences = ""
            j+=1024
        i+=1
print("Done")

Есть предложения?Спасибо.

1 Ответ

1 голос
/ 01 июля 2019

Когда вы делаете две строки ...

        phrases.add_vocab(sentences)
        phrases = Phrases(sentences)

... эта вторая строка отбрасывает любой существующий экземпляр внутри переменной phrases и заменяет его новым экземпляром (Phrases(sentences)). Там нет шансов для аддитивной корректировки в единственном экземпляре.

Во-вторых, две последовательные строки .save() -then-instant-re- .load() не могут спасти использование памяти. В лучшем случае .load() будет ненужным, только точно воспроизводя то, что было просто .save() d, но тратя много времени и временной памяти, загружая вторую копию, а затем отбрасывая дубликат, который уже был в phrases, для назначить phrases новому клону.

Хотя это проблемы, в более общем плане, проблема в том, что то, что вам нужно сделать, не должно быть таким сложным.

Класс Phrases примет в качестве корпуса sentences итеративный объект, каждый элемент которого представляет собой список строк-токенов. Вам не нужно беспокоиться о размерах куска и многократном вызове add_vocab() - вы можете просто предоставить один объект, который сам предлагает каждый элемент по очереди, и Phrases будет делать правильные вещи. Вы do должны беспокоиться о разбивке необработанных строк на конкретные слова, которые вы хотите рассмотреть («токенизация»).

(Для большого корпуса вы все равно можете столкнуться с проблемами памяти, связанными с количеством уникальных слов, которые Phrases пытается сосчитать. Но не имеет значения, насколько произвольно велико число элементы - потому что он будет смотреть только по одному за раз. Только накопление уникальных слов будет занимать оперативную память.)

Для хорошего представления о том, как итеративный объект может работать в таких ситуациях, хороший пост в блоге:

Поток данных в Python: генераторы, итераторы, итерируемые

Если ваш файл corpus.txt уже настроен на одно предложение разумного размера на строку и все слова уже разделены простыми пробелами, то итеративный класс может быть таким простым:

class FileByLineIterable(object):
    def __init__(self, filename):
        self.filename = filename
    def __iter__(self):
        with open(self.filename, 'r', encoding='utf8') as src:
            for line in src.readlines():
                yield line.split()

Тогда ваш код может быть таким же простым, как ...

sentences = FileByLineIterable('./final/corpus.txt')
phrases = Phrases(sentences, max_vocab_size=max_vocab_size)

... потому что класс Phrases получает то, что он хочет - корпус, который предлагает посредством итерации только один элемент списка слов за раз.

Примечание:

  • вы можете включить ведение журнала на уровне INFO для отслеживания прогресса и отслеживания любых признаков того, что что-то идет не так

  • есть немного более продвинутый построчный итератор, который также ограничивает текст одной строки не более чем 10 000 токенов (чтобы соответствовать внутреннему пределу реализации gensim Word2Vec) и открывает файлы из мест, отличных от локальных путей к файлам, доступно по gensim.models.word2vec.LineSentence См:

https://radimrehurek.com/gensim/models/word2vec.html#gensim.models.word2vec.LineSentence

(Несмотря на то, что он упакован в пакет word2vec, его можно использовать в другом месте.)

...