Empathic-Insight-Face-Large

10
license:cc-by-4.0
by
laion
Other
OTHER
New
0 downloads
Early-stage
Edge AI:
Mobile
Laptop
Server
Unknown
Mobile
Laptop
Server
Quick Summary

Empathic-Insight-Face-Large [](https://colab.

Code Examples

--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")
--- 1. Define MLP Architecture (Big Model) ---pythontransformers
import torch
import torch.nn as nn
from transformers import AutoModel, AutoProcessor
from PIL import Image
import numpy as np
import json
from pathlib import Path # Used for cleaner path handling

# --- 1. Define MLP Architecture (Big Model) ---
class MLP(nn.Module):
    def __init__(self, input_size=1152, output_size=1):
        super().__init__()
        self.layers = nn.Sequential(
            nn.Linear(input_size, 1024),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(1024, 512),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.2),
            nn.Linear(256, output_size)
        )
    def forward(self, x):
        return self.layers(x)

# --- 2. Load Models and Processor ---
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# === IMPORTANT: Set this to the directory where your .pth models are downloaded ===
# If you've cloned the repo, it might be "./" or the name of the cloned folder.
# Example: MODEL_DIRECTORY = Path("./Empathic-Insight-Face-Large_cloned_repo")
MODEL_DIRECTORY = Path(".") # Assumes models are in the current directory or a sub-directory
# If the models are in the root of where this script runs after cloning, "." is fine.
# If they are in a subfolder, e.g., "Empathic-Insight-Face-Large", use Path("./Empathic-Insight-Face-Large")
# ================================================================================


# Load SigLIP (ensure it's the correct one for 1152 dim)
siglip_model_id = "google/siglip2-so400m-patch16-384" # Produces 1152-dim embeddings
siglip_processor = AutoProcessor.from_pretrained(siglip_model_id)
siglip_model = AutoModel.from_pretrained(siglip_model_id).to(device).eval()

# Load neutral stats
neutral_stats_filename = "neutral_stats_cache-_human-binary-big-mlps_v8_two_stage_higher_lr_stage2_5_200+"
neutral_stats_path = MODEL_DIRECTORY / neutral_stats_filename
neutral_stats_all = {}
if neutral_stats_path.exists():
    with open(neutral_stats_path, 'r') as f:
        neutral_stats_all = json.load(f)
else:
    print(f"Warning: Neutral stats file not found at {neutral_stats_path}. Mean subtraction will use 0.0 for all models.")


# Load all emotion MLP models
emotion_mlps = {}
print(f"Loading emotion MLP models from: {MODEL_DIRECTORY.resolve()}") # .resolve() gives absolute path
model_files_found = list(MODEL_DIRECTORY.glob("model_*_best.pth"))
if not model_files_found:
    print(f"Warning: No model files found in {MODEL_DIRECTORY.resolve()}. Please check the MODEL_DIRECTORY path.")

for pth_file in model_files_found:
    model_key_name = pth_file.stem # e.g., "model_elation_best"
    try:
        mlp_model = MLP().to(device)
        mlp_model.load_state_dict(torch.load(pth_file, map_location=device))
        mlp_model.eval()
        emotion_mlps[model_key_name] = mlp_model
        # print(f"Loaded: {model_key_name}")
    except Exception as e:
        print(f"Error loading {model_key_name} from {pth_file}: {e}")

if not emotion_mlps:
    print(f"Error: No MLP models were successfully loaded. Check MODEL_DIRECTORY and file integrity.")
else:
    print(f"Successfully loaded {len(emotion_mlps)} emotion MLP models.")


# --- 3. Prepare Image and Get Embedding ---
def normalized(a, axis=-1, order=2):
    a = np.asarray(a) # Ensure 'a' is a numpy array
    l2 = np.atleast_1d(np.linalg.norm(a, order, axis))
    l2[l2 == 0] = 1
    return a / np.expand_dims(l2, axis)

# === Replace with your actual image path ===
# image_path_str = "path/to/your/image.jpg" 
# try:
#     image = Image.open(image_path_str).convert("RGB")
#     inputs = siglip_processor(images=[image], return_tensors="pt", padding="max_length", truncation=True).to(device)
#     with torch.no_grad():
#         image_features = siglip_model.get_image_features(**inputs) # PyTorch tensor
#     embedding_numpy_normalized = normalized(image_features.cpu().numpy()) # Normalize on CPU
#     embedding_tensor = torch.from_numpy(embedding_numpy_normalized).to(device).float()
# except FileNotFoundError:
#     print(f"Error: Image not found at {image_path_str}")
#     embedding_tensor = None # Or handle error as appropriate
# except Exception as e:
#     print(f"Error processing image {image_path_str}: {e}")
#     embedding_tensor = None
# ==========================================

# --- For demonstration, let's use a random embedding if no image is processed ---
print("\nUsing a random embedding for demonstration purposes as no image path was set.")
embedding_tensor = torch.randn(1, 1152).to(device).float()
# ==============================================================================


# --- 4. Inference for all loaded models ---
results = {}
if embedding_tensor is not None and emotion_mlps:
    with torch.no_grad():
        for model_key_name, mlp_model_instance in emotion_mlps.items():
            raw_score = mlp_model_instance(embedding_tensor).item()
            neutral_mean = neutral_stats_all.get(model_key_name, {}).get("mean", 0.0)
            mean_subtracted_score = raw_score - neutral_mean
            
            # Derive a human-readable emotion name from the model key
            emotion_name = model_key_name.replace("model_", "").replace("_best", "").replace("_", " ").title()
            results[emotion_name] = {
                "raw_score": raw_score,
                "neutral_mean": neutral_mean,
                "mean_subtracted_score": mean_subtracted_score
            }

    # Print results, sorted alphabetically by emotion name
    print("\n--- Emotion Scores (Mean-Subtracted) ---")
    # Sort items by emotion name for consistent output
    for emotion, scores in sorted(results.items()): 
        print(f"{emotion:<35}: {scores['mean_subtracted_score']:.4f} (Raw: {scores['raw_score']:.4f}, Neutral Mean: {scores['neutral_mean']:.4f})")
else:
    print("Skipping inference as either embedding_tensor is None or no MLP models were loaded.")

Deploy This Model

Production-ready deployment in minutes

Together.ai

Instant API access to this model

Fastest API

Production-ready inference API. Start free, scale to millions.

Try Free API

Replicate

One-click model deployment

Easiest Setup

Run models in the cloud with simple API. No DevOps required.

Deploy Now

Disclosure: We may earn a commission from these partners. This helps keep LLMYourWay free.