0

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

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

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

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

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

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

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

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

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

下面有我的代码的副本。它很简单。显然,我不能包含真实数据,但它应该让读者清楚地了解我正在尝试做什么。

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

部分 B - text2vec::sim2() 函数导致 R studio 在词汇量超过约 140,000 个标记(即列)时关闭。为了避免这种情况,我以大约 200 个为单位处理客户地址。

部分 C - 这是最昂贵的部分。当处理 200 个块的地址时,SECTION A 和 SECTION B 大约需要 2 分钟。但是 SECTION 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))
4

1 回答 1

0

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

可能有几种解决方案:

  1. 如果mat_sim不是很大,则转换为密集的as.matrix,然后使用apply
  2. 更好的是,您可以转换mat_sim为三元组格式的稀疏矩阵,as(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
    

另外作为一个建议 - 我建议尝试char_tokenizer()使用 ngrams(例如 size c(3, 3))来“模糊”匹配地址的不同拼写和缩写。

于 2018-02-16T16:40:13.780 回答