<jupyter_start><jupyter_text>K-Nearest Neighbors (ou K-Vizinhos mais próximos)* Chamado geramlente de KNN.* Algoritmo de classificação ( com adaptações pode ser usado para regressão).* Todas as features devem ser numéricas.* Pressuposto: ocorrências de mesma classe devem estar localizadas próximas no espaço, mesmo podendo ser um espaço n-dimensional.* O treino é muito rápido, mas o teste é demorado.* Distâncias mais usadas:  * Distância de Euclides  * Distância Manhattan* Lecture 4 'Curse of Dimensionality / Perceptron' -Cornell CS4780 SP17  * Explição sobre quanto maior a quantidade de dimensões, menos os vizinho são possíveis de calcular, pois todos os pontos estão distante e a mesma distância, Explicação Textual* Referência  Exercício 01 - Imagens de números Import e Sets<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics
from sklearn.model_selection import GridSearchCV

# Essas linhas sao apenas configuracoes de plotting. 
# Elas nao sao importantes para o seu aprendizado, entao as trataremos como "magica" por agora, ok?
%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

# Abaixo encontram-se os hiperparametros do modelo que sera construido.
# A nao ser que seja instruido, voce nao deve modifica-los
TRAIN_TEST_SPLIT_SIZE = 0.2 # Define que o TESTE correspondera a 20% do total do dataset<jupyter_output><empty_output><jupyter_text>Carregar os dadosUsaremos o dataset mnist<jupyter_code>dataset = datasets.load_digits()

datasetSize = len(dataset.images)
print("O dataset possui", datasetSize, " imagens.")

# Mostraremos uma imagem aleatoria dentro do dataset
plt.imshow(dataset.images[np.random.randint(datasetSize)])
plt.show()<jupyter_output>O dataset possui 1797  imagens.<jupyter_text>Separando o dataset em treino e teste<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(dataset.data, dataset.target, test_size=TRAIN_TEST_SPLIT_SIZE)

print("Train shapes - X =", X_train.shape," y =", y_train.shape)
print("Test shapes - X =", X_test.shape," y =", y_test.shape)
print()
print(f"Exemplo de uma linha de X:\n{X_train[0]}\n")
print(f"Exemplo de uma linha de Y:\n{y_train[0]}")<jupyter_output>Train shapes - X = (1437, 64)  y = (1437,)
Test shapes - X = (360, 64)  y = (360,)

Exemplo de uma linha de X:
[ 0.  0.  3. 12. 11.  4.  0.  0.  0.  4. 15. 13. 12. 16.  0.  0.  0.  9.
 14.  0.  0. 12.  2.  0.  0.  0. 13. 11.  7. 15.  3.  0.  0.  0.  0. 15.
 16.  7.  0.  0.  0.  0.  5. 16. 10. 14.  2.  0.  0.  0. 11. 13.  0.  8.
  8.  0.  0.  0.  2. 12. 16. 16.  7.  0.]

Exemplo de uma linha de Y:
8<jupyter_text>O modelo<jupyter_code>k = 1 # usarndo k = 1 ou seja, somente um vizinho
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train, y_train)
score = knn.score(X_test, y_test)
y_predito = knn.predict(X_test)

