| #!/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!") | |