天天看點

R語言實作︱局部敏感雜湊演算法(LSH)解決文本機械相似性的問題(二,textreuse介紹)一、語料資料格式識别與導入二、機械分詞技術三、hash化四、簡單文本相似性比較五、并行

———————————————————————————

上一篇(R語言實作︱局部敏感雜湊演算法(LSH)解決文本機械相似性的問題(一,基本原理))講解了LSH的基本原理,筆者在想這麼牛氣沖天的方法在R語言中能不能實作得了呢?

于是在網上搜尋了一下,真的發現了一個叫textreuse的包可以實作這樣的功能,而且該包較為完整,可以很好地滿足要求。

現在的版本是 0.1.3,最近的更新的時間為 2016-03-28。

國内貌似比較少的用這個包來實作這個功能,畢竟R語言在運作大規模資料的性能比較差,而LSH又是處理大規模資料的辦法,是以可能國内比較少的用R來執行這個算法。

回顧一下LSH的算法步驟:

1、一般的步驟是先把資料點(可以是原始資料,或者提取到的特征向量)組成矩陣;

2、第一次hash functions(有多個哈希函數,是從某個哈希函數族中選出來的)哈希成一個叫“簽名矩陣(Signature Matrix)”的東西,這個矩陣可以直接了解為是降維後的資料,此時用simhash、minhash來做,第一步的hash過程可以使用不同的functions來做;

3、第二次LSH把Signature Matrix哈希一下,就得到了每個資料點最終被hash到了哪個bucket裡,如果新來一個資料點,假如是一個網頁的特征向量,我想找和這個網頁相似的網頁,那麼把這個網頁對應的特征向量hash一下,看看它到哪個桶裡了。

那麼本篇詳細介紹一下textreuse包的基本功能,分為:

一、資料格式識别與導入

二、機械分詞技術

三、hash函數

四、簡單文本相似性比較

五、并行

————————————————————————————————————————

一、語料資料格式識别與導入

後續的LSH必須要用到textreuse指定的格式(類似tm包),是以資料導入過程之後還有一步資料轉化的過程。是以在textreuse包中有兩種方法實作以上功能:

1、直接從檔案讀入,同時轉化為指定格式(tm包格式),函數有兩個:TextReuseTextDocument、TextReuseCorpus;

2、先用正常方法讀入R環境,然後轉化資料格式,同樣可以用上述兩個函數。

這兩個函數是textreuse的資料基礎也是關鍵。兩個函數在轉化的過程中就可以直接分詞+基本hash形成簽名矩陣。當然,也可以設定以下兩個函數tokenizer=NULL,hash_func=NULL,先轉化,然後在自己分詞與hash化。

1、TextReuseTextDocument

這是textreuse比較獨特的一個讀入函數。

TextReuseTextDocument(text, file = NULL, meta = list(),
  tokenizer = tokenize_ngrams, ..., hash_func = hash_string,
  minhash_func = NULL, keep_tokens = FALSE, keep_text = TRUE,
  skip_short = TRUE)           

複制

file參數可以讀取檔案file資料,需要設定檔案目錄;tokeniezer表示讀入的資料自動分詞;同時讀入還将其直接哈希化了(hash_func)。

library(textreuse)
file <- system.file("extdata/ats/remember00palm.txt", 
                    package = "textreuse")
doc <- TextReuseTextDocument(file = file, meta = list("publisher" = "ATS"),
                             tokenizer = tokenize_ngrams, n = 5,
                             keep_tokens = TRUE)           

複制

來看看官方案例,其中的tokenizer和n後續會說,n代表多元組。官方案例中是讀入單個檔案,不知道是否能批量讀入某檔案夾裡的檔案。但是批量讀取的情況下,還是用下面的函數比較合适。

2、TextReuseCorpus

這個函數基于tm包演化而來的,跟上面的函數差不多。

TextReuseCorpus(paths, dir = NULL, text = NULL, meta = list(),
  progress = interactive(), tokenizer = tokenize_ngrams, ...,
  hash_func = hash_string, minhash_func = NULL, keep_tokens = FALSE,
  keep_text = TRUE, skip_short = TRUE)           

複制

該函數的讀入文本範圍比較廣,txt、字元串的都沒問題,如果有其他拓展名的檔案可以用dir來讀入。

而且可以批量導入某一個檔案夾中所有的檔案内容。

如果有多個檔案的基礎上,也可以通過corpus[["remember00palm"]]、corpus[c("calltounconv00baxt", "lifeofrevrichard00baxt")]這樣的形式選中對應的文本内容。

3、函數檢視與基本内容修改

以前在使用tm包的使用就覺得轉化格式之後,檢視起來就不是那麼友善了。同樣在這有一些函數可以檢視裡面具體内容。

其中轉化了之後的資料會帶有一些資訊,通過meta可以更改這些資訊,比如文檔的ID資訊。

#檢視基本資訊
meta(doc)      #檢視基本資訊 id
tokens(doc)    #機械分詞内容
wordcount(doc) #總詞數
content(doc)   #檢視原始内容
names(doc)     #每個文檔的姓名(可修改)           

複制

其中wordcount如果有多個檔案,那麼就會計算每個檔案的字數,這個跟table有點像。

如果需要修改其中的一些内容可以用meta。

#修改相關資訊
meta(doc)$id
meta(doc, "id")
meta(doc, "date") <- 1865             

複制

————————————————————————————————————————

二、機械分詞技術

分詞技術可以分為機械分詞以及基于統計序列标注的分詞技術,具體的拓展可以看我另外一篇部落格的内容:NLP︱中文分詞技術小結、幾大分詞引擎的介紹與比較

