laura.wagner commited on
Commit
d9c29ce
Β·
1 Parent(s): 53ffa2f

adjusted llm query code

Browse files
jupyter_notebooks/.ipynb_checkpoints/Section_2-3-1_Tag_occurences-checkpoint.ipynb ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {},
4
+ "nbformat": 4,
5
+ "nbformat_minor": 5
6
+ }
jupyter_notebooks/.ipynb_checkpoints/Section_2-3-4_Figure_8_Step_1_LLM_annotation-checkpoint.ipynb CHANGED
@@ -466,7 +466,7 @@
466
  " 'max_new_tokens': 512,\n",
467
  " 'temperature': 0.05,\n",
468
  " 'do_sample': True,\n",
469
- " 'top_p': 0.8,\n",
470
  " }\n",
471
  " },\n",
472
  " 'gemma': {\n",
@@ -477,7 +477,7 @@
477
  " 'max_new_tokens': 512,\n",
478
  " 'temperature': 0.1,\n",
479
  " 'do_sample': True,\n",
480
- " 'top_p': 0.9,\n",
481
  " }\n",
482
  " },\n",
483
  " 'qwen': {\n",
@@ -489,7 +489,7 @@
489
  " llm_int8_has_fp16_weight=False\n",
490
  " ),\n",
491
  " 'generation_params': {\n",
492
- " 'max_new_tokens': 100,\n",
493
  " 'temperature': 0.1,\n",
494
  " 'do_sample': False,\n",
495
  " }\n",
@@ -951,6 +951,14 @@
951
  "# annotate_dataset(model_type='mistral', test_mode=True, test_size=100)\n"
952
  ]
953
  },
 
 
 
 
 
 
 
 
954
  {
955
  "cell_type": "code",
956
  "execution_count": null,
@@ -987,7 +995,7 @@
987
  "output_file = current_dir.parent / f\"data/CSV/eurollm_local_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\n",
988
  "\n",
989
  "# Model settings\n",
990
- "MODEL_NAME = \"utter-project/EuroLLM-9B\"\n",
991
  "#MODEL_NAME = \"Qwen/Qwen2.5-32B-Instruct\"\n",
992
  "#MODEL_NAME = \"Qwen/Qwen2.5-14B-Instruct\"\n",
993
  "#MODEL_NAME = \"Qwen/Qwen3-235B-A22B-Instruct-2507-FP8\"\n",
@@ -1159,27 +1167,19 @@
1159
  "3. Gender: MUST be exactly one word: Male, Female, Other, or Unknown. VALUE ONLY.\n",
1160
  "4. Professions: Choose up to 3 from this list ONLY: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality. Comma-separated. VALUE ONLY.\n",
1161
  "5. Primary country: Country name only (e.g., \"China\", \"United States\", \"Colombia\"). VALUE ONLY.\n",
1162
- "6. If uncertain about an item, write \"Unknown\"\n",
1163
  "\n",
1164
  "CRITICAL RULES FOR PROFESSIONS (Line 4):\n",
1165
  "- ONLY use the exact profession categories listed above\n",
1166
  "- DO NOT use descriptive words like \"sexy\", \"photorealistic\", \"celebrity\"\n",
1167
  "- DO NOT copy the hint words as professions\n",
1168
- "- If uncertain write \"Unknown\"\n",
1169
  "- Valid professions are ONLY: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality\n",
1170
  "- Actress = actor, streamer = online personality, YouTuber = online personality\n",
1171
  "\n",
1172
  "OTHER RULES:\n",
1173
  "- Use \"Unknown\" when uncertain or for fictional characters\n",
1174
  "- NO explanatory text, NO labels like \"Gender:\", NO prefixes\n",
1175
- "- For multi-role people, list up to 3 categories by relevance\n",
1176
- "\n",
1177
- "EXAMPLE FORMAT:\n",
1178
- "1. Taylor Swift\n",
1179
- "2. None\n",
1180
- "3. Female\n",
1181
- "4. singer/musician, public figure\n",
1182
- "5. United States\"\"\"\n",
1183
  "\n",
1184
  "# Create prompts\n",
1185
  "print(\"\\nCreating prompts...\")\n",
@@ -1418,10 +1418,500 @@
1418
  "print(\"βœ… Finished full dataset.\")"
1419
  ]
1420
  },
 
 
 
 
 
 
 
 
1421
  {
1422
  "cell_type": "code",
1423
  "execution_count": null,
1424
  "id": "a55a5e30-83f3-4f7c-a537-b1216d4e8a07",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1425
  "metadata": {},
1426
  "outputs": [],
1427
  "source": []
 
466
  " 'max_new_tokens': 512,\n",
467
  " 'temperature': 0.05,\n",
468
  " 'do_sample': True,\n",
469
+ " 'top_p': 1.0,\n",
470
  " }\n",
471
  " },\n",
472
  " 'gemma': {\n",
 
477
  " 'max_new_tokens': 512,\n",
478
  " 'temperature': 0.1,\n",
479
  " 'do_sample': True,\n",
480
+ " 'top_p': 1.0,\n",
481
  " }\n",
482
  " },\n",
483
  " 'qwen': {\n",
 
489
  " llm_int8_has_fp16_weight=False\n",
490
  " ),\n",
491
  " 'generation_params': {\n",
492
+ " 'max_new_tokens': 512,\n",
493
  " 'temperature': 0.1,\n",
494
  " 'do_sample': False,\n",
495
  " }\n",
 
951
  "# annotate_dataset(model_type='mistral', test_mode=True, test_size=100)\n"
952
  ]
953
  },
954
+ {
955
+ "cell_type": "markdown",
956
+ "id": "6431d347-d80c-4e8b-83a7-531e5df95a72",
957
+ "metadata": {},
958
+ "source": [
959
+ "## EuroLLM-9B-Instruct"
960
+ ]
961
+ },
962
  {
963
  "cell_type": "code",
964
  "execution_count": null,
 
995
  "output_file = current_dir.parent / f\"data/CSV/eurollm_local_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\n",
996
  "\n",
997
  "# Model settings\n",
998
+ "MODEL_NAME = \"utter-project/EuroLLM-9B-Instruct\"\n",
999
  "#MODEL_NAME = \"Qwen/Qwen2.5-32B-Instruct\"\n",
1000
  "#MODEL_NAME = \"Qwen/Qwen2.5-14B-Instruct\"\n",
1001
  "#MODEL_NAME = \"Qwen/Qwen3-235B-A22B-Instruct-2507-FP8\"\n",
 
1167
  "3. Gender: MUST be exactly one word: Male, Female, Other, or Unknown. VALUE ONLY.\n",
1168
  "4. Professions: Choose up to 3 from this list ONLY: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality. Comma-separated. VALUE ONLY.\n",
1169
  "5. Primary country: Country name only (e.g., \"China\", \"United States\", \"Colombia\"). VALUE ONLY.\n",
 
1170
  "\n",
1171
  "CRITICAL RULES FOR PROFESSIONS (Line 4):\n",
1172
  "- ONLY use the exact profession categories listed above\n",
1173
  "- DO NOT use descriptive words like \"sexy\", \"photorealistic\", \"celebrity\"\n",
1174
  "- DO NOT copy the hint words as professions\n",
1175
+ "- If uncertain about profession, write \"Unknown\"\n",
1176
  "- Valid professions are ONLY: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality\n",
1177
  "- Actress = actor, streamer = online personality, YouTuber = online personality\n",
1178
  "\n",
1179
  "OTHER RULES:\n",
1180
  "- Use \"Unknown\" when uncertain or for fictional characters\n",
1181
  "- NO explanatory text, NO labels like \"Gender:\", NO prefixes\n",
1182
+ "- For multi-role people, list up to 3 categories by relevance\"\"\"\n",
 
 
 
 
 
 
 
1183
  "\n",
1184
  "# Create prompts\n",
1185
  "print(\"\\nCreating prompts...\")\n",
 
1418
  "print(\"βœ… Finished full dataset.\")"
1419
  ]
1420
  },
1421
+ {
1422
+ "cell_type": "markdown",
1423
+ "id": "472e5ac2-ec04-4bfa-8a67-116277238c15",
1424
+ "metadata": {},
1425
+ "source": [
1426
+ "## Mistral 24b instruct"
1427
+ ]
1428
+ },
1429
  {
1430
  "cell_type": "code",
1431
  "execution_count": null,
1432
  "id": "a55a5e30-83f3-4f7c-a537-b1216d4e8a07",
1433
+ "metadata": {
1434
+ "execution": {
1435
+ "iopub.execute_input": "2025-12-09T22:16:21.002786Z",
1436
+ "iopub.status.busy": "2025-12-09T22:16:21.002337Z"
1437
+ }
1438
+ },
1439
+ "outputs": [
1440
+ {
1441
+ "name": "stderr",
1442
+ "output_type": "stream",
1443
+ "text": [
1444
+ "/shares/weddigen.ki.uzh/laura_wagner/phase_01/pm-paper/.venv/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
1445
+ " from .autonotebook import tqdm as notebook_tqdm\n"
1446
+ ]
1447
+ },
1448
+ {
1449
+ "name": "stdout",
1450
+ "output_type": "stream",
1451
+ "text": [
1452
+ "Loading model: mistralai/Mistral-Small-Instruct-2409\n",
1453
+ "Cache directory: /shares/weddigen.ki.uzh/laura_wagner/phase_01/pm-paper/data/models\n",
1454
+ "This may take a while on first run (~65GB download)...\n",
1455
+ "\n",
1456
+ "Device: cuda\n",
1457
+ "Loading tokenizer...\n",
1458
+ "βœ… Tokenizer loaded\n",
1459
+ "Loading model (this may take several minutes)...\n"
1460
+ ]
1461
+ },
1462
+ {
1463
+ "name": "stderr",
1464
+ "output_type": "stream",
1465
+ "text": [
1466
+ "`torch_dtype` is deprecated! Use `dtype` instead!\n",
1467
+ "Loading checkpoint shards: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 9/9 [02:42<00:00, 18.06s/it]\n"
1468
+ ]
1469
+ },
1470
+ {
1471
+ "name": "stdout",
1472
+ "output_type": "stream",
1473
+ "text": [
1474
+ "βœ… Model loaded\n",
1475
+ "VRAM used: 21.40 GB\n",
1476
+ "\n",
1477
+ "Loading raw input CSV...\n",
1478
+ "Loaded 50861 rows from input file\n",
1479
+ "Found existing annotations, merging...\n"
1480
+ ]
1481
+ },
1482
+ {
1483
+ "name": "stderr",
1484
+ "output_type": "stream",
1485
+ "text": [
1486
+ "/tmp/ipykernel_3104208/1997558719.py:113: DtypeWarning: Columns (52,53,54,55,56) have mixed types. Specify dtype option on import or set low_memory=False.\n",
1487
+ " existing_df = pd.read_csv(output_file)\n"
1488
+ ]
1489
+ },
1490
+ {
1491
+ "name": "stdout",
1492
+ "output_type": "stream",
1493
+ "text": [
1494
+ "Existing annotations has 50861 rows\n",
1495
+ "Merged annotations, continuing with 50861 total rows\n",
1496
+ "βœ… Loaded professions.csv\n",
1497
+ "βœ… Loaded profession mapping with 9 categories\n",
1498
+ "Loaded 50861 rows\n",
1499
+ "\n",
1500
+ "Profession categories (9):\n",
1501
+ " - actor\n",
1502
+ " - adult performer\n",
1503
+ " - singer/musician\n",
1504
+ " - model\n",
1505
+ " - online personality\n",
1506
+ " - public figure\n",
1507
+ " - voice actor/ASMR\n",
1508
+ " - sports professional\n",
1509
+ " - tv personality\n",
1510
+ "\n",
1511
+ "Creating prompts...\n",
1512
+ "βœ… Prompts created\n",
1513
+ "Resuming from index 8810\n"
1514
+ ]
1515
+ },
1516
+ {
1517
+ "name": "stderr",
1518
+ "output_type": "stream",
1519
+ "text": [
1520
+ "Mistral Local: 0%| | 0/42051 [00:00<?, ?it/s]/shares/weddigen.ki.uzh/laura_wagner/phase_01/pm-paper/.venv/lib/python3.11/site-packages/bitsandbytes/autograd/_functions.py:181: UserWarning: MatMul8bitLt: inputs will be cast from torch.bfloat16 to float16 during quantization\n",
1521
+ " warnings.warn(f\"MatMul8bitLt: inputs will be cast from {A.dtype} to float16 during quantization\")\n",
1522
+ "Mistral Local: 0%| | 7/42051 [00:57<93:01:03, 7.96s/it] "
1523
+ ]
1524
+ }
1525
+ ],
1526
+ "source": [
1527
+ "import pandas as pd\n",
1528
+ "import json\n",
1529
+ "import time\n",
1530
+ "import re\n",
1531
+ "from pathlib import Path\n",
1532
+ "from tqdm import tqdm\n",
1533
+ "import torch\n",
1534
+ "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig\n",
1535
+ "\n",
1536
+ "current_dir = Path.cwd()\n",
1537
+ "input_file = current_dir.parent / \"data/CSV/model_adapter/real_person_adapter_step_02_NER.csv\"\n",
1538
+ "professions_file = current_dir.parent / \"misc/lists/professions.csv\"\n",
1539
+ "professions_mapped_file = current_dir.parent / \"misc/lists/professions_mapped.csv\"\n",
1540
+ "# === PROCESS DATA ===\n",
1541
+ "\n",
1542
+ "\n",
1543
+ "# === CONFIGURATION ===\n",
1544
+ "TEST_MODE = False\n",
1545
+ "TEST_SIZE = 100\n",
1546
+ "MAX_ROWS = 50862\n",
1547
+ "SAVE_INTERVAL = 10\n",
1548
+ "\n",
1549
+ "output_file = current_dir.parent / f\"data/CSV/mistral24_local_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\n",
1550
+ "index_file = current_dir.parent / \"misc/query_indicies/mistral24_local_query_index.txt\"\n",
1551
+ "\n",
1552
+ "\n",
1553
+ "# Model settings\n",
1554
+ "#MODEL_NAME = \"mistralai/Mistral-Small-3.1-24B-Instruct-2503\"\n",
1555
+ "MODEL_NAME = \"mistralai/Mistral-Small-Instruct-2409\"\n",
1556
+ "#MODEL_NAME = \"mistralai/Mistral-7B-Instruct-v0.3\"\n",
1557
+ "CACHE_DIR = current_dir.parent / \"data/models\"\n",
1558
+ "CACHE_DIR.mkdir(parents=True, exist_ok=True)\n",
1559
+ "\n",
1560
+ "# Define the SPECIFIC profession categories\n",
1561
+ "PROFESSION_CATEGORIES = [\n",
1562
+ " \"actor\",\n",
1563
+ " \"adult performer\",\n",
1564
+ " \"singer/musician\",\n",
1565
+ " \"model\",\n",
1566
+ " \"online personality\",\n",
1567
+ " \"public figure\",\n",
1568
+ " \"voice actor/ASMR\",\n",
1569
+ " \"sports professional\",\n",
1570
+ " \"tv personality\"\n",
1571
+ "]\n",
1572
+ "\n",
1573
+ "# === LOAD MODEL ===\n",
1574
+ "print(f\"Loading model: {MODEL_NAME}\")\n",
1575
+ "print(f\"Cache directory: {CACHE_DIR}\")\n",
1576
+ "print(f\"This may take a while on first run (~65GB download)...\\n\")\n",
1577
+ "\n",
1578
+ "# Check GPU availability\n",
1579
+ "device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
1580
+ "print(f\"Device: {device}\")\n",
1581
+ "\n",
1582
+ "if device == \"cpu\":\n",
1583
+ " print(\"⚠️ WARNING: No GPU detected! Inference will be VERY slow.\")\n",
1584
+ " print(\" Consider using a GPU or reducing model size.\")\n",
1585
+ "\n",
1586
+ "# Load tokenizer\n",
1587
+ "print(\"Loading tokenizer...\")\n",
1588
+ "try:\n",
1589
+ " tokenizer = AutoTokenizer.from_pretrained(\n",
1590
+ " MODEL_NAME,\n",
1591
+ " cache_dir=str(CACHE_DIR),\n",
1592
+ " use_fast=True\n",
1593
+ " )\n",
1594
+ "except Exception as e:\n",
1595
+ " print(f\"Failed with use_fast=True, trying use_fast=False...\")\n",
1596
+ " tokenizer = AutoTokenizer.from_pretrained(\n",
1597
+ " MODEL_NAME,\n",
1598
+ " cache_dir=str(CACHE_DIR),\n",
1599
+ " use_fast=False\n",
1600
+ " )\n",
1601
+ "\n",
1602
+ "# Ensure pad token is set\n",
1603
+ "if tokenizer.pad_token is None:\n",
1604
+ " tokenizer.pad_token = tokenizer.eos_token\n",
1605
+ "\n",
1606
+ "print(\"βœ… Tokenizer loaded\")\n",
1607
+ "\n",
1608
+ "quantization_config = BitsAndBytesConfig(\n",
1609
+ " load_in_8bit=True\n",
1610
+ ")\n",
1611
+ "\n",
1612
+ "\n",
1613
+ "# Load model with optimizations\n",
1614
+ "print(\"Loading model (this may take several minutes)...\")\n",
1615
+ "model = AutoModelForCausalLM.from_pretrained(\n",
1616
+ " MODEL_NAME,\n",
1617
+ " cache_dir=str(CACHE_DIR),\n",
1618
+ " torch_dtype=torch.bfloat16,\n",
1619
+ " quantization_config=quantization_config,\n",
1620
+ " device_map=\"auto\",\n",
1621
+ " trust_remote_code=False\n",
1622
+ ")\n",
1623
+ "model.eval()\n",
1624
+ "print(\"βœ… Model loaded\")\n",
1625
+ "\n",
1626
+ "# Check VRAM usage\n",
1627
+ "if torch.cuda.is_available():\n",
1628
+ " vram_gb = torch.cuda.max_memory_allocated() / 1024**3\n",
1629
+ " print(f\"VRAM used: {vram_gb:.2f} GB\\n\")\n",
1630
+ "\n",
1631
+ "# === LOAD DATA ===\n",
1632
+ "print(\"Loading raw input CSV...\")\n",
1633
+ "df = pd.read_csv(input_file) # ALWAYS load the full input\n",
1634
+ "print(f\"Loaded {len(df)} rows from input file\")\n",
1635
+ "\n",
1636
+ "# If we have previous annotations, merge them\n",
1637
+ "if output_file.exists():\n",
1638
+ " print(\"Found existing annotations, merging...\")\n",
1639
+ " existing_df = pd.read_csv(output_file)\n",
1640
+ " print(f\"Existing annotations has {len(existing_df)} rows\")\n",
1641
+ " \n",
1642
+ " # Update df with existing annotations\n",
1643
+ " # Only update the columns that were annotated\n",
1644
+ " annotation_cols = ['full_name', 'aliases', 'gender', 'profession_llm', 'country']\n",
1645
+ " for col in annotation_cols:\n",
1646
+ " if col in existing_df.columns:\n",
1647
+ " df[col] = existing_df[col][:len(df)] # Make sure we don't exceed df length\n",
1648
+ " \n",
1649
+ " print(f\"Merged annotations, continuing with {len(df)} total rows\")\n",
1650
+ "\n",
1651
+ "\n",
1652
+ "# Try to load profession mapping files\n",
1653
+ "try:\n",
1654
+ " professions_df = pd.read_csv(professions_file)\n",
1655
+ " print(f\"βœ… Loaded professions.csv\")\n",
1656
+ "except:\n",
1657
+ " print(\"⚠️ Warning: professions.csv not found\")\n",
1658
+ "\n",
1659
+ "try:\n",
1660
+ " prof_mapped_df = pd.read_csv(professions_mapped_file)\n",
1661
+ " print(f\"βœ… Loaded profession mapping with {len(prof_mapped_df)} categories\")\n",
1662
+ "except:\n",
1663
+ " print(\"⚠️ Warning: professions_mapped.csv not found, using default categories\")\n",
1664
+ "\n",
1665
+ "profession_str = \", \".join(PROFESSION_CATEGORIES)\n",
1666
+ "\n",
1667
+ "print(f\"Loaded {len(df)} rows\")\n",
1668
+ "print(f\"\\nProfession categories ({len(PROFESSION_CATEGORIES)}):\")\n",
1669
+ "for cat in PROFESSION_CATEGORIES:\n",
1670
+ " print(f\" - {cat}\")\n",
1671
+ "\n",
1672
+ "if TEST_MODE:\n",
1673
+ " print(f\"\\nRunning in TEST MODE with {TEST_SIZE} samples\")\n",
1674
+ " df = df.head(TEST_SIZE).copy()\n",
1675
+ "elif MAX_ROWS:\n",
1676
+ " df = df.head(MAX_ROWS).copy()\n",
1677
+ "\n",
1678
+ "# === CREATE PROMPTS (DEEPSEEK STYLE) ===\n",
1679
+ "def create_prompt(row):\n",
1680
+ " \"\"\"Create prompt for Mistral annotation with specific profession categories.\"\"\"\n",
1681
+ " name = row['real_name'] if pd.notna(row.get('real_name')) else row.get('name', '')\n",
1682
+ " \n",
1683
+ " # Gather hints\n",
1684
+ " hints = []\n",
1685
+ " if pd.notna(row.get('likely_profession')):\n",
1686
+ " hints.append(str(row['likely_profession']))\n",
1687
+ " if pd.notna(row.get('likely_nationality')):\n",
1688
+ " hints.append(str(row['likely_nationality']))\n",
1689
+ " if pd.notna(row.get('likely_country')):\n",
1690
+ " hints.append(str(row['likely_country']))\n",
1691
+ " \n",
1692
+ " # Add tags if we don't have enough hints\n",
1693
+ " if len(hints) < 3:\n",
1694
+ " for i in range(1, 8):\n",
1695
+ " tag_col = f'tag_{i}'\n",
1696
+ " if tag_col in row and pd.notna(row[tag_col]):\n",
1697
+ " tag_val = str(row[tag_col])\n",
1698
+ " if tag_val not in hints:\n",
1699
+ " hints.append(tag_val)\n",
1700
+ " if len(hints) >= 5:\n",
1701
+ " break\n",
1702
+ " \n",
1703
+ " hint_text = \", \".join(hints[:5]) if hints else \"none\"\n",
1704
+ " \n",
1705
+ " return f\"\"\"Given '{name}' ({hint_text}), provide:\n",
1706
+ "1. Full legal name (Western order if non-latin script)\n",
1707
+ "2. Any stage names/aliases (comma separated)\n",
1708
+ "3. Gender (Male/Female/Other/Unknown)\n",
1709
+ "4. Top 3 most likely professions from ONLY these categories:\n",
1710
+ " - actor\n",
1711
+ " - adult performer\n",
1712
+ " - singer/musician\n",
1713
+ " - model\n",
1714
+ " - online personality (includes streamers, cosplayers, influencers)\n",
1715
+ " - public figure (includes politicians, activists, journalists, authors)\n",
1716
+ " - voice actor/ASMR\n",
1717
+ " - sports professional\n",
1718
+ " - tv personality (includes hosts, presenters, reality TV)\n",
1719
+ "\n",
1720
+ "5. Primary country associated\n",
1721
+ "\n",
1722
+ "IMPORTANT:\n",
1723
+ "- Choose professions ONLY from the 9 categories above\n",
1724
+ "- Provide up to 3 professions, comma-separated, ordered by relevance\n",
1725
+ "- Be SPECIFIC: choose the most accurate category for each role\n",
1726
+ "- \"online personality\" includes: streamers, cosplayers, YouTubers, influencers, content creators\n",
1727
+ "- Use 'Unknown' when uncertain or for fictional characters/places\n",
1728
+ "- For multi-role people, list all relevant categories (e.g., \"actor, singer/musician, online personality\")\n",
1729
+ "- For country respond with one word only, for example China or Columbia\n",
1730
+ "- actress = actor\n",
1731
+ "\n",
1732
+ "Respond with exactly 5 numbered lines.\"\"\"\n",
1733
+ "\n",
1734
+ "# Create prompts\n",
1735
+ "print(\"\\nCreating prompts...\")\n",
1736
+ "df['prompt'] = df.apply(create_prompt, axis=1)\n",
1737
+ "print(\"βœ… Prompts created\")\n",
1738
+ "\n",
1739
+ "# === QUERY MISTRAL LOCAL ===\n",
1740
+ "def query_mistral_local(prompt: str) -> str:\n",
1741
+ " \"\"\"Query Mistral locally via transformers.\"\"\"\n",
1742
+ " try:\n",
1743
+ " # Format as chat message for Mistral\n",
1744
+ " messages = [\n",
1745
+ " {\"role\": \"system\", \"content\": \"You are an assistant that extracts key data on a person based on the name. Respond with exactly 5 numbered lines. For professions, choose ONLY from these categories: actor, adult performer, singer/musician, model, online personality, public figure, voice actor/ASMR, sports professional, tv personality.\"},\n",
1746
+ " {\"role\": \"user\", \"content\": prompt}\n",
1747
+ " ]\n",
1748
+ " \n",
1749
+ " # Tokenize\n",
1750
+ " if hasattr(tokenizer, 'apply_chat_template'):\n",
1751
+ " text = tokenizer.apply_chat_template(\n",
1752
+ " messages,\n",
1753
+ " tokenize=False,\n",
1754
+ " add_generation_prompt=True\n",
1755
+ " )\n",
1756
+ " else:\n",
1757
+ " # Fallback for older tokenizers\n",
1758
+ " text = f\"[INST] {prompt} [/INST]\"\n",
1759
+ " \n",
1760
+ " inputs = tokenizer([text], return_tensors=\"pt\", padding=True).to(device)\n",
1761
+ " \n",
1762
+ " # Generate\n",
1763
+ " with torch.no_grad():\n",
1764
+ " outputs = model.generate(\n",
1765
+ " **inputs,\n",
1766
+ " max_new_tokens=512,\n",
1767
+ " temperature=0.05,\n",
1768
+ " do_sample=True,\n",
1769
+ " top_p=0.8,\n",
1770
+ " pad_token_id=tokenizer.pad_token_id if tokenizer.pad_token_id else tokenizer.eos_token_id\n",
1771
+ " )\n",
1772
+ " \n",
1773
+ " # Decode\n",
1774
+ " generated_ids = outputs[0][inputs['input_ids'].shape[1]:]\n",
1775
+ " response = tokenizer.decode(generated_ids, skip_special_tokens=True)\n",
1776
+ " \n",
1777
+ " return response.strip()\n",
1778
+ " \n",
1779
+ " except Exception as e:\n",
1780
+ " print(f\"Generation error: {e}\")\n",
1781
+ " return None\n",
1782
+ "\n",
1783
+ "# === PARSE RESPONSE (DEEPSEEK STYLE) ===\n",
1784
+ "def parse_response(response):\n",
1785
+ " \"\"\"Parse Mistral response into structured fields.\"\"\"\n",
1786
+ " if not response:\n",
1787
+ " return {\n",
1788
+ " 'full_name': 'Unknown',\n",
1789
+ " 'aliases': 'Unknown',\n",
1790
+ " 'gender': 'Unknown',\n",
1791
+ " 'profession_llm': 'Unknown',\n",
1792
+ " 'country': 'Unknown'\n",
1793
+ " }\n",
1794
+ " \n",
1795
+ " # Split into lines and clean\n",
1796
+ " lines = [line.strip() for line in response.split('\\n') if line.strip()]\n",
1797
+ " \n",
1798
+ " # Initialize with Unknown values\n",
1799
+ " fields = {\n",
1800
+ " 'full_name': 'Unknown',\n",
1801
+ " 'aliases': 'Unknown',\n",
1802
+ " 'gender': 'Unknown',\n",
1803
+ " 'profession_llm': 'Unknown',\n",
1804
+ " 'country': 'Unknown'\n",
1805
+ " }\n",
1806
+ " \n",
1807
+ " # Extract information from each numbered line\n",
1808
+ " for line in lines:\n",
1809
+ " if line.startswith('1.'):\n",
1810
+ " fields['full_name'] = line[2:].strip()\n",
1811
+ " elif line.startswith('2.'):\n",
1812
+ " fields['aliases'] = line[2:].strip()\n",
1813
+ " elif line.startswith('3.'):\n",
1814
+ " fields['gender'] = line[2:].strip()\n",
1815
+ " elif line.startswith('4.'):\n",
1816
+ " fields['profession_llm'] = line[2:].strip()\n",
1817
+ " elif line.startswith('5.'):\n",
1818
+ " fields['country'] = line[2:].strip()\n",
1819
+ " \n",
1820
+ " return fields\n",
1821
+ "\n",
1822
+ "# === PROCESS DATA ===\n",
1823
+ "output_file = current_dir.parent / f\"data/CSV/mistral24_local_annotated_POI{'_test' if TEST_MODE else ''}.csv\"\n",
1824
+ "index_file = current_dir.parent / \"misc/query_indicies/mistral24_local_query_index.txt\"\n",
1825
+ "\n",
1826
+ "index_file.parent.mkdir(parents=True, exist_ok=True)\n",
1827
+ "\n",
1828
+ "# Load index\n",
1829
+ "current_index = 0\n",
1830
+ "if index_file.exists():\n",
1831
+ " try:\n",
1832
+ " current_index = int(index_file.read_text().strip())\n",
1833
+ " except:\n",
1834
+ " current_index = 0\n",
1835
+ "\n",
1836
+ "print(f\"Resuming from index {current_index}\")\n",
1837
+ "\n",
1838
+ "start_time = time.time()\n",
1839
+ "\n",
1840
+ "for i in tqdm(range(current_index, len(df)), desc=\"Mistral Local\"):\n",
1841
+ "\n",
1842
+ " prompt = df.at[i, \"prompt\"]\n",
1843
+ "\n",
1844
+ " # -------- MODEL QUERY WITH RETRIES --------\n",
1845
+ " response = None\n",
1846
+ " for attempt in range(3):\n",
1847
+ " response = query_mistral_local(prompt)\n",
1848
+ " \n",
1849
+ " # Valid response?\n",
1850
+ " if response and len(response.strip()) > 10:\n",
1851
+ " break\n",
1852
+ " \n",
1853
+ " print(f\"⚠️ Row {i}: Empty or invalid response, retry {attempt+1}/3\")\n",
1854
+ " time.sleep(0.5)\n",
1855
+ "\n",
1856
+ " # If still invalid β†’ DO NOT overwrite previous data\n",
1857
+ " if not response or len(response.strip()) <= 10:\n",
1858
+ " print(f\"❌ Row {i}: failed after retries, not writing, not advancing index\")\n",
1859
+ " continue\n",
1860
+ "\n",
1861
+ " parsed = parse_response(response)\n",
1862
+ "\n",
1863
+ " # Additional safety: skip rows that parsed as all 'Unknown'\n",
1864
+ " if all(v == \"Unknown\" for v in parsed.values()):\n",
1865
+ " print(f\"❌ Row {i}: parsed as all Unknown (likely model crash); skipping.\")\n",
1866
+ " continue\n",
1867
+ "\n",
1868
+ " # -------- WRITE PARSED FIELDS SAFELY --------\n",
1869
+ " for key, value in parsed.items():\n",
1870
+ " df.at[i, key] = value\n",
1871
+ "\n",
1872
+ " # Advance progress ONLY after successful write\n",
1873
+ " current_index = i + 1\n",
1874
+ "\n",
1875
+ " # -------- GPU MEMORY CLEANUP --------\n",
1876
+ " if torch.cuda.is_available():\n",
1877
+ " torch.cuda.empty_cache()\n",
1878
+ " torch.cuda.synchronize()\n",
1879
+ "\n",
1880
+ " # -------- SAVE LIKE YOUR DEEPSEEK VERSION --------\n",
1881
+ " if (i + 1) % SAVE_INTERVAL == 0 or (i + 1) == len(df):\n",
1882
+ " df.to_csv(output_file, index=False)\n",
1883
+ " with open(index_file, \"w\") as f:\n",
1884
+ " f.write(str(current_index))\n",
1885
+ " print(f\"πŸ’Ύ Progress saved after row {i+1}\")\n",
1886
+ "\n",
1887
+ "# Final save\n",
1888
+ "df.to_csv(output_file, index=False)\n",
1889
+ "index_file.write_text(str(current_index))\n",
1890
+ "print(\"βœ… Finished full dataset.\")\n"
1891
+ ]
1892
+ },
1893
+ {
1894
+ "cell_type": "code",
1895
+ "execution_count": null,
1896
+ "id": "d7212e75-0ff6-45a0-8695-c4a3d3e02818",
1897
+ "metadata": {},
1898
+ "outputs": [],
1899
+ "source": [
1900
+ "import transformers\n",
1901
+ "print(f\"Transformers version: {transformers.__version__}\")\n",
1902
+ "\n",
1903
+ "# Check if Mistral3 is available\n",
1904
+ "try:\n",
1905
+ " from transformers import Mistral3ForCausalLM\n",
1906
+ " print(\"βœ… Mistral3 is available\")\n",
1907
+ "except ImportError:\n",
1908
+ " print(\"❌ Mistral3 not available in this transformers version\")"
1909
+ ]
1910
+ },
1911
+ {
1912
+ "cell_type": "code",
1913
+ "execution_count": null,
1914
+ "id": "a6ab032e-246e-4c4e-9776-ff0bfbf6fd9c",
1915
  "metadata": {},
1916
  "outputs": [],
1917
  "source": []
jupyter_notebooks/Section_2-3-1_Tag_occurences.ipynb ADDED
@@ -0,0 +1,801 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "71008ae2-4465-45d1-9ad0-6e6d54c99a69",
6
+ "metadata": {
7
+ "execution": {
8
+ "iopub.execute_input": "2025-12-09T20:15:54.294327Z",
9
+ "iopub.status.busy": "2025-12-09T20:15:54.294119Z",
10
+ "iopub.status.idle": "2025-12-09T20:15:54.296418Z",
11
+ "shell.execute_reply": "2025-12-09T20:15:54.295943Z",
12
+ "shell.execute_reply.started": "2025-12-09T20:15:54.294313Z"
13
+ }
14
+ },
15
+ "source": [
16
+ "# Tag occurence percentages"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "code",
21
+ "execution_count": 31,
22
+ "id": "1577b529-19b4-471a-af8b-bc331087bb61",
23
+ "metadata": {
24
+ "execution": {
25
+ "iopub.execute_input": "2025-12-09T20:33:24.090495Z",
26
+ "iopub.status.busy": "2025-12-09T20:33:24.090266Z",
27
+ "iopub.status.idle": "2025-12-09T20:33:35.733409Z",
28
+ "shell.execute_reply": "2025-12-09T20:33:35.732815Z",
29
+ "shell.execute_reply.started": "2025-12-09T20:33:24.090478Z"
30
+ }
31
+ },
32
+ "outputs": [
33
+ {
34
+ "name": "stdout",
35
+ "output_type": "stream",
36
+ "text": [
37
+ "\n",
38
+ "==================================================\n",
39
+ "Tag Analysis for 'anime'\n",
40
+ "==================================================\n",
41
+ "Models with tag: 74721\n",
42
+ "Total models: 232164\n",
43
+ "Percentage: 32.18%\n",
44
+ "==================================================\n",
45
+ "\n"
46
+ ]
47
+ }
48
+ ],
49
+ "source": [
50
+ "from pathlib import Path\n",
51
+ "import pandas as pd\n",
52
+ "import sys\n",
53
+ "\n",
54
+ "current_dir = Path.cwd()\n",
55
+ "\n",
56
+ "# ============================================\n",
57
+ "# INPUT: Change these values\n",
58
+ "# ============================================\n",
59
+ "csv_file = current_dir.parent / \"data/CSV/models/Civi_models.csv\" # Your CSV file path\n",
60
+ "tag_to_find = \"anime\" # Tag to search for\n",
61
+ "# ============================================\n",
62
+ "\n",
63
+ "def calculate_tag_percentage(csv_file, tag_to_find):\n",
64
+ " \"\"\"\n",
65
+ " Calculate what percentage of models contain a specific tag.\n",
66
+ " \"\"\"\n",
67
+ " # Read the CSV file\n",
68
+ " df = pd.read_csv(csv_file)\n",
69
+ " \n",
70
+ " # Get all tag columns\n",
71
+ " tag_columns = [col for col in df.columns if col.startswith('tag_')]\n",
72
+ " \n",
73
+ " # Count total models\n",
74
+ " total_models = len(df)\n",
75
+ " \n",
76
+ " # Count models containing the tag (case-insensitive search)\n",
77
+ " tag_lower = tag_to_find.lower()\n",
78
+ " models_with_tag = 0\n",
79
+ " \n",
80
+ " for idx, row in df.iterrows():\n",
81
+ " # Check if the tag appears in any of the tag columns\n",
82
+ " for tag_col in tag_columns:\n",
83
+ " tag_value = str(row[tag_col]).lower().strip()\n",
84
+ " if tag_value == tag_lower:\n",
85
+ " models_with_tag += 1\n",
86
+ " break # Count each model only once\n",
87
+ " \n",
88
+ " # Calculate percentage\n",
89
+ " percentage = (models_with_tag / total_models * 100) if total_models > 0 else 0\n",
90
+ " \n",
91
+ " return {\n",
92
+ " 'tag': tag_to_find,\n",
93
+ " 'count': models_with_tag,\n",
94
+ " 'total': total_models,\n",
95
+ " 'percentage': percentage\n",
96
+ " }\n",
97
+ "\n",
98
+ "# Calculate and display results\n",
99
+ "result = calculate_tag_percentage(csv_file, tag_to_find)\n",
100
+ "\n",
101
+ "print(f\"\\n{'='*50}\")\n",
102
+ "print(f\"Tag Analysis for '{result['tag']}'\")\n",
103
+ "print(f\"{'='*50}\")\n",
104
+ "print(f\"Models with tag: {result['count']}\")\n",
105
+ "print(f\"Total models: {result['total']}\")\n",
106
+ "print(f\"Percentage: {result['percentage']:.2f}%\")\n",
107
+ "print(f\"{'='*50}\\n\")"
108
+ ]
109
+ },
110
+ {
111
+ "cell_type": "code",
112
+ "execution_count": 28,
113
+ "id": "825dc9b8-ff0e-4afd-b2f1-e4bca1036aea",
114
+ "metadata": {
115
+ "execution": {
116
+ "iopub.execute_input": "2025-12-09T20:22:12.641296Z",
117
+ "iopub.status.busy": "2025-12-09T20:22:12.641105Z",
118
+ "iopub.status.idle": "2025-12-09T20:22:25.293171Z",
119
+ "shell.execute_reply": "2025-12-09T20:22:25.292530Z",
120
+ "shell.execute_reply.started": "2025-12-09T20:22:12.641281Z"
121
+ }
122
+ },
123
+ "outputs": [
124
+ {
125
+ "name": "stdout",
126
+ "output_type": "stream",
127
+ "text": [
128
+ "\n",
129
+ "=== Tag Analysis for '-f' ===\n",
130
+ "Models with tag: 0\n",
131
+ "Total models: 232164\n",
132
+ "Percentage: 0.00%\n",
133
+ "\n"
134
+ ]
135
+ }
136
+ ],
137
+ "source": [
138
+ "from pathlib import Path\n",
139
+ "import pandas as pd\n",
140
+ "import sys\n",
141
+ "\n",
142
+ "current_dir = Path.cwd()\n",
143
+ "\n",
144
+ "\n",
145
+ "def calculate_tag_percentage(csv_file, tag_to_find):\n",
146
+ " # Read the CSV file\n",
147
+ " df = pd.read_csv(csv_file)\n",
148
+ " \n",
149
+ " # Get all tag columns\n",
150
+ " tag_columns = [col for col in df.columns if col.startswith('tag_')]\n",
151
+ " \n",
152
+ " # Count total models\n",
153
+ " total_models = len(df)\n",
154
+ " \n",
155
+ " # Count models containing the tag (case-insensitive search)\n",
156
+ " tag_lower = tag_to_find.lower()\n",
157
+ " models_with_tag = 0\n",
158
+ " \n",
159
+ " for idx, row in df.iterrows():\n",
160
+ " # Check if the tag appears in any of the tag columns\n",
161
+ " for tag_col in tag_columns:\n",
162
+ " tag_value = str(row[tag_col]).lower().strip()\n",
163
+ " if tag_value == tag_lower:\n",
164
+ " models_with_tag += 1\n",
165
+ " break # Count each model only once\n",
166
+ " \n",
167
+ " # Calculate percentage\n",
168
+ " percentage = (models_with_tag / total_models * 100) if total_models > 0 else 0\n",
169
+ " \n",
170
+ " return {\n",
171
+ " 'tag': tag_to_find,\n",
172
+ " 'count': models_with_tag,\n",
173
+ " 'total': total_models,\n",
174
+ " 'percentage': percentage\n",
175
+ " }\n",
176
+ "\n",
177
+ "\n",
178
+ "def analyze_all_tags(csv_file):\n",
179
+ "\n",
180
+ " df = pd.read_csv(csv_file)\n",
181
+ " tag_columns = [col for col in df.columns if col.startswith('tag_')]\n",
182
+ " total_models = len(df)\n",
183
+ " \n",
184
+ " # Collect all tags and count occurrences\n",
185
+ " tag_counts = {}\n",
186
+ " for tag_col in tag_columns:\n",
187
+ " for tag in df[tag_col].dropna():\n",
188
+ " tag = str(tag).strip()\n",
189
+ " if tag: # Ignore empty strings\n",
190
+ " tag_counts[tag] = tag_counts.get(tag, 0) + 1\n",
191
+ " \n",
192
+ " # Create results DataFrame\n",
193
+ " results = []\n",
194
+ " for tag, count in tag_counts.items():\n",
195
+ " percentage = (count / total_models * 100)\n",
196
+ " results.append({\n",
197
+ " 'tag': tag,\n",
198
+ " 'count': count,\n",
199
+ " 'percentage': round(percentage, 2)\n",
200
+ " })\n",
201
+ " \n",
202
+ " results_df = pd.DataFrame(results)\n",
203
+ " results_df = results_df.sort_values('count', ascending=False)\n",
204
+ " \n",
205
+ " return results_df\n",
206
+ "\n",
207
+ "\n",
208
+ "if __name__ == \"__main__\":\n",
209
+ " # Default CSV file path\n",
210
+ " csv_file = current_dir.parent / \"data/CSV/models/Civi_models.csv\"\n",
211
+ " \n",
212
+ " # Check if a specific tag is provided as argument\n",
213
+ " if len(sys.argv) > 1:\n",
214
+ " tag = sys.argv[1]\n",
215
+ " result = calculate_tag_percentage(csv_file, tag)\n",
216
+ " \n",
217
+ " print(f\"\\n=== Tag Analysis for '{result['tag']}' ===\")\n",
218
+ " print(f\"Models with tag: {result['count']}\")\n",
219
+ " print(f\"Total models: {result['total']}\")\n",
220
+ " print(f\"Percentage: {result['percentage']:.2f}%\\n\")\n",
221
+ " else:\n",
222
+ " # If no specific tag provided, show all tags\n",
223
+ " print(\"\\n=== All Tags Analysis ===\\n\")\n",
224
+ " results_df = analyze_all_tags(csv_file)\n",
225
+ " print(results_df.to_string(index=False))\n",
226
+ " print(f\"\\nTotal unique tags: {len(results_df)}\")\n",
227
+ " print(f\"Total models: {len(pd.read_csv(csv_file))}\\n\")\n",
228
+ " \n",
229
+ " # Show example usage\n",
230
+ " print(\"\\nTo search for a specific tag, run:\")\n",
231
+ " print(\" python tag_percentage_calculator.py <tag_name>\")\n",
232
+ " print(\"\\nExample:\")\n",
233
+ " print(\" python tag_percentage_calculator.py anime\")"
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "code",
238
+ "execution_count": null,
239
+ "id": "63187f58-9777-4ffb-bdf9-93b191a60241",
240
+ "metadata": {
241
+ "execution": {
242
+ "iopub.execute_input": "2025-12-09T19:08:49.572641Z",
243
+ "iopub.status.busy": "2025-12-09T19:08:49.572453Z",
244
+ "iopub.status.idle": "2025-12-09T19:08:49.634561Z",
245
+ "shell.execute_reply": "2025-12-09T19:08:49.634109Z",
246
+ "shell.execute_reply.started": "2025-12-09T19:08:49.572627Z"
247
+ }
248
+ },
249
+ "outputs": [],
250
+ "source": [
251
+ "from pathlib import Path\n",
252
+ "import json\n",
253
+ "from collections import defaultdict\n",
254
+ "\n",
255
+ "current_dir = Path.cwd()\n",
256
+ "\n",
257
+ "\n",
258
+ "\n",
259
+ "def load_data(filepath):\n",
260
+ " \"\"\"Load the JSON data from file.\"\"\"\n",
261
+ " with open(filepath, 'r', encoding='utf-8') as f:\n",
262
+ " return json.load(f)\n",
263
+ "\n",
264
+ "def calculate_cooccurrence_rate(data, target_tag, cooccurring_tags):\n",
265
+ " \"\"\"\n",
266
+ " Calculate what percentage of target_tag occurrences co-occur with each tag in cooccurring_tags.\n",
267
+ " \n",
268
+ " Args:\n",
269
+ " data: Dictionary with 'nodes' and 'links'\n",
270
+ " target_tag: The main tag to analyze (e.g., \"woman\")\n",
271
+ " cooccurring_tags: List of tags to check co-occurrence with (e.g., [\"sexy\", \"pose\"])\n",
272
+ " \n",
273
+ " Returns:\n",
274
+ " Dictionary with results\n",
275
+ " \"\"\"\n",
276
+ " # Find the target tag's total occurrences\n",
277
+ " target_size = None\n",
278
+ " for node in data['nodes']:\n",
279
+ " if node['id'] == target_tag:\n",
280
+ " target_size = node['size']\n",
281
+ " break\n",
282
+ " \n",
283
+ " if target_size is None:\n",
284
+ " print(f\"Warning: Tag '{target_tag}' not found in nodes!\")\n",
285
+ " return None\n",
286
+ " \n",
287
+ " print(f\"\\n{'='*60}\")\n",
288
+ " print(f\"Analysis for tag: '{target_tag}'\")\n",
289
+ " print(f\"{'='*60}\")\n",
290
+ " print(f\"Total occurrences of '{target_tag}': {target_size:,}\")\n",
291
+ " print()\n",
292
+ " \n",
293
+ " # Find co-occurrences in links\n",
294
+ " results = {}\n",
295
+ " for cooccurring_tag in cooccurring_tags:\n",
296
+ " cooccurrence_count = 0\n",
297
+ " \n",
298
+ " # Check both directions in links\n",
299
+ " for link in data['links']:\n",
300
+ " if (link['source'] == target_tag and link['target'] == cooccurring_tag) or \\\n",
301
+ " (link['source'] == cooccurring_tag and link['target'] == target_tag):\n",
302
+ " cooccurrence_count = link['value']\n",
303
+ " break\n",
304
+ " \n",
305
+ " if cooccurrence_count > 0:\n",
306
+ " percentage = (cooccurrence_count / target_size) * 100\n",
307
+ " results[cooccurring_tag] = {\n",
308
+ " 'count': cooccurrence_count,\n",
309
+ " 'percentage': percentage\n",
310
+ " }\n",
311
+ " print(f\"Tag: '{cooccurring_tag}'\")\n",
312
+ " print(f\" Co-occurrences: {cooccurrence_count:,}\")\n",
313
+ " print(f\" Percentage: {percentage:.2f}%\")\n",
314
+ " print(f\" (i.e., {percentage:.2f}% of '{target_tag}' occurrences also have '{cooccurring_tag}')\")\n",
315
+ " else:\n",
316
+ " results[cooccurring_tag] = {\n",
317
+ " 'count': 0,\n",
318
+ " 'percentage': 0.0\n",
319
+ " }\n",
320
+ " print(f\"Tag: '{cooccurring_tag}'\")\n",
321
+ " print(f\" No co-occurrences found\")\n",
322
+ " print()\n",
323
+ " \n",
324
+ " # Calculate combined co-occurrence (both tags together)\n",
325
+ " print(f\"\\n{'='*60}\")\n",
326
+ " print(\"Combined Analysis\")\n",
327
+ " print(f\"{'='*60}\")\n",
328
+ " \n",
329
+ " # To find items with ALL tags, we'd need to look at the underlying data\n",
330
+ " # With just the graph structure, we can only report individual co-occurrences\n",
331
+ " print(f\"Individual co-occurrence rates calculated above.\")\n",
332
+ " print(f\"Note: To calculate how often ALL tags appear together,\")\n",
333
+ " print(f\"we would need access to the raw item-level data.\")\n",
334
+ " \n",
335
+ " return results\n",
336
+ "\n",
337
+ "def main():\n",
338
+ " # Load the data\n",
339
+ " filepath = current_dir.parent / \"public/json/nodes_all.json\"\n",
340
+ " print(\"Loading data...\")\n",
341
+ " data = load_data(filepath)\n",
342
+ " print(f\"Loaded {len(data['nodes']):,} nodes and {len(data['links']):,} links\")\n",
343
+ " \n",
344
+ " # Calculate co-occurrence rates\n",
345
+ " target_tag = \"woman\"\n",
346
+ " cooccurring_tags = [\"sexy\", \"pose\"]\n",
347
+ " \n",
348
+ " results = calculate_cooccurrence_rate(data, target_tag, cooccurring_tags)\n",
349
+ " \n",
350
+ " # Summary\n",
351
+ " print(f\"\\n{'='*60}\")\n",
352
+ " print(\"SUMMARY\")\n",
353
+ " print(f\"{'='*60}\")\n",
354
+ " if results:\n",
355
+ " for tag, stats in results.items():\n",
356
+ " print(f\"'{target_tag}' + '{tag}': {stats['percentage']:.2f}% ({stats['count']:,} occurrences)\")\n",
357
+ "\n",
358
+ "if __name__ == \"__main__\":\n",
359
+ " main()"
360
+ ]
361
+ },
362
+ {
363
+ "cell_type": "code",
364
+ "execution_count": 26,
365
+ "id": "6e0e8c6b-547e-4899-b001-1d4c6b31476f",
366
+ "metadata": {
367
+ "execution": {
368
+ "iopub.execute_input": "2025-12-09T19:48:36.009465Z",
369
+ "iopub.status.busy": "2025-12-09T19:48:36.009249Z",
370
+ "iopub.status.idle": "2025-12-09T19:48:36.063251Z",
371
+ "shell.execute_reply": "2025-12-09T19:48:36.062750Z",
372
+ "shell.execute_reply.started": "2025-12-09T19:48:36.009449Z"
373
+ }
374
+ },
375
+ "outputs": [
376
+ {
377
+ "name": "stdout",
378
+ "output_type": "stream",
379
+ "text": [
380
+ "Loading data...\n",
381
+ "Loaded 60,330 nodes and 16,921 links\n",
382
+ "\n",
383
+ "================================================================================\n",
384
+ "Top 100 Co-occurring Tags for: 'anime'\n",
385
+ "================================================================================\n",
386
+ "Total occurrences of 'anime': 74,187\n",
387
+ "\n",
388
+ "Rank Tag Count Percentage \n",
389
+ "------ ------------------------------ ------------ ------------\n",
390
+ "1 character 53,792 72.51%\n",
391
+ "2 woman 30,731 41.42%\n",
392
+ "3 girls 21,434 28.89%\n",
393
+ "4 female 14,286 19.26%\n",
394
+ "5 style 11,593 15.63%\n",
395
+ "6 game character 9,309 12.55%\n",
396
+ "7 sexy 8,876 11.96%\n",
397
+ "8 male 4,476 6.03%\n",
398
+ "9 video game 3,411 4.60%\n",
399
+ "10 man 3,333 4.49%\n",
400
+ "11 lora 3,260 4.39%\n",
401
+ "12 concept 2,568 3.46%\n",
402
+ "13 girl 2,472 3.33%\n",
403
+ "14 base model 2,197 2.96%\n",
404
+ "15 photorealistic 2,191 2.95%\n",
405
+ "16 manga 2,015 2.72%\n",
406
+ "17 boys 2,006 2.70%\n",
407
+ "18 anime character 1,841 2.48%\n",
408
+ "19 cartoon 1,759 2.37%\n",
409
+ "20 game 1,536 2.07%\n",
410
+ "21 men 1,431 1.93%\n",
411
+ "22 cute 1,325 1.79%\n",
412
+ "23 hentai 1,222 1.65%\n",
413
+ "24 clothing 1,131 1.52%\n",
414
+ "25 furry 1,114 1.50%\n",
415
+ "26 realistic 1,074 1.45%\n",
416
+ "27 styles 1,057 1.42%\n",
417
+ "28 illustration 971 1.31%\n",
418
+ "29 characters 967 1.30%\n",
419
+ "30 art style 949 1.28%\n",
420
+ "31 pokemon 932 1.26%\n",
421
+ "32 vtuber 890 1.20%\n",
422
+ "33 person 872 1.18%\n",
423
+ "34 artstyle 801 1.08%\n",
424
+ "35 anime girl 756 1.02%\n",
425
+ "36 blue archive 684 0.92%\n",
426
+ "37 2d 666 0.90%\n",
427
+ "38 fantasy 643 0.87%\n",
428
+ "39 art 633 0.85%\n",
429
+ "40 poses 619 0.83%\n",
430
+ "41 3d 578 0.78%\n",
431
+ "42 nsfw 550 0.74%\n",
432
+ "43 artist 544 0.73%\n",
433
+ "44 genshin impact 519 0.70%\n",
434
+ "45 idolmaster 483 0.65%\n",
435
+ "46 fire emblem 481 0.65%\n",
436
+ "47 fate 432 0.58%\n",
437
+ "48 waifu 406 0.55%\n",
438
+ "49 azur lane 399 0.54%\n",
439
+ "50 dragon ball 388 0.52%\n",
440
+ "51 ponyxl 379 0.51%\n",
441
+ "52 naruto 379 0.51%\n",
442
+ "53 precure 379 0.51%\n",
443
+ "54 videogame 372 0.50%\n",
444
+ "55 retro 356 0.48%\n",
445
+ "56 meme 355 0.48%\n",
446
+ "57 arknights 354 0.48%\n",
447
+ "58 hololive 347 0.47%\n",
448
+ "59 virtual youtuber 347 0.47%\n",
449
+ "60 umamusume 333 0.45%\n",
450
+ "61 falcom 326 0.44%\n",
451
+ "62 one piece 325 0.44%\n",
452
+ "63 boy 321 0.43%\n",
453
+ "64 chibi 315 0.42%\n",
454
+ "65 comics 303 0.41%\n",
455
+ "66 idolm@ster 295 0.40%\n",
456
+ "67 gundam 294 0.40%\n",
457
+ "68 bleach 294 0.40%\n",
458
+ "69 pose 288 0.39%\n",
459
+ "70 guy 284 0.38%\n",
460
+ "71 milf 281 0.38%\n",
461
+ "72 my hero academia 279 0.38%\n",
462
+ "73 genshin 276 0.37%\n",
463
+ "74 porn 268 0.36%\n",
464
+ "75 kawaii 257 0.35%\n",
465
+ "76 kantai collection 255 0.34%\n",
466
+ "77 galgame 250 0.34%\n",
467
+ "78 eiyuu densetsu 246 0.33%\n",
468
+ "79 animals 239 0.32%\n",
469
+ "80 yu-gi-oh! 236 0.32%\n",
470
+ "81 comic 232 0.31%\n",
471
+ "82 sex 228 0.31%\n",
472
+ "83 cinderella girls 227 0.31%\n",
473
+ "84 kancolle 223 0.30%\n",
474
+ "85 huge breasts 220 0.30%\n",
475
+ "86 clothes 219 0.30%\n",
476
+ "87 digital art 217 0.29%\n",
477
+ "88 oc 216 0.29%\n",
478
+ "89 scenery 215 0.29%\n",
479
+ "90 nintendo 215 0.29%\n",
480
+ "91 manhwa 214 0.29%\n",
481
+ "92 final fantasy 211 0.28%\n",
482
+ "93 nikke 211 0.28%\n",
483
+ "94 cosplay 208 0.28%\n",
484
+ "95 beautiful 207 0.28%\n",
485
+ "96 dragon ball z 207 0.28%\n",
486
+ "97 concepts 207 0.28%\n",
487
+ "98 videogame character 207 0.28%\n",
488
+ "99 thick thighs 205 0.28%\n",
489
+ "100 wide hips 202 0.27%\n",
490
+ "\n",
491
+ "================================================================================\n",
492
+ "\n"
493
+ ]
494
+ }
495
+ ],
496
+ "source": [
497
+ "from pathlib import Path\n",
498
+ "import json\n",
499
+ "from collections import defaultdict\n",
500
+ "\n",
501
+ "current_dir = Path.cwd()\n",
502
+ "\n",
503
+ "def load_data(filepath):\n",
504
+ " \"\"\"Load the JSON data from file.\"\"\"\n",
505
+ " with open(filepath, 'r', encoding='utf-8') as f:\n",
506
+ " return json.load(f)\n",
507
+ "\n",
508
+ "def get_top_cooccurrences(data, target_tag, top_n=10):\n",
509
+ " \"\"\"\n",
510
+ " Find the top N tags that co-occur with the target tag.\n",
511
+ " \n",
512
+ " Args:\n",
513
+ " data: Dictionary with 'nodes' and 'links'\n",
514
+ " target_tag: The main tag to analyze (e.g., \"woman\")\n",
515
+ " top_n: Number of top co-occurring tags to return (default: 10)\n",
516
+ " \n",
517
+ " Returns:\n",
518
+ " List of tuples (tag, count, percentage) sorted by count\n",
519
+ " \"\"\"\n",
520
+ " # Find the target tag's total occurrences\n",
521
+ " target_size = None\n",
522
+ " for node in data['nodes']:\n",
523
+ " if node['id'] == target_tag:\n",
524
+ " target_size = node['size']\n",
525
+ " break\n",
526
+ " \n",
527
+ " if target_size is None:\n",
528
+ " print(f\"Error: Tag '{target_tag}' not found in nodes!\")\n",
529
+ " return None, None\n",
530
+ " \n",
531
+ " # Find all co-occurrences in links\n",
532
+ " cooccurrences = []\n",
533
+ " \n",
534
+ " for link in data['links']:\n",
535
+ " if link['source'] == target_tag:\n",
536
+ " cooccurrences.append({\n",
537
+ " 'tag': link['target'],\n",
538
+ " 'count': link['value']\n",
539
+ " })\n",
540
+ " elif link['target'] == target_tag:\n",
541
+ " cooccurrences.append({\n",
542
+ " 'tag': link['source'],\n",
543
+ " 'count': link['value']\n",
544
+ " })\n",
545
+ " \n",
546
+ " # Sort by count (descending) and take top N\n",
547
+ " cooccurrences.sort(key=lambda x: x['count'], reverse=True)\n",
548
+ " top_cooccurrences = cooccurrences[:top_n]\n",
549
+ " \n",
550
+ " # Calculate percentages\n",
551
+ " results = []\n",
552
+ " for item in top_cooccurrences:\n",
553
+ " percentage = (item['count'] / target_size) * 100\n",
554
+ " results.append((item['tag'], item['count'], percentage))\n",
555
+ " \n",
556
+ " return results, target_size\n",
557
+ "\n",
558
+ "def display_results(target_tag, results, target_size, top_n):\n",
559
+ " \"\"\"Display the results in a formatted table.\"\"\"\n",
560
+ " if results is None:\n",
561
+ " return\n",
562
+ " \n",
563
+ " print(f\"\\n{'='*80}\")\n",
564
+ " print(f\"Top {top_n} Co-occurring Tags for: '{target_tag}'\")\n",
565
+ " print(f\"{'='*80}\")\n",
566
+ " print(f\"Total occurrences of '{target_tag}': {target_size:,}\\n\")\n",
567
+ " \n",
568
+ " if not results:\n",
569
+ " print(f\"No co-occurrences found for '{target_tag}'\")\n",
570
+ " return\n",
571
+ " \n",
572
+ " # Print header\n",
573
+ " print(f\"{'Rank':<6} {'Tag':<30} {'Count':<12} {'Percentage':<12}\")\n",
574
+ " print(f\"{'-'*6} {'-'*30} {'-'*12} {'-'*12}\")\n",
575
+ " \n",
576
+ " # Print results\n",
577
+ " for i, (tag, count, percentage) in enumerate(results, 1):\n",
578
+ " print(f\"{i:<6} {tag:<30} {count:<12,} {percentage:>10.2f}%\")\n",
579
+ " \n",
580
+ " print(f\"\\n{'='*80}\\n\")\n",
581
+ "\n",
582
+ "def main():\n",
583
+ " # Load the data\n",
584
+ " filepath = current_dir.parent / \"public/json/nodes_all.json\"\n",
585
+ " print(\"Loading data...\")\n",
586
+ " data = load_data(filepath)\n",
587
+ " print(f\"Loaded {len(data['nodes']):,} nodes and {len(data['links']):,} links\")\n",
588
+ " \n",
589
+ " # Analyze different tags\n",
590
+ " target_tags = [\"anime\"] # Add more tags here to analyze multiple\n",
591
+ " top_n = 100\n",
592
+ " \n",
593
+ " for target_tag in target_tags:\n",
594
+ " results, target_size = get_top_cooccurrences(data, target_tag, top_n)\n",
595
+ " display_results(target_tag, results, target_size, top_n)\n",
596
+ "\n",
597
+ "if __name__ == \"__main__\":\n",
598
+ " main()"
599
+ ]
600
+ },
601
+ {
602
+ "cell_type": "code",
603
+ "execution_count": 25,
604
+ "id": "e8af35af-a4b9-4011-b8c3-5ec8e75ce6c1",
605
+ "metadata": {
606
+ "execution": {
607
+ "iopub.execute_input": "2025-12-09T19:47:44.592679Z",
608
+ "iopub.status.busy": "2025-12-09T19:47:44.592464Z",
609
+ "iopub.status.idle": "2025-12-09T19:47:44.649077Z",
610
+ "shell.execute_reply": "2025-12-09T19:47:44.648523Z",
611
+ "shell.execute_reply.started": "2025-12-09T19:47:44.592664Z"
612
+ }
613
+ },
614
+ "outputs": [
615
+ {
616
+ "name": "stdout",
617
+ "output_type": "stream",
618
+ "text": [
619
+ "Loading data...\n",
620
+ "Loaded 60,330 nodes and 16,921 links\n",
621
+ "\n",
622
+ "\n",
623
+ "================================================================================\n",
624
+ "Co-occurrence Analysis: 'anime' + 'dragon ball'\n",
625
+ "================================================================================\n",
626
+ "\n",
627
+ "Total occurrences of 'anime': 74,187\n",
628
+ "Total occurrences of 'dragon ball': 479\n",
629
+ "\n",
630
+ "Items with BOTH tags: 388\n",
631
+ "\n",
632
+ ">>> 0.52% of 'anime' occurrences also have 'dragon ball'\n",
633
+ "\n",
634
+ "================================================================================\n",
635
+ "\n"
636
+ ]
637
+ }
638
+ ],
639
+ "source": [
640
+ "from pathlib import Path\n",
641
+ "import json\n",
642
+ "from collections import defaultdict\n",
643
+ "\n",
644
+ "current_dir = Path.cwd()\n",
645
+ "\n",
646
+ "def load_data(filepath):\n",
647
+ " \"\"\"Load the JSON data from file.\"\"\"\n",
648
+ " with open(filepath, 'r', encoding='utf-8') as f:\n",
649
+ " return json.load(f)\n",
650
+ "\n",
651
+ "def get_tag_cooccurrence(data, tag1, tag2):\n",
652
+ " \"\"\"\n",
653
+ " Find what percentage of tag1 occurrences also have tag2.\n",
654
+ " \n",
655
+ " Args:\n",
656
+ " data: Dictionary with 'nodes' and 'links'\n",
657
+ " tag1: Primary tag to analyze (e.g., \"cat\")\n",
658
+ " tag2: Secondary tag to check for (e.g., \"dog\")\n",
659
+ " \n",
660
+ " Returns:\n",
661
+ " Dictionary with co-occurrence information\n",
662
+ " \"\"\"\n",
663
+ " # Find the tags' total occurrences\n",
664
+ " tag1_size = None\n",
665
+ " tag2_size = None\n",
666
+ " \n",
667
+ " for node in data['nodes']:\n",
668
+ " if node['id'] == tag1:\n",
669
+ " tag1_size = node['size']\n",
670
+ " if node['id'] == tag2:\n",
671
+ " tag2_size = node['size']\n",
672
+ " \n",
673
+ " if tag1_size is None:\n",
674
+ " print(f\"Error: Tag '{tag1}' not found in nodes!\")\n",
675
+ " return None\n",
676
+ " \n",
677
+ " if tag2_size is None:\n",
678
+ " print(f\"Error: Tag '{tag2}' not found in nodes!\")\n",
679
+ " return None\n",
680
+ " \n",
681
+ " # Find co-occurrence count in links\n",
682
+ " # This represents how many items have BOTH tag1 AND tag2\n",
683
+ " cooccurrence_count = 0\n",
684
+ " \n",
685
+ " for link in data['links']:\n",
686
+ " if (link['source'] == tag1 and link['target'] == tag2) or \\\n",
687
+ " (link['source'] == tag2 and link['target'] == tag1):\n",
688
+ " cooccurrence_count = link['value']\n",
689
+ " break\n",
690
+ " \n",
691
+ " # Calculate percentage: what % of tag1 items also have tag2\n",
692
+ " percentage_with_tag2 = (cooccurrence_count / tag1_size) * 100 if tag1_size > 0 else 0\n",
693
+ " \n",
694
+ " return {\n",
695
+ " 'primary_tag': tag1,\n",
696
+ " 'secondary_tag': tag2,\n",
697
+ " 'primary_tag_total': tag1_size,\n",
698
+ " 'secondary_tag_total': tag2_size,\n",
699
+ " 'cooccurrence_count': cooccurrence_count,\n",
700
+ " 'percentage_with_secondary': percentage_with_tag2\n",
701
+ " }\n",
702
+ "\n",
703
+ "def display_cooccurrence_results(result):\n",
704
+ " \"\"\"Display the co-occurrence results in a formatted way.\"\"\"\n",
705
+ " if result is None:\n",
706
+ " return\n",
707
+ " \n",
708
+ " print(f\"\\n{'='*80}\")\n",
709
+ " print(f\"Co-occurrence Analysis: '{result['primary_tag']}' + '{result['secondary_tag']}'\")\n",
710
+ " print(f\"{'='*80}\\n\")\n",
711
+ " \n",
712
+ " print(f\"Total occurrences of '{result['primary_tag']}': {result['primary_tag_total']:,}\")\n",
713
+ " print(f\"Total occurrences of '{result['secondary_tag']}': {result['secondary_tag_total']:,}\")\n",
714
+ " print(f\"\\nItems with BOTH tags: {result['cooccurrence_count']:,}\")\n",
715
+ " print(f\"\\n>>> {result['percentage_with_secondary']:.2f}% of '{result['primary_tag']}' occurrences also have '{result['secondary_tag']}'\")\n",
716
+ " \n",
717
+ " print(f\"\\n{'='*80}\\n\")\n",
718
+ "\n",
719
+ "def analyze_multiple_pairs(data, tag_pairs):\n",
720
+ " \"\"\"\n",
721
+ " Analyze multiple tag pairs at once.\n",
722
+ " \n",
723
+ " Args:\n",
724
+ " data: Dictionary with 'nodes' and 'links'\n",
725
+ " tag_pairs: List of tuples, each containing two tags to compare\n",
726
+ " \"\"\"\n",
727
+ " results = []\n",
728
+ " \n",
729
+ " for tag1, tag2 in tag_pairs:\n",
730
+ " result = get_tag_cooccurrence(data, tag1, tag2)\n",
731
+ " if result:\n",
732
+ " results.append(result)\n",
733
+ " display_cooccurrence_results(result)\n",
734
+ " \n",
735
+ " return results\n",
736
+ "\n",
737
+ "def main():\n",
738
+ " # Load the data\n",
739
+ " filepath = current_dir.parent / \"public/json/nodes_all.json\"\n",
740
+ " print(\"Loading data...\")\n",
741
+ " data = load_data(filepath)\n",
742
+ " print(f\"Loaded {len(data['nodes']):,} nodes and {len(data['links']):,} links\\n\")\n",
743
+ " \n",
744
+ " # Analyze: What percentage of \"cat\" occurrences also have \"dog\"?\n",
745
+ " primary_tag = \"anime\" # The main tag you're interested in\n",
746
+ " secondary_tag = \"dragon ball\" # The tag you want to check for\n",
747
+ " \n",
748
+ " result = get_tag_cooccurrence(data, primary_tag, secondary_tag)\n",
749
+ " display_cooccurrence_results(result)\n",
750
+ " \n",
751
+ " # You can also check the reverse: What percentage of \"dog\" occurrences also have \"cat\"?\n",
752
+ " # result_reverse = get_tag_cooccurrence(data, \"dog\", \"cat\")\n",
753
+ " # display_cooccurrence_results(result_reverse)\n",
754
+ " \n",
755
+ " # Option: Analyze multiple pairs at once\n",
756
+ " # Uncomment the lines below to analyze multiple pairs\n",
757
+ " \"\"\"\n",
758
+ " tag_pairs = [\n",
759
+ " (\"cat\", \"dog\"),\n",
760
+ " (\"boy\", \"anime\"),\n",
761
+ " (\"girl\", \"anime\"),\n",
762
+ " (\"man\", \"photorealistic\")\n",
763
+ " ]\n",
764
+ " results = analyze_multiple_pairs(data, tag_pairs)\n",
765
+ " \"\"\"\n",
766
+ "\n",
767
+ "if __name__ == \"__main__\":\n",
768
+ " main()"
769
+ ]
770
+ },
771
+ {
772
+ "cell_type": "code",
773
+ "execution_count": null,
774
+ "id": "a79ee96c-a060-4cc5-82d8-5642ccbef328",
775
+ "metadata": {},
776
+ "outputs": [],
777
+ "source": []
778
+ }
779
+ ],
780
+ "metadata": {
781
+ "kernelspec": {
782
+ "display_name": "Python 3 (ipykernel)",
783
+ "language": "python",
784
+ "name": "python3"
785
+ },
786
+ "language_info": {
787
+ "codemirror_mode": {
788
+ "name": "ipython",
789
+ "version": 3
790
+ },
791
+ "file_extension": ".py",
792
+ "mimetype": "text/x-python",
793
+ "name": "python",
794
+ "nbconvert_exporter": "python",
795
+ "pygments_lexer": "ipython3",
796
+ "version": "3.13.9"
797
+ }
798
+ },
799
+ "nbformat": 4,
800
+ "nbformat_minor": 5
801
+ }
jupyter_notebooks/Section_2-3-4_Figure_8_Step_1_LLM_annotation.ipynb CHANGED
@@ -466,7 +466,7 @@
466
  " 'max_new_tokens': 512,\n",
467
  " 'temperature': 0.05,\n",
468
  " 'do_sample': True,\n",
469
- " 'top_p': 0.8,\n",
470
  " }\n",
471
  " },\n",
472
  " 'gemma': {\n",
@@ -477,7 +477,7 @@
477
  " 'max_new_tokens': 512,\n",
478
  " 'temperature': 0.1,\n",
479
  " 'do_sample': True,\n",
480
- " 'top_p': 0.9,\n",
481
  " }\n",
482
  " },\n",
483
  " 'qwen': {\n",
@@ -489,7 +489,7 @@
489
  " llm_int8_has_fp16_weight=False\n",
490
  " ),\n",
491
  " 'generation_params': {\n",
492
- " 'max_new_tokens': 100,\n",
493
  " 'temperature': 0.1,\n",
494
  " 'do_sample': False,\n",
495
  " }\n",
@@ -1428,15 +1428,12 @@
1428
  },
1429
  {
1430
  "cell_type": "code",
1431
- "execution_count": 1,
1432
  "id": "a55a5e30-83f3-4f7c-a537-b1216d4e8a07",
1433
  "metadata": {
1434
  "execution": {
1435
- "iopub.execute_input": "2025-12-08T23:57:35.685431Z",
1436
- "iopub.status.busy": "2025-12-08T23:57:35.685314Z",
1437
- "iopub.status.idle": "2025-12-08T23:59:48.656498Z",
1438
- "shell.execute_reply": "2025-12-08T23:59:48.655927Z",
1439
- "shell.execute_reply.started": "2025-12-08T23:57:35.685419Z"
1440
  }
1441
  },
1442
  "outputs": [
@@ -1458,18 +1455,71 @@
1458
  "\n",
1459
  "Device: cuda\n",
1460
  "Loading tokenizer...\n",
1461
- "βœ… Tokenizer loaded\n"
 
1462
  ]
1463
  },
1464
  {
1465
- "ename": "NameError",
1466
- "evalue": "name 'BitsAndBytesConfig' is not defined",
1467
- "output_type": "error",
1468
- "traceback": [
1469
- "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
1470
- "\u001b[31mNameError\u001b[39m Traceback (most recent call last)",
1471
- "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 82\u001b[39m\n\u001b[32m 78\u001b[39m tokenizer.pad_token = tokenizer.eos_token\n\u001b[32m 80\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mβœ… Tokenizer loaded\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m82\u001b[39m quantization_config = \u001b[43mBitsAndBytesConfig\u001b[49m(\n\u001b[32m 83\u001b[39m load_in_8bit=\u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[32m 84\u001b[39m )\n\u001b[32m 87\u001b[39m \u001b[38;5;66;03m# Load model with optimizations\u001b[39;00m\n\u001b[32m 88\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mLoading model (this may take several minutes)...\u001b[39m\u001b[33m\"\u001b[39m)\n",
1472
- "\u001b[31mNameError\u001b[39m: name 'BitsAndBytesConfig' is not defined"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1473
  ]
1474
  }
1475
  ],
@@ -1481,7 +1531,7 @@
1481
  "from pathlib import Path\n",
1482
  "from tqdm import tqdm\n",
1483
  "import torch\n",
1484
- "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
1485
  "\n",
1486
  "current_dir = Path.cwd()\n",
1487
  "input_file = current_dir.parent / \"data/CSV/model_adapter/real_person_adapter_step_02_NER.csv\"\n",
 
466
  " 'max_new_tokens': 512,\n",
467
  " 'temperature': 0.05,\n",
468
  " 'do_sample': True,\n",
469
+ " 'top_p': 1.0,\n",
470
  " }\n",
471
  " },\n",
472
  " 'gemma': {\n",
 
477
  " 'max_new_tokens': 512,\n",
478
  " 'temperature': 0.1,\n",
479
  " 'do_sample': True,\n",
480
+ " 'top_p': 1.0,\n",
481
  " }\n",
482
  " },\n",
483
  " 'qwen': {\n",
 
489
  " llm_int8_has_fp16_weight=False\n",
490
  " ),\n",
491
  " 'generation_params': {\n",
492
+ " 'max_new_tokens': 512,\n",
493
  " 'temperature': 0.1,\n",
494
  " 'do_sample': False,\n",
495
  " }\n",
 
1428
  },
1429
  {
1430
  "cell_type": "code",
1431
+ "execution_count": null,
1432
  "id": "a55a5e30-83f3-4f7c-a537-b1216d4e8a07",
1433
  "metadata": {
1434
  "execution": {
1435
+ "iopub.execute_input": "2025-12-09T22:16:21.002786Z",
1436
+ "iopub.status.busy": "2025-12-09T22:16:21.002337Z"
 
 
 
1437
  }
1438
  },
1439
  "outputs": [
 
1455
  "\n",
1456
  "Device: cuda\n",
1457
  "Loading tokenizer...\n",
1458
+ "βœ… Tokenizer loaded\n",
1459
+ "Loading model (this may take several minutes)...\n"
1460
  ]
1461
  },
