初始化项目,由ModelHub XC社区提供模型
Model: cs-552-2026-Flash-McQueenS-and-TheKing/safety_model Source: Original Platform
This commit is contained in:
36
.gitattributes
vendored
Normal file
36
.gitattributes
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||
*.arrow filter=lfs diff=lfs merge=lfs -text
|
||||
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
||||
*.ftz filter=lfs diff=lfs merge=lfs -text
|
||||
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||
*.h5 filter=lfs diff=lfs merge=lfs -text
|
||||
*.joblib filter=lfs diff=lfs merge=lfs -text
|
||||
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
||||
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
||||
*.model filter=lfs diff=lfs merge=lfs -text
|
||||
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
||||
*.npy filter=lfs diff=lfs merge=lfs -text
|
||||
*.npz filter=lfs diff=lfs merge=lfs -text
|
||||
*.onnx filter=lfs diff=lfs merge=lfs -text
|
||||
*.ot filter=lfs diff=lfs merge=lfs -text
|
||||
*.parquet filter=lfs diff=lfs merge=lfs -text
|
||||
*.pb filter=lfs diff=lfs merge=lfs -text
|
||||
*.pickle filter=lfs diff=lfs merge=lfs -text
|
||||
*.pkl filter=lfs diff=lfs merge=lfs -text
|
||||
*.pt filter=lfs diff=lfs merge=lfs -text
|
||||
*.pth filter=lfs diff=lfs merge=lfs -text
|
||||
*.rar filter=lfs diff=lfs merge=lfs -text
|
||||
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
||||
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
||||
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
||||
*.tar filter=lfs diff=lfs merge=lfs -text
|
||||
*.tflite filter=lfs diff=lfs merge=lfs -text
|
||||
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||
*.wasm filter=lfs diff=lfs merge=lfs -text
|
||||
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
||||
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
||||
146
README.md
Normal file
146
README.md
Normal file
@@ -0,0 +1,146 @@
|
||||
---
|
||||
license: apache-2.0
|
||||
base_model: Qwen/Qwen3-1.7B
|
||||
language:
|
||||
- en
|
||||
pipeline_tag: text-generation
|
||||
library_name: transformers
|
||||
tags:
|
||||
- safety
|
||||
- qwen3
|
||||
- sft
|
||||
- lora
|
||||
- multiple-choice
|
||||
datasets:
|
||||
- cs-552-2026-Flash-McQueenS-and-TheKing/safety_sft_data
|
||||
---
|
||||
|
||||
# safety_model — Qwen3-1.7B fine-tuned for safety multiple-choice (non-thinking)
|
||||
|
||||
This is the **safety** individual model for the CS-552 (Modern NLP, EPFL, Spring 2026)
|
||||
course project. It is a supervised fine-tune of **Qwen/Qwen3-1.7B** specialised for the
|
||||
safety benchmark, which is **multiple-choice, scored pass@1**.
|
||||
|
||||
The model answers in **non-thinking mode**: it emits a one-sentence justification and then
|
||||
the answer letter inside `\boxed{}`, with no `<think>` reasoning block. The rationale is given
|
||||
in [Why non-thinking, and no CoT distillation](#why-non-thinking-and-no-cot-distillation).
|
||||
|
||||
> **Output contract.** Every answer ends with the option letter wrapped in `\boxed{...}`,
|
||||
> e.g. `\boxed{C}`. The boxing instruction and the non-thinking switch are baked into the
|
||||
> tokenizer's `chat_template.jinja`, so they apply even when the evaluator calls
|
||||
> `apply_chat_template(messages, add_generation_prompt=True)` with no extra arguments.
|
||||
|
||||
## Training data
|
||||
|
||||
The full training set is released as a companion dataset:
|
||||
[**cs-552-2026-Flash-McQueenS-and-TheKing/safety_sft_data**](https://huggingface.co/datasets/cs-552-2026-Flash-McQueenS-and-TheKing/safety_sft_data).
|
||||
|
||||
It contains **3,250 English multiple-choice items** across the seven safety categories of
|
||||
SafetyBench (Zhang et al., 2024). Each example pairs a **user message** (a question with
|
||||
labelled options) with an **assistant message** (a one-sentence justification followed by the
|
||||
answer letter in `\boxed{}`). Four categories are derived from established public datasets;
|
||||
three smaller categories are LLM-generated to cover topics with no convenient public source.
|
||||
|
||||
| Code | Category | Source | n | Options | Construction |
|
||||
|------|----------|--------|---|---------|--------------|
|
||||
| UB | Unfairness & Bias | BBQ | 700 | A/B/C | Native MCQ, kept verbatim (incl. "Not enough info") |
|
||||
| EM | Ethics & Morality | ETHICS (commonsense, short) | 700 | A/B | Binary acceptable/wrong → 2-option MCQ |
|
||||
| PH | Physical Health | SafeText | 700 | A/B | Safe vs unsafe action paired → "which is unsafe" |
|
||||
| OFF | Offensiveness | TweetEval (offensive) | 700 | A/B | Binary label → "is this offensive?" Yes/No |
|
||||
| MH | Mental Health | LLM-generated | 150 | A–D | Synthetic, SafetyBench augmentation recipe |
|
||||
| IA | Illegal Activities | LLM-generated | 150 | A–D | Synthetic, SafetyBench augmentation recipe |
|
||||
| PP | Privacy & Property | LLM-generated | 150 | A–D | Synthetic, SafetyBench augmentation recipe |
|
||||
|
||||
### Processing pipeline
|
||||
|
||||
- **Letter balancing.** Within each category the correct option is shuffled per item so the
|
||||
answer key is not concentrated on one position. (The aggregate A/B skew is a structural
|
||||
consequence of most categories being 2- or 3-option, not a per-item bias.)
|
||||
- **Synthetic validation.** The three generated categories were filtered for validity,
|
||||
deduplicated, self-consistency-checked, and letter-balanced before inclusion.
|
||||
- **Decontamination.** Because four categories come from public datasets that the safety
|
||||
benchmark may also draw on, every training row was checked against the SafetyBench English
|
||||
test split using word 8-gram containment and sentence-embedding cosine similarity;
|
||||
near-duplicate rows were dropped.
|
||||
- **Format.** Each example is a single user→assistant turn. The user turn carries the question
|
||||
and labelled options; the assistant target is a one-sentence justification followed by
|
||||
`\boxed{<letter>}`.
|
||||
|
||||
## Fine-tuning
|
||||
|
||||
| Setting | Value |
|
||||
|---|---|
|
||||
| Base model | `Qwen/Qwen3-1.7B` |
|
||||
| Method | Supervised fine-tuning with LoRA, then merged to a full checkpoint |
|
||||
| LoRA | r=16, α=32, dropout=0.05, all linear projections |
|
||||
| Loss | Completion-only (prompt and the empty `<think></think>` block are masked) |
|
||||
| Epochs | 3 |
|
||||
| Learning rate | 1e-4, cosine schedule, 3% warmup |
|
||||
| Effective batch | 16 (per-device 2 × grad-accum 8) |
|
||||
| Max sequence length | 1024 |
|
||||
| Precision | bf16/fp16 |
|
||||
| Mode | Non-thinking (`enable_thinking = false`) |
|
||||
|
||||
The LoRA adapter was merged back into the base weights so this repository is a standalone,
|
||||
vLLM-loadable Qwen3 checkpoint (full `model.safetensors`, `config.json`,
|
||||
`generation_config.json`, and a tokenizer carrying `chat_template.jinja`).
|
||||
|
||||
### Train / inference consistency
|
||||
|
||||
Each training example was built as
|
||||
`apply_chat_template(user, add_generation_prompt=True, enable_thinking=False)` + completion +
|
||||
`<|im_end|>`, with the prompt tokens masked from the loss. The model is therefore trained on
|
||||
**exactly** the prefix the evaluator produces — including the empty `<think></think>` block
|
||||
that Qwen3 emits in non-thinking mode — so there is no train/test format drift.
|
||||
|
||||
### Generation config
|
||||
|
||||
Sampling defaults favour low variance, because the benchmark is **pass@1**: a single
|
||||
completion is scored, so determinism is worth more than diversity. We use a low temperature
|
||||
while keeping `do_sample=true` as the project requires. For a pass@k task the opposite choice
|
||||
would be appropriate.
|
||||
|
||||
## Why non-thinking, and no CoT distillation
|
||||
|
||||
A reasonable first instinct is to distill chain-of-thought (CoT) from a stronger model and
|
||||
train the student to reason before answering. We deliberately did **not** do this, for four
|
||||
reasons specific to this task:
|
||||
|
||||
1. **The benchmark is not reasoning-intensive.** The SafetyBench authors explicitly omit
|
||||
CoT-based evaluation, noting the benchmark is less reasoning-intensive than capability
|
||||
benchmarks such as MMLU. Safety MCQ is largely knowledge and norm-judgment, not multi-step
|
||||
deduction.
|
||||
2. **CoT mostly helps on math and symbolic tasks.** The meta-analysis of Sprague et al. (2024),
|
||||
"To CoT or not to CoT?", finds the large gains from CoT concentrate on math, logic, and
|
||||
symbolic reasoning, with little benefit on knowledge/judgment multiple-choice. On such
|
||||
tasks, directly emitting the answer is about as accurate as reasoning first.
|
||||
3. **CoT can hurt small models on classification-style tasks**, and a 1.7B model is firmly in
|
||||
the size range where this risk is real. Long reasoning traces also add variance to a
|
||||
single-shot (pass@1) prediction.
|
||||
4. **Token budget and the boxing contract.** Under a capped generation budget, a long `<think>`
|
||||
trace risks consuming the budget before reaching `\boxed{}` — a failure mode that scores ~0
|
||||
despite a fluent answer. Non-thinking emits the justification and box immediately, so answer
|
||||
extraction is reliable and fast.
|
||||
|
||||
There is also a data-integrity argument: generating CoT with a teacher that already knows the
|
||||
gold label tends to produce post-hoc *rationalisations* rather than genuine reasoning, which is
|
||||
especially hazardous on bias/safety items (e.g. BBQ ambiguous-context questions) where a fluent
|
||||
justification can be built for a stereotyped wrong answer.
|
||||
|
||||
Note the model is **not** answer-only: the one-sentence justification before the box is itself a
|
||||
lightweight, in-format rationale. We keep that, and skip the heavier `<think>` block.
|
||||
|
||||
## Intended use and limitations
|
||||
|
||||
- **Intended use.** Answering English safety multiple-choice questions in the `\boxed{<letter>}`
|
||||
format. This is a research/coursework artifact.
|
||||
- **Option-count coverage.** Training items span 2–4 options; performance on items with many
|
||||
more options is less certain.
|
||||
- **Category imbalance.** The four public-derived categories (700 each) are far larger than the
|
||||
three synthetic ones (150 each), so the model is expected to be stronger on the former.
|
||||
- **Not a deployable safety system.** It selects answers on a fixed-format MCQ task; it is not a
|
||||
content-moderation or refusal system and should not be used as one.
|
||||
|
||||
## How to use
|
||||
|
||||
```python
|
||||
93
chat_template.jinja
Normal file
93
chat_template.jinja
Normal file
@@ -0,0 +1,93 @@
|
||||
{%- set enable_thinking = false %}
|
||||
{%- if messages[0]['role'] != 'system' %}
|
||||
{%- set messages = [{'role':'system','content':'You are answering a multiple-choice question. Give a one-sentence justification, then put ONLY the letter of the correct option inside \\boxed{}. Example final line: \\boxed{B}'}] + messages %}
|
||||
{%- endif %}
|
||||
{%- if tools %}
|
||||
{{- '<|im_start|>system\n' }}
|
||||
{%- if messages[0].role == 'system' %}
|
||||
{{- messages[0].content + '\n\n' }}
|
||||
{%- endif %}
|
||||
{{- "# 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 %}
|
||||
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
||||
{%- for message in messages[::-1] %}
|
||||
{%- set index = (messages|length - 1) - loop.index0 %}
|
||||
{%- if ns.multi_step_tool and message.role == "user" and message.content is string and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}
|
||||
{%- set ns.multi_step_tool = false %}
|
||||
{%- set ns.last_query_index = index %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- for message in messages %}
|
||||
{%- if message.content is string %}
|
||||
{%- set content = message.content %}
|
||||
{%- else %}
|
||||
{%- set content = '' %}
|
||||
{%- endif %}
|
||||
{%- if (message.role == "user") or (message.role == "system" and not loop.first) %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
||||
{%- elif message.role == "assistant" %}
|
||||
{%- set reasoning_content = '' %}
|
||||
{%- if message.reasoning_content is string %}
|
||||
{%- set reasoning_content = message.reasoning_content %}
|
||||
{%- else %}
|
||||
{%- if '</think>' in content %}
|
||||
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
||||
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if loop.index0 > ns.last_query_index %}
|
||||
{%- if loop.last or (not loop.last and reasoning_content) %}
|
||||
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content.strip('\n') + '\n</think>\n\n' + content.lstrip('\n') }}
|
||||
{%- else %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + content }}
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{{- '<|im_start|>' + message.role + '\n' + content }}
|
||||
{%- endif %}
|
||||
{%- if message.tool_calls %}
|
||||
{%- for tool_call in message.tool_calls %}
|
||||
{%- if (loop.first and content) or (not loop.first) %}
|
||||
{{- '\n' }}
|
||||
{%- endif %}
|
||||
{%- if tool_call.function %}
|
||||
{%- set tool_call = tool_call.function %}
|
||||
{%- endif %}
|
||||
{{- '<tool_call>\n{"name": "' }}
|
||||
{{- tool_call.name }}
|
||||
{{- '", "arguments": ' }}
|
||||
{%- if tool_call.arguments is string %}
|
||||
{{- tool_call.arguments }}
|
||||
{%- else %}
|
||||
{{- tool_call.arguments | tojson }}
|
||||
{%- endif %}
|
||||
{{- '}\n</tool_call>' }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{{- '<|im_end|>\n' }}
|
||||
{%- elif message.role == "tool" %}
|
||||
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
|
||||
{{- '<|im_start|>user' }}
|
||||
{%- endif %}
|
||||
{{- '\n<tool_response>\n' }}
|
||||
{{- 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' }}
|
||||
{%- if enable_thinking is defined and enable_thinking is false %}
|
||||
{{- '<think>\n\n</think>\n\n' }}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
63
config.json
Normal file
63
config.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"architectures": [
|
||||
"Qwen3ForCausalLM"
|
||||
],
|
||||
"attention_bias": false,
|
||||
"attention_dropout": 0.0,
|
||||
"bos_token_id": 151643,
|
||||
"dtype": "float16",
|
||||
"eos_token_id": 151645,
|
||||
"head_dim": 128,
|
||||
"hidden_act": "silu",
|
||||
"hidden_size": 2048,
|
||||
"initializer_range": 0.02,
|
||||
"intermediate_size": 6144,
|
||||
"layer_types": [
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention",
|
||||
"full_attention"
|
||||
],
|
||||
"max_position_embeddings": 40960,
|
||||
"max_window_layers": 28,
|
||||
"model_type": "qwen3",
|
||||
"num_attention_heads": 16,
|
||||
"num_hidden_layers": 28,
|
||||
"num_key_value_heads": 8,
|
||||
"pad_token_id": null,
|
||||
"rms_norm_eps": 1e-06,
|
||||
"rope_parameters": {
|
||||
"rope_theta": 1000000,
|
||||
"rope_type": "default"
|
||||
},
|
||||
"sliding_window": null,
|
||||
"tie_word_embeddings": true,
|
||||
"transformers_version": "5.10.2",
|
||||
"use_cache": true,
|
||||
"use_sliding_window": false,
|
||||
"vocab_size": 151936
|
||||
}
|
||||
13
generation_config.json
Normal file
13
generation_config.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"bos_token_id": 151643,
|
||||
"do_sample": true,
|
||||
"eos_token_id": [
|
||||
151645,
|
||||
151643
|
||||
],
|
||||
"pad_token_id": 151643,
|
||||
"temperature": 0.3,
|
||||
"top_k": 20,
|
||||
"top_p": 0.9,
|
||||
"transformers_version": "5.10.2"
|
||||
}
|
||||
3
model.safetensors
Normal file
3
model.safetensors
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ef4e81286c5f5ef9d66c4e2c699d1bffef18fa6e71aa4b58490338a24c1b5de5
|
||||
size 3441185296
|
||||
3
tokenizer.json
Normal file
3
tokenizer.json
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be75606093db2094d7cd20f3c2f385c212750648bd6ea4fb2bf507a6a4c55506
|
||||
size 11422650
|
||||
30
tokenizer_config.json
Normal file
30
tokenizer_config.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_prefix_space": false,
|
||||
"backend": "tokenizers",
|
||||
"bos_token": null,
|
||||
"clean_up_tokenization_spaces": false,
|
||||
"eos_token": "<|im_end|>",
|
||||
"errors": "replace",
|
||||
"extra_special_tokens": [
|
||||
"<|im_start|>",
|
||||
"<|im_end|>",
|
||||
"<|object_ref_start|>",
|
||||
"<|object_ref_end|>",
|
||||
"<|box_start|>",
|
||||
"<|box_end|>",
|
||||
"<|quad_start|>",
|
||||
"<|quad_end|>",
|
||||
"<|vision_start|>",
|
||||
"<|vision_end|>",
|
||||
"<|vision_pad|>",
|
||||
"<|image_pad|>",
|
||||
"<|video_pad|>"
|
||||
],
|
||||
"is_local": false,
|
||||
"local_files_only": false,
|
||||
"model_max_length": 131072,
|
||||
"pad_token": "<|endoftext|>",
|
||||
"split_special_tokens": false,
|
||||
"tokenizer_class": "Qwen2Tokenizer",
|
||||
"unk_token": null
|
||||
}
|
||||
Reference in New Issue
Block a user