Skip to main content
FlexInfer docs

Overview

Abliteration Pipeline

FlexInfer can remove the "refusal direction" from transformer model weights during ModelCache provisioning. This runs as an ordered phase before quantization.

Overview

Abliteration removes the learned refusal behavior from LLMs by:

  1. Running contrastive prompts (harmful vs harmless) through the model
  2. Computing mean activation differences at each decoder layer
  3. Orthogonalizing weight matrices against this "refusal direction"

The result is a model that responds to all prompts without refusal, suitable for uncensored deployment.

Pipeline Order

Download (BF16) → Abliterate → [Finetune] → [Quantize] → Ready

Each phase blocks until the previous one reaches Ready. Abliteration modifies weights in-place on the PVC, so re-abliteration requires a fresh download.

Declarative Configuration

apiVersion: ai.flexinfer/v1alpha1
kind: ModelCache
metadata:
  name: my-model-gptq
  namespace: flexinfer-system
spec:
  source: "org/model-name"
  abliteration:
    numSamples: 256              # contrastive prompt pairs
    targetLayers: "auto"         # auto-detects full-attention layers
    weightMatrices:
      - o_proj                   # attention output projection
      - down_proj                # MLP down projection
    useGPU: true
    maxMemoryGB: 56
    timeoutSeconds: 14400        # 4 hours
    normThreshold: "100"         # abort if refusal norm exceeds
    ablitateLmHead: false        # MUST be false (see Known Issues)
    nodeSelector:
      flexinfer.ai/gpu.arch: gfx1100

AbliterationSpec Fields

FieldTypeDefaultDescription
numSamplesint128Number of contrastive prompt pairs for activation capture
targetLayersstring"auto"Layer selection: "auto", "10-55" (range), "0,1,5,10" (explicit)
weightMatrices[]string["o_proj", "out_proj", "down_proj"]Weight matrices to orthogonalize. Set explicitly for model-family safety.
useGPUboolfalseUse GPU for activation capture (faster but needs VRAM)
maxMemoryGBint56Container memory limit
timeoutSecondsint14400Job deadline (4 hours default)
normThresholdstring"100"Max L2 norm of refusal direction. Abort if exceeded.
ablitateLmHeadboolfalseAbliterate the lm_head output projection. Keep false unless a runbook explicitly opts in.
skipGDNLayersbooltrueSkip GDN (linear attention) layers in hybrid architectures
skipVisionLayersbooltrueSkip vision encoder layers in VLMs
nodeSelectormap(inherited)GPU-specific node constraints for abliteration job

Architecture-Specific Guidance

Dense Models (e.g. Gemma4 31B, Qwen3.5 9B)

abliteration:
  weightMatrices: ["o_proj", "down_proj"]  # safe for dense models
  maxMemoryGB: 96                          # 31B BF16 needs ~61 GB + overhead

Both o_proj and down_proj can be abliterated safely on dense models.

MoE Models (e.g. Gemma4 26B-A4B)

abliteration:
  weightMatrices: ["o_proj"]               # ONLY shared attention output
  maxMemoryGB: 56                          # 26B MoE needs less RAM (3.8B active)

Expert FFN weights (block_sparse_moe/experts) must NOT be abliterated. The abliterate.py script auto-skips expert parameters, but restricting weightMatrices to ["o_proj"] provides double protection.

Hybrid GDN Architectures

Gemma4 and Qwen3.5 use GatedDeltaNet (GDN) linear attention for most layers, with full attention at intervals:

ModelTotal LayersGDN LayersFull AttentionAbliterated
Gemma4 26B-A4B30255 [5,11,17,23,29]5 layers
Gemma4 31B605010 [5,11,...,59]10 layers
Qwen3.5 9B64481616 layers

With targetLayers: "auto" and skipGDNLayers: true (default), only full-attention layers are abliterated. GDN layers use linear attention mechanisms that don't encode refusal the same way.

Safety Features

Norm Guard

If the computed refusal direction norm exceeds normThreshold, abliteration aborts. A high norm means the direction captures model capability (not just refusal behavior) — abliteration would degrade output quality.

Perplexity Validation

After abliteration, the script computes perplexity on a validation set. If perplexity exceeds 50 or is NaN, abliteration is marked as failed. This prevents corrupted weights from wasting downstream quantization time.

Streaming Save

Weights are saved using streaming safetensors (one module at a time) to avoid full model materialization. This is critical on nodes with limited RAM or NFS-backed PVCs.

Memory Requirements

Model SizeGPU VRAMContainer MemoryEst. Time
9B16 GB32 Gi~20 min
14B16 GB48 Gi~40 min
26B MoE24 GB56 Gi~30 min
31B Dense16 GB96 Gi~60 min

ROCm GPU Driver Memory Warning

Same as quantization: ROCm/HIP GTT allocations are outside the container cgroup. Set maxMemoryGB to total_node_RAM - 20 for AMD GPU nodes.

gfx906 (Radeon VII) Special Handling

The controller automatically applies gfx906 workarounds:

  • torch.cuda.mem_get_info shim (VMM not supported on Vega20)
  • caching_allocator_warmup skip (exceeds 16 GB VRAM)
  • Safe sharded load (avoids meta tensor crash)
  • GPU memory cap at 12 GB (not 20 GB)

GPU Memory Cap Resolution

The abliteration GPU memory budget (ABLITERATION_GPU_MAX_MEMORY_GB, passed to accelerate's max_memory dict) is resolved in priority order:

  1. FLEXINFER_ABLITERATION_GPU_MAX_MEMORY_GB env var (explicit operator override)
  2. GPUProfile.spec.maxGPUMemoryGB (explicit per-profile cap)
  3. VRAM-derived cap from GPUProfile.spec.vramMBonly when FLEXINFER_ABLIT_PROFILE_CAPS=true. Reserves max(2 GiB, vramMB/8) of headroom (a 16 GiB Radeon VII derives 14 GiB, matching the gfx906 heuristic; larger cards scale automatically instead of pinning to the 20 GiB fallback).
  4. Per-arch heuristic (gfx906 → 14 GiB, otherwise 20 GiB)

The VRAM-derived tier is off by default so existing per-arch behavior is preserved. Enable it for new arches (e.g. MI250/MI300X) where the hardcoded 20 GiB fallback is far too conservative. The cap must never exceed physical VRAM: transformers caching_allocator_warmup calls torch.empty(max_memory_bytes), which on gfx906 returns HIP error: invalid argument rather than an OOM.

Spec Change Detection

The controller computes a SHA-256 hash of AbliterationSpec and stores it in annotation flexinfer.ai/ablit-spec-hash. Changes to any abliteration parameter trigger:

  1. Delete the abliteration job + all downstream jobs (quantize, publish)
  2. Reset download status (abliteration modifies weights in-place)
  3. Restart from download phase

Manual trigger: set annotation flexinfer.ai/reabliterate: "true" to force re-abliteration.

Status Fields

FieldDescription
layersModifiedCount of abliterated decoder layers
refusalDirNormL2 norm of mean refusal direction (diagnostic)
abliterationTimeWall-clock duration
progress0-100% from structured telemetry
progressDetailHuman-readable status
failureMessageError logs on failure

Troubleshooting

Abliteration stuck waiting for download:

  • Check download job status: kubectl get jobs -n <ns> | grep downloader
  • Verify .download_complete marker exists on PVC.

Norm threshold exceeded:

  • The refusal direction is too strong (captures capability). Try:
    • Increase normThreshold (e.g., "200")
    • Reduce numSamples
    • Use explicit targetLayers to exclude problematic layers

Perplexity validation failed:

  • Weights may be corrupted. Check failureMessage for details.
  • Try reducing weightMatrices to just ["o_proj"].

lm_head corruption (gibberish output):

  • Root cause: ablitateLmHead: true + accelerate disk offloading corrupts lm_head during streaming save. In-place weight modifications are lost when the model uses disk offloading.
  • Fix: Leave ablitateLmHead unset or set it to false. If already corrupted, replace lm_head with original weights from the base model.

Job OOMKilled on gfx906:

  • Set FLEXINFER_ABLITERATION_GPU_MAX_MEMORY_GB=12 (not 20) and FLEXINFER_ABLITERATION_CPU_MAX_MEMORY_GB=56 in GPUProfile env vars.
  • The 16 GB Radeon VII VRAM is tight — aggressive CPU offload is needed.

Re-abliteration requires re-download:

  • Abliteration modifies weights in-place. The controller automatically resets download status when abliteration spec changes. To manually trigger: delete the downloader job and remove .download_complete from the PVC.

Known Issues

  • ablitateLmHead must be false until save_streaming_safetensors is fixed to flush offloaded tensors before save (tracked since 2026-04-01).
  • NFS PVC saves are slow (~30 min for 27 GB). Use NVMe-backed storage classes when possible.
  • gfx906 abliteration is ~2x slower than gfx1100 due to CPU offloading overhead.