机器学习笔记(Chapter 01-04)

最近在自学数据挖掘和机器学习方面的内容,参考《机器学习实战 - 美Peter Harrington》。整理笔记备忘,所有代码除小部分改动和增加外,都来自附书源码。下面为Chapter1~Chapter4内容。

Chapter 01

  • 使用NumPy
1
2
3
4
5
>>> from numpy import *
>>> random.rand(4,4) # make 4*4 random array
>>> mat1 = mat( random.rand(4,4) ) # make 4*4 random matrix
>>> mat1.I # get the reverse of mat1
>>> eye(4) # make 4*4 unit matrix
  • deb源安装numpy和matplotlib:pip install numpy,在此前需要安装python-dev。安装matplotlib前需要libpng,在sourceforge可以找到最新版本,之后apt-get install python-matplotlib。python3和python2类似。
  • 标称型和数值型:分别从有限和无限目标集中取值。
  • 选择合适算法
    • 预测目标变量的值:监督学习算法 => 目标变量为离散型:分类算法; 目标变量为连续型:回归算法。
    • 不预测目标变量:无监督学习算法 => 唯一需求是将数据划分为离散的组:聚类算法; 需要估计数据与每个组的相似度:密度估计算法。
  • 开发机器学习程序步骤:收集数据->准备输入数据->分析输入数据->训练算法->测试算法

Chapter 02 - k-近邻算法

已知样本集中每一数据与所属分类的对应关系,将待定数据的每个特征与样本集中数据对应的特征进行比较,取前k个最相似数据决断。

kNN - Category

  • 流程

    • 计算已知类别数据集中的点和当前点之间的距离(map)
    • 按距离递增排序(sort)
    • 选取于当前点距离最小的k个点(take)
    • 统计k个点所在类别的出现频率(sortBy frequency)
    • 返回前k个点中出现频率最高的类别(length . group)
    • Haskell的表示: reverse . sort . map length . group . sortBy frequency . take k . sort . map distance
  • Code - kNN.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from numpy import *
import operator
def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group, labels
def classify0(inX, dataSet, labels, k ):
dataSetSize = dataSet.shape[0]
# dataSet = group, dataSet.shape[0] = 4, dataSet.shape[1] = 2
diffMat = tile(inX, (dataSetSize, 1)) -dataSet
# inX = [1,2] tile(inX,(4,2)) = array([[1,2,1,2], tile(inX,(2,3)) = array([[1,2,1,2,1,2],
# [1,2,1,2], [1,2,1,2,1,2]])
# [1,2,1,2],
# [1,2,1,2]])
sqDiffMat = diffMat**2
# diffMat = array([[ 0. , 0.1], diffMat**2 = array([[0, 0.01],
# [ 0. , 0. ], [0, 0. ],
# [ 1. , 1. ], [1.,1. ],
# [ 1. , 0.9]]) [1.,0.81]])
sqDistances = sqDiffMat.sum(axis=1)
# sqDiffMat.sum(axis=1) = array([ 0.01, 0. , 2. , 1.81])
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()
# sortedDisIndicies = array([1, 0, 3, 2]), the indeices of the sorted distances
classCount = {}
for i in range(k) :
voteIlabel = labels[sortedDistIndicies[i]]
# get the first k data's label => k=3, ['A','A','B']
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# classCount.get(voteIlabel,default) if voteIlabel doesn't exist, return default
sortedClassCount = sorted(classCount.iteritems(),
key = operator.itemgetter(1), reverse =True)
# sortedClassCount = [('A',2),('B',1)]
return sortedClassCount[0][0]

约会网站配对效果

流程:收集数据(文本文件)->准备数据(用Python解析文本文件)->分析数据(用Matplotlib画二维扩散图)->训练算法(不适用k-近邻算法)->测试算法(使用提供的部分数据作为测试样本)->使用算法(产生命令行程序)

准备数据 - 文件->样本矩阵

  • 数据存放在文本文件datingTestSet.txt中,每行为一个样本,共1000行,每行包含三种特征。在kNN.py中创建file2matrix将输入文本文件转为样本矩阵和类标签向量。

  • Code - kNN.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines, 3))
# generate a matrix filled with zero
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat, classLabelVector

分析数据 - Matplotlib

  • 作用在于从已有的样本中观察数据分布的特点,选择合适的坐标,区分数据点从属的类别。

  • 交互命令

