首頁>技術>

這是每個資料科學職業生涯開始時的經典:樸素貝葉斯分類器。

或者更應該說是樸素貝葉斯分類器家族,因為它們有很多種風格例如,有多項式樸素貝葉斯,伯努利樸素貝葉斯,還有高斯樸素貝葉斯分類器,每個分類器在一個小細節上都不同,我們會發現:樸素貝葉斯演算法在設計上非常簡單,但在許多複雜的實際情況下被證明是有用的。

在本文中,你可以學習:

樸素貝葉斯分類器的工作原理,為什麼用他們是有意義的如何使用NumPy在Python中實現它們。

你可以在我的Github上找到程式碼:https://github.com/Garve/TDS/blob/main/TDS%20-%20Gaussian%20Naive%20Bayes.ipynb 。

我們將開始探索樸素貝葉斯分類的理論,然後轉向實現。

理論

分類時我們真正感興趣的是什麼?我們到底在做什麼,輸入和輸出是什麼?答案很簡單:

給定一個數據點x,x屬於某類c的機率是多少?

這就是本文要回答的。你可以直接將此語句建模為條件機率:p(c|x)。

例如,如果有:

3個類:c₁、c₃、c₃x由2個特徵x₁,x₁組成

分類器的結果可能類似於p(c₁|x₁, x₂)=0.3,p(c₂|x₁, x₂)=0.5和p(c₃|x₁, x₂)=0.2。如果我們想要一個標籤作為輸出,我們會選擇一個機率最高的標籤,即這裡機率為50%的c2。

樸素貝葉斯分類器試圖直接計算這些機率。

樸素貝葉斯

給定一個數據點x,我們要計算所有類c的p(c|x),然後以最大機率輸出c。在公式中,你經常把它看作:

注意:max p(c|x)返回最大機率,而argmax p(c|x) 返回具有此最大機率的c。

但在最佳化p(c|x)之前,我們必須先計算它。為此,我們使用貝葉斯定理:

這是樸素貝葉斯的貝葉斯部分。但是現在,我們有以下問題:什麼是 p(x|c)和p(c)?

這就是訓練樸素貝葉斯分類器的意義所在。

訓練

為了說明一切,讓我們使用一個玩具資料集,其中包含兩個真正的特徵x₁,x₁,以及下面的三個類c₁,c₃,c₃。

你可以透過以下程式碼建立:

from sklearn.datasets import make_blobsX, y = make_blobs(n_samples=20, centers=[(0,0), (5,5), (-5, 5)], random_state=0)

讓我們從類機率p(c)開始,即在標記的資料集中觀察到某些類c的機率。估計這一點的最簡單方法就是計算類的頻率,並將它們用作機率。我們可以使用我們的資料集來了解這到底意味著什麼。

在資料集中,20個點中有7個標記為c₁類(藍色),因此我們稱 p(c₁)=7/20。類(紅色)也有7個點,因此我們將p(c₂)=7/20。最後一類c₃(黃色)只有6個點,因此p(c₃)=6/20。

這類機率的簡單計算類似於最大似然法。但是,如果願意,也可以使用另一個先驗分佈。例如,如果你知道此資料集不代表真實總體,因為類c₃應該出現在50%的情況下,那麼你可以設定p(c₁)=0.25,p(c₂)=0.25和p(c₃)=0.5。或者任何有助於提高測試集效能的東西。

現在我們來討論可能性p(x|c)=p(x₁, x₂|c)。計算這種可能性的一種方法是過濾資料集中帶有標籤c的樣本,然後嘗試找到一個能夠捕獲特徵x₁,x₁的分佈(例如二維高斯分佈)。

可惜的是,通常情況下,每個類沒有足夠的樣本來正確估計可能性。

為了能夠建立一個更健壯的模型,我們樸素地假設,給定c,特徵x₁,x2是隨機獨立的。