在R語言中專門用來中文分詞的有jiebeR和Rwordseg,現在這兩個大多數的分詞技術都是基于序列标注的,是以計算量相對較大,但是文本機械相似性對分詞沒有那麼高的要求,要求分成單個字元串的形式就可以滿足要求了。

是以,textreuse就可以比較方面的實作簡單的機械分詞,隻是把文檔的内容去掉噪音+分開成字元串。

現在假如有一句話:

text <- "本次講習班主要圍繞知識擷取、學習及推理技術,以及基于知識圖譜的應用進展,邀請相關領域的專家學者做主題報告。"           

複制

1、機械分詞——tokenize_words()

> tokenize_words(text)
 [1] "本次"   "講習班" "主要"   "圍繞"   "知識"   "擷取"   "學習"   "及"     "推理"   "技術"  
[11] "以及"   "基于"   "知識"   "圖"     "譜"     "的"     "應用"   "進展"   "邀請"   "相關"  
[21] "領域"   "的"     "專家"   "學者"   "做"     "主題"   "報告"             

複制

從效果來看,還不錯,竟然能把一些詞提取出來,而且去掉了标點,而且速度較快。貌似可以再這樣的分詞技術上做很多後續的探究。

2、斷句——tokenize_sentences

> tokenize_sentences(text)
[1] "本次講習班主要圍繞知識擷取 學習及推理技術 以及基于知識圖譜的應用進展 邀請相關領域的專家學者做主題報告"           

複制

斷句的原理是根據标點符号的來進行。

3、多元組——tokenize_ngrams

可能對中文的支援不好,輸出的格式是亂碼的,是以在這不能示範中文,而是英文。

> text <- "How many roads must a man walk down? The answer is blowin' in the wind."
> tokenize_ngrams(text, n = 3)
 [1] "how many roads"   "many roads must"  "roads must a"     "must a man"      
 [5] "a man walk"       "man walk down"    "walk down the"    "down the answer" 
 [9] "the answer is"    "answer is blowin" "is blowin in"     "blowin in the"   
[13] "in the wind"               

複制

4、跨越式多元組——tokenize_skip_ngrams

> tokenize_skip_ngrams(text, n = 3, k = 2)
 [1] "how must walk"      "many a down"        "roads man the"      "must walk answer"  
 [5] "a down is"          "man the blowin"     "walk answer in"     "down is the"       
 [9] "the blowin wind"    "how roads a"        "many must man"      "roads a walk"      
[13] "must man down"      "a walk the"         "man down answer"    "walk the is"       
[17] "down answer blowin" "the is in"          "answer blowin the"  "is in wind"        
[21] "how many roads"     "many roads must"    "roads must a"       "must a man"        
[25] "a man walk"         "man walk down"      "walk down the"      "down the answer"   
[29] "the answer is"      "answer is blowin"   "is blowin in"       "blowin in the"     
[33] "in the wind"                  

複制

以上的3、4的多元組的具有很大的意義。但是對中文的支援一直不好,輸出的中文是亂碼的,這個暫時筆者沒有去細緻探究。

5、詩詞的斷句——英文場景

包的作者自己寫了一個斷句的函數。

> poem <- "Roses are red\nViolets are blue\nI like using R\nAnd you should too"
> string=poem
> tokenize_lines <- function(string) {
+   stringr::str_split(string, "\n+")[[1]]
+ }
> 
> tokenize_lines(poem)
[1] "Roses are red"      "Violets are blue"   "I like using R"     "And you should too"           

複制

通過\來分割。

————————————————————————————————————————

三、hash化

該包裡面有挺多的hash函數:hashes、minhashes、rehash、hash_string

R語言中構造hash函數也有專門的包:digest

其中hash_string(詞),有n個詞就hash成n個hash值;

而minhash則是把文檔,比如一個文檔1W個詞,還是固定的一個文檔,300維,有一個比較有效的降維功效,同時也不會損失太多資訊量,原來相似的文本表現的還是相似。

rehash則可以自己選擇:

rehash(x, func, type = c("hashes", "minhashes"))           

複制

自由選擇用hashes還是minhashes。

————————————————————————————————————————

四、簡單文本相似性比較

相似性距離在上篇講過,這裡不贅述。一般有兩類:海明距離(用在simhash)、Jaccard距離(用在Minhash)

如果隻是不hash,直接看樣本的相似性,必然是Jaccard要好一些。

> a <- tokenize_words(paste("How does it feel, how does it feel?",
+                           "To be without a home",
+                           "Like a complete unknown, like a rolling stone"))
> b <- tokenize_words(paste("How does it feel, how does it feel?",
+                           "To be on your own, with no direction home",
+                           "A complete unknown, like a rolling stone"))
> #?similarity-functions
> jaccard_similarity(a, b)      #相似程度
[1] 0.65           

複制

此時可以看出jaccard的相似性距離為0.65。

當然textreuse包中同樣有其他的一些距離,可以來看看:

> jaccard_dissimilarity(a, b)   #相差程度=1-相似程度
[1] 0.35
> jaccard_bag_similarity(a, b)  #最大值為0.5
[1] 0.4
> ratio_of_matches(a, b)        #在ab交叉/b總數
[1] 0.75           

複制

jaccard_dissimilarity=1-jaccard_similarity;

ratio_of_matches原理跟Jaccard差不多也是根據集合來求相似性的。

————————————————————————————————————————

五、并行

在求解過程中,一般來說,語料生成以及分詞部分耗費計算量,可以采用并行算法,textreuse基于parallel拓展了該包的性能,可以設定核心數。但是遺憾的是不能再windows系統下設定。

options("mc.cores" = 4L)           

複制

每每以為攀得衆山小,可、每每又切實來到起點,大牛們,緩緩腳步來俺筆記葩分享一下吧,please~

———————————————————————————