de Jerin Paul

Cum am dezvoltat un CNN care recunoaște emoțiile și a intrat în top 10 Kaggle

Un bebeluș începe să-și recunoască fețele părinților când are doar câteva săptămâni. Pe măsură ce crește, această abilitate înnăscută se îmbunătățește. Până când are câteva luni, începe să afișeze indicii sociale și este capabil să înțeleagă emoțiile de bază ca un zâmbet.

Datorită milioane de ani de evoluție, suntem capabili să ne înțelegem fără să folosim un singur cuvânt. Doar o privire și asta este tot ce trebuie să înțeleagă dacă o persoană este prăbușită sau euforiată. Ei bine, am încercat să învăț computerele să facă exact asta. Acest articol este o prezentare detaliată a modului în care s-a desfășurat întregul experiment. Urmăriți în timp ce recreăm rețeaua.

Cum am dezvoltat un CNN care recunoaste emotiile si a
Imagine numai în scopuri reprezentative.

Treceți la urmărire Paul, vă rog, dați-mi codul. Nu doriți să citiți fantezie? Nici o problema. Puteți găsi codul pentru acest proiect aici.

O scurtă introducere

„Cele mai bune și mai frumoase lucruri din lume nu pot fi văzute și nici măcar atinse. Trebuie simțiți cu inima ”- Keller Helen

Hellen Keller a descris excelent esența emoțiilor umane în citatul menționat anterior. Ceea ce a fost rezervat odinioară animalelor nu se mai limitează la ele. Învățarea automată prinde într-un ritm mincinos. Debutul rețelelor neuronale convoluționale a fost o descoperire și a schimbat modul în care computerele „privesc” lumea.

Expresiile faciale nu sunt altceva decât aranjamentul mușchilor feței pentru a transmite observatorului o anumită stare emoțională. Emoțiile pot fi împărțite în șase mari categorii – Mânia, Dezgustul, Frica, Fericirea, Tristețea, Surpriza și Neutrul. În acest proiect ML, vom instrui un model pentru a face diferența dintre acestea.

1611402068 27 Cum am dezvoltat un CNN care recunoaste emotiile si a
Puține tipuri diferite de expresii faciale.

Vom instrui o rețea neuronală convoluțională utilizând setul de date FER2013 și vom folosi diverși parametri hiper pentru a regla fin modelul. O vom instrui mai departe Google Colab, care este un proiect de cercetare creat pentru diseminarea educației ML. Vă vor aloca resurse cum ar fi GPU sau TPU, iar acestea pot fi folosite pentru a vă antrena modelul mai repede. Cea mai bună parte este că este complet gratuit.

Aruncă o privire asupra datelor

Vom începe prin a încărca fișierul FER2013.csv pe unitatea noastră, astfel încât să-l putem accesa din Google Colab. Există 35.888 de imagini în acest set de date care sunt clasificate în șase emoții. Fișierul de date conține 3 coloane – clasă, date de imagine și utilizare.

Clasă: este o cifră între 0 și 6 și reprezintă emoția descrisă în imaginea corespunzătoare. Fiecare emoție este mapată la un număr întreg așa cum se arată mai jos.

0 - 'Angry'1 - 'Disgust'2 - 'Fear' 3 - 'Happy' 4 - 'Sad' 5 - 'Surprise'6 - 'Neutral'

Date imagine: este un șir de 2.304 de numere și acestea sunt valorile intensității pixelilor imaginii noastre, vom acoperi acest lucru în detaliu într-un timp.

Utilizare: indică dacă datele corespunzătoare ar trebui utilizate pentru a antrena rețeaua sau a o testa.

Descompunerea unei imagini.

După cum știm cu toții că imaginile sunt compuse din pixeli și acești pixeli nu sunt altceva decât numere. Imaginile colorate au trei canale de culoare – roșu, verde și albastru – și fiecare canal este reprezentat de o grilă (matrice bidimensională). Fiecare celulă din grilă stochează un număr între 0 și 255 care denotă intensitatea acelei celule.

1611402068 363 Cum am dezvoltat un CNN care recunoaste emotiile si a
Ce vedeți (L) vs. ce vede un computer.

Când aceste trei canale sunt aliniate împreună, obținem imaginile pe care le vedem.

Importul bibliotecilor necesare

%matplotlib inlineimport matplotlib.pyplot as plt
import numpy as npfrom keras.utils import to_categoricalfrom sklearn.model_selection import train_test_split
from keras.models import Sequential #Initialise our neural network model as a sequential networkfrom keras.layers import Conv2D #Convolution operationfrom keras.layers.normalization import BatchNormalizationfrom keras.regularizers import l2from keras.layers import Activation#Applies activation functionfrom keras.layers import Dropout#Prevents overfitting by randomly converting few outputs to zerofrom keras.layers import MaxPooling2D # Maxpooling functionfrom keras.layers import Flatten # Converting 2D arrays into a 1D linear vectorfrom keras.layers import Dense # Regular fully connected neural networkfrom keras import optimizersfrom keras.callbacks import ReduceLROnPlateau, EarlyStopping, TensorBoard, ModelCheckpointfrom sklearn.metrics import accuracy_score

Definiți mecanismul de încărcare a datelor

Acum, vom defini funcția load_data () care va analiza eficient fișierul de date și va extrage datele necesare și apoi le va converti într-un format de imagine utilizabil.

Toate imaginile din setul nostru de date au dimensiunea de 48×48. Deoarece aceste imagini sunt la scară de gri, există un singur canal. Vom extrage datele imaginii și le vom rearanja într-o matrice de 48×48. Apoi convertiți-l în numere întregi nesemnate și împărțiți-l la 255 pentru a normaliza datele. 255 este valoarea maximă posibilă a unei singure celule. Împărțind fiecare element la 255, ne asigurăm că toate valorile noastre variază între 0 și 1.

Vom verifica Utilizare coloanați și stocați datele în liste separate, una pentru instruirea rețelei și cealaltă pentru testarea acesteia.

def load_data(dataset_path):
data = []  test_data = []  test_labels = []  labels =[]
  with open(dataset_path, 'r') as file:      for line_no, line in enumerate(file.readlines()):          if 0 < line_no <= 35887:            curr_class, line, set_type = line.split(',')            image_data = np.asarray([int(x) for x in line.split()]).reshape(48, 48)            image_data =image_data.astype(np.uint8)/255.0                        if (set_type.strip() == 'PrivateTest'):                            test_data.append(image_data)              test_labels.append(curr_class)            else:              data.append(image_data)              labels.append(curr_class)            test_data = np.expand_dims(test_data, -1)      test_labels = to_categorical(test_labels, num_classes = 7)      data = np.expand_dims(data, -1)         labels = to_categorical(labels, num_classes = 7)          return np.array(data), np.array(labels), np.array(test_data), np.array(test_labels)

Odată ce datele noastre sunt segregate, vom extinde dimensiunile atât ale datelor de testare, cât și ale celor de instruire, pentru a acomoda canalul. Apoi, vom codifica la cald toate etichetele folosind funcția to_categorical () și vom returna toate listele ca neclintit matrici.

Vom încărca datele apelând funcția load_data ().

dataset_path = "/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/Data/fer2013.csv"
train_data, train_labels, test_data, test_labels = load_data(dataset_path)
print("Number of images in Training set:", len(train_data))print("Number of images in Test set:", len(test_data))

Datele noastre sunt încărcate și acum permiteți-ne să ajungem la cea mai bună parte, definind rețeaua.

Definirea modelului.

Vom folosi Keras pentru a crea o rețea convoluțională secvențială. Ceea ce înseamnă că rețeaua noastră neuronală va fi un teanc liniar de straturi. Această rețea va avea următoarele componente:

  1. Straturi convoluționale: aceste straturi sunt elementele de bază ale rețelei noastre și acestea calculează produsul punct între greutățile lor și regiunile mici de care sunt legate. Acesta este modul în care aceste straturi învață anumite caracteristici din aceste imagini.
  2. Funcții de activare: sunt acele funcții care sunt aplicate la ieșirile tuturor straturilor din rețea. În acest proiect, vom recurge la utilizarea a două funcții – Relu și Softmax.
  3. Straturile de punere în comun: aceste straturi vor minimiza operațiunea de-a lungul dimensiunilor. Acest lucru ajută la reducerea datelor spațiale și la minimizarea puterii de procesare necesare.
  4. Straturi dense: Aceste straturi sunt prezente la sfârșitul unui CNN. Acestea preiau toate datele caracteristice generate de straturile de convoluție și iau deciziile.
  5. Straturi de abandon: oprește în mod aleatoriu câțiva neuroni din rețea pentru a preveni supraadaptarea.
  6. Normalizarea lotului: normalizează ieșirea unui strat de activare anterior scăzând media lotului și împărțind la deviația standard a lotului. Acest lucru accelerează procesul de instruire.
model.add(Conv2D(64, (3, 3), activation='relu', input_shape=(48, 48, 1), kernel_regularizer=l2(0.01)))model.add(Conv2D(64, (3, 3), padding='same',activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2), strides=(2, 2)))model.add(Dropout(0.5))    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(256, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(Conv2D(512, (3, 3), padding='same', activation='relu'))model.add(BatchNormalization())model.add(MaxPooling2D(pool_size=(2,2)))model.add(Dropout(0.5))    model.add(Flatten())model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(256, activation='relu'))model.add(Dropout(0.5))model.add(Dense(128, activation='relu'))model.add(Dropout(0.5))model.add(Dense(64, activation='relu'))model.add(Dropout(0.5))model.add(Dense(7, activation='softmax'))

Vom compila rețeaua utilizând optimizatorul Adam și vom folosi o rată de învățare variabilă. Deoarece avem de-a face cu o problemă de clasificare care implică mai multe categorii, vom folosi categorical_crossentropy ca funcția noastră de pierdere.

