Cost functions (ou funções de custo) são usadas pelos algoritmos de aprendizado de máquina para tomada de decisões, por exemplo, definir a melhor configuração dos hiper parâmetros de um modelo ou até mesmo escolher as melhores variáveis preditoras/independentes (em modelos de classificação ou regressão).

As funções mais comuns já foram citadas neste post anterior https://hackinganalytics.com/2017/01/06/metricas-para-avaliacao-de-modelos/ .

Recentemente eu estava criando um modelo de árvore de decisão com Python (sklearn) e, por mais que eu tentasse criar uma árvore um pouco mais genérica, os resultados das minhas otimizações (grid search, veja https://hackinganalytics.com/2016/09/04/model-tuning-otimizando-os-hiper-parametros-do-modelo/ ) sempre me davam um cenário em que os nós folhas (leaf nodes) continham uma quantidade de amostras/observações muito pequena.

Em modelos baseados em árvores de decisão temos que controlar a profundidade da árvore de alguma forma, seja por meio da quantidade de níveis permitidos (max_depth) ou por meio da quantidade mínima de amostras nas folhas (min_samples_leaf). Este tratamento é feito para evitar o que chamamos de overfitting, ou seja, quando o modelo possui pouca capacidade de generalização para outros conjuntos de dados em que o mesmo não foi treinado. Veja este outro post que fala sobre grande parte da teoria de árvores de decisão https://www.analyticsvidhya.com/blog/2016/04/complete-tutorial-tree-based-modeling-scratch-in-python/ .

Pois bem, voltando ao meu problema, depois de um longo processo de tentativa e erro a minha conclusão foi: preciso criar minha própria função de custo. Bom, me pareceu desafiador partir para esta solução, achei poucas referências na internet também, por isso a existência deste post.

Antes de continuar, ai vão os links que eu utilizei como referência neste trabalho. O último deles (aquele do stackoverflow) foi o que me salvou.

No código que estou compartilhando existem duas cost functions implementadas.

  1. Positive precision: Representa a probabilidade de uma observação classificada como positiva realmente pertencer à classe positiva.
  2. Positive precision with penalty for overfitting: Mesma definição do item 1, porém é atribuida uma penalidade de acordo com a quantidade de observações do nó folha. Vou te explicar em detalhes logo mais.

Estas funções estão sendo usadas por um processo de otimização (grid search) para encontrar o valor ótimo para o parâmetro min_samples_leaf da minha árvore (quantidade mínima de observações nas folhas).

Na verdade deixei o código do item 1 apenas como referência de consulta, vamos focar no item 2. Precisamos informar ao algoritmo de otimização quais são os valores que ele testará para o parâmetro min_samples_leaf, neste caso, estou passando uma lista de valores baseados no logspace.

A definição matemática para a minha função de custo é: positive precision – (k / num_samples_leaf), onde num_samples_leaf é a quantidade de observações do nó folha e k é a minha taxa de aprendizado que será usada para penalizar nós com poucas observações. Uma árvore de decisão geralmente possui muitos nós folhas, portanto esta equação deve ser executada em cada um deles. Neste caso, tirei a média ao final do processo.

Vamos colocar a lógica na mesa…Para simplificar, vamos analisar apenas um nó da árvore:

node num_samples_leaf positive_precision k k/num_samples_leaf cost
1 1 0.9 0.1 0.1 0.8
1 2 0.89 0.1 0.05 0.84
1 5 0.88 0.1 0.02 0.86
1 12 0.85 0.1 0.008333333 0.841666667

Note que o melhor valor de positive precision foi alcançado na linha 1 (valor igual a 0.9). Porém, a linha 1 também é aquela que nos fornece o modelo menos genérico, pois permite que um nó folha contenha no mínimo 1 observação.

Quando tentamos criar um modelo mais genérico, nossa precisão começa a cair (veja as linhas 2 em diante)…Portanto, se nossa otimização estivesse se baseando pela positive precision, o melhor resultado seria alcançado na linha 1 e, por consequência, teríamos o modelo menos genérico possível.

Por outro lado, veja o que acontece na coluna “cost”. Esta coluna armazena o resultado na nossa cost function customizada.

O melhor resultado foi alcançado na minha três (0.86). Basicamente estamos dizendo que é melhor ter um modelo com 0.88 de precisão desde que no mínimo tenhamos 5 observações nas folhas da árvore DO QUE ter um modelo com 0.9 de precisão que aceite folhas com uma única observação.

Fantático, não!?

https://github.com/weslleymoura/hackinganalytics/blob/master/cost_function.py

Abraços,
Weslley Moura

Anúncios