Мужчина OR Женщина (python/keras)
Здравствуйте, дорогие друзья, хабрчане. Меня зовут Илья, я тут новенький, и сегодня, я расскажу вам, как я решил научить нейронную сеть различать пол человека по фотографии лица.
Первым делом нам понадобятся сам датасет. Датасетов в интернете огромное количество, но ни один мне не подошел. И по этому, я решил сделать свой датасет.
Находим источник для фото — у меня это был сайт, где генерируются лица людей, которые никогда не существовали и с каждым обновлением лицо меняется — 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]
)
)
)
Вот фото которые получились.
Казалось бы, миссия выполнена. Но можно по другому. Можно использовать другую архитектуру. В чем будет разница: так-как у нас всего 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