百度生物特征识别大赛-初赛

写在前面

本文是18年8月参加科赛网的《首届生物特征识别大赛》初赛阶段相关整理。内容包括采用dlib进行人脸检测及对其的预处理,构建resnet进行人脸识别训练。

目前成绩是,13/34,已经进入复赛阶段。

使用的深度学习平台是百度paddlepaddle.v2。

先吐槽下这个API做的稀烂,相关说明文档也不够完善。

数据预处理

图片统一裁剪为64x64,经过人脸检测与对齐处理。

对于训练集,若未检测到人脸,或检测人脸置信度不高,则抛弃该张图片。若检测多张人脸,选取距离中心最近,并检测置信度。

对于测试集,若未检测到人脸或置信度不高,则强制中心裁剪。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#coding:utf-8
'''
模型下载地址 http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
'''
import dlib
import os,sys
import math
import numpy as np

rootpath_test='/mnt/datasets/WebFace_fusai/testing_set/'
savepath_test='/home/kesci/work/first_round/first_round_test/'

rootpath_train='/mnt/datasets/WebFace_fusai/train_set/'
savepath_train='/home/kesci/work/first_round/first_round_train/'

rootpath_val='/mnt/datasets/WebFace_fusai/validate_set/'
savepath_val='/home/kesci/work/first_round/first_round_validate/'


detector=dlib.get_frontal_face_detector()
sp=dlib.shape_predictor('/home/kesci/work/shape_predictor_68_face_landmarks.dat')

def CheckFace(dets,height,width):
num=len(dets)
center_x=width/2
center_y=height/2
dist=[]
for i in range(num):
c_x=dlib.center(dets[i]).x
c_y=dlib.center(dets[i]).y
distance=math.pow(c_x-center_x,2)+math.pow(c_y-center_y,2)
dist.append(distance)
return dist.index(min(dist))

#该部分代码应用于test文件夹,对于val与train应使用下面被注释部分

for class_num in os.listdir(rootpath_test):
if class_num=='.DS_Store':
continue
imgpath=rootpath_test+class_num
img=dlib.load_rgb_image(imgpath)
faces=dlib.full_object_detections()
#检测人脸,若超过1张,选取中心最近,若没检测到,中心裁剪
dets,scores,idx=detector.run(img,1,-1)
index=0
if len(dets)>1:
index=CheckFace(dets,img.shape[0],img.shape[1])
if len(dets)==1:
index=0

if len(dets)==0 or scores[index]<0.75:
#强制中心裁剪
img=img[50:200,50:200]
img=dlib.resize_image(img,128,128)
dlib.save_image(img,savepath_test+class_num)
continue

#人脸对齐
for detection in dets:
faces.append(sp(img,detection))
images = dlib.get_face_chips(img, faces, size=128)
dlib.save_image(images[index],savepath_test+class_num)
print "finish test file"


for class_num in os.listdir(rootpath_train):
if class_num=='.DS_Store':
continue
imgclass=rootpath_train+class_num+'/'
if not os.path.exists(savepath_train+class_num):
os.mkdir(savepath_train+class_num)
for img_name in os.listdir(imgclass):
imgpath=imgclass+img_name
img=dlib.load_rgb_image(imgpath)
faces=dlib.full_object_detections()
#检测人脸,若超过1张,选取中心最近,若没检测到,中心裁剪
dets,scores,idx=detector.run(img,1,-1)
index=0
if len(dets)>1:
index=CheckFace(dets,img.shape[0],img.shape[1])
if len(dets)==1:
index=0

if len(dets)==0 or scores[index]<0.75:
#强制中心裁剪
img=img[50:200,50:200]
img=dlib.resize_image(img,128,128)
dlib.save_image(img,savepath_train+class_num+'/'+img_name)
continue

#人脸对齐
for detection in dets:
faces.append(sp(img,detection))
images = dlib.get_face_chips(img, faces, size=128)
dlib.save_image(images[index],savepath_train+class_num+'/'+img_name)
print "finish train file"

for class_num in os.listdir(rootpath_val):
if class_num=='.DS_Store':
continue
imgclass=rootpath_val+class_num+'/'
if not os.path.exists(savepath_val+class_num):
os.mkdir(savepath_val+class_num)
for img_name in os.listdir(imgclass):
imgpath=imgclass+img_name
img=dlib.load_rgb_image(imgpath)
faces=dlib.full_object_detections()
#检测人脸,若超过1张,选取中心最近,若没检测到,中心裁剪
dets,scores,idx=detector.run(img,1,-1)
index=0
if len(dets)>1:
index=CheckFace(dets,img.shape[0],img.shape[1])
if len(dets)==1:
index=0

if len(dets)==0 or scores[index]<0.75:
#强制中心裁剪
img=img[50:200,50:200]
img=dlib.resize_image(img,128,128)
dlib.save_image(img,savepath_val+class_num+'/'+img_name)
continue

#人脸对齐
for detection in dets:
faces.append(sp(img,detection))
images = dlib.get_face_chips(img, faces, size=128)
dlib.save_image(images[index],savepath_val+class_num+'/'+img_name)
print "finish val file"

构建数据集

主要是对数据集进行均衡处理,默认50张为基准线。对低于50张的类别,进行随机上采样完成数据扩充。

利用paddle构建DL网络

由于数据量少,且官方在初赛阶段只提供GPU,因而采用resent结构进行网络训练。

1
2
3
4
5
6
#coding: utf-8
import paddle.v2 as paddle
import numpy as np
from PIL import Image
import os, sys
from multiprocessing import cpu_count

对于v2版本的paddle使用说明,主要包括几个大部分。

网络构建

构建resnet_network,定义损失函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def resnet(ipt,class_dim=1017):
def conv_bn_layer(input,
ch_out,
filter_size,
stride,
padding,
active_type=paddle.activation.Relu(),
ch_in=None):
tmp = paddle.layer.img_conv(
input=input,
filter_size=filter_size,
num_channels=ch_in,
num_filters=ch_out,
stride=stride,
padding=padding,
act=paddle.activation.Linear(),
bias_attr=False)
return paddle.layer.batch_norm(input=tmp,
act=active_type,
moving_average_fraction=0.999)

def shortcut(ipt, ch_in, ch_out, stride):
if ch_in != ch_out:
return conv_bn_layer(ipt, ch_out, 1, stride, 0, paddle.activation.Linear())
else:
return ipt

def basicblock(ipt, ch_in, ch_out, stride):
tmp = conv_bn_layer(ipt, ch_out, 3, stride, 1)
tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, paddle.activation.Linear())
short = shortcut(ipt, ch_in, ch_out, stride)
return paddle.layer.addto(input=[tmp, short], act=paddle.activation.Relu())

def layer_warp(block_func, ipt, ch_in, ch_out, count, stride):
tmp = block_func(ipt, ch_in, ch_out, stride)
for i in range(1, count):
tmp = block_func(tmp, ch_out, ch_out, 1)
return tmp

# resnet
n = 1
feature_maps = 128
ipt_bn = ipt - 128.0
conv1 = conv_bn_layer(ipt_bn, ch_in=1, ch_out=4, filter_size=3, stride=1, padding=1)
res1 = layer_warp(basicblock, conv1, 4, 8, n, 1)
res2 = layer_warp(basicblock, res1, 8, 16, n, 2)
res3 = layer_warp(basicblock, res2, 16, 32, n, 2)
res4 = layer_warp(basicblock, res3, 32, 64, n, 2)
res5 = layer_warp(basicblock, res4, 64, feature_maps, n, 2)

pool = paddle.layer.img_pool(input=res5, name='pool', pool_size=4, stride=1, pool_type=paddle.pooling.Avg())
fc = paddle.layer.fc(input=pool, size=class_dim, act=paddle.activation.Softmax())
return pool, fc

def resnet_loss(datadim,num_class):
img=paddle.layer.data(name='image',type=paddle.data_type.dense_vector(datadim))
label=paddle.layer.data(name='label',type=paddle.data_type.integer_value(num_class))
fea,fc=resnet(img,class_dim=num_class)
cost = paddle.layer.classification_cost(input=fc, label=label)
return cost

数据读入reader

paddle通过reader进行数据读取操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def train_mapper(sample):
path,label=sample
img=paddle.image.load_image(path,is_color=False)
img=img.reshape(img.shape[0],img.shape[1],1)
img=paddle.image.to_chw(img)
img=img.flatten().astype('float32')
return img,label

def test_mapper(sample):
path,label=sample
img=paddle.image.load_image(path,is_color=False)
img=img.reshape(img.shape[0],img.shape[1],1)
img=paddle.image.to_chw(img)
img=img.flatten().astype('float32')
return img,label

