在机器学习领域,线性判别分析(LDA)和二次判别分析(QDA)是两种常用的分类算法。它们都基于高斯分布的假设,但对协方差矩阵的处理方式不同。LDA假设所有类别共享相同的协方差矩阵,而QDA允许每个类别有自己的协方差矩阵。本文将通过生成的合成数据集来比较这两种算法的表现,并使用数据可视化来展示它们的决策边界和分类效果。
首先,定义了一个函数来生成合成数据。该函数创建了两个以(0, 0)和(1, 1)为中心的数据块,每个数据块被分配到一个特定的类别。数据块的离散程度由协方差矩阵参数控制,这些参数在从高斯分布生成样本时使用。
import numpy as np
def make_data(n_samples, n_features, cov_class_1, cov_class_2, seed=0):
rng = np.random.RandomState(seed)
X = np.concatenate([
rng.randn(n_samples, n_features) @ cov_class_1,
rng.randn(n_samples, n_features) @ cov_class_2 + np.array([1, 1]),
])
y = np.concatenate([np.zeros(n_samples), np.ones(n_samples)])
return X, y
生成了三个数据集。第一个数据集的两个类别共享相同的协方差矩阵,且该协方差矩阵是球形的(各向同性)。第二个数据集与第一个类似,但不强制协方差矩阵为球形。最后一个数据集为每个类别提供了一个非球形的协方差矩阵。
下面的代码用于绘制来自估计器的信息,即线性判别分析(LDA)和二次判别分析(QDA)。展示的信息包括:基于估计器的概率估计的决策边界;用圆圈表示正确分类样本的散点图;用叉号表示错误分类样本的散点图;估计器估计的每个类别的均值,用星号标记;以及在2个标准差处用椭圆表示的估计协方差。
import matplotlib as mpl
from matplotlib import colors
from sklearn.inspection import DecisionBoundaryDisplay
def plot_ellipse(mean, cov, color, ax):
v, w = np.linalg.eigh(cov)
u = w[0] / np.linalg.norm(w[0])
angle = np.arctan(u[1] / u[0])
angle = 180 * angle / np.pi # 转换为度
# 2个标准差处的高斯填充
ell = mpl.patches.Ellipse(mean, 2 * v[0] ** 0.5, 2 * v[1] ** 0.5, angle=180 + angle, facecolor=color, edgecolor="black", linewidth=2)
ell.set_clip_box(ax.bbox)
ell.set_alpha(0.4)
ax.add_artist(ell)
def plot_result(estimator, X, y, ax):
cmap = colors.ListedColormap(["tab:red", "tab:blue"])
DecisionBoundaryDisplay.from_estimator(estimator, X, response_method="predict_proba", plot_method="pcolormesh", ax=ax, cmap="RdBu", alpha=0.3)
DecisionBoundaryDisplay.from_estimator(estimator, X, response_method="predict_proba", plot_method="contour", ax=ax, alpha=1.0, levels=[0.5])
y_pred = estimator.predict(X)
X_right, y_right = X[y == y_pred], y[y == y_pred]
X_wrong, y_wrong = X[y != y_pred], y[y != y_pred]
ax.scatter(X_right[:, 0], X_right[:, 1], c=y_right, s=20, cmap=cmap, alpha=0.5)
ax.scatter(X_wrong[:, 0], X_wrong[:, 1], c=y_wrong, s=30, cmap=cmap, alpha=0.9, marker="x")
ax.scatter(estimator.means_[:, 0], estimator.means_[:, 1], c="yellow", s=200, marker="*", edgecolor="black")
if isinstance(estimator, LinearDiscriminantAnalysis):
covariance = [estimator.covariance_] * 2
else:
covariance = estimator.covariance_
plot_ellipse(estimator.means_[0], covariance[0], "tab:red", ax)
plot_ellipse(estimator.means_[1], covariance[1], "tab:blue", ax)
ax.set_box_aspect(1)
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.set(xticks=[], yticks=[])
这些函数将帮助直观地理解LDA和QDA在不同数据集上的表现差异。
将在所有三个数据集上比较LDA和QDA两种估计器。首先需要注意的是,在第一和第二个数据集上,LDA和QDA是等价的。实际上,主要的区别在于LDA假设每个类别的协方差矩阵相同,而QDA为每个类别估计一个协方差矩阵。由于在这些情况下,数据生成过程对两个类别有相同的协方差矩阵,QDA估计的两个协方差矩阵几乎相等,因此等同于LDA估计的协方差矩阵。
在第一个数据集中,用于生成数据集的协方差矩阵是球形的,这导致了一个与两个均值之间的垂直平分线对齐的判别边界。在第二个数据集中,判别边界不再通过两个均值的中间。
最后,在第三个数据集中,观察到LDA和QDA之间的真正区别。QDA拟合了两个协方差矩阵,并提供了一个非线性的判别边界,而LDA由于假设两个类别共享一个单一的协方差矩阵,因此拟合不足。