機器學習算法——實戰樸素貝葉斯分類器(附代碼)

樸素貝葉斯分類器原理

(1)X=(x1,x2,,xD)X=\left( x_{1},x_{2},\cdots ,x_{D}\right) 表示含有 DD 維屬性的數據對象。訓練集 SS 含有 kk 個類別,表示爲 Y=(y1,y2,,yk)Y=\left( y_{1},y_{2},\cdots ,y_{k}\right)
(2)已知待分類數據對象 xx 預測 xx 所屬類別,計算方式如下:
yk=argmaxyky(P(ykx))y_{k}=\arg \max_{y_{k}\in y} \left( P\left( y_{k}\mid x\right) \right)

所得 yky_{k} 即爲 xx 所屬類別。上式表示,已知待分類數據對象 xx 的情況下,分別計算 xx 屬於 y1y_{1}y2y_{2}、…、yky_{k} 的概率,選取其中概率的最大值,此時所對應的 yky_{k},即爲 xx 所屬類別。

(3)根據貝葉斯定理,P(ykx)P\left( y_{k}\mid x\right) 計算方式如下:
P(ykx)=P(xyk)×P(yk)P(x) P\left( y_{k}\mid x\right) =\frac{P\left( x\mid y_{k}\right) \times P\left( y_{k}\right) }{P\left( x\right) }
計算過程中,P(x)P(x) 對於 P(ykx)P\left( y_{k}\mid x\right) ,相當於常數。因此,若想得到 P(ykx)P\left( y_{k}\mid x\right) 最大值,只需計算 P(xyk)×P(yk)P\left( x\mid y_{k}\right) \times P\left( y_{k}\right) 最大值。如果類別的先驗概率未知,即 P(yk)P\left( y_{k}\right) 未知,則通常假定這些類別是等概率的,即 P(y1)=P(y2)=...=P(yk)P\left( y_{1}\right)=P\left( y_{2}\right)=...=P\left( y_{k}\right)

(4)假設數據對象的各屬性之間相互獨立P(xyk)P\left( x\mid y_{k}\right)計算方式如下:
P(xyk)=d=1DP(xdyk) P\left( x\mid y_{k}\right) =\prod^{D}_{d=1} P\left( x_{d}\mid y_{k}\right)

(5)其中,P(xdyk)P\left( x_{d}\mid y_{k}\right) 的計算方式如下:

  • 如果屬性 dd 是離散屬性或分類屬性。訓練集中屬於類別 ydy_{d} 的數據對象在屬性 dd 下的相異屬性值共有 nn 個;訓練集中屬於類別 yky_{k},且在屬性 dd 下的屬性值爲 xdx_{d} 的數據對象共有 mm 個。則 P(xdyk)P\left( x_{d}\mid y_{k}\right) 計算方式如下:
    P(xdyk)=mn P\left( x_{d}\mid y_{k}\right) =\frac{m}{n}
  • 如果屬性 dd 是連續屬性。通常假設連續屬性均服從均值爲μ\mu、標準差爲 σ\sigma 的高斯分佈, 即
    G(xd,μ,σ)=12πσe(xμ)22σ2 G\left( x_{d},\mu ,\sigma \right) =\frac{1}{\sqrt{2\pi } \sigma } e^{-\frac{\left( x-\mu \right)^{2} }{2\sigma^{2} } }
    於是 P(xdyk)=G(xd,μ,σ)P\left( x_{d}\mid y_{k}\right)=G\left( x_{d},\mu ,\sigma \right)
    其中,μ\muσ\sigma 表示訓練集中屬於類別 yky_{k} 的數據對象在屬性 dd 下的均值和標準差。

分類器實現

導入模塊

import numpy as np
import pandas as pd
from random import seed
from random import randrange
from math import sqrt
from math import exp
from math import pi
import plotly.express as px
from plotly.offline import init_notebook_mode
init_notebook_mode(connected=True)  

方法定義

def str_column_to_int(dataset, column):
    """
    將類別轉化爲int型
    @dataset: 數據
    @column: 需要轉化的列
    """
    class_values = [row[column] for row in dataset]
    unique = set(class_values)
    lookup = dict()
    for i, value in enumerate(unique):
        lookup[value] = i
    for row in dataset:
        row[column] = lookup[row[column]]
    print(lookup)
    return lookup

def cross_validation_split(dataset, n_folds):
    """
    使用交叉檢驗方法驗證算法
    @dataset: 數據
    @n_folds: 想要劃分的折數
    """
    dataset_split = list()
    dataset_copy = list(dataset)
    fold_size = int(len(dataset) / n_folds)   # 一個fold的大小
    for _ in range(n_folds):
        fold = list()
        while len(fold) < fold_size:
            index = randrange(len(dataset_copy))
            fold.append(dataset_copy.pop(index))            
        dataset_split.append(fold)       
    return dataset_split

def accuracy_metric(actual, predicted):
    """
    計算準確率
    @actual: 真實值
    @predicted: 預測值
    """
    correct = 0
    for i in range(len(actual)):
        if actual[i] == predicted[i]:
            correct += 1
    return correct / float(len(actual)) * 100.0

def evaluate_algorithm(dataset, algorithm, n_folds, *args):
    """
    評估使用的分類算法(基於交叉檢驗)
    @dataset: 數據
    @algorithm: 使用的算法
    @n_folds: 選擇要劃分的折數
    @*args: 根據使用的分類算法而定,在樸素貝葉斯里面不需要其他的參數
    """
    folds = cross_validation_split(dataset, n_folds)
    scores = list()
    for i in range(len(folds)):  
        train_set = np.delete(folds, i, axis=0)
#         print(train_set)        
        test_set = list()
        for row in folds[i]:
            row_copy = list(row)
            test_set.append(row_copy)
            row_copy[-1] = None
        predicted = algorithm(train_set, test_set, *args)
        actual = [row[-1] for row in folds[i]]
        accuracy = accuracy_metric(actual, predicted)
        scores.append(accuracy)
    return scores

def separate_by_class(dataset):
    """
    將數據集根據類別進行分類
    @dataset: 數據
    """
    separated = dict()
    dataset = dataset.reshape((-1,5))
    for i in range(len(dataset)):
        vector = dataset[i]
        class_value = vector[-1]       
        if (class_value not in separated):
            separated[class_value] = list()
        separated[class_value].append(vector)
    return separated

def summarize_dataset(dataset):
    """
    計算每一個特徵的統計性指標,這是爲了後續計算條件概率
    @dataset: 數據
    """
    summaries = [(np.mean(column), np.std(column), len(column)) for column in zip(*dataset)]
    del(summaries[-1])
    return summaries

def summarize_by_class(dataset):
    """
    將數據集根據類別分割,然後分別計算統計量
    @dataset: 數據
    """
    separated = separate_by_class(dataset)
    summaries = dict()
    for class_value, rows in separated.items():
        summaries[class_value] = summarize_dataset(rows)
    return summaries

def calculate_probability(x, mean, stdev):
    """
    根據統計量計算某一個特徵的正態分佈概率分佈函數
    @x: 特徵數據
    @mean: 均值
    @stdev: 標準差
    """
    exponent = exp(-((x-mean)**2 / (2 * stdev**2 )))
    return (1 / (sqrt(2 * pi) * stdev)) * exponent

def calculate_class_probabilities(summaries, row):
    """
    根據後驗概率來計算先驗概率
    @summaries: 統計量
    @row: 一行數據
    """
    total_rows = sum([summaries[label][0][2] for label in summaries])
    probabilities = dict()
    for class_value, class_summaries in summaries.items():
        probabilities[class_value] = summaries[class_value][0][2]/float(total_rows)
        for i in range(len(class_summaries)):
            mean, stdev, _ = class_summaries[i]
            probabilities[class_value] *= calculate_probability(row[i], mean, stdev)
    return probabilities

def predict(summaries, row):
    """
    預測
    @summaries: 統計量
    @row: 一行數據
    """
    probabilities = calculate_class_probabilities(summaries, row)
    best_label, best_prob = None, -1
    for class_value, probability in probabilities.items():
        if best_label is None or probability > best_prob:
            best_prob = probability
            best_label = class_value
    return best_label

def naive_bayes(train, test):
    """
    樸素貝葉斯分類器
    @train: 訓練集
    @test: 測試集
    """
    summarize = summarize_by_class(train)
    predictions = list()
    for row in test:
        output = predict(summarize, row)
        predictions.append(output)
    return(predictions)

使用鳶尾花數據集檢驗

以上便實現了樸素貝葉斯分類器,接下來我們使用鳶尾花數據集進行校驗。

seed(1)
filename = 'iris.csv'
dataset = pd.read_csv(filename).values
str_column_to_int(dataset, len(dataset[0])-1)
n_folds = 10
scores = evaluate_algorithm(dataset, naive_bayes, n_folds)
print('某個折上的準確率: %s' % scores)
print('算法的平均準確率: %.3f%%' % (sum(scores)/float(len(scores))))   

結果爲:

{'Iris-versicolor': 0, 'Iris-setosa': 1, 'Iris-virginica': 2}
某個折上的準確率: [86.66666666666667, 100.0, 93.33333333333333, 100.0, 100.0, 100.0, 100.0, 86.66666666666667, 86.66666666666667, 100.0]
算法的平均準確率: 95.333%

在不同的fold數下的結果:

scores, index, acc = [], [], []
for i in range(2, 22):
    score = evaluate_algorithm(dataset, naive_bayes, i)
    scores.append(list(score))
    acc.append(sum(score)/float(len(score)))
    index.append([i for j in range(i)])
    
fig = go.Figure(layout_title_text="Cross validation in different K")
fig.add_trace(go.Scatter(x=[i + 2 for i in range(20)], y=acc,
                    mode='lines+markers',
                    name='mean'))
fig.add_trace(go.Scatter(x=sum(index, []), y=sum(scores, []),
                    mode='markers',
                    name='each'))
fig.update_layout(template='none',
                  xaxis_title="K-folds",
                  yaxis_title="Accuracy",)
fig.show()

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章