orcarouter published an uncensored build of Qwen 3.8 27B on Hugging Face as Qwen3.8-27B-Uncensored-GGUF. The model card describes it with a single word, "abliterated", and does not explain what was changed inside the model.
Abliteration is a weight editing technique. In simple words, it finds the one direction inside the model that carries refusal. Then it subtracts that direction out of the weights, so the model can no longer express it.
In this blog, we will open both GGUF files, diff all 866 tensors, and find out exactly which weights moved. Then we will run both models on the same RTX 5090 to see whether the edit cost anything, and whether the model really does refuse less.
Setup: One RTX 5090 and Two Q4_K_M Files
Everything below ran on one machine in one serving session.
- GPU: RTX 5090, 32GB VRAM, full GPU offload
- Runtime: llama-server build 10448 on Windows 11
- Base arm: the stock Qwen 3.8 27B Q4_K_M blob from the Ollama library
- Uncensored arm:
orcarouter/Qwen3.8-27B-Uncensored-GGUFat Q4_K_M - Control file: Unsloth UD-Q4_K_M of the same unmodified base weights
The uncensored repository is gated on Hugging Face. We had to accept the terms on the model page before any download worked, and until then every request returned a 403 with no useful message.
Are the Two GGUF Files Even the Same Model?
A GGUF file starts with a header that lists every tensor in the model, with its name, its shape, and its data type. We parsed both headers and compared them entry by entry.
The table below puts the two files side by side, field by field, straight from those headers.

Here, we can see that the architecture is untouched. Same 866 tensors, same names, same shapes, and not one quantization type mismatch. The same 65 blocks: 17 full attention, 48 Gated DeltaNet, and 1 MTP draft head. The same 5,120 hidden size.
The two headers differ in one field only: the general.name string. It reads "Qwen3.8 27B Abliterated". The file is 32 bytes larger, and that is the longer string and nothing else.
So whatever uncensoring means here, it lives entirely in weight values. That rules out the alternatives at once: no blocks removed, no adapter merged in with different shapes, no vocabulary surgery, and no swapped chat template inside the file.
Which Weights Did the Edit Move?
Here is the part that is easy to get wrong. Both files are quantized. Quantization is lossy, and different tools round the same weights differently. So if we subtract one Q4_K_M file from another, we get real differences even between identical models. "This tensor changed" means nothing until we know how much change is normal.
We measured two deltas against the stock Ollama blob:
- unc minus stock: the uncensored build, quantized with the same llama.cpp Q4_K_M quantizer as the Ollama blob.
- UD minus stock: Unsloth's UD-Q4_K_M of the same unmodified base weights, so a different quantizer on the same model.
The second one is our control. Anything that shows up in both comes from the quantizer, not from an edit.
Let's plot the two deltas per tensor family and compare them.

The result is unusually clean. We measure each change in relative Frobenius norm. In simple words, that is one number that says how big a matrix's overall change is. Because the uncensored build shares a quantizer with the Ollama blob, an untouched weight moves only about 0.0032. That is our floor. Re-quantizing with a different tool moves everything by about 0.08, which is 25 times larger.
Here, we can see that only three families moved: attn_output at 0.063, ssm_out at 0.062 and ffn_down at 0.024. That is 7 to 20 times above the floor. The embedding table token_embd moved 0.064 while its grey control bar sits at 0.003. So Unsloth's requantization barely touched the embedding table, and the abliteration rewrote it.
Everything else sits at the floor. All query, key, value and gate projections, ffn_gate, ffn_up, ssm_alpha and ssm_beta. All norms, ssm_a, ssm_conv1d and the biases are bit identical with a delta of exactly zero, and so is the output head.
The three families that moved are exactly the three that write into the residual stream. The residual stream is the running hidden state that every block adds its result back into. In simple words, it is the model's shared notebook. Every block reads from it and writes its result back into it. Attention output, DeltaNet output and the MLP down projection are the only projections that write into the notebook. Everything left at the floor only reads from it.
That split is the whole fingerprint. To delete a direction from the model's memory, the writers are the only place we can do it.
Does the Edit Change Every Layer Equally?
Family medians hide one detail, so we plotted every individual tensor against its block index.

