Unsloth resuelve el problema de la inferencia duplicada en la versión cuantificada de QwQ-32B
Tutoriales prácticos sobre IAPublicado hace 5 meses Círculo de intercambio de inteligencia artificial 1.3K 00
Recientemente, el equipo de Qwen ha publicado QwQ-32B un modelo de inferencia que se ha utilizado en muchas Puntos de referencia El espectáculo es comparable al del DeepSeek-R1 Sin embargo, muchos usuarios se han encontrado con problemas de generación ilimitada, duplicados excesivos, problemas de tokens y de ajuste fino. Sin embargo, muchos usuarios se han encontrado con problemas de generación ilimitada, duplicados excesivos, problemas de tokens y de ajuste fino. Este artículo tiene como objetivo proporcionar una guía detallada para ayudarle a depurar y resolver estos problemas, y liberar todo el potencial de QwQ-32B.
Desenredar El modelo subido por el equipo corrige los fallos mencionados y permite una mejor compatibilidad con herramientas y marcos de trabajo como el ajuste fino, vLLM y Transformers. Para quienes utilicen llama.cpp Así como otros usuarios con llama.cpp como motor backend, véase este enlace Obtén orientación para solucionar el problema de la generación infinita.
Desbloqueo del modelo QwQ-32B (error corregido):
- Modelo de formato GGUF
- Modelos cuantitativos dinámicos de 4 bits
- Modelo cuantitativo BnB de 4 bits
- Modelo de precisión total de 16 bits
Ajustes oficiales recomendados
⚙️ Ajustes oficiales recomendados
Basándose en las recomendaciones oficiales de Qwen, a continuación se indican los ajustes de parámetros recomendados para la inferencia del modelo:
- Temperatura: 0,6
- Top_K: 40 (rango recomendado 20-40)
- Min_P: 0.1 (opcional pero funciona bien)
- Top_P: 0,95
- Penalización de repetición: 1.0 (en llama.cpp y transformadores, 1.0 significa desactivado)
- Plantilla de chat:
<|im_start|>user\nCreate a Flappy Bird game in Python.<|im_end|>\n<|im_start|>assistant\n<think>\n
llama.cpp Configuración recomendada
👍 llama.cpp configuración recomendada
El equipo de Unsloth se ha dado cuenta de que muchos usuarios tienden a utilizar una versión superior a la 1.0 de Repetition Penalty
Sin embargo, este enfoque en realidad interfiere con el mecanismo de muestreo de llama.cpp. La penalización por duplicado pretendía reducir el número de duplicados generados, pero los experimentos han demostrado que este enfoque no tiene el efecto deseado.
Dicho esto, desactivar por completo la penalización por repetición (establecerla en 1,0) también es una opción. Sin embargo, el equipo de Unsloth descubrió que una penalización por repetición adecuada sigue siendo eficaz para suprimir la generación infinita.
Para utilizar la penalización por repetición de forma eficaz, el orden de los muestreadores en llama.cpp debe ajustarse para garantizar que al aplicar la penalización por repetición Repetition Penalty
antes del muestreo, de lo contrario se producirá una generación infinita. Para ello, añada el siguiente parámetro:
--samplers "top_k;top_p;min_p;temperature;dry;typ_p;xtc"
Por defecto, llama.cpp utiliza el siguiente orden de muestreadores:
--samplers "dry;top_k;typ_p;top_p;min_p;xtc;temperature"
El orden ajustado del equipo Unsloth básicamente intercambia las posiciones de temperatura y seco, y adelanta min_p. Esto significa que el muestreador se aplicará en el siguiente orden:
top_k=40
top_p=0.95
min_p=0.1
temperature=0.6
dry
typ_p
xtc
Si el problema persiste, intente mover el --repeat-penalty
El valor de 1,0 se aumentó ligeramente a 1,2 o 1,3.
Gracias a @krist486 por avisarnos del problema con la dirección de muestreo en llama.cpp.
Sanción por repetición en seco
☀️ Sanción por repetición en seco
El equipo de Unsloth examinó las dry penalty
e intentamos utilizar un valor de 0,8. Sin embargo, los resultados experimentales muestran que eldry penalty
Es más probable que cause errores de sintaxis, especialmente al generar código . Si el usuario sigue teniendo problemas, pruebe a configurar la opción dry penalty
Aumentar a 0,8.
Si decide utilizar dry penalty
el orden de muestreo ajustado puede ser igualmente útil.
Ollama ejecutando QwQ-32B Tutoriales
🦙 Ollama Ejecución del tutorial QwQ-32B
- Si aún no está instalado
ollama
Por favor, instálelo primero.
apt-get update
apt-get install pciutils -y
curl -fSSL [https://ollama.com/install.sh](https://www.google.com/url?sa=E&q=https%3A%2F%2Follama.com%2Finstall.sh) | sh
- Ejecuta el modelo. Si la ejecución falla, prueba a ejecutarlo en otro terminal
ollama serve
El equipo de Unsloth ha incluido todas las correcciones y los parámetros sugeridos (temperatura, etc.) en el modelo de carga de Cara Abrazada.param
¡Documentación!
ollama run hf.co/unsloth/QwQ-32B-GGUF:Q4_K_M
llama.cpp Tutorial para ejecutar QwQ-32B
📖 llama.cpp ejecutando QwQ-32B Tutoriales
- a través de (un hueco) llama.cpp Obtener la última versión
llama.cpp
. Puedes consultar las siguientes instrucciones de compilación. Si no dispone de GPU o sólo desea realizar la inferencia en la CPU, defina la opción-DGGML_CUDA=ON
Sustituir por-DGGML_CUDA=OFF
.
apt-get update
apt-get install pciutils build-essential cmake curl libcurl4-openssl-dev -y
git clone [https://github.com/ggerganov/llama.cpp](https://www.google.com/url?sa=E&q=https%3A%2F%2Fgithub.com%2Fggerganov%2Fllama.cpp)
cmake llama.cpp -B llama.cpp/build
-DBUILD_SHARED_LIBS=ON -DGGML_CUDA=ON -DLLAMA_CURL=ON
cmake --build llama.cpp/build --config Release -j --clean-first --target llama-quantize llama-cli llama-gguf-split
cp llama.cpp/build/bin/llama-* llama.cpp
- Descargar el modelo (durante la instalación)
pip install huggingface_hub hf_transfer
(después). Puede seleccionarse Q4_K_M u otras versiones cuantificadas (por ejemplo, BF16 full precision). Para más versiones, visite: https://huggingface.co/unsloth/QwQ-32B-GGUF.
# !pip install huggingface_hub hf_transfer
import os
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
from huggingface_hub import snapshot_download
snapshot_download(
repo_id="unsloth/QwQ-32B-GGUF",
local_dir="unsloth-QwQ-32B-GGUF",
allow_patterns=[" Q4_K_M "], # For Q4_K_M
)
- Ejecute el script de prueba de Flappy Bird proporcionado por Unsloth y el resultado se guardará en la carpeta
Q4_K_M_yes_samplers.txt
Documentación. - Ajuste los parámetros en función de la situación real.
--threads 32
Establece el número de hilos de la CPU.--ctx-size 16384
Establece la longitud de contexto del--n-gpu-layers 99
Ajuste el número de niveles de descarga de la GPU. Si la GPU se está quedando sin memoria, pruebe a ajustar el parámetro--n-gpu-layers
valor. Elimine este parámetro si sólo se utiliza la inferencia de CPU. --repeat-penalty 1.1
responder cantando--dry-multiplier 0.5
son los parámetros de penalización por repetición y penalización en seco, que el usuario puede ajustar según sus necesidades.
./llama.cpp/llama-cli
--model unsloth-QwQ-32B-GGUF/QwQ-32B-Q4_K_M.gguf
--threads 32
--ctx-size 16384
--n-gpu-layers 99
--seed 3407
--prio 2
--temp 0.6
--repeat-penalty 1.1
--dry-multiplier 0.5
--min-p 0.1
--top-k 40
--top-p 0.95
-no-cnv
--samplers "top_k;top_p;min_p;temperature;dry;typ_p;xtc"
--prompt "<|im_start|>user\nCreate a Flappy Bird game in Python. You must include these things:\n1. You must use pygame.\n2. The background color should be randomly chosen and is a light shade. Start with a light blue color.\n3. Pressing SPACE multiple times will accelerate the bird.\n4. The bird's shape should be randomly chosen as a square, circle or triangle. The color should be randomly chosen as a dark color.\n5. Place on the bottom some land colored as dark brown or yellow chosen randomly.\n6. Make a score shown on the top right side. Increment if you pass pipes and don't hit them.\n7. Make randomly spaced pipes with enough space. Color them randomly as dark green or light brown or a dark gray shade.\n8. When you lose, show the best score. Make the text inside the screen. Pressing q or Esc will quit the game. Restarting is pressing SPACE again.\nThe final game should be inside a markdown section in Python. Check your code for errors and fix them before the final markdown section.<|im_end|>\n<|im_start|>assistant\n <think> \n"
2>&1 | tee Q4_K_M_yes_samplers.txt
Los consejos anteriores para el juego Flappy Bird son de Unsloth's DeepSeekR1-Dynamic 1.58bit Blogs. La palabra clave completa está más abajo:
<|im_start|>user
Create a Flappy Bird game in Python. You must include these things:
1. You must use pygame.
2. The background color should be randomly chosen and is a light shade. Start with a light blue color.
3. Pressing SPACE multiple times will accelerate the bird.
4. The bird's shape should be randomly chosen as a square, circle or triangle. The color should be randomly chosen as a dark color.
5. Place on the bottom some land colored as dark brown or yellow chosen randomly.
6. Make a score shown on the top right side. Increment if you pass pipes and don't hit them.
7. Make randomly spaced pipes with enough space. Color them randomly as dark green or light brown or a dark gray shade.
8. When you lose, show the best score. Make the text inside the screen. Pressing q or Esc will quit the game. Restarting is pressing SPACE again.
The final game should be inside a markdown section in Python. Check your code for errors and fix them before the final markdown section.<|im_end|>
<|im_start|>assistant
<think>
Aquí están las partes inicial y final del código Python generado por el modelo (con el proceso de pensamiento eliminado):
import pygame
import random
import sys
pygame.init()
### Continues
class Bird:
def __init__(self):
### Continues
def main():
best_score = 0
current_score = 0
game_over = False
pipes = []
first_time = True # Track first game play
# Initial setup
background_color = (173, 216, 230) # Light blue initially
land_color = random.choice(land_colors)
bird = Bird()
while True:
for event in pygame.event.get():
### Continues
if not game_over:
# Update bird and pipes
bird.update()
### Continues
# Drawing
### Continues
pygame.display.flip()
clock.tick(60)
if __name__ == "__main__":
main()
El modelo ha generado un juego de Flappy Bird que funciona.

A continuación, intente eliminar el parámetro --samplers "top_k;top_p;min_p;temperature;dry;typ_p;xtc" y ejecute el mismo comando sin la corrección Unsloth. El resultado se guardará en el archivo Q4_K_M_no_samplers.txt.
./llama.cpp/llama-cli
--model unsloth-QwQ-32B-GGUF/QwQ-32B-Q4_K_M.gguf
--threads 32
--ctx-size 16384
--n-gpu-layers 99
--seed 3407
--prio 2
--temp 0.6
--repeat-penalty 1.1
--dry-multiplier 0.5
--min-p 0.1
--top-k 40
--top-p 0.95
-no-cnv
--prompt "<|im_start|>user\nCreate a Flappy Bird game in Python. You must include these things:\n1. You must use pygame.\n2. The background color should be randomly chosen and is a light shade. Start with a light blue color.\n3. Pressing SPACE multiple times will accelerate the bird.\n4. The bird's shape should be randomly chosen as a square, circle or triangle. The color should be randomly chosen as a dark color.\n5. Place on the bottom some land colored as dark brown or yellow chosen randomly.\n6. Make a score shown on the top right side. Increment if you pass pipes and don't hit them.\n7. Make randomly spaced pipes with enough space. Color them randomly as dark green or light brown or a dark gray shade.\n8. When you lose, show the best score. Make the text inside the screen. Pressing q or Esc will quit the game. Restarting is pressing SPACE again.\nThe final game should be inside a markdown section in Python. Check your code for errors and fix them before the final markdown section.<|im_end|>\n<|im_start|>assistant\n<think>\n"
2>&1 | tee Q4_K_M_no_samplers.txt
Si no se utilizan correcciones, el modelo genera resultados con bucles y un elevado número de errores de sintaxis de Python , así como otros problemas diversos. Por ejemplo, el siguiente fragmento de código parece correcto, ¡pero en realidad es incorrecto! Línea 39 pipes.clear() ### <<< NameError: el nombre 'pipes' no está definido ¿Olvidaste importar 'pipes'? Se te ha olvidado importar 'pipes'?
import pygame
import random
pygame.init()
# Constants
WIDTH, HEIGHT = 800, 600
GROUND_HEIGHT = 20
GRAVITY = 0.7
PIPE_SPEED = -3
BIRD_SIZE = 45
MIN_GAP = 130
MAX_GAP = 200
PIPE_COLORS = [(0, 96, 0), (205, 133, 63), (89, 97, 107)]
DARK_BROWN = (94, 72, 4)
YELLOW = (252, 228, 6)
screen = pygame.display.set_mode((WIDTH, HEIGHT))
clock = pygame.time.Clock()
def random_light_color():
return (
random.randint(180, 230),
random.randint(190, 300),
random.randint(250, 255)
)
def reset_game():
global bird_x, bird_y
global pipes, score
global background_color, land_color
global bird_shape, bird_color
# Bird properties
bird_x = WIDTH * 0.3
bird_y = HEIGHT // 2
bird_vel = -5 # Initial upward thrust
pipes.clear() ### <<< NameError: name 'pipes' is not defined. Did you forget to import 'pipes'?
Si se aumenta --repeat-penalty a 1,5, la situación empeora, los errores de sintaxis se hacen más evidentes y el código no funciona en absoluto.
import pygame
from random import randint # For generating colors/shapes/positions randomly
pygame.init()
# Constants:
WIDTH, HEIGHT =456 ,702 #
BACKGROUND_COLOR_LIGHTS=['lightskyblue']
GAP_SIZE=189 #
BIRD_RADIUS=3.
PIPE_SPEED=- ( ) ?
class Game():
def __init__(self):
self.screen_size=( )
def reset_game_vars():
global current_scor e
# set to zero and other initial states.
# Main game loop:
while running :
for event in pygame.event.get() :
if quit ... etc
pygame.quit()
print("Code is simplified. Due time constraints, full working version requires further implementation.")
Uno podría pensar que esto es sólo un problema con la versión cuantificada de Q4_K_M... la versión de precisión completa de BF16 debería estar bien, ¿verdad? Sin embargo, no es así. Incluso con el modelo de precisión completa BF16, si no se aplica la corrección -samplers "top_k;top_p;min_p;temperature;dry;typ_p;xtc" proporcionada por el equipo Unsloth y se utiliza la penalización por repetición, también se experimentará un fallo de generación.
¿No aparece la ficha?
🤔 ficha ¿No aparece?
Los usuarios han comentado que algunos sistemas no pueden mostrar el proceso de pensamiento correctamente debido al token añadido por defecto a la plantilla de chat. Los usuarios deben editar manualmente la plantilla Jinja para incluirlo:
{%- if tools %} {{- '<|im_start|>system\n' }} {%- if messages[0]['role'] == 'system' %} {{- messages[0]['content'] }} {%- else %} {{- '' }} {%- endif %} {{- "\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }} {%- for tool in tools %} {{- "\n" }} {{- tool | tojson }} {%- endfor %} {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }} {%- else %} {%- if messages[0]['role'] == 'system' %} {{- '<|im_start|>system\n' + messages[0]['content'] + '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- for message in messages %} {%- if (message.role == "user") or (message.role == "system" and not loop.first) %} {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }} {%- elif message.role == "assistant" and not message.tool_calls %} {%- set content = message.content.split('</think>')[-1].lstrip('\n') %} {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }} {%- elif message.role == "assistant" %} {%- set content = message.content.split('</think>')[-1].lstrip('\n') %} {{- '<|im_start|>' + message.role }} {%- if message.content %} {{- '\n' + content }} {%- endif %} {%- for tool_call in message.tool_calls %} {%- if tool_call.function is defined %} {%- set tool_call = tool_call.function %} {%- endif %} {{- '\n<tool_call>\n{"name": "' }} {{- tool_call.name }} {{- '", "arguments": ' }} {{- tool_call.arguments | tojson }} {{- '}\n</tool_call>' }} {%- endfor %} {{- '<|im_end|>\n' }} {%- elif message.role == "tool" %} {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != "tool") %} {{- '<|im_start|>user' }} {%- endif %} {{- '\n<tool_response>\n' }} {{- message.content }} {{- '\n</tool_response>' }} {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %} {{- '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- endfor %} {%- if add_generation_prompt %} {{- '<|im_start|>assistant\n<think>\n' }} {%- endif %}
Modificado para eliminar el \n final. La modificación requiere que los modelos añadan manualmente \n durante la inferencia, pero puede que esto no siempre funcione. el equipo de DeepSeek también ha modificado todos los modelos para que añadan tokens por defecto para forzar al modelo a entrar en modo de inferencia.
Por lo tanto, cambie {%- if add_generation_prompt %} {{- 'assistant\n\n' }} {%- endif %} a {%- if add_generation_prompt %} {{- 'assistant\n\n' }} {%- endif %}. {- 'assistant\n' }} {%- endif %}, es decir, eliminar \n.
Plantilla jinja completa con la parte \n eliminada.
{%- if tools %} {{- '<|im_start|>system\n' }} {%- if messages[0]['role'] == 'system' %} {{- messages[0]['content'] }} {%- else %} {{- '' }} {%- endif %} {{- "\n\n# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }} {%- for tool in tools %} {{- "\n" }} {{- tool | tojson }} {%- endfor %} {{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }} {%- else %} {%- if messages[0]['role'] == 'system' %} {{- '<|im_start|>system\n' + messages[0]['content'] + '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- for message in messages %} {%- if (message.role == "user") or (message.role == "system" and not loop.first) %} {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>' + '\n' }} {%- elif message.role == "assistant" and not message.tool_calls %} {%- set content = message.content.split('</think>')[-1].lstrip('\n') %} {{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }} {%- elif message.role == "assistant" %} {%- set content = message.content.split('</think>')[-1].lstrip('\n') %} {{- '<|im_start|>' + message.role }} {%- if message.content %} {{- '\n' + content }} {%- endif %} {%- for tool_call in message.tool_calls %} {%- if tool_call.function is defined %} {%- set tool_call = tool_call.function %} {%- endif %} {{- '\n<tool_call>\n{"name": "' }} {{- tool_call.name }} {{- '", "arguments": ' }} {{- tool_call.arguments | tojson }} {{- '}\n</tool_call>' }} {%- endfor %} {{- '<|im_end|>\n' }} {%- elif message.role == "tool" %} {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != "tool") %} {{- '<|im_start|>user' }} {%- endif %} {{- '\n<tool_response>\n' }} {{- message.content }} {{- '\n</tool_response>' }} {%- if loop.last or (messages[loop.index0 + 1].role != "tool") %} {{- '<|im_end|>\n' }} {%- endif %} {%- endif %} {%- endfor %} {%- if add_generation_prompt %} {{- '<|im_start|>assistant\n' }} {%- endif %}
Notas adicionales
Notas adicionales
En un principio, el equipo de Unsloth supuso que el problema podía deberse a lo siguiente:
- La longitud de contexto de QwQ puede no ser la nativa de 128K, sino 32K más la extensión YaRN. Véase, por ejemplo, el archivo readme en https://huggingface.co/Qwen/QwQ-32B:
{
...,
"rope_scaling": {
"factor": 4.0,
"original_max_position_embeddings": 32768,
"type": "yarn"
}
}
El equipo de Unsloth intentó reescribir el manejo de YaRN en llama.cpp, pero el problema persistía.
--override-kv qwen2.context_length=int:131072
--override-kv qwen2.rope.scaling.type=str:yarn
--override-kv qwen2.rope.scaling.factor=float:4
--override-kv qwen2.rope.scaling.original_context_length=int:32768
--override-kv qqwen2.rope.scaling.attn_factor=float:1.13862943649292 \
- El equipo de Unsloth también sospechaba que el valor RMS Layernorm epsilon podría ser incorrecto, y quizá debería ser 1e-6 en lugar de 1e-5. Por ejemplo. Este enlace. en rms_norm_eps=1e-06 y Este enlace. en rms_norm_eps=1e-05. el equipo de Unsloth también intentó reescribir este valor, pero el problema sigue sin resolverse:
--override-kv qwen2.attention.layer_norm_rms_epsilon=float:0.000001 \
- Gracias a @kalomaze, el equipo de Unsloth también ha comprobado los tokeniser IDs entre llama.cpp y Transformers para ver si coincidían. Los resultados muestran que sí coinciden, por lo que la falta de coincidencia en los ID de los tokenizadores no es el origen del problema.
Estos son los resultados del experimento del equipo de Unsloth:
61KB archivo_BF16_no_samplers.txt
BF16 Precisión total, sin aplicación de reparación de muestras
55KB file_BF16_yes_samplers.txt
BF16 Precisión total, reparación por muestreo aplicada
71KB final_Q4_K_M_no_samplers.txt
Q4_K_M Precisión, sin fijación de muestra aplicada
65KB final_Q4_K_M_yes_samplers.txt
Q4_K_M Precisión, corrección de muestreo aplicada
Corrección de errores del tokenizador
✏️ Corrección de errores del tokenizador
- El equipo de Unsloth también ha encontrado algunos problemas específicos que afectan al ajuste.El token EOS es correcto, pero una opción más lógica para el token PAD sería "".El equipo de Unsloth ha actualizado la configuración en https://huggingface.co/unsloth/QwQ-32B/blob/ El equipo de Unsloth ha actualizado la configuración en main/tokenizer_config.json.
"eos_token": "<|im_end|>",
"pad_token": "<|endoftext|>",
Cuantificación dinámica de 4 bits
🛠️ Cuantificación dinámica de 4 bits
El equipo de Unsloth también subió un modelo de cuantificación dinámico de 4 bits, que mejora significativamente la precisión del modelo en comparación con la cuantificación simple de 4 bits. La siguiente figura muestra el análisis de errores de los valores de activación y pesos del modelo QwQ durante el proceso de cuantificación:

El equipo de Unsloth ha subido el modelo cuantitativo dinámico de 4 bits a: https://huggingface.co/unsloth/QwQ-32B-unsloth-bnb-4bit.
desde vLLM A partir de la versión 0.7.3 (20 de febrero de 2024) https://github.com/vllm-project/vllm/releases/tag/v0.7.3, ¡vLLM ha empezado a soportar la carga de modelos cuantitativos Unsloth dinámicos de 4 bits!
¡Todos los modelos de formato GGUF se pueden encontrar en https://huggingface.co/unsloth/QwQ-32B-GGUF!
© declaración de copyright
El artículo está protegido por derechos de autor y no debe reproducirse sin autorización.
Artículos relacionados
Sin comentarios...