Rilevamento e riconoscimento facciale in grado di battere gli umani

Contenuti

Questo articolo è stato pubblicato nell'ambito del Blogathon sulla scienza dei dati

La creazione del riconoscimento facciale è considerata un compito molto semplice nel campo della visione artificiale., ma è estremamente difficile avere una pipeline in grado di prevedere volti con sfondi complessi quando si hanno più volti, diverse condizioni di illuminazione e diverse scale dell'immagine. Questo blog descriverà come creiamo un modello che in alcuni casi può superare gli umani. Il nostro set di dati è composto da 3 Lezioni (Non posso condividere i dati a causa di problemi di riservatezza, ma ti mostrerò com'è). Classe 1 it Jesse Eisenberg (attore), classe 2 è Mila Kunis (pop star) e la classe 0, Chiunque. Ecco com'era il nostro treno (80 immagini) e dati di prova (più di 1800 immagini).

33114cattura-7668773

Questi sono i nostri dati di prova e i volti estratti da quelle immagini, questi dati sono estremamente complessi a causa di più facce, sfondi complessi e molte immagini pixelate. In secondo luogo, i nostri dati del treno sono estremamente puliti come mostrato nell'immagine qui sotto. Abbiamo molte differenze nella distribuzione dei dati di prova e di addestramento. Abbiamo bisogno di una tecnica che possa generalizzare bene indipendentemente dal numero di campioni necessari e da quanto diversi siano i dati del treno e dei test.

84116cattura-9941361

La tecnica che useremo per questo compito è, primo, generar la incrustación facial a partir de un modelo de apprendimento profondo y luego aplicar un clasificador simple.

Utilizzo di FACENET

Per spingere davvero i limiti del rilevamento dei volti, vedremo alcuni metodi all'avanguardia. Le moderne tecniche di estrazione del viso hanno fatto uso di Deep Convolution Networks. Come sappiamo tutti, le funzionalità create dai moderni framework di deep learning sono in realtà migliori della maggior parte delle funzionalità costruite a mano. controlliamo 4 modelli di apprendimento profondo, vale a dire, FaceNet (Google), DeepFace (Facebook), VGGFface (Oxford) e OpenFace (CMU). Di questi 4 Modelli FaceNet ci stava dando il miglior risultato. Generalmente, FaceNet offre risultati migliori degli altri 3 Modelli.

FaceNet è considerato un modello di nuova generazione sviluppato da Google. Si basa sul livello iniziale, spiegare l'architettura completa di FaceNet va oltre lo scopo di questo blog. Di seguito è riportata l'architettura FaceNet. FaceNet usa módulos de inicio en bloques para reducir la cantidad de parametri entrenables. Questo modello acquisisce immagini RGB di 160 × 160 e genera un incorporamento di dimensioni 128 per una foto. Per questa implementazione, avremo bisogno di un paio di funzioni aggiuntive. Pero antes de enviar la imagen de la cara a FaceNet, necesitamos extraer las caras de las imágenes.

31307cattura-2100659
39330cattura-4390412
rivelatore = dlib.cnn_face_detection_model_v1("../input/pretrained-models-faces/mmod_human_face_detector.dat")

def rect_to_bb(Rect):
    # Prendere un limite previsto da DLIB e convertirlo
    # al formato (X, e, w, h) come faremmo normalmente
    # with OpenCV
    x = rect.rect.left()
    y = rect.rect.top()
    w = rect.rect.right() - x
    h = rect.rect.bottom() - e

    # restituire una tupla di (X, e, w, h)
    Restituzione (X, e, w, h)

