朴素贝叶斯、费舍尔分类方法

朴素贝叶斯、费舍尔分类法

案例:有一批图书分类样本数据,用朴素贝叶斯、费舍尔分类法对样本数据学习进行分类。

数据清洗

这里我们不讨论数据的获取和清洗,而是聚焦在使用朴素贝叶斯、费舍尔分类法上。假设我们已经从数据中确定了几列有价值的数据,并做好了数据清洗。清洗后生成样本数据如下:

#第一列Title|第二列Publisher|第三列Summary|第四列图书分类
Handsbook for Python|Oreilly| a book for Python|Python
Handsbook for Linux|Oreilly| a book for Linux|Linux
Handsbook for Hive|Oreilly|a book for Hive|Hive
Handsbook for Spark|Oreilly| a book for Spark|Spark

思路逻辑图:

Python代码

1、定义特征、提取特征 getfeatures.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#@Time: 2019-11-10 22:25
#@Author: gaoll

#特征定义方法1
'''
(1)将Title中出现的单词word当作一个特征,字符长度2-10之间
(2)将Publiser当作一个特征
(3)将Summary中出现的单词word当作特征,字符长度2-20之间
(4)将Summary中出现的两个词的词组当作特征
(5)统计Summary中出现的大写单词,如果出现频次超多30%则作为一个特征,记作UPPERCASE。这是一个虚拟特征。
'''
import re
def getfeature1(item):
	arr = item.split('|') #训练样本数据,第一列Title|第二列Publisher|第三列Summary|分类
	Title = arr[0]
	Publisher = arr[1]
	Summary = arr[2]
	category = arr[3]
	#print(Title)
	#print(Publisher)
	#print(Summary)

	f = {} #建立一个空字典存放特征

	splitter = re.compile('\\W*') #按照文中出现的特殊字符,对字符串进行分割

	#(1)Title中的出现的单词
	titlewords = [s.lower() for s in Title.split(' ') if len(s)>2 and len(s)<20]
	#print(titlewords)
	for w in titlewords:
		f['Title:'+w] = 1 #将Title中出现的单词作为一个特征,它区分于其他位置出现的特征

	#(2)Publisher作为一个特征
	f['Publisher:'+Publisher] = 1


	#(3)Summary中出现的单词作为特征\(4)Summary中出现的两个词的词组作为特征
	summarywords = [s for s in Summary.split(' ')  if len(s)>2 and len(s)<20]
	#print(summarywords)
	uc = 0
	for i in range(len(summarywords)):
		w = summarywords[i]

		if w.isupper():
			uc += 1
		
		f[w.lower()] = 1  #(3)Summary中出现的单词作为特征

		if i < len(summarywords) -1 :
			twowords = ' '.join([summarywords[i].lower(),summarywords[i+1].lower()])  
			f[twowords] = 1  #(4)Summary中出现的两个词的词组作为特征

	#(5)统计Summary中出现的大写单词,如果出现频次超多30%则作为一个特征,记作UPPERCASE。这是一个虚拟特征。
	#print(uc)
	#print(summarywords)
	if float(uc)/len(summarywords) > 0.3:
		f['UPPERCASE'] = 1

	return f,category

2、创建分类器 docclass.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#@Time: 2019-11-10 23:18
#@Author: gaoll

import sqlite3  #使用sqlite操作数据库	
import math
#这里定义分类器
#创建一个类
class classifier():
	"""docstring for classifier"""
	def __init__(self, getfeatures,filename=None):
		#super(classifier, self).__init__()
		self.getfeatures = getfeatures

	def setdb(self,dbfile):
		#链接数据库,创建表fc,cc用于存放特征数据
		self.con = sqlite3.connect(dbfile)
		self.con.execute('create table if not exists fc(feature,category,count)')
		self.con.execute('create table if not exists cc(category,count)')

	def incf(self,f,cat):
		#将[f,cat,num]数据写入 fc 表中
		count = self.fcount(f,cat)
		if count == 0 :
			self.con.execute("insert into fc values(%s,%s,1)"%(f,cat))
		else:
			self.con.execute("update fs set count=%d where feature='%s' and category='%s'"%(count+1,f,cat))

	def fcount(self,f,cat):
		#从表 fc 中读取当前特征[f,cat,num]
		res = self.con.execute("select count from fc where feature='%s' and category='%s'"%(f,cat)).fetchone()
		if res == None:
			return 0
		else:
			return float(res[0])

	def incc(self,cat):
		#将[cat,num] 数据写入cc 表中
		count = self.catcount(cat)
		if count == 0 :
			self.con.execute("insert into cc values('%s',1)"%cat)
		else:
			self.con.execute("update cc set count=%d where cat='%s'"%(count+1,cat))

	def catcount(self,cat):
		#读取当前[cat,num]数据
		res = self.con.execute("select count from cc where category='%s'"%cat).fetchone()
		if res == None:
			return 0
		else:
			return float(res[0])

	def categories(self):
		#全部分类的列表
		cur = self.con.execute('select category form cc').fetcheall()
		category_list = [d[0] for d in cur]
		return category_list

	def totalcount(self):
		#总的样本量
		res = self.con.execute('select sum(count) from cc').fetchone()
		if res == None:
			return 0
		else:
			return res[0]

	def train(self,item):
		features,cat = self.getfeatures(item) #特征提取时返回的是特征列表和分类两个变量
		self.incc(cat)  #[cat,num]数据写入
		for f in features:
			self.incf(f,cat)  #[f,cat,num]数据写入
		self.con.commit() #提交数据库命令操作

	def basic_prob(self,f,cat):
		#类方法,基本概率P(f,cat),朴素贝叶斯和费舍尔分类方法都有用到
		#定义基本概率P(f,cat)= 特征f出现在cat中计数值/分类cat计数值。
		if self.catcount(cat) == 0:
			return 0
		else:
			return self.fcount(f,cat)/self.catcount(cat)

	def weightedprob(self,f,cat,prob_func,weight=1.0,assumedprob=0.5):
		#从初始值assumedprob=0.5开始,取其与新计算的概率值得加权概率值作为概率
		basicprob = prob_func(f,cat) #计算当前的概率值
		totals = 0
		for c in self.categories():
			totals += self.fcount(f,cat)  #计算f特征在所有分类中出现的次数,把它当作新概率的权重系数

		bp = (weight*assumedprob + totals*basicprob) / (weight + totals) #计算权重概率
		return bp


#创建朴素贝叶斯分类器
'''
1、根据贝叶斯公式:P(cat|item) = P(item|cat) * (P(cat) / P(item))
2、其中概率P(item|cat) *= P(f|cat),f为item中提取的特征
3、其中概率P(f|cat)=fc(f,cat)/cc(cat) #定义成了类方法
4、其中概率P(cat)=cc(cat)/total分类数
5、其中P(item)对于本次计算概率都一样,所以忽略此项
6、综上,对于给定的一个item属于分类cat的概率值P(cat|item)
'''

class naivebayes(classifier):
	"""新建一个classifier的子类,命名navibayes,朴素贝叶斯"""
	def __init__(self, getfeatures):
		super().__init__(getfeatures) #获取父类中定义的特征提取方法
		self.thresholds = {} #存储每个分类的阈值

	def setthreshold(self,cat,t):
		self.thresholds[cat] = t

	def getthreshold(self,cat):
		if cat not in self.thresholds:
			return 1.0
		else:
			return self.thresholds[cat]
		
	def naivebayes_prob(self,item,cat):
		#根据公式,计算贝叶斯分类概率
		cat_prob = self.catcount(cat) / self.totalcount() #分类cat的概率
		item_cat_prob = 1
		features = self.getfeatures(item) #提取特征
		for f in features:
			item_cat_prob *= self.weightedprob(f,cat,self.basic_prob) #计算item属于cat分类的概率,也可以用最简单的概率计算公式basic_prob

		bayesprob = item_cat_prob * cat_prob
		return bayesprob

	def classify(self,item,default=None):
		#比较分类概率,及阈值,得出最后的分类
		maxprob = 0.0
		best_cat = None
		for cat in self.categorys():
			bayesprob = self.naivebayes_prob(item,cat)
			if bayesprob > maxprob * self.getthreshold(cat):
				maxprob = bayesprob
				best_cat = cat
		return best_cat


