选自xolmon
机器之心编译
参与:Hitomi
情人节过了,单身狗也想飙一把车。在这篇技术博客(资源的真义)中,日本开发者使用深度学习框架实现了根据图片检索 AV 女优的功能。
![[译] 如何使用深度学习框架查找女Q*资源?](http://www.codesec.net/app_attach/201702/17/20170217_422_534817_0.jpg!web)
开发环境:
PC: MacBook Air
CPU: 1.4 GHz Intel Core i5
内存: 4GB
普通的 MacBook Air 就可以实现这一程序,但是学习速度缓慢,由内存不足导致的各种 Crash 让开发工作变得非常痛苦。
0. 简单的流程(1) 收集各女优的图片
(2) 使用 dlib 提取面部图像并调整为 96*96 的大小
(3) 使用数据扩张(Data Augmentation)将女优面部图像的数据扩张到 1000 张
(4) 将数据转换为 numpy 文件
(5) 使用 Chainer 进行面部图像的学习
(6) 在完成学习后的模型下,对任意图片进行预测
1. 收集女优图片这段有很多方法但是并不好写出来,所以请略过。只介绍下可以使用 python 的 Beautiful Soup4 从网页上批量抓取数据。
将取得的女优图片按名字建立存储目录。
./folder|--- /actress1
| |--- image1.jpg
| |--- image2.jpg
| |--- image3.jpg
|
|--- /actress2
| .
| .
|--- /actress3
.
.
. 2. 使用 dlib 提取面部图像
说到图像识别,OpenCV 应该更加有名。不过在提取面部图像部分,dlib 程序库的误识别比较少,因此这里使用 dlib 可以更加精确。
使用 OpenCV 和 dlib 进行人脸识别的比较可以参考这个视频:dlib vs OpenCV face detection (https://www.youtube.com/watch?v=LsK0hzcEyHI)(译者注:youtube 的视频,有必要的话可以搬过来。)
dlib 不仅可以提取面部图像,也有识别眼睛,鼻子,脸形等要素的机能。
importosimport sys
import glob
import cv2
from PIL import Image
import dlib
"""
INPUT_DIR是收集的女优图片所在的目录名
OUTPUT_DIR是提取后的图片存放的目录名(文件夹的构成与INPUT_DIR一样)
"""
detector = dlib . get_frontal_face_detector()
# 取得各女优的目录列表
dir_list = os . listdir(INPUT_DIR)
for i, dir_name in enumerate(dir_list):
if not os . path . exists(os . path . join(OUTPUT_DIR, dir_name)):
os . mkdir(os . path . join(OUTPUT_DIR, dir_name))
image_files = glob . glob(os . path . join(INPUT_DIR, dir_name, "*.jpg"))
for j, image_file in enumerate(image_files):
img = cv2 . imread(image_file)
dets = detector(img, 1)
open_img = Image . open(image_file)
for k, d in enumerate(dets):
# 丢弃尺寸小于80的图像
if d . right() - d . left() < 80 or d . bottom() - d . top() < 80:
continue
image_file = image_file . replace(INPUT_DIR, OUTPUT_DIR)
# 如果一张图中提取了多个人脸,则进行重命名
output_file = image_file . replace('.jpg', '_' + str(k) + '.jpg')
cropped_img = open_img . crop((d . left(), d . top(), d . right(), d . bottom()))
cropped_img . resize((96,96)) . save(output_file, 'JPEG', quality = 100, optimize = True)
参考资料:dlib.net face_detect.py (http://dlib.net/face_detector.py.html)
3. 数据扩张 (Data augmentation)在深度学习的过程中,如果数据量不够大,可以人工增加训练集的大小。通过平移, 翻转, 加噪声等方法从已有数据中创造出一批"新"的数据,这就是数据扩张 (Data augmentation)。
![[译] 如何使用深度学习框架查找女Q*资源?](http://www.codesec.net/app_attach/201702/17/20170217_422_534817_1.jpg!web)
![[译] 如何使用深度学习框架查找女Q*资源?](http://www.codesec.net/app_attach/201702/17/20170217_422_534817_2.jpg!web)
![[译] 如何使用深度学习框架查找女Q*资源?](http://www.codesec.net/app_attach/201702/17/20170217_422_534817_3.jpg!web)
4. 将数据转换为 numpy 格式 import os
import sys
import glob
import random
import numpy as np
from scipy import misc
""" 从选择的目录里提取文件 """
def load_data_from_dir(input_dir_name, input_dir_list, start_index, test_freq):
train_list = []
test_list = []
for dir_index, dir_name in enumerate(input_dir_list):
image_files = glob.glob(os.path.join(input_dir_name, dir_name, "*.jpg"))
train_count = 0
test_count = 0
print('directory:{} index:{}'.format(dir_name, dir_index + start_index))
for file_index, file_name in enumerate(image_files):
image = misc.imread(file_name)
label = np.int32(dir_index + start_index)
if not file_index % test_freq == 0: # set train datq
train_list.append((dir_name, image, label))
train_count += 1
else:
test_list.append((dir_name, image, label))
test_count += 1
print("directory:{} total:{} train:{} test:{}".format(
dir_name, train_count + test_count, train_count, test_count))
return train_list, test_list
""" 将数据储存为numpy格式 """
def save_dataset_numpy(data_list, image_path, label_path):
image_list = []
label_list = []
for _, image, label in data_list:
image_list.append(image)
label_list.append(label)
image_data = np.array(image_list, dtype=np.float32)
label_data = np.array(label_list, dtype=np.int32)
np.save(image_path, image_data)
np.save(label_path, label_data)
for i in xrange(0, len(DIR_LIST), 10):
# 生成10个分类的文件
train_list, test_list = load_data_from_dir(INPUT_DIR, dir_list[i:i+args.interval], i, 10)
train_data_path = os.path.join(OUTPUT_DIR, 'train', 'data-{}.npy'.format(i+args.interval))
train_label_path = os.path.join(OUTPUT_DIR, 'train', 'label-{}.npy'.format(i+args.interval))
test_data_path = os.path.join(OUTPUT_DIR, 'test', 'data-{}.npy'.format(i+args.interval))
test_label_path = os.path.join(OUTPUT_DIR, 'test', 'label-{}.npy'.format(i+args.interval))
save_dataset_numpy(train_list, train_data_path, train_label_path)
save_dataset_numpy(test_list, test_data_path, test_label_path) 5. 使用 Chainer 进行面部图像的学习
一开始打算使用 Tensorflow 做,不过由于自己想实现不少额外的机能,因此改用 Chainer 进行。
最初的学习,建立了一个 Cifar-10 (http://www.cs.toronto.edu/~kriz/cifar.html)(一般物品的 10 个分类)的学习方法,来对实际收集到的数据进行学习。
失败的地方:
最初是打算使用多进程来构建程序,不过 Debug 非常的辛苦,觉得还是先构建一个更简单的程序比较好。
如果一开始就读取所有图像,导入的图像会占用 1.7GB 的内存,导致死机。由于这个原因,创建了一个 BatchIterator 类,每 Batch 删除一次数据来释放内存,防止程序出现混乱。
// 每张图片的大小96×96×3 = 27648(byte)
// 每类图片的大小
27648×1000 = 27648000(byte) = 26.4(MB)
// 所有图片 (66类) ... 可以计算么?
26.4×66 = 1742.4(MB) = 1.7(GB)
"""
Batch iterator class
Usage:
batch_iter = BatchIter(DATA_DIR, 100)
for batch_data, batch_label in batch_iter:
batch_start_time = time.time()
x = np.asarray(batch_data, dtype=np.float32).transpose((0, 3, 1, 2))
t = np.asarray(train_batch_label, dtype=np.int32)
x = Variable(xp.asarray(x))
t = Variable(xp.asarray(t))
optimizer.update(model, x, t)
"""
class BatchIter (object):
def __init__ (self, data_dir, batch_size):
self . index = 0
self . batch_size = batch_size
self . data_files = glob . glob(os . path . join(data_dir, 'data-*.npy'))
self . label_files = glob . glob(os . path .