ai4sci-vs-contrastive-scoring

AI for ScienceHypSeekrigorous codebase

Description

Task: Scoring Objective Design for Virtual Screening

Research Question

Design the scoring objective — including projection heads, embedding space, and training loss — for contrastive protein-ligand virtual screening. Given pretrained backbone encoders (UniMol for molecules/pockets, ESM2 for protein sequences) that are fine-tuned jointly end-to-end with the scoring module, how should their features be projected, embedded, and trained to best discriminate active binders from decoys?

Background

Virtual screening computationally ranks large compound libraries against a protein target to identify potential drug candidates. Modern approaches use learned representations: encode protein pockets and molecules into a shared embedding space, then rank by similarity. Key design choices include:

  • Projection heads: How to project backbone features (512-dim UniMol, 480-dim ESM2) into a shared space
  • Embedding geometry: Euclidean (L2-normalized dot product), hyperbolic (Lorentz hyperboloid), spherical, or other manifolds
  • Training loss: In-batch contrastive (CLIP-style), ranking-aware losses, activity-dependent constraints, cone hierarchy

Existing approaches range from simple CLIP-style contrastive learning (DrugCLIP) to hyperbolic geometry with cone hierarchy constraints (HypSeek).

What to Implement

Implement the CustomScoring class in custom_scoring.py. You must implement:

  1. __init__: Define projection heads, embedding parameters, loss hyperparameters
  2. project_mol(mol_feat): Project molecule features [B, 512] → [B, embed_dim]
  3. project_pocket(poc_feat): Project pocket features [B, 512] → [B, embed_dim]
  4. project_protein(prot_feat): Project protein features [B, 480] → [B, embed_dim]
  5. compute_loss(mol_emb, poc_emb, prot_emb, batch_list, act_list, ...): Training loss
  6. score(mol_reps, pocket_reps, prot_reps): Evaluation scoring (numpy arrays)

Available Components

  • Backbone features (fine-tuned jointly): mol_feat [B, 512], poc_feat [B, 512], prot_feat [B, 480]
  • Lorentz hyperbolic operations: exp_map0, pairwise_dist, half_aperture, oxy_angle from unimol.losses.lorentz
  • Training data provides: batch_list (pocket→ligand mapping), act_list (pIC50 activities), uniprot_poc/mol (for false-negative masking), pocket_lig_smiles/lig_smiles (for duplicate masking)

Evaluation

The model is evaluated on three virtual screening benchmarks (zero-shot, no target-specific training):

  1. DUD-E (102 targets): Active compounds vs property-matched decoys
  2. LIT-PCBA (15 targets): Realistic screening with confirmed actives/inactives
  3. DEKOIS 2.0 (81 targets): Challenging decoy benchmark

Metrics (averaged across targets): AUROC, BEDROC (α=80.5), EF at 0.5%/1%/5%

Editable Region

The entire custom_scoring.py file is editable. You may define any helper classes or functions within this file. The backbone encoders and training loop are fixed; backbone parameters are loaded from pretrained weights and fine-tuned jointly with the scoring module.

Code

custom_scoring.py
EditableRead-only
1"""Custom scoring module for contrastive virtual screening.
2
3This module defines the projection heads, embedding space mapping, and
4training loss for protein-ligand virtual screening.
5
6Interface:
7 project_mol(mol_feat) -> [B, embed_dim] molecule embeddings
8 project_pocket(poc_feat) -> [B, embed_dim] pocket embeddings
9 project_protein(prot_feat) -> [B, embed_dim] protein embeddings
10 compute_loss(mol_emb, poc_emb, prot_emb, ...) -> (loss, dict)
11 score(mol_reps, pocket_reps, prot_reps) -> [N_mol] numpy scores
12
13Available utilities (imported in model wrapper):
14 torch, torch.nn, torch.nn.functional, numpy, math
15 lorentz ops: from unimol.losses.lorentz import exp_map0, pairwise_dist,
custom_vs_model.py
EditableRead-only
1"""Custom virtual screening model wrapper (FIXED — do not modify).
2
3This model wraps UniMol + ESM2 backbone encoders and delegates
4projection + embedding logic to the editable CustomScoring module.
5Backbones are FINE-TUNED jointly with the scoring head to match the
6HypSeek training protocol (paper uses joint end-to-end training).
7"""
8
9import argparse
10import math
11import numpy as np
12import torch
13import torch.nn as nn
14import torch.nn.functional as F
15from unicore import utils
custom_vs_loss.py
EditableRead-only
1"""Custom virtual screening loss wrapper (FIXED — do not modify).
2
3Delegates all loss computation to model.scoring.compute_loss().
4"""
5
6import math
7import numpy as np
8import torch
9import torch.nn.functional as F
10from unicore.losses import UnicoreLoss, register_loss
11from unicore import metrics
12from sklearn.metrics import roc_auc_score
13from rdkit.ML.Scoring.Scoring import CalcBEDROC
14
15

Additional context files (read-only):

  • HypSeek/unimol/losses/lorentz.py

Results

ModelTypeauc mean dude bedroc mean dude ef005 mean dude ef01 mean dude ef05 mean dude ef0005 mean dude ef001 mean dude ef002 mean dude auc mean dekois bedroc mean dekois ef005 mean dekois ef01 mean dekois ef05 mean dekois ef0005 mean dekois ef001 mean dekois ef002 mean dekois auc mean lit-pcba bedroc mean lit-pcba ef005 mean lit-pcba ef01 mean lit-pcba ef05 mean lit-pcba ef0005 mean lit-pcba ef001 mean lit-pcba ef002 mean lit-pcba
hccbaseline0.7320.20313.07611.4525.92013.07611.4528.9170.6940.2197.9787.1824.7097.9787.1826.3050.5650.0331.9772.8621.6701.9772.8622.343
hcc_hyp_conebaseline0.7020.18712.10010.3715.51912.10010.3718.2260.6810.2178.1487.5074.4878.1487.5076.2600.5460.0323.3682.5961.7993.3672.5962.190
vanilla_clipbaseline0.6830.16710.4479.4415.14510.4479.4417.6130.6150.1414.5914.4443.5054.5914.4434.0240.5250.0333.4892.6521.5383.4892.6521.776