Skip to content
This repository has been archived by the owner on Jul 12, 2018. It is now read-only.

Commit

Permalink
Init code.
Browse files Browse the repository at this point in the history
Signed-off-by: Zhou Chang <achang.zhou@gmail.com>
  • Loading branch information
Teaonly committed Oct 16, 2016
1 parent 5840816 commit 53248c2
Show file tree
Hide file tree
Showing 15 changed files with 922 additions and 0 deletions.
48 changes: 48 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6 FATAL_ERROR)
CMAKE_POLICY(VERSION 2.6)

SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")

FIND_PACKAGE(Torch REQUIRED)
FIND_PACKAGE(CUDA)

# Caffe may have been compiled without CUDA support
# (Caffe headers check for CPU_ONLY flag)
IF(NOT CUDA_FOUND)
add_definitions(-DCPU_ONLY)
SET(CUDA_INCLUDE_DIRS "")
ENDIF()

IF(NOT EXISTS ${CAFFE_DIR})
SET(CAFFE_DIR "$ENV{CAFFE_DIR}")
ENDIF()
MESSAGE("CAFFE_DIR: " ${CAFFE_DIR})

# Any thrid include and library path can be set here
SET(OPT_DIR "~/opt")

INCLUDE_DIRECTORIES("${Torch_INSTALL_INCLUDE}/TH" "${CAFFE_DIR}/include" "${CAFFE_DIR}/build/src" ${CUDA_INCLUDE_DIRS} "${OPT_DIR}/include")

LINK_DIRECTORIES("${Torch_INSTALL_LIB}" "${CAFFE_DIR}/build/lib")
add_definitions(-Wl,-as-needed)

SET(src caffe.cpp ${PROTO_SRCS})

FILE(GLOB luasrc *.lua)

ADD_LIBRARY(trans_torch MODULE ${src} "${OPT_DIR}/lib")
TARGET_LINK_LIBRARIES(trans_torch TH caffe)

### Torch packages supposes libraries prefix is "lib"
SET_TARGET_PROPERTIES(trans_torch PROPERTIES
PREFIX "lib"
IMPORT_PREFIX "lib")

INSTALL(TARGETS trans_torch
RUNTIME DESTINATION "${Torch_INSTALL_LUA_CPATH_SUBDIR}"
LIBRARY DESTINATION "${Torch_INSTALL_LUA_CPATH_SUBDIR}")

INSTALL(
FILES
${luasrc}
DESTINATION "${Torch_INSTALL_LUA_PATH_SUBDIR}/transtorch")
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,58 @@
# trans-torch

Translating Torch model to other framework such as Caffe, MxNet ...

Torch is excelent learning&research platform for deep learning, but its model can't be easy deployed without Luajit host.
This tool is a simple solution to this problem, translating Torch model to other easy deployed platform such as Caffe or Mxnet.

Directly translating Torch to Caffe (or others) is hard , we must Torch code to another DLS(like Caffe's prototxt) or python code(like Mxnet, Tensorflow)
But translating parameters ( weights and bias) only between diffrent frmamework is an easy task. For most model in zoo, we need only translating follow modules or layers:

* Linear module's weight and bias
* Convolution module's weight and bias
* BatchNormlaization's mean and var , also including alpha and beta .


## 1. Translating to Caffe

### building

```
CAFFE_DIR=/home/path_to/caffe luarocks make
```

### how to use this module

```
require('nn')
require('transtorch')
torch.setdefaulttensortype('torch.FloatTensor')
local net = nn.Sequential()
net:add(nn.Linear(2, 6))
net:add(nn.ReLU())
net:add(nn.Linear(6, 3))
net:add(nn.SoftMax())
local x = torch.ones(2) * 0.5
y = net:forward(x)
print(y)
local caffeNet = transTorch.loadCaffe('./mlp.prototxt');
local l = net:get(1)
transTorch.toCaffe(l, caffeNet, "ip1")
l = net:get(3)
transTorch.toCaffe(l, caffeNet, "ip2")
transTorch.writeCaffe(caffeNet, "mlp.caffemodel")
```

The Caffe's module will work same as the model build by Torch. We can verify model by simple_test.cpp in test floder.
See more demo code in test floder.

## 2. Translating to MxNet (TODO)

148 changes: 148 additions & 0 deletions caffe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <string>
#include <vector>
#include <sstream>
#include <iostream>

#include <TH/TH.h>
#include "caffe/caffe.hpp"
#include "caffe/util/io.hpp"

extern "C"
{
void* loadCaffeNet(const char* param_file, const char* model_file);
void releaseCaffeNet(void* net_);
void saveCaffeNet(void* net_, const char* weight_file);

void writeCaffeConvLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
void writeCaffeLinearLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
void writeCaffeBNLayer(void* net, const char* layername, THFloatTensor* mean, THFloatTensor* var);
void writeCaffeScaleLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
}

typedef float Dtype;

using namespace caffe; // NOLINT(build/namespaces)

void* loadCaffeNet(const char* param_file, const char* model_file) {
Net<Dtype>* net = new Net<Dtype>(string(param_file), TEST);
if(model_file != NULL)
net->CopyTrainedLayersFrom(string(model_file));

return net;
}

void releaseCaffeNet(void* net_) {
Net<Dtype>* net = (Net<Dtype>*)net_;
if ( net != NULL) {
delete net;
}
}

void saveCaffeNet(void* net_, const char* weight_file) {
Net<Dtype>* net = (Net<Dtype>*)net_;

NetParameter net_param;
net->ToProto(&net_param);

WriteProtoToBinaryFile(net_param, std::string(weight_file));
}

int getTHTensorSize(THFloatTensor* tensor) {
int size = tensor->size[0];
for (int i = 1; i < tensor->nDimension; i++) {
size = size * tensor->size[i];
}
return size;
}

void writeCaffeBNLayer(void* net_, const char* layerName, THFloatTensor* mean, THFloatTensor* var) {
Net<Dtype>* net = (Net<Dtype>*)net_;

const boost::shared_ptr<caffe::Layer<Dtype> > inLayer = net->layer_by_name(std::string(layerName));
vector<shared_ptr<Blob<Dtype> > > blobs = inLayer->blobs();

// Checking size
CHECK_EQ(blobs.size(), 3);
CHECK_EQ(getTHTensorSize(mean), blobs[0]->count());

// Converting 2 parameter(Torch) to 3 parameter(Caffe)
const float* mean_ptr = THFloatTensor_data(mean);
const float* var_ptr = THFloatTensor_data(var);

caffe_set(blobs[2]->count(), 1.0f, blobs[2]->mutable_cpu_data());
caffe_copy(blobs[0]->count(), mean_ptr, blobs[0]->mutable_cpu_data());
caffe_copy(blobs[1]->count(), var_ptr, blobs[1]->mutable_cpu_data());
}


void writeCaffeScaleLayer(void* net_, const char* layerName, THFloatTensor* weights, THFloatTensor* bias) {
Net<Dtype>* net = (Net<Dtype>*)net_;

const boost::shared_ptr<caffe::Layer<Dtype> > inLayer = net->layer_by_name(std::string(layerName));
vector<shared_ptr<Blob<Dtype> > > blobs = inLayer->blobs();

// Checking size
CHECK_EQ(blobs.size(), 2);
CHECK_EQ(getTHTensorSize(weights), blobs[0]->count());

// Copying data
const float* data_ptr = THFloatTensor_data(weights);
caffe_copy(blobs[0]->count(), data_ptr, blobs[0]->mutable_cpu_data());

data_ptr = THFloatTensor_data(bias);
caffe_copy(blobs[1]->count(), data_ptr, blobs[1]->mutable_cpu_data());
}

void writeCaffeConvLayer(void* net_, const char* layerName, THFloatTensor* weights, THFloatTensor* bias) {
Net<Dtype>* net = (Net<Dtype>*)net_;

const boost::shared_ptr<caffe::Layer<Dtype> > inLayer = net->layer_by_name(std::string(layerName));
vector<shared_ptr<Blob<Dtype> > > blobs = inLayer->blobs();

// Checking output layer is conv, so parameter's blob size is 2
if ( blobs.size() != 2) {
std::ostringstream oss;
oss << "Can't write into layer :" << layerName ;
THError(oss.str().c_str());
}

// Checking size
CHECK_EQ(getTHTensorSize(weights), blobs[0]->count());
CHECK_EQ(getTHTensorSize(bias), blobs[1]->count());

// Copying data
const float* data_ptr = THFloatTensor_data(weights);
caffe_copy(blobs[0]->count(), data_ptr, blobs[0]->mutable_cpu_data());

data_ptr = THFloatTensor_data(bias);
caffe_copy(blobs[1]->count(), data_ptr, blobs[1]->mutable_cpu_data());
}

void writeCaffeLinearLayer(void* net_, const char* layerName, THFloatTensor* weights, THFloatTensor* bias) {
Net<Dtype>* net = (Net<Dtype>*)net_;

const boost::shared_ptr<caffe::Layer<Dtype> > inLayer = net->layer_by_name(std::string(layerName));
vector<shared_ptr<Blob<Dtype> > > blobs = inLayer->blobs();

// Checking output layer is conv, so parameter's blob size is 2
if ( blobs.size() != 2) {
std::ostringstream oss;
oss << "Can't write into layer :" << layerName ;
THError(oss.str().c_str());
}

// Checking size
unsigned int th_weights_size = weights->size[0] * weights->size[1];
CHECK_EQ(th_weights_size, blobs[0]->count());

unsigned int th_bias_size = bias->size[0];
CHECK_EQ(th_bias_size, blobs[1]->count());

// Copying data
const float* data_ptr = THFloatTensor_data(weights);
caffe_copy(blobs[0]->count(), data_ptr, blobs[0]->mutable_cpu_data());

data_ptr = THFloatTensor_data(bias);
caffe_copy(blobs[1]->count(), data_ptr, blobs[1]->mutable_cpu_data());
}

70 changes: 70 additions & 0 deletions caffe.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
local ffi = require('ffi')
local C = transTorch._C

local toLinear = function(tm, caffeNet, layerName)
assert(tm.weight:type() == 'torch.FloatTensor')
local weight = tm.weight:cdata()
local bias = tm.bias:cdata()
C.writeCaffeLinearLayer(caffeNet[0], layerName, weight, bias)
end

local toConv = function(tm, caffeNet, layerName)
assert(tm.weight:type() == 'torch.FloatTensor')
local weights = tm.weight:cdata()
local bias = tm.bias:cdata()
C.writeCaffeConvLayer(caffeNet[0], layerName, weights, bias)
end

local toBatchNorm = function(tm, caffeNet, layerName)
if ( tm.affine == true) then
assert(type(layerName) == 'table')
assert(#layerName == 2)
local weights = tm.weight:cdata()
local bias = tm.bias:cdata()
local mean = tm.running_mean:cdata()
local var = tm.running_var:cdata()
C.writeCaffeBNLayer(caffeNet[0], layerName[1], mean, var);
C.writeCaffeScaleLayer(caffeNet[0], layerName[2], weights, bias);
else
assert(type(layerName) == 'string')
local mean = tm.running_mean:cdata()
local var = tm.running_var:cdata()
C.writeCaffeBNLayer(caffeNet[0], layerName[0], mean, var);
end
end

transTorch.loadCaffe = function(prototxt_name, binary_name)
assert(type(prototxt_name) == 'string')
if ( binary_name ~= nil ) then
assert(type(binary_name) == 'string')
end

local net = ffi.new("void*[1]")
net[0] = C.loadCaffeNet(prototxt_name, binary_name)

return net
end

transTorch.releaseCaffe = function(net)
C.releaseCaffeNet(net[0]);
end

transTorch.writeCaffe = function(net, fileName)
C.saveCaffeNet(net[0], fileName);
end

transTorch.toCaffe = function(tmodel, caffeNet, layerName)
local mtype = torch.type(tmodel)
if ( mtype == 'nn.Linear' ) then
toLinear(tmodel, caffeNet, layerName)
elseif ( mtype == 'nn.BatchNormalization' or mtype == 'nn.SpatialBatchNormalization' ) then
toBatchNorm(tmodel, caffeNet, layerName)
elseif ( string.match(mtype, 'Convolution') ) then
toConv(tmodel, caffeNet, layerName)
else
print(" ##ERROR## unspported layer:" .. mtype)
assert(false)
end
end


16 changes: 16 additions & 0 deletions ffi.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local ffi = require 'ffi'

ffi.cdef[[
void* loadCaffeNet(const char* param_file, const char* model_file);
void releaseCaffeNet(void* net);
void saveCaffeNet(void* net_, const char* weight_file);

void writeCaffeLinearLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
void writeCaffeConvLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
void writeCaffeBNLayer(void* net, const char* layername, THFloatTensor* mean, THFloatTensor* var);
void writeCaffeScaleLayer(void* net, const char* layername, THFloatTensor* weights, THFloatTensor* bias);
]]

transTorch._C = ffi.load(package.searchpath('libtrans_torch', package.cpath))


7 changes: 7 additions & 0 deletions init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'nn'
transTorch = {}

include 'ffi.lua'
include 'caffe.lua'

return transTorch
Loading

0 comments on commit 53248c2

Please sign in to comment.