天天看點

白話機器學習算法理論+實戰之樸素貝葉斯

1. 寫在前面

如果想從事資料挖掘或者機器學習的工作,掌握常用的機器學習算法是非常有必要的,比如我之前寫過的一篇十大機器學習算法的小總結,在這簡單的先捋一捋, 常見的機器學習算法:

  • 監督學習算法:邏輯回歸,線性回歸,決策樹,樸素貝葉斯,K近鄰,支援向量機,內建算法Adaboost等
  • 無監督算法:聚類,降維,關聯規則, PageRank等

為了詳細的了解這些原理,曾經看過西瓜書,統計學習方法,機器學習實戰等書,也聽過一些機器學習的課程,但總感覺話語裡比較深奧,讀起來沒有耐心,并且理論到處有,而實戰最重要, 是以在這裡想用最淺顯易懂的語言寫一個白話機器學習算法理論+實戰系列。

個人認為,了解算法背後的idea和使用,要比看懂它的數學推導更加重要。idea會讓你有一個直覺的感受,進而明白算法的合理性,數學推導隻是将這種合理性用更加嚴謹的語言表達出來而已,打個比方,一個梨很甜,用數學的語言可以表述為糖分含量90%,但隻有親自咬一口,你才能真正感覺到這個梨有多甜,也才能真正了解數學上的90%的糖分究竟是怎麼樣的。如果這些機器學習算法是個梨,本文的首要目的就是先帶領大家咬一口。

另外還有下面幾個目的:

  • 檢驗自己對算法的了解程度,對算法理論做一個小總結
  • 能開心的學習這些算法的核心思想, 找到學習這些算法的興趣,為深入的學習這些算法打一個基礎。
  • 每一節課的理論都會放一個實戰案例,能夠真正的做到學以緻用,既可以鍛煉程式設計能力,又可以加深算法理論的把握程度。
  • 也想把之前所有的筆記和參考放在一塊,友善以後檢視時的友善。

學習算法的過程,獲得的不應該隻有算法理論,還應該有樂趣和解決實際問題的能力!

今天是白話機器學習算法理論+實戰的第五篇,樸素貝葉斯算法,這個算法最适合的場景就是文本分類任務了,常用語自然語言處理任務,但可不單單是用于文本分類,貝葉斯方法被證明是非常general且強大的推理架構,通過今天的學習,快速掌握樸素貝葉斯的計算原理和工作流程,并且運用學習到的原理和流程,做一個文本分類的任務。

大綱如下:

  • 貝葉斯原理(不要畏懼不可知,要從已知推未知)
  • 樸素貝葉斯分類的工作原理(離散資料和連續資料案例)
  • 樸素貝葉斯分類實戰(文本分類,在這裡會掌握TF-IDF技術,會認識分詞技術)

OK, let’s go !

2. 樸素貝葉斯? 還是先從貝葉斯原理開始吧!

很多人都聽說過貝葉斯原理? 在哪? 當然是在學機率統計的時候了,有些人可能會說,完蛋, 機率統計的知識都忘光了, 哈哈, 那也沒有關系, 誰讓這裡是白話機器學習算法呢, 肯定是大白話的學習算法精華啊。 在這之前,得需要了解一下貝葉斯原理, 放心,這裡沒有複雜的公式,隻需要一個小例子,你就發現,不知不覺的就學到了貝葉斯原理的核心思想,對,就是這麼神奇。 不信? 那就接着往下看。

貝葉斯原理是英國數學家托馬斯·貝葉斯提出的。貝葉斯是個很神奇的人,他的經曆類似梵高。生前沒有得到重視,死後,他寫的一篇關于歸納推理的論文被朋友翻了出來,并發表了。這一發表不要緊,結果這篇論文的思想直接影響了接下來兩個多世紀的統計學,是科學史上著名的論文之一。(哈哈,厲害吧,隻可惜,貝葉斯看不見了)

貝葉斯原理是怎麼來的呢? 貝葉斯為了解決一個叫“逆向機率”問題寫了一篇文章,嘗試解答在沒有太多可靠證據的情況下,怎樣做出更符合數學邏輯的推測。這裡有個詞,叫做逆向機率。 What is “逆向機率”?

所謂“逆向機率”是相對“正向機率”而言。

正向機率總知道吧, 比如,一個袋子裡5個球, 3個黑球,2個白球,我随便從裡面拿出一個,問,是黑球的機率?。 這時候,立即答:3/5。

哈哈,這就是正向機率了,很容易了解吧,但這種情況往往是上帝的視角,即了解了事情的全貌做的判斷(事先知道了袋子裡有5個球)。

But, 如果我們事先隻知道,袋子裡不是黑球就是白球,并不知道各自有多少個,而是通過我們摸出的球的顔色,我們能判斷出袋子裡黑白球各自多少個來嗎? 這就是逆向機率了。

正是這樣一個普普通通的問題,影響了接下來近 200 年的統計學理論。這是因為,貝葉斯原理與其他統計學推斷方法截然不同,它是建立在主觀判斷的基礎上:在我們不了解所有客觀事實的情況下,同樣可以先估計一個值,然後根據實際結果不斷進行修正。

好吧, 猜你現在正迷糊呢! 看個簡單的例子吧,讓你不知不覺的就愛上貝葉斯,哦,原理:

一所學校裡面有 60% 的男生,40% 的女生。男生總是穿長褲,女生則一半穿長褲一半穿裙子。有了這些資訊之後我們可以容易地計算“随機選取一個學生,他(她)穿長褲的機率和穿裙子的機率是多大”,這個就是前面說的“正向機率”的計算。

然而,假設你走在校園中,迎面走來一個穿長褲的學生(很不幸的是你高度近視,你隻看得見他(她)穿的是否長褲,而無法确定他(她)的性别),你能夠推斷出他(她)是男生的機率是多大嗎?

看上面這個例子,你能算出來嗎? 好像涉及到逆向推理了來。

我們可能對形式化的這種貝葉斯問題不擅長,但是我們對數形式的等價問題應該很擅長,在這裡,不妨把問題換一下子: 你在校園裡随機遊走,遇到了N個長褲的人(仍然看不清性别), 問,這N個人裡面有多少個男生,多少個女生?

你說,這還不簡單? 算出學校裡有多少個穿長褲的,然後在這些人裡,再算出多少女生,多少男生不就行了?

哈哈,厲害,那麼我們就一起算一下吧:

假設,學校裡面有M個人。

  1. 首先,先算算,這個學校有多少男的,多少女的
    60%的男生,40%的女生,則男生的個數是 M ∗ P ( 男 ) M * P(男) M∗P(男), 女生的個數 M ∗ P ( 女 ) M * P(女) M∗P(女)。 這沒問題吧?
  2. 那我們再算算,男生裡面,穿長褲的有多少人?

    根據上面我們知道,男生都穿長褲,也就是隻要是男的,他就穿長褲(你發現了嗎? 這裡有個詞,前提是男的,這是個什麼? 對,條件機率), 即 P ( 長 褲 ∣ 男 ) = 100 P(長褲 | 男)= 100% P(長褲∣男)=100。

    那麼,穿長褲的男生的個數就等于:男生的個數乘以前面的機率 = M ∗ P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) M * P(男) * P(長褲 | 男) M∗P(男)∗P(長褲∣男)

  3. 同理, 我們算一下,女生裡面,穿長褲的多少人?

    根據上面我們知道,女生裡面,有一半的人穿長褲,一般的人穿裙子,也就是P(長褲 | 女) = 50%, 這個也是個條件機率了,因為前提是女的。

    那麼,穿長褲的女生的個數就等于: M ∗ P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) M * P(女) * P(長褲 | 女) M∗P(女)∗P(長褲∣女)

  4. 這就成了,那麼這個學校裡面,穿長褲的人就是穿長褲的男生+長褲的女生
    穿長褲的人 = M ∗ P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + M ∗ P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) M * P(男) * P(長褲 | 男) + M * P(女) * P(長褲 | 女) M∗P(男)∗P(長褲∣男)+M∗P(女)∗P(長褲∣女)
  5. 那麼穿長褲的這裡面,男生和女生的比例是多少呢?

    穿長褲的這裡面, 男生的比例應該這樣計算(注意,這裡發現改變條件了嗎? 前提是穿長褲了),即P(男 | 長褲)和P(女 | 長褲)。

    怎麼算呢? 簡單,總的穿長褲的人知道,又知道,長褲的男的和女的各自的數量,那麼:

    • P ( 男 ∣ 長 褲 ) = M ∗ P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) M ∗ P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + M ∗ P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(男 | 長褲)= \frac{M * P(男) * P(長褲 | 男)}{M * P(男) * P(長褲 | 男) + M * P(女) * P(長褲 | 女)} P(男∣長褲)=M∗P(男)∗P(長褲∣男)+M∗P(女)∗P(長褲∣女)M∗P(男)∗P(長褲∣男)​
    • P ( 女 ∣ 長 褲 ) = M ∗ P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) M ∗ P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + M ∗ P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(女 | 長褲) = \frac{M * P(女) * P(長褲 | 女)}{M * P(男) * P(長褲 | 男) + M * P(女) * P(長褲 | 女)} P(女∣長褲)=M∗P(男)∗P(長褲∣男)+M∗P(女)∗P(長褲∣女)M∗P(女)∗P(長褲∣女)​
  6. 上面的式子,發現分子分母,都有M,約掉,就變成了
    • P ( 男 ∣ 長 褲 ) = P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(男 | 長褲)= \frac{P(男) * P(長褲 | 男)}{P(男) * P(長褲 | 男) + P(女) * P(長褲 | 女)} P(男∣長褲)=P(男)∗P(長褲∣男)+P(女)∗P(長褲∣女)P(男)∗P(長褲∣男)​
    • P ( 女 ∣ 長 褲 ) = P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(女 | 長褲) = \frac{P(女) * P(長褲 | 女)}{P(男) * P(長褲 | 男) + P(女) * P(長褲 | 女)} P(女∣長褲)=P(男)∗P(長褲∣男)+P(女)∗P(長褲∣女)P(女)∗P(長褲∣女)​

