7.4 Keli įvesties ir keli išvesties kanalai
Nors 7.1.4 skyrelis aprašėme kelis kanalus, sudarančius kiekvieną vaizdą (pvz., spalvoti vaizdai turi standartinius RGB kanalus, nurodančius raudonos, žalios ir mėlynos kiekį), ir konvoliucinius sluoksnius keliems kanalams, iki šiol supaprastinome visus savo skaitinius pavyzdžius, dirbdami tik su vienu įvesties ir vienu išvesties kanalu. Tai leido mums savo įvestis, konvoliucijos branduolius ir išvestis laikyti dvimačiais tenzoriais.
Kai pridedame kanalus, mūsų įvestys ir paslėptieji atvaizdai tampa trimačiais tenzoriais. Pavyzdžiui, kiekvieno RGB įvesties vaizdo forma yra . Šią ašį, kurios dydis 3, vadiname kanalų dimensija. Kanalų sąvoka yra tokia pat sena kaip patys CNN: pavyzdžiui, LeNet-5 (LeCun ir kt., 1995) jas naudoja. Šiame skyrelyje giliau pažvelgsime į konvoliucijos branduolius su keliais įvesties ir keliais išvesties kanalais.
from d2l import mxnet as d2l
from mxnet import np, npx
npx.set_np()
from d2l import torch as d2l
import torch
from d2l import jax as d2l
import jax
from jax import numpy as jnp
from d2l import tensorflow as d2l
import tensorflow as tf
7.4.1 Keli įvesties kanalai
Kai įvesties duomenys turi kelis kanalus, turime sukonstruoti konvoliucijos branduolį su tokiu pat įvesties kanalų skaičiumi kaip ir įvesties duomenys, kad jis galėtų atlikti kryžminę koreliaciją su įvesties duomenimis. Tarkime, kad įvesties duomenų kanalų skaičius yra , tada konvoliucijos branduolio įvesties kanalų skaičius taip pat turi būti . Jei mūsų konvoliucijos branduolio lango forma yra , tai, kai , savo konvoliucijos branduolį galime laikyti tiesiog dvimačiu tenzoriumi, kurio forma .
Tačiau, kai , mums reikia branduolio, kuriame yra formos tenzorius kiekvienam įvesties kanalui. Sujungus šiuos tenzorių gaunamas konvoliucijos branduolys, kurio forma . Kadangi įvestis ir konvoliucijos branduolys turi po kanalų, galime atlikti kryžminės koreliacijos operaciją su dvimačiu įvesties tenzoriumi ir dvimačiu konvoliucijos branduolio tenzoriumi kiekvienam kanalui, sudėdami rezultatus (sumuodami per kanalus), kad gautume dvimatį tenzorių. Tai yra dvimatės kryžminės koreliacijos rezultatas tarp daugiakanalės įvesties ir konvoliucijos branduolio su keliais įvesties kanalais.
7.4.1 pav. pateiktas dvimatės kryžminės koreliacijos su dviem įvesties kanalais pavyzdys. Pažymėtos dalys yra pirmasis išvesties elementas ir įvesties bei branduolio tenzoriaus elementai, naudoti išvesčiai skaičiuoti: .
7.4.1 pav. Kryžminės koreliacijos skaičiavimas su dviem įvesties kanalais.
Kad įsitikintume, jog tikrai suprantame, kas čia vyksta, patys galime (įgyvendinti kryžminės koreliacijos operacijas su keliais įvesties kanalais). Atkreipkite dėmesį, kad viskas, ką darome, yra kryžminės koreliacijos operacija kiekvienam kanalui, o tada rezultatų sudėjimas.
def corr2d_multi_in(X, K):
# Iterate through the 0th dimension (channel) of K first, then add them up
return sum(d2l.corr2d(x, k) for x, k in zip(X, K))
def corr2d_multi_in(X, K):
# Iterate through the 0th dimension (channel) of K first, then add them up
return tf.reduce_sum([d2l.corr2d(x, k) for x, k in zip(X, K)], axis=0)
Galime sukonstruoti įvesties tenzorių X ir branduolio tenzorių K,
atitinkančius reikšmes 7.4.1 pav.,
kad (patvirtintume) kryžminės koreliacijos operacijos (išvestį).
X = d2l.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = d2l.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])
corr2d_multi_in(X, K)
7.4.2 Keli išvesties kanalai
Nepriklausomai nuo įvesties kanalų skaičiaus, iki šiol visada gaudavome vieną išvesties kanalą. Tačiau, kaip aptarėme 7.1.4 skyrelis, paaiškėja, kad kiekviename sluoksnyje būtina turėti kelis kanalus. Populiariausiose neuroninių tinklų architektūrose iš tikrųjų didiname kanalų dimensiją, eidami gilyn į neuroninį tinklą, paprastai sumažindami diskretizavimo dažnį, kad iškeistume erdvinę skyrą į didesnį kanalų gylį. Intuityviai galite įsivaizduoti, kad kiekvienas kanalas reaguoja į skirtingą požymių rinkinį. Tikrovė yra šiek tiek sudėtingesnė. Naivi interpretacija leistų manyti, kad atvaizdai išmokstami nepriklausomai kiekvienam pikseliui ar kiekvienam kanalui. Vietoj to kanalai optimizuojami taip, kad būtų naudingi kartu. Tai reiškia, kad užuot priskyrus vieną kanalą kraštų detektoriui, gali tiesiog reikšti, kad tam tikra kryptis kanalų erdvėje atitinka kraštų aptikimą.
Tegul ir žymi atitinkamai įvesties ir išvesties kanalų skaičių, o ir – branduolio aukštį ir plotį. Kad gautume išvestį su keliais kanalais, galime sukurti formos branduolio tenzorių kiekvienam išvesties kanalui. Juos sujungiame išvesties kanalų dimensijoje, taip kad konvoliucijos branduolio forma būtų . Kryžminės koreliacijos operacijose rezultatas kiekviename išvesties kanale apskaičiuojamas pagal tą išvesties kanalą atitinkantį konvoliucijos branduolį ir gauna įvestį iš visų įvesties tenzoriaus kanalų.
Įgyvendiname kryžminės koreliacijos funkciją, kad apskaičiuotume kelių kanalų išvestį, kaip parodyta žemiau.
def corr2d_multi_in_out(X, K):
# Iterate through the 0th dimension of K, and each time, perform
# cross-correlation operations with input X. All of the results are
# stacked together
return d2l.stack([corr2d_multi_in(X, k) for k in K], 0)
Sukonstruojame trivialų konvoliucijos branduolį su trimis išvesties kanalais,
sujungdami K branduolio tenzorių su K+1 ir K+2.
K = d2l.stack((K, K + 1, K + 2), 0)
K.shape
Toliau atliekame kryžminės koreliacijos operacijas
su įvesties tenzoriumi X ir branduolio tenzoriumi K.
Dabar išvestyje yra trys kanalai.
Pirmojo kanalo rezultatas sutampa
su ankstesnio įvesties tenzoriaus X
ir branduolio su keliais įvesties kanalais bei
vienu išvesties kanalu rezultatu.
corr2d_multi_in_out(X, K)
7.4.3 konvoliucinis sluoksnis
Iš pirmo žvilgsnio konvoliucija, t. y. , neatrodo labai prasminga. Juk konvoliucija koreliuoja gretimus pikselius. konvoliucija akivaizdžiai to nedaro. Vis dėlto tai populiarios operacijos, kurios kartais įtraukiamos į sudėtingų giliųjų tinklų dizainą (Lin ir kt., 2013; Szegedy ir kt., 2017). Pažiūrėkime šiek tiek išsamiau, ką ji iš tikrųjų daro.
Kadangi naudojamas mažiausias langas, konvoliucija praranda didesnių konvoliucinių sluoksnių gebėjimą atpažinti šablonus, sudarytus iš sąveikų tarp gretimų elementų aukščio ir pločio dimensijose. Vienintelis konvoliucijos skaičiavimas vyksta kanalų dimensijoje.
7.4.2 pav. parodytas kryžminės koreliacijos skaičiavimas naudojant konvoliucijos branduolį su 3 įvesties kanalais ir 2 išvesties kanalais. Atkreipkite dėmesį, kad įvestys ir išvestys turi tą patį aukštį ir plotį. Kiekvienas išvesties elementas gaunamas iš įvesties vaizdo elementų toje pačioje pozicijoje tiesinio derinio. konvoliucinį sluoksnį galite laikyti pilnai sujungtu sluoksniu, taikomu kiekvienoje pikselio vietoje, kad atitinkamų įvesties reikšmių transformuotų į išvesties reikšmių. Kadangi tai vis dar yra konvoliucinis sluoksnis, svoriai yra susieti per pikselių vietas. Taigi konvoliuciniam sluoksniui reikia svorių (plius poslinkis). Taip pat atkreipkite dėmesį, kad po konvoliucinių sluoksnių paprastai eina netiesiškumai. Tai užtikrina, kad konvoliucijų negalima tiesiog sulieti su kitomis konvoliucijomis.
7.4.2 pav. Kryžminės koreliacijos skaičiavimas naudoja konvoliucijos branduolį su trimis įvesties kanalais ir dviem išvesties kanalais. Įvestis ir išvestis turi tą patį aukštį ir plotį.
Patikrinkime, ar tai veikia praktikoje: įgyvendiname konvoliuciją naudodami pilnai sujungtą sluoksnį. Vienintelis dalykas – turime šiek tiek pakoreguoti duomenų formą prieš matricų daugybą ir po jos.
def corr2d_multi_in_out_1x1(X, K):
c_i, h, w = X.shape
c_o = K.shape[0]
X = d2l.reshape(X, (c_i, h * w))
K = d2l.reshape(K, (c_o, c_i))
# Matrix multiplication in the fully connected layer
Y = d2l.matmul(K, X)
return d2l.reshape(Y, (c_o, h, w))
Atliekant konvoliucijas,
aukščiau pateikta funkcija yra ekvivalenti anksčiau įgyvendintai kryžminės koreliacijos funkcijai corr2d_multi_in_out.
Patikrinkime tai su keliais pavyzdiniais duomenimis.
X = d2l.normal(0, 1, (3, 3, 3))
K = d2l.normal(0, 1, (2, 3, 1, 1))
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(d2l.reduce_sum(d2l.abs(Y1 - Y2))) < 1e-6
X = d2l.normal((3, 3, 3), 0, 1)
K = d2l.normal((2, 3, 1, 1), 0, 1)
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(d2l.reduce_sum(d2l.abs(Y1 - Y2))) < 1e-6
X = jax.random.normal(jax.random.PRNGKey(d2l.get_seed()), (3, 3, 3)) + 0 * 1
K = jax.random.normal(jax.random.PRNGKey(d2l.get_seed()), (2, 3, 1, 1)) + 0 * 1
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(d2l.reduce_sum(d2l.abs(Y1 - Y2))) < 1e-6
7.4.4 Diskusija
Kanalai leidžia mums sujungti geriausias abiejų pasaulių savybes: MLP, leidžiančius reikšmingus netiesiškumus, ir konvoliucijas, leidžiančias lokalizuotą požymių analizę. Visų pirma kanalai leidžia CNN samprotauti su keliais požymiais, tokiais kaip kraštų ir formų detektoriai vienu metu. Jie taip pat siūlo praktišką kompromisą tarp drastiško parametrų sumažinimo, atsirandančio dėl postūmio invariantiškumo ir lokalumo, ir raiškių bei įvairių modelių poreikio kompiuteriniame matyme.
Tačiau atkreipkite dėmesį, kad šis lankstumas kainuoja. Turint dydžio vaizdą, konvoliucijos skaičiavimo kaina yra . Atitinkamai įvesties ir išvesties kanalams tai padidėja iki . pikselių vaizdui su branduoliu ir atitinkamai įvesties ir išvesties kanalų tai sudaro daugiau nei 53 milijardus operacijų (daugybą ir sudėtį skaičiuojame atskirai). Vėliau susidursime su efektyviomis strategijomis kainai sumažinti, pvz., reikalaujant, kad operacijos pagal kanalus būtų blokinės įstrižainės, o tai veda prie tokių architektūrų kaip ResNeXt (Xie ir kt., 2017).
7.4.5 Pratimai
- Tarkime, kad turime du konvoliucijos branduolius, kurių dydžiai atitinkamai ir
(be netiesiškumo tarp jų).
- Įrodykite, kad operacijos rezultatą galima išreikšti viena konvoliucija.
- Kokia yra ekvivalentiškos vienos konvoliucijos dimensija?
- Ar teisingas atvirkštinis teiginys, t. y. ar visada galima konvoliuciją išskaidyti į dvi mažesnes?
- Tarkime, kad įvesties forma yra , konvoliucijos branduolio forma –
, papildymas – , o žingsnis – .
- Kokia yra tiesioginio sklidimo skaičiavimo kaina (daugybos ir sudėties)?
- Kokia yra atminties apimtis?
- Kokia yra atminties apimtis atgaliniam skaičiavimui?
- Kokia yra atgalinio sklidimo skaičiavimo kaina?
- Kiek kartų padidėja skaičiavimų skaičius, jei padvigubiname ir įvesties kanalų skaičių , ir išvesties kanalų skaičių ? Kas nutinka, jei padvigubiname papildymą?
- Ar kintamieji
Y1irY2paskutiniame šio skyrelio pavyzdyje yra visiškai vienodi? Kodėl? - Išreikškite konvoliucijas kaip matricų daugybą, net kai konvoliucijos langas nėra .
- Jūsų užduotis – įgyvendinti greitas konvoliucijas su branduoliu. Vienas iš algoritmo kandidatų yra skenuoti horizontaliai per šaltinį, skaitant pločio juostą ir apskaičiuojant pločio išvesties juostą po vieną reikšmę. Alternatyva yra nuskaityti pločio juostą ir apskaičiuoti pločio išvesties juostą. Kodėl pastaroji yra geresnė? Ar yra riba, kokį didelį turėtumėte rinktis?
- Tarkime, kad turime matricą.
- Kiek greičiau dauginti iš blokinės įstrižainės matricos, jei matrica išskaidyta į blokų?
- Koks yra blokų turėjimo trūkumas? Kaip jį galėtumėte ištaisyti, bent iš dalies?
