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新聞"在主新聞下方,還提供多條相似的新聞。

那麼如何才能找到 "相似" 的文本呢?
注意我這裡必須要解釋一下相似的幾個不同場景,否則很容易誤入魔道。
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],那麼可以将餘弦定理改寫成下面的形式:
數學家已經證明,餘弦的這種計算方法對n維向量也成立。假定a和b是兩個n維向量,a是 [a1, a2, ..., an] ,b是 [b1, b2, ..., bn] ,則a與b的夾角θ的餘弦等于:
使用這個公式,我們就可以得到,句子a與句子b的夾角的餘弦。
餘弦值越接近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>