第四章:材料数据获取与处理

做AI材料设计,说白了就是「数据喂进去,模型吐出来」。但数据从哪来?怎么喂?这中间的门道,我今天跟你好好聊聊。

我个人习惯把材料数据比作「食材」。你想想看,再厉害的厨师,拿到发臭的鱼肉也做不出好菜。AI模型也一样——垃圾进,垃圾出。这个道理我做了这么多年项目,体会太深了。

4.1 公开材料数据库:你的数据粮仓

先说说数据从哪搞。别自己闷头做实验测数据,那太慢了。公开数据库就是你的「数据粮仓」。我常用的有这么几个:

4.1.1 Materials Project

这个必须排第一。Materials Project(简称MP)是目前最大的开源材料数据库之一。它收录了超过15万种已知材料的计算性质,包括晶体结构、带隙、形成能、弹性常数等等。

怎么用?我一般直接调它的API。Python代码很简单:

# 安装:pip install pymatgen
from pymatgen.ext.matproj import MPRester

# 用你的API Key(去官网注册免费获取)
mpr = MPRester("你的API_KEY")

# 查询带隙在1.0-2.0 eV之间的氧化物
criteria = {
    "elements": ["O"],
    "band_gap": [1.0, 2.0]
}
properties = ["material_id", "formula_pretty", "band_gap", "formation_energy_per_atom"]
data = mpr.query(criteria, properties)

print(f"找到 {len(data)} 种材料")
# 输出示例:找到 342 种材料
小技巧:我个人习惯先把查询结果存成JSON文件,避免反复调API。MP的API有频率限制,一天内调用太多次会被暂时封禁。我曾经因为这个吃过亏,后来学乖了。

4.1.2 OQMD(Open Quantum Materials Database)

OQMD是另一个重量级选手。它更侧重于热力学稳定性数据,收录了超过100万种材料的计算数据。如果你关心「这个材料能不能稳定存在」,OQMD是首选。

OQMD的查询方式跟MP不太一样。它用的是基于MongoDB的查询语法:

# 安装:pip install pymongo
from pymongo import MongoClient

# 连接OQMD公共服务器
client = MongoClient("oqmd.org", 27017)
db = client.oqmd

# 查询形成能低于-0.5 eV/atom的二元氧化物
results = db.oqmd.find({
    "composition": {"$regex": "^[A-Z][a-z]?O"},
    "delta_e": {"$lt": -0.5}
}).limit(100)

for r in results:
    print(r["name"], r["delta_e"])
注意:OQMD的数据质量参差不齐。我遇到过好几次,同一个材料在MP和OQMD里的形成能差了0.3 eV以上。这时候怎么办?我的经验是:优先相信MP的数据,因为它的计算参数更统一。如果两个数据库差异太大,最好自己跑一遍DFT验证。

4.1.3 其他值得关注的数据库

数据库名称 特色数据 数据量级 我的使用场景
ICSD 实验晶体结构 ~20万 验证计算结构的准确性
COD 开源晶体结构 ~50万 免费替代ICSD的方案
NOMAD 原始DFT计算数据 ~1000万 做迁移学习时用
Citrine Informatics 实验数据+文献数据 ~100万 需要实验数据做验证时

4.2 数据清洗与标准化:别让脏数据毁了模型

数据拿到手,别急着训练模型。先做清洗。我见过太多人栽在这个坑里。

4.2.1 常见的数据问题

从公开数据库拿到的数据,通常有这几个毛病:

  • 缺失值:某个材料的带隙是NaN,或者弹性常数没算出来
  • 异常值:形成能突然蹦到+10 eV/atom,明显不合理
  • 重复数据:同一个材料被算了两次,结果还不一样
  • 单位不统一:有的用eV,有的用kJ/mol,有的用Hartree

举个例子。我曾经做一个钙钛矿带隙预测的项目,从MP下载了5000条数据。一检查,好家伙,有300多条带隙是负值。负带隙?那不就是金属嘛。但问题是,这些材料明明被标注为半导体。后来发现是计算收敛出了问题。

我的清洗流程是这样的:

import pandas as pd
import numpy as np

def clean_materials_data(df):
    """材料数据清洗流水线"""
    # 1. 删除缺失值过多的列(缺失率>30%)
    df = df.dropna(thresh=len(df) * 0.7, axis=1)
    
    # 2. 填充合理的缺失值
    # 带隙缺失:如果是金属,填0;如果是半导体,用同类材料均值
    df['band_gap'] = df['band_gap'].fillna(
        df.groupby('is_metal')['band_gap'].transform('mean')
    )
    
    # 3. 删除异常值(形成能超出物理范围)
    df = df[(df['formation_energy'] > -5) & (df['formation_energy'] < 2)]
    
    # 4. 去重:基于化学式+空间群去重
    df = df.drop_duplicates(subset=['formula', 'space_group'])
    
    # 5. 单位统一:全部转为eV
    if 'energy_unit' in df.columns:
        df.loc[df['energy_unit'] == 'kJ/mol', 'formation_energy'] /= 96.485
        df.loc[df['energy_unit'] == 'Hartree', 'formation_energy'] *= 27.2114
    
    return df

