特征降维-特征组合
特征组合:从n个特征中组合出m个特征,如pca等
SVD奇异值分解,是很多降维算法的基础,SVD 分解后原始矩阵是等价的,但通过筛选排序靠前的奇异值和对应的向量,我们可以实现降维,保留数据的主要信息,同时减少计算量和噪声影响
基础:求矩阵特征值特征向量、矩阵对角化、特征值分解、正交矩阵、对称矩阵、矩阵分解
结合vs看视频
超详细!彻底搞懂矩阵奇异值分解(SVD)本质+计算+应用!_哔哩哔哩_bilibili
正交矩阵表示旋转和反射变换,U、V都是正交阵
SVD的奇异值通过A^TA或AA^T的特征值取平方根得到,特征向量与奇异向量相关。其中A^TA和AA^T都是对称阵,所以可以特征值分解,进而求奇异值和奇异向量
输入:矩阵A
输出:原始矩阵A被分解为U,Sigma和V^T;通过USV^T的矩阵乘法,可以完全重构原始矩阵A,几乎没有信息损失
例1 矩阵降维
import numpy as np# 使用np.array()创建一个 5 行 3 列的二维数组(矩阵)
A = np.array([[1, 2, 3],[4, 5, 6],[7, 8, 9],[10, 11, 12],[13, 14, 15]])
print("原始矩阵 A:")
print(A)# 进行 SVD 分解
# 参数A是要分解的矩阵,参数full_matrices=False表示返回的U和Vt是非满秩矩阵,节省计算资源适合降维场景,只保留非0奇异值对应的向量
U, sigma, Vt = np.linalg.svd(A, full_matrices=False)
print("\n奇异值 sigma:")
print(sigma)# 保留前 k=1 个奇异值进行降维
k = 1
U_k = U[:, :k] # U 是左奇异矩阵,保留所有行,取 U 的前 k 列
sigma_k = sigma[:k] # 奇异值数组,取前 k 个奇异值
Vt_k = Vt[:k, :] # 右奇异矩阵的转置,保留所有列,取 Vt 的前 k 行# 近似重构矩阵 A,常用于信号or图像筛除噪声
# np.diag(sigma_k) 将奇异值数组转换为对角矩阵
A_approx = U_k @ np.diag(sigma_k) @ Vt_k
print("\n保留前", k, "个奇异值后的近似矩阵 A_approx:")
print(A_approx)# 计算近似误差
# np.linalg.norm() 计算矩阵的范数
# 参数'fro'表示计算 Frobenius 范数(矩阵元素平方和的平方根)
# 计算相对误差:近似矩阵与原始矩阵的差的范数除以原始矩阵的范数
error = np.linalg.norm(A - A_approx, 'fro') / np.linalg.norm(A, 'fro')
print("\n近似误差 (Frobenius 范数相对误差):", error)
定义A矩阵-计算完整分解矩阵-取前k个奇异值及其对应向量-构建新分解矩阵-计算误差
补:NumPy 切片的完整语法是【start:stop:step】
(起始索引、结束索引、步长),当某个参数省略时,会使用默认值:,步长可不写
1. [:, :k]
的原始形式
[:, :k]
用于二维数组,针对 “行” 和 “列” 分别切片:
- 第一个
:
表示对 “行” 的切片,完整形式是0:end:1
(end
为数组的行数),即 “取所有行”; - 第二个
:k
表示对 “列” 的切片,完整形式是0:k:1
,即 “取从第 0 列到第 k-1 列(共 k 列)”。
因此,[:, :k]
的原始完整写法是:
[0:行数:1, 0:k:1]
举例:对于形状为 (5, 3)
的矩阵 U
(5 行 3 列),U[:, :1]
等价于 U[0:5:1, 0:1:1]
,表示 “取所有 5 行,取第 0 列(共 1 列)”。
2. [:k, :]
的原始形式
[:k, :]
同样用于二维数组:
- 第一个
:k
表示对 “行” 的切片,完整形式是0:k:1
,即 “取从第 0 行到第 k-1 行(共 k 行)”; - 第二个
:
表示对 “列” 的切片,完整形式是0:end:1
(end
为数组的列数),即 “取所有列”。
因此,[:k, :]
的原始完整写法是:
[0:k:1, 0:列数:1]
举例:对于形状为 (3, 3)
的矩阵 Vt
(3 行 3 列),Vt[:1, :]
等价于 Vt[0:1:1, 0:3:1]
,表示 “取第 0 行(共 1 行),取所有 3 列”。
原始矩阵 A:
[[ 1 2 3][ 4 5 6][ 7 8 9][10 11 12][13 14 15]]奇异值 sigma:
[3.51826483e+01 1.47690770e+00 9.86023090e-16]保留前 1 个奇异值后的近似矩阵 A_approx:
[[ 1.85152908 2.05208851 2.25264793][ 4.5411984 5.03310541 5.52501242][ 7.23086771 8.01412231 8.7973769 ][ 9.92053702 10.99513921 12.06974139][12.61020633 13.9761561 15.34210588]]近似误差 (Frobenius 范数相对误差): 0.04194136031471535
#在保留了k=1个奇异值后,近似误差仅为 4.19%,表明这种降维方式非常有效,丢失的信息很少
例2 训练集和测试集的 SVD 降维
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score# 设置随机种子以便结果可重复
np.random.seed(42)# 模拟数据:1000 个样本,50 个特征
n_samples = 1000
n_features = 50
X = np.random.randn(n_samples, n_features) * 10 # 随机生成特征数据
y = (X[:, 0] + X[:, 1] > 0).astype(int) # 模拟二分类标签# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"训练集形状: {X_train.shape}")
print(f"测试集形状: {X_test.shape}")# 对训练集进行 SVD 分解
U_train, sigma_train, Vt_train = np.linalg.svd(X_train, full_matrices=False)
print(f"Vt_train 矩阵形状: {Vt_train.shape}")# 选择保留的奇异值数量 k
k = 10
Vt_k = Vt_train[:k, :] # 保留前 k 行,形状为 (k, 50)
print(f"保留 k={k} 后的 Vt_k 矩阵形状: {Vt_k.shape}")# 降维训练集:X_train_reduced = X_train @ Vt_k.T
X_train_reduced = X_train @ Vt_k.T
print(f"降维后训练集形状: {X_train_reduced.shape}")# 使用相同的 Vt_k 对测试集进行降维:X_test_reduced = X_test @ Vt_k.T
X_test_reduced = X_test @ Vt_k.T
print(f"降维后测试集形状: {X_test_reduced.shape}")# 训练模型(以逻辑回归为例)
model = LogisticRegression(random_state=42)
model.fit(X_train_reduced, y_train)# 预测并评估
y_pred = model.predict(X_test_reduced)
accuracy = accuracy_score(y_test, y_pred)
print(f"测试集准确率: {accuracy}")# 计算训练集的近似误差(可选,仅用于评估降维效果)
X_train_approx = U_train[:, :k] @ np.diag(sigma_train[:k]) @ Vt_k
error = np.linalg.norm(X_train - X_train_approx, 'fro') / np.linalg.norm(X_train, 'fro')
print(f"训练集近似误差 (Frobenius 范数相对误差): {error}")
训练集形状: (800, 50)
测试集形状: (200, 50)
Vt_train 矩阵形状: (50, 50)
保留 k=10 后的 Vt_k 矩阵形状: (10, 50)
降维后训练集形状: (800, 10)
降维后测试集形状: (200, 10)
测试集准确率: 0.595
训练集近似误差 (Frobenius 范数相对误差): 0.852479929511559