尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Transformer在化学分子建模中的应用:从原理到SMILES性质预测

Transformer在化学分子建模中的应用:从原理到SMILES性质预测 第一次把 SMILES 字符串当成“句子”输入模型时很多人会愣一下化学分子什么时候变成自然语言了实际上这正是 Transformer 能进入化学AI 核心领域的原因之一。它不关心输入是英文、代码还是分子序列它关心的是一堆 token 之间的依赖关系能不能被有效建模。这篇文章围绕 Transformer 在分子建模中的完整学习路径展开从架构原理、PyTorch 核心实现到 SMILES 分子性质预测实战最后给出论文复现的方法论与常见坑点。如果你已经具备 Python 和深度学习基础但不太清楚“Transformer 怎么用在化学数据上”或者想复现一篇分子建模论文却不知道如何下手这篇文章会给你一条清晰可执行的路线。读完以后你可以手写一个能跑通的分子性质预测 Transformer 小模型也能理解 ChemBERTa、Uni-Mol 这类分子大模型的底层逻辑。1. 为什么化学AI离不开Transformer1.1 分子数据如何变成模型能读的输入化学AI 要解决的核心问题是把分子的“结构信息”转化成模型能计算的“数值信息”。常见表示方式有三种第一种是 SMILES 字符串比如乙醇可以写成CCO苯环可以写成c1ccccc1。SMILES 把原子和化学键编码成 ASCII 字符本质上就是一个离散序列。序列模型可以直接处理它这也是 Transformer 最容易入手的分子建模入口。第二种是分子图。原子是图的节点化学键是图的边每个节点可以附带原子类型、电荷、手性等特征。图神经网络通常处理这种表示但 Transformer 同样可以改造为图 Transformer把原子间的空间距离、化学键类型作为注意力偏置。第三种是 3D 坐标。分子的真实性质往往取决于空间构象比如药物分子与蛋白质靶点的结合强度。处理 3D 结构时Transformer 需要在注意力机制中加入距离编码或几何编码这也是 Uni-Mol、Graphormer 等模型的核心改进点。理解了这三种表示方式你会发现一个关键事实Transformer 的自注意力机制并不依赖输入顺序而是直接建模任意两个 token 或节点之间的关系。对于分子这种“远程原子之间也可能存在强相互作用”的数据这种能力非常自然。1.2 Transformer在化学AI中的典型应用在化学AI 领域Transformer 的应用大致可以分成四类分子性质预测给定一个分子预测它的 LogP、溶解度、毒性、血脑屏障透过率等。这类任务通常用 Transformer Encoder 提取分子表示再接分类头或回归头。分子生成从随机噪声、片段或者约束条件出发生成符合化学规则的新分子。这类任务通常用 Transformer Decoder类似语言模型逐 token 生成 SMILES。化学反应预测给定反应物和试剂预测产物。这类任务可以建模成序列到序列问题用 Encoder-Decoder 结构。蛋白质-配体相互作用预测小分子与蛋白质靶标之间的结合亲和力需要同时编码分子和蛋白质序列。你会发现这些任务本质上都围绕“序列”和“结构依赖”两个关键词展开而 Transformer 恰好是这两个关键词的最佳结合点。1.3 本文的技术边界需要提前说明的是本文不涉及大规模预训练模型的完整复现因为那需要大量算力和数据。本文重点做两件事第一从零实现 Transformer 的核心组件让你理解它内部到底发生了什么第二用一个分子性质预测任务把 SMILES 数据处理、模型构建、训练验证整个流程打通。当你把这条小闭环跑通之后再去看 Uni-Mol、MolFormer 这类大模型就会更容易理解它们的改进点在哪里。2. Transformer架构核心原理拆解2.1 为什么最后是Transformer从CNN/RNN说起在 Transformer 出现之前序列建模主要靠 RNN 和 CNN。RNN 的缺点是串行计算。当前时刻的隐状态依赖上一时刻的输出无法并行训练速度慢同时长距离依赖会导致梯度消失或梯度爆炸。虽然 LSTM、GRU 缓解了一部分问题但本质上没有摆脱序列依赖。CNN 可以并行但感受野有限。要覆盖长距离依赖必须堆叠很多层或者使用膨胀卷积。对分子来说SMILES 序列的某个原子可能和几十个 token 之外的另一个原子存在重要关系这正好是 CNN 的短板。Transformer 用自注意力机制同时解决了两个问题一方面任意两个 token 可以直接计算相关性无论距离多远另一方面注意力计算可以高度并行非常适合 GPU 加速。这也是“为什么最后是 Transformer”的答案。2.2 自注意力机制的计算过程自注意力的输入是一个 token 序列每个 token 都被表示成一个向量。假设输入是矩阵 X形状为 (batch, seq_len, d_model)其中 d_model 是特征维度。模型通过三个可学习的权重矩阵把 X 映射成 Query、Key、Value 三个矩阵Query表示“我在找什么”。Key表示“我有什么特征可以被匹配”。Value表示“我实际携带的信息”。注意力分数计算如下[ Attention(Q,K,V) softmax(\frac{QK^T}{\sqrt{d_k}})V ]先计算 Q 和 K 的点积得到两个 token 之间的相似度除以 (\sqrt{d_k}) 是为了防止点积结果过大导致 softmax 梯度消失然后做 softmax 归一化得到注意力权重最后用权重对 V 做加权求和。在分子场景中可以这样理解模型在判断某个原子的最终表示时会先问“谁和我的化学环境最相关”然后把这些相关原子的信息聚合起来。即使两个原子在 SMILES 序列上隔得很远只要它们之间存在化学意义上的相互作用注意力权重就会把这种关系体现出来。2.3 多头注意力与位置编码多头注意力做的事情是把 d_model 维的向量切分成 n_heads 个子空间每个头独立做一次自注意力最后拼接起来。这样做的意义在于不同头可以关注不同模式。比如在分子中一个头可能关注局部官能团另一个头可能关注远程共轭效应还有一个头可能关注电荷分布。多个头互补模型表达能力更强。位置编码解决的是“顺序信息”问题。自注意力本身不包含顺序概念因为注意力计算是对 token 两两之间进行的如果交换两个 token结果不会变。但分子序列是有顺序的至少 SMILES 字符串的顺序对解析分子结构很重要。最经典的位置编码是正弦编码[ PE(pos, 2i) sin(pos / 10000^{2i/d_{model}}) ] [ PE(pos, 2i1) cos(pos / 10000^{2i/d_{model}}) ]其中 pos 是位置索引i 是维度索引。这种编码有很多优点值域在 [-1,1] 之间不会越训越大不同频率的三角函数可以帮助模型捕捉不同尺度的位置关系。2.4 残差、LayerNorm与Encoder整体结构Transformer Encoder 的基本单元由两部分组成多头注意力子层。前馈神经网络子层。每个子层都使用残差连接加 LayerNorm。原始 Transformer 使用的是 Post-Norm 结构也就是先做子层计算再加上残差最后做归一化后来的很多模型改用 Pre-Norm也就是先归一化再进子层。Pre-Norm 训练更稳定收敛更快尤其在深层模型中表现更好。Encoder 的整体结构可以理解为输入经过嵌入层和位置编码后送入 N 层 EncoderLayer每一层的输出都是形状相同的序列表示。对于分子性质预测我们可以对最后一层的所有 token 表示做池化得到整个分子的向量表示再输入到下游预测头。3. 环境准备与项目结构3.1 运行环境说明本文示例以常见环境为例具体版本需要根据你的项目实际情况调整。操作系统Windows / Linux / macOS 均可。Python建议 3.9 以上。深度学习框架PyTorch 2.x。分子处理库RDKit用于校验和处理 SMILES可选。IDEPyCharm、VS Code 或者 Jupyter Notebook 都可以。安装 PyTorch 时建议根据你的 CUDA 版本从官网选择对应命令。如果只是验证代码逻辑CPU 也能跑通本文的最小实验但训练速度会慢很多。3.2 项目目录结构为了让代码清晰可维护建议按下面的结构组织项目molecular_transformer/ ├── data/ # 存放数据集 ├── models/ │ ├── __init__.py │ ├── attention.py # 自注意力、多头注意力 │ ├── encoder.py # Transformer Encoder 相关模块 │ └── predictor.py # 分子性质预测模型 ├── utils/ │ ├── __init__.py │ ├── smiles_tokenizer.py # SMILES 分词 │ └── dataset.py # 数据加载与预处理 ├── train.py # 训练脚本 └── requirements.txt # 依赖文件这个结构对初学者正好不复杂但也照顾到了模块划分。后面实战部分我会把核心代码按文件拆分说明。4. 手撕核心代码从自注意力到Encoder围绕 Transformer 的原理接下来我们从零实现核心组件。这些代码是教学版本去掉了很多工程优化细节但主干逻辑与论文保持一致。4.1 位置编码的实现文件路径models/attention.pyimport math import torch import torch.nn as nn class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len512): super().__init__() # 建议 d_model 使用偶数维度简化实现 pe torch.zeros(max_len, d_model) position torch.arange(0, max_len, dtypetorch.float).unsqueeze(1) div_term torch.exp( torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model) ) pe[:, 0::2] torch.sin(position * div_term) pe[:, 1::2] torch.cos(position * div_term) pe pe.unsqueeze(0) # 形状: (1, max_len, d_model) self.register_buffer(pe, pe) def forward(self, x): # x 形状: (batch, seq_len, d_model) return x self.pe[:, : x.size(1)]这里需要注意一个细节register_buffer注册的张量会随模型一起保存到 checkpoint但不会被当作模型参数更新。在分子建模中位置编码的作用是给模型提供 token 位置信息所以直接加在嵌入向量上即可。4.2 缩放点积注意力的实现继续在models/attention.py中添加代码。class ScaledDotProductAttention(nn.Module): def __init__(self, dropout0.0): super().__init__() self.dropout nn.Dropout(dropout) self.softmax nn.Softmax(dim-1) def forward(self, q, k, v, maskNone): # q, k, v 形状: (batch, n_heads, seq_len, d_k) d_k q.size(-1) scores torch.matmul(q, k.transpose(-2, -1)) scores scores / math.sqrt(d_k) if mask is not None: scores scores.masked_fill(mask 0, -1e9) attn self.softmax(scores) attn self.dropout(attn) out torch.matmul(attn, v) return out, attnmask 的作用是屏蔽无效位置。在分子序列中padding 位置不应该参与注意力计算所以要把这些位置的分数设为一个很大的负数softmax 之后权重会趋近于 0。4.3 多头注意力的实现多头注意力的核心是把 d_model 维输入拆成 n_heads 个 d_k 维子空间分别做注意力最后再拼接。class MultiHeadAttention(nn.Module): def __init__(self, d_model, n_heads, dropout0.1): super().__init__() assert d_model % n_heads 0 self.d_model d_model self.n_heads n_heads self.d_k d_model // n_heads self.w_q nn.Linear(d_model, d_model) self.w_k nn.Linear(d_model, d_model) self.w_v nn.Linear(d_model, d_model) self.w_o nn.Linear(d_model, d_model) self.attention ScaledDotProductAttention(dropout) def forward(self, q, k, v, maskNone): batch_size q.size(0) Q self.w_q(q).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) K self.w_k(k).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) V self.w_v(v).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2) out, attn self.attention(Q, K, V, mask) out out.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model) out self.w_o(out) return out这段代码有两个容易出错的地方。第一view之前必须保证张量内存连续所以从transpose后回到原始形状时要调用contiguous()。第二Q、K、V 的维度变换逻辑是先通过线性层把 d_model 维映射到 d_model 维然后拆成 n_heads 个 d_k 维。注意view和transpose的先后顺序先拆头再交换维度让头维度排在 batch 之后。4.4 单层Encoder的实现文件路径models/encoder.pyimport torch.nn as nn from models.attention import MultiHeadAttention class TransformerEncoderLayer(nn.Module): def __init__(self, d_model, n_heads, d_ff2048, dropout0.1): super().__init__() self.self_attn MultiHeadAttention(d_model, n_heads, dropout) self.feed_forward nn.Sequential( nn.Linear(d_model, d_ff), nn.ReLU(), nn.Linear(d_ff, d_model), ) self.norm1 nn.LayerNorm(d_model) self.norm2 nn.LayerNorm(d_model) self.dropout1 nn.Dropout(dropout) self.dropout2 nn.Dropout(dropout) def forward(self, x, maskNone): # Post-Norm 结构与原始论文一致 x x self.dropout1(self.self_attn(x, x, x, mask)) x self.norm1(x) x x self.dropout2(self.feed_forward(x)) x self.norm2(x) return x这里使用的是 Post-Norm 结构。如果你发现深层模型训练不稳定可以把结构改成 Pre-Norm也就是先 LayerNorm 再进子层残差连接不变。在分子建模项目中我们通常还需要一个生成 padding mask 的函数def make_padding_mask(x, pad_idx0): # x 形状: (batch, seq_len) return (x ! pad_idx).unsqueeze(1).unsqueeze(2)生成的 mask 形状是(batch, 1, 1, seq_len)可以广播到注意力分数矩阵(batch, n_heads, seq_len, seq_len)。5. 分子建模实战用Transformer预测分子性质掌握了核心组件之后我们来做一个完整的分子性质预测实验。任务设定为给定一个分子的 SMILES 表示预测一个连续性质值比如 LogP。5.1 SMILES序列与tokenization文件路径utils/smiles_tokenizer.pySMILES 字符串不能简单按字符切分。比如Cl是一个氯原子如果拆成C和l就会出错。所以需要一个简单的原子级分词器import re ATOM_TOKEN_REGEX r(\[[^\]]]|Br?|Cl?|N|O|S|P|F|I|b|c|n|o|s|p|\(|\)|\.||#|-|\|\\|\/|:|~||\?|?|\*|\$|\%[0-9]{2}|[0-9]) def tokenize_smiles(smiles): return re.findall(ATOM_TOKEN_REGEX, smiles) if __name__ __main__: print(tokenize_smiles(CCO)) print(tokenize_smiles(c1ccccc1)) print(tokenize_smiles(CC(O)O))预期输出大概是[C, C, O] [c, 1, c, c, c, c, c, 1] [C, C, (, , O, ), O]这个分词器是教学用的简化版本已经能处理大多数常见 SMILES但不覆盖所有边界情况。实际项目建议使用datamol或tokenizers库中的预训练分子分词器。5.2 构建词表与数据加载文件路径utils/dataset.py词表的作用是把 token 映射成整数 ID。需要加入特殊 tokendef build_vocab(smiles_list): vocab {pad: 0, unk: 1, cls: 2, eos: 3} tokens set() for smiles in smiles_list: tokens.update(tokenize_smiles(smiles)) for token in sorted(tokens): if token not in vocab: vocab[token] len(vocab) return vocab def encode_smiles(smiles, vocab, max_len128): tokens tokenize_smiles(smiles) ids [vocab.get(token, vocab[unk]) for token in tokens] ids ids[: max_len - 2] ids [vocab[cls]] ids [vocab[eos]] ids ids [vocab[pad]] * (max_len - len(ids)) return idscls和eos是序列开始和结束标记pad用于把 batch 内序列对齐到相同长度。在池化阶段可以取cls位置的向量作为分子表示也可以对所有非 padding token 做平均池化。5.3 分子性质预测模型定义文件路径models/predictor.py把前面写的模块组合起来得到完整的分子性质预测模型import torch import torch.nn as nn from models.encoder import TransformerEncoderLayer, make_padding_mask from models.attention import PositionalEncoding class MoleculeTransformer(nn.Module): def __init__(self, vocab_size, d_model128, n_heads4, num_layers2, d_ff512, max_len128, dropout0.1): super().__init__() self.embedding nn.Embedding(vocab_size, d_model, padding_idx0) self.pos_encoding PositionalEncoding(d_model, max_len) self.encoder_layers nn.ModuleList([ TransformerEncoderLayer(d_model, n_heads, d_ff, dropout) for _ in range(num_layers) ]) self.reg_head nn.Sequential( nn.Linear(d_model, 32), nn.ReLU(), nn.Linear(32, 1), ) def forward(self, x): mask make_padding_mask(x, pad_idx0) h self.embedding(x) h self.pos_encoding(h) for layer in self.encoder_layers: h layer(h, mask) # 对非 padding 位置做平均池化 mask_expand mask.squeeze(1).squeeze(1).unsqueeze(-1).float() h (h * mask_expand).sum(dim1) / mask_expand.sum(dim1) return self.reg_head(h)这里我用平均池化代替cls池化原因是平均池化在短序列和长序列之间更稳定不容易丢失信息。5.4 训练循环与验证文件路径train.py训练前需要构造一个极简数据集。为了演示这里用随机 SMILES 列表代替真实数据实际项目中应该使用真实分子数据集并做数据清洗import torch from torch.utils.data import DataLoader, Dataset from models.predictor import MoleculeTransformer from utils.dataset import build_vocab, encode_smiles from utils.smiles_tokenizer import tokenize_smiles # 这里用随机 SMILES 演示流程 sample_smiles [ CCO, CC(O)O, c1ccccc1, CCN, CCOC, CC(C)C, C1CCCCC1, c1ccncc1, CC#N, CCO ] sample_labels [1.2, 0.5, 2.3, 1.8, 2.1, 1.5, 2.8, 2.0, 0.8, 1.0] class MoleculeDataset(Dataset): def __init__(self, smiles_list, labels, max_len64): self.vocab build_vocab(smiles_list) self.data [encode_smiles(s, self.vocab, max_len) for s in smiles_list] self.labels labels def __len__(self): return len(self.data) def __getitem__(self, idx): return torch.tensor(self.data[idx]), torch.tensor(self.labels[idx], dtypetorch.float) dataset MoleculeDataset(sample_smiles, sample_labels) dataloader DataLoader(dataset, batch_size4, shuffleTrue) model MoleculeTransformer(len(dataset.vocab)) optimizer torch.optim.AdamW(model.parameters(), lr1e-4) loss_fn torch.nn.MSELoss() for epoch in range(50): model.train() total_loss 0.0 for batch_x, batch_y in dataloader: optimizer.zero_grad() pred model(batch_x).squeeze(-1) loss loss_fn(pred, batch_y) loss.backward() optimizer.step() total_loss loss.item() if (epoch 1) % 10 0: print(fepoch {epoch 1}, loss {total_loss / len(dataloader):.4f})运行后你会看到 loss 随着 epoch 逐渐下降。这个最小实验的意义不是追求指标而是验证整条链路已经打通SMILES 分词、词表构建、编码、模型前向传播、反向传播。真正做分子性质预测时有几个点必须注意第一对回归目标做标准化。LogP、溶解度等数值尺度差异很大不标准化容易导致训练不稳定。第二使用科学划分方式。分子数据不能随机打散否则训练集和测试集可能出现高度相似的分子导致评估结果虚高。常用的划分是 scaffold split按分子骨架划分。第三数据量太少时不要指望模型学出真实化学规律。小实验只适合验证代码要得出可信结论需要上千甚至上万条数据。6. 论文复现方法论从论文到可复现代码很多读者卡在“读得懂论文写不出代码更复现不出结果”。这里分享一套可执行的论文复现路径。6.1 如何选择复现的论文对于化学AI 方向的初学者选择复现目标时建议满足以下条件论文附带了官方开源代码避免在没有参照的情况下从零 DEBUG。数据集公开可下载数据规模不能太大最好单机单卡能跑通。模型结构清晰不是十几个模块堆叠在一起的巨型系统。论文有明确的基线对比方便你验证自己的复现是否正确。满足这些条件的论文通常出现在分子性质预测、SMILES 生成、化学反应产率预测等方向。大模型预训练类论文的复现成本很高适合有一定经验后再尝试。6.2 数据划分与评价指标复现论文时数据划分方式直接决定结果的可靠性。分子建模领域最常见的划分方式包括Random split随机划分训练集、验证集、测试集。实现简单但可能高估模型泛化能力。Scaffold split基于 Bemis-Murcko 骨架划分保证训练集和测试集骨架分布不同更能体现模型对新骨架的泛化能力。Time split按时间顺序划分适合反应预测等有时序特征的任务。评价指标方面回归任务常用 RMSE、MAE、R²分类任务常用 ROC-AUC、PR-AUC。复现时不要只看论文里最漂亮的指标要确认它用的是哪种划分和哪套指标。6.3 基线与消融实验复现论文时只跑通主模型是不够的。严谨的复现应该包含基线和消融实验。基线实验的意义是回答“Transformer 比传统方法好在哪里”。比如在分子性质预测中你可以跑一个随机森林、一个 GNN再跑 Transformer对比结果。消融实验的意义是回答“每个模块分别贡献了多少”。你可以尝试以下改动去掉位置编码观察指标变化。把多头注意力改成单头注意力观察效果。把 Transformer 换成 BiLSTM验证自注意力是否真的有用。调整注意力头数、层数、隐藏维度观察超参敏感性。这些实验能帮你真正理解论文中的每一个设计决策而不是机械地照抄代码。6.4 复现中的常见坑复现效果不如论文时先按以下顺序排查第一数据预处理是否一致。SMILES 是否做了标准化是否排除了盐和混合物是否使用相同的 tokenizer这些细节对结果影响很大。第二随机种子是否固定。Transformer 存在随机初始化不同种子可能带来明显差异。论文通常报告多次实验的均值单次实验低于论文结果是正常的。第三超参数是否一致。学习率调度、warmup 步数、batch size 都会影响最终效果。尤其 batch size 变化时学习率往往需要同步调整。第四算力不足时是否缩小了模型。如果你为了省显存把 d_model 从 512 降到 128那得到的结果就不能直接和论文作数值对比。7. 常见问题与排查思路在实际操作过程中新手最常遇到的几类问题如下问题现象常见原因解决思路训练 loss 变成 NaN学习率过大、梯度爆炸、输入数据包含 NaN调低学习率加入梯度裁剪检查输入特征显存 OOM序列过长、batch size 过大、注意力复杂度 O(n²)减小 batch限制最大序列长度使用梯度累积模型不收敛回归目标未标准化、学习率不合适、数据量太少对标签做标准化使用 warmup 学习率增加数据复现结果与论文差距大数据划分不同、预处理不一致、随机种子差异对齐数据划分和预处理固定种子多次实验取均值SMILES 解析报错RDKit 版本差异、SMILES 本身非法用 RDKit 校验并过滤非法分子统一规范化规则注意力权重全一样模型未充分训练、softmax 温度问题增加训练轮数检查学习方法可视化注意力热力图逐个展开说一下几个重点。7.1 训练 loss 变成 NaN这是最常见也最吓人的问题。原因通常是学习率过大导致梯度更新步长过大参数跑到数值溢出区域。解决思路是降低学习率比如从 1e-3 改成 1e-4同时给模型增加梯度裁剪torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)另外如果你对回归标签做了 log 变换或标准化也要检查处理后是否出现无穷大值。7.2 显存 OOMTransformer 自注意力的空间复杂度是 O(n²)n 是序列长度。SMILES 序列虽然通常不长但如果 dataset 中存在特别长的分子一个 batch 内长度差异过大就会导致显存爆炸。解决思路有三个方向限制最大序列长度比如截断到 256 或 512。减小 batch size。使用梯度累积模拟大 batchaccumulation_steps 4 for step, (batch_x, batch_y) in enumerate(dataloader): loss loss_fn(model(batch_x), batch_y) loss loss / accumulation_steps loss.backward() if (step 1) % accumulation_steps 0: optimizer.step() optimizer.zero_grad()7.3 模型不收敛模型不收敛的情况很常见尤其在分子性质预测的回归任务中。最容易被忽略的问题是标签没有标准化。LogP 的取值范围可能在 -2 到 6 之间不同数据集尺度差异很大。建议在训练前计算训练集标签的均值和标准差mean train_labels.mean() std train_labels.std() train_labels (train_labels - mean) / std推理时再把预测值反标准化回原始尺度。这个简单的处理往往能让模型收敛速度大幅提升。8. 学习路径与工程建议8.1 推荐学习路径如果你是从零开始进入化学AI 方向可以参考下面的路径第一阶段是机器学习与深度学习基础。你需要理解反向传播、损失函数、优化器、正则化这些概念。不需要数学推导得特别深但要知道它们的作用。第二阶段是序列建模。从 RNN、LSTM 入手理解序列模型的基本思想然后学习注意力机制最后进入 Transformer。建议跟着 PyTorch 官方教程实现一遍 Transformer再回到本文的分子场景。第三阶段是化学基础知识。掌握 SMILES 语法、分子图的概念、常见分子描述符会用 RDKit 读取和处理分子。不需要成为化学专家但要能理解数据字段的含义。第四阶段是分子建模任务实践。从分子性质预测开始逐步扩展到分子生成、化学反应预测。每个任务跑通一个最小实验再去看经典论文。第五阶段是预训练模型与大模型。在完成前面阶段后可以研究 ChemBERTa、MolFormer、Uni-Mol 等模型理解它们如何在 Transformer 基础上改进。8.2 工程化建议在真实项目中有几条工程经验值得提前记住。依赖锁定很重要。分子处理库、深度学习框架、CUDA 版本之间经常互相影响。建议使用 conda 或 venv 创建独立环境并导出 requirements.txt 或 environment.yml保证复现环境一致。实验记录不能靠脑子。每个实验至少记录数据集版本、数据划分方式、超参数、随机种子、训练日志、最终指标。推荐使用 wandb 或简单的 CSV 记录否则几天后就分不清哪组配置对应哪个结果。模型与数据分离。数据清洗脚本、模型定义、训练脚本应该拆开避免在一个文件里越写越长。后续换数据集或换模型时维护成本会低很多。关于分子数据合规尤其涉及药物或化合物数据时要确认数据来源合法遵守开源协议。如果涉及企业私有数据注意脱敏和权限管理。8.3 下一步进阶方向当你跑通本文的最小分子性质预测实验后可以继续挑战下面的方向替换成真实数据集比如 ESOL、FreeSolv、Lipophilicity验证模型在标准数据上的表现。把模型从性质预测扩展到分子生成使用 Transformer Decoder 逐 token 生成 SMILES。引入 3D 信息给注意力加上距离偏置尝试理解 Uni-Mol 这类三维分子建模方法。研究预训练策略用大规模无标注分子做自监督预训练再在下游任务微调。这篇文章不可能覆盖所有内容但它给了你一个能落地的起点。动手建议是
返回列表