hans

hans

【Caffe】多标签训练、人脸属性多任务训练


前言#

参考了 https://zhuanlan.zhihu.com/p/22190532

细节有一些问题,我这里过一遍整个流程,涉及到的问题都会做讲解。包括部署问题,最后我也会讲讲。

文中涉及到的我用的代码都在这里: https://github.com/HansRen1024/Face-Attributes-MultiTask-Classification

正文#

去原作者 github 网站下载 https://github.com/HolidayXue/CodeSnap
这个项目。

将 convert_multilabel.cpp 放到 caffe/tools/ 目录下。

修改 81 行:

‘>>’ should be ‘> >’

然后注释掉 149 行。

命令行在 caffe / 下运行:

make clean
make all -j8
make py

我是用的 CelebA 数据集,都是人脸属性的。

下载地址: https://pan.baidu.com/s/17rp2gKqtvuT48yuPfJK3sA

在 Anno 目录下找到 list_attr_celeba.txt

先命令行运行:

sed -i 's/  / /g' list_attr_celeba.txt

将文档中两个空格都替换为一个空格。

然后可以通过下面代码提取你想要的人脸属性

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Mon Aug 20 16:57:52 2018

@author: hans
"""

ReadTxt = 'list_attr_celeba.txt'
WriteTxt = 'train.txt'
r = open(ReadTxt,'r')
w = open(WriteTxt,'w')
rLine = r.readline().split('\n')[0]
while rLine:
    rLine = r.readline().split('\n')[0]
    if not rLine:
        break
#    image,bangs,eyeglasses,gender,
    wLine = rLine.split(' ')[0]+' '+rLine.split(' ')[6]+' '+rLine.split(' ')[16]+' '+rLine.split(' ')[21]+'\n'
    w.write(wLine)
r.close()
w.close()

接着命令行运行:

sed -i 's/-1/0/g' train.txt

将 train.txt 文档中 - 1 改成索引 0。

对比了一下原图片,提取的属性是对的。

然后从 train.txt 拿出了一部分放到 val.txt 中

运行脚本,生成可供训练用的 lmdb 数据:

echo "Creating train lmdb..."
~/caffe-multi/build/tools/convert_multilabel \
-resize_height=227 \
-resize_width=227 \
-shuffle=false \
/home/hans/data/face/CelebA/Img/img_align_celeba/ \
train.txt \
./train_db \
./train_lb \
3

echo "Creating val lmdb..."
~/caffe-multi/build/tools/convert_multilabel \
-resize_height=227 \
-resize_width=227 \
-shuffle=false \
/home/hans/data/face/CelebA/Img/img_align_celeba/ \
val.txt \
./val_db \
./val_lb \
3

img_align_celeba 是切好人脸的数据集

后面参数 3 表示我这边提取了三种人脸属性。

最后是修改 mcnn_Attri.prototxt,均值,归一化,数据路径,还有一个很重要,backend 一定要改成 LMDB!

name: "MCNN_Attri"
layer {
  name: "data"
  type: "Data"
  top: "data"
  transform_param {
  	 scale: 0.007843
     mean_value: 127.5
     mean_value: 127.5
     mean_value: 127.5
     crop_size: 227     
  }  
  include {
    phase: TRAIN
  }
  data_param {
    source: "/home/hans/data/face/CelebA/attri/doc/train_db"
    batch_size: 192
    backend: LMDB
   }
}

layer {
  name: "labels"
  type: "Data"
  top: "labels"
  include {
     phase: TRAIN
  }
  data_param {
    source: "/home/hans/data/face/CelebA/attri/doc/train_lb"
    batch_size: 192
	backend: LMDB
  }
}

layer {
  name: "data"
  type: "Data"
  top: "data"
  transform_param {
  	scale: 0.007843
    mean_value: 127.5
    mean_value: 127.5
    mean_value: 127.5
    crop_size: 227   
  }
  include {
    phase: TEST
  }
  data_param {
    source: "/home/hans/data/face/CelebA/attri/doc/val_db"
    batch_size: 128
    backend: LMDB
  }
}

layer {
  name: "labels"
  type: "Data"
  top: "labels"
  include {
    phase: TEST
  }
  data_param {
    source: "/home/hans/data/face/CelebA/attri/doc/val_lb"
    batch_size: 128
    backend: LMDB
  }
}

layer {
  name: "sliceL"
  type: "Slice"
  bottom: "labels"
  top: "label_attr6"
  top: "label_attr16"
  top: "label_attr21"
  slice_param {
    slice_dim: 1  
	slice_point: 1
	slice_point: 2
  }
}

有三个输出,slice_point 删到 2。

【2018.08.22 更新】-----------------------------------------------

这个网络结构在 bn2 后分成了六组,每一组都有不同个输出。并且每一组前面都有结构一样的层,然后到各个任务的全连接层和最终输出层。将对应不需要的任务层和组删掉。对于我这三个类别,我发现 gender 是单独一组的,眼镜在第四组,刘海在第六组。为了减少参数量,我将眼镜和刘海都放到了第六组,删除了第四组的前面网络。第一组 gender 的我暂时没改,担心三个任务都放到一组里出现不收敛的情况。不过以后我肯定会做测试。

(训练输出很好,在验证集上准确率非常高,但是实际检测的时候发现对单独在一组的性别分别准确,对眼镜和刘海分别不准确。我下一步还是要将眼镜和刘海分别放在一组,并且将全连接层改成全局平均池化。)

(将刘海和眼镜分别放在了一组,同时删了全连接层。模型是变小了,只有 8M 了。验证集准确率还挺高,98% 左右,但是实际实验效果太差。接下来尝试换换核心网络,用 mobilenet
v2 或者 squeezenet 试试。同时我考虑到可能不一定是网络的原因,观察了一下数据集,有刘海和戴眼镜的图片很少。如果换网络也不行,那么下一步应该要对部分数据做 argument 了。)


【2018.08.29 更新】-----------------------------------------------

在验证集和测试集上结果都很美好,但是拿摄像头或者视频实际应用的时候效果差的离谱。

找到原因了,是我测试时候的预处理方式不对导致的。


【2018.09.04 更新】-----------------------------------------------

几点心得:

  1. 用全卷积 + 全局池化替代全连接层,效果影响不大,模型参数量不变。

2. 全连接层参数量大,但执行时间很快。也就是说全连接层对模型执行时间影响不大,对模型大小影响较大。

3. 查看了每一层的执行时间,发现大卷积核效率很低,最好都改用 3*3 的小卷积核。

4. 卷基层输出数量最好是 2^n。


最后改 loss_weight,所有任务的 loss_weight 加一起等于 1。我这里只有三个任务,所以 loss_weight 我都设置为 0.3333 了。

剩下的 solver.prototxt 我就不说了,跟单任务的一样。

最后放一张训练输出截图:

1668715386436.jpg

后面我会持续更新。。。#

1. 可视化代码 2018.08.21 已上传到 github, show.py

2.caffe-python 推理代码 2018.08.21 已上传到 github, face_attri.py

3.ncnn-C++ 部署代码

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。