Мужчина OR Женщина (python/keras)

Здравствуйте, дорогие друзья, хабрчане. Меня зовут Илья, я тут новенький, и сегодня, я расскажу вам, как я решил научить нейронную сеть различать пол человека по фотографии лица.

Первым делом нам понадобятся сам датасет. Датасетов в интернете огромное количество, но ни один мне не подошел. И по этому, я решил сделать свой датасет.

  1. Находим источник для фото — у меня это был сайт, где генерируются лица людей, которые никогда не существовали и с каждым обновлением лицо меняется — https://thispersondoesnotexist.com/

2)Теперь нужно закачать 2000 картинок с этого сайта, я буду для этого использовать следующий код:

import shutil
import time
import requests

while (i < 1000):
    url = 'https://thispersondoesnotexist.com/image'
    response = requests.get(url, stream=True)
    with open(str(i)+'.jpg', 'wb') as out_file:
        shutil.copyfileobj(response.raw, out_file)
    del response
    i = i + 1
    time.sleep(1)

Тут все просто, в любой папке создаем .py файл, добавляем туда этот код, и в консоли его вызываем, ждем 2000 сек. и гарантированно получаем 2к картинок.
Далее ручками (буквально) нужно набрать 500 картинок мужчин и женщин. Я делал следующим образом: открывал папку и выделял все фото мужчин через CTRL + клик, и вырезал в другую папку (конечно я отправлю ссылку на гидхаб где будут фото).

Теперь нам их нужно переименовать нормально: создаем функцию которая будет брать каждый файл из определенной папки и по номеру итерации цикла создает ему имя и переносит в другую папку, и так мы получаем файлы изобращений (0.jpg … 499.jpg)

import os as s
def renameFilesInNumbers(from_,to_, g=''):
    i = 0
    name1 = s.listdir('./'+from_)
    while(i < len(name1)):
    # while(i < 101):
        s.rename('ваш путь до папки'+from_+'/' +name1[i],'./'+to_+'/'+ ''+str(i)+g+'.jpg')
        i = i + 1 

Дальше нужно преобразовать эти фото в нужный формат: просто делаем их серыми и ставим разрешение 299×299.
Вот код:

import cv2
import os as s