其實,這個例子到這就結束了,這就是最上面的那個問題的答案。我先不說,上面這個公式是個什麼東西? 我得先保證你能看明白上面這個例子, 如果看不明白,我先解釋幾個概念:

  • 先驗機率:通過經驗來判斷事情發生的機率就是先驗機率。 比如上面的男生60%, 女生40%。 這就是個事實,不用任何條件。 再比如,南方的梅雨季是6-7月,就是通過往年的氣候總結出來的經驗,這個時候下雨的機率比其他時間高出很多,這些都是先驗機率。
  • 條件機率: 事件 A 在另外一個事件 B 已經發生條件下的發生機率,表示為 P(A|B),讀作“在 B 發生的條件下 A 發生的機率”。比如上面的男生裡面,穿長褲的P(長褲 | 男),女生裡面,穿長褲的人P(長褲 | 女)。
  • 後驗機率: 後驗機率就是發生結果之後,推測原因的機率。比如上面的我看到了穿長褲的人, 我推測這是個男人P(男 | 長褲)還是個女人P(女 | 長褲)的機率。 它屬于條件機率的一種。

上面的三個機率懂了嗎?可以測試一下:

如果你的女朋友,在你的手機裡發現了和别的女人的暧昧短信,于是她開始思考了 3 個機率問題,你來判斷下下面的 3 個機率分别屬于哪種機率:
  • 你在沒有任何情況下,出軌的機率;
  • 如果你出軌了,那麼你的手機裡有暧昧短信的機率;
  • 在你的手機裡發現了暧昧短信,認為你出軌的機率。
上面這三種機率能對号入座了嗎? 如果能,說明你懂了上面的概念,也弄了上面的例子,下面開始說正事。

我們再把上面例子中最後的機率寫到下面:

  • P ( 男 ∣ 長 褲 ) = P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(男 | 長褲)= \frac{P(男) * P(長褲 | 男)}{P(男) * P(長褲 | 男) + P(女) * P(長褲 | 女)} P(男∣長褲)=P(男)∗P(長褲∣男)+P(女)∗P(長褲∣女)P(男)∗P(長褲∣男)​
  • P ( 女 ∣ 長 褲 ) = P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P ( 男 ) ∗ P ( 長 褲 ∣ 男 ) + P ( 女 ) ∗ P ( 長 褲 ∣ 女 ) P(女 | 長褲) = \frac{P(女) * P(長褲 | 女)}{P(男) * P(長褲 | 男) + P(女) * P(長褲 | 女)} P(女∣長褲)=P(男)∗P(長褲∣男)+P(女)∗P(長褲∣女)P(女)∗P(長褲∣女)​

這裡的 P ( 男 ) P(男) P(男), P ( 女 ) P(女) P(女)就是先驗機率; P ( 長 褲 ∣ 男 ) P(長褲 | 男) P(長褲∣男), P ( 長 褲 ∣ 女 ) P(長褲 | 女) P(長褲∣女)就是條件機率; P ( 男 ∣ 長 褲 ) P(男 | 長褲) P(男∣長褲), P ( 女 ∣ 長 褲 ) P(女 | 長褲) P(女∣長褲)就是後驗機率。

上面長褲和男女可以指代一切東西,令 長 褲 = A , 男 = B 1 , 女 = B 2 長褲 = A, 男=B1, 女=B2 長褲=A,男=B1,女=B2, 那麼整理一下上面的公式:

P ( B i ∣ A ) = P ( B i ) P ( A ∣ B i ) P ( B 1 ) P ( A ∣ B 1 ) + P ( B 2 ) P ( A ∣ B 2 ) P\left(B_{i} \mid A\right)=\frac{P\left(B_{i}\right) P\left(A \mid B_{i}\right)}{P\left(B_{1}\right) P\left(A \mid B_{1}\right)+P\left(B_{2}\right) P\left(A \mid B_{2}\right)} P(Bi​∣A)=P(B1​)P(A∣B1​)+P(B2​)P(A∣B2​)P(Bi​)P(A∣Bi​)​

這個,就是偉大的貝葉斯公式。 下面這個是更通用的形式:

P ( B i ∣ A ) = P ( B i ) P ( A ∣ B i ) ∑ i = 1 n P ( B i ) P ( A ∣ B i ) P\left(B_{i} \mid A\right)=\frac{P\left(B_{i}\right) P\left(A \mid B_{i}\right)}{\sum_{i=1}^{n} P\left(B_{i}\right) P\left(A \mid B_{i}\right)} P(Bi​∣A)=∑i=1n​P(Bi​)P(A∣Bi​)P(Bi​)P(A∣Bi​)​

難怪拉普拉斯說機率論隻是把常識用數學公式表達了出來。

實際上,貝葉斯原理就是求解後驗機率。 通過啥? 貝葉斯公式。

現在,是不是感覺,沒有燒多少腦就了解了貝葉斯原理了。這就說明,如果我們遇到一個不知道的條件機率的計算,我們要通過貝葉斯公式進行轉換,不要畏懼不可知,要從已知推未知。

然而,看似這麼平凡的貝葉斯公式,背後卻隐含着非常深刻的原理。

在這裡我不多說,怕你犯困,如果感興趣,見我後面那篇經典部落格:通俗易懂講解貝葉斯。 因為我的目的,不僅是了解原理,還得實戰會用。這裡沒有完全了解也不怕,下面講樸素貝葉斯,我還會執行個體運算一波。

3. 樸素貝葉斯

講完貝葉斯原理之後,我們再來看下重點要說的算法,樸素貝葉斯。

