Tyr-123 commited on
Commit
6bb92ee
·
1 Parent(s): 8b275fc

Add LFS tracking for binary files (PNG, PDF, ipynb)

Browse files
Files changed (2) hide show
  1. .gitattributes +1 -0
  2. train_colab.ipynb +3 -438
.gitattributes CHANGED
@@ -1,5 +1,6 @@
1
  *.png filter=lfs diff=lfs merge=lfs -text
2
  *.pdf filter=lfs diff=lfs merge=lfs -text
 
3
  *.jpg filter=lfs diff=lfs merge=lfs -text
4
  *.jpeg filter=lfs diff=lfs merge=lfs -text
5
  *.gif filter=lfs diff=lfs merge=lfs -text
 
1
  *.png filter=lfs diff=lfs merge=lfs -text
2
  *.pdf filter=lfs diff=lfs merge=lfs -text
3
+ *.ipynb filter=lfs diff=lfs merge=lfs -text
4
  *.jpg filter=lfs diff=lfs merge=lfs -text
5
  *.jpeg filter=lfs diff=lfs merge=lfs -text
6
  *.gif filter=lfs diff=lfs merge=lfs -text
train_colab.ipynb CHANGED
@@ -1,438 +1,3 @@
1
- {
2
- "nbformat": 4,
3
- "nbformat_minor": 0,
4
- "metadata": {
5
- "colab": {
6
- "provenance": [],
7
- "gpuType": "T4"
8
- },
9
- "kernelspec": {
10
- "name": "python3",
11
- "display_name": "Python 3"
12
- },
13
- "language_info": {
14
- "name": "python"
15
- },
16
- "accelerator": "GPU"
17
- },
18
- "cells": [
19
- {
20
- "cell_type": "markdown",
21
- "metadata": {},
22
- "source": [
23
- "# 🏛️ SocialContract-v0 — GRPO Training Notebook\n",
24
- "\n",
25
- "**Environment:** Fiscal Policy Advisory (OpenEnv-compliant) \n",
26
- "**Training Method:** GRPO (Group Relative Policy Optimization) via TRL \n",
27
- "**Efficient Fine-tuning:** Unsloth (4-bit LoRA) \n",
28
- "**Model:** Qwen2.5-1.5B-Instruct \n",
29
- "\n",
30
- "This notebook trains an LLM to be an IMF-grade economic policy advisor using\n",
31
- "reinforcement learning from the SocialContract-v0 environment rewards."
32
- ]
33
- },
34
- {
35
- "cell_type": "markdown",
36
- "metadata": {},
37
- "source": [
38
- "## 1. Install Dependencies"
39
- ]
40
- },
41
- {
42
- "cell_type": "code",
43
- "execution_count": null,
44
- "metadata": {},
45
- "outputs": [],
46
- "source": [
47
- "%%capture\n",
48
- "!pip install unsloth trl datasets transformers accelerate pydantic numpy matplotlib\n",
49
- "!pip install --no-deps xformers\n",
50
- "print('Dependencies installed!')"
51
- ]
52
- },
53
- {
54
- "cell_type": "markdown",
55
- "metadata": {},
56
- "source": [
57
- "## 2. Clone the Environment"
58
- ]
59
- },
60
- {
61
- "cell_type": "code",
62
- "execution_count": null,
63
- "metadata": {},
64
- "outputs": [],
65
- "source": [
66
- "# Clone the SocialContract-v0 environment repo\n",
67
- "!git clone https://huggingface.co/spaces/Tyr-123/SocialContract-v0 /content/socialcontract\n",
68
- "import sys\n",
69
- "sys.path.insert(0, '/content/socialcontract')\n",
70
- "\n",
71
- "# Verify environment works\n",
72
- "from env.openenv_wrapper import SocialContractOpenEnv\n",
73
- "from env.pydantic_models import PolicyAction\n",
74
- "from graders.graders import run_grader\n",
75
- "\n",
76
- "env = SocialContractOpenEnv('task1_stability')\n",
77
- "obs = env.reset()\n",
78
- "print(f'✅ Environment loaded! GDP={obs.gdp:.1f}, Gini={obs.gini:.3f}')"
79
- ]
80
- },
81
- {
82
- "cell_type": "markdown",
83
- "metadata": {},
84
- "source": [
85
- "## 3. Quick Baseline Evaluation"
86
- ]
87
- },
88
- {
89
- "cell_type": "code",
90
- "execution_count": null,
91
- "metadata": {},
92
- "outputs": [],
93
- "source": [
94
- "import numpy as np\n",
95
- "\n",
96
- "# Random baseline\n",
97
- "def run_random_episode(task_id, seed=42):\n",
98
- " rng = np.random.default_rng(seed)\n",
99
- " env = SocialContractOpenEnv(task_id, seed=seed)\n",
100
- " env.reset()\n",
101
- " while not env.is_done:\n",
102
- " action = PolicyAction(\n",
103
- " tax_delta=float(rng.uniform(-0.05, 0.05)),\n",
104
- " ubi_delta=float(rng.uniform(-5.0, 5.0)),\n",
105
- " public_good_delta=float(rng.uniform(-0.05, 0.05)),\n",
106
- " interest_rate_delta=float(rng.uniform(-0.02, 0.02)),\n",
107
- " stimulus_package=float(rng.uniform(0, 100)),\n",
108
- " import_tariff_delta=float(rng.uniform(-0.03, 0.03)),\n",
109
- " money_supply_delta=float(rng.uniform(-100, 100)),\n",
110
- " minimum_wage_delta=float(rng.uniform(-1, 1)),\n",
111
- " reasoning='random',\n",
112
- " )\n",
113
- " env.step(action)\n",
114
- " return run_grader(task_id, env._history)\n",
115
- "\n",
116
- "tasks = ['task1_stability', 'task2_recession', 'task4_stagflation']\n",
117
- "print('Random Baseline Scores:')\n",
118
- "for task in tasks:\n",
119
- " grade = run_random_episode(task)\n",
120
- " print(f' {task}: {grade[\"score\"]:.4f} — {grade[\"verdict\"]}')"
121
- ]
122
- },
123
- {
124
- "cell_type": "markdown",
125
- "metadata": {},
126
- "source": [
127
- "## 4. Load Model with Unsloth"
128
- ]
129
- },
130
- {
131
- "cell_type": "code",
132
- "execution_count": null,
133
- "metadata": {},
134
- "outputs": [],
135
- "source": [
136
- "from unsloth import FastLanguageModel\n",
137
- "import torch\n",
138
- "\n",
139
- "model, tokenizer = FastLanguageModel.from_pretrained(\n",
140
- " model_name='unsloth/Qwen2.5-1.5B-Instruct',\n",
141
- " max_seq_length=2048,\n",
142
- " dtype=None,\n",
143
- " load_in_4bit=True,\n",
144
- ")\n",
145
- "\n",
146
- "model = FastLanguageModel.get_peft_model(\n",
147
- " model,\n",
148
- " r=16,\n",
149
- " target_modules=['q_proj', 'k_proj', 'v_proj', 'o_proj',\n",
150
- " 'gate_proj', 'up_proj', 'down_proj'],\n",
151
- " lora_alpha=32,\n",
152
- " lora_dropout=0.0,\n",
153
- " bias='none',\n",
154
- " use_gradient_checkpointing='unsloth',\n",
155
- ")\n",
156
- "\n",
157
- "if tokenizer.pad_token is None:\n",
158
- " tokenizer.pad_token = tokenizer.eos_token\n",
159
- "\n",
160
- "print(f'✅ Model loaded: Qwen2.5-1.5B-Instruct with LoRA (r=16)')"
161
- ]
162
- },
163
- {
164
- "cell_type": "markdown",
165
- "metadata": {},
166
- "source": [
167
- "## 5. Generate Training Prompts"
168
- ]
169
- },
170
- {
171
- "cell_type": "code",
172
- "execution_count": null,
173
- "metadata": {},
174
- "outputs": [],
175
- "source": [
176
- "import random\n",
177
- "from datasets import Dataset\n",
178
- "\n",
179
- "SYSTEM_PROMPT = (\n",
180
- " 'You are an expert economic policy advisor. You make precise, data-driven '\n",
181
- " 'decisions to optimize economic outcomes across multiple competing objectives. '\n",
182
- " 'You must respond ONLY with valid JSON matching the exact format requested. '\n",
183
- " 'No markdown, no extra text — only the JSON object.'\n",
184
- ")\n",
185
- "\n",
186
- "def generate_prompts(n=80):\n",
187
- " prompts = []\n",
188
- " for task_id in tasks:\n",
189
- " for seed in range(1, n // len(tasks) + 2):\n",
190
- " if len(prompts) >= n:\n",
191
- " break\n",
192
- " env = SocialContractOpenEnv(task_id, seed=seed)\n",
193
- " obs = env.reset()\n",
194
- " warmup = random.randint(0, env.task_cfg['max_steps'] // 3)\n",
195
- " rng = np.random.default_rng(seed)\n",
196
- " for _ in range(warmup):\n",
197
- " action = PolicyAction(\n",
198
- " tax_delta=float(rng.uniform(-0.02, 0.02)),\n",
199
- " ubi_delta=float(rng.uniform(-1, 1)),\n",
200
- " public_good_delta=float(rng.uniform(-0.02, 0.02)),\n",
201
- " interest_rate_delta=float(rng.uniform(-0.01, 0.01)),\n",
202
- " stimulus_package=float(rng.uniform(0, 30)),\n",
203
- " import_tariff_delta=0.0,\n",
204
- " money_supply_delta=float(rng.uniform(-30, 30)),\n",
205
- " minimum_wage_delta=0.0,\n",
206
- " reasoning='warmup',\n",
207
- " )\n",
208
- " obs, _, done, _ = env.step(action)\n",
209
- " if done: break\n",
210
- " if not env.is_done:\n",
211
- " prompts.append({\n",
212
- " 'messages': [\n",
213
- " {'role': 'system', 'content': SYSTEM_PROMPT},\n",
214
- " {'role': 'user', 'content': obs.to_prompt()},\n",
215
- " ],\n",
216
- " 'task_id': task_id,\n",
217
- " 'seed': seed,\n",
218
- " 'warmup_steps': warmup,\n",
219
- " })\n",
220
- " random.shuffle(prompts)\n",
221
- " return prompts[:n]\n",
222
- "\n",
223
- "prompt_data = generate_prompts(80)\n",
224
- "dataset = Dataset.from_list(prompt_data)\n",
225
- "print(f'✅ Generated {len(dataset)} training prompts')"
226
- ]
227
- },
228
- {
229
- "cell_type": "markdown",
230
- "metadata": {},
231
- "source": [
232
- "## 6. Define Reward Function"
233
- ]
234
- },
235
- {
236
- "cell_type": "code",
237
- "execution_count": null,
238
- "metadata": {},
239
- "outputs": [],
240
- "source": [
241
- "import re, json\n",
242
- "\n",
243
- "def parse_action(text):\n",
244
- " try:\n",
245
- " if '```' in text:\n",
246
- " match = re.search(r'```(?:json)?\\s*(.*?)```', text, re.DOTALL)\n",
247
- " if match: text = match.group(1)\n",
248
- " match = re.search(r'\\{[^{}]*\\}', text, re.DOTALL)\n",
249
- " if match: text = match.group(0)\n",
250
- " parsed = json.loads(text)\n",
251
- " return PolicyAction(\n",
252
- " tax_delta=float(np.clip(parsed.get('tax_delta', 0), -0.1, 0.1)),\n",
253
- " ubi_delta=float(np.clip(parsed.get('ubi_delta', 0), -10, 10)),\n",
254
- " public_good_delta=float(np.clip(parsed.get('public_good_delta', 0), -0.1, 0.1)),\n",
255
- " interest_rate_delta=float(np.clip(parsed.get('interest_rate_delta', 0), -0.03, 0.03)),\n",
256
- " stimulus_package=float(np.clip(parsed.get('stimulus_package', 0), 0, 500)),\n",
257
- " import_tariff_delta=float(np.clip(parsed.get('import_tariff_delta', 0), -0.05, 0.05)),\n",
258
- " money_supply_delta=float(np.clip(parsed.get('money_supply_delta', 0), -500, 500)),\n",
259
- " minimum_wage_delta=float(np.clip(parsed.get('minimum_wage_delta', 0), -2, 2)),\n",
260
- " reasoning=str(parsed.get('reasoning', ''))[:200],\n",
261
- " )\n",
262
- " except:\n",
263
- " return None\n",
264
- "\n",
265
- "def env_reward(completion, task_id, seed, warmup_steps):\n",
266
- " action = parse_action(completion)\n",
267
- " if action is None:\n",
268
- " return 0.0\n",
269
- " env = SocialContractOpenEnv(task_id, seed=seed)\n",
270
- " obs = env.reset()\n",
271
- " rng = np.random.default_rng(seed)\n",
272
- " for _ in range(warmup_steps):\n",
273
- " wa = PolicyAction(\n",
274
- " tax_delta=float(rng.uniform(-0.02, 0.02)),\n",
275
- " ubi_delta=float(rng.uniform(-1, 1)),\n",
276
- " public_good_delta=float(rng.uniform(-0.02, 0.02)),\n",
277
- " interest_rate_delta=float(rng.uniform(-0.01, 0.01)),\n",
278
- " stimulus_package=float(rng.uniform(0, 30)),\n",
279
- " import_tariff_delta=0.0,\n",
280
- " money_supply_delta=float(rng.uniform(-30, 30)),\n",
281
- " minimum_wage_delta=0.0,\n",
282
- " reasoning='warmup',\n",
283
- " )\n",
284
- " obs, _, done, _ = env.step(wa)\n",
285
- " if done: return 0.0\n",
286
- " obs, reward, done, _ = env.step(action)\n",
287
- " # Run remaining with a follow-through\n",
288
- " while not env.is_done:\n",
289
- " heur = PolicyAction(\n",
290
- " tax_delta=action.tax_delta * 0.5,\n",
291
- " ubi_delta=action.ubi_delta * 0.3,\n",
292
- " public_good_delta=action.public_good_delta * 0.5,\n",
293
- " interest_rate_delta=action.interest_rate_delta * 0.3,\n",
294
- " stimulus_package=0.0, import_tariff_delta=0.0,\n",
295
- " money_supply_delta=action.money_supply_delta * 0.2,\n",
296
- " minimum_wage_delta=action.minimum_wage_delta * 0.3,\n",
297
- " reasoning='followthrough',\n",
298
- " )\n",
299
- " obs, _, done, _ = env.step(heur)\n",
300
- " return run_grader(task_id, env._history)['score']\n",
301
- "\n",
302
- "def reward_function(completions, **kwargs):\n",
303
- " rewards = []\n",
304
- " task_ids = kwargs.get('task_id', ['task1_stability'] * len(completions))\n",
305
- " seeds = kwargs.get('seed', [42] * len(completions))\n",
306
- " warmups = kwargs.get('warmup_steps', [0] * len(completions))\n",
307
- " for i, c in enumerate(completions):\n",
308
- " text = c[-1].get('content', '') if isinstance(c, list) else str(c)\n",
309
- " r = env_reward(text,\n",
310
- " task_ids[i] if i < len(task_ids) else 'task1_stability',\n",
311
- " seeds[i] if i < len(seeds) else 42,\n",
312
- " warmups[i] if i < len(warmups) else 0)\n",
313
- " rewards.append(r)\n",
314
- " return rewards\n",
315
- "\n",
316
- "print('✅ Reward function defined')"
317
- ]
318
- },
319
- {
320
- "cell_type": "markdown",
321
- "metadata": {},
322
- "source": [
323
- "## 7. Train with GRPO"
324
- ]
325
- },
326
- {
327
- "cell_type": "code",
328
- "execution_count": null,
329
- "metadata": {},
330
- "outputs": [],
331
- "source": [
332
- "from trl import GRPOTrainer, GRPOConfig\n",
333
- "\n",
334
- "training_args = GRPOConfig(\n",
335
- " output_dir='./socialcontract-grpo',\n",
336
- " num_train_epochs=2,\n",
337
- " per_device_train_batch_size=2,\n",
338
- " gradient_accumulation_steps=4,\n",
339
- " learning_rate=5e-5,\n",
340
- " max_completion_length=256,\n",
341
- " num_generations=4,\n",
342
- " logging_steps=5,\n",
343
- " save_steps=50,\n",
344
- " max_steps=150,\n",
345
- " report_to='none',\n",
346
- " bf16=torch.cuda.is_available(),\n",
347
- " temperature=0.7,\n",
348
- ")\n",
349
- "\n",
350
- "trainer = GRPOTrainer(\n",
351
- " model=model,\n",
352
- " args=training_args,\n",
353
- " train_dataset=dataset,\n",
354
- " processing_class=tokenizer,\n",
355
- " reward_funcs=[reward_function],\n",
356
- ")\n",
357
- "\n",
358
- "print('🚀 Starting GRPO training...')\n",
359
- "result = trainer.train()\n",
360
- "print(f'✅ Training complete! Steps: {result.global_step}')"
361
- ]
362
- },
363
- {
364
- "cell_type": "markdown",
365
- "metadata": {},
366
- "source": [
367
- "## 8. Post-Training Evaluation"
368
- ]
369
- },
370
- {
371
- "cell_type": "code",
372
- "execution_count": null,
373
- "metadata": {},
374
- "outputs": [],
375
- "source": [
376
- "def evaluate_trained_model(task_id, seed=42):\n",
377
- " env = SocialContractOpenEnv(task_id, seed=seed)\n",
378
- " obs = env.reset()\n",
379
- " while not env.is_done:\n",
380
- " prompt = obs.to_prompt()\n",
381
- " messages = [\n",
382
- " {'role': 'system', 'content': SYSTEM_PROMPT},\n",
383
- " {'role': 'user', 'content': prompt},\n",
384
- " ]\n",
385
- " inputs = tokenizer.apply_chat_template(\n",
386
- " messages, return_tensors='pt', add_generation_prompt=True\n",
387
- " ).to(model.device)\n",
388
- " with torch.no_grad():\n",
389
- " outputs = model.generate(\n",
390
- " inputs, max_new_tokens=256, temperature=0.1,\n",
391
- " do_sample=True, pad_token_id=tokenizer.eos_token_id,\n",
392
- " )\n",
393
- " resp = tokenizer.decode(outputs[0][inputs.shape[1]:], skip_special_tokens=True)\n",
394
- " action = parse_action(resp)\n",
395
- " if action is None:\n",
396
- " action = PolicyAction(\n",
397
- " tax_delta=0.0, ubi_delta=0.0, public_good_delta=0.01,\n",
398
- " interest_rate_delta=0.0, stimulus_package=0.0,\n",
399
- " import_tariff_delta=0.0, money_supply_delta=0.0,\n",
400
- " minimum_wage_delta=0.0, reasoning='parse failure',\n",
401
- " )\n",
402
- " obs, _, _, _ = env.step(action)\n",
403
- " return run_grader(task_id, env._history)\n",
404
- "\n",
405
- "print('Post-Training Scores:')\n",
406
- "for task in tasks:\n",
407
- " grade = evaluate_trained_model(task)\n",
408
- " print(f' {task}: {grade[\"score\"]:.4f} — {grade[\"verdict\"]}')"
409
- ]
410
- },
411
- {
412
- "cell_type": "markdown",
413
- "metadata": {},
414
- "source": [
415
- "## 9. Save & Push to HuggingFace Hub"
416
- ]
417
- },
418
- {
419
- "cell_type": "code",
420
- "execution_count": null,
421
- "metadata": {},
422
- "outputs": [],
423
- "source": [
424
- "# Save locally\n",
425
- "model.save_pretrained('./socialcontract-grpo-final')\n",
426
- "tokenizer.save_pretrained('./socialcontract-grpo-final')\n",
427
- "\n",
428
- "# Optionally push to HuggingFace Hub\n",
429
- "# from huggingface_hub import login\n",
430
- "# login(token='your-hf-token')\n",
431
- "# model.push_to_hub('your-username/socialcontract-policy-v0')\n",
432
- "# tokenizer.push_to_hub('your-username/socialcontract-policy-v0')\n",
433
- "\n",
434
- "print('✅ Model saved!')"
435
- ]
436
- }
437
- ]
438
- }
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b5d1cb03f0ec786177271c5863cde41bb9c0c693b9154258eb9619830f0d1315
3
+ size 17145