返回列表

8th Place Solution | No Tg Post Processing

667. NeurIPS - Open Polymer Prediction 2025 | neurips-open-polymer-prediction-2025

开始: 2025-06-16 结束: 2025-09-15 化学与材料 数据算法赛
第 8 名解决方案 | 无 Tg 后处理

第 8 名解决方案 | 无 Tg 后处理

TabM 和外部数据的探索性分析(EDA)就是你所需要的一切

作者: Dmitry Uarov (Master)
发布时间: 2025-09-17
竞赛排名: 第 8 名

外部数据

在我与所有参赛者分享了外部数据集之后,我(以及许多其他人)发现了另一个重要的数据源——POINT2 数据。我对每个外部数据集进行了简单的检查,追踪了组织者数据和外部数据中共同存在的 SMILES 的差异。我也使用了其他外部数据,但该数据是获胜的关键。

Tg 差异图

这里有 91 个 SMILES 交集。我们可以假设,除了某些严重偏差外,POINT2 的所有数据都比组织者的数据低 >20°C。这显然与模拟的持续时间和参数有关,这些目标在所有外部数据中都很容易调整(许多参与者不明白为什么我在基线中为 Density 减去了 0.118)。因此,在训练之前进行简单而明显的调整:

extra_data_tg['Tg'] += 20

重要说明

在有人开始抱怨禁止使用外部数据之前,请记住,我是第一个采取拯救这次竞赛的重大步骤的人。我们都曾多次尝试获得关于可以使用什么和不能使用什么的明确答复,但得到的却是沉默。因此,我遵循了两个简单的规则:1 - 数据对所有人开放,2 - 组织者未禁止该数据。我只追求学术目标。我的良心是清白的,我没有违反任何规则。当我详细研究使用外部数据的许可问题时,我得出的结论是,这是一个法律漏洞:首先,“无许可证”≠“免费使用”,其次,许多外部数据仅限学术用途(例如 PI1M)。几乎所有人都违反……或者没有违反规则(这完全取决于组织者根据他们的意愿对规则的解释)。在未来,通过律师更胜任的工作或组织者更清晰地指出可以使用和不能使用什么,很容易修补这个漏洞。我不打算再讨论这个问题了,因为我真的累了——我是个程序员,不是律师。

特征工程

我使用 Mordred 分子描述符,因为事实证明它在计算中更准确并提供了更多信息。我还对 TgFFVRg 使用了 Morgan 指纹(512 位),对 TcDensity 使用了 MACCS 指纹。在移除不必要的特征后,每个目标的特征数量如下:Tg: 789, FFV: 836, Tc: 520, Density: 423, Rg: 654。

交叉验证

我使用了极其保守的数据分离方法,通过创建 Butina 聚类,并在训练数据中删除所有与验证数据具有 >0.9 Tanimoto 相似度的 SMILES。共 5 折。

from rdkit.ML.Cluster import Butina
from rdkit import DataStructs

fps = [rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048).GetFingerprint(Chem.MolFromSmiles(s)) for s in train['SMILES'].tolist()]

dists = []
nfps = len(fps)
for i in range(nfps):
    sims = DataStructs.BulkTanimotoSimilarity(fps[i], fps[:i])
    dists.extend([1-x for x in sims])
clusters = Butina.ClusterData(dists, nfps, distThresh=0.7, isDistData=True)
groups = [0] * nfps
for idx, cluster in enumerate(clusters):
    for member in cluster:
        groups[member] = idx

train['Tanimoto_group'] = groups

def filter_train_by_val_similarity(train_idx, val_idx, fps_all, sim_thresh=0.9):
    train_idx = np.asarray(train_idx, dtype=int)
    val_idx = np.asarray(val_idx, dtype=int)
    train_fps = [fps_all[i] for i in train_idx]

    banned = set()
    for v in val_idx:
        if fps_all[v] is None:
            continue
        sims = DataStructs.BulkTanimotoSimilarity(fps_all[v], train_fps)
        for loc, s in enumerate(sims):
            if s >= sim_thresh:
                banned.add(train_idx[loc])

    keep = np.array([i for i in train_idx if i not in banned], dtype=int)
    return keep

fps = [rdFingerprintGenerator.GetMorganGenerator(radius=2, fpSize=2048).GetFingerprint(Chem.MolFromSmiles(s)) for s in train_part['SMILES'].tolist()]
gkf = RandomGroupKFold(n_splits=CFG.FOLDS, shuffle=True, random_state=seed)
for fold, (trn_idx, val_idx) in enumerate(gkf.split(train_part, train_part[target], train_part['Tanimoto_group'])):
    trn_idx_filtered = filter_train_by_val_similarity(trn_idx, val_idx, fps, sim_thresh=0.9)

模型

对于所有目标,我使用了相同的策略,但针对 TabM 使用了不同的参数和线性学习率调度器:

策略聚合物模型图
目标 (Target) k n_blocks d_block dropout
Tg 64 2 512 0.1
FFV 64 2 512 0.1
Tc 32 2 128 0.0
Density 32 3 256 0.0
Rg 32 2 256 0.0

CatBoost 和 LGBM 在哪里?对于许多目标,它们在折间具有很高的标准差,所以我决定不使用它们。此外,它们的存在几乎没有任何方式改善整体结果,因为除了 Rg 之外,TabM 在所有目标上与树模型相比都有巨大的差距。我不认为有必要分享每个目标和每个模型的 CV 结果,因为这些结果强烈依赖于数据量和所选的交叉验证方法。

最后的想法

在比赛结束后的第一天,我克制住没有写任何东西,因为显然一切都令我满意,而其他人都想再次运行这个轮盘赌,希望能提高私有排行榜(Private LB)的结果。请不要咒骂。我在过去的 3 次比赛中都因为各种原因输了,我非常清楚几个月的努力付诸东流意味着什么。我坚持认为测试数据是以摄氏度表示的,但由于它们的建模显然与原始数据不同,因此数值要高得多。这也得到了证实,即在几乎所有外部数据中,任何目标都存在分布偏差。我同意偏差太高了,但这完全是可能的。当我们看到这次竞赛中的许多问题时,我们都知道自己在做什么。许多比我们更明智的参与者 refrain from taking part(克制没有参加),他们节省时间和神经细胞的做法是正确的。

祝大家好运和耐心 ❤️

同比赛其他方案