Python · 樸素貝葉斯(一)· 框架

(這裡是本章會用到的 GitHub 地址)

(話說知乎居然沒有樸素貝葉斯的話題……嗚呼 (ノへ ̄、))

========== 寫在前面的話 ==========

對於我個人而言、光看這麼一個框架是非常容易摸不著頭腦的

畢竟之前花了許多時間在數學部分講的那些演算法完全沒有體現在這個框架中、取而代之的是一些我抽象出來的和演算法無關的結構性部分……

雖然從邏輯上來說應該先說明如何搭建這個框架,但從容易理解的角度來說、個人建議先不看這章的內容而是先看後續的實現具體演算法的章節

然後如果那時有不懂的定義、再對照這一章的相關部分來看

不過如果是對樸素貝葉斯演算法非常熟悉的觀眾老爺的話、直接看本章的抽象會引起一些共鳴也說不定 ( σ"ω")σ

========== 分割線的說 ==========

所謂的框架、自然是指三種樸素貝葉斯模型(離散、連續、混合)共性的抽象了。由於貝葉斯決策論就擺在那裡、不難知道如下功能是通用的:

  • 計算類別的先驗概率
  • 訓練出一個能輸出後驗概率的決策函數
  • 利用該決策函數進行預測和評估

雖說樸素貝葉斯大體上來說只是簡單的計數、但是想以比較高的效率做好這件事卻比想像中的要麻煩不少(說實話麻煩到我有些不想講的程度了)(喂)

總之先來看看這個框架的初始化步驟吧(前方……高能?!)

class NaiveBayes(ClassifierBase, metaclass=ClassifierMeta): """ 初始化結構 self._x, self._y:記錄訓練集的變數 self._data:核心數組,存儲實際使用的條件概率的相關信息 self._func:模型核心——決策函數,能夠根據輸入的x、y輸出對應的後驗概率 self._n_possibilities:記錄各個維度特徵取值個數的數組 self._labelled_x:記錄按類別分開後的輸入數據的數組 self._label_zip:記錄類別相關信息的數組,視具體演算法、定義會有所不同 self._cat_counter:核心數組,記錄第i類數據的個數(cat是category的縮寫) self._con_counter:核心數組,用於記錄數據條件概率的原始極大似然估計 self.label_dic:核心字典,用於記錄數值化類別時的轉換關係 self._feat_dics:核心字典,用於記錄數值化各維度特徵(feat)時的轉換關係 """ def __init__(self): self._x = self._y = None self._data = self._func = None self._n_possibilities = None self._labelled_x = self._label_zip = None self._cat_counter = self._con_counter = None self.label_dic = self._feat_dics = None

其中、self._con_counter[d][c][p] =hat p(x^{(d)}=p|y=c)(con是conditional的縮寫)

(注釋比代碼還多是想鬧哪樣???(╯‵□′)╯︵┻━┻)

總之和我一樣懵逼了的觀眾老爺們可以先不太在意這一坨是什麼玩意兒,畢竟這些東西是抽象程度比較高的屬性……等結合具體演算法時、這些屬性的意義可能就會明確得多

下面進入正題……首先來看怎麼計算先驗概率(直接利用上面的 self._cat_counter屬性即可)

def get_prior_probability(self, lb=1): return [(_c_num + lb) / (len(self._y) + lb * len(self._cat_counter)) for _c_num in self._cat_counter]

其中參數 lb 即為平滑項,默認為 1 意味著默認使用拉普拉斯平滑

然後看看訓練步驟能如何進行抽象

def fit(self, x=None, y=None, sample_weight=None, lb=1): if x is not None and y is not None: self.feed_data(x, y, sample_weight) self._func = self._fit(lb)

(豈可修不就只是調用了一下 feed_data 方法而已嘛還說成抽象什麼的行不行啊!)

其中用到的 feed_data 方法是留給各個子類定義的、進行數據預處理的方法;然後 self._fit 可說是核心訓練函數、它會返回我們的決策函數 self._func

最後看看怎樣利用 self._func 來預測未知數據

def predict(self, x, get_raw_result=False): # 調用相應方法進行數據預處理(這在離散型樸素貝葉斯中尤為重要) x = self._transfer_x(x) # 只有將演算法進行向量化之後才能做以下的步驟 m_arg, m_probability = np.zeros(len(x), dtype=np.int8), np.zeros(len(x)) # len(self._cat_counter) 其實就是類別個數 for i in range(len(self._cat_counter)): # 注意這裡的 x 其實是矩陣、p 是對應的「後驗概率矩陣」:p = p(y=i|x) # 這意味著決策函數 self._func 需要支持矩陣運算 p = self._func(x, i) # 利用 Numpy 進行向量化操作 _mask = p > m_probability m_arg[_mask], m_probability[_mask] = i, p[_mask] # 利用轉換字典 self.label_dic 輸出決策 # 參數 get_raw_result 控制該函數是輸出預測的類別還是輸出相應的後驗概率 if not get_raw_result: return np.array([self.label_dic[arg] for arg in m_arg]) return m_probability

其中 self.label_dic 大概是這個德性的:比如訓練集的類別空間為 {red, green, blue} 然後第一個樣本的類別是 red 且第二個樣本的類別是 blue、那麼就有

self.label_dic = np.array(["red", "blue", "green"])

以上就是樸素貝葉斯模型框架的搭建,下一章會在該框架的基礎上實現離散型樸素貝葉斯模型

希望觀眾老爺們能不討厭ヽ(?ω??)?

(猛戳我進入下一章! ( σ"ω")σ )


推薦閱讀:

學會最簡單的資料庫|看完這7招就夠了
Python 高級編程:理解生成器
Python Generator漫談
在 Pycom 使用 Python + Micropython + MQTT 進行物聯網編程
Python 字元編碼的二三事

TAG:Python | 机器学习 | 贝叶斯分类 |