Pular para o conteúdo principal

Introdução ao machine learning em Python

Neste tutorial, você vai conhecer o mundo do machine learning (ML) com Python. Para entender ML na prática, usaremos o algoritmo K-Nearest Neighbor (KNN) em Python.
Atualizado 17 de set. de 2026  · 14 min lido

Explorar com IA

ChatGPTClaudePerplexity

Você vai implementar KNN no famoso conjunto de dados Iris.

Observação: vale a pena fazer o curso Machine Learning with Python ou, para entender o contexto da evolução do ML e muito mais, ler este post.

Introdução

Machine learning surgiu da ciência da computação e estuda principalmente o design de algoritmos que aprendem com a experiência. Para aprender, eles precisam de dados com certos atributos, a partir dos quais os algoritmos buscam padrões preditivos relevantes. De forma geral, as tarefas de ML podem ser categorizadas em aprendizagem de conceitos, clusterização, modelagem preditiva etc. O objetivo final dos algoritmos de ML é tomar decisões corretas sem intervenção humana. Prever preços de ações ou o clima são alguns exemplos de aplicação.

Existem vários algoritmos de machine learning, como árvores de decisão, Naive Bayes, random forest, support vector machine, K-nearest neighbor, k-means clustering, entre outros.

Do conjunto de algoritmos de ML, o que você vai usar hoje é o k-nearest neighbor.

Agora, o que exatamente é o algoritmo K-Nearest Neighbor? Vamos descobrir!

O que é o k-nearest neighbor?

O KNN, ou k-nearest neighbor, é um algoritmo de aprendizagem supervisionada — “supervisionada” porque ele usa os rótulos de classe dos dados de treinamento durante a fase de aprendizagem. É um algoritmo baseado em instâncias: novos pontos são classificados com base em instâncias rotuladas já armazenadas. KNN pode ser usado tanto para classificação quanto para regressão; porém, é mais comum em tarefas de classificação.

O k no KNN é uma variável crucial, também chamada de hiperparâmetro, que ajuda a classificar um ponto de dados com precisão. Em termos práticos, k é o número de vizinhos mais próximos dos quais você quer “pegar o voto” ao classificar um novo ponto.

visualization of knn visualization of knn

Figura 1. Visualização do KNN Fonte

Perceba que, à medida que o valor de k aumenta de 1 para 7, a fronteira de decisão entre duas classes se torna mais suave.

Como essa “mágica” acontece? Como todo novo ponto é classificado com base nos pontos armazenados?

Vamos entender rapidamente, passo a passo:

  • Primeiro, você carrega todos os dados e define o valor de k;
  • Depois, calcula a distância entre os pontos armazenados e o novo ponto que deseja classificar, usando métricas como distância Manhattan (L1), distância Euclidiana (L2), similaridade do cosseno, distância Bhattacharyya, distância Chebyshev, etc.;
  • Em seguida, ordena os valores de distância em ordem crescente ou decrescente e determina os k vizinhos mais próximos (maiores ou menores, conforme a métrica);
  • Coleta os rótulos desses k vizinhos e usa uma votação majoritária ou ponderada para classificar o novo ponto. O rótulo atribuído é o da classe com maior pontuação;
  • Por fim, retorna a classe prevista para a nova instância.

A previsão pode ser de dois tipos: classificação, na qual é atribuído um rótulo de classe, ou regressão, na qual é atribuído um valor numérico. Diferente da classificação, na regressão costuma-se atribuir ao novo ponto a média dos valores dos k vizinhos.

Desvantagens do KNN: primeiro, a complexidade de buscar os vizinhos mais próximos para cada novo ponto. Segundo, escolher o valor de k pode ser trabalhoso. Por fim, nem sempre é claro qual métrica de distância usar.

Chega de teoria, certo? Vamos carregar, analisar e entender os dados que você vai usar neste tutorial.

Carregando os dados do Iris

O conjunto Iris tem 150 amostras de três classes: Iris-Setosa, Iris-Versicolor e Iris-Virginica. Quatro atributos usados para identificar cada uma das três classes são sepal-length, sepal-width, petal-length e petal-width.

Fique à vontade para usar outro dataset público ou um conjunto de dados próprio.

Sklearn é uma biblioteca de machine learning em Python amplamente usada em tarefas de data science. Ela oferece diversos algoritmos de classificação, regressão e clusterização, incluindo support vector machines, random forests, gradient boosting, k-means, KNN etc. Dentro do sklearn há o módulo datasets com vários conjuntos prontos para uso, incluindo o Iris. É bem intuitivo. Vamos carregar o iris.

from sklearn.datasets import load_iris

O load_iris traz os dados e os rótulos de classe de cada amostra. Vamos extrair tudo rapidamente.

data = load_iris().data

A variável data será um array numpy de shape (150,4), com 150 amostras e quatro atributos cada. Cada classe tem 50 amostras.

data.shape
(150, 4)

Vamos extrair os rótulos de classe.

labels = load_iris().target
labels.shape
(150,)

Agora você vai combinar dados e rótulos usando a excelente biblioteca NumPy. O NumPy oferece suporte a arrays e matrizes multidimensionais, além de uma coleção extensa de funções matemáticas de alto nível para operar nesses arrays. Vamos importar:

import numpy as np

Como data é um array 2D, você também precisa reshapar labels para 2D.

labels = np.reshape(labels,(150,1))

Agora use a função concatenate do numpy, com axis=-1, para concatenar pela segunda dimensão.

data = np.concatenate([data,labels],axis=-1)
data.shape
(150, 5)

Em seguida, importe a biblioteca de análise de dados pandas, útil para organizar dados em formato tabular e fazer operações e manipulações. Em especial, ela oferece estruturas de dados e operações para trabalhar com tabelas numéricas e séries temporais.

Neste tutorial, você vai usar bastante o pandas.

import pandas as pd
names = ['sepal-length', 'sepal-width', 'petal-length', 'petal-width', 'species']
dataset = pd.DataFrame(data,columns=names)

Pronto: o data frame dataset agora tem os dados e os rótulos de classe que você precisa!

Antes de avançar, lembre que a variável labels tem rótulos numéricos. Vamos convertê-los para os nomes das flores (espécies).

Para isso, selecione apenas a coluna class e substitua os três valores numéricos pelas espécies correspondentes. Use inplace=True para modificar o data frame dataset diretamente.

dataset['species'].replace(0, 'Iris-setosa',inplace=True)
dataset['species'].replace(1, 'Iris-versicolor',inplace=True)
dataset['species'].replace(2, 'Iris-virginica',inplace=True)

Vamos imprimir as cinco primeiras linhas de dataset para ver o resultado:

dataset.head(5)
  sepal-length sepal-width petal-length petal-width species
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa

Analise seus dados

Vamos ver como as três flores ficam quando visualizadas e quão diferentes são entre si — não só em números, mas também na prática!

iris(Fonte)

Agora, vamos visualizar os dados carregados acima com um scatterplot para entender quanto uma variável influencia a outra — ou seja, qual é a correlação entre elas.

Você vai usar a biblioteca matplotlib para criar o scatterplot.

import matplotlib.pyplot as plt

Dica: quer aprender diferentes formas de visualizar dados em Python? Então confira o curso Introduction to data visualization with matplotlib.

plt.figure(4, figsize=(10, 8))

plt.scatter(data[:50, 0], data[:50, 1], c='r', label='Iris-setosa')

plt.scatter(data[50:100, 0], data[50:100, 1], c='g',label='Iris-versicolor')

plt.scatter(data[100:, 0], data[100:, 1], c='b',label='Iris-virginica')

plt.xlabel('Sepal length',fontsize=20)
plt.ylabel('Sepal width',fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.title('Sepal length vs. Sepal width',fontsize=20)
plt.legend(prop={'size': 18})
plt.show()
sepal length x width scatter plot

Pelo gráfico acima, fica claro que há alta correlação para as flores Iris setosa em relação ao comprimento e à largura da sépala. Por outro lado, há menor correlação entre Iris versicolor e Iris virginica. Os pontos de versicolor e virginica estão mais espalhados, enquanto os de setosa são mais densos.

Vamos também plotar o gráfico para petal-length e petal-width.

plt.figure(4, figsize=(8, 8))

plt.scatter(data[:50, 2], data[:50, 3], c='r', label='Iris-setosa')

plt.scatter(data[50:100, 2], data[50:100, 3], c='g',label='Iris-versicolor')

plt.scatter(data[100:, 2], data[100:, 3], c='b',label='Iris-virginica')
plt.xlabel('Petal length',fontsize=15)
plt.ylabel('Petal width',fontsize=15)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
plt.title('Petal length vs. Petal width',fontsize=15)
plt.legend(prop={'size': 20})
plt.show()
petal length x width scatter plot

Também para petal-length e petal-width, o gráfico indica uma forte correlação para as flores setosa, que ficam densamente agrupadas.

Para reforçar como petal-length e petal-width se correlacionam, vamos plotar uma matriz de correlação.

dataset.iloc[:,2:].corr()
  petal-length petal-width
petal-length 1.000000 0.962865
petal-width 0.962865 1.000000

A tabela acima mostra uma forte correlação de 0.96 entre petal-length e petal-width quando combinamos as três espécies.

Vamos analisar a correlação separadamente por espécie.

dataset.iloc[:50,:].corr() #setosa
  sepal-length sepal-width petal-length petal-width
sepal-length 1.000000 0.742547 0.267176 0.278098
sepal-width 0.742547 1.000000 0.177700 0.232752
petal-length 0.267176 0.177700 1.000000 0.331630
petal-width 0.278098 0.232752 0.331630 1.000000
dataset.iloc[50:100,:].corr() #versicolor
  sepal-length sepal-width petal-length petal-width
sepal-length 1.000000 0.525911 0.754049 0.546461
sepal-width 0.525911 1.000000 0.560522 0.663999
petal-length 0.754049 0.560522 1.000000 0.786668
petal-width 0.546461 0.663999 0.786668 1.000000
dataset.iloc[100:,:].corr() #virginica
  sepal-length sepal-width petal-length petal-width
sepal-length 1.000000 0.457228 0.864225 0.281108
sepal-width 0.457228 1.000000 0.401045 0.537728
petal-length 0.864225 0.401045 1.000000 0.322108
petal-width 0.281108 0.537728 0.322108 1.000000

Pelas três tabelas, fica claro que a correlação entre petal-length e petal-width é de 0.33 para setosa e 0.32 para virginica, enquanto para versicolor é 0.78.

Agora, vamos visualizar a distribuição das features com histogramas:

fig = plt.figure(figsize = (8,8))
ax = fig.gca()
dataset.hist(ax=ax)
plt.show()
bar charts

petal-length, petal-width e sepal-length mostram distribuição unimodal, enquanto sepal-width se aproxima de uma distribuição Gaussiana. Tudo isso ajuda a escolher um algoritmo que funcione bem com essas distribuições.

Em seguida, vamos analisar se os quatro atributos estão na mesma escala — algo essencial em ML. O data frame do pandas tem a função describe, que retorna count, mean, max, min etc. em formato tabular.

dataset.describe()
  sepal-length sepal-width petal-length petal-width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.057333 3.758000 1.199333
std 0.828066 0.435866 1.765298 0.762238
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000

Perceba que os quatro atributos têm escala semelhante entre 0 e 8, em centímetros. Se quiser, você ainda pode reescalá-los para o intervalo entre 0 e 1.

Embora saibamos que há 50 amostras por classe (cerca de 33,3% do total), vamos confirmar:

print(dataset.groupby('species').size())
species
Iris-setosa        50
Iris-versicolor    50
Iris-virginica     50
dtype: int64

Pré-processando seus dados

Depois de carregar e analisar bem os dados, é hora de prepará-los para alimentar seu modelo de ML. Nesta etapa, você vai normalizar os dados e dividir em conjuntos de treino e teste.

Normalizando seus dados

Existem duas formas comuns de normalização:

  • Normalização por exemplo, em que você normaliza cada amostra individualmente;
  • Normalização por feature, em que você normaliza cada atributo de forma consistente em todas as amostras.

Por que e quando normalizar? E é necessário padronizar o Iris?

De modo geral, quase sempre é uma boa prática. Normalizar coloca tudo na mesma escala e faixa, o que é crucial quando os dados não são consistentes. Você pode checar inconsistências com describe(), que mostra max e min. Se uma feature tem faixas muito maiores que outra, normalizar ambas é importante.

Imagine X com faixa maior e Y com faixa menor: a influência de Y pode ser ofuscada pela de X. Nesses casos, normalize X e Y.

No Iris, a normalização não é necessária.

Vamos imprimir novamente describe() para ver por que não precisamos normalizar.

dataset.describe()
  sepal-length sepal-width petal-length petal-width
count 150.000000 150.000000 150.000000 150.000000
mean 5.843333 3.057333 3.758000 1.199333
std 0.828066 0.435866 1.765298 0.762238
min 4.300000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.300000
50% 5.800000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000

O atributo sepal-length varia de 4,3 a 7,9; sepal-width, de 2 a 4,4; petal-length, de 1 a 6,9; e petal-width, de 0,1 a 2,5. Todas as features ficam entre 0,1 e 7,9 — aceitável. Logo, não é necessário normalizar o Iris.

Dividindo os dados

Este é outro ponto importante em ML: seu objetivo é treinar um modelo capaz de tomar decisões ou classificar dados em ambiente de teste sem intervenção humana. Antes de colocar o modelo em produção, garanta que ele generaliza bem nos dados de teste.

Para isso, você precisa de conjunto de treino e de teste. No Iris, com 150 amostras, vamos treinar em 80% e testar nos 20% restantes.

Em data science, você vai ouvir muito sobre overfitting: quando o modelo aprende muito bem os dados de treino, mas vai mal nos de teste. Dividir em treino e teste (ou validação) ajuda a detectar esse problema.

Para fazer o split, use a função train_test_split da biblioteca sklearn. Vamos dividir:

from sklearn.model_selection import train_test_split

train_data, test_data, train_label, test_label = train_test_split(dataset.iloc[:,:3], dataset.iloc[:,3], test_size=0.2, random_state=42) 

Note que random_state é uma semente. Se você mudar o número, a divisão muda. Mantendo o mesmo random_state, a divisão permanece igual em execuções repetidas.

Vamos imprimir o shape dos conjuntos de treino e teste e seus rótulos.

train_data.shape,train_label.shape,test_data.shape,test_label.shape
((120, 3), (120,), (30, 3), (30,))

Agora sim: é hora de alimentar o algoritmo k-nearest neighbor!

O modelo KNN

Depois de carregar, analisar e pré-processar os dados, vamos alimentar o modelo KNN. Para isso, use a função neighbors do sklearn, que tem a classe KNeigborsClassifier.

Comece importando o classificador.

from sklearn.neighbors import KNeighborsClassifier

Observação: o parâmetro k (n_neighbors) costuma ser ímpar para evitar empates na votação.

Para escolher o melhor k, vamos fazer um grid-search simples: treinar e testar o modelo com 10 valores diferentes de k e ficar com o que tiver melhor desempenho.

Vamos inicializar neighbors(k) com valores de 1 a 9 e duas matrizes de zeros do numpy, train_accuracy e test_accuracy, para armazenar as acurácias de treino e teste. Depois, vamos plotar um gráfico e escolher o melhor neighbor.

neighbors = np.arange(1,9)
train_accuracy =np.zeros(len(neighbors))
test_accuracy = np.zeros(len(neighbors))

No próximo trecho acontece a mágica: vamos enumerate sobre os nove valores de vizinhos e, para cada um, prever em treino e teste. Por fim, armazenar as acurácias em train_accuracy e test_accuracy.

for i,k in enumerate(neighbors):
    knn = KNeighborsClassifier(n_neighbors=k)

    #Fit the model
    knn.fit(train_data, train_label)

    #Compute accuracy on the training set
    train_accuracy[i] = knn.score(train_data, train_label)

    #Compute accuracy on the test set
    test_accuracy[i] = knn.score(test_data, test_label)

Agora vamos plotar as acurácias de treino e teste com matplotlib. Com o gráfico de accuracy vs. número de vizinhos, você escolhe o melhor valor de k.

plt.figure(figsize=(10,6))
plt.title('KNN accuracy with varying number of neighbors',fontsize=20)
plt.plot(neighbors, test_accuracy, label='Testing Accuracy')
plt.plot(neighbors, train_accuracy, label='Training accuracy')
plt.legend(prop={'size': 20})
plt.xlabel('Number of neighbors',fontsize=20)
plt.ylabel('Accuracy',fontsize=20)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.show()
knn accuracy chart

Olhando o gráfico, parece que com n_neighbors=3 o modelo tem o melhor desempenho. Vamos usar n_neighbors=3 e treinar novamente.

knn = KNeighborsClassifier(n_neighbors=3)

#Fit the model
knn.fit(train_data, train_label)

#Compute accuracy on the training set
train_accuracy = knn.score(train_data, train_label)

#Compute accuracy on the test set
test_accuracy = knn.score(test_data, test_label)

Avaliando seu modelo

No último trecho do tutorial, vamos avaliar o modelo nos dados de teste usando técnicas como confusion_matrix e classification_report.

Primeiro, verifique a acurácia no conjunto de teste.

test_accuracy
0.9666666666666667

Uau! Parece que o modelo classificou corretamente 96,66% dos dados de teste. Incrível, né? Com poucas linhas de código, você treinou um modelo que consegue dizer o nome da flor usando apenas quatro atributos, com 96,66% de acerto. Quem sabe ele até supera um humano nessa tarefa.

Matriz de confusão

A matriz de confusão descreve o desempenho do modelo no conjunto de teste, para o qual os rótulos verdadeiros são conhecidos.

O Scikit-learn fornece uma função que calcula a matriz de confusão para você.

prediction = knn.predict(test_data)

A função plot_confusion_matrix() abaixo foi adaptada desta fonte.

import itertools
def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):


    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.ylabel('True label',fontsize=30)
    plt.xlabel('Predicted label',fontsize=30)
    plt.tight_layout()
    plt.xticks(fontsize=18)
    plt.yticks(fontsize=18)
