Machine learning do zero · Aula 3.4 · 17 de 33 aulas prontas · 12 min
Regularização: ridge e lasso
Em resumo
Penalizar pesos grandes acalma o modelo. O ridge encolhe todos os pesos, o lasso os zera um a um: com 3 de 10 atributos o R² de teste cai de 0,470 para 0,386.
Aquecimento Duas perguntas de aulas anteriores. Um minuto, e a aula nunca fica bloqueada.
Na aula 3.1 um modelo com muitos parâmetros aprendeu o ruído: o erro de treino caía e o de teste subia. Em vez de tirar atributos do modelo, você pode cobrar por eles. A regularização é uma penalidade somada ao erro de treino: quanto maiores os pesos, maior a penalidade. Um número α define a força dela. Hoje você vai testar duas penalidades em 10 atributos de pacientes com diabetes e ver que uma delas escolhe os atributos sozinha.
Escolha uma resposta e o seu grau de certeza.
Responda para liberar o experimento.
Experimento interativo
Este experimento interativo funciona em um navegador com JavaScript ativado. O texto ao redor descreve o que ele mostra.
Ver a resposta
Resposta certa: O IMC, índice de massa corporal, porque sozinho carrega mais informação
O último é o IMC: com α = 39,8 ele é o único atributo com peso. s2 é o primeiro a sumir de vez (a partir de α = 0,398). Com 3 atributos (IMC, pressão arterial e s5) o R² de teste cai de 0,470 para 0,386.
Experimente
- Aumente α no lasso a partir do menor valor. Em que ordem as linhas dos pesos chegam a zero? Quantos atributos ficam com α = 15,8 e quanto isso custa no teste?
- Observe s3. Quando ele toca o zero pela primeira vez e o que acontece depois?
- Mude para ridge e leve α até o fim. Algum peso fica exatamente zero?
Depois de conferir no lab, vamos dar nome ao que você vê.
A ideia em palavras simples
A regressão linear comum escolhe os pesos que deixam o erro nos dados de treino o menor possível. A regularização soma a esse erro uma penalidade pelo tamanho dos pesos. Agora o modelo precisa escolher: ajustar melhor os dados ou ter pesos menores. Um atributo só mantém um peso grande se realmente melhora as previsões.
O ridge penaliza a soma dos quadrados dos pesos. Ele puxa todos os pesos para zero, mas não zera nenhum: no lab, com α = 398, o R² de teste cai para 0,405 e os 10 atributos ainda têm peso. O lasso penaliza a soma dos valores absolutos dos pesos e os zera um a um. Com α = 15,8 ficam três atributos: IMC, pressão arterial e s5. Sem penalidade o R² de teste é 0,470; com três atributos, 0,386. O modelo é mais simples e mais barato de usar, mas prevê pior.
Os atributos estão em desvios-padrão. Isso importa: a penalidade mede o tamanho dos pesos, então um atributo medido em unidades grandes teria um peso pequeno e escaparia da penalidade.
Jak to policzono
- Dados: Diabetes, Efron et al. (2004), Annals of Statistics. 442 pacientes, 10 atributos medidos no início do estudo, alvo: avanço da doença um ano depois. Os autores não indicaram licença; o scikit-learn redistribui o conjunto (licença BSD-3-Clause).
- Divisão aleatória: 309 pacientes para aprender, 133 para o teste. Médias e desvios dos atributos calculados só na parte de treino.
- Lasso: 11 valores de α de 0,01 a 100. Ridge: 11 valores de α de 0,1 a 100000 (no scikit-learn o ridge conta a penalidade em outra escala que o lasso, daí os números diferentes).
- R² de teste: que parte da variação do resultado dos pacientes de teste o modelo explica (1 = toda, 0 = o mesmo que a média).
Por que funciona
A penalidade do lasso cresce de forma linear: reduzir um peso em uma unidade sempre economiza a mesma penalidade, seja o peso grande ou pequeno. Se um atributo melhora o ajuste menos do que a penalidade custa, vale a pena dar a ele peso exatamente zero. A penalidade do ridge cresce com o quadrado do peso, então pesos pequenos quase não custam nada. O ridge encolhe o peso cada vez mais, mas nunca precisa zerá-lo.
A penalidade também acalma os pesos de atributos que dizem quase a mesma coisa. s1 e s2 são dois resultados de exames muito ligados entre si (no código abaixo a correlação deles é 0,90). Com a penalidade lasso mais fraca, s1 tem peso −39,0 e s2 +26,2: dois números grandes de sinais opostos que quase se anulam. Pesos assim mudam muito de uma amostra para outra. A penalidade os recolhe, e logo o lasso remove s2 de vez, porque s1 carrega quase a mesma informação.
Não é acaso de uma única divisão. No código abaixo o mesmo experimento roda em 50 divisões aleatórias: o IMC fica por último em 37 delas, s5 nas outras 13. O atributo que some primeiro muda ainda mais de uma divisão para outra: a idade em 21 divisões, s2 em 16.
Erro comum
Verdadeiro ou falso: "o lasso deu peso zero a um atributo, então ele não importa". Falso. s2 some primeiro, embora esteja fortemente ligado a s1: o lasso escolhe um atributo do par, e o outro recebe zero porque não traz nada de novo ao lado de s1. Sem s1, s2 poderia ser importante. O lab mostra isso também com s3: com α = 0,158 o peso dele é zero e com α = 0,398 ele volta, porque está ligado a s4 (r = −0,73) e o lasso passa o peso de um para o outro.
A escolha entre atributos correlacionados pode ser questão de sorte. No código abaixo o primeiro a sumir é às vezes s2, às vezes a idade, e até o último a ficar é às vezes s5 em vez do IMC, conforme os pacientes que caem na parte de treino. Um zero no lasso quer dizer: "este modelo não precisou deste atributo, com estes dados e este α". Não quer dizer: "este atributo não tem nada a ver com a doença".
Quiz
Três perguntas para você se testar. Escolha uma resposta ou abra a resposta embaixo de cada pergunta. No teclado: as setas escolhem, Enter ou Espaço confirma.
Pontos-chave
- A regularização soma ao erro uma penalidade por pesos grandes; α define a força dela.
- O ridge encolhe todos os pesos e não zera nenhum; o lasso os zera um a um e escolhe atributos.
- Um modelo mais simples costuma custar um pouco de acurácia: α é escolhido na validação, não no teste.
- Um peso zero no lasso não significa que o atributo não importa: de um par de atributos correlacionados o lasso muitas vezes mantém um.
Mais a fundo: fórmula, código, prática
A matemática
Ridge: min_w ‖y − Xw‖² + α · Σⱼ wⱼ². Lasso: min_w (1/(2n)) · ‖y − Xw‖² + α · Σⱼ |wⱼ|. É assim que o scikit-learn calcula, por isso os mesmos números de α significam coisas diferentes nas duas penalidades.
Por que o lasso zera: com atributos de média 0 e variância 1, um peso depois de uma atualização vale wⱼ = sign(ρⱼ) · max(|ρⱼ| − α, 0), onde ρⱼ = (1/n) · xⱼᵀ rⱼ é a ligação do atributo com o erro que resta sem ele. Quando |ρⱼ| ≤ α, o peso é exatamente zero. O ridge dá wⱼ = ρⱼ / (1 + α/n) com as mesmas hipóteses: o peso encolhe, mas só é zero quando ρⱼ = 0.
R²: R² = 1 − Σ(yᵢ − ŷᵢ)² / Σ(yᵢ − ȳ)², calculado nos pacientes de teste.
No código (Python)
O mesmo experimento no scikit-learn em 50 divisões aleatórias (70% para aprender, 30% para o teste): qual atributo o lasso mantém por último, qual tira primeiro e quanto custa ficar com no máximo 3 atributos.
import numpy as np
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression, Lasso, lars_path
from sklearn.metrics import r2_score
X, y = load_diabetes(return_X_y=True)
names = np.array(["age", "sex", "bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6"])
alphas = 10 ** np.linspace(-2, 2, 41) # a faixa de penalidades do lab, mais fina
last, first, r2 = [], [], []
for s in range(50):
tr, te = train_test_split(np.arange(len(y)), test_size=0.3, random_state=s)
sc = StandardScaler().fit(X[tr])
Xtr, Xte = sc.transform(X[tr]), sc.transform(X[te])
path_alphas, _, coefs = lars_path(Xtr, y[tr] - y[tr].mean(), method="lasso") # o caminho exato do lasso, da penalidade mais forte para baixo
enters = [np.flatnonzero(coefs[j])[0] if coefs[j].any() else len(path_alphas) for j in range(10)] # onde cada atributo recebe peso pela primeira vez
last.append(names[np.argmin(enters)]); first.append(names[np.argmax(enters)]) # o primeiro a receber peso = o último a perdê-lo quando a penalidade cresce
W = np.array([Lasso(alpha=a, max_iter=100000).fit(Xtr, y[tr]).coef_ for a in alphas])
k3 = min(k for k in range(len(alphas)) if (W[k] != 0).sum() <= 3) # a penalidade mais fraca que deixa no máximo 3 atributos
m3 = Lasso(alpha=alphas[k3], max_iter=100000).fit(Xtr, y[tr])
r2.append((r2_score(y[te], LinearRegression().fit(Xtr, y[tr]).predict(Xte)), r2_score(y[te], m3.predict(Xte))))
r2 = np.array(r2)
print(f"last feature lasso keeps, 50 splits: {', '.join(f'{n} {last.count(n)}' for n in sorted(set(last), key=lambda n: (-last.count(n), n)))}")
print(f"first feature it drops for good: {', '.join(f'{n} {first.count(n)}' for n in sorted(set(first), key=lambda n: (-first.count(n), n)))}")
print(f"test R2: no penalty {r2[:, 0].mean():.3f}, at most 3 features {r2[:, 1].mean():.3f}; lower with 3 in {(r2[:, 1] < r2[:, 0]).sum()} of 50")
print(f"correlation of s1 and s2: {np.corrcoef(X[:, 4], X[:, 5])[0, 1]:.2f}")
Saída
last feature lasso keeps, 50 splits: bmi 37, s5 13
first feature it drops for good: age 21, s2 16, s4 9, s1 2, s6 2
test R2: no penalty 0.471, at most 3 features 0.379; lower with 3 in 50 of 50
correlation of s1 and s2: 0.90
As divisões são diferentes das do lab, então os números também. O quadro é o mesmo: o IMC fica por último em 37 divisões de 50 (s5 nas outras 13), e ficar com no máximo 3 atributos baixa o R² de teste em todas as divisões, em média de 0,471 para 0,379. O primeiro a sumir é às vezes s2, às vezes a idade: o lasso escolhe entre atributos ligados, e a escolha depende de quais pacientes caíram na parte de treino.
Baixar o notebook (.ipynb). Abra no Jupyter ou envie para o Google Colab; ele baixa sozinho os dados que faltarem.
Para quem pratica
- No scikit-learn:
Ridge,LassoeElasticNet(uma mistura das duas penalidades, útil com grupos de atributos correlacionados). Coloque a padronização em um pipeline (make_pipeline(StandardScaler(), Lasso())), para que médias e desvios sejam calculados só no treino. - Escolha a força da penalidade por validação cruzada:
RidgeCVeLassoCVfazem isso de uma vez. Não escolha α pelo resultado do teste: isso é ajustar no conjunto de teste (aula 3.5). - A regressão logística do scikit-learn tem penalidade ridge por padrão (
penalty="l2"); a força é definida porC, o inverso de α: umCmenor é uma penalidade mais forte. - Se quiser falar da importância dos atributos, não leia só os zeros do lasso. Confira a estabilidade da escolha em muitas divisões ou use os métodos do módulo 6.
Isto foi útil?
Obrigado! Isso nos ajuda a melhorar.
Vá mais fundo no Atlas
Verbetes da enciclopédia sobre as ideias desta aula: