
本文主要探讨了機器學習算法中一些比較容易了解的分類算法,包括二次判别分析QDA,線性判别分析LDA,樸素貝葉斯Naive Bayes,以及邏輯回歸Logistic Regression,還會給出在irsi資料集上相應的手寫python代碼以及在sklearn上運用的執行個體。在本文的讨論中,我們強調了簡單分類模型,是為了差別于樹模型以及支援向量基模型,這兩類模型将單獨在以後的文章中跟大家分享,而本文也是深度學習的理論引入,後面的文章中将給大家從兩個分支繼續讨論機器學習,包括我們的深度學習和樹模型等。
(本文章部分圖檔引用李宏毅老師的機器學習課程,侵權即删)1. 如何描述一個分類問題(機率角度)
我們先來看看iris資料集:
我們的任務是:給定一個樣本,這個樣本包含花萼長度,花萼寬度,花瓣長度,花瓣寬度的值,我們需要預測這個樣本是屬于哪個品種的鸢尾花。
2.二次判别分析QDA
對于以上的iris分類問題,我們按照以上步驟解得:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
%matplotlib inline
class QDA():
def __init__(self):
self.data = data
self.target = target
def find_mean(self):
target_unique = np.unique(self.target)
self.means = []
for i in target_unique:
self.means.append(np.sum(self.data[self.target==i,],axis=0)/np.sum(self.target==i))
print("u為:n",self.means)
return self.means
def find_sigma(self):
target_unique = np.unique(self.target)
self.sigmas = []
for k,i in enumerate(target_unique):
self.sigmas.append(((self.data[self.target==i,]-self.means[k]).T.dot((self.data[self.target==i,]-self.means[k])))/np.sum(self.target==i))
print("sigma為:n",self.sigmas)
return self.sigmas
def find_f(self):
self.f_i = []
self.f_ii = []
for i in range(len(np.unique(target))):
for j in range(len(target)):
self.f_ii.append(self.p_ci[i]*1/(((2*np.pi)**self.data.shape[1])*(np.linalg.det(self.sigmas[i])**0.5))*np.exp(-0.5*(self.data[j,:]-self.means[i]).dot(np.linalg.inv(self.sigmas[i])).dot((self.data[j,:]-self.means[i]).T)))
self.f_i.append(np.array(self.f_ii))
self.f_ii = []
return(self.f_i)
def p_c(self):
self.p_ci = []
for i in np.unique(self.target):
self.p_ci.append(sum(self.target==i)/len(self.target))
return self.p_ci
def arg_max(self):
self.final_p = np.array(self.f_i)
self.max_p = np.argmax(self.final_p,axis=0)
return self.max_p
def score(self):
self.classification_score = sum(target==self.max_p)/len(target)
print("預測分類準确率為:{} %".format(self.classification_score*100))
return self.classification_score
def start(self):
mean = self.find_mean()
sigma = self.find_sigma()
p_c1 = self.p_c()
f = self.find_f()
predict = self.arg_max()
c_score = self.score()
if __name__=='__main__':
iris = load_iris()
data = iris.data
target = iris.target
features = iris.feature_names
qda = QDA()
qda.start()
預測準确率98%,還是不錯的。接着我們使用sklearn驗證我們寫的對不對:
# 我們使⽤sklearn驗證我們寫的對不對
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA
clf = QDA()
clf.fit(data,target)
clf.score(data,target)
由于我們的判别邊界P=0.5是⼀個⼆次函數,是以叫⼆次判别分析。
3.線性判别分析LDA
是以我們寫成python程式:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
%matplotlib inline
class LDA():
def __init__(self):
self.data = data
self.target = target
def find_mean(self):
target_unique = np.unique(self.target)
self.means = []
for i in target_unique:
self.means.append(np.sum(self.data[self.target==i,],axis=0)/np.sum(self.target==i))
return self.means
def find_sigma(self):
target_unique = np.unique(self.target)
self.sigmas = []
for k,i in enumerate(target_unique):
self.sigmas.append(((self.data[self.target==i,]-self.means[k]).T.dot((self.data[self.target==i,]-self.means[k])))/np.sum(self.target==i))
self.sigma_k = []
for i in range(len(np.unique(self.target))):
self.sigma_k.append(sum(target==i)/len(target))
self.sigma_com = []
for i in range(len(np.unique(self.target))):
self.sigma_com.append(self.sigma_k[i]*self.sigmas[i])
self.sigmas1 = []
for i in range(len(np.unique(self.target))):
self.sigmas1.append(sum(self.sigma_com))
self.sigmas = self.sigmas1
print("我們的共同的sigma為:n{}".format(self.sigmas[0]))
return self.sigmas
def find_f(self):
self.f_i = []
self.f_ii = []
for i in range(len(np.unique(target))):
for j in range(len(target)):
self.f_ii.append(self.p_ci[i]*1/(((2*np.pi)**self.data.shape[1])*(np.linalg.det(self.sigmas[i])**0.5))*np.exp(-0.5*(self.data[j,:]-self.means[i]).dot(np.linalg.inv(self.sigmas[i])).dot((self.data[j,:]-self.means[i]).T)))
self.f_i.append(np.array(self.f_ii))
self.f_ii = []
return(self.f_i)
def p_c(self):
self.p_ci = []
for i in np.unique(self.target):
self.p_ci.append(sum(self.target==i)/len(self.target))
return self.p_ci
def arg_max(self):
self.final_p = np.array(self.f_i)
self.max_p = np.argmax(self.final_p,axis=0)
return self.max_p
def score(self):
self.classification_score = sum(target==self.max_p)/len(target)
print("預測分類準确率為:{} %".format(self.classification_score*100))
return self.classification_score
def start(self):
mean = self.find_mean()
sigma = self.find_sigma()
p_c1 = self.p_c()
f = self.find_f()
predict = self.arg_max()
c_score = self.score()
if __name__=='__main__':
iris = load_iris()
data = iris.data
target = iris.target
features = iris.feature_names
lda = LDA()
lda.start()
我們使⽤sklearn驗證:
# 我們使用sklearn驗證我們寫的對不對
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
clf = LDA()
clf.fit(data,target)
clf.score(data,target)
由于我們的LDA判别邊界是線性的,是以我們叫做線性判别分析。
4. 樸素⻉葉斯Naive Bayes
是以,我們寫成python代碼:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
%matplotlib inline
class Naive():
def __init__(self):
self.data = data
self.target = target
def find_mean(self):
target_unique = np.unique(self.target)
self.means = []
for i in target_unique:
self.means.append(np.sum(self.data[self.target==i,],axis=0)/np.sum(self.target==i))
return self.means
def find_sigma(self):
target_unique = np.unique(self.target)
self.sigmas = []
for k,i in enumerate(target_unique):
self.sigmas.append(((self.data[self.target==i,]-self.means[k]).T.dot((self.data[self.target==i,]-self.means[k])))/np.sum(self.target==i))
self.sigma_k = []
for i in range(len(np.unique(self.target))):
self.sigma_k.append(sum(target==i)/len(target))
self.sigma_com = []
for i in range(len(np.unique(self.target))):
self.sigma_com.append(self.sigma_k[i]*self.sigmas[i])
self.sigmas1 = []
for i in range(len(np.unique(self.target))):
s = sum(self.sigma_com)
s = np.diagonal(s)
s = np.identity(self.data.shape[1])*s
self.sigmas1.append(s)
self.sigmas = self.sigmas1
print("我們的共同的sigma為:n{}".format(self.sigmas[0]))
return self.sigmas
def find_f(self):
self.f_i = []
self.f_ii = []
for i in range(len(np.unique(target))):
for j in range(len(target)):
self.f_ii.append(self.p_ci[i]*1/(((2*np.pi)**self.data.shape[1])*(np.linalg.det(self.sigmas[i])**0.5))*np.exp(-0.5*(self.data[j,:]-self.means[i]).dot(np.linalg.inv(self.sigmas[i])).dot((self.data[j,:]-self.means[i]).T)))
self.f_i.append(np.array(self.f_ii))
self.f_ii = []
return(self.f_i)
def p_c(self):
self.p_ci = []
for i in np.unique(self.target):
self.p_ci.append(sum(self.target==i)/len(self.target))
return self.p_ci
def arg_max(self):
self.final_p = np.array(self.f_i)
self.max_p = np.argmax(self.final_p,axis=0)
return self.max_p
def score(self):
self.classification_score = sum(target==self.max_p)/len(target)
print("預測分類準确率為:{} %".format(self.classification_score*100))
return self.classification_score
def start(self):
mean = self.find_mean()
sigma = self.find_sigma()
p_c1 = self.p_c()
f = self.find_f()
predict = self.arg_max()
c_score = self.score()
if __name__=='__main__':
iris = load_iris()
data = iris.data
target = iris.target
features = iris.feature_names
naive = Naive()
naive.start()
5. 邏輯回歸 Logistic Regression
我們⾸先對LDA的式⼦做⼀個變形轉化:(不喜歡看推導的可以直接跳過,直接看結論)
從上⾯的結論中,我們很驚訝的發現了我們之前千⾟萬苦需要找的正态分布其實就是⼀個線性函數複合⼀個sigmoid函數,在LDA的估計中,我們怎麼找出w和b呢?emmmm那還是找到樣本估計均值和⽅差等。
我們這⾥有個想法,那就是能不能通過樣本直接像線性回歸⼀樣直接使⽤梯度下降法求出w和b呢?答案是肯定的,但是求出來的w跟b⼀般跟LDA求出來的不太⼀樣。
我們和之前步驟⼀樣,先寫出似然函數:
寫出來後我們得到⼀個發現,這個表達式不就是交叉熵嗎?(上下圖對⽐)
接下來求梯度:
接着疊代就算完成啦!(梯度下降的代碼在之前的⽂章最優化當中有,想看的可以去找找我寫的那篇⽂章)
那我們反思⼀下剛剛的步驟,我們對付分類問題上來就⽤了極⼤似然估計,誤打誤撞推出了交叉熵cross-entropy,那我們為什麼不使⽤最⼩⼆乘法⾥⾯的平⽅和作為我們的損失函數呢?
那我們使⽤最⼩⼆乘法試試解決邏輯回歸:
我們可以發現,如果真實值是0,我們預測為1,那梯度接近0;同理,我們預測為0,梯度也接近與0,我們的梯度仿佛消失了。
為了⼀探究竟,我們畫個圖表⽰⼀下:
紅⾊的是平⽅損失,⿊⾊的是交叉熵,很明顯差距就出來了,紅色的很靠近0,且很平緩。
最後,我們回到LDA與Logistic Regression的⽐較中,我們發現:(左邊是Logistic Regression,右邊是LDA),他們的分界線不⼀樣,也就是他們通過不同⽅式得出來的w和b是不⼀樣的。
⼀般來說,在⼩樣本⾥⾯LDA會顯得更加優秀,⼤樣本中Logistic Regression會先更更加魯棒。
6. 總結
在本次分享中,我們已經把簡單分類模型的前世今⽣都說清楚了,以及他們之間難以割舍的聯系也講明⽩了,我們還知道了怎麼⾃⼰編寫代碼實作這些模型。那在下一次我們将從兩個不同的發展方向給大家講清楚機器學習的複雜的分類和回歸問題是怎麼做的,包括我們的深度學習和樹模型支援向量機等。