Saltar al contenido

# machine-learning-en-python

Machine Learning en Python

Volver a la portada del curso
Proyecto

Proyecto: Sistema de Recomendación

Sección 6 · Proyecto Final

Proyecto final: sistema de recomendación de PyShop

PyShop quiere sugerir productos a cada cliente. Construyes un recomendador ítem-ítem: dos productos se parecen si los compran los mismos clientes, y a cada cliente se le recomienda lo más parecido a lo que ya compró. Luego lo mides con un protocolo honesto contra una línea base. Se usa Python 3.11+ con NumPy, pandas y scikit-learn. El notebook recomendador contiene una solución de referencia completa: consúltalo al terminar, no antes.

Datos de entrada

Copia las funciones generar_clientes y generar_compras de la primera celda del notebook primer-script-ml. Son deterministas: con la semilla 42 producen siempre la misma matriz de 1000 clientes × 20 productos (1 = lo compró).

df, seg = generar_clientes()
keep = ~df.duplicated()
df = df[keep].reset_index(drop=True)
seg = seg[keep].reset_index(drop=True)
M = generar_compras(seg)
print(M.shape, round(M.values.mean(), 3))

Quita los duplicados antes de generar las compras: así la matriz queda alineada con los 1000 clientes limpios.

Requisitos mínimos

  1. Exploración. Densidad de la matriz y los cinco productos más comprados.
  2. Similitud ítem-ítem. Matriz de 20 × 20 con cosine_similarity(M.T) en un DataFrame con los nombres de los productos, y los tres productos más parecidos a Teclado y a Ebook de Python.
  3. Función recomendar(fila, S, n=3). El puntaje de cada producto es S @ fila, la suma de sus similitudes con lo que el cliente compró. Lo ya comprado nunca se recomienda.
  4. Evaluación «ocultar una compra» sin fuga. A cada cliente con al menos 2 compras se le oculta una al azar (np.random.default_rng(0), recorriendo los clientes en orden), se recomienda con el resto y se cuenta un acierto si la compra oculta aparece en el top 3. El modelo no puede ver la compra oculta: para cada cliente, la similitud se recalcula sin ella.
  5. Línea base. Con el mismo protocolo, recomendar los tres productos más populares que el cliente no tiene, con conteos que tampoco incluyen la compra oculta.
  6. Conclusión. Un párrafo que compare ambas tasas y explique por qué la línea base es necesaria.

No hace falta recalcularlo todo: la coocurrencia $C = M^\top M$ se corrige restando la fila original del cliente y sumando la fila con la compra oculta. Su diagonal es la popularidad de cada producto y la similitud coseno es $C_{ab} / \sqrt{C_{aa} C_{bb}}$. Un esqueleto:

A = M.values.astype(float)
C_glob = A.T @ A
rng = np.random.default_rng(0)
aciertos = total = 0
for i in range(len(A)):
    compras = np.flatnonzero(A[i])
    if len(compras) < 2:
        continue
    oculta = rng.choice(compras)
    f = A[i].copy()
    f[oculta] = 0
    C = C_glob - np.outer(A[i], A[i]) + np.outer(f, f)
    d = np.sqrt(np.diag(C))
    S = C / np.outer(d, d)
    aciertos += M.columns[oculta] in recomendar(f, S)
    total += 1
print(round(aciertos / total, 3), total)

Cifras esperadas

Tu solución debe reproducir estos valores:

  • Matriz de 1000 × 20 con densidad 0.203.
  • Más comprados: Libreta 320, Taza 302, Pluma 267, Ebook de Python 265 y Termo 251.
  • Más parecidos a Teclado: Hub USB 0.478, Mouse 0.457 y Monitor 0.435.
  • Más parecidos a Ebook de Python: Libro de ML 0.442, Ebook de SQL 0.422 y Libro de Estadística 0.405.
  • El cliente C0001 compró Taza y Gorra; se le recomiendan Pluma, Libreta y Calcetines.
  • Acierto en top 3 sin fuga: 0.453 del recomendador contra 0.288 de los populares, con n = 872 clientes evaluados.
  • En top 5 sin fuga: 0.618 contra 0.414.

Trampa: por qué 0.470 es optimista

Si calculas S una sola vez con todas las compras y evalúas con ella, obtendrás 0.470. Esa matriz contiene la compra oculta: la propia compra del cliente eleva la similitud entre el producto oculto y los que conserva, así que el modelo ve parte de la respuesta. Es una fuga de datos, como ajustar un escalador con el conjunto de prueba. Con 1000 clientes el efecto es pequeño; en grupos menores crece.

Extras opcionales

  • Recomendar dentro del segmento. Segmenta con K-Means (k = 4, n_init=10, random_state=42) sobre visitas, minutos, compras y ticket escalados, y calcula la coocurrencia por segmento. Resultado esperado sin fuga: 0.509, por debajo de los populares del mismo segmento (0.517). Con fuga parecería lo contrario (0.580 contra 0.532). Explica por qué.
  • Filtrar similitudes bajas o combinar el puntaje ítem-ítem con la popularidad del segmento, y medir el efecto con el mismo protocolo.
  • Recomendar a un cliente nuevo a partir de una lista de productos.

Entregables

  1. Un notebook que corra de principio a fin sin errores: datos, similitud, recomendar y la evaluación.
  2. Una tabla de resultados con cada variante evaluada y su tasa de acierto.
  3. Una nota breve (media página) que responda: ¿por qué hay que comparar contra una línea base?, ¿por qué 0.470 es una cifra optimista?, y ¿qué recomendarías a PyShop poner en producción?

Rúbrica (100 puntos)

  • Datos y exploración (10 puntos): matriz alineada con los clientes limpios, densidad y más comprados correctos.
  • Similitud (20 puntos): coseno ítem-ítem con la orientación correcta (productos, no clientes) y vecinos esperados.
  • Función recomendar (20 puntos): puntaje vectorizado, sin productos ya comprados y resultado correcto para C0001.
  • Evaluación (25 puntos): protocolo «ocultar una compra» reproducible y sin fuga, 0.453 con n = 872.
  • Línea base y comparación (15 puntos): populares con el mismo protocolo (0.288) y conclusión sustentada en las cifras.
  • Claridad (10 puntos): nombres claros, funciones reutilizables, tabla de resultados y nota.

Los extras no suman puntos, pero enseñan a distinguir una mejora real de un efecto de la evaluación.

Recursos