查看全课大纲(第 26 / 35 节)
- 1.特征工程简介
- 2.特征工程的处理流程
- 3.数据与特征概述
- 4.数据统计、相似度与数据探索
- 5.数据探索工具 Pandas_profiling
- 6.工具应用:《泰坦尼克》分析报告
- 7.构造一个微型链家租房数据集用于演示
- 8.数据预处理综述
- 9.缺失值处理实操
- 10.异常值处理实操
- 11.连续值特征构建
- 12.类别型数据特征构建
- 13.特征幅度缩放
- 14.特征离散化
- 15.特征编码:让类别数据参与建模
- 16.特征变换
- 17.时间特征
- 18.特征衍生
- 19.自动化特征工具
- 20.特征选择综述
- 21.特征选择方法
- 22.过滤式特征选择方法实操
- 23.包裹式特征选择方法
- 24.嵌入式特征选择
- 25.混序法与混合法特征选择
- 26.特征降维综述
- 27.案例实操:特征降维
- 28.特征监控概述与指标
- 29.Iris 数据集特征漂移检测案例
- 30.特征监控实战:用分布对比检测数据漂移
- 31.实时特征监控
- 32.电商销量预测项目背景介绍
- 33.数据预处理和特征工程(一)
- 34.数据预处理和特征工程(二)
- 35.数据建模
特征降维综述
约 10 分钟
小象实战讲义 · 特征工程精讲与实践
大家好,欢迎来到特征工程课程。在完成了特征选择之后,我们即将进入另一个关键的预处理环节——特征转换与降维。本节我们将系统性地介绍数据降维的定义、原因、与特征选择的区别,以及主流的线性和非线性降维方法,为你后续的模型构建打下坚实基础。
💡 核心导读
- 降维是什么:学习一个映射函数,将高维数据点映射到低维空间,同时尽可能保留原始数据的信息。
- 为何要降维:解决多重共线性、高维稀疏性、模型过拟合等问题,并常用于数据可视化与压缩。
- 降维与特征选择的区别:特征选择是剔除无关特征,降维是通过映射生成新的低维特征。
- 主流降维方法:线性方法如PCA(主成分分析)、LDA(线性判别分析);非线性方法如t-SNE(t分布随机近邻嵌入)。
- 降维的工程位置:通常位于特征选择之后,模型训练之前。
一、数据降维的定义与阶段
在机器学习领域,数据降维(Dimensionality Reduction) 指的是采用某种映射方法,将原本存在于高维空间中的数据点,转换到低维度的空间中进行表示。
其本质是学习一个映射函数 。其中:
- 是原始数据点的表达,通常为向量形式。
- 是映射后的低维向量表达,其维度通常小于 的维度(当然,也存在升维的方法,但降维更常见)。
- 映射函数 可以是显式或隐式的,也可以是线性或非线性的。
想象一下,我们有一群分布在三维空间中的样本点,用不同颜色标识了它们的类别差异。通过某种映射,我们将它们投影到一个二维平面上。虽然维度降低了,但你会发现,在二维空间中,相同颜色的点依然聚集在一起,不同颜色的点则相对分散。这说明降维过程成功保留了原始数据中最重要的结构和信息。
三维空间样本点 (x, y, z) 映射 f 二维空间样本点 (u, v)
点A: (1, 2, 3) ---------> 点A': (0.5, 1.2)
点B: (2, 3, 4) ---------> 点B': (1.1, 1.8)
... (颜色:红) ... (依旧聚集)
点M: (8, 1, 2) ---------> 点M': (4.2, 0.3)
点N: (9, 2, 1) ---------> 点N': (4.5, 0.5)
... (颜色:蓝) ... (依旧聚集)在机器学习的标准流程中,降维通常处于特征选择之后、模型训练之前的位置。这意味着我们先通过特征选择筛选出有价值的特征子集,再在这个子集上进行降维操作,以进一步优化数据表达。
二、为何需要数据降维?
数据降维并非必经步骤,但在许多场景下它能显著提升模型性能与可解释性,主要原因如下:
- 缓解多重共线性:当特征属性之间存在较强的关联关系时,会导致模型解的空间不稳定,削弱模型的泛化能力。降维可以帮助我们消除或减弱这种共线性。
- 应对高维稀疏性:在高维空间中,数据样本往往会变得非常稀疏,使得模型难以捕捉有效的统计规律,即所谓的“维数灾难”。
- 简化模型,防止过拟合:过多的特征变量可能会让模型过于复杂,更容易捕捉到噪声而非真实规律,从而导致过拟合。降维有助于模型聚焦于核心模式。
- 发现潜在关系:仅考虑单个特征对目标的影响,可能会忽略特征之间复杂的交互作用。降维有时能揭示这些潜在的结构关系。
- 数据可视化与压缩:将高维数据降至2维或3维,是人类可以直观理解的范围,便于探索性分析。同时,降维也是一种有效的数据压缩手段。
三、特征选择 vs. 数据降维
特征选择和降维都旨在减少数据维度,但它们的核心思想和结果有本质区别:
| 方面 | 特征选择 (Feature Selection) | 数据降维 (Dimensionality Reduction) |
|---|---|---|
| 核心操作 | 剔除与预测目标关系不大的原始特征。 | 通过映射/变换,将原始特征转换为新的低维特征。 |
| 结果特征 | 得到原始特征的一个子集。 | 得到一组全新的、通常无法直接解释的合成特征(如主成分)。 |
| 主要目标 | 提升模型性能、增强可解释性、减少计算开销。 | 保留大部分信息以降低维度、用于可视化、消除共线性、发现数据结构。 |
| 代表方法 | 过滤法、包裹法、嵌入法。 | PCA、LDA、t-SNE。 |
简而言之,特征选择是“做减法”,从原有特征中挑选一部分;而降维是“做变换”,用一套新特征来代表旧特征。
四、数据线性降维方法
4.1 主成分分析 (PCA)
主成分分析(Principal Component Analysis, PCA) 是目前应用最广泛的线性降维方法。它的目标是:在高维数据中找到方差最大的方向(即数据分布最分散的方向),并将原始的 维特征映射到 () 维上。这 维全新的正交特征被称为主成分。
PCA的核心步骤(约定 为 行 列:每行是一个样本、每列是一个特征):
- 将原始数据组成 行 列的矩阵 ( 个样本, 个特征)。
- 对 的每一列(每个特征)进行零均值化,即减去该特征的均值。
- 计算零均值化后数据的协方差矩阵()。
- 计算协方差矩阵的特征值和对应的特征向量(每个特征向量为 维)。
- 将特征值按从大到小排序,并整理对应的特征向量。
- 取前 个最大特征值对应的特征向量组成变换矩阵 ()。
- 通过 计算得到降维到 维后的数据 (,每行是一个样本的主成分得分)。
第一主成分是方差最大的方向,第二主成分是在与第一主成分正交的方向上方差最大的方向,依此类推。
下面我们用一个小例子来演示PCA的用法。注意,每个代码块都是完全自包含的。
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 1. 构造一个简单的微型数据集(4个样本,3个特征)
# 特征间存在一定的相关性,以模拟真实情况
data = np.array([
[2.0, 3.0, 5.0],
[4.0, 1.0, 4.0],
[6.0, 5.0, 11.0],
[8.0, 7.0, 15.0]
])
df = pd.DataFrame(data, columns=['Feature1', 'Feature2', 'Feature3'])
print("原始数据:")
print(df)
print("\n原始数据形状:", data.shape)
# 2. 标准化数据(PCA前通常需要,尤其是特征量纲不同时)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(data)
print("\n标准化后的数据:")
print(X_scaled)
# 3. 应用PCA,指定降维到2维
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
print("\nPCA降维后的数据(2个主成分):")
print(X_pca)
print("降维后数据形状:", X_pca.shape)
# 4. 查看主成分的方差解释比例
print("\n各主成分的方差解释比例:", pca.explained_variance_ratio_)
print("累计方差解释比例:", np.cumsum(pca.explained_variance_ratio_))原始数据:
Feature1 Feature2 Feature3
0 2.0 3.0 5.0
1 4.0 1.0 4.0
2 6.0 5.0 11.0
3 8.0 7.0 15.0
原始数据形状: (4, 3)
标准化后的数据:
[[-1.34164079 -0.4472136 -0.83462233]
[-0.4472136 -1.34164079 -1.05718828]
[ 0.4472136 0.4472136 0.5007734 ]
[ 1.34164079 1.34164079 1.39103721]]
PCA降维后的数据(2个主成分):
[[-1.50782856 -0.65040239]
[-1.65100914 0.62593412]
[ 0.80632675 0.00312653]
[ 2.35251095 0.02134173]]
降维后数据形状: (4, 2)
各主成分的方差解释比例: [0.93198739 0.06794017]
累计方差解释比例: [0.93198739 0.99992756]从输出可以看到,原始3维数据被降到了2维。第一个主成分解释了约93.2%的方差,第二个主成分解释了约6.8%,两个主成分累计保留了约99.99%的方差——被舍弃的第三主成分仅贡献约0.007%,几乎可以忽略。这说明这份数据的主要变化集中在前两个方向上。在实际应用中,我们可以通过设定 n_components 为一个0到1之间的浮点数(如0.95),来保留指定比例的方差。
4.2 线性判别分析 (LDA)
线性判别分析(Linear Discriminant Analysis, LDA) 也可以用于降维,但其出发点和PCA不同。LDA是一种监督学习的降维技术,其核心思想是:将数据投影到一条直线上,使得同类样本的投影点尽可能接近(类内距离最小化),而不同类样本的投影点尽可能远离(类间距离最大化)。它寻找的是最有利于分类的特征子空间。
LDA的核心步骤:
- 计算每个类别的类内散度矩阵 。
- 计算类间散度矩阵 。
- 计算矩阵 。
- 计算 的特征值和特征向量。
- 选取前 个最大特征值对应的特征向量,构成变换矩阵 。
- 对样本 ,通过 得到降维后的新样本。
由于LDA需要类别标签,它通常用于分类任务的特征降维。下面我们用一个简单的二分类例子来演示。
import numpy as np
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
# 1. 构造微型数据集:3个特征,2个类别
X = np.array([
[1, 2, 3],
[2, 3, 4],
[3, 4, 5], # 类别0的样本
[7, 8, 9],
[8, 9, 10],
[9, 10, 11] # 类别1的样本
])
y = np.array([0, 0, 0, 1, 1, 1]) # 标签
print("原始数据X形状:", X.shape)
print("标签y:", y)
# 2. 应用LDA,目标降维到1维(对于2分类,最多降到 min(特征数, 类别数-1)=1 维)
lda = LinearDiscriminantAnalysis(n_components=1)
X_lda = lda.fit_transform(X, y)
print("\nLDA降维后的数据(1维):")
print(X_lda.flatten())
print("降维后数据形状:", X_lda.shape)
# 3. 查看投影方向(系数)
print("\nLDA投影方向(系数):", lda.coef_)原始数据X形状: (6, 3)
标签y: [0 0 0 1 1 1]
LDA降维后的数据(1维):
[-4.89897949 -3.67423461 -2.44948974 2.44948974 3.67423461 4.89897949]
降维后数据形状: (6, 1)
LDA投影方向(系数): [[3. 3. 3.]]可以看到,LDA成功地将3维数据降到了1维。降维后的数据中,前三个样本(类别0)的值较小,后三个样本(类别1)的值较大,实现了良好的类间分离。
五、数据非线性降维方法
5.1 t-SNE (t-Distributed Stochastic Neighbor Embedding)
对于具有复杂非线性结构的数据,线性降维方法可能无法有效保留其局部结构。这时就需要非线性降维方法,其中 t-SNE 是最著名和常用的方法之一。
t-SNE 的主要优势在于其保持局部结构的能力。这意味着在高维空间中距离相近的点,投影到低维空间后,它们的距离仍然很近。因此,t-SNE 特别适用于高维数据的可视化,例如将词向量、图像特征降维到2D或3D进行观察。
t-SNE的核心思想:
- 在高维空间构建概率分布:t-SNE首先计算高维空间中数据点两两之间的相似度(通常用高斯分布表示),并转换为一个概率分布。相似度高的点,被选为“邻居”的概率高。
- 在低维空间构建概率分布:在低维空间(如2维)中,同样为数据点构建一个概率分布,但这里使用t分布(比高斯分布有更重的尾部)来计算相似度。t分布对异常点更不敏感,能更好地保持局部结构。
- 最小化分布差异:t-SNE的目标是,通过梯度下降等方法,调整低维空间中点的位置,使得高维空间的概率分布与低维空间的概率分布之间的KL散度最小化。
一个经典的例子是MNIST手写数字数据集。将每张28x28的图片(784维)通过神经网络提取特征后,再用t-SNE降维到2维进行可视化。结果会发现,数字“0”的点聚集在一片区域,“1”聚集在另一片区域,不同数字的集群清晰可辨。
由于t-SNE计算复杂度较高,且结果具有随机性(每次运行可能略有不同),它主要用于探索性数据分析而非模型训练前的固定转换。在scikit-learn中,我们可以方便地使用TSNE类。
import numpy as np
from sklearn.manifold import TSNE
# 1. 构造一个模拟的高维数据(10个样本,5个特征)
np.random.seed(42) # 固定随机种子以保证示例的可复现性(仅用于shuffle,不用于生成印刷数据)
X_high_dim = np.array([
[1, 0, 0, 0, 0],
[0.9, 0.1, 0, 0, 0],
[0, 1, 0, 0, 0], # 前三个样本比较相似(组A)
[0, 0, 0, 1, 0],
[0, 0, 0, 0.9, 0.1],
[0, 0, 0, 0, 1], # 中间三个样本比较相似(组B)
[10, 10, 0, 0, 0],
[10, 9, 0, 0, 0], # 最后两个样本自成一类,且远离前两组(组C)
])
print("模拟高维数据形状:", X_high_dim.shape)
# 2. 应用t-SNE降维到2维
# 注意:perplexity(困惑度)是一个重要参数,通常取值在5到50之间,这里用小样本设置小值
tsne = TSNE(n_components=2, perplexity=2, random_state=42)
X_low_dim = tsne.fit_transform(X_high_dim)
print("\nt-SNE降维后的2维数据:")
print(X_low_dim)模拟高维数据形状: (8, 5)
t-SNE降维后的2维数据:
[[139.82472 -6.936391]
[131.44608 12.22962 ]
[122.98106 41.45131 ]
[ 58.059982 -32.04932 ]
[ 56.22224 -11.219399]
[ 49.909084 18.453953]
[-76.95439 67.100334]
[-67.656815 84.94002 ]]观察输出结果(数值可能因版本略有浮动):
- 样本0、1、2(组A)的坐标落在同一片区域,彼此之间的距离远小于它们与其他组的距离。
- 样本3、4、5(组B)聚在另一片区域,与组A之间有明显的间隔。
- 样本6、7(组C)自成一簇,与A、B两组的距离都非常远。 这体现了t-SNE“保持局部结构”的特性:高维空间中相似的样本,在低维空间中依然邻近。
📝 动手练一练
- 概念辨析:假设你有一个包含100个特征的数据集,并且你知道其中很多特征是高度相关的。你的目标是构建一个泛化能力强的线性回归模型。请问,在这种情况下,优先使用特征选择还是PCA进行降维?为什么?
👉 点击查看参考答案
参考答案: 在这种情况下,优先使用特征选择可能更合适,尤其是如果可解释性对业务很重要的话。
- 原因1:消除共线性。特征选择中的嵌入法(如Lasso回归)或一些过滤法(如基于相关性的过滤)可以直接剔除高度相关的特征,解决多重共线性问题。
- 原因2:保持可解释性。特征选择得到的是原始特征的子集,业务上可以理解每个特征的含义。而PCA生成的主成分是原始特征的线性组合,难以直接解释,不利于模型上线后与业务方沟通。
- 原因3:模型简单性。线性回归模型本身比较简单,使用原始特征子集可能已经足够。PCA虽然能消除共线性,但引入了变换,增加了流程复杂性。 当然,如果特征数量极大(远超样本数),或者可解释性不是首要考虑,PCA也是一个非常强大的工具。最终选择需要通过交叉验证来比较两种方案的实际效果。
- 动手实践:在上面PCA的代码示例中,我们指定了
n_components=2。请修改代码,使用n_components=0.95参数(即保留95%的方差),重新运行PCA,并观察降维后的特征数量是多少。
👉 点击查看参考答案
参考答案: 修改PCA初始化部分即可。以下是修改后的关键代码块(自包含):
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 使用与之前相同的微型数据
data = np.array([
[2.0, 3.0, 5.0],
[4.0, 1.0, 4.0],
[6.0, 5.0, 11.0],
[8.0, 7.0, 15.0]
])
scaler = StandardScaler()
X_scaled = scaler.fit_transform(data)
# 修改PCA参数,保留95%的方差
pca_95 = PCA(n_components=0.95)
X_pca_95 = pca_95.fit_transform(X_scaled)
print("当n_components=0.95时,降维后数据形状:", X_pca_95.shape)
print("保留的方差比例:", pca_95.explained_variance_ratio_)
print("累计方差比例:", np.cumsum(pca_95.explained_variance_ratio_))运行结果可能如下(因浮点数计算略有差异):
当n_components=0.95时,降维后数据形状: (4, 2)
保留的方差比例: [0.93198739 0.06794017]
累计方差比例: [0.93198739 0.99992756]在这个特定的微型数据集上,第一个主成分已经解释了约93.2%的方差,但未达到95%。由于n_components=0.95要求保留至少95%的方差,而第二个主成分贡献约6.8%,加上第一个的93.2%则累计约99.99%、超过95%,因此算法会保留2个主成分。但请注意,这是一个刻意构造的极小样本示例;在真实数据集上,你会看到更典型的行为:保留多个主成分,直到累计方差超过阈值。
本章小结
本节我们系统学习了特征降维的核心理念与方法。降维通过映射函数将高维数据压缩到低维空间,是解决多重共线性、高维稀疏性、过拟合以及进行数据可视化的有力工具。它与特征选择“剔除”的思路不同,重在“变换”。
我们重点掌握了两种线性降维方法:
- PCA(无监督):寻找方差最大的方向,最大化保留全局信息。
- LDA(有监督):寻找最利于分类的投影方向,最大化类间距离,最小化类内距离。 以及一种非线性降维方法:
- t-SNE:擅长保持局部结构,是高维数据可视化的首选工具。
理解这些方法的原理、适用场景及其在scikit-learn中的实现,是你在实际项目中灵活运用降维技术的关键。
📋 行动清单
- 在个人项目或练习数据集中,尝试对特征选择后的数据应用PCA,观察累计方差解释曲线,并选择合适的主成分数量。
- 找一个分类数据集(如Iris),分别用PCA和LDA进行降维(至2维),并思考两者结果差异的原因。
- 使用t-SNE对一个中等维度的数据集(如手写数字特征)进行降维可视化,直观感受其保持局部结构的能力。
—— 小象教研组