print(f'O score do modelo com k={k} é {score}')
plt.scatter(x=y_test, y=y_predito)
plt.xlabel("y de teste")
plt.ylabel("y de predito")
plt.show()<jupyter_output>O score do modelo com k=1 é 0.9888888888888889<jupyter_text>Métricas<jupyter_code>print(metrics.classification_report(y_test, y_predito)<jupyter_output>precision    recall  f1-score   support

           0       1.00      1.00      1.00        33
           1       0.97      1.00      0.98        31
           2       1.00      0.97      0.99        36
           3       0.97      1.00      0.99        36
           4       1.00      1.00      1.00        45
           5       1.00      0.97      0.99        37
           6       1.00      1.00      1.00        33
           7       1.00      0.97      0.99        39
           8       1.00      1.00      1.00        34
           9       0.95      0.97      0.96        36

    accuracy                           0.99       360
   macro avg       0.99      0.99      0.99       360
weighted avg       0.99      0.99      0.99       360<jupyter_text>* *Precision* é intuitivamente a capacidade do classificador de não rotular como **positiva** uma amostra **negativa**    Precision = tp / (tp + fp)    * tp é o verdadeiro positivo    * fp é o falso positivo* *Recall* é intuitivamente a capacidade do classificador de encontrar **todas** as amostras **positivas**.  Recall = tp / (tp + fn)    * tp é o verdadeiro positivo     * fn é o falso negativo* *F1-Score* pode ser interpretada como uma média pondera da *precision* e *recall*, onde 1 é o melhor valor e 0 o pior valor.  f1 = 2 * (precision * recall) / (precision + recall)* *Support* é o número de cada classe no conjunto de teste, ou seja **y_test**.* *Accuracy* é a quantidade de acerto frente o total de amostras. Ajuste de hiper-parâmetrosComo esse ajuste podemos encontrar o melhor modelo, a partir de um conjunto de parâmetros. [ref]<jupyter_code>knn = KNeighborsClassifier(n_neighbors=2)

k_list = list(range(1, 31)) # k variando de 1 a 30
parametros = dict(n_neighbors=k_list) # converte a liste em dicionários de parâmetros

grid = GridSearchCV(knn, parametros, cv=2, scoring='accuracy')
# knn é o modelo que faremos o ajuste
# cv quantidade de rodadas, maior que zero
# scoring qual métrica usaremos para selecionar o melhor

grid.fit(X_train, y_train)

print(f"Melhores parametros {grid.best_params_} com o valor de acurácia {grid.best_score_} ")
plt.plot(k_list, grid.cv_results_['mean_test_score'], color='red', linestyle='dashed', marker='o')
plt.xlabel("K (vizinhos)")
plt.ylabel("Accuracy")
plt.show()<jupyter_output>Melhores parametros {'n_neighbors': 1} com o valor de acurácia 0.98538766702438<jupyter_text>Exercício 02 - Classificação de vinhos  imports<jupyter_code>from sklearn import datasets
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics<jupyter_output><empty_output><jupyter_text>biblioteca de visualização http://rasbt.github.io/mlxtend/<jupyter_code>!pip install mlxtend
from mlxtend.plotting import plot_decision_regions<jupyter_output>Requirement already satisfied: mlxtend in /usr/local/lib/python3.7/dist-packages (0.14.0)
Requirement already satisfied: scipy>=0.17 in /usr/local/lib/python3.7/dist-packages (from mlxtend) (1.4.1)
Requirement already satisfied: scikit-learn>=0.18 in /usr/local/lib/python3.7/dist-packages (from mlxtend) (0.22.2.post1)
Requirement already satisfied: matplotlib>=1.5.1 in /usr/local/lib/python3.7/dist-packages (from mlxtend) (3.2.2)
Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (from mlxtend) (56.1.0)
Requirement already satisfied: numpy>=1.10.4 in /usr/local/lib/python3.7/dist-packages (from mlxtend) (1.19.5)
Requirement already satisfied: pandas>=0.17.1 in /usr/local/lib/python3.7/dist-packages (from mlxtend) (1.1.5)
Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.18->mlxtend) (1.0.1)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.7/dist-pa[...]<jupyter_text>Dataset base de vinhoshttps://archive.ics.uci.edu/ml/datasets/winehttps://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_wine.htmlsklearn.datasets.load_wine<jupyter_code>wine = datasets.load_wine()
wine
# Criando um df a partir da base carregada
df_wine = pd.DataFrame(data=wine.data, columns=wine.feature_names)
df_wine['class'] = wine.target
df_wine.head()<jupyter_output><empty_output><jupyter_text>Explorando os dados<jupyter_code># informações gerais sobre os dados.
df_wine.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 178 entries, 0 to 177
Data columns (total 14 columns):
 #   Column                        Non-Null Count  Dtype  
---  ------                        --------------  -----  
 0   alcohol                       178 non-null    float64
 1   malic_acid                    178 non-null    float64
 2   ash                           178 non-null    float64
 3   alcalinity_of_ash             178 non-null    float64
 4   magnesium                     178 non-null    float64
 5   total_phenols                 178 non-null    float64
 6   flavanoids                    178 non-null    float64
 7   nonflavanoid_phenols          178 non-null    float64
 8   proanthocyanins               178 non-null    float64
 9   color_intensity               178 non-null    float64
 10  hue                           178 non-null    float64
 11  od280/od315_of_diluted_wines  178 non-null    float64
 12  proline                       178 non-null    float64
 13  class[...]<jupyter_text>* Podemos concluir que não há dados nulos. * Todas as colunas são numéricas, float64 ou int64* Existem 178 ocorrências (ou linhas)* Existem 14 colunas. Podemos intuir que são 13 features (caracteristicas) e 1 target (class).<jupyter_code># analisando algumas métricas estatísticas sobre os dados
