Embeddings för kategoriska variabler
Kunna ersätta one-hot med inlärda embeddings i tabelldata.
Förkunskaper
- DEmbeddings — ord som vektorerkrävs
- DFeature engineeringkrävs
Intuition
One-hot ger varje kategori en egen kolumn med en etta. Det fungerar — tills kategorierna blir många.
| Antal kategorier | One-hot | Embedding (dim 8) |
|---|---|---|
| 5 | 5 kolumner | 40 tal — sämre affär |
| 100 | 100 kolumner | 800 tal |
| 50 000 postnummer | 50 000 kolumner | 400 000 tal, men täta |
Det verkliga problemet med one-hot är inte antalet kolumner utan att alla kategorier är lika olika varandra. Postnummer 11122 och 11123 är grannar i verkligheten men lika olika som 11122 och 98765 i one-hot-rummet.
En embedding är en inlärd tabell: varje kategori får en vektor med några tal, och de talen justeras under träningen. Kategorier som beter sig lika hamnar nära varandra — utan att någon talat om att de hör ihop.
Det är samma idé som ordembeddingar, tillämpad på tabelldata.
Formellt
Dimensionen. En tumregel från fast.ai som fungerar förvånansvärt bra:
| Kategorier | Dimension |
|---|---|
| 10 | 6 |
| 100 | 22 |
| 1 000 | 79 |
| 50 000 | 600 |
Den exakta formeln spelar mindre roll än storleksordningen — och att dimensionen är mycket mindre än antalet kategorier.
En embedding-tabell är en uppslagning, inte en matrismultiplikation. Matematiskt är det one-hot gånger en viktmatris, men implementationen slår upp raden direkt — vilket är det som gör det praktiskt möjligt med 50 000 kategorier.
Tre saker som måste hanteras:
| Problem | Lösning |
|---|---|
| Okänd kategori i produktion | reservera index 0 för <okänd> |
| Sällsynta kategorier | slå ihop allt under en tröskel till <sällsynt> |
| Läckage | bygg kategoriordningen på träningsdatan, inte på allt |
När lönar det sig?
| Läge | Välj |
|---|---|
| Få kategorier, linjär modell | one-hot |
| Träd eller boosting | one-hot eller ordinal — träd hanterar det ändå |
| Många kategorier + neuralt nät | embedding |
| Många kategorier, ingen djupinlärning | target encoding med out-of-fold |
Det oväntade utbytet är att embeddingarna blir tolkbara. Tränar du på butiksförsäljning och projicerar butiks-embeddingarna till två dimensioner grupperar sig ofta butiker av samma typ eller i samma region — trots att du aldrig matade in den informationen. Det är en bra kontroll av att modellen lärt sig något verkligt.
Kod
import torch, torch.nn as nn
KATEGORIER = {"butik": 1100, "produkt": 4200, "veckodag": 7}
def dim_for(n):
return min(600, int(round(1.6 * n ** 0.56)))
for namn, n in KATEGORIER.items():
print(f"{namn:<9} {n:>5} kategorier → dimension {dim_for(n)}")
# butik 1100 kategorier → dimension 79
# produkt 4200 kategorier → dimension 168
# veckodag 7 kategorier → dimension 5
class TabellNat(nn.Module):
def __init__(self, kategorier: dict[str, int], n_numeriska: int):
super().__init__()
# +1 för index 0 = okänd kategori
self.emb = nn.ModuleDict({k: nn.Embedding(n + 1, dim_for(n))
for k, n in kategorier.items()})
bredd = sum(e.embedding_dim for e in self.emb.values()) + n_numeriska
self.huvud = nn.Sequential(nn.Linear(bredd, 128), nn.ReLU(),
nn.Dropout(0.2), nn.Linear(128, 1))
def forward(self, kat: dict[str, torch.Tensor], num: torch.Tensor):
delar = [self.emb[k](v) for k, v in kat.items()] + [num]
return self.huvud(torch.cat(delar, dim=1)).squeeze(-1)
m = TabellNat(KATEGORIER, n_numeriska=4)
print(sum(p.numel() for p in m.parameters()), "parametrar")
# Efter träning: liknar sig butiker med samma profil?
E = m.emb["butik"].weight.detach()
E = E / E.norm(dim=1, keepdim=True)
likhet = E @ E.t()
likhet.fill_diagonal_(-1)
print("butik 5 liknar mest:", int(likhet[5].argmax()))
# Okänd kategori i produktion mappas till 0 — kraschar inte
index = {"Malmö": 1, "Lund": 2}
print(index.get("Kiruna", 0)) # 0 = <okänd>
Behärskning innebär
- Förklarar när embeddings slår one-hot
- Väljer dimension för en embedding
- Tolkar en inlärd embedding
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- arXiv — Entity Embeddings of Categorical Variables — arXiv (öppen åtkomst; licens per artikel)
- PyTorch — tutorials (BSD-3) — BSD-3-Clause
- Dive into Deep Learning (CC BY-SA 4.0) — CC BY-SA 4.0