Índice

O Que é NDCG?

O NDCG é uma métrica de avaliação de desempenho que é especialmente útil para modelos de machine learning de ranking.

Ela é muito usada em sistemas de busca e recomendação. Eu mesmo a usava para avaliar os sistemas de ranking de freelancers quando trabalhei na Upwork.

Ele mede a qualidade de uma lista de resultados retornada pelo modelo em relação aos itens que o usuário realmente indicou como relevantes.

O “Normalized” no nome se refere ao fato do NDCG ser o Discounted Cumulative Gain (DCG) dos resultados previstos dividido pelo “Ideal DCG” (IDCG) que seria a lista de resultados perfeita.

Isso facilita a comparação entre diferentes modelos de machine learning.

Você vai entender melhor o que tudo isso significa depois de ler este artigo.

Uma das maiores vantagens desta métrica é que ela leva em conta a posição dos resultados relevantes na lista.

Isso significa que um resultado relevante que aparece no topo da lista terá uma pontuação mais alta do que se ele aparecesse no final.

A desvantagem é a dificuldade em calcular e interpretar a fórmula.

Como Interpretar o NDCG?

Primeiro precisamos entender o Discounted Cumulative Gain (DCG) que é a parte mais importante do NDCG.

Ele é calculado atribuindo pontos aos resultados relevantes de acordo com a relevância e a posição deles na lista.

A fórmula do DCG para uma lista de resultados é:

fórmula do Discounted Cumulative Gain - DCG

Onde:

  • n é o número de resultados na lista (podemos pensar como itens recomendados ao usuário)
  • i é a posição do item na lista
  • rel_i é a relevância do item i (por exemplo, qual nota o usuário deu a um produto)

Vamos ver um exemplo de como calcular o DCG para uma lista de resultados.

Posição ID do Item Relevância 2^rel_i - 1 log2(i+1) DCG Posição
1 A 2 3 1 3
2 B 3 7 1.58496 4.41651
3 C 0 0 2 0
4 D 1 1 2.32193 0.430677
5 E 2 3 2.58496 1.16056

Considere que este é o resultado de um modelo de machine learning de recomendação, mas o mesmo vale para qualquer modelo que precise ordenar uma lista.

A coluna “Posição” é a posição do item na lista retornada, sendo 1 para o primeiro item e 5 para o último.

A coluna “ID do Item” é o ID do item recomendado, que poderia ser um filme, música, livro, vídeo, etc.

A coluna “Relevância” é a relevância do item para o usuário. Para treinar e validar o modelo isso pode ser calculado com base nos dados históricos considerando interações do usuário com itens como cliques, compras, tempo de visualização, etc.

O item mais relevante para esse usuário é o B, com relevância 3, seguido pelos itens A e E com relevância 2 e D com relevância 1. O item C é irrelevante.

A relevância pode ser um valor binário, como 0 ou 1, ou um valor numérico, como 0, 1, 2, 3, etc. E podem ter empates como no caso dos itens A e E no exemplo acima.

A coluna “2^rel_i - 1” é numerador do DCG: o valor de 2 elevado à relevância do item menos 1.

Isso significa que cada ponto de relevância traz ganhos exponenciais.

Um item com relevância 1 vale 2^1 - 1 = 1 ponto, um item com relevância 2 vale 2^2 - 1 = 3 pontos, um item com relevância 3 vale 2^3 - 1 = 7 pontos, e assim vai.

A coluna “log2(i+1)” é o denominador do DCG: o logaritmo na base 2 da posição do item na lista mais 1.

Isso significa que quanto mais baixa a posição do item na lista, maior será a penalidade sobre a relevância no cálculo do DCG, mas numa escala logarítmica.

A importância diminui exponencialmente conforme os resultados vão ficando para trás.

Isso reflete a ideia de que os primeiros resultados são mais importantes que os seguintes.

A coluna “DCG Posição” mostra o cálculo dessa fração para cada item.

Nós temos um ganho bom com os dois primeiros itens, mas veja como apesar da relevância do item B ser maior, o ganho acaba penalizado por estar na segunda posição.

Enquanto o item C, irrelevante, zera o ganho que poderíamos ter na terceira posição.

Para obter o DCG desta lista, basta somar os valores de cada item, que dá um total aproximado de 9,01.

Se pararmos por aqui, o DCG seria uma métrica útil mas difícil de comparar e interpretar, já que ele não tem uma escala específica e depende do número de itens na lista.

Vamos ver então como normalizar o DCG para facilitar a interpretação e comparação.

Qual a Fórmula do NDCG?

O NDCG é o DCG dos resultados normalizado pelo DCG ideal.

fórmula do Normalized Discounted Cumulative Gain - NDCG

Onde:

  • DCG é o DCG dos resultados como calculado acima
  • IDCG é o DCG ideal, que é o DCG dos resultados ordenados por relevância (a lista perfeita)

Para calcular o IDCG, vamos ordenar os resultados por relevância e calcular o DCG como fizemos acima.

Posição ID do Item Relevância 2^rel_i - 1 log2(i+1) DCG Posição
1 B 3 7 1 7
2 A 2 3 1.58496 1.89279
3 E 2 3 2 1.5
4 D 1 1 2.32193 0.430677
5 C 0 0 2.58496 0

Veja que agora o item B é o melhor posicionado, seguido pelos itens A e E, e assim por diante.

Ao somar o DCG de cada item, temos um total aproximado de 10,82 que é o IDCG ou DCG ideal.

Esta é a pontuação máxima que o modelo poderia ter alcançado se ordenasse os itens perfeitamente.

Com estes dois números, basta dividir o DCG pelo IDCG para obter o NDCG. Um valor entre 0 e 1, sendo que quanto mais próximo de 1, melhor.

Neste caso, seria de aproximadamente 0,83. Muito bom!

Agora basta calcular o NDCG para cada lista de resultados e calcular a média para obter o NDCG do seu modelo em todo o conjunto de dados.

Como Calcular o NDCG em Python?

Apesar do scikit-learn ter uma função para calcular o NDCG, ela faz o cálculo com uma fórmula diferente da mostrada acima, que é utilizada pela maior parte da indústria e competições de machine learning.

Em vez de considerar 2 elevado à relevância menos 1, ela considera apenas a relevância bruta no numerador.

Então, vou te mostrar como calcular esta versão com mais peso para a relevância usando Numpy:

import numpy as np

y_pred_rel = np.array([[2,3,0,1,2],
                [1,2,1,1,0],
                [3,3,2,1,1]])

positions = np.arange(1, y_pred_rel.shape[1]+1)
denominator = np.log2(positions+1)

dcg = np.sum((2**y_pred_rel - 1) / denominator,axis=1)

ideal_rel = np.sort(y_pred_rel, axis=1)[:,::-1]

idcg = np.sum((2**ideal_rel - 1) / denominator, axis=1)

ndcg = dcg / idcg

print(ndcg.mean())

Neste caso imagine que temos uma matriz de resultados para três usuários, com cinco itens em cada lista.

Os valores da matriz y_pred_rel são as relevâncias dos itens recomendados naquela posição para cada usuário.

Por exemplo, para o primeiro usuário (primeira linha), são os mesmos valores do exemplo na explicação da fórmula do DCG: o item na primeira posição tem relevância 2, o segundo item tem relevância 3, e assim por diante.

Na segunda linha já temos outra lista de resultados, com os itens recomendados para o segundo usuário.

Na prática é comum que você calcule o NDCG sobre uma lista de listas de resultados, que pode ser facilmente organizada na mesma forma da matriz acima.

Para deixar os cálculos mais rápidos, vamos criar o vetor denominator com o log2 aplicado às posições de cada item mais 1.

Este vetor é o mesmo para todos os usuários, já que todas as listas têm o mesmo número de itens.

O Numpy faz o broadcast automático para que possamos dividir a matriz y_pred_rel, após a transformação no numerador, pelo vetor denominator.

Isso significa que mesmo passando uma matriz para divisão por um vetor, o vetor é replicado para cada linha da matriz internamente.

Fazendo a soma com axis=1, temos o DCG para cada linha da matriz, ou seja, para cada lista de resultados.

Agora vamos criar a matriz ideal_rel com os itens ordenados por relevância, do mais relevante para o menos relevante.

Para isso, vamos usar a função np.sort com o argumento axis=1 para ordenar cada linha da matriz de maneira ascendente, e o argumento ::-1 para inverter a ordem.

Depois disso é só repetir o processo de cálculo do DCG com a matriz ideal_rel para obter o IDCG de cada lista.

Por fim, basta dividir o DCG pelo IDCG para obter o NDCG de cada usuário, e calcular a média para obter o NDCG do seu modelo.

Qual a Diferença Entre NDCG e Mean Average Precision?

Essas duas métricas são muito similares, mas existem duas diferenças principais que você deve levar em consideração:

O NDCG é mais difícil de interpretar do que a MAP.

Basta você olhar a fórmula explicada lá em cima e perceber que é muito mais difícil entender como o equilíbrio entre a relevância dos itens e a posição deles na lista afeta a pontuação.

A MAP é muito mais fácil de calcular e entender, já que é similar a uma média da precisão em cada posição da lista.

O MAP não leva em conta a magnitude da relevância dos itens.

Ela é uma métrica baseada em precisão, o que significa que ela não se importa se o item 1 é 10 vezes mais relevante que o item 2.

Se a relevância dos itens é binária, pode ser melhor usar o MAP por causa da interpretabilidade.

Caso não, o NDCG pode ser melhor, já que ele leva em conta a magnitude da relevância.

Uma observação adicional: existem muitas maneiras de manipular a fórmula do NDCG para ajustar a transformação da relevância e a penalidade da posição.

Em casos mais complexos, isso pode ser uma vantagem, já que ajustar a fórmula pode fazê-la correlacionar melhor com melhorias em métricas de negócio durante o teste em produção.

No nosso exemplo, este seriam os valores da Average Precision para cada posição, considerando que um item é relevante se a relevância for maior que 0:

Posição ID do Item Relevância DCG Posição Average Precision
1 A 2 3 1
2 B 3 4.41651 1
3 C 0 0 0
4 D 1 0.430677 0.75
5 E 2 1.16056 0.8

Na primeira posição temos 1 item relevante de 1 possível, o que dá uma precisão de 1.

Na segunda posição temos 2 itens relevantes de 2 possíveis, que também dá uma precisão de 1.

Na terceira posição temos 2 itens relevantes de 3 possíveis, mas como o item na terceira posição não é relevante, substituímos a precisão por 0.

Na quarta posição temos 3 itens relevantes de 4 possíveis, e como o item é relevante, a precisão é 3/4 = 0,75.

Como você pode ver, a MAP não levou em conta que o item da segunda posição é mais relevante que o item da primeira posição e deu o mesmo score para ambos. Já o DCG conseguiu diferenciar.

No fim, somamos todas as precisões e dividimos pelo número máximo possível de itens relevantes.

Esse número é de, pelo menos, quatro, já que temos 4 itens relevantes na lista mas podem haver outros que não foram retornados entre os 5 primeiros.

Qual a Diferença Entre NDCG e Mean Reciprocal Rank?

Expliquei a diferença entre Mean Reciprocal Rank e NDCG neste post.

Seja o primeiro a saber das novidades em Machine Learning. Me siga no LinkedIn.