機器學習7:樸素貝葉斯

我們的任務

垃圾郵件檢測是機器學習在現今互聯網領域的主要應用之一。幾乎所有大型電子郵箱服務提供商都內置了垃圾郵件檢測系統,能夠自動將此類郵件分類爲“垃圾郵件”。

在此項目中,我們將使用樸素貝葉斯算法創建一個模型,該模型會通過我們對模型的訓練將信息數據集分類爲垃圾信息或非垃圾信息。對垃圾文本信息進行大致瞭解十分重要。通常它們都包含“免費”、“贏取”、“獲獎者”、“現金”、“獎品”等字眼,因爲這些它們專門用來吸引你的注意力,誘惑你打開信息。此外,垃圾信息的文字一般都使用大寫形式和大量感嘆號。收信人能輕易辨認垃圾信息,而我們的目標是訓練模型幫助我們識別垃圾信息!

能夠識別垃圾信息是一種二元分類問題,因爲此處信息只有“垃圾信息”或“非垃圾信息”這兩種分類。此外,這是一種監督式學習問題,因爲我們會向模型中提供帶標籤數據集,模型能夠從中學習規律並在日後做出預測。

第 0 步:樸素貝葉斯定理簡介

貝葉斯定理是最早的概率推理算法之一,由 Reverend Bayes 提出(他用來推理上帝是否存在),該定理在某些用例中依然很有用。

理解該定理的最佳方式是通過一個例子來講解。

假設你是一名特勤人員,你接到任務,需要在共和黨總統候選人的某次競選演說中保護他/她的安全。這場競選演說是所有人都可以參加的公開活動,你的任務並不簡單,需要時刻注意危險是否存在。一種方式是對每個人都設定一個威脅因子,根據人的特徵(例如年齡、性別,是否隨身帶包以及緊張程度等等),你可以判斷此人是否存在威脅。

如果某人符合所有這些特徵,已經超出了你內心中的疑慮閾值,你可以採取措施並將此人帶離活動現場。貝葉斯定理的原理也是如此,我們將根據某些相關事件(某人的年齡、性別、是否帶包了、緊張程度等)的發生概率計算某個事件(某人存在威脅)的概率。

你還需要考慮這些特徵之間的獨立性。例如,如果在活動現場,有個孩子看起來很緊張,那麼與緊張的成人相比,孩子存在威脅的可能性會更低。爲了深入講解這一點,看看下面兩個特徵:年齡和緊張程度。假設我們單獨研究這些特徵,我們可以設計一個將所有緊張的人視作潛在威脅人士的模型。但是,很有可能會有很多假正例,因爲現場的未成年人很有可能會緊張。因此同時考慮年齡和“緊張程度”特徵肯定會更準確地反映哪些人存在威脅。

這就是該定理的“樸素”一詞的含義,該定理會認爲每個特徵相互之間都保持獨立,但實際上並非始終是這樣,因此會影響到最終的結論。

簡而言之,貝葉斯定理根據某些其他事件(在此例中是信息被分類爲垃圾信息)的聯合概率分佈計算某個事件(在此例中是信息爲垃圾信息)的發生概率。稍後我們將深入瞭解貝葉斯定理的原理,但首先了解下我們將處理的數據。

第 1.1 步:瞭解我們的數據集

我們將使用來自 UCI 機器學習資源庫中的數據集,該資源庫有大量供實驗性研究的精彩數據集。這是直接數據鏈接。

** 下面是該數據的預覽:**

在這裏插入圖片描述

數據集中的列目前沒有命名,可以看出有 2 列。

第一列有兩個值:“ham”,表示信息不是垃圾信息,以及“spam”,表示信息是垃圾信息。

第二列是被分類的信息的文本內容。

** 說明:**

  • 使用 read_table 方法可以將數據集導入 pandas 數據幀。因爲這是一個用製表符分隔的數據集,因此我們將使用“\t”作爲“sep”參數的值,表示這種分隔格式。
  • 此外,通過爲 read_table() 的“names”參數指定列表 [‘label’, ‘sms_message’],重命名列。
  • 用新的列名輸出數據幀的前五個值。
'''
Solution
'''
import pandas as pd
# Dataset from - https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection
df = pd.read_table('smsspamcollection/SMSSpamCollection',
                   sep='\t', 
                   header=None, 
                   names=['label', 'sms_message'])

# Output printing out first 5 columns
df.head()
/home/leon/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:9: FutureWarning: read_table is deprecated, use read_csv instead.
  if __name__ == '__main__':
label sms_message
0 ham Go until jurong point, crazy.. Available only ...
1 ham Ok lar... Joking wif u oni...
2 spam Free entry in 2 a wkly comp to win FA Cup fina...
3 ham U dun say so early hor... U c already then say...
4 ham Nah I don't think he goes to usf, he lives aro...

第 1.2 步:數據預處理

