3. 特征工程(下):结构特征、物理指纹与降维

好,咱们接着聊特征工程的下半场。上一节我们把元素属性、组分描述子这些“宏观”特征搞定了,这一节要深入材料的“骨骼”和“灵魂”——结构特征和物理指纹。说白了,就是怎么把原子怎么排、排多密、谁挨着谁这些信息,变成模型能啃的数字。

3.1 结构特征:从空间群到配位数

我个人习惯,拿到一个晶体结构,先看三样东西:空间群、晶格常数、配位数。这三板斧砍下去,结构的大致轮廓就出来了。

3.1.1 空间群与晶格常数

空间群是啥?就是晶体对称性的“身份证”。230种空间群,每种都对应一套对称操作。我早期做钙钛矿项目时,就吃过亏——把Pm-3m和P4mm搞混了,结果模型预测的带隙差了0.5 eV。你想想看,对称性一变,电子结构全变了。

怎么编码空间群?常见做法有:

  • One-hot编码:230维向量,简单粗暴,但稀疏
  • 数值映射:直接用空间群序号(1-230),但模型可能学不到对称性关系
  • 对称性特征:提取晶系、点群、对称元素数量等衍生特征

晶格常数就直观多了:a, b, c, α, β, γ。但要注意,不同材料体系晶格常数差异很大。我建议做标准化处理,不然模型会把注意力全放在数值大的特征上。

避坑指南:我曾经把晶格常数直接扔进模型,结果预测弹性模量时误差巨大。后来发现,对于立方晶系,a=b=c,但数据里有微小差异(测量误差),模型误以为这是各向异性。所以,对于高对称性体系,建议强制对称化处理。

3.1.2 配位数与局部环境

配位数描述的是“一个原子周围有多少邻居”。但这里有个坑:截断半径怎么选?

我一般用Voronoi多面体方法,自动确定配位壳层。举个例子,NaCl中Na的配位数是6,但如果你用固定半径3Å,可能数出8个来。为什么会这样?因为Cl⁻半径大,把Na⁺挤得远了些。

配位数特征可以这样构建:

# 伪代码示例
def compute_coordination_numbers(structure, cutoff=5.0):
    cn_list = []
    for site in structure:
        neighbors = structure.get_neighbors(site, cutoff)
        cn = len(neighbors)
        cn_list.append(cn)
    return np.mean(cn_list), np.std(cn_list), np.max(cn_list)

除了平均配位数,我还喜欢加两个统计量:配位数的标准差(反映局部无序程度)和最大配位数(反映密堆积程度)。

3.2 基于物理的指纹:Sine Matrix与Coulomb Matrix

这部分是我觉得最有意思的。结构特征太“宏观”,原子级信息不够。物理指纹就是要把原子间的相互作用编码进去。

3.2.1 Coulomb Matrix

Coulomb Matrix是2013年Rupp等人提出的,核心思想很简单:用库仑相互作用来描述原子对。矩阵元素定义为:

M_ij = 0.5 * Z_i^2.4 (当i=j时)
M_ij = Z_i * Z_j / |R_i - R_j| (当i≠j时)

嗯,这里要注意:对角线项是原子自身的“能量”,用有效核电荷的2.4次方近似。非对角线项就是经典的库仑势。

我实际用过这个特征做带隙预测。效果不错,但有个问题:矩阵大小随原子数变化。解决方案是排序后取前N个特征,或者用矩阵的谱特征(特征值、迹等)。

个人经验:Coulomb Matrix对离子晶体效果特别好,因为库仑相互作用占主导。但对共价晶体(比如硅),效果就一般了。这时候我更喜欢用Sine Matrix。

3.2.2 Sine Matrix

Sine Matrix是Coulomb Matrix的变体,用正弦函数代替库仑势:

M_ij = 0.5 * Z_i^2.4 (i=j)
M_ij = Z_i * Z_j * sin(π * |R_i - R_j|) / (π * |R_i - R_j|) (i≠j)

为什么要用正弦?说白了,正弦函数在远距离衰减更快,而且有振荡特性,能更好地描述共价键的方向性。我在做二维材料项目时,用Sine Matrix预测MoS₂的能带结构,比Coulomb Matrix提升了约15%的R²。

3.2.3 特征向量化与标准化

无论哪种矩阵,最终都要变成一维向量。我常用的方法:

  1. 将矩阵按行展开(上三角部分)
  2. 或者计算矩阵的谱特征(前k个特征值)
  3. 或者用随机投影降维

我个人偏好谱特征,因为保留了矩阵的全局信息,而且维度可控。

3.3 特征选择与降维:PCA与t-SNE

特征多了,问题就来了:维度灾难、过拟合、计算慢。这时候就需要降维。

3.3.1 PCA:线性降维的经典

PCA(主成分分析)是我最常用的降维方法。它的本质是找数据方差最大的方向。

实际操作中,我一般保留能解释95%方差的主成分。举个例子,我有200维特征,PCA后可能只剩20维,但模型性能几乎不变。

from sklearn.decomposition import PCA

pca = PCA(n_components=0.95)  # 保留95%方差
X_pca = pca.fit_transform(X_scaled)
print(f"原始维度: {X_scaled.shape[1]}, 降维后: {X_pca.shape[1]}")

注意:PCA对异常值敏感。我建议先做标准化,再用IQR方法剔除异常值。不然一个离群点就能把主成分方向带偏。

3.3.2 t-SNE:可视化利器

t-SNE主要用于可视化,不是用来做特征输入的。它的特点是能保持数据的局部结构。

我记得有一次做高熵合金项目,2000多个样本,用PCA降维后各类混在一起。但用t-SNE一画,不同成分的合金自动聚成几簇,跟相图完全对应。那一刻,我真是服了t-SNE。

使用t-SNE的几个要点:

  • perplexity参数一般设5-50,我习惯用30
  • 先做PCA降维到50维,再用t-SNE,速度会快很多
  • t-SNE的结果每次运行可能不同,建议固定random_state

3.3.3 特征选择:不只是降维

降维是“组合”特征,特征选择是“挑选”特征。我常用的方法:

  1. 方差阈值法:去掉方差接近0的特征
  2. 互信息法:计算每个特征与目标变量的互信息
  3. 递归特征消除:用模型反复训练,去掉最不重要的特征

我个人经验,互信息法在材料领域特别好用。因为它能捕捉非线性关系,而材料性能往往跟特征呈非线性关系。

3.4 本章知识体系

下面这张图总结了本章的核心逻辑:从原始晶体结构出发,经过结构特征提取、物理指纹计算、特征选择与降维,最终得到可用于模型训练的高质量特征集。

特征工程(下):结构特征 → 物理指纹 → 降维 原始晶体结构 结构特征提取 空间群 · 晶格常数 · 配位数 基于物理的指纹计算 Coulomb Matrix · Sine Matrix · 谱特征 特征选择与降维 PCA · t-SNE · 互信息法 · 递归消除 高质量特征集 → 模型训练 结构决定性质 物理先验知识 避免维度灾难

3.5 实战建议

最后,给几个实战中的建议:

特征组合策略:我一般把结构特征(空间群one-hot + 晶格常数 + 配位数统计量)和物理指纹(Coulomb或Sine矩阵的谱特征)拼接起来。这样既有宏观结构信息,又有微观相互作用信息。

注意过拟合:特征维度超过样本数的1/10时,就要小心了。我习惯用交叉验证来评估特征集的有效性,而不是只看训练集表现。

好了,特征工程这部分就到这里。记住一句话:好的特征胜过复杂的模型。你花80%的时间做特征工程,20%的时间调模型,这是值得的。


专注资料整理