Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pytorch to header convertor #177

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions c_reference/include/fastgrnn.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
* @var uRank rank of U matrix
* @var Bg pointer to bias for sigmoid
* @var Bh pointer to bias for tanh
* @var zeta first weight parameter for update from input from next step
* @var nu second weight parameter for update from input from next step
* @var sigmoid_zeta first weight parameter for update from input from next step
* @var sigmoid_nu second weight parameter for update from input from next step
*/
typedef struct FastGRNN_LR_Params {
float* mean;
Expand All @@ -35,8 +35,8 @@ typedef struct FastGRNN_LR_Params {
unsigned uRank;
float* Bg;
float* Bh;
float zeta;
float nu;
float sigmoid_zeta;
float sigmoid_nu;
} FastGRNN_LR_Params;

/**
Expand Down Expand Up @@ -82,8 +82,8 @@ int fastgrnn_lr(float* const hiddenState, unsigned hiddenDims,
* @var U pointer U matrix
* @var Bg pointer to bias for sigmoid
* @var Bh pointer to bias for tanh
* @var zeta first weight parameter for update from input from next step
* @var nu second weight parameter for update from input from next step
* @var sigmoid_zeta first weight parameter for update from input from next step
* @var sigmoid_nu second weight parameter for update from input from next step
*/
typedef struct FastGRNN_Params {
float* mean;
Expand All @@ -92,8 +92,8 @@ typedef struct FastGRNN_Params {
float* U;
float* Bg;
float* Bh;
float zeta;
float nu;
float sigmoid_zeta;
float sigmoid_nu;
} FastGRNN_Params;

/**
Expand Down
6 changes: 5 additions & 1 deletion c_reference/include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ void v_add(float scalar1, const float* const vec1,
float scalar2, const float* const vec2,
unsigned len, float* const ret);

// point-wise vector division ret = vec2 / vec1
// point-wise vector multiplication ret = vec2 * vec1
void v_mult(const float* const vec1, const float* const vec2,
unsigned len, float* const ret);

// point-wise vector division ret = vec2 / vec1
void v_div(const float* const vec1, const float* const vec2,
unsigned len, float* const ret);

// Return squared Euclidean distance between vec1 and vec2
float l2squared(const float* const vec1,
const float* const vec2, unsigned dim);

// Return index with max value, if tied, return first tied index.
unsigned argmax(const float* const vec, unsigned len);

Expand Down
8 changes: 4 additions & 4 deletions c_reference/src/fastgrnn.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ int fastgrnn_lr(float* const hiddenState, unsigned hiddenDims,
// #steps iterations of the RNN cell starting from hiddenState
for (unsigned t = 0; t < steps; t++) {
// Normalize the features
unsigned offset = backward ? steps - t : t;
unsigned offset = backward ? steps - 1 - t : t;
if (normalize) {
v_add(1.0f, input + offset * inputDims, -1.0f, tparams->mean + t * inputDims,
inputDims, tbuffers->normFeatures);
Expand All @@ -45,7 +45,7 @@ int fastgrnn_lr(float* const hiddenState, unsigned hiddenDims,
for (unsigned i = 0; i < hiddenDims; i++) {
float gate = sigmoid(tbuffers->preComp[i] + tparams->Bg[i]);
float update = tanh(tbuffers->preComp[i] + tparams->Bh[i]);
hiddenState[i] = gate * hiddenState[i] + (tparams->zeta * (1.0 - gate) + tparams->nu) * update;
hiddenState[i] = gate * hiddenState[i] + (tparams->sigmoid_zeta * (1.0 - gate) + tparams->sigmoid_nu) * update;
}
}
return 0;
Expand All @@ -63,7 +63,7 @@ int fastgrnn(float* const hiddenState, unsigned hiddenDims,

for (unsigned t = 0; t < steps; t++) {
// Normalize the features
unsigned offset = backward ? steps - t : t;
unsigned offset = backward ? steps - 1 - t : t;
if (normalize) {
v_add(1.0f, input + offset * inputDims, -1.0f, tparams->mean + t * inputDims,
inputDims, tbuffers->normFeatures);
Expand All @@ -86,7 +86,7 @@ int fastgrnn(float* const hiddenState, unsigned hiddenDims,
for (unsigned i = 0; i < hiddenDims; i++) {
float gate = sigmoid(tbuffers->preComp[i] + tparams->Bg[i]);
float update = tanh(tbuffers->preComp[i] + tparams->Bh[i]);
hiddenState[i] = gate * hiddenState[i] + (tparams->zeta * (1.0 - gate) + tparams->nu) * update;
hiddenState[i] = gate * hiddenState[i] + (tparams->sigmoid_zeta * (1.0 - gate) + tparams->sigmoid_nu) * update;
}
}
return 0;
Expand Down
8 changes: 8 additions & 0 deletions c_reference/src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ void v_div(const float* const vec1, const float* const vec2,
ret[i] = vec2[i] / vec1[i];
}

float l2squared(const float* const vec1,
const float* const vec2, unsigned dim) {
float sum = 0.0f;
for (unsigned i = 0; i < dim; i++)
sum += (vec1[i] - vec2[i]) * (vec1[i] - vec2[i]);
return sum;
}

unsigned argmax(const float* const vec, unsigned len) {
unsigned maxId = 0;
float maxScore = FLT_MIN;
Expand Down
5 changes: 4 additions & 1 deletion c_reference/tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ INCLUDE_DIR=../include
SRC_DIR=../src
IFLAGS = -I $(INCLUDE_DIR)

all: test_fastgrnn_lr test_rnnpool
all: test_fastgrnn_lr test_rnnpool test_rnnpool_face

FASTGRNN_DIR=fastgrnn
test_fastgrnn_lr: $(FASTGRNN_DIR)/test_fastgrnn_lr.c $(SRC_DIR)/utils.o $(SRC_DIR)/fastgrnn.o $(SRC_DIR)/classifier.o
Expand All @@ -17,6 +17,9 @@ RNNPOOL_DIR=rnnpool
test_rnnpool: $(RNNPOOL_DIR)/test_rnnpool.c $(SRC_DIR)/utils.o $(SRC_DIR)/fastgrnn.o $(SRC_DIR)/rnnpool.o
$(CC) -o $@ $^ $(IFLAGS) $(CFLAGS) -lm

test_rnnpool_face: $(RNNPOOL_DIR)/test_rnnpool_face.c $(SRC_DIR)/utils.o $(SRC_DIR)/fastgrnn.o $(SRC_DIR)/rnnpool.o
$(CC) -o $@ $^ $(IFLAGS) $(CFLAGS) -lm


.PHONY: clean cleanest

Expand Down
189 changes: 189 additions & 0 deletions c_reference/tests/converters/ConvertNumpytoHeaderFilesRNNPool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.

import numpy as np
import argparse
import os
import sys

def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))

def saveTraces(tracesInputDir, tracesOutputDir):
if os.path.isdir(tracesOutputDir) is False:
try:
os.mkdir(tracesOutputDir)
except OSError:
print("Creation of the directory %s failed" % tracesOutputDir)
return
input_files = os.listdir(tracesInputDir+'/inputs/')

for file in input_files:
trace_input = np.load(tracesInputDir + "/inputs/" + file)
trace_output = np.load(tracesInputDir + "/outputs/" + file)

inputDims = trace_input.shape[-1]
patchDim = trace_input.shape[-2]
hiddenDims2 = int(trace_output.shape[0]/4)

f_input = open(tracesOutputDir+'/'+str(file)[:-4]+'_input.h','w')
f_output = open(tracesOutputDir+'/'+str(file)[:-4]+'_output.h','w')
f_input.write('#define INPUT_DIMS '+str(inputDims)+'\n#define PATCH_DIM '+ str(patchDim)+'\n\n')
f_output.write('#define HIDDEN_DIMS2 '+str(hiddenDims2)+'\n\n')

f_input.write('static float input[INPUT_DIMS * PATCH_DIM * PATCH_DIM] = ' + convertMatrixToVecString(trace_input) + ';')
f_output.write('static float output[4 * HIDDEN_DIMS2] = ' + convertMatrixToVecString(trace_output) + ';')

f_input.flush()
f_input.close()
f_output.flush()
f_output.close()




def loadModel(modelDir):
model = {
"W1": np.transpose(np.load(modelDir + "/W1.npy")),
"W2": np.transpose(np.load(modelDir + "/W2.npy")),
"U1": np.transpose(np.load(modelDir + "/U1.npy")),
"U2": np.transpose(np.load(modelDir + "/U2.npy")),
"Bg1": np.load(modelDir + "/Bg1.npy"),
"Bh1": np.load(modelDir + "/Bh1.npy"),
"Bg2": np.load(modelDir + "/Bg2.npy"),
"Bh2": np.load(modelDir + "/Bh2.npy"),
"zeta1": sigmoid(np.load(modelDir + "/zeta1.npy")),
"nu1": sigmoid(np.load(modelDir + "/nu1.npy")),
"zeta2": sigmoid(np.load(modelDir + "/zeta2.npy")),
"nu2": sigmoid(np.load(modelDir + "/nu2.npy")),
}

return model


def getArgs():
'''
Function to parse arguments for FastCells
'''
parser = argparse.ArgumentParser(
description='HyperParams for RNNPool inference')

parser.add_argument('-mdir', '--model-dir', required=False, default=None,
help='Model directory containing' +
'RNNPool model')

parser.add_argument('-tidir', '--trace-input-dir', required=False, default=None,
help='Directory containing RnnPool input output numpy traces')

parser.add_argument('-todir', '--trace-output-dir', required=False, default=None,
help='Output Directory for saving RnnPool input output .h traces')

parser.add_argument('-rnn1oF', '--rnn1-out-file', default=None,
help='Give a output header file name for the model to dump rnn1 weights' +
'default: stdout')

parser.add_argument('-rnn2oF', '--rnn2-out-file', default=None,
help='Give a output header file name for the model to dump rnn2 weights' +
'default: stdout')


return parser.parse_args()


def saveReadableModel(modelDir, model):
if os.path.isdir(modelDir + '/ReadableModel') is False:
try:
os.mkdir(modelDir + '/ReadableModel')
except OSError:
print("Creation of the directory %s failed" %
modelDir + '/ReadableModel')
currDir = modelDir + '/ReadableModel'

np.savetxt(currDir + "/W1.txt",
np.reshape(model["W1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/W2.txt",
np.reshape(model["W2"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/U1.txt",
np.reshape(model["U1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/U2.txt",
np.reshape(model["U2"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/Bg1.txt",
np.reshape(model["Bg1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/Bh1.txt",
np.reshape(model["Bh1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/Bg2.txt",
np.reshape(model["Bg2"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/Bh2.txt",
np.reshape(model["Bh2"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/zeta1.txt",
np.reshape(model["zeta1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/nu1.txt",
np.reshape(model["nu1"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/zeta2.txt",
np.reshape(model["zeta2"], [1, -1]), delimiter=',')
np.savetxt(currDir + "/nu2.txt",
np.reshape(model["nu2"], [1, -1]), delimiter=',')

return currDir


def convertMatrixToVecString(mat):
mat = str(np.reshape(mat, [1, -1])[0, :].tolist())
mat = '{' + mat[1:-1] + '}'
return mat


def saveModelHeader(rnn1OutFile, rnn2OutFile, model):
rnn1OutFile = open(rnn1OutFile, 'w')
rnn2OutFile = open(rnn2OutFile, 'w')

print ("#define HIDDEN_DIMS1 8\n", file=rnn1OutFile)

print("static float W1[INPUT_DIMS * HIDDEN_DIMS1] = " + convertMatrixToVecString(model['W1']) + ";", file=rnn1OutFile)

print("static float U1[HIDDEN_DIMS1 * HIDDEN_DIMS1] = " + convertMatrixToVecString(model['U1']) + ";", file=rnn1OutFile)

print("static float Bg1[HIDDEN_DIMS1] = " + convertMatrixToVecString(model['Bg1']) + ";", file=rnn1OutFile)
print("static float Bh1[HIDDEN_DIMS1] = " +
convertMatrixToVecString(model['Bh1']) + ";\n", file=rnn1OutFile)

print("static float sigmoid_zeta1 = " + str(model['zeta1'][0][0]) + ";", file=rnn1OutFile)
print("static float sigmoid_nu1 = " + str(model['nu1'][0][0]) + ";\n", file=rnn1OutFile)


print("static float W2[HIDDEN_DIMS1 * HIDDEN_DIMS2] = " + convertMatrixToVecString(model['W2']) + ";", file=rnn2OutFile)

print("static float U2[HIDDEN_DIMS2 * HIDDEN_DIMS2] = " + convertMatrixToVecString(model['U2']) + ";\n", file=rnn2OutFile)

print("static float Bg2[HIDDEN_DIMS2] = " + convertMatrixToVecString(model['Bg2']) + ";", file=rnn2OutFile)
print("static float Bh2[HIDDEN_DIMS2] = " +
convertMatrixToVecString(model['Bh2']) + ";\n", file=rnn2OutFile)

print("static float sigmoid_zeta2 = " + str(model['zeta2'][0][0]) + ";", file=rnn2OutFile)
print("static float sigmoid_nu2 = " + str(model['nu2'][0][0]) + ";\n", file=rnn2OutFile)


rnn1OutFile.flush()
rnn1OutFile.close()
rnn2OutFile.flush()
rnn2OutFile.close()


def main():
args = getArgs()

if args.model_dir is not None:
model = loadModel(args.model_dir)
currDir = saveReadableModel(args.model_dir, model)
if args.rnn1_out_file is not None and args.rnn2_out_file is not None:
saveModelHeader(args.rnn1_out_file, args.rnn2_out_file, model)
else:
print('Not saving output header files as names are not specified')


if args.trace_input_dir is not None and args.trace_output_dir is not None:
saveTraces(args.trace_input_dir, args.trace_output_dir)



main()
19 changes: 19 additions & 0 deletions c_reference/tests/converters/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Code for Converting Numpy Model Weights/Traces to Header Files


## RNNPool

Specify the path to model weights saved in numpy format in <your-numpy-model-weights-dir> and path to input output traces saved in numpy format in <your-numpy-traces-dir>. Specify folder to save converted header files for first rnn (which does the row-wise/column-wise traversal) of RNNPool in <your-path-to-rnn1-header-output-file> and that of second rnn (which does the bidirectional summarization of outputs of first rnn pass) in <your-path-to-rnn2-header-output-file>.

```shell

python3 ConvertNumpytoHeaderFilesRNNPool.py --model-dir <your-numpy-model-weights-dir> -tidir <your-numpy-traces-dir> -todir <your-traces-header-outputs-dir> -rnn1oF <your-path-to-rnn1-header-output-file> -rnn2oF <your-path-to-rnn2-header-output-file>

```

eg.
```shell

python3 ConvertNumpytoHeaderFilesRNNPool.py --model-dir ./model_weights_face -tidir ./model_traces_face -todir ./traces_headers -rnn1oF rnn1.h -rnn2oF rnn2.h

```
Loading