我們已經大概瞭解數據集的結構,現在將標籤轉換爲二元變量,0 表示“ham”(即非垃圾信息),1表示“spam”,這樣比較方便計算。

你可能會疑問,爲何要執行這一步?答案在於 scikit-learn 處理輸入的方式。Scikit-learn 只處理數字值,因此如果標籤值保留爲字符串,scikit-learn 會自己進行轉換(更確切地說,字符串標籤將轉型爲未知浮點值)。

如果標籤保留爲字符串,模型依然能夠做出預測,但是稍後計算效果指標(例如計算精確率和召回率分數)時可能會遇到問題。因此,爲了避免稍後出現意外的陷阱,最好將分類值轉換爲整數,再傳入模型中。

說明:

  • 使用映射方法將“標籤”列中的值轉換爲數字值,如下所示:
    {‘ham’:0, ‘spam’:1} 這樣會將“ham”值映射爲 0,將“spam”值映射爲 1。
  • 此外,爲了知道我們正在處理的數據集有多大,使用“shape”輸出行數和列數
'''
Solution
'''
df['label'] = df.label.map({'ham':0, 'spam':1})
print(df.shape)
df.head()
(5572, 2)
label sms_message
0 0 Go until jurong point, crazy.. Available only ...
1 0 Ok lar... Joking wif u oni...
2 1 Free entry in 2 a wkly comp to win FA Cup fina...
3 0 U dun say so early hor... U c already then say...
4 0 Nah I don't think he goes to usf, he lives aro...

第 2.1 步:Bag of words

我們的數據集中有大量文本數據(5,572 行數據)。大多數機器學習算法都要求傳入的輸入是數字數據,而電子郵件/信息通常都是文本。

現在我們要介紹 Bag of Words (BoW) 這個概念,它用來表示要處理的問題具有“大量單詞”或很多文本數據。BoW 的基本概念是拿出一段文本,計算該文本中單詞的出現頻率。注意:BoW 平等地對待每個單詞,單詞的出現順序並不重要。

利用我們將介紹的流程,我們可以將文檔集合轉換成矩陣,每個文檔是一行,每個單詞(令牌)是一列,對應的(行,列)值是每個單詞或令牌在此文檔中出現的頻率。

例如:

假設有四個如下所示的文檔:

['Hello, how are you!', 'Win money, win from home.', 'Call me now', 'Hello, Call you tomorrow?']

我們的目標是將這組文本轉換爲頻率分佈矩陣,如下所示:

在這裏插入圖片描述

從圖中可以看出,文檔在行中進行了編號,每個單詞是一個列名稱,相應的值是該單詞在文檔中出現的頻率。

我們詳細講解下,看看如何使用一小組文檔進行轉換。

要處理這一步,我們將使用 sklearns
count vectorizer 方法,該方法的作用如下所示:

  • 它會令牌化字符串(將字符串劃分爲單個單詞)併爲每個令牌設定一個整型 ID。
  • 它會計算每個令牌的出現次數。

** 請注意:**

  • CountVectorizer 方法會自動將所有令牌化單詞轉換爲小寫形式,避免區分“He”和“he”等單詞。爲此,它會使用參數 lowercase,該參數默認設爲 True
  • 它還會忽略所有標點符號,避免區分後面有標點的單詞(例如“hello!”)和前後沒有標點的同一單詞(例如“hello”)。爲此,它會使用參數 token_pattern,該參數使用默認正則表達式選擇具有 2 個或多個字母數字字符的令牌。
  • 要注意的第三個參數是 stop_words。停用詞是指某個語言中最常用的字詞,包括“am”、“an”、“and”、“the”等。 通過將此參數值設爲 english,CountVectorizer 將自動忽略(輸入文本中)出現在 scikit-learn 中的內置英語停用詞列表中的所有單詞。這非常有用,因爲當我們嘗試查找表明是垃圾內容的某些單詞時,停用詞會使我們的結論出現偏差。

我們將在之後的步驟中深入講解在模型中應用每種預處理技巧的效果,暫時先知道在處理文本數據時,有這些預處理技巧可採用。

第 2.2 步:從頭實現 Bag of Words

在深入瞭解幫助我們處理繁重工作的 scikit-learn 的 Bag of Words(BoW) 庫之前,首先我們自己實現該步驟,以便了解該庫的背後原理。

** 第 1 步:將所有字符串轉換成小寫形式。**

假設有一個文檔集合:

documents = ['Hello, how are you!',
             'Win money, win from home.',
             'Call me now.',
             'Hello, Call hello you tomorrow?']

** 說明:**

  • 將文檔集合中的所有字符串轉換成小寫形式。將它們保存到叫做“lower_case_documents”的列表中。你可以使用 lower() 方法在 python 中將字符串轉換成小寫形式。
'''
Solution:
'''
documents = ['Hello, how are you!',
             'Win money, win from home.',
             'Call me now.',
             'Hello, Call hello you tomorrow?']

lower_case_documents = []
for i in documents:
    lower_case_documents.append(i.lower())
    print(i)
print(lower_case_documents)
Hello, how are you!
Win money, win from home.
Call me now.
Hello, Call hello you tomorrow?
['hello, how are you!', 'win money, win from home.', 'call me now.', 'hello, call hello you tomorrow?']

** 第 2 步:刪除所有標點符號 **

說明:
刪除文檔集合中的字符串中的所有標點。將它們保存在叫做“sans_punctuation_documents”的列表中。

'''
Solution:
'''
sans_punctuation_documents = []
import string

for i in lower_case_documents:
    sans_punctuation_documents.append(i.translate(str.maketrans('', '', string.punctuation)))
    
print(sans_punctuation_documents)
['hello how are you', 'win money win from home', 'call me now', 'hello call hello you tomorrow']

** 第 3 步:令牌化 **

令牌化文檔集合中的句子是指使用分隔符將句子拆分成單個單詞。分隔符指定了我們將使用哪個字符來表示單詞的開始和結束位置(例如,我們可以使用一個空格作爲我們的文檔集合的單詞分隔符。)

說明:
使用 split() 方法令牌化“sans_punctuation_documents”中存儲的字符串,並將最終文檔集合存儲在叫做“preprocessed_documents”的列表中。

'''
Solution:
'''
preprocessed_documents = []
for i in sans_punctuation_documents:
    preprocessed_documents.append(i.split(' '))
print(preprocessed_documents)
[['hello', 'how', 'are', 'you'], ['win', 'money', 'win', 'from', 'home'], ['call', 'me', 'now'], ['hello', 'call', 'hello', 'you', 'tomorrow']]

** 第 4 步:計算頻率 **

我們已經獲得所需格式的文檔集合,現在可以數出每個單詞在文檔集合的每個文檔中出現的次數了。爲此,我們將使用 Python collections 庫中的 Counter 方法。

Counter 會數出列表中每項的出現次數,並返回一個字典,鍵是被數的項目,相應的值是該項目在列表中的計數。

說明:
使用 Counter() 方法和作爲輸入的 preprocessed_documents 創建一個字典,鍵是每個文檔中的每個單詞,相應的值是該單詞的出現頻率。將每個 Counter 字典當做項目另存到一個叫做“frequency_list”的列表中。

'''
Solution
'''
frequency_list = []
import pprint
from collections import Counter

for i in preprocessed_documents:
    freq_counts = Counter(i)
    frequency_list.append(freq_counts)
    
pprint.pprint(frequency_list)
[Counter({'hello': 1, 'how': 1, 'are': 1, 'you': 1}),
 Counter({'win': 2, 'money': 1, 'from': 1, 'home': 1}),
 Counter({'call': 1, 'me': 1, 'now': 1}),
 Counter({'hello': 2, 'call': 1, 'you': 1, 'tomorrow': 1})]

恭喜!你從頭實現了 Bag of Words 流程!正如在上一個輸出中看到的,我們有一個頻率分佈字典,清晰地顯示了我們正在處理的文本。

我們現在應該充分理解 scikit-learn 中的 sklearn.feature_extraction.text.CountVectorizer 方法的背後原理了。

我們將在下一步實現 sklearn.feature_extraction.text.CountVectorizer 方法。

第 2.3 步:在 scikit-learn 中實現 Bag of Words

我們已經從頭實現了 BoW 概念,並使用 scikit-learn 以簡潔的方式實現這一流程。我們將使用在上一步用到的相同文檔集合。

'''
Here we will look to create a frequency matrix on a smaller document set to make sure we understand how the 
document-term matrix generation happens. We have created a sample document set 'documents'.
'''
documents = ['Hello, how are you!',
                'Win money, win from home.',
                'Call me now.',
                'Hello, Call hello you tomorrow?']

說明:
導入 sklearn.feature_extraction.text.CountVectorizer 方法並創建一個實例,命名爲 ‘count_vector’。

'''
Solution
'''
from sklearn.feature_extraction.text import CountVectorizer
count_vector = CountVectorizer()

** 使用 CountVectorizer() 預處理數據 **

在第 2.2 步,我們從頭實現了可以首先清理數據的 CountVectorizer() 方法。清理過程包括將所有數據轉換爲小寫形式,並刪除所有標點符號。CountVectorizer() 具有某些可以幫助我們完成這些步驟的參數,這些參數包括:

  • lowercase = True

lowercase 參數的默認值爲 True,它會將所有文本都轉換爲小寫形式。

  • token_pattern = (?u)\\b\\w\\w+\\b

token_pattern 參數具有默認正則表達式值 (?u)\\b\\w\\w+\\b,它會忽略所有標點符號並將它們當做分隔符,並將長度大於等於 2 的字母數字字符串當做單個令牌或單詞。

  • stop_words

stop_words 參數如果設爲 english,將從文檔集合中刪除與 scikit-learn 中定義的英語停用詞列表匹配的所有單詞。考慮到我們的數據集規模不大,並且我們處理的是信息,並不是電子郵件這樣的更龐大文本來源,因此我們將不設置此參數值。

你可以通過如下所示輸出 count_vector 對象,查看該對象的所有參數值:

'''
Practice node:
Print the 'count_vector' object which is an instance of 'CountVectorizer()'
'''
print(count_vector)
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
                dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
                lowercase=True, max_df=1.0, max_features=None, min_df=1,
                ngram_range=(1, 1), preprocessor=None, stop_words=None,
                strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
                tokenizer=None, vocabulary=None)

說明:
使用 fit() 將你的文檔數據集與 CountVectorizer 對象進行擬合,並使用 get_feature_names() 方法獲得被歸類爲特徵的單詞列表。

'''
Solution:
'''
print(documents)
count_vector.fit(documents)
#count_vector.get_feature_names()
['Hello, how are you!', 'Win money, win from home.', 'Call me now.', 'Hello, Call hello you tomorrow?']





CountVectorizer(analyzer='word', binary=False, decode_error='strict',
                dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
                lowercase=True, max_df=1.0, max_features=None, min_df=1,
                ngram_range=(1, 1), preprocessor=None, stop_words=None,
                strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
                tokenizer=None, vocabulary=None)

get_feature_names() 方法會返回此數據集的特徵名稱,即組成 ‘documents’ 詞彙表的單詞集合。

**
說明:**
創建一個矩陣,行是 4 個文檔中每個文檔的行,列是每個單詞。對應的值(行,列)是該單詞(在列中)在特定文檔(在行中)中出現的頻率。爲此,你可以使用 transform() 方法並傳入文檔數據集作爲參數。transform() 方法會返回一個 numpy 整數矩陣,你可以使用 toarray() 將其轉換爲數組,稱之爲 ‘doc_array’

'''
Solution
'''
doc_array  = count_vector.transform(documents).toarray()
doc_array
array([[1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
       [0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0],
       [0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
       [0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 1]])

現在,對於單詞在文檔中的出現頻率,我們已經獲得了整潔的文檔表示形式。爲了方便理解,下一步我們會將此數組轉換爲數據幀,並相應地爲列命名。

說明:
將我們獲得並加載到 ‘doc_array’ 中的數組轉換爲數據幀,並將列名設爲單詞名稱(你之前使用 get_feature_names() 計算了名稱)。將該數據幀命名爲 ‘frequency_matrix’。

'''
Solution
'''
frequency_matrix = pd.DataFrame(doc_array,columns = count_vector.get_feature_names())
frequency_matrix
are call from hello home how me money now tomorrow win you
0 1 0 0 1 0 1 0 0 0 0 0 1
1 0 0 1 0 1 0 0 1 0 0 2 0
2 0 1 0 0 0 0 1 0 1 0 0 0
3 0 1 0 2 0 0 0 0 0 1 0 1

恭喜!你爲我們創建的文檔數據集成功地實現了 Bag of Words 問題。

直接使用該方法的一個潛在問題是如果我們的文本數據集非常龐大(假設有一大批新聞文章或電子郵件數據),由於語言本身的原因,肯定有某些值比其他值更常見。例如“is”、“the”、“an”等單詞、代詞、語法結構等會使矩陣出現偏斜並影響到分析結果。

有幾種方式可以減輕這種情況。一種方式是使用 stop_words 參數並將其值設爲 english。這樣會自動忽略 scikit-learn 中的內置英語停用詞列表中出現的所有單詞(來自輸入文本)。

另一種方式是使用 tfidf 方法。該方法已經超出了這門課程的講解範疇。

第 3.1 步:訓練集和測試集

我們已經知道如何處理 Bag of Words 問題,現在回到我們的數據集並繼續我們的分析工作。第一步是將數據集拆分爲訓練集和測試集,以便稍後測試我們的模型。

說明:
通過在 sklearn 中使用 train_test_split 方法,將數據集拆分爲訓練集和測試集。使用以下變量拆分數據:

  • X_train 是 ‘sms_message’ 列的訓練數據。
  • y_train 是 ‘label’ 列的訓練數據
  • X_test 是 ‘sms_message’ 列的測試數據。
  • y_test 是 ‘label’ 列的測試數據。
    輸出每個訓練數據和測試數據的行數。
'''
Solution

NOTE: sklearn.cross_validation will be deprecated soon to sklearn.model_selection 
'''
# split into training and testing sets
# USE from sklearn.model_selection import train_test_split to avoid seeing deprecation warning.
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df['sms_message'], 
                                                    df['label'], 
                                                    random_state=1)

print('Number of rows in the total set: {}'.format(df.shape[0]))
print('Number of rows in the training set: {}'.format(X_train.shape[0]))
print('Number of rows in the test set: {}'.format(X_test.shape[0]))
Number of rows in the total set: 5572
Number of rows in the training set: 4179
Number of rows in the test set: 1393

第 3.2 步:對數據集應用 Bag of Words 流程。

我們已經拆分了數據,下個目標是按照第 2 步:Bag of words 中的步驟操作,並將數據轉換爲期望的矩陣格式。爲此,我們將像之前一樣使用 CountVectorizer()。我們需要完成兩步:

  • 首先,我們需要對 CountVectorizer()擬合訓練數據 (X_train) 並返回矩陣。
  • 其次,我們需要轉換測試數據 (X_test) 以返回矩陣。

注意:X_train 是數據集中 ‘sms_message’ 列的訓練數據,我們將使用此數據訓練模型。

X_test 是 ‘sms_message’ 列的測試數據,我們將使用該數據(轉換爲矩陣後)進行預測。然後在後面的步驟中將這些預測與 y_test 進行比較。

我們暫時爲你提供了進行矩陣轉換的代碼!

‘’’
[Practice Node]

The code for this segment is in 2 parts. Firstly, we are learning a vocabulary dictionary for the training data
and then transforming the data into a document-term matrix; secondly, for the testing data we are only
transforming the data into a document-term matrix.

This is similar to the process we followed in Step 2.3

We will provide the transformed data to students in the variables ‘training_data’ and ‘testing_data’.
‘’’

'''
Solution
'''
# Instantiate the CountVectorizer method
count_vector = CountVectorizer()

# Fit the training data and then return the matrix
training_data = count_vector.fit_transform(X_train)

# Transform testing data and return the matrix. Note we are not fitting the testing data into the CountVectorizer()
testing_data = count_vector.transform(X_test)

第 4.1 步:從頭實現貝葉斯定理

我們的數據集已經是我們希望的格式,現在可以進行任務的下一步了,即研究用來做出預測並將信息分類爲垃圾信息或非垃圾信息的算法。記得在該項目的開頭,我們簡要介紹了貝葉斯定理,現在我們將深入講解該定理。通俗地說,貝葉斯定理根據與相關事件有關的其他事件的概率計算該事件的發生概率。它由先驗概率(我們知道的概率或提供給我們的概率)和後驗概率(我們希望用先驗部分計算的概率)組成。

我們用一個簡單的示例從頭實現貝葉斯定理。假設我們要根據某人接受糖尿病檢測後獲得陽性結果計算此人有糖尿病的概率。
在醫學領域,此類概率非常重要,因爲它們涉及的是生死情況。

我們假設:

P(D) 是某人患有糖尿病的概率。值爲 0.01,換句話說,普通人羣中有 1% 的人患有糖尿病(免責聲明:這些值只是假設,並非任何醫學研究的結論)。

P(Pos):是獲得陽性測試結果的概率。

P(Neg):是獲得陰性測試結果的概率。

P(Pos|D):是本身有糖尿病並且獲得陽性測試結果的概率,值爲 0.9,換句話說,該測試在 90% 的情況下是正確的。亦稱爲敏感性或真正例率

P(Neg|~D):是本身沒有糖尿病並且獲得陰性測試結果的概率,值也爲 0.9 ,因此在 90% 的情況下是正確的。亦稱爲特異性或真負例率

貝葉斯公式如下所示:

在這裏插入圖片描述

  • P(A):A 獨立發生的先驗概率。在我們的示例中爲 P(D),該值已經提供給我們了 。

  • P(B):B 獨立發生的先驗概率。在我們的示例中爲 P(Pos)

  • P(A|B):在給定 B 的情況下 A 發生的後驗概率,在我們的示例中爲 P(D|Pos),即某人的測試結果爲陽性時患有糖尿病的概率。這是我們要計算的值。

  • P(B|A):在給定 A 的情況下 B 可能發生的概率。在我們的示例中爲 P(Pos|D),該值已經提供給我們了 。

將這些值代入貝葉斯定理公式中:

P(D|Pos) = P(D) * P(Pos|D) / P(Pos)

獲得陽性測試結果 P(Pos) 的概率可以使用敏感性和特異性來計算,如下所示:

P(Pos) = [P(D) * Sensitivity] + [P(~D) * (1-Specificity))]

‘’’
Instructions:
Calculate probability of getting a positive test result, P(Pos)
‘’’

'''
Solution (skeleton code will be provided)
'''
# P(D)
p_diabetes = 0.01

# P(~D)
p_no_diabetes = 0.99

# Sensitivity or P(Pos|D)
p_pos_diabetes = 0.9

# Specificity or P(Neg|~D)
p_neg_no_diabetes = 0.9

# P(Pos)
p_pos = p_diabetes*p_pos_diabetes + p_no_diabetes*(1 - p_neg_no_diabetes)
print('The probability of getting a positive test result P(Pos) is: {}',format(p_pos))
The probability of getting a positive test result P(Pos) is: {} 0.10799999999999998

