Reproducible Kaggle experiments for evaluating Orthrus, a dual-view model that combines autoregressive (AR) decoding with parallel diffusion proposals and batched AR verification.
The experiments focus on three questions:
The tested checkpoints are:
Qwen/Qwen3-1.7Bchiennv/Orthrus-Qwen3-1.7BThe experiments use examples from:
| Script | Purpose |
|---|---|
orthrus_lossless_kaggle.py | Initial token-ID and latency comparison. Kept as a legacy version. |
orthrus_lossless_kaggle_v2.py | Recommended losslessness benchmark with explicit dtype selection, hardware checks, acceptance tracing, TPF, and mismatch reporting. |
orthrus_first_mismatch_logits.py | Compares sequential AR and batched-verifier logits at the first mismatching token. |
orthrus_block_size_kaggle.py | FP16 sweep over fixed block sizes K = 4, 8, 16, 32. |
orthrus_dynamic_block_kaggle.py | FP16 generation with a block size adapted after every verification cycle. |
The scripts are designed for a Kaggle notebook with GPU and Internet access enabled.
The experiments reported below were run with:
GPU: Tesla T4, compute capability 7.5
torch: 2.10.0+cu128
transformers: 5.8.1
Install the required Python packages:
pip install -U "transformers==5.8.1" "datasets>=4.0.0" "accelerate>=1.0.0"
The Orthrus checkpoint uses custom Hugging Face model code and is loaded with trust_remote_code=True. Review and pin the model revision when running in a security-sensitive environment.
An authenticated Hugging Face token is optional but avoids anonymous Hub rate limits:
import os
os.environ["HF_TOKEN"] = "..."
git clone https://github.com/NikitaKukuzey/orthrus_exps.git
cd orthrus_exps
pip install -U "transformers==5.8.1" "datasets>=4.0.0" "accelerate>=1.0.0"
Run a small smoke test before a full benchmark:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 64 \
--repeats 1 \
--dtype float16 \
--attn-implementation sdpa
orthrus_lossless_kaggle_v2.py evaluates three modes:
use_diffusion_mode=False.use_diffusion_mode=True.Generation is greedy (temperature = 0) so exact token-ID comparison is meaningful. Models are loaded sequentially to fit on a Kaggle GPU, and CUDA is synchronized around every timed generation call.
Recommended T4 run:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 256 \
--repeats 2 \
--dtype float16 \
--attn-implementation sdpa
Slow FP32 numerical control:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 256 \
--repeats 2 \
--dtype float32 \
--attn-implementation eager
These results use only the first example from each dataset and should be treated as diagnostic observations, not universal performance claims.
| Precision / backend | Qwen AR = Orthrus diffusion | Median effective TPF | Median speedup vs Qwen AR |
|---|---|---|---|
| FP16 / SDPA | 3/3 | 3.110 | 3.003× |
| BF16 / eager | 0/3 | 3.269 | 1.054× |
| FP32 / eager | 3/3 | 3.110 | 1.889× |
On a Tesla T4, FP16 is the appropriate performance dtype. T4 has native FP16 Tensor Cores but no native BF16 Tensor Cores. BF16 execution on this GPU is useful as a numerical diagnostic, not as a performance configuration.
The benchmark writes:
orthrus_lossless_results.json
orthrus_lossless_summary.csv
orthrus_first_mismatch_logits.py isolates the first token where sequential Orthrus AR and Orthrus diffusion generation diverge. It ensures that both paths have the same prompt and generated prefix, then captures the vocabulary-logit rows that predict the mismatching token.
python orthrus_first_mismatch_logits.py \
--dtype bfloat16 \
--attn-implementation eager \
--max-new-tokens 256
The diagnostic reports:
| Dataset | Generated index | Sequential AR choice | AR margin | Verifier choice | Verifier margin | Max logit difference |
|---|---|---|---|---|---|---|
| GSM8K | 38 | 66963 ':**' | 0.25 | 1449 ' every' | 0.00, two-way tie | 0.50000 |
| MATH-500 | 206 | 12 '-' | 0.00, two-way tie | 12549 'Since' | 0.25 | 0.40625 |
| HumanEval | 80 | 1759 ' List' | 0.50 | 362 ' A' | 0.00, two-way tie | 0.43750 |
All three divergences occurred under an identical generated prefix. Sequential AR used query length = 1, while the batched verifier used query length = 32.
At logit magnitudes between 32 and 64, adjacent BF16 values are spaced by 0.25. The observed differences can therefore create or remove exact ties and change greedy argmax, even when the two paths are mathematically equivalent in exact arithmetic.
Output:
orthrus_first_mismatch_logits.json
orthrus_block_size_kaggle.py evaluates K = 4, 8, 16, 32 in FP16 on 30 prompts: ten from each dataset.
python orthrus_block_size_kaggle.py \
--block-sizes 4 8 16 32 \
--samples-per-dataset 10 \
--max-new-tokens 256 \
--repeats 2 \
--attn-implementation sdpa
Timed generation and acceptance tracing are separate. The additional diagnostic trace is not included in tokens/second.
| K | Pooled tokens/s | Effective TPF | Accepted/cycle | p50 | p90 | Progress/cycle | Exact vs K=4 |
|---|---|---|---|---|---|---|---|
| 4 | 38.07 | 1.746 | 2.500 | 3.0 | 3.0 | 3.492 | 30/30 |
| 8 | 57.75 | 2.659 | 4.335 | 5.0 | 7.0 | 5.317 | 25/30 |
| 16 | 70.41 | 3.262 | 5.545 | 4.0 | 15.0 | 6.524 | 27/30 |
| 32 | 73.01 | 3.473 | 5.971 | 4.0 | 15.0 | 6.945 | 26/30 |
Increasing K from 16 to 32 improved pooled throughput by approximately 3.7%, while doubling the maximum proposed block length. The identical p90 value of 15 indicates that most K=32 cycles did not benefit from the additional proposal capacity.
Exact vs K=4 compares complete generated token sequences with the K=4 result. It is not a comparison with Qwen AR and is not an accuracy metric. K=4 reports 30/30 because it is the reference.
Outputs:
orthrus_block_size_sweep.json
orthrus_block_size_sweep_details.csv
orthrus_block_size_sweep_summary.csv
orthrus_dynamic_block_kaggle.py adapts K after every verifier cycle. Let A be the number of accepted draft tokens and P the number proposed in the current cycle:
r = A / P
r >= 0.75 -> next K = min(32, current K * 2)
r <= 0.25 -> next K = max(4, current K // 2)
otherwise -> keep the current K
Run the default policy in FP16:
python orthrus_dynamic_block_kaggle.py \
--samples-per-dataset 10 \
--max-new-tokens 256 \
--repeats 2 \
--initial-k 16 \
--attn-implementation sdpa
The script reports overall and per-dataset:
Outputs:
orthrus_dynamic_block_results.json
orthrus_dynamic_block_details.csv
orthrus_dynamic_block_summary.csv
The number of consecutive diffusion-proposed tokens that match the batched AR verifier before the first mismatch. The verifier correction token is not included.
The number of new output tokens committed by a cycle. It normally equals:
accepted draft tokens + 1 verifier correction token
It can be smaller in the final cycle because of EOS or the max_new_tokens boundary.
Steady-state Tokens Per Forward Pass:
effective_tpf = (generated_tokens - 1) / (2 * generation_cycles)
The initial prompt prefill and first generated token are excluded. Every Orthrus generation cycle performs one diffusion pass and one verifier pass.
TPF including the initial prefill:
inclusive_tpf = generated_tokens / (1 + 2 * generation_cycles)
Generated tokens divided by CUDA-synchronized wall time. Dataset/model loading, tokenization, warm-up, result serialization, and separate diagnostic traces are excluded unless a script states otherwise.
The 50th and 90th percentiles of accepted draft tokens across all generation cycles in the selected sample.
The arithmetic mean of the block sizes used across dynamic generation cycles. progress_weighted_mean_k additionally weights each K by the number of output tokens committed during that cycle.
Reproducible Kaggle experiments for evaluating Orthrus, a dual-view model that combines autoregressive (AR) decoding with parallel diffusion proposals and batched AR verification.
The experiments focus on three questions:
The tested checkpoints are:
Qwen/Qwen3-1.7Bchiennv/Orthrus-Qwen3-1.7BThe experiments use examples from:
| Script | Purpose |
|---|---|
orthrus_lossless_kaggle.py | Initial token-ID and latency comparison. Kept as a legacy version. |
orthrus_lossless_kaggle_v2.py | Recommended losslessness benchmark with explicit dtype selection, hardware checks, acceptance tracing, TPF, and mismatch reporting. |
orthrus_first_mismatch_logits.py | Compares sequential AR and batched-verifier logits at the first mismatching token. |
orthrus_block_size_kaggle.py | FP16 sweep over fixed block sizes K = 4, 8, 16, 32. |
orthrus_dynamic_block_kaggle.py | FP16 generation with a block size adapted after every verification cycle. |
The scripts are designed for a Kaggle notebook with GPU and Internet access enabled.
The experiments reported below were run with:
GPU: Tesla T4, compute capability 7.5
torch: 2.10.0+cu128
transformers: 5.8.1
Install the required Python packages:
pip install -U "transformers==5.8.1" "datasets>=4.0.0" "accelerate>=1.0.0"
The Orthrus checkpoint uses custom Hugging Face model code and is loaded with trust_remote_code=True. Review and pin the model revision when running in a security-sensitive environment.
An authenticated Hugging Face token is optional but avoids anonymous Hub rate limits:
import os
os.environ["HF_TOKEN"] = "..."
git clone https://github.com/NikitaKukuzey/orthrus_exps.git
cd orthrus_exps
pip install -U "transformers==5.8.1" "datasets>=4.0.0" "accelerate>=1.0.0"
Run a small smoke test before a full benchmark:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 64 \
--repeats 1 \
--dtype float16 \
--attn-implementation sdpa
orthrus_lossless_kaggle_v2.py evaluates three modes:
use_diffusion_mode=False.use_diffusion_mode=True.Generation is greedy (temperature = 0) so exact token-ID comparison is meaningful. Models are loaded sequentially to fit on a Kaggle GPU, and CUDA is synchronized around every timed generation call.
Recommended T4 run:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 256 \
--repeats 2 \
--dtype float16 \
--attn-implementation sdpa
Slow FP32 numerical control:
python orthrus_lossless_kaggle_v2.py \
--samples-per-dataset 1 \
--max-new-tokens 256 \
--repeats 2 \
--dtype float32 \
--attn-implementation eager
These results use only the first example from each dataset and should be treated as diagnostic observations, not universal performance claims.
| Precision / backend | Qwen AR = Orthrus diffusion | Median effective TPF | Median speedup vs Qwen AR |
|---|---|---|---|
| FP16 / SDPA | 3/3 | 3.110 | 3.003× |
| BF16 / eager | 0/3 | 3.269 | 1.054× |
| FP32 / eager | 3/3 | 3.110 | 1.889× |
On a Tesla T4, FP16 is the appropriate performance dtype. T4 has native FP16 Tensor Cores but no native BF16 Tensor Cores. BF16 execution on this GPU is useful as a numerical diagnostic, not as a performance configuration.
The benchmark writes:
orthrus_lossless_results.json
orthrus_lossless_summary.csv
orthrus_first_mismatch_logits.py isolates the first token where sequential Orthrus AR and Orthrus diffusion generation diverge. It ensures that both paths have the same prompt and generated prefix, then captures the vocabulary-logit rows that predict the mismatching token.
python orthrus_first_mismatch_logits.py \
--dtype bfloat16 \
--attn-implementation eager \
--max-new-tokens 256
The diagnostic reports:
| Dataset | Generated index | Sequential AR choice | AR margin | Verifier choice | Verifier margin | Max logit difference |
|---|---|---|---|---|---|---|
| GSM8K | 38 | 66963 ':**' | 0.25 | 1449 ' every' | 0.00, two-way tie | 0.50000 |
| MATH-500 | 206 | 12 '-' | 0.00, two-way tie | 12549 'Since' | 0.25 | 0.40625 |
| HumanEval | 80 | 1759 ' List' | 0.50 | 362 ' A' | 0.00, two-way tie | 0.43750 |
All three divergences occurred under an identical generated prefix. Sequential AR used query length = 1, while the batched verifier used query length = 32.
At logit magnitudes between 32 and 64, adjacent BF16 values are spaced by 0.25. The observed differences can therefore create or remove exact ties and change greedy argmax, even when the two paths are mathematically equivalent in exact arithmetic.
Output:
orthrus_first_mismatch_logits.json
orthrus_block_size_kaggle.py evaluates K = 4, 8, 16, 32 in FP16 on 30 prompts: ten from each dataset.
python orthrus_block_size_kaggle.py \
--block-sizes 4 8 16 32 \
--samples-per-dataset 10 \
--max-new-tokens 256 \
--repeats 2 \
--attn-implementation sdpa
Timed generation and acceptance tracing are separate. The additional diagnostic trace is not included in tokens/second.
| K | Pooled tokens/s | Effective TPF | Accepted/cycle | p50 | p90 | Progress/cycle | Exact vs K=4 |
|---|---|---|---|---|---|---|---|
| 4 | 38.07 | 1.746 | 2.500 | 3.0 | 3.0 | 3.492 | 30/30 |
| 8 | 57.75 | 2.659 | 4.335 | 5.0 | 7.0 | 5.317 | 25/30 |
| 16 | 70.41 | 3.262 | 5.545 | 4.0 | 15.0 | 6.524 | 27/30 |
| 32 | 73.01 | 3.473 | 5.971 | 4.0 | 15.0 | 6.945 | 26/30 |
Increasing K from 16 to 32 improved pooled throughput by approximately 3.7%, while doubling the maximum proposed block length. The identical p90 value of 15 indicates that most K=32 cycles did not benefit from the additional proposal capacity.
Exact vs K=4 compares complete generated token sequences with the K=4 result. It is not a comparison with Qwen AR and is not an accuracy metric. K=4 reports 30/30 because it is the reference.
Outputs:
orthrus_block_size_sweep.json
orthrus_block_size_sweep_details.csv
orthrus_block_size_sweep_summary.csv
orthrus_dynamic_block_kaggle.py adapts K after every verifier cycle. Let A be the number of accepted draft tokens and P the number proposed in the current cycle:
r = A / P
r >= 0.75 -> next K = min(32, current K * 2)
r <= 0.25 -> next K = max(4, current K // 2)
otherwise -> keep the current K
Run the default policy in FP16:
python orthrus_dynamic_block_kaggle.py \
--samples-per-dataset 10 \
--max-new-tokens 256 \
--repeats 2 \
--initial-k 16 \
--attn-implementation sdpa
The script reports overall and per-dataset:
Outputs:
orthrus_dynamic_block_results.json
orthrus_dynamic_block_details.csv
orthrus_dynamic_block_summary.csv
The number of consecutive diffusion-proposed tokens that match the batched AR verifier before the first mismatch. The verifier correction token is not included.
The number of new output tokens committed by a cycle. It normally equals:
accepted draft tokens + 1 verifier correction token
It can be smaller in the final cycle because of EOS or the max_new_tokens boundary.
Steady-state Tokens Per Forward Pass:
effective_tpf = (generated_tokens - 1) / (2 * generation_cycles)
The initial prompt prefill and first generated token are excluded. Every Orthrus generation cycle performs one diffusion pass and one verifier pass.
TPF including the initial prefill:
inclusive_tpf = generated_tokens / (1 + 2 * generation_cycles)
Generated tokens divided by CUDA-synchronized wall time. Dataset/model loading, tokenization, warm-up, result serialization, and separate diagnostic traces are excluded unless a script states otherwise.
The 50th and 90th percentiles of accepted draft tokens across all generation cycles in the selected sample.
The arithmetic mean of the block sizes used across dynamic generation cycles. progress_weighted_mean_k additionally weights each K by the number of output tokens committed during that cycle.