Skip to main content
 首页 » 编程设计

python之doc2vec/gensim之在时代中改组句子的问题

2025年05月04日17落叶无声

我正在尝试使用优秀的教程开始使用 word2vec 和 doc2vec,here和 here并尝试使用代码示例。我只添加了一个 line_clean() 方法来删​​除标点符号、停用词等。

但是我在使用训练迭代中调用的 line_clean() 方法时遇到了问题。我知道对全局方法的调用搞砸了,但我不确定如何解决这个问题。

Iteration 1 
Traceback (most recent call last): 
  File "/Users/santino/Dev/doc2vec_exp/doc2vec_exp_app/doc2vec/untitled.py", line 96, in <module> 
    train() 
  File "/Users/santino/Dev/doc2vec_exp/doc2vec_exp_app/doc2vec/untitled.py", line 91, in train 
    model.train(sentences.sentences_perm(),total_examples=model.corpus_count,epochs=model.iter) 
  File "/Users/santino/Dev/doc2vec_exp/doc2vec_exp_app/doc2vec/untitled.py", line 61, in sentences_perm 
    shuffled = list(self.sentences) 
AttributeError: 'TaggedLineSentence' object has no attribute 'sentences' 

我的代码如下:

import gensim 
from gensim import utils 
from gensim.models.doc2vec import TaggedDocument 
from gensim.models import Doc2Vec 
import os 
import random 
import numpy 
from sklearn.linear_model import LogisticRegression 
import logging 
import sys 
from nltk import RegexpTokenizer 
from nltk.corpus import stopwords 
 
tokenizer = RegexpTokenizer(r'\w+') 
stopword_set = set(stopwords.words('english')) 
 
 
def clean_line(line): 
    new_str = unicode(line, errors='replace').lower() #encoding issues 
    dlist = tokenizer.tokenize(new_str) 
    dlist = list(set(dlist).difference(stopword_set)) 
    new_line = ' '.join(dlist) 
    return new_line 
 
 
class TaggedLineSentence(object): 
    def __init__(self, sources): 
        self.sources = sources 
 
        flipped = {} 
 
        # make sure that keys are unique 
        for key, value in sources.items(): 
            if value not in flipped: 
                flipped[value] = [key] 
            else: 
                raise Exception('Non-unique prefix encountered') 
 
    def __iter__(self): 
        for source, prefix in self.sources.items(): 
            with utils.smart_open(source) as fin: 
                for item_no, line in enumerate(fin): 
                    yield TaggedDocument(utils.to_unicode(clean_line(line)).split(), [prefix + '_%s' % item_no]) 
 
    def to_array(self): 
        self.sentences = [] 
        for source, prefix in self.sources.items(): 
            with utils.smart_open(source) as fin: 
                for item_no, line in enumerate(fin): 
                    self.sentences.append(TaggedDocument(utils.to_unicode(clean_line(line)).split(), [prefix + '_%s' % item_no])) 
        return(self.sentences) 
 
    def sentences_perm(self): 
        shuffled = list(self.sentences) 
        random.shuffle(shuffled) 
        return(shuffled) 
 
 
def train(): 
    #create a list data that stores the content of all text files in order of their names in docLabels 
    doc_files = [f for f in os.listdir('./data/') if f.endswith('.csv')] 
 
    sources = {} 
    for doc in doc_files: 
        doc2 = os.path.join('./data',doc) 
        sources[doc2] = doc.replace('.csv','') 
 
    sentences = TaggedLineSentence(sources) 
 
 
    # #iterator returned over all documents 
    model = gensim.models.Doc2Vec(size=300, min_count=2, alpha=0.025, min_alpha=0.025) 
    model.build_vocab(sentences) 
 
    #training of model 
    for epoch in range(10): 
        #random.shuffle(sentences) 
        print 'iteration '+str(epoch+1) 
        #model.train(it) 
        model.alpha -= 0.002 
        model.min_alpha = model.alpha 
        model.train(sentences.sentences_perm(),total_examples=model.corpus_count,epochs=model.iter) 
    #saving the created model 
    model.save('reddit.doc2vec') 
    print "model saved"  
 
train() 

请您参考如下方法:

对于最新版本的 gensim,这些教程并不是很好。特别是,使用您自己手动管理 alpha/min_alpha 在循环中多次调用 train() 是个坏主意。它很容易搞砸——例如,错误的事情会发生在你的代码中——并且对大多数用户没有任何好处。不要更改默认的 min_alpha,只调用一次 train() —— 然后它会执行 epochs 迭代,衰减学习-rate alpha 从最大值到最小值正确。

您的特定错误是因为您的 TaggedLineSentence 类没有 sentences 属性——至少在 to_array() 被调用之前没有– 然而代码试图访问那个不存在的属性。

整个 to_array()/sentences_perm() 方法有点破。使用此类可迭代类的原因通常是将大型数据集保留在主内存之外,从磁盘流式传输。但是 to_array() 然后只是加载所有内容,将其缓存在类中 - 消除了可迭代的好处。如果你负担得起,因为完整的数据集很容易放入内存,你可以做...

sentences = list(TaggedLineSentence(sources) 

...从磁盘迭代一次,然后将语料库保存在内存列表中。

通常不需要在训练期间反复洗牌。只有当训练数据存在一些聚集时——比如所有带有特定单词/主题的例子都在排序的顶部或底部粘在一起——原生排序可能会导致训练问题。在这种情况下,在任何训练之前进行一次洗牌应该足以消除结 block 。所以再次假设你的数据适合内存,你可以做...

sentences = random.shuffle(list(TaggedLineSentence(sources) 

...一次,然后你有一个 sentences 可以在 build_vocab() 和 中传递给 Doc2Vec >train() (一次)如下。