一、线性判别分析
(一)简介
线性判别分析(linear discriminant analysis,LDA)是对费舍尔的线性鉴别方法的归纳,这种方法使用统计学,模式识别和机器学习方法,试图找到两类物体或事件的特征的一个线性组合,以能够特征化或区分它们。所得的组合可用来作为一个线性分类器,或者,更常见的是,为后续的分类做降维处理
线性判别分析是一种经典的线性学习方法,在二分类问题上最早由Fisher在1936年提出,亦称Fisher线性判别。线性判别的思想非常朴素:给定训练样例集,设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近,异样样例的投影点尽可能远离;在对新样本进行分类时,将其投影到同样的直线上,再根据投影点的位置来确定新样本的类别。 [2]
LDA与方差分析(ANOVA)和回归分析紧密相关,这两种分析方法也试图通过一些特征或测量值的线性组合来表示一个因变量。然而,方差分析使用类别自变量和连续数因变量,而判别分析连续自变量和类别因变量(即类标签)。逻辑回归和概率回归比方差分析更类似于LDA,因为他们也是用连续自变量来解释类别因变量的。
LDA的基本假设是自变量是正态分布的,当这一假设无法满足时,在实际应用中更倾向于用上述的其他方法。LDA也与主成分分析(PCA)和因子分析紧密相关,它们都在寻找最佳解释数据的变量线性组合。LDA明确的尝试为数据类之间不同建立模型。 另一方面,PCA不考虑类的任何不同,因子分析是根据不同点而不是相同点来建立特征组合。判别的分析不同因子分析还在于,它不是一个相互依存技术:即必须区分出自变量和因变量(也称为准则变量)的不同。在对自变量每一次观察测量值都是连续量的时候,LDA能有效的起作用。当处理类别自变量时,与LDA相对应的技术称为判别反应分析。
(二)优点
Fisher提出LDA距今已近七十年,仍然是降维和模式分类领域应用中最为广泛采用而且极 为有效的方法之一,其典型应用包括人脸检测、人脸识别、基于视觉飞行的地平线检测、目标跟踪和检测、信用卡欺诈检测和图像检索、语音识别等。之所以有如此广泛的应用,其 主要原因是,LDA(包括其多类推广)具有以下优点:可以直接求得基于广义特征值问题的解析解,从而避免了在一般非线性算法中,如多层感知器,构建中所常遇到的局部最小问题无需对模式的输出类别进行人为的编码,从而使 LDA 对不平衡模式类的处理表现出尤其明显的优势。与神经网络方法相比,LDA 不需要调整参数,因而也不存在学习参数和优化权重以及神经元激活函数的选择等问题;对模式的归一化或随机化不敏感,而这在基于梯度 下降的各种算法中则显得比较突出 [3] 。在某些实际情形中,LDA 具有与基于结构风险最小化原理的支持向量机(SVM)相当的甚至更优的推广性能,但其计算效率则远优于SVM。正则判别分析法(CDA)寻找最优区分类别的坐标轴(k-1个正则坐标,k为类别的数量)。 这些线性函数是不相关的,实际上,它们通过n维数据云定义了一个最优化的k-1个空间,能够最优的区分k个类(通过其在空间的投影)。。
多类LDA:当出现超过两类的情况时,可以使用由费舍尔判别派生出的分析方法,它延伸为寻找一个保留了所有类的变化性的子空间。这是由 C.R.Rao 总结出来的。假设,C个类中每一个类都有均值和相同的协方差。
要实现典型的LDA技术前提是所有的样本都必须提前准备完毕。但有些情况下,没有现成的完整数据集或者输入观察数据是流的形式。这样,就要求LDA的特征提取有能力随着观察新样本的增加而更新LDA的特征,而不是在整个数据集上运行算法。例如,在移动机器人或实时脸部识别等实时应用中,提取的LDA特征能随着新观察值实时更新是非常重要的。这种能够通过简单观察新样本来更新LDA特征的技术就叫做增量LDA算法,在过去二十年里,它已经被广泛的研究过。Catterjee和Roychowdhury提出了一种增量自组织LDA算法来更新LDA特征。另外,Demir和Ozmehmet提出利用误差改正和赫布学习规则的线上本地学习算法来更新LDA特征。最后,Aliyari等人提供了快速增量LDA算法。
二、编程实现
处理莺尾花数据集:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_classification
class LDA():
def Train(self, X, y):
"""X为训练数据集,y为训练label"""
X1 = np.array([X[i] for i in range(len(X)) if y[i] == 0])
X2 = np.array([X[i] for i in range(len(X)) if y[i] == 1])
# 求中心点
mju1 = np.mean(X1, axis=0) # mju1是ndrray类型
mju2 = np.mean(X2, axis=0)
# dot(a, b, out=None) 计算矩阵乘法
cov1 = np.dot((X1 - mju1).T, (X1 - mju1))
cov2 = np.dot((X2 - mju2).T, (X2 - mju2))
Sw = cov1 + cov2
# 计算w
w = np.dot(np.mat(Sw).I, (mju1 - mju2).reshape((len(mju1), 1)))
# 记录训练结果
self.mju1 = mju1 # 第1类的分类中心
self.cov1 = cov1
self.mju2 = mju2 # 第2类的分类中心
self.cov2 = cov2
self.Sw = Sw # 类内散度矩阵
self.w = w # 判别权重矩阵
def Test(self, X, y):
"""X为测试数据集,y为测试label"""
# 分类结果
y_new = np.dot((X), self.w)
# 计算fisher线性判别式
nums = len(y)
c1 = np.dot((self.mju1 - self.mju2).reshape(1, (len(self.mju1))), np.mat(self.Sw).I)
c2 = np.dot(c1, (self.mju1 + self.mju2).reshape((len(self.mju1), 1)))
c = 1/2 * c2 # 2个分类的中心
h = y_new - c
# 判别
y_hat = []
for i in range(nums):
if h[i] >= 0:
y_hat.append(0)
else:
y_hat.append(1)
# 计算分类精度
count = 0
for i in range(nums):
if y_hat[i] == y[i]:
count += 1
precise = count / nums
# 显示信息
print("测试样本数量:", nums)
print("预测正确样本的数量:", count)
print("测试准确度:", precise)
return precise
if '__main__' == __name__:
# 产生分类数据
n_samples = 500
X, y = make_classification(n_samples=n_samples, n_features=2, n_redundant=0, n_classes=2,n_informative=1, n_clusters_per_class=1, class_sep=0.5, random_state=10)
# LDA线性判别分析(二分类)
lda = LDA()
# 60% 用作训练,40%用作测试
Xtrain = X[:299, :]
Ytrain = y[:299]
Xtest = X[300:, :]
Ytest = y[300:]
lda.Train(Xtrain, Ytrain)
precise = lda.Test(Xtest, Ytest)
# 原始数据
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)
plt.xlabel("x1")
plt.ylabel("x2")
plt.title("Test precise:" + str(precise))
plt.show()
在运行代码时出现了如下问题:
方法1:直接从sklearn.datasets中导入make_blobs
将from sklearn.datasets.samples_generator import make_blobs修改为:
from sklearn.datasets import make_blobs
得以解决
方法2:版本问题,降低版本
在当前终端输入:
pip install scikit-learn==0.22.1
虽然还有警告,但是起码可以运行了。
结果:
处理月亮数据集:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
class LDA():
def Train(self, X, y):
"""X为训练数据集,y为训练label"""
X1 = np.array([X[i] for i in range(len(X)) if y[i] == 0])
X2 = np.array([X[i] for i in range(len(X)) if y[i] == 1])
# 求中心点
mju1 = np.mean(X1, axis=0) # mju1是ndrray类型
mju2 = np.mean(X2, axis=0)
# dot(a, b, out=None) 计算矩阵乘法
cov1 = np.dot((X1 - mju1).T, (X1 - mju1))
cov2 = np.dot((X2 - mju2).T, (X2 - mju2))
Sw = cov1 + cov2
# 计算w
w = np.dot(np.mat(Sw).I, (mju1 - mju2).reshape((len(mju1), 1)))
# 记录训练结果
self.mju1 = mju1 # 第1类的分类中心
self.cov1 = cov1
self.mju2 = mju2 # 第1类的分类中心
self.cov2 = cov2
self.Sw = Sw # 类内散度矩阵
self.w = w # 判别权重矩阵
def Test(self, X, y):
"""X为测试数据集,y为测试label"""
# 分类结果
y_new = np.dot((X), self.w)
# 计算fisher线性判别式
nums = len(y)
c1 = np.dot((self.mju1 - self.mju2).reshape(1, (len(self.mju1))), np.mat(self.Sw).I)
c2 = np.dot(c1, (self.mju1 + self.mju2).reshape((len(self.mju1), 1)))
c = 1/2 * c2 # 2个分类的中心
h = y_new - c
# 判别
y_hat = []
for i in range(nums):
if h[i] >= 0:
y_hat.append(0)
else:
y_hat.append(1)
# 计算分类精度
count = 0
for i in range(nums):
if y_hat[i] == y[i]:
count += 1
precise = count / (nums+0.000001)
# 显示信息
print("测试样本数量:", nums)
print("预测正确样本的数量:", count)
print("测试准确度:", precise)
return precise
if '__main__' == __name__:
# 产生分类数据
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
# LDA线性判别分析(二分类)
lda = LDA()
# 60% 用作训练,40%用作测试
Xtrain = X[:60, :]
Ytrain = y[:60]
Xtest = X[40:, :]
Ytest = y[40:]
lda.Train(Xtrain, Ytrain)
precise = lda.Test(Xtest, Ytest)
# 原始数据
plt.scatter(X[:, 0], X[:, 1], marker='o', c=y)
plt.xlabel("x1")
plt.ylabel("x2")
plt.title("Test precise:" + str(precise))
plt.show()
运行结果 :
对月亮数据集进行SVM分类:
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
import numpy as np
import matplotlib as mpl
from sklearn.datasets import make_moons
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
# 为了显示中文
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False#rc配置或rc参数,通过rc参数可以修改默认的属性,包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等。
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)#生成月亮数据集
def plot_dataset(X, y, axes):#绘制图形
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
plt.axis(axes)
plt.grid(True, which='both')
plt.xlabel(r"$x_1$", fontsize=20)
plt.ylabel(r"$x_2$", fontsize=20, rotation=0)
plt.title("月亮数据",fontsize=20)
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
结果:
鸢尾花数据集SVM算法二分类
# 鸢尾花数据集SVM算法二分类
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, svm
import pandas as pd
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
iris = datasets.load_iris()
iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X[y != 0, :2] # 选择X的前两个特性
y = y[y != 0]
n_sample = len(X)
np.random.seed(0)
order = np.random.permutation(n_sample) # 排列,置换
X = X[order]
y = y[order].astype(np.float)
X_train = X[:int(.9 * n_sample)]
y_train = y[:int(.9 * n_sample)]
X_test = X[int(.9 * n_sample):]
y_test = y[int(.9 * n_sample):]
#合适的模型
for fig_num, kernel in enumerate(('linear', 'rbf','poly')): # 径向基函数 (Radial Basis Function 简称 RBF),常用的是高斯基函数
clf = svm.SVC(kernel=kernel, gamma=10) # gamma是“rbf”、“poly”和“sigmoid”的核系数。
clf.fit(X_train, y_train)
plt.figure(str(kernel))
plt.xlabel('x1')
plt.ylabel('x2')
plt.scatter(X[:, 0], X[:, 1], c=y, zorder=10, cmap=plt.cm.Paired, edgecolor='k', s=20)
# zorder: z方向上排列顺序,数值越大,在上方显示
# paired两个色彩相近输出(paired)
# 圈出测试数据
plt.scatter(X_test[:, 0], X_test[:, 1], s=80, facecolors='none',zorder=10, edgecolor='k')
plt.axis('tight') #更改 x 和 y 轴限制,以便显示所有数据
x_min = X[:, 0].min()
x_max = X[:, 0].max()
y_min = X[:, 1].min()
y_max = X[:, 1].max()
XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]
Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]) # 样本X到分离超平面的距离
Z = Z.reshape(XX.shape)
plt.contourf(XX,YY,Z>0,cmap=plt.cm.Paired)
plt.contour(XX, YY, Z, colors=['r', 'k', 'b'],
linestyles=['--', '-', '--'], levels=[-0.5, 0, 0.5]) # 范围
plt.title(kernel)
plt.show()
结果:
月亮数据集SVM二分类
# 月亮数据集SVM二分类
import matplotlib.pyplot as plt
from sklearn.pipeline import Pipeline
import numpy as np
import matplotlib as mpl
from sklearn.datasets import make_moons
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
# 为了显示中文
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
def plot_dataset(X, y, axes):
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
plt.axis(axes)
plt.grid(True, which='both')
plt.xlabel(r"$x_1$", fontsize=20)
plt.ylabel(r"$x_2$", fontsize=20, rotation=0)
plt.title("月亮数据",fontsize=20)
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
polynomial_svm_clf = Pipeline([
# 将源数据 映射到 3阶多项式
("poly_features", PolynomialFeatures(degree=3)),
# 标准化
("scaler", StandardScaler()),
# SVC线性分类器
("svm_clf", LinearSVC(C=10, loss="hinge", random_state=42))
])
polynomial_svm_clf.fit(X, y)
def plot_predictions(clf, axes):
# 打表
x0s = np.linspace(axes[0], axes[1], 100)
x1s = np.linspace(axes[2], axes[3], 100)
x0, x1 = np.meshgrid(x0s, x1s)
X = np.c_[x0.ravel(), x1.ravel()]
y_pred = clf.predict(X).reshape(x0.shape)
y_decision = clf.decision_function(X).reshape(x0.shape)
# print(y_pred)
# print(y_decision)
plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1)
plot_predictions(polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
结果如下
from sklearn.svm import SVC
gamma1, gamma2 = 0.1, 5
C1, C2 = 0.001, 1000
hyperparams = (gamma1, C1), (gamma1, C2)
svm_clfs = []
for gamma, C in hyperparams:
rbf_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=gamma, C=C))
])
rbf_kernel_svm_clf.fit(X, y)
svm_clfs.append(rbf_kernel_svm_clf)
plt.figure(figsize=(11, 7))
for i, svm_clf in enumerate(svm_clfs):
plt.subplot(221 + i)
plot_predictions(svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
gamma, C = hyperparams[i]
plt.title(r"$\gamma = {}, C = {}$".format(gamma, C), fontsize=16)
plt.tight_layout()
plt.show()
结果
三、参考文章
python下对鸢尾花数据集和月亮数据集,分别采用线性LDA、k-means和SVM算法进行二分类可视化分析_白水的博客-CSDN博客