您的位置:首页 > 编程语言 > Python开发

caffe 利用Python API 做数据输入层

2017-10-20 14:30 411 查看
caffe (Convolutional Architecture for Fast Feature Embedding)

在caffe中,主要使用LMDB提供数据管理,将形形色色的原始数据转换为统一的Key-Value形式存储,便于数据输入层获得这些数据,而且提高了磁盘IO的利用率。

但是,有时我们可以使用python作为网络结构数据的输入层,毕竟python 简单易写。

参考网址:https://chrischoy.github.io/research/caffe-python-layer/

编译选择

想要使用Python Layer,我们需要在编译的时候修改Makefile.config中的pyhon 的选项。

WITH_PYTHON_LAYER := 1


修改完之后,重新编译:

#在caffe_root依次运行
make clean
make
make pycaffe


如果还是 import caffe错误,则将以下几句加入到Python文件的最前面,再导入caffe库:

import sys
sys.path.append("/home/yonghuming/caffe-master/python")
sys.path.append("/home/yonghuming/caffe-master/python/caffe")


或者是修改全局变量:

sudo vim /etc/profile


添加全局变量:

export PYTHONPATH=/home/yonghu/caffe/python


最后:source /etc/profile

先看一个官方例程

此历程所在位置为$caffe_root/examples/pycaffe下

配置文件linreg.prototxt

layer {
type: 'Python'
name: 'loss'
top: 'loss'
bottom: 'ipx'
bottom: 'ipy'
python_param {
# the module name -- usually the filename -- that needs to be in $PYTHONPATH
module: 'pyloss'
# the layer name -- the class name in the module
layer: 'EuclideanLossLayer'
}
# set loss weight so Caffe knows this is a loss layer.
# since PythonLayer inherits directly from Layer, this isn't automatically
# known to Caffe
loss_weight: 1
}


此配置文件中的loss层就是用Python写的层。

module: 'pyloss'
layer: 'EuclideanLossLayer'


module指的是python文件的名字;layer值得是Python文件中的类。

# the module name -- usually the filename -- that needs to be in $PYTHONPATH


其中$PYTHONPATH指的是$caffe_root/python , 只要把python文件放到此目录下就可以了。

再看一下pyloss.py文件

import caffe
import numpy as np

class EuclideanLossLayer(caffe.Layer):
"""
Compute the Euclidean Loss in the same manner as the C++ EuclideanLossLayer
to demonstrate the class interface for developing layers in Python.
"""

def setup(self, bottom, top):
# check input pair
if len(bottom) != 2:
raise Exceptio
da80
n("Need two inputs to compute distance.")

def reshape(self, bottom, top):
# check input dimensions match
if bottom[0].count != bottom[1].count:
raise Exception("Inputs must have the same dimension.")
# difference is shape of inputs
self.diff = np.zeros_like(bottom[0].data, dtype=np.float32)
# loss output is scalar
top[0].reshape(1)

def forward(self, bottom, top):
self.diff[...] = bottom[0].data - bottom[1].data
top[0].data[...] = np.sum(self.diff**2) / bottom[0].num / 2.

def backward(self, top, propagate_down, bottom):
for i in range(2):
if not propagate_down[i]:
continue
if i == 0:
sign = 1
else:
sign = -1
bottom[i].diff[...] = sign * self.diff / bottom[i].num


使用Python layer 做数据输入层

此处我导入的数据为28*28*6的数组,也就是数据类型为:

print type(data)
print np.shape(data)

# <type 'numpy.ndarray'>
# (28, 28, 6)


图片也是类似,利用opencv读入的图片也是数组结构:

import numpy as np
import cv2
data = cv2.imread('1.jpg')
print type(data)
#<type 'numpy.ndarray'>
print np.shape(data)
# <type 'numpy.ndarray'>
# (375, 500, 3)


但是一定要注意三色通道问题!

此时我的配置文件中有关Python层的定义:

name: "LeNet"
layer {
name: "Data"
type: "Python"
top: "data"
top: "label"
include {
phase: TRAIN
}
python_param {
module: "dataLayer"
layer: "Custom_Data_Layer"
param_str: '{"batch_size":64, "im_shape":28, "src_file":"data/input/train"}'
}
}


param_str为参数。

此时py文件:

import caffe
import numpy as np
import os
import random
def GetTupleList(src_file, dirtag):
subDirTuples = []
folder = os.path.join(src_file, dirtag)
fns = os.listdir(folder)
if dirtag=='pos':
tag = 0
elif dirtag=='neg':
tag = 1
else:
raise Exception('Invalid dirtag {}'.format(str(dirtag)))

for fn in fns:
path = os.path.join(folder, fn)
data = np.load(path)
subDirTuples.append((data, np.array([tag])))
return subDirTuples

def readSrcFile(src_file):
posTuples = GetTupleList(src_file, 'pos')
print(len(posTuples))
negTuples = GetTupleList(src_file, 'neg')
print(len(negTuples))
imgTuples = posTuples + negTuples

return imgTuples

class Custom_Data_Layer(caffe.Layer):
def setup(self, bottom, top):
# Check top shape
if len(top) != 2:
raise Exception("Need to define tops (data and label1)")

# Check bottom shape
if len(bottom) != 0:
raise Exception("Do not define a bottom")

# Read parameters
params = eval(self.param_str)
src_file = params["src_file"]
self.batch_size = params["batch_size"]
self.im_shape = params["im_shape"]

top[0].reshape(self.batch_size, 6, self.im_shape, self.im_shape)
top[1].reshape(self.batch_size, 1)
self.imgTuples = readSrcFile(src_file)
self._cur = 0 # use this to check if we need to restart the list of images

def forward(self, bottom, top):
for itt in range(self.batch_size):
# Use the batch loader to load the next image
im, label = self.load_next_image()
# Here we could preprocess the image

# Add directly to the top blob
im_data = np.reshape(im, (6,28,28))
#注意!!np.reshape()
top[0].data[itt, ...] = im_data
top[1].data[itt, ...] = label

def load_next_image(self):
# If we have finished forwarding all images, then an epoch has finished
# and it is time to start a new one
if self._cur == len(self.imgTuples):
self._cur = 0
random.shuffle(self.imgTuples)

im, label = self.imgTuples[self._cur]
self._cur += 1
return im, label

def reshape(self, bottom, top):
"""
There is no need to reshape the data, since the input is of fixed size
(img shape and batch size)
"""
pass

def backward(self, bottom, top):
"""
This layer does not back propagate
"""
pass


完成!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: