Text-to-Image
MLX
MiniMax H3
Safetensors
English
apple-silicon
macos
image-to-image
image-editing
image-generation
Instructions to use devin-lai/MiniMax-H3-Image-mlx with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use devin-lai/MiniMax-H3-Image-mlx with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] hf download devin-lai/MiniMax-H3-Image-mlx --local-dir MiniMax-H3-Image-mlx
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
File size: 4,919 Bytes
3da88e3 | 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 | """Experimental Metal RMSNorm + indexed AdaLN, with BF16 operation boundaries.
One threadgroup owns one row. The tuning harness checks output error as well as
latency for 128/256/512 threads; this is opt-in because reduction order differs.
"""
from functools import lru_cache
import mlx.core as mx
@lru_cache(maxsize=8)
def _kernel(threads):
if threads not in (128, 256, 512):
raise ValueError("norm threads must be 128, 256 or 512")
return mx.fast.metal_kernel(
name=f"h3_norm_mod_{threads}",
input_names=["x", "weight", "shift", "scale", "indices", "epsilon"],
output_names=["out"],
source=r"""
uint tid = thread_position_in_threadgroup.x;
uint row = threadgroup_position_in_grid.y;
uint width = x_shape[1];
float sum = 0;
for (uint c = tid; c < width; c += TG) {
float a = float(x[row * width + c]);
sum += a * a;
}
sum = simd_sum(sum);
threadgroup float partial[32];
uint lane = tid % 32, warp = tid / 32;
if (lane == 0) partial[warp] = sum;
threadgroup_barrier(mem_flags::mem_threadgroup);
if (warp == 0) {
float v = lane < TG / 32 ? partial[lane] : 0.0f;
v = simd_sum(v);
if (lane == 0) partial[0] = metal::rsqrt(v / float(width) + epsilon[0]);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
float inv = partial[0];
uint index = indices[row] * width;
for (uint c = tid; c < width; c += TG) {
T affine = T(float(x[row * width + c]) * inv * float(weight[c]));
T factor = T(T(1) + scale[index + c]);
T product = T(affine * factor);
out[row * width + c] = T(product + shift[index + c]);
}
""",
)
def norm_modulate(x, weight, shift, scale, indices, eps=1e-5, threads=256):
if x.ndim != 2 or x.shape[-1] != weight.shape[0]:
raise ValueError("Expected [rows,width] and a matching norm weight")
return _kernel(threads)(
inputs=[
x,
weight,
shift,
scale,
indices.astype(mx.uint32),
mx.array([eps], mx.float32),
],
template=[("T", x.dtype), ("TG", threads)],
grid=(threads, x.shape[0], 1),
threadgroup=(threads, 1, 1),
output_shapes=[x.shape],
output_dtypes=[x.dtype],
)[0]
@lru_cache(maxsize=1)
def _qk_kernel():
return mx.fast.metal_kernel(
name="h3_qk_norm_rope",
input_names=["qkv", "qw", "kw", "cosine", "sine", "epsilon"],
output_names=["qout", "kout"],
source=r"""
uint lane = thread_position_in_threadgroup.x;
uint row = threadgroup_position_in_grid.y;
uint heads = qkv_shape[2], dim = qkv_shape[3];
uint token = row / heads, head = row % heads;
uint qb = token * 3 * heads * dim + head * dim;
uint kb = qb + heads * dim;
uint rot = cosine_shape[1];
float qsum = 0, ksum = 0;
for (uint c = lane; c < dim; c += 32) {
float q = float(qkv[qb+c]), k = float(qkv[kb+c]);
qsum += q*q; ksum += k*k;
}
float qi = metal::rsqrt(simd_sum(qsum)/float(dim)+epsilon[0]);
float ki = metal::rsqrt(simd_sum(ksum)/float(dim)+epsilon[0]);
for (uint c = lane; c < dim; c += 32) {
T qn = T(float(qkv[qb+c])*qi*float(qw[c]));
T kn = T(float(qkv[kb+c])*ki*float(kw[c]));
if (c < rot) {
uint partner = (c+rot/2)%rot;
T qother = T(float(qkv[qb+partner])*qi*float(qw[partner]));
T kother = T(float(qkv[kb+partner])*ki*float(kw[partner]));
if (c < rot/2) { qother = -qother; kother = -kother; }
T qcos = T(qn*cosine[token*rot+c]);
T kcos = T(kn*cosine[token*rot+c]);
T qsin = T(qother*sine[token*rot+c]);
T ksin = T(kother*sine[token*rot+c]);
qn = T(qcos+qsin); kn = T(kcos+ksin);
}
qout[row*dim+c] = qn;
kout[row*dim+c] = kn;
}
""",
)
def qk_norm_rope(qkv, qw, kw, cos, sin, eps=1e-5):
"""Fused head RMSNorm/partial RoPE; qkv [S,3,H,D], outputs [1,S,H,D]."""
if qkv.ndim != 4 or qkv.shape[1] != 3:
raise ValueError("QKV must have shape [sequence,3,heads,dim]")
n, _, heads, dim = qkv.shape
shape = (1, n, heads, dim)
rot = cos.shape[-1]
return _qk_kernel()(
inputs=[
qkv,
qw,
kw,
cos.reshape(n, rot),
sin.reshape(n, rot),
mx.array([eps], mx.float32),
],
template=[("T", qkv.dtype)],
grid=(32, n * heads, 1),
threadgroup=(32, 1, 1),
output_shapes=[shape, shape],
output_dtypes=[qkv.dtype, qkv.dtype],
)
|