它是一種簡單但極為強大的預測模組化算法。之是以稱為樸素貝葉斯,是因為它假設每個輸入變量是獨立的。這是一個強硬的假設,實際情況并不一定,但是這項技術對于絕大部分的複雜問題仍然非常有效。

這裡的輸入變量是啥? 就類似與我們上面的性别特征,因為實際問題裡面,可能不僅隻有性别這一列特征,可能還會有什麼身高啊,體重啊,這些特征,基于這些特征再利用貝葉斯公式去做分類問題的時候,就涉及很多個輸入特征了。

樸素貝葉斯做的就是,假設這些身高,體重,性别這些特征之間是沒有關系的,互相不影響。那麼我們算同時符合這三個特征機率的時候,就可以分開算了 P ( A B C ) = P ( A ) ∗ P ( B ) ∗ P ( C ) P(ABC) = P(A)* P(B)* P(C) P(ABC)=P(A)∗P(B)∗P(C)就是這個道理了。

樸素貝葉斯模型由兩種類型的機率組成:

  • 每個類别的機率 P ( C j ) P(C_j) P(Cj​);
  • 每個屬性的條件機率 P ( A i ∣ C j ) P(A_i|C_j) P(Ai​∣Cj​)。

再舉個例子說明一下類别機率和條件機率:

假設我有 7 個棋子,其中 3 個是白色的,4 個是黑色的。那麼棋子是白色的機率就是 3/7,黑色的機率就是 4/7,這個就是類别機率。

假設我把這 7 個棋子放到了兩個盒子裡,其中盒子 A 裡面有 2 個白棋,2 個黑棋;盒子 B 裡面有 1 個白棋,2 個黑棋。那麼在盒子 A 中抓到白棋的機率就是 1/2,抓到黑棋的機率也是 1/2,這個就是條件機率,也就是在某個條件(比如在盒子 A 中)下的機率。

假設,我取出來的是白色的棋子,我問,屬于A盒子的機率? 你會算嗎?

不會? 上面的貝葉斯公式白學了!

貼出計算過程:

白話機器學習算法理論+實戰之樸素貝葉斯

為了訓練樸素貝葉斯模型,我們需要先給出訓練資料,以及這些資料對應的分類。那麼上面這兩個機率,也就是類别機率和條件機率。他們都可以從給出的訓練資料中計算出來。一旦計算出來,機率模型就可以使用貝葉斯原理對新資料進行預測。(後面會有案例)

白話機器學習算法理論+實戰之樸素貝葉斯

另外,之前還要注意一下,貝葉斯原理,貝葉斯分類和樸素貝葉斯并不是一回事:

貝葉斯原理是最大的概念,它解決了機率論中“逆向機率”的問題,在這個理論基礎上,人們設計出了貝葉斯分類器,樸素貝葉斯分類是貝葉斯分類器中的一種,也是最簡單,最常用的分類器。樸素貝葉斯之是以樸素是因為它假設屬性是互相獨立的,是以對實際情況有所限制,如果屬性之間存在關聯,分類準确率會降低。不過好在對于大部分情況下,樸素貝葉斯的分類效果都不錯。
白話機器學習算法理論+實戰之樸素貝葉斯

好了,明白了樸素貝葉斯之後,我們兩個案例,再來體會一下樸素貝葉斯的計算過程吧(關于樸素貝葉斯的詳細推導公式,可以看我下面統計學習方法的筆記)

4. 樸素貝葉斯分類的工作原理

樸素貝葉斯分類是常用的貝葉斯分類方法。我們日常生活中看到一個陌生人,要做的第一件事情就是判斷 TA 的性别,判斷性别的過程就是一個分類的過程。根據以往的經驗,我們通常會從身高、體重、鞋碼、頭發長短、服飾、聲音等角度進行判斷。這裡的“經驗”就是一個訓練好的關于性别判斷的模型,其訓練資料是日常中遇到的各式各樣的人,以及這些人實際的性别資料。

4.1 離散資料案例

我們遇到的資料可以分為兩種,一種是離散資料,另一種是連續資料。那什麼是離散資料呢?離散就是不連續的意思,有明确的邊界,比如整數 1,2,3 就是離散資料,而 1 到 3 之間的任何數,就是連續資料,它可以取在這個區間裡的任何數值。

我以下面的資料為例,這些是根據你之前的經驗所獲得的資料。然後給你一個新的資料:身高“高”、體重“中”,鞋碼“中”,請問這個人是男還是女?

白話機器學習算法理論+實戰之樸素貝葉斯

看這個題吧,根據這個題,才可以看出樸素貝葉斯的樸素之地。

下面貼出這個題的過程:

白話機器學習算法理論+實戰之樸素貝葉斯

4.2 連續資料案例

實際生活中我們得到的是連續的數值,比如下面這組資料:

白話機器學習算法理論+實戰之樸素貝葉斯

那麼如果給你一個新的資料,身高 180、體重 120,鞋碼 41,請問該人是男是女呢?

公式還是上面的公式,這裡的困難在于,由于身高、體重、鞋碼都是連續變量,不能采用離散變量的方法計算機率。而且由于樣本太少,是以也無法分成區間計算。怎麼辦呢?

這時,可以假設男性和女性的身高、體重、鞋碼都是正态分布,通過樣本計算出均值和方差,也就是得到正态分布的密度函數。有了密度函數,就可以把值代入,算出某一點的密度函數的值。(求連續型随機變量在某一個取值點的機率的時候,可以看目前機率密度函數在該點的函數值,值越大,機率越大。 但目前機率密度函數的值不和機率相等,隻可以比大小用)

比如,男性的身高是均值 179.5、标準差為 3.697 的正态分布。是以男性的身高為 180 的機率為 0.1069。

這怎麼算的? 這裡需要用到工具了, Excel的一個函數:

NORMDIST(x, mean, standard_dev, cumulative)
  • x:正态分布中,需要計算的數值;
  • Mean:正态分布的平均值;
  • Standard_dev:正态分布的标準差;
  • Cumulative:取值為邏輯值,即 False 或 True。它決定了函數的形式。當為 TRUE 時,函數結果為累積分布(标準正态);為 False 時,函數結果為機率密度。

這裡我們使用的是NORMDIST(180,179.5,3.697,0)=0.1069。

同理我們可以計算得出男性體重為 120 的機率為 0.000382324 0.000382324 0.000382324,男性鞋碼為 41 号的機率為 0.120304111 0.120304111 0.120304111。

是以我們可以計算得出: P ( A 1 A 2 A 3 ∣ C 1 ) = P ( A 1 ∣ C 1 ) P ( A 2 ∣ C 1 ) P ( A 3 ∣ C 1 ) = 0.1069 ∗ 0.000382324 ∗ 0.120304111 = 4.9169 e − 6 P(A1A2A3|C1)=P(A1|C1)P(A2|C1)P(A3|C1)=0.1069 * 0.000382324 * 0.120304111=4.9169e-6 P(A1A2A3∣C1)=P(A1∣C1)P(A2∣C1)P(A3∣C1)=0.1069∗0.000382324∗0.120304111=4.9169e−6

同理我們也可以計算出來該人為女的可能性: P ( A 1 A 2 A 3 ∣ C 2 ) = P ( A 1 ∣ C 2 ) P ( A 2 ∣ C 2 ) P ( A 3 ∣ C 2 ) = 0.00000147489 ∗ 0.015354144 ∗ 0.120306074 = 2.7244 e − 9 P(A1A2A3|C2)=P(A1|C2)P(A2|C2)P(A3|C2)=0.00000147489 * 0.015354144 * 0.120306074=2.7244e-9 P(A1A2A3∣C2)=P(A1∣C2)P(A2∣C2)P(A3∣C2)=0.00000147489∗0.015354144∗0.120306074=2.7244e−9

很明顯這組資料分類為男的機率大于分類為女的機率。

哈哈,是不是計算原理很簡單啊。 下面就要檢驗是不是真的掌握了, 要用樸素貝葉斯進行一個實戰,在實戰之前,先貼出樸素貝葉斯分類器的工作流程:

白話機器學習算法理論+實戰之樸素貝葉斯

5. 樸素貝葉斯之文本分類

樸素貝葉斯分類常用于文本分類,尤其是對于英文等語言來說,分類效果很好。它常用于垃圾文本過濾、情感預測、推薦系統等。

但是在分類之前,有必要介紹一些文本處理的和模型的知識。

5.1 sklearn中的樸素貝葉斯

sklearn 的全稱叫 Scikit-learn,它給我們提供了 3 個樸素貝葉斯分類算法,分别是高斯樸素貝葉斯(GaussianNB)、多項式樸素貝葉斯(MultinomialNB)和伯努利樸素貝葉斯(BernoulliNB)。

這三種算法适合應用在不同的場景下,我們應該根據特征變量的不同選擇不同的算法:
  • 高斯樸素貝葉斯:特征變量是連續變量,符合高斯分布,比如說人的身高,物體的長度。
  • 多項式樸素貝葉斯:特征變量是離散變量,符合多項分布,在文檔分類中特征變量展現在一個單詞出現的次數,或者是單詞的 TF-IDF 值等。

    注意, 多項式樸素貝葉斯實際上符合多項式分布,不會存在負數,是以傳入輸入的時候,别用StandardScaler進行歸一化資料,可以使用MinMaxScaler進行歸一化

  • 伯努利樸素貝葉斯:特征變量是布爾變量,符合 0/1 分布,在文檔分類中特征是單詞是否出現。

    伯努利樸素貝葉斯是以檔案為粒度,如果該單詞在某檔案中出現了即為 1,否則為 0。而多項式樸素貝葉斯是以單詞為粒度,會計算在某個檔案中的具體次數。而高斯樸素貝葉斯适合處理特征變量是連續變量,且符合正态分布(高斯分布)的情況。比如身高、體重這種自然界的現象就比較适合用高斯樸素貝葉斯來處理。而文本分類是使用多項式樸素貝葉斯或者伯努利樸素貝葉斯。

5.2 什麼是TF-IDF值呢?

這一個解釋起來,篇幅很多,在這裡不單獨解釋,請移步參考我的另一篇部落格:TF-IDF? 這一篇就夠了

下面,主要是講一下,怎麼用工具實作這個步驟。

5.3 如何求 TF-IDF?

