Categorías
Informática Universitario Videojuego

Ejercicios

Estos son algunos ejercicios que pueden realizarse para practicar lo aprendido en el tema de Aprendizaje Supervisado, sobre el uso de las biblioteca Scikit-Learn desde Python, principalmente.

⚠️Salvo que se indique lo contrario, estos ejercicios pueden resolverse en condiciones de prueba final, esto es, examen individual en el laboratorio, sin acceso a Internet y sin usar IA generativa. Tan sólo hace falta la plantilla o el contexto que proporcione el profesor junto al enunciado, y un servidor de lenguaje como Pylance que viene por defecto en la extensión oficial de Python para Visual Studio Code y que permite explorar la documentación del API.

  1. En una desarrolladora de videojuegos móviles quieren predecir si un jugador abandonará el juego (churn) según los días que lleva sin conectarse y las microtransacciones realizadas. Se entrena un modelo de LogisticRegression y se ejecuta el siguiente código. ¿Qué diferencia existe en la salida producida por model.predict() frente a model.predict_proba() y por qué la función logística/sigmoide es la que permite esta interpretación probabilística? Explica qué efecto tendría cambiar el umbral de decisión predeterminado (por defecto 0.5) a 0.3 para la clase 1 (churn). ¿Qué implicación práctica tendría en la retención de usuarios?
import numpy as np
from sklearn.linear_model import LogisticRegression

# X: [dias_inactivo, total_compras_euros]
X_train = np.array([
    [1, 50.0], [2, 0.0], [14, 0.0], [20, 10.0], 
    [30, 0.0], [3, 100.0], [25, 5.0], [18, 0.0]
])
# y: 1 si abandona el juego (churn), 0 si permanece activo
y_train = np.array([0, 0, 1, 1, 1, 0, 1, 1])

# Modelar y entrenar

# Evaluación de dos jugadores nuevos
X_nuevos = np.array([[2, 10.0], [15, 0.0]])
# Predecir

print("Predicciones de clase:", predicciones)
print("Matriz de probabilidades:\n", probs)
  1. Analizando la retención de un juego según la duración del contenido principal, un analista de datos entrena tres modelos de regresión polinómica (grados 1, 2 y 9) utilizando una división del conjunto de datos en entrenamiento y validación cruzada. La tabla muestra el error residual (coste J) en ambos conjuntos.

Diagnostica la situación teórica (Sesgo/Varianza, Subajuste/Sobreajuste) para el modelo de Grado 1 y el de Grado 9 basándote en la relación entre sus costes de entrenamiento y validación. Si estuviéramos obligados a trabajar con el modelo de Grado 9 por razones del sistema, ¿qué técnica de regularización (L1 ó L2) podrías aplicar sobre la función de coste y qué efecto matemático tiene sobre los pesos w?

  1. Un modelo supervisado de detección de tramposos (cheaters) en partidas clasificatorias en línea ha generado el siguiente reporte tras evaluar 1500 jugadores (como comentario en el código). Calcula la Sensibilidad y la Precisión para la clase objetivo (tramposos / clase 1). En el diseño de un sistema de suspensión automática de cuentas de usuario, ¿cuál de los dos tipos de error (Falsos Positivos o Falsos Negativos) es más crítico reducir y qué métrica nos indicaría que el sistema es muy seguro ante baneos injustificados?
from sklearn.metrics import classification_report, confusion_matrix

# Matriz de confusión obtenida:
# [[1100,  20],   Row 0: Jugadores Legítimos (Clase 0)
#  [  80, 300]]   Row 1: Tramposos / Cheaters (Clase 1)
  1. Observa el código de entrenamiento de un clasificador para distinguir el tipo de comportamiento de bots mediante Support Vector Machines. Sabiendo que los datos sintéticos make_moons representan dos semicírculos entrelazados que no son linealmente separables, ¿por qué el núcleo kernel='linear' resultará insuficiente y qué función de núcleo (ej. 'rbf') deberíamos emplear para aplicar el «truco del kernel» (kernel trick)? Modifica la instanciación del objeto SVC utilizando kernel='rbf' y ajustando el parámetro de regularización C=10.0. Explica qué sucede visualmente con la frontera de decisión si incrementamos demasiado el valor del hiperparámetro C.
from sklearn.svm import SVC
# Generador de datos sintéticos
from sklearn.datasets import make_moons 

# Generación de datos sintéticos con make_moons
X, y = make_moons(n_samples=200, noise=0.3, random_state=42)

# Creación y entrenamiento del modelo
  1. Para clasificar las clases de personajes de un videojuego (Guerrero, Mago, Pícaro) en función de la Salud Máxima y el Poder Mágico, se requiere utilizar el algoritmo de K Vecinos más Cercanos. Como se basa en la distancia euclídea entre muestras, es imprescindible realizar primero un escalado de datos. Primero completa la inicialización del objeto StandardScaler y transforma las características de entrada X_train y X_test. Después instancia el modelo KNeighborsClassifier configurandolo para 3 vecinos. Entrena el modelo y calcula la precisión de exactitud en el conjunto de prueba.
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

np.random.seed(42)
# X: [salud, mana]
X = np.random.uniform(100, 1000, (150, 2))
# y: 0=Guerrero, 1=Mago, 2=Pícaro
y = np.random.choice([0, 1, 2], size=150)

# Particiones

# 1. Escalar características
# scaler = ...
# X_train_scaled = ...
# X_test_scaled = ...

# 2. Crear y entrenar K-NN
# knn = ...
# knn.fit(...)

# 3. Evaluar exactitud y mostrarla
# acc = knn.score(...) 
  1. Se pretende predecir si un usuario comprará un pase de batalla (Pase Adquirido: 0 o 1) utilizando la información de su Nivel de Cuenta, Horas Jugadas y Victorias Totales. Para evitar que el árbol memorice las muestras de entrenamiento (sobreajuste por profundidad excesiva), es necesario limitar su desarrollo mediante hiperparámetros. Primero instancia y entrena un DecisionTreeClassifier configurando la profundidad máxima max_depth=3. Después exporta las reglas aprendidas por el árbol en texto legible utilizando la función export_text de Scikit-Learn.
import pandas as pd
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.model_selection import train_test_split

# Datos de muestra de jugadores
data = {
    'nivel': [5, 12, 45, 80, 23, 67, 10, 95, 34, 50],
    'horas': [10, 25, 120, 200, 40, 150, 15, 300, 60, 100],
    'victorias': [2, 5, 40, 90, 12, 70, 3, 110, 18, 45],
    'compro_pase': [0, 0, 1, 1, 0, 1, 0, 1, 0, 1]
}
df = pd.DataFrame(data)
X = df[['nivel', 'horas', 'victorias']]
y = df['compro_pase']

# 1. Crear el árbol de decisión limitado a profundidad 3
# tree = ...
# tree.fit(X, y)

# 2. Imprimir las reglas del árbol entrenado usando export_text
# reglas = export_text(...)
  1. Para optimizar un modelo de RandomForestClassifier encargado de predecir la victoria o derrota de un equipo en una partida MOBA, se debe probar sistemáticamente distintas combinaciones del número de estimadores y la profundidad máxima de los árboles. Primero define el diccionario de parámetros param_grid para probar en n_estimators los valores $[10, 50]$ y en max_depth los valores [3, 5, \text{None}]. Luego configura e inicia GridSearchCV empleando validación cruzada con 3 pliegues (cv=3). Finalmente muestra por pantalla la mejor combinación de hiperparámetros encontrada (best_params_).
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

np.random.seed(42)
# X: Muestras simuladas de partidas [oro_diferencia, bajas_diferencia, torres_destruidas]
X_train = np.random.randn(100, 3)
y_train = np.random.choice([0, 1], size=100)

# 1. Definir la rejilla de parámetros
# param_grid = { ... }

# 2. Crear el objeto GridSearchCV con RandomForestClassifier y cv=3, y entrenarlo
# grid_search = GridSearchCV(...)

# 3. Mostrar los mejores parámetros
# best_params_
  1. Se desea predecir la puntuación final (Score) que obtendrá un jugador en un nivel de plataformas continuo a partir de tres métricas de rendimiento: Monedas Recogidas, Tiempo Empleado (s) y Enemigos Derrotados. Debido a que las redes neuronales son hiper-sensibles a la escala de las entradas, usaremos un flujo estructurado con StandardScaler. Primero instancia un MLPRegressor configurando una capa oculta con 16 neuronas (hidden_layer_sizes=(16,)), función de activación 'relu', el optimizador 'lbfgs' (adecuado para conjuntos de datos pequeños) e iteraciones máximas max_iter=500. Después normaliza los datos de entrenamiento X_train usando StandardScaler. Finalmente entrena la red neuronal y realiza la predicción para el conjunto de test X_test.
import numpy as np
from sklearn.neural_network import MLPRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

np.random.seed(42)
X = np.random.uniform(1, 100, (200, 3)) # Métrica de jugador
y = X[:, 0] * 10 + X[:, 1] * (-2) + X[:, 2] * 50 # Puntuación sintética

X_train, X_test, y_train, y_test = ... 

# 1. Escalar características

# 2. Definir e instanciar el perceptrón multicapa MLPRegressor (usando solver='lbfgs')
# mlp = MLPRegressor(hidden_layer_sizes=(16,), activation='relu', solver='lbfgs', max_iter=500, random_state=42)

# 3. Ajustar el modelo y realizar predicciones
# mlp.fit(X_train_scaled, y_train)
# predicciones 
  1. Para asegurar un flujo de trabajo profesional y evitar la fuga de datos (data leakage) entre entrenamiento y prueba, se deben encadenar las etapas de transformación y clasificación en un objeto Pipeline de Scikit-Learn. Se utilizará MinMaxScaler junto a un clasificador LogisticRegression para categorizar el tipo de jugador (VIP / Estándar). Primero crea un Pipeline (o usa make_pipeline) que integre en el primer paso la escala MinMaxScaler y en el segundo el clasificador LogisticRegression. Después entrena el pipeline directamente sobre los datos sin escalar X_train y y_train. Finalmente evalúa la precisión global empleando el método .score(X_test, y_test).
import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

np.random.seed(42)
X = np.random.uniform(0, 5000, (100, 2)) # X: [gemas_compradas, minutos_juego]
y = (X[:, 0] > 2000).astype(int)          # y: 1 si es jugador VIP, 0 en otro caso

X_train, X_test, y_train, y_test = ...

# 1. Crear el pipeline combinando MinMaxScaler y LogisticRegression
# pipeline = make_pipeline(...)

# 2. Entrenar el pipeline completo
# pipeline.fit(X_train, y_train)

# 3. Evaluar en test, con la precisión 
  1. Con el objetivo de evaluar la robustez de un clasificador de hábitos de juego multiclase (Casual, Core, Hardcore) de forma imparcial, es preferible utilizar validación cruzada K-Fold en lugar de una partición fija tipo Hold-Out. Primero instancia un modelo NaiveBayes gaussiano (GaussianNB). Después aplica la función cross_val_score con 5 pliegues (cv=5), configurando la métrica de puntuación en scoring=’f1_macro’. Finalmente calcula e imprime el valor medio y la desviación estándar de las puntuaciones obtenidas en los pliegues.
import numpy as np
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score

np.random.seed(42)
X = np.random.randn(120, 4) # 4 características de comportamiento
y = np.random.choice([0, 1, 2], size=120) # 3 clases de perfil de jugador

# 1. Instanciar el clasificador GaussianNB
# model = ...

# 2. Calcular los scores usando cross_val_score con cv=5 y scoring='f1_macro'

# 3. Mostrar la media y desviación estándar de los resultados
# print(f"F1-Macro Promedio: {scores.mean():.3f} +/- {scores.std():.3f}")

Esta página está licenciada bajo CC BY-NC-SA 4.0 por Laboratorios Narratech.