特征工程是机器学习流程中的关键步骤,在此过程中,原始数据被转换为更具意义的特征,以增强模型对数据关系的理解能力。
特征工程通常涉及对现有数据应用转换,以生成或修改数据,这些转换后的数据在机器学习和数据科学的语境下用于训练模型,从而提高模型性能。
本文主要介绍处理数值变量特征工程,将探讨使用Python的Scikit-Learn库、Numpy等工具处理数值的高级特征工程技术,旨在提升机器学习模型的效能。
特征优化是提升机器学习模型质量的核心要素,尤其在分析复杂数据集时。有针对性地应用特征工程技术可带来以下优势:
- 揭示数据中的潜在模式:此技术能够发现初步观察中不易察觉的隐藏关系和结构。
- 优化变量表示:此过程将原始数据转换为更适合机器学习的格式。
- 应对数据分布和内在特性相关的挑战:此方法解决了诸如偏度、异常值和变量可扩展性等问题。
精确实施这些特征优化技术可显著提升机器学习模型的性能。
这些改进体现在模型性能的多个方面,从预测能力到可解释性。高质量特征使模型能够捕捉到数据中可能被忽视的细微差别和复杂模式。
特征优化还有助于增强模型的稳健性和泛化能力,这对于实际应用至关重要,同时降低了过拟合的风险。
接下来,我们将介绍一些实用的特征工程技术。
1、归一化
归一化(也称为缩放)可能是数据科学家学习的第一个数值特征工程技术。这种方法通过减去平均值并除以标准差来调整变量。
执行此转换后,结果变量将具有0均值和1的标准差及方差。
在机器学习中,特别是深度学习领域,将变量限制在特定范围内(如仅在0和1之间)有助于模型更快地收敛到最优解。这是一种学习型转换 - 我们使用训练数据来推导正确的均值和标准差值,然后在应用于新数据时使用这些值进行转换。
需要注意的是,这种转换不会改变分布,而是重新缩放了值。
我们将使用Sklearn的葡萄酒数据集进行分类任务。我们将比较使用和不使用混淆矩阵归一化的性能,使用Sklearn实现。
importmatplotlib.pyplotasplt
importseabornassns
fromsklearn.datasetsimportload_wine
fromsklearn.model_selectionimporttrain_test_split
fromsklearn.preprocessingimportStandardScaler
fromsklearn.neighborsimportKNeighborsClassifier
fromsklearn.metricsimportconfusion_matrix
importnumpyasnp
X, y=load_wine(return_X_y=True)
# 将数据划分为训练集和测试集
X_train, X_test, y_train, y_test=train_test_split(X, y, test_size=0.30, random_state=42)
# 定义训练模型并获取混淆矩阵的函数
defget_confusion_matrix(X_train, X_test, y_train, y_test):
model=KNeighborsClassifier(n_neighbors=5)
model.fit(X_train, y_train)
y_pred=model.predict(X_test)
returnconfusion_matrix(y_test, y_pred)
# 获取未归一化的混淆矩阵
cm_without_norm=get_confusion_matrix(X_train, X_test, y_train, y_test)
# 归一化数据
scaler=StandardScaler()
X_train_scaled=scaler.fit_transform(X_train)
X_test_scaled=scaler.transform(X_test)
# 获取归一化后的混淆矩阵
cm_with_norm=get_confusion_matrix(X_train_scaled, X_test_scaled, y_train, y_test)
# 创建两个并列的子图
fig, (ax1, ax2) =plt.subplots(1, 2, figsize=(20, 8))
# 定义绘制热图的函数
defplot_heatmap(ax, cm, title):
sns.heatmap(cm, annot=True, fmt='d', cmap='viridis', ax=ax, cbar=False)
ax.set_title(title, fontsize=16, pad=20)
ax.set_xlabel(''Predicted', fontsize=12, labelpad=10)
ax.set_ylabel('Actual', fontsize=12, labelpad=10)
# 绘制热图
plot_heatmap(ax1, cm_without_norm, 'Confusion Matrix\nWithout Normalization')
plot_heatmap(ax2, cm_with_norm, 'Confusion Matrix\nWith Normalization')
# 添加共用的颜色条
cbar_ax=fig.add_axes([0.92, 0.15, 0.02, 0.7])
sm=plt.cm.ScalarMappable(cmap='viridis', norm=plt.Normalize(vmin=0, vmax=np.max([cm_without_norm, cm_with_norm])))
fig.colorbar(sm, cax=cbar_ax)
# 调整布局并显示图表
plt.tight_layout(rect=[0, 0, 0.9, 1])
plt.show()
性能提升约为30% - 对某些算法而言,归一化的影响如此显著,以至于不正确地应用它可能导致数据科学家犯严重错误。
归一化还有一些变体。在Sklearn中,这些变体被称为
RobustScaler
和
MinMaxScaler
。
Sklearn示例中提供了一个更复杂的图表,展示了归一化和未归一化的
KNNClassifier
模型的分类边界对比
2、多项式特征
多项式特征是一种在线性模型中引入非线性的有效方法。Scikit-Learn的
PolynomialFeatures
类能够生成多项式特征和变量间的交互项。
常见的多项式特征包括:
- x ² (平方项)
- x ³ (立方项)
- x ⁴ (四次方项)
- 更高次项
对于具有多个特征的模型(x_1, x_2, …, x_n),还可以创建交互项:
- x_ 1 × x_ 2 (一阶交互项)
- x_ 1² × x_ 2 (二阶交互项)
- x_ 1 × x_ 2² (二阶交互项)
- 更高阶交互项
多项式特征的主要目标是使线性模型能够学习数据中的非线性关系,而无需改变底层算法。
多项式特征的主要优势在于显著增加了模型的灵活性,使线性模型能够捕捉数据中的非线性关系。具体表现为:
- 能够在特征空间中建模复杂的曲线和曲面
- 对本质上非线性的数据可能产生积极的性能贡献
- 模型在捕捉非线性关系的同时保持线性的基础结构。这允许使用熟悉的分析工具,并且比复杂的非线性模型更容易解释系数,尽管这种简单性随着多项式次数的增加而降低。
另一个关键优势是能够揭示变量之间隐藏的交互。在物理学或经济学等领域,关系通常是非线性的,这个特性尤为有价值。
但是多项式特征也存在一些缺点:
- 它迅速增加了数据集的维度,为每个输入特征创建额外的列
- 过度使用可能导致过拟合
- 由于需要处理更多的特征,计算资源需求增加
从实际角度来看,多项式特征的实现相对简单,这要归功于Python中的Sklearn库。下面我们将展示如何实现它。
PolynomialFeatures
是Scikit-learn中用于生成多项式特征的类,位于
sklearn.preprocessing
模块中。
该类将一维输入数组转换为包含所有多项式项(直至指定次数)的新数组。例如,如果原始特征是[a, b],次数为2,则结果特征将是[1, a, b, a², ab, b²]。
该类的主要参数如下:
- degree (int, 默认=2):多项式的次数。 确定生成的多项式项的最高次数。
- interaction_only (bool, 默认=False):如果为True,则仅生成交互项。不产生单个特征的幂。
- include_bias (bool, 默认=True):如果为True,包括一列1(偏置项)。在使用没有单独截距项的模型时有用。
- order (C或F, 默认=C):确定特征的输出顺序。C表示C风格顺序(最后的特征变化最快),'F'表示Fortran风格顺序。
以下是在Sklearn中实现该类的示例:
fromsklearn.preprocessingimportPolynomialFeatures
importnumpyasnp
X=np.array([[1, 2], [3, 4]])
poly=PolynomialFeatures(degree=2, include_bias=False)
X_poly=poly.fit_transform(X)
print(X_poly)
# 输出: [[1. 2. 1. 2. 4.]
# [3. 4. 9. 12. 16.]]
print(poly.get_feature_names(['x1', 'x2']))
# 输出: ['x1', 'x2', 'x1^2', 'x1 x2', 'x2^2']
这些生成的特征可以为机器学习模型提供额外的信息,潜在地提高其性能。
3、FunctionTransformer
FunctionTransformer
是Scikit-learn中的一个多功能工具,允许将自定义函数集成到数据转换过程中。它能够将任意函数应用于数据,作为预处理或特征工程管道的一部分。它将Python函数转换为与Scikit-learn API兼容的"转换器"对象(这里的Transformer不同于深度学习模型,而是指Sklearn中的转换器概念)。
FunctionTransformer
以Python函数为主要输入,创建一个转换器对象。当应用于数据时,这个对象执行指定的函数。它可以与其他转换器结合使用,或在Scikit-learn管道中使用。
一个具体的应用例子是将该对象应用于时间序列以提取三角函数特征。
importmatplotlib.pyplotasplt
fig, ax=plt.subplots(figsize=(12, 4))
average_week_demand=df.groupby(["weekday", "hour"])["count"].mean()
average_week_demand.plot(ax=ax)
_=ax.set(
title="Average hourly bike demand during the week",
xticks=[i*24foriinrange(7)],
xticklabels=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
xlabel="Time of the week"
)
FunctionTransformer
的典型应用包括:
- 创建复杂或领域特定的特征
- 对数据应用非标准数学运算
- 将现有的预处理逻辑集成到Scikit-learn管道中
FunctionTransformer
充当了自定义Python函数和Scikit-learn函数之间的桥梁,为数据预处理和特征工程提供了灵活性。
以下代码展示了如何应用函数来创建上述时间序列的三角函数转换:
importnumpyasnp
importpandasaspd
fromsklearn.preprocessingimportFunctionTransformer
importmatplotlib.pyplotasplt
defsin_transformer(period):
returnFunctionTransformer(lambdax: np.sin(x/period*2*np.pi))
defcos_transformer(period):
returnFunctionTransformer(lambdax: np.cos(x/period*2*np.pi))
hour_df=pd.DataFrame(
np.arange(26).reshape(-1, 1),
columns=["hour"],
)
hour_df["hour_sin"] =sin_transformer(24).fit_transform(hour_df)["hour"]
hour_df["hour_cos"] =cos_transformer(24).fit_transform(hour_df)["hour"]
hour_df.plot(x="hour")
_=plt.title("Trigonometric encoding of the 'hour' feature")
FunctionTransformer
在时间序列分析中有广泛的应用。
4、KBinsDiscretizer
KBinsDiscretizer
是Scikit-learn中的一个预处理类,设计用于将连续特征转换为离散分类特征。这个过程被称为离散化、量化或分箱。某些具有连续特征的数据集可能会从离散化中受益,因为它可以将具有连续属性的数据集转换为仅具有名义属性的数据集。
其主要目标是将连续变量的范围划分为特定数量的区间(或箱)。每个原始值都被替换为它所属的箱的标签。
该算法的工作原理如下:
- 分析连续特征的分布
- 基于这个分布创建预定义数量的箱
- 将每个原始值分配到适当的箱中
- 用箱标签或箱的独热编码替换原始值
关键参数:
n_bins:要创建的箱数。可以是整数或整数数组,用于每个特征的不同箱数。
encode:编码箱的方法(onehot、ordinal或onehot-dense)。
- onehot:使用独热编码对转换后的结果进行编码,并返回稀疏矩阵。被忽略的特征总是堆叠在右侧。
- onehot-dense:使用独热编码对转换后的结果进行编码,并返回"密集"数组(即非稀疏格式)。
- ordinal:返回编码为整数值的箱。
strategy:定义箱边界的策略(uniform、quantile或kmeans)。
- uniform:创建等宽的箱。
- quantile:为每个特征创建包含相同数量点的箱。
- kmeans:使用k-means聚类定义箱。
需要考虑的因素:
- 箱数和策略的选择会显著影响结果。
- 可能导致信息损失,尤其是在箱数很少的情况下(类似于使用很少的组绘制直方图时)。
- 对于对非正态分布或非线性关系敏感的算法特别有用。
我们将通过观察线性回归和决策树在学习连续模式与离散化模式时的性能来展示应用。创建一个随机但半线性数字的模拟数据集,将模型应用于连续数据,然后将相同的数据集应用于离散化特征。
importmatplotlib.pyplotasplt
importnumpyasnp
fromsklearn.linear_modelimportLinearRegression
fromsklearn.preprocessingimportKBinsDiscretizer
fromsklearn.treeimportDecisionTreeRegressor
# 设置随机数生成器以确保可重复性
rnd=np.random.RandomState(42)
# 创建数据集
X=rnd.uniform(-3, 3, size=100) # 在-3和3之间的100个点
y=np.sin(X) +rnd.normal(size=len(X)) /3 # 正弦函数加噪声
X=X.reshape(-1, 1) # 重塑为sklearn所需的正确格式
# 应用KBinsDiscretizer
discretizer=KBinsDiscretizer(n_bins=10, encode="onehot")
X_binned=discretizer.fit_transform(X)
# 准备可视化
fig, (ax1, ax2) =plt.subplots(ncols=2, sharey=True, figsize=(12, 5))
line=np.linspace(-3, 3, 1000).reshape(-1, 1) # 用于绘图的点
# 训练和绘制模型的函数
deftrain_and_plot(X_train, X_plot, ax, title):
# 线性回归
linear_reg=LinearRegression().fit(X_train, y)
ax.plot(line, linear_reg.predict(X_plot), linewidth=2, color="green", label="Linear Regression")
# 决策树
tree_reg=DecisionTreeRegressor(min_samples_split=3, random_state=0).fit(X_train, y)
ax.plot(line, tree_reg.predict(X_plot), linewidth=2, color="red", label="Decision Tree")
# 原始数据
ax.plot(X[:, 0], y, "o", c="k", alpha=0.5)
ax.legend(loc="best")
ax.set_xlabel("Input Feature")
ax.set_title(title)
# 绘制原始数据的图
train_and_plot(X, line, ax1, "Results Before Discretization")
ax1.set_ylabel("Regression Output")
# 绘制离散化数据的图
line_binned=discretizer.transform(line)
train_and_plot(X_binned, line_binned, ax2, "Results After Discretization")
plt.tight_layout()
plt.show()
线性回归在离散化后显著改善,更好地捕捉了非线性。决策树显示的变化较小,因为它本身就能处理非线性。这个例子说明了离散化如何帮助线性模型捕捉非线性关系,在某些情况下可能会提高性能。
5、对数变换
对数变换的主要优势在于其压缩值范围的能力,这对于具有高可变性或异常值的数据特别有用。
- 范围压缩:对数变换减少了最大值之间的距离,同时保持较小值相对不变。有助于规范化偏斜分布,使右尾分布更对称,更接近正态分布。
- 线性化:它可以将非线性关系线性化。它将指数关系转换为线性关系,简化了分析并提高了假设变量之间线性关系的模型的性能。
- 处理异常值:该变换有效地管理极端数据,允许处理异常值而无需删除,从而保留潜在的重要信息。
- 数学定义:最常见的对数变换使用自然对数(以e为底),定义为 y =ln(x),其中 x 是原始值,y 是变换后的值。请注意,这种变换仅对正值的 x 定义,如果存在零或负值,可能需要添加常数。
- 特征缩放:它可以用作特征缩放技术,补充或替代标准化或最小-最大规范化等方法。可以提高线性回归等模型的性能,这些模型受益于具有更对称分布的特征。
在机器学习中,当想要规范化一个不是自然分布的分布时,通常会使用对数变换。
例如,一个众所周知的不正态分布的变量是年收入 —— 你经常想要对这个变量建模以提供价值预测,但使用这种分布工作并不方便,特别是如果你使用不能正确建模非线性数据的算法。
通过对数变换,使用numpy,可以趋向正态分布,使变量更容易预测。
importnumpyasnp
importpandasaspd
importmatplotlib.pyplotasplt
# 创建一个具有正偏度值的示例数据集
np.random.seed(42)
data= {
'Income': np.random.exponential(scale=50000, size=1000) # 指数分布以模拟偏度
}
df=pd.DataFrame(data)
# 创建一个具有两个并排子图的图形
fig, axes=plt.subplots(1, 2, figsize=(15, 5))
# 原始分布的图
axes[0].hist(df['Income'], bins=50, color='blue', alpha=0.7)
axes[0].set_title('Original Income Distribution')
axes[0].set_xlabel('Income')
axes[0].set_ylabel('Frequency')
# 应用对数变换
df['Log_Income'] =np.log1p(df['Income']) # log1p相当于log(x + 1)
# 变换后分布的图
axes[1].hist(df['Log_Income'], bins=50, color='green', alpha=0.7)
axes[1].set_title('Log-transformed Income Distribution')
axes[1].set_xlabel('Log_Income')
axes[1].set_ylabel('Frequency')
# 显示图形
plt.tight_layout()
plt.show()
6、PowerTransformer
PowerTransformer
是Sklearn
preprocessing
模块中的一个类,包含用于使数据更接近高斯分布的逻辑。这对于建模与异方差(即非恒定方差)相关的问题或其他需要正态性的情况很有用。
,
PowerTransformer
支持Box-Cox和Yeo-Johnson变换。使用最大似然(对数似然)估计最优参数以稳定方差并最小化偏度。
Box-Cox要求输入数据严格为正,而Yeo-Johnson支持正负数据。
在机器学习的背景下,这些变换解决了几个常见挑战:
- 数据规范化: 许多机器学习算法,如线性回归、神经网络和一些聚类方法,假设数据遵循正态分布。PowerTransformer可以将偏斜或重尾分布转换为更接近高斯分布的形状,这可以提高这些模型的性能。
- 方差稳定化: 在真实数据集中,特征的方差通常随其幅度变化,这种现象称为异方差。这可能会影响许多算法的有效性。PowerTransformer有助于稳定方差,使其在特征值的不同范围内更加一致。
- 关系线性化: 一些算法,如线性回归,假设变量之间存在线性关系。PowerTransformer可以将非线性关系线性化,扩大这些模型在更复杂数据集上的适用性。
Box-Cox变换
Box-Cox变换是一系列幂变换,可以稳定方差并使数据更接近正态分布。它在数学上定义为:
- y(λ) = (y λ — 1) / λ 如果 y ≠ 0
- y(λ) = log(y) 如果 y = 0
其中:
- x 是原始值,
- y 是变换后的值,
- λ 是变换参数
Box-Cox变换应用于正数据,并要求从数据中估计参数λ,以找到使数据正态化的最佳变换。
PowerTransformer
的行为类似于Sklearn估计器,支持
.fit()
和
.transform()
方法。
Yeo-Johnson变换基于Box-Cox变换,但允许负值。本文不会详细介绍Yeo-Johnson变换。
如前所述,Yeo-Johnson变换基于Box-Cox变换,但lambda可以取的值可能会改变。这使得这些变换本质上不同,因为它们可能给出不同的结果。
在Python中,只需将其中一种变换方法作为字符串传递给
PowerTransformer
对象
importnumpyasnp
importmatplotlib.pyplotasplt
fromsklearn.preprocessingimportPowerTransformer
# 生成包含正值和负值的数据
np.random.seed(42)
data_positive=np.random.exponential(scale=2, size=1000) # 正值
data_negative=-np.random.exponential(scale=0.5, size=200) # 负值
data=np.concatenate([data_positive, data_negative]) # 合并正值和负值数据
# 创建两个PowerTransformer实例:一个用于Yeo-Johnson,一个用于Box-Cox进行比较
pt_yj=PowerTransformer(method='yeo-johnson', standardize=False) # Yeo-Johnson变换
pt_bc=PowerTransformer(method='box-cox', standardize=False) # Box-Cox变换
# 应用变换
data_yj=pt_yj.fit_transform(data.reshape(-1, 1)) # 应用Yeo-Johnson变换
# Box-Cox需要正数据,所以我们添加一个偏移量使所有值为正
data_offset=data-np.min(data) +1e-6 # 偏移量以确保所有值为正
data_bc=pt_bc.fit_transform(data_offset.reshape(-1, 1)) # 应用Box-Cox变换
# 可视化结果
fig, (ax1, ax2, ax3) =plt.subplots(1, 3, figsize=(18, 5))
# 原始数据的直方图
ax1.hist(data, bins=50, edgecolor='black')
ax1.set_title("Original Data")
ax1.set_xlabel("Value")
ax1.set_ylabel("Frequency")
# Yeo-Johnson变换后数据的直方图
ax2.histax2.hist(data_yj, bins=50, edgecolor='black')
ax2.set_title("Yeo-Johnson")
ax2.set_xlabel("Value")
# Box-Cox变换后数据的直方图
ax3.hist(data_bc, bins=50, edgecolor='black')
ax3.set_title("Box-Cox")
ax3.set_xlabel("Value")
plt.tight_layout()
plt.show()
7、QuantileTransformer
分位数变换将一个变量的分布映射到另一个目标分布。使用Sklearn的
QuantileTransformer
类,可以将非正态分布转换为所需的分布。
考虑任何事件分布 —— 这个分布中的每个事件都会有一个与之相关的发生概率。这种行为由累积分布函数(CDF)定义,每种分布的CDF都不同。
分位数函数是CDF的逆:而CDF是一个返回值等于或小于给定值的概率的函数,PPF(百分位点函数)是这个函数的逆,返回等于或小于给定概率的值。
在异常值检测的背景下,
QuantileTransformer
可用于转换数据以使其更加可见。通过将数据转换为均匀分布,异常值将被映射到分布的极端,使它们与内部点更容易区分。
QuantileTransformer
可以将任意分布强制转换为高斯分布,前提是有足够的训练样本(数千个)。由于它是一种非参数方法,比参数方法(Box-Cox和Yeo-Johnson)更难解释。
Sklearn再次为我们提供了专用的
QuantileTransformer
对象,其中一个重要参数是output_distribution,它可以接受"uniform"或"normal"值。这些代表数据被映射到的分布。
importnumpyasnp
fromsklearn.preprocessingimportQuantileTransformer
importmatplotlib.pyplotasplt
# 创建一个具有偏斜分布的样本数据集
np.random.seed(0)
data=np.random.exponential(scale=2, size=(1000, 1)) # 指数分布
# 初始化QuantileTransformer
quantile_transformer=QuantileTransformer(n_quantiles=100, output_distribution='normal')
# 应用变换
data_transformed=quantile_transformer.fit_transform(data)
# 可视化原始和变换后的数据
plt.figure(figsize=(12, 6))
# 原始数据的直方图
plt.subplot(1, 2, 1)
plt.hist(data, bins=50, color='blue', edgecolor='black')
plt.title("Original Data (Exponential)")
# 变换后数据的直方图
plt.subplot(1, 2, 2)
plt.hist(data_transformed, bins=50, color='green', edgecolor='black')
plt.title("Transformed Data (Normal)")
plt.show()
变换示例:从特定分布到正态分布
下面是一个图形可视化,比较了不同的非正态分布及其相对变换,通过我们探讨的一些技术。
我使用Sklearn文档中的图像作为参考,修改了图表的排序以便于阅读。
这张图突出显示了某些变换的局限性,但是它们并不总是成功。例如对于双峰分布,除了分位数变换外,所有尝试转换为正态曲线的努力都失败了。
8、主成分分析 PCA
主成分分析(PCA)将一组可能相关的变量转换为一组线性不相关的变量,称为主成分。这些主成分按顺序排列,使得前几个包含原始变量中存在的大部分方差。
在特征工程的背景下,PCA可以用于减少特征数量,同时保留大部分信息。这可能会减少数据中的噪声和可能混淆模型的低级相关性。
作为一种降维技术,PCA应该用于压缩数据集而不是扩展它。PCA可以提取一些潜在特征,这些特征是影响您的数据的隐藏或潜在因素。例如有一个图像数据集,可以使用PCA来找到代表图像中对象的形状、颜色或纹理的特征。这些潜在特征可以帮助您更好地理解数据并改进机器学习模型。
PCA不仅可以应用在表格类数据,它还可以作为特征工程应用是在处理图像中。
让我们取Sklearn lfw_people数据集中存在的随机人脸,可以使用TruncatedSVD(一种与PCA非常相似的压缩技术)来提取关键图像特征,如边缘、阴影和主要对象的位置,为机器学习模型提供相关信息进行建模。
这种方法的一个典型应用是规范化图像,使系统对光照或其他条件的变化更加稳健 —— 这使得减少的人脸成为最终面部识别的基础。
从Sklearn的开放LFW数据集中随机选取的图像。
创建此可视化的Python代码:
importnumpyasnp
importmatplotlib.pyplotasplt
fromsklearn.datasetsimportfetch_lfw_people
fromsklearn.decompositionimportTruncatedSVD
# 加载Labeled Faces in the Wild数据集
lfw_people=fetch_lfw_people(min_faces_per_person=70, resize=0.4)
X=lfw_people.data
n_samples, h, w=lfw_people.images.shape
print(f"数据集维度:{X.shape}")
# 用于降维的组件数
n_components=50
# 应用TruncatedSVD
svd=TruncatedSVD(n_components=n_components, random_state=42)
X_transformed=svd.fit_transform(X)
# 重构图像的函数
defreconstruct_image(svd, X_transformed):
returnnp.dot(X_transformed, svd.components_)
# 重构所有图像
X_reconstructed=reconstruct_image(svd, X_transformed)
# 显示原始和降维人脸的函数
defplot_transposed_original_and_reduced(X_original, X_reduced, n_faces=5):
fig, axes=plt.subplots(2, n_faces, figsize=(3*n_faces, 6))
random_indices=np.random.choice(X_original.shape[0], n_faces, replace=False)
fori, idxinenumerate(random_indices):
# 原始人脸
axes[0, i].imshow(X_original[idx].reshape(h, w), cmap='gray')
axes[0, i].set_title(f'Face {i+1}\nOriginal')
axes[0, i].axis('off')
# 降维人脸
axes[1, i].imshow(X_reduced[idx].reshape(h, w), cmap='gray')
axes[1, i].set_title(f'Reduced\n({n_components} comp.)')
axes[1, i].axis('off')
plt.tight_layout()
plt.show()
# 显示原始和降维人脸
plot_transposed_original_and_reduced(X, X_reconstructed)
# 计算并打印平均重构误差
mse=np.mean((X-X_reconstructed) **2)
print(f"\n平均重构误差 (MSE):{mse:.4f}")
# 打印累积解释方差
explained_variance_ratio=svd.explained_variance_ratio_
cumulative_variance_ratio=np.cumsum(explained_variance_ratio)
print(f"\n{n_components} 个组件的累积解释方差:{cumulative_variance_ratio[-1]:.4f}")
# 显示前几个组件
n_components_show=min(10, n_components)
components=svd.components_[:n_components_show].reshape((n_components_show, h, w))
fig, axes=plt.subplots(2, 5, figsize=(15, 6))
fori, axinenumerate(axes.flat):
ifi<n_components_show:
ax.imshow(components[i], cmap='gray')
ax.set_title(f'Component {i+1}')
ax.axis('off')
plt.tight_layout()
plt.show()
结论
本文探讨了几种用于数值数据的高级特征工程技术,强调了它们在提高机器学习模型性能方面的关键作用。讨论的方法包括:
- 归一化
- 多项式特征
- FunctionTransformer
- KBinsDiscretizer
- 对数变换
- PowerTransformer (Box-Cox和Yeo-Johnson)
- QuantileTransformer
- PCA
每种技术都提供了特定的优势,适用于特定类型的数据和问题。最合适的变换选择取决于数据的性质、要解决的问题以及选择的模型。没有一种通用的解决方案,这使得实验和验证变得至关重要。