初始化项目,由ModelHub XC社区提供模型
Model: chartreuse-verte/prose-rewriter-4b-v2.2 Source: Original Platform
This commit is contained in:
287
README.md
Normal file
287
README.md
Normal file
@@ -0,0 +1,287 @@
|
||||
---
|
||||
license: agpl-3.0
|
||||
base_model: Qwen/Qwen3-4B-Base
|
||||
pipeline_tag: text-generation
|
||||
language:
|
||||
- en
|
||||
tags:
|
||||
- prose
|
||||
- rewriting
|
||||
- style-transfer
|
||||
- creative-writing
|
||||
- deslop
|
||||
- qwen3
|
||||
library_name: transformers
|
||||
---
|
||||
|
||||
# prose-rewriter-4b-v2.2
|
||||
|
||||
A paragraph-level **prose rewriter**: it takes prose written by a large model and
|
||||
re-renders it to be more human, preserving the semantics it was given.
|
||||
|
||||
`Qwen/Qwen3-4B-Base` with a rank-32 LoRA merged in at strength 1.20.
|
||||
|
||||
**A third less repetition.** Every training target that repeats itself -- the
|
||||
same sentence twice, a phrase said again, three sentences opening on the same
|
||||
words, `happy, happy` -- is now rejected, so the pool only ever shows the model
|
||||
the repetition going away. Rewrites that repeat themselves fall from 13.5% to
|
||||
8.7%.
|
||||
|
||||
**Less unsupported content, less slop vocabulary**, at the same strength as
|
||||
v2.1. See [Evaluation](#evaluation).
|
||||
|
||||
## Variants
|
||||
|
||||
| Path | Format | Use with |
|
||||
|---|---|---|
|
||||
| `/` | safetensors bf16, `qwen3` arch | transformers |
|
||||
| `GGUF/prose-rewriter-4b-v2.2-Q8_0.gguf` | GGUF Q8_0, 4.69 GB | llama.cpp / llama-cpp-python |
|
||||
| `GGUF/prose-rewriter-4b-v2.2-Q4_K_M.gguf` | GGUF Q4_K_M, 2.72 GB | llama.cpp / llama-cpp-python |
|
||||
|
||||
The quants carry the chat template and stop on `<|im_end|>`, and the adapted
|
||||
output head is kept separate from the token embeddings, stored at Q8_0.
|
||||
|
||||
## Prompt format
|
||||
|
||||
```
|
||||
<|im_start|>source
|
||||
{paragraph}<|im_end|>
|
||||
<|im_start|>rewrite
|
||||
```
|
||||
|
||||
The chat template in this repo builds exactly that string, byte for byte, from
|
||||
one message:
|
||||
|
||||
```python
|
||||
messages = [{"role": "source", "content": paragraph}]
|
||||
tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
|
||||
```
|
||||
|
||||
It is not a chat model. Every message is rendered as the source paragraph, so a
|
||||
runtime that probes the template with a `user` message still gets a valid
|
||||
prompt. Send one message per call.
|
||||
|
||||
## Serving recipe
|
||||
|
||||
Sampled at `temperature=0.9, top_p=0.9`. However, temperature 0.9 hallucinates harder
|
||||
because this release edits harder than v2. It's recommended you use 0.8-0.85 instead.
|
||||
|
||||
```python
|
||||
import torch
|
||||
from transformers import AutoModelForCausalLM, AutoTokenizer
|
||||
|
||||
repo = "chartreuse-verte/prose-rewriter-4b-v2.2"
|
||||
tok = AutoTokenizer.from_pretrained(repo)
|
||||
model = AutoModelForCausalLM.from_pretrained(repo, dtype=torch.bfloat16, device_map="cuda").eval()
|
||||
|
||||
def rewrite(paragraph):
|
||||
text = tok.apply_chat_template(
|
||||
[{"role": "source", "content": paragraph}],
|
||||
tokenize=False, add_generation_prompt=True,
|
||||
)
|
||||
ids = tok(text, return_tensors="pt", add_special_tokens=False).input_ids.to(model.device)
|
||||
out = model.generate(ids, max_new_tokens=512, do_sample=True, temperature=0.9, top_p=0.9)
|
||||
return tok.decode(out[0, ids.shape[1]:], skip_special_tokens=True).strip()
|
||||
```
|
||||
|
||||
Same thing under llama.cpp. The role is `source`, which no chat API models, so
|
||||
build the string yourself; `<|im_end|>` stops it:
|
||||
|
||||
```bash
|
||||
llama-cli -m GGUF/prose-rewriter-4b-v2.2-Q8_0.gguf -no-cnv -n 512 --temp 0.9 --top-p 0.9 \
|
||||
-p '<|im_start|>source
|
||||
{paragraph}<|im_end|>
|
||||
<|im_start|>rewrite
|
||||
'
|
||||
```
|
||||
|
||||
## Input length
|
||||
|
||||
The training pool's median input is 37 words and 80% of it is under 70, so serve
|
||||
it on anything from a full sentence up.
|
||||
|
||||
The practical floor is about **15 words**. Below it the failure mode is padding,
|
||||
cutting and fabrication rather than gibberish. Below 80 bytes, pass the text
|
||||
through unchanged.
|
||||
|
||||
## Evaluation
|
||||
|
||||
Both releases measured **as they ship**, baked at strength 1.20, on 365 held-out
|
||||
paragraphs of LLM-written prose that neither model saw in training, at
|
||||
`temperature=0.9, top_p=0.95`, three swipes each. 1,095 generations an arm.
|
||||
|
||||
Pronoun invention numbers for this release, on 400 single-gender roleplay
|
||||
paragraphs:
|
||||
|
||||
| | v2.1 | v2.2 |
|
||||
|---|---|---|
|
||||
| a gendered pronoun the input never licensed | 0.8% | 0.8% |
|
||||
| ... belonging to the other gender | 0.8% | 0.8% |
|
||||
| a proper noun the input does not have | 0.3% | 0.3% |
|
||||
|
||||
Paired over the 400 inputs, none of these separates.
|
||||
|
||||
Repetition on the 365 paragraphs -- a rewrite that repeats a sentence, a phrase,
|
||||
or a sentence opening:
|
||||
|
||||
| | input | v2.1 | v2.2 |
|
||||
|---|---|---|---|
|
||||
| any repetition | 2.2% | 13.5% | **8.7%** |
|
||||
| ... a whole sentence | | 0.4% | 0.3% |
|
||||
| ... a phrase | | 7.3% | **4.6%** |
|
||||
| ... a sentence opening (anaphora) | | 4.5% | **2.6%** |
|
||||
|
||||
Paired over the 365 inputs, any repetition falls (t = -3.6), repeated phrases
|
||||
fall (t = -2.8) and anaphora falls (t = -2.5).
|
||||
|
||||
Structural, on the same paragraphs:
|
||||
|
||||
| | v2.1 | v2.2 |
|
||||
|---|---|---|
|
||||
| words changed | 44.0% | 43.7% |
|
||||
| passed through unchanged | 3.1% | 2.5% |
|
||||
| near-verbatim outputs | 1.4% | 0.9% |
|
||||
| below the training edit floor | 35.4% | 35.9% |
|
||||
| sentence count moved | 75.5% | 79.2% |
|
||||
| sentence-length variety vs input | +0.115 | +0.116 |
|
||||
| words kept from the input | 0.616 | 0.620 |
|
||||
| length preserved | 0.851 | 0.857 |
|
||||
| truncated below 0.75x | 23.7% | 21.4% |
|
||||
| repeated 3-grams | 0.005 | 0.004 |
|
||||
|
||||
Paired over the 365 inputs, only sentence count moved separates (t = +2.2). Over
|
||||
the 303 inputs of 51-90 words, no column does.
|
||||
|
||||
Unsupported content -- a sentence in the rewrite the input does not entail,
|
||||
scored by NLI with the input alone as the premise:
|
||||
|
||||
| | v2.1 | v2.2 |
|
||||
|---|---|---|
|
||||
| unsupported sentences, mean per rewrite | 12.7% | **11.1%** |
|
||||
| unsupported sentences, all rewrites | 11.5% | **10.2%** |
|
||||
| coverage of the input (reverse entailment) | 0.487 | 0.484 |
|
||||
|
||||
Paired over the 365 inputs, the unsupported rate falls (t = -2.3); coverage
|
||||
does not move.
|
||||
|
||||
The register numbers on the same paragraphs, against the input:
|
||||
|
||||
| | input | v2.1 | v2.2 | human corpus |
|
||||
|---|---|---|---|---|
|
||||
| banned constructions /1k | 7.52 | 2.51 | 2.50 | 0.00 |
|
||||
| slop lexicon density | 0.081 | 0.048 | **0.044** | 0.019 |
|
||||
| purple score | 0.498 | 0.314 | 0.316 | -- |
|
||||
|
||||
Paired over all 365 inputs, lexicon density falls (t = -2.6). Banned
|
||||
constructions and purple do not separate.
|
||||
|
||||
Dialogue: the number of spoken lines changes on 1.6% of rewrites against v2.1's
|
||||
0.8% (t = +1.7, does not separate), and of the rewrites whose input carried
|
||||
quoted speech, 1.0% lost the quotes entirely -- the same for both.
|
||||
|
||||
Markup round-trip on 365 tagged paragraphs -- every marker handed back in order,
|
||||
with the same counts: 79.5% for v2.1, 78.1% here.
|
||||
|
||||
### A note on strength
|
||||
|
||||
Every number above is measured at the strength the release is baked at, which is
|
||||
not the same as measuring the adapter. LoRA strength is spent at merge time
|
||||
(`W + (B @ A) * 2.40` here), and the metrics move with it: read at the adapter's
|
||||
natural 1.0 this same checkpoint is a materially different artifact.
|
||||
|
||||
Both releases ship at 1.20, so every difference above is the training pool and
|
||||
not the strength. 1.20 is where banned constructions bottomed out on the
|
||||
strength sweep: 1.15 carries a third more of them.
|
||||
|
||||
## Training
|
||||
|
||||
**Corrupt forward, train backward.** The human paragraph is the target; an
|
||||
on-policy LLM manufactures the input by slop-ifying it.
|
||||
|
||||
The target side is human prose: published contemporary literary fiction,
|
||||
r/WritingPrompts
|
||||
([`Mollymo/Human-to-AI-writing`](https://huggingface.co/datasets/Mollymo/Human-to-AI-writing)),
|
||||
AO3
|
||||
([`midwestern-simulation-active/ao3_random_subset`](https://huggingface.co/datasets/midwestern-simulation-active/ao3_random_subset)),
|
||||
a scrape of `bluemoonroleplaying.com` -- the only human writing in the pool
|
||||
already in the deployment's own register -- and a sliver of fanfiction.net
|
||||
([`atom-in-the-universe/fanfics-10k-10k`](https://huggingface.co/datasets/atom-in-the-universe/fanfics-10k-10k)).
|
||||
|
||||
The input side is manufactured from those targets, weighted and share-capped so
|
||||
that no single generator's tics dominate:
|
||||
|
||||
| pool axis | composition |
|
||||
|---|---|
|
||||
| rows | 15,290 over 10,982 distinct target paragraphs |
|
||||
| corruption band | medium 35%, heavy 31%, light 30%, identity/no-op 3%, curated real slop 1% |
|
||||
| `len_mode` | match only |
|
||||
| markup | 15% of rows, identical on both sides |
|
||||
| kind | prose 91%, dialogue 8%, structural no-ops 1% |
|
||||
| target source | published fiction 39%, r/WritingPrompts 29%, AO3 20%, roleplay forum 8%, other 4% |
|
||||
|
||||
Pairs pass invariant gates before they reach the GPU: POV, tense, who is in the
|
||||
scene, the gender of who is in the scene, the number of spoken lines,
|
||||
grammatical correctness on the target side, no repetition on the target side,
|
||||
content recall stratified by target length, and NLI entailment both ways. Two further screens shape what reaches
|
||||
training -- a floor on how much a pair actually changes, and a floor on the
|
||||
sentence-length variety of the target, applied at a higher threshold for long
|
||||
paragraphs than short ones.
|
||||
|
||||
**Loss on the target paragraph only.** Everything before `rewrite` is masked.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| LoRA | r=32, alpha=64, dropout 0.05 |
|
||||
| target modules | q, k, v, o, gate, up, down, **and `lm_head`** |
|
||||
| trainable | 71,004,160 params (1.73%) |
|
||||
| schedule | 2 epochs, lr 1e-4 cosine, batch 8 × accum 4, seq 2048 |
|
||||
| steps | 920 on one RTX 3090 |
|
||||
|
||||
## The merge
|
||||
|
||||
Merged at strength 1.20. Rank 32 with alpha 64 is a LoRA scaling of 2.0, so the
|
||||
effective scaling is **2.40**: `W + (B @ A) * 2.40`. Merged in float32, stored
|
||||
bfloat16.
|
||||
|
||||
`lm_head` is adapted, and `Qwen3-4B-Base` ties `lm_head.weight` to
|
||||
`embed_tokens.weight`. **This checkpoint is untied**: the merged output head is
|
||||
stored separately and the input embeddings are bit-identical to the base
|
||||
model's, which is what training assumed. `config.json` says
|
||||
`tie_word_embeddings: false` and it means it. Do not re-tie it, and if you
|
||||
convert to another format, check that the head survived.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Not an instruct model.** It has one job and one prompt. There is nothing to
|
||||
ask it.
|
||||
- **Works on fictional prose only.** May not work on technical documentation.
|
||||
- **One paragraph per call.** Longer input degrades; split it.
|
||||
- **Still invents people occasionally.** A phantom pronoun appears in 0.8% of
|
||||
rewrites. Check the output when the cast matters.
|
||||
- **Truncates.** It drops below 0.75x the input's length on 21.4% of paragraphs.
|
||||
Usually that is the slop going; check the output when length matters.
|
||||
- **Still repeats itself.** 8.7% of rewrites repeat a sentence, a phrase or a
|
||||
sentence opening the input did not. Check the output for loops.
|
||||
- **Will not pass AI detectors.** Pangram and such will still know because this
|
||||
model preserves word choices and certain sentence structures.
|
||||
- **English only**, narrative register (third and first person fiction, dialogue
|
||||
with quoted speech).
|
||||
- **Short input pads, cuts and invents.** The floor is about 15 words. See
|
||||
[Input length](#input-length).
|
||||
- **Repeats a noun sooner than an LLM would.** Human prose reuses a plain noun
|
||||
where generated prose uses a synonym, and this model has learned that habit.
|
||||
|
||||
## License
|
||||
|
||||
The weights in this repository are released under the **GNU Affero General
|
||||
Public License, version 3**. The full text is in `LICENSE`.
|
||||
|
||||
This is a derivative of [`Qwen/Qwen3-4B-Base`](https://huggingface.co/Qwen/Qwen3-4B-Base),
|
||||
which is licensed under **Apache License 2.0**. That license is preserved and
|
||||
its terms continue to apply to the base weights this model was built from; the
|
||||
AGPL covers the combined work as distributed here. Apache-2.0 is one-way
|
||||
compatible with AGPLv3, which is what makes this combination possible.
|
||||
|
||||
If you run a modified version of this model as a network service, AGPL section
|
||||
13 requires you to offer the corresponding source of your modifications to its
|
||||
users.
|
||||
Reference in New Issue
Block a user