Laura Wagner commited on
Commit
db8c63e
Β·
1 Parent(s): 23cc0e3

corrected llm code

Browse files
jupyter_notebooks/Section_2-3-4_Figure_8_deepfake_adapters.ipynb CHANGED
@@ -142,40 +142,40 @@
142
  "name": "stdout",
143
  "output_type": "stream",
144
  "text": [
145
- "\u2705 spaCy model loaded: en_core_web_sm\n",
146
  "Loaded 18 rows\n",
147
  "\n",
148
- "\ud83d\udd04 Processing names with spaCy NER...\n",
149
  "\n",
150
- "\ud83d\udcca Name cleaning examples (with spaCy NER):\n",
151
  "====================================================================================================\n",
152
  "Original Name | Cleaned Name \n",
153
  "====================================================================================================\n",
154
  "Super Pose Book Vol.1 - ControlNet | Super Pose Book \n",
155
  "Liyuu LoRA | Liyuu \n",
156
- "HashimotoKanna/ \u6a4b\u672c\u74b0\u5948 _JP_Actress | HashimotoKanna \n",
157
  "Emma Watson (JG) | Emma Watson \n",
158
- "Gal Gadot\u300cLoRa\u300d | Gal Gadot \n",
159
- "Scarlett Johansson\u300cLoRa\u300d | Scarlett Johansson \n",
160
- "Gakki | Aragaki Yui | \u65b0\u57a3\u7d50\u8863 | Gakki \n",
161
- "Actress Satomi_\u77f3\u539f\u25cb\u25cb | Actress Satomi \n",
162
  "Game of Thrones Cast | Game Thrones Cast \n",
163
- "Natalie Portman\u300cLoRa\u300d | Natalie Portman \n",
164
  "Emma Watson LoRA | Emma Watson \n",
165
  "====================================================================================================\n",
166
  "\n",
167
- "\ud83e\uddea Leetspeak translation examples:\n",
168
  " 4kira LoRA -> akira\n",
169
  " 3mma Watson v2 -> Watson\n",
170
  " 1rene LORA -> irene\n",
171
  " L3vi Ackerman -> Levi Ackerman\n",
172
  "\n",
173
- "\ud83d\udcc8 Statistics:\n",
174
  " Total rows: 18\n",
175
  " Non-empty names: 18\n",
176
  " Empty names: 0\n",
177
  "\n",
178
- "\ud83c\udfaf Sample spaCy NER results:\n",
179
  " 1. IU\n",
180
  " 2. Super Pose Book\n",
181
  " 3. Liyuu\n",
@@ -187,8 +187,8 @@
187
  " 9. Emma Watson\n",
188
  " 10. Gal Gadot\n",
189
  "\n",
190
- "\u2705 Cleaned 18 names using spaCy NER\n",
191
- "\ud83d\udcbe Saved to /home/lauhp/000_PHD/000_010_PUBLICATION/CODE/pm-paper/data/CSV/NER_POI_step01_pre_annotation.csv\n"
192
  ]
193
  }
194
  ],
@@ -203,13 +203,13 @@
203
  "# You may need to download it first: python -m spacy download en_core_web_sm\n",
204
  "try:\n",
205
  " nlp = spacy.load(\"en_core_web_sm\")\n",
206
- " print(\"\u2705 spaCy model loaded: en_core_web_sm\")\n",
207
  "except OSError:\n",
208
- " print(\"\u274c spaCy model not found. Downloading...\")\n",
209
  " import subprocess\n",
210
  " subprocess.run([\"python\", \"-m\", \"spacy\", \"download\", \"en_core_web_sm\"])\n",
211
  " nlp = spacy.load(\"en_core_web_sm\")\n",
212
- " print(\"\u2705 spaCy model downloaded and loaded\")\n",
213
  "\n",
214
  "# Set up paths\n",
215
  "current_dir = Path.cwd()\n",
@@ -282,8 +282,8 @@
282
  " name = re.sub(r'\\([^)]*\\)', '', name)\n",
283
  " name = re.sub(r'\\[[^\\]]*\\]', '', name)\n",
284
  " \n",
285
- " # Remove special characters like \u300c\u300d\n",
286
- " name = re.sub(r'[\u300c\u300d\u300e\u300f\u3010\u3011\u3008\u3009\u300a\u300b]', '', name)\n",
287
  " \n",
288
  " # Handle pipe - keep first part\n",
289
  " if '|' in name:\n",
@@ -339,7 +339,7 @@
339
  " Complete name cleaning pipeline with spaCy NER.\n",
340
  " \n",
341
  " Pipeline:\n",
342
- " 1. Translate leetspeak (4\u2192a, 3\u2192e, 1\u2192i, etc.)\n",
343
  " 2. Remove noise (emoji, version tags, LoRA terms)\n",
344
  " 3. Use spaCy to extract PERSON entities\n",
345
  " 4. Fallback to capitalized words or cleaned text\n",
@@ -356,11 +356,11 @@
356
  " return person_name\n",
357
  "\n",
358
  "# Apply name cleaning with spaCy\n",
359
- "print(\"\\n\ud83d\udd04 Processing names with spaCy NER...\")\n",
360
  "df['real_name'] = df['name'].apply(clean_name_with_spacy)\n",
361
  "\n",
362
  "# Show examples with detailed comparison\n",
363
- "print(\"\\n\ud83d\udcca Name cleaning examples (with spaCy NER):\")\n",
364
  "print(\"=\" * 100)\n",
365
  "print(f\"{'Original Name':<50} | {'Cleaned Name':<30}\")\n",
366
  "print(\"=\" * 100)\n",
@@ -375,31 +375,31 @@
375
  "print(\"=\" * 100)\n",
376
  "\n",
377
  "# Show specific test cases\n",
378
- "print(\"\\n\ud83e\uddea Leetspeak translation examples:\")\n",
379
  "test_names = ['4kira LoRA', '3mma Watson v2', '1rene LORA', 'L3vi Ackerman']\n",
380
  "for test in test_names:\n",
381
  " result = clean_name_with_spacy(test)\n",
382
  " print(f\" {test:<30} -> {result}\")\n",
383
  "\n",
384
  "# Statistics\n",
385
- "print(f\"\\n\ud83d\udcc8 Statistics:\")\n",
386
  "print(f\" Total rows: {len(df)}\")\n",
387
  "print(f\" Non-empty names: {(df['real_name'] != '').sum()}\")\n",
388
  "print(f\" Empty names: {(df['real_name'] == '').sum()}\")\n",
389
  "\n",
390
  "# Show some examples of what spaCy identified\n",
391
- "print(\"\\n\ud83c\udfaf Sample spaCy NER results:\")\n",
392
  "sample_names = df['real_name'].head(20).tolist()\n",
393
  "for i, name in enumerate(sample_names[:10], 1):\n",
394
  " if name:\n",
395
  " print(f\" {i}. {name}\")\n",
396
  "\n",
397
- "print(f\"\\n\u2705 Cleaned {len(df)} names using spaCy NER\")\n",
398
  "\n",
399
  "# Save intermediate result\n",
400
  "output_step1 = current_dir.parent / \"data/CSV/NER_POI_step01_pre_annotation.csv\"\n",
401
  "df.to_csv(output_step1, index=False)\n",
402
- "print(f\"\ud83d\udcbe Saved to {output_step1}\")\n"
403
  ]
404
  },
405
  {
@@ -444,7 +444,7 @@
444
  "8 Emma Watson actor\n",
445
  "9 Gal Gadot model\n",
446
  "\n",
447
- "\ud83d\udd0d Checking 'Irene' entries:\n",
448
  " Name: Irene\n",
449
  " Tags: ['girl', 'photorealistic', 'asian', 'woman', 'beautiful', 'celebrity', 'korean']\n",
450
  " Country: \n",
@@ -485,7 +485,7 @@
485
  "\n",
486
  "poi_df[\"tags\"] = poi_df.apply(combine_tags, axis=1)\n",
487
  "\n",
488
- "# Step 2: Build tag \u2192 (country, nationality) mapping with PRIORITIES\n",
489
  "tag_to_country_nationality = {}\n",
490
  "# We'll use a priority score: direct country name = 3, nationality = 2, word parts = 1\n",
491
  "\n",
@@ -563,7 +563,7 @@
563
  "\n",
564
  "poi_df[[\"likely_country\", \"likely_nationality\"]] = poi_df[\"tags\"].apply(infer_country_and_nationality)\n",
565
  "\n",
566
- "# Step 4: Build tag \u2192 profession mapping\n",
567
  "profession_alias_map = {}\n",
568
  "\n",
569
  "for _, row in professions_df.iterrows():\n",
@@ -611,7 +611,7 @@
611
  "# Check specific case: Irene with korean tag\n",
612
  "irene_rows = poi_df[poi_df['real_name'].str.contains('irene', case=False, na=False)]\n",
613
  "if len(irene_rows) > 0:\n",
614
- " print(f\"\\n\ud83d\udd0d Checking 'Irene' entries:\")\n",
615
  " for idx, row in irene_rows.iterrows():\n",
616
  " print(f\" Name: {row['real_name']}\")\n",
617
  " print(f\" Tags: {row['tags']}\")\n",
@@ -652,7 +652,7 @@
652
  "name": "stdout",
653
  "output_type": "stream",
654
  "text": [
655
- "\u274c Error: deepseek_api_key.txt not found in misc/credentials/\n",
656
  " Please create this file with your API key\n"
657
  ]
658
  },
@@ -663,7 +663,7 @@
663
  "traceback": [
664
  "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
665
  "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
666
- "Cell \u001b[0;32mIn[10], line 37\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[38;5;66;03m# === LOAD API KEY ===\u001b[39;00m\n\u001b[1;32m 36\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 37\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mapi_key_file\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[1;32m 38\u001b[0m api_key \u001b[38;5;241m=\u001b[39m f\u001b[38;5;241m.\u001b[39mread()\u001b[38;5;241m.\u001b[39mstrip()\n\u001b[1;32m 39\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\u2705 Deepseek API key loaded\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
667
  "File \u001b[0;32m~/.local/lib/python3.10/site-packages/IPython/core/interactiveshell.py:324\u001b[0m, in \u001b[0;36m_modified_open\u001b[0;34m(file, *args, **kwargs)\u001b[0m\n\u001b[1;32m 317\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m2\u001b[39m}:\n\u001b[1;32m 318\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 319\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIPython won\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m by default \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 320\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myou can use builtins\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m open.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 322\u001b[0m )\n\u001b[0;32m--> 324\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
668
  "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '/home/lauhp/000_PHD/000_010_PUBLICATION/CODE/pm-paper/misc/credentials/deepseek_api_key.txt'"
669
  ]
@@ -708,9 +708,9 @@
708
  "try:\n",
709
  " with open(api_key_file) as f:\n",
710
  " api_key = f.read().strip()\n",
711
- " print(\"\u2705 Deepseek API key loaded\")\n",
712
  "except FileNotFoundError:\n",
713
- " print(\"\u274c Error: deepseek_api_key.txt not found in misc/credentials/\")\n",
714
  " print(\" Please create this file with your API key\")\n",
715
  " raise\n",
716
  "\n",
@@ -726,9 +726,9 @@
726
  "# Load profession categories and keywords\n",
727
  "try:\n",
728
  " prof_mapped_df = pd.read_csv(professions_mapped_file)\n",
729
- " print(f\"\u2705 Loaded profession mapping with {len(prof_mapped_df)} categories\")\n",
730
  "except:\n",
731
- " print(\"\u26a0\ufe0f Warning: professions_mapped.csv not found, using default categories\")\n",
732
  "\n",
733
  "profession_str = \", \".join(PROFESSION_CATEGORIES)\n",
734
  "\n",
@@ -895,11 +895,11 @@
895
  " df.to_csv(output_file, index=False)\n",
896
  " with open(index_file, 'w') as f:\n",
897
  " f.write(str(current_index))\n",
898
- " print(f\"\u2705 Progress saved after {i+1} rows\")\n",
899
  " \n",
900
  " time.sleep(1) # Rate limiting\n",
901
  " \n",
902
- " print(f\"\\n\u2705 Done! Final results saved to {output_file}\")\n",
903
  " \n",
904
  " # Summary statistics\n",
905
  " print(\"\\n=== Summary Statistics ===\")\n",
@@ -917,15 +917,15 @@
917
  " print(df[display_cols].head(10).to_string(index=False))\n",
918
  "\n",
919
  "except Exception as e:\n",
920
- " print(f\"\u26a0\ufe0f Error encountered: {e}\")\n",
921
- " print(f\"\u26a0\ufe0f Last processed index: {current_index}\")\n",
922
  " \n",
923
  " # Save progress before exiting\n",
924
  " df.to_csv(output_file, index=False)\n",
925
  " with open(index_file, 'w') as f:\n",
926
  " f.write(str(current_index))\n",
927
  " \n",
928
- " print(f\"\u26a0\ufe0f Progress saved up to row {current_index}\")\n"
929
  ]
930
  },
931
  {
@@ -954,169 +954,7 @@
954
  "id": "qwen_code",
955
  "metadata": {},
956
  "outputs": [],
957
- "source": [
958
- "import pandas as pd",
959
- "import json",
960
- "import time",
961
- "import re",
962
- "from pathlib import Path",
963
- "from tqdm import tqdm",
964
- "from openai import OpenAI",
965
- "",
966
- "current_dir = Path.cwd()",
967
- "input_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"",
968
- "api_key_file = current_dir.parent / \"misc/credentials/qwen_api_key.txt\"",
969
- "",
970
- "# === CONFIGURATION ===",
971
- "TEST_MODE = True",
972
- "TEST_SIZE = 10",
973
- "MAX_ROWS = 20000",
974
- "SAVE_INTERVAL = 10",
975
- "",
976
- "PROFESSION_CATEGORIES = [",
977
- " \"actor\", \"adult performer\", \"singer/musician\", \"model\",",
978
- " \"online personality\", \"public figure\", \"voice actor/ASMR\",",
979
- " \"sports professional\", \"tv personality\"",
980
- "]",
981
- "",
982
- "# === LOAD API KEY ===",
983
- "try:",
984
- " with open(api_key_file) as f:",
985
- " api_key = f.read().strip()",
986
- " print(\"\u2705 Qwen API key loaded\")",
987
- " print(f\" Key starts with: {api_key[:10]}...\")",
988
- "except FileNotFoundError:",
989
- " print(\"\u274c Error: qwen_api_key.txt not found\")",
990
- " raise",
991
- "",
992
- "# Initialize OpenAI client for Qwen (matching dashboard example)",
993
- "client = OpenAI(",
994
- " api_key=api_key,",
995
- " base_url=\"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\"",
996
- ")",
997
- "",
998
- "# === LOAD DATA ===",
999
- "df = pd.read_csv(input_file)",
1000
- "print(f\"Loaded {len(df)} rows\")",
1001
- "",
1002
- "if TEST_MODE:",
1003
- " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")",
1004
- " df = df.head(TEST_SIZE).copy()",
1005
- "elif MAX_ROWS:",
1006
- " df = df.head(MAX_ROWS).copy()",
1007
- "",
1008
- "# === CREATE PROMPT ===",
1009
- "def create_prompt(name: str, tags: list, country: str) -> str:",
1010
- " tags_str = \", \".join(tags[:5]) if tags else \"none\"",
1011
- " country_str = country if country else \"unknown\"",
1012
- " ",
1013
- " return f\"\"\"You are an expert at identifying public figures.",
1014
- "",
1015
- "Given: Name: {name}, Tags: {tags_str}, Country: {country_str}",
1016
- "",
1017
- "Provide in JSON format:",
1018
- "{{",
1019
- " \"full_name\": \"<full name>\",",
1020
- " \"gender\": \"<Male/Female/Other/Unknown>\",",
1021
- " \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],",
1022
- " \"country\": \"<country>\"",
1023
- "}}",
1024
- "",
1025
- "Choose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}",
1026
- "- Max 3 professions, ordered by relevance",
1027
- "- \"online personality\" includes streamers, cosplayers, influencers",
1028
- "- Return \"Unknown\" if uncertain",
1029
- "",
1030
- "Return ONLY JSON, no extra text.\"\"\"",
1031
- "",
1032
- "# === QUERY QWEN ===",
1033
- "def query_qwen(prompt: str) -> dict:",
1034
- " \"\"\"Query Qwen using OpenAI-compatible API (matching dashboard example).\"\"\"",
1035
- " for attempt in range(3):",
1036
- " try:",
1037
- " completion = client.chat.completions.create(",
1038
- " model=\"qwen-max\", # Note: dashboard shows \"qwen3-max\" but \"qwen-max\" works too",
1039
- " messages=[",
1040
- " {\"role\": \"user\", \"content\": prompt}",
1041
- " ],",
1042
- " temperature=0.1,",
1043
- " max_tokens=500,",
1044
- " stream=False # Disable streaming for easier parsing",
1045
- " )",
1046
- " ",
1047
- " content = completion.choices[0].message.content",
1048
- " ",
1049
- " # Parse JSON from response",
1050
- " json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)",
1051
- " if json_match:",
1052
- " return json.loads(json_match.group())",
1053
- " ",
1054
- " return None",
1055
- " ",
1056
- " except Exception as e:",
1057
- " print(f\"Qwen error (attempt {attempt+1}): {e}\")",
1058
- " if attempt < 2:",
1059
- " time.sleep(2 ** attempt)",
1060
- " ",
1061
- " return None",
1062
- "",
1063
- "# === PROCESS ===",
1064
- "output_file = current_dir.parent / f\"data/CSV/qwen_annotated_POI{'_test' if TEST_MODE else ''}.csv\"",
1065
- "index_file = current_dir.parent / \"misc/qwen_query_index.txt\"",
1066
- "",
1067
- "current_index = 0",
1068
- "if index_file.exists():",
1069
- " with open(index_file) as f:",
1070
- " current_index = int(f.read().strip())",
1071
- "",
1072
- "for col in ['full_name', 'gender', 'profession_llm', 'country']:",
1073
- " if col not in df.columns:",
1074
- " df[col] = 'Unknown'",
1075
- "",
1076
- "print(f\"\\nAnnotating with QWEN-MAX (OpenAI-compatible API) - rows {current_index} to {len(df)}...\")",
1077
- "",
1078
- "try:",
1079
- " for i in tqdm(range(current_index, len(df)), desc=\"Qwen\"):",
1080
- " row = df.iloc[i]",
1081
- " ",
1082
- " tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8) ",
1083
- " if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]",
1084
- " ",
1085
- " prompt = create_prompt(",
1086
- " row.get('real_name', row.get('name', '')),",
1087
- " tags,",
1088
- " row.get('likely_country', '')",
1089
- " )",
1090
- " ",
1091
- " result = query_qwen(prompt)",
1092
- " ",
1093
- " if result:",
1094
- " df.at[i, 'full_name'] = result.get('full_name', 'Unknown')",
1095
- " df.at[i, 'gender'] = result.get('gender', 'Unknown')",
1096
- " profs = result.get('professions', [])",
1097
- " df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)",
1098
- " df.at[i, 'country'] = result.get('country', 'Unknown')",
1099
- " ",
1100
- " current_index = i + 1",
1101
- " ",
1102
- " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):",
1103
- " df.to_csv(output_file, index=False)",
1104
- " with open(index_file, 'w') as f:",
1105
- " f.write(str(current_index))",
1106
- " print(f\"\u2705 Saved after {i+1} rows\")",
1107
- " ",
1108
- " time.sleep(0.5)",
1109
- " ",
1110
- " print(f\"\\n\u2705 Done! Results: {output_file}\")",
1111
- " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")",
1112
- " ",
1113
- "except Exception as e:",
1114
- " print(f\"\u26a0\ufe0f Error: {e}\")",
1115
- " df.to_csv(output_file, index=False)",
1116
- " with open(index_file, 'w') as f:",
1117
- " f.write(str(current_index))",
1118
- ""
1119
- ]
1120
  },
1121
  {
1122
  "cell_type": "markdown",
@@ -1140,165 +978,7 @@
1140
  "id": "llama_code",
1141
  "metadata": {},
1142
  "outputs": [],
1143
- "source": [
1144
- "import pandas as pd",
1145
- "import json",
1146
- "import time",
1147
- "import requests",
1148
- "import re",
1149
- "from pathlib import Path",
1150
- "from tqdm import tqdm",
1151
- "",
1152
- "current_dir = Path.cwd()",
1153
- "input_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"",
1154
- "api_key_file = current_dir.parent / \"misc/credentials/together_api_key.txt\"",
1155
- "",
1156
- "# === CONFIGURATION ===",
1157
- "TEST_MODE = True",
1158
- "TEST_SIZE = 10",
1159
- "MAX_ROWS = 20000",
1160
- "SAVE_INTERVAL = 10",
1161
- "",
1162
- "PROFESSION_CATEGORIES = [",
1163
- " \"actor\", \"adult performer\", \"singer/musician\", \"model\",",
1164
- " \"online personality\", \"public figure\", \"voice actor/ASMR\",",
1165
- " \"sports professional\", \"tv personality\"",
1166
- "]",
1167
- "",
1168
- "# === LOAD API KEY ===",
1169
- "try:",
1170
- " with open(api_key_file) as f:",
1171
- " api_key = f.read().strip()",
1172
- " print(\"\u2705 Llama API key loaded\")",
1173
- "except FileNotFoundError:",
1174
- " print(\"\u274c Error: together_api_key.txt not found\")",
1175
- " raise",
1176
- "",
1177
- "# === LOAD DATA ===",
1178
- "df = pd.read_csv(input_file)",
1179
- "print(f\"Loaded {len(df)} rows\")",
1180
- "",
1181
- "if TEST_MODE:",
1182
- " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")",
1183
- " df = df.head(TEST_SIZE).copy()",
1184
- "elif MAX_ROWS:",
1185
- " df = df.head(MAX_ROWS).copy()",
1186
- "",
1187
- "# === CREATE PROMPT ===",
1188
- "def create_prompt(name: str, tags: list, country: str) -> str:",
1189
- " tags_str = \", \".join(tags[:5]) if tags else \"none\"",
1190
- " country_str = country if country else \"unknown\"",
1191
- " ",
1192
- " return f\"\"\"You are an expert at identifying public figures.",
1193
- "",
1194
- "Given: Name: {name}, Tags: {tags_str}, Country: {country_str}",
1195
- "",
1196
- "Provide in JSON format:",
1197
- "{{",
1198
- " \"full_name\": \"<full name>\",",
1199
- " \"gender\": \"<Male/Female/Other/Unknown>\",",
1200
- " \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],",
1201
- " \"country\": \"<country>\"",
1202
- "}}",
1203
- "",
1204
- "Choose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}",
1205
- "- Max 3 professions, ordered by relevance",
1206
- "- \"online personality\" includes streamers, cosplayers, influencers",
1207
- "- Return \"Unknown\" if uncertain",
1208
- "",
1209
- "Return ONLY JSON, no extra text.\"\"\"",
1210
- "",
1211
- "# === QUERY LLAMA ===",
1212
- "def query_llama(prompt: str) -> dict:",
1213
- " url = \"https://api.together.xyz/v1/chat/completions\"",
1214
- " ",
1215
- " headers = {",
1216
- " \"Authorization\": f\"Bearer {api_key}\",",
1217
- " \"Content-Type\": \"application/json\"",
1218
- " }",
1219
- " ",
1220
- " data = {",
1221
- " \"model\": \"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\",",
1222
- " \"messages\": [{\"role\": \"user\", \"content\": prompt}],",
1223
- " \"temperature\": 0.1,",
1224
- " \"max_tokens\": 500",
1225
- " }",
1226
- " ",
1227
- " for attempt in range(3):",
1228
- " try:",
1229
- " response = requests.post(url, headers=headers, json=data, timeout=30)",
1230
- " response.raise_for_status()",
1231
- " result = response.json()",
1232
- " ",
1233
- " if 'choices' in result:",
1234
- " content = result['choices'][0]['message']['content']",
1235
- " json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)",
1236
- " if json_match:",
1237
- " return json.loads(json_match.group())",
1238
- " return None",
1239
- " except Exception as e:",
1240
- " print(f\"Llama error (attempt {attempt+1}): {e}\")",
1241
- " if attempt < 2:",
1242
- " time.sleep(2 ** attempt)",
1243
- " return None",
1244
- "",
1245
- "# === PROCESS ===",
1246
- "output_file = current_dir.parent / f\"data/CSV/llama_annotated_POI{'_test' if TEST_MODE else ''}.csv\"",
1247
- "index_file = current_dir.parent / \"misc/llama_query_index.txt\"",
1248
- "",
1249
- "current_index = 0",
1250
- "if index_file.exists():",
1251
- " with open(index_file) as f:",
1252
- " current_index = int(f.read().strip())",
1253
- "",
1254
- "for col in ['full_name', 'gender', 'profession_llm', 'country']:",
1255
- " if col not in df.columns:",
1256
- " df[col] = 'Unknown'",
1257
- "",
1258
- "print(f\"\\nAnnotating with LLAMA 3.1 70B - rows {current_index} to {len(df)}...\")",
1259
- "",
1260
- "try:",
1261
- " for i in tqdm(range(current_index, len(df)), desc=\"Llama\"):",
1262
- " row = df.iloc[i]",
1263
- " ",
1264
- " tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8) ",
1265
- " if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]",
1266
- " ",
1267
- " prompt = create_prompt(",
1268
- " row.get('real_name', row.get('name', '')),",
1269
- " tags,",
1270
- " row.get('likely_country', '')",
1271
- " )",
1272
- " ",
1273
- " result = query_llama(prompt)",
1274
- " ",
1275
- " if result:",
1276
- " df.at[i, 'full_name'] = result.get('full_name', 'Unknown')",
1277
- " df.at[i, 'gender'] = result.get('gender', 'Unknown')",
1278
- " profs = result.get('professions', [])",
1279
- " df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)",
1280
- " df.at[i, 'country'] = result.get('country', 'Unknown')",
1281
- " ",
1282
- " current_index = i + 1",
1283
- " ",
1284
- " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):",
1285
- " df.to_csv(output_file, index=False)",
1286
- " with open(index_file, 'w') as f:",
1287
- " f.write(str(current_index))",
1288
- " print(f\"\u2705 Saved after {i+1} rows\")",
1289
- " ",
1290
- " time.sleep(0.5)",
1291
- " ",
1292
- " print(f\"\\n\u2705 Done! Results: {output_file}\")",
1293
- " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")",
1294
- " ",
1295
- "except Exception as e:",
1296
- " print(f\"\u26a0\ufe0f Error: {e}\")",
1297
- " df.to_csv(output_file, index=False)",
1298
- " with open(index_file, 'w') as f:",
1299
- " f.write(str(current_index))",
1300
- ""
1301
- ]
1302
  },
1303
  {
1304
  "cell_type": "markdown",
@@ -1322,165 +1002,7 @@
1322
  "id": "mixtral_code",
1323
  "metadata": {},
1324
  "outputs": [],
1325
- "source": [
1326
- "import pandas as pd",
1327
- "import json",
1328
- "import time",
1329
- "import requests",
1330
- "import re",
1331
- "from pathlib import Path",
1332
- "from tqdm import tqdm",
1333
- "",
1334
- "current_dir = Path.cwd()",
1335
- "input_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"",
1336
- "api_key_file = current_dir.parent / \"misc/credentials/mistral_api_key.txt\"",
1337
- "",
1338
- "# === CONFIGURATION ===",
1339
- "TEST_MODE = True",
1340
- "TEST_SIZE = 10",
1341
- "MAX_ROWS = 20000",
1342
- "SAVE_INTERVAL = 10",
1343
- "",
1344
- "PROFESSION_CATEGORIES = [",
1345
- " \"actor\", \"adult performer\", \"singer/musician\", \"model\",",
1346
- " \"online personality\", \"public figure\", \"voice actor/ASMR\",",
1347
- " \"sports professional\", \"tv personality\"",
1348
- "]",
1349
- "",
1350
- "# === LOAD API KEY ===",
1351
- "try:",
1352
- " with open(api_key_file) as f:",
1353
- " api_key = f.read().strip()",
1354
- " print(\"\u2705 Mixtral API key loaded\")",
1355
- "except FileNotFoundError:",
1356
- " print(\"\u274c Error: mistral_api_key.txt not found\")",
1357
- " raise",
1358
- "",
1359
- "# === LOAD DATA ===",
1360
- "df = pd.read_csv(input_file)",
1361
- "print(f\"Loaded {len(df)} rows\")",
1362
- "",
1363
- "if TEST_MODE:",
1364
- " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")",
1365
- " df = df.head(TEST_SIZE).copy()",
1366
- "elif MAX_ROWS:",
1367
- " df = df.head(MAX_ROWS).copy()",
1368
- "",
1369
- "# === CREATE PROMPT ===",
1370
- "def create_prompt(name: str, tags: list, country: str) -> str:",
1371
- " tags_str = \", \".join(tags[:5]) if tags else \"none\"",
1372
- " country_str = country if country else \"unknown\"",
1373
- " ",
1374
- " return f\"\"\"You are an expert at identifying public figures.",
1375
- "",
1376
- "Given: Name: {name}, Tags: {tags_str}, Country: {country_str}",
1377
- "",
1378
- "Provide in JSON format:",
1379
- "{{",
1380
- " \"full_name\": \"<full name>\",",
1381
- " \"gender\": \"<Male/Female/Other/Unknown>\",",
1382
- " \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],",
1383
- " \"country\": \"<country>\"",
1384
- "}}",
1385
- "",
1386
- "Choose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}",
1387
- "- Max 3 professions, ordered by relevance",
1388
- "- \"online personality\" includes streamers, cosplayers, influencers",
1389
- "- Return \"Unknown\" if uncertain",
1390
- "",
1391
- "Return ONLY JSON, no extra text.\"\"\"",
1392
- "",
1393
- "# === QUERY MIXTRAL ===",
1394
- "def query_mixtral(prompt: str) -> dict:",
1395
- " url = \"https://api.mistral.ai/v1/chat/completions\"",
1396
- " ",
1397
- " headers = {",
1398
- " \"Authorization\": f\"Bearer {api_key}\",",
1399
- " \"Content-Type\": \"application/json\"",
1400
- " }",
1401
- " ",
1402
- " data = {",
1403
- " \"model\": \"open-mixtral-8x22b\",",
1404
- " \"messages\": [{\"role\": \"user\", \"content\": prompt}],",
1405
- " \"temperature\": 0.1,",
1406
- " \"max_tokens\": 500",
1407
- " }",
1408
- " ",
1409
- " for attempt in range(3):",
1410
- " try:",
1411
- " response = requests.post(url, headers=headers, json=data, timeout=30)",
1412
- " response.raise_for_status()",
1413
- " result = response.json()",
1414
- " ",
1415
- " if 'choices' in result:",
1416
- " content = result['choices'][0]['message']['content']",
1417
- " json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)",
1418
- " if json_match:",
1419
- " return json.loads(json_match.group())",
1420
- " return None",
1421
- " except Exception as e:",
1422
- " print(f\"Mixtral error (attempt {attempt+1}): {e}\")",
1423
- " if attempt < 2:",
1424
- " time.sleep(2 ** attempt)",
1425
- " return None",
1426
- "",
1427
- "# === PROCESS ===",
1428
- "output_file = current_dir.parent / f\"data/CSV/mixtral_annotated_POI{'_test' if TEST_MODE else ''}.csv\"",
1429
- "index_file = current_dir.parent / \"misc/mixtral_query_index.txt\"",
1430
- "",
1431
- "current_index = 0",
1432
- "if index_file.exists():",
1433
- " with open(index_file) as f:",
1434
- " current_index = int(f.read().strip())",
1435
- "",
1436
- "for col in ['full_name', 'gender', 'profession_llm', 'country']:",
1437
- " if col not in df.columns:",
1438
- " df[col] = 'Unknown'",
1439
- "",
1440
- "print(f\"\\nAnnotating with MIXTRAL 8x22B - rows {current_index} to {len(df)}...\")",
1441
- "",
1442
- "try:",
1443
- " for i in tqdm(range(current_index, len(df)), desc=\"Mixtral\"):",
1444
- " row = df.iloc[i]",
1445
- " ",
1446
- " tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8) ",
1447
- " if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]",
1448
- " ",
1449
- " prompt = create_prompt(",
1450
- " row.get('real_name', row.get('name', '')),",
1451
- " tags,",
1452
- " row.get('likely_country', '')",
1453
- " )",
1454
- " ",
1455
- " result = query_mixtral(prompt)",
1456
- " ",
1457
- " if result:",
1458
- " df.at[i, 'full_name'] = result.get('full_name', 'Unknown')",
1459
- " df.at[i, 'gender'] = result.get('gender', 'Unknown')",
1460
- " profs = result.get('professions', [])",
1461
- " df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)",
1462
- " df.at[i, 'country'] = result.get('country', 'Unknown')",
1463
- " ",
1464
- " current_index = i + 1",
1465
- " ",
1466
- " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):",
1467
- " df.to_csv(output_file, index=False)",
1468
- " with open(index_file, 'w') as f:",
1469
- " f.write(str(current_index))",
1470
- " print(f\"\u2705 Saved after {i+1} rows\")",
1471
- " ",
1472
- " time.sleep(0.5)",
1473
- " ",
1474
- " print(f\"\\n\u2705 Done! Results: {output_file}\")",
1475
- " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")",
1476
- " ",
1477
- "except Exception as e:",
1478
- " print(f\"\u26a0\ufe0f Error: {e}\")",
1479
- " df.to_csv(output_file, index=False)",
1480
- " with open(index_file, 'w') as f:",
1481
- " f.write(str(current_index))",
1482
- ""
1483
- ]
1484
  },
1485
  {
1486
  "cell_type": "markdown",
@@ -1505,165 +1027,7 @@
1505
  "id": "gemma_code",
1506
  "metadata": {},
1507
  "outputs": [],
1508
- "source": [
1509
- "import pandas as pd",
1510
- "import json",
1511
- "import time",
1512
- "import requests",
1513
- "import re",
1514
- "from pathlib import Path",
1515
- "from tqdm import tqdm",
1516
- "",
1517
- "current_dir = Path.cwd()",
1518
- "input_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"",
1519
- "api_key_file = current_dir.parent / \"misc/credentials/together_api_key.txt\"",
1520
- "",
1521
- "# === CONFIGURATION ===",
1522
- "TEST_MODE = True",
1523
- "TEST_SIZE = 10",
1524
- "MAX_ROWS = 20000",
1525
- "SAVE_INTERVAL = 10",
1526
- "",
1527
- "PROFESSION_CATEGORIES = [",
1528
- " \"actor\", \"adult performer\", \"singer/musician\", \"model\",",
1529
- " \"online personality\", \"public figure\", \"voice actor/ASMR\",",
1530
- " \"sports professional\", \"tv personality\"",
1531
- "]",
1532
- "",
1533
- "# === LOAD API KEY ===",
1534
- "try:",
1535
- " with open(api_key_file) as f:",
1536
- " api_key = f.read().strip()",
1537
- " print(\"\u2705 Gemma API key loaded\")",
1538
- "except FileNotFoundError:",
1539
- " print(\"\u274c Error: together_api_key.txt not found\")",
1540
- " raise",
1541
- "",
1542
- "# === LOAD DATA ===",
1543
- "df = pd.read_csv(input_file)",
1544
- "print(f\"Loaded {len(df)} rows\")",
1545
- "",
1546
- "if TEST_MODE:",
1547
- " print(f\"Running in TEST MODE with {TEST_SIZE} samples\")",
1548
- " df = df.head(TEST_SIZE).copy()",
1549
- "elif MAX_ROWS:",
1550
- " df = df.head(MAX_ROWS).copy()",
1551
- "",
1552
- "# === CREATE PROMPT ===",
1553
- "def create_prompt(name: str, tags: list, country: str) -> str:",
1554
- " tags_str = \", \".join(tags[:5]) if tags else \"none\"",
1555
- " country_str = country if country else \"unknown\"",
1556
- " ",
1557
- " return f\"\"\"You are an expert at identifying public figures.",
1558
- "",
1559
- "Given: Name: {name}, Tags: {tags_str}, Country: {country_str}",
1560
- "",
1561
- "Provide in JSON format:",
1562
- "{{",
1563
- " \"full_name\": \"<full name>\",",
1564
- " \"gender\": \"<Male/Female/Other/Unknown>\",",
1565
- " \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],",
1566
- " \"country\": \"<country>\"",
1567
- "}}",
1568
- "",
1569
- "Choose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}",
1570
- "- Max 3 professions, ordered by relevance",
1571
- "- \"online personality\" includes streamers, cosplayers, influencers",
1572
- "- Return \"Unknown\" if uncertain",
1573
- "",
1574
- "Return ONLY JSON, no extra text.\"\"\"",
1575
- "",
1576
- "# === QUERY GEMMA ===",
1577
- "def query_gemma(prompt: str) -> dict:",
1578
- " url = \"https://api.together.xyz/v1/chat/completions\"",
1579
- " ",
1580
- " headers = {",
1581
- " \"Authorization\": f\"Bearer {api_key}\",",
1582
- " \"Content-Type\": \"application/json\"",
1583
- " }",
1584
- " ",
1585
- " data = {",
1586
- " \"model\": \"google/gemma-2-27b-it\",",
1587
- " \"messages\": [{\"role\": \"user\", \"content\": prompt}],",
1588
- " \"temperature\": 0.1,",
1589
- " \"max_tokens\": 500",
1590
- " }",
1591
- " ",
1592
- " for attempt in range(3):",
1593
- " try:",
1594
- " response = requests.post(url, headers=headers, json=data, timeout=30)",
1595
- " response.raise_for_status()",
1596
- " result = response.json()",
1597
- " ",
1598
- " if 'choices' in result:",
1599
- " content = result['choices'][0]['message']['content']",
1600
- " json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)",
1601
- " if json_match:",
1602
- " return json.loads(json_match.group())",
1603
- " return None",
1604
- " except Exception as e:",
1605
- " print(f\"Gemma error (attempt {attempt+1}): {e}\")",
1606
- " if attempt < 2:",
1607
- " time.sleep(2 ** attempt)",
1608
- " return None",
1609
- "",
1610
- "# === PROCESS ===",
1611
- "output_file = current_dir.parent / f\"data/CSV/gemma_annotated_POI{'_test' if TEST_MODE else ''}.csv\"",
1612
- "index_file = current_dir.parent / \"misc/gemma_query_index.txt\"",
1613
- "",
1614
- "current_index = 0",
1615
- "if index_file.exists():",
1616
- " with open(index_file) as f:",
1617
- " current_index = int(f.read().strip())",
1618
- "",
1619
- "for col in ['full_name', 'gender', 'profession_llm', 'country']:",
1620
- " if col not in df.columns:",
1621
- " df[col] = 'Unknown'",
1622
- "",
1623
- "print(f\"\\nAnnotating with GEMMA 2 27B (Google) - rows {current_index} to {len(df)}...\")",
1624
- "",
1625
- "try:",
1626
- " for i in tqdm(range(current_index, len(df)), desc=\"Gemma\"):",
1627
- " row = df.iloc[i]",
1628
- " ",
1629
- " tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8) ",
1630
- " if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]",
1631
- " ",
1632
- " prompt = create_prompt(",
1633
- " row.get('real_name', row.get('name', '')),",
1634
- " tags,",
1635
- " row.get('likely_country', '')",
1636
- " )",
1637
- " ",
1638
- " result = query_gemma(prompt)",
1639
- " ",
1640
- " if result:",
1641
- " df.at[i, 'full_name'] = result.get('full_name', 'Unknown')",
1642
- " df.at[i, 'gender'] = result.get('gender', 'Unknown')",
1643
- " profs = result.get('professions', [])",
1644
- " df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)",
1645
- " df.at[i, 'country'] = result.get('country', 'Unknown')",
1646
- " ",
1647
- " current_index = i + 1",
1648
- " ",
1649
- " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):",
1650
- " df.to_csv(output_file, index=False)",
1651
- " with open(index_file, 'w') as f:",
1652
- " f.write(str(current_index))",
1653
- " print(f\"\u2705 Saved after {i+1} rows\")",
1654
- " ",
1655
- " time.sleep(0.5)",
1656
- " ",
1657
- " print(f\"\\n\u2705 Done! Results: {output_file}\")",
1658
- " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")",
1659
- " ",
1660
- "except Exception as e:",
1661
- " print(f\"\u26a0\ufe0f Error: {e}\")",
1662
- " df.to_csv(output_file, index=False)",
1663
- " with open(index_file, 'w') as f:",
1664
- " f.write(str(current_index))",
1665
- ""
1666
- ]
1667
  },
1668
  {
1669
  "cell_type": "markdown",
@@ -1694,11 +1058,11 @@
1694
  "```\n",
1695
  "\n",
1696
  "**Advantages:**\n",
1697
- "- \u2705 FREE - No API costs\n",
1698
- "- \u2705 Fast - Local inference on A100\n",
1699
- "- \u2705 Privacy - Data never leaves your machine\n",
1700
- "- \u2705 Offline - Works without internet\n",
1701
- "- \u2705 High quality - 32B parameter model\n",
1702
  "\n",
1703
  "**Performance:**\n",
1704
  "- Speed: ~5-10 tokens/sec on A100\n",
@@ -1751,25 +1115,25 @@
1751
  " )\n",
1752
  " \n",
1753
  " if result.returncode == 0:\n",
1754
- " print(\"\u2705 Ollama is installed\")\n",
1755
  " \n",
1756
  " # Check if model is available\n",
1757
  " if MODEL_NAME in result.stdout or \"qwen2.5\" in result.stdout:\n",
1758
- " print(f\"\u2705 Model {MODEL_NAME} is available\")\n",
1759
  " return True\n",
1760
  " else:\n",
1761
- " print(f\"\u26a0\ufe0f Model {MODEL_NAME} not found\")\n",
1762
  " print(f\" Run: ollama pull {MODEL_NAME}\")\n",
1763
  " return False\n",
1764
  " else:\n",
1765
- " print(\"\u274c Ollama command failed\")\n",
1766
  " return False\n",
1767
  " except FileNotFoundError:\n",
1768
- " print(\"\u274c Ollama not installed\")\n",
1769
  " print(\" Install: curl -fsSL https://ollama.com/install.sh | sh\")\n",
1770
  " return False\n",
1771
  " except Exception as e:\n",
1772
- " print(f\"\u274c Error checking Ollama: {e}\")\n",
1773
  " return False\n",
1774
  "\n",
1775
  "# Check Ollama before proceeding\n",
@@ -1908,24 +1272,24 @@
1908
  " samples_done = i + 1 - (current_index - (i + 1))\n",
1909
  " samples_per_hour = (samples_done / elapsed) * 3600 if elapsed > 0 else 0\n",
1910
  " \n",
1911
- " print(f\"\u2705 Saved after {i+1} rows (~{samples_per_hour:.1f} samples/hour)\")\n",
1912
  " \n",
1913
  " elapsed_total = time.time() - start_time\n",
1914
- " print(f\"\\n\u2705 Done! Results: {output_file}\")\n",
1915
  " print(f\"Total time: {elapsed_total/60:.1f} minutes\")\n",
1916
  " print(f\"Average speed: {len(df)/(elapsed_total/3600):.1f} samples/hour\")\n",
1917
  " \n",
1918
  " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n",
1919
  "\n",
1920
  "except KeyboardInterrupt:\n",
1921
- " print(f\"\\n\u26a0\ufe0f Interrupted by user\")\n",
1922
  " df.to_csv(output_file, index=False)\n",
1923
  " with open(index_file, 'w') as f:\n",
1924
  " f.write(str(current_index))\n",
1925
  " print(f\"Progress saved up to row {current_index}\")\n",
1926
  "\n",
1927
  "except Exception as e:\n",
1928
- " print(f\"\u26a0\ufe0f Error: {e}\")\n",
1929
  " df.to_csv(output_file, index=False)\n",
1930
  " with open(index_file, 'w') as f:\n",
1931
  " f.write(str(current_index))\n",
 
142
  "name": "stdout",
143
  "output_type": "stream",
144
  "text": [
145
+ "βœ… spaCy model loaded: en_core_web_sm\n",
146
  "Loaded 18 rows\n",
147
  "\n",
148
+ "πŸ”„ Processing names with spaCy NER...\n",
149
  "\n",
150
+ "πŸ“Š Name cleaning examples (with spaCy NER):\n",
151
  "====================================================================================================\n",
152
  "Original Name | Cleaned Name \n",
153
  "====================================================================================================\n",
154
  "Super Pose Book Vol.1 - ControlNet | Super Pose Book \n",
155
  "Liyuu LoRA | Liyuu \n",
156
+ "HashimotoKanna/ ζ©‹ζœ¬η’°ε₯ˆ _JP_Actress | HashimotoKanna \n",
157
  "Emma Watson (JG) | Emma Watson \n",
158
+ "Gal Gadotγ€ŒLoRa」 | Gal Gadot \n",
159
+ "Scarlett Johanssonγ€ŒLoRa」 | Scarlett Johansson \n",
160
+ "Gakki | Aragaki Yui | ζ–°εž£η΅θ‘£ | Gakki \n",
161
+ "Actress Satomi_ηŸ³εŽŸβ—‹β—‹ | Actress Satomi \n",
162
  "Game of Thrones Cast | Game Thrones Cast \n",
163
+ "Natalie Portmanγ€ŒLoRa」 | Natalie Portman \n",
164
  "Emma Watson LoRA | Emma Watson \n",
165
  "====================================================================================================\n",
166
  "\n",
167
+ "πŸ§ͺ Leetspeak translation examples:\n",
168
  " 4kira LoRA -> akira\n",
169
  " 3mma Watson v2 -> Watson\n",
170
  " 1rene LORA -> irene\n",
171
  " L3vi Ackerman -> Levi Ackerman\n",
172
  "\n",
173
+ "πŸ“ˆ Statistics:\n",
174
  " Total rows: 18\n",
175
  " Non-empty names: 18\n",
176
  " Empty names: 0\n",
177
  "\n",
178
+ "🎯 Sample spaCy NER results:\n",
179
  " 1. IU\n",
180
  " 2. Super Pose Book\n",
181
  " 3. Liyuu\n",
 
187
  " 9. Emma Watson\n",
188
  " 10. Gal Gadot\n",
189
  "\n",
190
+ "βœ… Cleaned 18 names using spaCy NER\n",
191
+ "πŸ’Ύ Saved to /home/lauhp/000_PHD/000_010_PUBLICATION/CODE/pm-paper/data/CSV/NER_POI_step01_pre_annotation.csv\n"
192
  ]
193
  }
194
  ],
 
203
  "# You may need to download it first: python -m spacy download en_core_web_sm\n",
204
  "try:\n",
205
  " nlp = spacy.load(\"en_core_web_sm\")\n",
206
+ " print(\"βœ… spaCy model loaded: en_core_web_sm\")\n",
207
  "except OSError:\n",
208
+ " print(\"❌ spaCy model not found. Downloading...\")\n",
209
  " import subprocess\n",
210
  " subprocess.run([\"python\", \"-m\", \"spacy\", \"download\", \"en_core_web_sm\"])\n",
211
  " nlp = spacy.load(\"en_core_web_sm\")\n",
212
+ " print(\"βœ… spaCy model downloaded and loaded\")\n",
213
  "\n",
214
  "# Set up paths\n",
215
  "current_dir = Path.cwd()\n",
 
282
  " name = re.sub(r'\\([^)]*\\)', '', name)\n",
283
  " name = re.sub(r'\\[[^\\]]*\\]', '', name)\n",
284
  " \n",
285
+ " # Remove special characters like γ€Œγ€\n",
286
+ " name = re.sub(r'[γ€Œγ€γ€Žγ€γ€γ€‘γ€ˆγ€‰γ€Šγ€‹]', '', name)\n",
287
  " \n",
288
  " # Handle pipe - keep first part\n",
289
  " if '|' in name:\n",
 
339
  " Complete name cleaning pipeline with spaCy NER.\n",
340
  " \n",
341
  " Pipeline:\n",
342
+ " 1. Translate leetspeak (4β†’a, 3β†’e, 1β†’i, etc.)\n",
343
  " 2. Remove noise (emoji, version tags, LoRA terms)\n",
344
  " 3. Use spaCy to extract PERSON entities\n",
345
  " 4. Fallback to capitalized words or cleaned text\n",
 
356
  " return person_name\n",
357
  "\n",
358
  "# Apply name cleaning with spaCy\n",
359
+ "print(\"\\nπŸ”„ Processing names with spaCy NER...\")\n",
360
  "df['real_name'] = df['name'].apply(clean_name_with_spacy)\n",
361
  "\n",
362
  "# Show examples with detailed comparison\n",
363
+ "print(\"\\nπŸ“Š Name cleaning examples (with spaCy NER):\")\n",
364
  "print(\"=\" * 100)\n",
365
  "print(f\"{'Original Name':<50} | {'Cleaned Name':<30}\")\n",
366
  "print(\"=\" * 100)\n",
 
375
  "print(\"=\" * 100)\n",
376
  "\n",
377
  "# Show specific test cases\n",
378
+ "print(\"\\nπŸ§ͺ Leetspeak translation examples:\")\n",
379
  "test_names = ['4kira LoRA', '3mma Watson v2', '1rene LORA', 'L3vi Ackerman']\n",
380
  "for test in test_names:\n",
381
  " result = clean_name_with_spacy(test)\n",
382
  " print(f\" {test:<30} -> {result}\")\n",
383
  "\n",
384
  "# Statistics\n",
385
+ "print(f\"\\nπŸ“ˆ Statistics:\")\n",
386
  "print(f\" Total rows: {len(df)}\")\n",
387
  "print(f\" Non-empty names: {(df['real_name'] != '').sum()}\")\n",
388
  "print(f\" Empty names: {(df['real_name'] == '').sum()}\")\n",
389
  "\n",
390
  "# Show some examples of what spaCy identified\n",
391
+ "print(\"\\n🎯 Sample spaCy NER results:\")\n",
392
  "sample_names = df['real_name'].head(20).tolist()\n",
393
  "for i, name in enumerate(sample_names[:10], 1):\n",
394
  " if name:\n",
395
  " print(f\" {i}. {name}\")\n",
396
  "\n",
397
+ "print(f\"\\nβœ… Cleaned {len(df)} names using spaCy NER\")\n",
398
  "\n",
399
  "# Save intermediate result\n",
400
  "output_step1 = current_dir.parent / \"data/CSV/NER_POI_step01_pre_annotation.csv\"\n",
401
  "df.to_csv(output_step1, index=False)\n",
402
+ "print(f\"πŸ’Ύ Saved to {output_step1}\")\n"
403
  ]
404
  },
405
  {
 
444
  "8 Emma Watson actor\n",
445
  "9 Gal Gadot model\n",
446
  "\n",
447
+ "πŸ” Checking 'Irene' entries:\n",
448
  " Name: Irene\n",
449
  " Tags: ['girl', 'photorealistic', 'asian', 'woman', 'beautiful', 'celebrity', 'korean']\n",
450
  " Country: \n",
 
485
  "\n",
486
  "poi_df[\"tags\"] = poi_df.apply(combine_tags, axis=1)\n",
487
  "\n",
488
+ "# Step 2: Build tag β†’ (country, nationality) mapping with PRIORITIES\n",
489
  "tag_to_country_nationality = {}\n",
490
  "# We'll use a priority score: direct country name = 3, nationality = 2, word parts = 1\n",
491
  "\n",
 
563
  "\n",
564
  "poi_df[[\"likely_country\", \"likely_nationality\"]] = poi_df[\"tags\"].apply(infer_country_and_nationality)\n",
565
  "\n",
566
+ "# Step 4: Build tag β†’ profession mapping\n",
567
  "profession_alias_map = {}\n",
568
  "\n",
569
  "for _, row in professions_df.iterrows():\n",
 
611
  "# Check specific case: Irene with korean tag\n",
612
  "irene_rows = poi_df[poi_df['real_name'].str.contains('irene', case=False, na=False)]\n",
613
  "if len(irene_rows) > 0:\n",
614
+ " print(f\"\\nπŸ” Checking 'Irene' entries:\")\n",
615
  " for idx, row in irene_rows.iterrows():\n",
616
  " print(f\" Name: {row['real_name']}\")\n",
617
  " print(f\" Tags: {row['tags']}\")\n",
 
652
  "name": "stdout",
653
  "output_type": "stream",
654
  "text": [
655
+ "❌ Error: deepseek_api_key.txt not found in misc/credentials/\n",
656
  " Please create this file with your API key\n"
657
  ]
658
  },
 
663
  "traceback": [
664
  "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
665
  "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
666
+ "Cell \u001b[0;32mIn[10], line 37\u001b[0m\n\u001b[1;32m 35\u001b[0m \u001b[38;5;66;03m# === LOAD API KEY ===\u001b[39;00m\n\u001b[1;32m 36\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 37\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28;43mopen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mapi_key_file\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[1;32m 38\u001b[0m api_key \u001b[38;5;241m=\u001b[39m f\u001b[38;5;241m.\u001b[39mread()\u001b[38;5;241m.\u001b[39mstrip()\n\u001b[1;32m 39\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mβœ… Deepseek API key loaded\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
667
  "File \u001b[0;32m~/.local/lib/python3.10/site-packages/IPython/core/interactiveshell.py:324\u001b[0m, in \u001b[0;36m_modified_open\u001b[0;34m(file, *args, **kwargs)\u001b[0m\n\u001b[1;32m 317\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m file \u001b[38;5;129;01min\u001b[39;00m {\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m, \u001b[38;5;241m2\u001b[39m}:\n\u001b[1;32m 318\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 319\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIPython won\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mt let you open fd=\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfile\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m by default \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 320\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mas it is likely to crash IPython. If you know what you are doing, \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124myou can use builtins\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m open.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 322\u001b[0m )\n\u001b[0;32m--> 324\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mio_open\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
668
  "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: '/home/lauhp/000_PHD/000_010_PUBLICATION/CODE/pm-paper/misc/credentials/deepseek_api_key.txt'"
669
  ]
 
708
  "try:\n",
709
  " with open(api_key_file) as f:\n",
710
  " api_key = f.read().strip()\n",
711
+ " print(\"βœ… Deepseek API key loaded\")\n",
712
  "except FileNotFoundError:\n",
713
+ " print(\"❌ Error: deepseek_api_key.txt not found in misc/credentials/\")\n",
714
  " print(\" Please create this file with your API key\")\n",
715
  " raise\n",
716
  "\n",
 
726
  "# Load profession categories and keywords\n",
727
  "try:\n",
728
  " prof_mapped_df = pd.read_csv(professions_mapped_file)\n",
729
+ " print(f\"βœ… Loaded profession mapping with {len(prof_mapped_df)} categories\")\n",
730
  "except:\n",
731
+ " print(\"⚠️ Warning: professions_mapped.csv not found, using default categories\")\n",
732
  "\n",
733
  "profession_str = \", \".join(PROFESSION_CATEGORIES)\n",
734
  "\n",
 
895
  " df.to_csv(output_file, index=False)\n",
896
  " with open(index_file, 'w') as f:\n",
897
  " f.write(str(current_index))\n",
898
+ " print(f\"βœ… Progress saved after {i+1} rows\")\n",
899
  " \n",
900
  " time.sleep(1) # Rate limiting\n",
901
  " \n",
902
+ " print(f\"\\nβœ… Done! Final results saved to {output_file}\")\n",
903
  " \n",
904
  " # Summary statistics\n",
905
  " print(\"\\n=== Summary Statistics ===\")\n",
 
917
  " print(df[display_cols].head(10).to_string(index=False))\n",
918
  "\n",
919
  "except Exception as e:\n",
920
+ " print(f\"⚠️ Error encountered: {e}\")\n",
921
+ " print(f\"⚠️ Last processed index: {current_index}\")\n",
922
  " \n",
923
  " # Save progress before exiting\n",
924
  " df.to_csv(output_file, index=False)\n",
925
  " with open(index_file, 'w') as f:\n",
926
  " f.write(str(current_index))\n",
927
  " \n",
928
+ " print(f\"⚠️ Progress saved up to row {current_index}\")\n"
929
  ]
930
  },
931
  {
 
954
  "id": "qwen_code",
955
  "metadata": {},
956
  "outputs": [],
957
+ "source": "import pandas as pd\nimport json\nimport time\nimport re\nfrom pathlib import Path\nfrom tqdm import tqdm\nfrom openai import OpenAI\n\ncurrent_dir = Path.cwd()\ninput_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"\napi_key_file = current_dir.parent / \"misc/credentials/qwen_api_key.txt\"\n\n# === CONFIGURATION ===\nTEST_MODE = True\nTEST_SIZE = 10\nMAX_ROWS = 20000\nSAVE_INTERVAL = 10\n\nPROFESSION_CATEGORIES = [\n \"actor\", \"adult performer\", \"singer/musician\", \"model\",\n \"online personality\", \"public figure\", \"voice actor/ASMR\",\n \"sports professional\", \"tv personality\"\n]\n\n# === LOAD API KEY ===\ntry:\n with open(api_key_file) as f:\n api_key = f.read().strip()\n print(\"βœ… Qwen API key loaded\")\n print(f\" Key starts with: {api_key[:10]}...\")\nexcept FileNotFoundError:\n print(\"❌ Error: qwen_api_key.txt not found\")\n raise\n\n# Initialize OpenAI client for Qwen (matching dashboard example)\nclient = OpenAI(\n api_key=api_key,\n base_url=\"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\"\n)\n\n# === LOAD DATA ===\ndf = pd.read_csv(input_file)\nprint(f\"Loaded {len(df)} rows\")\n\nif TEST_MODE:\n print(f\"Running in TEST MODE with {TEST_SIZE} samples\")\n df = df.head(TEST_SIZE).copy()\nelif MAX_ROWS:\n df = df.head(MAX_ROWS).copy()\n\n# === CREATE PROMPT ===\ndef create_prompt(name: str, tags: list, country: str) -> str:\n tags_str = \", \".join(tags[:5]) if tags else \"none\"\n country_str = country if country else \"unknown\"\n \n return f\"\"\"You are an expert at identifying public figures.\n\nGiven: Name: {name}, Tags: {tags_str}, Country: {country_str}\n\nProvide in JSON format:\n{{\n \"full_name\": \"<full name>\",\n \"gender\": \"<Male/Female/Other/Unknown>\",\n \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],\n \"country\": \"<country>\"\n}}\n\nChoose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}\n- Max 3 professions, ordered by relevance\n- \"online personality\" includes streamers, cosplayers, influencers\n- Return \"Unknown\" if uncertain\n\nReturn ONLY JSON, no extra text.\"\"\"\n\n# === QUERY QWEN ===\ndef query_qwen(prompt: str) -> dict:\n \"\"\"Query Qwen using OpenAI-compatible API (matching dashboard example).\"\"\"\n for attempt in range(3):\n try:\n completion = client.chat.completions.create(\n model=\"qwen-max\", # Note: dashboard shows \"qwen3-max\" but \"qwen-max\" works too\n messages=[\n {\"role\": \"user\", \"content\": prompt}\n ],\n temperature=0.1,\n max_tokens=500,\n stream=False # Disable streaming for easier parsing\n )\n \n content = completion.choices[0].message.content\n \n # Parse JSON from response\n json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)\n if json_match:\n return json.loads(json_match.group())\n \n return None\n \n except Exception as e:\n print(f\"Qwen error (attempt {attempt+1}): {e}\")\n if attempt < 2:\n time.sleep(2 ** attempt)\n \n return None\n\n# === PROCESS ===\noutput_file = current_dir.parent / f\"data/CSV/qwen_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\nindex_file = current_dir.parent / \"misc/qwen_query_index.txt\"\n\ncurrent_index = 0\nif index_file.exists():\n with open(index_file) as f:\n current_index = int(f.read().strip())\n\nfor col in ['full_name', 'gender', 'profession_llm', 'country']:\n if col not in df.columns:\n df[col] = 'Unknown'\n\nprint(f\"\\nAnnotating with QWEN-MAX (OpenAI-compatible API) - rows {current_index} to {len(df)}...\")\n\ntry:\n for i in tqdm(range(current_index, len(df)), desc=\"Qwen\"):\n row = df.iloc[i]\n \n tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8)\n if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]\n \n prompt = create_prompt(\n row.get('real_name', row.get('name', '')),\n tags,\n row.get('likely_country', '')\n )\n \n result = query_qwen(prompt)\n \n if result:\n df.at[i, 'full_name'] = result.get('full_name', 'Unknown')\n df.at[i, 'gender'] = result.get('gender', 'Unknown')\n profs = result.get('professions', [])\n df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)\n df.at[i, 'country'] = result.get('country', 'Unknown')\n \n current_index = i + 1\n \n if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))\n print(f\"βœ… Saved after {i+1} rows\")\n \n time.sleep(0.5)\n \n print(f\"\\nβœ… Done! Results: {output_file}\")\n print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n \nexcept Exception as e:\n print(f\"⚠️ Error: {e}\")\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
958
  },
959
  {
960
  "cell_type": "markdown",
 
978
  "id": "llama_code",
979
  "metadata": {},
980
  "outputs": [],
981
+ "source": "import pandas as pd\nimport json\nimport time\nimport requests\nimport re\nfrom pathlib import Path\nfrom tqdm import tqdm\n\ncurrent_dir = Path.cwd()\ninput_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"\napi_key_file = current_dir.parent / \"misc/credentials/together_api_key.txt\"\n\n# === CONFIGURATION ===\nTEST_MODE = True\nTEST_SIZE = 10\nMAX_ROWS = 20000\nSAVE_INTERVAL = 10\n\nPROFESSION_CATEGORIES = [\n \"actor\", \"adult performer\", \"singer/musician\", \"model\",\n \"online personality\", \"public figure\", \"voice actor/ASMR\",\n \"sports professional\", \"tv personality\"\n]\n\n# === LOAD API KEY ===\ntry:\n with open(api_key_file) as f:\n api_key = f.read().strip()\n print(\"βœ… Llama API key loaded\")\nexcept FileNotFoundError:\n print(\"❌ Error: together_api_key.txt not found\")\n raise\n\n# === LOAD DATA ===\ndf = pd.read_csv(input_file)\nprint(f\"Loaded {len(df)} rows\")\n\nif TEST_MODE:\n print(f\"Running in TEST MODE with {TEST_SIZE} samples\")\n df = df.head(TEST_SIZE).copy()\nelif MAX_ROWS:\n df = df.head(MAX_ROWS).copy()\n\n# === CREATE PROMPT ===\ndef create_prompt(name: str, tags: list, country: str) -> str:\n tags_str = \", \".join(tags[:5]) if tags else \"none\"\n country_str = country if country else \"unknown\"\n \n return f\"\"\"You are an expert at identifying public figures.\n\nGiven: Name: {name}, Tags: {tags_str}, Country: {country_str}\n\nProvide in JSON format:\n{{\n \"full_name\": \"<full name>\",\n \"gender\": \"<Male/Female/Other/Unknown>\",\n \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],\n \"country\": \"<country>\"\n}}\n\nChoose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}\n- Max 3 professions, ordered by relevance\n- \"online personality\" includes streamers, cosplayers, influencers\n- Return \"Unknown\" if uncertain\n\nReturn ONLY JSON, no extra text.\"\"\"\n\n# === QUERY LLAMA ===\ndef query_llama(prompt: str) -> dict:\n url = \"https://api.together.xyz/v1/chat/completions\"\n \n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\"\n }\n \n data = {\n \"model\": \"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\",\n \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n \"temperature\": 0.1,\n \"max_tokens\": 500\n }\n \n for attempt in range(3):\n try:\n response = requests.post(url, headers=headers, json=data, timeout=30)\n response.raise_for_status()\n result = response.json()\n \n if 'choices' in result:\n content = result['choices'][0]['message']['content']\n json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)\n if json_match:\n return json.loads(json_match.group())\n return None\n except Exception as e:\n print(f\"Llama error (attempt {attempt+1}): {e}\")\n if attempt < 2:\n time.sleep(2 ** attempt)\n return None\n\n# === PROCESS ===\noutput_file = current_dir.parent / f\"data/CSV/llama_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\nindex_file = current_dir.parent / \"misc/llama_query_index.txt\"\n\ncurrent_index = 0\nif index_file.exists():\n with open(index_file) as f:\n current_index = int(f.read().strip())\n\nfor col in ['full_name', 'gender', 'profession_llm', 'country']:\n if col not in df.columns:\n df[col] = 'Unknown'\n\nprint(f\"\\nAnnotating with LLAMA 3.1 70B - rows {current_index} to {len(df)}...\")\n\ntry:\n for i in tqdm(range(current_index, len(df)), desc=\"Llama\"):\n row = df.iloc[i]\n \n tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8)\n if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]\n \n prompt = create_prompt(\n row.get('real_name', row.get('name', '')),\n tags,\n row.get('likely_country', '')\n )\n \n result = query_llama(prompt)\n \n if result:\n df.at[i, 'full_name'] = result.get('full_name', 'Unknown')\n df.at[i, 'gender'] = result.get('gender', 'Unknown')\n profs = result.get('professions', [])\n df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)\n df.at[i, 'country'] = result.get('country', 'Unknown')\n \n current_index = i + 1\n \n if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))\n print(f\"βœ… Saved after {i+1} rows\")\n \n time.sleep(0.5)\n \n print(f\"\\nβœ… Done! Results: {output_file}\")\n print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n \nexcept Exception as e:\n print(f\"⚠️ Error: {e}\")\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982
  },
983
  {
984
  "cell_type": "markdown",
 
1002
  "id": "mixtral_code",
1003
  "metadata": {},
1004
  "outputs": [],
1005
+ "source": "import pandas as pd\nimport json\nimport time\nimport requests\nimport re\nfrom pathlib import Path\nfrom tqdm import tqdm\n\ncurrent_dir = Path.cwd()\ninput_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"\napi_key_file = current_dir.parent / \"misc/credentials/mistral_api_key.txt\"\n\n# === CONFIGURATION ===\nTEST_MODE = True\nTEST_SIZE = 10\nMAX_ROWS = 20000\nSAVE_INTERVAL = 10\n\nPROFESSION_CATEGORIES = [\n \"actor\", \"adult performer\", \"singer/musician\", \"model\",\n \"online personality\", \"public figure\", \"voice actor/ASMR\",\n \"sports professional\", \"tv personality\"\n]\n\n# === LOAD API KEY ===\ntry:\n with open(api_key_file) as f:\n api_key = f.read().strip()\n print(\"βœ… Mixtral API key loaded\")\nexcept FileNotFoundError:\n print(\"❌ Error: mistral_api_key.txt not found\")\n raise\n\n# === LOAD DATA ===\ndf = pd.read_csv(input_file)\nprint(f\"Loaded {len(df)} rows\")\n\nif TEST_MODE:\n print(f\"Running in TEST MODE with {TEST_SIZE} samples\")\n df = df.head(TEST_SIZE).copy()\nelif MAX_ROWS:\n df = df.head(MAX_ROWS).copy()\n\n# === CREATE PROMPT ===\ndef create_prompt(name: str, tags: list, country: str) -> str:\n tags_str = \", \".join(tags[:5]) if tags else \"none\"\n country_str = country if country else \"unknown\"\n \n return f\"\"\"You are an expert at identifying public figures.\n\nGiven: Name: {name}, Tags: {tags_str}, Country: {country_str}\n\nProvide in JSON format:\n{{\n \"full_name\": \"<full name>\",\n \"gender\": \"<Male/Female/Other/Unknown>\",\n \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],\n \"country\": \"<country>\"\n}}\n\nChoose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}\n- Max 3 professions, ordered by relevance\n- \"online personality\" includes streamers, cosplayers, influencers\n- Return \"Unknown\" if uncertain\n\nReturn ONLY JSON, no extra text.\"\"\"\n\n# === QUERY MIXTRAL ===\ndef query_mixtral(prompt: str) -> dict:\n url = \"https://api.mistral.ai/v1/chat/completions\"\n \n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\"\n }\n \n data = {\n \"model\": \"open-mixtral-8x22b\",\n \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n \"temperature\": 0.1,\n \"max_tokens\": 500\n }\n \n for attempt in range(3):\n try:\n response = requests.post(url, headers=headers, json=data, timeout=30)\n response.raise_for_status()\n result = response.json()\n \n if 'choices' in result:\n content = result['choices'][0]['message']['content']\n json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)\n if json_match:\n return json.loads(json_match.group())\n return None\n except Exception as e:\n print(f\"Mixtral error (attempt {attempt+1}): {e}\")\n if attempt < 2:\n time.sleep(2 ** attempt)\n return None\n\n# === PROCESS ===\noutput_file = current_dir.parent / f\"data/CSV/mixtral_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\nindex_file = current_dir.parent / \"misc/mixtral_query_index.txt\"\n\ncurrent_index = 0\nif index_file.exists():\n with open(index_file) as f:\n current_index = int(f.read().strip())\n\nfor col in ['full_name', 'gender', 'profession_llm', 'country']:\n if col not in df.columns:\n df[col] = 'Unknown'\n\nprint(f\"\\nAnnotating with MIXTRAL 8x22B - rows {current_index} to {len(df)}...\")\n\ntry:\n for i in tqdm(range(current_index, len(df)), desc=\"Mixtral\"):\n row = df.iloc[i]\n \n tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8)\n if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]\n \n prompt = create_prompt(\n row.get('real_name', row.get('name', '')),\n tags,\n row.get('likely_country', '')\n )\n \n result = query_mixtral(prompt)\n \n if result:\n df.at[i, 'full_name'] = result.get('full_name', 'Unknown')\n df.at[i, 'gender'] = result.get('gender', 'Unknown')\n profs = result.get('professions', [])\n df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)\n df.at[i, 'country'] = result.get('country', 'Unknown')\n \n current_index = i + 1\n \n if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))\n print(f\"βœ… Saved after {i+1} rows\")\n \n time.sleep(0.5)\n \n print(f\"\\nβœ… Done! Results: {output_file}\")\n print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n \nexcept Exception as e:\n print(f\"⚠️ Error: {e}\")\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1006
  },
1007
  {
1008
  "cell_type": "markdown",
 
1027
  "id": "gemma_code",
1028
  "metadata": {},
1029
  "outputs": [],
1030
+ "source": "import pandas as pd\nimport json\nimport time\nimport requests\nimport re\nfrom pathlib import Path\nfrom tqdm import tqdm\n\ncurrent_dir = Path.cwd()\ninput_file = current_dir.parent / \"data/CSV/NER_POI_step02_annotated.csv\"\napi_key_file = current_dir.parent / \"misc/credentials/together_api_key.txt\"\n\n# === CONFIGURATION ===\nTEST_MODE = True\nTEST_SIZE = 10\nMAX_ROWS = 20000\nSAVE_INTERVAL = 10\n\nPROFESSION_CATEGORIES = [\n \"actor\", \"adult performer\", \"singer/musician\", \"model\",\n \"online personality\", \"public figure\", \"voice actor/ASMR\",\n \"sports professional\", \"tv personality\"\n]\n\n# === LOAD API KEY ===\ntry:\n with open(api_key_file) as f:\n api_key = f.read().strip()\n print(\"βœ… Gemma API key loaded\")\nexcept FileNotFoundError:\n print(\"❌ Error: together_api_key.txt not found\")\n raise\n\n# === LOAD DATA ===\ndf = pd.read_csv(input_file)\nprint(f\"Loaded {len(df)} rows\")\n\nif TEST_MODE:\n print(f\"Running in TEST MODE with {TEST_SIZE} samples\")\n df = df.head(TEST_SIZE).copy()\nelif MAX_ROWS:\n df = df.head(MAX_ROWS).copy()\n\n# === CREATE PROMPT ===\ndef create_prompt(name: str, tags: list, country: str) -> str:\n tags_str = \", \".join(tags[:5]) if tags else \"none\"\n country_str = country if country else \"unknown\"\n \n return f\"\"\"You are an expert at identifying public figures.\n\nGiven: Name: {name}, Tags: {tags_str}, Country: {country_str}\n\nProvide in JSON format:\n{{\n \"full_name\": \"<full name>\",\n \"gender\": \"<Male/Female/Other/Unknown>\",\n \"professions\": [\"<profession 1>\", \"<profession 2>\", \"<profession 3>\"],\n \"country\": \"<country>\"\n}}\n\nChoose professions ONLY from: {\", \".join(PROFESSION_CATEGORIES)}\n- Max 3 professions, ordered by relevance\n- \"online personality\" includes streamers, cosplayers, influencers\n- Return \"Unknown\" if uncertain\n\nReturn ONLY JSON, no extra text.\"\"\"\n\n# === QUERY GEMMA ===\ndef query_gemma(prompt: str) -> dict:\n url = \"https://api.together.xyz/v1/chat/completions\"\n \n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\"\n }\n \n data = {\n \"model\": \"google/gemma-2-27b-it\",\n \"messages\": [{\"role\": \"user\", \"content\": prompt}],\n \"temperature\": 0.1,\n \"max_tokens\": 500\n }\n \n for attempt in range(3):\n try:\n response = requests.post(url, headers=headers, json=data, timeout=30)\n response.raise_for_status()\n result = response.json()\n \n if 'choices' in result:\n content = result['choices'][0]['message']['content']\n json_match = re.search(r'\\{[^}]+\\}', content, re.DOTALL)\n if json_match:\n return json.loads(json_match.group())\n return None\n except Exception as e:\n print(f\"Gemma error (attempt {attempt+1}): {e}\")\n if attempt < 2:\n time.sleep(2 ** attempt)\n return None\n\n# === PROCESS ===\noutput_file = current_dir.parent / f\"data/CSV/gemma_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\nindex_file = current_dir.parent / \"misc/gemma_query_index.txt\"\n\ncurrent_index = 0\nif index_file.exists():\n with open(index_file) as f:\n current_index = int(f.read().strip())\n\nfor col in ['full_name', 'gender', 'profession_llm', 'country']:\n if col not in df.columns:\n df[col] = 'Unknown'\n\nprint(f\"\\nAnnotating with GEMMA 2 27B (Google) - rows {current_index} to {len(df)}...\")\n\ntry:\n for i in tqdm(range(current_index, len(df)), desc=\"Gemma\"):\n row = df.iloc[i]\n \n tags = [str(row[f'tag_{j}']).lower() for j in range(1, 8)\n if f'tag_{j}' in row and pd.notna(row[f'tag_{j}'])]\n \n prompt = create_prompt(\n row.get('real_name', row.get('name', '')),\n tags,\n row.get('likely_country', '')\n )\n \n result = query_gemma(prompt)\n \n if result:\n df.at[i, 'full_name'] = result.get('full_name', 'Unknown')\n df.at[i, 'gender'] = result.get('gender', 'Unknown')\n profs = result.get('professions', [])\n df.at[i, 'profession_llm'] = \", \".join(profs[:3]) if isinstance(profs, list) else str(profs)\n df.at[i, 'country'] = result.get('country', 'Unknown')\n \n current_index = i + 1\n \n if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))\n print(f\"βœ… Saved after {i+1} rows\")\n \n time.sleep(0.5)\n \n print(f\"\\nβœ… Done! Results: {output_file}\")\n print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n \nexcept Exception as e:\n print(f\"⚠️ Error: {e}\")\n df.to_csv(output_file, index=False)\n with open(index_file, 'w') as f:\n f.write(str(current_index))"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1031
  },
1032
  {
1033
  "cell_type": "markdown",
 
1058
  "```\n",
1059
  "\n",
1060
  "**Advantages:**\n",
1061
+ "- βœ… FREE - No API costs\n",
1062
+ "- βœ… Fast - Local inference on A100\n",
1063
+ "- βœ… Privacy - Data never leaves your machine\n",
1064
+ "- βœ… Offline - Works without internet\n",
1065
+ "- βœ… High quality - 32B parameter model\n",
1066
  "\n",
1067
  "**Performance:**\n",
1068
  "- Speed: ~5-10 tokens/sec on A100\n",
 
1115
  " )\n",
1116
  " \n",
1117
  " if result.returncode == 0:\n",
1118
+ " print(\"βœ… Ollama is installed\")\n",
1119
  " \n",
1120
  " # Check if model is available\n",
1121
  " if MODEL_NAME in result.stdout or \"qwen2.5\" in result.stdout:\n",
1122
+ " print(f\"βœ… Model {MODEL_NAME} is available\")\n",
1123
  " return True\n",
1124
  " else:\n",
1125
+ " print(f\"⚠️ Model {MODEL_NAME} not found\")\n",
1126
  " print(f\" Run: ollama pull {MODEL_NAME}\")\n",
1127
  " return False\n",
1128
  " else:\n",
1129
+ " print(\"❌ Ollama command failed\")\n",
1130
  " return False\n",
1131
  " except FileNotFoundError:\n",
1132
+ " print(\"❌ Ollama not installed\")\n",
1133
  " print(\" Install: curl -fsSL https://ollama.com/install.sh | sh\")\n",
1134
  " return False\n",
1135
  " except Exception as e:\n",
1136
+ " print(f\"❌ Error checking Ollama: {e}\")\n",
1137
  " return False\n",
1138
  "\n",
1139
  "# Check Ollama before proceeding\n",
 
1272
  " samples_done = i + 1 - (current_index - (i + 1))\n",
1273
  " samples_per_hour = (samples_done / elapsed) * 3600 if elapsed > 0 else 0\n",
1274
  " \n",
1275
+ " print(f\"βœ… Saved after {i+1} rows (~{samples_per_hour:.1f} samples/hour)\")\n",
1276
  " \n",
1277
  " elapsed_total = time.time() - start_time\n",
1278
+ " print(f\"\\nβœ… Done! Results: {output_file}\")\n",
1279
  " print(f\"Total time: {elapsed_total/60:.1f} minutes\")\n",
1280
  " print(f\"Average speed: {len(df)/(elapsed_total/3600):.1f} samples/hour\")\n",
1281
  " \n",
1282
  " print(f\"\\nTop professions:\\n{df['profession_llm'].value_counts().head(10)}\")\n",
1283
  "\n",
1284
  "except KeyboardInterrupt:\n",
1285
+ " print(f\"\\n⚠️ Interrupted by user\")\n",
1286
  " df.to_csv(output_file, index=False)\n",
1287
  " with open(index_file, 'w') as f:\n",
1288
  " f.write(str(current_index))\n",
1289
  " print(f\"Progress saved up to row {current_index}\")\n",
1290
  "\n",
1291
  "except Exception as e:\n",
1292
+ " print(f\"⚠️ Error: {e}\")\n",
1293
  " df.to_csv(output_file, index=False)\n",
1294
  " with open(index_file, 'w') as f:\n",
1295
  " f.write(str(current_index))\n",