作者: 寒小阳 && 龙心尘
时间:2016年2月。
出处:http://blog.csdn.net/han_xiaoyang/article/details/50629608
http://blog.csdn.net/longxinchen_ml/article/details/50629613
声明:版权所有,转载请联系作者并注明出处
前两篇博文介绍了朴素贝叶斯这个名字读着"萌蠢"但实际上简单直接高效的方法,我们也介绍了一下贝叶斯方法的一些细节。按照老规矩,『锄头』给你了,得负责教教怎么用和注意事项,也顺便带大家去除除草对吧。恩,此节作为更贴近实际应用的部分,将介绍贝叶斯方法的优缺点、常见适用场景和可优化点,然后找点实际场景撸点例子练练手,看看工具怎么用。
PS:本文所有的python代码和ipython notebook已整理至github相应页面,欢迎下载和尝试。
既然讲的是朴素贝叶斯,那博主保持和它一致的风格,简单直接高效地丢干货了:
- 对待预测样本进行预测,过程简单速度快(想想邮件分类的问题,预测就是分词后进行概率乘积,在log域直接做加法更快)。
- 对于多分类问题也同样很有效,复杂度也不会有大程度上升。
- 在分布独立这个假设成立的情况下,贝叶斯分类器效果奇好,会略胜于逻辑回归,同时我们需要的样本量也更少一点。
- 对于类别类的输入特征变量,效果非常好。对于数值型变量特征,我们是默认它符合正态分布的。
- 对于测试集中的一个类别变量特征,如果在训练集里没见过,直接算的话概率就是0了,预测功能就失效了。当然,我们前面的文章提过我们有一种技术叫做**『平滑』操作**,可以缓解这个问题,最常见的平滑技术是拉普拉斯估测。
- 那个…咳咳,朴素贝叶斯算出的概率结果,比较大小还凑合,实际物理含义…恩,别太当真。
- 朴素贝叶斯有分布独立的假设前提,而现实生活中这些predictor很难是完全独立的。
这个部分的内容,本来应该在最后说的,不过为了把干货集中放在代码示例之前,先搁这儿了,大家也可以看完朴素贝叶斯的各种例子之后,回来再看看这些tips。
理论干货和注意点都说完了,来提提怎么快速用朴素贝叶斯训练模型吧。博主一直提倡要站在巨人的肩膀上编程(其实就是懒…同时一直很担忧写出来的代码的健壮性…),咳咳,我们又很自然地把scikit-learn拿过来了。scikit-learn里面有3种不同类型的朴素贝叶斯:
根据你的数据集,可以选择scikit-learn中以上任意一种朴素贝叶斯,我们直接举个简单的例子,用高斯分布型朴素贝叶斯建模:
# 我们直接取iris数据集,这个数据集有名到都不想介绍了...
# 其实就是根据花的各种数据特征,判定是什么花
from sklearn import datasets
iris = datasets.load_iris()
iris.data[:5]
#array([[ 5.1, 3.5, 1.4, 0.2],
# [ 4.9, 3. , 1.4, 0.2],
# [ 4.7, 3.2, 1.3, 0.2],
# [ 4.6, 3.1, 1.5, 0.2],
# [ 5. , 3.6, 1.4, 0.2]])
#我们假定sepal length, sepal width, petal length, petal width 4个量独立且服从高斯分布,用贝叶斯分类器建模
from sklearn.naive_bayes import GaussianNB
gnb = GaussianNB()
y_pred = gnb.fit(iris.data, iris.target).predict(iris.data)
right_num = (iris.target == y_pred).sum()
print("Total testing num :%d , naive bayes accuracy :%f" %(iris.data.shape[0], float(right_num)/iris.data.shape[0]))
# Total testing num :150 , naive bayes accuracy :0.960000
你看,朴素贝叶斯分类器,简单直接高效,在150个测试样本上,准确率为96%。
这是朴素贝叶斯最擅长的应用场景之一,对于不同主题的文本,我们可以用朴素贝叶斯训练一个分类器,然后将其应用在新数据上,预测主题类型。
我们使用搜狐新闻数据来实验朴素贝叶斯分类器,这部分新闻数据包括it、汽车、财经、健康等9个类别,简洁版数据解压缩后总共16289条新闻,一篇新闻一个txt,我们把数据合并到一个大文件中,一行一篇文章,同时将新闻id(指明新闻的类别)放在文章之前,然后用ICTCLAS(python的话你也可以用结巴分词)进行分词,得到以下的文本内容:
我们随机选取3/5的数据作为训练集,2/5的数据作为测试集,采用互信息对文本特征进行提取,提取出1000个左右的特征词。然后用朴素贝叶斯分类器进行训练,实际训练过程就是对于特征词,统计在训练集和各个类别出现的次数,测试阶段做预测也是扫描一遍测试集,计算相应的概率。因此整个过程非常高效,完整的运行代码如下:
# 这部分代码基本纯手撸的...没有调用开源库...大家看看就好...
#!encoding=utf-8
import sys, math, random, collections
def shuffle(inFile):
'''
简单的乱序操作,用于生成训练集和测试集
'''
textLines = [line.strip() for line in open(inFile)]
print "正在准备训练和测试数据,请稍后..."
random.shuffle(textLines)
num = len(textLines)
trainText = textLines[:3*num/5]
testText = textLines[3*num/5:]
print "准备训练和测试数据准备完毕,下一步..."
return trainText, testText
#总共有9种新闻类别,我们给每个类别一个编号
lables = ['A','B','C','D','E','F','G','H','I']
def lable2id(lable):
for i in xrange(len(lables)):
if lable == lables[i]:
return i
raise Exception('Error lable %s' % (lable))
def doc_dict():
'''
构造和类别数等长的0向量
'''
return [0]*len(lables)
def mutual_info(N,Nij,Ni_,N_j):
'''
计算互信息,这里log的底取为2
'''
return Nij * 1.0 / N * math.log(N * (Nij+1)*1.0/(Ni_*N_j))/ math.log(2)
def count_for_cates(trainText, featureFile):
'''
遍历文件,统计每个词在每个类别出现的次数,和每类的文档数
并写入结果特征文件
'''
docCount = [0] * len(lables)
wordCount = collections.defaultdict(doc_dict())
#扫描文件和计数
for line in trainText:
lable,text = line.strip().split(' ',1)
index = lable2id(lable[0])
words = text.split(' ')
for word in words:
wordCount[word][index] += 1
docCount[index] += 1
#计算互信息值
print "计算互信息,提取关键/特征词中,请稍后..."
miDict = collections.defaultdict(doc_dict())
N = sum(docCount)
for k,vs in wordCount.items():
for i in xrange(len(vs)):
N11 = vs[i]
N10 = sum(vs) - N11
N01 = docCount[i] - N11
N00 = N - N11 - N10 - N01
mi = mutual_info(N,N11,N10+N11,N01+N11) + mutual_info(N,N10,N10+N11,N00+N10)+ mutual_info(N,N01,N01+N11,N01+N00)+ mutual_info(N,N00,N00+N10,N00+N01)
miDict[k][i] = mi
fWords = set()
for i in xrange(len(docCount)):
keyf = lambda x:x[1][i]
sortedDict = sorted(miDict.items(),key=keyf,reverse=True)
for j in xrange(100):
fWords.add(sortedDict[j][0])
out = open(featureFile, 'w')
#输出各个类的文档数目
out.write(str(docCount)+"\n")
#输出互信息最高的词作为特征词
for fword in fWords:
out.write(fword+"\n")
print "特征词写入完毕..."
out.close()
def load_feature_words(featureFile):
'''
从特征文件导入特征词
'''
f = open(featureFile)
#各个类的文档数目
docCounts = eval(f.readline())
features = set()
#读取特征词
for line in f:
features.add(line.strip())
f.close()
return docCounts,features
def train_bayes(featureFile, textFile, modelFile):
'''
训练贝叶斯模型,实际上计算每个类中特征词的出现次数
'''
print "使用朴素贝叶斯训练中..."
docCounts,features = load_feature_words(featureFile)
wordCount = collections.defaultdict(doc_dict())
#每类文档特征词出现的次数
tCount = [0]*len(docCounts)
for line in open(textFile):
lable,text = line.strip().split(' ',1)
index = lable2id(lable[0])
words = text.split(' ')
for word in words:
if word in features:
tCount[index] += 1
wordCount[word][index] += 1
outModel = open(modelFile, 'w')
#拉普拉斯平滑
print "训练完毕,写入模型..."
for k,v in wordCount.items():
scores = [(v[i]+1) * 1.0 / (tCount[i]+len(wordCount)) for i in xrange(len(v))]
outModel.write(k+"\t"+scores+"\n")
outModel.close()
def load_model(modelFile):
'''
从模型文件中导入计算好的贝叶斯模型
'''
print "加载模型中..."
f = open(modelFile)
scores = {}
for line in f:
word,counts = line.strip().rsplit('\t',1)
scores[word] = eval(counts)
f.close()
return scores
def predict(featureFile, modelFile, testText):
'''
预测文档的类标,标准输入每一行为一个文档
'''
docCounts,features = load_feature_words()
docScores = [math.log(count * 1.0 /sum(docCounts)) for count in docCounts]
scores = load_model(modelFile)
rCount = 0
docCount = 0
print "正在使用测试数据验证模型效果..."
for line in testText:
lable,text = line.strip().split(' ',1)
index = lable2id(lable[0])
words = text.split(' ')
preValues = list(docScores)
for word in words:
if word in features:
for i in xrange(len(preValues)):
preValues[i]+=math.log(scores[word][i])
m = max(preValues)
pIndex = preValues.index(m)
if pIndex == index:
rCount += 1
#print lable,lables[pIndex],text
docCount += 1
print("总共测试文本量: %d , 预测正确的类别量: %d, 朴素贝叶斯分类器准确度:%f" %(rCount,docCount,rCount * 1.0 / docCount))
if __name__=="__main__":
if len(sys.argv) != 4:
print "Usage: python naive_bayes_text_classifier.py sougou_news.txt feature_file.out model_file.out"
sys.exit()
inFile = sys.argv[1]
featureFile = sys.argv[2]
modelFile = sys.argv[3]
trainText, testText = shuffle(inFile)
count_for_cates(trainText, featureFile)
train_bayes(featureFile, trainText, modelFile)
predict(featureFile, modelFile, testText)
运行结果如下,在6515条数据上,9个类别的新闻上,有84.1%的准确度:
没过瘾对吧,确实每次学完一个机器学习算法,不在实际数据上倒腾倒腾,总感觉不那么踏实(想起来高中各种理科科目都要找点题来做的感觉)。好,我们继续去Kaggle扒点场景和数据来练练手。正巧之前Kaggle上有一个分类问题,场景和数据也都比较简单,我们拿来用朴素贝叶斯试试水。问题请戳这里。
我们大致介绍一下,说的是『水深火热』的大米国,在旧金山这个地方,一度犯罪率还挺高的,然后很多人都经历过大到暴力案件,小到东西被偷,车被划的事情。当地警方也是努力地去总结和想办法降低犯罪率,一个挑战是在给出犯罪的地点和时间的之后,要第一时间确定这可能是一个什么样的犯罪类型,以确定警力等等。后来干脆一不做二不休,直接把12年内旧金山城内的犯罪报告都丢带Kaggle上,说『大家折腾折腾吧,看看谁能帮忙第一时间预测一下犯罪类型』。犯罪报告里面包括日期
,描述
,星期几
,所属警区
,处理结果
,地址
,GPS定位
等信息。当然,分类问题有很多分类器可以选择,我们既然刚讲过朴素贝叶斯,刚好就拿来练练手好了。
数据可以在Kaggle比赛数据页面下载到,大家也可以在博主提供的百度网盘地址中下载到。我们依旧用pandas载入数据,先看看数据内容。
import pandas as pd
import numpy as np
#用pandas载入csv训练数据,并解析第一列为日期格式
train=pd.read_csv('/Users/Hanxiaoyang/sf_crime_data/train.csv', parse_dates = ['Dates'])
test=pd.read_csv('/Users/Hanxiaoyang/sf_crime_data/test.csv', parse_dates = ['Dates'])
train
train.csv中的数据时间跨度为12年,包含了90w+的记录。另外,这部分数据,大家从上图上也可以看出来,大部分都是『类别』型,比如犯罪类型,比如星期几。
上述数据中类别和文本型非常多,我们要进行特征预处理,对于特征预处理的部分,我们在前面的博文机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾和机器学习系列(6)_从白富美相亲看特征预处理与选择(下)都有较细的介绍。对于类别特征,我们用最常见的因子化操作将其转成数值型,比如我们把犯罪类型用因子化进行encode,也就是说生成如下的向量:
星期一/Monday = 1,0,0,0,...
星期二/Tuesday = 0,1,0,0,...
星期三/Wednesday = 0,0,1,0,...
...
我们之前也提到过,用pandas的get_dummies()可以直接拿到这样的一个二值化的01向量。Pandas里面还有一个很有用的方法LabelEncoder可以用于对类别编号。对于已有的数据特征,我们打算做下面的粗略变换:
街区
,星期几
,时间点
用get_dummies()因子化;具体的数据和特征处理如下:
import pandas as pd
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn import preprocessing
#用LabelEncoder对不同的犯罪类型编号
leCrime = preprocessing.LabelEncoder()
crime = leCrime.fit_transform(train.Category)
#因子化星期几,街区,小时等特征
days = pd.get_dummies(train.DayOfWeek)
district = pd.get_dummies(train.PdDistrict)
hour = train.Dates.dt.hour
hour = pd.get_dummies(hour)
#组合特征
trainData = pd.concat([hour, days, district], axis=1)
trainData['crime']=crime
#对于测试数据做同样的处理
days = pd.get_dummies(test.DayOfWeek)
district = pd.get_dummies(test.PdDistrict)
hour = test.Dates.dt.hour
hour = pd.get_dummies(hour)
testData = pd.concat([hour, days, district], axis=1)
trainData
拿到初步的特征了,下一步就可以开始建模了。
因为之前的博客机器学习系列(1)_逻辑回归初步,机器学习系列(2)_从初等数学视角解读逻辑回归,机器学习系列(3)_逻辑回归应用之Kaggle泰坦尼克之灾中提到过逻辑回归这种分类算法,我们这里打算一并拿来建模,做个比较。
还需要提到的一点是,大家参加Kaggle的比赛,一定要注意最后排名和评定好坏用的标准,比如说在现在这个多分类问题中,Kaggle的评定标准并不是precision,而是multi-class log_loss,这个值越小,表示最后的效果越好。
我们可以快速地筛出一部分重要的特征,搭建一个baseline系统,再考虑步步优化。比如我们这里简单一点,就只取星期几
和街区
作为分类器输入特征,我们用scikit-learn中的train_test_split
函数拿到训练集和交叉验证集,用朴素贝叶斯和逻辑回归都建立模型,对比一下它们的表现:
ffrom sklearn.cross_validation import train_test_split
from sklearn import preprocessing
from sklearn.metrics import log_loss
from sklearn.naive_bayes import BernoulliNB
from sklearn.linear_model import LogisticRegression
import time
# 只取星期几和街区作为分类器输入特征
features = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'BAYVIEW', 'CENTRAL', 'INGLESIDE', 'MISSION',
'NORTHERN', 'PARK', 'RICHMOND', 'SOUTHERN', 'TARAVAL', 'TENDERLOIN']
# 分割训练集(3/5)和测试集(2/5)
training, validation = train_test_split(trainData, train_size=.60)
# 朴素贝叶斯建模,计算log_loss
model = BernoulliNB()
nbStart = time.time()
model.fit(training[features], training['crime'])
nbCostTime = time.time() - nbStart
predicted = np.array(model.predict_proba(validation[features]))
print "朴素贝叶斯建模耗时 %f 秒" %(nbCostTime)
print "朴素贝叶斯log损失为 %f" %(log_loss(validation['crime'], predicted))
#逻辑回归建模,计算log_loss
model = LogisticRegression(C=.01)
lrStart= time.time()
model.fit(training[features], training['crime'])
lrCostTime = time.time() - lrStart
predicted = np.array(model.predict_proba(validation[features]))
log_loss(validation['crime'], predicted)
print "逻辑回归建模耗时 %f 秒" %(lrCostTime)
print "逻辑回归log损失为 %f" %(log_loss(validation['crime'], predicted))
实验的结果如下:
我们可以看到目前的特征和参数设定下,朴素贝叶斯的log损失还低一些,另外我们可以明显看到,朴素贝叶斯建模消耗的时间0.640398秒远小于逻辑回归建模42.856376秒。
考虑到犯罪类型可能和犯罪事件发生的小时时间点相关,我们加入小时时间点特征再次建模,代码和结果如下:
from sklearn.cross_validation import train_test_split
from sklearn import preprocessing
from sklearn.metrics import log_loss
from sklearn.naive_bayes import BernoulliNB
from sklearn.linear_model import LogisticRegression
import time
# 添加犯罪的小时时间点作为特征
features = ['Friday', 'Monday', 'Saturday', 'Sunday', 'Thursday', 'Tuesday',
'Wednesday', 'BAYVIEW', 'CENTRAL', 'INGLESIDE', 'MISSION',
'NORTHERN', 'PARK', 'RICHMOND', 'SOUTHERN', 'TARAVAL', 'TENDERLOIN']
hourFea = [x for x in range(0,24)]
features = features + hourFea
# 分割训练集(3/5)和测试集(2/5)
training, validation = train_test_split(trainData, train_size=.60)
# 朴素贝叶斯建模,计算log_loss
model = BernoulliNB()
nbStart = time.time()
model.fit(training[features], training['crime'])
nbCostTime = time.time() - nbStart
predicted = np.array(model.predict_proba(validation[features]))
print "朴素贝叶斯建模耗时 %f 秒" %(nbCostTime)
print "朴素贝叶斯log损失为 %f" %(log_loss(validation['crime'], predicted))
#逻辑回归建模,计算log_loss
model = LogisticRegression(C=.01)
lrStart= time.time()
model.fit(training[features], training['crime'])
lrCostTime = time.time() - lrStart
predicted = np.array(model.predict_proba(validation[features]))
log_loss(validation['crime'], predicted)
print "逻辑回归建模耗时 %f 秒" %(lrCostTime)
print "逻辑回归log损失为 %f" %(log_loss(validation['crime'], predicted))
可以看到在这三个类别特征下,朴素贝叶斯相对于逻辑回归,依旧有一定的优势(log损失更小),同时训练时间很短,这意味着模型虽然简单,但是效果依旧强大。顺便提一下,朴素贝叶斯1.13s训练出来的模型,预测的效果在Kaggle排行榜上已经能进入Top 35%了,如果进行一些优化,比如特征处理、特征组合等,结果会进一步提高。
博主想了想,既然朴素贝叶斯最常见的应用场景就那么几个,干脆我们都一并覆盖得了。咳咳,对,还有一个非常重要的应用场景是情感分析(尤其是褒贬判定),于是我又上Kaggle溜达了一圈,扒下来一个类似场景的比赛。比赛的名字叫做当词袋/Bag of words 遇上 爆米花/Bags of Popcorn,地址为https://www.kaggle.com/c/word2vec-nlp-tutorial/,有兴趣的同学可以上去瞄一眼。
这个比赛的背景大概是:国外有一个类似豆瓣电影一样的IMDB,也是你看完电影,可以上去打个分,吐个槽的地方。然后大家就在想,有这么多数据,总得折腾点什么吧,于是乎,第一个想到的就是,赞的喷的内容都有了,咱们就来分分类,看看能不能根据内容分布褒贬。PS:很多同学表示,分个褒贬有毛线难的,咳咳,计算机比较笨,另外,语言这种东西,真心是博大精深的,我们随手从豆瓣上抓了几条《功夫熊猫3》影评下来,表示有些虽然我是能看懂,但是不处理直接给计算机看,它应该是一副『什么鬼』的表情。。。
多说一句,Kaggle原文引导里是用word2vec的方式将词转为词向量,后再用deep learning的方式做的。深度学习好归好,但是毕竟耗时耗力耗资源,我们用最最naive的朴素贝叶斯撸一把,说不定效果也能不错,不试试谁知道呢。另外,朴素贝叶斯建模真心速度快,很多场景下,快速建模快速迭代优化正是我们需要的嘛。
言归正传,回到Kaggle中这个问题上来,先瞄一眼数据。Kaggle数据页面地址为https://www.kaggle.com/c/word2vec-nlp-tutorial/data,大家也可以到博主的百度网盘中下载。数据包如下图所示:
其中包含有情绪标签的训练数据labeledTrainData,没有情绪标签的训练数据unlabeledTrainData,以及测试数据testData。labeledTrainData包括id,sentiment和review3个部分,分别指代用户id,情感标签,评论内容。
解压缩labeledTrainData后用vim打开,内容如下:
下面我们读取数据并做一些基本的预处理(比如说把评论部分的html标签去掉等等):
import re #正则表达式
from bs4 import BeautifulSoup #html标签处理
import pandas as pd
def review_to_wordlist(review):
'''
把IMDB的评论转成词序列
'''
# 去掉HTML标签,拿到内容
review_text = BeautifulSoup(review).get_text()
# 用正则表达式取出符合规范的部分
review_text = re.sub("[^a-zA-Z]"," ", review_text)
# 小写化所有的词,并转成词list
words = review_text.lower().split()
# 返回words
return words
# 使用pandas读入训练和测试csv文件
train = pd.read_csv('/Users/Hanxiaoyang/IMDB_sentiment_analysis_data/labeledTrainData.tsv', header=0, delimiter="\t", quoting=3)
test = pd.read_csv('/Users/Hanxiaoyang/IMDB_sentiment_analysis_data/testData.tsv', header=0, delimiter="\t", quoting=3 )
# 取出情感标签,positive/褒 或者 negative/贬
y_train = train['sentiment']
# 将训练和测试数据都转成词list
train_data = []
for i in xrange(0,len(train['review'])):
train_data.append(" ".join(review_to_wordlist(train['review'][i])))
test_data = []
for i in xrange(0,len(test['review'])):
test_data.append(" ".join(review_to_wordlist(test['review'][i])))
我们在ipython notebook里面看一眼,发现数据已经格式化了,如下:
紧接着又到了头疼的部分了,数据有了,我们得想办法从数据里面拿到有区分度的特征。比如说Kaggle该问题的引导页提供的word2vec就是一种文本到数值域的特征抽取方式,比如说我们在第6小节提到的用互信息提取关键字也是提取特征的一种。比如说在这里,我们打算用在文本检索系统中非常有效的一种特征:TF-IDF(term frequency-interdocument frequency)向量。每一个电影评论最后转化成一个TF-IDF向量。对了,对于TF-IDF不熟悉的同学们,我们稍加解释一下,TF-IDF是一种统计方法,用以评估一字词(或者n-gram)对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。这是一个能很有效地判定对评论褒贬影响大的词或短语的方法。
那个…博主打算继续偷懒,把scikit-learn中TFIDF向量化方法直接拿来用,想详细了解的同学可以戳sklearn TFIDF向量类。对了,再多说几句我的处理细节,停用词被我掐掉了,同时我在单词的级别上又拓展到2元语言模型(对这个不了解的同学别着急,后续的博客介绍马上就来),恩,你可以再加3元4元语言模型…博主主要是单机内存不够了,先就2元上,凑活用吧…
from sklearn.feature_extraction.text import TfidfVectorizer as TFIV
# 初始化TFIV对象,去停用词,加2元语言模型
tfv = TFIV(min_df=3, max_features=None, strip_accents='unicode', analyzer='word',token_pattern=r'\w{1,}', ngram_range=(1, 2), use_idf=1,smooth_idf=1,sublinear_tf=1, stop_words = 'english')
# 合并训练和测试集以便进行TFIDF向量化操作
X_all = train_data + test_data
len_train = len(train_data)
# 这一步有点慢,去喝杯茶刷会儿微博知乎歇会儿...
tfv.fit(X_all)
X_all = tfv.transform(X_all)
# 恢复成训练集和测试集部分
X = X_all[:len_train]
X_test = X_all[len_train:]
特征现在我们拿到手了,该建模了,好吧,博主折腾劲又上来了,那个…咳咳…我们还是朴素贝叶斯和逻辑回归都建个分类器吧,然后也可以比较比较,恩。
『talk is cheap, I’ll show you the code』,直接放码过来了哈。
# 多项式朴素贝叶斯
from sklearn.naive_bayes import MultinomialNB as MNB
model_NB = MNB()
model_NB.fit(X, y_train) #特征数据直接灌进来
MNB(alpha=1.0, class_prior=None, fit_prior=True)
from sklearn.cross_validation import cross_val_score
import numpy as np
print "多项式贝叶斯分类器20折交叉验证得分: ", np.mean(cross_val_score(model_NB, X, y_train, cv=20, scoring='roc_auc'))
# 多项式贝叶斯分类器20折交叉验证得分: 0.950837239
# 折腾一下逻辑回归,恩
from sklearn.linear_model import LogisticRegression as LR
from sklearn.grid_search import GridSearchCV
# 设定grid search的参数
grid_values = {'C':[30]}
# 设定打分为roc_auc
model_LR = GridSearchCV(LR(penalty = 'L2', dual = True, random_state = 0), grid_values, scoring = 'roc_auc', cv = 20)
# 数据灌进来
model_LR.fit(X,y_train)
# 20折交叉验证,开始漫长的等待...
GridSearchCV(cv=20, estimator=LogisticRegression(C=1.0, class_weight=None, dual=True,
fit_intercept=True, intercept_scaling=1, penalty='L2', random_state=0, tol=0.0001),
fit_params={}, iid=True, loss_func=None, n_jobs=1,
param_grid={'C': [30]}, pre_dispatch='2*n_jobs', refit=True,
score_func=None, scoring='roc_auc', verbose=0)
#输出结果
print model_LR.grid_scores_
最后逻辑回归的结果是[mean: 0.96459, std: 0.00489, params: {'C': 30}]
咳咳…看似逻辑回归在这个问题中,TF-IDF特征下表现要稍强一些…不过同学们自己跑一下就知道,这2个模型的训练时长真心不在一个数量级,逻辑回归在数据量大的情况下,要等到睡着…另外,要提到的一点是,因为我这里只用了2元语言模型(2-gram),加到3-gram和4-gram,最后两者的结果还会提高,而且朴素贝叶斯说不定会提升更快一点,内存够的同学们自己动手试试吧_
本文为朴素贝叶斯的实践和进阶篇,先丢了点干货,总结了贝叶斯方法的优缺点,应用场景,注意点和一般建模方法。紧接着对它最常见的应用场景,抓了几个例子,又来了一遍手把手系列,不管是对于文本主题分类、多分类问题(犯罪类型分类) 还是 情感分析/分类,朴素贝叶斯都是一个简单直接高效的方法。尤其是在和逻辑回归的对比中可以看出,在这些问题中,朴素贝叶斯能取得和逻辑回归相近的成绩,但是训练速度远快于逻辑回归,真正的直接和高效。