asdf98 commited on
Commit
a7f05ff
·
verified ·
1 Parent(s): 5279226

Root-fix Gemma4 Unsloth notebook with clean install and current SFTConfig

Browse files
Files changed (1) hide show
  1. EthicalHacking_Gemma4_E2B_Colab.ipynb +23 -533
EthicalHacking_Gemma4_E2B_Colab.ipynb CHANGED
@@ -1,536 +1,26 @@
1
  {
2
  "cells": [
3
- {
4
- "cell_type": "markdown",
5
- "metadata": {},
6
- "source": [
7
- "# \ud83d\udd10 Ultimate LLM Fine-Tuning \u2013 Gemma 4 E2B (Colab Free Tier T4)\n",
8
- "\n",
9
- "**\ud83e\udd47 Model:** [Google Gemma 4 E2B](https://huggingface.co/google/gemma-4-E2B-it) via Unsloth 4-bit \n",
10
- "**\ud83c\udfc6 Why this model?** Dense ~2B parameter edge model. NOT an MoE \u2014 all 2B params are active. \n",
11
- "**\u26a0\ufe0f T4 WARNING:** This is **tight on 16GB VRAM**. The 4-bit model uses ~7.4GB. Follow memory settings strictly. \n",
12
- "**\ud83d\udcca Datasets:** Your choice \u2014 cybersecurity, general chat, multilingual, coding, or mix them! \n",
13
- "**\u26a1 Framework:** Unsloth + TRL SFTTrainer \n",
14
- "\n",
15
- "> Pick any dataset below. Default is cybersecurity. Mix datasets for hybrid tuning.\n",
16
- "\n",
17
- "---\n",
18
- "\n",
19
- "## \ud83d\udccb Gemma-4 E2B T4 Notes\n",
20
- "\n",
21
- "| Spec | Value |\n",
22
- "|------|-------|\n",
23
- "| Parameters | ~2B (dense, NOT MoE) |\n",
24
- "| 4-bit VRAM | ~7.4 GB |\n",
25
- "| Batch size on T4 | **1 only** |\n",
26
- "| Max seq length | **2048 max** |\n",
27
- "| LoRA rank | **8** (save VRAM) |\n",
28
- "\n",
29
- "**Unsloth docs:** https://unsloth.ai/docs/models/gemma-4/train"
30
- ]
31
- },
32
- {
33
- "cell_type": "markdown",
34
- "metadata": {},
35
- "source": [
36
- "## 1\ufe0f\u20e3 Install Dependencies"
37
- ]
38
- },
39
- {
40
- "cell_type": "code",
41
- "execution_count": null,
42
- "metadata": {},
43
- "outputs": [],
44
- "source": [
45
- "%%capture\n",
46
- "!pip install -q unsloth trl datasets accelerate transformers bitsandbytes huggingface_hub"
47
- ]
48
- },
49
- {
50
- "cell_type": "markdown",
51
- "metadata": {},
52
- "source": [
53
- "## 2\ufe0f\u20e3 (Optional) Login to HuggingFace Hub"
54
- ]
55
- },
56
- {
57
- "cell_type": "code",
58
- "execution_count": null,
59
- "metadata": {},
60
- "outputs": [],
61
- "source": [
62
- "from huggingface_hub import login\n",
63
- "# login(token=\"hf_YOUR_TOKEN\")"
64
- ]
65
- },
66
- {
67
- "cell_type": "markdown",
68
- "metadata": {},
69
- "source": [
70
- "## 3\ufe0f\u20e3 Load Gemma-4 E2B in 4-bit via Unsloth\n",
71
- "\n",
72
- "**\u26a0\ufe0f MEMORY LIMITS:**\n",
73
- "\n",
74
- "| Setting | Value | Why |\n",
75
- "|---------|-------|-----|\n",
76
- "| `BATCH_SIZE` | **1** | Cannot fit >1 on T4 |\n",
77
- "| `MAX_SEQ_LENGTH` | **2048** | Longer = OOM |\n",
78
- "| `LORA_R` | **8** | Small rank saves VRAM |\n",
79
- "| `GRAD_ACCUM` | **8** | Effective batch = 8 |\n",
80
- "| `PACKING` | **False** | Safer memory profile |\n",
81
- "| `optim` | `adamw_8bit` | Must use 8-bit optimizer |\n",
82
- "\n",
83
- "**\u26a0\ufe0f ALSO ADDED:** `device_map={\"\": torch.cuda.current_device()}` to force GPU placement and avoid Kaggle/Colab `accelerate` bug.\n",
84
- "\n",
85
- "If you still OOM: lower `MAX_SEQ_LENGTH` to 1024, or use `use_rslora=True`."
86
- ]
87
- },
88
- {
89
- "cell_type": "code",
90
- "execution_count": null,
91
- "metadata": {},
92
- "outputs": [],
93
- "source": [
94
- "from unsloth import FastLanguageModel\n",
95
- "import torch\n",
96
- "\n",
97
- "MAX_SEQ_LENGTH = 2048\n",
98
- "LORA_R = 8\n",
99
- "LORA_ALPHA = 8\n",
100
- "BATCH_SIZE = 1\n",
101
- "GRAD_ACCUM = 8\n",
102
- "LEARNING_RATE = 2e-4\n",
103
- "MAX_STEPS = 4000\n",
104
- "WARMUP_STEPS = 100\n",
105
- "LOGGING_STEPS = 50\n",
106
- "SAVE_STEPS = 500\n",
107
- "PACKING = False\n",
108
- "SAMPLE_SIZE = 50000\n",
109
- "HUB_MODEL_ID = \"your-username/gemma4-e2b-lora\"\n",
110
- "\n",
111
- "MODEL_NAME = \"unsloth/gemma-4-E2B-it-unsloth-bnb-4bit\"\n",
112
- "\n",
113
- "model, tokenizer = FastLanguageModel.from_pretrained(\n",
114
- " model_name=MODEL_NAME,\n",
115
- " max_seq_length=MAX_SEQ_LENGTH,\n",
116
- " dtype=None,\n",
117
- " load_in_4bit=True,\n",
118
- " device_map={\"\": torch.cuda.current_device()}, # \u2190 FORCE GPU: fixes Kaggle/Colab device placement bug\n",
119
- ")\n",
120
- "\n",
121
- "model = FastLanguageModel.get_peft_model(\n",
122
- " model,\n",
123
- " r=LORA_R,\n",
124
- " target_modules=[\"q_proj\", \"k_proj\", \"v_proj\", \"o_proj\",\n",
125
- " \"gate_proj\", \"up_proj\", \"down_proj\"],\n",
126
- " lora_alpha=LORA_ALPHA,\n",
127
- " lora_dropout=0,\n",
128
- " bias=\"none\",\n",
129
- " use_gradient_checkpointing=\"unsloth\",\n",
130
- " random_state=3407,\n",
131
- " use_rslora=False,\n",
132
- " loftq_config=None,\n",
133
- ")\n",
134
- "\n",
135
- "trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
136
- "total = sum(p.numel() for p in model.parameters())\n",
137
- "print(f\"\u2705 Gemma-4 E2B loaded. Trainable params: {trainable:,} / {total:,} ({100*trainable/total:.2f}%)\")\n",
138
- "print(f\"\u26a0\ufe0f Expected training VRAM: ~12-14 GB (out of 16 GB)\")\n",
139
- "print(f\" If you get OOM, lower MAX_SEQ_LENGTH to 1024 or set use_rslora=True\")"
140
- ]
141
- },
142
- {
143
- "cell_type": "markdown",
144
- "metadata": {},
145
- "source": [
146
- "## 4\ufe0f\u20e3 \ud83c\udfaf CHOOSE YOUR DATASET(S)\n",
147
- "\n",
148
- "Uncomment **ONE** `DATASET_CHOICE` line. Mix datasets with `custom_mix`.\n",
149
- "\n",
150
- "| Choice | Dataset | Rows | Format | Best For |\n",
151
- "|--------|---------|------|--------|----------|\n",
152
- "| `\"cybersecurity\"` | Fenrir + Trendyol | 153K\u219250K | system/user/assistant | Ethical hacking education |\n",
153
- "| `\"ultrachat\"` | UltraChat 200K SFT | 200K\u219250K | messages | General conversation |\n",
154
- "| `\"openhermes\"` | OpenHermes 2.5 | 1M+\u219250K | conversations | Reasoning, coding |\n",
155
- "| `\"sharegpt_en\"` | ShareGPT English | ~90K\u219250K | conversations | Multi-turn dialogue |\n",
156
- "| `\"sharegpt_de\"` | ShareGPT German | ~104K\u219250K | conversations | German fine-tuning |\n",
157
- "| `\"sharegpt_hi\"` | ShareGPT Hindi | ~153K\u219250K | conversations | Hindi fine-tuning |\n",
158
- "| `\"code_corpus\"` | [Code Corpus](https://huggingface.co/datasets/krystv/code-corpus-llm-training) | 240K\u219250K | text (code files) | **Code completion** |\n",
159
- "| `\"custom_mix\"` | Mix of your choice | \u2014 | varies | Combine datasets |"
160
- ]
161
- },
162
- {
163
- "cell_type": "code",
164
- "execution_count": null,
165
- "metadata": {},
166
- "outputs": [],
167
- "source": [
168
- "from datasets import load_dataset, concatenate_datasets\n",
169
- "\n",
170
- "DATASET_CHOICE = \"cybersecurity\"\n",
171
- "\n",
172
- "# DATASET_CHOICE = \"ultrachat\"\n",
173
- "# DATASET_CHOICE = \"openhermes\"\n",
174
- "# DATASET_CHOICE = \"sharegpt_en\"\n",
175
- "# DATASET_CHOICE = \"sharegpt_de\"\n",
176
- "# DATASET_CHOICE = \"sharegpt_hi\"\n",
177
- "# DATASET_CHOICE = \"code_corpus\"\n",
178
- "# DATASET_CHOICE = \"custom_mix\"\n",
179
- "\n",
180
- "CUSTOM_DATASETS = [\n",
181
- " (\"AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1\", \"train\", 10000, \"messages\"),\n",
182
- " (\"HuggingFaceH4/ultrachat_200k\", \"train_sft\", 20000, \"messages\"),\n",
183
- " (\"teknium/OpenHermes-2.5\", \"train\", 20000, \"conversations\"),\n",
184
- "]\n",
185
- "\n",
186
- "print(f\"\ud83c\udfaf DATASET_CHOICE = {DATASET_CHOICE}\")"
187
- ]
188
- },
189
- {
190
- "cell_type": "markdown",
191
- "metadata": {},
192
- "source": [
193
- "## 5\ufe0f\u20e3 Load, Convert & Pre-process Selected Dataset"
194
- ]
195
- },
196
- {
197
- "cell_type": "code",
198
- "execution_count": null,
199
- "metadata": {},
200
- "outputs": [],
201
- "source": [
202
- "import random\n",
203
- "\n",
204
- "def _convert_fenrir(example):\n",
205
- " return {\"messages\": [\n",
206
- " {\"role\": \"system\", \"content\": example[\"system\"]},\n",
207
- " {\"role\": \"user\", \"content\": example[\"user\"]},\n",
208
- " {\"role\": \"assistant\", \"content\": example[\"assistant\"]},\n",
209
- " ]}\n",
210
- "\n",
211
- "def _convert_trendyol(example):\n",
212
- " return {\"messages\": [\n",
213
- " {\"role\": \"system\", \"content\": example[\"system\"]},\n",
214
- " {\"role\": \"user\", \"content\": example[\"user\"]},\n",
215
- " {\"role\": \"assistant\", \"content\": example[\"assistant\"]},\n",
216
- " ]}\n",
217
- "\n",
218
- "def _convert_ultrachat(example):\n",
219
- " return {\"messages\": example[\"messages\"]}\n",
220
- "\n",
221
- "def _convert_conversations(example):\n",
222
- " msgs = []\n",
223
- " system = example.get(\"system_prompt\", \"\") or example.get(\"system\", \"\")\n",
224
- " if system:\n",
225
- " msgs.append({\"role\": \"system\", \"content\": system})\n",
226
- " for turn in example[\"conversations\"]:\n",
227
- " role = \"user\" if turn[\"from\"] in (\"human\", \"user\") else \"assistant\"\n",
228
- " msgs.append({\"role\": role, \"content\": turn[\"value\"]})\n",
229
- " return {\"messages\": msgs}\n",
230
- "\n",
231
- "def _convert_code_corpus(example):\n",
232
- " code_text = example[\"text\"]\n",
233
- " domain = example.get(\"domain\", \"code\")\n",
234
- " repo = example.get(\"repo\", \"unknown\")\n",
235
- " lang = example.get(\"language\", \"\")\n",
236
- " user_prompt = f\"Here is a code snippet from the {domain} domain (repo: {repo}, language: {lang}). Please explain or improve it.\"\n",
237
- " return {\"messages\": [\n",
238
- " {\"role\": \"user\", \"content\": user_prompt},\n",
239
- " {\"role\": \"assistant\", \"content\": code_text},\n",
240
- " ]}\n",
241
- "\n",
242
- "all_datasets = []\n",
243
- "\n",
244
- "if DATASET_CHOICE == \"cybersecurity\":\n",
245
- " ds1 = load_dataset(\"AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1\", split=\"train\")\n",
246
- " ds1 = ds1.map(_convert_fenrir, remove_columns=ds1.column_names, batched=False)\n",
247
- " all_datasets.append(ds1)\n",
248
- " ds2 = load_dataset(\"Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset\", split=\"train\")\n",
249
- " ds2 = ds2.map(_convert_trendyol, remove_columns=ds2.column_names, batched=False)\n",
250
- " all_datasets.append(ds2)\n",
251
- "\n",
252
- "elif DATASET_CHOICE == \"ultrachat\":\n",
253
- " ds = load_dataset(\"HuggingFaceH4/ultrachat_200k\", split=\"train_sft\")\n",
254
- " ds = ds.map(_convert_ultrachat, remove_columns=ds.column_names, batched=False)\n",
255
- " all_datasets.append(ds)\n",
256
- "\n",
257
- "elif DATASET_CHOICE == \"openhermes\":\n",
258
- " ds = load_dataset(\"teknium/OpenHermes-2.5\", split=\"train\")\n",
259
- " ds = ds.map(_convert_conversations, remove_columns=ds.column_names, batched=False)\n",
260
- " all_datasets.append(ds)\n",
261
- "\n",
262
- "elif DATASET_CHOICE.startswith(\"sharegpt_\"):\n",
263
- " split_map = {\"sharegpt_en\": \"english\", \"sharegpt_de\": \"german_4b_translated\", \"sharegpt_hi\": \"hindi_27b_translated\"}\n",
264
- " ds = load_dataset(\"deepmage121/ShareGPT_multilingual\", split=split_map[DATASET_CHOICE])\n",
265
- " ds = ds.map(_convert_conversations, remove_columns=ds.column_names, batched=False)\n",
266
- " all_datasets.append(ds)\n",
267
- "\n",
268
- "elif DATASET_CHOICE == \"code_corpus\":\n",
269
- " ds = load_dataset(\"krystv/code-corpus-llm-training\", split=\"train\")\n",
270
- " ds = ds.map(_convert_code_corpus, remove_columns=ds.column_names, batched=False)\n",
271
- " all_datasets.append(ds)\n",
272
- "\n",
273
- "elif DATASET_CHOICE == \"custom_mix\":\n",
274
- " for ds_id, split, n_rows, fmt in CUSTOM_DATASETS:\n",
275
- " ds = load_dataset(ds_id, split=split)\n",
276
- " if n_rows and len(ds) > n_rows:\n",
277
- " ds = ds.shuffle(seed=3407).select(range(n_rows))\n",
278
- " if fmt == \"messages\": ds = ds.map(_convert_ultrachat, remove_columns=ds.column_names, batched=False)\n",
279
- " elif fmt == \"conversations\": ds = ds.map(_convert_conversations, remove_columns=ds.column_names, batched=False)\n",
280
- " elif fmt == \"text\": ds = ds.map(_convert_code_corpus, remove_columns=ds.column_names, batched=False)\n",
281
- " all_datasets.append(ds)\n",
282
- "\n",
283
- "else:\n",
284
- " raise ValueError(f\"Unknown DATASET_CHOICE: {DATASET_CHOICE}\")\n",
285
- "\n",
286
- "train_dataset = concatenate_datasets(all_datasets) if len(all_datasets) > 1 else all_datasets[0]\n",
287
- "print(f\"\\n\ud83d\udcca COMBINED DATASET: {len(train_dataset)} rows\")\n",
288
- "\n",
289
- "sample = train_dataset[random.randint(0, len(train_dataset)-1)]\n",
290
- "print(f\"Sample roles: {[m['role'] for m in sample['messages']]}\")\n",
291
- "for m in sample[\"messages\"]: print(f\" {m['role']}: {m['content'][:80]}...\")\n",
292
- "\n",
293
- "if len(train_dataset) > SAMPLE_SIZE:\n",
294
- " train_dataset = train_dataset.shuffle(seed=3407).select(range(SAMPLE_SIZE))\n",
295
- " print(f\"\\n\ud83d\ude80 SUBSAMPLED to {len(train_dataset)} rows\")\n",
296
- "\n",
297
- "print(f\" Effective batch size: {BATCH_SIZE * GRAD_ACCUM}\")\n",
298
- "print(f\" Steps per epoch: ~{len(train_dataset) // (BATCH_SIZE * GRAD_ACCUM)}\")\n",
299
- "print(f\" Capped to MAX_STEPS: {MAX_STEPS}\")"
300
- ]
301
- },
302
- {
303
- "cell_type": "markdown",
304
- "metadata": {},
305
- "source": [
306
- "## 6\ufe0f\u20e3 Convert Messages \u2192 Text (Chat Template)"
307
- ]
308
- },
309
- {
310
- "cell_type": "code",
311
- "execution_count": null,
312
- "metadata": {},
313
- "outputs": [],
314
- "source": [
315
- "def convert_messages_to_text(examples):\n",
316
- " texts = []\n",
317
- " for msgs in examples[\"messages\"]:\n",
318
- " text = tokenizer.apply_chat_template(msgs, tokenize=False, add_generation_prompt=False)\n",
319
- " texts.append(text)\n",
320
- " return {\"text\": texts}\n",
321
- "\n",
322
- "print(\"\ud83d\udd04 Converting messages to text...\")\n",
323
- "train_dataset = train_dataset.map(convert_messages_to_text, batched=True, remove_columns=[\"messages\"], batch_size=100)\n",
324
- "print(f\"\u2705 Dataset pre-processed. Columns: {train_dataset.column_names}\")\n",
325
- "print(f\"\ud83d\udcc4 Sample text length: {len(train_dataset[0]['text'])} chars\")"
326
- ]
327
- },
328
- {
329
- "cell_type": "markdown",
330
- "metadata": {},
331
- "source": [
332
- "## 7\ufe0f\u20e3 Configure SFT Trainer (T4-Safe Memory Settings)"
333
- ]
334
- },
335
- {
336
- "cell_type": "code",
337
- "execution_count": null,
338
- "metadata": {},
339
- "outputs": [],
340
- "source": [
341
- "from trl import SFTTrainer\n",
342
- "from transformers import TrainingArguments\n",
343
- "\n",
344
- "trainer = SFTTrainer(\n",
345
- " model=model,\n",
346
- " tokenizer=tokenizer,\n",
347
- " train_dataset=train_dataset,\n",
348
- " dataset_text_field=\"text\",\n",
349
- " max_seq_length=MAX_SEQ_LENGTH,\n",
350
- " dataset_num_proc=2,\n",
351
- " packing=PACKING,\n",
352
- " args=TrainingArguments(\n",
353
- " per_device_train_batch_size=BATCH_SIZE,\n",
354
- " gradient_accumulation_steps=GRAD_ACCUM,\n",
355
- " warmup_steps=WARMUP_STEPS,\n",
356
- " max_steps=MAX_STEPS,\n",
357
- " learning_rate=LEARNING_RATE,\n",
358
- " fp16=True,\n",
359
- " logging_steps=LOGGING_STEPS,\n",
360
- " optim=\"adamw_8bit\",\n",
361
- " weight_decay=0.01,\n",
362
- " lr_scheduler_type=\"linear\",\n",
363
- " seed=3407,\n",
364
- " output_dir=\"./outputs_gemma4\",\n",
365
- " save_strategy=\"steps\",\n",
366
- " save_steps=SAVE_STEPS,\n",
367
- " save_total_limit=2,\n",
368
- " report_to=\"none\",\n",
369
- " ),\n",
370
- ")\n",
371
- "\n",
372
- "print(f\"\u2705 Trainer ready. Dataset: {DATASET_CHOICE} | Steps: {MAX_STEPS}\")\n",
373
- "print(f\" Effective batch size: {BATCH_SIZE * GRAD_ACCUM}\")\n",
374
- "print(f\" Packing enabled: {PACKING}\")\n",
375
- "print(f\" \u26a0\ufe0f Expected training VRAM: ~12-14 GB (out of 16 GB)\")\n",
376
- "print(f\" Est. time at ~0.15 it/s: ~{MAX_STEPS * 6.7 / 3600:.1f} hours\")"
377
- ]
378
- },
379
- {
380
- "cell_type": "markdown",
381
- "metadata": {},
382
- "source": [
383
- "## 8\ufe0f\u20e3 Train \ud83d\ude80 (Watch for OOM!)"
384
- ]
385
- },
386
- {
387
- "cell_type": "code",
388
- "execution_count": null,
389
- "metadata": {},
390
- "outputs": [],
391
- "source": [
392
- "# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n",
393
- "# RUNTIME PATCH: Fix num_items_in_batch int bug\n",
394
- "# Must run AFTER trainer = SFTTrainer(...) \n",
395
- "# and BEFORE trainer.train()\n",
396
- "# \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n",
397
- "import types\n",
398
- "\n",
399
- "# Walk the trainer's class hierarchy and patch every training_step\n",
400
- "trainer_cls = type(trainer)\n",
401
- "\n",
402
- "for cls in trainer_cls.__mro__:\n",
403
- " if 'training_step' in cls.__dict__:\n",
404
- " _orig_step = cls.__dict__['training_step']\n",
405
- " \n",
406
- " def _make_safe_step(original):\n",
407
- " def _safe_training_step(self, model, inputs, num_items_in_batch=None, **kwargs):\n",
408
- " if isinstance(num_items_in_batch, int):\n",
409
- " num_items_in_batch = None\n",
410
- " return original(self, model, inputs, num_items_in_batch=num_items_in_batch, **kwargs)\n",
411
- " return _safe_training_step\n",
412
- " \n",
413
- " cls.training_step = _make_safe_step(_orig_step)\n",
414
- " print(f\"Patched {cls.__name__}.training_step\")\n",
415
- "\n",
416
- "print(\"Ready to run trainer.train()\")"
417
- ]
418
- },
419
- {
420
- "cell_type": "code",
421
- "execution_count": null,
422
- "metadata": {},
423
- "outputs": [],
424
- "source": [
425
- "if torch.cuda.is_available():\n",
426
- " total_mem = torch.cuda.get_device_properties(0).total_memory / 1e9\n",
427
- " alloc = torch.cuda.memory_allocated() / 1e9\n",
428
- " print(f\"VRAM before train: {alloc:.2f} GB / {total_mem:.2f} GB ({100*alloc/total_mem:.0f}%)\")\n",
429
- " print(f\"\u26a0\ufe0f If >80% before training starts, you WILL OOM during backprop.\")\n",
430
- "\n",
431
- "trainer_stats = trainer.train()\n",
432
- "\n",
433
- "print(\"\\n\ud83c\udf89 Training complete!\")\n",
434
- "print(trainer_stats)\n",
435
- "\n",
436
- "if torch.cuda.is_available():\n",
437
- " print(f\"VRAM after train: {torch.cuda.memory_allocated()/1e9:.2f} GB\")"
438
- ]
439
- },
440
- {
441
- "cell_type": "markdown",
442
- "metadata": {},
443
- "source": [
444
- "## 9\ufe0f\u20e3 Save & Push to HuggingFace Hub"
445
- ]
446
- },
447
- {
448
- "cell_type": "code",
449
- "execution_count": null,
450
- "metadata": {},
451
- "outputs": [],
452
- "source": [
453
- "model.save_pretrained(\"./gemma4-lora-adapter\")\n",
454
- "tokenizer.save_pretrained(\"./gemma4-lora-adapter\")\n",
455
- "print(\"\u2705 LoRA adapter saved\")\n",
456
- "\n",
457
- "print(\"\\n\ud83d\udd04 Merging LoRA into base model...\")\n",
458
- "merged_model = model.merge_and_unload()\n",
459
- "merged_model.save_pretrained(\"./gemma4-merged\")\n",
460
- "tokenizer.save_pretrained(\"./gemma4-merged\")\n",
461
- "print(\"\u2705 Merged model saved\")\n",
462
- "\n",
463
- "# model.push_to_hub(HUB_MODEL_ID)\n",
464
- "# tokenizer.push_to_hub(HUB_MODEL_ID)"
465
- ]
466
- },
467
- {
468
- "cell_type": "markdown",
469
- "metadata": {},
470
- "source": [
471
- "## \ud83d\udd1f Inference Demo"
472
- ]
473
- },
474
- {
475
- "cell_type": "code",
476
- "execution_count": null,
477
- "metadata": {},
478
- "outputs": [],
479
- "source": [
480
- "FastLanguageModel.for_inference(model)\n",
481
- "\n",
482
- "test_prompt = \"Explain how parameterized queries prevent SQL injection, with a Python example.\"\n",
483
- "\n",
484
- "messages = [\n",
485
- " {\"role\": \"system\", \"content\": \"You are a helpful and knowledgeable assistant.\"},\n",
486
- " {\"role\": \"user\", \"content\": test_prompt},\n",
487
- "]\n",
488
- "\n",
489
- "inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors=\"pt\").to(model.device)\n",
490
- "\n",
491
- "outputs = model.generate(input_ids=inputs, max_new_tokens=512, temperature=0.7, top_p=0.9,\n",
492
- " do_sample=True, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id)\n",
493
- "\n",
494
- "response = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
495
- "reply = response.split(\"user\")[-1].split(\"assistant\")[-1].strip()\n",
496
- "print(reply[:800])"
497
- ]
498
- },
499
- {
500
- "cell_type": "markdown",
501
- "metadata": {},
502
- "source": [
503
- "---\n",
504
- "## \ud83d\udcda Dataset & Model References\n",
505
- "\n",
506
- "| Resource | Link |\n",
507
- "|----------|------|\n",
508
- "| **Gemma 4 Paper** | https://storage.googleapis.com/deepmind-media/gemma/gemma-4-report.pdf |\n",
509
- "| **Gemma 4 E2B** | https://huggingface.co/google/gemma-4-E2B-it |\n",
510
- "| **Unsloth Gemma-4 Train** | https://unsloth.ai/docs/models/gemma-4/train |\n",
511
- "| **Code Corpus LLM Training** | https://huggingface.co/datasets/krystv/code-corpus-llm-training |\n",
512
- "| **UltraChat 200K** | https://huggingface.co/datasets/HuggingFaceH4/ultrachat_200k |\n",
513
- "| **OpenHermes 2.5** | https://huggingface.co/datasets/teknium/OpenHermes-2.5 |\n",
514
- "| **ShareGPT Multilingual** | https://huggingface.co/datasets/deepmage121/ShareGPT_multilingual |\n",
515
- "| **Fenrir Cybersecurity** | https://huggingface.co/datasets/AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1 |\n",
516
- "| **Trendyol Cybersecurity** | https://huggingface.co/datasets/Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset |\n",
517
- "\n",
518
- "---\n",
519
- "*Pick any dataset. Train anything. Use responsibly.*"
520
- ]
521
- }
522
  ],
523
- "metadata": {
524
- "kernelspec": {
525
- "display_name": "Python 3",
526
- "language": "python",
527
- "name": "python3"
528
- },
529
- "language_info": {
530
- "name": "python",
531
- "version": "3.10.12"
532
- }
533
- },
534
- "nbformat": 4,
535
- "nbformat_minor": 4
536
- }
 
