1. 程式人生 > >樸素貝葉斯分類器實現成績等級預測

樸素貝葉斯分類器實現成績等級預測

    最近在學習機器學習相關的演算法,希望能通過筆記和做的小的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,如果有錯誤問題歡迎指正!

     參考資料:《機器學習》 周志華