** 我們可以利用所有這些信息計算後驗概率,如下所示:**

某人測試結果爲陽性時患有糖尿病的概率爲:

P(D|Pos) = (P(D) * Sensitivity)) / P(Pos)

某人測試結果爲陽性時沒有糖尿病的概率爲:

P(~D|Pos) = (P(~D) * (1-Specificity)) / P(Pos)

後驗概率的和將始終爲 1

‘’’
Instructions:
Compute the probability of an individual having diabetes, given that, that individual got a positive test result.
In other words, compute P(D|Pos).

The formula is: P(D|Pos) = (P(D) * P(Pos|D) / P(Pos)
‘’’

'''
Solution
'''
# P(D|Pos)
p_diabetes_pos = p_diabetes*p_pos_diabetes/p_pos
print('Probability of an individual having diabetes, given that that individual got a positive test result is:\
',format(p_diabetes_pos)) 
Probability of an individual having diabetes, given that that individual got a positive test result is: 0.08333333333333336

‘’’
Instructions:
Compute the probability of an individual not having diabetes, given that, that individual got a positive test result.
In other words, compute P(~D|Pos).

The formula is: P(~D|Pos) = P(~D) * P(Pos|~D) / P(Pos)

Note that P(Pos|~D) can be computed as 1 - P(Neg|~D).

Therefore:
P(Pos|~D) = p_pos_no_diabetes = 1 - 0.9 = 0.1
‘’’

'''
Solution
'''
# P(Pos|~D)
p_pos_no_diabetes = 0.1

# P(~D|Pos)
p_no_diabetes_pos = p_no_diabetes*p_pos_no_diabetes/p_pos
print('Probability of an individual not having diabetes, given that that individual got a positive test result is:'\
,p_no_diabetes_pos)
Probability of an individual not having diabetes, given that that individual got a positive test result is: 0.9166666666666669

恭喜!你從頭實現了貝葉斯定理。你的分析表明即使某人的測試結果爲陽性,他/她也有 8.3% 的概率實際上患有糖尿病,以及 91.67% 的概率沒有糖尿病。當然前提是全球只有 1% 的人羣患有糖尿病,這只是個假設。

** “樸素貝葉斯”中的“樸素”一詞是什麼意思?**

樸素貝葉斯中的**“樸素”**一詞實際上是指,算法在進行預測時使用的特徵相互之間是獨立的,但實際上並非始終這樣。在我們的糖尿病示例中,我們只考慮了一個特徵,即測試結果。假設我們添加了另一個特徵“鍛鍊”。假設此特徵具有二元值 01,0 表示某人一週的鍛鍊時間不超過 2 天,1 表示某人一週的鍛鍊時間超過 2 天。如果我們要同時使用這兩個特徵(即測試結果和“鍛鍊”特徵的值)計算最終概率,貝葉斯定理將不可行。樸素貝葉斯是貝葉斯定理的一種延伸,假設所有特徵相互之間是獨立的。

第 4.2 步:從頭實現樸素貝葉斯

你已經知道貝葉斯定理的詳細原理,現在我們將用它來考慮有多個特徵的情況。

假設有兩個政黨的候選人,“Jill Stein”是綠黨候選人,“Gary Johnson”是自由黨的候選人,兩位候選人在演講中提到“自由”、“移民”和“環境”這些字眼的概率爲:

  • Jill Stein 提到“自由”的概率:0.1 ---------> P(F|J)

  • Jill Stein 提到“移民”的概率:0.1 -----> P(I|J)

  • Jill Stein 提到“環境”的概率:0.8 -----> P(E|J)

  • Gary Johnson 提到“自由”的概率:0.7 -------> P(F|G)

  • Gary Johnson 提到“移民”的概率:0.2 —> P(I|G)

  • Gary Johnson 提到“環境”的概率:0.1 —> P(E|G)

假設 Jill Stein 發表演講的概率 P(J)0.5,Gary Johnson 也是 P(G) = 0.5

瞭解這些信息後,如果我們要計算 Jill Stein 提到“自由”和“移民”的概率,該怎麼做呢?這時候樸素貝葉斯定理就派上用場了,我們將考慮兩個特徵:“自由”和“移民”。

現在我們可以定義樸素貝葉斯定理的公式:

在這裏插入圖片描述

在該公式中,y 是分類變量,即候選人的姓名,x1xn 是特徵向量,即單個單詞。該定理假設每個特徵向量或單詞 (xi) 相互之間是獨立的。

爲了詳細講解該公式,我們需要計算以下後驗概率:

  • P(J|F,I):Jill Stein 提到“自由”和“移民”的概率。

根據上述公式和貝葉斯定理,我們可以進行以下計算:P(J|F,I) = (P(J) * P(F|J) * P(I|J)) / P(F,I)。在此等式中,P(F,I) 是在研究中提到“自由”和“移民”的概率。

  • P(G|F,I):Gary Johnson 提到“自由”和“移民”的概率。

根據上述公式,我們可以進行以下計算:P(G|F,I) = (P(G) * P(F|G) * P(I|G)) / P(F,I)

'''
Instructions: Compute the probability of the words 'freedom' and 'immigration' being said in a speech, or
P(F,I).

The first step is multiplying the probabilities of Jill Stein giving a speech with her individual 
probabilities of saying the words 'freedom' and 'immigration'. Store this in a variable called p_j_text

The second step is multiplying the probabilities of Gary Johnson giving a speech with his individual 
probabilities of saying the words 'freedom' and 'immigration'. Store this in a variable called p_g_text

The third step is to add both of these probabilities and you will get P(F,I).
'''
"\nInstructions: Compute the probability of the words 'freedom' and 'immigration' being said in a speech, or\nP(F,I).\n\nThe first step is multiplying the probabilities of Jill Stein giving a speech with her individual \nprobabilities of saying the words 'freedom' and 'immigration'. Store this in a variable called p_j_text\n\nThe second step is multiplying the probabilities of Gary Johnson giving a speech with his individual \nprobabilities of saying the words 'freedom' and 'immigration'. Store this in a variable called p_g_text\n\nThe third step is to add both of these probabilities and you will get P(F,I).\n"
'''
Solution: Step 1
'''
# P(J)
p_j = 0.5

# P(F/J)
p_j_f = 0.1

# P(I/J)
p_j_i = 0.1

p_j_text = p_j * p_j_f * p_j_i
print(p_j_text)
0.005000000000000001
'''
Solution: Step 2
'''
# P(G)
p_g = 0.5

# P(F/G)
p_g_f = 0.7

# P(I/G)
p_g_i = 0.2

p_g_text = p_g * p_g_f * p_g_i
print(p_g_text)
0.06999999999999999
'''
Solution: Step 3: Compute P(F,I) and store in p_f_i
'''
p_f_i = p_j_text + p_g_text
print('Probability of words freedom and immigration being said are: ', format(p_f_i))
Probability of words freedom and immigration being said are:  0.075

現在可以計算 P(J|F,I) 的概率,即 Jill Stein 提到“自由”和“移民”的概率,以及 P(G|F,I),即 Gary Johnson 提到“自由”和“移民”的概率。

'''
Instructions:
Compute P(J|F,I) using the formula P(J|F,I) = (P(J) * P(F|J) * P(I|J)) / P(F,I) and store it in a variable p_j_fi
'''
'''
Solution
'''
p_j_fi = p_j_text/p_f_i
print('The probability of Jill Stein saying the words Freedom and Immigration: ', format(p_j_fi))
The probability of Jill Stein saying the words Freedom and Immigration:  0.06666666666666668
'''
Instructions:
Compute P(G|F,I) using the formula P(G|F,I) = (P(G) * P(F|G) * P(I|G)) / P(F,I) and store it in a variable p_g_fi
'''
'\nInstructions:\nCompute P(G|F,I) using the formula P(G|F,I) = (P(G) * P(F|G) * P(I|G)) / P(F,I) and store it in a variable p_g_fi\n'
'''
Solution
'''
p_g_fi = p_g_text/p_f_i
print('The probability of Gary Johnson saying the words Freedom and Immigration: ', format(p_g_fi))
The probability of Gary Johnson saying the words Freedom and Immigration:  0.9333333333333332

可以看出,和貝葉斯定理一樣,後驗概率之和等於 1。恭喜!你從頭實現了樸素貝葉斯定理。分析表明,綠黨的 Jill Stein 在演講中提到“自由”和“移民”的概率只有 6.6%,而自由黨的 Gary Johnson 有 93.3% 的可能性會提到這兩個詞。

另一個比較常見的樸素貝葉斯定理應用示例是在搜索引擎中搜索“薩克拉門託國王”。爲了使我們能夠獲得與薩克拉門託國王隊 NBA 籃球隊相關的結果,搜索引擎需要將這兩個單詞關聯到一起,而不是單獨處理它們,否則就會獲得標有“薩克拉門託”的圖片(例如風光圖片)以及關於“國王”的圖片(可能是歷史上的國王),而實際上我們想要搜索的是關於籃球隊的圖片。這是一種搜索引擎將單詞當做非獨立個體(因此採用的是“樸素”方式)的經典示例。

將此方法應用到我們的垃圾信息分類問題上,樸素貝葉斯算法會查看每個單詞,而不是將它們當做有任何聯繫的關聯體。對於垃圾內容檢測器來說,這麼做通常都可行,因爲有些禁用詞幾乎肯定會被分類爲垃圾內容,例如包含“偉哥”的電子郵件通常都被歸類爲垃圾郵件。

第 5 步:使用 scikit-learn 實現樸素貝葉斯

幸運的是,sklearn 具有多個樸素貝葉斯實現,這樣我們就不用從頭進行計算。我們將使用 sklearns 的 sklearn.naive_bayes 方法對我們的數據集做出預測。

具體而言,我們將使用多項式樸素貝葉斯實現。這個分類器適合分類離散特徵(例如我們的單詞計數文本分類)。它會將整數單詞計數作爲輸入。另一方面,高斯樸素貝葉斯更適合連續數據,因爲它假設輸入數據是高斯(正態)分佈。

'''
Instructions:

We have loaded the training data into the variable 'training_data' and the testing data into the 
variable 'testing_data'.

Import the MultinomialNB classifier and fit the training data into the classifier using fit(). Name your classifier
'naive_bayes'. You will be training the classifier using 'training_data' and y_train' from our split earlier. 
'''
'''
Solution
'''
from sklearn.naive_bayes import MultinomialNB
naive_bayes = MultinomialNB()
naive_bayes.fit(training_data,y_train)
MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)
'''
Instructions:
Now that our algorithm has been trained using the training data set we can now make some predictions on the test data
stored in 'testing_data' using predict(). Save your predictions into the 'predictions' variable.
'''
"\nInstructions:\nNow that our algorithm has been trained using the training data set we can now make some predictions on the test data\nstored in 'testing_data' using predict(). Save your predictions into the 'predictions' variable.\n"
'''
Solution
'''
predictions = naive_bayes.predict(testing_data)

