ML Atlas

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.
Da aula 3.3: Curvas de aprendizado: mais dados ou um modelo melhor? O treino dá 100%, a validação 82,3%, e a curva de validação ainda sobe. O que deve ajudar mais?

Ver a resposta

Resposta certa: Mais dados de treino, porque uma lacuna grande indica que o modelo decora

Lacuna grande e validação subindo significam variância alta. Com todo o treino o mesmo modelo chega a 95,8% na validação, e a lacuna cai para 2,2 pontos.

Da aula 3.2: Validação e validação cruzada O modelo A tem acurácia média na validação cruzada um pouco acima da do modelo B, mas a dispersão entre os folds é muito maior que a diferença. O que se conclui?

Ver a resposta

Resposta certa: Que os dados não mostram diferença confiável entre os dois modelos

Quando a diferença entre as médias é menor que a variação entre os folds, ela pode facilmente ser obra do acaso.

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.

Preveja, depois confira Uma regressão linear prevê quanto o diabetes avançou depois de um ano a partir de 10 atributos: idade, sexo, IMC, pressão arterial e seis resultados de exames de sangue (s1–s6). O lasso é uma penalidade que, conforme α cresce, zera os pesos dos atributos um a um. Qual atributo fica por último?
Qual é o seu grau de certeza?

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.

1. Em que a penalidade ridge difere da penalidade lasso?
Ver a resposta

Resposta certa: O ridge puxa todos os pesos para zero e o lasso deixa alguns exatamente em zero

O ridge penaliza os quadrados dos pesos e não zera nenhum; o lasso penaliza os valores absolutos e zera os pesos um a um. No lab o ridge com α = 398 tem 10 pesos diferentes de zero, o lasso com α = 15,8 só 3.

2. No lab o lasso mantém 3 atributos e o R² de teste cai de 0,470 para 0,386. O que isso significa?
Ver a resposta

Resposta certa: Que um modelo mais simples custa um pouco de acurácia: é preciso pesar os dois

Menos atributos deixam o modelo mais fácil de usar e explicar, mas aqui ele explica uma parte menor da variação do resultado. A força da penalidade é escolhida na validação, pesando simplicidade e acurácia.

3. Verdadeiro ou falso: "o lasso deu peso zero a um atributo, então ele não tem relação com a doença".
Ver a resposta

Resposta certa: Falso, porque de um par de atributos correlacionados o lasso muitas vezes mantém um

s2 some primeiro, embora esteja fortemente ligado a s1. Não traz nada de novo ao lado de s1, então o lasso dá zero a ele. Em 50 divisões o primeiro a sumir é às vezes s2, às vezes a idade.

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.

Para quem pratica
  • No scikit-learn: Ridge, Lasso e ElasticNet (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: RidgeCV e LassoCV fazem 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 por C, o inverso de α: um C menor é 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.

Vá mais fundo no Atlas

Verbetes da enciclopédia sobre as ideias desta aula: