Skip to content

Instantly share code, notes, and snippets.

@jc4p
Last active October 10, 2025 22:04
Show Gist options
  • Select an option

  • Save jc4p/334d52de25624e6419aa21f4ccd35f10 to your computer and use it in GitHub Desktop.

Select an option

Save jc4p/334d52de25624e6419aa21f4ccd35f10 to your computer and use it in GitHub Desktop.
Making and running a BERT classifier on Farcaster casts
#!/usr/bin/env python3
"""
Classify Farcaster casts using Google Gemini Batch API.
Classifies each cast into one of 5 categories:
- Personal Life
- Spam
- App/Bot Interactions
- Crypto Content
- Not Enough Context To Tell
"""
import json
import time
from google import genai
from google.genai import types
def create_classification_prompt(cast_text):
"""Create the prompt for classifying the cast."""
return f"""Classify this Farcaster cast into exactly ONE of these categories:
1. Personal Life
2. Spam
3. App/Bot Interactions
4. Crypto Content
5. Not Enough Context To Tell
Cast: "{cast_text}"
Respond in this exact format:
Category: [category name]
Confidence: [number from 0-10]"""
def main():
# Initialize the client
client = genai.Client()
# Load the filtered casts from JSON
print("Loading data/casts_filtered.json...")
with open('data/casts_filtered.json', 'r') as f:
casts = []
for line in f:
casts.append(json.loads(line))
# Sample to 10k records
import random
if len(casts) > 10000:
print(f"Loaded {len(casts)} casts, sampling 10,000...")
random.seed(42) # For reproducibility
casts = random.sample(casts, 10000)
print(f"Processing {len(casts)} casts")
# Create JSONL file with batch requests
jsonl_filename = 'classification_batch_requests.jsonl'
print(f"Creating {jsonl_filename}...")
with open(jsonl_filename, 'w') as f:
for i, cast in enumerate(casts):
cast_hash = cast['Hash']
cast_text = cast['Text']
# Create the classification prompt
prompt = create_classification_prompt(cast_text)
# Create the request object
request = {
"key": f"cast-{i}-{cast_hash}",
"request": {
"contents": [
{
"parts": [{"text": prompt}],
"role": "user"
}
],
"generation_config": {
"temperature": 0.1 # Very low temperature for consistent classification
}
}
}
f.write(json.dumps(request) + '\n')
print(f"Created {jsonl_filename} with {len(casts)} requests")
# Upload the file to the File API
print("Uploading JSONL file to Google File API...")
uploaded_file = client.files.upload(
file=jsonl_filename,
config=types.UploadFileConfig(
display_name='farcaster-cast-classification',
mime_type='application/jsonl'
)
)
print(f"Uploaded file: {uploaded_file.name}")
# Create the batch job
print("Creating batch job...")
batch_job = client.batches.create(
model="gemini-flash-latest",
src=uploaded_file.name,
config={
'display_name': "farcaster-cast-classification",
},
)
job_name = batch_job.name
print(f"Created batch job: {job_name}")
# Poll for job completion
print("\nPolling job status...")
completed_states = {
'JOB_STATE_SUCCEEDED',
'JOB_STATE_FAILED',
'JOB_STATE_CANCELLED',
'JOB_STATE_EXPIRED',
}
while batch_job.state.name not in completed_states:
print(f"Current state: {batch_job.state.name}")
time.sleep(30) # Wait 30 seconds before polling again
batch_job = client.batches.get(name=job_name)
print(f"\nJob finished with state: {batch_job.state.name}")
# Handle results
if batch_job.state.name == 'JOB_STATE_SUCCEEDED':
if batch_job.dest and batch_job.dest.file_name:
result_file_name = batch_job.dest.file_name
print(f"Results are in file: {result_file_name}")
# Download the results
print("Downloading results...")
file_content = client.files.download(file=result_file_name)
# Save raw results (bulk file)
raw_results_filename = 'cast_classification_raw.jsonl'
with open(raw_results_filename, 'wb') as f:
f.write(file_content)
print(f"Saved raw results to {raw_results_filename}")
# Parse and format results
print("Parsing results...")
results = []
for line in file_content.decode('utf-8').strip().split('\n'):
result = json.loads(line)
results.append(result)
# Create a structured output with the original cast data + classification
structured_results = []
for i, cast in enumerate(casts):
# Find matching result
key = f"cast-{i}-{cast['Hash']}"
matching_result = next((r for r in results if r.get('key') == key), None)
output = {
'Fid': cast['Fid'],
'Hash': cast['Hash'],
'Text': cast['Text'],
'Timestamp': cast['Timestamp'],
'Classification': None,
'Confidence': None,
'error': None
}
if matching_result:
if 'response' in matching_result:
# Extract the classification text
response_text = matching_result['response']['candidates'][0]['content']['parts'][0]['text'].strip()
# Parse the response to extract category and confidence
lines = response_text.split('\n')
for line in lines:
if line.startswith('Category:'):
output['Classification'] = line.replace('Category:', '').strip()
elif line.startswith('Confidence:'):
try:
output['Confidence'] = int(line.replace('Confidence:', '').strip())
except ValueError:
output['Confidence'] = None
elif 'status' in matching_result:
output['error'] = matching_result['status']
structured_results.append(output)
# Save structured results (filtered file for analysis)
output_filename = 'cast_classification_results.json'
with open(output_filename, 'w') as f:
json.dump(structured_results, f, indent=2)
print(f"Saved structured results to {output_filename}")
# Print summary by category
successful = sum(1 for r in structured_results if r['Classification'])
failed = sum(1 for r in structured_results if r['error'])
# Count by category
category_counts = {}
for r in structured_results:
if r['Classification']:
cat = r['Classification']
category_counts[cat] = category_counts.get(cat, 0) + 1
# Calculate average confidence per category
category_confidence = {}
for r in structured_results:
if r['Classification'] and r['Confidence'] is not None:
cat = r['Classification']
if cat not in category_confidence:
category_confidence[cat] = []
category_confidence[cat].append(r['Confidence'])
print(f"\nSummary:")
print(f" Total casts: {len(structured_results)}")
print(f" Successful: {successful}")
print(f" Failed: {failed}")
print(f"\nClassification breakdown:")
for cat, count in sorted(category_counts.items(), key=lambda x: x[1], reverse=True):
avg_conf = sum(category_confidence.get(cat, [0])) / len(category_confidence.get(cat, [1])) if cat in category_confidence else 0
print(f" {cat}: {count} (avg confidence: {avg_conf:.1f})")
elif batch_job.state.name == 'JOB_STATE_FAILED':
print(f"Error: {batch_job.error}")
print("\nDone!")
if __name__ == "__main__":
main()
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Farcaster Cast Classification Training\n",
"\n",
"Training a BERT base model to classify casts into 5 categories:\n",
"- Personal Life - Salient\n",
"- Personal Life - Not Salient\n",
"- Crypto Content\n",
"- App/Bot Interactions\n",
"- Not Enough Context To Tell\n",
"\n",
"(Spam category removed - low confidence predictions will be treated as spam)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/ubuntu/.venv/lib/python3.10/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",
" from .autonotebook import tqdm as notebook_tqdm\n",
"/home/ubuntu/.venv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:2249: UnsupportedFieldAttributeWarning: The 'repr' attribute with value False was provided to the `Field()` function, which has no effect in the context it was used. 'repr' is field-specific metadata, and can only be attached to a model field using `Annotated` metadata or by assignment. This may have happened because an `Annotated` type alias using the `type` statement was used, or if the `Field()` function was attached to a single member of a union type.\n",
" warnings.warn(\n",
"/home/ubuntu/.venv/lib/python3.10/site-packages/pydantic/_internal/_generate_schema.py:2249: UnsupportedFieldAttributeWarning: The 'frozen' attribute with value True was provided to the `Field()` function, which has no effect in the context it was used. 'frozen' is field-specific metadata, and can only be attached to a model field using `Annotated` metadata or by assignment. This may have happened because an `Annotated` type alias using the `type` statement was used, or if the `Field()` function was attached to a single member of a union type.\n",
" warnings.warn(\n"
]
}
],
"source": [
"# Imports and environment setup\n",
"import os\n",
"os.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\n",
"\n",
"import torch\n",
"from torch.utils.data import Dataset, DataLoader\n",
"from transformers import (\n",
" AutoTokenizer, \n",
" AutoModelForSequenceClassification, \n",
" Trainer, \n",
" TrainingArguments\n",
")\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.metrics import f1_score, classification_report, precision_recall_fscore_support, accuracy_score\n",
"import numpy as np\n",
"from collections import Counter\n",
"import wandb\n",
"import json\n",
"import random"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\u001b[34m\u001b[1mwandb\u001b[0m: \u001b[32m\u001b[41mERROR\u001b[0m Failed to detect the name of this notebook. You can set it manually with the WANDB_NOTEBOOK_NAME environment variable to enable code saving.\n",
"\u001b[34m\u001b[1mwandb\u001b[0m: Currently logged in as: \u001b[33mjc4p\u001b[0m to \u001b[32mhttps://api.wandb.ai\u001b[0m. Use \u001b[1m`wandb login --relogin`\u001b[0m to force relogin\n"
]
},
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Login to Weights & Biases\n",
"import wandb\n",
"wandb.login()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loaded 8,030 samples\n",
"Number of classes: 5\n",
"Labels: ['Personal Life - Salient', 'Personal Life - Not Salient', 'Crypto Content', 'App/Bot Interactions', 'Not Enough Context To Tell']\n"
]
}
],
"source": [
"# Load and prepare balanced training data\n",
"def load_and_prepare_data(data_file):\n",
" \"\"\"\n",
" Load merged and balanced training data.\n",
" \"\"\"\n",
" # Load the training data\n",
" with open(data_file, 'r') as f:\n",
" data = json.load(f)\n",
" \n",
" # Define label mapping for 5 categories (no Spam)\n",
" label_map = {\n",
" 'Personal Life - Salient': 0,\n",
" 'Personal Life - Not Salient': 1,\n",
" 'Crypto Content': 2,\n",
" 'App/Bot Interactions': 3,\n",
" 'Not Enough Context To Tell': 4\n",
" }\n",
" \n",
" # Prepare data\n",
" texts = []\n",
" labels = []\n",
" \n",
" for item in data:\n",
" classification = item.get('Classification')\n",
" if classification and classification in label_map:\n",
" texts.append(item['Text'])\n",
" labels.append(label_map[classification])\n",
" \n",
" return texts, labels, label_map\n",
"\n",
"# Load the balanced dataset\n",
"texts, labels, label_map = load_and_prepare_data('training_data_balanced.json')\n",
"\n",
"print(f\"Loaded {len(texts):,} samples\")\n",
"print(f\"Number of classes: {len(label_map)}\")\n",
"print(f\"Labels: {list(label_map.keys())}\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Dataset statistics:\n",
"Total samples: 8030\n",
"\n",
"Class distribution:\n",
" Personal Life - Salient: 486 (6.1%)\n",
" Personal Life - Not Salient: 2582 (32.2%)\n",
" Crypto Content: 2582 (32.2%)\n",
" App/Bot Interactions: 1089 (13.6%)\n",
" Not Enough Context To Tell: 1291 (16.1%)\n",
"\n",
"Sample texts per category:\n",
"\n",
"Personal Life - Salient:\n",
" Agreed, Syracuse's terrain makes for pleasant walks. Flat surfaces are great for leisurely strolls and easy biking too....\n",
"\n",
"Personal Life - Not Salient:\n",
" You for talk since nau 😂😭...\n",
"\n",
"Crypto Content:\n",
" Nike’s .SWOOSH platform hits 5M users – Sneaker NFTs minted. 👟 #NFT #Fashion...\n",
"\n",
"App/Bot Interactions:\n",
" I currently rank #421 on The Leaderboard. Where do you rank?...\n",
"\n",
"Not Enough Context To Tell:\n",
" I couldn't have said it better myself. Well done!...\n"
]
}
],
"source": [
"# Display dataset statistics\n",
"from collections import Counter\n",
"\n",
"label_counts = Counter(labels)\n",
"label_names = sorted(label_map.keys(), key=lambda x: label_map[x])\n",
"\n",
"print(\"Dataset statistics:\")\n",
"print(f\"Total samples: {len(texts)}\")\n",
"print(\"\\nClass distribution:\")\n",
"for name in label_names:\n",
" idx = label_map[name]\n",
" count = label_counts[idx]\n",
" percentage = (count / len(texts)) * 100\n",
" print(f\" {name}: {count} ({percentage:.1f}%)\")\n",
"\n",
"# Show sample texts\n",
"print(\"\\nSample texts per category:\")\n",
"for name in label_names:\n",
" idx = label_map[name]\n",
" sample_indices = [i for i, l in enumerate(labels) if l == idx]\n",
" if sample_indices:\n",
" sample_idx = sample_indices[0]\n",
" print(f\"\\n{name}:\")\n",
" print(f\" {texts[sample_idx][:150]}...\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"# Define dataset class for single-label classification\n",
"class TextClassificationDataset(Dataset):\n",
" def __init__(self, texts, labels, tokenizer, max_length=512):\n",
" self.encodings = tokenizer(\n",
" texts,\n",
" padding=True,\n",
" truncation=True,\n",
" max_length=max_length,\n",
" return_tensors=None\n",
" )\n",
" self.labels = labels\n",
"\n",
" def __getitem__(self, idx):\n",
" item = {\n",
" key: torch.tensor(val[idx]) \n",
" for key, val in self.encodings.items()\n",
" }\n",
" item['labels'] = torch.tensor(self.labels[idx])\n",
" return item\n",
"\n",
" def __len__(self):\n",
" return len(self.labels)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"# Define weighted trainer for handling remaining class imbalance\n",
"class WeightedTrainer(Trainer):\n",
" def __init__(self, class_weights, *args, **kwargs):\n",
" super().__init__(*args, **kwargs)\n",
" self.class_weights = torch.FloatTensor(class_weights)\n",
"\n",
" def compute_loss(self, model, inputs, return_outputs=False, num_items_in_batch=None):\n",
" labels = inputs.pop(\"labels\")\n",
" outputs = model(**inputs)\n",
" logits = outputs.logits\n",
" \n",
" # Move class weights to same device as model\n",
" weights = self.class_weights.to(logits.device)\n",
" \n",
" # Apply class weights to CrossEntropyLoss\n",
" loss_fct = torch.nn.CrossEntropyLoss(weight=weights)\n",
" loss = loss_fct(logits.view(-1, model.config.num_labels), labels.view(-1))\n",
" \n",
" return (loss, outputs) if return_outputs else loss"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"# Training function\n",
"def train_classifier(\n",
" texts,\n",
" labels,\n",
" label_map,\n",
" model_name='bert-base-uncased',\n",
" batch_size=128,\n",
" num_epochs=10,\n",
" learning_rate=2e-5,\n",
" max_length=512,\n",
" weight_decay=0.01,\n",
" warmup_ratio=0.1,\n",
"):\n",
" \"\"\"\n",
" Training function for single-label classification with class weighting\n",
" \"\"\"\n",
" wandb.init(project=\"farcaster-cast-classification\")\n",
" \n",
" # Calculate class weights (inverse frequency) for remaining imbalance\n",
" label_counts = Counter(labels)\n",
" num_samples = len(labels)\n",
" num_classes = len(label_map)\n",
" \n",
" class_weights = []\n",
" for i in range(num_classes):\n",
" count = label_counts.get(i, 1)\n",
" weight = num_samples / (num_classes * count)\n",
" class_weights.append(weight)\n",
" \n",
" print(\"Class weights:\")\n",
" label_names = sorted(label_map.keys(), key=lambda x: label_map[x])\n",
" for i, name in enumerate(label_names):\n",
" print(f\" {name}: {class_weights[i]:.2f}\")\n",
" \n",
" # Initialize tokenizer and model\n",
" tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
" model = AutoModelForSequenceClassification.from_pretrained(\n",
" model_name,\n",
" num_labels=len(label_map),\n",
" problem_type=\"single_label_classification\"\n",
" )\n",
" \n",
" model.gradient_checkpointing_enable()\n",
" \n",
" # Split data\n",
" train_texts, val_texts, train_labels, val_labels = train_test_split(\n",
" texts, labels, test_size=0.15, stratify=labels, random_state=42\n",
" )\n",
" \n",
" # Create datasets\n",
" train_dataset = TextClassificationDataset(train_texts, train_labels, tokenizer, max_length)\n",
" val_dataset = TextClassificationDataset(val_texts, val_labels, tokenizer, max_length)\n",
" \n",
" # Calculate training steps\n",
" num_training_steps = (len(train_dataset) // batch_size) * num_epochs\n",
" num_warmup_steps = int(num_training_steps * warmup_ratio)\n",
" \n",
" # Define training arguments\n",
" training_args = TrainingArguments(\n",
" output_dir=\"./farcaster-classifier\",\n",
" num_train_epochs=num_epochs,\n",
" per_device_train_batch_size=batch_size,\n",
" per_device_eval_batch_size=batch_size * 2,\n",
" warmup_steps=num_warmup_steps,\n",
" weight_decay=weight_decay,\n",
" logging_dir=\"./logs\",\n",
" logging_steps=10,\n",
" eval_strategy=\"steps\",\n",
" eval_steps=50,\n",
" save_strategy=\"steps\",\n",
" save_steps=50,\n",
" load_best_model_at_end=True,\n",
" metric_for_best_model=\"eval_f1_macro\",\n",
" learning_rate=learning_rate,\n",
" bf16=True,\n",
" dataloader_num_workers=4,\n",
" group_by_length=True,\n",
" optim=\"adamw_torch_fused\",\n",
" lr_scheduler_type=\"cosine\",\n",
" gradient_checkpointing=True\n",
" )\n",
" \n",
" def compute_metrics(eval_pred):\n",
" predictions, labels = eval_pred\n",
" predictions = np.argmax(predictions, axis=1)\n",
" \n",
" # Calculate per-class metrics\n",
" metrics = {}\n",
" label_names = sorted(label_map.keys(), key=lambda x: label_map[x])\n",
" \n",
" f1_scores = []\n",
" for i, name in enumerate(label_names):\n",
" # Get binary labels for this class\n",
" binary_labels = (labels == i).astype(int)\n",
" binary_preds = (predictions == i).astype(int)\n",
" \n",
" precision, recall, f1, _ = precision_recall_fscore_support(\n",
" binary_labels, binary_preds, average='binary', zero_division=0\n",
" )\n",
" metrics[f\"f1_{name.lower().replace(' ', '_').replace('-', '')}\"[:20]] = f1\n",
" metrics[f\"precision_{name.lower().replace(' ', '_').replace('-', '')}\"[:20]] = precision\n",
" metrics[f\"recall_{name.lower().replace(' ', '_').replace('-', '')}\"[:20]] = recall\n",
" f1_scores.append(f1)\n",
" \n",
" # Overall metrics\n",
" metrics[\"f1_macro\"] = np.mean(f1_scores)\n",
" metrics[\"accuracy\"] = accuracy_score(labels, predictions)\n",
" \n",
" return metrics\n",
" \n",
" # Initialize trainer with class weights\n",
" trainer = WeightedTrainer(\n",
" class_weights=class_weights,\n",
" model=model,\n",
" args=training_args,\n",
" train_dataset=train_dataset,\n",
" eval_dataset=val_dataset,\n",
" compute_metrics=compute_metrics,\n",
" )\n",
" \n",
" # Train the model\n",
" trainer.train()\n",
" \n",
" # Final evaluation\n",
" final_metrics = trainer.evaluate()\n",
" print(\"\\nFinal Evaluation Metrics:\")\n",
" for key, value in final_metrics.items():\n",
" print(f\"{key}: {value}\")\n",
" \n",
" # Save the model and tokenizer\n",
" model.save_pretrained(\"./farcaster-classifier-final\")\n",
" tokenizer.save_pretrained(\"./farcaster-classifier-final\")\n",
" \n",
" # Save label map\n",
" with open(\"./farcaster-classifier-final/label_map.json\", 'w') as f:\n",
" json.dump(label_map, f)\n",
" \n",
" wandb.finish()\n",
" \n",
" return model, tokenizer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Tracking run with wandb version 0.22.2"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Run data is saved locally in <code>/home/ubuntu/wandb/run-20251009_204124-j5y5edzx</code>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"Syncing run <strong><a href='https://wandb.ai/jc4p/farcaster-cast-classification/runs/j5y5edzx' target=\"_blank\">vital-glitter-3</a></strong> to <a href='https://wandb.ai/jc4p/farcaster-cast-classification' target=\"_blank\">Weights & Biases</a> (<a href='https://wandb.me/developer-guide' target=\"_blank\">docs</a>)<br>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" View project at <a href='https://wandb.ai/jc4p/farcaster-cast-classification' target=\"_blank\">https://wandb.ai/jc4p/farcaster-cast-classification</a>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" View run at <a href='https://wandb.ai/jc4p/farcaster-cast-classification/runs/j5y5edzx' target=\"_blank\">https://wandb.ai/jc4p/farcaster-cast-classification/runs/j5y5edzx</a>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Class weights:\n",
" Personal Life - Salient: 3.30\n",
" Personal Life - Not Salient: 0.62\n",
" Crypto Content: 0.62\n",
" App/Bot Interactions: 1.47\n",
" Not Enough Context To Tell: 1.24\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight']\n",
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
]
},
{
"data": {
"text/html": [
"\n",
" <div>\n",
" \n",
" <progress value='1668' max='1712' style='width:300px; height:20px; vertical-align: middle;'></progress>\n",
" [1668/1712 02:33 < 00:04, 10.88 it/s, Epoch 7.79/8]\n",
" </div>\n",
" <table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: left;\">\n",
" <th>Step</th>\n",
" <th>Training Loss</th>\n",
" <th>Validation Loss</th>\n",
" <th>F1 Personal Life Sa</th>\n",
" <th>Precision Personal L</th>\n",
" <th>Recall Personal Life</th>\n",
" <th>F1 Personal Life No</th>\n",
" <th>F1 Crypto Content</th>\n",
" <th>Precision Crypto Con</th>\n",
" <th>Recall Crypto Conten</th>\n",
" <th>F1 App/bot Interacti</th>\n",
" <th>Precision App/bot In</th>\n",
" <th>Recall App/bot Inter</th>\n",
" <th>F1 Not Enough Contex</th>\n",
" <th>Precision Not Enough</th>\n",
" <th>Recall Not Enough Co</th>\n",
" <th>F1 Macro</th>\n",
" <th>Accuracy</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <td>50</td>\n",
" <td>1.595800</td>\n",
" <td>1.591155</td>\n",
" <td>0.030303</td>\n",
" <td>0.434368</td>\n",
" <td>0.469072</td>\n",
" <td>0.451053</td>\n",
" <td>0.577519</td>\n",
" <td>0.462016</td>\n",
" <td>0.770026</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.085714</td>\n",
" <td>0.562500</td>\n",
" <td>0.046392</td>\n",
" <td>0.228918</td>\n",
" <td>0.408299</td>\n",
" </tr>\n",
" <tr>\n",
" <td>100</td>\n",
" <td>1.525800</td>\n",
" <td>1.460845</td>\n",
" <td>0.119403</td>\n",
" <td>0.538462</td>\n",
" <td>0.306701</td>\n",
" <td>0.390805</td>\n",
" <td>0.682927</td>\n",
" <td>0.620253</td>\n",
" <td>0.759690</td>\n",
" <td>0.215962</td>\n",
" <td>0.460000</td>\n",
" <td>0.141104</td>\n",
" <td>0.448802</td>\n",
" <td>0.388679</td>\n",
" <td>0.530928</td>\n",
" <td>0.371580</td>\n",
" <td>0.460581</td>\n",
" </tr>\n",
" <tr>\n",
" <td>150</td>\n",
" <td>1.369000</td>\n",
" <td>1.313050</td>\n",
" <td>0.084211</td>\n",
" <td>0.661905</td>\n",
" <td>0.358247</td>\n",
" <td>0.464883</td>\n",
" <td>0.609756</td>\n",
" <td>0.743494</td>\n",
" <td>0.516796</td>\n",
" <td>0.543326</td>\n",
" <td>0.439394</td>\n",
" <td>0.711656</td>\n",
" <td>0.438486</td>\n",
" <td>0.315909</td>\n",
" <td>0.716495</td>\n",
" <td>0.428132</td>\n",
" <td>0.496266</td>\n",
" </tr>\n",
" <tr>\n",
" <td>200</td>\n",
" <td>1.214200</td>\n",
" <td>1.148946</td>\n",
" <td>0.370044</td>\n",
" <td>0.796875</td>\n",
" <td>0.657216</td>\n",
" <td>0.720339</td>\n",
" <td>0.758270</td>\n",
" <td>0.746867</td>\n",
" <td>0.770026</td>\n",
" <td>0.580460</td>\n",
" <td>0.545946</td>\n",
" <td>0.619632</td>\n",
" <td>0.516129</td>\n",
" <td>0.598639</td>\n",
" <td>0.453608</td>\n",
" <td>0.589048</td>\n",
" <td>0.650622</td>\n",
" </tr>\n",
" <tr>\n",
" <td>250</td>\n",
" <td>1.010600</td>\n",
" <td>1.004799</td>\n",
" <td>0.453988</td>\n",
" <td>0.841270</td>\n",
" <td>0.546392</td>\n",
" <td>0.662500</td>\n",
" <td>0.734993</td>\n",
" <td>0.847973</td>\n",
" <td>0.648579</td>\n",
" <td>0.592593</td>\n",
" <td>0.495868</td>\n",
" <td>0.736196</td>\n",
" <td>0.535645</td>\n",
" <td>0.427692</td>\n",
" <td>0.716495</td>\n",
" <td>0.595944</td>\n",
" <td>0.629876</td>\n",
" </tr>\n",
" <tr>\n",
" <td>300</td>\n",
" <td>0.931000</td>\n",
" <td>0.896634</td>\n",
" <td>0.450980</td>\n",
" <td>0.762136</td>\n",
" <td>0.809278</td>\n",
" <td>0.785000</td>\n",
" <td>0.824632</td>\n",
" <td>0.855556</td>\n",
" <td>0.795866</td>\n",
" <td>0.653061</td>\n",
" <td>0.622222</td>\n",
" <td>0.687117</td>\n",
" <td>0.575949</td>\n",
" <td>0.745902</td>\n",
" <td>0.469072</td>\n",
" <td>0.657925</td>\n",
" <td>0.722822</td>\n",
" </tr>\n",
" <tr>\n",
" <td>350</td>\n",
" <td>0.879800</td>\n",
" <td>0.839245</td>\n",
" <td>0.490196</td>\n",
" <td>0.800000</td>\n",
" <td>0.752577</td>\n",
" <td>0.775564</td>\n",
" <td>0.800000</td>\n",
" <td>0.838527</td>\n",
" <td>0.764858</td>\n",
" <td>0.655949</td>\n",
" <td>0.689189</td>\n",
" <td>0.625767</td>\n",
" <td>0.621891</td>\n",
" <td>0.600962</td>\n",
" <td>0.644330</td>\n",
" <td>0.668720</td>\n",
" <td>0.717842</td>\n",
" </tr>\n",
" <tr>\n",
" <td>400</td>\n",
" <td>0.751500</td>\n",
" <td>0.808259</td>\n",
" <td>0.494624</td>\n",
" <td>0.816456</td>\n",
" <td>0.664948</td>\n",
" <td>0.732955</td>\n",
" <td>0.814921</td>\n",
" <td>0.916129</td>\n",
" <td>0.733850</td>\n",
" <td>0.672131</td>\n",
" <td>0.605911</td>\n",
" <td>0.754601</td>\n",
" <td>0.621444</td>\n",
" <td>0.539924</td>\n",
" <td>0.731959</td>\n",
" <td>0.667215</td>\n",
" <td>0.707884</td>\n",
" </tr>\n",
" <tr>\n",
" <td>450</td>\n",
" <td>0.580500</td>\n",
" <td>0.793636</td>\n",
" <td>0.523810</td>\n",
" <td>0.781170</td>\n",
" <td>0.791237</td>\n",
" <td>0.786172</td>\n",
" <td>0.848322</td>\n",
" <td>0.882682</td>\n",
" <td>0.816537</td>\n",
" <td>0.674221</td>\n",
" <td>0.626316</td>\n",
" <td>0.730061</td>\n",
" <td>0.639118</td>\n",
" <td>0.686391</td>\n",
" <td>0.597938</td>\n",
" <td>0.694329</td>\n",
" <td>0.748548</td>\n",
" </tr>\n",
" <tr>\n",
" <td>500</td>\n",
" <td>0.619300</td>\n",
" <td>0.752609</td>\n",
" <td>0.507177</td>\n",
" <td>0.852843</td>\n",
" <td>0.657216</td>\n",
" <td>0.742358</td>\n",
" <td>0.838174</td>\n",
" <td>0.901786</td>\n",
" <td>0.782946</td>\n",
" <td>0.702857</td>\n",
" <td>0.657754</td>\n",
" <td>0.754601</td>\n",
" <td>0.657596</td>\n",
" <td>0.587045</td>\n",
" <td>0.747423</td>\n",
" <td>0.689633</td>\n",
" <td>0.729461</td>\n",
" </tr>\n",
" <tr>\n",
" <td>550</td>\n",
" <td>0.604800</td>\n",
" <td>0.790841</td>\n",
" <td>0.532374</td>\n",
" <td>0.787879</td>\n",
" <td>0.804124</td>\n",
" <td>0.795918</td>\n",
" <td>0.866920</td>\n",
" <td>0.850746</td>\n",
" <td>0.883721</td>\n",
" <td>0.699422</td>\n",
" <td>0.661202</td>\n",
" <td>0.742331</td>\n",
" <td>0.659091</td>\n",
" <td>0.734177</td>\n",
" <td>0.597938</td>\n",
" <td>0.710745</td>\n",
" <td>0.770124</td>\n",
" </tr>\n",
" <tr>\n",
" <td>600</td>\n",
" <td>0.560500</td>\n",
" <td>0.717147</td>\n",
" <td>0.544503</td>\n",
" <td>0.858491</td>\n",
" <td>0.703608</td>\n",
" <td>0.773371</td>\n",
" <td>0.865789</td>\n",
" <td>0.882038</td>\n",
" <td>0.850129</td>\n",
" <td>0.727811</td>\n",
" <td>0.702857</td>\n",
" <td>0.754601</td>\n",
" <td>0.674699</td>\n",
" <td>0.633484</td>\n",
" <td>0.721649</td>\n",
" <td>0.717235</td>\n",
" <td>0.760996</td>\n",
" </tr>\n",
" <tr>\n",
" <td>650</td>\n",
" <td>0.492000</td>\n",
" <td>0.872252</td>\n",
" <td>0.480000</td>\n",
" <td>0.821023</td>\n",
" <td>0.744845</td>\n",
" <td>0.781081</td>\n",
" <td>0.875989</td>\n",
" <td>0.894879</td>\n",
" <td>0.857881</td>\n",
" <td>0.715084</td>\n",
" <td>0.656410</td>\n",
" <td>0.785276</td>\n",
" <td>0.675991</td>\n",
" <td>0.617021</td>\n",
" <td>0.747423</td>\n",
" <td>0.705629</td>\n",
" <td>0.766805</td>\n",
" </tr>\n",
" <tr>\n",
" <td>700</td>\n",
" <td>0.396000</td>\n",
" <td>0.870288</td>\n",
" <td>0.503817</td>\n",
" <td>0.843844</td>\n",
" <td>0.724227</td>\n",
" <td>0.779473</td>\n",
" <td>0.858942</td>\n",
" <td>0.837838</td>\n",
" <td>0.881137</td>\n",
" <td>0.709877</td>\n",
" <td>0.714286</td>\n",
" <td>0.705521</td>\n",
" <td>0.663636</td>\n",
" <td>0.593496</td>\n",
" <td>0.752577</td>\n",
" <td>0.703149</td>\n",
" <td>0.760166</td>\n",
" </tr>\n",
" <tr>\n",
" <td>750</td>\n",
" <td>0.343400</td>\n",
" <td>0.845435</td>\n",
" <td>0.519481</td>\n",
" <td>0.818697</td>\n",
" <td>0.744845</td>\n",
" <td>0.780027</td>\n",
" <td>0.840499</td>\n",
" <td>0.907186</td>\n",
" <td>0.782946</td>\n",
" <td>0.711111</td>\n",
" <td>0.649746</td>\n",
" <td>0.785276</td>\n",
" <td>0.691244</td>\n",
" <td>0.625000</td>\n",
" <td>0.773196</td>\n",
" <td>0.708472</td>\n",
" <td>0.755187</td>\n",
" </tr>\n",
" <tr>\n",
" <td>800</td>\n",
" <td>0.401300</td>\n",
" <td>0.907402</td>\n",
" <td>0.518519</td>\n",
" <td>0.804178</td>\n",
" <td>0.793814</td>\n",
" <td>0.798962</td>\n",
" <td>0.865409</td>\n",
" <td>0.843137</td>\n",
" <td>0.888889</td>\n",
" <td>0.713846</td>\n",
" <td>0.716049</td>\n",
" <td>0.711656</td>\n",
" <td>0.692708</td>\n",
" <td>0.700000</td>\n",
" <td>0.685567</td>\n",
" <td>0.717889</td>\n",
" <td>0.776763</td>\n",
" </tr>\n",
" <tr>\n",
" <td>850</td>\n",
" <td>0.360000</td>\n",
" <td>0.906668</td>\n",
" <td>0.500000</td>\n",
" <td>0.795918</td>\n",
" <td>0.804124</td>\n",
" <td>0.800000</td>\n",
" <td>0.869677</td>\n",
" <td>0.868557</td>\n",
" <td>0.870801</td>\n",
" <td>0.703226</td>\n",
" <td>0.741497</td>\n",
" <td>0.668712</td>\n",
" <td>0.685851</td>\n",
" <td>0.641256</td>\n",
" <td>0.737113</td>\n",
" <td>0.711751</td>\n",
" <td>0.774274</td>\n",
" </tr>\n",
" <tr>\n",
" <td>900</td>\n",
" <td>0.209300</td>\n",
" <td>0.899608</td>\n",
" <td>0.571429</td>\n",
" <td>0.835165</td>\n",
" <td>0.783505</td>\n",
" <td>0.808511</td>\n",
" <td>0.864230</td>\n",
" <td>0.873351</td>\n",
" <td>0.855297</td>\n",
" <td>0.709877</td>\n",
" <td>0.714286</td>\n",
" <td>0.705521</td>\n",
" <td>0.705314</td>\n",
" <td>0.663636</td>\n",
" <td>0.752577</td>\n",
" <td>0.731872</td>\n",
" <td>0.780083</td>\n",
" </tr>\n",
" <tr>\n",
" <td>950</td>\n",
" <td>0.226400</td>\n",
" <td>0.973592</td>\n",
" <td>0.523810</td>\n",
" <td>0.799479</td>\n",
" <td>0.791237</td>\n",
" <td>0.795337</td>\n",
" <td>0.855643</td>\n",
" <td>0.869333</td>\n",
" <td>0.842377</td>\n",
" <td>0.707965</td>\n",
" <td>0.681818</td>\n",
" <td>0.736196</td>\n",
" <td>0.676399</td>\n",
" <td>0.640553</td>\n",
" <td>0.716495</td>\n",
" <td>0.711831</td>\n",
" <td>0.767635</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1000</td>\n",
" <td>0.240300</td>\n",
" <td>0.921706</td>\n",
" <td>0.551724</td>\n",
" <td>0.814324</td>\n",
" <td>0.791237</td>\n",
" <td>0.802614</td>\n",
" <td>0.851406</td>\n",
" <td>0.883333</td>\n",
" <td>0.821705</td>\n",
" <td>0.685714</td>\n",
" <td>0.641711</td>\n",
" <td>0.736196</td>\n",
" <td>0.684864</td>\n",
" <td>0.660287</td>\n",
" <td>0.711340</td>\n",
" <td>0.715264</td>\n",
" <td>0.765975</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1050</td>\n",
" <td>0.217900</td>\n",
" <td>1.024940</td>\n",
" <td>0.481203</td>\n",
" <td>0.773956</td>\n",
" <td>0.811856</td>\n",
" <td>0.792453</td>\n",
" <td>0.869340</td>\n",
" <td>0.870466</td>\n",
" <td>0.868217</td>\n",
" <td>0.711656</td>\n",
" <td>0.711656</td>\n",
" <td>0.711656</td>\n",
" <td>0.678851</td>\n",
" <td>0.687831</td>\n",
" <td>0.670103</td>\n",
" <td>0.706701</td>\n",
" <td>0.770954</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1100</td>\n",
" <td>0.196000</td>\n",
" <td>1.072047</td>\n",
" <td>0.487805</td>\n",
" <td>0.778846</td>\n",
" <td>0.835052</td>\n",
" <td>0.805970</td>\n",
" <td>0.869001</td>\n",
" <td>0.872396</td>\n",
" <td>0.865633</td>\n",
" <td>0.704819</td>\n",
" <td>0.692308</td>\n",
" <td>0.717791</td>\n",
" <td>0.689474</td>\n",
" <td>0.704301</td>\n",
" <td>0.675258</td>\n",
" <td>0.711414</td>\n",
" <td>0.777593</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1150</td>\n",
" <td>0.113100</td>\n",
" <td>1.009274</td>\n",
" <td>0.565789</td>\n",
" <td>0.794344</td>\n",
" <td>0.796392</td>\n",
" <td>0.795367</td>\n",
" <td>0.861809</td>\n",
" <td>0.838631</td>\n",
" <td>0.886305</td>\n",
" <td>0.689189</td>\n",
" <td>0.766917</td>\n",
" <td>0.625767</td>\n",
" <td>0.683805</td>\n",
" <td>0.682051</td>\n",
" <td>0.685567</td>\n",
" <td>0.719192</td>\n",
" <td>0.771784</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1200</td>\n",
" <td>0.160200</td>\n",
" <td>1.015507</td>\n",
" <td>0.567568</td>\n",
" <td>0.782082</td>\n",
" <td>0.832474</td>\n",
" <td>0.806492</td>\n",
" <td>0.867280</td>\n",
" <td>0.882353</td>\n",
" <td>0.852713</td>\n",
" <td>0.727273</td>\n",
" <td>0.696629</td>\n",
" <td>0.760736</td>\n",
" <td>0.674095</td>\n",
" <td>0.733333</td>\n",
" <td>0.623711</td>\n",
" <td>0.728541</td>\n",
" <td>0.780083</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1250</td>\n",
" <td>0.117900</td>\n",
" <td>1.075018</td>\n",
" <td>0.514706</td>\n",
" <td>0.802667</td>\n",
" <td>0.775773</td>\n",
" <td>0.788991</td>\n",
" <td>0.859671</td>\n",
" <td>0.841584</td>\n",
" <td>0.878553</td>\n",
" <td>0.701538</td>\n",
" <td>0.703704</td>\n",
" <td>0.699387</td>\n",
" <td>0.678481</td>\n",
" <td>0.666667</td>\n",
" <td>0.690722</td>\n",
" <td>0.708677</td>\n",
" <td>0.766805</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1300</td>\n",
" <td>0.103900</td>\n",
" <td>1.106329</td>\n",
" <td>0.516129</td>\n",
" <td>0.791774</td>\n",
" <td>0.793814</td>\n",
" <td>0.792793</td>\n",
" <td>0.871595</td>\n",
" <td>0.875000</td>\n",
" <td>0.868217</td>\n",
" <td>0.703226</td>\n",
" <td>0.741497</td>\n",
" <td>0.668712</td>\n",
" <td>0.668224</td>\n",
" <td>0.611111</td>\n",
" <td>0.737113</td>\n",
" <td>0.710393</td>\n",
" <td>0.770124</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1350</td>\n",
" <td>0.130300</td>\n",
" <td>1.165310</td>\n",
" <td>0.504348</td>\n",
" <td>0.788413</td>\n",
" <td>0.806701</td>\n",
" <td>0.797452</td>\n",
" <td>0.868895</td>\n",
" <td>0.864450</td>\n",
" <td>0.873385</td>\n",
" <td>0.696486</td>\n",
" <td>0.726667</td>\n",
" <td>0.668712</td>\n",
" <td>0.668258</td>\n",
" <td>0.622222</td>\n",
" <td>0.721649</td>\n",
" <td>0.707088</td>\n",
" <td>0.770954</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1400</td>\n",
" <td>0.113800</td>\n",
" <td>1.137059</td>\n",
" <td>0.515625</td>\n",
" <td>0.776442</td>\n",
" <td>0.832474</td>\n",
" <td>0.803483</td>\n",
" <td>0.863402</td>\n",
" <td>0.861183</td>\n",
" <td>0.865633</td>\n",
" <td>0.723404</td>\n",
" <td>0.716867</td>\n",
" <td>0.730061</td>\n",
" <td>0.664879</td>\n",
" <td>0.692737</td>\n",
" <td>0.639175</td>\n",
" <td>0.714159</td>\n",
" <td>0.775104</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1450</td>\n",
" <td>0.096600</td>\n",
" <td>1.125042</td>\n",
" <td>0.511628</td>\n",
" <td>0.785000</td>\n",
" <td>0.809278</td>\n",
" <td>0.796954</td>\n",
" <td>0.868895</td>\n",
" <td>0.864450</td>\n",
" <td>0.873385</td>\n",
" <td>0.719512</td>\n",
" <td>0.715152</td>\n",
" <td>0.723926</td>\n",
" <td>0.671835</td>\n",
" <td>0.673575</td>\n",
" <td>0.670103</td>\n",
" <td>0.713765</td>\n",
" <td>0.774274</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1500</td>\n",
" <td>0.157300</td>\n",
" <td>1.149831</td>\n",
" <td>0.516129</td>\n",
" <td>0.787500</td>\n",
" <td>0.811856</td>\n",
" <td>0.799492</td>\n",
" <td>0.870013</td>\n",
" <td>0.866667</td>\n",
" <td>0.873385</td>\n",
" <td>0.721212</td>\n",
" <td>0.712575</td>\n",
" <td>0.730061</td>\n",
" <td>0.670077</td>\n",
" <td>0.664975</td>\n",
" <td>0.675258</td>\n",
" <td>0.715385</td>\n",
" <td>0.775934</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1550</td>\n",
" <td>0.112300</td>\n",
" <td>1.122491</td>\n",
" <td>0.515625</td>\n",
" <td>0.780247</td>\n",
" <td>0.814433</td>\n",
" <td>0.796974</td>\n",
" <td>0.868557</td>\n",
" <td>0.866324</td>\n",
" <td>0.870801</td>\n",
" <td>0.717325</td>\n",
" <td>0.710843</td>\n",
" <td>0.723926</td>\n",
" <td>0.661458</td>\n",
" <td>0.668421</td>\n",
" <td>0.654639</td>\n",
" <td>0.711988</td>\n",
" <td>0.772614</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1600</td>\n",
" <td>0.108300</td>\n",
" <td>1.167189</td>\n",
" <td>0.508197</td>\n",
" <td>0.779412</td>\n",
" <td>0.819588</td>\n",
" <td>0.798995</td>\n",
" <td>0.867779</td>\n",
" <td>0.862245</td>\n",
" <td>0.873385</td>\n",
" <td>0.716049</td>\n",
" <td>0.720497</td>\n",
" <td>0.711656</td>\n",
" <td>0.668380</td>\n",
" <td>0.666667</td>\n",
" <td>0.670103</td>\n",
" <td>0.711880</td>\n",
" <td>0.774274</td>\n",
" </tr>\n",
" <tr>\n",
" <td>1650</td>\n",
" <td>0.069700</td>\n",
" <td>1.164802</td>\n",
" <td>0.528000</td>\n",
" <td>0.782716</td>\n",
" <td>0.817010</td>\n",
" <td>0.799496</td>\n",
" <td>0.867779</td>\n",
" <td>0.862245</td>\n",
" <td>0.873385</td>\n",
" <td>0.716049</td>\n",
" <td>0.720497</td>\n",
" <td>0.711656</td>\n",
" <td>0.668380</td>\n",
" <td>0.666667</td>\n",
" <td>0.670103</td>\n",
" <td>0.715941</td>\n",
" <td>0.775104</td>\n",
" </tr>\n",
" </tbody>\n",
"</table><p>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Train the model with BERT base\n",
"model, tokenizer = train_classifier(\n",
" texts,\n",
" labels,\n",
" label_map,\n",
" model_name='bert-base-uncased',\n",
" batch_size=32,\n",
" num_epochs=8,\n",
" learning_rate=2e-5,\n",
" weight_decay=0.01,\n",
" warmup_ratio=0.2\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Text: GM everyone! Hope you have a great day\n",
"Prediction: Personal Life - Not Salient (confidence: 0.974)\n",
"\n",
"Text: Just bought more ETH, bullish on the merge\n",
"Prediction: Crypto Content (confidence: 0.988)\n",
"\n",
"Text: This bot is spamming my feed with nonsense\n",
"Prediction: App/Bot Interactions (confidence: 0.971)\n",
"\n",
"Text: I've been reflecting on my journey as a developer and realized that the best projects come from solving real problems I face daily\n",
"Prediction: Personal Life - Salient (confidence: 0.860)\n"
]
}
],
"source": [
"# Batch prediction function\n",
"def predict_batch(texts, model, tokenizer, device='cuda' if torch.cuda.is_available() else 'cpu'):\n",
" \"\"\"\n",
" Efficient batch prediction for single-label classification\n",
" \"\"\"\n",
" model.to(device)\n",
" model.eval()\n",
" \n",
" inputs = tokenizer(\n",
" texts,\n",
" padding=True,\n",
" truncation=True,\n",
" max_length=512,\n",
" return_tensors=\"pt\"\n",
" ).to(device)\n",
" \n",
" with torch.no_grad():\n",
" outputs = model(**inputs)\n",
" # Apply softmax for single-label\n",
" predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)\n",
" predicted_labels = torch.argmax(predictions, dim=-1)\n",
" \n",
" return predicted_labels.cpu().numpy(), predictions.cpu().numpy()\n",
"\n",
"# Test on some examples\n",
"test_texts = [\n",
" \"GM everyone! Hope you have a great day\",\n",
" \"Just bought more ETH, bullish on the merge\",\n",
" \"This bot is spamming my feed with nonsense\",\n",
" \"I've been reflecting on my journey as a developer and realized that the best projects come from solving real problems I face daily\"\n",
"]\n",
"\n",
"pred_labels, pred_probs = predict_batch(test_texts, model, tokenizer)\n",
"\n",
"# Get reverse label mapping\n",
"idx_to_label = {v: k for k, v in label_map.items()}\n",
"\n",
"for i, text in enumerate(test_texts):\n",
" print(f\"\\nText: {text}\")\n",
" predicted_class = idx_to_label[pred_labels[i]]\n",
" confidence = pred_probs[i][pred_labels[i]]\n",
" print(f\"Prediction: {predicted_class} (confidence: {confidence:.3f})\")"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Saving model to ./farcaster-classifier-final...\n",
"✓ Model saved successfully!\n",
"✓ Tokenizer saved successfully!\n",
"✓ Label map saved successfully!\n",
"\n",
"Model location: ./farcaster-classifier-final/\n",
"Files saved:\n",
" - config.json\n",
" - model.safetensors (or pytorch_model.bin)\n",
" - tokenizer files\n",
" - label_map.json\n"
]
}
],
"source": [
"# Save the final model and configuration\n",
"print(\"Saving model to ./farcaster-classifier-final...\")\n",
"\n",
"# Save model and tokenizer\n",
"model.save_pretrained(\"./farcaster-classifier-final\")\n",
"tokenizer.save_pretrained(\"./farcaster-classifier-final\")\n",
"\n",
"# Save label map\n",
"with open(\"./farcaster-classifier-final/label_map.json\", 'w') as f:\n",
" json.dump(label_map, f, indent=2)\n",
"\n",
"print(\"✓ Model saved successfully!\")\n",
"print(\"✓ Tokenizer saved successfully!\")\n",
"print(\"✓ Label map saved successfully!\")\n",
"print(f\"\\nModel location: ./farcaster-classifier-final/\")\n",
"print(f\"Files saved:\")\n",
"print(f\" - config.json\")\n",
"print(f\" - model.safetensors (or pytorch_model.bin)\")\n",
"print(f\" - tokenizer files\")\n",
"print(f\" - label_map.json\")"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Random Samples from Each Category with Model Predictions\n",
"================================================================================\n",
"\n",
"\n",
"================================================================================\n",
"CATEGORY: Personal Life - Salient\n",
"================================================================================\n",
"\n",
"1. ✓ Text:\n",
" many such cases\n",
"\n",
"born and raised in LA\n",
"\n",
"26 years later have still never developed the muscle to check the weather daily\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.991)\n",
"\n",
"\n",
"2. ✓ Text:\n",
" facebook just reminded me that 4 years ago i came 3rd at the diamond league\n",
"\n",
"1st and 2nd place in the photo are now olympic medalists!\n",
"\n",
"and they are both 6+ years older than me \n",
"\n",
"by the time LA 2028 c...\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.991)\n",
"\n",
"\n",
"3. ✓ Text:\n",
" The Butterfly Nebula from Hubble. Stars can make beautiful patterns as they age -- sometimes similar to flowers or insects. NGC 6302, the Butterfly Nebula, is a notable example.\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.983)\n",
"\n",
"\n",
"4. ✓ Text:\n",
" Và khi điều quan trọng nhất còn chưa được thực hiện, các mắt xích dậm chân tại chỗ, tổ chức chưa thể tiến về phía trước, sự bận rộn với những điều nhỏ nhặt đó cũng chẳng khác gì 1 sự lười biếng được v...\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.988)\n",
"\n",
"\n",
"5. ✓ Text:\n",
" Republican love to lied. I sold insurance for years, and my wife used to works for Medicaid—you need proof of legal status to obtain insurance. The only type of insurance someone can get without that ...\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.936)\n",
"\n",
"\n",
"\n",
"================================================================================\n",
"CATEGORY: Personal Life - Not Salient\n",
"================================================================================\n",
"\n",
"1. ✓ Text:\n",
" “naomi can u make a video about ai”\n",
"\n",
"me: yes i can i am quite the expert \n",
"\n",
"also me:\n",
"\n",
" Predicted: Personal Life - Not Salient (confidence: 0.574)\n",
"\n",
"\n",
"2. ✗ Text:\n",
" すごい計画👏子ども達も体力あるのね👀❤️素晴らしい👏\n",
"\n",
" Predicted: Personal Life - Salient (confidence: 0.539)\n",
" Top 3 predictions:\n",
" - Personal Life - Salient: 0.539\n",
" - Personal Life - Not Salient: 0.397\n",
" - App/Bot Interactions: 0.023\n",
"\n",
"\n",
"3. ✗ Text:\n",
" ITAP\n",
"I took this photo \n",
"What do you think?\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.879)\n",
" Top 3 predictions:\n",
" - Not Enough Context To Tell: 0.879\n",
" - Personal Life - Not Salient: 0.104\n",
" - App/Bot Interactions: 0.009\n",
"\n",
"\n",
"4. ✓ Text:\n",
" Happy tree Tuesday friend\n",
"\n",
" Predicted: Personal Life - Not Salient (confidence: 0.982)\n",
"\n",
"\n",
"5. ✓ Text:\n",
" Good morning, dear friend.\n",
"\n",
" Predicted: Personal Life - Not Salient (confidence: 0.984)\n",
"\n",
"\n",
"\n",
"================================================================================\n",
"CATEGORY: Crypto Content\n",
"================================================================================\n",
"\n",
"1. ✓ Text:\n",
" so glad you like it mate!! 👍 \n",
"https://www.empirebuilder.world/empire/0x0c22b5e951683f6fadebdcb931cdf801d2aaa70e\n",
"\n",
" Predicted: Crypto Content (confidence: 0.907)\n",
"\n",
"\n",
"2. ✗ Text:\n",
" Help me get Fuel by liking this cast!\n",
"5 Likes = 1 Fuel🔋\n",
"Support my mech battles in Wreck League Versus 🤖 by \n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.982)\n",
" Top 3 predictions:\n",
" - App/Bot Interactions: 0.982\n",
" - Crypto Content: 0.012\n",
" - Personal Life - Salient: 0.002\n",
"\n",
"\n",
"3. ✓ Text:\n",
" does token trade history in the app help?\n",
"\n",
"https://warpcast.com/rainbow/0x3c1185b9\n",
"\n",
" Predicted: Crypto Content (confidence: 0.930)\n",
"\n",
"\n",
"4. ✓ Text:\n",
" Cryptocurrency derivatives amplify trading opportunities, catering to experienced traders exclusively.\n",
"\n",
" Predicted: Crypto Content (confidence: 0.989)\n",
"\n",
"\n",
"5. ✓ Text:\n",
" Exit scams vanish with funds, betraying community trust completely.\n",
"\n",
" Predicted: Crypto Content (confidence: 0.989)\n",
"\n",
"\n",
"\n",
"================================================================================\n",
"CATEGORY: App/Bot Interactions\n",
"================================================================================\n",
"\n",
"1. ✓ Text:\n",
" \\#Privasea\n",
"\n",
"Privasea Update : All tasks will be removed tomorrow March 31st🔶\n",
"\n",
"⭕️ Complete all tasks in ImHuman app ➡️ privasea.ai/download-app\n",
"\n",
"⏺ ref code ➡️ ZwkQxkV 👈\n",
"\n",
"📌You have also time to complete...\n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.960)\n",
"\n",
"\n",
"2. ✓ Text:\n",
" I currently rank #1000+ on The Leaderboard. Where do you rank?\n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.988)\n",
"\n",
"\n",
"3. ✓ Text:\n",
" 🌅 Just sent my daily GM on RISE!\n",
"\n",
"Join me in this amazing journey and discover a new way to connect with the community.\n",
"\n",
"Start your journey here: https://onchaingm.com?ref=0xB68B3e0BfA96fd90c1D889e6F8...\n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.977)\n",
"\n",
"\n",
"4. ✓ Text:\n",
" I currently rank #1000+ on The Leaderboard. Where do you rank?\n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.988)\n",
"\n",
"\n",
"5. ✓ Text:\n",
" has blocked \n",
" has blocked \n",
" has sneakily blocked \n",
"\n",
"\n",
" Predicted: App/Bot Interactions (confidence: 0.813)\n",
"\n",
"\n",
"\n",
"================================================================================\n",
"CATEGORY: Not Enough Context To Tell\n",
"================================================================================\n",
"\n",
"1. ✓ Text:\n",
" 11 👏 Tally-ho! Spectacular!\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.700)\n",
"\n",
"\n",
"2. ✓ Text:\n",
" I couldn't have said it better myself. Well done!\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.972)\n",
"\n",
"\n",
"3. ✓ Text:\n",
" This is exactly how I feel. Great perspective!\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.977)\n",
"\n",
"\n",
"4. ✓ Text:\n",
" Yes! This is such an important topic, and you nailed it.\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.966)\n",
"\n",
"\n",
"5. ✓ Text:\n",
" That's holds a deep meaning\n",
"\n",
" Predicted: Not Enough Context To Tell (confidence: 0.906)\n",
"\n",
"\n",
"================================================================================\n"
]
}
],
"source": [
"# Show 5 random samples from each category with predictions\n",
"import random\n",
"\n",
"print(\"Random Samples from Each Category with Model Predictions\")\n",
"print(\"=\"*80)\n",
"\n",
"# Get reverse label mapping\n",
"idx_to_label = {v: k for k, v in label_map.items()}\n",
"\n",
"# For each category\n",
"for category_name in sorted(label_map.keys(), key=lambda x: label_map[x]):\n",
" category_idx = label_map[category_name]\n",
" \n",
" print(f\"\\n\\n{'='*80}\")\n",
" print(f\"CATEGORY: {category_name}\")\n",
" print('='*80)\n",
" \n",
" # Get all indices for this category\n",
" category_indices = [i for i, l in enumerate(labels) if l == category_idx]\n",
" \n",
" # Sample 5 random examples\n",
" sample_indices = random.sample(category_indices, min(5, len(category_indices)))\n",
" sample_texts = [texts[i] for i in sample_indices]\n",
" \n",
" # Get predictions\n",
" pred_labels, pred_probs = predict_batch(sample_texts, model, tokenizer)\n",
" \n",
" # Display each sample\n",
" for i, (text, pred_label, probs) in enumerate(zip(sample_texts, pred_labels, pred_probs), 1):\n",
" predicted_category = idx_to_label[pred_label]\n",
" confidence = probs[pred_label]\n",
" is_correct = \"✓\" if pred_label == category_idx else \"✗\"\n",
" \n",
" print(f\"\\n{i}. {is_correct} Text:\")\n",
" print(f\" {text[:200]}{'...' if len(text) > 200 else ''}\")\n",
" print(f\"\\n Predicted: {predicted_category} (confidence: {confidence:.3f})\")\n",
" \n",
" # Show top 3 predictions if wrong\n",
" if pred_label != category_idx:\n",
" top3_indices = np.argsort(probs)[-3:][::-1]\n",
" print(f\" Top 3 predictions:\")\n",
" for idx in top3_indices:\n",
" print(f\" - {idx_to_label[idx]}: {probs[idx]:.3f}\")\n",
" print()\n",
"\n",
"print(\"\\n\" + \"=\"*80)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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LF25TtlDeIbxk+zI4T/Vq2dX6+uZ0x78m9KkeSvrKsfr166WIUMNG7GCPp4svr62q8A7OTmpSJEiGjNmjBo2TPmzmC1GSlY5Qrr55ZdfVK9ePZ07d07ZstkuQmSxWLRs2TK1atXqsccVeZ+PBxIXy48OJCGoyhtmh4AM6twvM8wOARmUsxNVLiQui6djzFjou/zRa+uklxktmZpOpdZkUVFRunLlikaNGqW2bdsmSGgBAAAA4Em0b98+622aJUqUUNmyZVM1Dkmtyb766iv16NFDZcqU0WeffWZ2OAAAAACQri5fvqz27dtr8+bN8vPzk/Rgcd06depoyZIlCgwMTNF4PFLJZF27dlVsbKz27dunHDkSv3fGMAxTph4DAAAAeDQni3mbI+rTp49u376tP//8U9evX9f169d16NAh3bp1S2+8kfLbmKjUAgAAAAAemzVr1mjDhg3WR5RKUvHixTV79uxULRRlV1J74cIFbd26VZcvX1abNm2UM2dOxcbG6ubNm/L19WUpegAAAABPPB7pkzJxcXFycXFJsN/FxUVxcXEpHi9V048Nw1D//v2VL18+derUSf3799dff/0lSYqIiFDevHn1/vvvp2ZoAAAAAMATrG7dunrzzTd18eJF674LFy6oX79+qlevXorHS1VSO2XKFM2cOVMDBw7U+vXr9e+nAvn6+qp169b67rvvUjM0AAAAADgU7qlNmQ8++EC3bt1S3rx5VaBAARUoUED58uXTrVu3UlUcTdX0408++UQvvfSSxo8fr2vXriVoL1WqlH766afUDA0AAAAAeILlypVLv/32mzZs2KCjRx8847dYsWKqX79+qsZLVaX23LlzqlatWpLtXl5eunXrVqoCAgAAAAA8eX7++WcVL15ct27dksViUYMGDdSnTx/16dNHFStWVIkSJfTLL7+keNxUJbVBQUE6d+5cku379u1T7ty5UzM0AAAAADgUi8W8zZHMmDFDPXv2lI+PT4I2X19fvfLKK5o+fXqKx01VUtu6dWvNnTtXf//9t3Vf/Ipf69at08KFC9W2bdvUDA0AAAAAeAIdOHBAjRo1SrK9YcOG2rdvX4rHTVVSO3r0aGXPnl1lypTRSy+9JIvFokmTJqlGjRpq3LixSpUqpaFDh6ZmaAAAAABwKE4Wi2mbI7l06VKij/KJlylTJl25ciXF46YqqfX1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YxERERERkddNE0VZlyq1IiIiIiIikmypUisiIiIiIvIvmDRVlFWpUisiIiIiIiLJliq1IiIiIiIi/4YKtValSq2IiIiIiIgkW0pqRUREREREJNlS92MREREREZF/Qb2PrUuVWhEREREREUm2VKkVERERERH5F0wq1VqVKrUiIiIiIiKSbCmpFRERERERkWRL3Y9FRERERET+BZOmirIqVWpFREREREQk2VKlVkRERERE5N9QodaqVKkVERERERGRZEuVWhERERERkX9BhVrrUqVWREREREREki0ltSIiIiIiIpJsqfuxiIiIiIjIv2BS/2OrUqVWREREREREki1VakVERERERP4Fk6aKsipVakVERERERCTZUlIrIiIiIiIiyZa6H4uIiIiIiPwLmijKulSpFRERERERkWRLSa2IiIiIiIgkW0pqRUREREREJNlSUisiIiIiIiLJliaKEhERERER+Rc0UZR1qVIrIiIiIiIiyZYqtSIiIiIiIv+CCZVqrUmVWhEREREREUm2VKkVERERERH5FzSm1rpUqRUREREREZFkS0mtiIiIiIiIJFvqfiwiIiIiIvIvqPexdalSKyIiIiIiIsmWKrUiIiIiIiL/hkq1VqVKrYiIiIiIiCRbSmpFREREREQk2VL3YxERERERkX/BpP7HVqVKrYiIiIiIiCRbqtSKiIiIiIj8CyYVaq1KlVoRERERERFJtlSpFRERERER+RdUqLUuVWpFREREREQk2VJSKyIiIiIiIsmWuh+LiIiIiIj8G+p/bFWq1IqIiIiIiEiypUqtiIiIiIjIv2BSqdaqVKkVERERERGRZEtJrYiIiIiIiCRb6n4sIiIiIiLyL5jU+9iqVKkVERERERGRZMtkGIZh7SBEkoPo6GjGjRvH4MGDcXR0tHY48gZR25CXUduQl1HbkJdR2xD555TUivxNDx48wNXVlYiICNKnT2/tcOQNorYhL6O2IS+jtiEvo7Yh8s+p+7GIiIiIiIgkW0pqRUREREREJNlSUisiIiIiIiLJlpJakb/J0dGRYcOGadIGeY7ahryM2oa8jNqGvIzahsg/p4miREREREREJNlSpVZERERERESSLSW1IiIiIiIikmwpqRUREREREZFkS0mtiIiIiIiIJFtKakVERERERCTZUlIrIiIiIiIiyZaSWkn1tKqVvMif24XaiYiI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PRiGQbZs2Th+/Di9e/dmzpw5tGnTBkhchY2NjeXMmTO89957VoxeRERSkps3bwLg7u5u5UhE5E2ipFZEXiohoT1x4gT169fHwcGBixcv4uPjQ58+fXj48CF9+vRh/vz5fPLJJ5b3xcTE0L17d+bOncudO3dInz69KrQiIiIi8lqo+7GIvNCzCW2ZMmVo1KgRW7duZdWqVXh4eDBjxgwqV67MkCFDaNOmDYsWLQIgOjqaPn36sHTpUvbt24erq6sSWhERERF5bTRlnIi8kI2NDb/99htVqlShdu3ajB8/HgBPT09CQ0Pp27cvTk5ODBs2DJPJROvWrYmPj+f8+fPMnTuX3bt3U6xYMSt/ChERERFJ6ZTUishLxcfHkydPHqKjo9m9ezfly5cHIE+ePDg6OhIdHY2dnR0DBw7E1taWdu3aAXDo0CEltCIiIiKSJDSmVkT+UnBwMD169MBsNuPv70/OnDnJmzcvbdu2tVRvASIiIli0aBFVqlShQIECVoxYRERERFITJbUi8j8FBwfTs2dPoqKiOHHiBK1bt2by5MnA02qura0t8Mc4XBERERGRpKJvnyLyP7311ltMmTIFW1tb0qdPT/369S37nk1ildCKiIiISFJTpVZE/raLFy/y2WefYRgGQ4cOpVy5ctYOSURERERSOZVVRORv8/b2JiAgAHt7e/r168e+ffusHZKIiIiIpHJKakXkH3nrrbeYOHEiOXLkwNPT09rhiIiIiEgqp+7HIvL/EhMTg4ODg7XDEBEREZFUTkmtiIiIiIiIJFvqfiwiIiIiIiLJlpJaERERERERSbaU1IqIiIiIiEiypaRWREREREREki0ltSIiIiIiIpJsKakVERERERGRZEtJrYiIpApeXl60adPG8nrHjh2YTCZ27NhhtZj+7M8xJoVKlSrx7rvvvtJzWuNziIhI6qWkVkREXrv58+djMpks/9KkSYOPjw/du3fn5s2b1g7vH1m/fj3Dhw+3agwmk4nu3btbNQYREZE3hZ21AxARkdRj5MiR5MmThydPnrB7925mzJjB+vXrOXXqFM7Ozkkai6+vL48fP8bBweEfvW/9+vVMmzbN6omtiIiIPKWkVkREkkytWrUoUaIEAB06dCBz5sx88803rFmzho8//viF73n06BFp06Z95bHY2NiQJk2aV35eERERSVrqfiwiIlbzwQcf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",
"text/plain": [
"<Figure size 1000x800 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Normalized Confusion Matrix (row percentages):\n",
"============================================================\n",
"\n",
"PL-Salient:\n",
" -> PL-Salient: 60.3%\n",
" -> PL-NotSalient: 24.7%\n",
" -> Crypto: 2.7%\n",
" -> App/Bot: 2.7%\n",
" -> No Context: 9.6%\n",
"\n",
"PL-NotSalient:\n",
" -> PL-Salient: 5.9%\n",
" -> PL-NotSalient: 78.4%\n",
" -> Crypto: 1.3%\n",
" -> App/Bot: 3.9%\n",
" -> No Context: 10.6%\n",
"\n",
"Crypto:\n",
" -> PL-Salient: 1.8%\n",
" -> PL-NotSalient: 2.6%\n",
" -> Crypto: 85.5%\n",
" -> App/Bot: 5.7%\n",
" -> No Context: 4.4%\n",
"\n",
"App/Bot:\n",
" -> PL-Salient: 2.5%\n",
" -> PL-NotSalient: 3.1%\n",
" -> Crypto: 18.4%\n",
" -> App/Bot: 70.6%\n",
" -> No Context: 5.5%\n",
"\n",
"No Context:\n",
" -> PL-Salient: 1.5%\n",
" -> PL-NotSalient: 13.9%\n",
" -> Crypto: 5.7%\n",
" -> App/Bot: 3.6%\n",
" -> No Context: 75.3%\n",
"\n",
"============================================================\n",
"Per-Class Accuracy:\n",
"============================================================\n",
"PL-Salient: 60.3%\n",
"PL-NotSalient: 78.4%\n",
"Crypto: 85.5%\n",
"App/Bot: 70.6%\n",
"No Context: 75.3%\n"
]
}
],
"source": [
"# Generate confusion matrix on validation set\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from sklearn.metrics import confusion_matrix\n",
"\n",
"# Get validation dataset predictions\n",
"train_texts, val_texts, train_labels, val_labels = train_test_split(\n",
" texts, labels, test_size=0.15, stratify=labels, random_state=42\n",
")\n",
"\n",
"# Predict on validation set\n",
"val_pred_labels, val_pred_probs = predict_batch(val_texts, model, tokenizer)\n",
"\n",
"# Create confusion matrix\n",
"cm = confusion_matrix(val_labels, val_pred_labels)\n",
"\n",
"# Get label names in correct order\n",
"label_names = sorted(label_map.keys(), key=lambda x: label_map[x])\n",
"label_names_short = [\n",
" \"PL-Salient\",\n",
" \"PL-NotSalient\", \n",
" \"Crypto\",\n",
" \"App/Bot\",\n",
" \"No Context\"\n",
"]\n",
"\n",
"# Plot confusion matrix\n",
"plt.figure(figsize=(10, 8))\n",
"sns.heatmap(\n",
" cm, \n",
" annot=True, \n",
" fmt='d', \n",
" cmap='Blues',\n",
" xticklabels=label_names_short,\n",
" yticklabels=label_names_short,\n",
" cbar_kws={'label': 'Count'}\n",
")\n",
"plt.title('Confusion Matrix - Validation Set', fontsize=16, pad=20)\n",
"plt.ylabel('True Label', fontsize=12)\n",
"plt.xlabel('Predicted Label', fontsize=12)\n",
"plt.xticks(rotation=45, ha='right')\n",
"plt.yticks(rotation=0)\n",
"plt.tight_layout()\n",
"plt.savefig('confusion_matrix.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n",
"\n",
"# Print normalized confusion matrix (percentages)\n",
"cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
"print(\"\\nNormalized Confusion Matrix (row percentages):\")\n",
"print(\"=\"*60)\n",
"for i, true_label in enumerate(label_names_short):\n",
" print(f\"\\n{true_label}:\")\n",
" for j, pred_label in enumerate(label_names_short):\n",
" percentage = cm_normalized[i, j] * 100\n",
" if percentage > 0:\n",
" print(f\" -> {pred_label}: {percentage:.1f}%\")\n",
"\n",
"# Print per-class accuracy\n",
"print(\"\\n\" + \"=\"*60)\n",
"print(\"Per-Class Accuracy:\")\n",
"print(\"=\"*60)\n",
"for i, label_name in enumerate(label_names_short):\n",
" class_accuracy = cm[i, i] / cm[i, :].sum() * 100\n",
" print(f\"{label_name}: {class_accuracy:.1f}%\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "My Project (uv)",
"language": "python",
"name": "myproject"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment