Existem duas grandes causas de erro em modelos de machine learning:
- Viés descreve um modelo que faz suposições simplificadas para tornar a função alvo mais fácil de aproximar; por exemplo, um modelo pode “aprender” que todo homem de 1,75 m no mundo veste camiseta tamanho M — claramente enviesado.
- Variância descreve a variabilidade na previsão do modelo; isto é, o quanto a predição muda quando alteramos os dados usados no treinamento.
Para chegar a uma solução mais precisa, buscamos reduzir a quantidade de viés e de variância presentes no modelo. Isso não é trivial. Viés e variância estão em conflito — reduzir um tende a aumentar o outro, por causa do conceito conhecido como trade-off viés-variância.
Neste artigo, você vai aprender:
- Como detectar se um modelo sofre de alto viés ou alta variância
- Como diagnosticar um modelo com qualquer um desses sintomas
- Como construir um modelo com bom ajuste
Antes de falarmos sobre como detectar os sintomas de erro, vamos aprofundar o trade-off viés-variância.
Um olhar sobre o trade-off viés-variância
Todos os algoritmos de aprendizado supervisionado buscam o mesmo objetivo: estimar a função de mapeamento (f_hat) para uma variável alvo (y) dada uma entrada de dados (X). Chamamos de função alvo a função que um modelo de machine learning tenta aproximar.
Alterar os dados de entrada usados para aproximar a variável alvo provavelmente resultará em uma função alvo diferente, o que pode impactar as saídas previstas pelo modelo. O quanto nossa função alvo varia à medida que os dados de treino mudam é conhecido como variância. Não queremos que o modelo tenha alta variância, porque, embora o algoritmo possa ter um desempenho impecável durante o treinamento, ele falha em generalizar para instâncias não vistas.

Na imagem acima, a função alvo aproximada é a linha verde e a linha de melhor ajuste é a preta. Repare como o modelo “aprende” muito bem os dados de treino com a linha verde. Ele faz o possível para separar todas as observações vermelhas e azuis. Se treinássemos esse modelo com novas observações, ele aprenderia uma função alvo inteiramente nova e tentaria repetir o mesmo comportamento.
Considere um cenário em que usamos um método linear, como regressão linear, para aproximar a função alvo. A primeira coisa a notar é que a regressão linear assume um relacionamento linear entre os dados de entrada e o alvo que queremos prever. Eventos do mundo real são bem mais complexos. Ao custo de alguma flexibilidade, essa suposição simples torna a função alvo muito mais rápida de aprender e mais fácil de interpretar. Chamamos esse paradigma de viés.

Na imagem acima, a linha vermelha representa a função alvo aprendida. Muitas observações ficam bem distantes dos valores previstos pelo modelo.
Podemos reduzir o viés de um modelo tornando-o mais flexível, mas isso introduz variância. Por outro lado, podemos reduzir a variância simplificando o modelo, mas isso introduz viés. Não há como escapar dessa relação. A melhor alternativa é escolher e configurar um modelo de modo que ele encontre um equilíbrio entre viés e variância.

Devido a fatores desconhecidos que influenciam a função alvo, sempre haverá algum erro presente no modelo, conhecido como erro irredutível. Isso pode ser observado na imagem acima ao notar a quantidade de erro que ocorre no ponto mais baixo do gráfico de erro total. Para construir o modelo ideal, precisamos encontrar um equilíbrio entre viés e variância de forma que o erro total seja minimizado. Isso é ilustrado pela linha pontilhada chamada complexidade ótima do modelo.
Vamos aprofundar viés e variância usando curvas de aprendizado.
A anatomia de uma curva de aprendizado
Curvas de aprendizado são gráficos usados para mostrar o desempenho de um modelo à medida que o tamanho do conjunto de treinamento aumenta. Outra forma de uso é mostrar o desempenho do modelo ao longo de um período de tempo definido. Normalmente, usamos essas curvas para diagnosticar algoritmos que aprendem incrementalmente a partir dos dados. Elas funcionam avaliando um modelo nos conjuntos de treinamento e validação e, em seguida, plotando o desempenho medido.
Por exemplo, imagine que modelamos a relação entre algumas entradas e saídas usando um algoritmo de machine learning. Começamos treinando o modelo em uma única instância e validando contra cem instâncias. O que você acha que vai acontecer? Se você disse que o modelo aprenderá perfeitamente os dados de treinamento, está certo — não haveria erros.
Não é difícil modelar a relação de uma única entrada para uma saída; basta “memorizar” essa relação. A parte difícil é fazer previsões precisas quando surgirem novas instâncias, nunca vistas antes. Como nosso modelo aprendeu tão bem os dados de treino, ele teria muita dificuldade para generalizar para dados desconhecidos. O desempenho no conjunto de validação seria ruim. Isso significa que haveria uma grande diferença entre o desempenho do modelo nos dados de treinamento e nos de validação. Chamamos essa diferença de erro de generalização.
Para que nosso algoritmo tenha chance de fazer previsões melhores no conjunto de validação, precisamos adicionar mais dados. A introdução de novas instâncias no treinamento inevitavelmente muda a função alvo do modelo. O desempenho do modelo à medida que ampliamos o conjunto de treinamento pode ser monitorado e plotado para revelar a evolução dos erros de treinamento e de validação.
Isso significa que o gráfico exibirá dois resultados diferentes:
- Curva de treinamento: calculada a partir dos dados de treino; indica o quanto o modelo está aprendendo.
- Curva de validação: calculada a partir dos dados de validação; indica o quanto o modelo está generalizando para instâncias não vistas.
Essas curvas mostram como o modelo evolui conforme os dados crescem — por isso o nome curvas de aprendizado.
Observação: o mesmo processo pode ser usado para mostrar como o modelo aprende ao longo do tempo. Em vez de monitorar o desempenho conforme o volume de dados aumenta, monitoramos o aprendizado com o passar do tempo. Por exemplo, você pode decidir aprender um novo idioma. Seu domínio poderia ser avaliado e receber uma pontuação numérica para mostrar sua evolução ao longo de 52 semanas.
Agora que você conhece a anatomia de uma curva de aprendizado, vamos colocá-la em prática com um dataset do mundo real para uma compreensão visual.
Caso de uso: prevendo avaliações imobiliárias
Vamos usar o conjunto de dados: market historical data set of real estate valuation. Esses dados foram coletados em Sindian Dist., New Taipei, Taiwan, e consistem em histórico de mercado.
Nosso objetivo é prever a avaliação imobiliária dadas as seguintes variáveis:
- X1 = data da transação (por exemplo, 2013.250 = março de 2013, 2013.500 = junho de 2013, etc.)
- X2 = idade do imóvel (unidade: ano)
- X3 = distância até a estação de MRT mais próxima (unidade: metro)
- X4 = número de lojas de conveniência a uma distância caminhável (inteiro)
- X5 = coordenada geográfica, latitude (unidade: grau)
- X6 = coordenada geográfica, longitude (unidade: grau)
A variável alvo é definida como:
- Y = preço do imóvel por unidade de área (10.000 Novo Dólar de Taiwan/Ping; Ping é uma unidade local, 1 Ping = 3,3 metros quadrados)
Como a variável que estamos prevendo é contínua, o problema requer técnicas de regressão.
Vamos começar dando uma olhada nos dados:
import pandas as pd
data = pd.read_excel("/content/gdrive/MyDrive/real_estate_valuation_data.xlsx")
print(data.info())
data.head()
>>>>
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 414 entries, 0 to 413
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 No 414 non-null int64
1 X1 transaction date 414 non-null float64
2 X2 house age 414 non-null float64
3 X3 distance to the nearest MRT station 414 non-null float64
4 X4 number of convenience stores 414 non-null int64
5 X5 latitude 414 non-null float64
6 X6 longitude 414 non-null float64
7 Y house price of unit area 414 non-null float64
dtypes: float64(6), int64(2)
memory usage: 26.0 KB
None
No X1 transaction date X2 house age X3 distance to the nearest MRT station X4 number of convenience stores X5 latitude X6 longitude Y house price of unit area
0 1 2012.916667 32.0 84.87882 10 24.98298 121.54024 37.9
1 2 2012.916667 19.5 306.59470 9 24.98034 121.53951 42.2
2 3 2013.583333 13.3 561.98450 5 24.98746 121.54391 47.3
3 4 2013.500000 13.3 561.98450 5 24.98746 121.54391 54.8
4 5 2012.833333 5.0 390.56840 5 24.97937 121.54245 43.1
Há um atributo extra chamado No que não foi mencionado na documentação dos dados. É possível que seja um índice, mas, para simplificar, vamos removê-lo. Além disso, os nomes dos atributos não refletem exatamente os da documentação, então vamos fazer uma limpeza.
# renomear as colunas
renamed_columns = [col.split()[0] for col in data.columns]
renamed_columns_map = {data.columns[i]:renamed_columns[i] for i in range(len(data.columns))}
data.rename(renamed_columns_map, axis=1, inplace=True)
# remover a coluna No
data.drop("No", axis=1, inplace=True)
print(data.head())
# separar atributos e alvo
features, target = data.columns[:-1], data.columns[-1]
X = data[features]
y = data[target]
É assim que o dataset final fica antes de separarmos atributos e rótulos do alvo:
X1 X2 X3 X4 X5 X6 Y
0 2012.916667 32.0 84.87882 10 24.98298 121.54024 37.9
1 2012.916667 19.5 306.59470 9 24.98034 121.53951 42.2
2 2013.583333 13.3 561.98450 5 24.98746 121.54391 47.3
3 2013.500000 13.3 561.98450 5 24.98746 121.54391 54.8
4 2012.833333 5.0 390.56840 5 24.97937 121.54245 43.1
Para demonstrar viés, variância e bom ajuste, vamos construir três modelos: um regressor de árvore de decisão, uma máquina de vetores de suporte para regressão e um regressor de random forest. Depois de treinar os modelos, vamos plotar as curvas de aprendizado de cada um e compartilhar algumas técnicas de diagnóstico.
Diagnosticando curvas de aprendizado
Curvas de aprendizado são interpretadas avaliando seu formato. Depois de entender a forma e a dinâmica, podemos usá-las para diagnosticar eventuais problemas no comportamento de um modelo de machine learning.
A função learning_curve() do Scikit-learn facilita monitorar as pontuações de treinamento e validação, que é o que precisamos para plotar uma curva de aprendizado.
Os parâmetros que passamos para a função learning_curve() são:
- estimator: o modelo usado para aproximar a função alvo
- X: os dados de entrada
- y: o alvo
- cv: a estratégia de divisão para validação cruzada
- scoring: a métrica usada para avaliar o desempenho do modelo
- train_sizes: o número absoluto de exemplos de treino usados para gerar a curva de aprendizado; aqui os valores são arbitrários.
Modelo 1: decision tree regressor
Um modelo com alta variância é considerado overfit. Ele aprende muito bem os dados de treino e o ruído aleatório, resultando em ótimo desempenho no treinamento, mas falhando em generalizar para instâncias não vistas. Observamos esse comportamento quando o algoritmo usado é flexível demais para o problema ou quando o treinamento é excessivo.
Por exemplo, o regressor de árvore de decisão é um algoritmo não linear. Algoritmos não lineares tipicamente têm baixo viés e alta variância. Isso sugere que mudanças no dataset causarão grandes variações na função alvo.
Vamos demonstrar alta variância com nosso decision tree regressor:
from sklearn.model_selection import learning_curve
from sklearn.tree import DecisionTreeRegressor
import matplotlib.pyplot as plt
# overfitting
decision_tree = DecisionTreeRegressor()
train_sizes, train_scores, test_scores = learning_curve(
estimator=decision_tree,
X=X,
y=y,
cv=5,
scoring="neg_root_mean_squared_error",
train_sizes = [1, 75, 165, 270, 331]
)
train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)
plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")
plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")
plt.show()

O modelo comete poucos erros quando precisa prever instâncias vistas no treinamento, mas vai muito mal em novas instâncias às quais não foi exposto. Você pode observar isso notando o grande erro de generalização entre a curva de treinamento e a de validação. Uma forma de melhorar é adicionar mais instâncias ao conjunto de treino, o que introduz viés. Outra solução é adicionar regularização ao modelo (por exemplo, restringindo a profundidade máxima da árvore).
Modelo 2: Support Vector Machine
Um modelo com alto viés é considerado underfit. Ele faz suposições simplistas sobre os dados de treinamento, o que dificulta aprender os padrões subjacentes. O resultado é um modelo com alto erro tanto no treino quanto na validação. Isso ocorre quando o modelo é simples demais para o problema ou quando ele não é treinado por tempo suficiente.
Por exemplo, a Support Vector Machine é um algoritmo linear. Algoritmos lineares tendem a ter alto viés e baixa variância. Isso sugere que são feitas mais suposições sobre a forma da função alvo. Para introduzir mais viés no nosso modelo, adicionamos regularização definindo o parâmetro C.
Vamos demonstrar alto viés com nossa support vector machine:
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
# Underfitting
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
svm = SVR(C=0.25)
train_sizes, train_scores, test_scores = learning_curve(
estimator=svm,
X=X_scaled,
y=y,
cv=5,
scoring="neg_root_mean_squared_error",
train_sizes = [1, 75, 150, 270, 331]
)
train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)
plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")
plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")
plt.show()

O gap de generalização entre as curvas de treinamento e validação fica extremamente pequeno conforme o tamanho do conjunto de treino aumenta. Isso indica que adicionar mais exemplos não vai melhorar o desempenho. Uma solução pode ser criar mais features ou tornar o modelo mais flexível para reduzir o número de suposições.
Modelo 3: Random Forest Regressor
Um modelo com bom ajuste fica no meio-termo entre underfitting e overfitting. Ele pode não ser tão bom nos dados de treino quanto no caso de overfitting, mas comete bem menos erros diante de instâncias não vistas. Esse comportamento pode ser observado quando o erro de treinamento aumenta até um ponto de estabilidade, enquanto o erro de validação diminui até se estabilizar.
Para demonstrar isso, vamos usar uma random forest, que é um ensemble de árvores de decisão. Isso significa que o modelo também é não linear, mas viés é adicionado ao criar vários modelos diversos e combinar suas previsões.
Também adicionamos mais regularização definindo max_depth, que controla a profundidade máxima de cada árvore, com valor três.
Veja como isso fica no código:
from sklearn.ensemble import RandomForestRegressor
# melhor
random_forest = RandomForestRegressor(max_depth=3)
train_sizes, train_scores, test_scores = learning_curve(
estimator=random_forest,
X=X,
y=y,
cv=5,
scoring="neg_root_mean_squared_error",
train_sizes = [1, 75, 150, 270, 331]
)
train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)
plt.subplots(figsize=(10,8))
plt.plot(train_sizes, train_mean, label="train")
plt.plot(train_sizes, test_mean, label="validation")
plt.title("Learning Curve")
plt.xlabel("Training Set Size")
plt.ylabel("RMSE")
plt.legend(loc="best")
plt.show()

Agora dá para ver que reduzimos o erro nos dados de validação. Isso veio ao custo de um desempenho um pouco pior no treinamento, mas, no geral, é um modelo melhor.
O erro de generalização é bem menor, com poucas falhas. Além disso, ambas as curvas ficam estáveis a partir de um tamanho de treino em torno de 250, o que sugere que adicionar mais instâncias talvez não traga grandes ganhos.
Em resumo, o comportamento de um modelo pode ser observado usando curvas de aprendizado. O cenário ideal ao construir modelos de machine learning é manter o erro o mais baixo possível. Dois fatores que elevam o erro são o viés e a variância — encontrar o equilíbrio entre ambos leva a um modelo com melhor desempenho.



