天天看點

對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析

postgresql , 文本相似性分析 , tf , idf , tf-idf , tag , 相關性 , 餘弦相關性 , 線性相關性 , 關鍵詞 , tfidf向量 , rum , smlar , cosine

前面介紹了tf-idf算法,以及它在文本分析中的應用(提取關鍵詞),參考如下。

<a href="https://github.com/digoal/blog/blob/master/201701/20170116_02.md">《文本(關鍵詞)分析 - tf(term frequency 詞頻) idf(inverse document frequency 逆向文本頻率)》</a>

<a href="https://github.com/digoal/blog/blob/master/201701/20170116_03.md">《postgresql 與 tf-idf 文本相似算法》</a>

除了提取文章的關鍵詞,另外一方面,比較兩篇文本的相似性也是一個比較常見的應用,例如在搜尋引擎中,比如,"google新聞"在主新聞下方,還提供多條相似的新聞。

對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析

那麼如何才能找到 "相似" 的文本呢?

注意我這裡必須要解釋一下相似的幾個不同場景,否則很容易誤入魔道。

1. 打個比方,很多人寫作文,會引用一些名人名句,那麼這些引用了同樣的名人名句的文章他們具有相似性嗎?

2. 小時候估計很多小朋友都抄過作業,甚至有一些考試的時候,會出現一模一樣的卷子,答案完全一緻。他們相似嗎?

3. 老師帶小朋友出去春遊,回來叫大夥寫春遊遊記,大家寫的内容可能大緻上差不多,這些文章相似嗎?

其實以上三個問題,都是相似性問題,隻是要找出他們,用到的方法可能不一樣(或者說面向它們的算法可以不一樣,以提升效率)。

1. 文章的部分内容相似,包含了一模一樣的詞句,這個其實是全文檢索的範疇,比如我搜一下哪些文章用到了這些名人名句。

在postgresql中,我們可以使用tsvector, tsquery, gist/gin/rum索引來實作高效的全文檢索

<a href="https://github.com/digoal/blog/blob/master/201610/20161019_01.md">《postgresql 全文檢索加速 快到沒有朋友 - rum索引接口(潘多拉魔盒)》</a>

<a href="https://github.com/digoal/blog/blob/master/201612/20161231_01.md">《從難纏的模糊查詢聊開 - postgresql獨門絕招之一 gin , gist , sp-gist , rum 索引原理與技術背景》</a>

<a href="https://github.com/digoal/blog/blob/master/201611/20161115_01.md">《聊一聊雙十一背後的技術 - 分詞和搜尋》</a>

<a href="https://github.com/digoal/blog/blob/master/201611/20161118_01.md">《聊一聊雙十一背後的技術 - 毫秒分詞算啥, 試試正則和相似度》</a>

2. 完全一緻的文本,其實通過文本的hash值上的b-tree或hash index,可以快速的搜尋到。

3. 小朋友們寫的春遊遊記,看起來肯定不一樣,但是大家可能都會展現一些共同點,比如遊玩中遇到的一些事情,看到的一些風景啥。

如果使用tf-idf算法來提取小朋友們的文章關鍵詞,那麼是不是可以通過文章關鍵詞來對比不同文章的相似性呢?

(建議tf使用相對頻率(出現次數/全文總詞組(計算重複值)數),避免因為文章長短帶來的差異)

是以第三個問題變成了關鍵詞的相似性計算方法。

我們首先來了解幾種相似性算法。

1801年,意大利天文學家朱賽普·皮亞齊發現了第一顆小行星谷神星。

經過40天的跟蹤觀測後,由于谷神星運作至太陽背後,使得皮亞齊失去了谷神星的位置。

随後全世界的科學家利用皮亞齊的觀測資料開始尋找谷神星,但是根據大多數人計算的結果來尋找谷神星都沒有果。

時年24歲的高斯也計算了谷神星的軌道。奧地利天文學家海因裡希·奧爾伯斯根據高斯計算出來的軌道重新發現了谷神星。

這是一個線性回歸分析linear regression, 最小二乘法least-squares-fit的小故事。

背景知識參考:

<a href="https://en.wikipedia.org/wiki/correlation_coefficient">https://en.wikipedia.org/wiki/correlation_coefficient</a>

<a href="https://en.wikipedia.org/wiki/correlation_and_dependence">https://en.wikipedia.org/wiki/correlation_and_dependence</a>

<a href="https://en.wikipedia.org/wiki/coefficient_of_determination">https://en.wikipedia.org/wiki/coefficient_of_determination</a>

線性回歸分析,除了用于天文,其實還有很多應用,比如金融預測,天氣預報,疾病預測等。

下面我給了一些在postgresql中使用一進制、多元回歸的應用例子。

<a href="https://github.com/digoal/blog/blob/master/201503/20150303_02.md">《用postgresql了解一些統計學術語以及計算方法和表示方法 - 1》</a>

<a href="https://github.com/digoal/blog/blob/master/201503/20150303_03.md">《postgresql aggregate function 2 : aggregate functions for statistics》</a>

<a href="https://github.com/digoal/blog/blob/master/201503/20150303_01.md">《在postgresql中用線性回歸分析(linear regression) - 實作資料預測》</a>

<a href="https://github.com/digoal/blog/blob/master/201503/20150304_01.md">《postgresql 線性回歸 - 股價預測 1》</a>

<a href="https://github.com/digoal/blog/blob/master/201503/20150305_01.md">《在postgresql中用線性回歸分析linear regression做預測 - 例子2, 預測未來數日某股收盤價》</a>

<a href="https://github.com/digoal/blog/blob/master/201502/20150228_01.md">《postgresql 統計資訊之 - 邏輯與實體存儲的線性相關性》</a>

<a href="https://github.com/digoal/blog/blob/master/201604/20160403_01.md">《postgresql 計算 任意類型 字段之間的線性相關性》</a>

那麼線性回歸和文本相似性分析有什麼關系嗎?

我個人了解,它也可以用于計算兩個相似tf-idf向量的相關性,通過相關性來判斷他們的相似性。

算出來的相關性越接近1,越相似。

比如:

但是它們的關鍵詞并不完全重疊,是以有兩種修正方法。

1. 求相交關鍵詞的相關性,同時使用權重進行修正(如通過相交集的分值占比,或者元素個數占比來權重)。

相交線性相關計算的是以下兩組值的線性相關性=1

2. 将兩篇文章的關鍵詞取并集,然後在對應的文本中繼續取出其詞語的tf-idf值,得到兩組向量,再計算兩組向量的相關性。

例如

sql如下

3. 如果你覺得并集的确實關鍵詞的tf-idf不好取,也可以用0直接代替缺失詞的tf-idf。

最簡單的還是直接求交集的相關性,索引實作也比較簡單,然後使用權重。

除了線性相關,還有一種方法計算文本相似性:餘弦相似算法

同樣需要提取關鍵詞的tf-idf值向量,然後對兩組相交向量進行計算,是以依舊需要通過相交集的分值占比,或者元素個數占比來權重進行修正。

背景知識

<a href="http://www.ruanyifeng.com/blog/2013/03/cosine_similarity.html">http://www.ruanyifeng.com/blog/2013/03/cosine_similarity.html</a>

<a href="http://en.wikipedia.org/wiki/cosine_similarity">http://en.wikipedia.org/wiki/cosine_similarity</a>

下面的例子取自

為了簡單起見,我們先從句子着手。

請問怎樣才能計算上面兩句話的相似程度?

基本思路是:如果這兩句話的用詞越相似,它們的内容就應該越相似。是以,可以從詞頻入手,計算它們的相似程度。

第一步,分詞。

第二步,列出所有的詞。

第三步,計算詞頻。

第四步,寫出詞頻向量,注意對齊關鍵詞。

到這裡,問題就變成了如何計算這兩個向量的相似程度。

假定a向量是[x1, y1],b向量是[x2, y2],那麼可以将餘弦定理改寫成下面的形式:

對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析
對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析

數學家已經證明,餘弦的這種計算方法對n維向量也成立。假定a和b是兩個n維向量,a是 [a1, a2, ..., an] ,b是 [b1, b2, ..., bn] ,則a與b的夾角θ的餘弦等于:

對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析

使用這個公式,我們就可以得到,句子a與句子b的夾角的餘弦。

對盜圖、盜文、盜墓深惡痛絕嗎?PostgreSQL結合餘弦、線性相關算法 在文本、圖檔、數組相似 等領域的應用 - 3 rum, smlar應用場景分析

餘弦值越接近1,就表明夾角越接近0度,也就是兩個向量越相似,這就叫"餘弦相似性"。是以,上面的句子a和句子b是很相似的,事實上它們的夾角大約為20.3度。

由此,我們就得到了"找出相似文章"的一種算法:

"餘弦相似度"是一種非常有用的算法,隻要是計算兩個向量的相似程度,都可以采用它。

其原理與餘弦近似的算法類似,同時還允許使用者自定義相似度公式。

但是目前僅支援内置類型數組的gin,gist索引,也就是說針對自定義資料類型的數組,不能使用索引進行檢索。

目前,smlar更适合于非複合類型(即内置類型數組)的檢索,比如屬性固定的相似度檢索。

例如已經抽象好了固定的1000個屬性,每行代表一個對象,存儲了這個對象的每個屬性的打分。

比如标簽類應用,通過标簽向量的近似度,找出相似人群。

又比如圖像像素點的rgb值,每張圖像的大小一緻,可以通過像素點向量進行相似度比較。

當然,它也支援其他内置類型數組的相似度比較,隻是元素沒有權重!!!!!,正如我前面說的,你要tf-idf,暫時沒門。

ps:

如果我們擴充smlar這個插件,讓它的複合類型數組能支援gin, gist索引的話,它的适用範圍就更加廣闊了。

在應用中算出關鍵詞以及tf-idf,作為複合類型的向量數組,将其數組内容存入資料庫,在資料庫中隻需要計算兩組向量的相似性。(這個支援索引會不會很強大呢)

smlar插件還有很大的潛力可挖掘。

通過關鍵詞計算文本的相似性是一種簡化的方法,精确度不一定高。

還有一種方法,比對整篇文章的相似性,postgresql的rum插件提供了這種能力,當然它實際上也支援關鍵詞的手段。

關于整篇文章的用法就不介紹了,就是普通的用法

使用全文比較的好處當然是準确度更高,但是帶來一個問題,需要存儲更多的詞,消耗更多的存儲空間,同時效率會有所下降(相比隻存儲關鍵詞時)。

下面以關鍵詞為例,介紹rum插件和關鍵詞結合的用法

(因為tsvector裡面不存儲tf-idf,是以我們這裡需要存儲的是詞頻)

表結構如下(假設你已經将資料如上格式存入了tbl_test表的info字段)

接下來要做的是查詢相似性(rank)

使用rum查出相似的文本,程式的輸入就是tsquery,使用|拼裝即可。

(注意, 因為tsquery沒有權重資訊,是以輸入時不支援權重或tf,是以rum插件的方法計算的ts_rank并不是通過餘弦相似的算法計算的,至少目前并不是這樣)

雖然不支援權重,但是你可以輸入重複值,來加重這個詞的比重

加重place比重

rum同樣支援不同的rank算法,使用一個整數來表示,他們可以相加(1~32)

算法的差異,具體要參考rum/src/rum_ts_utils.c代碼,看看加了這些值後,算法有什麼差異。

使用自定義method的query 例如 :

同時我們也可以使用傳統的postgresql rank計算值進行排序(隻不過按傳統的rank排序不支援索引)

如果要讓rum支援使用關鍵詞的話,用起來有點繁瑣,rum的todo裡已經說了,接下來會引入tf-idf,以後用起來就不繁瑣了。

性能測試 (1000萬測試資料,每條記錄33個關鍵詞)

不管是smlar或者rum,目前對文本的關鍵詞向量化相似查詢都有一定的缺陷,smlar目前更适合固定屬性的屬性值組成的資料的相似查詢。

rum的算法并不是餘弦向量化算法,雖然它也支援相似查詢,但是如果你就認準了向量化的算法,那還是等等吧。

個人還是傾向使用smlar插件,你可以先不加tf-idf權重,将關鍵詞存成數組即可,如下:

threshold越大,說明相似度越高,要求越嚴苛,複合條件的記錄就越少,速度也越快。

smlar用法詳解參考

資料挖掘-基于貝葉斯算法及knn算法的newsgroup18828文本分類器的java實作

<a href="http://blog.csdn.net/yangliuy/article/details/7400984">http://blog.csdn.net/yangliuy/article/details/7400984</a>

<a href="http://blog.csdn.net/yangliuy/article/details/7401142">http://blog.csdn.net/yangliuy/article/details/7401142</a>

繼續閱讀