1462
  {
1463
+ "name": "stderr",
1464
+ "output_type": "stream",
1465
+ "text": [
1466
+ "`torch_dtype` is deprecated! Use `dtype` instead!\n",
1467
+ "Loading checkpoint shards: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 9/9 [02:42<00:00, 18.06s/it]\n"
1468
+ ]
1469
+ },
1470
+ {
1471
+ "name": "stdout",
1472
+ "output_type": "stream",
1473
+ "text": [
1474
+ "βœ… Model loaded\n",
1475
+ "VRAM used: 21.40 GB\n",
1476
+ "\n",
1477
+ "Loading raw input CSV...\n",
1478
+ "Loaded 50861 rows from input file\n",
1479
+ "Found existing annotations, merging...\n"
1480
+ ]
1481
+ },
1482
+ {
1483
+ "name": "stderr",
1484
+ "output_type": "stream",
1485
+ "text": [
1486
+ "/tmp/ipykernel_3104208/1997558719.py:113: DtypeWarning: Columns (52,53,54,55,56) have mixed types. Specify dtype option on import or set low_memory=False.\n",
1487
+ " existing_df = pd.read_csv(output_file)\n"
1488
+ ]
1489
+ },
1490
+ {
1491
+ "name": "stdout",
1492
+ "output_type": "stream",
1493
+ "text": [
1494
+ "Existing annotations has 50861 rows\n",
1495
+ "Merged annotations, continuing with 50861 total rows\n",
1496
+ "βœ… Loaded professions.csv\n",
1497
+ "βœ… Loaded profession mapping with 9 categories\n",
1498
+ "Loaded 50861 rows\n",
1499
+ "\n",
1500
+ "Profession categories (9):\n",
1501
+ " - actor\n",
1502
+ " - adult performer\n",
1503
+ " - singer/musician\n",
1504
+ " - model\n",
1505
+ " - online personality\n",
1506
+ " - public figure\n",
1507
+ " - voice actor/ASMR\n",
1508
+ " - sports professional\n",
1509
+ " - tv personality\n",
1510
+ "\n",
1511
+ "Creating prompts...\n",
1512
+ "βœ… Prompts created\n",
1513
+ "Resuming from index 8810\n"
1514
+ ]
1515
+ },
1516
+ {
1517
+ "name": "stderr",
1518
+ "output_type": "stream",
1519
+ "text": [
1520
+ "Mistral Local: 0%| | 0/42051 [00:00<?, ?it/s]/shares/weddigen.ki.uzh/laura_wagner/phase_01/pm-paper/.venv/lib/python3.11/site-packages/bitsandbytes/autograd/_functions.py:181: UserWarning: MatMul8bitLt: inputs will be cast from torch.bfloat16 to float16 during quantization\n",
1521
+ " warnings.warn(f\"MatMul8bitLt: inputs will be cast from {A.dtype} to float16 during quantization\")\n",
1522
+ "Mistral Local: 0%| | 7/42051 [00:57<93:01:03, 7.96s/it] "
1523
  ]
1524
  }
1525
  ],
 
1531
  "from pathlib import Path\n",
1532
  "from tqdm import tqdm\n",
1533
  "import torch\n",
1534
+ "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig\n",
1535
  "\n",
1536
  "current_dir = Path.cwd()\n",
1537
  "input_file = current_dir.parent / \"data/CSV/model_adapter/real_person_adapter_step_02_NER.csv\"\n",
misc/query_indicies/mistral24_local_query_index.txt CHANGED
@@ -1 +1 @@
1
- 3890
 
1
+ 14840