NLP&ML入門:數據分析of zhihu.com
剛剛接觸NLP和ML,用Python做了一點東西,也有了一點成果,就分享一下。
這個實驗的初衷是想做一個書上示例的重複:我想知道昵稱和性別的關係——通過昵稱來猜性別(我想不少人肯定都做過)。我選擇了知乎,因為知乎網我比較熟悉,也比較好爬(水平太差)。我發現知乎新頁面幾乎都採用了前端渲染,數據是存在一個叫react-data的屬性裡面的。這是一個JSON,直接解包即可。然後我們就很容易地獲得了用戶基本信息,還可以用同樣的方法獲得一些文章數據之類的。由於此文不是講爬蟲,這裡不作展開討論。
由於知乎(可能)存在一定的反爬機制,爬蟲的速度是非常不理想的。5個爬蟲在3台伺服器運行了大約100多個小時,爬到了80870個用戶和132385篇文章。基本屬於小數據,但我也只是做個小實驗哈?
性別和昵稱關係
用簡單的書上示例代碼和樸素貝葉斯分類器就可以獲得。分類特徵用的是:
def gender_features(word): return {last_letter: word[-1], last_second_letter: word[-2:], # first_letter: word[0] }
為什麼把第一個字去掉了呢?因為真的沒啥用。。。我不知道這個是不是。。可能也許叫。。過度訓練?
只說結論:比random guess稍微好那麼一點點。
accuracy = 0.6588993554784334Most Informative Featureslast_letter = 姐 female : male = 73.5 : 1.0last_second_letter = 小姐 female : male = 38.6 : 1.0last_letter = 娘 female : male = 34.8 : 1.0last_letter = 女 female : male = 30.7 : 1.0last_second_letter = 姑娘 female : male = 30.6 : 1.0last_letter = 麗 female : male = 25.2 : 1.0last_letter = 茜 female : male = 24.3 : 1.0last_letter = 妮 female : male = 21.7 : 1.0last_letter = 玲 female : male = 20.6 : 1.0last_letter = 瑤 female : male = 20.6 : 1.0
我們可以看到,女性的特徵是非常明顯的。我們再來看看錯誤的記錄:
correct=female guess=male name=層層c correct=female guess=male name=SoniaShen correct=male guess=female name=上善若水 correct=male guess=female name=為什麼會這樣呢 correct=female guess=male name=二十八畫生 correct=female guess=male name=Lvat2000 correct=female guess=male name=子有說 correct=female guess=male name=秭歸 correct=male guess=female name=爆穿腸 correct=female guess=male name=Zenobia Yao correct=female guess=male name=小小z correct=male guess=female name=唐公子
很多錯誤……好吧,我覺得我也猜不出來……或者說我也會猜錯?
我又試著加上了description作為特徵。這次使用了jieba分詞器,把其中出現的辭彙全部提取出來。
def gender_description_features(description): features = dict() if description: for word in list(jieba.cut(description)): if word.strip() != "": features[contains(%s) % word.strip()] = True return features
我試著運行了很多次,準確率也一直在0.6-0.7之間徘徊。看上去是真的不大行了。。。
知乎獲贊分布
我們首先要知道獲贊的分布情況。
在我獲得的數據集中,有將近一半是「無贊用戶」(當然這個比例是不準確的,因為我採用的是順藤摸瓜式爬取,人與人之間是緊密相關的)。在小於贊這個比例內,積累用戶總數,與獲贊的對數幾乎是線性關係。也就是說一旦獲贊,獲贊的數目幾乎是指數級增長的。
作為項目的一個小福利,我也分析了Related Users的數目,所謂Related Users就是關注和被關注之和。但我後來發現這個是不準確的。因為我爬蟲寫錯了,導致這個數字不會超過100……QAQ(有興趣的去閱讀源碼)猜猜你有多少個贊?
贊數到底與什麼有關呢?我首先想到的是Description,就是Profile裡面的那個詳細信息。這個猜測好像非常不靠譜,但是由於我剛開始做的時候數據十分有限,所以……
還要解決的一個問題就是,如果還採用貝葉斯分類器,必須對用戶的獲贊數進行離散化處理,才可以進行分類。這裡我採用的標籤是獲贊總數取對數,再取下整。這種方案大概會產生從0開始的6到7個類別。
特徵選擇跟剛才是一致的。我們直接看結果:
accuracy = 0.308Most Informative Featurescontains(妖) = True 6 : 0 = 5431.8 : 1.0contains(餅) = True 6 : 0 = 5431.8 : 1.0contains(搜) = True 6 : 0 = 5431.8 : 1.0contains(暫停) = True 6 : 2 = 1324.1 : 1.0contains(酸奶) = True 6 : 1 = 1321.8 : 1.0contains(師兄) = True 6 : 1 = 1321.8 : 1.0contains(寫字) = True 6 : 0 = 958.5 : 1.0contains(姿勢) = True 6 : 0 = 857.6 : 1.0contains(分答) = True 6 : 3 = 804.1 : 1.0contains(悶騷) = True 6 : 0 = 776.0 : 1.0
可以看到,在高贊用戶中某些詞出現的是相當頻繁的。為什麼會是這樣的結果,我覺得可能與高贊用戶戰線的一致性vs低贊用戶的零散性有關。我們很難從低贊用戶中找到共同的特徵。
另外一方面,accuracy雖然是0.308,比random guess稍微高一點,但注意到分布是嚴重不均勻的,全猜是0,正確率可能也會高達50%!
實驗證明,上面的猜測是非常不靠譜的。
猜猜你有多少個贊?(高級版)
一些不換特徵稍加改進的方法,包括使用LinearSVC分類器,不對贊數進行強要求,而簡單的分高贊和低贊兩類,都能夠提高準確率。
僅僅改變分類器,可以獲得0.516的準確率;使用二元分法,準確率更高,接近0.7。
具體原理不作展開討論(我也不懂)。
從分析人到分析文章
對文章中出現頻率最高的辭彙進行提取,加上文章的長度,作為特徵;標籤仍然跟上面一致,採用獲贊數的對數。
def cut_analyzer(text): features = {length: int(len(text) / 100)} counter = Counter() if text: for word in list(jieba.cut(text)): if word.strip() != "": counter[word.strip()] += 1 for key in counter.most_common(15): if counter[key] < 3: break features[key] = True return features
分析結果顯示:文章長度與獲贊數有很大關係,用詞與獲贊數關係不大。
0.7410484967517752Most Informative Featureslength = 73 4 : 0 = 629.4 : 1.0length = 74 4 : 0 = 449.6 : 1.0length = 158 4 : 0 = 269.7 : 1.0length = 78 4 : 0 = 269.7 : 1.0length = 84 4 : 0 = 269.7 : 1.0length = 100 4 : 0 = 269.7 : 1.0length = 101 4 : 0 = 269.7 : 1.0length = 61 4 : 0 = 269.7 : 1.0length = 50 3 : 0 = 209.8 : 1.0length = 71 3 : 0 = 205.0 : 1.0
(這裡的length都是以100為單位的)
文章長度和獲贊數的關係到底多大呢?
上圖擬合了一條四次方曲線,從曲線中可以看到,在萬級規模的文章當中,基本是越長贊越多的……(看來本文也要寫長點)但是我們可以看到在兩萬以後,贊數發生了一次驟降。個人認為這可能是數據當中存在的某些干擾導致的。我也嘗試過使用五次方六次方曲線,但均無濟於事。
為什麼有的回答那麼(不)受歡迎?
有如下猜測:
- 邏輯清晰,觀點鮮明(遠超本入門文討論範圍)
- 激情(有沒有真情實感)
- 標點使用(???)
- 垃圾信息(廣告等)
- 作者受到的關注
在源碼中有一個標點符號的使用(???)的實驗,但由於實驗結果not helpful,我就不把它列舉在這邊了。
我們主要關注的有兩個。一個是作者受到的關注多不多,還有一個是文章夠不夠激情。
首先我對作者總獲贊數和單篇文章做了一個關係分析。有兩個圖,前一個是30%數據,後一個是90%。至於後10%的數據,樣本太小,參考性不大。
至於文章內包含了多少情感,BosonNLP sentiment score提供了很多幫助。通過分詞系統產生的每一個詞的情感趨向的平方和進行累加,除以文本長度,我們就得到了情感偏離值。情感偏離值和獲贊數的關係如下:
這個分布很像鐘形曲線,但個人認為參考價值略低。我們來看看分類的一些樣本:- (1.299291868814027, [求個opengl公司])
- (3.7892459802902576, [不太好賣!!!])
- (10.274760629713104, [你是什麼樣的人,你就會遇到什麼樣的人。])
- (12.201216876972543, [警察人數太少])
- (12.832526582097051, [謝邀!首先,先天的性善或者性惡並不重要,重要的是社會需要懲惡揚善。其次,人的動物性決定了人的本性是趨利避害。再次,懲惡揚善是為了綜合的群體利益,這可能會傷及群體中某些個體的短期利益。])
- (15.56526918836185, [因為王小能撒貝南時期一去不復返 今日說法從一個普法類節目敗落為一個宣傳警世類節目])
- (15.629396733694772, [屬相相刑,近幾年雙方流年不吉,堅持幾年到2020有緣分結婚。])
- (76.32018283200652, [畫畫將來能幹什麼啊你不會想當畫家吧,畫家都是死了才出名的(出你媽…)])
- (84.36073472927028, [可以加QQ595972391. 提升銷量,打造爆款,引流])
- (101.58486034804461, [數學家的美妙情懷])
- (138.63839574453257, [都是很可愛的藍孩子啊可愛可憐沒人愛 :)])
還有一些偏離值異常高的文本,卻只有幾個字:
- [餓]
- [煩]
- [簡約]
項目源碼:ultmaster/nlp-2017
參考文獻:
- Steven Bird, Evan Klein & Edward Loper, Natural Language Processing with Python
- Python做文本情感分析之情感極性分析
推薦閱讀:
※關於神經網路參數初始化為全0的思考
※自然語言處理透析希拉里和特朗普各自的「演講范兒」
※《Dialogue Act Sequence Labeling using Hierarchical encoder with CRF》閱讀筆記
※自然語言處理入門學習<七>HMM模型解析