我們已經對測試集進行預測,現在需要檢查預測的準確率了。

第 6 步:評估模型

我們已經對測試集進行了預測,下一個目標是評估模型的效果。我們可以採用各種衡量指標,但首先快速總結下這些指標。

準確率 衡量的是分類器做出正確預測的概率,即正確預測的數量與預測總數(測試數據點的數量)之比。

精確率 指的是分類爲垃圾信息的信息實際上是垃圾信息的概率,即真正例(分類爲垃圾內容並且實際上是垃圾內容的單詞)與所有正例(所有分類爲垃圾內容的單詞,無論是否分類正確)之比,換句話說,是以下公式的比值結果:

TruePositivesTruePositives+FalsePositives\frac{True Positives}{True Positives + False Positives}

召回率(敏感性) 表示實際上爲垃圾信息並且被分類爲垃圾信息的信息所佔比例,即真正例(分類爲垃圾內容並且實際上是垃圾內容的單詞)與所有爲垃圾內容的單詞之比,換句話說,是以下公式的比值結果:

TruePositivesTruePositives+FalseNegatives\frac{True Positives}{True Positives + False Negatives}

對於偏態分類分佈問題(我們的數據集就屬於偏態分類),例如如果有 100 條信息,只有 2 條是垃圾信息,剩下的 98 條不是,則準確率本身並不是很好的指標。我們將 90 條信息分類爲非垃圾信息(包括 2 條垃圾信息,但是我們將其分類爲非垃圾信息,因此它們屬於假負例),並將 10 條信息分類爲垃圾信息(所有 10 條都是假正例),依然會獲得比較高的準確率分數。對於此類情形,精確率和召回率非常實用。可以通過這兩個指標獲得 F1 分數,即精確率和召回率分數的加權平均值。該分數的範圍是 0 到 1,1 表示最佳潛在 F1 分數。

