Chegamos à nossa última aula de conteúdo novo. Na aula passada, ensinamos o computador a ser um juiz e a classificar Pokémon (Lendário ou Não-Lendário). Hoje, vamos ensiná-lo a prever números.
- O que é Aprendizado de Máquina (AM)? É a área da computação que ensina o computador a tomar decisões aprendendo com exemplos (dados), em vez de seguirmos regras (
if/else) que um programador escreveu. - Divisão de Desenvolvimento e Teste Para saber se nosso modelo aprendeu de verdade (e não apenas decorou), nós separamos nossos dados.
- Conjunto de Desenvolvimento:
- Dados de Treino: Usado para ajustar o modelo. É como assistir uma aula.
- Dados de Validação: Utilizamos um conjunto que não foi utilizado no treino para avaliar a qualidade esperada do modelo. É como fazer um simulado ou lista de exercícios.
- Conjunto de Teste: Os que escondemos do modelo e usamos como a "prova final" para ver se ele realmente aprendeu.
- Conjunto de Desenvolvimento:
Na aula passada, resolvemos a primeira tarefa do Professor Carvalho (Classificação). Hoje, vamos resolver a segunda:
"Você consegue prever o poder de Ataque de um Pokémon olhando apenas sua Defesa e HP?"
Este é um problema de Regressão, porque a nossa resposta (o Ataque) não é uma categoria "Lendário" ou "Não Lendário", mas sim um número contínuo (pode ser 50, 82, 134, etc.).
Vamos preparar nosso ambiente.
# Instale o scikit-learn se ainda não o fez
# pip install scikit-learn matplotlib seaborn pandas
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
import numpy as np
# Carregar nossos dados
df = pd.read_csv('https://raw.githubusercontent.com/Assaoka/Decolar--Introducao_a_Ciencia_de_Dados/refs/heads/main/pokemon.csv')
# Limpar alguns dados que podem quebrar o modelo
df = df[['Health', 'Attack', 'Defense']]
df.sample(5)Em Aprendizado de Máquina, tudo se resume a X e Y. Nosso objetivo é descobrir um conjunto de operações sobre X (características de entrada, nesse caso HP e Defesa) que melhor aproxima o valor de Y (característica alvo, no caso Ataque).
# Definir X e Y
features = ['Health', 'Defense']
target = 'Attack'
X = df[features]
y = df[target]
X_dev, X_test, y_dev, y_test = train_test_split(
X, y,
test_size=0.1,
random_state=42
)
X_train, X_val, y_train, y_val = train_test_split(
X_dev, y_dev,
test_size=0.2,
random_state=42
)
print(f"Total de dados: {len(X)}")
print(f"Dados de Treino: {len(X_train)}")
print(f"Dados de Validação: {len(X_val)}")
print(f"Dados de Teste: {len(X_test)}")Por exemplo, suponha que temos os dados X e Y:
| X | Y |
|---|---|
| 1 | 4 |
| 2 | 7 |
| 5 | 16 |
Observe que esses dados são gerados por
x = np.array([1, 2, 5])
def modelo(x):
return 3*x + 1
y = modelo(x)
# Gráfico de dispersão com a linha de tendência.
plt.figure(figsize=(8, 5))
sns.lineplot(x=x, y=y, color='red', label='Linha de Tendência (y = 3*x + 1)')
sns.scatterplot(x=x, y=y, s=100, label='Dados Reais', color='blue')
plt.title('Relação Perfeita (y = 3x + 1)')
plt.xlabel('X')
plt.ylabel('Y')
plt.grid(True)
plt.legend()
plt.show()Essa equação que utilizamos é a equação da reta, que é:
O objetivo da regressão linear é encontrar os números a e b que minimiza o erro (distância do número previsto para o real) no nosso conjunto de treinamento.
Antes de usar HP e Defesa juntos, vamos simplificar. E se usássemos apenas a Defesa para prever o Ataque?
Vamos ver a relação entre eles no nosso gráfico:
# Plotar Defesa vs Ataque
plt.figure(figsize=(10, 6))
sns.scatterplot(data=X_train, x='Defense', y=y_train, alpha=0.5)
plt.title('Relação entre Defesa e Ataque (Dados de Treino)')
plt.xlabel('Defesa')
plt.ylabel('Ataque')
plt.grid(True)
plt.show()Olhando o gráfico, parece que existe uma tendência: Pokémon com mais defesa geralmente têm um ataque um pouco maior.
Nós queremos achar os melhores valores para
# --- Modelo 1: Apenas Defesa ---
# 1. Preparar os dados (só com Defesa)
X_train_def = X_train[['Defense']]
X_val_def = X_val[['Defense']]
X_test_def = X_test[['Defense']]
# 2. Criar e Treinar o modelo
modelo_1 = LinearRegression()
modelo_1.fit(X_train_def, y_train)
# 3. Fazer previsões
y_pred_1 = modelo_1.predict(X_train_def)
# 4. Ver a "linha" que o modelo encontrou
plt.figure(figsize=(10, 6))
sns.scatterplot(data=X_train_def, x='Defense', y=y_train, alpha=0.5, label='Dados Reais')
plt.plot(X_train_def, y_pred_1, color='red', linewidth=3, label='Linha de Regressão (Previsão)')
plt.title('Modelo 1: Apenas Defesa')
plt.legend()
plt.grid(True)
plt.show()a = modelo_1.coef_[0]
b = modelo_1.intercept_
print(f"Y = Modelo(X) = {a:.2f} * x + {b:.2f}")Na Classificação, usamos Acurácia, Precisão, Recall e F1-Score. Em Regressão, o mais comum é o RMSE (Raiz do Erro Quadrático Médio).
O RMSE nos diz, em média, quantos pontos a nossa previsão errou. Se o Ataque real era 100 e o modelo previu 90, o erro foi 10. O RMSE é uma média de todos esses erros (penalizando erros maiores). Quanto menor o RMSE, melhor o modelo.
# Calcular o erro do Modelo 1
y_pred_1 = modelo_1.predict(X_val_def)
rmse_1 = np.sqrt(mean_squared_error(y_val, y_pred_1))
print(f"Erro Médio do Modelo 1 (RMSE): {rmse_1:.2f} pontos de Ataque")Isso significa que, ao usar apenas a Defesa, nosso modelo erra a previsão de Ataque por cerca de 26 pontos.
Nossa tarefa original era usar HP e Defesa. Agora a equação não é mais uma linha, é um plano 3D:
sklearn, o código é quase o mesmo!
# --- Modelo 2: Defesa + HP ---
# 1. Preparar os dados (já estão em X_train e X_test)
# 2. Criar e Treinar o modelo
modelo_2 = LinearRegression()
modelo_2.fit(X_train, y_train) # X_train tem 'Defense' e 'Health'
# 3. Fazer previsões
y_pred_2 = modelo_2.predict(X_val)
# 4. Calcular o erro do Modelo 2
rmse_2 = np.sqrt(mean_squared_error(y_val, y_pred_2))
print(f"Erro Médio do Modelo 1 (só Defesa): {rmse_1:.2f}")
print(f"Erro Médio do Modelo 2 (Defesa + HP): {rmse_2:.2f}")a = modelo_2.coef_[0]
b = modelo_2.coef_[1]
c = modelo_2.intercept_
print(f"Ataque = modelo(Defesa, HP) = {a:.2f} * Defesa + {b:.2f} * HP + {c:.2f}")# Plotar o plano 3D
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
# Pontos reais
ax.scatter(X_test['Defense'], X_test['Health'], y_test, c='blue', alpha=0.5, label='Dados Reais (Teste)')
# Criando a superfície do plano
xx, yy = np.meshgrid(range(0, 250, 25), range(0, 250, 25))
zz = modelo_2.coef_[1] * xx + modelo_2.coef_[0] * yy + modelo_2.intercept_
ax.plot_surface(xx, yy, zz, alpha=0.3, color='red', label='Plano de Regressão')
ax.set_xlabel('Defesa')
ax.set_ylabel('HP')
ax.set_zlabel('Ataque (Previsto)')
plt.title("Regressão Linear com 2 Variáveis")
plt.show()Com base no erro de validação selecionamos o modelo 2 como nosso modelo final. O erro dele no conjunto de teste foi:
y_pred = modelo_2.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"RMSE: {rmse:.2f}")O objetivo é vocês criarem um projeto aplicando os conteúdos apresentados:
- Encontrar um Dataset: Vocês vão procurar seus próprios dados no Kaggle (vamos aprender como agora!).
- Análise de Dados: Fazer a limpeza, manipulação e criar os gráficos que aprendemos para encontrar padrões.
- App Interativo: Criar uma interface no Streamlit para exibir suas análises.
- Modelo de AM: Treinar um modelo (Classificação ou Regressão) para fazer previsões sobre seus dados.