Skip to content

Fix PaccMann integration - #447

Open
tereshchuk1 wants to merge 18 commits into
daisybio:developmentfrom
tereshchuk1:pr-388
Open

Fix PaccMann integration#447
tereshchuk1 wants to merge 18 commits into
daisybio:developmentfrom
tereshchuk1:pr-388

Conversation

@tereshchuk1

@tereshchuk1 tereshchuk1 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

This PR fixes the PaccMann implementation originally submitted in #388 by @gretag04, adapting it to work within the drevalpy framework.

Bug fixes

  • Removed the top-level pytoda imports: pytoda is not a drevalpy dependency. The only code that needed it was the confidence-estimation path, which has since been removed entirely (see below), so neither the import nor the vendored stub remains.
  • Fixed SMILES loading: only the id and SMILES columns are read from drug_smiles.csv; the huge fingerprint bit-string columns are skipped rather than loaded and dropped, since pandas' numeric type inference on them can raise an OverflowError.
  • Fixed gene list name.

Review follow-ups

  • early_stopping is now False. The wrapper does not train against an early-stopping split, so it no longer requests one.
  • Removed dead code: the forward(confidence=...) uncertainty path, _associate_language, utils/interpret.py, the vendored pytoda stub, and the unused gene_projection / smiles_projection helpers in utils/layers.py.
  • Renamed paccmann_v2.py to paccmann_network_v2.py, to make clear that paccmann.py is the DrEval wrapper and that this file holds the network it uses.
  • Removed the module-level logging configuration, which was overriding drevalpy's global logging.
  • predict() now runs batch-wise through a DataLoader instead of pushing all inputs through the network at once.
  • Added self.log_hyperparameters(hyperparameters) in build_model for Weights & Biases logging.
  • gene_list is now a hyperparameter, defaulting to the gene_list_paccmann_network_prop panel that ships with the repo. The TOY test sets it to None, so the toy data does not need that panel.
  • Added documentation: an API page (docs/drevalpy.models.PaccMann.rst, wired into the models toctree) and a row in the model overview table in docs/usage.rst.

@codecov-commenter

codecov-commenter commented Jul 3, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 94.10029% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.48%. Comparing base (7d24cd6) to head (e27445b).
⚠️ Report is 75 commits behind head on development.

Files with missing lines Patch % Lines
drevalpy/models/PaccMann/paccmann.py 93.30% 16 Missing ⚠️
drevalpy/models/PaccMann/network.py 95.74% 4 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@               Coverage Diff               @@
##           development     #447      +/-   ##
===============================================
+ Coverage        80.34%   85.48%   +5.13%     
===============================================
  Files              101      123      +22     
  Lines             8171    10119    +1948     
===============================================
+ Hits              6565     8650    +2085     
+ Misses            1606     1469     -137     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@PascalIversen

PascalIversen commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Nice, thanks for the fix! I still see some issues:

1.) early_stopping is set to true, but the early stopping set is not used. Could you check if they use early stopping validation in the original implementation and either set it to false if they don't, or implement it?

  1. Can you remove dead code? There is the uncertainty stuff forward(confidence=True) would fail if someone uses it. I think at the moment we don't need it, so we could just remove that. Similar with gene_projection, smiles_projection, alpha_projection

3.) can you add the model to the documentation?

Comment thread drevalpy/models/PaccMann/paccmann.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann_network_v2.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann_v2.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann.py
tereshchuk1 and others added 8 commits July 16, 2026 15:25
…nfidence=...), _associate_language, utils/interpret.py, vendored pytoda) and unused gene_projection/smiles_projection/alpha_projection helpers in utils/layers.py
…for the TOY test, so the model no longer requires the gene_list_paccmann_network_prop panel on toy data
The original implementation (PaccMann/paccmann_predictor) trains for a fixed
number of epochs and checkpoints the model whenever the loss on a held-out set
improves, using that checkpoint as the final model. Follow the same procedure
with the early stopping set: evaluate it after every epoch, keep the weights of
the best epoch, and restore them at the end. Training still runs the full epoch
budget, since the original does not terminate early either.

Also deduplicate the input encoding shared by the validation loader and
predict() into a _encode_inputs helper.
Tokenization: the wrapper split SMILES by character, so multi-character atoms
fell apart -- 'Cl' became C+l and 'Br' became B+r, colliding with carbon and
with boron (Bortezomib really contains boron), and bracket atoms such as
'[C@@h]' or '[Pt+2]' were shredded into their individual characters. Use the
atom-level regex from pytoda.smiles.processing, which is what the original
implementation tokenizes with.

Batch of size 1: torch.squeeze in the context attention layer also dropped the
batch dimension, so predict() crashed whenever the row count left a trailing
batch of one, and training hit the same in the batch norm layers. Squeeze only
the last dimension, and drop a trailing single-sample training batch. The
original implementation always sets drop_last=True; dropping only a size-1
batch keeps training sets smaller than one batch usable.

Hyperparameters: raise smiles_padding_length 128 -> 512 and epochs 3 -> 10, the
values from the original paccmann_v2_params.json. At 128 tokens, 13 CTRPv2
drugs were silently truncated; at 512 no drug in any bundled dataset is.

Also remove six symbols that were defined but referenced nowhere:
dense_attention_layer, to_np, attention_list_to_matrix, Unsqueeze,
RNN_CELL_FACTORY and OPTIMIZER_FACTORY.
The original implementation re-randomizes every SMILES string on each access,
so a drug is seen through a different but chemically identical SMILES in every
epoch. With only a few hundred distinct drugs per dataset this is the main
regularizer on the drug modality, and it was missing here.

Each distinct drug gets a bank of variants built once with RDKit by
re-serializing the molecule from a shuffled atom order; training then draws one
variant per row per batch. The dataset stores the drug index instead of the
encoded SMILES, so no per-epoch tensor is materialized. The vocabulary is built
over all variants, otherwise their tokens would encode as unknown.

Enabled by default via the augment_smiles hyperparameter. RDKit is a new
optional dependency behind the paccmann extra; when it is missing, training
warns and falls back to the unaugmented SMILES rather than failing.
Comments and code carried over from the original repository that never applied
here:

- Eight '# yapf: disable' directives. The original repository formats with yapf,
  drevalpy formats with black, which ignores them; black had already reformatted
  several of the lines they were attached to.
- The prediction_dict returned by forward(). It held attention weights and IC50
  variants 'to ease postprocessing' in the original analysis pipeline, but the
  wrapper always discarded it. It was still built on every evaluation batch, so
  removing it also drops two torch.cat calls per batch from predict(). forward()
  now returns the predictions alone.
- The min_max_scaling branch with IC50_max/IC50_min and get_log_molar. It only
  activates when drug_sensitivity_processing_parameters is passed, which the
  wrapper never does, so it could not run. DrEval scales responses itself.
- The 2128 default for number_of_genes, the gene panel size of the original
  implementation. Both construction paths pass the real count, so requiring it
  turns a stale magic number into a clear error.

The attention weights remain available in the git history and upstream should
interpretability ever be wired up.
@JudithBernett

Copy link
Copy Markdown
Contributor

Nice, thanks for the fix! I still see some issues:

1.) early_stopping is set to true, but the early stopping set is not used. Could you check if they use early stopping validation in the original implementation and either set it to false if they don't, or implement it?

  1. Can you remove dead code? There is the uncertainty stuff forward(confidence=True) would fail if someone uses it. I think at the moment we don't need it, so we could just remove that. Similar with gene_projection, smiles_projection, alpha_projection

3.) can you add the model to the documentation?

@PascalIversen why wouldn't we want to use early stopping no matter the original publication?

Comment thread drevalpy/models/PaccMann/utils/hyperparams.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann_network_v2.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann.py Outdated
Comment thread drevalpy/models/PaccMann/paccmann.py Outdated
Comment thread drevalpy/models/PaccMann/utils/utils.py Outdated
Comment thread drevalpy/models/PaccMann/utils/utils.py Outdated
Comment thread drevalpy/models/PaccMann/utils/loss_functions.py Outdated
Comment thread drevalpy/models/PaccMann/utils/loss_functions.py Outdated
PascalIversen and others added 2 commits August 18, 2026 15:59
…ing and dropping upstream cruft

- consolidate paccmann_network_v2.py and the utils/ subpackage into a single network.py,
  with a typed PaccMannConfig replacing the raw hyperparameter dict threaded through the network
- drop unused activation/loss factories and other dead configurability inherited from upstream
- replace OrderedDict-of-named-Sequential blocks with plain nn.Module/nn.Sequential blocks
- add patience-based early stopping, matching the rest of the models in this repo
- simplify SMILES-array normalization and update docs accordingly
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants