Laura Wagner commited on
Commit
0f34c19
·
1 Parent(s): d162b65

added looging to bloom query

Browse files
jupyter_notebooks/Section_2-3-4_Bloomz_query.ipynb CHANGED
@@ -7,342 +7,408 @@
7
  "metadata": {},
8
  "outputs": [],
9
  "source": [
10
- "import pandas as pd\n",
11
- "import json\n",
12
- "import time\n",
13
- "import re\n",
14
- "from pathlib import Path\n",
15
- "from tqdm import tqdm\n",
16
- "import torch\n",
17
- "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
18
- "\n",
19
- "# Import is used for pd.notna() and pd.isna() checks\n",
20
- "\n",
21
- "current_dir = Path.cwd()\n",
22
- "input_file = current_dir.parent / \"data/CSV/model_adapter/real_person_adapter_step_02_NER.csv\"\n",
23
- "\n",
24
- "# === CONFIGURATION ===\n",
25
- "TEST_MODE = True\n",
26
- "TEST_SIZE = 10\n",
27
- "MAX_ROWS = 20000\n",
28
- "SAVE_INTERVAL = 10\n",
29
- "\n",
30
- "# Model settings - BLOOMZ (BigScience - European consortium)\n",
31
- "MODEL_NAME = \"bigscience/bloomz-7b1\" # Largest instruction-tuned BLOOM model\n",
32
- "CACHE_DIR = current_dir.parent / \"data/models\"\n",
33
- "CACHE_DIR.mkdir(parents=True, exist_ok=True)\n",
34
- "\n",
35
- "PROFESSION_CATEGORIES = [\n",
36
- " \"actor\", \"adult performer\", \"singer/musician\", \"model\",\n",
37
- " \"online personality\", \"public figure\", \"voice actor/ASMR\",\n",
38
- " \"sports professional\", \"tv personality\"\n",
39
- "]\n",
40
- "\n",
41
- "# === LOAD MODEL ===\n",
42
- "print(f\"Loading model: {MODEL_NAME}\")\n",
43
- "print(f\"Cache directory: {CACHE_DIR}\")\n",
44
- "print(f\"This may take a while on first run (~14GB download)...\\n\")\n",
45
- "\n",
46
- "# Check GPU availability\n",
47
- "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
48
- "print(f\"Device: {device}\")\n",
49
- "\n",
50
- "if device == \"cpu\":\n",
51
- " print(\"⚠️ WARNING: No GPU detected! Inference will be VERY slow.\")\n",
52
- " print(\" Consider using a GPU or reducing model size.\")\n",
53
- "\n",
54
- "# Load tokenizer\n",
55
- "print(\"Loading tokenizer...\")\n",
56
- "try:\n",
57
- " tokenizer = AutoTokenizer.from_pretrained(\n",
58
- " MODEL_NAME,\n",
59
- " cache_dir=str(CACHE_DIR)\n",
60
- " )\n",
61
- " print(\"✅ Tokenizer loaded\")\n",
62
- "except Exception as e:\n",
63
- " print(f\"❌ Error loading tokenizer: {e}\")\n",
64
- " raise\n",
65
- "\n",
66
- "# Ensure pad token is set\n",
67
- "if tokenizer.pad_token is None:\n",
68
- " tokenizer.pad_token = tokenizer.eos_token\n",
69
- " print(f\"Set pad_token to eos_token: {tokenizer.eos_token}\")\n",
70
- "\n",
71
- "# Load model with optimizations\n",
72
- "print(\"Loading model (this may take several minutes)...\")\n",
73
- "try:\n",
74
- " model = AutoModelForCausalLM.from_pretrained(\n",
75
- " MODEL_NAME,\n",
76
- " cache_dir=str(CACHE_DIR),\n",
77
- " torch_dtype=torch.bfloat16, # Use BF16 for efficiency\n",
78
- " device_map=\"auto\", # Automatically distribute across GPUs\n",
79
- " low_cpu_mem_usage=True # Optimize memory usage\n",
80
- " )\n",
81
- " model.eval() # Set to evaluation mode\n",
82
- " print(\"✅ Model loaded\")\n",
83
- "except Exception as e:\n",
84
- " print(f\"❌ Error loading model: {e}\")\n",
85
- " raise\n",
86
- "\n",
87
- "# Check VRAM usage\n",
88
- "if torch.cuda.is_available():\n",
89
- " vram_gb = torch.cuda.max_memory_allocated() / 1024**3\n",
90
- " print(f\"VRAM used: {vram_gb:.2f} GB\\n\")\n",
91
- "\n",
92
- "# === LOAD DATA ===\n",
93
- "df = pd.read_csv(input_file)\n",
94
- "print(f\"Loaded {len(df)} rows\")\n",
95
- "\n",
96
- "if TEST_MODE:\n",
97
- " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")\n",
98
- " df = df.head(TEST_SIZE).copy()\n",
99
- "elif MAX_ROWS:\n",
100
- " df = df.head(MAX_ROWS).copy()\n",
101
- "\n",
102
- "# === CREATE PROMPT (Exact DeepSeek style) ===\n",
103
- "def create_prompt(row):\n",
104
- " \"\"\"Create prompt.\"\"\"\n",
105
- " name = row.get('real_name', row.get('name', ''))\n",
106
- " if pd.isna(name):\n",
107
- " name = row.get('name', '')\n",
108
- " \n",
109
- " # Gather hints exactly like DeepSeek version\n",
110
- " hints = []\n",
111
- " if pd.notna(row.get('likely_profession')):\n",
112
- " hints.append(str(row['likely_profession']))\n",
113
- " if pd.notna(row.get('likely_nationality')):\n",
114
- " hints.append(str(row['likely_nationality']))\n",
115
- " if pd.notna(row.get('likely_country')):\n",
116
- " hints.append(str(row['likely_country']))\n",
117
- " \n",
118
- " # Add tags if we don't have enough hints\n",
119
- " if len(hints) < 3:\n",
120
- " for i in range(1, 8):\n",
121
- " tag_col = f'tag_{i}'\n",
122
- " if tag_col in row and pd.notna(row[tag_col]):\n",
123
- " tag_val = str(row[tag_col])\n",
124
- " if tag_val not in hints:\n",
125
- " hints.append(tag_val)\n",
126
- " if len(hints) >= 5:\n",
127
- " break\n",
128
- " \n",
129
- " hint_text = \", \".join(hints[:5]) if hints else \"none\"\n",
130
- " \n",
131
- " return f\"\"\"Given '{name}' ({hint_text}), provide:\n",
132
- "1. Full legal name (Western order if non-latin script)\n",
133
- "2. Any stage names/aliases (comma separated)\n",
134
- "3. Gender (Male/Female/Other/Unknown)\n",
135
- "4. Top 3 most likely professions from ONLY these categories:\n",
136
- " - actor\n",
137
- " - adult performer\n",
138
- " - singer/musician\n",
139
- " - model\n",
140
- " - online personality (includes streamers, cosplayers, influencers)\n",
141
- " - public figure (includes politicians, activists, journalists, authors)\n",
142
- " - voice actor/ASMR\n",
143
- " - sports professional\n",
144
- " - tv personality (includes hosts, presenters, reality TV)\n",
145
- "\n",
146
- "5. Primary country associated\n",
147
- "\n",
148
- "IMPORTANT:\n",
149
- "- Choose professions ONLY from the 9 categories above\n",
150
- "- Provide up to 3 professions, comma-separated, ordered by relevance\n",
151
- "- Be SPECIFIC: choose the most accurate category for each role\n",
152
- "- \"online personality\" includes: streamers, cosplayers, YouTubers, influencers, content creators\n",
153
- "- Use 'Unknown' when uncertain or for fictional characters/places\n",
154
- "- For multi-role people, list all relevant categories (e.g., \"actor, singer/musician, online personality\")\n",
155
- "\n",
156
- "Respond with exactly 5 numbered lines.\"\"\"\n",
157
- "\n",
158
- "df['prompt'] = df.apply(create_prompt, axis=1)\n",
159
- "\n",
160
- "# === QUERY BLOOMZ LOCAL ===\n",
161
- "def query_bloomz_local(prompt: str) -> str:\n",
162
- " \"\"\"Query BLOOMZ-7B1 locally via transformers, return raw response string.\"\"\"\n",
163
- " try:\n",
164
- " # BLOOMZ works better with instruction-response format\n",
165
- " full_prompt = f\"\"\"Instruction: Extract key data on a person based on the name and hints.\n",
166
- "You must respond with exactly 5 numbered lines in this format:\n",
167
- "1. Full legal name\n",
168
- "2. Stage names/aliases \n",
169
- "3. Gender\n",
170
- "4. Professions (comma-separated, choose ONLY from: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality)\n",
171
- "5. Country\n",
172
- "\n",
173
- "{prompt}\n",
174
- "\n",
175
- "Response:\"\"\"\n",
176
- " \n",
177
- " inputs = tokenizer(\n",
178
- " full_prompt, \n",
179
- " return_tensors=\"pt\", \n",
180
- " truncation=True,\n",
181
- " max_length=2048\n",
182
- " ).to(device)\n",
183
- " \n",
184
- " # Generate with adjusted parameters for BLOOMZ\n",
185
- " with torch.no_grad():\n",
186
- " outputs = model.generate(\n",
187
- " **inputs,\n",
188
- " max_new_tokens=256,\n",
189
- " temperature=0.3, # Increased for more variability\n",
190
- " do_sample=True,\n",
191
- " top_p=0.9,\n",
192
- " top_k=40,\n",
193
- " repetition_penalty=1.1,\n",
194
- " pad_token_id=tokenizer.eos_token_id, # Use EOS as pad token\n",
195
- " eos_token_id=tokenizer.eos_token_id,\n",
196
- " early_stopping=True\n",
197
- " )\n",
198
- " \n",
199
- " # Decode the entire output to see what's happening\n",
200
- " full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)\n",
201
- " \n",
202
- " # Extract only the generated part (after the prompt)\n",
203
- " generated_text = full_output[len(tokenizer.decode(inputs['input_ids'][0], skip_special_tokens=True)):]\n",
204
- " \n",
205
- " # Debug output\n",
206
- " if not hasattr(query_bloomz_local, 'debug_count'):\n",
207
- " query_bloomz_local.debug_count = 0\n",
208
- " \n",
209
- " if query_bloomz_local.debug_count < 3:\n",
210
- " print(f\"\\n📝 BLOOMZ Debug #{query_bloomz_local.debug_count + 1}:\")\n",
211
- " print(f\"Prompt: {full_prompt[:200]}...\")\n",
212
- " print(f\"Full output: {full_output[:500]}...\")\n",
213
- " print(f\"Generated text: {generated_text}\")\n",
214
- " print(f\"{'='*60}\\n\")\n",
215
- " query_bloomz_local.debug_count += 1\n",
216
- " \n",
217
- " return generated_text.strip()\n",
218
- " \n",
219
- " except Exception as e:\n",
220
- " print(f\"Error querying BLOOMZ: {e}\")\n",
221
- " return None\n",
222
- "\n",
223
- "# === PARSE RESPONSE (Exact DeepSeek format) ===\n",
224
- "def parse_response(response):\n",
225
- " \"\"\"Parse numbered response into structured fields.\"\"\"\n",
226
- " if not response:\n",
227
- " return {\n",
228
- " 'full_name': 'Unknown',\n",
229
- " 'aliases': 'Unknown',\n",
230
- " 'gender': 'Unknown',\n",
231
- " 'profession_llm': 'Unknown',\n",
232
- " 'country': 'Unknown'\n",
233
- " }\n",
234
- " \n",
235
- " # Split into lines and clean\n",
236
- " lines = [line.strip() for line in response.split('\\n') if line.strip()]\n",
237
- " \n",
238
- " # Initialize with Unknown values\n",
239
- " fields = {\n",
240
- " 'full_name': 'Unknown',\n",
241
- " 'aliases': 'Unknown',\n",
242
- " 'gender': 'Unknown',\n",
243
- " 'profession_llm': 'Unknown',\n",
244
- " 'country': 'Unknown'\n",
245
- " }\n",
246
- " \n",
247
- " # Extract information from each numbered line\n",
248
- " for line in lines:\n",
249
- " if line.startswith('1.') or line.startswith('1)'):\n",
250
- " fields['full_name'] = line[2:].strip()\n",
251
- " elif line.startswith('2.') or line.startswith('2)'):\n",
252
- " fields['aliases'] = line[2:].strip()\n",
253
- " elif line.startswith('3.') or line.startswith('3)'):\n",
254
- " fields['gender'] = line[2:].strip()\n",
255
- " elif line.startswith('4.') or line.startswith('4)'):\n",
256
- " fields['profession_llm'] = line[2:].strip()\n",
257
- " elif line.startswith('5.') or line.startswith('5)'):\n",
258
- " fields['country'] = line[2:].strip()\n",
259
- " \n",
260
- " return fields\n",
261
- "\n",
262
- "# === PROCESS ===\n",
263
- "output_file = current_dir.parent / f\"data/CSV/bloomz_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\n",
264
- "index_file = current_dir.parent / \"misc/bloomz_query_index.txt\"\n",
265
- "\n",
266
- "current_index = 0\n",
267
- "if index_file.exists():\n",
268
- " with open(index_file) as f:\n",
269
- " current_index = int(f.read().strip())\n",
270
- " print(f\"Resuming from index {current_index}\")\n",
271
- "\n",
272
- "# Initialize columns (same as DeepSeek)\n",
273
- "for col in ['full_name', 'gender', 'profession_llm', 'country', 'aliases']:\n",
274
- " if col not in df.columns:\n",
275
- " df[col] = 'Unknown'\n",
276
- "\n",
277
- "# Create prompts for all rows (same as DeepSeek)\n",
278
- "print(\"Creating prompts...\")\n",
279
- "df['prompt'] = df.apply(create_prompt, axis=1)\n",
280
- "\n",
281
- "print(f\"\\nAnnotating with BLOOMZ-7B1 LOCAL - rows {current_index} to {len(df)}...\")\n",
282
- "print(f\"Model: {MODEL_NAME}\")\n",
283
- "print(f\"This may take a while...\\n\")\n",
284
- "\n",
285
- "try:\n",
286
- " start_time = time.time()\n",
287
- " \n",
288
- " for i in tqdm(range(current_index, len(df)), desc=\"Annotating\"):\n",
289
- " row = df.iloc[i]\n",
290
- " \n",
291
- " # Query BLOOMZ (equivalent to DeepSeek query)\n",
292
- " response = query_bloomz_local(row['prompt'])\n",
293
- " parsed_data = parse_response(response)\n",
294
- " \n",
295
- " # Update dataframe\n",
296
- " for key, value in parsed_data.items():\n",
297
- " df.at[i, key] = value\n",
298
- " \n",
299
- " current_index = i + 1\n",
300
- " \n",
301
- " # Save progress at intervals\n",
302
- " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n",
303
- " df.to_csv(output_file, index=False)\n",
304
- " with open(index_file, 'w') as f:\n",
305
- " f.write(str(current_index))\n",
306
- " print(f\"✅ Progress saved after {i+1} rows\")\n",
307
- " \n",
308
- " # Optional: Add small delay to prevent overheating (not needed for rate limiting like DeepSeek)\n",
309
- " # time.sleep(0.1)\n",
310
- " \n",
311
- " elapsed_total = time.time() - start_time\n",
312
- " print(f\"\\n✅ Done! Final results saved to {output_file}\")\n",
313
- " \n",
314
- " # Summary statistics (same as DeepSeek)\n",
315
- " print(\"\\n=== Summary Statistics ===\")\n",
316
- " print(f\"Total processed: {len(df)}\")\n",
317
- " print(f\"\\nGender distribution:\")\n",
318
- " print(df['gender'].value_counts())\n",
319
- " print(f\"\\nTop 10 profession combinations:\")\n",
320
- " print(df['profession_llm'].value_counts().head(10))\n",
321
- " print(f\"\\nTop 10 countries:\")\n",
322
- " print(df['country'].value_counts().head(10))\n",
323
- " \n",
324
- " # Sample results\n",
325
- " print(\"\\n=== Sample Results ===\")\n",
326
- " display_cols = ['real_name', 'full_name', 'gender', 'profession_llm', 'country']\n",
327
- " available_cols = [col for col in display_cols if col in df.columns]\n",
328
- " print(df[available_cols].head(10).to_string(index=False))\n",
329
- " \n",
330
- " # Additional info for local model\n",
331
- " print(f\"\\nTotal time: {elapsed_total/60:.1f} minutes\")\n",
332
- " print(f\"Average speed: {len(df)/(elapsed_total/3600):.1f} samples/hour\")\n",
333
- " if torch.cuda.is_available():\n",
334
- " print(f\"Final VRAM usage: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB\")\n",
335
- "\n",
336
- "except Exception as e:\n",
337
- " print(f\"⚠️ Error encountered: {e}\")\n",
338
- " print(f\"⚠️ Last processed index: {current_index}\")\n",
339
- " \n",
340
- " # Save progress before exiting\n",
341
- " df.to_csv(output_file, index=False)\n",
342
- " with open(index_file, 'w') as f:\n",
343
- " f.write(str(current_index))\n",
344
- " \n",
345
- " print(f\"⚠️ Progress saved up to row {current_index}\")"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  ]
347
  }
348
  ],
@@ -367,4 +433,4 @@
367
  },
368
  "nbformat": 4,
369
  "nbformat_minor": 5
370
- }
 
7
  "metadata": {},
8
  "outputs": [],
9
  "source": [
10
+ "import pandas as pd",
11
+ "import json",
12
+ "import time",
13
+ "import re",
14
+ "from pathlib import Path",
15
+ "from tqdm import tqdm",
16
+ "import torch",
17
+ "from transformers import AutoModelForCausalLM, AutoTokenizer",
18
+ "from datetime import datetime",
19
+ "",
20
+ "# Import is used for pd.notna() and pd.isna() checks",
21
+ "",
22
+ "current_dir = Path.cwd())",
23
+ "input_file = current_dir.parent / \"data/CSV/model_adapter/real_person_adapter_step_02_NER.csv\"",
24
+ "",
25
+ "# === CONFIGURATION ===",
26
+ "TEST_MODE = True",
27
+ "TEST_SIZE = 10",
28
+ "MAX_ROWS = 20000",
29
+ "SAVE_INTERVAL = 10",
30
+ "",
31
+ "# Model settings - BLOOMZ (BigScience - European consortium)",
32
+ "MODEL_NAME = \"bigscience/bloomz-7b1\" # Largest instruction-tuned BLOOM model",
33
+ "CACHE_DIR = current_dir.parent / \"data/models\"",
34
+ "CACHE_DIR.mkdir(parents=True, exist_ok=True)",
35
+ "",
36
+ "PROFESSION_CATEGORIES = [",
37
+ " \"actor\", \"adult performer\", \"singer/musician\", \"model\",",
38
+ " \"online personality\", \"public figure\", \"voice actor/ASMR\",",
39
+ " \"sports professional\", \"tv personality\"",
40
+ "]",
41
+ "",
42
+ "# === LOAD MODEL ===",
43
+ "print(f\"Loading model: {MODEL_NAME}\")",
44
+ "print(f\"Cache directory: {CACHE_DIR}\")",
45
+ "print(f\"This may take a while on first run (~14GB download)...\\n\")",
46
+ "",
47
+ "# Check GPU availability",
48
+ "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"",
49
+ "print(f\"Device: {device}\")",
50
+ "",
51
+ "if device == \"cpu\":",
52
+ " print(\"\u26a0\ufe0f WARNING: No GPU detected! Inference will be VERY slow.\")",
53
+ " print(\" Consider using a GPU or reducing model size.\")",
54
+ "",
55
+ "# Load tokenizer",
56
+ "print(\"Loading tokenizer...\")",
57
+ "try:",
58
+ " tokenizer = AutoTokenizer.from_pretrained(",
59
+ " MODEL_NAME,",
60
+ " cache_dir=str(CACHE_DIR)",
61
+ " )",
62
+ " print(\"\u2705 Tokenizer loaded\")",
63
+ "except Exception as e:",
64
+ " print(f\"\u274c Error loading tokenizer: {e}\")",
65
+ " raise",
66
+ "",
67
+ "# Ensure pad token is set",
68
+ "if tokenizer.pad_token is None:",
69
+ " tokenizer.pad_token = tokenizer.eos_token",
70
+ " print(f\"Set pad_token to eos_token: {tokenizer.eos_token}\")",
71
+ "",
72
+ "# Load model with optimizations",
73
+ "print(\"Loading model (this may take several minutes)...\")",
74
+ "try:",
75
+ " model = AutoModelForCausalLM.from_pretrained(",
76
+ " MODEL_NAME,",
77
+ " cache_dir=str(CACHE_DIR),",
78
+ " torch_dtype=torch.bfloat16, # Use BF16 for efficiency",
79
+ " device_map=\"auto\", # Automatically distribute across GPUs",
80
+ " low_cpu_mem_usage=True # Optimize memory usage",
81
+ " )",
82
+ " model.eval() # Set to evaluation mode",
83
+ " print(\"\u2705 Model loaded\")",
84
+ "except Exception as e:",
85
+ " print(f\"\u274c Error loading model: {e}\")",
86
+ " raise",
87
+ "",
88
+ "# Check VRAM usage",
89
+ "if torch.cuda.is_available():",
90
+ " vram_gb = torch.cuda.max_memory_allocated() / 1024**3",
91
+ " print(f\"VRAM used: {vram_gb:.2f} GB\\n\")",
92
+ "",
93
+ "# === LOAD DATA ===",
94
+ "df = pd.read_csv(input_file)",
95
+ "print(f\"Loaded {len(df)} rows\")",
96
+ "",
97
+ "if TEST_MODE:",
98
+ " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")",
99
+ " df = df.head(TEST_SIZE).copy()",
100
+ "elif MAX_ROWS:",
101
+ " df = df.head(MAX_ROWS).copy()",
102
+ "",
103
+ "# === CREATE PROMPT (Exact DeepSeek style) ===",
104
+ "def create_prompt(row):",
105
+ " \"\"\"Create prompt.\"\"\"",
106
+ " name = row.get('real_name', row.get('name', ''))",
107
+ " if pd.isna(name):",
108
+ " name = row.get('name', '')",
109
+ "",
110
+ " # Gather hints exactly like DeepSeek version",
111
+ " hints = []",
112
+ " if pd.notna(row.get('likely_profession')):",
113
+ " hints.append(str(row['likely_profession']))",
114
+ " if pd.notna(row.get('likely_nationality')):",
115
+ " hints.append(str(row['likely_nationality']))",
116
+ " if pd.notna(row.get('likely_country')):",
117
+ " hints.append(str(row['likely_country']))",
118
+ "",
119
+ " # Add tags if we don't have enough hints",
120
+ " if len(hints) < 3:",
121
+ " for i in range(1, 8):",
122
+ " tag_col = f'tag_{i}'",
123
+ " if tag_col in row and pd.notna(row[tag_col]):",
124
+ " tag_val = str(row[tag_col])",
125
+ " if tag_val not in hints:",
126
+ " hints.append(tag_val)",
127
+ " if len(hints) >= 5:",
128
+ " break",
129
+ "",
130
+ " hint_text = \", \".join(hints[:5]) if hints else \"none\"",
131
+ "",
132
+ " return f\"\"\"Given '{name}' ({hint_text}), provide:",
133
+ "1. Full legal name (Western order if non-latin script)",
134
+ "2. Any stage names/aliases (comma separated)",
135
+ "3. Gender (Male/Female/Other/Unknown)",
136
+ "4. Top 3 most likely professions from ONLY these categories:",
137
+ " - actor",
138
+ " - adult performer",
139
+ " - singer/musician",
140
+ " - model",
141
+ " - online personality (includes streamers, cosplayers, influencers)",
142
+ " - public figure (includes politicians, activists, journalists, authors)",
143
+ " - voice actor/ASMR",
144
+ " - sports professional",
145
+ " - tv personality (includes hosts, presenters, reality TV)",
146
+ "",
147
+ "5. Primary country associated",
148
+ "",
149
+ "IMPORTANT:",
150
+ "- Choose professions ONLY from the 9 categories above",
151
+ "- Provide up to 3 professions, comma-separated, ordered by relevance",
152
+ "- Be SPECIFIC: choose the most accurate category for each role",
153
+ "- \"online personality\" includes: streamers, cosplayers, YouTubers, influencers, content creators",
154
+ "- Use 'Unknown' when uncertain or for fictional characters/places",
155
+ "- For multi-role people, list all relevant categories (e.g., \"actor, singer/musician, online personality\")",
156
+ "",
157
+ "Respond with exactly 5 numbered lines.\"\"\"",
158
+ "",
159
+ "df['prompt'] = df.apply(create_prompt, axis=1)",
160
+ "",
161
+ "# === SETUP LOGGING ===",
162
+ "log_dir = current_dir.parent / \"logs\"",
163
+ "log_dir.mkdir(parents=True, exist_ok=True)",
164
+ "log_file = log_dir / f\"bloomz_responses_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log\"",
165
+ "",
166
+ "def log_response(idx, name, prompt, full_prompt, raw_response, parsed_data):",
167
+ " \"\"\"Log all BLOOM responses to file for debugging.\"\"\"",
168
+ " with open(log_file, 'a', encoding='utf-8') as f:",
169
+ " f.write(f\"{'='*80}\\n\")",
170
+ " f.write(f\"INDEX: {idx}\\n\")",
171
+ " f.write(f\"NAME: {name}\\n\")",
172
+ " f.write(f\"TIMESTAMP: {datetime.now().isoformat()}\\n\")",
173
+ " f.write(f\"\\n--- ORIGINAL PROMPT ---\\n{prompt}\\n\")",
174
+ " f.write(f\"\\n--- FULL PROMPT SENT TO MODEL ---\\n{full_prompt}\\n\")",
175
+ " f.write(f\"\\n--- RAW MODEL RESPONSE ---\\n{raw_response}\\n\")",
176
+ " f.write(f\"\\n--- PARSED DATA ---\\n{json.dumps(parsed_data, indent=2)}\\n\")",
177
+ " f.write(f\"{'='*80}\\n\\n\")",
178
+ "",
179
+ "print(f\"\ud83d\udcdd Logging BLOOM responses to: {log_file}\\n\")",
180
+ "",
181
+ "# === QUERY BLOOMZ LOCAL (IMPROVED WITH FEW-SHOT) ===",
182
+ "def query_bloomz_local(prompt: str) -> str:",
183
+ " \"\"\"Query BLOOMZ-7B1 locally via transformers, return raw response string.",
184
+ "",
185
+ " Uses few-shot prompting which works much better for BLOOMZ.",
186
+ " \"\"\"",
187
+ " try:",
188
+ " # Extract name and hints from the original prompt",
189
+ " # Format: \"Given 'Name' (hints), provide:\"",
190
+ " name_match = re.search(r\"Given '([^']+)' \\(([^)]+)\\)\", prompt)",
191
+ " if name_match:",
192
+ " name = name_match.group(1)",
193
+ " hints = name_match.group(2)",
194
+ " else:",
195
+ " name = \"Unknown\"",
196
+ " hints = \"no hints\"",
197
+ "",
198
+ " # BLOOMZ responds MUCH better to few-shot examples than long instructions",
199
+ " full_prompt = f\"\"\"Extract person information in 5 numbered lines.",
200
+ "",
201
+ "Example 1:",
202
+ "Name: Scarlett Johansson (actor, model)",
203
+ "1. Scarlett Ingrid Johansson",
204
+ "2. ScarJo",
205
+ "3. Female",
206
+ "4. actor, model",
207
+ "5. United States",
208
+ "",
209
+ "Example 2:",
210
+ "Name: Ed Sheeran (singer, musician)",
211
+ "1. Edward Christopher Sheeran",
212
+ "2. Ed Sheeran",
213
+ "3. Male",
214
+ "4. singer/musician",
215
+ "5. United Kingdom",
216
+ "",
217
+ "Example 3:",
218
+ "Name: Amouranth (streamer, cosplayer, model)",
219
+ "1. Kaitlyn Siragusa",
220
+ "2. Amouranth",
221
+ "3. Female",
222
+ "4. online personality, model",
223
+ "5. United States",
224
+ "",
225
+ "Now extract for:",
226
+ "Name: {name} ({hints})",
227
+ "1.\"\"\"",
228
+ "",
229
+ " # Store full prompt for logging",
230
+ " query_bloomz_local.last_full_prompt = full_prompt",
231
+ "",
232
+ " inputs = tokenizer(",
233
+ " full_prompt,",
234
+ " return_tensors=\"pt\",",
235
+ " truncation=True,",
236
+ " max_length=2048",
237
+ " ).to(device)",
238
+ "",
239
+ " # Generate with adjusted parameters for BLOOMZ",
240
+ " with torch.no_grad():",
241
+ " outputs = model.generate(",
242
+ " **inputs,",
243
+ " max_new_tokens=256,",
244
+ " temperature=0.3, # Increased for more variability",
245
+ " do_sample=True,",
246
+ " top_p=0.9,",
247
+ " top_k=40,",
248
+ " repetition_penalty=1.1,",
249
+ " pad_token_id=tokenizer.eos_token_id, # Use EOS as pad token",
250
+ " eos_token_id=tokenizer.eos_token_id,",
251
+ " early_stopping=True",
252
+ " )",
253
+ "",
254
+ " # Decode the entire output to see what's happening",
255
+ " full_output = tokenizer.decode(outputs[0], skip_special_tokens=True)",
256
+ "",
257
+ " # Extract only the generated part (after the prompt)",
258
+ " generated_text = full_output[len(tokenizer.decode(inputs['input_ids'][0], skip_special_tokens=True)):]",
259
+ "",
260
+ " # Debug output (show first 3)",
261
+ " if not hasattr(query_bloomz_local, 'debug_count'):",
262
+ " query_bloomz_local.debug_count = 0",
263
+ "",
264
+ " if query_bloomz_local.debug_count < 3:",
265
+ " print(f\"\\n\ud83d\udcdd BLOOMZ Debug #{query_bloomz_local.debug_count + 1}:\")",
266
+ " print(f\"Input: {name} ({hints})\")",
267
+ " print(f\"Generated: {generated_text[:200]}\")",
268
+ " print(f\"{'='*60}\\n\")",
269
+ " query_bloomz_local.debug_count += 1",
270
+ "",
271
+ " return generated_text.strip()",
272
+ "",
273
+ " except Exception as e:",
274
+ " print(f\"Error querying BLOOMZ: {e}\")",
275
+ " query_bloomz_local.last_full_prompt = f\"ERROR: {e}\"",
276
+ " return None",
277
+ "",
278
+ "# === PARSE RESPONSE (Exact DeepSeek format) ===",
279
+ "def parse_response(response):",
280
+ " \"\"\"Parse numbered response into structured fields.\"\"\"",
281
+ " if not response:",
282
+ " return {",
283
+ " 'full_name': 'Unknown',",
284
+ " 'aliases': 'Unknown',",
285
+ " 'gender': 'Unknown',",
286
+ " 'profession_llm': 'Unknown',",
287
+ " 'country': 'Unknown'",
288
+ " }",
289
+ "",
290
+ " # Split into lines and clean",
291
+ " lines = [line.strip() for line in response.split('\\n') if line.strip()]",
292
+ "",
293
+ " # Initialize with Unknown values",
294
+ " fields = {",
295
+ " 'full_name': 'Unknown',",
296
+ " 'aliases': 'Unknown',",
297
+ " 'gender': 'Unknown',",
298
+ " 'profession_llm': 'Unknown',",
299
+ " 'country': 'Unknown'",
300
+ " }",
301
+ "",
302
+ " # Extract information from each numbered line",
303
+ " for line in lines:",
304
+ " if line.startswith('1.') or line.startswith('1)'):",
305
+ " fields['full_name'] = line[2:].strip()",
306
+ " elif line.startswith('2.') or line.startswith('2)'):",
307
+ " fields['aliases'] = line[2:].strip()",
308
+ " elif line.startswith('3.') or line.startswith('3)'):",
309
+ " fields['gender'] = line[2:].strip()",
310
+ " elif line.startswith('4.') or line.startswith('4)'):",
311
+ " fields['profession_llm'] = line[2:].strip()",
312
+ " elif line.startswith('5.') or line.startswith('5)'):",
313
+ " fields['country'] = line[2:].strip()",
314
+ "",
315
+ " return fields",
316
+ "",
317
+ "# === PROCESS ===",
318
+ "output_file = current_dir.parent / f\"data/CSV/bloomz_annotated_POI{'_test' if TEST_MODE else ''}.csv\"",
319
+ "index_file = current_dir.parent / \"misc/bloomz_query_index.txt\"",
320
+ "",
321
+ "current_index = 0",
322
+ "if index_file.exists():",
323
+ " with open(index_file) as f:",
324
+ " current_index = int(f.read().strip())",
325
+ " print(f\"Resuming from index {current_index}\")",
326
+ "",
327
+ "# Initialize columns (same as DeepSeek)",
328
+ "for col in ['full_name', 'gender', 'profession_llm', 'country', 'aliases']:",
329
+ " if col not in df.columns:",
330
+ " df[col] = 'Unknown'",
331
+ "",
332
+ "# Create prompts for all rows (same as DeepSeek)",
333
+ "print(\"Creating prompts...\")",
334
+ "df['prompt'] = df.apply(create_prompt, axis=1)",
335
+ "",
336
+ "print(f\"\\nAnnotating with BLOOMZ-7B1 LOCAL - rows {current_index} to {len(df)}...\")",
337
+ "print(f\"Model: {MODEL_NAME}\")",
338
+ "print(f\"This may take a while...\\n\")",
339
+ "",
340
+ "try:",
341
+ " start_time = time.time()",
342
+ "",
343
+ " for i in tqdm(range(current_index, len(df)), desc=\"Annotating\"):",
344
+ " row = df.iloc[i]",
345
+ "",
346
+ " # Query BLOOMZ (equivalent to DeepSeek query)",
347
+ " response = query_bloomz_local(row['prompt'])",
348
+ " parsed_data = parse_response(response)",
349
+ "",
350
+ " # Log the complete interaction for debugging",
351
+ " log_response(",
352
+ " idx=i,",
353
+ " name=row.get('real_name', row.get('name', 'Unknown')),",
354
+ " prompt=row['prompt'],",
355
+ " full_prompt=query_bloomz_local.last_full_prompt if hasattr(query_bloomz_local, 'last_full_prompt') else 'N/A',",
356
+ " raw_response=response if response else 'None',",
357
+ " parsed_data=parsed_data",
358
+ " )",
359
+ "",
360
+ " # Update dataframe",
361
+ " for key, value in parsed_data.items():",
362
+ " df.at[i, key] = value",
363
+ "",
364
+ " current_index = i + 1",
365
+ "",
366
+ " # Save progress at intervals",
367
+ " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):",
368
+ " df.to_csv(output_file, index=False)",
369
+ " with open(index_file, 'w') as f:",
370
+ " f.write(str(current_index))",
371
+ " print(f\"\u2705 Progress saved after {i+1} rows\")",
372
+ "",
373
+ " # Optional: Add small delay to prevent overheating (not needed for rate limiting like DeepSeek)",
374
+ " # time.sleep(0.1)",
375
+ "",
376
+ " elapsed_total = time.time() - start_time",
377
+ " print(f\"\\n\u2705 Done! Final results saved to {output_file}\")",
378
+ "",
379
+ " # Summary statistics (same as DeepSeek)",
380
+ " print(\"\\n=== Summary Statistics ===\")",
381
+ " print(f\"Total processed: {len(df)}\")",
382
+ " print(f\"\\nGender distribution:\")",
383
+ " print(df['gender'].value_counts())",
384
+ " print(f\"\\nTop 10 profession combinations:\")",
385
+ " print(df['profession_llm'].value_counts().head(10))",
386
+ " print(f\"\\nTop 10 countries:\")",
387
+ " print(df['country'].value_counts().head(10))",
388
+ "",
389
+ " # Sample results",
390
+ " print(\"\\n=== Sample Results ===\")",
391
+ " display_cols = ['real_name', 'full_name', 'gender', 'profession_llm', 'country']",
392
+ " available_cols = [col for col in display_cols if col in df.columns]",
393
+ " print(df[available_cols].head(10).to_string(index=False))",
394
+ "",
395
+ " # Additional info for local model",
396
+ " print(f\"\\nTotal time: {elapsed_total/60:.1f} minutes\")",
397
+ " print(f\"Average speed: {len(df)/(elapsed_total/3600):.1f} samples/hour\")",
398
+ " if torch.cuda.is_available():",
399
+ " print(f\"Final VRAM usage: {torch.cuda.max_memory_allocated() / 1024**3:.2f} GB\")",
400
+ "",
401
+ "except Exception as e:",
402
+ " print(f\"\u26a0\ufe0f Error encountered: {e}\")",
403
+ " print(f\"\u26a0\ufe0f Last processed index: {current_index}\")",
404
+ "",
405
+ " # Save progress before exiting",
406
+ " df.to_csv(output_file, index=False)",
407
+ " with open(index_file, 'w') as f:",
408
+ " f.write(str(current_index))",
409
+ "",
410
+ " print(f\"\u26a0\ufe0f Progress saved up to row {current_index}\")",
411
+ ""
412
  ]
413
  }
414
  ],
 
433
  },
434
  "nbformat": 4,
435
  "nbformat_minor": 5
436
+ }