pearcodermini / README.md
Pear Labs
v1.1: add real C++ training data, fix eos_token_id, add MMLU/MMLU-Pro/MMLU-Redux + best-practice inference config
5afc2f6
|
Raw
History Blame Contribute Delete
10.1 kB
metadata
license: other
base_model: Qwen/Qwen3.5-4B
tags:
  - code
  - lora
  - qlora
  - qwen3
  - pear-coder-mini
language:
  - en
  - es

Pear Coder Mini (v1.1)

Pear Coder Mini es un asistente de código desarrollado por Pear Labs, basado en Qwen3.5-4B y afinado con QLoRA (rank 32, rsLoRA) sobre una mezcla de datos de código general, C++, razonamiento sobre código, COBOL y datos de instrucción general bilingüe (inglés/español) para reducir el olvido catastrófico.

Este repositorio contiene el modelo fusionado (adapter LoRA + pesos base ya combinados) listo para servir directamente. La versión anterior (v1.0) sigue disponible en la revisión de git v1.0 de este mismo repo.

Qué cambió en v1.1

v1.0 mostraba un desempeño en C++ notablemente por debajo de Python/JS (21.1% pass@1 en MultiPL-E C++). Se agregó una porción real de C++ (codeparrot/xlcost-text-to-code, config C++-program-level, detokenizado y reformateado con clang-format), reemplazando una porción equivalente del bucket generalista (no se agregó dataset extra, se mantuvo el tamaño total ~8,955 ejemplos y la proporción 60/25/15 código/razonamiento/general) para no diluir lo que ya funcionaba bien ni alargar el tiempo/costo de entrenamiento.

Benchmark v1.0 v1.1 Δ
HumanEval (n=164) 56.7% 57.9% +1.2
HumanEval+ (n=164) 56.7% 57.9% +1.2
MBPP sanitized (n=257) 58.4% 63.4% +5.0
MBPP+ (n=378) 66.4% 66.1% -0.3
MultiPL-E JavaScript (n=161) 52.8% 50.9% -1.9
MultiPL-E C++ (n=161) 21.1% 29.2% +8.1
MMLU (5-shot, n=14,042) no medido 68.9%
MMLU-Pro (5-shot, n=12,032)* no medido 42.1%
MMLU-Redux (5-shot, n=5,440) no medido 71.6%
Instruction-Following-Mini (n=20) no medido 90.0%

Mejora clara en C++ (objetivo principal), MBPP, HumanEval; JS y MBPP+ se mantienen esencialmente parejos (variación dentro de ruido de muestreo). No se detectó evidencia de olvido catastrófico: MMLU/MMLU-Redux muestran capacidad general sólida para un 4B.

* MMLU-Pro no es comparable directo al 79.1 oficial de Qwen3.5-4B — ese número se mide con razonamiento explícito (CoT); aquí se midió con comparación directa de log-probabilidad (sin razonamiento) por velocidad, lo cual castiga mucho el resultado en este benchmark específico ya que fue diseñado para requerir varios pasos de razonamiento. No implica pérdida de capacidad real, implica que la metodología de medición no es la misma. Ver la nota sobre thinking mode más abajo — es evidencia directa de esto: con thinking mode activado, el pass@1 en código sube ~20 puntos.

Buenas prácticas / configuración recomendada de inferencia

Se corrió un barrido de configuraciones (greedy, temperatura/top_p, y thinking mode) sobre un subset de HumanEval para encontrar el punto dulce:

Configuración pass@1 (n=40)
greedy (T=0), thinking OFF 50.0%
T=0.2, top_p=0.95 45.0%
T=0.4, top_p=0.9 42.5%
T=0.7, top_p=0.9 42.5%
T=1.0, top_p=1.0 32.5%
thinking mode ON, greedy 70.0%

Recomendaciones:

  • Para tareas de código donde la precisión importa más que la latencia: activa thinking mode (enable_thinking=True al aplicar el chat template) con max_new_tokens>=2048 — el modelo necesita espacio para razonar antes de responder. Esto sube el pass@1 en código de 50% a 70%, al costo de ~5x más tokens generados y por lo tanto más latencia/costo.
  • Para respuestas rápidas/baratas: enable_thinking=False, greedy (do_sample=False), max_new_tokens~400. Es el modo usado en la mayoría de las evaluaciones de este repo.
  • Evita temperatura alta para generación de código: el pass@1 cae de forma monótona con la temperatura (50%→32.5% de T=0 a T=1.0). Si necesitas variabilidad (ej. para dar varias opciones), usa T=0.2-0.4 como máximo, no más.
  • El modelo sí sabe cerrar correctamente el bloque <think>...</think> cuando se le da presupuesto de tokens suficiente — un hallazgo temprano de "thinking mode roto" durante el desarrollo resultó ser un artefacto de medir con max_new_tokens demasiado bajo (400), no un defecto real del modelo.

