2

我需要使用来自Google Books N-grams的词汇数据来构造一个术语共现的(稀疏!)矩阵(其中行是单词,列是相同的单词,并且单元格反映了它们在同一个词中出现的次数上下文窗口)。然后,生成的 tcm 将用于测量一堆词汇统计数据,并作为向量语义方法(Glove、LSA、LDA)的输入。

作为参考,Google Books (v2) 数据集的格式如下(制表符分隔)

ngram      year    match_count    volume_count
some word  1999    32             12            # example bigram

但是,问题当然是,这些数据非常庞大。虽然,我只需要某些几十年的数据子集(大约 20 年的 ngrams),并且我对最多 2 个上下文窗口感到满意(即,使用 trigram 语料库)。我有一些想法,但没有一个看起来特别好。

-想法1-最初或多或少是这样的:

# preprocessing (pseudo)
for file in trigram-files:
    download $file
    filter $lines where 'year' tag matches one of years of interest
    find the frequency of each of those ngrams (match_count)
    cat those $lines * $match_count >> file2
     # (write the same line x times according to the match_count tag)  
    remove $file

# tcm construction (using R)
grams <- # read lines from file2 into list
library(text2vec)
# treat lines (ngrams) as documents to avoid unrelated ngram overlap
it         <- itoken(grams)
vocab      <- create_vocabulary(it)
vectorizer <- vocab_vectorizer(vocab, skip_grams_window = 2)
tcm        <- create_tcm(it, vectorizer) # nice and sparse

但是,我有一种预感,这可能不是最好的解决方案。ngram 数据文件已经包含 n-gram 形式的共现数据,并且有一个标签给出频率。我感觉应该有更直接的方法。

-想法 2-我还考虑将每个过滤的 ngram 仅一次放入新文件中(而不是复制它match_count多次),然后创建一个空的 tcm,然后循环整个(按年份过滤的)ngram 数据集并记录实例(使用match_count标签)任何两个词同时出现以填充 tcm。但是,同样,数据很大,这种循环可能需要很长时间。

- 想法 3-我发现了一个名为google-ngram-downloader的 Python 库,它显然具有共现矩阵创建功能,但查看代码,它将创建一个常规(非稀疏)矩阵(考虑到大多数情况,这将是巨大的条目是 0),并且(如果我做对了)它只是循环遍历所有内容(我假设对这么多数据的 Python 循环会超级慢),所以它似乎更针对相当小的数据子集。

编辑 -Idea 4-遇到这个旧的 SO 问题,询问有关使用 Hadoop 和 Hive 执行类似任务的问题,简短的回答带有断开的链接和关于 MapReduce 的评论(我都不熟悉,所以我不知道在哪里开始)。


我想我不能成为第一个需要处理这样一个任务的人,因为 Ngram 数据集的流行,以及在 tcm 或 dtm 输入上运行的(非 word2vec)分布式语义方法的流行; 因此->

...问题:从 Google Books Ngram 数据构建词项共现矩阵的更合理/有效的方法是什么?(无论是完全不同的提议想法的变体;R 首选但不是必需的)

4

1 回答 1

0

我会告诉你如何做到这一点。但它可以在几个地方进行改进。为了更好的解释性,我特意写成“spagetti-style”,但它可以推广到不止三元组

ngram_dt = data.table(ngram = c("as we know", "i know you"), match_count = c(32, 54))
# here we split tri-grams to obtain words
tokens_matrix = strsplit(ngram_dt$ngram, " ", fixed = T) %>% simplify2array()

# vocab here is vocabulary from chunk, but you can be interested first 
# to create vocabulary from whole corpus of ngrams and filter non 
# interesting/rare words

vocab = unique(tokens_matrix)
# convert char matrix to integer matrix for faster downstream calculations 
tokens_matrix_int = match(tokens_matrix, vocab)
dim(tokens_matrix_int) = dim(tokens_matrix)

ngram_dt[, token_1 := tokens_matrix_int[1, ]]
ngram_dt[, token_2 := tokens_matrix_int[2, ]]
ngram_dt[, token_3 := tokens_matrix_int[3, ]]

dt_12 = ngram_dt[, .(cnt = sum(match_count)), keyby = .(token_1, token_2)]
dt_23 = ngram_dt[, .(cnt = sum(match_count)), keyby = .(token_2, token_3)]
# note here 0.5 - discount for more distant word - we follow text2vec discount of 1 / distance
dt_13 = ngram_dt[, .(cnt = 0.5 * sum(match_count)), keyby = .(token_1, token_3)]

dt = rbindlist(list(dt_12, dt_13, dt_23))
# "reduce" by word indices again - sum pair co-occurences which were in different tri-grams
dt = dt[, .(cnt = sum(cnt)), keyby = .(token_1, token_2)]

tcm = Matrix::sparseMatrix(i = dt$token_1, j = dt$token_2, x = dt$cnt, dims = rep(length(vocab), 2), index1 = T, 
                   giveCsparse = F, check = F, dimnames = list(vocab, vocab))
于 2017-01-25T18:33:17.373 回答