def ResizeImags(from_,to_):
    i = 0
    name1 = s.listdir('ваш путь до папки'+from_)
    while( i < len(name1)):
        img = cv2.imread('ваш путь до папки'+from_+'/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED)
        gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        resized = cv2.resize(gray_image, (299, 299), interpolation = cv2.INTER_AREA)
        cv2.imwrite('./'+to_+'/'+str(i)+'.jpg',resized)
        i = i + 1

Далее мы создаем файл в google colab и пишем саму нейронку.

Архитектура максимально простая, была взята из примера про определение одежды:

model = Sequential()
model.add(Conv2D( 2, 3, activation='relu', padding="same", input_shape=(299,299,1)))
model.add(MaxPooling2D())
model.add(Conv2D( 2, 3, activation='relu', padding="same"))
model.add(MaxPooling2D())
model.add(Conv2D( 2, 3, activation='relu', padding="same"))
model.add(MaxPooling2D())
model.add(Flatten())
model.add(Dense(128,activation='relu'))
model.add(Dense(64,activation='relu'))
model.add(Dense(18,activation='relu'))
model.add(Dense(2,activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

А теперь, вспомним о нашем датасете, как из папки картинок сделать дата сет, ну честно говоря, никакой датасет мы делать не будем, просто из каждой папки сделаем массив картинок.

Выглядеть это будет так:

i = 0 
while (i < 500):
# тут все понятно , 500 фоток == 500 этераций 

  data.append( 
      np.array(cv2.imread('./M/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
  )
  # при каждой итерации добаляем изображение мужчины
  data.append(
      np.array(cv2.imread('./W/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
  )
  # женщины 
  data1.append( [0., 1.])
  # при каждой итерации добаляем вектор значения мужчины
  data1.append( [1., 0.])
  # женщины 
  i = i + 1

Вектор значения — это мой собственный термин, обозначающий преобразование любого значения, любой определенной формы в векторный формат.
Уверен , что для того , что я имею ввиду , есть свой термин , — я его просто не знаю.

Дальше просто указываю с какими параметрами будет компилироваться моя нейронная сеть, а именно: какая будет функция потерь (loss) — 'категориальная кросс энтропия', оптимизатор — 'adam', метрика которую будем видеть в момент обучения — точность.
model.compile (loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

Весь код:

from typing_extensions import Text
import keras
import numpy as np
import cv2
from keras.models import Sequential
from keras.layers import Dense, Dropout , Conv2D , Flatten, MaxPooling2D



data = []
data1 = []
i = 0 
while (i < 500):
  
  data.append( 
      np.array(cv2.imread('./M/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
  )
  data.append(
      np.array(cv2.imread('./W/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
      )
  data1.append( [0., 1.])
  data1.append( [1., 0.])
  i = i + 1

model = Sequential()
model.add(Conv2D( 2, 3, activation='relu', padding="same", input_shape=(299,299,1)))
model.add(MaxPooling2D())
model.add(Conv2D( 2, 3, activation='relu', padding="same"))
model.add(MaxPooling2D())
model.add(Flatten())
model.add(Dense(128,activation='relu'))
model.add(Dense(64,activation='relu'))
model.add(Dense(18,activation='relu'))
model.add(Dense(2,activation='softmax'))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(np.array(data), np.array(data1) ,      epochs=7 )


print(
    model.predict(
        np.array(
    [np.array(cv2.imread('./M/'+str(519)+'.jpg', cv2.IMREAD_UNCHANGED))/255]
        )
    )
)

print(
    model.predict(
        np.array(
    [np.array(cv2.imread('./M/'+str(541)+'.jpg', cv2.IMREAD_UNCHANGED))/255]
        )
    )
)

Вот фото которые получились.

9e9fa03aedfe710bb7073e217cdb8463.pngddc5c8b702a49d1b2e4b587f733fd602.png3cae1e6b1d032f1ea16ade54df628cf7.png35c6e6ab3944bccf28495ed8b0e5c42d.png

Казалось бы, миссия выполнена. Но можно по другому. Можно использовать другую архитектуру. В чем будет разница: так-как у нас всего 2 варианта, то можно вместо категориальной кросс энтропии использовать бинарную кросс энтропию.

Новый код:

import os
import keras
import numpy as np
import cv2
from keras.models import Sequential
from keras.layers import Dense, Dropout , Conv3D , Flatten, MaxPooling2D

data = [] 
data1 = []

i = 0 
while (i < 500):
  data.append( 
      np.array(cv2.imread('./M/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
  )
  data.append(
      np.array(cv2.imread('./W/'+str(i)+'.jpg', cv2.IMREAD_UNCHANGED))/255
  )
  data1.append( [1.])
  data1.append( [0.])
  i = i + 1

model = Sequential()
model.add(Conv2D( 2, 3, activation='relu', padding="same", input_shape=(299,299,1)))
model.add(MaxPooling2D())
model.add(Conv2D( 2, 3, activation='relu', padding="same"))
model.add(Flatten())
model.add(Dense(64, activation='relu'))

model.add(Dense(64, activation='relu'))

model.add(Dropout(0.5))

model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',

              optimizer='rmsprop',

              metrics=['accuracy'])

model.fit(np.array(data), np.array(data1) ,      epochs=10 )

Все тоже самое кроме функции активации, теперь она не softmax, а sigmoid, и функция потерь.

Надеюсь моя первая статья вам понравится, и новичкам она как-то поможет, всем удачи !

git: https://github.com/paradiseMaestro

© Habrahabr.ru