df_wine.describe()
# analisando as classes
df_wine['class'].value_counts()<jupyter_output><empty_output><jupyter_text>A quantidade de ocorrência por classe não é balanceda.  Separação dos dados em train e test<jupyter_code># usaremos uma proporção de 80% para train e 20% para teste, indicadda em test_size=0.2
X_train, X_test, y_train, y_test = train_test_split(df_wine.drop('class',axis=1), df_wine['class'], test_size=0.2)<jupyter_output><empty_output><jupyter_text>O modelo - KNN* n_neighbors: Número de vizinhos (Default=5)* weights: Peso de amostras vizinhas (Default=uniform).* metric: Métrica utilizada para o cálculo de distância (Default=minkowski).* p: Parametro de poder para a métrica (Default=2).* n_jobs: Número de jobs paralelos para executar no momento da busca de vizinhos. (Default=1)<jupyter_code># para nosso caso usaremos k = 1, ou seja, somente 1 (um) vizinho
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)
y_predito = knn.predict(X_test)
y_predito<jupyter_output><empty_output><jupyter_text>Métricas  Matriz de confusão<jupyter_code>print(pd.crosstab(y_test, y_predito, rownames=['Real'], colnames=['Predito'], margins=True))<jupyter_output>Predito   0   1  2  All
Real                   
0        11   0  0   11
1         1  13  2   16
2         0   6  3    9
All      12  19  5   36<jupyter_text>Na matriz de confusão, qualque valor for da diagonal principal é um erro.   Recall, Precision, f1-score, ...<jupyter_code>print(metrics.classification_report(y_test, y_predito, target_names=wine.target_names))<jupyter_output>precision    recall  f1-score   support

     class_0       0.92      1.00      0.96        11
     class_1       0.68      0.81      0.74        16
     class_2       0.60      0.33      0.43         9

    accuracy                           0.75        36
   macro avg       0.73      0.72      0.71        36
weighted avg       0.73      0.75      0.73        36<jupyter_text>Ajustando os hiper-parâmetrosNesse caso vamos ajustar a quantidade de vizinhos, com isso encontraremos a quantidade de vizinho que fornece o menor erro.**negrito**<jupyter_code># variamos os viznhos de 1 a 30
k_list = list(range(1,31))

# Variamos os pesos entre uniforme e distance
weight_list = ['uniform','distance']

# variamos p entre 1 e 2
p_list = [1,2]

parametros = dict(n_neighbors=k_list, weights=weight_list, p=p_list)
parametros
# cv é a quantidade de varificações por conjunto
# scoring é a métrica usada para obter o melhor modelo, estamos usando accuracy
grid = GridSearchCV(knn, parametros, cv=5, scoring='accuracy')
grid.fit(df_wine.drop('class',axis=1),df_wine['class'])
# o atributo cv_results_ contém todos os resultados coletados.
grid.cv_results_
# obtendo os scores
scores = grid.cv_results_.get('mean_test_score')
print(scores)

# rank dos melhores modelos
k_rank = grid.cv_results_.get('rank_test_score')
print(k_rank)

print(f"Melhores parametros {grid.best_params_} com o valor de acurácia {grid.best_score_} ")
plt.plot(scores, color='red', linestyle='dashed', marker='o')<jupyter_output><empty_output><jupyter_text>Analisando a fronteira<jupyter_code>def plot_fronteiras(X, y, n_vizinhos):
  knn = KNeighborsClassifier(n_neighbors=n_vizinhos)
  knn.fit(X, y)
  plt.figure(figsize=(8,5))
  plot_decision_regions(X, y, clf=knn, legend=2)
  plt.xlabel('alcohol')
  plt.ylabel('malic_acid')
  plt.title(f'Fronteiras de Complexidade - KNN {n_vizinhos} vizinhos')
  plt.show()

plot_fronteiras(wine.data[:,[0,2]], wine.target, 1)
plot_fronteiras(wine.data[:,[0,2]], wine.target, 2)<jupyter_output>/usr/local/lib/python3.7/dist-packages/mlxtend/plotting/decision_regions.py:244: MatplotlibDeprecationWarning: Passing unsupported keyword arguments to axis() will raise a TypeError in 3.3.
  ax.axis(xmin=xx.min(), xmax=xx.max(), y_min=yy.min(), y_max=yy.max())<jupyter_text>Exemplo 03 - Iris  imports<jupyter_code>from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.model_selection import GridSearchCV<jupyter_output><empty_output><jupyter_text>dataset<jupyter_code>iris = datasets.load_iris()