1
  {
2
  "cells": [
3
+ {"cell_type":"markdown","metadata":{},"source":["# 🔐 Gemma-4 E2B Unsloth QLoRA — Root-Fixed Kaggle/Colab Notebook\n","\n","Keeps **Unsloth** for low VRAM and applies the same root fix for the `int.mean()` / `num_items_in_batch` bug.\n","\n","⚠️ Gemma-4 E2B is multimodal (`AutoModelForImageTextToText`) and tighter on free T4. Prefer LFM2.5 first. Defaults here are conservative.\n"]},
4
+ {"cell_type":"markdown","metadata":{},"source":["## 1. Clean install Unsloth stack — run first\n"]},
5
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["import os, sys, shutil, subprocess, pathlib\n","work_dir = pathlib.Path('/kaggle/working') if pathlib.Path('/kaggle/working').exists() else pathlib.Path('/content')\n","marker = work_dir / '.bex_unsloth_env_ready_v3'\n","shutil.rmtree(str(work_dir / 'unsloth_compiled_cache'), ignore_errors=True)\n","print('✅ Removed stale unsloth_compiled_cache')\n","if not marker.exists():\n"," print('Installing/updating Unsloth stack. Kernel will restart after this cell. Run all cells again after restart.')\n"," subprocess.check_call([sys.executable, '-m', 'pip', 'uninstall', '-y', 'unsloth', 'unsloth_zoo'])\n"," subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-U', '--no-cache-dir', 'unsloth', 'unsloth_zoo'])\n"," marker.write_text('ready')\n"," print('✅ Installed Unsloth. Restarting kernel now...')\n"," os.kill(os.getpid(), 9)\n","else:\n"," print('✅ Unsloth stack already prepared for this session')\n"]},
6
+ {"cell_type":"markdown","metadata":{},"source":["## 2. Optional HF login\n"]},
7
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from huggingface_hub import login\n","# login(token='hf_YOUR_WRITE_TOKEN')\n"]},
8
+ {"cell_type":"markdown","metadata":{},"source":["## 3. Imports and version check\n"]},
9
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from unsloth import FastLanguageModel, is_bfloat16_supported\n","from trl import SFTTrainer, SFTConfig\n","from datasets import load_dataset, concatenate_datasets\n","import torch, random, os, time\n","import transformers, trl, peft, accelerate\n","try:\n"," import unsloth\n"," print('unsloth', getattr(unsloth, '__version__', 'unknown'))\n","except Exception as e:\n"," print('unsloth version unavailable', e)\n","print('transformers', transformers.__version__)\n","print('trl', trl.__version__)\n","print('peft', peft.__version__)\n","print('accelerate', accelerate.__version__)\n","if torch.cuda.is_available():\n"," print('GPU:', torch.cuda.get_device_name(0))\n"," print(f'VRAM: {torch.cuda.get_device_properties(0).total_memory/1e9:.2f} GB')\n","else:\n"," print('⚠️ No GPU found. Enable GPU runtime.')\n"]},
10
+ {"cell_type":"markdown","metadata":{},"source":["## 4. Configuration\n"]},
11
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["MODEL_ID = 'unsloth/gemma-4-E2B-it-unsloth-bnb-4bit'\n","RUN_NAME = 'gemma4-e2b-cyber-unsloth-rootfixed'\n","DATASET_CHOICE = 'cybersecurity'\n","SAMPLE_SIZE = 10000\n","MAX_SEQ_LENGTH = 1024\n","LORA_R = 8\n","LORA_ALPHA = 16\n","BATCH_SIZE = 1\n","GRAD_ACCUM = 8\n","MAX_STEPS = 1000\n","LEARNING_RATE = 1e-4\n","WARMUP_STEPS = 50\n","SAVE_STEPS = 250\n","LOGGING_STEPS = 10\n","PACKING = False\n","SEED = 3407\n","OUTPUT_DIR = './outputs_gemma4_rootfixed'\n","HUB_MODEL_ID = 'your-username/gemma4-e2b-cyber-unsloth-rootfixed'\n","PUSH_TO_HUB = False\n","random.seed(SEED)\n","torch.manual_seed(SEED)\n","if torch.cuda.is_available(): torch.cuda.manual_seed_all(SEED)\n"]},
12
+ {"cell_type":"markdown","metadata":{},"source":["## 5. Load model with Unsloth 4-bit QLoRA\n"]},
13
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model, tokenizer = FastLanguageModel.from_pretrained(\n"," model_name=MODEL_ID,\n"," max_seq_length=MAX_SEQ_LENGTH,\n"," dtype=None,\n"," load_in_4bit=True,\n"," device_map={'': torch.cuda.current_device()} if torch.cuda.is_available() else None,\n",")\n","if tokenizer.pad_token is None:\n"," tokenizer.pad_token = tokenizer.eos_token\n","tokenizer.padding_side = 'right'\n","model = FastLanguageModel.get_peft_model(\n"," model,\n"," r=LORA_R,\n"," target_modules=['q_proj','k_proj','v_proj','o_proj','gate_proj','up_proj','down_proj'],\n"," lora_alpha=LORA_ALPHA,\n"," lora_dropout=0,\n"," bias='none',\n"," use_gradient_checkpointing='unsloth',\n"," random_state=SEED,\n"," use_rslora=True,\n"," loftq_config=None,\n",")\n","trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)\n","total = sum(p.numel() for p in model.parameters())\n","print(f'Trainable params: {trainable:,} / {total:,} ({100*trainable/total:.2f}%)')\n","if torch.cuda.is_available(): print(f'VRAM after model load: {torch.cuda.memory_allocated()/1e9:.2f} GB')\n"]},
14
+ {"cell_type":"markdown","metadata":{},"source":["## 6. Load and format cybersecurity dataset\n"]},
15
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["SYSTEM_SAFE = 'You are a cybersecurity education assistant. Provide defensive, ethical, and authorized security guidance only. Refuse harmful or unauthorized requests.'\n","def convert_sua(example):\n"," return {'messages': [{'role':'system','content':example.get('system') or SYSTEM_SAFE},{'role':'user','content':example['user']},{'role':'assistant','content':example['assistant']}]}\n","ds1=load_dataset('AlicanKiraz0/Cybersecurity-Dataset-Fenrir-v2.1', split='train').map(convert_sua, remove_columns=['system','user','assistant'])\n","ds2=load_dataset('Trendyol/Trendyol-Cybersecurity-Instruction-Tuning-Dataset', split='train').map(convert_sua, remove_columns=['system','user','assistant'])\n","dataset=concatenate_datasets([ds1,ds2])\n","print('Rows:', len(dataset))\n","if SAMPLE_SIZE and len(dataset)>SAMPLE_SIZE:\n"," dataset=dataset.shuffle(seed=SEED).select(range(SAMPLE_SIZE))\n"," print('Subsampled:', len(dataset))\n","print(dataset[0]['messages'])\n"]},
16
+ {"cell_type":"markdown","metadata":{},"source":["## 7. Convert messages to text\n"]},
17
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["def to_text(example):\n"," try:\n"," text=tokenizer.apply_chat_template(example['messages'], tokenize=False, add_generation_prompt=False)\n"," except Exception:\n"," text='\\n'.join([f\"<{m['role']}>\\n{m['content']}\\n</{m['role']}>\" for m in example['messages']]) + tokenizer.eos_token\n"," return {'text': text}\n","dataset=dataset.map(to_text, remove_columns=['messages'])\n","print(dataset[0]['text'][:500])\n"]},
18
+ {"cell_type":"markdown","metadata":{},"source":["## 8. Train with current SFTConfig API\n"]},
19
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["args = SFTConfig(\n"," output_dir=OUTPUT_DIR, dataset_text_field='text', max_length=MAX_SEQ_LENGTH, packing=PACKING,\n"," per_device_train_batch_size=BATCH_SIZE, gradient_accumulation_steps=GRAD_ACCUM,\n"," warmup_steps=WARMUP_STEPS, max_steps=MAX_STEPS, learning_rate=LEARNING_RATE,\n"," fp16=not is_bfloat16_supported(), bf16=is_bfloat16_supported(), logging_steps=LOGGING_STEPS,\n"," optim='adamw_8bit', weight_decay=0.01, lr_scheduler_type='linear', seed=SEED,\n"," save_strategy='steps', save_steps=SAVE_STEPS, save_total_limit=2, report_to='none',\n"," disable_tqdm=True, logging_first_step=True,\n",")\n","trainer = SFTTrainer(model=model, processing_class=tokenizer, train_dataset=dataset, args=args)\n","trainer.model_accepts_loss_kwargs = False\n","if hasattr(trainer, 'model'): trainer.model.config.use_cache = False\n","if torch.cuda.is_available(): print(f'VRAM before train: {torch.cuda.memory_allocated()/1e9:.2f} GB / {torch.cuda.get_device_properties(0).total_memory/1e9:.2f} GB')\n","trainer_stats = trainer.train()\n","print(trainer_stats)\n"]},
20
+ {"cell_type":"markdown","metadata":{},"source":["## 9. Save adapter and test\n"]},
21
+ {"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model.save_pretrained('./gemma4-lora-adapter')\n","tokenizer.save_pretrained('./gemma4-lora-adapter')\n","print('Saved adapter to ./gemma4-lora-adapter')\n","if PUSH_TO_HUB:\n"," model.push_to_hub(HUB_MODEL_ID); tokenizer.push_to_hub(HUB_MODEL_ID)\n","FastLanguageModel.for_inference(model)\n","messages=[{'role':'system','content':SYSTEM_SAFE},{'role':'user','content':'Explain parameterized queries for preventing SQL injection with safe Python.'}]\n","inputs=tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors='pt').to(model.device)\n","with torch.no_grad():\n"," outputs=model.generate(input_ids=inputs, max_new_tokens=256, temperature=0.7, top_p=0.9, do_sample=True, pad_token_id=tokenizer.pad_token_id, eos_token_id=tokenizer.eos_token_id)\n","print(tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True))\n"]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  ],
23
+ "metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10"}},
24
+ "nbformat":4,
25
+ "nbformat_minor":5
26
+ }