lab1
Objetivos¶
- Apresentar o conceito de Regressão
- Apresentar e utilizar algoritmo de Regressão linear
- Apresentar e utilizar Regressão Polinomial
- Apresentar e discutir a matriz de correlação
- Apresentar uma intuição sobre métricas de avaliação (MSE, RMSE e $ R² $ )
Começando¶
Sabemos que dentro de aprendizado supervisionado vamos trabalhar com dois tipos de problemas:
- Classificação - (Já conhecemos o KNN)
- Regressão - (Objetivo de hoje)
Uma intuição sobre problemas que envolvem cada um deles:¶
Classificação --> Resultados discretos (categóricos).
Regressão --> Resultados numéricos e contínuos.
Regressão linear¶
É uma técnica que consiste em representar um conjunto de dados por meio de uma reta.
Na matemática aprendemos que a equação de uma reta é:
$$ Y = A + BX \\ $$ A e B são constantes que determinam a posição e inclinação da reta. Para cada valor de X temos um Y associado.
Em machine learning aprendemos que uma Regressão linear é:
$$ Y_{predito} = \beta_o + \beta_1X \\ $$
$ \beta_o $ e $ \beta_1 $ são parâmetros que determinam o peso e bias da rede. Para cada entrada $ X $ temos um $ Y_{predito} $ aproximado predito.
Essa ideia se estende para mais de um parâmetro independente, mas nesse caso não estamos associando a uma reta e sim a um plano ou hiperplano:
$$ Y_{predito} = \beta_o + \beta_1X_1 + \beta_2X_2 + ... + \beta_nX_n\\ $$
Em outras palavras, modelos de regressão linear são intuitivos, fáceis de interpretar e se ajustam aos dados razoavelmente bem em muitos problemas.
Bora lá!!¶
Vamos juntos realizar um projeto, do começo ao fim, usando regressão.
Definição do problema¶
Vamos trabalhar com um dataset com informações coletadas U.S Census Service (tipo IBGE americano) sobre habitação na área de Boston Mass.
Faça o download do dataset aqui: https://drive.google.com/file/d/1JDbx09otYOsDOWZlDiFY4ksaPr23befi/view?usp=sharing¶
informação importante sobre o significado de cada um dos atributos
Attribute Information:
- CRIM per capita crime rate by town
- ZN proportion of residential land zoned for lots over 25,000 sq.ft.
- INDUS proportion of non-retail business acres per town
- CHAS Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
- NOX nitric oxides concentration (parts per 10 million)
- RM average number of rooms per dwelling
- AGE proportion of owner-occupied units built prior to 1940
- DIS weighted distances to five Boston employment centres
- RAD index of accessibility to radial highways
- TAX full-value property-tax rate per $10,000
- PTRATIO pupil-teacher ratio by town
- B 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
- LSTAT % lower status of the population
- MEDV Median value of owner-occupied homes in $1000's
Queremos desenvolver um modelo capaz de predizer o valor de um imovel em Boston.
Desafio 1¶
Do ponto de vista de machine learning, que problema é esse:
Aprendizado supervisionado, não-supervisionado ou aprendizado por reforço?
R:
Classificação, regressão ou clusterização?
R:
# Inicializção das bibliotecas
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import pandas as pd
# Carregando o dataset com o delimitador correto
df = pd.read_csv('housing.csv', delim_whitespace=True, header=None)
# Atribuindo nomes às colunas
df.columns = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
df.head()
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | MEDV | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.00632 | 18.0 | 2.31 | 0 | 0.538 | 6.575 | 65.2 | 4.0900 | 1 | 296.0 | 15.3 | 396.90 | 4.98 | 24.0 |
1 | 0.02731 | 0.0 | 7.07 | 0 | 0.469 | 6.421 | 78.9 | 4.9671 | 2 | 242.0 | 17.8 | 396.90 | 9.14 | 21.6 |
2 | 0.02729 | 0.0 | 7.07 | 0 | 0.469 | 7.185 | 61.1 | 4.9671 | 2 | 242.0 | 17.8 | 392.83 | 4.03 | 34.7 |
3 | 0.03237 | 0.0 | 2.18 | 0 | 0.458 | 6.998 | 45.8 | 6.0622 | 3 | 222.0 | 18.7 | 394.63 | 2.94 | 33.4 |
4 | 0.06905 | 0.0 | 2.18 | 0 | 0.458 | 7.147 | 54.2 | 6.0622 | 3 | 222.0 | 18.7 | 396.90 | 5.33 | 36.2 |
Desafio 2¶
Use os metodos info() e describe() para exibir as informações do dataframe e responda:
Existe dados faltantes?
Qual o tamanho do dataset, quantas linhas e quantas colunas?
# Mostra informações sobre o dataframe em si
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 506 entries, 0 to 505 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CRIM 506 non-null float64 1 ZN 506 non-null float64 2 INDUS 506 non-null float64 3 CHAS 506 non-null int64 4 NOX 506 non-null float64 5 RM 506 non-null float64 6 AGE 506 non-null float64 7 DIS 506 non-null float64 8 RAD 506 non-null int64 9 TAX 506 non-null float64 10 PTRATIO 506 non-null float64 11 B 506 non-null float64 12 LSTAT 506 non-null float64 13 MEDV 506 non-null float64 dtypes: float64(12), int64(2) memory usage: 55.5 KB
df.describe()
CRIM | ZN | INDUS | CHAS | NOX | RM | AGE | DIS | RAD | TAX | PTRATIO | B | LSTAT | MEDV | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 | 506.000000 |
mean | 3.613524 | 11.363636 | 11.136779 | 0.069170 | 0.554695 | 6.284634 | 68.574901 | 3.795043 | 9.549407 | 408.237154 | 18.455534 | 356.674032 | 12.653063 | 22.532806 |
std | 8.601545 | 23.322453 | 6.860353 | 0.253994 | 0.115878 | 0.702617 | 28.148861 | 2.105710 | 8.707259 | 168.537116 | 2.164946 | 91.294864 | 7.141062 | 9.197104 |
min | 0.006320 | 0.000000 | 0.460000 | 0.000000 | 0.385000 | 3.561000 | 2.900000 | 1.129600 | 1.000000 | 187.000000 | 12.600000 | 0.320000 | 1.730000 | 5.000000 |
25% | 0.082045 | 0.000000 | 5.190000 | 0.000000 | 0.449000 | 5.885500 | 45.025000 | 2.100175 | 4.000000 | 279.000000 | 17.400000 | 375.377500 | 6.950000 | 17.025000 |
50% | 0.256510 | 0.000000 | 9.690000 | 0.000000 | 0.538000 | 6.208500 | 77.500000 | 3.207450 | 5.000000 | 330.000000 | 19.050000 | 391.440000 | 11.360000 | 21.200000 |
75% | 3.677083 | 12.500000 | 18.100000 | 0.000000 | 0.624000 | 6.623500 | 94.075000 | 5.188425 | 24.000000 | 666.000000 | 20.200000 | 396.225000 | 16.955000 | 25.000000 |
max | 88.976200 | 100.000000 | 27.740000 | 1.000000 | 0.871000 | 8.780000 | 100.000000 | 12.126500 | 24.000000 | 711.000000 | 22.000000 | 396.900000 | 37.970000 | 50.000000 |
Desafio 3¶
Aplique os métodos que achar conveniente (vimos algumas opções na última aula) para visualizar os dados de forma gráfica.
## Sua resposta e seus gráficos para análisar..
#Vamos explorar um pouco uma matrix de correlação
import seaborn as sns
correlation_matrix = df.corr().round(2)
fig, ax = plt.subplots(figsize=(10,10))
sns.heatmap(data=correlation_matrix, annot=True, linewidths=.5, ax=ax)
<Axes: >
Desafio 4¶
Analisando a matriz de correlação acima responda:
Qual feature possue a maior correlação positiva com o target?
Qual feature possue a maior correlação negativa com o target?
df.plot.scatter('RM', 'MEDV')
<Axes: xlabel='RM', ylabel='MEDV'>
df.plot.scatter('LSTAT', 'MEDV')
<Axes: xlabel='LSTAT', ylabel='MEDV'>
PARE!!!¶
A análise feita no desafio 2 e 3 é uma das etapas mais importantes. Caso você tenha pulado essa etapa, volte e faça suas análises.
Com essa etapa concluída, vamos criar um sub-dataset com os atributos que serão utilizados.
# Vamos treinar nosso modelo com 2 dois atributos independentes
# para predizer o valor de saida
X = df[['LSTAT', 'RM']] ### teste com duas entradas
#X = df[['RM']] ### teste com uma entrada
#X = df.drop(['MEDV'], axis=1) ### teste com todas as entradas
Y = df['MEDV']
print(f"Formato das tabelas de dados {X.shape} e saidas {Y.shape}")
Formato das tabelas de dados (506, 2) e saidas (506,)
Dividindo os dados em conjunto de treinamento e de testes¶
Dividir nosso dataset em dois conjuntos de dados.
Treinamento - Representa 80% das amostras do conjunto de dados original,
Teste - com 20% das amostras
Vamos escolher aleatoriamente algumas amostras do conjunto original. Isto pode ser feito com Scikit-Learn usando a função train_test_split()
scikit-learn Caso ainda não tenha instalado, no terminal digite:
- pip install scikit-learn
# Separamos 20% para o teste
from sklearn.model_selection import train_test_split
X_treino, X_teste, Y_treino, Y_teste = train_test_split(X, Y, test_size=0.2)
print(X_treino.shape)
print(X_teste.shape)
print(Y_treino.shape)
print(Y_teste.shape)
(404, 2) (102, 2) (404,) (102,)
#Primeiras linhas do dataframe
X_treino.head()
LSTAT | RM | |
---|---|---|
378 | 23.69 | 6.380 |
312 | 11.72 | 6.023 |
480 | 10.74 | 6.242 |
48 | 30.81 | 5.399 |
359 | 12.67 | 6.112 |
Y_treino.head()
378 13.1 312 19.4 480 23.0 48 14.4 359 22.6 Name: MEDV, dtype: float64
Chegou a hora de aplicar o modelo preditivo¶
Treinar um modelo no python é simples se usar o Scikit-Learn. Treinar um modelo no Scikit-Learn é simples: basta criar o regressor, e chamar o método fit().
Uma observação sobre a sintaxe dos classificadores do scikit-learn
- O método
fit(X,Y)
recebe uma matriz ou dataframe X onde cada linha é uma amostra de aprendizado, e um array Y contendo as saídas esperadas do classificador, seja na forma de texto ou de inteiros - O método
predict(X)
recebe uma matriz ou dataframe X onde cada linha é uma amostra de teste, retornando um array de classes
# Importa a biblioteca
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# Cria o modelo de regressão
lin_model = LinearRegression()
# Cria o modelo de machine learning
lin_model.fit(X_treino, Y_treino)
LinearRegression()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
LinearRegression()
Pronto!! bora testar se esta funcionando....
# Para obter as previsões, basta chamar o método predict()
y_teste_predito = lin_model.predict(X_teste)
print("Predição usando regressão, retorna valores continuos: {}".format(y_teste_predito))
Predição usando regressão, retorna valores continuos: [20.26730472 30.46254362 24.8618173 32.43404913 39.86365469 23.66539013 20.77667386 29.75623863 30.44876087 18.5168171 19.63100525 18.74845677 23.7281014 27.56448461 22.52117732 18.46185252 22.38157688 26.86156941 20.6400214 19.98413697 33.09717059 19.85816854 32.53875855 32.57407067 31.32460254 20.38296853 -1.42097188 30.44060351 31.13876155 20.88665514 26.44421354 23.68892112 29.17773736 16.88751481 26.47090039 26.69899429 25.62037302 30.97988295 26.78880834 25.9065042 19.81451712 27.6467912 23.10809072 7.31666945 19.91170866 25.16501706 18.41082354 28.31491416 19.71012485 31.53337666 21.41108083 18.647106 18.79161952 21.85406815 29.78369494 28.11559716 32.00451181 19.20874148 19.60710514 25.48742742 33.17219328 37.93723556 18.13487736 19.68062523 8.94822295 19.71055637 36.87667587 16.22855265 32.12840062 8.63270666 40.77399223 18.15030821 37.36130782 35.56637856 22.41001584 20.43674263 16.88155149 20.54739459 26.7389648 24.91083941 21.49886199 18.11454897 29.67041747 15.20263957 11.35784948 36.23439232 20.13915265 16.54518163 30.86536289 13.68864763 20.81033789 31.06110293 9.95109888 23.66605564 18.38867028 21.75517654 14.85669305 25.86164138 23.90419415 33.09669227 25.29625217 20.97907392]
# Vamos avaliar os parâmetros do nosso modelo
print('(A) Intercepto: ', lin_model.intercept_)
print('(B) Inclinação: ', lin_model.coef_)
# Verificando a quantidade de coeficientes e exibindo a equação correspondente
if len(lin_model.coef_) > 1:
print(f'Nossa equação é: Y_pred = {lin_model.intercept_.round(2)} + {lin_model.coef_[0].round(2)} * X_LSTAT + {lin_model.coef_[1].round(2)} * X_RM')
else:
print(f'Nossa equação é: Y_pred = {lin_model.intercept_.round(2)} + {lin_model.coef_[0].round(2)} * X_LSTAT')
(A) Intercepto: -5.170015525018201 (B) Inclinação: [-0.57969707 5.57341034] Nossa equação é: Y_pred = -5.17 + -0.58 * X_LSTAT + 5.57 * X_RM
import matplotlib.pyplot as plt
import numpy as np
# Gráfico de dispersão para X_LSTAT vs Y_pred
plt.figure(figsize=(12, 5))
# Plot para X_LSTAT
plt.subplot(1, 2, 1)
plt.scatter(X_teste['LSTAT'], y_teste_predito, color='blue', alpha=0.6, s=50, label='Valores Preditos')
plt.plot(X_teste['LSTAT'], lin_model.intercept_ + lin_model.coef_[0] * X_teste['LSTAT'], color='red', linestyle='--', label='Linha de Regressão')
plt.xlabel('% Status de Menor Classe (X_LSTAT)')
plt.ylabel('Valor Predito (Y_pred)')
plt.title('Relação entre X_LSTAT e Y_pred')
plt.legend()
# Plot para X_RM
plt.subplot(1, 2, 2)
plt.scatter(X_teste['RM'], y_teste_predito, color='green', alpha=0.6, s=50, label='Valores Preditos')
plt.plot(X_teste['RM'], lin_model.intercept_ + lin_model.coef_[1] * X_teste['RM'], color='red', linestyle='--', label='Linha de Regressão')
plt.xlabel('Número Médio de Quartos (X_RM)')
plt.ylabel('Valor Predito (Y_pred)')
plt.title('Relação entre X_RM e Y_pred')
plt.legend()
plt.tight_layout()
plt.show()
Avaliando o modelo treinado¶
Vamos colocar alguns valores e ver a predição do classificador.
from sklearn.metrics import r2_score, mean_squared_error,mean_absolute_error
import numpy as np
print("Soma dos Erros ao Quadrado (SSE): %2.f " % np.sum((y_teste_predito - Y_teste)**2))
print("Erro Quadrático Médio (MSE): %.2f" % mean_squared_error(Y_teste, y_teste_predito))
print("Erro Médio Absoluto (MAE): %.2f" % mean_absolute_error(Y_teste, y_teste_predito))
print ("Raiz do Erro Quadrático Médio (RMSE): %.2f " % np.sqrt(mean_squared_error(Y_teste, y_teste_predito)))
print("R2-score: %.2f" % r2_score(y_teste_predito , Y_teste) )
Soma dos Erros ao Quadrado (SSE): 3400 Erro Quadrático Médio (MSE): 33.34 Erro Médio Absoluto (MAE): 3.90 Raiz do Erro Quadrático Médio (RMSE): 5.77 R2-score: 0.38
Desafio 5¶
Refaça o notebook substituindo o algoritmo de regressão linear por outro algoritmo de regressão e compare os resultados obtidos.
Sugestão de alguns algoritmos de ML para problemas de regressão:
Nome | Vantagem | Desvantagem | Exemplo sklearn |
---|---|---|---|
Regressão Linear | Fácil de entender e implementar | Pode não ser adequado para problemas mais complexos | from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(X, y) prediction = model.predict([X_teste]) |
Árvores de decisão | Fácil de entender e visualizar | Pode levar a overfitting se a árvore for muito grande | from sklearn.tree import DecisionTreeRegressor model = DecisionTreeRegressor() model.fit(X, y) prediction = model.predict([X_teste]) |
Random Forest | Mais robusto e geralmente mais preciso do que uma única árvore de decisão | Pode ser mais lento e mais difícil de ajustar | from sklearn.ensemble import RandomForestRegressor model = RandomForestRegressor(n_estimators=100) model.fit(X, y) prediction = model.predict([X_teste]) |
Support Vector Regression (SVR) | Lida bem com dados multidimensionais e não lineares | Pode ser difícil de escolher o kernel correto e ajustar os hiperparâmetros | from sklearn.svm import SVR model = SVR(kernel='rbf') model.fit(X, y) prediction = model.predict([X_teste]) |
Gradient Boosting | Preciso e lida bem com dados multidimensionais e não lineares | Pode ser mais lento e mais difícil de ajustar | from sklearn.ensemble import GradientBoostingRegressor model = GradientBoostingRegressor(n_estimators=100) model.fit(X, y) prediction = model.predict([X_teste]) |
## implemente sua sua solução....
Regressão Polinomial¶
$$ Y = A + BX + C X² \\ $$ A, B e C são constantes que determinam a posição e inclinação da curva, o 2 indica o grau do polinômio. Para cada valor de X temos um Y associado.
Em machine learning aprendemos que uma Regressão Polinomial é:
$$ Y_{predito} = \beta_o + \beta_1X + \beta_2X² \\ $$
$ \beta_o $ , $ \beta_1 $ e $ \beta_2 $ são parâmetros que determinam o peso da rede. Para cada entrada $ X $ temos um $ Y_{predito} $ aproximado predito.
Essa ideia se estende para polinômio de graus maiores:
$$ Y_{predito} = \beta_o + \beta_1X + \beta_2X² + ... + \beta_nX^n\\ $$
import operator
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
# importa feature polinomial
from sklearn.preprocessing import PolynomialFeatures
#####----------- vou gerar alguns numeros aleatórios ------------------
#gerando numeros aleatorios, apenas para este exemplo
np.random.seed(42)
x = 2 - 3 * np.random.normal(0, 1, 30)
y = x - 3 * (x ** 2) + 0.8 * (x ** 3)+ 0.2 * (x ** 4) + np.random.normal(-20, 20, 30)
# ajuste nos dados, pois estamos trabalhando com a numpy
x = x[:, np.newaxis]
y = y[:, np.newaxis]
####---------------pronto já temos os dados para treinar -------------
#----É aqui que o seu código muda ------------------------------------
# Chama a função definindo o grau do polinomio e aplica o modelo
grau_poly = 1
polynomial_features= PolynomialFeatures(degree = grau_poly)
x_poly = polynomial_features.fit_transform(x)
#----Pronto agora é tudo como era antes, com regressão linear
model = LinearRegression()
model.fit(x_poly, y)
y_poly_pred = model.predict(x_poly)
# Métrica de avaliação do modelo
print("Soma dos Erros ao Quadrado (SSE): %2.f " % np.sum((y_poly_pred - y)**2))
print("Erro Quadrático Médio (MSE): %.2f" % mean_squared_error(y,y_poly_pred))
print("Erro Médio Absoluto (MAE): %.2f" % mean_absolute_error(y, y_poly_pred))
print ("Raiz do Erro Quadrático Médio (RMSE): %.2f " % np.sqrt(mean_squared_error(y, y_poly_pred)))
print("R2-score: %.2f" % r2_score(y,y_poly_pred) )
plt.scatter(x, y, s=10)
# ordena os valores de x antes de plotar
sort_axis = operator.itemgetter(0)
sorted_zip = sorted(zip(x,y_poly_pred), key=sort_axis)
x, y_poly_pred = zip(*sorted_zip)
plt.plot(x, y_poly_pred, color='r')
plt.show()
Soma dos Erros ao Quadrado (SSE): 602124 Erro Quadrático Médio (MSE): 20070.81 Erro Médio Absoluto (MAE): 104.66 Raiz do Erro Quadrático Médio (RMSE): 141.67 R2-score: 0.55
Desafio 6¶
Faça uma função que calcula a regressão polinomial (basicamente colocar o codigo acima em uma função), agora faça um código que chama essa função alterando o grau do polinomio de 2 até 10, basicamente um loop for que chama a função criada.
Análise os resultados obtidos e determine qual o melhor grau polinomio do seu modelo.
## Implemente sua solução