本节为吴恩达教授机器学习课程第四部分,生成学习算法(1),包括:多元正态分布,高斯判别分析模型GDA以及及GDA与逻辑回归的关系,并附上高斯判别分析的python实现代码
之前关于该部分的学习算法尝试对给定x条件下y的分布建模,即对
p
(
y
∣
x
;
θ
)
p(y|x;\theta)
p(y∣x;θ)建模。比如逻辑回归对
p
(
y
∣
x
;
θ
)
p(y|x;\theta)
p(y∣x;θ)建模,且
h
θ
(
x
)
=
g
(
θ
T
x
)
h_{\theta}(x)=g(\theta^Tx)
hθ(x)=g(θTx),其中g是sigmoid函数。
考虑一个二分类问题,判断一个动物是象(y=1)还是狗(y=0)。给定训练集,逻辑回归或者基础的感知机算法都是尝试找到一个决策边界来分离象和狗。当输入测试样本时,算法会看该样本位于决策边界的哪边,以此进行预测。
本节介绍的生成学习算法不同于此,此算法首先建立一个大象的模型,再建立一个狗的模型。当输入测试样本时,我们将该样本分别与两个模型进行比对,更像那个模型就输出哪一类。
前者如逻辑回归等算法,尝试对
p
(
y
∣
x
)
p(y|x)
p(y∣x)建模,称为判别学习算法。后者对
p
(
x
∣
y
)
p(x|y)
p(x∣y)和
p
(
y
)
p(y)
p(y)建模,称为生成学习算法,即
p
(
x
∣
y
=
0
)
p(x|y=0)
p(x∣y=0)对狗的特征进行建模,
p
(
x
∣
y
=
1
)
p(x|y=1)
p(x∣y=1)对象的特征进行建模。
完成对
p
(
x
∣
y
)
p(x|y)
p(x∣y)和类别先验
p
(
y
)
p(y)
p(y)建模后,算法通过贝叶斯法则得到给定x条件下y的后验分布:
其中分母
p
(
x
)
=
p
(
x
∣
y
=
1
)
p
(
y
=
1
)
+
p
(
x
∣
y
=
0
)
p
(
y
=
0
)
p(x)=p(x|y=1)p(y=1)+p(x|y=0)p(y=0)
p(x)=p(x∣y=1)p(y=1)+p(x∣y=0)p(y=0),但实际上我们并不需要计算这个分母因为:
1. 高斯判别分析GDA
第一个生成学习算法即GDA,这个模型中,我们假设 p ( x ∣ y ) p(x|y) p(x∣y)服从多元正态分布,在学习GDA之前,先学习一下多元正态分布的基本知识。
1.1 多元正态分布
n
n
n维多元正态分布又称多元高斯分布,参数维均值向量
μ
∈
R
n
\mu \in R^n
μ∈Rn和协方差矩阵
Σ
∈
R
n
×
n
\Sigma \in R^{n \times n}
Σ∈Rn×n,并且
Σ
\Sigma
Σ时非负对称半正定矩阵,多元高斯分布的概率密度可以写作:
其中
∣
Σ
∣
|\Sigma|
∣Σ∣表示矩阵的行列式。
正定矩阵和半正定矩阵
对于服从多元正态分布的随机变量来说,有:
又因为Cov(Z)可以如下计算
即:
所以有:
下面给出高斯分布的一些图像以更好理解:
上图中均值为 2x1维零向量,协方差矩阵从左到右依次为
I
、
0.6
I
、
2
I
(
2
×
2
)
I、0.6I、2I(2 \times 2)
I、0.6I、2I(2×2),第一个也称为标准分布。
上图中均值也都为零向量,协方差矩阵从左到右依次为:
对应地,其在底面上的投影为:
变化协方差矩阵如下:
得到的高斯分布底面投影如下所示:
而下图表示的是协方差矩阵为单位矩阵时,变化均值得到的高斯分布图像:
其中均值分别为:
1.2 高斯判别分析模型
给定一个分类问题,输入的特征
x
x
x是连续型随机变量,则可以使用GDA模型,使用多元正态分布对
p
(
x
∣
y
)
p(x|y)
p(x∣y)进行建模,模型为:
分布可以写为:
模型的参数为
φ
、
Σ
、
μ
0
、
μ
1
\varphi、\Sigma、\mu_0、\mu_1
φ、Σ、μ0、μ1,数据的对数似然函数可以写为:
这个对数似然函数也叫做joint likelihood(逻辑回归里的对数似然函数叫做conditional likelihood),通过最大化这个对数似然函数,我们可以得到参数的极大似然估计:
其中
μ
0
\mu_0
μ0的分子即对所有的负样本的特征x求均值。
算法要做的事情其实如下图所示:
图中画出了所有的训练样本和两个高斯分布的等值线(表示两个不同的类别),两个分布由于协方差矩阵相同所以等值线形状也相同,但是他们的均值不同的所以位置不同,图中的直线是一个决策边界,上面的点有
p
(
y
=
1
∣
x
)
=
0.5
p(y=1|x)=0.5
p(y=1∣x)=0.5,直线两边分别表示不同的类别。
1.3 GDA和逻辑回归的比较
GDA和逻辑回归之间确实存在这某种有趣的关系,如果我们将
p
(
y
=
1
∣
x
;
φ
,
μ
0
,
μ
1
,
Σ
)
p(y=1|x;\varphi,\mu_0,\mu_1,\Sigma)
p(y=1∣x;φ,μ0,μ1,Σ)视为
x
x
x的函数,那么有:
其中
θ
\theta
θ是
φ
,
μ
0
,
μ
1
,
Σ
\varphi,\mu_0,\mu_1,\Sigma
φ,μ0,μ1,Σ的函数,这和逻辑回归的形式是相同的。
然而GDA和逻辑回归在给定相同数据集时会训练得到不同的决策边界,哪一个更好呢?
总的来说,GDA做了更强的建模假设,当假设正确或者接近正确时模型利用数据的效率更高(需要较少的训练数据就能的到很好的效果);逻辑回归建模假设相对较弱,因此对于偏差更加鲁棒,而且当数据并不服从高斯分布时,逻辑回归由于GDA。基于这个原因,逻辑回归在实践中比GDA更加常用。附上GDA的代码。
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
import matplotlib as mpl
import matplotlib.pyplot as plt
class GDA:
def __init__(self,train_data,train_label):
self.Train_Data = train_data
self.Train_Label = train_label
self.postive_num = 0
self.negetive_num = 0
postive_data = []
negetive_data = []
for (data,label) in zip(self.Train_Data,self.Train_Label):
if label == 1: # 正样本
self.postive_num += 1
postive_data.append(list(data))
else: # 负样本
self.negetive_num += 1
negetive_data.append(list(data))
row,col = np.shape(train_data)
# 计算正负样本的高斯分布的均值向量
postive_data = np.array(postive_data)
negetive_data = np.array(negetive_data)
postive_data_sum = np.sum(postive_data, 0)
negetive_data_sum = np.sum(negetive_data, 0)
self.mu_positive = postive_data_sum*1.0/self.postive_num # 正样本的高斯分布的均值向量
self.mu_negetive = negetive_data_sum*1.0/self.negetive_num # 负样本的高斯分布的均值向量
# 计算高斯分布的协方差矩阵
positive_deta = postive_data-self.mu_positive
negetive_deta = negetive_data-self.mu_negetive
self.sigma = []
for deta in positive_deta:
deta = deta.reshape(1,col)
ans = deta.T.dot(deta)
self.sigma.append(ans)
for deta in negetive_deta:
deta = deta.reshape(1,col)
ans = deta.T.dot(deta)
self.sigma.append(ans)
self.sigma = np.array(self.sigma)
#print(np.shape(self.sigma))
self.sigma = np.sum(self.sigma,0)
self.sigma = self.sigma/row
self.mu_positive = self.mu_positive.reshape(1,col)
self.mu_negetive = self.mu_negetive.reshape(1,col)
def Gaussian(self, x, mean, cov):
dim = np.shape(cov)[0]
# cov的行列式为零时的措施
covdet = np.linalg.det(cov + np.eye(dim) * 0.001)
covinv = np.linalg.inv(cov + np.eye(dim) * 0.001)
xdiff = (x - mean).reshape((1, dim))
# 概率密度
prob = 1.0 / (np.power(np.power(2 * np.pi, dim) * np.abs(covdet), 0.5)) * \
np.exp(-0.5 * xdiff.dot(covinv).dot(xdiff.T))[0][0]
return prob
def predict(self,test_data):
predict_label = []
for data in test_data:
positive_pro = self.Gaussian(data,self.mu_positive,self.sigma)
negetive_pro = self.Gaussian(data,self.mu_negetive,self.sigma)
if positive_pro >= negetive_pro:
predict_label.append(1)
else:
predict_label.append(0)
return predict_label
def main():
# 导入乳腺癌数据,scikit-learn自带的
breast_cancer = load_breast_cancer()
data = np.array(breast_cancer.data)
label = np.array(breast_cancer.target)
data = MinMaxScaler().fit_transform(data)
## 解决画图是的中文乱码问题
# mpl.rcParams['font.sans-serif'] = [u'simHei']
#mpl.rcParams['axes.unicode_minus'] = False
# 分割训练集与测试集
train_data,test_data,train_label,test_label = train_test_split(data,label,test_size=3/7)
# 数据可视化
plt.scatter(test_data[:,0],test_data[:,1],c = test_label)
plt.title("乳腺癌数据集显示")
plt.show()
# GDA结果
gda = GDA(train_data,train_label)
test_predict = gda.predict(test_data)
print("GDA的正确率为:",accuracy_score(test_label,test_predict))
# 数据可视化
plt.scatter(test_data[:,0],test_data[:,1],c = test_predict)
plt.title("GDA分类结果显示")
plt.show()
## Logistic回归结果
# lr = LogisticRegression()
# lr.fit(train_data,train_label)
# test_predict = lr.predict(test_data)
# print("Logistic回归的正确率为:",accuracy_score(test_label,test_predict))
## 数据可视化
# plt.scatter(test_data[:,0],test_data[:,1],c = test_predict)
# plt.title("Logistic回归分类结果显示")
# plt.show()
if __name__ == '__main__':
main()
欢迎扫描二维码关注微信公众号 深度学习与数学 [每天获取免费的大数据、AI等相关的学习资源、经典和最新的深度学习相关的论文研读,算法和其他互联网技能的学习,概率论、线性代数等高等数学知识的回顾]