211 lines
6.8 KiB
Markdown
211 lines
6.8 KiB
Markdown
|
|
---
|
|||
|
|
license: apache-2.0
|
|||
|
|
pipeline_tag: text-generation
|
|||
|
|
library_name: transformers
|
|||
|
|
tags:
|
|||
|
|
- text-generation
|
|||
|
|
- transformers
|
|||
|
|
- qwen3
|
|||
|
|
- qwen
|
|||
|
|
- ai
|
|||
|
|
- llm
|
|||
|
|
- qwen3
|
|||
|
|
- thinking
|
|||
|
|
base_model:
|
|||
|
|
- Qwen/Qwen3-1.7B
|
|||
|
|
---
|
|||
|
|
# Qwen3-1.77B-g023 (Full Precision)
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
This is an optimized variant of [Qwen/Qwen3-1.7B](https://huggingface.co/Qwen/Qwen3-1.7B) created by duplicating **layer 21** to produce a 29-layer model (up from the original 28). The optimal duplication point was found through 5 rounds of iterative testing across layers 9–25, evaluating factual accuracy, perplexity, repetition, and thinking mode functionality.
|
|||
|
|
|
|||
|
|
## TurboQuant-able?
|
|||
|
|
Why yes, yes it can:
|
|||
|
|
(https://github.com/g023/turboquant)
|
|||
|
|
|
|||
|
|
## Key Result
|
|||
|
|
|
|||
|
|
| Metric | Baseline (28 layers) | This Model (29 layers) |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **Overall Score** | 85.9 / 100 | **93.6 / 100** (+7.7) |
|
|||
|
|
| **Factual Accuracy** | 7 / 9 | **9 / 9** |
|
|||
|
|
| **Avg Perplexity** | 17.71 | 19.50 |
|
|||
|
|
| **Thinking Mode** | Working | Working |
|
|||
|
|
| **Non-Thinking Mode** | Working | Working |
|
|||
|
|
|
|||
|
|
## Architecture
|
|||
|
|
|
|||
|
|
| Parameter | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| Layers | 29 (28 original + 1 duplicated) |
|
|||
|
|
| Hidden Size | 2048 |
|
|||
|
|
| Intermediate Size | 6144 |
|
|||
|
|
| Attention Heads | 16 (query) / 8 (KV) |
|
|||
|
|
| Head Dimension | 128 |
|
|||
|
|
| Vocab Size | 151,936 |
|
|||
|
|
| Max Position Embeddings | 40,960 |
|
|||
|
|
| Total Parameters | ~1.77B |
|
|||
|
|
| Dtype | bfloat16 |
|
|||
|
|
| Tied Embeddings | Yes |
|
|||
|
|
|
|||
|
|
## Layer Mapping
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Source Layer → Output Layer
|
|||
|
|
0–20 → 0–20 (unchanged)
|
|||
|
|
21 → 21, 22 (duplicated with noise std=0.001 + depth scaling)
|
|||
|
|
22–27 → 23–28 (shifted +1)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Duplication Method
|
|||
|
|
|
|||
|
|
- **Noise injection**: Gaussian noise (std=0.001) added to duplicate layer to break symmetry
|
|||
|
|
- **Depth scaling**: Factor of √(28/29) ≈ 0.983 applied to prevent activation explosion
|
|||
|
|
- **Anchors preserved**: First layer (0) and last layer (27→28) remain unmodified
|
|||
|
|
|
|||
|
|
## Files
|
|||
|
|
|
|||
|
|
| File | Size | Description |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `model-00001-of-00001.safetensors` | 3.3 GB | Model weights (bfloat16) |
|
|||
|
|
| `config.json` | <1 KB | Model configuration |
|
|||
|
|
| `tokenizer.json` | 11 MB | Tokenizer |
|
|||
|
|
| `tokenizer_config.json` | 10 KB | Tokenizer configuration |
|
|||
|
|
| `vocab.json` | 2.7 MB | Vocabulary |
|
|||
|
|
| `merges.txt` | 1.6 MB | BPE merges |
|
|||
|
|
| `generation_config.json` | <1 KB | Generation defaults |
|
|||
|
|
| `eval_results.json` | 1 KB | Full evaluation metrics |
|
|||
|
|
|
|||
|
|
## Usage
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Tweakable parameters
|
|||
|
|
# MODEL_PATH = "./Qwen3-BEST" # local run
|
|||
|
|
MODEL_PATH = "g023/Qwen3-1.77B-g023"
|
|||
|
|
MAX_NEW_TOKENS = 8192
|
|||
|
|
TEMPERATURE = 0.7
|
|||
|
|
DO_SAMPLE = True
|
|||
|
|
TOP_P = 0.9
|
|||
|
|
TOP_K = 50
|
|||
|
|
REPETITION_PENALTY = 1.1
|
|||
|
|
STREAMING = True # Set to True for streaming inference
|
|||
|
|
INPUT_MESSAGE = "You are completing the next step in a task to create an arcade game in javascript. Your available tools are rationalize, red_green_tdd, and create_plan. Synthesize their output when reasoning. "
|
|||
|
|
|
|||
|
|
from transformers import AutoModelForCausalLM, AutoTokenizer, TextStreamer
|
|||
|
|
import time
|
|||
|
|
|
|||
|
|
def load_model():
|
|||
|
|
print("Loading model...")
|
|||
|
|
model = AutoModelForCausalLM.from_pretrained(
|
|||
|
|
MODEL_PATH,
|
|||
|
|
device_map="auto",
|
|||
|
|
)
|
|||
|
|
tokenizer = AutoTokenizer.from_pretrained(MODEL_PATH)
|
|||
|
|
print("Model loaded.")
|
|||
|
|
return model, tokenizer
|
|||
|
|
|
|||
|
|
def inference_non_streaming(model, tokenizer, messages):
|
|||
|
|
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=True)
|
|||
|
|
inputs = tokenizer(text, return_tensors="pt").to(model.device)
|
|||
|
|
outputs = model.generate(
|
|||
|
|
**inputs,
|
|||
|
|
max_new_tokens=MAX_NEW_TOKENS,
|
|||
|
|
temperature=TEMPERATURE,
|
|||
|
|
do_sample=DO_SAMPLE,
|
|||
|
|
top_p=TOP_P,
|
|||
|
|
top_k=TOP_K,
|
|||
|
|
repetition_penalty=REPETITION_PENALTY,
|
|||
|
|
)
|
|||
|
|
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
|
|||
|
|
print("Response:", response)
|
|||
|
|
return response
|
|||
|
|
|
|||
|
|
def inference_streaming(model, tokenizer, messages):
|
|||
|
|
final_response = ""
|
|||
|
|
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=True)
|
|||
|
|
inputs = tokenizer(text, return_tensors="pt").to(model.device)
|
|||
|
|
streamer = TextStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
|
|||
|
|
outputs = model.generate(
|
|||
|
|
**inputs,
|
|||
|
|
max_new_tokens=MAX_NEW_TOKENS,
|
|||
|
|
temperature=TEMPERATURE,
|
|||
|
|
do_sample=DO_SAMPLE,
|
|||
|
|
top_p=TOP_P,
|
|||
|
|
top_k=TOP_K,
|
|||
|
|
repetition_penalty=REPETITION_PENALTY,
|
|||
|
|
streamer=streamer,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# return a final str
|
|||
|
|
return final_response
|
|||
|
|
|
|||
|
|
def llm_stream(model, tokenizer, conversation):
|
|||
|
|
import time
|
|||
|
|
start_time = time.time()
|
|||
|
|
text = tokenizer.apply_chat_template(conversation, tokenize=False, add_generation_prompt=True, enable_thinking=True)
|
|||
|
|
inputs = tokenizer(text, return_tensors="pt").to(model.device)
|
|||
|
|
from io import StringIO
|
|||
|
|
buffer = StringIO()
|
|||
|
|
class CapturingTextStreamer(TextStreamer):
|
|||
|
|
def __init__(self, tokenizer, buffer):
|
|||
|
|
super().__init__(tokenizer, skip_prompt=True, skip_special_tokens=True)
|
|||
|
|
self.buffer = buffer
|
|||
|
|
def on_finalized_text(self, text, stream_end=False):
|
|||
|
|
self.buffer.write(text)
|
|||
|
|
print(text, end="", flush=True)
|
|||
|
|
streamer = CapturingTextStreamer(tokenizer, buffer)
|
|||
|
|
outputs = model.generate(
|
|||
|
|
**inputs,
|
|||
|
|
max_new_tokens=MAX_NEW_TOKENS,
|
|||
|
|
temperature=TEMPERATURE,
|
|||
|
|
do_sample=DO_SAMPLE,
|
|||
|
|
top_p=TOP_P,
|
|||
|
|
top_k=TOP_K,
|
|||
|
|
repetition_penalty=REPETITION_PENALTY,
|
|||
|
|
streamer=streamer,
|
|||
|
|
)
|
|||
|
|
response = buffer.getvalue()
|
|||
|
|
|
|||
|
|
if "</think>" in response:
|
|||
|
|
parts = response.rsplit("</think>", 1)
|
|||
|
|
reasoning = parts[0].strip()
|
|||
|
|
content = parts[1].strip()
|
|||
|
|
else:
|
|||
|
|
reasoning = ""
|
|||
|
|
content = response.strip()
|
|||
|
|
char_per_token = 3.245
|
|||
|
|
reasoning_tokens = round(len(reasoning) / char_per_token)
|
|||
|
|
content_tokens = round(len(content) / char_per_token)
|
|||
|
|
total_tokens = reasoning_tokens + content_tokens
|
|||
|
|
time_taken = time.time() - start_time
|
|||
|
|
ret_dict = {
|
|||
|
|
"reasoning": reasoning,
|
|||
|
|
"content": content,
|
|||
|
|
"usage": {
|
|||
|
|
"reasoning_tokens": reasoning_tokens,
|
|||
|
|
"content_tokens": content_tokens,
|
|||
|
|
"total_tokens": total_tokens,
|
|||
|
|
},
|
|||
|
|
"time_taken": time_taken,
|
|||
|
|
}
|
|||
|
|
return ret_dict
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
model, tokenizer = load_model()
|
|||
|
|
messages = [{"role": "user", "content": INPUT_MESSAGE}]
|
|||
|
|
ret = llm_stream(model, tokenizer, messages)
|
|||
|
|
print("Result dict:", ret)
|
|||
|
|
|
|||
|
|
# output tokens per second by taking total_tokens and time_taken
|
|||
|
|
if ret["usage"]["total_tokens"] > 0 and ret["time_taken"] > 0:
|
|||
|
|
tps = ret["usage"]["total_tokens"] / ret["time_taken"]
|
|||
|
|
print(f"Tokens per second: {tps:.2f}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Base Model
|
|||
|
|
|
|||
|
|
- **Model**: Qwen/Qwen3-1.7B
|
|||
|
|
- **Architecture**: Qwen3ForCausalLM (decoder-only transformer with GQA)
|
|||
|
|
- **License**: Apache 2.0
|