Text Generation
MLX
Safetensors
progen
progen2
protein-language-model
mlx-lm
bfloat16
icl-many-replication
custom_code
Instructions to use N8Programs/ProGen2-base-bf16 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use N8Programs/ProGen2-base-bf16 with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # if on a CUDA device, also pip install mlx[cuda] # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("N8Programs/ProGen2-base-bf16") prompt = "Once upon a time in" text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- MLX LM
How to use N8Programs/ProGen2-base-bf16 with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Generate some text mlx_lm.generate --model "N8Programs/ProGen2-base-bf16" --prompt "Once upon a time"
| # Copyright (c) 2026 | |
| """MLX-LM architecture for ProGen2 causal protein LMs.""" | |
| from dataclasses import dataclass | |
| from typing import Any, Optional | |
| import mlx.core as mx | |
| import mlx.nn as nn | |
| from mlx_lm.models.base import ( | |
| BaseModelArgs, | |
| create_attention_mask, | |
| scaled_dot_product_attention, | |
| ) | |
| class ModelArgs(BaseModelArgs): | |
| model_type: str | |
| vocab_size_emb: int | |
| vocab_size_lm_head: int | |
| n_positions: int | |
| embed_dim: int | |
| n_layer: int | |
| n_head: int | |
| rotary_dim: int = 64 | |
| n_inner: Optional[int] = None | |
| activation_function: str = "gelu_new" | |
| layer_norm_epsilon: float = 1e-5 | |
| bos_token_id: int = 1 | |
| eos_token_id: int = 2 | |
| pad_token_id: int = 0 | |
| def gelu_new(x: mx.array) -> mx.array: | |
| return 0.5 * x * ( | |
| 1.0 | |
| + mx.tanh(0.7978845608028654 * (x + 0.044715 * mx.power(x, 3))) | |
| ) | |
| def rotate_every_two(x: mx.array) -> mx.array: | |
| x1 = x[..., ::2] | |
| x2 = x[..., 1::2] | |
| stacked = mx.stack((-x2, x1), axis=-1) | |
| return stacked.reshape(*x.shape) | |
| class PartialRotaryEmbedding(nn.Module): | |
| def __init__(self, rotary_dim: int): | |
| super().__init__() | |
| self.rotary_dim = rotary_dim | |
| inv_freq = 1.0 / ( | |
| 10000 | |
| ** (mx.arange(0, rotary_dim, 2, dtype=mx.float32) / rotary_dim) | |
| ) | |
| self.inv_freq = inv_freq | |
| def __call__(self, x: mx.array, offset: int | mx.array = 0) -> mx.array: | |
| seq_len = x.shape[-2] | |
| offset = mx.array(offset, dtype=mx.float32) | |
| positions = mx.arange(seq_len, dtype=mx.float32) | |
| if offset.ndim == 0: | |
| positions = positions + offset | |
| freqs = positions[:, None] * self.inv_freq[None, :] | |
| emb = mx.repeat(freqs, 2, axis=-1) | |
| cos = mx.cos(emb).astype(x.dtype).reshape( | |
| 1, | |
| 1, | |
| seq_len, | |
| self.rotary_dim, | |
| ) | |
| sin = mx.sin(emb).astype(x.dtype).reshape( | |
| 1, | |
| 1, | |
| seq_len, | |
| self.rotary_dim, | |
| ) | |
| else: | |
| positions = positions[None, :] + offset[:, None] | |
| freqs = positions[:, :, None] * self.inv_freq[None, None, :] | |
| emb = mx.repeat(freqs, 2, axis=-1) | |
| cos = mx.cos(emb).astype(x.dtype).reshape( | |
| x.shape[0], | |
| 1, | |
| seq_len, | |
| self.rotary_dim, | |
| ) | |
| sin = mx.sin(emb).astype(x.dtype).reshape( | |
| x.shape[0], | |
| 1, | |
| seq_len, | |
| self.rotary_dim, | |
| ) | |
| return (x * cos) + (rotate_every_two(x) * sin) | |
| class ProGenAttention(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| if args.embed_dim % args.n_head != 0: | |
| raise ValueError("embed_dim must be divisible by n_head") | |
| self.embed_dim = args.embed_dim | |
| self.num_heads = args.n_head | |
| self.head_dim = args.embed_dim // args.n_head | |
| self.mp_num = 8 | |
| self.mp_part = args.embed_dim // self.mp_num | |
| self.scale = self.head_dim**-0.5 | |
| self.rotary_dim = args.rotary_dim | |
| self.qkv_proj = nn.Linear(args.embed_dim, args.embed_dim * 3, bias=False) | |
| self.out_proj = nn.Linear(args.embed_dim, args.embed_dim, bias=False) | |
| self.rotary = PartialRotaryEmbedding(self.rotary_dim) | |
| def _split_heads_from_mp(self, x: mx.array) -> mx.array: | |
| batch_size, seq_len = x.shape[:2] | |
| x = x.reshape(batch_size, seq_len, self.embed_dim) | |
| return x.reshape( | |
| batch_size, | |
| seq_len, | |
| self.num_heads, | |
| self.head_dim, | |
| ).transpose(0, 2, 1, 3) | |
| def _apply_partial_rotary(self, x: mx.array, offset: int | mx.array) -> mx.array: | |
| x_rot = x[..., : self.rotary_dim] | |
| x_pass = x[..., self.rotary_dim :] | |
| return mx.concatenate([self.rotary(x_rot, offset=offset), x_pass], axis=-1) | |
| def __call__( | |
| self, | |
| hidden_states: mx.array, | |
| mask: Optional[Any] = None, | |
| cache: Optional[Any] = None, | |
| ) -> mx.array: | |
| batch_size, seq_len, _ = hidden_states.shape | |
| qkv = self.qkv_proj(hidden_states) | |
| qkv = qkv.reshape(batch_size, seq_len, self.mp_num, -1) | |
| query, value, key = mx.split(qkv, 3, axis=-1) | |
| query = self._split_heads_from_mp(query) | |
| key = self._split_heads_from_mp(key) | |
| value = self._split_heads_from_mp(value) | |
| offset = 0 if cache is None else cache.offset | |
| query = self._apply_partial_rotary(query, offset=offset) | |
| key = self._apply_partial_rotary(key, offset=offset) | |
| if cache is not None: | |
| key, value = cache.update_and_fetch(key, value) | |
| attn_output = scaled_dot_product_attention( | |
| query, | |
| key, | |
| value, | |
| cache=cache, | |
| scale=self.scale, | |
| mask=mask, | |
| ) | |
| attn_output = attn_output.transpose(0, 2, 1, 3).reshape( | |
| batch_size, | |
| seq_len, | |
| self.embed_dim, | |
| ) | |
| return self.out_proj(attn_output) | |
| class ProGenMLP(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| inner_dim = args.n_inner if args.n_inner is not None else 4 * args.embed_dim | |
| self.fc_in = nn.Linear(args.embed_dim, inner_dim, bias=True) | |
| self.fc_out = nn.Linear(inner_dim, args.embed_dim, bias=True) | |
| def __call__(self, hidden_states: mx.array) -> mx.array: | |
| return self.fc_out(gelu_new(self.fc_in(hidden_states))) | |
| class ProGenBlock(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.ln_1 = nn.LayerNorm( | |
| args.embed_dim, | |
| eps=args.layer_norm_epsilon, | |
| affine=True, | |
| bias=True, | |
| ) | |
| self.attn = ProGenAttention(args) | |
| self.mlp = ProGenMLP(args) | |
| def __call__( | |
| self, | |
| hidden_states: mx.array, | |
| mask: Optional[Any] = None, | |
| cache: Optional[Any] = None, | |
| ) -> mx.array: | |
| residual = hidden_states | |
| normed = self.ln_1(hidden_states) | |
| attn_output = self.attn(normed, mask=mask, cache=cache) | |
| mlp_output = self.mlp(normed) | |
| return residual + attn_output + mlp_output | |
| class ProGenModel(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.wte = nn.Embedding(args.vocab_size_emb, args.embed_dim) | |
| self.h = [ProGenBlock(args) for _ in range(args.n_layer)] | |
| self.ln_f = nn.LayerNorm( | |
| args.embed_dim, | |
| eps=args.layer_norm_epsilon, | |
| affine=True, | |
| bias=True, | |
| ) | |
| def __call__(self, inputs: mx.array, cache=None) -> mx.array: | |
| hidden_states = self.wte(inputs) | |
| if cache is None: | |
| cache = [None] * len(self.h) | |
| mask = create_attention_mask(hidden_states, cache[0]) | |
| for block, c in zip(self.h, cache): | |
| hidden_states = block(hidden_states, mask=mask, cache=c) | |
| return self.ln_f(hidden_states) | |
| class Model(nn.Module): | |
| def __init__(self, args: ModelArgs): | |
| super().__init__() | |
| self.args = args | |
| self.model_type = args.model_type | |
| self.transformer = ProGenModel(args) | |
| self.lm_head = nn.Linear(args.embed_dim, args.vocab_size_lm_head, bias=True) | |
| def __call__(self, inputs: mx.array, cache=None) -> mx.array: | |
| return self.lm_head(self.transformer(inputs, cache=cache)) | |
| def sanitize(self, weights): | |
| weights = dict(weights) | |
| inv_freq = 1.0 / ( | |
| 10000 | |
| ** ( | |
| mx.arange(0, self.args.rotary_dim, 2, dtype=mx.float32) | |
| / self.args.rotary_dim | |
| ) | |
| ) | |
| for layer_idx in range(self.args.n_layer): | |
| weights[f"transformer.h.{layer_idx}.attn.rotary.inv_freq"] = inv_freq | |
| return weights | |
| def layers(self): | |
| return self.transformer.h | |