- Je ordent HWC-patches, past een gedeelde projectie toe en voegt CLS en posities toe.
- Je bewaakt pixelvolgorde, kanaalvolgorde, aansluitende vormen en onafhankelijke uitvoer.
Een taalmodel krijgt tokens, maar een foto is een rooster met kleurwaarden. Hoe kun je dezelfde transformerbouwstenen toch op beelden toepassen? Je maakt eerst kleine beeldtegels, zet iedere tegel om naar een vector en bewaart waar die tegel zat.
Denk aan een foto die je in vier kaarten knipt. Iedere kaart houdt zijn eigen pixels; de transformer kan de kaarten later met elkaar vergelijken. Alleen knippen leert nog niets: de gedeelde projectie en de latere lagen moeten tijdens echte training bruikbare patronen leren.
In deze les kies je de gewichten zelf om iedere stap te kunnen narekenen. Het voorbeeld is geen getrainde beeldherkenner. Je bouwt de invoer van een Vision Transformer (ViT); de attention en de uiteindelijke voorspelling volgen in de volgende les.
1. Van rooster naar geordende tegels
Onze invoer heeft drie assen: hoogte H, breedte W en kleurkanalen C. Een grijsbeeld heeft hier expliciet één kanaal; een RGB-beeld heeft er drie. Een vierkante patch heeft zijde P en bevat dus P×P×C getallen.
N is het aantal patches. Voor een beeld van 4 bij 4 met patches van 2 bij 2 krijg je vier patches, met vier grijswaarden per patch. Hoogte en breedte moeten in ons lescontract deelbaar zijn door de patchmaat; we vullen de rand niet ongemerkt met nullen aan.
image = np.arange(16).reshape(4,4,1)
patches = patchify(image,2)
print(patches[0].tolist())
# [0.0, 1.0, 4.0, 5.0]
Lees binnen een patch eerst de bovenste pixelrij, dan de volgende. Bij kleur komen de kanalen van dezelfde pixel direct achter elkaar. Lees de patches zelf van links naar rechts en vervolgens van boven naar beneden; een andere volgorde verwisselt de betekenis van de latere posities.
print(patches.shape)
print(patches[1].tolist())
# (4, 4)
# [2.0, 3.0, 6.0, 7.0]
Maak patchify met deze volgorde en een onafhankelijke uitvoer. Alleen image.reshape(4,4) gebruiken is een veelgemaakte fout: de eerste rij bevat dan een hele beeldrij in plaats van de linker bovenpatch. Controleer daarom de waarden, niet alleen de vorm.
Verander de patchmaat en kies een patch. Volg dezelfde pixels van het rooster naar één rij en zijn projectie.
[0, 1, 4, 5]
De vaste projectie telt afwisselende kenmerken op; bias [0, 1]. Uitvoer: [4, 7].
- Patches
- 4
- Met CLS
- 5
- Getallen per patch
- 4
Handgekozen rekenvoorbeelden. De getallen komen uit de eigen NumPy-functies; er is geen getrainde beeld- of tekstencoder.
2. Geef iedere patch dezelfde projectie
De transformer verwacht steeds dezelfde vectorbreedte D. Vermenigvuldig daarom iedere uitgevlakte patchrij x met dezelfde gewichtsmatrix E, en tel de biasvector b op. E heeft P²C rijen en D kolommen; iedere geprojecteerde rij e heeft D getallen.
Kies D=2. Ons eerste kenmerk telt de eerste en derde patchwaarde op; het tweede telt de tweede en vierde op, plus bias 1. Voor de eerste patch geeft dat 0+4=4 en 1+5+1=7.
weight = np.array([
[1,0],[0,1],[1,0],[0,1]])
bias = np.array([0,1])
embedded = project_patches(
patches,weight,bias)
print(embedded[0].tolist())
# [4.0, 7.0]
Maak project_patches voor de hele matrix. Reken met floats vóór de vermenigvuldiging, behoud de invoer en controleer de aansluitende vormen. Deze gedeelde lineaire stap lijkt op een convolutie met stapgrootte P; hij maakt op zichzelf nog geen contact tussen verschillende patches.
023. Bewaar een verzamelplek en de positie
Zet vóór de patchrijen een class-token, afgekort CLS. Dit is één extra vector die later informatie uit het hele beeld kan verzamelen. Tel daarna een positietabel op met één rij voor ieder token, inclusief CLS; de tabelbreedte is opnieuw D.
cls = np.array([0,0])
positions = np.array([
[0,0],[0,0],[1,0],[0,1],[1,1]])
tokens = add_visual_tokens(
embedded,cls,positions)
print(tokens.shape)
# (5, 2)
CLS start hier op nul, maar is niet hetzelfde als een gemiddelde van de patches. In een getrainde ViT zijn dit leerbare parameters. Onze gekozen positiegetallen maken alleen zichtbaar welke rij wordt aangepast; ze bewijzen niet dat een model ruimtelijk begrip heeft.
print(tokens[0].tolist())
print(tokens[2].tolist())
# [0.0, 0.0]
# [9.0, 11.0]
Maak add_visual_tokens met CLS op rij nul en de opgegeven positietabel. Posities achteraf verwisselen verandert de invoer van het model. Ook hebben kleinere patches meer tokens tot gevolg: volgende keer zie je waarom dat de attentionberekening duurder maakt.
03Wat neem je mee?
- De patchvolgorde en kanaalvolgorde horen bij het invoercontract.
- Alle patches delen één projectie naar dezelfde vectorbreedte.
- CLS en positierijen maken de tokenreeks klaar voor beeldattention.