def train_reader(train_list,buffered_size=4000):
def reader():
with open(train_list,'r') as f:
lines = [line.strip() for line in f]
for line in lines:
img_path, lab = line.strip().split('|')
yield img_path, int(lab)
return paddle.reader.xmap_readers(train_mapper, reader,
cpu_count(), buffered_size)

def test_reader(test_list,buffered_size=4000):
def reader():
with open(test_list,'r') as f:
lines = [line.strip() for line in f]
for line in lines:
img_path, lab = line.strip().split('|')
yield img_path, int(lab)
return paddle.reader.xmap_readers(test_mapper, reader,
cpu_count(), buffered_size)

进行训练

该部分包括,读取mini-batch,进行shuffle,设置优化选项。定义事件处理函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
optimizer = paddle.optimizer.Momentum(momentum=0.9,
regularization=paddle.optimizer.L2Regularization(
rate=0.0002*128),
learning_rate=0.01/128,
learning_rate_decay_a=0.1,
learning_rate_decay_b=60000*10,
learning_rate_schedule="discexp" )

def resnet_train(datadim,
num_class,
num_passes,
model_save_dir,
trainpath,testpath,readfromtar=False):
cost=resnet_loss(datadim,num_class)
with open(model_save_dir,'r') as f:
parameters=paddle.parameters.Parameters.from_tar(f)
#parameters=paddle.parameters.create(cost)

trainreader = paddle.batch(paddle.reader.shuffle(train_reader(trainpath),
buf_size=40000),
batch_size=256)

testreader = paddle.batch(test_reader(testpath),batch_size=128)

trainer = paddle.trainer.SGD(cost=cost,
parameters=parameters,
update_equation=optimizer)
feeding={'image':0,'label':1}

def event_handler(event):
min_cost=0.446815
if isinstance(event,paddle.event.EndIteration):
if event.batch_id % 2==0:
print "\nPass %d, Batch %d, Cost %f, %s" %(
event.pass_id,event.batch_id,event.cost,event.metrics)
if event.batch_id % 10==0:
if event.cost < min_cost:
min_cost = event.cost
with open(model_save_dir,'w') as f:
trainer.save_parameter_to_tar(f)

if isinstance(event,paddle.event.EndPass):
#测试准确率
result=trainer.test(reader=testreader,feeding=feeding)
print "\nTest with Pass %d, Cost %f,, %s" %(
event.pass_id,result.cost, result.metrics)

trainer.train(reader=trainreader,
event_handler=event_handler,
num_passes=num_passes,
feeding=feeding)

parameters_path = "/home/kesci/work/model/model.tar"
imageSize=64
datadim=1*imageSize*imageSize
num_class=1017
paddle.init(use_gpu=False, trainer_count=4)
trainpath='/home/kesci/work/train.txt'
testpath='/home/kesci/work/val.txt'
resnet_train(datadim,num_class,100,parameters_path,trainpath,testpath,True)

数据提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import paddle.v2 as paddle
import numpy as np
from PIL import Image
import os, sys
from multiprocessing import cpu_count
import csv

paddle.init(use_gpu=0,trainer_count=1)
def resnet(ipt,class_dim=1258):
def conv_bn_layer(input,
ch_out,
filter_size,
stride,
padding,
active_type=paddle.activation.Relu(),
ch_in=None):
tmp = paddle.layer.img_conv(
input=input,
filter_size=filter_size,
num_channels=ch_in,
num_filters=ch_out,
stride=stride,
padding=padding,
act=paddle.activation.Linear(),
bias_attr=False)
return paddle.layer.batch_norm(input=tmp, act=active_type, moving_average_fraction=0.999)

def shortcut(ipt, ch_in, ch_out, stride):
if ch_in != ch_out:
return conv_bn_layer(ipt, ch_out, 1, stride, 0, paddle.activation.Linear())
else:
return ipt

def basicblock(ipt, ch_in, ch_out, stride):
tmp = conv_bn_layer(ipt, ch_out, 3, stride, 1)
tmp = conv_bn_layer(tmp, ch_out, 3, 1, 1, paddle.activation.Linear())
short = shortcut(ipt, ch_in, ch_out, stride)
return paddle.layer.addto(input=[tmp, short], act=paddle.activation.Relu())

def layer_warp(block_func, ipt, ch_in, ch_out, count, stride):
tmp = block_func(ipt, ch_in, ch_out, stride)
for i in range(1, count):
tmp = block_func(tmp, ch_out, ch_out, 1)
return tmp

# resnet
n = 1
feature_maps = 256
ipt_bn = ipt - 128.0
conv1 = conv_bn_layer(ipt_bn, ch_in=1, ch_out=4, filter_size=3, stride=1, padding=1)
res1 = layer_warp(basicblock, conv1, 4, 8, n, 1)
res2 = layer_warp(basicblock, res1, 8, 16, n, 2)
res3 = layer_warp(basicblock, res2, 16, 32, n, 2)
res4 = layer_warp(basicblock, res3, 32, 64, n, 2)
res5 = layer_warp(basicblock, res4, 64, feature_maps, n, 2)

pool = paddle.layer.img_pool(input=res5, name='pool', pool_size=4, stride=1, pool_type=paddle.pooling.Avg())
drop = paddle.layer.dropout(input=pool,dropout_rate=0.5)
fc = paddle.layer.fc(input=drop, size=class_dim, act=paddle.activation.Softmax())
return pool, fc

def resnet_loss(datadim,num_class):
img=paddle.layer.data(name='image',type=paddle.data_type.dense_vector(datadim))
label=paddle.layer.data(name='label',type=paddle.data_type.integer_value(num_class))
fea,fc=resnet(img,class_dim=num_class)
cost = paddle.layer.classification_cost(input=fc, label=label)
return cost


def val(datadim,num_class,model_path):
img=paddle.layer.data(name='image',type=paddle.data_type.dense_vector(datadim))
label=paddle.layer.data(name='label',type=paddle.data_type.integer_value(num_class))
fea,fc=resnet(img,class_dim=num_class)

def load_image(file):
img1=paddle.image.load_image(file,is_color=False)
img1=img1.reshape(img1.shape[0],img1.shape[1],1)
img1=paddle.image.resize_short(img1,64)
img1=img1.reshape(img1.shape[0],img1.shape[1],1)
img1=paddle.image.to_chw(img1)
img1=img1.flatten().astype('float32')
return img1

def cosine_distance(vector1,vector2):
vector1=vector1.reshape([-1])
vector2=vector2.reshape([-1])
cos_dist=np.dot(vector1,vector2)/(np.linalg.norm(vector1)*np.linalg.norm(vector2))
score=0.5*cos_dist+0.5
return score
with open(model_path,'r') as f:
parameters=paddle.parameters.Parameters.from_tar(f)

infile=open('/mnt/datasets/WebFace/first_round/first_round_pairs_id.txt','r')
csv_file_path='/home/kesci/work/myresult.csv'
img_root_path='/home/kesci/work/first_round_test/'
testdata1,testdata2=[],[]
fea1,fea2=[],[]
img_pair=[]
count=0
for pairname in infile.readlines():
if pairname=='pairs_id\n':
continue
count+=1
img_pair.append(pairname.strip())
img_name1,img_name2=pairname.strip().split('_')
img_path1=img_root_path+img_name1+'.jpg'
img_path2=img_root_path+img_name2+'.jpg'
#print img_path1,img_path2
img1=load_image(img_path1)
img2=load_image(img_path2)

testdata1.append([img1])
testdata2.append([img2])

if count % 100 ==0:
for i in paddle.infer(output_layer=fea,parameters=parameters,input=testdata1):
fea1.append(i)
for i in paddle.infer(output_layer=fea,parameters=parameters,input=testdata2):
fea2.append(i)
testdata1,testdata2=[],[]

num_feature=len(fea1)
score=[cosine_distance(np.asarray(fea1[i]),np.asarray(fea2[i])) for i in range(num_feature)]

csvFile = open(csv_file_path,'wb') # 设置newline,否则两行之间会空一行
writer = csv.writer(csvFile)
writer.writerow(['submit_pairsID','prob'])
for i in range(num_feature):
writer.writerow([img_pair[i],score[i]])
csvFile.close()
infile.close()

datadim=1*64*64
num_class=1258
model_path='/home/kesci/work/upload_model/classification.tar'
val(datadim,num_class,model_path)

本文标题:百度生物特征识别大赛-初赛

文章作者:Lumo Wang

发布时间:2018年08月12日 - 20:08

最后更新:2018年11月07日 - 15:11

原始链接:https://luameows.github.io/2018/08/12/比赛-百度生物特征识别大赛-初赛/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

请我喝杯咖啡,我会继续熬夜的~