具体的内容请关注公众号: 互联网的大社区
本文说下如何速成我们平时常用的深度学习框架,总共有 7 个—— Caffe,Tensorflow,pytroch,paddlepaddle,Keras,mxnet,cntk。
我们以一个分类任务为例,给大家准备了 500 张微笑的图片、500 张非微笑的图片,放置在 data 目录下,图片预览如下,已经缩放到 60*60 的大小:
这是非微笑的图片:
这是微笑的图片:
01
Caffe
我们首先讲讲 Caffe 这个主流的开源框架从训练到测试出结果的全流程。到此,我必须假设大家已经有了深度学习的基础知识并了解卷积网络的工作原理。
1.1 Caffe 是什么
Caffe 是以 C++/CUDA 代码为主,最早的深度学习框架之一,比 TensorFlow、Mxnet、Pytorch 等都更早,支持命令行、Python 和 Matlab 接口,单机多卡、多机多卡等都可以很方便的使用,CPU 和 GPU 之间无缝切换。
对于入门级别的任务,如图像分类,Caffe 上手的成本最低,几乎不需要写一行代码就可以开始训练,所以我推荐 Caffe 作为入门学习的框架。
Caffe 相对于 TensorFlow 等使用 pip 一键安装的方式来说,编译安装稍微麻烦一些,但其实依旧很简单,我们以 Ubuntu 16.04 为例,官网的安装脚本足够用了,方法如下:
sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-devsudo apt-get install libatlas-base-dev
sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev
装完之后,到 Git 上 clone 代码,修改 Makefile.config 就可以进行编译安装,如果其中有任何问题,多 Google,还有什么问题,就联系我们吧。当然,对于有 GPU 的读者,还需要安装 cuda 以及 Nvidia 驱动。
1.2 Caffe 训练
Caffe 完成一个训练,必要准备以下资料:一个是 train.prototxt 作为网络配置文件,另一个是 solver.prototxt 作为优化参数配置文件,再一个是训练文件 list。
另外,在大多数情况下,需要一个预训练模型作为权重的初始化。
( 1 )准备网络配置文件
我们准备了一个 3*3 的卷积神经网络,我们把相关代码保存在它的 train.prototxt 文件中。从 Git 上 clone 下来我们就能看到。
现在我们分析下 Caffe 的这个网络配置文件:每一个卷积层,都是以 layer{} 的形式定义,layer 的 bottom、top 就是它的输入输出,type 就是它的类型,有的是数据层、有的是卷积层、有的是 loss 层。
我们采用 netscope 来可视化一下这个模型。
从上面看很直观的看到,网络的输入层是 data 层,后面接了 3 个卷积层,其中每一个卷积层都后接了一个 relu 层,最后 ip1-mouth、fc-mouth 是全连接层。Loss 和 acc 分别是计算 loss 和 acc 的层。
各层的配置有一些参数,比如 conv1 有卷积核的学习率、卷积核的大小、输出通道数、初始化方法等,这些可以后续详细了解。
( 2 )准备训练 list
我们看上面的 data layer,可以到
image_data_param 看,里面有
source: "all_shuffle_train.txt"
它是什么呢,就是输入用于训练的 list,它的内容是这样的:
../../../../datas/mouth/1/182smile.jpg 1
../../../../datas/mouth/1/435smile.jpg 1
../../../../datas/mouth/0/40neutral.jpg 0
../../../../datas/mouth/1/206smile.jpg 1
../../../../datas/mouth/0/458neutral.jpg 0
../../../../datas/mouth/0/158neutral.jpg 0
../../../../datas/mouth/1/322smile.jpg 1
../../../../datas/mouth/1/83smile.jpg 1
../../../../datas/mouth/0/403neutral.jpg 0
../../../../datas/mouth/1/425smile.jpg 1
../../../../datas/mouth/1/180smile.jpg 1
../../../../datas/mouth/1/233smile.jpg 1
../../../../datas/mouth/1/213smile.jpg 1
../../../../datas/mouth/1/144smile.jpg 1
../../../../datas/mouth/0/327neutral.jpg 0
格式就是,图片的名字 + 空格 + label,这就是 Caffe 用于图片分类默认的输入格式。
( 3 )准备优化配置文件:
net: "./train.prototxt"
test_iter: 100
test_interval: 10
base_lr: 0.00001
momentum: 0.9
type: "Adam"
lr_policy: "fixed"
display: 100
max_iter: 10000
snapshot: 2000
snapshot_prefix: "./snaps/conv3_finetune"
solver_mode: GPU
介绍一下上面的参数。
net 是网络的配置路径。test_interval 是指训练迭代多少次之后,进行一次测试。test_iter 是测试多少个 batch,如果它等于 1,就说明只取一个 batchsize 的数据来做测试,如果 batchsize 太小,那么对于分类任务来说统计出来的指标也不可信,所以最好一次测试,用到所有测试数据。因为,常令 test_iter*test_batchsize=测试集合的大小。
base_lr、momentum、type、lr_policy 是和学习率有关的参数,base_lr 和 lr_policy 决定了学习率大小如何变化。type 是优化的方法,以后再谈。max_iter 是最大的迭代次数,snapshot 是每迭代多少次之后存储迭代结果,snapshot_prefix 为存储结果的目录,caffe 存储的模型后缀是 .caffemodel。solver_mode 可以指定用 GPU 或者 CPU 进行训练。
( 4 )训练与结果可视化
我们利用 C++ 的接口进行训练,命令如下:
SOLVER=./solver.prototxt
WEIGHTS=./init.caffemodel
../../../../libs/Caffe_Long/build/tools/caffe train -solver $SOLVER -weights $WEIGHTS -gpu 0 2>&1 | tee log.txt
其中,caffe train 就是指定训练。我们可以利用脚本可视化一下训练结果,具体参考 git 项目:
1.3 Caffe 测试
上面我们得到了训练结果,下面开始采用自己的图片进行测试。
train.prototxt 与 test.prototxt 的区别
训练时的网络配置与测试时的网络配置是不同的,测试没有 acc 层,也没有 loss 层,取输出的 softmax 就是分类的结果。同时,输入层的格式也有出入,不需要再输入 label,也不需要指定图片 list,但是要指定输入尺度,我们看一下 可视化结果。
使用 Python 进行测试
由于 Python 目前广泛使用,下面使用 Python 来进行测试,它要做的就是导入模型、导入图片、输出结果。
下面是所有的代码,我们详细解释下:
---代码段 1,这一段,我导入一些基本库,同时导入 caffe 的路径---
#* coding:utf8
import sys
sys.path.insert(0, '../../../../../libs/Caffe_Long/python/')
import caffe
import os,shutil
import numpy as np
from PIL import Image as PILImage
from PIL import ImageMath
import matplotlib.pyplot as plt
import time
import cv2
---代码段 2,这一段,我们添加一个参数解释器,方便参数管理---
debug=True
import argparse
def parse_args():
parser = argparse.ArgumentParser(description='test resnet model for portrait segmentation')
parser.add_argument('--model', dest='model_proto', help='the model', default='test.prototxt', type=str)
parser.add_argument('--weights', dest='model_weight', help='the weights', default='./test.caffemodel', type=str)
parser.add_argument('--testsize', dest='testsize', help='inference size', default=60,type=int)
parser.add_argument('--src', dest='img_folder', help='the src image folder', type=str, default='./')
parser.add_argument('--gt', dest='gt', help='the gt', type=int, default=0)
args = parser.parse_args()
return args
def start_test(model_proto,model_weight,img_folder,testsize):
---代码段 3,这一段,我们就完成了网络的初始化---
caffe.set_device(0)
#caffe.set_mode_cpu()
net = caffe.Net(model_proto, model_weight, caffe.TEST)
imgs = os.listdir(img_folder)
pos = 0
neg = 0
for imgname in imgs:
---代码段 4,这一段,是读取图片并进行预处理,还记得我们之前的训练,是采用 BGR 的输入格式,减去了图像均值吧,同时,输入网络的图像,也需要 resize 到相应尺度。预处理是通过 caffe 的类,transformer 来完成,set_mean 完成均值,set_transpose 完成维度的替换,因为 caffe blob 的格式是 batch、channel、height、width,而 numpy 图像的维度是 height、width、channel 的顺序---
imgtype = imgname.split('.')[-1]
imgid = imgname.split('.')[0]
if imgtype != 'png' and imgtype != 'jpg' and imgtype != 'JPG' and imgtype != 'jpeg' and imgtype != 'tif' and imgtype != 'bmp':
print imgtype,"error"
continue
imgpath = os.path.join(img_folder,imgname)
img = cv2.imread(imgpath)
if img is None:
print "---------img is empty---------",imgpath
continue
img = cv2.resize(img,(testsize,testsize))
transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape})
transformer.set_mean('data', np.array([104.008,116.669,122.675]))
transformer.set_transpose('data', (2,0,1))
---代码段 5,这一段,就得到了输出结果了,并做一些可视化显示---
out = net.forward_all(data=np.asarray([transformer.preprocess('data', img)]))
result = out['prob'][0]
print "---------result prob---------",result,"-------result size--------",result.shape
probneutral = result[0]
print "prob neutral",probneutral
probsmile = result[1]
print "prob smile",probsmile
problabel = -1
probstr = 'none'
if probneutral > probsmile:
probstr = "neutral:"+str(probneutral)
pos = pos + 1
else:
probstr = "smile:"+str(probsmile)
neg = neg + 1
if debug:
showimg = cv2.resize(img,(256,256))
cv2.putText(showimg,probstr,(30,50),cv2.FONT_HERSHEY_SIMPLEX,1,(0,0,255),1)
cv2.imshow("test",showimg)
k = cv2.waitKey(0)
if k == ord('q'):
break
print "pos=",pos
print "neg=",neg
if name == 'main':
args = parse_args()
start_test(args.model_proto,args.model_weight,args.img_folder,args.testsize)
经过前面的介绍,我们已经学会了 Caffe 的基本使用,但是我们不能停留于此。Caffe 是一个非常优秀的开源框架,有必要去细读它的源代码。
具体的内容请关注公众号: 互联网的大社区
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.