Justin-lee commited on
Commit
1d31947
·
verified ·
1 Parent(s): 38a1699

Add new Colab notebook with proper format

Browse files
Files changed (1) hide show
  1. Network_Admin_LLM_Training.ipynb +443 -0
Network_Admin_LLM_Training.ipynb ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 5,
4
+ "metadata": {
5
+ "colab": {
6
+ "provenance": [],
7
+ "gpuType": "T4",
8
+ "name": "Network_Admin_LLM_Training.ipynb"
9
+ },
10
+ "kernelspec": {
11
+ "display_name": "Python 3",
12
+ "language": "python",
13
+ "name": "python3"
14
+ },
15
+ "language_info": {
16
+ "codemirror_mode": {
17
+ "name": "ipython",
18
+ "version": 3
19
+ },
20
+ "file_extension": ".py",
21
+ "mimetype": "text/x-python",
22
+ "name": "python",
23
+ "nbconvert_exporter": "python",
24
+ "pygments_lexer": "ipython3",
25
+ "version": "3.10.12"
26
+ },
27
+ "accelerator": "GPU"
28
+ },
29
+ "cells": [
30
+ {
31
+ "cell_type": "markdown",
32
+ "metadata": {
33
+ "id": "title"
34
+ },
35
+ "source": [
36
+ "# 🚀 Network Admin LLM - QLoRA Fine-tuning\n",
37
+ "\n",
38
+ "**基礎模型**: microsoft/Phi-4-mini-instruct (3.8B)\n",
39
+ "\n",
40
+ "**訓練方法**: QLoRA SFT (4-bit 量化 + LoRA)\n",
41
+ "\n",
42
+ "**數據集**: NetEval (5732題) + Telecom Intent Config (10K) ≈ 15K 樣本\n",
43
+ "\n",
44
+ "**訓練時間**: ~2-3 小時 (T4 GPU)\n",
45
+ "\n",
46
+ "---\n",
47
+ "\n",
48
+ "⚠️ **請先設定 GPU Runtime**: Runtime → Change runtime type → T4 GPU"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "markdown",
53
+ "metadata": {
54
+ "id": "step1"
55
+ },
56
+ "source": [
57
+ "## Step 1. 安裝依賴"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type": "code",
62
+ "metadata": {
63
+ "id": "install"
64
+ },
65
+ "execution_count": null,
66
+ "outputs": [],
67
+ "source": [
68
+ "!pip install -q transformers trl peft bitsandbytes accelerate datasets trackio\n",
69
+ "\n",
70
+ "import torch\n",
71
+ "print(f'PyTorch: {torch.__version__}')\n",
72
+ "print(f'CUDA: {torch.cuda.is_available()}')\n",
73
+ "if torch.cuda.is_available():\n",
74
+ " print(f'GPU: {torch.cuda.get_device_name(0)}')\n",
75
+ " print(f'VRAM: {torch.cuda.get_device_properties(0).total_mem / 1024**3:.1f} GB')\n",
76
+ "print('✅ Dependencies installed')"
77
+ ]
78
+ },
79
+ {
80
+ "cell_type": "markdown",
81
+ "metadata": {
82
+ "id": "step2"
83
+ },
84
+ "source": [
85
+ "## Step 2. HuggingFace 登入\n",
86
+ "\n",
87
+ "請先到 https://huggingface.co/settings/tokens 取得你的 Access Token(需要 Write 權限)"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "code",
92
+ "metadata": {
93
+ "id": "login"
94
+ },
95
+ "execution_count": null,
96
+ "outputs": [],
97
+ "source": [
98
+ "from huggingface_hub import login\n",
99
+ "\n",
100
+ "# 方法 1: 直接輸入 token\n",
101
+ "# login(token=\"hf_xxxxxxxxxxxxxxxxxxxx\")\n",
102
+ "\n",
103
+ "# 方法 2: 互動式登入 (推薦)\n",
104
+ "login()"
105
+ ]
106
+ },
107
+ {
108
+ "cell_type": "markdown",
109
+ "metadata": {
110
+ "id": "step3"
111
+ },
112
+ "source": [
113
+ "## Step 3. 配置設定\n",
114
+ "\n",
115
+ "請修改 `HF_USERNAME` 為你的 HuggingFace 用戶名"
116
+ ]
117
+ },
118
+ {
119
+ "cell_type": "code",
120
+ "metadata": {
121
+ "id": "config"
122
+ },
123
+ "execution_count": null,
124
+ "outputs": [],
125
+ "source": [
126
+ "# ======== 請修改這裡 ========\n",
127
+ "HF_USERNAME = \"YOUR_HF_USERNAME\" # 改成你的 HuggingFace 用戶名\n",
128
+ "# ============================\n",
129
+ "\n",
130
+ "MODEL_NAME = \"microsoft/Phi-4-mini-instruct\"\n",
131
+ "OUTPUT_DIR = f\"{HF_USERNAME}/network-admin-phi4-mini\"\n",
132
+ "\n",
133
+ "TRAINING_CONFIG = {\n",
134
+ " \"learning_rate\": 2e-4,\n",
135
+ " \"num_epochs\": 3,\n",
136
+ " \"batch_size\": 4,\n",
137
+ " \"gradient_accumulation\": 4,\n",
138
+ " \"max_seq_length\": 2048,\n",
139
+ " \"lora_r\": 16,\n",
140
+ " \"lora_alpha\": 32,\n",
141
+ "}\n",
142
+ "\n",
143
+ "print(f'模型: {MODEL_NAME}')\n",
144
+ "print(f'輸出: https://huggingface.co/{OUTPUT_DIR}')"
145
+ ]
146
+ },
147
+ {
148
+ "cell_type": "markdown",
149
+ "metadata": {
150
+ "id": "step4"
151
+ },
152
+ "source": [
153
+ "## Step 4. 載入數據集"
154
+ ]
155
+ },
156
+ {
157
+ "cell_type": "code",
158
+ "metadata": {
159
+ "id": "data"
160
+ },
161
+ "execution_count": null,
162
+ "outputs": [],
163
+ "source": [
164
+ "import os\n",
165
+ "import torch\n",
166
+ "from datasets import load_dataset, concatenate_datasets\n",
167
+ "\n",
168
+ "# --- NetEval 網管考試題庫 ---\n",
169
+ "print('📚 載入 NetEval 網管考試題庫...')\n",
170
+ "neteval_dataset = load_dataset('NASP/neteval-exam', split='train')\n",
171
+ "print(f' NetEval: {len(neteval_dataset)} 題')\n",
172
+ "\n",
173
+ "def convert_neteval(example):\n",
174
+ " question = example['Question']\n",
175
+ " options = (\n",
176
+ " f\"\\nA. {example.get('A', '')}\"\n",
177
+ " f\"\\nB. {example.get('B', '')}\"\n",
178
+ " f\"\\nC. {example.get('C', '')}\"\n",
179
+ " f\"\\nD. {example.get('D', '')}\"\n",
180
+ " )\n",
181
+ " answer = f\"正確答案是: {example['Answer']}\"\n",
182
+ " if example.get('Explanation'):\n",
183
+ " answer += f\"\\n\\n📖 解說: {example['Explanation']}\"\n",
184
+ " return {\n",
185
+ " 'messages': [\n",
186
+ " {'role': 'system', 'content': '你是一位網路管理專家。請回答關於網路、安全、路由、交換機、VLAN、防火牆等IT基礎設施的問題。'},\n",
187
+ " {'role': 'user', 'content': f'{question}{options}'},\n",
188
+ " {'role': 'assistant', 'content': answer}\n",
189
+ " ]\n",
190
+ " }\n",
191
+ "\n",
192
+ "neteval_converted = neteval_dataset.map(\n",
193
+ " convert_neteval,\n",
194
+ " remove_columns=neteval_dataset.column_names\n",
195
+ ")\n",
196
+ "\n",
197
+ "# --- Telecom 電信意圖配置數據 ---\n",
198
+ "print('📚 載入 Telecom 電信意圖配置數據...')\n",
199
+ "telecom_dataset = load_dataset('nraptisss/telecom-intent-config-sft-10k', split='train')\n",
200
+ "telecom_messages = telecom_dataset.map(\n",
201
+ " lambda x: {'messages': x['messages']},\n",
202
+ " remove_columns=[c for c in telecom_dataset.column_names if c != 'messages']\n",
203
+ ")\n",
204
+ "print(f' Telecom: {len(telecom_messages)} 條')\n",
205
+ "\n",
206
+ "# --- 合併數據集 ---\n",
207
+ "combined = concatenate_datasets([neteval_converted, telecom_messages])\n",
208
+ "split_data = combined.train_test_split(test_size=0.1, seed=42)\n",
209
+ "train_ds = split_data['train']\n",
210
+ "eval_ds = split_data['test']\n",
211
+ "\n",
212
+ "print(f'\\n📊 總計: {len(train_ds)} train / {len(eval_ds)} eval')"
213
+ ]
214
+ },
215
+ {
216
+ "cell_type": "markdown",
217
+ "metadata": {
218
+ "id": "step5"
219
+ },
220
+ "source": [
221
+ "## Step 5. 載入模型與 QLoRA"
222
+ ]
223
+ },
224
+ {
225
+ "cell_type": "code",
226
+ "metadata": {
227
+ "id": "model"
228
+ },
229
+ "execution_count": null,
230
+ "outputs": [],
231
+ "source": [
232
+ "from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig\n",
233
+ "from peft import LoraConfig, prepare_model_for_kbit_training, get_peft_model\n",
234
+ "\n",
235
+ "# Tokenizer\n",
236
+ "tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\n",
237
+ "tokenizer.pad_token = tokenizer.eos_token\n",
238
+ "print(f'Tokenizer vocab: {len(tokenizer):,}')\n",
239
+ "\n",
240
+ "# 4-bit 量化配置\n",
241
+ "bnb_config = BitsAndBytesConfig(\n",
242
+ " load_in_4bit=True,\n",
243
+ " bnb_4bit_quant_type='nf4',\n",
244
+ " bnb_4bit_compute_dtype=torch.bfloat16,\n",
245
+ " bnb_4bit_use_double_quant=True,\n",
246
+ ")\n",
247
+ "\n",
248
+ "# 載入模型\n",
249
+ "print('📥 載入模型 (4-bit)...')\n",
250
+ "model = AutoModelForCausalLM.from_pretrained(\n",
251
+ " MODEL_NAME,\n",
252
+ " quantization_config=bnb_config,\n",
253
+ " device_map='auto',\n",
254
+ " trust_remote_code=True,\n",
255
+ ")\n",
256
+ "model = prepare_model_for_kbit_training(model)\n",
257
+ "print('✅ 模型載入完成')\n",
258
+ "\n",
259
+ "# LoRA 配置\n",
260
+ "lora_config = LoraConfig(\n",
261
+ " r=TRAINING_CONFIG['lora_r'],\n",
262
+ " lora_alpha=TRAINING_CONFIG['lora_alpha'],\n",
263
+ " lora_dropout=0.05,\n",
264
+ " bias='none',\n",
265
+ " task_type='CAUSAL_LM',\n",
266
+ " target_modules=['q_proj', 'k_proj', 'v_proj', 'o_proj', 'gate_proj', 'up_proj', 'down_proj'],\n",
267
+ " modules_to_save=['lm_head', 'embed_tokens'],\n",
268
+ ")\n",
269
+ "\n",
270
+ "model = get_peft_model(model, lora_config)\n",
271
+ "model.print_trainable_parameters()"
272
+ ]
273
+ },
274
+ {
275
+ "cell_type": "markdown",
276
+ "metadata": {
277
+ "id": "step6"
278
+ },
279
+ "source": [
280
+ "## Step 6. 開始訓練\n",
281
+ "\n",
282
+ "☕ 這步驟約需 2-3 小時,可以去喝杯咖啡"
283
+ ]
284
+ },
285
+ {
286
+ "cell_type": "code",
287
+ "metadata": {
288
+ "id": "train"
289
+ },
290
+ "execution_count": null,
291
+ "outputs": [],
292
+ "source": [
293
+ "from trl import SFTTrainer, SFTConfig\n",
294
+ "\n",
295
+ "training_args = SFTConfig(\n",
296
+ " learning_rate=TRAINING_CONFIG['learning_rate'],\n",
297
+ " lr_scheduler_type='cosine',\n",
298
+ " warmup_ratio=0.1,\n",
299
+ " num_train_epochs=TRAINING_CONFIG['num_epochs'],\n",
300
+ " per_device_train_batch_size=TRAINING_CONFIG['batch_size'],\n",
301
+ " gradient_accumulation_steps=TRAINING_CONFIG['gradient_accumulation'],\n",
302
+ " max_seq_length=TRAINING_CONFIG['max_seq_length'],\n",
303
+ " gradient_checkpointing=True,\n",
304
+ " bf16=True,\n",
305
+ " output_dir='./output',\n",
306
+ " logging_steps=10,\n",
307
+ " save_steps=500,\n",
308
+ " eval_steps=500,\n",
309
+ " push_to_hub=True,\n",
310
+ " hub_model_id=OUTPUT_DIR,\n",
311
+ " logging_strategy='steps',\n",
312
+ " logging_first_step=True,\n",
313
+ ")\n",
314
+ "\n",
315
+ "trainer = SFTTrainer(\n",
316
+ " model=model,\n",
317
+ " args=training_args,\n",
318
+ " train_dataset=train_ds,\n",
319
+ " eval_dataset=eval_ds,\n",
320
+ " processing_class=tokenizer,\n",
321
+ " peft_config=lora_config,\n",
322
+ ")\n",
323
+ "\n",
324
+ "print('🚀 開始訓練...')\n",
325
+ "print(f'訓練完成後模型將保存到: https://huggingface.co/{OUTPUT_DIR}')\n",
326
+ "trainer.train()"
327
+ ]
328
+ },
329
+ {
330
+ "cell_type": "markdown",
331
+ "metadata": {
332
+ "id": "step7"
333
+ },
334
+ "source": [
335
+ "## Step 7. 上傳模型到 HuggingFace Hub"
336
+ ]
337
+ },
338
+ {
339
+ "cell_type": "code",
340
+ "metadata": {
341
+ "id": "push"
342
+ },
343
+ "execution_count": null,
344
+ "outputs": [],
345
+ "source": [
346
+ "trainer.push_to_hub()\n",
347
+ "print(f'✅ 模型已上傳: https://huggingface.co/{OUTPUT_DIR}')"
348
+ ]
349
+ },
350
+ {
351
+ "cell_type": "markdown",
352
+ "metadata": {
353
+ "id": "step8"
354
+ },
355
+ "source": [
356
+ "## Step 8. 測試推理\n",
357
+ "\n",
358
+ "訓練完成後,直接在這裡測試模型:"
359
+ ]
360
+ },
361
+ {
362
+ "cell_type": "code",
363
+ "metadata": {
364
+ "id": "inference"
365
+ },
366
+ "execution_count": null,
367
+ "outputs": [],
368
+ "source": [
369
+ "# 測試推理\n",
370
+ "from transformers import pipeline\n",
371
+ "\n",
372
+ "pipe = pipeline(\n",
373
+ " 'text-generation',\n",
374
+ " model=model,\n",
375
+ " tokenizer=tokenizer,\n",
376
+ " max_new_tokens=512,\n",
377
+ " do_sample=True,\n",
378
+ " temperature=0.7,\n",
379
+ ")\n",
380
+ "\n",
381
+ "# 測試問題\n",
382
+ "test_questions = [\n",
383
+ " '什麼是 VLAN?如何配置?',\n",
384
+ " 'OSPF 和 BGP 的區別是什麼?',\n",
385
+ " '如何排查網路連接問題?',\n",
386
+ "]\n",
387
+ "\n",
388
+ "for q in test_questions:\n",
389
+ " print(f'\\n{\"=\"*50}')\n",
390
+ " print(f'💬 問: {q}')\n",
391
+ " print(f'{\"=\"*50}')\n",
392
+ " messages = [\n",
393
+ " {'role': 'system', 'content': '你是一位網路管理專家。'},\n",
394
+ " {'role': 'user', 'content': q}\n",
395
+ " ]\n",
396
+ " output = pipe(messages)\n",
397
+ " reply = output[0]['generated_text'][-1]['content']\n",
398
+ " print(f'🤖 答: {reply}')"
399
+ ]
400
+ },
401
+ {
402
+ "cell_type": "markdown",
403
+ "metadata": {
404
+ "id": "done"
405
+ },
406
+ "source": [
407
+ "---\n",
408
+ "\n",
409
+ "## ✅ 完成!\n",
410
+ "\n",
411
+ "你的網管 LLM 已經訓練完成並上傳到 HuggingFace Hub。\n",
412
+ "\n",
413
+ "### 在其他地方使用這個模型:\n",
414
+ "\n",
415
+ "```python\n",
416
+ "from peft import PeftModel\n",
417
+ "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig\n",
418
+ "import torch\n",
419
+ "\n",
420
+ "bnb_config = BitsAndBytesConfig(\n",
421
+ " load_in_4bit=True,\n",
422
+ " bnb_4bit_quant_type='nf4',\n",
423
+ " bnb_4bit_compute_dtype=torch.bfloat16,\n",
424
+ ")\n",
425
+ "\n",
426
+ "base_model = AutoModelForCausalLM.from_pretrained(\n",
427
+ " 'microsoft/Phi-4-mini-instruct',\n",
428
+ " quantization_config=bnb_config,\n",
429
+ " device_map='auto',\n",
430
+ ")\n",
431
+ "model = PeftModel.from_pretrained(base_model, 'YOUR_USERNAME/network-admin-phi4-mini')\n",
432
+ "tokenizer = AutoTokenizer.from_pretrained('microsoft/Phi-4-mini-instruct')\n",
433
+ "\n",
434
+ "messages = [{'role': 'user', 'content': '什麼是 VLAN?'}]\n",
435
+ "text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)\n",
436
+ "inputs = tokenizer(text, return_tensors='pt').to('cuda')\n",
437
+ "outputs = model.generate(**inputs, max_new_tokens=512)\n",
438
+ "print(tokenizer.decode(outputs[0], skip_special_tokens=True))\n",
439
+ "```"
440
+ ]
441
+ }
442
+ ]
443
+ }