![71078b010d50aeefcc4237485e6ff10e.png](https://i-blog.csdnimg.cn/blog_migrate/5fb22063e906c2a84df8a888ef7d7a3a.png)
本文主要探讨了机器学习算法中一些比较容易理解的分类算法,包括二次判别分析QDA,线性判别分析LDA,朴素贝叶斯Naive Bayes,以及逻辑回归Logistic Regression,还会给出在irsi数据集上相应的手写python代码以及在sklearn上运用的实例。在本文的讨论中,我们强调了简单分类模型,是为了区别于树模型以及支持向量基模型,这两类模型将单独在以后的文章中跟大家分享,而本文也是深度学习的理论引入,后面的文章中将给大家从两个分支继续讨论机器学习,包括我们的深度学习和树模型等。(本文章部分图片引用李宏毅老师的机器学习课程,侵权即删)
1. 如何描述一个分类问题(概率角度)
我们先来看看iris数据集:
![504078d95cea2bde879fb64a90cf87db.png](https://i-blog.csdnimg.cn/blog_migrate/d655056fa4790c654e49209e36d6aaf7.png)
我们的任务是:给定一个样本,这个样本包含花萼长度,花萼宽度,花瓣长度,花瓣宽度的值,我们需要预测这个样本是属于哪个品种的鸢尾花。
![f1daa645f1355657407b7254e7062c08.png](https://i-blog.csdnimg.cn/blog_migrate/a7ddb5dec669ef8bac56bf7c17b00c07.png)
2.二次判别分析QDA
![d0f39702d0c6f4c61fc0d69ce4e5bea3.png](https://i-blog.csdnimg.cn/blog_migrate/e9b0d693a3fcb6f43b411ffdce76b37b.jpeg)
![18c14f35ff38672dee144ccd9ca6899f.png](https://i-blog.csdnimg.cn/blog_migrate/b1976b94aacb0ae433d879f472cad0f8.jpeg)
对于以上的iris分类问题,我们按照以上步骤解得:
![b72bd7e9c9e29960c4bf47768020e5ef.png](https://i-blog.csdnimg.cn/blog_migrate/6d3761deeb427ea301931ee4c24e5632.jpeg)
![b550d0180e2a3211e133afd724d13612.png](https://i-blog.csdnimg.cn/blog_migrate/24e7de97844333a6c0cd00f908bcb324.png)
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()
![fde19aeb96481d3e8961308fd9de8f56.png](https://i-blog.csdnimg.cn/blog_migrate/cc5f1552c5d1183784335c7a4c120912.jpeg)
预测准确率98%,还是不错的。接着我们使用sklearn验证我们写的对不对:
# 我们使⽤sklearn验证我们写的对不对
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis as QDA
clf = QDA()
clf.fit(data,target)
clf.score(data,target)
![9240e318312e45966a061736bb628613.png](https://i-blog.csdnimg.cn/blog_migrate/e7a950c562a8ad5e804e4dcec474340d.png)
由于我们的判别边界P=0.5是⼀个⼆次函数,所以叫⼆次判别分析。
![8f751f7ed21835fdc4108069a2d9c761.png](https://i-blog.csdnimg.cn/blog_migrate/8007cf0647c5882ff7279ee618070fc7.jpeg)
3.线性判别分析LDA
![b1522f25d33091530b66fc2a75aba7d9.png](https://i-blog.csdnimg.cn/blog_migrate/de307d05029446ee911ba88ba6f9e20d.jpeg)
![ce8b8ffe0ab4ecf518b717d11f56f023.png](https://i-blog.csdnimg.cn/blog_migrate/0f956a4bb036482837910c6458ac8450.jpeg)
因此我们写成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()
![051e4ab002096873f7343c715e1ad6bf.png](https://i-blog.csdnimg.cn/blog_migrate/83bc9334ecac71b7da8d92e548cf852f.png)
我们使⽤sklearn验证:
# 我们使用sklearn验证我们写的对不对
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
clf = LDA()
clf.fit(data,target)
clf.score(data,target)
![883a02620a34e207cc5bb91af784743f.png](https://i-blog.csdnimg.cn/blog_migrate/32beee9d1df158065fc1837c79b5f888.png)
由于我们的LDA判别边界是线性的,所以我们叫做线性判别分析。
![f24aa1f7092eedc8879daed45dd2c7a6.png](https://i-blog.csdnimg.cn/blog_migrate/cdc136b7a55d0d32336b015b4bb4cf8d.jpeg)
4. 朴素⻉叶斯Naive Bayes
![736a7e328ba7fe93ffcc8b7ab6fd4fb1.png](https://i-blog.csdnimg.cn/blog_migrate/7f6f6f231069aab9d185221bcaf02f9c.jpeg)
![7db4e2057b75f98a629e527367e526fa.png](https://i-blog.csdnimg.cn/blog_migrate/a1f426a4b90caf719fb7bef9c20c57fd.jpeg)
因此,我们写成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()
![da625779347d8c812e01593ad7509f96.png](https://i-blog.csdnimg.cn/blog_migrate/25f11414ac2052c6253d11ca4183855a.png)
5. 逻辑回归 Logistic Regression
我们⾸先对LDA的式⼦做⼀个变形转化:(不喜欢看推导的可以直接跳过,直接看结论)
![50abbab204ead17fd18cd80ef7489fe5.png](https://i-blog.csdnimg.cn/blog_migrate/650f2d8d88233d40f9733f9266a99ac8.jpeg)
![9f4834412b4ac3df8aff5b22bf6aeb52.png](https://i-blog.csdnimg.cn/blog_migrate/df20410e75b4258f24478bab84da97d0.jpeg)
![c298e3284f067e2bc0a3f41264167950.png](https://i-blog.csdnimg.cn/blog_migrate/73f2611b4cd72da83db8e7472d042d9e.jpeg)
![85844702dc2673bcd725e51353c928f1.png](https://i-blog.csdnimg.cn/blog_migrate/665c9fb82e03581024d22a796948e435.jpeg)
从上⾯的结论中,我们很惊讶的发现了我们之前千⾟万苦需要找的正态分布其实就是⼀个线性函数复合⼀个sigmoid函数,在LDA的估计中,我们怎么找出w和b呢?emmmm那还是找到样本估计均值和⽅差等。
我们这⾥有个想法,那就是能不能通过样本直接像线性回归⼀样直接使⽤梯度下降法求出w和b呢?答案是肯定的,但是求出来的w跟b⼀般跟LDA求出来的不太⼀样。
我们和之前步骤⼀样,先写出似然函数:
![3659287837110ce8a84139bbbb7ee050.png](https://i-blog.csdnimg.cn/blog_migrate/9031a70d91860643f3f388c6ee0d082f.jpeg)
写出来后我们得到⼀个发现,这个表达式不就是交叉熵吗?(上下图对⽐)
![e5e4fdb8a0e7fc770c45bb3246bacf3f.png](https://i-blog.csdnimg.cn/blog_migrate/2a862f2baf5ac855c39b1edc72195b4c.jpeg)
接下来求梯度:
![661040ec269f0f9d48c0fd951b6c2207.png](https://i-blog.csdnimg.cn/blog_migrate/6bb47884ecb05762f49e8f4bc6377359.jpeg)
接着迭代就算完成啦!(梯度下降的代码在之前的⽂章最优化当中有,想看的可以去找找我写的那篇⽂章)
那我们反思⼀下刚刚的步骤,我们对付分类问题上来就⽤了极⼤似然估计,误打误撞推出了交叉熵cross-entropy,那我们为什么不使⽤最⼩⼆乘法⾥⾯的平⽅和作为我们的损失函数呢?
那我们使⽤最⼩⼆乘法试试解决逻辑回归:
![edbac268c4adabbcbf4840913e6a5ad5.png](https://i-blog.csdnimg.cn/blog_migrate/22144cab5087a2dbea05605b5987cb53.jpeg)
我们可以发现,如果真实值是0,我们预测为1,那梯度接近0;同理,我们预测为0,梯度也接近与0,我们的梯度仿佛消失了。
![dbae4784727920da105efec3e2276e2d.png](https://i-blog.csdnimg.cn/blog_migrate/1eaa39d0e79a144fc48b4ff10110fb76.png)
为了⼀探究竟,我们画个图表⽰⼀下:
红⾊的是平⽅损失,⿊⾊的是交叉熵,很明显差距就出来了,红色的很靠近0,且很平缓。
![50d0521c003a974c985659200cf72301.png](https://i-blog.csdnimg.cn/blog_migrate/a864f87875f1af60bd3834320e5a90c9.jpeg)
最后,我们回到LDA与Logistic Regression的⽐较中,我们发现:(左边是Logistic Regression,右边是LDA),他们的分界线不⼀样,也就是他们通过不同⽅式得出来的w和b是不⼀样的。
![65bfd87cd3179be6127ef10ebd2da63d.png](https://i-blog.csdnimg.cn/blog_migrate/882686e128c7ad85911e4adf8a0cfc96.jpeg)
⼀般来说,在⼩样本⾥⾯LDA会显得更加优秀,⼤样本中Logistic Regression会先更更加鲁棒。
6. 总结
在本次分享中,我们已经把简单分类模型的前世今⽣都说清楚了,以及他们之间难以割舍的联系也讲明⽩了,我们还知道了怎么⾃⼰编写代码实现这些模型。那在下一次我们将从两个不同的发展方向给大家讲清楚机器学习的复杂的分类和回归问题是怎么做的,包括我们的深度学习和树模型支持向量机等。