我們將使用所有四個指標確保我們的模型效果很好。這四個指標的值範圍都在 0 到 1 之間,分數儘量接近 1 可以很好地表示模型的效果如何。

'''
Instructions:
Compute the accuracy, precision, recall and F1 scores of your model using your test data 'y_test' and the predictions
you made earlier stored in the 'predictions' variable.
'''
"\nInstructions:\nCompute the accuracy, precision, recall and F1 scores of your model using your test data 'y_test' and the predictions\nyou made earlier stored in the 'predictions' variable.\n"
'''
Solution
'''
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
print('Accuracy score: ', format(accuracy_score(y_test, predictions)))
print('Precision score: ', format(precision_score(y_test, predictions)))
print('Recall score: ', format(recall_score(y_test, predictions)))
print('F1 score: ', format(f1_score(y_test, predictions)))
Accuracy score:  0.9885139985642498
Precision score:  0.9720670391061452
Recall score:  0.9405405405405406
F1 score:  0.9560439560439562

第 7 步:總結

和其他分類算法相比,樸素貝葉斯具有的一大主要優勢是能夠處理大量特徵。在我們的示例中,有數千個不同的單詞,每個單詞都被當做一個特徵。此外,即使存在不相關的特徵也有很好的效果,不容易受到這種特徵的影響。另一個主要優勢是相對比較簡單。樸素貝葉斯完全可以直接使用,很少需要調整參數,除非通常分佈數據已知的情況需要調整。
它很少會過擬合數據。另一個重要優勢是相對於它能處理的數據量來說,訓練和預測速度很快。總之,樸素貝葉斯是非常實用的算法!

恭喜!你成功地設計了一個可以有效地預測信息是否爲垃圾信息的模型!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章