--- license: mit base_model: unsloth/gpt-oss-20b library_name: peft tags: - minizinc - constraint-programming - optimization - code-generation - lora - unsloth - learn2zinc - harmony-format datasets: - skadio/learn2zinc-p2c-augmented pipeline_tag: text-generation --- # GPT-OSS-20B — MiniZinc Code Generation (LoRA) A fine-tuned version of **GPT-OSS-20B** for generating MiniZinc constraint programming code from natural language optimization problem descriptions. > **Important:** This model uses the **Harmony response format** (`<|start|>`, `<|channel|>`, `<|message|>`, `<|end|>` tags) — it does **not** use standard chat templates. ## Model Description This model translates plain-English optimization problems into executable [MiniZinc](https://www.minizinc.org/) code. It was fine-tuned with LoRA on the [learn2zinc](https://huggingface.co/datasets/skadio/learn2zinc-p2c-augmented) dataset using the [Unsloth](https://github.com/unslothai/unsloth) library. | Attribute | Value | |---|---| | Base model | `unsloth/gpt-oss-20b` | | Parameters | 20B | | Fine-tuning method | LoRA (rank 64) | | Chat template | **Harmony** (custom — no `apply_chat_template`) | | Max sequence length | 4096 | ## Training Details | Hyperparameter | Value | |---|---| | LoRA rank (r) | 64 | | LoRA alpha | 64 | | LoRA dropout | 0 | | Target modules | q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj | | Learning rate | 2e-4 | | LR scheduler | Cosine | | Warmup steps | 50 | | Epochs | 3 | | Optimizer | AdamW 8-bit | | Weight decay | 0.01 | | Precision | bf16 | | Quantization during training | 4-bit | | Seed | 42 | | Training | Response-only (SFTTrainer with `train_on_responses_only`) | ### Harmony Format GPT-OSS uses a **Harmony response format** with structured channels instead of standard chat templates: - **Roles:** `system`, `developer`, `user`, `assistant` - **Channels:** `analysis` (reasoning), `commentary`, `final` (answer) - **Tags:** `<|start|>`, `<|message|>`, `<|channel|>`, `<|end|>` The `developer` role carries the task instructions (not `system`). The `system` role is reserved for meta-information (identity, knowledge cutoff, reasoning level). The model's answer appears in the `<|channel|>final<|message|>` section. ## Usage ### Installation ```bash pip install unsloth torch transformers ``` ### Inference ```python import torch from unsloth import FastLanguageModel # Load model — do NOT apply a chat template model, tokenizer = FastLanguageModel.from_pretrained( model_name="skadio/learn2zinc-GPT-oss-20B" max_seq_length=4096, dtype=None, load_in_4bit=True, ) FastLanguageModel.for_inference(model) # Define the problem problem = """A farmer needs to decide how many cows, sheep, and chickens to raise in order to achieve maximum profit. The farmer can sell cows, sheep, and chickens for $500, $200, and $8 each, respectively. The feed costs for each cow, sheep, and chicken are $100, $80, and $5, respectively. The profit is the difference between the selling price and the feed cost. Each cow, sheep, and chicken produces 10, 5, and 3 units of manure per day, respectively. Due to the limited time the farm staff has for cleaning the farm each day, they can handle up to 800 units of manure. Additionally, because of the limited farm size, the farmer can raise at most 50 chickens. Furthermore, the farmer must have at least 10 cows to meet customer demand. The farmer must also raise at least 20 sheep. Finally, the total number of animals cannot exceed 100.""" # Build Harmony-format prompt prompt = ( "<|start|>system<|message|>You are ChatGPT, a large language model trained by OpenAI.\n" "Knowledge cutoff: 2024-06\n" "Current date: 2026-03-04\n\n" "Reasoning: medium\n\n" "# Valid channels: analysis, commentary, final. " "Channel must be included for every message.<|end|>" "<|start|>developer<|message|># Instructions\n\n" "Generate MiniZinc code for the following optimization problem.<|end|>" f"<|start|>user<|message|>{problem}<|end|>" "<|start|>assistant" ) inputs = tokenizer(prompt, return_tensors="pt").to(model.device) # Resolve stop-token IDs stop_token_ids = [] for token in ["<|end|>", "<|return|>"]: encoded = tokenizer.encode(token, add_special_tokens=False) if encoded: stop_token_ids.append(encoded[0]) # Generate with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=4096, do_sample=False, eos_token_id=stop_token_ids, pad_token_id=tokenizer.pad_token_id, ) generated = tokenizer.decode( outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=False, ) # --- Extract content from the final channel --- if "<|channel|>final<|message|>" in generated: content = generated.split("<|channel|>final<|message|>")[-1] for stop in ["<|end|>", "<|return|>"]: content = content.split(stop)[0] response = content.strip() else: # Fallback: strip stop tags for stop in ["<|end|>", "<|return|>"]: generated = generated.split(stop)[0] response = generated.strip() print(response) ``` ### Extracting MiniZinc Code The model wraps its output in a fenced code block. To extract the code: ```python import re def extract_minizinc_code(text): match = re.search(r'```(?:\w+)?\n(.*?)\n```', text, re.DOTALL | re.IGNORECASE) return match.group(1).strip() if match else None code = extract_minizinc_code(response) ``` ## Evaluation Models were evaluated on the **IndustryOR** subset of [learn2zinc](https://huggingface.co/datasets/skadio/learn2zinc-p2c-augmented) (`cardinal_operations_industryor`). Generated MiniZinc code was executed with the **HiGHS** solver (120 s timeout). All generations used **temperature = 0** for reproducibility. **Metrics:** Execution Success Rate (code compiles and runs) and Solution Correctness (objective matches expected value within 1e-6). For full evaluation details, see [learn2zinc](https://github.com/skadio/learn2zinc). ## Dataset Training data comes from [skadio/learn2zinc-augmented](https://huggingface.co/datasets/skadio/learn2zinc-augmented), which pairs natural language optimization problem descriptions with corresponding MiniZinc code. For GPT-OSS, training examples were reformatted into Harmony format with automatic CoT detection: examples containing reasoning are routed to the `analysis` channel, while direct answers use only the `final` channel. ## Framework - [Unsloth](https://github.com/unslothai/unsloth) - [PEFT / LoRA](https://github.com/huggingface/peft) - [TRL SFTTrainer](https://github.com/huggingface/trl)