def dlib_corrected(dati, data_type="treno"):
    #We set the size of the image
    dim = (160, 160)
    data_images=[]
    #If we are processing training data we need to keep track of the labels
    if data_type=='train':
        data_labels=[]
    #Loop over all images
    for cnt in range(0,len(dati)):
        immagine = dati['img'][cnt]
        #The large images are resized
        if image.shape[0] > 1000 e image.shape[1] > 1000:
            immagine = cv2.resize(Immagine, (1000,1000), interpolazione = cv2.INTER_AREA)
        #The image is converted to grey-scales
        gray = cv2.cvtColor(Immagine, cv2.COLOR_BGR2GRAY)
        #Detect the faces
        rects = detector(grigio, 1)
        sub_images_data = []
        #Loop over all faces in the image
        for (io, Rect) in enumerare(rects):
            #Convertire il rettangolo di selezione in spigoli
            (X, e, w, h) = rect_to_bb(Rect)
            #Here we copy and crop the face out of the image
            clone = image.copy()
            Se(X>=0 e y>=0 e w>=0 e h>=0):
                crop_img = clone[e:y+h, X:x+w]
            altro:
                crop_img = clone.copy()
            #We resize the face to the correct size
            rgbImg = cv2.resize(crop_img, Oscuro, interpolazione = cv2.INTER_AREA)
            #In the test set we keep track of all faces in an image
            if data_type == 'train':
                sub_images_data = rgbImg.copy()
            altro:
                sub_images_data.append(rgbImg)
        #If no face is detected in the image we will add a NaN
        if(len(rects)==0):
            se data_type == 'treno':
                sub_images_data = np.empty(Oscuro + (3,))
                sub_images_data[:] = np.nan
            if data_type=='test':
                nan_images_data = np.empty(Oscuro + (3,))
                nan_images_data[:] = np.nan
                sub_images_data.append(nan_images_data)
        #Qui aggiungiamo l'immagine(S) to the list we will return
        data_images.append(sub_images_data)
        #And add the label to the list
        if data_type=='train':
            data_labels.append(dati['classe'][cnt])
    #Lastly we need to return the correct number of arrays
    if data_type=='train':
        restituire np.array(data_images), np.array(data_labels)
    altro:
        restituire np.array(data_images)

USANDO DLIB

DLIB es un modelo ampliamente utilizado para detectar rostros. En nuestros experimentos, abbiamo scoperto che dlib produce risultati migliori di HAAR, anche se notiamo che alcuni miglioramenti possono ancora essere apportati:

  • Se i bordi della faccia del rettangolo vengono spostati fuori dall'immagine, prendiamo l'intera immagine invece del ritaglio del viso. È implementato come segue:
    • e (X> = 0 e e> = 0 y w> = 0 eh> = 0):
      • crop_img = clon[e:y+h, X:x+w]
    • il riposo:
  • Per le immagini di prova, invece di salvare un volto per immagine, salviamo tutti i volti per la previsione.
  • Invece di un rilevatore basato su HOG, possiamo usare un rilevatore basato sulla CNN. Come questi miglioramenti sono progettati per ottimizzare il tuo utilizzo con FaceNet, definiremo un nuovo rilevamento del volto corretto.

Il blocco di codice sopra estrae i volti dall'immagine, per molte immagini abbiamo diverse facce, quindi dobbiamo mettere tutte quelle facce in una lista. Per estrarre le facce che stiamo usando dlib.cnn_face_detection_model_v1, nota che non dovresti inserire immagini di dimensioni molto grandi in questo, altrimenti otterrai un errore di memoria dlib. Se un'immagine non ha un volto, immagazzina NaN in quei posti. Let's FaceNet queste immagini di dati ora. La pre-elaborazione di cui sopra è necessaria solo per i dati di test, i dati del treno sono già puliti, cosa si può vedere nelle immagini sopra. Una volta che abbiamo finito di ottenere gli intarsi facciali dai dati del treno, ottenere intarsi facciali per i dati di test, pero primero debe usar el preprocesamiento proporcionado en el bloque de código anterior para extraer caras de los datos de prueba.

def get_embedding(modello, face_pixels):
    # scale pixel values
    face_pixels = face_pixels.astype('float32')
    # standardizzare i valori dei pixel su più canali (globale)
    Significare, std = face_pixels.mean(), face_pixels.std()
    face_pixels = (face_pixels - Significare) / standard
    # transform face into one sample
    samples = expand_dims(face_pixels, asse=0)
    # make prediction to get embedding
    yhat = model.predict(campioni)
    ritorno yhat[0]

