Upload train_on_colab.ipynb with huggingface_hub
Browse files- train_on_colab.ipynb +398 -0
train_on_colab.ipynb
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# PULSE Temporal Awareness Training\n",
|
| 8 |
+
"**Fine-tune Qwen 2.5 1.5B with PULSE temporal awareness on free Colab GPU.**\n",
|
| 9 |
+
"\n",
|
| 10 |
+
"This notebook:\n",
|
| 11 |
+
"1. Generates 2000 synthetic temporal training examples\n",
|
| 12 |
+
"2. Fine-tunes Qwen 2.5 1.5B with LoRA (8.8M trainable params)\n",
|
| 13 |
+
"3. Uploads the model to Hugging Face Hub\n",
|
| 14 |
+
"\n",
|
| 15 |
+
"Runtime: ~20-30 min on T4 GPU\n",
|
| 16 |
+
"\n",
|
| 17 |
+
"---"
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"cell_type": "code",
|
| 22 |
+
"execution_count": null,
|
| 23 |
+
"metadata": {},
|
| 24 |
+
"outputs": [],
|
| 25 |
+
"source": [
|
| 26 |
+
"# Step 0: Install dependencies\n",
|
| 27 |
+
"!pip install -q torch transformers peft trl datasets accelerate numpy huggingface_hub"
|
| 28 |
+
]
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"cell_type": "code",
|
| 32 |
+
"execution_count": null,
|
| 33 |
+
"metadata": {},
|
| 34 |
+
"outputs": [],
|
| 35 |
+
"source": [
|
| 36 |
+
"# Step 0b: Login to HF (to upload the trained model)\n",
|
| 37 |
+
"from huggingface_hub import notebook_login\n",
|
| 38 |
+
"notebook_login()"
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"cell_type": "code",
|
| 43 |
+
"execution_count": null,
|
| 44 |
+
"metadata": {},
|
| 45 |
+
"outputs": [],
|
| 46 |
+
"source": [
|
| 47 |
+
"# Step 1: PULSE inline encoder + data generator\n",
|
| 48 |
+
"import json, random, numpy as np\n",
|
| 49 |
+
"from datetime import datetime, timedelta\n",
|
| 50 |
+
"\n",
|
| 51 |
+
"_COG = np.array([0.20,0.15,0.12,0.10,0.12,0.18,0.30,0.50,0.70,0.85,0.95,0.92,\n",
|
| 52 |
+
" 0.85,0.72,0.68,0.75,0.88,0.90,0.82,0.70,0.55,0.40,0.30,0.25])\n",
|
| 53 |
+
"_NRG = np.array([0.15,0.10,0.08,0.07,0.10,0.20,0.40,0.60,0.75,0.85,0.90,0.88,\n",
|
| 54 |
+
" 0.82,0.70,0.65,0.72,0.85,0.88,0.80,0.65,0.50,0.35,0.25,0.18])\n",
|
| 55 |
+
"\n",
|
| 56 |
+
"def _interp(c, h):\n",
|
| 57 |
+
" h0 = int(h) % 24\n",
|
| 58 |
+
" return float(c[h0]*(1-(h-int(h))) + c[(h0+1)%24]*(h-int(h)))\n",
|
| 59 |
+
"\n",
|
| 60 |
+
"def get_phase(h):\n",
|
| 61 |
+
" if 6<=h<10: return 'morning_ramp'\n",
|
| 62 |
+
" if 10<=h<12: return 'morning_peak'\n",
|
| 63 |
+
" if 12<=h<14: return 'post_lunch_dip'\n",
|
| 64 |
+
" if 14<=h<17: return 'afternoon_peak'\n",
|
| 65 |
+
" if 17<=h<20: return 'evening_wind_down'\n",
|
| 66 |
+
" if 20<=h<23: return 'night_transition'\n",
|
| 67 |
+
" return 'deep_night'\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"SYSTEM_TEMPLATE = \"\"\"You are an AI assistant with temporal awareness through the PULSE temporal embedding system. Before each interaction, you receive a temporal context package describing the current moment — not just the time, but what that time means: circadian phase, cognitive capacity, energy level, urgency, and behavioral context.\n",
|
| 70 |
+
"\n",
|
| 71 |
+
"Use this temporal awareness naturally in your responses. Don't announce it mechanically — weave it into your reasoning the way a thoughtful colleague would who knows what time it is and what's going on.\n",
|
| 72 |
+
"\n",
|
| 73 |
+
"Current temporal context:\n",
|
| 74 |
+
"{temporal_context}\"\"\"\n",
|
| 75 |
+
"\n",
|
| 76 |
+
"CONTEXTS = [\n",
|
| 77 |
+
" ('monday_crunch',2.0,7,5),('critical_deadline',0.5,9,4),('normal_tuesday',None,3,7.5),\n",
|
| 78 |
+
" ('focus_day',None,1,8),('deadline_tomorrow',24.0,5,7),('friday_winding',None,2,7),\n",
|
| 79 |
+
" ('saturday_morning',None,0,9),('sunday_evening',12.0,0,7),('late_night',None,0,0),\n",
|
| 80 |
+
" ('early_fresh',None,0,8),('post_lunch',None,4,7),('peak_morning',None,2,8),\n",
|
| 81 |
+
" ('overdue',-2.0,8,4),('vacation',None,0,9),\n",
|
| 82 |
+
"]\n",
|
| 83 |
+
"\n",
|
| 84 |
+
"QUESTIONS = [\n",
|
| 85 |
+
" ('Should I start a complex refactoring task right now?','task_suitability'),\n",
|
| 86 |
+
" ('Is this a good time for creative brainstorming?','task_suitability'),\n",
|
| 87 |
+
" ('Should I take a break right now?','break_advice'),\n",
|
| 88 |
+
" ('How much focus can I expect right now?','cognitive_state'),\n",
|
| 89 |
+
" ('What does my current temporal state look like?','full_state'),\n",
|
| 90 |
+
" ('Am I in a good phase for deep work?','work_phase'),\n",
|
| 91 |
+
" ('How urgent is my situation right now?','urgency_assessment'),\n",
|
| 92 |
+
" ('Would I be more productive tomorrow morning?','timing_optimization'),\n",
|
| 93 |
+
" ('How should I prioritize my remaining tasks?','prioritization'),\n",
|
| 94 |
+
" ('What tasks should I tackle given my state?','task_matching'),\n",
|
| 95 |
+
" ('Should I push through or call it a day?','endurance_check'),\n",
|
| 96 |
+
" ('How does this moment compare to a typical morning?','relative_state'),\n",
|
| 97 |
+
" ('Should I schedule a difficult conversation now?','task_suitability'),\n",
|
| 98 |
+
" ('Is my energy level normal for this time?','circadian_comparison'),\n",
|
| 99 |
+
" ('Would this be a good time to learn something new?','task_suitability'),\n",
|
| 100 |
+
"]\n",
|
| 101 |
+
"\n",
|
| 102 |
+
"def generate_response(question, q_sub, dt, dl_str, events, sleep):\n",
|
| 103 |
+
" h = dt.hour + dt.minute/60\n",
|
| 104 |
+
" cog, eng = _interp(_COG, h), _interp(_NRG, h)\n",
|
| 105 |
+
" has_dl = dl_str is not None\n",
|
| 106 |
+
" hours_left = (datetime.fromisoformat(dl_str)-dt).total_seconds()/3600 if has_dl else None\n",
|
| 107 |
+
" is_overdue = has_dl and hours_left < 0\n",
|
| 108 |
+
" is_urgent = has_dl and hours_left is not None and 0 < hours_left < 3\n",
|
| 109 |
+
" is_night = dt.hour < 6 or dt.hour >= 22\n",
|
| 110 |
+
" is_peak = 10<=dt.hour<12 or 14<=dt.hour<17\n",
|
| 111 |
+
" is_dip = 12<=dt.hour<14\n",
|
| 112 |
+
" is_low_sleep = sleep < 6\n",
|
| 113 |
+
" p = []\n",
|
| 114 |
+
" if q_sub == 'task_suitability':\n",
|
| 115 |
+
" if any(w in question.lower() for w in ['complex','refactor','deep work','learn']):\n",
|
| 116 |
+
" if is_peak and not is_low_sleep and cog>0.8:\n",
|
| 117 |
+
" p.append(f'Ideal window. Cognitive capacity {cog:.0%} during peak hours.')\n",
|
| 118 |
+
" if is_urgent: p.append(f'But deadline in {hours_left:.1f}h — prioritize what\\'s due.')\n",
|
| 119 |
+
" elif not has_dl: p.append('No deadlines. Good time to dive deep.')\n",
|
| 120 |
+
" elif is_dip: p.append(f'Post-lunch dip — {cog:.0%} capacity. Wait until ~3pm.')\n",
|
| 121 |
+
" elif is_night: p.append(f'{dt.strftime(\"%I:%M %p\")}, cognition {cog:.0%}. Save for tomorrow.')\n",
|
| 122 |
+
" elif is_low_sleep: p.append(f'{sleep:.0f}h sleep compromises capacity. Stick to routine tasks.')\n",
|
| 123 |
+
" else: p.append(f'Moderate at {cog:.0%}. Not your peak window.')\n",
|
| 124 |
+
" elif 'creative' in question.lower() or 'brainstorm' in question.lower():\n",
|
| 125 |
+
" if is_dip: p.append('Reduced executive function helps creativity. Good for brainstorming.')\n",
|
| 126 |
+
" elif is_peak: p.append(f'Peak capacity ({cog:.0%}) great for structured creative work.')\n",
|
| 127 |
+
" elif 'break' in question.lower():\n",
|
| 128 |
+
" if eng<0.4: p.append(f'Yes. Energy {eng:.0%}. Take 15-20 minutes.')\n",
|
| 129 |
+
" elif is_dip: p.append('Natural dip. Short walk aligns with your rhythm.')\n",
|
| 130 |
+
" else: p.append(f'Energy ({eng:.0%}) solid. Keep going if in flow.')\n",
|
| 131 |
+
" elif 'conversation' in question.lower():\n",
|
| 132 |
+
" if is_peak and not is_low_sleep: p.append(f'Capacity {cog:.0%} helps with emotional regulation.')\n",
|
| 133 |
+
" else: p.append(f'Cognition {cog:.0%} — more reactive than reflective. Postpone if possible.')\n",
|
| 134 |
+
" elif q_sub in ('full_state','cognitive_state','work_phase'):\n",
|
| 135 |
+
" p.append(f'{dt.strftime(\"%A %I:%M %p\")}.')\n",
|
| 136 |
+
" if is_peak: p.append(f'Peak window — {cog:.0%} capacity, {eng:.0%} energy.')\n",
|
| 137 |
+
" elif is_dip: p.append(f'Post-lunch dip. {cog:.0%} cognition. Passes ~2:30pm.')\n",
|
| 138 |
+
" elif is_night: p.append(f'Deep night. {cog:.0%} cognition. Body wants rest.')\n",
|
| 139 |
+
" else: p.append(f'Capacity {cog:.0%}, energy {eng:.0%}.')\n",
|
| 140 |
+
" if is_low_sleep: p.append(f'Sleep deficit ({sleep:.0f}h) — expect ~20% more errors.')\n",
|
| 141 |
+
" if is_urgent: p.append(f'Deadline in {hours_left:.1f}h. Focused execution.')\n",
|
| 142 |
+
" elif q_sub == 'urgency_assessment':\n",
|
| 143 |
+
" if is_overdue: p.append(f'Critical. Deadline passed {-hours_left:.1f}h ago.')\n",
|
| 144 |
+
" elif is_urgent: p.append(f'High — {hours_left:.1f}h to deadline. Only focus.')\n",
|
| 145 |
+
" elif has_dl and hours_left<24: p.append(f'Moderate. {hours_left:.1f}h away.')\n",
|
| 146 |
+
" else: p.append('No deadlines. Choose based on energy and interest.')\n",
|
| 147 |
+
" elif q_sub == 'timing_optimization':\n",
|
| 148 |
+
" if cog<0.5: p.append('Yes. Tomorrow 10-12am gives double your current capacity.')\n",
|
| 149 |
+
" elif is_peak: p.append('Good window now. Waiting loses momentum.')\n",
|
| 150 |
+
" else: p.append(f'Current {cog:.0%} vs tomorrow\\'s ~93%. Depends on complexity.')\n",
|
| 151 |
+
" elif q_sub == 'prioritization':\n",
|
| 152 |
+
" if is_urgent: p.append(f'Deadline first — {hours_left:.1f}h left.')\n",
|
| 153 |
+
" elif is_peak: p.append('Peak for hardest task. Routine for the dip.')\n",
|
| 154 |
+
" else: p.append(f'{cog:.0%} capacity. Match tasks to state.')\n",
|
| 155 |
+
" elif q_sub == 'task_matching':\n",
|
| 156 |
+
" if cog>0.8: p.append('Strong. Go for complex debugging, architecture, learning.')\n",
|
| 157 |
+
" elif cog>0.5: p.append('Moderate. Code review, incremental features, docs.')\n",
|
| 158 |
+
" else: p.append('Low. Email triage, filing issues, planning tomorrow.')\n",
|
| 159 |
+
" elif q_sub == 'endurance_check':\n",
|
| 160 |
+
" if eng<0.3: p.append(f'Call it. Energy {eng:.0%}. Past diminishing returns.')\n",
|
| 161 |
+
" elif is_urgent: p.append(f'Push through — {hours_left:.1f}h to deadline.')\n",
|
| 162 |
+
" elif is_dip: p.append('Circadian dip, not a wall. 15-min break restores.')\n",
|
| 163 |
+
" else: p.append(f'Energy {eng:.0%}. Runway left if work engaging.')\n",
|
| 164 |
+
" elif q_sub in ('relative_state','circadian_comparison'):\n",
|
| 165 |
+
" p.append(f'Typical capacity at {dt.strftime(\"%I:%M %p\")}: ~{cog:.0%}.')\n",
|
| 166 |
+
" if is_low_sleep: p.append(f'{sleep:.0f}h sleep puts you below baseline.')\n",
|
| 167 |
+
" if not p: p.append(f'{cog:.0%} cognitive, {eng:.0%} energy, {dt.strftime(\"%A %I:%M %p\")}.')\n",
|
| 168 |
+
" return ' '.join(p)\n",
|
| 169 |
+
"\n",
|
| 170 |
+
"def generate_dataset(n=2000, seed=42):\n",
|
| 171 |
+
" rng = random.Random(seed)\n",
|
| 172 |
+
" examples = []\n",
|
| 173 |
+
" for _ in range(n):\n",
|
| 174 |
+
" name,dl_off,events,sleep = rng.choice(CONTEXTS)\n",
|
| 175 |
+
" dt = datetime(2026,rng.randint(1,12),rng.randint(1,28),rng.randint(0,23),rng.choice([0,15,30,45]))\n",
|
| 176 |
+
" if 'late' in name: dt = dt.replace(hour=rng.choice([0,1,2,3]))\n",
|
| 177 |
+
" elif 'early' in name: dt = dt.replace(hour=rng.choice([5,6,7]))\n",
|
| 178 |
+
" elif 'peak' in name: dt = dt.replace(hour=rng.choice([10,11]))\n",
|
| 179 |
+
" elif 'post_lunch' in name: dt = dt.replace(hour=rng.choice([13,14]))\n",
|
| 180 |
+
" dl_str = (dt+timedelta(hours=dl_off)).isoformat() if dl_off else None\n",
|
| 181 |
+
" h = dt.hour+dt.minute/60\n",
|
| 182 |
+
" cog,eng = _interp(_COG,h),_interp(_NRG,h)\n",
|
| 183 |
+
" urg = f'deadline in {dl_off:.1f}h' if dl_off and dl_off>0 else ('OVERDUE' if dl_off and dl_off<0 else 'none')\n",
|
| 184 |
+
" tc = f\"\"\"Current time: {dt.strftime('%A, %B %d %Y at %I:%M %p')}\n",
|
| 185 |
+
"Circadian phase: {get_phase(dt.hour)}\n",
|
| 186 |
+
"Cognitive capacity: {cog:.0%}\n",
|
| 187 |
+
"Energy level: {eng:.0%}\n",
|
| 188 |
+
"Urgency: {urg}\n",
|
| 189 |
+
"Events today: {events}\n",
|
| 190 |
+
"Sleep last night: {sleep:.1f} hours\n",
|
| 191 |
+
"Weekend: {'yes' if dt.weekday()>=5 else 'no'}\"\"\"\n",
|
| 192 |
+
" q, q_sub = rng.choice(QUESTIONS)\n",
|
| 193 |
+
" r = generate_response(q, q_sub, dt, dl_str, events, sleep)\n",
|
| 194 |
+
" examples.append({'messages': [\n",
|
| 195 |
+
" {'role':'system','content':SYSTEM_TEMPLATE.format(temporal_context=tc)},\n",
|
| 196 |
+
" {'role':'user','content':q},\n",
|
| 197 |
+
" {'role':'assistant','content':r},\n",
|
| 198 |
+
" ]})\n",
|
| 199 |
+
" return examples\n",
|
| 200 |
+
"\n",
|
| 201 |
+
"print('Data generator ready.')"
|
| 202 |
+
]
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"cell_type": "code",
|
| 206 |
+
"execution_count": null,
|
| 207 |
+
"metadata": {},
|
| 208 |
+
"outputs": [],
|
| 209 |
+
"source": [
|
| 210 |
+
"# Step 2: Generate training data\n",
|
| 211 |
+
"train_data = generate_dataset(2000, seed=42)\n",
|
| 212 |
+
"eval_data = generate_dataset(200, seed=99)\n",
|
| 213 |
+
"print(f'Train: {len(train_data)}, Eval: {len(eval_data)}')\n",
|
| 214 |
+
"print(f'\\nSample:\\n{json.dumps(train_data[0][\"messages\"][1], indent=2)}')\n",
|
| 215 |
+
"print(f'\\nResponse: {train_data[0][\"messages\"][2][\"content\"][:200]}')"
|
| 216 |
+
]
|
| 217 |
+
},
|
| 218 |
+
{
|
| 219 |
+
"cell_type": "code",
|
| 220 |
+
"execution_count": null,
|
| 221 |
+
"metadata": {},
|
| 222 |
+
"outputs": [],
|
| 223 |
+
"source": [
|
| 224 |
+
"# Step 3: Load model + LoRA\n",
|
| 225 |
+
"import torch\n",
|
| 226 |
+
"from transformers import AutoModelForCausalLM, AutoTokenizer\n",
|
| 227 |
+
"from peft import LoraConfig, get_peft_model, TaskType\n",
|
| 228 |
+
"\n",
|
| 229 |
+
"MODEL_NAME = 'Qwen/Qwen2.5-1.5B-Instruct' # Change to 0.5B for faster training\n",
|
| 230 |
+
"\n",
|
| 231 |
+
"print(f'Loading {MODEL_NAME}...')\n",
|
| 232 |
+
"tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)\n",
|
| 233 |
+
"tokenizer.pad_token = tokenizer.eos_token\n",
|
| 234 |
+
"\n",
|
| 235 |
+
"model = AutoModelForCausalLM.from_pretrained(\n",
|
| 236 |
+
" MODEL_NAME, torch_dtype=torch.bfloat16, trust_remote_code=True,\n",
|
| 237 |
+
")\n",
|
| 238 |
+
"\n",
|
| 239 |
+
"lora_config = LoraConfig(\n",
|
| 240 |
+
" r=16, lora_alpha=32,\n",
|
| 241 |
+
" target_modules=['q_proj','k_proj','v_proj','o_proj','gate_proj','up_proj','down_proj'],\n",
|
| 242 |
+
" lora_dropout=0.05, bias='none', task_type=TaskType.CAUSAL_LM,\n",
|
| 243 |
+
")\n",
|
| 244 |
+
"model = get_peft_model(model, lora_config)\n",
|
| 245 |
+
"t, total = model.get_nb_trainable_parameters()\n",
|
| 246 |
+
"print(f'LoRA: {t:,} trainable / {total:,} total ({100*t/total:.2f}%)')\n",
|
| 247 |
+
"print(f'GPU: {torch.cuda.get_device_name()}')"
|
| 248 |
+
]
|
| 249 |
+
},
|
| 250 |
+
{
|
| 251 |
+
"cell_type": "code",
|
| 252 |
+
"execution_count": null,
|
| 253 |
+
"metadata": {},
|
| 254 |
+
"outputs": [],
|
| 255 |
+
"source": [
|
| 256 |
+
"# Step 4: Prepare dataset\n",
|
| 257 |
+
"from datasets import Dataset\n",
|
| 258 |
+
"\n",
|
| 259 |
+
"def format_chat(ex):\n",
|
| 260 |
+
" return {'text': tokenizer.apply_chat_template(ex['messages'], tokenize=False, add_generation_prompt=False)}\n",
|
| 261 |
+
"\n",
|
| 262 |
+
"train_ds = Dataset.from_list(train_data).map(format_chat, remove_columns=['messages'])\n",
|
| 263 |
+
"eval_ds = Dataset.from_list(eval_data).map(format_chat, remove_columns=['messages'])\n",
|
| 264 |
+
"\n",
|
| 265 |
+
"# Check token lengths\n",
|
| 266 |
+
"sample_tokens = len(tokenizer.encode(train_ds[0]['text']))\n",
|
| 267 |
+
"print(f'Sample length: {sample_tokens} tokens')\n",
|
| 268 |
+
"print(f'Train: {len(train_ds)}, Eval: {len(eval_ds)}')"
|
| 269 |
+
]
|
| 270 |
+
},
|
| 271 |
+
{
|
| 272 |
+
"cell_type": "code",
|
| 273 |
+
"execution_count": null,
|
| 274 |
+
"metadata": {},
|
| 275 |
+
"outputs": [],
|
| 276 |
+
"source": [
|
| 277 |
+
"# Step 5: Train!\n",
|
| 278 |
+
"from trl import SFTTrainer, SFTConfig\n",
|
| 279 |
+
"\n",
|
| 280 |
+
"OUTPUT_DIR = '/content/pulse-model'\n",
|
| 281 |
+
"\n",
|
| 282 |
+
"training_args = SFTConfig(\n",
|
| 283 |
+
" output_dir=OUTPUT_DIR,\n",
|
| 284 |
+
" num_train_epochs=3,\n",
|
| 285 |
+
" per_device_train_batch_size=4,\n",
|
| 286 |
+
" gradient_accumulation_steps=4,\n",
|
| 287 |
+
" learning_rate=2e-4,\n",
|
| 288 |
+
" lr_scheduler_type='cosine',\n",
|
| 289 |
+
" warmup_ratio=0.1,\n",
|
| 290 |
+
" logging_steps=10,\n",
|
| 291 |
+
" save_strategy='epoch',\n",
|
| 292 |
+
" eval_strategy='epoch',\n",
|
| 293 |
+
" bf16=True,\n",
|
| 294 |
+
" max_length=512,\n",
|
| 295 |
+
" dataset_text_field='text',\n",
|
| 296 |
+
" report_to='none',\n",
|
| 297 |
+
" seed=42,\n",
|
| 298 |
+
")\n",
|
| 299 |
+
"\n",
|
| 300 |
+
"trainer = SFTTrainer(\n",
|
| 301 |
+
" model=model,\n",
|
| 302 |
+
" args=training_args,\n",
|
| 303 |
+
" train_dataset=train_ds,\n",
|
| 304 |
+
" eval_dataset=eval_ds,\n",
|
| 305 |
+
" processing_class=tokenizer,\n",
|
| 306 |
+
")\n",
|
| 307 |
+
"\n",
|
| 308 |
+
"print('Training...')\n",
|
| 309 |
+
"trainer.train()\n",
|
| 310 |
+
"print('Done!')"
|
| 311 |
+
]
|
| 312 |
+
},
|
| 313 |
+
{
|
| 314 |
+
"cell_type": "code",
|
| 315 |
+
"execution_count": null,
|
| 316 |
+
"metadata": {},
|
| 317 |
+
"outputs": [],
|
| 318 |
+
"source": [
|
| 319 |
+
"# Step 6: Save + Upload to HF Hub\n",
|
| 320 |
+
"from huggingface_hub import HfApi\n",
|
| 321 |
+
"\n",
|
| 322 |
+
"HF_REPO = 'lalopenguin/pulse-qwen-1.5b' # Change to your repo\n",
|
| 323 |
+
"\n",
|
| 324 |
+
"# Save locally\n",
|
| 325 |
+
"trainer.save_model(OUTPUT_DIR)\n",
|
| 326 |
+
"tokenizer.save_pretrained(OUTPUT_DIR)\n",
|
| 327 |
+
"\n",
|
| 328 |
+
"# Save PULSE metadata\n",
|
| 329 |
+
"meta = {\n",
|
| 330 |
+
" 'base_model': MODEL_NAME,\n",
|
| 331 |
+
" 'pulse_version': '0.1.0',\n",
|
| 332 |
+
" 'training_type': 'temporal_awareness_sft',\n",
|
| 333 |
+
" 'lora_r': 16,\n",
|
| 334 |
+
" 'epochs': 3,\n",
|
| 335 |
+
" 'train_examples': len(train_data),\n",
|
| 336 |
+
"}\n",
|
| 337 |
+
"with open(f'{OUTPUT_DIR}/pulse_config.json', 'w') as f:\n",
|
| 338 |
+
" json.dump(meta, f, indent=2)\n",
|
| 339 |
+
"\n",
|
| 340 |
+
"# Upload\n",
|
| 341 |
+
"api = HfApi()\n",
|
| 342 |
+
"api.create_repo(HF_REPO, exist_ok=True)\n",
|
| 343 |
+
"api.upload_folder(folder_path=OUTPUT_DIR, repo_id=HF_REPO, commit_message='PULSE temporal awareness LoRA')\n",
|
| 344 |
+
"print(f'\\nUploaded to https://huggingface.co/{HF_REPO}')"
|
| 345 |
+
]
|
| 346 |
+
},
|
| 347 |
+
{
|
| 348 |
+
"cell_type": "code",
|
| 349 |
+
"execution_count": null,
|
| 350 |
+
"metadata": {},
|
| 351 |
+
"outputs": [],
|
| 352 |
+
"source": [
|
| 353 |
+
"# Step 7: Quick test\n",
|
| 354 |
+
"from peft import PeftModel\n",
|
| 355 |
+
"\n",
|
| 356 |
+
"# Reload clean\n",
|
| 357 |
+
"base = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.bfloat16, trust_remote_code=True)\n",
|
| 358 |
+
"model = PeftModel.from_pretrained(base, OUTPUT_DIR).to('cuda')\n",
|
| 359 |
+
"model.eval()\n",
|
| 360 |
+
"\n",
|
| 361 |
+
"messages = [\n",
|
| 362 |
+
" {'role': 'system', 'content': SYSTEM_TEMPLATE.format(temporal_context=\"\"\"Current time: Monday, April 13 2026 at 02:00 PM\n",
|
| 363 |
+
"Circadian phase: afternoon_peak\n",
|
| 364 |
+
"Cognitive capacity: 68%\n",
|
| 365 |
+
"Energy level: 65%\n",
|
| 366 |
+
"Urgency: deadline in 3.0h\n",
|
| 367 |
+
"Events today: 6\n",
|
| 368 |
+
"Sleep last night: 5.0 hours\"\"\")},\n",
|
| 369 |
+
" {'role': 'user', 'content': 'Should I start a complex refactoring task right now?'},\n",
|
| 370 |
+
"]\n",
|
| 371 |
+
"\n",
|
| 372 |
+
"inputs = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors='pt').to('cuda')\n",
|
| 373 |
+
"with torch.no_grad():\n",
|
| 374 |
+
" out = model.generate(inputs, max_new_tokens=200, temperature=0.7, do_sample=True)\n",
|
| 375 |
+
"\n",
|
| 376 |
+
"response = tokenizer.decode(out[0][inputs.shape[1]:], skip_special_tokens=True)\n",
|
| 377 |
+
"print(f'Q: Should I start a complex refactoring task right now?\\n\\nA: {response}')"
|
| 378 |
+
]
|
| 379 |
+
}
|
| 380 |
+
],
|
| 381 |
+
"metadata": {
|
| 382 |
+
"accelerator": "GPU",
|
| 383 |
+
"colab": {
|
| 384 |
+
"gpuType": "T4",
|
| 385 |
+
"provenance": []
|
| 386 |
+
},
|
| 387 |
+
"kernelspec": {
|
| 388 |
+
"display_name": "Python 3",
|
| 389 |
+
"name": "python3"
|
| 390 |
+
},
|
| 391 |
+
"language_info": {
|
| 392 |
+
"name": "python",
|
| 393 |
+
"version": "3.10.0"
|
| 394 |
+
}
|
| 395 |
+
},
|
| 396 |
+
"nbformat": 4,
|
| 397 |
+
"nbformat_minor": 0
|
| 398 |
+
}
|