代码笔记:caffe-reid中generate_caffenet.py解析

代码地址如下。该代码是用caffe实现的《A Discriminatively Learned CNN Embedding for Person Re-identification》
https://github.com/D-X-Y/caffe-reid

generate_caffenet.py实现的功能是,生成网络文件。其中包括了如下3个文件:
(1)train.proto,该文件的代码实现的是训练所用的网络构架。其中包括了数据层、卷积层、全连接层及最后的loss计算层。值得注意的是,该网络中所有的conv层均有weight_filler却没有bias_filler的参数设置。weight_filler {type: "xavier"}
(2)solver.proto,该文件是网络参数的配置。
(3)dev.proto,该文件是测试所用的网络构架。
(4)devploy.proto,该文件是输出结果。
这里写图片描述
实际上,如上几个文件也是可以各自手动来实现的,但是确实不如只通过一个generate_caffenet.py文件来实现高效。之所以能够通过脚本来生成网络文件,其很大一部分原因是,训练网络中的前向部分和测试网络中的前向部分是保持一致的。

编译问题

如果如下代码运行时报出import caffe error的问题,则很可能需要在Mikefile所在的目录下对pycaffe进行编译make pycaffe

python models/market1501/generate_caffenet.py 

导入的包

import了两个包,一个是layers,另一个是params。layers里面包含了Caffe所以内置的层(比如卷积,ReLU等),而params则包含了各种枚举值。

import _init_paths
import os
import os.path as osp
import caffe
from caffe import layers as L, params as P
from caffe import tools
from caffe.model_libs import *

net主体

由于训练所需的train.proto中的前向部分与测试所需的dev.proto有很大一部分是相同的。因此,通过一个通用的函数主体来实现,而其具体的细节差异通过参数传递来实现。

def caffenet_body(net, data, post, is_train):
  # the net itself
  net['conv1'+post], net['relu1'+post] = conv_relu(net[data],         11, 96, stride=4, is_train=is_train)
  net['pool1'+post]                    = max_pool(net['relu1'+post], 3, stride=2)
  net['norm1'+post]                    = L.LRN(net['pool1'+post], local_size=5, alpha=1e-4, beta=0.75, engine=P.LRN.CAFFE)
  net['conv2'+post], net['relu2'+post] = conv_relu(net['norm1'+post], 5, 256, pad=2, group=2, is_train=is_train)
  net['pool2'+post]                    = max_pool(net['relu2'+post], 3, stride=2)
  net['norm2'+post]                    = L.LRN(net['pool2'+post], local_size=5, alpha=1e-4, beta=0.75, engine=P.LRN.CAFFE)
  net['conv3'+post], net['relu3'+post] = conv_relu(net['norm2'+post], 3, 384, pad=1, is_train=is_train)
  net['conv4'+post], net['relu4'+post] = conv_relu(net['relu3'+post], 3, 384, pad=1, group=2, is_train=is_train)
  net['conv5'+post], net['relu5'+post] = conv_relu(net['relu4'+post], 3, 256, pad=1, group=2, is_train=is_train)
  net['pool5'+post]                    = max_pool(net['relu5'+post], 3, stride=2)
  net['fc6'+post], net['relu6'+post]   = fc_relu(net['pool5'+post], 4096, is_train=is_train)
  net['drop6'+post]                    = L.Dropout(net['relu6'+post], in_place=True)
  net['fc7'+post], net['relu7'+post]   = fc_relu(net['drop6'+post], 4096, is_train=is_train)
  net['drop7'+post]                    = L.Dropout(net['relu7'+post], in_place=True)
  #n.score = L.InnerProduct(n.drop7, num_output=20, weight_filler=dict(type='gaussian', std=0.01))
  #n.loss = L.SigmoidCrossEntropyLoss(n.score, n.label)
  final = 'drop7'+post
return net, final

train.proto的实现

该函数有3个参数:mean_value是均值;list_file是文件目录,该目录记载了训练集中每一张图片的地址;is_train是用于说明是否为训练所需的bool变量。

注意:net=caffe.NetSpec()是获取Caffe的一个net,我们只需不断的填充这个net,最后面把net输出到文件就会得到net的protobuf定义。

