This repository has been archived by the owner on Jul 12, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Zhou Chang <achang.zhou@gmail.com>
- Loading branch information
Showing
15 changed files
with
922 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.