颜值
|
技术
|
信用
|
是否录用
|
高
|
大牛
|
好
|
肯定录用
|
高
|
菜鸡
|
好
|
考虑考虑
|
低
|
大牛
|
好
|
肯定录用
|
高
|
中庸
|
差
|
考虑考虑
|
高
|
大牛
|
差
|
考虑考虑
|
低
|
中庸
|
差
|
肯定不录用
|
低
|
菜鸡
|
好
|
考虑考虑
|
二、实战之实现言论过滤(简单算法)
1、问题描述(在线社区留言板)
为了不影响社区的发展,我们要屏蔽侮辱性的言论,所以要构建一个快速过滤器,如果某条留言使用而来负面或者侮辱性的语言,那么就将该留言标识为内容不当,过滤这类内容是一个很常见的需求,对此问题建立两个类别:侮辱类和非侮辱类,使用1和0表示(即,用1表示侮辱,用0表示非非侮辱)。
主要思路:给你大量的样本语句(同时每个语句附带标签,比如这句话是侮辱还是非侮辱)
2、直接上代码
# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce
"""
函数1:创建实验样本
功能说明:首先要将文本切分成词条,这个函数就是干这个用的不过,现在已经切好了
返回值说明:postingList就是词条,classVec则是词条对应的分类标签
"""
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] #类别标签向量,1代表侮辱性词汇,0代表不是
return postingList,classVec #返回实验样本切分的词条和类别标签向量
"""
函数2: 制作词汇表
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
参数说明:dataSet就是上面的postingList,也就是重复的词条样本集,而vocabSet则是无重复的词汇表
"""
def createVocabList(dataSet):
vocabSet = set([]) #创建一个空的不重复列表
for document in dataSet:
vocabSet = vocabSet | set(document) #取并集
return list(vocabSet)
"""
函数3:词汇向量化
函数说明: 根据vocabList词汇表(也就是上面函数制作的词汇表),将inputSet(你输入的词汇)向量化,
向量的每个元素为1或0,如果词汇表中有这个单词,就置1;没有,就置0
参数说明:最后返回的是文档向量(不是0就是1)
"""
def setOfWords2Vec(vocabList,inputSet):
returnVec = [0] * len(vocabList) # 创建一个其中所含元素都为0的向量
for word in inputSet: #遍历每个词条
if word in inputSet: #如果词条存在于词汇表中,则置1
returnVec[vocabList.index(word)] = 1
else:
print("词汇:%s 并没有在词汇表中" % word) # 词汇表中没有这个单词,表示出现了问
return returnVec #返回文档向量
"""
函数4:朴素贝叶斯分类器训练函数
函数说明: 利用朴树贝叶斯求出分类概率,也可以说是求出先验概率
参数说明:
输入参数trainMatrix:是所有样本数据矩阵,每行是一个样本,一列代表一个词条
输入参数trainCategory:是所有样本对应的分类标签,是一个向量,维数等于矩阵的行数
输出参数p0Vect:是一个向量,维数与上面相同,每个元素表示对应样本属于侮辱类的概率
输出参数p1Vect:是一个向量,和上面那个向量互补(因为是二分类问题),每个元素对应样本属于非侮辱类的概率
输出参数pAbusive:是一个概率值,表示这篇文档(所有样本的综合)属于侮辱类的概率
"""
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) #训练集中样本数量
numWords = len(trainMatrix[0]) #每条样本中的词条数量
pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于侮辱类的概率
p0Num = np.zeros(numWords); p1Num = np.zeros(numWords) #创建numpy.zeros数组(维度和numWords一样,但元素全是0)
p0Denom = 0.0; p1Denom = 0.0 #分母初始化为0.0
for i in range(numTrainDocs):
if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = p1Num/p1Denom #相除
p0Vect = p0Num/p0Denom
return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
"""
函数5:朴素贝叶斯分类器分类函数
函数说明: 利用几个函数得到的结果,直接对vec2Classify进行分类,说白了就是利用贝叶斯定理来求了,注意这里没有用到分母,直接求的分子
参数说明:
输入参数vec2Classify——要分类的向量
输入参数后面三个——就是函数3得到的三个输出向量
输入参数:就是分类结果了(因为分类标签就只有2个,如果更改数据的话,这里要改一下)
"""
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = reduce(lambda x,y:x*y, vec2Classify * p1Vec)*pClass1 #对应元素相乘,相同为1,不同为0
p0 =reduce(lambda x,y:x*y, vec2Classify*p0Vec)*(1.0-pClass1)
print('p0:',p0)
print('p1:',p1)
if p1>p0:
return 1
else:
return 0
"""
函数6:测试朴素贝叶斯分类器
函数说明: 这个就是一个测试函数了
"""
def testingNB():
listOPosts,listClasses = loadDataSet() #创建实验样本
myVocabList = createVocabList(listOPosts) #创建词汇表
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) #将实验样本向量化
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses)) #训练朴素贝叶斯分类器
testEntry = ['love', 'my', 'dalmation'] #测试样本1
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
testEntry = ['stupid', 'garbage'] #测试样本2
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
if __name__ == '__main__':
testingNB()
3、上述算法函数分析
(1)函数1:创建实验样本
这个就相当简单了,将训练样本语句分割成一个个词条(注意这是重复的),返回词条矩阵和标签向量
(2)函数2:制作词汇表
最后生成的词汇表不再是矩阵,而是一个单纯的列表,依次列举了所有的单词,如下图所示:
(3)函数3:词汇向量化
这个函数有两个参数vocabList和inputSet,第一个参数vocabList就是词汇表,而inputSet则是一句输入单词向量(),举个例子,我输入的vacabList(就是上面的词汇表)为:
输入的inputSet为:
最后输出结果(即returnVec)是:
上面标1的表示,在词汇表的这些位置的单词,你的inputSet语句中都曾经有,而标有0的位置表示没有出现过
注意:这里的1表示的是——出现过,而不是出现次数,这里你无论出现多少次,只要出现就是1,没有出现就是0,不会出现大于1的情况
(4)函数4:朴素贝叶斯分类器训练函数
这个函数说白了就是将你所有样本语句中的词汇都训练一下,看看这些词汇哪些属于侮辱类的词汇(当然得到的是概率值),里面涉及到几个参数,这里讲解一下
输入参数:
函数内部的变量参数:
上述列出的数据集中,一共有六个数据样本,但是标有侮辱标签也就是1的,只有三个,找出这三个语句,然后将其词汇按照词汇表一一对照,有一个词,就在那个词上数量,加1,最后就得到上面的p1Num向量
输出参数:
(5)函数5:朴素贝叶斯分类器分类函数
这个函数是上面4个函数的综合运用,主要功能就是你给我一条语句,然后我将这条语句划分成一个个词条,然后根据我拥有的带有标签的样本数据集得到的模型参数(也就是各种先验概率),计算这条语句属于侮辱类语句的概率和非侮辱类的概率,然后取两者大的概率为其分类标签
输入参数:
函数内部参数:
困难函数:
(6)函数6:测试朴素贝叶斯分类器
这个函数其实属于函数5的升级版,虽然函数5也可以对一条语句进行分类,但是运行函数5要进行语句的预处理,因为函数5的输出参数并不是我们直接就有的,而是要通过运行上面前4个函数才可以得到,所以所以说函数6是总领的作用,将前5个函数都综合起来
当然,这是一个简单的测试函数,测试语句都是自己写在代码中的,如果要运用到现实生活中,肯定是不能这么写的,因为测试数据集往往是比较大的,要程序自己会读取文本才可以,下一个实例,我们会再讲如何进行,这个实例只是简单的将朴素贝叶斯的算法框架得了出来
(7)显示结果:
(1)首先,要改进函数4,也就是trainNB0(trainMatrix,trainCategory),因为这个函数中有分子和分母初始化的步骤,具体改动如下:
"""
函数4:朴素贝叶斯分类器训练函数
函数说明: 利用朴树贝叶斯求出分类概率,也可以说是求出先验概率
参数说明:
输入参数trainMatrix:是所有样本数据矩阵,每行是一个样本,一列代表一个词条
输入参数trainCategory:是所有样本对应的分类标签,是一个向量,维数等于矩阵的行数
输出参数p0Vect:是一个向量,维数与上面相同,每个元素表示对应样本属于侮辱类的概率
输出参数p1Vect:是一个向量,和上面那个向量互补(因为是二分类问题),每个元素对应样本属于非侮辱类的概率
输出参数pAbusive:是一个概率值,表示这篇文档(所有样本的综合)属于侮辱类的概率
"""
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory)/float(numTrainDocs)
p0Num = np.ones(numWords);p1Num = np.ones(numWords) #词条初始化次数为1,避免出现0的情况,拉普拉斯平滑第一步
p0Denom = 2.0; p1Denom = 2.0 #分母初始化为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 = np.log(p1Num/p1Denom) #相除,然后取对数,防止下溢出
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive
上图中,我只对有更改的地方写了注释,其他地方,你们可以看上面的,避免看的干扰
(2)这一步更改函数5,因为取对数后,我们最后的乘积方式也要改变一下
"""
函数5:朴素贝叶斯分类器分类函数
函数说明: 利用几个函数得到的结果,直接对vec2Classify进行分类,说白了就是利用贝叶斯定理直接求了
但是这里并没有求分母,因为要比较的概率中分母都一样,可以不用求分母
参数说明:
输入参数vec2Classify——要分类的向量
输入参数后面三个——就是函数3得到的三个输出向量
输入参数:就是分类结果了(因为分类标签就只有2个,如果更改数据的话,这里要改一下)
"""
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify*p1Vec) + np.log(pClass1) #对应元素相乘,log(A*B)=logA + logB
p0 =sum(vec2Classify*p0Vec) + np.log(1.0-pClass1)
print('p0:',p0)
print('p1:',p1)
if p1>p0:
return 1
else:
return 0
(3)好了,这里再贴一下最终改版:
# -*- coding: UTF-8 -*-
import numpy as np
from functools import reduce
"""
函数1:创建实验样本
功能说明:首先要将文本切分成词条,这个函数就是干这个用的不过,现在已经切好了
返回值说明:postingList就是词条,classVec则是词条对应的分类标签
"""
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] #类别标签向量,1代表侮辱性词汇,0代表不是
return postingList,classVec #返回实验样本切分的词条和类别标签向量
"""
函数2: 制作词汇表
函数说明:将切分的实验样本词条整理成不重复的词条列表,也就是词汇表
参数说明:dataSet就是上面的postingList,也就是重复的词条样本集,而vocabSet则是无重复的词汇表
"""
def createVocabList(dataSet):
vocabSet = set([]) #创建一个空的不重复列表
for document in dataSet:
vocabSet = vocabSet | set(document) #取并集
return list(vocabSet)
"""
函数3:词汇向量化
函数说明: 根据vocabList词汇表(也就是上面函数制作的词汇表),将inputSet(你输入的词汇)向量化,
向量的每个元素为1或0,如果词汇表中有这个单词,就置1;没有,就置0
参数说明:最后返回的是文档向量(不是0就是1)
"""
def setOfWords2Vec(vocabList,inputSet):
returnVec = [0] * len(vocabList) # 创建一个其中所含元素都为0的向量
for word in inputSet: #遍历每个词条
if word in inputSet: #如果词条存在于词汇表中,则置1
returnVec[vocabList.index(word)] = 1
else:
print("词汇:%s 并没有在词汇表中" % word) # 词汇表中没有这个单词,表示出现了问
return returnVec #返回文档向量
"""
函数4:朴素贝叶斯分类器训练函数
函数说明: 利用朴树贝叶斯求出分类概率,也可以说是求出先验概率
参数说明:
输入参数trainMatrix:是所有样本数据矩阵,每行是一个样本,一列代表一个词条
输入参数trainCategory:是所有样本对应的分类标签,是一个向量,维数等于矩阵的行数
输出参数p0Vect:是一个向量,维数与上面相同,每个元素表示对应样本属于侮辱类的概率
输出参数p1Vect:是一个向量,和上面那个向量互补(因为是二分类问题),每个元素对应样本属于非侮辱类的概率
输出参数pAbusive:是一个概率值,表示这篇文档(所有样本的综合)属于侮辱类的概率
"""
def trainNB0(trainMatrix,trainCategory):
numTrainDocs = len(trainMatrix) #训练集中样本数量
numWords = len(trainMatrix[0]) #每条样本中的词条数量
pAbusive = sum(trainCategory)/float(numTrainDocs) #文档属于侮辱类的概率
p0Num = np.ones(numWords);p1Num = np.ones(numWords) #词条初始化次数为1,避免出现0的情况,拉普拉斯平滑第一步
p0Denom = 2.0; p1Denom = 2.0 #分母初始化为2.0,拉普拉斯平滑第二步
for i in range(numTrainDocs): #对每个标签进行判断
if trainCategory[i] == 1: #统计属于侮辱类的条件概率所需的数据,即P(w0|1),P(w1|1),P(w2|1)···
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else: #统计属于非侮辱类的条件概率所需的数据,即P(w0|0),P(w1|0),P(w2|0)···
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = np.log(p1Num/p1Denom) #相除,然后取对数,防止下溢出
p0Vect = np.log(p0Num/p0Denom)
return p0Vect,p1Vect,pAbusive #返回属于侮辱类的条件概率数组,属于非侮辱类的条件概率数组,文档属于侮辱类的概率
"""
函数5:朴素贝叶斯分类器分类函数
函数说明: 利用几个函数得到的结果,直接对vec2Classify进行分类,说白了就是利用贝叶斯定理直接求了
但是这里并没有求分母,因为要比较的概率中分母都一样,可以不用求分母
参数说明:
输入参数vec2Classify——要分类的向量
输入参数后面三个——就是函数3得到的三个输出向量
输入参数:就是分类结果了(因为分类标签就只有2个,如果更改数据的话,这里要改一下)
"""
def classifyNB(vec2Classify,p0Vec,p1Vec,pClass1):
p1 = sum(vec2Classify*p1Vec) + np.log(pClass1) #对应元素相乘,log(A*B)=logA + logB
p0 =sum(vec2Classify*p0Vec) + np.log(1.0-pClass1)
print('p0:',p0)
print('p1:',p1)
if p1>p0:
return 1
else:
return 0
"""
函数6:测试朴素贝叶斯分类器
函数说明: 这个就是一个测试函数了
"""
def testingNB():
listOPosts,listClasses = loadDataSet() #创建实验样本
myVocabList = createVocabList(listOPosts) #创建词汇表
trainMat=[]
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc)) #将实验样本向量化
p0V,p1V,pAb = trainNB0(np.array(trainMat),np.array(listClasses)) #训练朴素贝叶斯分类器
testEntry = ['love', 'my', 'dalmation'] #测试样本1
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
testEntry = ['stupid', 'garbage'] #测试样本2
thisDoc = np.array(setOfWords2Vec(myVocabList, testEntry)) #测试样本向量化
if classifyNB(thisDoc,p0V,p1V,pAb):
print(testEntry,'属于侮辱类') #执行分类并打印分类结果
else:
print(testEntry,'属于非侮辱类') #执行分类并打印分类结果
if __name__ == '__main__':
testingNB()
然后输出结果:
这里输出为什么是负数呢?因为我们最后都采用了对数,那几个相乘的元素都是小于1的,自然是负数,负数相加最后自然也是负数了!这个问题可以参考上面的ln函数图!
1、IndentationError: unindent does not match any outer indentation level
截图如下:
说白了,就是粘贴复制代码的时候,有些tab键和空格搞混了,缩进的问题,具体解决方案:解决问题
当然,我用的是Pycharm,所以上面那个解决方面没有什么用,我直接将那个出问题的代码重新自己写了一遍,这才搞定了,以后如果遇到解决方案,会在这里写出来的。
四、资源
五、资源参考
1、西瓜书:链接:https://pan.baidu.com/s/1ozCZb-912fB2auyAGRcNmw 密码:3wg2
2、首先自然是《机器学习实战》这本电子书了,链接为:链接:https://pan.baidu.com/s/1nfJuwI2JQ6OAjM5Jbi7MOg 密码:l5xv(高清彩色版本)
3、这本书附带的源代码与数据集(这里的源代码是这本书附带的,不是我上面写的,有一部分是Python2格式):链接:https://pan.baidu.com/s/1mDqTlRVPAZBkok4E7ToVHQ 密码:162r
4、一些参考书:
用Python做科学计算:链接:https://pan.baidu.com/s/1hEwKT4k3jAqEDslla2L1eQ 密码:0k5l
笨方法学Python:链接:https://pan.baidu.com/s/1MKCKoRZjPV0Q4rQoa2LEpg 密码:enfk
流畅的Python:链接:https://pan.baidu.com/s/1Ln28HA3ITarp4sCPtT85VA 密码:elpc