最近在学习机器学习相关的算法,希望能通过笔记和做的小的Demo来巩固一下所学的知识和算法。
今天来讲解一下朴素贝叶斯分类器,并利用朴素贝叶斯做一个简单的成绩等级预测。
贝叶斯决策论:在所有相关概率都已知的理想情形下,贝叶斯决策论考虑如何基于这些概率和误判损失来选择最优的类别标记。 而朴素贝叶斯分类器采用了“属性条件独立性假设”(attribute conditional independence assumption):对已知类别,假设所有属性相互独立。换言之,假设每个属性独立地对分类结果发生影响。
朴素贝叶斯分类器的表达式
进入正题:
这是我找到的某市高中一次期末考试的成绩单,去掉了个人信息和物理化学生物成绩,只留下语文数学英语和总分。我的目标是根据成绩划分等级,然后我可以通过语数英的等级预测总成绩的等级。
等级划分规则如下:
语文:大于等于120分为A;大于等于105分,小于120分为B;大于等于90分,小于105分为C;小于90分为D。
数学、英语: 大于等于135分为A;大于等于120分,小于135分为B;大于等于105分,小于90分为C;小于90分为D。
总分:大于等于650分为A;大于等于550分,小于650分为B;大于等于550分,小于450分为C;小于450分为D。
(本来打算总分等级失分率和其他科目一样,但后来通过录入数据发现,能拿到A的太少了,全是C,果然高中题目难啊~)
我使用的语言是python,框架是Django,使用Django的主要原因是我编程水平太烂了,用Django可以比较方便简单的操作数据库~
首先将Excel表格里面的数据导入数据库。
新建一个django工程,再新建一个app叫grades,将其加入settings.py中
在grades.models里面新建表
#grades.models
#coding:utf-8
from __future__ import unicode_literals
from django.db import models
class Student(models.Model):
chinese = models.FloatField() #语文成绩
english = models.FloatField() #英语成绩
math = models.FloatField() #数学成绩
total = models.FloatField() #总成绩
通过一下命令同步数据库
python manage.py migrate
python manage.py makemigrations
再在views.py通过函数将表格数据导入数据库
#grades.views
#coding:utf-8
import xlrd
from grades.models import Student
#可以选择导入的行数
def import_data_to_database(x,y):
data = xlrd.open_workbook('1.xls')
table = data.sheets()[0]
start = x
end = y
i = 0
while(start<=end):
Student.objects.create(chinese = table.cell(start-1, 0).value,english = table.cell(start-1, 1).value,
math = table.cell(start-1, 2).value,total=table.cell(start-1,3).value)
start = start +1
return 'success'
将下列方法写入Student类中,方便获取等级。
def get_student_rank(self):
if(self.total>=650):
return 'A'
if(self.total<650 and self.total>=550):
return 'B'
if (self.total < 550 and self.total >= 450):
return 'C'
if (self.total < 450):
return 'D'
def get_student_chinese_rank(self):
if(self.chinese>=120):
return 'A'
if(self.chinese<120 and self.chinese>=105):
return 'B'
if (self.chinese < 105 and self.chinese >= 90):
return 'C'
if (self.chinese < 90):
return 'D'
def get_student_math_rank(self):
if(self.math>=135):
return 'A'
if(self.math<135 and self.math>=120):
return 'B'
if (self.math < 120 and self.math >= 90):
return 'C'
if (self.math < 90):
return 'D'
def get_student_english_rank(self):
if(self.english>=135):
return 'A'
if(self.english<135 and self.english>=120):
return 'B'
if (self.english < 120 and self.english >= 90):
return 'C'
if (self.english < 90):
return 'D'
将下面的函数写入views.py中,获取各个等级的数量
def count_num(subject,rank,total_tank):
count = 0
if(subject=="chinese"):
students = Student.objects.all()
for student in students:
if student.get_student_chinese_rank()==rank and student.get_student_rank()==total_tank:
count = count +1
return count
elif(subject=="math"):
students = Student.objects.all()
for student in students:
if student.get_student_math_rank() == rank and student.get_student_rank()==total_tank:
count = count + 1
return count
elif (subject == "english"):
students = Student.objects.all()
for student in students:
if student.get_student_english_rank() == rank and student.get_student_rank()==total_tank:
count = count + 1
return count
elif (subject == "total"):
students = Student.objects.all()
for student in students:
if student.get_student_rank() == rank:
count = count + 1
return count
最重要的贝叶斯分类器
def Bias_classifier(chi,ma,eng):
chi_rank = get_student_chinese_rank(chi)
ma_rank = get_student_math_rank(ma)
eng_rank = get_student_english_rank(eng)
rank_A = count_num("total","A",None)
rank_B = count_num("total", "B",None)
rank_C = count_num("total", "C",None)
rank_D = count_num("total", "D",None)
chi_rank_A = count_num("chinese",chi_rank,'A')
chi_rank_B = count_num("chinese", chi_rank, 'B')
chi_rank_C = count_num("chinese", chi_rank, 'C')
chi_rank_D = count_num("chinese", chi_rank, 'D')
ma_rank_A = count_num("math", ma_rank,'A')
ma_rank_B = count_num("math", ma_rank, 'B')
ma_rank_C = count_num("math", ma_rank, 'C')
ma_rank_D = count_num("math", ma_rank, 'D')
eng_rank_A = count_num("english", eng_rank,'A')
eng_rank_B = count_num("english", eng_rank, 'B')
eng_rank_C = count_num("english", eng_rank, 'C')
eng_rank_D = count_num("english", eng_rank, 'D')
p_A = (rank_A+1)/float(Student.objects.all().count()+4)
p_B = (rank_B + 1) / float(Student.objects.all().count() + 4)
p_C = (rank_C + 1) / float(Student.objects.all().count() + 4)
p_D = (rank_D + 1) / float(Student.objects.all().count() + 4)
p_chi_A = (chi_rank_A+1)/(float(rank_A)+4)
p_chi_B = (chi_rank_B+1)/(float(rank_B)+4)
p_chi_C = (chi_rank_C+1) / (float(rank_C)+4)
p_chi_D = (chi_rank_D+1) / (float(rank_D)+4)
p_ma_A = (ma_rank_A+1)/(float(rank_A)+4)
p_ma_B = (ma_rank_B+1)/(float(rank_B)+4)
p_ma_C = (ma_rank_C+1) / (float(rank_C)+4)
p_ma_D = (ma_rank_D+1) / (float(rank_D)+4)
p_eng_A = (eng_rank_A+1)/(float(rank_A)+4)
p_eng_B = (eng_rank_B+1) / (float(rank_B)+4)
p_eng_C = (eng_rank_C+1) / (float(rank_C)+4)
p_eng_D = (eng_rank_D+1) / (float(rank_D)+4)
d = {'A':p_A*p_chi_A * p_ma_A * p_eng_A,'B' : p_B*p_chi_B * p_ma_B * p_eng_B,'C' : p_C*p_chi_C * p_ma_C * p_eng_C
,'D' : p_D*p_chi_D * p_ma_D * p_eng_D}
return max(d, key=lambda x: d[x])
(写的好丑~)
大致思路是
如果传入的成绩语数英等级分别是A,A,B,
那么我们要计算的值为
P(总成绩等级为A)*P(语文成绩为A|总成绩为A)*(数学成绩为A|总成绩为A)*(英语成绩为B|总成绩为A)
P(总成绩等级为B)*P(语文成绩为A|总成绩为B)*(数学成绩为A|总成绩为B)*(英语成绩为B|总成绩为B)
P(总成绩等级为C)*P(语文成绩为A|总成绩为C)*(数学成绩为A|总成绩为C)*(英语成绩为B|总成绩为C)
P(总成绩等级为D)*P(语文成绩为A|总成绩为D)*(数学成绩为A|总成绩为D)*(英语成绩为B|总成绩为D)
取值最高的一个对应的等级即为预测结果。
但是按照上面的函数写的话我们根本得不到预测结果,因为很多时候,我们上面计算的值为0,这样整个式子的值都为0,这是极不合理的我们必须做出修正。这个时候就要用到拉普拉斯修正。具体来说,令N表示训练集,D中可能的类别数,Ni表示第i个属性可能的取值数,则修正为
就是我在代码中写的+1与+4
有了这些函数我们就可以做成绩等级预测了,为了得到预测结果的准确性我们写入下列函数
def get_correct_pro(x,y):
correct_num =0
data = xlrd.open_workbook('1.xls')
table = data.sheets()[0]
start = x
end = y
i = 0
while (start <= end):
chi = table.cell(start - 1, 0).value
eng = table.cell(start - 1, 1).value
ma = table.cell(start - 1, 2).value
total = table.cell(start - 1, 3).value
start = start + 1
rank = Bias_classifier(float(chi), float(ma), float(eng))
if(rank==get_student_rank(float(total))):
correct_num=correct_num +1
return correct_num/float(y-x+1)
训练数据我是从前7000个数据中导入了大约5500个数据,在控制台执行函数,得到的准确率极不稳定,一方面是成绩单是按照学校分的,不同学校可能情况不同,另一方面是这个方法不大适用,再者就是我的代码写的有问题。
之前测验的时候发现数据量小一点结果似乎会更好一下,我怀疑是我代码的问题~
只是自己做的一个小Demo,如果有错误问题欢迎指正!
参考资料:《机器学习》 周志华
代码上传到github上了:https://github.com/caozixuan/mlgrades
里面除了朴素贝叶斯之外,还有梯度下降预测的内容,可能还有一些我很久之前想做但没有做完的东西。