上面給出的引數對於你可以找到的任何樸素貝葉斯分類器都是相同的。現在它只取決於如何建模 p(x₁|c₁), p(x₂|c₁), p(x₁|c₂), p(x₂|c₂), p(x₁|c₃) 和p(x₂|c₃)。

如果特徵僅為0和1,則可以使用伯努利分佈。如果它們是整數,則為多項式分佈。然而,我們有真實的特徵值,並決定為高斯分佈,因此得名高斯樸素貝葉斯。我們採用以下形式:

其中μᵢ,ⱼ是平均值,σ₂,₁是我們必須根據資料估計的標準差。這意味著我們得到一個平均值,每個特徵i加上一個類cⱼ,在我們的例子中2*3=6平均值。標準差也是如此。這就需要一個例子。

我們試著估算μ₂,₁和σ₂,₁。因為j=1,我們只對c₁類感興趣,所以只保留有這個標籤的樣品。保留以下樣本:

# label = c_1的樣本array([[ 0.14404357,  1.45427351],       [ 0.97873798,  2.2408932 ],       [ 1.86755799, -0.97727788],       [ 1.76405235,  0.40015721],       [ 0.76103773,  0.12167502],       [-0.10321885,  0.4105985 ],       [ 0.95008842, -0.15135721]])

現在,因為i=2,我們只需要考慮第二列。 μ₂,₁是此列的平均值,σ₂,₁是標準差,即μ₂,₁=0.49985176和σ₂,₁=0.9789976。

如果你再從上面看散點圖,這些數字是有意義的。從圖中可以看出,c₁類樣品的特徵 x₂約為0.5。

我們現在為其他五個組合計算這個就完成了!

在Python中,可以這樣做:

from sklearn.datasets import make_blobsimport numpy as np# 建立資料。這些類是c_1=0, c_2=1 and c_3=2.X, y = make_blobs(n_samples=20, centers=[(0,0), (5,5), (-5, 5)], random_state=0)# 類機率.# np.bin統計每個標籤的出現次數.prior = np.bincount(y) / len(y)# np.where(y==i) 返回所有索引,其中y==i.# 這是過濾步驟.means = np.array([X[np.where(y==i)].mean(axis=0) for i in range(3)])stds = np.array([X[np.where(y==i)].std(axis=0) for i in range(3)])

我們得到:

# 先驗array([0.35, 0.35, 0.3 ])# 均值 array([[ 0.90889988,  0.49985176],       [ 5.4111385 ,  4.6491892 ],       [-4.7841679 ,  5.15385848]])       # 標準差array([[0.6853714 , 0.9789976 ],       [1.40218915, 0.67078568],       [0.88192625, 1.12879666]])

這是高斯樸素貝葉斯分類器訓練的結果。

預測

完整的預測公式是:

假設有一個新的資料點x*=(-2,5)。

為了確定它屬於哪個類,我們計算所有類的p(c|x*)。從圖片上看,它應該屬於c₃=2類,但我們暫時忽略分母p(x),使用下面的迴圈計算結果。

x_new = np.array([-2, 5])for j in range(3):    print(f'Probability for class {j}: {(1/np.sqrt(2*np.pi*stds[j]**2)*np.exp(-0.5*((x_new-means[j])/stds[j])**2)).prod()*p[j]:.12f}')

我們收到:

Probability for class 0: 0.000000000263Probability for class 1: 0.000000044359Probability for class 2: 0.000325643718

當然,這些機率(我們不應該這樣稱呼它們)加起來不是一,因為我們忽略了分母。然而這不是問題,因為我們可以把這些非標準化的機率除以它們的和,那麼它們加起來就是一。所以,把這三個值除以它們的和,大約0.00032569,我們得到:

是準確的。現在我們實現它!

你可以忽略所有的檢查函式,或者閱讀我的文章構建你自己的定製scikit-learn。

請注意,我首先實現了一個predict_proba方法來計算機率。predict方法只調用這個方法,並使用argmax函式返回機率最大的索引。

