ouroboros / README.md
Nanny7's picture
Comprehensive README: architecture docs, implementation details, known issues
79ddd4a
|
Raw
History Blame Contribute Delete
7.87 kB
---
title: Ouroboros
emoji: 🧬
colorFrom: blue
colorTo: purple
sdk: gradio
python_version: "3.10"
app_file: app.py
pinned: false
---
# Ouroboros: Bidirectional Protein-Text Generation
Dual T5 model for protein sequence ↔ text description bidirectional generation.
- **Protein β†’ Text**: Given an amino acid sequence, generate a functional description.
- **Text β†’ Protein**: Given a text description, generate candidate protein sequences.
---
## Architecture
### Model: `ouroboros_model.py`
Two independent T5-large models connected by cross-modal FC projection layers.
```
Protein Modality Text Modality
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ p_t5_model (T5-large)β”‚ β”‚ t_t5_model (T5-large)β”‚
β”‚ d_model=1024, 24L β”‚ β”‚ d_model=1024, 24L β”‚
β”‚ vocab=128 (ProtT5) β”‚ β”‚ vocab=32128 (T5-base)β”‚
β”‚ ↑↓ β”‚ β”‚ ↑↓ β”‚
β”‚ p_t5_encoder β”‚ β”‚ t_t5_encoder β”‚
β”‚ ↓ β”‚ β”‚ ↓ β”‚
β”‚ fc_p2t (1024β†’1024) │←───→│ fc_t2p (1024β†’1024) β”‚
β”‚ ↓ β”‚ β”‚ ↓ β”‚
β”‚ t_t5_decoder β”‚ β”‚ p_t5_decoder β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
- **share_fc**: `fc_t2p.weight = transpose(fc_p2t.weight)`, no bias
- **text_share_param / protein_share_param**: Encoder-decoder SA weights tied within each T5
- **use_mean_pooling**: False (full sequence cross-attention)
- **FP32, ~1.23B params** (after weight sharing)
- **Checkpoint**: `dual_model.pt` (2.45 GB) β€” single full state_dict
#### Weight Sharing (`share_weight_t5`)
Replicated from `dual_training_t5/utils/share_weight.py:126-159`:
- SA q/k/v/o weights: encoder.block[i] ↔ decoder.block[i]
- SA layer_norm: encoder.block[i] ↔ decoder.block[i]
- FFN DenseReluDense: encoder.block[i] ↔ **decoder.block[0]** (`block[0]` is intentional β€” mirrors training behavior)
- No bias sharing, no FFN layer_norm sharing
#### Tokenizer Handling
| | Protein | Text |
|---|---|---|
| **Tokenizer** | `Rostlab/prot_t5_xl_uniref50` | `google-t5/t5-base` |
| **vocab_size (model)** | 128 (hardcoded) | 32128 (hardcoded) |
| **Vocab from tokenizer** | 28 (transformers >=4.40) | 32000 (transformers >=4.40) |
**Critical**: Model vocab_sizes are hardcoded, not read from tokenizer. Newer transformers report 28/32000 but checkpoint was trained with 128/32128. Loading from tokenizer causes `RuntimeError: size mismatch`.
### Inference Pipeline
```
p2t: protein_seq β†’ [" ".join(chars)] β†’ protein_tokenizer β†’ p_t5_encoder β†’ fc_p2t β†’ t_t5_model.generate() β†’ text_tokenizer.decode()
t2p: text_desc β†’ text_tokenizer β†’ t_t5_encoder β†’ fc_t2p β†’ p_t5_model.generate() β†’ protein_tokenizer.decode() β†’ .replace(" ", "")
```
**Generation params**: temperature=1.0, top_k=40, top_p=0.9, num_beams=4, max_length=512
### Checkpoint Loading
```python
state_dict = torch.load(path, map_location='cpu', weights_only=True)
# Strip 'module.' prefix from DDP training
new_state_dict = {k.replace('module.', ''): v for k, v in state_dict.items()}
model.load_state_dict(new_state_dict, strict=False)
```
`strict=False` because shared weights (encoder-decoder tied params, `share_fc` transposed params) may cause missing/unexpected key warnings.
---
## Gradio UI (`app.py`)
### Color Scheme
| Element | Protein (red/pink) | Text (blue) |
|---------|-------------------|-------------|
| Accent | `#E74C3C` | `#2471A3` |
| Border | `#E6B0AA` | `#A9CCE3` |
| Background | `#FDEDEC` | `#EBF5FB` |
### Tab 1: Protein β†’ Text
- Monospace input with red border/focus
- Output with blue left-accent border
- Advanced: temperature, top_k, top_p, num_beams, greedy toggle
### Tab 2: Text β†’ Protein
Three input modes:
1. **Structured Prompt Builder** (default): 29 annotation fields matching UniProt categories
- 7 common fields visible (similarity, function, catalytic activity, subunit, cofactor, pathway, subcellular location)
- 22 more fields in collapsible accordion
- "Build Prompt" button: assembles all fields per ORDER_LIST sequence
- "Random Example" button: loads from `100sample.json`
2. **Direct Input**: Collapsible accordion, paste raw text prompt
3. **Assembled Prompt** textbox: always editable, serve as final input to generation
### Prompt Assembly Rule
```
ORDER_LIST traversal β†’ if field has content β†’ append to prompt (no separator)
Training format: similarity + function + catalytic activity + path way + ...
```
### 100sample.json Data
JSON array of UniProt samples, each with:
- `uniprot_id`, `sequence` (amino acid string)
- Annotation fields matching ORDER_LIST keys (each a single-string list)
- `prompt`: assembled concatenation of all annotation fields
- `func_sim`: short version (similarity + function only)
---
## HF Spaces Deployment
### Current Setup
- **Space**: `REXWind/ouroboros` (Gradio SDK, Python 3.10, CPU basic)
- **Model Repo**: `REXWind/ouroboros-weights` (stores `dual_model.pt`, ~2.5 GB via Git LFS)
- **Auto-download**: `app.py` calls `huggingface_hub.hf_hub_download()` on startup
### Requirements (`requirements.txt`)
```
torch>=2.0.0
transformers>=4.36.0
sentencepiece>=0.1.99
huggingface_hub>=0.20.0
```
**Do NOT pin gradio** β€” HF Spaces injects its own gradio version during build. Pinning causes dependency conflicts.
### YAML Front Matter (`README.md`)
```yaml
sdk: gradio
python_version: "3.10"
app_file: app.py
```
- Python 3.10 required (3.13 has pydub/audioop incompatibility)
- Gradio SDK version auto-managed by HF
---
## Key Files
| File | Purpose |
|------|---------|
| `app.py` | Gradio web interface (model loading, UI layout, event bindings) |
| `ouroboros_model.py` | Self-contained model class (no dependency on training `utils/`) |
| `t5config_large.json` | T5-large architecture config |
| `100sample.json` | Sample UniProt annotations for Random Example button |
| `requirements.txt` | Minimal Python dependencies (gradio managed by HF) |
### Source Project Paths
All paths on gpfs shared storage:
```
Training code: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/dual_training_t5/
Checkpoint: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/model_weights/dual_t5_large_newds_v5/best_recovery/dual_model.pt
Tokenizer: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/hf_weights/prot_t5_xl_uniref50/
Tokenizer: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/hf_weights/t5-base/
Demo source: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/Ouroboros_hf_demo/
HF Space clone: /mnt/shared-storage-user/dnacoding/protein-text-dual-learn/dual_t5_workspace/hf_space_ouroboros/
```
---
## Known Issues & Fixes
| Issue | Cause | Fix |
|-------|-------|-----|
| `size mismatch: [32128] vs [32000]` | New transformers reports vocab=32000 | Hardcode `configs_t.vocab_size = 32128` |
| `ImportError: cannot import HfFolder` | gradio 5 + huggingface_hub >=1.0 | Don't pin gradio version in requirements |
| `TypeError: unhashable type: 'dict'` | gradio 4.44 + jinja2 >=3.1.5 | Don't pin gradio; use Python 3.10 |
| `Cannot install gradio<5.0.0 and gradio==6.15.2` | HF installs gradio 6 | Don't specify gradio in requirements |
| `Repository storage limit (Max: 1 GB)` | Space only; model 2.5GB | Put checkpoint in separate Model Repo |
| Protein vocab=28 | New transformers prot_t5 vocab | `configs_p.vocab_size = 128` (hardcoded) |