理解貝葉斯公式其實就只要掌握:一、條件機率的定義;二、乘法原理html
\[ P(c_i|x) = \cfrac{P(x|c_i)P(c_i)}{P(x)} \]python
這裏 \(x\) 是一個向量,有幾個特徵,就有幾個維度。樸素貝葉斯就假設這些特徵獨立同分布,即算法
\[ P(x|c_i) = P(x_1|c_i)P(x_2|c_i) \cdots P(x_n|c_i) \]網絡
在實現樸素貝葉斯的時候,還要注意一寫技巧:app
一、數據平滑處理;
二、在計算機中,多個小數相乘趨於 0 ,所以,經常對每個機率取對數(這種狀況不少書籍上稱之爲「下溢」)。機器學習
數據下載:http://archive.ics.uci.edu/ml/datasets.html學習
關於短信的分類在這個網頁下載:http://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection測試
數據以下:每一行表示一條信息和真實的分類結果。一行的開始不是 ham 就是 spam,其中,ham 表示合理合法的郵件,spam 表示是一些廣告,即垃圾短信。ui
每一行按照分隔符 "\t" 分割成先後兩部分:
第 1 部分:標識短信是不是垃圾短信;
第 2 部分:一條短信的具體內容。spa
第 2 部分還要繼續作處理:
一、按照空格進行分割,即分詞,若是是中文短信,就要使用一些中文分詞庫了;
二、把分割之後的單詞所有處理成小寫;
語法上的大小寫不該該被算法認爲是兩個詞。
三、去除停用詞:
這一步,實際上就是把一些常見的詞「你」、「我」、「他」、「是」、「的」以內的去掉,這些詞很大很長度上也只是撐起了句子的結構,對錶達句子的情感來講,沒有幫助。
參考代碼:
class FileOperate: def __init__(self, data_path, label): self.data_path = data_path self.label = label def load_data(self): with open(self.data_path, 'r', encoding='utf-8') as fr: content = fr.readlines() print("一共 {} 條數據。".format(len(content))) X = list() y = list() for line in content: result = line.split(self.label, maxsplit=2) X.append(FileOperate.__clean_data(result[1])) y.append(1 if result[0]=='spam' else 0) return X, y @staticmethod def __clean_data(origin_info): ''' 清洗數據,去掉非字母的字符,和字節長度小於 2 的單詞 :return: ''' # 先轉換成小寫 # 把標點符號都替換成空格 temp_info = re.sub('\W', ' ', origin_info.lower()) # 根據空格(大於等於 1 個空格) words = re.split(r'\s+', temp_info) return list(filter(lambda x: len(x) >= 3, words))
通過上面的處理,獲得的一條短信的其實是下面這樣一個單詞列表:
['until', 'jurong', 'point', 'crazy', 'available', 'only', 'bugis', 'great', 'world', 'buffet', 'cine', 'there', 'got', 'amore', 'wat']
接下來,把所有的數據集分紅訓練數據集和測試數據集
根據公式
\[ P(c_i|x) = \cfrac{P(x|c_i)P(c_i)}{P(x)} \]
\(P(x)\) :對全部的數據都同樣,所以咱們能夠不用計算。
\(P(c_i)\):這是先驗機率,其實把 y 遍歷一遍,就能夠獲得了。
\(P(x|c_i)\):由於咱們假設 \(P(x|c_i) = P(x_1|c_i)P(x_2|c_i) \cdots P(x_n|c_i)\),所以就要對兩個類別都去作詞頻統計。具體細節以下:
一、首先創建單詞表,這個單詞表是從全部的數據中獲得;
二、而後針對兩個類別,分別統計單詞表出現的次數,其實就是 word count,用一個 map(Python 中叫 dict)去統計詞頻;
這裏有個細節:
參考代碼(包含了預測的代碼,看這一部分的時候能夠暫時略過,只看 fit 的部分,fit 其實就是在作單詞頻數統計):
class NaiveBayes: def __init__(self): self.__ham_count = 0 # 非垃圾短信數量 self.__spam_count = 0 # 垃圾短信數量 self.__ham_words_count = 0 # 非垃圾短信單詞總數 self.__spam_words_count = 0 # 垃圾短信單詞總數 self.__ham_words = list() # 非垃圾短信單詞列表 self.__spam_words = list() # 垃圾短信單詞列表 # 訓練集中不重複單詞集合 self.__word_dictionary_set = set() self.__word_dictionary_size = 0 self.__ham_map = dict() # 非垃圾短信的詞頻統計 self.__spam_map = dict() # 垃圾短信的詞頻統計 self.__ham_probability = 0 self.__spam_probability = 0 def fit(self, X_train, y_train): self.build_word_set(X_train, y_train) self.word_count() def predict(self, X_train): return [self.predict_one(sentence) for sentence in X_train] def build_word_set(self, X_train, y_train): ''' 第 1 步:創建單詞集合 :param X_train: :param y_train: :return: ''' for words, y in zip(X_train, y_train): if y == 0: # 非垃圾短信 self.__ham_count += 1 self.__ham_words_count += len(words) for word in words: self.__ham_words.append(word) self.__word_dictionary_set.add(word) if y == 1: # 垃圾短信 self.__spam_count += 1 self.__spam_words_count += len(words) for word in words: self.__spam_words.append(word) self.__word_dictionary_set.add(word) # print('非垃圾短信數量', self.__ham_count) # print('垃圾短信數量', self.__spam_count) # print('非垃圾短信單詞總數', self.__ham_words_count) # print('垃圾短信單詞總數', self.__spam_words_count) # print(self.__word_dictionary_set) self.__word_dictionary_size = len(self.__word_dictionary_set) def word_count(self): # 第 2 步:不一樣類別下的詞頻統計 for word in self.__ham_words: self.__ham_map[word] = self.__ham_map.setdefault(word, 0) + 1 for word in self.__spam_words: self.__spam_map[word] = self.__spam_map.setdefault(word, 0) + 1 # 【下面兩行計算先驗機率】 # 非垃圾短信的機率 self.__ham_probability = self.__ham_count / (self.__ham_count + self.__spam_count) # 垃圾短信的機率 self.__spam_probability = self.__spam_count / (self.__ham_count + self.__spam_count) def predict_one(self, sentence): ham_pro = 0 spam_pro = 0 for word in sentence: # print('word', word) ham_pro += math.log( (self.__ham_map.get(word, 0) + 1) / (self.__ham_count + self.__word_dictionary_size)) spam_pro += math.log( (self.__spam_map.get(word, 0) + 1) / (self.__spam_count + self.__word_dictionary_size)) ham_pro += math.log(self.__ham_probability) spam_pro += math.log(self.__spam_probability) # print('垃圾短信機率', spam_pro) # print('非垃圾短信機率', ham_pro) return int(spam_pro >= ham_pro)
咱們再看看樸素貝葉斯公式:
\[ P(c_i|x) = \cfrac{P(x|c_i)P(c_i)}{P(x)} = \cfrac{P(x_1|c_i)P(x_2|c_i) \cdots P(x_n|c_i) \cdot P(c_i)}{P(x)} \]
對於一條預測數據,分別針對兩個類,計算分子的對數,而後比較大小便可,即
\[ \log P(x_1|c_i)P(x_2|c_i) \cdots P(x_n|c_i) \cdot P(c_i) = \log P(x_1|c_i) + \log P(x_2|c_i) \cdots + \log P(x_n|c_i) + \log \cdot P(c_i) \]
樸素貝葉斯其實就是這麼簡單。在這個數據集上,咱們能夠獲得準確率:0.9755922469490309。
(把代碼傳到 GitHub 上。)
咱們這個例子只是對於二分類問題而言,而且特徵都是離散型的。樸素貝葉斯在 scikit-learn 上有伯努利樸素貝葉斯(就是咱們這個例子使用到的模型),多項式樸素貝葉斯、高斯樸素貝葉斯,這些在劉建平的文章《scikit-learn 樸素貝葉斯類庫使用小結》(https://www.cnblogs.com/pinard/p/6074222.html)中有介紹。
一、李航《統計學習方法》
關鍵詞:貝葉斯估計、拉普拉斯平滑
二、周志華《機器學習》
這兩本教材上都給出了詳細的講解和例子。
三、《機器學習實戰》
這本書上給出了參考的代碼,可是代碼比較冗長,看起來會有點累。
其它在網絡上的參考資料:
樸素貝葉斯分類器詳解及中文文本輿情分析(附代碼實踐)
https://mp.weixin.qq.com/s/Pi30jA1xUbXg3dSdlvdU-g
深刻理解樸素貝葉斯(Naive Bayes)
https://blog.csdn.net/li8zi8fa/article/details/76176597
劉建平:樸素貝葉斯算法原理小結 https://www.cnblogs.com/pinard/p/6069267.html