import numpy as npfrom sklearn.base import BaseEstimator, ClassifierMixinfrom sklearn.utils.validation import check_X_y, check_array, check_is_fittedclass GaussianNaiveBayesClassifier(BaseEstimator, ClassifierMixin):    def fit(self, X, y):        X, y = check_X_y(X, y)        self.priors_ = np.bincount(y) / len(y)        self.n_classes_ = np.max(y) + 1                self.means_ = np.array([X[np.where(y==i)].mean(axis=0) for i in range(self.n_classes_)])        self.stds_ = np.array([X[np.where(y==i)].std(axis=0) for i in range(self.n_classes_)])                        return self        def predict_proba(self, X):        check_is_fitted(self)        X = check_array(X)                res = []        for i in range(len(X)):            probas = []            for j in range(self.n_classes_):                            probas.append((1/np.sqrt(2*np.pi*self.stds_[j]**2)*np.exp(-0.5*((X[i]-self.means_[j])/self.stds_[j])**2)).prod()*self.priors_[j])            probas = np.array(probas)            res.append(probas / probas.sum())                            return np.array(res)        def predict(self, X):        check_is_fitted(self)        X = check_array(X)                res = self.predict_proba(X)                return res.argmax(axis=1)
測試實現

程式碼有點長,無法完全確定我們沒有犯任何錯誤。所以,我們比較一下它與scikit-learn GaussianNB分類器的差異。

my_gauss = GaussianNaiveBayesClassifier()my_gauss.fit(X, y)my_gauss.predict_proba([[-2, 5], [0,0], [6, -0.3]])

輸出:

array([[8.06313823e-07, 1.36201957e-04, 9.99862992e-01],       [1.00000000e+00, 4.23258691e-14, 1.92051255e-11],       [4.30879705e-01, 5.69120295e-01, 9.66618838e-27]])

使用預測方法進行的預測如下:

# my_gauss.predict([[-2, 5], [0,0], [6, -0.3]])array([2, 0, 1])

現在,讓我們使用scikit-learn。輸入一些程式碼:

from sklearn.naive_bayes import GaussianNBgnb = GaussianNB()gnb.fit(X, y)gnb.predict_proba([[-2, 5], [0,0], [6, -0.3]])

輸出:

array([[8.06314158e-07, 1.36201959e-04, 9.99862992e-01],       [1.00000000e+00, 4.23259111e-14, 1.92051343e-11],       [4.30879698e-01, 5.69120302e-01, 9.66619630e-27]])

這些數字看起來有點像我們的分類器,但是在最後幾個顯示的數字中它們有點不一致。我們做錯什麼了嗎?沒有。scikit-learn僅僅使用了另一個超引數var_smoothing=1e-09。如果我們把這個設為零,我們就能得到準確的數字。很完美!

看看分類器的決策區域。我標記了用於測試的三個點。這一點接近邊界只有56.9%的機會屬於紅色類,你可以看到predict_proba輸出。另外兩點分類的可信度要高得多。

結論

在這篇文章中,我們學習了高斯樸素貝葉斯分類器是如何工作的,並給出了它為什麼被設計成這樣,它是一種直接的建模機率的方法。將其與Logistic迴歸進行比較:在Logistic迴歸,機率是使用線性函式建模的,並在其上應用sigmoid函式。它仍然是一個簡單的模型,但感覺不像樸素的貝葉斯分類器那麼自然。

我們繼續計算了一些例子,並收集了一些有用的程式碼。最後,我們已經實現了一個完整的高斯樸素貝葉斯分類器的方式,以及與sciket-learn版本。

最後,我們透過匯入sciket-learns自己的Gaussian-naivebayes分類器並測試我們的分類器和sciket-learns的分類器是否產生相同的結果,進行了一個小的健全性檢查。這次測試成功了。

感謝閱讀!

17
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • java面試:leetcode 贖金信(383)