#创建费舍尔分类器
'''
1、根据F检验,计算概率公式P(cat|item)
=invchi2(fscore,df),其中invchi2为倒置对数卡方函数,自由度df = item中特征量*2。
2、其中值fscore= -2 * math.log(P(item|cat))
3、其中概率P(item|cat) *= P(f|cat),f为item中提取的特征
4、其中P(f|cat)=P1(f|cat)/sum(P1(f|cat),cat)
5、其中概率值P1(f|cat)=fc(f,cat)/cc(cat) 
6、综上,对于给定的一个item属于分类cat的概率值P(cat|item)
'''
class fisherclassfier(classifier):
	"""新建一个classifier的子类,命名fisherclassfier,费舍尔分类器"""
	def __init__(self, getfeatures):
	    super().__init__(getfeatures) #获取父类中定义的特征提取方法
	    self.minimuns = {} #存储每个分类的概率下限

	def setminimum(self,cat,min):
		self.minimuns[cat] = min

	def getminmun(self,cat):
		if cat not in self.minimuns:
			return 0
		else:
			return self.minimuns[cat]

	def cprob(self,f,cat):
		#特征在该分类中出现的频率,即基本概率
		clf = self.basic_prob(f,cat)
		if clf == 0:
			return 0
		#特征在所有分类中出现的频率
		freqsum = 0 
		for c in self.categories():
			freqsum += self.basic_prob(f,c)
		#概率等于特征在该分类中出现的频率除以总体频率
		p = clf/freqsum
		return p 

	def invchi2(self,chi,df):
		m = chi/2.0
		sum = term = math.exp(-m)
		for i in range(1,df//2):
			term *= m/i 
			sum +=term
		return min(sum,1.0)

	def fisher_prob(self,item,cat):
		item_cat_prob = 1
		features = self.getfeatures(item)
		for f in features:
			item_cat_prob *= self.weightedprob(f,cat,self.cprob)

		#计算F值
		fscore = -2 * math.log(item_cat_prob)

		#利用倒置对数卡方函数求得概率
		return self.invchi2(fscore,len(features)*2)

	def classify(self,item,default=None):
		#比较各分类概率,及下限,得出最终概率
		maxprob = 0.0
		best_cat = None
		for c in self.categories():
			fisherprob = self.fisher_prob(item,cat)
			if fisher_prob > self.getminmun(c) anf fisher_prob > maxprob:
				maxprob = fisher_prob
				best_cat = c 
		return best_cat

3、训练和预测 train_predict.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#@Time: 2019-11-11 01:18
#@Author: gaoll

def sampletrain(samplefile,cf,database):
	#对样本文件进行训练
	cf.setdb(database) #输入sql使用的database
	f = open(samplefile,'r')
	for line in f:
		cf.train(line) #对样本进行训练

def predict(testfile,cf):
	#对测试文件进行预测
	#cf.setdb('databasename.db') #输入sql使用的database
	f = open(testfile,'r')
	for line in f:
		cat=cf.classify(line) #对样本进行训练
		print('|'.join([line,cat]))

4、运行 run.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#@Time: 2019-11-11 02:53
#@Author: gaoll

import os,sys,datetime
import getfeatures
import docclass
import train_predict
from getfeatures import getfeature1
from getfeatures import getfeature2
from train_predict import sampletrain
from train_predict import predict

if __name__ == '__main__':
	train_file = 'train_sample_file.txt'
	test_file = 'test_sample_file.txt'
	cf = docclass.fisherclassfier(getfeatures.getfeature1) #使用费舍尔方法 + 第一种特征定义
	database = 'gaoll' #数据库名
	train_predict.sampletrain(train_file,cf,database) #开始训练
	train_predict.predict(test_file,cf) #预测

Done

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值