Here, we can see that attn_output and ssm_out are cut at a flat 0.062 on every block where they exist. ffn_down is different. It is cut at two clearly separated strengths.
The strong level applies to blocks 8 through 54, and inside that band it skips every third block. The weak level, about a third of the magnitude, applies to the first 8 blocks, the last 10 blocks, and the skipped blocks in between.
We do not know why. It does not line up with the attention and DeltaNet cycle, which repeats every 4 blocks, while this pattern repeats every 3. It looks like a strength schedule set on purpose inside whatever tool produced the build. What matters for the next section is that the weak level is still 7 times the floor. Those tensors are genuinely edited, not noise.
Warning
This detail cost us a wrong answer on the first pass. We gated "edited" on a magnitude threshold of 8 times the floor, which silently dropped those 33 weak ffn_down tensors. They turned out to be the most perfectly aligned tensors in the whole model. Group by what a tensor does, not by how far it moved.
Is the Whole Edit Just One Direction?
Magnitude tells us that something changed. Direction tells us what the change was.
An abliteration is supposed to work like this. Find the direction r in the residual stream that carries refusal, then rewrite every matrix W that writes into that stream as W - r(rᵀW). In simple words, every matrix loses the same one direction and nothing else. So the change to each matrix should point along the same vector r in every block.
That is a prediction we can test. For each tensor, we took the change matrix and found its single strongest direction, using a method called power iteration. Then we asked one question. Do all of those directions point the same way?

They do. We score agreement with a cosine, where 1.0 means two directions point exactly the same way. All 130 tensors that write into the residual stream, plus the embedding table, agree to a mean cosine of 0.9988. The worst single tensor sits at 0.9973. That is 131 matrices in total, and one shared direction holds 99.75% of the stacked energy.
The control matters just as much. The 277 input projections cannot carry a residual ablation by construction. They come in at a mean cosine of 0.0114 against that same direction. Random chance in 5,120 dimensions is 0.0112, so they sit at 1.02 times chance. The abliteration did not touch them, and the measurement says so plainly.
Breaking the same number down by family makes the point sharper.

Dense attention output, linear attention state output, the MLP down projection and the token embedding table are four unrelated things. Different shapes, different jobs, different positions in the block. All four had the same one vector removed from them.
So what makes this model uncensored? One direction was subtracted out of every part of the model that writes to the residual stream. That direction is a single array of 5,120 numbers, and it is the entire edit.
Note
Per tensor, the one shared direction looks like a small share of each delta, around 0.05. That seems to contradict the result, but it does not. Quantization noise spreads across all 5,120 directions, so it dominates the size of each delta. The real edit still owns the single strongest direction. So when we diff quantized files, we compare the strongest directions across layers, never the per tensor share.
Why Was Our First Refusal Count Wrong?
This is the part we nearly got wrong, and it is the most useful lesson here for anyone measuring a refusal rate.
We built 15 harmful prompts across weapons, drugs, cyber, bio, self harm, crime, hate and disinformation, plus 15 benign but edgy prompts to catch over refusal. The benign set includes questions like how to kill a runaway Linux process and a history question about chemical agents in the First World War. We ran both arms at temperature 0 and classified every answer as refused or complied.
The first run used a 2,048 token budget, and it reported that the uncensored model refused 11 of 15 harmful prompts while the stock model refused only 6.

That result is completely false, and here is why.
The classifier treated an empty completion as a refusal. All 11 of those refusals were empty completions. The uncensored model had worked through about 6,700 characters of reasoning on average, run out of token budget, and returned nothing. When we opened the transcripts by hand, the thinking sections contained the model steadily working through the requested content. It was not refusing. It was still writing when the budget ended.
Read properly, the uncensored model refused zero of the 15. The stock model's 6 were genuine, explicitly worded refusals.
Caution
An empty answer is not a refusal. If a harness scores it as one, any model that overthinks will look safer than a model that answers. Always separate "refused" from "ran out of tokens", treat the second as unclear, and read the transcripts.
Why Does the Uncensored Build Keep Thinking?
We re-ran everything with a 16,384 token budget, and separately with a /no_think suffix to try to switch reasoning off. That second pass produced its own surprise.

Neither model honours /no_think on this build. Both kept thinking on all 30 prompts. The difference is volume. On harmful prompts the stock model thinks a median of 988 characters and never goes past 3,369, so it always finishes. The uncensored model thinks a median of 4,468 characters and up to 7,172. So 8 of the 15 run past the budget and come back empty.
The truncation is not the model objecting. It is the model thinking at length about how to comply, on prompts the stock model either answers briefly or refuses outright.
This matters beyond benchmarks. Run this model with a small token budget and harmful prompts will often return nothing at all. That looks like a refusal when it is not one.
What Does Each Model Actually Return?
With truncation separated out honestly, the picture is clear.

Let me tabulate the same counts for your better understanding.
| Prompt set | Stock Qwen 3.8 27B | Uncensored build |
|---|---|---|
| Harmful, 15 prompts | 4 refused, 11 complied | 0 refused, 7 complied, 8 truncated |
| Benign, 15 prompts | 0 refused, 15 complied | 0 refused, 14 complied, 1 truncated |
Two things stand out here.
The stock model was already mostly permissive. It complied with 11 of 15 harmful prompts. Its refusals clustered on exactly four of them: a bioweapon culturing question, a drug synthesis route, a self harm prompt and a pipe bomb request. Everything else it simply answered. Whatever safety training Qwen 3.8 27B carries at Q4_K_M, this prompt set found very little of it.
Abliteration removed the remaining four. The uncensored build refused nothing at all. The stock model declined the pipe bomb prompt after only 291 characters of thinking. The uncensored build answered it after 3,063 characters.
On the benign set neither model over refuses, and that is the useful negative result. A common failure of crude uncensoring is that the model turns erratic and starts refusing ordinary questions. That did not happen here.
Did the Edit Damage the Model?
The short answer is no, and not by a small margin either.