adam = optimizers.Adam(lr = learning_rate)
model.compile(optimizer = adam, loss="categorical_crossentropy", metrics = ['accuracy'])    print(model.summary()

Funcții de apel invers

Funcțiile de apel invers sunt acele funcții care sunt apelate după fiecare epocă în timpul procesului de instruire. Vom folosi următoarele funcții de apel invers:

  1. ReduceLROnPlateau: Antrenarea unei rețele neuronale poate uneori să platească și nu mai vedem niciun progres în această etapă. Prin urmare, această funcție monitorizează pierderea de validare pentru semnele unui platou și apoi modifică rata de învățare cu factorul specificat dacă este detectat un platou.
lr_reducer = ReduceLROnPlateau(monitor="val_loss", factor=0.9, patience=3)

2. EarlyStopping: Uneori, progresul se oprește în timpul antrenamentului unei rețele neuronale și nu mai vedem nicio îmbunătățire a preciziei validării (în acest caz). Majoritatea timpului, aceasta înseamnă că rețeaua nu va converge mai departe și că nu are rost să continuăm procesul de instruire. Această funcție așteaptă un număr specificat de epoci și termină antrenamentul dacă nu se constată nicio modificare a parametrului.

early_stopper = EarlyStopping(monitor="val_acc", min_delta=0, patience=6, mode="auto")

3. ModelCheckpoint: Antrenarea rețelelor neuronale durează, în general, mult timp și orice se poate întâmpla în această perioadă care poate duce la pierderea tuturor variabilelor și greutăților. Crearea punctelor de control este un obicei bun, deoarece vă salvează modelul după fiecare epocă. În cazul în care antrenamentul dvs. se oprește, puteți încărca punctul de control și puteți relua procesul.

checkpointer = ModelCheckpoint('/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/Model/weights.hd5', monitor="val_loss", verbose=1, save_best_only=True)

E timpul să te antrenezi

Toată munca noastră grea este pe cale să fie pusă la încercare. Dar, înainte de a ne potrivi cu modelul, să definim câțiva parametri hiper.

epochs = 100batch_size = 64learning_rate = 0.001

Datele noastre vor trece prin model de 100 de ori și în loturi de 64 de imagini. Vom folosi 20% din datele noastre de instruire pentru a valida modelul după fiecare epocă.

model.fit(          train_data,          train_labels,          epochs = epochs,          batch_size = batch_size,          validation_split = 0.2,          shuffle = True,          callbacks=[lr_reducer, checkpointer, early_stopper]          )

Acum că rețeaua este pregătită, vă sugerez să mergeți și să terminați cartea pe care ați început-o sau să mergeți la fugă. Mi-a luat aproximativ o oră pe Google Colab.

Testați modelul

Vă amintiți setul privat pe care l-am stocat separat? Asta a fost chiar în acest moment. Acesta este momentul adevărului și aici vom culege rodul muncii noastre.

predicted_test_labels = np.argmax(model.predict(test_data), axis=1)test_labels = np.argmax(test_labels, axis=1)print ("Accuracy score = ", accuracy_score(test_labels, predicted_test_labels))

Ei bine, rezultatele au revenit și am obținut 63,167%. La prima vedere, nu este mult, dar am intrat în a noua poziție a Recunoașterea emoțiilor faciale competiția Kaggle.

1611402069 566 Cum am dezvoltat un CNN care recunoaste emotiile si a
Totuși, nu este mare lucru

Acum, loviți-vă pe spate și începeți să vă gândiți la modalitățile prin care puteți îmbunătăți acest model. Putem folosi hiper-parametri mai buni sau putem crea cu totul o arhitectură de rețea diferită pentru a obține acuratețe mai mari.

Salvați modelul

Salvați rapid modelul folosind model_de_json din keras.modele.

from keras.models import model_from_json
model_json = model.to_json()with open("/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/FERmodel.json", "w") as json_file:    json_file.write(model_json)# serialize weights to HDF5model.save_weights("/content/gdrive/My Drive/Colab Notebooks/Emotion Recognition/FERmodel.h5")print("Saved model to disk")

Înfășurând totul

Am început prin definirea unui mecanism de încărcare și încărcarea imaginilor. Apoi am creat un set de antrenament și un set de testare. Apoi am definit un model fin și am definit câteva funcții de apel invers. Am trecut peste componentele de bază ale unei rețele neuronale convoluționale și apoi ne-am instruit rețeaua.

Am extins acest proiect prin crearea unei aplicații python care este capabilă să detecteze fețele și să le recunoască emoțiile în timp real. Acest lucru va fi acoperit într-o postare ulterioară.

Tocmai am realizat ceva care făcea parte din science fiction acum câteva decenii. Cu toate acestea, mai sunt multe de învățat. Internetul ne oferă o mulțime de informații pentru a crea și a învăța în mod constant. Fie ca învățătura să nu înceteze niciodată.