在 sklearn 中我們直接使用 TfidfVectorizer 類,它可以幫我們計算單詞 TF-IDF 向量的值。在這個類中,取 sklearn 計算的對數 log 時,底數是 e,不是 10。

如何建立TfidfVectorizer類呢?

我們在建立的時候,有兩個構造參數,可以自定義停用詞 stop_words 和規律規則 token_pattern。需要注意的是傳遞的資料結構,停用詞 stop_words 是一個清單 List 類型,而過濾規則 token_pattern 是正規表達式。

白話機器學習算法理論+實戰之樸素貝葉斯

什麼是停用詞?停用詞就是在分類中沒有用的詞,這些詞一般詞頻 TF 高,但是 IDF 很低,起不到分類的作用。為了節省空間和計算時間,我們把這些詞作為停用詞 stop words,告訴機器這些詞不需要幫我計算。

當我們建立好 TF-IDF 向量類型時,可以用 fit_transform 幫我們計算,傳回給我們文本矩陣,該矩陣表示了每個單詞在每個文檔中的 TF-IDF 值。

白話機器學習算法理論+實戰之樸素貝葉斯

在我們進行 fit_transform 拟合模型後,我們可以得到更多的 TF-IDF 向量屬性,比如,我們可以得到詞彙的對應關系(字典類型)和向量的 IDF 值,當然也可以擷取設定的停用詞 stop_words。

白話機器學習算法理論+實戰之樸素貝葉斯

舉個小例子吧:

假設我們有 4 個文檔:

  • 文檔 1:this is the bayes document;
  • 文檔 2:this is the second second document;
  • 文檔 3:and the third one;
  • 文檔 4:is this the document。

現在想要計算文檔裡都有哪些單詞,這些單詞在不同文檔中的 TF-IDF 值是多少呢?

  1. 首先我們建立 TfidfVectorizer 類:
    from sklearn.feature_extraction.text import TfidfVectorizer
    tfidf_vec = TfidfVectorizer()
               
  2. 然後我們建立 4 個文檔的清單 documents,并讓建立好的 tfidf_vec 對 documents 進行拟合,得到 TF-IDF 矩陣:
    documents = [
        'this is the bayes document',
        'this is the second second document',
        'and the third one',
        'is this the document'
    ]
    tfidf_matrix = tfidf_vec.fit_transform(documents)
               
    輸出文檔中所有不重複的詞:
    print('不重複的詞:', tfidf_vec.get_feature_names())
    
    # 結果如下:
    不重複的詞: ['and', 'bayes', 'document', 'is', 'one', 'second', 'the', 'third', 'this']
               
    輸出每個單詞對應的 id 值:
    print('每個單詞的ID:', tfidf_vec.vocabulary_)
    
    # 結果如下:
    每個單詞的ID: {'this': 8, 'is': 3, 'the': 6, 'bayes': 1, 'document': 2, 'second': 5, 'and': 0, 'third': 7, 'one': 4}
               
    輸出每個單詞在每個文檔中的 TF-IDF 值,向量裡的順序是按照詞語的 id 順序來的:
    print('每個單詞的tfidf值:', tfidf_matrix.toarray())
    
    # 結果如下:
    
    每個單詞的tfidf值: [[0.         0.63314609 0.40412895 0.40412895 0.         0.
      0.33040189 0.         0.40412895]
     [0.         0.         0.27230147 0.27230147 0.         0.85322574
      0.22262429 0.         0.27230147]
     [0.55280532 0.         0.         0.         0.55280532 0.
      0.28847675 0.55280532 0.        ]
     [0.         0.         0.52210862 0.52210862 0.         0.
      0.42685801 0.         0.52210862]]
               

5.3 如何對文檔進行分類 - 思路分析

如果我們要對文檔進行分類,有兩個重要的階段:

白話機器學習算法理論+實戰之樸素貝葉斯
  1. 基于分詞的資料準備,包括分詞、單詞權重計算、去掉停用詞;
  2. 應用樸素貝葉斯分類進行分類,首先通過訓練集得到樸素貝葉斯分類器,然後将分類器應用于測試集,并與實際結果做對比,最終得到測試集的分類準确率。