1
2
3
4
5
6
7
8
>>> import kNN
>>> datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')
>>> import matplotlib
>>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111)
>>> ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
>>> plt.show()
  • 增加区分,修改上面的ax.scatter为ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels)),需要从numpy库中导入array数组。此时绘制的图形采用数据中的第2列和第3列作为横纵坐标,区分出三种类型但不够清晰。若采用第1列和第2列作为坐标,得到散点图如下。

准备数据 - 归一化

  • 简单通过欧几里得距离衡量样本与数据间距离会受到数据大小范围影响,例如每年飞行常客里程数的差距远大于视频游戏百分比。因此将所有数据的特征值都归一到0~1之间或-1~1之间,公式为newValue = (old - value - min) / ( max - min)

  • Code - 归一化特征值 - kNN.py

1
2
3
4
5
6
7
8
9
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet / tile(ranges,(m,1))
return normDataSet, ranges, minvals

测试算法

  • 取样本中一部分作为测试数据

  • Code - 测试算法 - kNN.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def datingClassTest():
hoRatio = 0.10
datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],
datingLabels[numTestVecs:m], 3)
print "the classifier came back with: %d, the real answer is: %d"\
% (classifierResult, datingLabels[i])
if (classifierResult != datingLabels[i]): errorCount += 1.0
print "the total error rate is :%f" % (errorCount / float(numTestVecs))

手写识别系统

步骤

  • 收集数据:提供32*32像素格式的txt文件
  • 准备数据:编写函数img2vector()将文本文件转换为样本矩阵
  • 分析数据:在命令提示符中检验数据正确性
  • 训练算法:不适用于k-近邻算法
  • 测试算法:使用部分数据集作为测试样本

准备数据

  • 使用trainingDigits中的大约2000个例子作为样本,平均每个0~9的数字有200个样本。使用testDigits目录下的测试数据作为测试。

  • Code - img2vector - kNN.py

1
2
3
4
5
6
7
8
def img2vector(filename):
returnVect = zeros((1,1024))
fr = open(filename)
for i in range (32):
lineStr = fr.readline()
for j in range (32):
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect

测试算法

  • 测试946个文件,错误11个,修改变量k的值、随即训练样本的取样、训练样本的数目,都会对错误率产生影响。

  • Code - handwritingClassTest - kNN.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def handwritingClassTest():
from os import listdir
hwLabels = []
trainingFileList = listdir('digits/trainingDigits')
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector('digits/trainingDigits/%s' % fileNameStr)
testFileList= listdir('digits/testDigits')
errorCount = 0.0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
vectorUnderTest = img2vector('digits/testDigits/%s' % fileNameStr)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print "the classifier came back with: %d, the real answer is %d"\
% (classifierResult, classNumStr)
if (classifierResult != classNumStr) : errorCount += 1.0
print "\nthe total number of errors is: %d" % errorCount
print "\nthe total error rate is: %f" % (errorCount/float(mTest))

k-近邻算法 总结

k-近邻算法是基于实例的学习,使用算法是必须有接近实际数据的训练样本数据,并且必须保存全部数据集,使用大量的存储空间。需要对数据集中的每个数据计算距离值,实际使用过程非常耗时。并且k-近邻算法无法给出任何数据的基础结构信息,因而我们无法知晓平均实例样本和典型实例样本具有什么样的特征。k-决策树是k-近邻算法的优化,减少存储空间和计算时间开销。


Chapter 03 - 决策树 - 熵度量

k-近邻算法无法持久化分类器,每次分类都需要重新学习,并且无法给出数据的内在含义。决策树的主要优势在于其数据形式非常容易理解,并且可以保存在硬盘上。

特性和思想

  • 决策树优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关的特征数据。缺点:可能产生过度匹配问题。适用于数值型和标称型,数值型需先离散化。
  • 整体思路:大原则是“将无序的数据变得更加有序”。从当前可供学习的数据集中,选择一个特性,根据这个特性划分出来的数据分类,可以获得最高的信息增益(在划分数据集前后信息发生的变化),也可以说是最小的熵(公式l(xi) = p(xi)log(p(xi)),出现的概率越小,携带的信息量越大,熵越高,混合的数据也就越多),信息增益是熵的减少,或者是数据无序度的减少。在此划分之后,对划分出的各个分类再次进行算法,直到所有分类中均为同一类元素,或所有特性均已使用

  • Code - trees.py - 计算香农熵

1
2
3
4
5
6
7
8
9
10
11
12
13
def calcShannonEnt(dataSet): 		# Calculate the shannonEnt
numEntries = len(dataSet)
labelCounts = {}
for featVec in dataSet:
currentLabel = featVec[-1]
if currentLabel not in labelCounts.keys():
labelCounts[currentLabel] = 0
labelCounts[currentLabel] += 1
shannonEnt = 0.0
for key in labelCounts:
prob = float(labelCounts[key])/numEntries
shannonEnt -= prob * log(prob,2) # p(x)logp(x)
return shannonEnt
  • Code - trees.py - 按给定特征划分数据集
1
2
3
4
5
6
7
8
def splitDataSet(dataSet, axis, value):   	
retDataSet = []
for featVec in dataSet:
if featVec[axis] == value:
reducedFeatVec = featVec[:axis]
reducedFeatVec.extend(featVec[axis+1:])
retDataSet.append(reducedFeatVec)
return retDataSet
  • Code - trees.py - 选择最优数据集划分特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def chooseBestFeatureToSplit(dataSet):			
numFeatures = len(dataSet[0]) - 1
baseEntropy = calcShannonEnt(dataSet)
bestInfoGain = 0.0 ; bestFeature = -1
for i in range(numFeatures):
featList = [example[i] for example in dataSet]
uniqueVals = set(featList)
newEntropy = 0.0
for value in uniqueVals:
subDataSet = splitDataSet(dataSet, i ,value)
prob = len(subDataSet)/float(len(dataSet))
newEntropy += prob * calcShannonEnt(subDataSet)
# careful, newEntropy < 0
infoGain = baseEntropy - newEntropy
if infoGain > bestInfoGain:
bestInfoGain = infoGain
bestFeature = i
return bestFeature
  • Code - trees.py - 选择该分类的代表种类
1
2
3
4
5
6
7
8
9
def majorityCnt(classList): 		
classCount = {}
for vote in classList:
if vote not in classCount.keys() :
classCount[vote] = 0
classCount[vote] += 1
sortedClassCount = sorted(classCount.iteritems(),\
key = operator.itemgetter(1), reverse = True)
return sortedClassCount[0][0]
  • Code - trees.py - 递归构建决策树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def createTree(dataSet, labels):			
classList = [example[-1] for example in dataSet]
if classList.count(classList[0]) == len(classList):
return classList[0]
if len(dataSet[0]) == 1: # all the features have been used
return majorityCnt(classList)
bestFeat = chooseBestFeatureToSplit(dataSet)
bestFeatLabel = labels[bestFeat]
myTree = {bestFeatLabel:{}}
del(labels[bestFeat])
featValues = [example[bestFeat] for example in dataSet]
uniqueVals = set(featValues)
for value in uniqueVals:
subLabels = labels[:] # deep copy
myTree[bestFeatLabel][value] = createTree(splitDataSet\
(dataSet, bestFeat, value), subLabels)
return myTree

Matplotlib绘制树形图

绘制代码在treePlotter.py

使用决策树分类

  • 使用构造好的决策树对输入样例进行分类。已建立好的决策树可以使用pickle保存在本地。

  • Code - trees.py - 分类函数

1
2
3
4
5
6
7
8
9
10
11
def classify(inputTree, featLabels, testVec):
firstStr = inputTree.keys()[0]
secondDict = inputTree[firstStr]
featIndex = featLabels.index(firstStr)
for key in secondDict.keys():
if testVec[featIndex] == key:
if type(secondDict[key]).__name__ == 'dict':
classLabel = classify(secondDict[key], featLabels, testVec)
else:
classLabel = secondDict[key]
return classLabel
  • Code - trees.py - 保存和读取决策树
1
2
3
4
5
6
7
8
9
def storeTree(inputTree, filename):
import pickle
with open(filename,'w') as fw:
pickle.dump(inputTree, fw)

def grabTree(filename):
import pickle
fr = open(filename)
return pickle.load(fr)
  • Shell - 构建隐形眼镜决策树
1
2
3
4
5
>>> fr = open('lenses.txt')
>>> lenses = [inst.strip().split('\t') for inst in fr.readlines()]
>>> lensesLabels = ['age','prescript','astigmatic','tearRate']
>>> lensesTree = trees.createTree(lenses, lensesLabels)
>>> treePlotter.createPlot(lensesTree)
  • 得到的决策树如下

ID3决策树总结

上述为ID3算法构造,通过熵度量信息增益。另一个度量集合无序程度的方法是基尼不纯度,从一个数据集中随机选取子项,度量其被错误分到其他分组里的概率。然而ID3构造出的决策树可能产生过度匹配问题,即叶子结点产生的匹配过多。可以通过裁剪决策树,合并相邻的无法产生大量信息增益的叶节点消除该问题。其他算法如C4.5和CART。k-近邻算法和ID3决策树都是结果确定的分类算法,数据实例最终会被明确划分到某个分类中。


Chapter 04 - 朴素贝叶斯

基于贝叶斯决策理论的分类,在数据较少的情况下依然有效,可以处理多类别问题,但对于输入数据的准备方式较为敏感。适用数据类型为标称型数据。对于待测数据X,Pi(x)表示X属于类别i的概率,取最高者作为最终类别。

条件概率和朴素贝叶斯决策

  • 条件概率:在事件x的前提下发生事件c的概率为P(c|x),其计算公式为P(c|x) = P(x|c)P(c)/P(x)
  • 朴素贝叶斯分类器的两个假设:朴素独立,指一个特征或者单词出现的可能性与其他特征没有关系;每个特征同等重要
  • 朴素贝叶斯的一般过程
    • 收集数据
    • 准备数据:标称型或布尔型数据
    • 分析数据:有大量特征时可使用直方图
    • 训练算法:计算不同的独立特征的条件概率
    • 测试算法:计算错误率
    • 使用算法
  • 使用朴素贝叶斯进行文档分类:使用文档中的每个词作为特征并观察它们是否出现,假设每个特征需要N个样本,有M个特征就需要N^M个样本,若特征之间相互独立,则所需要的样本数为M*N个。对于一篇文章,计算出其对应的数据向量,计算这个向量所属各个类别的概率,并取最高者。

Python - 文本分类

  • 以在线社区的留言板为例,屏蔽侮辱性的言论。通过统计文档中出现各个单词的数量,进行频率分析并确定待测留言是否属于侮辱性言论。

  • Code - 从文本构建词向量 - bayes.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def loadDataSet():
postingList = [['my', 'dog', 'has', 'flea', \
'problems', 'help', 'please'],
['maybe', 'not', 'take', 'him', \
'to', 'dog', 'park', 'stupid'],
['my', 'dalmation', 'is', 'so', 'cute', \
'I', 'love', 'him'],
['stop', 'posting', 'stupid', 'worthless', 'garbage'],
['mr', 'licks', 'ate', 'my', 'steak', 'how', \
'to', 'stop', 'him'],
['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
classVec = [0, 1, 0, 1, 0, 1]
return postingList, classVec

def createVocabList(dataSet):
vocabSet = set([])
for document in dataSet:
vocabSet = vocabSet | set(document)
return list(vocabSet)

def setOfWords2Vec(vocabList, inputSet): # 贝努利模型
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print "the word: %s i snot in my Vocabulary!" % word
return returnVec
  • Code - 从词向量计算概率 - bayes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = ones(numWords); p1Num = ones(numWords)
p0Denom = 2.0; p1Denom = 2.0 # 初始化概率
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num / p1Denom) # 取对数避免出现0
p0Vect = log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
  • Code - 计算最大概率 - bayes.py
1
2
3
4
5
6
7
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
  • Code - 测试分类函数 - bayes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
def testingNB():
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(array(trainMat),array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)
testEntry = ['stupid', 'garbage']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry, 'classified as: ', classifyNB(thisDoc, p0V, p1V, pAb)
  • 文档词袋模型:在词集中,每个词出现一次和出现多次是等同的,在词袋模型中,单词出现次数对概率有影响。
1
2
3
4
5
6
def bagOfWords2VecMN(vocabList, inputSet): # 多项式模型
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] += 1
return returnVec

过滤垃圾邮件

  • 切分文本,并使用朴素贝叶斯交叉验证。将训练集中随机选择的文件从训练集中剔除,作为测试集,称为“留存交叉验证”。

  • Code - 文件解析 - bayes.py

1
2
3
4
def textParse(bigString):
import re
listOfTokens = re.split(r'\W*', bigString)
return [tok.lower() for tok in listOfTokens if len(tok) > 2]
  • Code - 垃圾邮件测试 - bayes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def spamTest():
