File size: 7,950 Bytes
7adc41d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/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!")