Skip to content

【Hackathon 10th Spring No.10】ECDFormer模型复现任务#245

Open
PlumBlossomMaid wants to merge 23 commits intoPaddlePaddle:developfrom
PlumBlossomMaid:ECFormer-Model
Open

【Hackathon 10th Spring No.10】ECDFormer模型复现任务#245
PlumBlossomMaid wants to merge 23 commits intoPaddlePaddle:developfrom
PlumBlossomMaid:ECFormer-Model

Conversation

@PlumBlossomMaid
Copy link
Copy Markdown

@PlumBlossomMaid PlumBlossomMaid commented Feb 24, 2026

📌 项目介绍

[used AI Studio] 本PR是ECDFormer模型复现任务的核心代码合入申请。ECDFormer(Nature Computational Science 2025)通过“解耦峰属性学习”实现高效、可解释的ECD/IR光谱预测。

目前已根据第一次Review意见完成PR合并版权声明添加,现根据第二次Review意见整理待办清单如下。

✅ 工作进展

已完成

  • 将PR-245与PR-246合并,关闭PR-246
  • 为所有新增文件添加Paddle版权声明

进行中

  • loss/metrics迁移:将utils/loss/下的损失函数移至ppmat/models/losses/公共模块
  • utils工具迁移:将通用工具函数提取到ppmat/utils/公共目录
  • 补充材料提供
    • 提供ECD/IR数据集下载链接
    • 上传预训练模型权重及训练日志文件(如果有)
  • 数据集工厂函数改造:参考build_spectrum的实现方式,重构数据集加载逻辑
  • 补完训练脚本与推理脚本: 根据上述已经固定的API,仿照DiffNMR训练脚本的风格,在spectrum_prediction\ECFormer目录下补充适用于ECFormer的训练与推理脚本
  • 代码注释英文化:将所有.py文件的注释、docstring修改为英文
  • README文档完善
    • spectrum_prediction/README.md添加ECFormer系列模型说明
    • 新建spectrum_prediction/ECFormer/README.md,包含模型说明、复现精度
    • 参考其他任务(如DiffNMR)完善文档格式
  • RFC文档同步更新:根据最终代码实现,更新之前提交的RFC设计文档

🔗 相关链接


感谢Reviewer的耐心指导,所有待办将按清单顺序依次推进。

@paddle-bot
Copy link
Copy Markdown

paddle-bot Bot commented Feb 24, 2026

Thanks for your contribution!

@paddle-bot paddle-bot Bot added the contributor External developers label Feb 24, 2026
Removed duplicate import of OMol25Dataset.
@leeleolay
Copy link
Copy Markdown
Collaborator

leeleolay commented Feb 26, 2026

@PlumBlossomMaid 感谢您的代码贡献,有如下合入规范的建议辛苦参考:辛苦将本PR和PR-246合并为一个,代码注释修改为英文,并在每个文件前添加Paddle权利声明,数据集的加载辛苦使用build的工厂函数方式,utils里面的内容可以提取到model外ppmat里相应的部分,在spectrunm elucidation里面的readme添加模型说明,并且添加模型readme页面并且声明模型复现效果,辛苦提供数据集链接和预训练模型权重log文件

@PlumBlossomMaid PlumBlossomMaid changed the title 【Hackathon 10th Spring No.10】ECDFormer模型复现任务Models部分 【Hackathon 10th Spring No.10】ECDFormer模型复现任务 Feb 27, 2026
Copy link
Copy Markdown
Collaborator

@leeleolay leeleolay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

添加模型复现精度说明,并且补充readme文档,可以参考其他任务里的模型的情况。loss和metric的部分可以移动到ppmat下的公共模块部分

# limitations under the License.

"""
ECDFormer数据集加载模块
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释请修改为英文

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好的好的~近几天较忙,所有review中的要求我会依次进行~

Comment thread ppmat/datasets/build_ecd.py
@PlumBlossomMaid
Copy link
Copy Markdown
Author

IR数据集我发天翼云网盘了,请通过这里获取,访问码:7sax。
如果不方便注册账号,可以通过文件直链的形式进行下载,点击这里,文件直链有效期为30天。

@leeleolay
Copy link
Copy Markdown
Collaborator

IR数据集我发天翼云网盘了,请通过这里获取,访问码:7sax。 如果不方便注册账号,可以通过文件直链的形式进行下载,点击这里,文件直链有效期为30天。

https://paddle-org.bj.bcebos.com/paddlematerials/datasets/IR/IR.tar.gz 请参考这个链接 @PlumBlossomMaid

@PlumBlossomMaid
Copy link
Copy Markdown
Author

@leeleolay

麻烦帮忙看看 ECD 和 IR 数据集的工厂函数改造是否符合要求:

  • ECD 数据集:拆分为 ecformer_dataset.py + build_ecd.py(工厂函数+核心逻辑)
  • IR 数据集:同样模式拆分为 ir_dataset.py + build_ir.py

如果结构没问题,我就继续推进后续任务:

  • 补完训练/推理脚本
  • 注释英文化
  • README 文档完善
  • RFC 同步更新

多谢!

@leeleolay
Copy link
Copy Markdown
Collaborator

leeleolay commented Mar 12, 2026

注释comment辛苦修改为英文

@PlumBlossomMaid
Copy link
Copy Markdown
Author

注释comment辛苦修改为英文

好的好的,这次申请Review主要是想确认数据集代码格式没有问题。关于代码和项目的语言我会在最后统一修改。我将抽时间继续完成训练代码的编写。

@JinZongxiao
Copy link
Copy Markdown
Collaborator

感谢提交ECDFormer的复现代码!在本地进行测试时发现了以下几个问题,麻烦确认并修复一下:

1. 配置文件默认路径有误

根据 spectrum_elucidation/ecformer/train.py 中的代码实现,默认的 default config 路径写成了:
-c, default="./spectrum_elucidation/ecformer/configs/ecd.yaml"

但实际上,代码库里真正的 yaml 配置文件放在了 spectrum_elucidation/configs/ecformer/ecd.yaml(注意 configs 文件夹的层级位置不同)。
建议将 train.py 中的默认配置路径修正为正确的位置,否则不带 -c 参数直接运行 python spectrum_elucidation/ecformer/train.py 会因为找不到配置文件而报错。

2. 导入Paddle API报错 (AttributeError)

在运行测试时触发了以下异常:

AttributeError: module 'paddle.nn' has no attribute 'parameter'

定位: ppmat/models/ecformer/layers/rbf.py

原因分析: 在RBF类的类型提示中使用了 paddle.nn.parameter.Parameter

    def __init__(self,
                 centers: paddle.nn.parameter.Parameter,
                 gamma: paddle.nn.parameter.Parameter):

在较新版本的PaddlePaddle(我测试是用的3.0.0)中,官方对API进行了清理,paddle.nn.parameter这个路径不再被暴露或直接支持。

@PlumBlossomMaid
Copy link
Copy Markdown
Author

@JinZongxiao 感谢测试!我将会在接下来的15天内对评论内所描述的情况进行一一测试并修复。😊

@PlumBlossomMaid
Copy link
Copy Markdown
Author

PlumBlossomMaid commented Mar 17, 2026

@JinZongxiao 今天我抽空看了看第二个问题,似乎在最新版里面,paddle.nn.parameter.Parameter这个API是可用的:Paramter-API文档
此外,一开始我开发这个任务的时候用的paddle版本也是3.0.0,但是我发现3.0.0的飞桨版本会导致无法使用paddle_geometric,导入直接炸,所以我直接把本地的飞桨版本升级到3.4.0了:

PS C:\Users\15895> python
Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import paddle
>>> paddle.__version__
'3.4.0.dev20260305'
>>> paddle.nn.parameter.Parameter
<class 'paddle.base.framework.EagerParamBase'>
>>>

需要补充说明的是,在paddlepaddle 3.3.0中,官方似乎增加或修改了许多的用于兼容业界内主流用法的API,包括但不限于:

  1. paddle.Tensor.shape返回类型从list变成paddle.Size了,一个tuple类的子类;
  2. 我忘了,但是没了这个API,导入paddle_geometric会直接炸;
  3. paddle.nn.parameter.Parameter这个。

建议Reviewer在测试的时候使用最新版本的飞桨框架,因为通过这一次ECDFormer的复现,我光是Issue和PR就给框架提交了一大堆,目前有已经merge的PR(见Issue 78197中的内容),也有暂时顾不过来的(见Issue 78100)和PR(见PR 78307)。由于框架本身可能迭代也比较快,所以用最新版本的或许更可靠一些。
此外,由于感受到Reviewer对待我的劳动成果也是非常的用心,所以我也打算等我自己忙过来之后,对训练算法进行进一步的优化。等我优化了算法之后Reviewer再测试一遍即可。

@PlumBlossomMaid
Copy link
Copy Markdown
Author

重新整理了一下训练器的位置,优化了Logger的输出和增强了多卡训练的支持,同时修正了config文件的路径,直接cd进PaddleMaterials下的spectrum_elucidation\ecformer目录运行python train.py即可运行训练。

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的文件为什么移动位置哈,辛苦保证可兼容diffnmr的正常使用

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leeleolay

感谢review。

这个文件的移动是为了将不同模型的训练脚本分离,避免与ECFormer耦合。原train.py本就是DiffNMR专用的,现在移到diffnmr/目录下,DiffNMR的功能和用法完全不变,只是路径需要相应调整。

ECFormer作为独立模型,单独维护自己的训练脚本,这样两个模型各自独立,互不影响,也符合ppmat的设计惯例。

如果原路径的兼容性是必须的,我可以后续处理。谢谢!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是否可以做统一的兼容,每个任务下的train.py是为了支持单一任务的。如果不容易兼容,辛苦提供原因说明

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

关于ECFormer与DiffNMR训练脚本无法统一合并的技术说明

感谢Reviewer的认真审阅和宝贵意见。针对“是否可以做统一的兼容,每个任务下的train.py是为了支持单一任务”这一问题,经过详细的源码和配置对比分析,现说明ECFormer无法与现有DiffNMR训练脚本合并的原因。

一、任务本质差异

维度 DiffNMR ECFormer
任务类型 分子生成(生成式模型) 光谱预测(判别式模型)
核心功能 根据NMR谱图生成对应的分子结构 根据分子结构预测ECD/IR光谱的峰属性
输入输出 输入:分子图+NMR谱图 → 输出:生成的分子图 输入:分子图+几何增强特征 → 输出:峰数、峰位置、峰强度
训练范式 扩散去噪 + CLIP对比学习 多任务分类/回归(峰数、位置、强度)

二、依赖组件差异

DiffNMR独有的依赖(ECFormer完全不需要)

# DiffNMR train.py 第18-20行
from ppmat.datasets.msd_nmr_dataset import DataLoaderCollection
from ppmat.models.diffnmr.extra_features_graph import DummyExtraFeatures, ExtraFeatures
from ppmat.models.diffnmr.extra_features_molecular_graph import ExtraMolecularFeatures
from ppmat.utils.visualization import MolecularVisualization

ECFormer的依赖

# ECFormer train.py
from ppmat.trainer import ECFormerTrainer  # 自定义Trainer
# 不需要上述任何DiffNMR特有的依赖

三、数据加载与预处理流程差异

DiffNMR的复杂流程

# DiffNMR train.py 第95-128行
# 1. 需要DataLoaderCollection统一管理
dataloaders = DataLoaderCollection(train_loader, val_loader, test_loader)

# 2. 需要构建dataset_infos(包含分子统计信息)
dataset_infos = build_dataset_infos(
    dataloaders=dataloaders, cfg=config, recompute_statistics=False
)
train_smiles = dataset_infos.train_smiles

# 3. 需要extra_features和domain_features
if config.get("DataInfo", None) is not None:
    extra_features = ExtraFeatures(
        config["DataInfo"]["extra_features"],
        dataset_infos=dataset_infos,
    )
    domain_features = ExtraMolecularFeatures(
        dataset_infos=dataset_infos,
    )
    # 4. 需要计算输入输出维度
    dataset_infos.compute_input_output_dims(
        dataloader=fallback_loader,
        extra_features=extra_features,
        domain_features=domain_features,
        conditionDim=config["DataInfo"]["conditdim"],
    )

ECFormer的简洁流程

# ECFormer train.py
# 直接构建dataloader即可,无需上述任何中间步骤
if config["Global"].get("do_train", True):
    train_cfg = config["Dataset"].get("train")
    dataloaders["train"] = build_dataloader(train_cfg)

四、模型构建参数差异

DiffNMR需要传递大量额外参数

# DiffNMR train.py 第154-160行
model = build_model(
    model_cfg,
    extra_features=extra_features,      # ECFormer不需要
    domain_features=domain_features,     # ECFormer不需要
    dataset_infos=dataset_infos,         # ECFormer不需要
    visualization_tools=visualization_tools,  # ECFormer不需要
    clip=clip_module,                    # ECFormer不需要
)

ECFormer只需配置参数

# ECFormer train.py
model = build_model(model_cfg)  # 仅需配置参数

五、Trainer类差异

DiffNMR使用通用BaseTrainer

# DiffNMR train.py 第180-186行
trainer = BaseTrainer(
    config["Trainer"],
    model,
    train_dataloader=train_loader,
    val_dataloader=val_loader,
    optimizer=optimizer,
    lr_scheduler=lr_scheduler,
    compute_metric_func_dict=None,
)

ECFormer使用自定义Trainer

# ECFormer train.py
trainer = ECFormerTrainer(  # 继承自BaseTrainer但包含特殊逻辑
    config=config["Trainer"],
    model=model,
    train_dataloader=dataloaders.get("train"),
    val_dataloader=dataloaders.get("val"),
    optimizer=optimizer,
    lr_scheduler=lr_scheduler,
)

ECFormerTrainer的特殊逻辑(无法被BaseTrainer覆盖):

  • 处理ECD/IR任务的变长序列损失计算
  • 支持分类任务(ECD)和回归任务(IR)的动态切换
  • 内置任务自动检测机制(通过模型类名识别)
  • 流式指标累积与计算

六、配置文件结构差异

DiffNMR.yaml包含的独特配置

Global:
  molecule_converter: {...}      # ECFormer不需要
  graph_converter: {...}         # ECFormer不需要
  spectrum_converter: {...}      # ECFormer不需要
  num_train_timesteps: 500       # 扩散步数

DataInfo:                         # ECFormer没有此节
  extra_features: 'all'
  conditdim: 512

CLIP:                             # ECFormer没有此节
  __class_name__: NMRNetCLIP
  ...

Sampler:                          # ECFormer没有此节(推理采样)
  sample_every_val: 500
  visual_num: 10
  ...

ECFormer配置的简洁性

# ecd.yaml / ir.yaml
Global:
  do_train: True
  do_eval: True
  do_test: True
  label_names: ["peak_number", "peak_position", "peak_height"]
# 没有Global.molecule_converter等额外配置

Model:
  __class_name__: ECFormerECD  # 或 ECFormerIR
  __init_params__: {...}
# 没有DataInfo节,没有CLIP节

Trainer:
  output_dir: ./output/ecformer_ecd
  max_epochs: 100
  # 简洁的训练配置

七、结论

综上所述,DiffNMR和ECFormer在以下方面存在本质性差异,无法通过简单的配置修改实现统一:

  1. 任务范式不同:DiffNMR是生成式模型(扩散+CLIP),ECFormer是判别式模型(多任务分类/回归)

  2. 依赖组件不同:DiffNMR需要CLIP模块、扩散调度器、分子可视化、extra_features等ECFormer完全不依赖的组件

  3. 数据预处理不同:DiffNMR需要复杂的dataset_infos构建和维度计算,ECFormer的加载方式无法执行这些步骤

  4. Trainer实现不同:ECFormer需要使用自定义Trainer处理变长序列损失、动态任务切换等特殊逻辑

  5. 配置结构不同:DiffNMR的配置文件包含大量ECFormer不需要的配置节

当前设计中,每个模型在spectrum_elucidation/下拥有独立的命名空间(diffnmr/ecformer/),各自的train.py专注于单一任务的训练流程。这种设计符合“关注点分离”原则,便于独立维护和扩展,添加新模型时无需修改已有代码。若强行合并,代码中将充斥大量条件分支,可维护性将严重下降。

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果是任务不一致,建议参考其他任务页面,新增任务Spectrum Prediction

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

行,这几天我改一下

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leeleolay 改了

Copy link
Copy Markdown
Collaborator

@leeleolay leeleolay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

参考其他任务,辛苦添加任务readme页面的说明,模型readme页面

Copy link
Copy Markdown
Collaborator

@leeleolay leeleolay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

提交模型精度对齐结果说明,辛苦参考issue里的贡献指南

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

辛苦兼容已有trainer

@PlumBlossomMaid
Copy link
Copy Markdown
Author

感谢review。关于两个问题回复如下:

一、模型精度对齐结果说明

我将在几天内提交完整的模型精度对齐结果说明,按照贡献指南的要求整理,包含ECD和IR两个任务的复现精度、训练日志、以及预训练模型权重链接。

二、关于“兼容已有trainer”的设计说明

1. 当前方案:继承而非强行兼容

当前ECFormerTrainer继承自BaseTrainer,复用了分布式训练、AMP、checkpoint管理、日志可视化等基础设施,同时根据ECFormer的实际需求重写了train_epocheval_epoch

这是一个合理且必要的设计选择,而不是“不兼容”。

2. BaseTrainer的设计局限

经过阅读源码,BaseTrainer存在以下设计问题,使得直接复用变得困难:

(1)抽象层级错位

BaseTrainer要求模型的forward必须接受单个batch_data参数,并返回{"loss_dict": {...}, "pred_dict": {...}}的固定结构,将数据格式固化到了接口层面。

更好的设计应该是:Trainer只负责调度,模型自己决定输入输出的格式。

(2)过度假设

compute_metric_func_dictmetric_strategy_during_eval等配置是对评估流程的过度抽象。对于简单的分类任务可能够用,但对于需要复杂自定义逻辑的任务(如ECFormer的变长序列处理、动态任务切换),这些抽象反而成为束缚。

(3)扩展性不足

BaseTrainer几乎没有提供有意义的hook方法。想要修改训练循环的某个环节,只能重写整个train_epocheval_epoch方法。

3. 参考设计:PyTorch Lightning

说到训练器的抽象设计,业界公认做得比较好的框架是PyTorch Lightning。它的设计哲学值得借鉴:

  • 接口简洁training_step(self, batch, batch_idx)中的batch可以是任何格式,Trainer不关心具体结构
  • 丰富的hook机制:提供on_before_zero_gradon_after_backwardon_train_epoch_end等几十个hook,用户可精准介入
  • 约定优于配置,但不强制:对标准流程有合理默认行为,复杂任务可自由定制

在大量基于Lightning的成熟项目中,对于复杂训练逻辑,重写training_step是标准做法,甚至自定义Trainer子类也很常见。这正是其灵活性的体现。

4. 差异对比

维度 BaseTrainer假设 ECFormer实际需求
输入格式 单个batch_data 分子图、键角图等多个独立张量
输出格式 {"loss_dict": {...}, "pred_dict": {...}} 原始预测结果,损失独立计算
损失计算 内置于模型forward 独立的ECDLoss/IRLoss,支持分类/回归动态切换
评估逻辑 统一的step/epoch策略 需要处理变长序列的复杂指标累积

强行将所有逻辑塞入BaseTrainer,会导致代码中充斥条件分支,可维护性严重下降。

5. 结论

继承并重写是合理的设计选择。借鉴Lightning的成熟实践,复用基础设施、按需定制核心逻辑正是框架设计的核心理念。当前方案在复用BaseTrainer基础能力的同时,保持了ECFormer训练逻辑的独立性和可读性。

如果未来BaseTrainer能够增加更多hook点,或采用更灵活的接口设计,ECFormer将能更好地复用。在现有设计下,当前方案是最优选择。

@PlumBlossomMaid
Copy link
Copy Markdown
Author

突然发现,这个问题其实和paddle.Model的设计短板是同一个根源——对多任务、多 Loss、自定义训练流程的支持不够。我在 #78078 中有更详细的讨论,官方也承认这是历史遗留问题。在框架层彻底重构之前,继承重写是唯一合理的选择。我也有意愿在后续参与这方面的优化。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants