理解贝叶斯公式其实就只要掌握:一、条件几率的定义;二、乘法原理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