Entrenamiento

  • Base: Qwen/Qwen3.5-4B
  • Método: QLoRA (4-bit NF4), rank 32, rsLoRA (use_rslora=True), alpha=32
  • Módulos objetivo: q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj
  • Learning rate: 1e-4, cosine schedule, warmup 3%
  • Épocas: 1 (sobre ~8,955 ejemplos)
  • Hardware: 1x RTX 3090 24GB (vast.ai)
  • Longitud de secuencia: 2048 tokens
  • Pérdida final: 0.732 (desde 1.19 inicial) · mean_token_accuracy final: 0.796

Mezcla de datos v1.1 (~8,955 ejemplos)

Categoría Fuente % aprox.
Código general sahil2801/CodeAlpaca-20k, glaiveai/glaive-code-assistant-v3 ~46%
C++ real (nuevo en v1.1) codeparrot/xlcost-text-to-code (C++-program-level, reformateado con clang-format) ~15%
COBOL harshini-kumar/CobolCodeBench ~0.5%
Razonamiento sobre código nvidia/OpenCodeReasoning, hkust-nlp/CodeIO-PyEdu-Reasoning ~25%
Instrucción general EN/ES (replay anti-olvido) databricks/databricks-dolly-15k, argilla/databricks-dolly-15k-curated-multilingual ~13%
Identidad (Pear Coder Mini / Pear Labs, EN/ES) sintético ~0.7%

Se evitaron deliberadamente datasets tipo "Alpaca" cuyas respuestas fueron generadas por modelos de OpenAI, por las restricciones de sus TOS sobre uso para entrenar modelos competidores.

Nota importante de configuración (leer antes de servir el modelo)

El generation_config.json de este repo fue corregido manualmente: el checkpoint base de Qwen3.5-4B trae por defecto eos_token_id apuntando a <|endoftext|>, pero el token real de cierre de turno de chat es <|im_end|>. Sin esta corrección el modelo no para de generar y alucina turnos de conversación falsos. Este repo ya incluye el fix (eos_token_id: [248046, 248044]); si conviertes o vuelves a exportar los pesos, conserva este ajuste.

Resultados de evaluación completos

Metodología: greedy decoding salvo donde se indique, pass@1 con ejecución real de tests sobre los sets completos (no subsets) de HumanEval, MBPP, HumanEval+, MBPP+ y MultiPL-E. MMLU/MMLU-Pro/MMLU-Redux con metodología estándar 5-shot, comparación de log-probabilidad (sin CoT, ver nota arriba). Evaluación propia (no el CLI oficial de evalplus/lm-evaluation-harness) pero replicando la misma metodología sobre los datasets públicos oficiales.

Benchmark n Resultado
HumanEval 164 57.9% pass@1
HumanEval+ 164 57.9% pass@1
MBPP sanitized 257 63.4% pass@1
MBPP+ 378 66.1% pass@1
MultiPL-E JavaScript 161 50.9% pass@1
MultiPL-E C++ 161 29.2% pass@1
MMLU (5-shot) 14,042 68.9% accuracy
MMLU-Pro (5-shot, sin CoT)* 12,032 42.1% accuracy
MMLU-Redux (5-shot) 5,440 71.6% accuracy
Instruction-Following-Mini 20 90.0%
COBOL (estructura correcta) 3 100%
Identidad EN/ES 6 nunca respondió "Qwen" ni otro nombre incorrecto

Uso

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

model = AutoModelForCausalLM.from_pretrained(
    "elianalfonsolopezpreciado/pearcodermini", torch_dtype=torch.bfloat16, device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("elianalfonsolopezpreciado/pearcodermini")

messages = [{"role": "user", "content": "Write a python function that reverses a string."}]

# Rapido/barato:
text = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=False
)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=400, do_sample=False)
print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))

# Mayor precision en codigo (thinking mode, ver seccion de buenas practicas):
text_thinking = tokenizer.apply_chat_template(
    messages, tokenize=False, add_generation_prompt=True, enable_thinking=True
)
inputs = tokenizer(text_thinking, return_tensors="pt").to(model.device)
out = model.generate(**inputs, max_new_tokens=2048, do_sample=False)
print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))

Limitaciones conocidas

  • Solo 1 época de entrenamiento sobre un dataset moderado (~9k ejemplos); hay margen para mejorar pass@1 con más datos/épocas si el presupuesto lo permite.
  • Los benchmarks se corrieron con arnés propio, no el CLI oficial de evalplus ni lm-evaluation-harness — recomendado revalidar ahí antes de citar las cifras en materiales de marketing/lanzamiento oficiales, especialmente MMLU-Pro (ver nota de metodología sin CoT arriba).
  • MBPP+ se evaluó con el campo assertion (test base), no el comparador diferencial completo base_input+plus_input del checker 100% oficial.
  • No se probó explícitamente resistencia a jailbreaks/seguridad adversarial.
  • No fue entrenado con formato de tool-calling/function-calling — no está pensado para uso agéntico (ej. SWE-bench, BFCL) tal cual, solo para generación/asistencia de código directa en chat.
  • C++ mejoró sustancialmente pero sigue por debajo de Python/JS (29.2% vs 50-58%) — sigue siendo el lenguaje más débil del modelo.