Qwen/Qwen3-0.6B→ GRPO-fine-tuned for explainable candidate ranking, under a rule-based reward model (no LLM-as-a-judge).
| Metric | Baseline (Qwen/Qwen3-0.6B) | redrob-qwen-grpo | Δ |
|---|---|---|---|
Mean rule-based reward [0,1] | 0.539 | 0.713 | +0.173 |
| Eval episodes | 12 | 12 | — |
| Hardware | Apple M1 Pro 16 GB · MPS | Apple M1 Pro 16 GB · MPS | — |
Eval max_new_tokens | 384 | 384 | — |
seed=0, sequential, identical prompts)
is used for both rows so the comparison is fair.| Reward component | Baseline | Trained | Δ |
|---|---|---|---|
format_valid | 0.833 | 1.000 | +0.167 |
decision_match | 0.500 | 0.500 | +0.000 |
score_alignment | 0.373 | 0.653 | +0.280 |
reason_quality | 0.000 | 0.778 | +0.778 |
length_penalty | 1.000 | 1.000 | +0.000 |
no_hallucination | 0.779 | 0.656 | -0.124 |
total | 0.539 | 0.713 | +0.173 |
1from transformers import AutoModelForCausalLM, AutoTokenizer
2import torch
3
4tok = AutoTokenizer.from_pretrained("williyam/redrob-qwen-grpo")
5mdl = AutoModelForCausalLM.from_pretrained(
6 "williyam/redrob-qwen-grpo", dtype=torch.float32
7).eval()
8
9system = (
10 "You are RedRob, an explainable candidate-ranking assistant. "
11 "Decide whether the candidate should be SHORTLISTED for the role. "
12 "Respond with a single JSON object: "
13 '{"decision":"shortlist"|"reject","score":0..1,"reasons":[..]}.'
14)
15user = (
16 "[JOB DESCRIPTION]\n<your JD here>\n\n"
17 "[CANDIDATE]\n<candidate profile>"
18)
19
20prompt = tok.apply_chat_template(
21 [
22 {"role": "system", "content": system},
23 {"role": "user", "content": user},
24 ],
25 tokenize=False,
26 add_generation_prompt=True,
27)
28inputs = tok(prompt, return_tensors="pt")
29out = mdl.generate(**inputs, max_new_tokens=512, do_sample=False)
30print(tok.decode(out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True))1{
2 "decision": "shortlist" | "reject",
3 "score": 0.0-1.0,
4 "reasons": ["short, grounded bullet", "..."]
5}| Aspect | Value |
|---|---|
| Base model | Qwen/Qwen3-0.6B (600M params, Qwen3 chat template) |
| Algorithm | GRPO (TRL GRPOTrainer) |
| Reward signal | Rule-based (no LLM judge): six interpretable components |
| Reward components | format_valid, decision_match, score_alignment, reason_quality, length_penalty, no_hallucination |
| Optimiser steps | 10 (deliberately short — sample-efficient demo on a laptop GPU) |
num_generations | 2 (group size; 2-arm advantage estimate) |
KL coefficient β | 0.04 |
| Learning rate | 5e-6 |
| Sampling temperature / top-p | 1.0 / 0.95 |
| Max completion length | 96 tokens (training); 512 tokens (eval, this card) |
| Hardware | Apple M1 Pro 16 GB · MPS (bf16=False, fp16=False, fp32) |
| Gradient checkpointing | Yes (use_reentrant=False) |
| Training wall-clock | ~4.5 minutes for 10 steps |
configs/grpo_qwen3_0p6b.yaml.RuleBasedRewardModel
on six components, each clipped to [0, 1]:| Component | What it measures |
|---|---|
format_valid | Output parses as {"decision","score","reasons"} JSON. |
decision_match | Matches gold "shortlist" / "reject" label. |
score_alignment | 1 - │pred_score - gold_score│. |
reason_quality | 2–5 short, diverse reasons that aren't copy-pasted from the input. |
length_penalty | Stays inside a sensible character budget. |
no_hallucination | Proper nouns / numbers in reasons all appear in the JD or candidate text. |
total ∈ [0, 1].



| File | Description |
|---|---|
training_curves.png | Mean reward [0,1] (left axis) + GRPO loss (right axis) vs train step. |
baseline_vs_trained.png | Per-episode reward on the same eval rollout, baseline vs trained. |
reward_components.png | Mean value of each rule-based reward component, baseline vs trained. |
reward_distribution.png | Histogram of episode rewards across the eval rollout. |
{decision, score, reasons} response.Qwen/Qwen3-0.6B's pre-training data
and from the synthetic dataset of 50 Redrob candidates.1@misc{redrob_qwen_grpo_2026,
2 title = {redrob-qwen-grpo: GRPO fine-tune of Qwen3-0.6B for explainable candidate ranking},
3 author = {Williyam M},
4 year = {2026},
5 url = {https://huggingface.co/williyam/redrob-qwen-grpo},
6 note = {Open-source artifact from the Talentry-AI / Redrob × Hack2Skill - India Runs submission.}
7}Qwen/Qwen3-0.6B from the Qwen team.trl for the GRPO implementation.Redrob × Hack2Skill — India Runs for the JD + 50-candidate fixture.