modello = load_model('.. /input/pretrained-models-faces/facenet_keras.h5')

svmtrainX = []
per indice, face_pixels in enumerare(nuovoTrenoX):
    embedding = get_embedding(modello, face_pixels)
    svmtrainX.append(incorporamento)

Después de generar las incrustaciones para el addestramento y la prueba, useremo SVM per la classificazione. Perché SVM, Puoi chiedere? Con molta esperienza, Sono venuto a sapere che le funzioni basate su SVM + DL può superare qualsiasi altro metodo, anche ai metodi di deep learning, quando la quantità di dati è piccola.

from sklearn.svm import SVC
from sklearn.pipeline import make_pipeline
from sklearn.naive_bayes import GaussianNB
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import StandardScaler, MinMaxScaler, Normalizer

linear_model = make_pipeline(StandardScaler(), SVC(kernel="rbf", C=1,0, gamma=0,01, probabilità = Vero))
linear_model.fit(svmtrainX, svmtrainY)

Una vez que el SVM está entrenado, es hora de hacer algunas pruebas, pero nuestros datos de prueba tienen varias caras en una lista. Quindi, siempre que tengamos a Jesse o Mila en una imagen, ignoraremos la clase 0 y cuando tanto Jesse como Mila estén presentes en una imagen, entonces elegiremos la que nos brinde la mayor precisión.

predicitons=[]
per me in corrected_test_X:    
    flag=0
    if(len(io)==1):
        embedding = get_embedding(modello, io[0])
        tmp_output = linear_model.predict([incorporamento])
        predicitons.append(tmp_output[0])
    altro:
        tmp_sub_pred = []
        tmp_sub_prob = []
        per j in i:
            j= j.astype(int)
            embedding = get_embedding(modello, J)
            tmp_output = linear_model.predict([incorporamento])
            tmp_sub_pred.append(tmp_output[0])
            tmp_output_prob = linear_model.predict_log_proba([incorporamento])
            tmp_sub_prob.append(np.max(tmp_output_prob[0]))
            
        Se 1 in tmp_sub_pred e 2 a tmp_sub_pred:
            index_1 = np.where(np.array(tmp_sub_pred)==1)[0][0]
            index_2 = np.where(np.array(tmp_sub_pred)==2)[0][0]
            Se(tmp_sub_prob[indice_1] > tmp_sub_prob[indice_2] ):
                predicitons.append(1)
            altro:
                predicitons.append(2)
        elifa 1 non in tmp_sub_pred e 2 non in tmp_sub_pred:
            predicitons.append(0)
        elifa 1 in tmp_sub_pred e 2 non in tmp_sub_pred:
            predicitons.append(1)
        elifa 1 non in tmp_sub_pred e 2 a tmp_sub_pred:
            predicitons.append(2)

DISCUSSIONE

Osservazioni finali, questo è un set di dati molto piccolo, quindi i risultati possono cambiare enormemente anche quando si aggiungono o si rimuovono alcune immagini. Nel nostro test abbiamo scoperto che ci ha tradito molte volte, c'era in giro 20 immagini nel test che sono state erroneamente previste da noi ma correttamente dal nostro modello. Confermiamo il risultato atteso cercando quelle immagini su Google.

Le reti neurali profonde possono estrarre funzionalità più significative rispetto ai modelli di apprendimento automatico. tuttavia, il crollo di queste grandi reti è la necessità di una grande quantità di dati. Siamo riusciti ad affrontare questo problema utilizzando un modello precedentemente addestrato, un modello che è stato addestrato su un set di dati molto più ampio per conservare la conoscenza di come codificare le immagini facciali, che poi usiamo per i nostri scopi in questa sfida. Cosa c'è di più, La messa a punto di SVM ci ha davvero aiutato ad andare oltre la precisione del 95%.

Il supporto mostrato in questo articolo non è di proprietà di DataPeaker e viene utilizzato a discrezione dell'autore.

Iscriviti alla nostra Newsletter

Non ti invieremo posta SPAM. Lo odiamo quanto te.