Välkommen till del 3 av Applied Deep Learning series. Del 1 var en praktisk introduktion till artificiella neurala nätverk, som täcker både teori och tillämpning med många kodexempel och visualisering. I del 2 tillämpade vi djupinlärning på verkliga dataset, som täcker de 3 vanligaste problemen som fallstudier: binär klassificering, multiklassklassificering och regression.
nu börjar vi dyka in i specifika djupinlärningsarkitekturer, börjar med det enklaste: Autoencoders.
- Inledning
- arkitektur
- genomförande
- Denoising Autoencoders
- glesa Autoencoders
- användningsfall
- slutsats
koden för den här artikeln finns här som en jupyter-anteckningsbok, ladda ner och prova själv.
introduktion
Autoencoders är en specifik typ av feedforward neurala nätverk där ingången är densamma som utgången. De komprimerar ingången till en lägre dimensionell kod och rekonstruerar sedan utgången från denna representation. Koden är en kompakt ”sammanfattning” eller ”komprimering” av ingången, även kallad latent rymdrepresentation.
en autoencoder består av 3 komponenter: kodare, kod och avkodare. Kodaren komprimerar ingången och producerar koden, avkodaren rekonstruerar sedan ingången endast med den här koden.
för att bygga en autoencoder behöver vi 3 Saker: en kodningsmetod, avkodningsmetod och en förlustfunktion för att jämföra utgången med målet. Vi kommer att undersöka dessa i nästa avsnitt.
Autoencoders är huvudsakligen en dimensionalitetsreduktion (eller kompression) algoritm med ett par viktiga egenskaper:
- Dataspecifik: Autoencoders kan bara meningsfullt komprimera data som liknar vad de har utbildats på. Eftersom de lär sig funktioner som är specifika för den givna träningsdata, är de annorlunda än en standard datakomprimeringsalgoritm som gzip. Så vi kan inte förvänta oss en autoencoder utbildad på handskrivna siffror för att komprimera landskapsbilder.
- förstörande: Utgången från autoencoder kommer inte att vara exakt densamma som ingången, det kommer att vara en nära men försämrad representation. Om du vill ha förlustfri komprimering är de inte vägen att gå.
- Unsupervised: för att träna en autoencoder behöver vi inte göra något snyggt, bara kasta de råa inmatningsdata på den. Autoencoders anses vara en oövervakad inlärningsteknik eftersom de inte behöver explicita etiketter för att träna på. Men för att vara mer exakt är de självövervakade eftersom de genererar sina egna etiketter från träningsdata.
arkitektur
Låt oss utforska detaljerna i kodaren, koden och avkodaren. Både kodaren och avkodaren är helt anslutna feedforward neurala nätverk, i huvudsak ANNs vi täckte i Del 1. Kod är ett enda lager av en ANN med dimensionen av vårt val. Antalet noder i kodskiktet (kodstorlek) är en hyperparameter som vi ställer in innan du tränar autoencoder.
detta är en mer detaljerad visualisering av en autoencoder. Först passerar ingången genom kodaren, som är en helt ansluten ANN, för att producera koden. Avkodaren, som har liknande ANN-struktur, producerar sedan utmatningen endast med koden. Målet är att få en utgång identisk med ingången. Observera att avkodararkitekturen är spegelbilden för kodaren. Detta är inte ett krav men det är vanligtvis fallet. Det enda kravet är dimensionen av ingången och utgången måste vara densamma. Allt i mitten kan spelas med.
det finns 4 hyperparametrar som vi måste ställa in innan vi tränar en autoencoder:
- Kodstorlek: antal noder i mellanlagret. Mindre storlek resulterar i mer komprimering.
- antal lager: autoencoder kan vara så djup som vi vill. I figuren ovan har vi 2 lager i både kodaren och avkodaren, utan att ta hänsyn till ingången och utgången.
- antal noder per lager: autoencoder-arkitekturen vi arbetar med kallas en staplad autoencoder eftersom lagren staplas efter varandra. Vanligtvis staplade autoencoders ser ut som en”sandwitch”. Antalet noder per lager minskar med varje efterföljande lager av kodaren och ökar tillbaka i avkodaren. Även avkodaren är symmetrisk till kodaren när det gäller lagerstruktur. Som nämnts ovan är detta inte nödvändigt och vi har total kontroll över dessa parametrar.
- förlustfunktion: vi använder antingen mean squared error (mse) eller binär crossentropy. Om ingångsvärdena ligger i intervallet använder vi vanligtvis crossentropy, annars använder vi det genomsnittliga kvadratfelet. För mer information kolla in den här videon.
Autoencoders tränas på samma sätt som ANNs via backpropagation. Kolla in introduktionen av Del 1 för mer information om hur neurala nätverk tränas, det gäller direkt för autoencoders.
implementering
låt oss nu implementera en autoencoder för följande arkitektur, 1 dolt lager i kodaren och avkodaren.
vi kommer att använda den extremt populära mnist dataset som input. Den innehåller svartvita bilder av handskrivna siffror.
de är av storlek 28×28 och vi använder dem som en vektor av 784 siffror mellan . Kontrollera Jupyter-anteckningsboken för detaljerna.
vi kommer nu att implementera autoencoder med Keras. Hyperparametrarna är: 128 noder i det dolda lagret, kodstorleken är 32 och binär crossentropi är förlustfunktionen.
Detta liknar mycket ANNs vi arbetade med, men nu använder vi keras funktionella API. Se den här guiden för mer information, men här är en snabb jämförelse. Innan vi brukade lägga till lager med hjälp av sekventiell API enligt följande:
model.add(Dense(16, activation='relu'))
model.add(Dense(8, activation='relu'))
med det funktionella API: et gör vi detta:
layer_1 = Dense(16, activation='relu')(input)
layer_2 = Dense(8, activation='relu')(layer_1)
det är mer verbose men ett mer flexibelt sätt att definiera komplexa modeller. Vi kan enkelt ta tag i delar av vår modell, till exempel bara avkodaren, och arbeta med det. Utgången från den täta metoden är ett anropbart lager, med hjälp av det funktionella API som vi tillhandahåller den med ingången och lagrar utgången. Utgången från ett lager blir ingången till nästa lager. Med sekventiell API add metoden underförstått hanteras detta för oss.
Observera att alla lager använder Relu-aktiveringsfunktionen, eftersom det är standarden med djupa neurala nätverk. Det sista lagret använder sigmoidaktiveringen eftersom vi behöver utgångarna vara mellan . Ingången ligger också i samma intervall.
notera också funktionen call to fit, innan med ANNs vi brukade göra:
model.fit(x_train, y_train)
men nu gör vi:
model.fit(x_train, x_train)
kom ihåg att målen för autoencoder är desamma som ingången. Det är därför vi levererar träningsdata som mål.
visualisering
låt oss nu visualisera hur bra vår autoencoder rekonstruerar sin inmatning.
vi kör autoencoder på testuppsättningen helt enkelt genom att använda predict-funktionen för Keras. För varje bild i testuppsättningen får vi utmatningen från autoencoder. Vi förväntar oss att utgången är mycket lik ingången.
de är verkligen ganska lika, men inte exakt samma. Vi kan märka det tydligare i den sista siffran”4″. Eftersom detta var en enkel uppgift presterade vår autoencoder ganska bra.
råd
vi har total kontroll över arkitekturen i autoencoder. Vi kan göra det mycket kraftfullt genom att öka antalet lager, noder per lager och viktigast av allt kodstorleken. Öka dessa hyperparametrar kommer att låta autoencoder att lära sig mer komplexa kodningar. Men vi bör vara noga med att inte göra det för kraftfullt. Annars kommer autoencoderen helt enkelt att lära sig att kopiera sina ingångar till utgången utan att lära sig någon meningsfull representation. Det kommer bara att efterlikna identitetsfunktionen. Autoencoder kommer att rekonstruera träningsdata perfekt, men det kommer att överfitta utan att kunna generalisera till nya instanser, vilket inte är vad vi vill ha.
det är därför vi föredrar en ”sandwitch” – arkitektur och medvetet håller kodstorleken liten. Eftersom kodningsskiktet har en lägre dimension än ingångsdata, sägs autoencoder vara underkomplett. Det kommer inte att kunna kopiera sina ingångar direkt till utgången och kommer att tvingas lära sig intelligenta funktioner. Om ingångsdata har ett mönster, till exempel siffran ”1” innehåller vanligtvis en något rak linje och siffran ”0” är cirkulär, kommer den att lära sig detta faktum och koda det i en mer kompakt form. Om ingångsdata var helt slumpmässigt utan någon intern korrelation eller beroende, kommer en underkomplett autoencoder inte att kunna återställa den perfekt. Men lyckligtvis i den verkliga världen finns det mycket beroende.
Denoising Autoencoders
att hålla kodskiktet litet tvingade vår autoencoder att lära sig en intelligent representation av data. Det finns ett annat sätt att tvinga autoencoder att lära sig användbara funktioner, som lägger slumpmässigt brus till sina ingångar och gör det återställa den ursprungliga brusfria data. På så sätt kan autoencoder inte bara kopiera ingången till dess utgång eftersom ingången också innehåller slumpmässigt brus. Vi ber den att subtrahera bruset och producera de underliggande meningsfulla data. Detta kallas en denoising autoencoder.
den översta raden innehåller originalbilderna. Vi lägger till slumpmässigt Gaussiskt brus till dem och bullriga data blir ingången till autoencoder. Autoencoder ser inte originalbilden alls. Men då förväntar vi oss att autoencoderen regenererar den brusfria originalbilden.
det finns bara en liten skillnad mellan implementeringen av denoising autoencoder och den vanliga. Arkitekturen förändras inte alls, bara fit-funktionen. Vi utbildade den vanliga autoencoder enligt följande:
autoencoder.fit(x_train, x_train)
denoising autoencoder är utbildad som:
autoencoder.fit(x_train_noisy, x_train)
enkelt är det, allt annat är exakt detsamma. Ingången till autoencoder är den bullriga bilden, och det förväntade målet är den ursprungliga brusfria.
visualisering
låt oss nu visualisera om vi kan återställa de brusfria bilderna.
ser ganska bra ut. Den nedre raden är autoencoder-utgången. Vi kan göra bättre genom att använda mer komplexa autoencoder arkitektur, såsom convolutional autoencoders. Vi kommer att täcka omvälvningar i den kommande artikeln.
glesa Autoencoders
vi introducerade två sätt att tvinga autoencoder att lära sig användbara funktioner: att hålla kodstorleken liten och denoising autoencoders. Den tredje metoden använder regularisering. Vi kan reglera autoencoderen genom att använda en sparsity-begränsning så att endast en bråkdel av noderna skulle ha icke-nollvärden, kallade aktiva noder.
i synnerhet lägger vi till en straffperiod till förlustfunktionen så att endast en bråkdel av noderna blir aktiva. Detta tvingar autoencoder att representera varje ingång som en kombination av ett litet antal noder och kräver att den upptäcker intressant struktur i data. Denna metod fungerar även om kodstorleken är stor, eftersom endast en liten delmängd av noderna kommer att vara aktiv när som helst.
det är ganska enkelt att göra detta i Keras med bara en parameter. Som en påminnelse skapade vi tidigare kodlagret enligt följande:
code = Dense(code_size, activation='relu')(input_img)
vi lägger nu till en annan parameter som heter activity_regularizer genom att ange regulariseringsstyrkan. Detta är vanligtvis ett värde i intervallet . Här valde vi 10e-6.
code = Dense(code_size, activation='relu', activity_regularizer=l1(10e-6))(input_img)
den slutliga förlusten av den glesa modellen är 0,01 högre än den vanliga, på grund av den extra regulariseringsperioden.
Låt oss visa att kodningarna som genereras av den regulariserade modellen verkligen är glesa. Om vi tittar på histogrammet för kodvärden för bilderna i testuppsättningen är fördelningen enligt följande:
medelvärdet för standardmodellen är 6,6 men för den regulariserade modellen är det 0,8, en ganska stor minskning. Vi kan se att en stor del av kodvärdena i den regulariserade modellen verkligen är 0, vilket är vad vi ville ha. Variansen hos den regulariserade modellen är också ganska låg.
använd Fall
nu kan vi ställa följande frågor. Hur bra är autoencoders vid komprimering av ingången? Och är de en vanligt använd djupinlärningsteknik?
tyvärr används autoencoders inte i stor utsträckning i verkliga applikationer. Som komprimeringsmetod presterar de inte bättre än dess alternativ, till exempel gör jpeg fotokomprimering bättre än en autoencoder. Och det faktum att autoencoders är dataspecifika gör dem opraktiska som en allmän teknik. De har dock 3 vanliga användningsfall:
- Data denoising: vi har sett ett exempel på detta på bilder.
- Dimensionalitetsreduktion: visualisering av högdimensionella data är utmanande. t-SNE är den vanligaste metoden men kämpar med stort antal dimensioner (vanligtvis över 32). Så autoencoders används som ett förbehandlingssteg för att minska dimensionen, och denna komprimerade representation används av T-SNE för att visualisera data i 2D-utrymme. För bra artiklar om t-SNE se här och här.
- Variational Autoencoders (VAE): Detta är ett mer modernt och komplext användningsfall för autoencoders och vi kommer att täcka dem i en annan artikel. Men som en snabb sammanfattning lär VAE parametrarna för sannolikhetsfördelningen som modellerar ingångsdata, istället för att lära sig en godtycklig funktion när det gäller vanilj autoencoders. Genom provtagningspunkter från denna distribution kan vi också använda VAE som en generativ modell. Här är en bra referens.
slutsats
Autoencoders är en mycket användbar dimensionalitetsreduceringsteknik. De är mycket populära som undervisningsmaterial i inledande djupa inlärningskurser, troligen på grund av deras enkelhet. I den här artikeln täckte vi dem i detalj och jag hoppas att du tyckte om det.
hela koden för den här artikeln finns här om du vill hacka på det själv. Om du har några synpunkter gärna nå ut till mig på twitter.