--- license: mit metrics: - accuracy pipeline_tag: image-to-text tags: - chemistry --- # MS²-SMILES AlignNet 模型文档 ## 📋 目录 1. [概述](#概述) 2. [数据处理流程](#数据处理流程) 3. [模型架构](#模型架构) 4. [损失函数设计](#损失函数设计) 5. [训练流程](#训练流程) 6. [配置参数](#配置参数) --- ## 概述 MS²-SMILES AlignNet 是一个基于**对比学习**的跨模态对齐模型,旨在学习质谱(MS²)数据与分子结构(SMILES)之间的语义对齐表示。该模型采用双分支架构,分别编码 MS 谱图和分子结构,并通过对比学习使得匹配的 MS-分子对在嵌入空间中靠近。 ### 核心思想 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ MS²-SMILES AlignNet 架构 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ SMILES ──→ [分子编码分支] ──→ 256维向量 ─┐ │ │ ├──→ 对比学习 ──→ 对齐损失 │ │ MS谱图 ──→ [MS编码分支] ──→ 256维向量 ──┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` --- ## 数据处理流程 ### 2.1 数据加载入口 **文件**: `train.py` (第174-175行) ```python train_loader = build_loaders(train_set, "train", cfg, 10) valid_loader = build_loaders(valid_set, "valid", cfg, 10) ``` 数据支持两种输入格式: - **JSON/MGF 文件列表**: 使用 `PathDataset` 类 - **预处理字典列表**: 使用 `Dataset` 类 ### 2.2 原始数据获取 **文件**: `dataset.py` (第103-128行) ``` PathDataset ├── proc_data(): 解析 JSON/MGF 文件 │ └── 提取: ms (质谱峰列表), smiles (分子结构) │ └── __getitem__(): 调用 calc_feats() 计算特征 ``` **数据格式**: - `ms`: List of (m/z, intensity) 元组 - `smiles`: 分子 SMILES 字符串 ### 2.3 特征预处理 **文件**: `dataset.py` (第7-41行) - `calc_feats()` 函数 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ 特征计算流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 输入: smi (SMILES), ms (质谱), nls (中性丢失) │ │ │ │ ┌────────────────────────┐ ┌────────────────────────────┐ │ │ │ MS 特征处理 │ │ 分子特征处理 │ │ │ ├────────────────────────┤ ├────────────────────────────┤ │ │ │ 1. ms_binner() │ │ 1. mol_fp_encoder() │ │ │ │ → ms_bins │ │ → mol_fps (指纹) │ │ │ │ (39600维稀疏向量) │ │ (2048维) │ │ │ │ │ │ │ │ │ │ 2. ms_feature_processor│ │ 2. mol_graph_featurizer() │ │ │ │ → ms_bins1 (100,29) │ │ → V (节点特征) │ │ │ │ → ms_bins2 (100,) │ │ → A (邻接矩阵) │ │ │ │ (峰特征 + m/z值) │ │ → mol_size │ │ │ └────────────────────────┘ └────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` #### 2.3.1 MS 特征处理详解 **方法一: `ms_binner()` - 稀疏分桶表示** ```python # 参数配置 min_mz = 20, max_mz = 2000, bin_size = 0.05 # 计算: (2000-20)/0.05 = 39600 bins # 流程: 1. 过滤 m/z 范围外的峰 2. 归一化强度 (0-1) 3. 分桶 (bin_idx = (m/z - min_mz) / bin_size) 4. 可选: 添加中性丢失特征 (额外 4000 bins) ``` **方法二: `ms_feature_processor()` - 序列化峰表示** ```python 输入: ms, precursor_mz, metadata_vec(135维) 输出: - ms_bins1: (100,135) 峰特征矩阵 - ms_bins2: (100,) m/z 值序列 ## 135 维全局特征 = 仪器(20) + 加合物(21) + 碰撞能量(5) + 电荷电离(4) + 诊断离子(46) + 中性丢失(39) 线性变换 → 256 维模型输 ``` #### 2.3.2 分子特征处理详解 **Morgan 指纹编码**: ```python mol_fp_encoder(smiles, tp='morgan', nbits=2048) # → 2048 维二进制向量 ``` **分子图特征化**: ```python mol_graph_featurizer(smiles) # → V: 节点特征矩阵 (N_atoms, 74) # → A: 邻接张量 (N_atoms, 6, N_atoms) # 6 通道 = 4 键类型 + 环内 + 共轭 # → mol_size: 原子数量 ``` **原子节点特征 (74维)**: | 特征类型 | 维度 | 说明 | |---------|------|------| | 原子类型 | 12 | One-hot: H,C,N,O,S,F,Si,P,Cl,Br,I,B | | 显式价态 | 7 | One-hot: 0-6 | | 隐式价态 | 7 | One-hot: 0-6 | | 氢原子数 | 5 | One-hot: 0-4 | | 自由基电子 | 5 | One-hot: 0-4 | | 总度数 | 7 | One-hot: 0-6 | | 形式电荷 | 5 | One-hot: -2 to 2 | | 杂化类型 | 5 | SP,SP2,SP3,SP3D,SP3D2 | | 芳香性 | 2 | True/False | | 环内原子 | 2 | True/False | | 手性 | 4 | 4种类型 | | CIP编码 | 2 | R/S | | 范德华半径 | 1 | 连续值 | | 手性可能性 | 1 | True/False | | 原子序数 | 1 | 连续值 | | 原子质量 | 1 | 连续值 × 0.01 | | 度数 | 1 | 连续值 | | 特征不变量 | 6 | 6位二进制编码 | ### 2.4 数据批处理聚合 **文件**: `train.py` (第36-83行) - `my_collate()` 函数 ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ Batch 聚合流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ 输入: [sample_1, sample_2, ..., sample_B] (B = batch_size) │ │ │ │ 1. 过滤 None 样本 │ │ │ │ 2. 堆叠各特征: │ │ ├── ms_bins: stack → (B, 39600+4000) │ │ ├── ms_bins1: stack → (B, 100, 135) │ │ ├── ms_bins2: stack → (B, 100) │ │ ├── mol_fps: stack → (B, 2048) │ │ └── mol_fmvec: stack → (B, 10) [可选: 分子式向量] │ │ │ │ 3. 图数据 Padding: │ │ ├── 计算 max_n = max(所有分子的原子数) │ │ ├── V: pad → (B, max_n, 74) │ │ ├── A: pad → (B, max_n, 6, max_n) │ │ └── mol_size: concat → (B,) │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` --- ## 模型架构 **文件**: `modules.py` (第298-457行) - `FragSimiModelNew` 类 ### 3.1 整体架构图 ``` ┌─────────────────────────────────────────────────────────────────────────────────────────┐ │ FragSimiModelNew 双分支架构 │ ├─────────────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────┐ ┌──────────────────────────────────────────┐ │ │ │ 分子编码分支 (LEFT) │ │ MS 编码分支 (RIGHT) │ │ │ ├──────────────────────────────────┤ ├──────────────────────────────────────────┤ │ │ │ │ │ │ │ │ │ SMILES │ │ MS 谱图 │ │ │ │ │ │ │ │ │ │ │ │ ▼ │ │ ▼ │ │ │ │ ┌─────────────┐ │ │ ┌─────────────────────────────────────┐│ │ │ │ │ RDKit 解析 │ │ │ │ 分支A: ms_bins (稀疏分桶) ││ │ │ │ └──────┬──────┘ │ │ │ │ ││ │ │ │ │ │ │ │ ▼ ms_input_proj (Linear) ││ │ │ │ ┌────┴────┐ │ │ │ │ ││ │ │ │ ▼ ▼ │ │ │ ▼ 6层 Transformer ││ │ │ │ ┌──────┐ ┌───────┐ │ │ │ │ ││ │ │ │ │ 分子图 │ │ Morgan│ │ │ │ ▼ Mean Pooling ││ │ │ │ │构建 │ │ 指纹 │ │ │ │ │ ││ │ │ │ └──┬───┘ └───┬───┘ │ │ │ ▼ ms_final_proj ││ │ │ │ │ │ │ │ │ └───→ 256维 ││ │ │ │ ▼ │ │ │ └─────────────────────────────────────┘│ │ │ │ ┌──────────┐ │ │ │ │ │ │ │ │ │MolGNN │ │ │ │ ▼ │ │ │ │ │Encoder │ │ │ │ ┌─────────────────────────────────────┐│ │ │ │ │(3层GCN │ │ │ │ │ 分支B: ms_bins1/2 (峰序列) ││ │ │ │ │+Attention│ │ │ │ │ │ ││ │ │ │ │+Readout) │ │ │ │ │ ▼ SinusoidalMzEmbedding ││ │ │ │ └────┬─────┘ │ │ │ │ + feature_proj (135→256) ││ │ │ │ │ │ │ │ │ │ ││ │ │ │ ▼ ▼ │ │ │ ▼ 6层 Transformer ││ │ │ │ ┌──────────────────┐ │ │ │ │ ││ │ │ │ │ MolFusionHead │ │ │ │ ▼ Mean Pooling ││ │ │ │ │ (GNN→128, FP→256)│ │ │ │ │ ││ │ │ │ │ → Concat → 256 │ │ │ │ ▼ final_proj ││ │ │ │ └────────┬─────────┘ │ │ │ └───→ 256维 ││ │ │ │ │ │ │ └─────────────────────────────────────┘│ │ │ │ ▼ │ │ │ │ │ │ │ 256维向量 │ │ ▼ │ │ │ │ │ │ ┌─────────────────────────────────────┐│ │ │ │ │ │ │ Concat (256+256=512) ││ │ │ │ │ │ │ │ ││ │ │ │ │ │ │ ▼ ││ │ │ │ │ │ │ all_final_proj → 256维 ││ │ │ │ │ │ └─────────────────────────────────────┘│ │ │ └──────────────────────────────────┘ └──────────────────────────────────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ mol_embeddings (256) ms_embeddings (256) │ │ │ │ │ │ └───────────────────┬───────────────────────┘ │ │ ▼ │ │ ┌───────────────────────┐ │ │ │ L2 Normalize │ │ │ └───────────┬───────────┘ │ │ ▼ │ │ ┌───────────────────────┐ │ │ │ HybridAlignLoss │ │ │ │ (InfoNCE + Tanimoto) │ │ │ └───────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────────────┘ ``` ### 3.2 分子编码分支 #### 3.2.1 MolGNNEncoder ```python class MolGNNEncoder(nn.Module): # 输入: V (B, N, 74), A (B, N, 6, N), mol_size (B,) # 输出: (B, mol_embedding_dim) 结构: ├── block_layers: 3 × GConvBlockNoGF │ └── GraphCNNLayer: 图卷积 + BatchNorm + ReLU │ [74 → 256 → 256 → 256] │ ├── attention_layer: MultiHeadGlobalAttention (4 heads) │ └── 对节点进行加权聚合,去除 padding 影响 │ [256 → 256×4 = 1024] │ └── readout_layers: 2 × Linear + GELU └── [1024 → 2048 → 2048] ``` #### 3.2.2 MolFusionHead ```python class MolFusionHead(nn.Module): # 输入: gnn_feat (B, 2048), fps (B, 2048) # 输出: (B, 256) 结构: ├── gnn_proj: Linear(2048 → 128) │ ├── fp_proj: Linear(2048 → 256) + LayerNorm + GELU │ └── fusion_layer: ├── Concat: [128, 256] → 384 └── Linear(384 → 256) + LayerNorm + Dropout + GELU ``` ### 3.3 MS 编码分支 #### 3.3.1 分支A: 稀疏分桶编码 ```python # 输入: ms_bins (B, 43600) # 输出: 256维 流程: 1. ms_input_proj: Linear(43600 → 256) 2. unsqueeze → (B, 1, 256) [假序列] 3. ms_transformer: 6层 Transformer (width=256, heads=8) 4. mean(dim=1) → (B, 256) 5. ms_final_proj: Linear + LayerNorm + Dropout → 256维 ``` #### 3.3.2 分支B: 峰序列编码 ```python # 输入: ms_bins1 (B, 100, 29), ms_bins2 (B, 100) # 输出: 256维 流程: 1. mz_embedder(ms_bins2) → (B, 100, 256) # 正弦位置编码 2. feature_proj(ms_bins1) → (B, 100, 256) # 峰特征投影 3. 相加融合 → (B, 100, 256) 4. transformer: 6层 Transformer 5. mean(dim=1) → (B, 256) 6. final_proj: Linear + LayerNorm + Dropout → 256维 ``` #### 3.3.3 正弦 m/z 嵌入 ```python class SinusoidalMzEmbedding(nn.Module): # 将 m/z 值编码为 256 维向量 # 类似 Transformer 的位置编码 PE[pos, 2i] = sin(pos / 10000^(2i/256)) PE[pos, 2i+1] = cos(pos / 10000^(2i/256)) ``` #### 3.3.4 双分支融合 ```python # 两个 MS 分支的输出融合 out1 = torch.cat([ms_embeddings_A, ms_embeddings_B], dim=-1) # (B, 512) ms_embeddings = all_final_proj(out1) # Linear(512→256) + LayerNorm + Dropout ``` ### 3.4 Transformer 结构 **文件**: `cliplayers.py` - 采用 CLIP 风格的 Transformer ```python class ResidualAttentionBlock(nn.Module): # 单个 Transformer Block 结构: ├── ln_1 → MultiheadAttention → 残差连接 │ └── ln_2 → MLP (Linear→QuickGELU→Linear) → 残差连接 # QuickGELU: x * sigmoid(1.702 * x) # 比标准 GELU 更快 ``` --- ## 损失函数设计 **文件**: `modules.py` (第196-233行) - `HybridAlignLoss` 类 ### 4.1 联合损失公式 $$\mathcal{L}_{total} = \beta \cdot \mathcal{L}_{InfoNCE} + \alpha \cdot \mathcal{L}_{Tanimoto-MSE}$$ **默认参数**: α = 0.5, β = 1.0 ### 4.2 InfoNCE 对比损失 ```python # 目标: 让匹配的 MS-分子对相似度高,不匹配的对相似度低 logits = (ms_emb @ mol_emb.T) / temperature # 相似度矩阵 labels = torch.arange(batch_size) # 对角线为正样本 loss_i2t = CrossEntropy(logits, labels) # MS → Mol loss_t2i = CrossEntropy(logits.T, labels) # Mol → MS L_InfoNCE = (loss_i2t + loss_t2i) / 2 ``` **温度参数**: τ = 0.07 ### 4.3 Tanimoto MSE 损失 ```python # 目标: 让嵌入空间的相似度矩阵逼近真实的分子结构相似度 # 1. 预测的相似度矩阵 (余弦相似度) pred_sim = ms_emb @ mol_emb.T # 因为已 L2 normalize # 2. 真实的结构相似度 (Tanimoto 系数) # Tanimoto(A,B) = (A·B) / (|A|² + |B|² - A·B) target_sim = batch_tanimoto_sim(mol_fps, mol_fps) # 3. MSE 损失 L_MSE = MSELoss(pred_sim, target_sim) ``` ### 4.4 损失设计的意义 | 损失组件 | 作用 | 特点 | |---------|------|------| | InfoNCE | 跨模态对齐 | 区分正负样本对,对比学习核心 | | Tanimoto MSE | 结构约束 | 保持分子结构相似性关系 | ``` ┌─────────────────────────────────────────────────────────────────┐ │ 联合损失的几何意义 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ InfoNCE: "匹配的 MS-Mol 对要靠近" │ │ │ │ MS₁ ←──── 拉近 ────→ Mol₁ │ │ MS₂ ←──── 拉近 ────→ Mol₂ │ │ ↑ ↑ │ │ 推远 推远 │ │ ↓ ↓ │ │ MS₃ ←──── 拉近 ────→ Mol₃ │ │ │ │ Tanimoto MSE: "结构相似的分子在嵌入空间也要相似" │ │ │ │ 如果 Tanimoto(Mol₁, Mol₂) = 0.8 │ │ 则希望 CosSim(Emb₁, Emb₂) ≈ 0.8 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 训练流程 **文件**: `train.py` ### 5.1 训练入口 ```python # main() 函数 1. 数据划分: make_train_valid(data, valid_ratio=0.1) 2. 构建 DataLoader: build_loaders() 3. 初始化模型: FragSimiModelNew(cfg).to(device) 4. 优化器: AdamW(lr=1e-3, weight_decay=1e-3) 5. 学习率调度: ReduceLROnPlateau(patience=2, factor=0.5) 6. 训练循环: epochs=50 ``` ### 5.2 训练周期 ```python def train_epoch(model, train_loader, optimizer, lr_scheduler, step): for batch in train_loader: # 1. 数据移到 GPU batch = {k: v.to(device) for k, v in batch.items()} # 2. 前向传播 loss = model(batch) # 返回总损失 # 3. 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() ``` ### 5.3 模型保存策略 ```python # 保存最佳模型 (基于验证集损失) if valid_loss < best_loss: checkpoint = { 'state_dict': model.state_dict(), 'optimizer': optimizer.state_dict(), 'config': dict(CFG) } torch.save(checkpoint, best_model_fn) # 只保留最新的 3 个最佳模型 keep_best_models_num = 3 ``` --- ## 配置参数 **文件**: `config.py` ### 6.1 核心参数 | 参数 | 默认值 | 说明 | |------|-------|------| | `batch_size` | 64 | 批次大小 | | `lr` | 1e-3 | 学习率 | | `epochs` | 50 | 训练轮数 | | `dropout` | 0.1 | Dropout 比例 | | `projection_dim` | 256 | 最终嵌入维度 | ### 6.2 MS 相关参数 | 参数 | 默认值 | 说明 | |------|-------|------| | `min_mz` | 20 | 最小 m/z | | `max_mz` | 2000 | 最大 m/z | | `bin_size` | 0.05 | 分桶大小 | | `add_nl` | True | 是否添加中性丢失特征 | | `binary_intn` | False | 是否二值化强度 | ### 6.3 分子编码参数 | 参数 | 默认值 | 说明 | |------|-------|------| | `mol_encoder` | 'gnn+fp' | 编码方式 (fp/gnn/gnn+fp) | | `mol_embedding_dim` | 2048 | Morgan 指纹维度 | | `fptype` | 'morgan' | 指纹类型 | | `molgnn_n_filters_list` | [256,256,256] | GNN 各层维度 | | `molgnn_nhead` | 4 | 注意力头数 | | `molgnn_readout_layers` | 2 | Readout 层数 | ### 6.4 Transformer 参数 | 参数 | 默认值 | 说明 | |------|-------|------| | `tsfm_layers` | 6 | Transformer 层数 | | `tsfm_heads` | 8 | 注意力头数 | | `tsfm_in_ms` | True | MS 分支使用 Transformer | | `tsfm_in_mol` | False | 分子分支使用 Transformer | --- ## 附录: 文件结构 ``` train-001/ ├── train.py # 训练入口 ├── dataset.py # 数据集定义 ├── modules.py # 模型定义 (FragSimiModelNew) ├── config.py # 配置参数 ├── utils.py # 工具函数 (特征处理等) ├── cliplayers.py # CLIP 风格的 Transformer └── GNN/ ├── layers.py # 图卷积层 └── featurizer.py # 分子图特征化 ``` --- ## 参考 本模型设计参考了以下工作: - CLIP (Contrastive Language-Image Pre-training) - Graph Attention Networks (GAT) - Morgan Fingerprints (Extended Connectivity Fingerprints) - Tanimoto Coefficient for molecular similarity ---