———————————————————————————
上一篇(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~
———————————————————————————