下面,分别對這些子產品介紹:

  • 子產品 1:對文檔進行分詞

    在準備階段裡,最重要的就是分詞。那麼如果給文檔進行分詞呢?英文文檔和中文文檔所使用的分詞工具不同。

    在英文文檔中,最常用的是 NTLK 包。NTLK 包中包含了英文的停用詞 stop words、分詞和标注方法。

    import nltk
    word_list = nltk.word_tokenize(text) #分詞
    nltk.pos_tag(word_list) #标注單詞的詞性
               
    在中文文檔中,最常用的是 jieba 包。jieba 包中包含了中文的停用詞 stop words 和分詞方法。
    import jieba
    word_list = jieba.cut (text) #中文分詞
               
  • 子產品 2:加載停用詞表

    我們需要自己讀取停用詞表檔案,從網上可以找到中文常用的停用詞儲存在 stop_words.txt,然後利用 Python 的檔案讀取函數讀取檔案,儲存在 stop_words 數組中。

  • 子產品 3:計算單詞的權重

    直接建立 TfidfVectorizer 類,然後使用 fit_transform 方法進行拟合,得到 TF-IDF 特征空間 features,你可以了解為選出來的分詞就是特征。我們計算這些特征在文檔上的特征向量,得到特征空間 features。

    tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
    features = tf.fit_transform(train_contents)
               
    這裡 max_df 參數用來描述單詞在文檔中的最高出現率。假設 max_df=0.5,代表一個單詞在 50% 的文檔中都出現過了,那麼它隻攜帶了非常少的資訊,是以就不作為分詞統計。一般很少設定 min_df,因為 min_df 通常都會很小。
  • 子產品 4:生成樸素貝葉斯分類器

    我們将特征訓練集的特征空間 train_features,以及訓練集對應的分類 train_labels 傳遞給貝葉斯分類器 clf,它會自動生成一個符合特征空間和對應分類的分類器。

    這裡我們采用的是多項式貝葉斯分類器,其中 alpha 為平滑參數。為什麼要使用平滑呢?因為如果一個單詞在訓練樣本中沒有出現,這個單詞的機率就會被計算為 0。但訓練集樣本隻是整體的抽樣情況,我們不能因為一個事件沒有觀察到,就認為整個事件的機率為 0。為了解決這個問題,我們需要做平滑處理。

    當 alpha=1 時,使用的是 Laplace 平滑。Laplace 平滑就是采用加 1 的方式,來統計沒有出現過的單詞的機率。這樣當訓練樣本很大的時候,加 1 得到的機率變化可以忽略不計,也同時避免了零機率的問題。

    當 0<alpha<1 時,使用的是 Lidstone 平滑。對于 Lidstone 平滑來說,alpha 越小,疊代次數越多,精度越高。我們可以設定 alpha 為 0.001。

    # 多項式貝葉斯分類器
    from sklearn.naive_bayes import MultinomialNB  
    clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels)
               
  • 子產品 5:使用生成的分類器做預測

    首先我們需要得到測試集的特征矩陣。方法是用訓練集的分詞建立一個 TfidfVectorizer 類,使用同樣的 stop_words 和 max_df,然後用這個 TfidfVectorizer 類對測試集的内容進行 fit_transform 拟合,得到測試集的特征矩陣 test_features。

    test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=train_vocabulary)
    test_features=test_tf.fit_transform(test_contents)
               

    然後我們用訓練好的分類器對新資料做預測。方法是使用 predict 函數,傳入測試集的特征矩陣 test_features,得到分類結果 predicted_labels。

    predict 函數做的工作就是求解所有後驗機率并找出最大的那個。

  • 子產品 6:計算準确率

    計算準确率實際上是對分類模型的評估。我們可以調用 sklearn 中的 metrics 包,在 metrics 中提供了 accuracy_score 函數,友善我們對實際結果和預測的結果做對比,給出模型的準确率。

    from sklearn import metrics
    print metrics.accuracy_score(test_labels, predicted_labels)
               

5.4 實戰文本分類

中文文檔資料集點選這裡下載下傳。

資料說明:

文檔共有 4 種類型:女性、體育、文學、校園;
白話機器學習算法理論+實戰之樸素貝葉斯
訓練集放到 train 檔案夾裡,測試集放到 test 檔案,停用詞放到 stop 檔案夾裡。
白話機器學習算法理論+實戰之樸素貝葉斯

使用樸素貝葉斯分類對訓練集進行訓練,并對測試集進行驗證,并給出測試集的準确率。

好吧,一步步的根據前面的思路進行做:

  1. 導入包
    import os
    
    import jieba
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNB
    from sklearn.metrics import accuracy_score
               
  2. 加載停用詞
    LABAL_MAP = {'體育':0, '女性':1, '文學':2, '校園':3}
    
    """加載停用詞"""
    with open('./text classification/stop/stopword.txt', 'rb') as f:
        STOP_WORDS = [line.strip() for line in f.readlines()]
               
  3. 加載資料集
    """定義加載資料的函數"""
    def load_data(path):
        """
            base_path: 基礎路徑
            return: 分詞清單,标簽清單
        """
        
        documents = []
        labels = []
    
        for label_dir in os.listdir(path):   # 這是周遊那四個标簽目錄
            file_path = os.path.join(path, label_dir)     
            for file in os.listdir(file_path):  # 這是周遊每個标簽目錄下面的文本
                labels.append(LABAL_MAP[label_dir])
                filename = os.path.join(file_path, file)
                with open(filename, 'rb') as fr:    # 讀取檔案  用二進制的方式讀取,不用考慮字元編碼問題
                    content = fr.read()
                    word_list = list(jieba.cut(content))
                    words = [wl for wl in word_list if wl not in STOP_WORDS]
                    documents.append(' '.join(words))
        
        return documents, labels
               
    """加載資料"""
    train_x, train_y = load_data('./text classification/train')
    test_x, text_y = load_data('./text classification/test')
               
  4. 計算詞的權重
    """計算TF-IDF矩陣"""
    tfidf_vec = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5)
    new_train_x = tfidf_vec.fit_transform(train_x)
    
    # 測試集用訓練集的詞典
    test_tfidf_vec = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5, vocabulary=tfidf_vec.vocabulary_)
    new_test_x = test_tfidf_vec.fit_transform(test_x)
               
  5. 建立模型并預測(這裡我對比了三種貝葉斯方式)
    """建立模型"""
    bayes_model = {}
    
    bayes_model['MultinomialNB'] = MultinomialNB(alpha=0.001)
    bayes_model['BernoulliNB'] = BernoulliNB(alpha=0.001)
    bayes_model['GaussianNB'] = GaussianNB()
    
    for item in bayes_model.keys():
        clf = bayes_model[item]
        clf.fit(new_train_x.toarray(), train_y)
        pred = clf.predict(new_test_x.toarray())
        print(item, "accuracy_score: ", accuracy_score(text_y, pred))
               

最後結果如下:

MultinomialNB accuracy_score:  0.91
BernoulliNB accuracy_score:  0.9
GaussianNB accuracy_score:  0.89
           

6. 總結

到這終于寫完了, 我的天啊,沒想到這個這麼多,趕緊來總結一下吧,今天我們從貝葉斯原理出發,通過生活中的例子得出了偉大的貝葉斯公式,貝葉斯原理就是基于這個求後驗機率。

然後又介紹了樸素貝葉斯及樸素之處,用兩個案例解釋了一下樸素貝葉斯的計算流程

然後,進行文本分類的實戰,實戰之前,介紹了文本處理時的一些知識,比如分詞,比如TF-IDF統計方法原理及實作, 然後完成實戰任務。

希望通過今天的學習能夠掌握樸素貝葉斯的用法和原理。

參考:

  • 極客時間資料分析實戰45講(上面網格圖檔的出處和實踐項目都是摘自于此)
  • 統計學習方法之樸素貝葉斯
  • 樸素貝葉斯實戰
  • 通俗易懂講解貝葉斯
  • 資料分析實戰45講貝葉斯筆記
  • 條件機率,全機率,貝葉斯公式了解
  • 樸素貝葉斯的三個常用模型:高斯、多項式、伯努利

什麼,還沒看夠? 哈哈,可以看看其他的系列,也同樣有趣又能學習到知識

  • 白話機器學習算法理論+實戰之決策樹 - 幫你搞定相親選擇的煩惱
  • 白話機器學習算法理論+實戰之關聯規則 - 帶你領略導演拍戲一般會喜歡哪些演員的
  • 白話機器學習算法理論+實戰之PageRank算法 - 幫你分析與你對象交往的人員之間的關系緊密程度
  • 白話機器學習算法理論+實戰之AdaBoost算法 - 帶你實作三個臭皮匠頂個諸葛亮的故事
  • 白話機器學習算法理論+實戰之支援向量機(SVM) - 教你檢測是否有乳腺癌
  • 白話機器學習算法理論+實戰之K近鄰算法 - 教你識别手寫數字
  • 白話機器學習算法理論+實戰之KMearns聚類算法 - 幫你進行圖像分割
  • 白話機器學習算法理論+實戰之EM聚類 - 帶你走進王者榮耀英雄的聚類
  • 白話機器學習算法理論+實戰之PCA降維 - 帶你玩一下人臉識别的降維

關于樸素貝葉斯, 高頻面試題

樸素貝葉斯與LR的差別

樸素貝葉斯是生成模型,根據已有樣本進行貝葉斯估計學習出先驗機率 P ( Y ) P(Y) P(Y)和條件機率 P ( X ∣ Y ) P(X|Y) P(X∣Y),進而求出聯合分布機率 P ( X , Y ) P(X,Y) P(X,Y),最後利用貝葉斯定理求解 P ( Y ∣ X ) P(Y|X) P(Y∣X), 而LR是判别模型,根據極大化對數似然函數直接求出條件機率 P ( Y ∣ X ) P(Y|X) P(Y∣X);樸素貝葉斯是基于很強的條件獨立假設(在已知分類 Y Y Y的條件下,各個特征變量取值是互相獨立的),而LR則對此沒有要求;樸素貝葉斯适用于資料集少的情景,而LR适用于大規模資料集。

樸素貝葉斯“樸素”在哪裡?

簡單來說:利用貝葉斯定理求解聯合機率 P ( X , Y ) P(X,Y) P(X,Y)時,需要計算條件機率 P ( X ∣ Y ) P(X|Y) P(X∣Y)。在計算 P ( X ∣ Y ) P(X|Y) P(X∣Y)時,樸素貝葉斯做了一個很強的條件獨立假設(當 Y Y Y确定時, X X X的各個分量取值之間互相獨立),即

P ( X 1 = x 1 , X 2 = x 2 , . . . X j = x j ∣ Y = y k ) = P ( X 1 = x 1 ∣ Y = y k ) ∗ P ( X 2 = x 2 ∣ Y = y k ) ∗ . . . ∗ P ( X j = x j ∣ Y = y k ) P(X1=x1,X2=x2,...Xj=xj|Y=yk) = P(X1=x1|Y=yk)*P(X2=x2|Y=yk)*...*P(Xj=xj|Y=yk) P(X1=x1,X2=x2,...Xj=xj∣Y=yk)=P(X1=x1∣Y=yk)∗P(X2=x2∣Y=yk)∗...∗P(Xj=xj∣Y=yk)

樸素貝葉斯的優缺點

  • 優點:對小規模的資料表現很好,适合多分類任務,适合增量式訓練。
  • 缺點:對輸入資料的表達形式很敏感(離散、連續,值極大極小之類的),獨立性假設一般不太可能滿足。

繼續閱讀