将文档与text2vec匹配 - 缩放问题

时间:2018-02-16 07:21:20

标签: r text-mining text2vec

我在缩放文本匹配程序时遇到了一些问题。我正在使用text2vec,它提供了非常好的和快速的结果。

我遇到的主要问题是操纵text2vec :: sim2()函数返回的大矩阵。

首先,我的硬件/操作系统设置的一些细节:Windows 7,12核,约3.5 GHz和128 Gb内存。它是一台非常好的机器。

其次,我的R程序试图实现的一些基本细节。

我们拥有一个包含1000万个独特规范地址的数据库,用于地址中的每个房屋/企业。这些参考地址还具有每个条目的纬度和经度信息。

我正在尝试将这些参考地址与我们数据库中的客户地址相匹配。我们有大约600,000个客户地址。这些客户地址的质量不好。一点都不好!它们存储为单个字符串字段,对输入进行绝对零检查。

匹配这些地址的技术策略非常简单。创建客户地址和参考地址的两个文档术语矩阵(DTM),并使用余弦相似性查找与特定客户地址最相似的参考地址。一些客户地址非常差,导致余弦相似度非常低 - 因此,对于这些地址,将分配“不匹配”。

尽管这是一个非常简单的解决方案,但获得的结果非常令人鼓舞。

但是,我在缩放事物方面遇到了问题....我想知道是否有人有任何建议。

下面有我的代码副本。它非常简单。显然,我不能包含真实数据,但它应该让读者清楚地知道我想要做什么。

A部分 - 即使在完整的600,000 * 1000万输入数据集上也能很好地工作。

第B节 - 当词汇量超过大约140,000个令牌(即列)时,text2vec :: sim2()函数会导致R studio关闭。为了避免这种情况,我以大约200个块的方式处理客户地址。

C部分 - 这是最昂贵的部分。处理200块的地址时,A部分和B部分大约需要2分钟。但是使用(我认为是超快速函数)的C部分需要大约5分钟来处理1000万行* 200列矩阵。

组合,SECIONS A:C大约需要7分钟来处理200个地址。由于有600,000个地址需要处理,这需要大约14天的时间来处理。

他们是想让这段代码运行得更快的想法......?

rm(list = ls())
library(text2vec)
library(dplyr)


# Create some test data

# example is 10 entries.  
# but in reality we have 10 million addresses
vct_ref_address <- c("15 smith street beaconsfield 2506 NSW", 
"107 orange grove linfield 2659 NSW",
"88 melon drive calton 3922 VIC", 
"949 eyre street sunnybank 4053 QLD",
"12 black avenue kingston 2605 ACT", 
"5 sweet lane 2004 wynyard NSW",
"32 mugga way 2688 manuka ACT",
"4 black swan avenue freemantle 5943 WA",
"832 big street narrabeet 2543 NSW", 
"5 dust road 5040 NT")


# example is 4 entries
# but in reality, we have 1.5 million addresses
vct_test_address <- c("949 eyre street sunnybank 4053 QLD",  
"1113 completely invalid suburb with no post code QLD", 
"12 black road kingston 2605 ACT",  
"949 eyre roaod sunnybank 4053 QLD" )

# ==========================
# SECTION A ===== prepare data
# A.1 create vocabulary 
t2v_token <- text2vec::itoken(c(vct_test_address, vct_ref_address),  progressbar = FALSE)
t2v_vocab <- text2vec::create_vocabulary(t2v_token)
t2v_vectorizer <- text2vec::vocab_vectorizer(t2v_vocab)
# A.2 create document term matrices dtm
t2v_dtm_test <- text2vec::create_dtm(itoken(vct_test_address, progressbar = FALSE), t2v_vectorizer)
t2v_dtm_reference <- text2vec::create_dtm(itoken(vct_ref_address, progressbar = FALSE), t2v_vectorizer)

# ===========================
# SECTION B ===== similarity matrix
mat_sim <- text2vec::sim2(t2v_dtm_reference, t2v_dtm_test,  method = 'cosine', norm = 'l2')

# ===========================
# SECTION C ===== process matrix
vct_which_reference <- apply(mat_sim, 2, which.max)
vct_sim_score <- apply(mat_sim, 2, max)

# ============================
# SECTION D ===== apply results
# D.1 assemble results
df_results <- data.frame(
test_addr = vct_test_address,
matched_addr = vct_ref_address[vct_which_reference],
similarity =  vct_sim_score )

# D.2 print results
df_results %>% arrange(desc(similarity))

1 个答案:

答案 0 :(得分:0)

步骤 C 中的问题是mat_sim是稀疏的,并且所有apply调用都会使列/行子集超级慢(并将稀疏向量转换为密集)

可能有几种解决方案:

  1. 如果mat_sim不是很大,请使用as.matrix转换为密集,然后使用apply
  2. 最好使用mat_simas(mat_sim, "TsparseMatrix")转换为三元组格式的稀疏矩阵,然后使用data.table获取最大元素的索引。这是一个例子:

    library(text2vec)
    library(Matrix)
    data("movie_review")
    it = itoken(movie_review$review, tolower, word_tokenizer)
    dtm = create_dtm(it, hash_vectorizer(2**14))
    
    
    mat_sim = sim2(dtm[1:100, ], dtm[101:5000, ])
    mat_sim = as(mat_sim, "TsparseMatrix")
    
    library(data.table)
    
    # we add 1 because indices in sparse matrices in Matrix package start from 1
    mat_sim_dt = data.table(row_index = mat_sim@i + 1L, col_index = mat_sim@j + 1L, value = mat_sim@x)
    
    res = mat_sim_dt[, 
            { k = which.max(value); list(max_sim = value[[k]], row_index = row_index[[k]]) }, 
            keyby = col_index]  
    res
    
  3. 另外作为附带建议 - 我建议尝试使用ngrams char_tokenizer()(例如大小为c(3, 3))来“模糊”匹配地址的不同拼写和缩写。