df_iris = pd.DataFrame(iris.data, columns=iris.feature_names)
df_iris['class'] = iris.target
df_iris.head()<jupyter_output><empty_output><jupyter_text>Analisando os dados<jupyter_code>df_iris.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   sepal length (cm)  150 non-null    float64
 1   sepal width (cm)   150 non-null    float64
 2   petal length (cm)  150 non-null    float64
 3   petal width (cm)   150 non-null    float64
 4   class              150 non-null    int64  
dtypes: float64(4), int64(1)
memory usage: 6.0 KB<jupyter_text>Podemos verificar que o conjunto de dados contém 5 colunas, sendo 4 de features e 1 de classificação.Todas as colunas são numéricas.Não há dados faltantes ou nulos.<jupyter_code>df_iris.describe()
df_iris['class'].value_counts()<jupyter_output><empty_output><jupyter_text>Há 3 classe diferentes, e são totamente balanceadas.  Dados de treino e validação<jupyter_code>X = df_iris.iloc[:, :-1].values
print(X)
y = df_iris.iloc[:, 4].values
print(y)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

print(f'Tamanhos do X de treino {X_train.shape}')
print(f'Tamanhos do y de treino {y_train.shape}')

print(f'Tamanhos do X de teste {X_test.shape}')
print(f'Tamanhos do y de teste {y_test.shape}')<jupyter_output>Tamanhos do X de treino (120, 4)
Tamanhos do y de treino (120,)
Tamanhos do X de teste (30, 4)
Tamanhos do y de teste (30,)<jupyter_text>Normalizando os dados Note que o fit da normalização será no X_train, isso pq o X_test na prática não sabemos a priore, ou seja, o conjunto de dados que temos a mão para saber fazer a normalização é o X_train, e o X_test são os dados que viram futuramente para aplicarmos o modelo.<jupyter_code>scaler = StandardScaler()
scaler.fit(X_train)

X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)<jupyter_output><empty_output><jupyter_text>O modelo KNN<jupyter_code>knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

# predizendo
y_predito = knn.predict(X_test)<jupyter_output><empty_output><jupyter_text>Métricas<jupyter_code># Matriz de confusao
matriz_confusao = confusion_matrix(y_true=y_test, y_pred=y_predito)
print(matriz_confusao)

print('\n---\n')

# melhorando a visualização da matriz de confusao
print(pd.crosstab(y_test, y_predito, rownames=['Real'], colnames=['Predito'], margins=True))
print(classification_report(y_test, y_predito, target_names=iris.target_names))<jupyter_output>precision    recall  f1-score   support

      setosa       1.00      1.00      1.00        11
  versicolor       0.90      1.00      0.95         9
   virginica       1.00      0.90      0.95        10

    accuracy                           0.97        30
   macro avg       0.97      0.97      0.96        30
weighted avg       0.97      0.97      0.97        30<jupyter_text>Grid search<jupyter_code>knn = KNeighborsClassifier(n_neighbors=2)

k_list = list(range(1, 31)) # k variando de 1 a 30
parametros = dict(n_neighbors=k_list) # converte a liste em dicionários de parâmetros

grid = GridSearchCV(knn, parametros, cv=2, scoring='accuracy')
# knn é o modelo que faremos o ajuste
# cv quantidade de rodadas, maior que zero
# scoring qual métrica usaremos para selecionar o melhor

grid.fit(X_train, y_train)

print(f"Melhores parametros {grid.best_params_} com o valor de acurácia {grid.best_score_} ")
plt.plot(k_list, grid.cv_results_['mean_test_score'], color='red', linestyle='dashed', marker='o')
plt.xlabel("K (vizinhos)")
plt.ylabel("Accuracy")
plt.show()<jupyter_output>Melhores parametros {'n_neighbors': 7} com o valor de acurácia 0.9666666666666667
{'fraction_non_alphanumeric': 0.10148126633749636, 'fraction_numerical': 0.04722625617194307, 'mean_word_length': 3.534105873057677, 'pattern_counts': {'":': 0, '<': 93, '<?xml version=': 0, '>': 102, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '26', 'dir': 'jupyter-structured-clean-dedup', 'id': '489149', 'license': 'Apache-2.0', 'n_tokens_mistral': 6835, 'n_tokens_neox': 6273, 'n_words': 1686, 'path': 'Notebooks/Classificacao_KNN.ipynb', 'repo_name': 'cotozelo/Data_Science_Machine_Learning_-_Data_ICMC', 'size': '17215'}