首页 > 代码库 > 机器学习之基于朴素贝叶斯文本分类算法
机器学习之基于朴素贝叶斯文本分类算法
原理
在分类(classification)问题中,常常需要把一个事物分到某个类别。一个事物具有很多属性,把它的众多属性看做一个向量,即
x的集合记为X,称为属性集。一般X和Y的关系是不确定的,你只能在某种程度上说x有多大可能性属于类
在训练阶段,我们要根据从训练数据中收集的信息,对X和Y的每一种组合学习后验概率
在比较不同Y值的后验概率时,分母P(X)总是常数,因此可以忽略。先验概率P(Y)可以通过计算训练集中属于每一个类的训练样本所占的比例容易地估计。这里直接计算P(Y|X)比较麻烦,而朴素贝叶斯分类提出了一个独立性假设:x1,x2,...,xn相互独立,这也是被称之为“朴素”的原因之一。于是P(X|Y) = P(x1|Y)P(x2|Y)...P(xn|Y),而P(xi|Y)很好求了。
对于二元分类,要比较P(Y=1|X)和P(Y=0|X)的大小,只需比较分子P(X|Y)P(Y)部分。因此只需计算n个条件概率和先验概率。
文本分类
<d,c>={Beijing joins the World Trade Organization, China}
对于这个只有一句话的文档,我们把它归类到 China,即打上china标签。
我们期望用某种训练算法,训练出一个函数γ,能够将文档映射到某一个类别:
γ:X→C
这种类型的学习方法叫做有监督学习,因为事先有一个监督者(我们事先给出了一堆打好标签的文档)像个老师一样监督着整个学习过程。
朴素贝叶斯分类器是一种有监督学习,常见有两种模型,多项式模型(multinomial model)和伯努利模型(Bernoulli model)。
多项式模型
在多项式模型中, 设某文档d=(t1,t2,…,tk),tk是该文档中出现过的单词,允许重复,则
先验概率P(c)= 类c下单词总数/整个训练样本的单词总数
类条件概率P(tk|c)=(类c下单词tk在各个文档中出现过的次数之和+1)/(类c下单词总数+|V|)
V是训练样本的单词表(即抽取单词集合,单词出现多次,只算一个),|V|则表示训练样本包含多少种单词。在这里,m=|V|, p=1/|V|。
P(tk|c)可以看作是单词tk在证明d属于类c上提供了多大的证据,而P(c)则可以认为是类别c在整体上占多大比例(有多大可能性)。
伯努利模型
P(c)= 类c下文件总数/整个训练样本的文档总数
P(tk|c)=(类c下包含单词tk的文件数+1)/(类c下文档总数+2) 在这里,m=2, p=1/2。
#这里一定要注意:伯努利是以文档为粒度的,所以分母是文档总数,而不是网上以讹传讹的类c下单词总数
这里贴几个以讹传讹的链接:
http://blog.csdn.net/kongying168/article/details/7026389
http://cn.soulmachine.me/blog/20100528/
还有好多其他的就不一一列举了。
上面两个模型中的分子分母都加上了一些数,这是为了防止某个条件概率为0,从而整个P(X|Y) = P(x1|Y)P(x2|Y)...P(xn|Y)乘积为0。
实例演示
#!/usr/bin/python # NaiveBayes classification # lming_08 2014.07.06 import math import numpy as np from sklearn import metrics class NaiveBayes: def __init__(self, trainFile): self.trainingFile = trainFile self.trainingData = self.read_data(trainFile) # Read training or testing data def read_data(self, file): data = [] fd = open(file) for line in fd: arr = line.rstrip().split(',') # Turn an instance's last column(y column) as integer arr[-1] = int(arr[-1]) # Append the instance to trainingData data.append(tuple(arr)) fd.close() return data def train_model_with_Bernoulli(self): self.sumPosInstance = 0. self.sumNegInstance = 0. self.termfreq = {} for instance in self.trainingData: if int(instance[-1]) == 1: self.sumPosInstance += 1 else: self.sumNegInstance += 1 for i in range(len(instance) - 1): key = str() if i < 55: if float(instance[i]) > 0: key = "freq" + "|" + str(i + 1) + "|" + "1" else: key = "freq" + "|" + str(i + 1) + "|" + "0" else: key = "length" + "|" + str(i + 1) + "|" + instance[i] if key not in self.termfreq: self.termfreq[key] = [0, 0] if int(instance[-1]) == 1: self.termfreq[key][1] += 1 else: self.termfreq[key][0] += 1 # prior_prob = p(y = 1) self.prior_prob = self.sumPosInstance / (self.sumPosInstance + self.sumNegInstance) # prior_ratio = p(y=1) / p(y=0) self.prior_ratio = self.sumPosInstance / self.sumNegInstance # the function should be called before predict() def set_testfile(self, testFile): self.testing_data = self.read_data(testFile) def predict(self): self.testingY = [] self.predict_result = [] for instance in self.testing_data: self.predict_result.append(self.predict_instance_with_Bernoulli(instance)) self.testingY.append(instance[-1]) def get_statistics(self): true_classify_count = 0. false_classify_count = 0. for instance in self.testing_data: post_prob = self.predict_instance_with_Bernoulli(instance) if post_prob >= 0.5 and instance[-1] == 1: true_classify_count += 1 elif post_prob >= 0.5 and instance[-1] == 0: false_classify_count += 1 elif post_prob < 0.5 and instance[-1] == 1: false_classify_count += 1 elif post_prob < 0.5 and instance[-1] == 0: true_classify_count += 1 return true_classify_count, false_classify_count def predict_instance_with_Bernoulli(self, instance): f = 0. for i in range(len(instance) - 1): key = str() if i < 55: if float(instance[i]) > 0: key = "freq" + "|" + str(i + 1) + "|" + "1" else: key = "freq" + "|" + str(i + 1) + "|" + "0" else: key = "length" + "|" + str(i + 1) + "|" + instance[i] if key in self.termfreq: f += math.log( (self.termfreq[key][1] + 1) / ((self.termfreq[key][0] + 2) * self.prior_ratio) ) posterior_ratio = self.prior_ratio * math.exp(f) # posterior probability prob = posterior_ratio / (1. + posterior_ratio) return prob def getAUC(self): y = np.array(self.testingY) pred = np.array(self.predict_result) fpr, tpr, thresholds = metrics.roc_curve(y, pred) # metrics.roc_curve(y, pred, pos_label=1) auc = metrics.auc(fpr, tpr) return auc def main(trainFile, testFile): nb = NaiveBayes(trainFile) nb.train_model_with_Bernoulli() nb.set_testfile(testFile) nb.predict() print("the value of AUC: " % nb.getAUC()) print("the value of priori probability:" % nb.priorProb) if __name__ == "__main__": if len(argv) != 3: print "Usage: python %s trainFile(in) testFile(out)" % __file__ sys.exit(0) main(argv[1], argv[2])
#!/usr/bin/python # Partitioning a file into training(80%) and testing file(20%) # lming_08 2014.07.06 from random import randint def partition_file(file, train_file, test_file): ltest = [] ltrain = [] fd = open(file, "r") train_fd = open(train_file, "w") test_fd = open(test_file, "w") test_index = 0 train_index = 0 for line in fd: rnum = randint(1, 10) if rnum == 5 or rnum == 6: test_index += 1 ltest.extend(line) if test_index == 100: test_fd.writelines(ltest) ltest = [] test_index = 0 else: train_index += 1 ltrain.extend(line) if train_index == 100: train_fd.writelines(ltrain) ltrain = [] train_index = 0 if len(ltest) > 0: test_fd.writelines(ltest) if len(ltrain) > 0: train_fd.writelines(ltrain) train_fd.close() test_fd.close() fd.close()
Classify.py 执行文件
#!/usr/bin/python% # main execute file # lming_08 2014.07.06 import sys from sys import argv from NaiveBayes import NaiveBayes from PartitionFile import partition_file def main(srcFile, trainFile, testFile): partition_file(srcFile, trainFile, testFile) nb = NaiveBayes(trainFile) nb.train_model_with_Bernoulli() nb.set_testfile(testFile) nb.predict() print("the value of AUC: %f" % nb.getAUC()) print("the value of priori probability: %f" % nb.prior_prob) true_classify_count, false_classify_count = nb.get_statistics() error_rate = false_classify_count/(true_classify_count + false_classify_count) print("the error rate : %f" % error_rate) if __name__ == "__main__": if len(argv) != 4: print "Usage: python %s srcFile(in) trainFile(out) testFile(out)" % __file__ sys.exit(0) main(argv[1], argv[2], argv[3])
执行结果为:
参考文献
http://cn.soulmachine.me/blog/20100528/
http://www.chepoo.com/naive-bayesian-text-classification-algorithm-to-learn.html
http://blog.csdn.net/chjjunking/article/details/5933105