# main netspec wrapper
def caffenet_train(mean_value, list_file, is_train=True):
  # setup the python data layer 
  net = caffe.NetSpec()
  #将数据和标签单独存入网络
  net.data, net.label \
                  = L.ReidData(transform_param=dict(mirror=True,crop_size=227,mean_value=mean_value), 
                               reid_data_param=dict(source=list_file,batch_size=128,new_height=256,new_width=256,
                               pos_fraction=1,neg_fraction=1,pos_limit=1,neg_limit=4,pos_factor=1,neg_factor=1.01), 
                               ntop = 2)
  #调用caffenet_body来生成网络
  net, final = caffenet_body(net, 'data', '', is_train)

  #final是长度为4096的向量,score是长度为751的向量,通过fc网络将4096变为751个类别,
  net['score']     = fc_relu(net[final],     nout=751, is_train=is_train, has_relu=False)
  #通过final与label之间的欧式距离来得到两个输出层euclidean和label_dif,其长度均为4096
  net['euclidean'], net['label_dif'] = L.PairEuclidean(net[final], net['label'], ntop = 2)
  #euclidean是长为4096的向量,score_dif是长为2的向量。通过全连接层由4096变成2维
  net['score_dif'] = fc_relu(net['euclidean'], nout=2, is_train=is_train, has_relu=False)
  #通过score层和label层的值来计算分类的loss,score和label均为长度为751的向量
  net['loss']      = L.SoftmaxWithLoss(net['score'],     net['label']    , propagate_down=[1,0], loss_weight=1)
  #通过score_dif和label_dif来计算相似的loss_dif
  net['loss_dif']  = L.SoftmaxWithLoss(net['score_dif'], net['label_dif'], propagate_down=[1,0], loss_weight=0.5)
  #返回网络结果
return str(net.to_proto())

描述输入数据的代码只有一行,该行代码在train.proto的实现结果如下图所示:文件中的参数与输入的参数是一致的。
这里写图片描述

通过调用如下语句:实现了train.proto的主体,该函数返回的是fc7层,该层是一个长度为4096的向量。

 net, final = caffenet_body(net, 'data', '', is_train)

这里写图片描述

如下的代码则是根据score及输入label来计算的分类的loss,同时根据score_diflabel_dif来计算相似的dif_loss。在分类的loss中,score是计算得到的长度为751的向量,label是输入的长度为751的向量。在相似的dif_loss中,score_dif是计算所得的长度为2的向量,但是label_dif应该是标注所得,但

这里写图片描述

deploy.proto的实现

最后得到的结果是由euclidean输出一个长度为2的score_dif,该值返回的是相似的结果

def caffenet_dev(data_param = dict(shape=dict(dim=[2, 3, 227, 227])), label_param = dict(shape=dict(dim=[2]))):
  # setup the python data layer 
  net = caffe.NetSpec()
  net['data']  = L.Input(input_param = data_param)
  net['label'] = L.Input(input_param = label_param)
  net, final   = caffenet_body(net, 'data', '', is_train=False)
  net['score']     = fc_relu(net[final],     nout=751, is_train=False, has_relu=False)
  net['euclidean'], net['label_dif'] = L.PairEuclidean(net[final], net['label'], ntop = 2)
  net['score_dif'] = fc_relu(net['euclidean'], nout=2,   is_train=False, has_relu=False)
return str(net.to_proto())

dev.proto的实现

该函数返回的是一个长度为751的向量prediction,该值是用于计算类别的。

def caffenet_score(input_param = dict(shape=dict(dim=[1, 3, 227, 227]))):
  # setup the python data layer 
  net = caffe.NetSpec()
  net['data']       = L.Input(input_param = input_param)
  net, final = caffenet_body(net, 'data', '', is_train=False)
  net['score']      = fc_relu(net[final],     nout=751, is_train=False, has_relu=False)
  net['prediction'] = L.Softmax(net['score'])
return str(net.to_proto())

目录的建立

workdir = osp.join(osp.dirname(__file__), 'caffenet')
if not os.path.isdir(workdir):
  os.makedirs(workdir)

logdir = osp.join(workdir, 'log')
if not os.path.isdir(logdir):
  os.makedirs(logdir)

snapshotdir = osp.join(workdir, 'snapshot')
if not os.path.isdir(snapshotdir):
  os.makedirs(snapshotdir)
print('Work Dir : {}'.format(workdir))

train_proto = osp.join(workdir, "train.proto")

solver.proto的实现

solverproto = tools.CaffeSolver(trainnet_prototxt_path = train_proto, testnet_prototxt_path = None)
solverproto.sp['display'] = "20"
solverproto.sp['base_lr'] = "0.001"
solverproto.sp['stepsize'] = "16000"
solverproto.sp['max_iter'] = "18000"
solverproto.sp['snapshot'] = "2000"
solverproto.sp['snapshot_prefix'] = "\"{}/snapshot/caffenet.full\"".format(workdir)
solverproto.write(osp.join(workdir, 'solver.proto'))

参数的值

list_file = 'examples/market1501/lists/train.lst'

mean_value = [97.8286, 99.0468, 105.606]

将文件写到本地

# write train net.
with open(train_proto, 'w') as f:
  f.write(caffenet_train(mean_value, list_file, True))

dev_proto = osp.join(workdir, "dev.proto")
with open(dev_proto, 'w') as f:
  f.write(caffenet_score())

dep_proto = osp.join(workdir, "deploy.proto")
with open(dep_proto, 'w') as f:
f.write(caffenet_dev())
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值