你好,我是小白~
今儿和大家聊降维算法,内容很多、很细,一文讲清楚,记得点赞收藏,慢慢学习~
降维算法通过减少特征数量,降低数据集的复杂度,使模型在处理数据时更加高效,同时保留最重要的信息,减少冗余。另外,模型性能方面,降维能减少噪声,防止过拟合,使得机器学习模型在测试数据上表现更稳定,从而提高预测的准确性。
今天涉及到的十大降维算法有:
主成分分析 线性判别分析 核主成分分析 独立成分分析 因子分析 t-SNE 多维尺度分析 自编码器 局部线性嵌入 奇异值分解
如果需要本文PDF版本的同学,文末获取~
另外,文末有总结性的干货~
一起来看下具体细化内容~
1. 主成分分析 (PCA)
PCA 是一种广泛使用的线性降维技术,主要用于减少数据的维数,同时保持尽可能多的原始数据变异。PCA 通过找到一组新的正交轴(称为主成分),使得投影到这些轴上的数据方差最大。
原理
PCA 的核心思想是通过线性变换将数据投影到新的坐标系中,这些坐标轴(主成分)是数据协方差矩阵的特征向量。第一个主成分是数据方差最大的方向,第二个主成分在正交于第一个主成分的方向上拥有次大方差,依此类推。
核心公式
1. 数据中心化:首先将数据矩阵 (大小为 ,其中 是样本数, 是特征数)中心化,即减去每个特征的均值:
其中, 是每个特征的均值向量。
2. 协方差矩阵:计算中心化数据的协方差矩阵:
3. 特征值分解:对协方差矩阵 进行特征值分解:
其中, 是特征向量(主成分方向), 是对应的特征值,表示沿着 方向的方差。
4. 投影:将原始数据投影到前 个主成分上:
其中, 是由前 个特征向量组成的矩阵。
推导步骤
步骤 1:数据中心化
为了去除偏差,首先需要将每个特征的均值减去,使得数据均值为零。中心化后的数据矩阵为:
步骤 2:计算协方差矩阵
协方差矩阵度量了每对特征之间的线性关系。它的公式为:
这里的 是一个 的矩阵。
步骤 3:特征值分解
对于协方差矩阵 ,我们执行特征值分解:
求解这个特征值问题会得到一组特征值 和对应的特征向量 。这些特征值的大小表示数据在对应主成分方向上的方差,特征向量则表示这些主成分的方向。
步骤 4:选择主成分和投影
通常选择前 个特征值最大的特征向量(主成分)来进行降维。将原数据投影到这些主成分上:
这一步将原始数据从 维降到 维。
经典案例
生成一个三维虚拟数据集,其中有明显的相关性。 使用PCA将数据降维到二维。 绘制原始数据集的三维散点图和PCA降维后的二维散点图。 绘制主成分的方差解释图。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.decomposition import PCA
# 设置随机种子以确保可重复性
np.random.seed(42)
# 生成一个三维数据集
mean = [0, 0, 0]
cov = [[1, 0.8, 0.5], [0.8, 1, 0.3], [0.5, 0.3, 1]] # 协方差矩阵,定义数据的相关性
data = np.random.multivariate_normal(mean, cov, 5000)
# 使用PCA将数据降维到2D
pca = PCA(n_components=2)
data_2d = pca.fit_transform(data)
# 创建一个图形并绘制多个子图
fig = plt.figure(figsize=(16, 8))
# 第一个子图:三维散点图(原始数据)
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(data[:, 0], data[:, 1], data[:, 2], c='r', marker='o', alpha=0.6)
ax1.set_title('3D Scatter Plot of Original Data', fontsize=15)
ax1.set_xlabel('X axis')
ax1.set_ylabel('Y axis')
ax1.set_zlabel('Z axis')
# 第二个子图:二维散点图(PCA降维后的数据)
ax2 = fig.add_subplot(222)
ax2.scatter(data_2d[:, 0], data_2d[:, 1], c='b', marker='o', alpha=0.6)
ax2.set_title('2D Scatter Plot after PCA', fontsize=15)
ax2.set_xlabel('Principal Component 1')
ax2.set_ylabel('Principal Component 2')
# 第三个子图:解释方差比例的柱状图
explained_variance = pca.explained_variance_ratio_
ax3 = fig.add_subplot(224)
ax3.bar(range(1, len(explained_variance) + 1), explained_variance, color='g', alpha=0.7)
ax3.set_title('Explained Variance Ratio of Principal Components', fontsize=15)
ax3.set_xlabel('Principal Component')
ax3.set_ylabel('Variance Explained')
# 调整布局
plt.tight_layout()
plt.show()
1. 数据生成:我们生成了一个三维的高斯分布数据集,该数据集具有特定的协方差矩阵来引入各维度之间的相关性。
2. PCA降维:通过sklearn.decomposition.PCA
库将数据降维到二维,并保留主成分。
第一个子图展示了原始三维数据的散点图,以红色显示。 第二个子图展示了通过PCA降维后的二维数据散点图,以蓝色显示。 第三个子图展示了每个主成分的方差解释比例,通过绿色的柱状图显示。
2. 线性判别分析 (LDA)
LDA 是一种监督降维方法,通过最大化类间距离与类内距离的比值来进行降维。
原理
寻找能最大化类间方差和最小化类内方差的投影方向。
核心公式
计算类内散布矩阵 和类间散布矩阵
通过解广义特征值问题求解投影矩阵
投影到前 个判别向量
其中 是由前 个判别向量组成的矩阵。
计算类内散布矩阵:
计算类间散布矩阵:
解广义特征值问题:
选择前 个最大特征值对应的特征向量,构建投影矩阵:
将原始数据投影到新的低维空间:
经典案例
使用线性判别分析(Linear Discriminant Analysis, LDA)进行分类的代码,涉及到数据分析和可视化。我们将使用鸢尾花数据集(Iris dataset),并进行数据分析和可视化。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
# 加载数据
iris = load_iris()
X = iris.data
y = iris.target
# 分割数据为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 进行LDA
lda = LDA(n_components=2)
X_train_lda = lda.fit_transform(X_train, y_train)
X_test_lda = lda.transform(X_test)
# 绘制降维后的数据分布图
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
for i, target_name in enumerate(iris.target_names):
plt.scatter(X_train_lda[y_train == i, 0], X_train_lda[y_train == i, 1], label=target_name)
plt.title('LDA of IRIS dataset (Training set)')
plt.xlabel('Component 1')
plt.ylabel('Component 2')
plt.legend()
plt.subplot(1, 2, 2)
for i, target_name in enumerate(iris.target_names):
plt.scatter(X_test_lda[y_test == i, 0], X_test_lda[y_test == i, 1], label=target_name)
plt.title('LDA of IRIS dataset (Test set)')
plt.xlabel('Component 1')
plt.ylabel('Component 2')
plt.legend()
plt.tight_layout()
plt.show()
# 使用训练好的模型进行预测
y_pred = lda.predict(X_test)
# 计算并绘制混淆矩阵
conf_matrix = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(8, 6))
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues', xticklabels=iris.target_names, yticklabels=iris.target_names)
plt.title('Confusion Matrix')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()
# 打印分类报告
print("Classification Report:\n", classification_report(y_test, y_pred))
print("Accuracy Score:", accuracy_score(y_test, y_pred))
3. 核主成分分析 (KPCA)
KPCA 通过核技巧将数据映射到高维空间,再在高维空间中进行 PCA,以捕捉非线性结构。
原理
使用核函数计算高维空间中的内积矩阵,然后在高维空间中进行 PCA。
核心公式
计算核矩阵
中心化核矩阵
其中 是全 1 矩阵。
对中心化核矩阵求特征值和特征向量
投影到前 个主成分
计算核矩阵:
中心化核矩阵:
对中心化核矩阵求特征值和特征向量:
选择前 个最大特征值对应的特征向量,构建投影矩阵:
将原始数据投影到新的低维空间:
经典案例
通过使用核技巧将数据映射到高维空间,然后在该高维空间中进行主成分分析。下面是一个使用Python进行核主成分分析的示例。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import KernelPCA
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_circles
# 生成非线性数据
X, y = make_circles(n_samples=800, factor=0.3, noise=0.1, random_state=42)
# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 核主成分分析
kpca = KernelPCA(n_components=2, kernel='rbf', gamma=15)
X_kpca = kpca.fit_transform(X_scaled)
# 可视化原始数据
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=y, cmap=plt.cm.Spectral)
plt.title('Original Data')
# 可视化核主成分分析后的数据
plt.subplot(1, 2, 2)
plt.scatter(X_kpca[:, 0], X_kpca[:, 1], c=y, cmap=plt.cm.Spectral)
plt.title('Kernel PCA Transformed Data')
plt.show()
# 额外图形:核PCA前后的特征分布
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
ax1.hist(X_scaled[:, 0], bins=30, alpha=0.5, label='Feature 1')
ax1.hist(X_scaled[:, 1], bins=30, alpha=0.5, label='Feature 2')
ax1.set_title('Original Features Distribution')
ax1.legend()
ax2.hist(X_kpca[:, 0], bins=30, alpha=0.5, label='Principal Component 1')
ax2.hist(X_kpca[:, 1], bins=30, alpha=0.5, label='Principal Component 2')
ax2.set_title('Transformed Principal Components Distribution')
ax2.legend()
plt.show()
可以帮助理解核主成分分析如何将复杂的非线性数据映射到新的低维空间中,使得数据更加容易分离和分析。
4. 独立成分分析 (ICA)
ICA 是一种统计技术,旨在将多变量信号分解为若干个相互统计独立的分量。它被广泛应用于信号处理领域,如语音分离。
原理
ICA 假设观察到的信号是若干个
独立信号的线性组合。通过找到这种组合,可以恢复原始的独立信号。ICA 的一个重要性质是:如果一个信号的概率分布偏离高斯分布,则可以通过最大化其非高斯性来实现信号的分离。
核心公式
1. 观测信号的模型:
其中, 是观测信号向量, 是混合矩阵, 是独立源信号向量。
2. 非高斯性度量:使用 Kurtosis 或 Negentropy 作为非高斯性度量。
Kurtosis 度量:
Negentropy 度量:
其中 是信号的熵, 是高斯分布信号的熵。
推导步骤
步骤 1:白化数据
在进行 ICA 之前,首先要对数据进行白化处理,使得各个维度之间不相关且具有相同的方差。白化后的数据满足:
其中 是白化后的数据。
步骤 2:最大化非高斯性
ICA 的关键在于寻找一个线性变换 ,使得变换后的信号 的非高斯性最大化。可以使用不同的非高斯性度量方法,比如 Kurtosis 或 Negentropy。
步骤 3:迭代优化
通过迭代优化算法,如梯度上升法,最大化信号的非高斯性来求解 。典型的算法有 FastICA。
步骤 4:恢复独立信号
最终,通过 的逆变换可以从观察到的信号中分离出独立信号 :
经典案例
创建一个合成信号的虚拟数据集,这些信号是通过混合独立的信号(如正弦波、方波和噪声)生成的。然后我们将使用ICA从混合信号中分离出独立分量,并绘制原始信号、混合信号和分离后的信号。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import FastICA
# 设置随机种子
np.random.seed(0)
# 生成虚拟信号数据
n_samples = 2000
time = np.linspace(0, 8, n_samples)
# 生成3个独立的信号:正弦波、方波和噪声
s1 = np.sin(2 * time) # 正弦波
s2 = np.sign(np.sin(3 * time)) # 方波
s3 = np.random.normal(size=n_samples) # 高斯噪声
# 将信号组合成矩阵
S = np.c_[s1, s2, s3]
# 将信号标准化到范围内
S /= S.std(axis=0)
# 生成混合数据(线性混合)
A = np.array([[1, 1, 0.5], [0.5, 2, 1], [1.5, 1, 2.5]]) # 混合矩阵
X = np.dot(S, A.T) # 生成混合信号
# 使用FastICA从混合信号中分离独立成分
ica = FastICA(n_components=3)
S_ = ica.fit_transform(X) # 重构后的信号
A_ = ica.mixing_ # 分离出的混合矩阵
# 绘制原始信号、混合信号和分离出的信号
plt.figure(figsize=(15, 10))
# 原始信号
plt.subplot(3, 1, 1)
plt.title("Original Signals")
colors = ['red', 'blue', 'green']
for i, signal in enumerate(S.T):
plt.plot(time, signal, color=colors[i], label=f"Signal {i+1}")
plt.legend(loc='upper right')
# 混合信号
plt.subplot(3, 1, 2)
plt.title("Mixed Signals")
colors = ['orange', 'purple', 'brown']
for i, signal in enumerate(X.T):
plt.plot(time, signal, color=colors[i], label=f"Mixed {i+1}")
plt.legend(loc='upper right')
# 分离出的信号
plt.subplot(3, 1, 3)
plt.title("ICA Recovered Signals")
colors = ['cyan', 'magenta', 'yellow']
for i, signal in enumerate(S_.T):
plt.plot(time, signal, color=colors[i], label=f"Recovered {i+1}")
plt.legend(loc='upper right')
plt.tight_layout()
plt.show()
1. 信号生成:我们创建了三个不同的独立信号:正弦波、方波和高斯噪声。
2. 混合信号:使用一个线性混合矩阵将原始信号混合在一起,生成我们观察到的混合信号。
3. 独立成分分析(ICA):应用FastICA算法将混合信号分离回原始独立信号。
第一个子图展示了三个独立的原始信号。 第二个子图展示了混合后的信号,这些信号是从线性混合矩阵得到的。 第三个子图展示了通过ICA分离出的信号,理论上应该与第一个子图中的原始信号相似(但顺序和符号可能不同)。
5. 因子分析 (Factor Analysis)
因子分析假设观测变量是少数潜在变量(因子)和误差项的线性组合,旨在发现隐藏的因子。
原理
通过最大化观测变量与因子的协方差来估计因子。
核心公式
假设模型
其中 是观测变量, 是因子载荷矩阵, 是因子, 是误差项。
估计因子载荷矩阵 和因子
假设观测变量 的协方差矩阵为 :
其中 是误差项的协方差矩阵。
通过最大似然估计(MLE)或期望最大化算法(EM)迭代估计 和 。
经典案例
一个典型的因子分析案例是市场研究中的消费者偏好分析。
假设我们有一组消费者对手机的各种特征的评分数据,比如屏幕大小、摄像头质量、电池续航等等。我们希望通过因子分析找出潜在的因子,例如“高性能”、“高屏幕质量”、“长续航时间”等,来解释消费者的偏好。
import pandas as pd
import numpy as np
# 创建虚拟数据集
np.random.seed(0)
n_samples = 1000
n_features = 5
# 生成数据集
data = np.random.randint(1, 11, size=(n_samples, n_features))
df = pd.DataFrame(data, columns=['Screen Size', 'Camera Quality', 'Battery Life', 'Performance', 'Design'])
# 添加一些噪音列
df['other factory'] = np.random.randint(1, 11, size=n_samples)
# 显示数据集的前几行
print(df.head())
使用因子分析来提取潜在因子,并使用图形来展示分析结果。
from sklearn.decomposition import FactorAnalysis
import matplotlib.pyplot as plt
# 因子分析
fa = FactorAnalysis(n_components=3)
fa.fit(df)
# 提取因子负荷
factors = pd.DataFrame(fa.components_, columns=df.columns)
# 绘制因子负荷热图
plt.figure(figsize=(10, 6))
plt.imshow(factors, cmap='viridis', aspect='auto', interpolation='nearest')
plt.colorbar()
plt.xticks(range(len(df.columns)), df.columns, rotation=45)
plt.yticks(range(len(factors)), ['Factor {}'.format(i+1) for i in range(len(factors))])
plt.title('Factor Loading Heatmap')
plt.show()
# 绘制因子得分散点图
factors_scores = fa.transform(df)
plt.figure(figsize=(8, 6))
plt.scatter(factors_scores[:, 0], factors_scores[:, 1])
plt.xlabel('Factor 1')
plt.ylabel('Factor 2')
plt.title('Factor Scores Scatter Plot')
plt.grid(True)
plt.show()
6. t-SNE
t-SNE 是一种非线性降维技术,主要用于高维数据的可视化。它能有效地将高维数据映射到二维或三维空间中,同时保持数据的局部结构。
原理
t-SNE 将高维数据中的相似性度量转化为低维空间中的相似性度量,通过最小化两者之间的 Kullback-Leibler 散度来实现降维。t-SNE 的核心在于将原始高维空间的距离关系映射到低维空间中,尽量保持数据的邻近关系。
核心公式
1. 高维空间中的相似性度量:使用条件概率 来表示点 和点 之间的相似性:
其中 是一个用于控制点 $ i \
) 邻域范围的参数。
2. 低维空间中的相似性度量:使用类似的条件概率 来表示低维空间中点 和点 之间的相似性:
3. 优化目标:通过最小化高维空间和低维空间之间的 Kullback-Leibler 散度来进行优化:
推导步骤
步骤 1:计算高维空间的相似性
对于每一对数据点 和 ,在高维空间中计算它们的相似性 ,这种相似性可以视为点 作为点 邻居的概率:
这里的 是通过精确寻找使得 $ p_j $ 的熵满足预设的值。
步骤 2:定义低维空间的相似性
在低维空间中,为保持高维空间中的结构,定义 来度量点 和 的相似性:
步骤 3:优化目标函数
通过最小化高维和低维空间中相似性分布之间的差异(即 Kullback-Leibler 散度),找到能够保持数据局部结构的低维嵌入:
这个优化过程可以通过梯度下降法来实现。
步骤 4:梯度下降
在优化过程中,通过不断调整低维嵌入 来减少目标函数 ,直到达到收敛条件。
经典案例
这个案例使用虚拟数据集并生成两个不同的可视化图形:一个是二维的t-SNE结果图,另一个是降维前后的散点图对比。
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_blobs, make_moons
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
# 生成虚拟数据集
n_samples = 1000
X1, y1 = make_blobs(n_samples=n_samples, centers=4, cluster_std=1.0, random_state=42)
X2, y2 = make_moons(n_samples=n_samples, noise=0.1, random_state=42)
# 将两个数据集结合起来
X = np.vstack([X1, X2])
y = np.hstack([y1, y2 + 4])
# 使用PCA进行初步降维
pca = PCA(n_components=2) # 将组件数改为2
X_pca = pca.fit_transform(X)
# 使用t-SNE降维到2D
tsne = TSNE(n_components=2, random_state=42, perplexity=30, n_iter=1000)
X_tsne = tsne.fit_transform(X_pca)
# 绘图
plt.figure(figsize=(14, 7))
# t-SNE降维结果图
plt.subplot(1, 2, 1)
palette = sns.color_palette("hsv", len(np.unique(y)))
sns.scatterplot(x=X_tsne[:, 0], y=X_tsne[:, 1], hue=y, palette=palette, s=60, legend='full')
plt.title('t-SNE visualization')
plt.xlabel('t-SNE Component 1')
plt.ylabel('t-SNE Component 2')
# 原始数据集的PCA降维前后的对比图
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='tab10', s=10, alpha=0.6)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='tab10', s=60, alpha=0.8, edgecolors='k')
plt.title('Original vs t-SNE reduced')
plt.xlabel('Feature 1 / t-SNE Component 1')
plt.ylabel('Feature 2 / t-SNE Component 2')
# 调整图例
plt.legend(title='Class')
plt.tight_layout()
plt.show()
1. 数据生成:使用make_blobs
和make_moons
生成两个不同类型的虚拟数据集,分别模拟高斯分布的数据和非线性的数据。将两个数据集合并以形成更复杂的数据结构。
2. 降维:使用PCA将数据先降维到50维,以减少数据的噪声和计算复杂度。使用t-SNE算法进一步将数据降维到2维。
左侧图显示t-SNE降维后的2D可视化,颜色表示不同的类别。 右侧图对比了原始数据和t-SNE降维后的数据在同一图中的分布情况。
7. 多维尺度分析 (MDS)
MDS 通过保留数据点之间的距离关系将高维数据映射到低维空间,常用于可视化和数据分析。
原理
在低维空间中找到数据点位置,使得它们之间的距离尽可能接近原始高维空间中的距离。
核心公式
计算原始高维空间中的距离矩阵
在低维空间中找到数据点位置 ,最小化应力函数
其中 是低维空间中的距离。
计算原始高维空间中的距离矩阵 :
定义应力函数:
通过迭代优化应力函数,找到低维空间中的位置 。
经典案例
使用Python进行多维尺度分析的案例,包含两个及以上的数据分析图形。使用MDS将数据降维到二维。可视化原始高维数据和降维后的二维数据。通过比较可视化图来展示MDS的效果。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.manifold import MDS
from mpl_toolkits.mplot3d import Axes3D
from scipy.spatial.distance import pdist, squareform
# 生成高维数据
n_samples = 1000
n_features = 5
X, y = make_blobs(n_samples=n_samples, n_features=n_features, centers=5, random_state=42)
# 计算原始数据的距离矩阵
distances = squareform(pdist(X))
# 使用MDS进行降维
mds = MDS(n_components=2, dissimilarity="precomputed", random_state=42)
X_mds = mds.fit_transform(distances)
# 可视化原始高维数据(选取前3个特征)
fig = plt.figure(figsize=(14, 6))
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(X[:, 0], X[:, 1], X[:, 2], c=y, cmap=plt.cm.get_cmap('viridis', 5))
ax1.set_title('Original High-dimensional Data (First 3 Features)')
ax1.set_xlabel('Feature 1')
ax1.set_ylabel('Feature 2')
ax1.set_zlabel('Feature 3')
# 可视化降维后的二维数据
ax2 = fig.add_subplot(122)
scatter = ax2.scatter(X_mds[:, 0], X_mds[:, 1], c=y, cmap=plt.cm.get_cmap('viridis', 5))
ax2.set_title('MDS Reduced 2D Data')
ax2.set_xlabel('MDS Dimension 1')
ax2.set_ylabel('MDS Dimension 2')
legend1 = ax2.legend(*scatter.legend_elements(), title="Classes")
ax2.add_artist(legend1)
plt.show()
# 计算降维前后的距离矩阵差异
original_distances = pdist(X)
reduced_distances = pdist(X_mds)
correlation = np.corrcoef(original_distances, reduced_distances)[0, 1]
print(f"Correlation between original and reduced distances: {correlation:.4f}")
我们首先生成了一个5维的数据集,并使用MDS将数据降维到2维。然后,我们分别可视化了原始高维数据和降维后的二维数据。此外,通过计算降维前后的距离矩阵的相关性,展示了MDS的效果。
8. 自编码器 (Autoencoder)
自编码器是一种神经网络,通过训练将输入数据编码为低维表示,再解码重建原始数据,从而实现降维。
原理
通过编码器和解码器神经网络的组合,将数据压缩到低维表示再恢复出来。
核心公式
编码器函数
解码器函数
最小化重构误差
定义编码器:
定义解码器:
定义重构误差:
通过反向传播和梯度下降优化网络参数 。
经典案例
将使用Keras和TensorFlow来创建自编码器,并使用Matplotlib来可视化结果。我们会使用MNIST数据集来展示自编码器的效果,并生成多个图形来进行数据分析。
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense
# 加载MNIST数据集
(x_train, _), (x_test, _) = mnist.load_data()
# 数据预处理
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print(x_train.shape)
print(x_test.shape)
接下来,我们定义自编码器模型:
# 定义自编码器
input_img = Input(shape=(784,))
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(32, activation='relu')(encoded)
decoded = Dense(64, activation='relu')(encoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)
autoencoder = Model(input_img, decoded)
# 编译模型
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
# 训练模型
autoencoder.fit(x_train, x_train,
epochs=50,
batch_size=256,
shuffle=True,
validation_data=(x_test, x_test))
训练完成后,我们可以构建用于降维的编码器模型:
# 构建编码器模型
encoder = Model(input_img, encoded)
# 使用编码器对测试集进行降维
encoded_imgs = encoder.predict(x_test)
# 使用自编码器对测试集进行重建
decoded_imgs = autoencoder.predict(x_test)
接下来,我们绘制多个图形来分析降维后的数据:
原始图像与重建图像的对比: 使用t-SNE算法对降维后的数据进行二维可视化:
# 绘制原始图像与重建图像的对比
n = 10 # 要显示的图像数量
plt.figure(figsize=(20, 4))
for i in range(n):
# 显示原始图像
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# 显示重建图像
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
# 使用t-SNE算法对降维后的数据进行二维可视化
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, random_state=0)
encoded_imgs_2d = tsne.fit_transform(encoded_imgs)
plt.figure(figsize=(10, 10))
plt.scatter(encoded_imgs_2d[:, 0], encoded_imgs_2d[:, 1], c=_[:10000], cmap='tab10')
plt.colorbar()
plt.show()
通过自编码器对MNIST数据集进行降维,并展示了原始图像与重建图像的对比,以及降维后数据的二维可视化。
9. 局部线性嵌入 (LLE)
LLE 保留局部邻域的线性关系,通过优化目标函数将数据嵌入到低维空间,适用于非线性降维。
原理
保持每个数据点及其邻居的线性重构权重在降维后的空间中不变。
核心公式
计算每个数据点及其邻居之间的线性重构权重
在低维空间中保持重构权重不变
计算线性重构权重 :
对每个数据点 ,在其邻居 上找到最优的重构权重 。
保持重构权重在低维空间中不变:
通过求解上述优化问题,找到低维空间中的位置 。
经典案例
使用局部线性嵌入 (Locally Linear Embedding, LLE) 进行降维的一个示例
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import make_swiss_roll
from sklearn.manifold import LocallyLinearEmbedding
# 生成瑞士卷数据
n_samples = 1500
X, color = make_swiss_roll(n_samples)
# 进行局部线性嵌入 (LLE)
n_neighbors = 12
n_components = 2
lle = LocallyLinearEmbedding(n_neighbors=n_neighbors, n_components=n_components, method='standard')
X_r = lle.fit_transform(X)
# 绘制原始瑞士卷数据
fig = plt.figure(figsize=(15, 7))
ax = fig.add_subplot(121, projection='3d')
ax.scatter(X[:, 0], X[:, 1], X[:, 2], c=color, cmap=plt.get_cmap('Spectral'))
ax.set_title("Original Swiss Roll Data")
ax.set_xlabel("X axis")
ax.set_ylabel("Y axis")
ax.set_zlabel("Z axis")
# 绘制降维后的数据
ax = fig.add_subplot(122)
sc = ax.scatter(X_r[:, 0], X_r[:, 1], c=color, cmap=plt.get_cmap('Spectral'))
ax.set_title("2D LLE Projection")
ax.set_xlabel("Component 1")
ax.set_ylabel("Component 2")
plt.colorbar(sc)
plt.show()
# 进一步分析和可视化
# 分析降维后的数据密度分布
sns.set(style="whitegrid")
plt.figure(figsize=(12, 6))
# KDE plot of the first component
plt.subplot(1, 2, 1)
sns.kdeplot(X_r[:, 0], shade=True, color="r")
plt.title("Density plot of Component 1")
# KDE plot of the second component
plt.subplot(1, 2, 2)
sns.kdeplot(X_r[:, 1], shade=True, color="b")
plt.title("Density plot of Component 2")
plt.show()
# Pairplot to see the relationships between the components and the original features
import pandas as pd
df = pd.DataFrame(X_r, columns=["Component 1", "Component 2"])
df["Color"] = color
sns.pairplot(df, vars=["Component 1", "Component 2"], hue="Color", palette="Spectral")
plt.show()
在这个示例中,我们使用了瑞士卷数据集,并对其应用局部线性嵌入 (LLE) 进行降维。
10. 奇异值分解 (SVD)
SVD 是一种矩阵分解技术,将原始数据矩阵分解为三个矩阵的乘积,从而实现降维。它广泛应用于推荐系统和文本分析中,如矩阵填充和潜在语义分析。
原理
SVD 将一个矩阵 分解为三个矩阵的乘积:左奇异矩阵 ,奇异值矩阵 和右奇异矩阵 。其中,奇异值矩阵的对角线元素表示原矩阵的特征信息,可以通过保留前 个奇异值实现降维。
核心公式
SVD 分解:
其中, 是 的正交矩阵, 是 的对角矩阵, 是 的正交矩阵。
降维:保留前 个奇异值和对应的奇异向量,降维后的数据表示为:
推导步骤
步骤 1:SVD 分解的计算
对原始矩阵 进行奇异值分解:
其中, 和 分别是 和 的特征向量矩阵, 是奇异值构成的对角矩阵。
步骤 2:奇异值的排序与选择
对奇异值进行排序,选择前 个奇异值 及其对应的奇异向量,保留这些奇异值对应的特征向量。
步骤 3:数据投影
通过选择前 个奇异值和对应的特征向量,将原始数据投影到低维空间:
步骤 4:数据重构
在需要的情况下,可以通过保留的 个奇异值和对应的奇异向量,重构近似的原始数据。
经典案例
这个案例通过将一个随机生成的矩阵进行 SVD 分解,然后利用不同数量的奇异值重构矩阵,最后通过绘制原始矩阵和不同重构矩阵的热力图来展示数据的降维效果。
import numpy as np
import matplotlib.pyplot as plt
# 设置随机种子以保证结果可复现
np.random.seed(42)
# 生成一个随机矩阵(比如:50x50)
original_matrix = np.random.rand(50, 50)
# 对矩阵执行 SVD 分解
U, S, VT = np.linalg.svd(original_matrix, full_matrices=False)
# 定义重构矩阵的奇异值数量
k_values = [5, 10, 20, 50]
# 创建图像
fig, axes = plt.subplots(1, len(k_values) + 1, figsize=(20, 5))
# 显示原始矩阵
axes[0].imshow(original_matrix, cmap='viridis')
axes[0].set_title("Original Matrix", fontsize=14)
axes[0].axis('off')
# 根据不同的奇异值数量重构矩阵并绘制热力图
for i, k in enumerate(k_values):
# 使用前 k 个奇异值重构矩阵
S_k = np.zeros((k, k))
np.fill_diagonal(S_k, S[:k])
# 重构矩阵
reconstructed_matrix = U[:, :k] @ S_k @ VT[:k, :]
# 显示重构的矩阵
axes[i + 1].imshow(reconstructed_matrix, cmap='viridis')
axes[i + 1].set_title(f"Reconstructed Matrix (k={k})", fontsize=14)
axes[i + 1].axis('off')
# 调整布局
plt.tight_layout()
plt.show()
1. 矩阵生成: 首先,生成一个50x50的随机矩阵,作为要降维的原始数据。
2. 奇异值分解: 通过 np.linalg.svd
对矩阵进行SVD分解,得到U、S和VT三个矩阵。
3. 重构矩阵: 使用不同数量的奇异值(5、10、20、50)来重构矩阵。较少的奇异值意味着更多的信息丢失,因此重构后的矩阵会与原始矩阵有较大的差异。
最终的图像包含5个子图:第一个子图是原始矩阵的热力图,后四个子图分别是使用5、10、20、50个奇异值重构后的矩阵热力图。通过这些热力图,可以直观地看到降维(减少奇异值数量)对数据的影响。
最后