docList = []; classList = []; fullText = [];
for i in range(1,26):
wordList = textParse(open('email/spam/%d.txt' % i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(1)
wordList = textParse(open('email/ham/%d.txt' %i).read())
docList.append(wordList)
fullText.extend(wordList)
classList.append(0)
vocabList = createVocabList(docList)
trainingSet = range(50); testSet = []
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet))) # 修改trainingSet,长度随之修改
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []; trainClasses = []
for docIndex in trainingSet:
trainMat.append(setOfWords2Vec(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(array(trainMat),array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = setOfWords2Vec(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print 'the error rate is: ', float(errorCount)/len(testSet)

从RSS个人广告中获取区域倾向

  • 安装feedparser,从RSS源收集内容。从美国两个城市中选取一些人,通过分析这些人发布的征婚广告信息,比较两个城市的人在广告用词上是否不同。
  • Code - RSS源分类器 - 高频词分类去除 - bayes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def calcMostFreq(vocabList, fullText):    # 计算出现频率
import operator
freqDict = {}
for token in vocabList:
freqDict[token] = fullText.count(token)
sortedFreq = sorted(freqDict.iteritems(), key = operator.itemgetter(1), reverse = True)
return sortedFreq[:30]

def localWords(feed):
import feedparser
feedLen = len(feed)
docList = []; classList = []; fullText = []
minLen = min([len(feedElem['entries']) for feedElem in feed])
for i in range(minLen):
for j in range(feedLen):
wordList = textParse(feed[j]['entries'][i]['summary'])
docList.append(wordList)
fullText.extend(wordList)
classList.append(j)
vocabList = createVocabList(docList)
top30Words = calcMostFreq(vocabList, fullText)
# print top30Words
# for pairW in top30Words:
# if pairW[0] in vocabList: vocabList.remove(pairW[0]) # 移除高频词
trainingSet = range(2*minLen); testSet = []
for i in range(10):
randIndex = int(random.uniform(0, len(trainingSet)))
testSet.append(trainingSet[randIndex])
del(trainingSet[randIndex])
trainMat = []; trainClasses = []
for docIndex in trainingSet:
trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex]))
trainClasses.append(classList[docIndex])
p0V, p1V, pSpam = trainNB0(array(trainMat), array(trainClasses))
errorCount = 0
for docIndex in testSet:
wordVector = bagOfWords2VecMN(vocabList, docList[docIndex])
if classifyNB(array(wordVector), p0V, p1V, pSpam) != classList[docIndex]:
errorCount += 1
print 'the error rate is: ', float(errorCount)/len(testSet)
return vocabList, p0V, p1V
  • 注释掉移除高频词的三行代码后,错误率为54%,保留这些代码错误率为70%。留言中出现次数最多的三十个词涵盖了所有用词的30%。通过下面的代码测试错误率。
1
2
3
4
5
6
7
>>> import bayes
>>> ny = feedparser.parse('http://newyork.craigslist.org/stp/index.rss')
>>> sf = feedparser.parse('http://sfbay.craigslist.org/stp/index.rss')
>>> vocabList, pSF, pNY = bayes.localWords(ny, sf)
the error rate is 0.1
>>> vocabList, pSF, pNY = bayes.localWords(ny, sf)
the error rate is 0.35
  • Code - 显示地域相关用词 - bayes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def getTopWords(ny,sf):
import operator
vocabList, p0V, p1V = localWords([ny,sf])
topNY = []; topSF = []
for i in range(len(p0V)):
if p0V[i] > -4.0 : topSF.append((vocabList[i],p0V[i]))
if p1V[i] > -4.0 : topNY.append((vocabList[i],p1V[i]))
sortedSF = sorted(topSF, key = lambda pair : pair[1], reverse = True)
print "SF**SF**SF**SF**SF"
for item in sortedSF:
print item[0]
sortedNY = sorted(topNY, key = lambda pair : pair[1], reverse = True)
print "NY**NY**NY**NY**NY"
for item in sortedNY:
print item[0]

朴素贝叶斯总结

贝叶斯概率和准则提供了一种利用已知值来估计未知概率的有效方法。可以通过特征之间的条件独立性假设,降低对数据量的需求。下溢出问题可以通过对概率求对数解决,词袋模型在解决文档分类问题比词集模型有所提高。


参考文献: 《机器学习实战 - 美Peter Harrington》

原创作品,允许转载,转载时无需告知,但请务必以超链接形式标明文章原始出处(https://forec.github.io/2016/02/04/machinelearning1-4/) 、作者信息(Forec)和本声明。

分享到