简介:PDF全文来自《中国有色金属学报》2020年第8期,是一篇关于钙钛矿结构稳定性预测的学术论文,内容面向材料科学、计算化学及机器学习交叉领域的研究者与从业者,解决了传统容许因子预测精度不足的问题。文中基于SISSO方法与键价模型,提出新型容许因子τBV,并以决策树算法构建验证模型,有效克服了传统离子半径容许因子tIR的局限性,显著提升了对ABO3型化合物是否为钙钛矿结构的预测精度。资源压缩包仅含1个PDF文件,大小2.05MB,便于阅读和存档。全文完整呈现了钙钛矿结构定义、传统容许因子推导、SISSO特征筛选流程、键价模型键长计算及实验数据集(376种ABO3化合物)等关键内容,可作为相关课题的参考文献或专业入门指导。目前已有896人在线学习,适合需要系统性理解机器学习辅助材料设计方法的用户。
1. 为什么用SISSO重做钙钛矿容许因子:稳定性的数据驱动解法
钙钛矿结构稳定性的判定,过去几十年基本靠 Goldschmidt 容许因子 t = (rA + rO) / [√2 × (rB + rO)] 这种人工经验公式。做材料计算的人很快会碰到它的边界:同一组成在不同温度下结构会变,A/B 位半径接近时 t 值差零点零几就会把稳定区间判反。机器学习进来以后,很多人第一反应是直接上随机森林或者神经网络,但这在材料描述符场景里容易变成黑匣子——你拿到一堆重要度排序,却给不出一个能写进论文的解析表达式。SISSO(Sure Independence Screening and Sparsifying Operator)恰好卡在中间:它从海量候选特征组合里筛出一个稀疏的代数表达式,输出的是可解析、可代入验证的公式,而不是不可解释的模型。这份 PDF 做的事就是把 SISSO 和机器学习流程应用到钙钛矿 ABO3 体系的稳定性预测上,重新建立一个可验证的新型容许因子。适合正在做材料信息学课题、想从传统经验公式转向数据驱动描述符的研究生,也适合刚进钙钛矿方向、需要一套可复现流程的从业者。
2. 数据集与特征工程:先解决「用什么学」的问题
2.1 数据集来源与正负样本怎么定
钙钛矿稳定性预测的第一道坎不是模型,是数据。常见做法是从 Materials Project、ICSD、OQMD 这类结构库里拉 ABO3 计量比的化合物,然后用空间群和容忍因子做初筛:稳定相通常取立方钙钛矿空间群 Pm-3m(221)以及常见的畸变钙钛矿结构,负样本则从同计量比但不是钙钛矿结构的相里挑。这里最容易翻车的是直接用组成去数据库查,结果同一化学式返回了多个结构原型,稳定标签互相矛盾。
我的建议是先固定数据版本,把下载日期和数据库版本写进特征文件抬头。从 Materials Project 导出时,至少保留这几个字段:material_id、space_group、formation_energy_per_atom、band_gap、A 位元素、B 位元素、结构原型。稳定标签不能只看形成能,要结合实验合成记录或已知相图,否则模型学的是数据库采样偏差,不是物理稳定性。
负样本的构造也没有统一标准。常见策略是:固定 ABO3 计量比,把 A 位和 B 位元素随机组合后去结构库里查「是否存在钙钛矿相」,把不形成钙钛矿的比例压住,同时要控制正负样本数量别太悬殊。我一般会把负样本上限压到正样本的 2.5 倍以内,超过这个比例,分类阈值会被拉偏。
import pandas as pd # 假设你从 MP 导出后存成了 mp_abos.csv df = pd.read_csv("mp_abos.csv") # 筛选 ABO3 计量比且 A/B 位为阳离子的条目 df = df[df["composition"].str.match(r"^[A-Z][a-z]?[A-Z][a-z]?O3$")] # 结构原型标记 stable_structures = ["Pm-3m", "Pbnm", "R-3c"] df["target"] = df["structure_prototype"].apply( lambda x: 1 if x in stable_structures else 0 ) # 负样本封顶,防止不平衡 pos = df[df["target"] == 1] neg = df[df["target"] == 0] if len(neg) > 2.5 * len(pos): neg = neg.sample(n=int(2.5 * len(pos)), random_state=42) train_df = pd.concat([pos, neg], axis=0).reset_index(drop=True) print(train_df["target"].value_counts())这段代码的关键是最后一行的负样本抽样:2.5倍这个系数不是拍脑袋,超过它之后,分类器会倾向于预测大多数类,尤其在做交叉验证时表现得特别明显。random_state=42固定下来,保证你重跑论文实验时数据划分是一致的。
2.2 特征工程:从元素属性到候选描述符
SISSO 不吃结构描述符,吃的是元素属性的组合。基础特征通常从这几个维度收集:离子半径(Shannon 半径)、Pauling 电负性、Allen 电负性、第一电离能、价态、d 电子数、原子序数。每个元素都有一组这样的属性,特征矩阵里一个样本就是 A 位元素属性 + B 位元素属性 + 氧位属性组合起来的向量。
需要注意,离子半径要区分配位数和价态。A 位离子通常取 XII 配位,B 位取 VI 配位,氧取 II 配位。同一元素在不同价态下半径差 0.2 Å 以上,混用的话计算出的任何半径比值都会失真。特征表建好之后,最好按元素符号对齐,不要把不同数据库的半径版本混着用。
| 特征类别 | 包含属性 | 来源维度 | 常见问题 |
|---|---|---|---|
| 半径类 | Shannon 半径(A/B/O 位) | 配位数、价态 | 配位数不匹配导致数值失真 |
| 电负性类 | Pauling / Allen 电负性 | 元素本征 | Pauling 在部分过渡金属上争议大 |
| 能量类 | 第一电离能、电子亲和能 | 元素本征 | 和电离度高度共线 |
| 电子结构类 | d 电子数、价电子数 | 元素周期表位置 | 变量是离散整数,SISSO 做除法时注意零值 |
| 派生描述符 | 半径比 rA/rB、电负性差 | 由基础特征计算 | 与人工经验公式重复,先保留无妨 |
# 从元素周期表性质文件构造基础特征 periodic = pd.read_csv("periodic_properties.csv", index_col="element") def build_feature_vector(a_elem, b_elem, oxy_radius=1.40): a = periodic.loc[a_elem] b = periodic.loc[b_elem] return { "rA": a["shannon_radius_XII"], "rB": b["shannon_radius_VI"], "chiA": a["pauling_en"], "chiB": b["pauling_en"], "IE_A": a["ionization_energy"], "IE_B": b["ionization_energy"], "d_electrons_A": a["d_electrons"], "d_electrons_B": b["d_electrons"], "rO": oxy_radius, } train_df["feat"] = train_df.apply( lambda row: build_feature_vector(row["A"], row["B"]), axis=1 ) features = pd.json_normalize(train_df["feat"]) print(features.head())这段代码把元素性质键进特征向量,后面接入 SISSO 前要做一步:把所有特征min-max归一化到 [0, 1] 区间。SISSO 的算子组合里全是加减乘除和范数运算,量纲不统一时,乘法组合项的数值会被某个量级特别大的特征主导,筛选结果完全偏向那个特征。这个坑我踩过,归一化之后再跑,描述符表达式稳定很多。
3. SISSO 实操:从候选特征组合里筛出稀疏描述符
3.1 SISSO 两步法在做什么:SIS 负责砍维度,SO 负责挑稀疏表达式
SISSO 的全称拆开看就理解了。第一步是 Sure Independence Screening(确定独立筛选),它对每个候选特征计算与目标值的相关度量,按相关性排序后只保留前面一小撮特征。这一步的意义很实际:特征算子组合起来数量级轻松到 10^5 以上,直接做稀疏回归计算量扛不住,先用便宜的相关性筛掉绝大多数无关特征。
第二步是 Sparsifying Operator(稀疏算子),在筛选后的特征子空间里找描述符维度固定(比如 2 维或 3 维)的稀疏表达式,目标是让模型误差最小的同时保持表达式维度最低。SISSO 区别于 LASSO 的地方在于,它做的是 L0 范数意义上的稀疏,不是 L1 的软约束。L0 直接限制非零项数量,出来就是一个形式干净的解析式。
这个设计对钙钛矿稳定性特别合适。Goldschmidt 因子本身就是一个二维表达式,SISSO 的默认输出恰好是同样的形式,比如新容许因子可能长成 c0 + c1 × (rB/rA) + c2 × |chiA - chiB| 这种样子。你不用额外解释神经网络的权重到底代表什么物理过程,直接把解析式画成等值线图,就能看到稳定区在特征空间里的形状。
3.2 安装、运行与参数设置
SISSO 官方提供命令行工具和 Python 接口,推荐在 conda 环境里装,避免系统 Python 的依赖冲突。装好之后先跑一遍官方 example 验证环境正常,再替换成自己的数据。
conda create -n sisso_env python=3.9 -y conda activate sisso_env pip install sisso # 跑一下官方示例,确认 SISSO 能正常输出描述符 python examples/example_classifier.pySISSO 的输入通常是一个 CSV 特征矩阵,目标列单独标出来。核心参数集中在运行脚本里:
| 参数 | 推荐值 | 说明 |
|---|---|---|
task | classification | 稳定性预测是二分类问题 |
feature_space | sum, diff, mult, div, abs_diff | 算子越多,候选组合爆炸越明显 |
dimension | 2 或 3 | 描述符的非零项数,对应解析式里的项数 |
nsample | 50~200 | SIS 筛选阶段保留的候选特征数 |
max_rung | 2 | 特征算子迭代轮数,第一轮生成基础组合 |
cross_validation | 5-fold | 评估描述符泛化能力 |
# 调用 SISSO 接口完成筛选(不同版本 API 有差异,以官方 example 为准) from sisso import SISSORegressor # 或 SISSORegressor 对应的分类版本 model = SISSORegressor( task="classification", dimension=2, n_features=100, operators=["sum", "diff", "mult", "div", "abs_diff"], max_rung=2, cross_validation=True, cv_folds=5, ) X = features.values y = train_df["target"].values model.fit(X, y) # 输出最优描述符表达式 print(model.best_desc) print(model.coef_)dimension=2意味着最终表达式只有两个非零特征项,这是为了和传统两变量容许因子对齐,物理上也好解释。nsample(上面代码里的n_features)是 SIS 阶段保留的候选特征数,这个值太大会让 SO 阶段回归变慢,太小可能把真正关键的特征组合在第一轮就筛掉。我一般先跑一个nsample=100的快速版看输出是否合理,再逐步减小找表达式最稳定的区间。
跑完之后重点看best_desc的结构:表达式里哪几个特征被选中,每一项的符号是正还是负,系数绝对值哪个占主导。如果输出里混进了 d 电子数这类离散特征而且符号不稳定,说明数据里同一元素反复出现导致泄漏,回到第 2 章检查特征构造。
4. 新型容许因子如何落地:无量纲化与物理解读
4.1 从 SISSO 输出到无量纲化
SISSO 直接输出的表达式往往带有量纲,因为输入特征里半径单位是 Å,电负性是 Pauling 标度没有单位,电离能是 eV,混在一起做乘除之后单位很乱。按原样拿去做阈值分类没问题,但作为「容许因子」写进论文并和 Goldschmidt t 对比时,必须做无量纲化处理。
常见做法是把 SISSO 输出的描述符除以其在训练集上的标准差,或者把基础特征先归一化再做回归,这样输出表达式天然无量纲。我喜欢把 SISSO 得到的系数先不动,保持原始形式,然后用训练集均值和标准差做标准化:t_new = (d - mean_d) / std_d。好处是给其他研究者用新因子时,他们只需要带入元素的原始属性,减去均值再除以标准差就能得到可比较的数值。
标准化之后的阈值也更有意义:分类边界不再依赖具体数据集的量级,而可以对应一个相对稳定的区间。比如训练集上标准化后新容许因子的分类阈值落在 0.3 附近,那么新样本计算出的值超过 0.3 就判定为钙钛矿稳定相,这和 Goldschmidt 因子 t 在 0.9~1.1 之间为稳定的用法完全一致。
4.2 物理解读:新型因子比传统因子多了什么
把 SISSO 挑选出的特征和 Goldschmidt 因子对比,通常能看出两个关键差异。第一,传统因子只装半径比,SISSO 倾向于把电负性差或电离能差作为第二项纳入表达式,这说明化学键离子性对结构稳定性的影响在数据里是显著存在的,而经典因子完全没提这一维。第二,SISSO 会对半径比加幂次或开方变换,这相当于对传统因子的非线性修正,能解释为什么某些组分用 Goldschmidt 因子判断是稳定区但实际合成失败。
建立新因子之后要做的验证不是只看准确率。我一般会画一张二维散点图:横轴 Goldschmidt t,纵轴新容许因子,稳定样本和不稳定样本用不同颜色标记。如果新因子的加入能把传统因子分界线附近的重叠区域切开,这个描述符才有增量价值;如果两个因子高度相关,说明 SISSO 筛了半天只是把传统公式换了个皮。
import matplotlib.pyplot as plt t_gold = (features["rA"] + features["rO"]) / ( 1.414 * (features["rB"] + features["rO"]) ) t_new = model.predict(X) # 新容许因子 plt.scatter(t_gold, t_new, c=y, cmap="bwr", alpha=0.6) plt.xlabel("Goldschmidt t") plt.ylabel("ML tolerance factor") plt.colorbar(label="stable (1) / unstable (0)") plt.savefig("factor_comparison.png", dpi=150)这个图是判断描述符有没有物理意义的关键一步。如果t_new对稳定样本和不稳定样本几乎没有区分度,别急着加维度,先回头检查特征是否归一化、数据标签是否可靠。SISSO 不像深度学习能用 loss 掩盖数据问题,它的表达式太清楚,数据脏最终都会暴露在散点图里。
5. 复现避坑:特征、稀疏度与验证的常见问题
5.1 数据制备时的三个坑
现象 1:交叉验证准确率很高,但换一个数据集立刻崩。原因大概率是同源样本泄漏。Materials Project 里同一化合物的不同磁结构或不同空间群会被当成多个条目,训练集和验证集里各出现一次,模型其实记住了化学式而不是结构特征。解决方法是按material_id或化学式做分组交叉验证,也就是 GroupKFold,保证同一种化合物不会同时出现在训练和验证集里。
现象 2:正负样本比例失衡,模型把所有样本都判成负样本。原因是负样本数量远超正样本,分类阈值被推向正样本一侧。解决方法是先把负样本封顶,再在 SISSO 分类时设置class_weight="balanced"或者对少数类做简单过采样。注意不要用过采样生成完全相同的重复样本,SISSO 的特征空间本来就有大量冗余组合,重复样本会让稀疏算子选择偏好不稳定。
现象 3:特征里混入了合成条件变量,比如温度或退火时间。这种特征如果包含在训练集里,模型会被温度直接主导,输出表达式变成温度阈值的等价物,一旦做新组分预测就失效。解决方法是严格区分「元素本征属性」和「实验条件」,资源里的新容许因子只允许用元素本征属性构建,合成条件只作为外部验证变量,不进特征矩阵。
5.2 SISSO 运行与解释时的三个坑
现象 4:运行时间从几分钟突然变成几小时,甚至内存爆炸。原因是特征算子加得太多或max_rung设得过大。算子组合是乘法增长的:sum, diff, mult, div, abs_diff五个算子,rung 到 3 就已经接近百万级候选特征。解决办法是先用max_rung=1跑通流程,再逐步加到 2,永远不要在初始调试阶段就开全算子。如果只是做稳定性分类,mult和diff两个算子通常就足够覆盖物理规律。
现象 5:SISSO 每次运行选出的描述符不一样,表达式不稳定。原因通常是 SIS 阶段的相关性排序对数值噪声太敏感,或者样本量太小让相关性估计方差大。解决方法是多种子运行:固定random_state,分别跑 5 次看被选特征的重合率,只有那些每次都入选的特征才值得做物理解读。重合率低于 60% 的时候,补充数据或删掉高度共线的特征。
现象 6:新容许因子表达式里有一项系数特别小,去掉之后准确率几乎不变。原因是 SO 阶段的稀疏约束没有识别出这项是冗余的,选择了局部最优。解决方法是手动做一次消融:把该项设为零,重新计算训练集准确率。如果准确率下降小于 0.5%,就把这个因子降维,用剩余项重新跑一次 SISSO。最终版本的描述符应该满足去掉任何一项都会造成明显准确率下降,这才是一个紧致且自洽的容许因子。
6. 模型验证与进阶用法:从分类准确率到可迁移性
6.1 阈值与 AUC 的验证套路
新容许因子建立后的第一件事不是看准确率,而是看 ROC 曲线下的面积(AUC)。准确率依赖你选定的分类阈值,而靶材数据集里正负样本比例本来就不天然均匀,阈值一变准确率就变。AUC 则不依赖阈值,直接反映描述符把正负样本分开的能力。我一般跑 5 折交叉验证,每折单独计算 AUC,取均值和标准差;均值低于 0.85 的容许因子,我基本不会拿去做后续物理分析。
分类阈值的选择用训练集上的 Youden 指数(J = sensitivity + specificity - 1)最大点,然后固定这个阈值到验证集上使用。这一步要警惕的是:不要在验证集上看阈值。很多复现项目翻车在这里——先在验证集上找到最佳阈值再报准确率,这个准确率是虚高的。阈值必须在训练集内定死,验证集只做一次评估。
6.2 把这个流程复用到其他体系
SISSO 容许因子的方法不局限于无机氧化物钙钛矿。卤化物钙钛矿、双钙钛矿 A2BB’O6、甚至有机-无机杂化钙钛矿,换一套元素属性特征集,流程完全能复用。换体系时要注意两点:一是特征空间要增加新的属性维度,比如卤化物体系要加入卤素离子半径和极化率;二是稳定标签的构造逻辑不一样,有机-无机杂化体系可能参考容忍因子和合成温度窗口双重标准。
我自己复现这类描述符项目的习惯是:先固定数据版本和标签定义,再把归一化代码和特征表生成脚本一起归档,最后才跑 SISSO 调算子。数据版本一换,描述符表达式跟着变,不归档的设计基本等于没有复现性。从那以后我每次拿到新的结构体系,都强制先做一遍「固定数据版本 → 检查标签泄漏 → 归一化验证 → 多样本跑 SISSO → 消融测试」这个顺序,没有再被描述符不稳定的问题折腾过。这套流程配合这份 PDF 里的参数设置和验证细节,能让你在钙钛矿或类似材料体系里少走不少弯路,希望帮到你。
本文还有配套的精品资源,点击获取