4. 材料特征工程(下):特征选择与降维实战
好,咱们接着聊特征工程的下半场。上一章我们把特征造出来了,但问题来了——特征太多,反而坏事。我见过不少项目,特征堆到上千维,模型跑三天三夜,结果精度还不如人家用几十个特征的。说白了,特征不是越多越好,关键是要精。
4.1 特征选择:从海量特征中淘金
特征选择,就是帮我们从一堆特征里挑出最管用的那几个。我个人习惯把方法分成三类:过滤法、包裹法、嵌入法。咱们一个一个说。
4.1.1 过滤法(Filter Method)
过滤法是最快的。它不依赖任何模型,直接算特征和目标变量之间的相关性。常用的有皮尔逊相关系数、互信息、卡方检验等。
核心思路:给每个特征打个分,按分数排序,取前k个。
我在做钙钛矿材料带隙预测时,一开始用了200多个特征。用皮尔逊相关系数一筛,发现大部分特征跟带隙的相关系数不到0.1。最后只留了30个相关性高的,模型速度提升了5倍,精度反而涨了2%。
import pandas as pd
import numpy as np
from sklearn.feature_selection import SelectKBest, f_regression
# 假设 X 是特征矩阵,y 是目标属性(如带隙)
selector = SelectKBest(score_func=f_regression, k=30)
X_selected = selector.fit_transform(X, y)
# 看看哪些特征被选中了
selected_indices = selector.get_support(indices=True)
print(f"选中的特征索引: {selected_indices}")
我的经验:过滤法适合做第一道粗筛。先把明显没用的特征扔掉,后面再用更精细的方法。别指望一步到位。
4.1.2 包裹法(Wrapper Method)
包裹法就暴力多了。它把特征选择当成一个搜索问题,用模型的表现来评判特征组合的好坏。典型的代表是递归特征消除(RFE)。
为什么会用这么费劲的方法?因为特征之间可能有交互作用。两个单独看都很弱的特征,组合起来可能很强。过滤法发现不了这种关系,但包裹法可以。
from sklearn.feature_selection import RFE
from sklearn.ensemble import RandomForestRegressor
# 用随机森林做包裹法
estimator = RandomForestRegressor(n_estimators=100)
selector = RFE(estimator, n_features_to_select=20, step=5)
selector = selector.fit(X, y)
# 看看排名
print(f"特征排名: {selector.ranking_}")
避坑指南:我曾经在一个高熵合金项目里用包裹法,300个特征跑了整整两天。后来学乖了——先用过滤法砍到50个,再用包裹法精挑。时间从两天缩到两小时。
4.1.3 嵌入法(Embedded Method)
嵌入法算是前两种的折中。它在模型训练过程中自动做特征选择,不额外增加计算量。最典型的就是Lasso回归(L1正则化)和树模型的特征重要性。
你想想看,Lasso回归会自动把不重要的特征系数压到0,这不就是天然的特征选择器吗?
from sklearn.linear_model import Lasso
# Lasso会自动做特征选择
lasso = Lasso(alpha=0.01)
lasso.fit(X, y)
# 系数不为0的特征就是被选中的
selected_features = np.where(lasso.coef_ != 0)[0]
print(f"Lasso选中的特征数: {len(selected_features)}")
| 方法 | 速度 | 精度 | 适用场景 |
|---|---|---|---|
| 过滤法 | 快 | 一般 | 初步筛选,特征数量极大时 |
| 包裹法 | 慢 | 高 | 特征数量适中,追求最佳组合 |
| 嵌入法 | 中等 | 较高 | 有明确模型时,兼顾效率与效果 |
4.2 特征降维:把高维数据压扁
特征选择和降维不一样。选择是「挑」,降维是「造」。降维会把原始特征组合成新的特征,维度变少了,但信息尽量保留。
4.2.1 PCA(主成分分析)
PCA是最经典的降维方法。它找的是数据方差最大的方向。说白了,就是找到最能区分不同样本的那些方向。
我记得有一次做锂电池材料筛选,原始特征有80多个,很多特征之间高度相关。用PCA降到10维后,模型训练时间从半小时缩到3分钟,精度只掉了不到1%。值不值?太值了。
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# 降到20维
pca = PCA(n_components=20)
X_pca = pca.fit_transform(X)
# 看看每个主成分解释了多少方差
explained_variance = pca.explained_variance_ratio_
print(f"前20个主成分累计解释方差: {sum(explained_variance):.2%}")
# 画个碎石图,帮我们决定保留多少维
plt.plot(np.cumsum(explained_variance))
plt.xlabel('主成分数量')
plt.ylabel('累计解释方差')
plt.show()
我的习惯:一般保留能解释90%以上方差的主成分数量。但别死磕这个数字,有时候80%就够用了,看具体任务。
4.2.2 t-SNE(t分布随机邻域嵌入)
t-SNE跟PCA不一样。PCA是线性的,t-SNE是非线性的。它特别擅长把高维数据降到2维或3维,方便可视化。
但注意了,t-SNE有个坑——它不保留全局结构。也就是说,降维后不同簇之间的距离没有实际意义。我见过有人拿t-SNE的结果去做聚类分析,结果一塌糊涂。
from sklearn.manifold import TSNE
# 降到2维,方便可视化
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X)
# 可视化不同材料类别
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='viridis')
plt.xlabel('t-SNE 维度1')
plt.ylabel('t-SNE 维度2')
plt.colorbar(label='材料类别')
plt.show()
避坑指南:我曾经用t-SNE做合金相分类的可视化,perplexity参数设得太小,结果同一类的点散得到处都是。后来调大了perplexity,结构才清晰起来。记住,perplexity一般取5到50之间,数据量越大,值可以设得越大。
4.3 自动化特征生成工具:MatMiner
说到材料特征工程,不得不提MatMiner。这是个专门为材料科学设计的Python工具包,能自动从晶体结构中生成特征。
MatMiner的核心功能包括:
- 元素特征:自动提取元素的周期表属性(原子半径、电负性、价电子数等)
- 结构特征:从CIF文件中计算配位数、键长、键角等
- 组合特征:自动生成元素之间的比值、差值、乘积等组合
from matminer.featurizers.base import MultipleFeaturizer
from matminer.featurizers.composition import ElementProperty, Stoichiometry
from matminer.featurizers.structure import SiteStatsFingerprint
# 组合多个特征生成器
featurizer = MultipleFeaturizer([
ElementProperty.from_preset("magpie"),
Stoichiometry(),
SiteStatsFingerprint.from_preset("cn")
])
# 从晶体结构生成特征
features = featurizer.featurize_dataframe(df, col_id="structure")
print(f"生成的特征数量: {len(features.columns)}")
我的建议:MatMiner生成的原始特征往往很多,别一股脑全扔进模型。先用过滤法粗筛一遍,再用嵌入法精挑。我一般会保留50-100个特征,再多就容易过拟合。
4.4 本章知识体系
下面这张图把本章的核心逻辑串起来了。从原始特征出发,经过特征选择和降维,最终得到高质量的特征子集。
嗯,这一章的内容就这些。特征选择和降维是材料AI落地中非常关键的一环。我见过太多人花大量时间调模型参数,却忽略了特征质量。其实,把特征搞好了,模型随便跑跑效果都不差。
记住一句话:好的特征胜过好的模型。这话虽然绝对了点,但在材料科学这个领域,八九不离十。