GenomeClip / example.py
H2dddhxh's picture
Upload 7 files
7adc41d verified
Raw
History Blame Contribute Delete
7.95 kB
#!/usr/bin/env python3
"""GenomeClip usage examples.
This script demonstrates how to use GenomeClip to encode DNA sequence
embeddings and text embeddings into a shared 512-dim space.
Prerequisites:
pip install torch transformers
Usage:
python example.py
"""
import torch
import torch.nn.functional as F
from transformers import AutoModel
# ──────────────────────────────────────────────────────────────────────
# 1. Load GenomeClip
# ──────────────────────────────────────────────────────────────────────
MODEL_PATH = "your-username/GenomeClip-v1" # or a local path
model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True)
model.eval()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)
print(f"Loaded GenomeClip on {device}")
print(f" seq_input_dim = {model.config.seq_input_dim}")
print(f" text_input_dim = {model.config.text_input_dim}")
print(f" projection_dim = {model.config.projection_dim}")
print(f" seq_pooling = {model.config.seq_pooling}")
print()
# ──────────────────────────────────────────────────────────────────────
# 2. Prepare dummy inputs (replace with real embeddings in practice)
# ──────────────────────────────────────────────────────────────────────
# In practice:
# seq_emb = AlphaGenome embeddings_128bp for each gene β†’ (L, 3072)
# text_emb = OpenAI text-embedding-3-large for gene desc β†’ (3072,)
batch_size = 4
seq_lengths_list = [120, 80, 50, 200] # variable token counts per gene
# Simulate per-token sequence embeddings (L2-normalized, as used in training)
max_len = max(seq_lengths_list)
seq_emb = torch.randn(batch_size, max_len, 3072, device=device)
seq_emb = F.normalize(seq_emb, dim=-1) # L2-normalize each token
seq_lengths = torch.tensor(seq_lengths_list, device=device)
# Simulate text embeddings
text_emb = torch.randn(batch_size, 3072, device=device)
# ──────────────────────────────────────────────────────────────────────
# 3. Encode sequence embeddings only
# ──────────────────────────────────────────────────────────────────────
with torch.no_grad():
seq_repr = model.encode_sequence(seq_emb, seq_lengths)
print("=== Encode sequence only ===")
print(f" Input: seq_emb {tuple(seq_emb.shape)}, seq_lengths {tuple(seq_lengths.shape)}")
print(f" Output: seq_repr {tuple(seq_repr.shape)}")
print(f" Norms: {seq_repr.norm(dim=-1).tolist()} (should be ~1.0)")
print()
# ──────────────────────────────────────────────────────────────────────
# 4. Encode text embeddings only
# ──────────────────────────────────────────────────────────────────────
with torch.no_grad():
text_repr = model.encode_text(text_emb)
print("=== Encode text only ===")
print(f" Input: text_emb {tuple(text_emb.shape)}")
print(f" Output: text_repr {tuple(text_repr.shape)}")
print(f" Norms: {text_repr.norm(dim=-1).tolist()} (should be ~1.0)")
print()
# ──────────────────────────────────────────────────────────────────────
# 5. Cross-modal similarity (cosine, since both are L2-normalized)
# ──────────────────────────────────────────────────────────────────────
similarity = seq_repr @ text_repr.t() # (B, B)
print("=== Cross-modal similarity matrix ===")
print(f" Shape: {tuple(similarity.shape)}")
for i in range(batch_size):
row = ", ".join(f"{similarity[i, j]:.4f}" for j in range(batch_size))
print(f" seq[{i}] vs text[0..{batch_size-1}]: [{row}]")
print()
# ──────────────────────────────────────────────────────────────────────
# 6. Forward with both modalities (computes contrastive loss)
# ──────────────────────────────────────────────────────────────────────
with torch.no_grad():
out = model(
seq_embeddings=seq_emb,
text_embeddings=text_emb,
seq_lengths=seq_lengths,
)
print("=== Forward with both modalities ===")
print(f" seq_repr: {tuple(out.seq_repr.shape)}")
print(f" text_repr: {tuple(out.text_repr.shape)}")
print(f" loss: {out.loss.item():.4f} (InfoNCE)")
print(f" logits: {tuple(out.logits.shape)}")
print()
# ──────────────────────────────────────────────────────────────────────
# 7. Pooled (mean) sequence input β€” already-pooled (3072,) vectors
# ──────────────────────────────────────────────────────────────────────
# If you already have gene-level pooled AlphaGenome embeddings (3072,),
# you can pass them directly (2D tensor). The model auto-expands to L=1.
seq_pooled = torch.randn(batch_size, 3072, device=device)
seq_pooled = F.normalize(seq_pooled, dim=-1)
with torch.no_grad():
pooled_repr = model.encode_sequence(seq_pooled) # no seq_lengths needed
print("=== Pooled (2D) sequence input ===")
print(f" Input: seq_pooled {tuple(seq_pooled.shape)}")
print(f" Output: pooled_repr {tuple(pooled_repr.shape)}")
print()
# ──────────────────────────────────────────────────────────────────────
# 8. Cross-modal retrieval example
# ──────────────────────────────────────────────────────────────────────
print("=== Cross-modal retrieval ===")
# Simulate a database of 100 gene sequence embeddings
n_genes = 100
db_seq = F.normalize(torch.randn(n_genes, 3072, device=device), dim=-1)
with torch.no_grad():
db_seq_repr = model.encode_sequence(db_seq) # (100, 512)
# Query with a single text embedding
query_text = torch.randn(1, 3072, device=device)
with torch.no_grad():
query_repr = model.encode_text(query_text) # (1, 512)
# Find top-5 most similar genes
scores = (query_repr @ db_seq_repr.t()).squeeze(0) # (100,)
top5_indices = scores.argsort(descending=True)[:5]
print(f" Database: {n_genes} genes")
print(f" Top-5 matching gene indices: {top5_indices.tolist()}")
print(f" Top-5 scores: {scores[top5_indices].tolist()}")
print()
print("Done!")