《机器学习公式推导与代码实现》学习笔记,记录一下自己的学习过程,详细的内容请大家购买作者的书籍查阅。
线性判别分析
线性判别分析
(linear discriminant analysis, LDA
)是一种经典的线性分类方法,其基本思想是将数据投影到低维空间,使得同类数据尽可能接近,异类数据尽可能疏远。
另外线性判别分析能够通过投影来降低样本维度,并且在投影过程中用到了标签信息,线性判别分析是一种监督降维方法。
1 LDA数学推导
2 基于numpy的LDA算法实现
完整的LDA算法流程如下:
- (1)对训练集按类别进行分组;
- (2)分别计算每组样本的均值和协方差;
- (3)计算类间散度矩阵 S w S_{w} Sw;
- (4)计算两类样本的均值差 μ 0 − μ 1 \mu _{0} -\mu_{1} μ0−μ1;
- (5)对类间散度矩阵 S w S_{w} Sw进行奇异值分解,并求逆;
- (6)根据$S_{w}^{-1} \left ( \mu _{0} -\mu _{1} \right ) 得到 得到 得到w$;
- (7)计算投影后的数据点 Y = w X Y = wX Y=wX。
模型定义:
import numpy as np
class LDA():
def __init__(self): # 初始化权重矩阵
self.w = None
def calc_cov(self, X, Y=None): # 计算协方差矩阵
m = X.shape[0]
# 将数据缩放到均值为0,标准差为1的标准正态分布
X = (X - np.mean(X, axis=0))/np.std(X, axis=0) # 数据标准化
Y = X if Y == None else (Y - np.mean(Y, axis=0))/np.std(Y, axis=0)
return 1 / m * np.matmul(X.T, Y)
def fit(self, X, y): # LDA拟合过程
# 按类分组
X0 = X[y == 0]
X1 = X[y == 1]
# 分别计算两类数据自变量的协方差矩阵
sigma0 = self.calc_cov(X0)
sigma1 = self.calc_cov(X1)
Sw = sigma0 + sigma1 # 计算类内散度矩阵
# 分别计算两类数据自变量的均值和差
u0, u1 = np.mean(X0, axis=0), np.mean(X1, axis=0)
mean_diff = np.atleast_1d(u0 - u1) # 如果 u0 - u1 是一个标量,则将其转换为长度为1的一维数组。如果 u0 - u1 是一个数组,则保持不变
# 利用 SVD 分解,我们可以通过计算矩阵 U、Σ 和 V 来得到原始矩阵的逆矩阵的近似
U, S, V = np.linalg.svd(Sw) # # 对类内散度矩阵进行奇异值分解
Sw_ = np.dot(np.dot(V.T, np.linalg.pinv(np.diag(S))), U.T) # 计算类内散度矩阵的逆
self.w = Sw_.dot(mean_diff) # 计算w
def predict(self, X): # LDA分类预测
y_pred = []
for sample in X:
h = sample.dot(self.w)
y = 1 * (h < 0) # 如果h小于0,则将y设置为1,否则将y设置为0
y_pred.append(y)
return y_pred
读取数据:
from sklearn import datasets
from sklearn.model_selection import train_test_split
data = datasets.load_iris()
X = data.data
y = data.target
X = X[y != 2]
y = y[y != 2]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=41)
X_train.shape, X_test.shape, y_train.shape, y_test.shape
((80, 4), (20, 4), (80,), (20,))
数据测试:
lda = LDA()
lda.fit(X_train, y_train)
y_pred = lda.predict(X_test)
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)
0.85
结果可视化:
import matplotlib.pyplot as plt
class Plot():
def __init__(self):
self.cmap = plt.get_cmap('viridis')
def _transform(self, X, dim):
covariance = LDA().calc_cov(X)
eigenvalues, eigenvectors = np.linalg.eig(covariance)
# Sort eigenvalues and eigenvector by largest eigenvalues
idx = eigenvalues.argsort()[::-1]
eigenvalues = eigenvalues[idx][:dim]
eigenvectors = np.atleast_1d(eigenvectors[:, idx])[:, :dim]
# Project the data onto principal components
X_transformed = X.dot(eigenvectors)
return X_transformed
# Plot the dataset X and the corresponding labels y in 2D using PCA.
def plot_in_2d(self, X, y=None, title=None, accuracy=None, legend_labels=None):
X_transformed = self._transform(X, dim=2)
x1 = X_transformed[:, 0]
x2 = X_transformed[:, 1]
class_distr = []
y = np.array(y).astype(int)
colors = [self.cmap(i) for i in np.linspace(0, 1, len(np.unique(y)))]
# Plot the different class distributions
for i, l in enumerate(np.unique(y)):
_x1 = x1[y == l]
_x2 = x2[y == l]
_y = y[y == l]
class_distr.append(plt.scatter(_x1, _x2, color=colors[i]))
# Plot legend
if not legend_labels is None:
plt.legend(class_distr, legend_labels, loc=1)
# Plot title
if title:
if accuracy:
perc = 100 * accuracy
plt.suptitle(title)
plt.title("Accuracy: %.1f%%" % perc, fontsize=10)
else:
plt.title(title)
# Axis labels
plt.xlabel('class 1')
plt.ylabel('class 2')
plt.show()
Plot().plot_in_2d(X_test, y_pred, title="LDA", accuracy=accuracy)
Plot().plot_in_2d(X_test, y_test, title="LDA", accuracy=accuracy)
3 基于sklearn的LDA算法实现
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
clf = LinearDiscriminantAnalysis()
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print(accuracy)
1.0