Introdução
Neste laboratório, exploraremos o conceito de decomposição viés-variância e como ele se relaciona a estimadores únicos versus conjuntos bagging. Usaremos o scikit-learn para gerar e visualizar problemas de regressão artificiais e comparar o erro quadrático médio esperado de um estimador único versus um conjunto bagging de árvores de decisão.
Dicas da Máquina Virtual
Após o término da inicialização da máquina virtual, clique no canto superior esquerdo para mudar para a aba Notebook para acessar o Jupyter Notebook para praticar.
Às vezes, pode ser necessário aguardar alguns segundos para que o Jupyter Notebook termine de carregar. A validação de operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se você enfrentar problemas durante o aprendizado, sinta-se à vontade para perguntar ao Labby. Forneça feedback após a sessão e resolveremos o problema rapidamente para você.
Importação de Bibliotecas Necessárias
Primeiro, precisamos importar as bibliotecas necessárias para gerar dados, treinar modelos e visualizar os resultados.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import BaggingRegressor
from sklearn.tree import DecisionTreeRegressor
Definição dos Parâmetros
Precisamos definir os parâmetros que controlam o tamanho dos conjuntos de dados, o número de iterações e o desvio padrão do ruído.
n_repeat = 50 ## Número de iterações para o cálculo das expectativas
n_train = 50 ## Tamanho do conjunto de treinamento
n_test = 1000 ## Tamanho do conjunto de teste
noise = 0.1 ## Desvio padrão do ruído
np.random.seed(0)
Gerar Dados
Vamos gerar um problema de regressão 1D de brinquedo usando uma função conhecida e adicionando ruído aleatório aos conjuntos de treinamento e teste. Geraremos vários conjuntos de treinamento para calcular o erro quadrático médio esperado.
def f(x):
x = x.ravel()
return np.exp(-(x**2)) + 1.5 * np.exp(-((x - 2) ** 2))
def generate(n_samples, noise, n_repeat=1):
X = np.random.rand(n_samples) * 10 - 5
X = np.sort(X)
if n_repeat == 1:
y = f(X) + np.random.normal(0.0, noise, n_samples)
else:
y = np.zeros((n_samples, n_repeat))
for i in range(n_repeat):
y[:, i] = f(X) + np.random.normal(0.0, noise, n_samples)
X = X.reshape((n_samples, 1))
return X, y
X_train = []
y_train = []
for i in range(n_repeat):
X, y = generate(n_samples=n_train, noise=noise)
X_train.append(X)
y_train.append(y)
X_test, y_test = generate(n_samples=n_test, noise=noise, n_repeat=n_repeat)
Definir os Modelos para Comparação
Vamos definir dois modelos para comparação: uma única árvore de decisão e um conjunto de árvores de decisão por bagging.
estimators = [
("Tree", DecisionTreeRegressor()),
("Bagging(Tree)", BaggingRegressor(DecisionTreeRegressor())),
]
n_estimators = len(estimators)
Treinar os Modelos e Calcular o Erro Quadrático Médio Esperado
Vamos iterar sobre os estimadores, treiná-los nos múltiplos conjuntos de treinamento e calcular o erro quadrático médio esperado, decompondo-o em termos de viés, variância e ruído. Também plotaremos as previsões dos modelos e a decomposição viés-variância.
plt.figure(figsize=(10, 8))
## Iterar sobre os estimadores para comparação
for n, (name, estimator) in enumerate(estimators):
## Calcular as previsões
y_predict = np.zeros((n_test, n_repeat))
for i in range(n_repeat):
estimator.fit(X_train[i], y_train[i])
y_predict[:, i] = estimator.predict(X_test)
## Decomposição do erro quadrático médio em viés^2 + Variância + Ruído
y_error = np.zeros(n_test)
for i in range(n_repeat):
for j in range(n_repeat):
y_error += (y_test[:, j] - y_predict[:, i]) ** 2
y_error /= n_repeat * n_repeat
y_noise = np.var(y_test, axis=1)
y_bias = (f(X_test) - np.mean(y_predict, axis=1)) ** 2
y_var = np.var(y_predict, axis=1)
print(
"{0}: {1:.4f} (erro) = {2:.4f} (viés^2) "
" + {3:.4f} (var) + {4:.4f} (ruído)".format(
name, np.mean(y_error), np.mean(y_bias), np.mean(y_var), np.mean(y_noise)
)
)
## Plotar figuras
plt.subplot(2, n_estimators, n + 1)
plt.plot(X_test, f(X_test), "b", label="$f(x)$")
plt.plot(X_train[0], y_train[0], ".b", label="LS ~ $y = f(x)+ruído$")
for i in range(n_repeat):
if i == 0:
plt.plot(X_test, y_predict[:, i], "r", label=r"$\^y(x)$")
else:
plt.plot(X_test, y_predict[:, i], "r", alpha=0.05)
plt.plot(X_test, np.mean(y_predict, axis=1), "c", label=r"$\mathbb{E}_{LS} \^y(x)$")
plt.xlim([-5, 5])
plt.title(name)
if n == n_estimators - 1:
plt.legend(loc=(1.1, 0.5))
plt.subplot(2, n_estimators, n_estimators + n + 1)
plt.plot(X_test, y_error, "r", label="$erro(x)$")
plt.plot(X_test, y_bias, "b", label="$viés^2(x)$"),
plt.plot(X_test, y_var, "g", label="$variância(x)$"),
plt.plot(X_test, y_noise, "c", label="$ruído(x)$")
plt.xlim([-5, 5])
plt.ylim([0, 0.1])
if n == n_estimators - 1:
plt.legend(loc=(1.1, 0.5))
plt.subplots_adjust(right=0.75)
plt.show()
Interpretar os Resultados
Podemos observar a decomposição viés-variância do erro quadrático médio esperado para cada modelo, bem como as previsões dos modelos. Também podemos comparar o erro total dos dois modelos e seu trade-off entre viés e variância.
Resumo
Neste laboratório, exploramos o conceito de decomposição viés-variância e como ele se relaciona a estimadores únicos versus conjuntos bagging. Geramos e visualizamos problemas de regressão artificiais usando o scikit-learn, e comparamos o erro quadrático médio esperado de uma árvore de decisão única versus um conjunto bagging de árvores de decisão. Descobrimos que o trade-off entre viés e variância é melhor para o bagging, pois aumenta ligeiramente o termo de viés, mas permite uma maior redução da variância, resultando em um erro quadrático médio geral menor.