MSI_Julia_CNN/scripts_python/legacy/cluster_msi_direct.py

117 lines
4.6 KiB
Python

import os
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset
from torchvision import models, transforms
from PIL import Image
import numpy as np
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.manifold import TSNE
import pandas as pd
import matplotlib.pyplot as plt
# 1. CONFIGURACIÓN
IMG_DIR = "datos_para_ai"
OUTPUT_DIR = "pruebas_clusters"
N_CLUSTERS = 10
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
os.makedirs(OUTPUT_DIR, exist_ok=True)
# 2. CARGAR MODELO PRE-ENTRENADO (Directo de PyTorch)
def get_pretrained_extractor():
# Usamos los pesos por defecto de ImageNet
model = models.efficientnet_b0(weights=models.EfficientNet_B0_Weights.DEFAULT)
model.classifier = nn.Identity() # Quitamos la capa de clasificación
model.to(DEVICE)
model.eval()
return model
# 3. DATASET CON NORMALIZACIÓN ESTÁNDAR
class MSIDatasetDirect(Dataset):
def __init__(self, img_dir):
self.img_dir = img_dir
self.img_names = sorted([f for f in os.listdir(img_dir) if f.endswith('.png')])
self.transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
# Normalización obligatoria para modelos pre-entrenados
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
def __len__(self):
return len(self.img_names)
def __getitem__(self, idx):
img_path = os.path.join(self.img_dir, self.img_names[idx])
image = Image.open(img_path).convert("RGB")
return self.transform(image), self.img_names[idx]
if __name__ == "__main__":
print(f"Extrayendo características con red pre-entrenada en {DEVICE}...")
model = get_pretrained_extractor()
dataset = MSIDatasetDirect(IMG_DIR)
loader = DataLoader(dataset, batch_size=1, shuffle=False)
features = []
ion_names = []
with torch.no_grad():
for img, name in loader:
img = img.to(DEVICE)
feat = model(img)
# Aplanar el vector de características (1280 dimensiones para EffNet-B0)
features.append(feat.cpu().numpy().flatten())
ion_names.append(name[0])
features = np.array(features)
print(f"Características extraídas: {features.shape}")
# 4. REDUCCIÓN DE DIMENSIONALIDAD (PCA)
# Esto ayuda a que el clustering no se pierda en dimensiones irrelevantes
print("Reduciendo ruido con PCA...")
features_scaled = StandardScaler().fit_transform(features)
pca = PCA(n_components=0.95) # Mantenemos el 95% de la varianza
features_pca = pca.fit_transform(features_scaled)
print(f"Dimensiones tras PCA: {features_pca.shape[1]}")
# 5. CLUSTERING K-MEANS
print(f"Agrupando en {N_CLUSTERS} clusters...")
km = KMeans(n_clusters=N_CLUSTERS, n_init=20, random_state=42)
labels = km.fit_predict(features_pca)
# 6. VISUALIZACIÓN (t-SNE)
print("Generando visualización t-SNE para verificar consistencia...")
tsne = TSNE(n_components=2, random_state=42)
features_tsne = tsne.fit_transform(features_pca)
plt.figure(figsize=(10, 8))
scatter = plt.scatter(features_tsne[:, 0], features_tsne[:, 1], c=labels, cmap='tab10', alpha=0.7)
plt.colorbar(scatter, ticks=range(N_CLUSTERS), label='Cluster ID')
plt.title(f"Visualización de Clusters (K={N_CLUSTERS}) - Directo")
plt.xlabel("t-SNE 1")
plt.ylabel("t-SNE 2")
plt.grid(True, linestyle='--', alpha=0.5)
plt.savefig(os.path.join(OUTPUT_DIR, "msi_clusters_direct_plot.png"))
print(f"Gráfico de consistencia guardado en '{OUTPUT_DIR}/msi_clusters_direct_plot.png'.")
# 7. GUARDAR Y REPORTAR
df = pd.DataFrame({'ion_image': ion_names, 'cluster': labels})
df['mz'] = df['ion_image'].str.extract(r'(\d+\.\d+)').astype(float)
df.to_csv(os.path.join(OUTPUT_DIR, "msi_clusters_direct_results.csv"), index=False)
# Crear un resumen de cuántos iones hay por cluster
print("\nResumen de Clusters (Consistente con el CSV y el Gráfico):")
counts = df['cluster'].value_counts().sort_index()
print(counts)
# Guardar también el resumen para evitar confusiones con otros archivos
summary_df = counts.reset_index()
summary_df.columns = ['Cluster_ID', 'Ion_Count']
summary_df.to_csv(os.path.join(OUTPUT_DIR, "msi_clusters_direct_summary.csv"), index=False)
print(f"\nResultados guardados en '{OUTPUT_DIR}/msi_clusters_direct_results.csv' y '{OUTPUT_DIR}/msi_clusters_direct_summary.csv'.")
print(f"Usa '{OUTPUT_DIR}/msi_clusters_direct_plot.png' para validar visualmente los puntos.")