# 使用
clean_data = clean_materials_data(raw_data)
print(f"清洗前:{len(raw_data)}条,清洗后:{len(clean_data)}条")
核心原则:清洗数据时,宁可删掉可疑数据,也不要保留。少几条数据不会让模型崩掉,但脏数据会。我有个惨痛教训:一次保留了一批形成能异常的数据,结果模型预测出来的新材料全是热力学不稳定的,白费了三个月实验验证。

4.2.2 数据标准化

标准化说白了就是把不同尺度的特征拉到同一个量级。材料数据里,有的特征在0-1之间(比如带隙),有的在几百到几千(比如熔点),不标准化的话,模型会「偏科」。

我常用的标准化方法:

from sklearn.preprocessing import StandardScaler, MinMaxScaler

# 方法1:Z-score标准化(适合数据近似正态分布)
scaler = StandardScaler()
features_zscore = scaler.fit_transform(features)

# 方法2:Min-Max标准化(适合数据有明确边界)
scaler = MinMaxScaler(feature_range=(0, 1))
features_minmax = scaler.fit_transform(features)

# 方法3:Robust标准化(适合有异常值的数据)
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
features_robust = scaler.fit_transform(features)
我的选择:材料数据经常有异常值(比如某个材料的熔点特别高),所以我一般用RobustScaler。它基于中位数和四分位数,对异常值不敏感。这个选择帮我避免了好几次模型训练崩溃的问题。

4.3 数据增强技术:让有限的数据发挥更大价值

材料数据不像图像数据那么好搞。你不能随便旋转、裁剪一个晶体结构,那会破坏物理意义。但也不是没办法。我总结了几种靠谱的数据增强方法:

4.3.1 元素替换(Element Substitution)

这是材料领域最经典的数据增强方法。原理很简单:把晶体结构中的某个元素替换成同族的其他元素。比如把BaTiO₃中的Ba换成Sr,得到SrTiO₃。结构相似,性质相近但不同。

from pymatgen.core import Structure, Element
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer

def element_substitution(structure, target_site, new_element):
    """元素替换增强"""
    # 获取目标位点的元素
    old_element = structure[target_site].specie
    
    # 检查是否同族
    if old_element.group == Element(new_element).group:
        # 替换元素
        structure[target_site] = new_element
        
        # 重新优化结构(可选,但建议做)
        # 这里省略DFT优化步骤
        
        return structure
    else:
        print(f"警告:{old_element}和{new_element}不同族,替换可能不合理")
        return None
注意:元素替换不是万能的。我曾经把PbTiO₃中的Pb替换成C(碳),结果结构直接崩了。为什么?因为C原子太小,撑不起钙钛矿结构。所以替换时要考虑原子半径、电负性等物理约束。

4.3.2 晶格畸变(Lattice Distortion)

给晶格参数加一点微小的随机扰动,可以生成新的训练样本。扰动幅度要控制好,一般不超过5%。

import numpy as np

def lattice_distortion(structure, distortion_ratio=0.02):
    """晶格畸变增强"""
    lattice = structure.lattice
    
    # 对晶格参数加随机扰动
    new_a = lattice.a * (1 + np.random.uniform(-distortion_ratio, distortion_ratio))
    new_b = lattice.b * (1 + np.random.uniform(-distortion_ratio, distortion_ratio))
    new_c = lattice.c * (1 + np.random.uniform(-distortion_ratio, distortion_ratio))
    
    # 更新晶格
    structure.lattice = Lattice.from_parameters(
        new_a, new_b, new_c,
        lattice.alpha, lattice.beta, lattice.gamma
    )
    
    return structure

4.3.3 组合增强策略

实际项目中,我通常把多种增强方法组合使用。比如:先做元素替换,再做晶格畸变,最后加一点原子位置扰动。这样可以从一条数据生成几十条新数据。

但要注意,增强后的数据必须经过物理合理性检查。我一般会检查:

  • 原子间距是否合理(不能太近也不能太远)
  • 形成能是否在合理范围内
  • 结构是否保持原有的对称性

4.4 本章知识体系

说了这么多,我画张图帮你理清思路:

材料数据获取与处理知识体系 数据获取 数据清洗 数据增强 Materials Project OQMD ICSD / COD / NOMAD 缺失值处理 异常值检测与删除 重复数据去重 单位统一与标准化 元素替换(同族替换) 晶格畸变(<5%) 原子位置扰动 组合增强策略 核心原则 数据质量 > 数据数量 | 物理合理性检查 | 标准化统一尺度

这张图把整个流程串起来了。从数据获取开始,经过清洗标准化,再到数据增强,每一步都有对应的技术和方法。记住,这个流程不是一次性的。我通常会在模型训练过程中反复迭代——发现模型效果不好,回头检查数据,再清洗,再增强,直到满意为止。

好了,这一章的内容就到这里。数据是AI材料设计的基石,花再多时间在上面都值得。下一章我们聊聊特征工程,到时候我会分享一些「把材料结构变成模型能理解的特征」的实战技巧。


专注资料整理