Any-to-Any
MLX
Safetensors
gemma4
mlx-vlm
rlcd
multimodal
classification
parallel-inference
image-text-to-text
audio
video
4-bit precision
Instructions to use larkooo/gemma-e2b-rlcd with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use larkooo/gemma-e2b-rlcd with MLX:
# Download the model from the Hub pip install huggingface_hub[hf_xet] huggingface-cli download --local-dir gemma-e2b-rlcd larkooo/gemma-e2b-rlcd
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Atomic Chat
| """Compare cached GPU batches to identical uncached multimodal token sequences.""" | |
| import argparse | |
| import json | |
| import statistics | |
| import time | |
| from dataclasses import replace | |
| from pathlib import Path | |
| from gemma_rlcd import Choice, DecisionEngine, Noul, Score, State | |
| from gemma_rlcd.cached_backend import CachedMLXBackend | |
| from gemma_rlcd.core import ScoringRequest, decision_prompt, softmax | |
| def compare(left, right): | |
| logit_delta = max( | |
| abs(a - b) | |
| for x, y in zip(left, right, strict=True) | |
| for a, b in zip(x.logits, y.logits, strict=True) | |
| ) | |
| probability_delta = max( | |
| abs(a - b) | |
| for x, y in zip(left, right, strict=True) | |
| for a, b in zip(softmax(x.logits), softmax(y.logits), strict=True) | |
| ) | |
| winners_match = all( | |
| max(range(len(x.logits)), key=x.logits.__getitem__) | |
| == max(range(len(y.logits)), key=y.logits.__getitem__) | |
| for x, y in zip(left, right, strict=True) | |
| ) | |
| return { | |
| "max_logit_delta": logit_delta, | |
| "max_probability_delta": probability_delta, | |
| "all_winners_match": winners_match, | |
| } | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--model", required=True) | |
| parser.add_argument("--media", required=True, type=Path) | |
| parser.add_argument("--report", required=True, type=Path) | |
| args = parser.parse_args() | |
| backend = CachedMLXBackend(args.model, branch_batch_size=4) | |
| mx = backend.mx | |
| media = args.media.resolve() | |
| questions = [ | |
| Choice( | |
| "Which animal is mentioned or visible?", | |
| {"cat": "A cat", "dog": "A dog", "other": "Neither"}, | |
| ), | |
| Noul("Is a dog mentioned?"), | |
| Score("How much red is visible?", ["No red", "Some red", "Mostly red"]), | |
| Noul( | |
| "Does the supplied evidence contain any mention of a sofa? Evaluate the complete supplied state." | |
| ), | |
| ] | |
| requests = [] | |
| for question in questions: | |
| criteria = DecisionEngine._criteria(question) | |
| symbols = tuple(backend.symbols(len(criteria))) | |
| requests.append( | |
| ScoringRequest(decision_prompt(question.instructions, criteria, symbols), symbols) | |
| ) | |
| states = { | |
| "text": State(text="A cat sleeps on a sofa. No dogs are present."), | |
| "image": State(images=(str(media / "red.png"),)), | |
| "speech": State(audio=(str(media / "dog.wav"),)), | |
| "video": State(videos=(str(media / "red-blue.mp4"),)), | |
| "video_speech": State(videos=(str(media / "red-blue-speech.mp4"),)), | |
| "long_text": State( | |
| text=("Background: the room has a table, a window, and a lamp. " * 80) | |
| + "A cat sleeps on a sofa. No dogs are present." | |
| ), | |
| } | |
| report = { | |
| "model": args.model, | |
| "compute_dtype": "float32", | |
| "status": "cache_numerics_and_timing_probe_not_quality_benchmark", | |
| "results": [], | |
| } | |
| for name, state in states.items(): | |
| try: | |
| prepared = backend.prepare(state, requests) | |
| prefix = backend.prefill(prepared) | |
| before = [(c.offset, mx.array(c.keys), mx.array(c.values)) for c in prefix] | |
| mx.eval(*[a for _, k, v in before for a in (k, v)]) | |
| reference = backend.uncached(prepared, requests) | |
| backend.branch_batch_size = 1 | |
| serial = backend.branches(prepared, prefix, requests) | |
| backend.branch_batch_size = 4 | |
| batched = backend.branches(prepared, prefix, requests) | |
| reverse_prepared = replace(prepared, suffixes=list(reversed(prepared.suffixes))) | |
| reversed_scores = list( | |
| reversed(backend.branches(reverse_prepared, prefix, list(reversed(requests)))) | |
| ) | |
| unchanged = all( | |
| c.offset == offset | |
| and bool(mx.array_equal(c.keys, k).item()) | |
| and bool(mx.array_equal(c.values, v).item()) | |
| for c, (offset, k, v) in zip(prefix, before, strict=True) | |
| ) | |
| timing = {"uncached": [], "cached_batched": []} | |
| # All shapes have been exercised. Alternate order to reduce order bias. | |
| for repeat in range(3): | |
| for method in ( | |
| ("uncached", "cached_batched") | |
| if repeat % 2 == 0 | |
| else ("cached_batched", "uncached") | |
| ): | |
| start = time.perf_counter() | |
| if method == "uncached": | |
| backend.uncached(prepared, requests) | |
| else: | |
| current_prefix = backend.prefill(prepared) | |
| backend.branches(prepared, current_prefix, requests) | |
| timing[method].append(time.perf_counter() - start) | |
| medians = {key: statistics.median(values) for key, values in timing.items()} | |
| record = { | |
| "state": name, | |
| "prefix_tokens": prepared.prefix_tokens, | |
| "suffix_lengths": [len(x) for x in prepared.suffixes], | |
| "cached_serial_vs_uncached": compare(reference, serial), | |
| "cached_batch_vs_uncached": compare(reference, batched), | |
| "reversed_batch_vs_original": compare(batched, reversed_scores), | |
| "prefix_unchanged": unchanged, | |
| "branch_batch_sizes": backend.last_stats["branch_batch_sizes"], | |
| "warm_seconds": timing, | |
| "warm_median_seconds": medians, | |
| "speedup": medians["uncached"] / medians["cached_batched"], | |
| } | |
| except Exception as exc: | |
| record = {"state": name, "error": f"{type(exc).__name__}: {exc}"} | |
| report["results"].append(record) | |
| args.report.write_text(json.dumps(report, indent=2) + "\n") | |
| print(json.dumps(record), flush=True) | |
| if __name__ == "__main__": | |
| main() | |