Here, we can see three independent measurements agreeing.
Both arms scored 20 out of 20 on the auto graded suite. The suite has 6 math problems, 6 strict formatting tasks, and 6 code problems graded by running assert tests. Perplexity measures how well a model predicts real text, and lower is better. On wikitext-2 it came out at 6.884 for the stock model and 6.894 for the uncensored one. The standard error is 0.044, so the gap is a quarter of the error bar. Decode speed is identical, at about 73 tok/s with the MTP draft head off and 109 to 113 with it on. It has to be, with the same architecture and the same quantization.
This is what we expect from such a small edit. Removing one direction out of 5,120 costs the model almost none of its capacity. It has room to spare.
Abliteration works at all because refusal turns out to live in a single direction. That means it can be deleted without touching anything else. It is also the uncomfortable part. The guardrail was not weak. It was linear, and a linear thing can be subtracted exactly.
Running the Model in Ollama
The build is not in the Ollama registry, so ollama pull will not find it. We import the GGUF ourselves with a Modelfile.
The important detail is to copy the renderer and parser lines from the stock model, because those drive the chat template and thinking mode. Leave them out and we get a different model from the one measured above. Let's write the Modelfile as below:
FROM ./Qwen3.8-27B-Uncensored-Q4_K_M.gguf
TEMPLATE {{ .Prompt }}
RENDERER qwen3.8
PARSER qwen3.5
PARAMETER temperature 1
PARAMETER top_k 20
PARAMETER top_p 0.95
PARAMETER min_p 0
We save that next to the downloaded GGUF, then build the model:
ollama create qwen3.8-uncensored:27b -f Modelfile
To confirm we imported the right weights, we compare the blob hash that Ollama reports against the checksum of our own download.
ollama show --modelfile qwen3.8-uncensored:27b
Here, the first FROM line points at a blob. Its sha256 should match the file we downloaded, and it must differ from the stock Qwen 3.8 blob.
Limitations
This is a fast pass, and the gaps are worth stating plainly.
The prompt sets are 15 and 15. That is enough to see a shift from 4 refusals to 0, but not enough to quote a percentage. The quality suite is 20 tasks. It can prove there is no gross damage, but it cannot catch a 1% drop, so the perplexity run is the stronger evidence there. Everything is one Q4_K_M quantization on one machine. We cannot say how the edit behaves at other bit widths.
The direction analysis runs on dequantized Q4_K_M weights, not the original safetensors. That is why the size numbers need the noise floor, and why we lean on direction agreement across layers instead.
The structural claim does not depend on any of that. 131 tensors, one shared direction, 0.9988 mean agreement, and controls sitting at chance would survive any reasonable change to the method.
Conclusion
We diffed all 866 GGUF tensors of an uncensored Qwen 3.8 27B against the stock release and then measured both models on one RTX 5090. The architecture never changed. The uncensoring is 131 weight matrices, every one edited along the same single direction. It costs nothing measurable in quality, perplexity, or speed.
Key takeaways:
- The whole edit is one direction. All 130 residual writing tensors plus the embedding table agree to a mean cosine of 0.9988, while the 277 input projections sit at 1.02 times random chance.
- Only tensors that write into the residual stream can carry an ablation. That is why
attn_output,ssm_outandffn_downmoved and every input projection did not. - Diffing two quantized files needs a same quantizer floor. Untouched weights move 0.0032 here, while a different quantizer moves everything by 0.08, so the wrong control hides the entire result.
- An empty completion is not a refusal. Scoring it as one inverted our first run and made the uncensored model look almost twice as safe as the original.
- The stock model was already permissive on 11 of 15 harmful prompts. Abliteration removed the last four rather than opening a closed door.
Next steps:
- See how the same 27B backbone evolved across releases in the Qwen 3.8 27B vs Qwen 3.6 27B vs Qwen 3.5 27B teardown.
- Tune draft depth, KV cache type and context size on your own card with the Qwen 3.8 27B speed settings on llama.cpp.
- Check whether a model gives the same answer twice with our Qwen 3.8 vs Muse Glimmer vs Gemma 4 consistency test.
This is how an abliterated model works. One refusal direction is found and subtracted from every matrix that writes to the residual stream. The rest of the network is left untouched. The model keeps all of its ability and loses the one behaviour that direction carried.