在非常大的文本数据集上检测bigram集合

时间:2019-07-01 11:39:01

标签: python nlp gensim word2vec

我想以文本格式找到大型语料库中的二元语言。由于语料库无法立即加载到内存中,并且其行很大,因此我按块加载,每个块1 kb

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 个答案:

答案 0 :(得分:1)

当你做两行时...

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

...第二行删除了phrases变量内的所有现有实例,并用全新的实例(Phrases(sentences)代替了它。没有机会对单个实例进行附加调整。

其次,.save() -then-immediate-re-.load()的连续两行无法节省网络内存使用量。最好情况下,.load()是不必要的,仅精确地复制刚刚.save() d的内容,但是浪费大量时间和临时内存来加载第二个副本,然后丢弃已经在其中的副本phrasesphrases分配给新克隆。

虽然这些都是问题,但更普遍的是,问题在于您要做的事情不必这么复杂。

Phrases类将接受一个可迭代的对象作为其sentences的主体,其中每个项目都是一个字符串令牌列表。您不必担心块大小,也不必多次调用add_vocab() –您可以提供一个对象,该对象本身依次提供每个项目,而Phrases将做正确的事情。您要做必须担心将原始行分解为您要考虑的特定单词(“令牌化”)。

(对于大型语料库,您可能仍然会遇到与Phrases试图计算的唯一单词数有关的内存问题。但是,无论大小如何,都没有关系项的数量是–因为一次只能查看一个。只有个唯一单词的累积会消耗运行内存。)

要很好地介绍可迭代对象在这种情况下的工作方式,好的博客文章是:

Data streaming in Python: generators, iterators, iterables

如果您的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包中,但可以在其他地方使用。)