我正在尝试在.txt文件的语料库中运行word2vec(在gensim中实现的skip-gram模型,默认窗口大小为5)。我使用的迭代器看起来像这样:
class Corpus(object):
"""Iterator for feeding sentences to word2vec"""
def __init__(self, dirname):
self.dirname = dirname
def __iter__(self):
word_tokenizer = TreebankWordTokenizer()
sent_tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
text = ''
for root, dirs, files in os.walk(self.dirname):
for file in files:
if file.endswith(".txt"):
file_path = os.path.join(root, file)
with open(file_path, 'r') as f:
text = f.read().decode('utf-8')
sentences = sent_tokenizer.tokenize(text)
for sent in sentences:
yield word_tokenizer.tokenize(sent)
这里我使用nltk包中的punkt标记器(使用无监督算法检测句子边界)将文本拆分成句子。然而,当我用一个简单的line.split()
替换它时,即只考虑每个句子作为一行并拆分单词,我得到的时间效率比使用nltk解析器快1.5倍。 'with open'中的代码看起来像这样:
with open(file_path, 'r') as f:
for line in f:
line.decode('utf-8')
yield line.split()
我的问题是word2vec算法被赋予实际句子的句子(我试图用punkt tokenizer做的事情)有多重要?算法中的每个单词是否足以接收位于一行上的周围单词的上下文(这些单词在跨越多行的句子的情况下可能不一定是实际句子),而不是单词的上下文这个词可能在一个跨越几行的句子中。此外,窗口大小在这个中扮演什么样的角色。例如,当窗口大小设置为5时,句子迭代器产生的句子大小是否会停止发挥作用?只有窗口大小决定上下文单词吗?在这种情况下,我应该使用line.split()
而不是尝试使用punkt tokenizer检测实际的句子边界?
我希望我能够充分描述这个问题,我真的很感激任何意见或指示或帮助。
答案 0 :(得分:2)
窗口只是上下文窗口。如果window
设置为5,那么对于当前单词w
,将围绕10个单词作为上下文单词。根据原始的word2vec代码,该单词仅在句子中存在的上下文中训练。如果单词+上下文单词超出句子边界,那么其余的上下文单词将被忽略(近似)。
例如:
考虑一下句子:I am a boy
如果当前单词为boy
且window
为2,那么我们可以观察到没有正确的上下文。在这种情况下,代码将采用向量am
和a
的平均值,并将其视为boy
的上下文(参考word2vec的CBOW模型)。
对于第二个疑问,我使用了没有句子边界的文本语料库,而且word2vec仍然没问题。 (在维基百科语料库中测试过)
希望这可以解决您的疑问。