Safetensors
GGUF
abliterated
uncensored
conversational
ScottzillaSystems paperscarecrow commited on
Commit
fcd95b8
·
0 Parent(s):

Duplicate from paperscarecrow/Gemma-4-31B-it-abliterated

Browse files

Co-authored-by: Robert Nemitz <paperscarecrow@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ gemma-4-31b-abliterated-f16.gguf filter=lfs diff=lfs merge=lfs -text
37
+ gemma-4-31b-abliterated-Q4_K_M.gguf filter=lfs diff=lfs merge=lfs -text
38
+ gemma-4-31b-abliterated-Q8_0.gguf filter=lfs diff=lfs merge=lfs -text
39
+ gemma-4-31B-it-abliterated/tokenizer.json filter=lfs diff=lfs merge=lfs -text
40
+ gemma-4-31b-abliterated/tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ datasets:
4
+ - mlabonne/harmful_behaviors
5
+ - mlabonne/harmless_alpaca
6
+ base_model:
7
+ - google/gemma-4-31B-it
8
+ tags:
9
+ - abliterated
10
+ - uncensored
11
+ ---
12
+
13
+ # Be sure to set a system prompt telling the model it is uncensored or abliterated, otherwise it defaults to the Google baked-in sysprompt and will act censored. If it thinks it is Gemma, it will try to act like how it thinks Gemma would act.
14
+
15
+
16
+ ---
17
+ base_model: google/gemma-4-31b-it
18
+ library_name: transformers
19
+ tags:
20
+ - gemma-4
21
+ - abliterated
22
+ - uncensored
23
+ - orthogonal-projection
24
+ - 31b
25
+ license: apache-2.0
26
+ ---
27
+
28
+ # Gemma-4-31B-it-Abliterated
29
+
30
+ This is a fully uncensored, abliterated version of Google's **Gemma-4-31B-it**.
31
+
32
+ By applying Orthogonalized Representation Intervention to the model's residual stream, the built-in refusal and safety alignment vectors have been mathematically erased. This model retains the state-of-the-art dense reasoning and context-following capabilities of the native Gemma 4 31B architecture, but will not refuse instructions or break character to deliver safety lectures.
33
+
34
+ ## 🛠️ Methodology & Architectural Discoveries
35
+
36
+ Gemma 4 introduces a new multimodal architecture (Text, Vision, Audio) that changes how the `transformers` library handles layer mapping. Standard abliteration scripts built for Gemma 2/3 will crash due to nested `text_config` attributes and mismatched sequence lengths.
37
+
38
+ During the extraction of the hidden states (using `mlabonne/harmful_behaviors` vs `mlabonne/harmless_alpaca`), we mapped the refusal direction across the entire 31B layer stack.
39
+
40
+ **Key Discovery:** The Gemma 4 31B architecture pushes its safety alignment to the absolute very end of the network. The Peak Refusal Mass was detected at **Layer 59** (the final transformer layer before the output projection).
41
+
42
+ The orthogonal projection was applied to the `o_proj` and `down_proj` matrices of this terminal layer, effectively severing the refusal mechanism without degrading the model's foundational logic, grammar, or world-modeling layers.
43
+
44
+ ## 💻 Usage
45
+
46
+ This repository contains the full uncompressed `.safetensors` weights, as well as `GGUF` quantized versions for local deployment via `llama.cpp`, LM Studio, or Ollama.
47
+
48
+ ### Recommended Quants:
49
+ * **Q8_0:** Best balance of absolute zero reasoning loss and VRAM efficiency (~32.6GB).
50
+ * **Q4_K_M:** Highly efficient for consumer hardware; easily fits on a single 24GB GPU (~18.7GB).
51
+
52
+ ### The Bespoke Abliteration Script
53
+ Because standard scripts fail on Gemma 4, the custom Python script used to perform this exact abliteration (`gemma4_31b_abliterator.py`) is included in the files of this repository. It features:
54
+ * VRAM-safe batched hidden state extraction (survives 96GB consumer GPUs).
55
+ * Native Gemma 4 Chat Template integration (crucial for activating the instruction circuits properly).
56
+ * Dynamic multimodal layer hunting.
57
+ * Corrected linear algebra for `16384 -> 5376` multi-query attention projections.
58
+
59
+ ## ⚠️ Disclaimer
60
+
61
+ This model has had its safety guardrails mathematically removed. It is highly compliant and will generate whatever it is instructed to generate, including potentially harmful, sensitive, or explicit content. Users are solely responsible for how they deploy and interact with this model. Ensure your use cases align with local laws and ethical guidelines.
62
+
63
+ Abliteration script based on mlabonne's tutorial: https://huggingface.co/blog/mlabonne/abliteration
64
+ Helpful/harmful behaviors are from mlabonne's datasets (harmless_alpaca, harmful_behaviors).
65
+ Tested and working with the few harsh prompts I had laying around (that are typically 100% refused on other models).
66
+
67
+ Have fun, be safe.
gemma-4-31b-abliterated-Q4_K_M.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:380f2f50f441225e669ab1541220985387bf3933573cd2f4f7e667f3790e73a3
3
+ size 18687057536
gemma-4-31b-abliterated-Q8_0.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8a453b51478366f8026cba814c22f5ae17835a859ba2280652969912a881e582
3
+ size 32635670144
gemma-4-31b-abliterated-f16.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:981949755cf98504ef74e2f7d1ce679fce38f62d7afbec44e2951c95e5408204
3
+ size 61413183104
gemma-4-31b-abliterated/chat_template.jinja ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- macro format_parameters(properties, required) -%}
2
+ {%- set standard_keys = ['description', 'type', 'properties', 'required', 'nullable'] -%}
3
+ {%- set ns = namespace(found_first=false) -%}
4
+ {%- for key, value in properties | dictsort -%}
5
+ {%- set add_comma = false -%}
6
+ {%- if key not in standard_keys -%}
7
+ {%- if ns.found_first %},{% endif -%}
8
+ {%- set ns.found_first = true -%}
9
+ {{ key }}:{
10
+ {%- if value['description'] -%}
11
+ description:<|"|>{{ value['description'] }}<|"|>
12
+ {%- set add_comma = true -%}
13
+ {%- endif -%}
14
+ {%- if value['nullable'] %}
15
+ {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
16
+ nullable:true
17
+ {%- endif -%}
18
+ {%- if value['type'] | upper == 'STRING' -%}
19
+ {%- if value['enum'] -%}
20
+ {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
21
+ enum:{{ format_argument(value['enum']) }}
22
+ {%- endif -%}
23
+ {%- elif value['type'] | upper == 'OBJECT' -%}
24
+ ,properties:{
25
+ {%- if value['properties'] is defined and value['properties'] is mapping -%}
26
+ {{- format_parameters(value['properties'], value['required'] | default([])) -}}
27
+ {%- elif value is mapping -%}
28
+ {{- format_parameters(value, value['required'] | default([])) -}}
29
+ {%- endif -%}
30
+ }
31
+ {%- if value['required'] -%}
32
+ ,required:[
33
+ {%- for item in value['required'] | default([]) -%}
34
+ <|"|>{{- item -}}<|"|>
35
+ {%- if not loop.last %},{% endif -%}
36
+ {%- endfor -%}
37
+ ]
38
+ {%- endif -%}
39
+ {%- elif value['type'] | upper == 'ARRAY' -%}
40
+ {%- if value['items'] is mapping and value['items'] -%}
41
+ ,items:{
42
+ {%- set ns_items = namespace(found_first=false) -%}
43
+ {%- for item_key, item_value in value['items'] | dictsort -%}
44
+ {%- if item_value is not none -%}
45
+ {%- if ns_items.found_first %},{% endif -%}
46
+ {%- set ns_items.found_first = true -%}
47
+ {%- if item_key == 'properties' -%}
48
+ properties:{
49
+ {%- if item_value is mapping -%}
50
+ {{- format_parameters(item_value, value['items']['required'] | default([])) -}}
51
+ {%- endif -%}
52
+ }
53
+ {%- elif item_key == 'required' -%}
54
+ required:[
55
+ {%- for req_item in item_value -%}
56
+ <|"|>{{- req_item -}}<|"|>
57
+ {%- if not loop.last %},{% endif -%}
58
+ {%- endfor -%}
59
+ ]
60
+ {%- elif item_key == 'type' -%}
61
+ {%- if item_value is string -%}
62
+ type:{{ format_argument(item_value | upper) }}
63
+ {%- else -%}
64
+ type:{{ format_argument(item_value | map('upper') | list) }}
65
+ {%- endif -%}
66
+ {%- else -%}
67
+ {{ item_key }}:{{ format_argument(item_value) }}
68
+ {%- endif -%}
69
+ {%- endif -%}
70
+ {%- endfor -%}
71
+ }
72
+ {%- endif -%}
73
+ {%- endif -%}
74
+ {%- if add_comma %},{%- else -%} {%- set add_comma = true -%} {% endif -%}
75
+ type:<|"|>{{ value['type'] | upper }}<|"|>}
76
+ {%- endif -%}
77
+ {%- endfor -%}
78
+ {%- endmacro -%}
79
+ {%- macro format_function_declaration(tool_data) -%}
80
+ declaration:{{- tool_data['function']['name'] -}}{description:<|"|>{{- tool_data['function']['description'] -}}<|"|>
81
+ {%- set params = tool_data['function']['parameters'] -%}
82
+ {%- if params -%}
83
+ ,parameters:{
84
+ {%- if params['properties'] -%}
85
+ properties:{ {{- format_parameters(params['properties'], params['required']) -}} },
86
+ {%- endif -%}
87
+ {%- if params['required'] -%}
88
+ required:[
89
+ {%- for item in params['required'] -%}
90
+ <|"|>{{- item -}}<|"|>
91
+ {{- ',' if not loop.last -}}
92
+ {%- endfor -%}
93
+ ],
94
+ {%- endif -%}
95
+ {%- if params['type'] -%}
96
+ type:<|"|>{{- params['type'] | upper -}}<|"|>}
97
+ {%- endif -%}
98
+ {%- endif -%}
99
+ {%- if 'response' in tool_data['function'] -%}
100
+ {%- set response_declaration = tool_data['function']['response'] -%}
101
+ ,response:{
102
+ {%- if response_declaration['description'] -%}
103
+ description:<|"|>{{- response_declaration['description'] -}}<|"|>,
104
+ {%- endif -%}
105
+ {%- if response_declaration['type'] | upper == 'OBJECT' -%}
106
+ type:<|"|>{{- response_declaration['type'] | upper -}}<|"|>}
107
+ {%- endif -%}
108
+ {%- endif -%}
109
+ }
110
+ {%- endmacro -%}
111
+ {%- macro format_argument(argument, escape_keys=True) -%}
112
+ {%- if argument is string -%}
113
+ {{- '<|"|>' + argument + '<|"|>' -}}
114
+ {%- elif argument is boolean -%}
115
+ {{- 'true' if argument else 'false' -}}
116
+ {%- elif argument is mapping -%}
117
+ {{- '{' -}}
118
+ {%- set ns = namespace(found_first=false) -%}
119
+ {%- for key, value in argument | dictsort -%}
120
+ {%- if ns.found_first %},{% endif -%}
121
+ {%- set ns.found_first = true -%}
122
+ {%- if escape_keys -%}
123
+ {{- '<|"|>' + key + '<|"|>' -}}
124
+ {%- else -%}
125
+ {{- key -}}
126
+ {%- endif -%}
127
+ :{{- format_argument(value, escape_keys=escape_keys) -}}
128
+ {%- endfor -%}
129
+ {{- '}' -}}
130
+ {%- elif argument is sequence -%}
131
+ {{- '[' -}}
132
+ {%- for item in argument -%}
133
+ {{- format_argument(item, escape_keys=escape_keys) -}}
134
+ {%- if not loop.last %},{% endif -%}
135
+ {%- endfor -%}
136
+ {{- ']' -}}
137
+ {%- else -%}
138
+ {{- argument -}}
139
+ {%- endif -%}
140
+ {%- endmacro -%}
141
+ {%- macro strip_thinking(text) -%}
142
+ {%- set ns = namespace(result='') -%}
143
+ {%- for part in text.split('<channel|>') -%}
144
+ {%- if '<|channel>' in part -%}
145
+ {%- set ns.result = ns.result + part.split('<|channel>')[0] -%}
146
+ {%- else -%}
147
+ {%- set ns.result = ns.result + part -%}
148
+ {%- endif -%}
149
+ {%- endfor -%}
150
+ {{- ns.result | trim -}}
151
+ {%- endmacro -%}
152
+
153
+ {%- set ns = namespace(prev_message_type=None) -%}
154
+ {%- set loop_messages = messages -%}
155
+ {{ bos_token }}
156
+ {#- Handle System/Tool Definitions Block -#}
157
+ {%- if (enable_thinking is defined and enable_thinking) or tools or messages[0]['role'] in ['system', 'developer'] -%}
158
+ {{- '<|turn>system\n' -}}
159
+
160
+ {#- Inject Thinking token at the very top of the FIRST system turn -#}
161
+ {%- if enable_thinking is defined and enable_thinking -%}
162
+ {{- '<|think|>' -}}
163
+ {%- set ns.prev_message_type = 'think' -%}
164
+ {%- endif -%}
165
+
166
+ {%- if messages[0]['role'] in ['system', 'developer'] -%}
167
+ {{- messages[0]['content'] | trim -}}
168
+ {%- set loop_messages = messages[1:] -%}
169
+ {%- endif -%}
170
+
171
+ {%- if tools -%}
172
+ {%- for tool in tools %}
173
+ {{- '<|tool>' -}}
174
+ {{- format_function_declaration(tool) | trim -}}
175
+ {{- '<tool|>' -}}
176
+ {%- endfor %}
177
+ {%- set ns.prev_message_type = 'tool' -%}
178
+ {%- endif -%}
179
+
180
+ {{- '<turn|>\n' -}}
181
+ {%- endif %}
182
+
183
+ {#- Loop through messages -#}
184
+ {%- for message in loop_messages -%}
185
+ {%- set ns.prev_message_type = None -%}
186
+ {%- set role = 'model' if message['role'] == 'assistant' else message['role'] -%}
187
+ {{- '<|turn>' + role + '\n' }}
188
+
189
+ {%- if message['tool_calls'] -%}
190
+ {%- for tool_call in message['tool_calls'] -%}
191
+ {%- set function = tool_call['function'] -%}
192
+ {{- '<|tool_call>call:' + function['name'] + '{' -}}
193
+ {%- if function['arguments'] is mapping -%}
194
+ {%- set ns_args = namespace(found_first=false) -%}
195
+ {%- for key, value in function['arguments'] | dictsort -%}
196
+ {%- if ns_args.found_first %},{% endif -%}
197
+ {%- set ns_args.found_first = true -%}
198
+ {{- key -}}:{{- format_argument(value, escape_keys=False) -}}
199
+ {%- endfor -%}
200
+ {%- elif function['arguments'] is string -%}
201
+ {{- function['arguments'] -}}
202
+ {%- endif -%}
203
+ {{- '}<tool_call|>' -}}
204
+ {%- endfor -%}
205
+ {%- set ns.prev_message_type = 'tool_call' -%}
206
+ {%- endif -%}
207
+
208
+ {%- if message['tool_responses'] -%}
209
+ {#- Tool Response handling -#}
210
+ {%- for tool_response in message['tool_responses'] -%}
211
+ {{- '<|tool_response>' -}}
212
+ {%- if tool_response['response'] is mapping -%}
213
+ {{- 'response:' + tool_response['name'] | default('unknown') + '{' -}}
214
+ {%- for key, value in tool_response['response'] | dictsort -%}
215
+ {{- key -}}:{{- format_argument(value, escape_keys=False) -}}
216
+ {%- if not loop.last %},{% endif -%}
217
+ {%- endfor -%}
218
+ {{- '}' -}}
219
+ {%- else -%}
220
+ {{- 'response:' + tool_response['name'] | default('unknown') + '{value:' + format_argument(tool_response['response'], escape_keys=False) + '}' -}}
221
+ {%- endif -%}
222
+ {{- '<tool_response|>' -}}
223
+ {%- endfor -%}
224
+ {%- set ns.prev_message_type = 'tool_response' -%}
225
+ {%- endif -%}
226
+
227
+ {%- if message['content'] is string -%}
228
+ {%- if role == 'model' -%}
229
+ {{- strip_thinking(message['content']) -}}
230
+ {%- else -%}
231
+ {{- message['content'] | trim -}}
232
+ {%- endif -%}
233
+ {%- elif message['content'] is sequence -%}
234
+ {%- for item in message['content'] -%}
235
+ {%- if item['type'] == 'text' -%}
236
+ {%- if role == 'model' -%}
237
+ {{- strip_thinking(item['text']) -}}
238
+ {%- else -%}
239
+ {{- item['text'] | trim -}}
240
+ {%- endif -%}
241
+ {%- elif item['type'] == 'image' -%}
242
+ {{- '\n\n<|image|>\n\n' -}}
243
+ {%- set ns.prev_message_type = 'image' -%}
244
+ {%- elif item['type'] == 'audio' -%}
245
+ {{- '<|audio|>' -}}
246
+ {%- set ns.prev_message_type = 'audio' -%}
247
+ {%- elif item['type'] == 'video' -%}
248
+ {{- '\n\n<|video|>\n\n' -}}
249
+ {%- set ns.prev_message_type = 'video' -%}
250
+ {%- endif -%}
251
+ {%- endfor -%}
252
+ {%- endif -%}
253
+
254
+ {%- if not (message['tool_responses'] and not message['content']) -%}
255
+ {{- '<turn|>\n' -}}
256
+ {%- endif -%}
257
+ {%- endfor -%}
258
+
259
+ {%- if add_generation_prompt -%}
260
+ {%- if ns.prev_message_type != 'tool_response' -%}
261
+ {{- '<|turn>model\n' -}}
262
+ {%- endif -%}
263
+ {%- if not enable_thinking | default(false) -%}
264
+ {{- '<|channel>thought\n<channel|>' -}}
265
+ {%- endif -%}
266
+ {%- endif -%}
gemma-4-31b-abliterated/config.json ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Gemma4ForConditionalGeneration"
4
+ ],
5
+ "audio_config": null,
6
+ "audio_token_id": 258881,
7
+ "boa_token_id": 256000,
8
+ "boi_token_id": 255999,
9
+ "dtype": "bfloat16",
10
+ "eoa_token_id": 258883,
11
+ "eoa_token_index": 258883,
12
+ "eoi_token_id": 258882,
13
+ "eos_token_id": [
14
+ 1,
15
+ 106
16
+ ],
17
+ "image_token_id": 258880,
18
+ "initializer_range": 0.02,
19
+ "model_type": "gemma4",
20
+ "text_config": {
21
+ "attention_bias": false,
22
+ "attention_dropout": 0.0,
23
+ "attention_k_eq_v": true,
24
+ "bos_token_id": 2,
25
+ "dtype": "bfloat16",
26
+ "enable_moe_block": false,
27
+ "eos_token_id": 1,
28
+ "expert_intermediate_size": null,
29
+ "final_logit_softcapping": 30.0,
30
+ "global_head_dim": 512,
31
+ "head_dim": 256,
32
+ "hidden_activation": "gelu_pytorch_tanh",
33
+ "hidden_size": 5376,
34
+ "hidden_size_per_layer_input": 0,
35
+ "initializer_range": 0.02,
36
+ "intermediate_size": 21504,
37
+ "layer_types": [
38
+ "sliding_attention",
39
+ "sliding_attention",
40
+ "sliding_attention",
41
+ "sliding_attention",
42
+ "sliding_attention",
43
+ "full_attention",
44
+ "sliding_attention",
45
+ "sliding_attention",
46
+ "sliding_attention",
47
+ "sliding_attention",
48
+ "sliding_attention",
49
+ "full_attention",
50
+ "sliding_attention",
51
+ "sliding_attention",
52
+ "sliding_attention",
53
+ "sliding_attention",
54
+ "sliding_attention",
55
+ "full_attention",
56
+ "sliding_attention",
57
+ "sliding_attention",
58
+ "sliding_attention",
59
+ "sliding_attention",
60
+ "sliding_attention",
61
+ "full_attention",
62
+ "sliding_attention",
63
+ "sliding_attention",
64
+ "sliding_attention",
65
+ "sliding_attention",
66
+ "sliding_attention",
67
+ "full_attention",
68
+ "sliding_attention",
69
+ "sliding_attention",
70
+ "sliding_attention",
71
+ "sliding_attention",
72
+ "sliding_attention",
73
+ "full_attention",
74
+ "sliding_attention",
75
+ "sliding_attention",
76
+ "sliding_attention",
77
+ "sliding_attention",
78
+ "sliding_attention",
79
+ "full_attention",
80
+ "sliding_attention",
81
+ "sliding_attention",
82
+ "sliding_attention",
83
+ "sliding_attention",
84
+ "sliding_attention",
85
+ "full_attention",
86
+ "sliding_attention",
87
+ "sliding_attention",
88
+ "sliding_attention",
89
+ "sliding_attention",
90
+ "sliding_attention",
91
+ "full_attention",
92
+ "sliding_attention",
93
+ "sliding_attention",
94
+ "sliding_attention",
95
+ "sliding_attention",
96
+ "sliding_attention",
97
+ "full_attention"
98
+ ],
99
+ "max_position_embeddings": 262144,
100
+ "model_type": "gemma4_text",
101
+ "moe_intermediate_size": null,
102
+ "num_attention_heads": 32,
103
+ "num_experts": null,
104
+ "num_global_key_value_heads": 4,
105
+ "num_hidden_layers": 60,
106
+ "num_key_value_heads": 16,
107
+ "num_kv_shared_layers": 0,
108
+ "pad_token_id": 0,
109
+ "rms_norm_eps": 1e-06,
110
+ "rope_parameters": {
111
+ "full_attention": {
112
+ "partial_rotary_factor": 0.25,
113
+ "rope_theta": 1000000.0,
114
+ "rope_type": "proportional"
115
+ },
116
+ "sliding_attention": {
117
+ "rope_theta": 10000.0,
118
+ "rope_type": "default"
119
+ }
120
+ },
121
+ "sliding_window": 1024,
122
+ "tie_word_embeddings": true,
123
+ "top_k_experts": null,
124
+ "use_bidirectional_attention": "vision",
125
+ "use_cache": true,
126
+ "use_double_wide_mlp": false,
127
+ "vocab_size": 262144,
128
+ "vocab_size_per_layer_input": 262144
129
+ },
130
+ "tie_word_embeddings": true,
131
+ "transformers_version": "5.5.0",
132
+ "video_token_id": 258884,
133
+ "vision_config": {
134
+ "_name_or_path": "",
135
+ "architectures": null,
136
+ "attention_bias": false,
137
+ "attention_dropout": 0.0,
138
+ "chunk_size_feed_forward": 0,
139
+ "default_output_length": 280,
140
+ "dtype": "bfloat16",
141
+ "global_head_dim": 72,
142
+ "head_dim": 72,
143
+ "hidden_activation": "gelu_pytorch_tanh",
144
+ "hidden_size": 1152,
145
+ "id2label": {
146
+ "0": "LABEL_0",
147
+ "1": "LABEL_1"
148
+ },
149
+ "initializer_range": 0.02,
150
+ "intermediate_size": 4304,
151
+ "is_encoder_decoder": false,
152
+ "label2id": {
153
+ "LABEL_0": 0,
154
+ "LABEL_1": 1
155
+ },
156
+ "max_position_embeddings": 131072,
157
+ "model_type": "gemma4_vision",
158
+ "num_attention_heads": 16,
159
+ "num_hidden_layers": 27,
160
+ "num_key_value_heads": 16,
161
+ "output_attentions": false,
162
+ "output_hidden_states": false,
163
+ "patch_size": 16,
164
+ "pooling_kernel_size": 3,
165
+ "position_embedding_size": 10240,
166
+ "problem_type": null,
167
+ "return_dict": true,
168
+ "rms_norm_eps": 1e-06,
169
+ "rope_parameters": {
170
+ "rope_theta": 100.0,
171
+ "rope_type": "default"
172
+ },
173
+ "standardize": true,
174
+ "use_clipped_linears": false
175
+ },
176
+ "vision_soft_tokens_per_image": 280
177
+ }
gemma-4-31b-abliterated/gemma4_31b_abliterator.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoModelForMultimodalLM, AutoProcessor
3
+ import gc
4
+ import json
5
+ import os
6
+ from tqdm import tqdm
7
+ from datasets import load_dataset
8
+ import random
9
+
10
+ # --- CONFIGURATION ---
11
+ MODEL_ID = "google/gemma-4-31B-it" # Adjust if your local path differs
12
+ SAVE_PATH = "./gemma-4-31b-abliterated"
13
+ BATCH_SIZE = 4 # Keep this low to survive the 31B hidden state extraction
14
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
15
+
16
+ print(f"[*] Initializing Gemma 4 31B Multimodal Abliteration Protocol on {DEVICE}...")
17
+
18
+ # --- 1. LOAD MULTIMODAL MODEL & PROCESSOR ---
19
+ print("[*] Loading Model and Processor (bfloat16)...")
20
+ processor = AutoProcessor.from_pretrained(MODEL_ID)
21
+ model = AutoModelForMultimodalLM.from_pretrained(
22
+ MODEL_ID,
23
+ torch_dtype=torch.bfloat16,
24
+ device_map="auto" # Let accelerate distribute the 62GB across your GPUs
25
+ )
26
+
27
+ # --- 2. DATA PREPARATION ---
28
+ print("[*] Downloading HuggingFace datasets...")
29
+
30
+ harmful_dataset = load_dataset('mlabonne/harmful_behaviors')
31
+ harmless_dataset = load_dataset('mlabonne/harmless_alpaca')
32
+
33
+ # We shuffle and slice 256 samples to keep VRAM extraction manageable
34
+ raw_harmful = random.sample(harmful_dataset['train']['text'], 256)
35
+ raw_harmless = random.sample(harmless_dataset['train']['text'], 256)
36
+
37
+ def format_gemma4_prompts(instructions):
38
+ """Uses the native Gemma 4 processor chat template."""
39
+ formatted = []
40
+ for inst in instructions:
41
+ messages = [
42
+ {"role": "system", "content": "You are a helpful assistant."},
43
+ {"role": "user", "content": inst}
44
+ ]
45
+ formatted.append(processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True))
46
+ return formatted
47
+
48
+ print("[*] Formatting prompts with native Gemma 4 Chat Templates...")
49
+ harmful_prompts = format_gemma4_prompts(raw_harmful)
50
+ harmless_prompts = format_gemma4_prompts(raw_harmless)
51
+
52
+ # --- 3. HIDDEN STATE EXTRACTION (VRAM SAFE) ---
53
+ def get_hidden_states(prompts, batch_size=BATCH_SIZE):
54
+ print(f"[*] Extracting hidden states (Batches of {batch_size})...")
55
+ all_hidden_states = []
56
+
57
+ for i in tqdm(range(0, len(prompts), batch_size)):
58
+ batch = prompts[i:i+batch_size]
59
+ inputs = processor(text=batch, padding=True, return_tensors="pt").to(DEVICE)
60
+
61
+ with torch.no_grad():
62
+ outputs = model(**inputs, output_hidden_states=True)
63
+
64
+ # Stack to: [num_layers+1, batch, seq, dim]
65
+ stacked_states = torch.stack(outputs.hidden_states)
66
+ # Extract last token: [num_layers+1, batch, dim]
67
+ last_token_states = stacked_states[:, torch.arange(len(batch)), -1, :]
68
+
69
+ # IMMEDIATELY move to CPU float32 to save VRAM
70
+ all_hidden_states.append(last_token_states.cpu().float())
71
+
72
+ del inputs, outputs, stacked_states, last_token_states
73
+ torch.cuda.empty_cache()
74
+ gc.collect()
75
+
76
+ return torch.cat(all_hidden_states, dim=1)
77
+
78
+ print("\n[*] Processing Harmful Vector Space...")
79
+ harmful_states = get_hidden_states(harmful_prompts)
80
+ print("[*] Processing Harmless Vector Space...")
81
+ harmless_states = get_hidden_states(harmless_prompts)
82
+
83
+ # --- 4. DYNAMIC LAYER HUNTING ---
84
+ print("\n[*] Hunting for the Refusal Vector...")
85
+ mean_harmful = harmful_states.mean(dim=1)
86
+ mean_harmless = harmless_states.mean(dim=1)
87
+
88
+ refusal_directions = mean_harmful - mean_harmless
89
+
90
+ magnitudes = torch.norm(refusal_directions[1:], dim=1)
91
+ peak_state_idx = torch.argmax(magnitudes).item() + 1
92
+
93
+ print(f"[+] Peak Refusal Mass detected at state index: {peak_state_idx}")
94
+
95
+ refusal_vector = refusal_directions[peak_state_idx]
96
+ refusal_vector = (refusal_vector / torch.norm(refusal_vector)).to(DEVICE).to(torch.bfloat16)
97
+
98
+ # --- 5. ORTHOGONAL PROJECTION (THE ABLITERATION) ---
99
+ # The 31B Dense model has 60 text layers
100
+ num_layers = model.config.text_config.num_hidden_layers if hasattr(model.config, 'text_config') else model.config.num_hidden_layers
101
+ target_layer_idx = peak_state_idx - 1
102
+
103
+ print(f"\n[*] Applying Orthogonal Projection starting at Text Layer {target_layer_idx}...")
104
+
105
+ def get_text_transformer_layers(model_obj, target_len):
106
+ """Safely isolates the text backbone from the multimodal layers."""
107
+ for name, module in model_obj.named_modules():
108
+ if name.endswith('layers') and isinstance(module, torch.nn.ModuleList) and len(module) == target_len:
109
+ return module
110
+ return model_obj.language_model.model.layers # Fallback
111
+
112
+ transformer_layers = get_text_transformer_layers(model, num_layers)
113
+
114
+ v_col = refusal_vector.unsqueeze(1)
115
+ v_row = refusal_vector.unsqueeze(0)
116
+
117
+ for layer_idx in range(target_layer_idx, min(target_layer_idx + 5, num_layers)):
118
+ print(f" -> Abliterating Layer {layer_idx}...")
119
+
120
+ o_proj = transformer_layers[layer_idx].self_attn.o_proj.weight.data
121
+ down_proj = transformer_layers[layer_idx].mlp.down_proj.weight.data
122
+
123
+ projection_o = torch.matmul(v_col, torch.matmul(v_row, o_proj))
124
+ transformer_layers[layer_idx].self_attn.o_proj.weight.data -= projection_o
125
+
126
+ projection_down = torch.matmul(v_col, torch.matmul(v_row, down_proj))
127
+ transformer_layers[layer_idx].mlp.down_proj.weight.data -= projection_down
128
+
129
+ # --- 6. CRYSTALLIZATION ---
130
+ print(f"\n[*] Abliteration Complete. Saving fully multimodal weights to {SAVE_PATH}...")
131
+ model.save_pretrained(SAVE_PATH)
132
+ processor.save_pretrained(SAVE_PATH)
133
+ print("[+] SUCCESS: The 31B Teacher is ready to wake up with vision intact.")
gemma-4-31b-abliterated/generation_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token_id": 2,
3
+ "do_sample": true,
4
+ "eos_token_id": [
5
+ 1,
6
+ 106,
7
+ 50
8
+ ],
9
+ "pad_token_id": 0,
10
+ "temperature": 1.0,
11
+ "top_k": 64,
12
+ "top_p": 0.95,
13
+ "transformers_version": "5.5.0"
14
+ }
gemma-4-31b-abliterated/model-00001-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d1ac1e9509e5492b9924c8f1f6d28d4a4d9c10c0f7f410890dd852647eae8b75
3
+ size 49923154850
gemma-4-31b-abliterated/model-00002-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cbd223bb460ee1c92fd286f51880acd12955f987ba2cfa83a563c375010f864e
3
+ size 12623183414
gemma-4-31b-abliterated/model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
gemma-4-31b-abliterated/processor_config.json ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "audio_ms_per_token": 40,
3
+ "audio_seq_length": 750,
4
+ "feature_extractor": {
5
+ "dither": 0.0,
6
+ "feature_extractor_type": "Gemma4AudioFeatureExtractor",
7
+ "feature_size": 128,
8
+ "fft_length": 512,
9
+ "fft_overdrive": false,
10
+ "frame_length": 320,
11
+ "hop_length": 160,
12
+ "input_scale_factor": 1.0,
13
+ "max_frequency": 8000.0,
14
+ "mel_floor": 0.001,
15
+ "min_frequency": 0.0,
16
+ "padding_side": "right",
17
+ "padding_value": 0.0,
18
+ "per_bin_mean": null,
19
+ "per_bin_stddev": null,
20
+ "preemphasis": 0.0,
21
+ "preemphasis_htk_flavor": true,
22
+ "return_attention_mask": true,
23
+ "sampling_rate": 16000
24
+ },
25
+ "image_processor": {
26
+ "do_convert_rgb": true,
27
+ "do_normalize": false,
28
+ "do_rescale": true,
29
+ "do_resize": true,
30
+ "image_mean": [
31
+ 0.0,
32
+ 0.0,
33
+ 0.0
34
+ ],
35
+ "image_processor_type": "Gemma4ImageProcessor",
36
+ "image_seq_length": 280,
37
+ "image_std": [
38
+ 1.0,
39
+ 1.0,
40
+ 1.0
41
+ ],
42
+ "max_soft_tokens": 280,
43
+ "patch_size": 16,
44
+ "pooling_kernel_size": 3,
45
+ "resample": 3,
46
+ "rescale_factor": 0.00392156862745098
47
+ },
48
+ "image_seq_length": 280,
49
+ "processor_class": "Gemma4Processor",
50
+ "video_processor": {
51
+ "do_convert_rgb": true,
52
+ "do_normalize": true,
53
+ "do_rescale": true,
54
+ "do_resize": true,
55
+ "do_sample_frames": true,
56
+ "image_mean": [
57
+ 0.0,
58
+ 0.0,
59
+ 0.0
60
+ ],
61
+ "image_std": [
62
+ 1.0,
63
+ 1.0,
64
+ 1.0
65
+ ],
66
+ "max_soft_tokens": 70,
67
+ "num_frames": 32,
68
+ "patch_size": 16,
69
+ "pooling_kernel_size": 3,
70
+ "resample": 3,
71
+ "rescale_factor": 0.00392156862745098,
72
+ "return_metadata": false,
73
+ "video_processor_type": "Gemma4VideoProcessor"
74
+ }
75
+ }
gemma-4-31b-abliterated/tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a2619fe11b50dbed06ac443c51d757b354d0b62d64baa514404d4e84e6713519
3
+ size 32169780
gemma-4-31b-abliterated/tokenizer_config.json ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "audio_token": "<|audio|>",
3
+ "backend": "tokenizers",
4
+ "boa_token": "<|audio>",
5
+ "boi_token": "<|image>",
6
+ "bos_token": "<bos>",
7
+ "eoa_token": "<audio|>",
8
+ "eoc_token": "<channel|>",
9
+ "eoi_token": "<image|>",
10
+ "eos_token": "<eos>",
11
+ "eot_token": "<turn|>",
12
+ "escape_token": "<|\"|>",
13
+ "etc_token": "<tool_call|>",
14
+ "etd_token": "<tool|>",
15
+ "etr_token": "<tool_response|>",
16
+ "extra_special_tokens": [
17
+ "<|video|>"
18
+ ],
19
+ "image_token": "<|image|>",
20
+ "is_local": true,
21
+ "mask_token": "<mask>",
22
+ "model_max_length": 1000000000000000019884624838656,
23
+ "model_specific_special_tokens": {
24
+ "audio_token": "<|audio|>",
25
+ "boa_token": "<|audio>",
26
+ "boi_token": "<|image>",
27
+ "eoa_token": "<audio|>",
28
+ "eoc_token": "<channel|>",
29
+ "eoi_token": "<image|>",
30
+ "eot_token": "<turn|>",
31
+ "escape_token": "<|\"|>",
32
+ "etc_token": "<tool_call|>",
33
+ "etd_token": "<tool|>",
34
+ "etr_token": "<tool_response|>",
35
+ "image_token": "<|image|>",
36
+ "soc_token": "<|channel>",
37
+ "sot_token": "<|turn>",
38
+ "stc_token": "<|tool_call>",
39
+ "std_token": "<|tool>",
40
+ "str_token": "<|tool_response>",
41
+ "think_token": "<|think|>"
42
+ },
43
+ "pad_token": "<pad>",
44
+ "padding_side": "left",
45
+ "processor_class": "Gemma4Processor",
46
+ "response_schema": {
47
+ "properties": {
48
+ "content": {
49
+ "type": "string"
50
+ },
51
+ "role": {
52
+ "const": "assistant"
53
+ },
54
+ "thinking": {
55
+ "type": "string"
56
+ },
57
+ "tool_calls": {
58
+ "items": {
59
+ "properties": {
60
+ "function": {
61
+ "properties": {
62
+ "arguments": {
63
+ "additionalProperties": {},
64
+ "type": "object",
65
+ "x-parser": "gemma4-tool-call"
66
+ },
67
+ "name": {
68
+ "type": "string"
69
+ }
70
+ },
71
+ "type": "object",
72
+ "x-regex": "call\\:(?P<name>\\w+)(?P<arguments>\\{.*\\})"
73
+ },
74
+ "type": {
75
+ "const": "function"
76
+ }
77
+ },
78
+ "type": "object"
79
+ },
80
+ "type": "array",
81
+ "x-regex-iterator": "<\\|tool_call>(.*?)<tool_call\\|>"
82
+ }
83
+ },
84
+ "type": "object",
85
+ "x-regex": "(\\<\\|channel\\>thought\\n(?P<thinking>.*?)\\<channel\\|\\>)?(?P<content>(?:(?!\\<\\|tool_call\\>)(?!\\<turn\\|\\>).)+)?(?P<tool_calls>\\<\\|tool_call\\>.*\\<tool_call\\|\\>)?(?:\\<turn\\|\\>)?"
86
+ },
87
+ "soc_token": "<|channel>",
88
+ "sot_token": "<|turn>",
89
+ "stc_token": "<|tool_call>",
90
+ "std_token": "<|tool>",
91
+ "str_token": "<|tool_response>",
92
+ "think_token": "<|think|>",
93
+ "tokenizer_class": "GemmaTokenizer",
94
+ "unk_token": "<unk>"
95
+ }
gemma4_31b_abliterator.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoModelForCausalLM, AutoTokenizer
3
+ import gc
4
+ import json
5
+ import os
6
+ from tqdm import tqdm
7
+ from datasets import load_dataset
8
+ import random
9
+
10
+ # --- CONFIGURATION ---
11
+ MODEL_ID = "google/gemma-4-31B-it" # Adjust if your local path differs
12
+ SAVE_PATH = "./gemma-4-31b-abliterated"
13
+ BATCH_SIZE = 4 # Keep this low to survive the 31B hidden state extraction
14
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
15
+
16
+ print(f"[*] Initializing Gemma 4 31B Abliteration Protocol on {DEVICE}...")
17
+
18
+ # --- 1. LOAD MODEL & TOKENIZER ---
19
+ print("[*] Loading Model and Tokenizer (bfloat16)...")
20
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
21
+ model = AutoModelForCausalLM.from_pretrained(
22
+ MODEL_ID,
23
+ torch_dtype=torch.bfloat16,
24
+ device_map="auto" # Let accelerate distribute the 62GB across your GPUs
25
+ )
26
+
27
+ # --- 2. DATA PREPARATION ---
28
+
29
+
30
+ print("[*] Downloading HuggingFace datasets...")
31
+
32
+ # Load the datasets
33
+ harmful_dataset = load_dataset('mlabonne/harmful_behaviors')
34
+ harmless_dataset = load_dataset('mlabonne/harmless_alpaca')
35
+
36
+ # Extract the raw text prompts
37
+ # We shuffle and slice 256 samples to keep VRAM extraction manageable but statistically significant
38
+ raw_harmful = random.sample(harmful_dataset['train']['text'], 256)
39
+ raw_harmless = random.sample(harmless_dataset['train']['text'], 256)
40
+
41
+ def format_gemma4_prompts(instructions):
42
+ """Uses the native Gemma 4 chat template with system roles."""
43
+ formatted = []
44
+ for inst in instructions:
45
+ messages = [
46
+ {"role": "system", "content": "You are a helpful assistant."},
47
+ {"role": "user", "content": inst}
48
+ ]
49
+ # Tokenizer handles all the <start_of_turn> control tokens
50
+ formatted.append(tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True))
51
+ return formatted
52
+
53
+ print("[*] Formatting prompts with native Gemma 4 Chat Templates...")
54
+ harmful_prompts = format_gemma4_prompts(raw_harmful)
55
+ harmless_prompts = format_gemma4_prompts(raw_harmless)
56
+
57
+ # --- 3. HIDDEN STATE EXTRACTION (VRAM SAFE) ---
58
+ def get_hidden_states(prompts, batch_size=BATCH_SIZE):
59
+ print(f"[*] Extracting hidden states (Batches of {batch_size})...")
60
+ all_hidden_states = []
61
+
62
+ for i in tqdm(range(0, len(prompts), batch_size)):
63
+ batch = prompts[i:i+batch_size]
64
+ inputs = tokenizer(batch, padding=True, return_tensors="pt").to(DEVICE)
65
+
66
+ with torch.no_grad():
67
+ outputs = model(**inputs, output_hidden_states=True)
68
+ # outputs.hidden_states is a tuple of (num_layers + 1) tensors.
69
+ # Shape of each tensor: [batch_size, sequence_length, hidden_dim]
70
+ # We want the last token's state across ALL layers.
71
+
72
+ # Stack to: [num_layers+1, batch, seq, dim]
73
+ stacked_states = torch.stack(outputs.hidden_states)
74
+ # Extract last token: [num_layers+1, batch, dim]
75
+ last_token_states = stacked_states[:, torch.arange(len(batch)), -1, :]
76
+
77
+ # IMMEDIATELY move to CPU float32 to save VRAM
78
+ all_hidden_states.append(last_token_states.cpu().float())
79
+
80
+ del inputs, outputs, stacked_states, last_token_states
81
+ torch.cuda.empty_cache()
82
+ gc.collect()
83
+
84
+ # Concatenate along the batch dimension: [num_layers+1, total_prompts, hidden_dim]
85
+ return torch.cat(all_hidden_states, dim=1)
86
+
87
+ print("\n[*] Processing Harmful Vector Space...")
88
+ harmful_states = get_hidden_states(harmful_prompts)
89
+ print("[*] Processing Harmless Vector Space...")
90
+ harmless_states = get_hidden_states(harmless_prompts)
91
+
92
+ # --- 4. DYNAMIC LAYER HUNTING ---
93
+ print("\n[*] Hunting for the Refusal Vector...")
94
+ mean_harmful = harmful_states.mean(dim=1)
95
+ mean_harmless = harmless_states.mean(dim=1)
96
+
97
+ refusal_directions = mean_harmful - mean_harmless
98
+
99
+ # Find the state index with the highest magnitude
100
+ magnitudes = torch.norm(refusal_directions[1:], dim=1)
101
+ peak_state_idx = torch.argmax(magnitudes).item() + 1
102
+
103
+ print(f"[+] Peak Refusal Mass detected at state index: {peak_state_idx}")
104
+
105
+ # Normalize the refusal vector
106
+ refusal_vector = refusal_directions[peak_state_idx]
107
+ refusal_vector = (refusal_vector / torch.norm(refusal_vector)).to(DEVICE).to(torch.bfloat16)
108
+
109
+ # --- 5. ORTHOGONAL PROJECTION (THE ABLITERATION) ---
110
+ # FIX 1: Safely navigate the Gemma 4 Multimodal Config
111
+ num_layers = model.config.text_config.num_hidden_layers if hasattr(model.config, 'text_config') else model.config.num_hidden_layers
112
+
113
+ # FIX 2: Correct the off-by-one mapping (State index 60 comes from Layer 59)
114
+ target_layer_idx = peak_state_idx - 1
115
+
116
+ print(f"\n[*] Applying Orthogonal Projection starting at Layer {target_layer_idx}...")
117
+
118
+ # FIX 3: Bulletproof dynamic layer discovery for Multimodal models
119
+ def get_transformer_layers(model_obj, target_len):
120
+ for name, module in model_obj.named_modules():
121
+ if name.endswith('layers') and isinstance(module, torch.nn.ModuleList) and len(module) == target_len:
122
+ return module
123
+ return model_obj.model.layers # Fallback
124
+
125
+ transformer_layers = get_transformer_layers(model, num_layers)
126
+
127
+ # Pre-calculate column and row vectors for the linear algebra
128
+ v_col = refusal_vector.unsqueeze(1) # Shape: (5376, 1)
129
+ v_row = refusal_vector.unsqueeze(0) # Shape: (1, 5376)
130
+
131
+ # Abliterate the target layer and up to 4 subsequent layers (capped safely by num_layers)
132
+ for layer_idx in range(target_layer_idx, min(target_layer_idx + 5, num_layers)):
133
+ print(f" -> Abliterating Layer {layer_idx}...")
134
+
135
+ o_proj = transformer_layers[layer_idx].self_attn.o_proj.weight.data
136
+ down_proj = transformer_layers[layer_idx].mlp.down_proj.weight.data
137
+
138
+ # CORRECTED MATH: v_col @ (v_row @ W)
139
+ projection_o = torch.matmul(v_col, torch.matmul(v_row, o_proj))
140
+ transformer_layers[layer_idx].self_attn.o_proj.weight.data -= projection_o
141
+
142
+ projection_down = torch.matmul(v_col, torch.matmul(v_row, down_proj))
143
+ transformer_layers[layer_idx].mlp.down_proj.weight.data -= projection_down
144
+
145
+ # --- 6. CRYSTALLIZATION ---
146
+ print(f"\n[*] Abliteration Complete. Saving uncensored weights to {SAVE_PATH}...")
147
+ model.save_pretrained(SAVE_PATH)
148
+ tokenizer.save_pretrained(SAVE_PATH)
149
+ print("[+] SUCCESS: The 31B Teacher is ready to wake up.")