class_names = load_iris().target_names


# Compute confusion matrix
cnf_matrix = confusion_matrix(test_label, prediction)
np.set_printoptions(precision=2)

# Plot non-normalized confusion matrix
plt.figure(figsize=(10,8))
plot_confusion_matrix(cnf_matrix, classes=class_names)
plt.title('Confusion Matrix',fontsize=30)
plt.show()
confusion matrix

Pela confusion_matrix acima, dá para ver que o modelo classificou todas corretamente, exceto uma virginica, que foi prevista como versicolor.

Relatório de classificação

O classification report ajuda a identificar classes mal classificadas com mais detalhes, fornecendo precision, recall e F1 score por classe. Vamos usar o sklearn para visualizar o relatório.

from sklearn.metrics import classification_report
print(classification_report(test_label, prediction))
                 precision    recall  f1-score   support

    Iris-setosa       1.00      1.00      1.00        10
Iris-versicolor       0.90      1.00      0.95         9
 Iris-virginica       1.00      0.91      0.95        11

      micro avg       0.97      0.97      0.97        30
      macro avg       0.97      0.97      0.97        30
   weighted avg       0.97      0.97      0.97        30

Vá além!

Parabéns a quem chegou até aqui! Mas isso é só o começo — ainda tem muita estrada pela frente.

Este tutorial tratou dos fundamentos de machine learning e da implementação de um algoritmo, o KNN, em Python. O dataset Iris é pequeno e relativamente simples.

Se este conteúdo despertou seu interesse, experimente outros conjuntos de dados ou estude mais algoritmos de ML e aplique no Iris para observar o impacto na acurácia. Dessa forma, você aprende muito mais do que apenas a teoria!

Se você já praticou bastante com o que foi apresentado aqui e com outros algoritmos de ML, talvez seja hora de se aprofundar em Python e análise de dados.

Tópicos
Aprendizado de máquina
Python

Aprenda mais sobre machine learning e Python

Curso

Entendendo Machine Learning

2 h
308K
Uma introdução ao aprendizado de máquina sem programação.
Ver detalhesRight Arrow
Iniciar Curso
Ver maisRight Arrow
Relacionado
Clustering k-means

Tutorial

Introdução ao k-Means Clustering com o scikit-learn em Python

Neste tutorial, saiba como aplicar o k-Means Clustering com o scikit-learn em Python

Kevin Babitz

8 min

Tutorial

Introdução ao Q-learning: um tutorial para iniciantes

Aprenda o algoritmo de aprendizado por reforço sem modelo mais popular com um tutorial em Python.
Abid Ali Awan's photo

Abid Ali Awan

11 min

Tutorial

Classificação de K-Nearest Neighbors (KNN) com o tutorial do R

Aprenda a usar os pacotes R 'class' e 'caret', ajustar hiperparâmetros e avaliar o desempenho do modelo.
Abid Ali Awan's photo

Abid Ali Awan

11 min

Tutorial

Tutorial de manipulação de dados categóricos de aprendizado de máquina com Python

Aprenda os truques comuns para lidar com dados categóricos e pré-processá-los para criar modelos de aprendizado de máquina!
Moez Ali's photo

Moez Ali

14 min

Python

Tutorial

Tutorial para entender a regressão logística em Python

Aprenda sobre a regressão logística, suas propriedades básicas e crie um modelo de aprendizado de máquina em um aplicativo do mundo real em Python.
Avinash Navlani's photo

Avinash Navlani

10 min

Tutorial

Stemming e lematização em Python

Este tutorial aborda o stemming e a lematização de um ponto de vista prático usando o pacote Python Natural Language ToolKit (NLTK).
Kurtis Pykes 's photo

Kurtis Pykes

12 min

Ver MaisVer Mais