PUC-RecSys-Class / RecSysPUC-2021

MIT License
22 stars 6 forks source link

[Tarea 1] Implementación de métricas de evaluación #11

Open josuetsm opened 3 years ago

josuetsm commented 3 years ago

Hola!, estaba realizando una implementación alternativa de las métricas MAP y nDCG para evaluar más rápido y me quedan dos dudas respecto a la implementación de nDCG en el práctico de Implicit feedback:

# Definicion de métricas (No editar)
# Obtenido de https://gist.github.com/bwhite/3726239

def precision_at_k(r, k):
    assert k >= 1
    r = np.asarray(r)[:k] != 0
    if r.size != k:
        raise ValueError('Relevance score length < k')
    return np.mean(r)

def average_precision(r):
    r = np.asarray(r) != 0
    out = [precision_at_k(r, k + 1) for k in range(r.size) if r[k]]
    if not out:
        return 0.
    return np.mean(out)

def mean_average_precision(rs):
    return np.mean([average_precision(r) for r in rs])

def dcg_at_k(r, k):
    r = np.asfarray(r)[:k]
    if r.size:
        return np.sum(np.subtract(np.power(2, r), 1) / np.log2(np.arange(2, r.size + 2)))
    return 0.

def ndcg_at_k(r, k):
    idcg = dcg_at_k(sorted(r, reverse=True), k)

    if not idcg:
        return 0.
    return dcg_at_k(r, k) / idcg

def evaluate_model(model, n):
    mean_map = 0.
    mean_ndcg = 0.
    for u in user_items_test.keys():
        rec = [t[0] for t in model.recommend(u, user_item_matrix, n)]
        rel_vector = [np.isin(user_items_test[u], rec, assume_unique=True).astype(int)]
        mean_map += mean_average_precision(rel_vector)
        mean_ndcg += ndcg_at_k(rel_vector, n)

    mean_map /= len(user_items_test)
    mean_ndcg /= len(user_items_test)

    return mean_map, mean_ndcg

Lo primero (entiendo que ya lo comentaron), tiene que ver con rel_vector = [np.isin(user_items_test[u], rec, assume_unique=True).astype(int)]

Esto genera vectores de tamaño variable, dependiendo de cuántos items relevantes hay en el test. Ante esto, entiendo que debería ser al revés: rel_vector = [np.isin(rec, user_items_test[u], assume_unique=True).astype(int)] De esta forma estaríamos consultando si las recomendaciones (10 recomendaciones por ejemplo) están en los ítems del test del usuario, obteniendo vectores de tamaño fijo. ¿Estoy en lo correcto?

Mi segunda duda tiene que ver con ndcg_at_k(rel_vector, n), debido a que esta da solo 0 o 1 para cada usuario. Entiendo que esto tiene que ver con que rel_vectorno es un array, sino que una lista que contiene 1 array. De esta forma, al calcular nDCG@10 en [0, 0, 0, 0, 0, 0, 1, 0, 0, 0] nos queda:

ndcg_at_k([np.array([0, 0, 0, 0, 0, 0, 1, 0, 0, 0])], 10) 1

Mientras que si lo aplicamos directamente sobre el array nos queda: ndcg_at_k(np.array([0, 0, 0, 0, 0, 0, 1, 0, 0, 0]), 10) 0.3333333

Una solución rápida a esto podría ser calcular mean_ndcg += ndcg_at_k(rel_vector[0], n) ¿Estoy en lo correcto? Quedo atento, saludos!

alfa-labarca commented 2 years ago

Hola, estás en lo correcto en todo lo que dijiste!

josuetsm commented 2 years ago

Gracias!