<jupyter_start><jupyter_text>Interpretando os resultados do seu modelo![Machine learning](imagens/mammogram.png)  O que vamos aprender?- Avaliar o modelo utilizando somente a acurácia é uma boa ideia?- O que é e como interpretar uma Matriz de Confusão- Métricas calculadas a partir da matriz de confusão- Como alterar a performance de um modelo alterando o limiar de predição- Curva ROC- AUC  Dataset Câncer de Mama:* 569 instâncias, no qual cada instância representa um tecido mamário;* 30 atributos, que são representados pelas colunas 2 até a coluna 32;* As classes das instâncias correspondem se o tumor é maligno ou benigno, representadas pela segunda coluna, a coluna diagnosis;* Trata-se de um problema de classificação.<jupyter_code># Abrindo nosso dataset 
import pandas as pd
cancer = pd.read_csv('breast-cancer-wisconsin-data/data.csv', index_col=0)

# Substituir as variáveis categóricas por núméricas
diag = {'M':1, 'B':0}
cancer.diagnosis = [diag[item] for item in cancer.diagnosis]

# Separar os objetos dos atributos e das classes
X = cancer[cancer.columns[1:31]]
y = cancer.diagnosis
# Dividindo X e y em conjunto de treino e de teste
from sklearn.model_selection  import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# Treinando o modelo de Regressão Logística com o conjunto de treino
from sklearn.linear_model import LogisticRegression
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
# Fazendo predições da classe das instâncias do conjunto de teste
y_pred = logreg.predict(X_test)<jupyter_output><empty_output><jupyter_text>**Acurácia de classificação:** porcentagem de predições corretas<jupyter_code># Calculando a acurácia
from sklearn import metrics
print(metrics.accuracy_score(y_test, y_pred))<jupyter_output>0.9649122807017544<jupyter_text>**Acurácia Nula:** Acurácia alcançada predizendo sempre a classe mais frequente<jupyter_code># Examinando a distribuição das classes do conjunto de teste (Utilizando um método do pandas)
y_test.value_counts()
# Calculando a acurácia nula
print(108/(108+63))<jupyter_output>0.631578947368421<jupyter_text>Comparando os valores verdadeiros e preditos<jupyter_code># Visualizando os 15 primeiros resultados verdadeiros e preditos
print(y_test.values[:15])
print(y_pred[:15])<jupyter_output>[1 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0 0 0 0 0 0 1 0]<jupyter_text>**Conclusão:**- Acurácia de classificação é a métrica mais fácil de entender- Porém, ela não nos diz nada a respeito da distribução de classes dos valores de resposta- Além disso, não nos diz quais tipos de erros nosso classificador está cometendo  Matriz de confusãoTabela que descreve a performance de um modelo de classificação<jupyter_code># Importante: primeiro argumento é valor verdadeiro, segundo argumento é valor predito
print(metrics.confusion_matrix(y_test, y_pred))<jupyter_output>[[103   5]
 [  1  62]]<jupyter_text>![Small confusion matrix](imagens/matriz_confusao.png) - Cada observação do conjunto de teste está representada em alguma das "caixas" - Nesse caso, é uma matriz 2x2, pois temos duas classes possíveis- O formato apresentado aqui não é universal **Terminologia**- **True Positives (TP):** Os verdadeiros positivos, são as predições corretas que o pacientes teve derrame.- **True Negatives (TN):** Os verdadeiros negativos, são as predições corretas que o paciente não teve derrame.- **False Positives (FP):** Os falsos positivos, são as predições incorretas de que o paciente teve derrame.- **False Negatives (FN):** Os falsos negativos, são as predições incorretas de que o paciente não teve derrame.<jupyter_code>import mglearn
mglearn.plots.plot_binary_confusion_matrix()
# Salvando a matriz de confusão e fatiando em 4 pedaços
confusion = metrics.confusion_matrix(y_test, y_pred)
TP = confusion[1,1]
TN = confusion[0,0]
FP = confusion[0,1]
FN = confusion[1,0]<jupyter_output><empty_output><jupyter_text>Métricas calculadas a partir da matriz de confusão **Acurácia de classificação:** Qual frequente o classificador está correto?<jupyter_code># Calculando a acurácia de classificação
print((TP+TN)/(TP+TN+FP+FN))
print(metrics.accuracy_score(y_test, y_pred))<jupyter_output>0.9649122807017544
0.9649122807017544<jupyter_text>**Sensibilidade:** Quando o valor real é positivo, quão frequente as predições são corretas?<jupyter_code># Calculando a sensibilidade 
print(TP/(TP+FN))
print(metrics.recall_score(y_test, y_pred))<jupyter_output>0.9841269841269841
0.9841269841269841<jupyter_text>**Especificidade:** Quando o valor real é negativo, quão frequente as predições são corretas?<jupyter_code># Calculando a especificidade
print(TN/(TN+FP))<jupyter_output>0.9537037037037037<jupyter_text>**Conclusão:**- Matriz de confusão nos fornece uma visão geral de como nosso classificador está performando- Também permite que você calcule várias métricas de classificação**Qual métrica eu devo focar?**- A escolha da métrica depende muito do **objetivo do seu projeto**- **Filtro de Spam** (a classe positiva é "spam"): Otimize pela precisão ou especificidade, pois os falsos negativos (spam que foram parar na caixa de entrada) são mais aceitáveis que os falsos positivos (emails que não eram spam e foram capturados pelo filtro)- **Detector de transações fraudulentas** (a classe positiva é "fraude"): Otimize pela sensibilidade, pois os falsos positivos (transações normais que foram rotuladas como uma possível fraude) são mais aceitáveis que os falsos negativos (transações fraudulentas que não foram detectadas)  Ajustando o limiar de classificação<jupyter_code># Mostrando as 10 primeiras respostas preditas
logreg.predict(X_test)[:10]
# Mostrando as 10 primeiras probabilidades das respostas preditas relacionadas as classes
logreg.predict_proba(X_test)[:10,1]
# Armazenando as probabilidades de predição para a classe 1 
y_pred_proba = logreg.predict_proba(X_test)[:,1]
# Histograma das probabilidades de predição
import matplotlib.pyplot as plt
plt.hist(y_pred_prob, bins=8)
plt.xlim(0,1)
plt.title('Histograma das probabilidades das predições')
plt.ylabel("Frequência")
plt.show()
# Revendo a matriz de confusão (limiar padrão de 0.5)
print(confusion)<jupyter_output>[[103   5]
 [  1  62]]<jupyter_text>Diminuindo o limiar para predição, aumentamos a sensibilidade do modelo<jupyter_code># Predizendo diabetes se a probablidade de diabetes for maior que 0.15
from sklearn.preprocessing import binarize
y_pred = binarize([y_pred_prob],0.15)[0]
# Nova matriz de confusão (limiar de 0.15)
print(metrics.confusion_matrix(y_test, y_pred))
# Sensibilidade foi aumentada (era 0.98)
print(63/(63+0))
# Especificidade foi diminuida (era 0.95)
print(93/(93+15))<jupyter_output>0.8611111111111112<jupyter_text>**Conclusion:**- Limiar de 0.5 é usado como default (para problemas binários) para converter probabilidades de predição em predição de classes- Limiar pode ser ajustado aumentando a sensibilidade ou a especificidade- Sensibilidade e Especificidade são inversamente proporcionais  ROC Curves e Area Under the Curve (AUC)Seria legal se a gente conseguisse visualizar como a sensibilidade e especificidade são afetadas por vários limiares, sem ficar tentando um por um?<jupyter_code># IMPORTANTE: o primeiro argumento são os valores verdadeiros, segundo argumento são as probabilidades de predição
fpr, tpr, thresholds = metrics.roc_curve(y_test, y_pred_prob)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('1 - Especificidade')
plt.ylabel('Sensibilidade')
plt.grid(True)
plt.show()<jupyter_output><empty_output><jupyter_text>- Curva ROC pode te ajudar a escolher um limiar que balanceie sensibilidade e esecificidade de um jeito que faça sentido pra o contexto do seu projeto- Você não pode **ver**, de fato, os limiares usados pra gerar a curva ROC<jupyter_code># Definindo uma função que recebe um limiar e mostra a sensibilidade e especificidade
def limiar(threshold):
    print('Sensitivity:', tpr[thresholds > threshold][-1])
    print('Specificity:', 1 - fpr[thresholds > threshold][-1])
limiar(0.5)
limiar(0.15)<jupyter_output>Sensitivity: 1.0
Specificity: 0.8703703703703703<jupyter_text>AUC é a porcentagem do gráfico que a curva ocupa. - AUC é usada como uma métrica que resume a performance do classificador.- AUC é bastante útil mesmo quando você tem um dataset desbalanceado.<jupyter_code># Calculando AUC
print(metrics.roc_auc_score(y_test, y_pred))<jupyter_output>0.9305555555555556<jupyter_text>**Vantagens da Matriz de Confusão:**- Permite que você calcule uma varidade de métricas*- Muito útil para problemas multiclasses (mais útil do que para problemas binários)**Vantagens da curva ROC e AUC:**- Não é necessário que você configure um limiar de classificação- Ainda é útil para problemas com classes desbalanceadas  Comentários ou Dúvidas?- Email: - Website: http://viniciusgalvaoia.com.br<jupyter_code>import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
from IPython.core.display import HTML
def css_styling():
    styles = open("styles/custom.css", "r").read()
    return HTML(styles)
css_styling()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08078147294479202, 'fraction_numerical': 0.03413456261661728, 'mean_word_length': 6.354317998385795, 'pattern_counts': {'":': 0, '<': 50, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '284515', 'license': 'MIT', 'n_tokens_mistral': 3409, 'n_tokens_neox': 3146, 'n_words': 1030, 'path': 'Aula 8 - Interpretando os resultados do seu modelo.ipynb', 'repo_name': 'viniciusgalvaoia/Breast-cancer-videos', 'size': '9111'}