blob: 0ad57bbb4d43de94b8e76f04ec126ff56a2daa56 [file]
/*!
* Copyright (c) 2015 by Contributors
*/
#include <iostream>
#include <utility>
#include <map>
#include <string>
#include <vector>
#include "mxnet/ndarray.h"
#include "mxnet/base.h"
#include "mxnet/operator.h"
#include "mxnet/symbolic.h"
using namespace std;
#if MSHADOW_USE_CUDA
#define DEV_CTX \
(mxnet::Context::Create(mxnet::Context::kGPU, 0)) // use no.0 gpu
#else
#define DEV_CTX (mxnet::Context::Create(mxnet::Context::kCPU, 0)) // use cpu
#endif
/*
* In this example,
* we make by hand some data in 10 classes with some pattern
* and try to use MLP to recognize the pattern.
*/
class MLP {
public:
mxnet::Symbol LeakyReLULayer(mxnet::Symbol input, std::string name = "relu") {
mxnet::OperatorProperty* leaky_relu_op =
mxnet::OperatorProperty::Create("LeakyReLU");
std::vector<std::pair<std::string, std::string> > relu_config;
relu_config.push_back(
std::make_pair("act_type", "leaky")); // rrelu leaky prelu elu
relu_config.push_back(std::make_pair("slope", "0.25"));
relu_config.push_back(std::make_pair("lower_bound", "0.125"));
relu_config.push_back(std::make_pair("upper_bound", "0.334"));
leaky_relu_op->Init(relu_config);
std::vector<mxnet::Symbol> sym_vec;
sym_vec.push_back(input);
mxnet::Symbol leaky_relu =
mxnet::Symbol::Create(leaky_relu_op)(sym_vec, name);
return leaky_relu;
}
mxnet::Symbol FullyConnectedLayer(mxnet::Symbol input,
std::string num_hidden = "28",
std::string name = "fc") {
mxnet::OperatorProperty* fully_connected_op =
mxnet::OperatorProperty::Create("FullyConnected");
std::vector<std::pair<std::string, std::string> > fc_config;
fc_config.push_back(std::make_pair("num_hidden", num_hidden));
fc_config.push_back(std::make_pair("no_bias", "false"));
fully_connected_op->Init(fc_config);
std::vector<mxnet::Symbol> sym_vec;
sym_vec.push_back(input);
mxnet::Symbol fc = mxnet::Symbol::Create(fully_connected_op)(sym_vec, name);
return fc;
}
mxnet::Symbol SoftmaxLayer(mxnet::Symbol input,
std::string name = "softmax") {
mxnet::OperatorProperty* softmax_output_op =
mxnet::OperatorProperty::Create("SoftmaxOutput");
std::vector<std::pair<std::string, std::string> > config;
softmax_output_op->Init(config);
std::vector<mxnet::Symbol> sym_vec;
sym_vec.push_back(input);
mxnet::Symbol softmax =
mxnet::Symbol::Create(softmax_output_op)(sym_vec, name);
return softmax;
}
void OutputAccuracy(mxnet::real_t* pred, mxnet::real_t* target) {
int right = 0;
for (int i = 0; i < 128; ++i) {
float mx_p = pred[i * 10 + 0];
float p_y = 0;
for (int j = 0; j < 10; ++j) {
if (pred[i * 10 + j] > mx_p) {
mx_p = pred[i * 10 + j];
p_y = j;
}
}
if (p_y == target[i]) right++;
}
cout << "Accuracy: " << right / 128.0 << endl;
}
void Train() {
// setup sym network
mxnet::Symbol sym_x = mxnet::Symbol::CreateVariable("X");
mxnet::Symbol sym_fc_1 = FullyConnectedLayer(sym_x, "512", "fc1");
mxnet::Symbol sym_act_1 = LeakyReLULayer(sym_fc_1, "act_1");
mxnet::Symbol sym_fc_2 = FullyConnectedLayer(sym_act_1, "10", "fc2");
mxnet::Symbol sym_act_2 = LeakyReLULayer(sym_fc_2, "act_2");
mxnet::Symbol sym_out = SoftmaxLayer(sym_act_2, "softmax");
// prepare train data
mxnet::Context ctx_cpu = mxnet::Context::Create(mxnet::Context::kCPU, 1);
mxnet::Context ctx_dev = DEV_CTX; // use gpu if possible
mxnet::NDArray array_x(mshadow::Shape2(128, 28), ctx_dev, false);
mxnet::NDArray array_y(mshadow::Shape1(128), ctx_dev, false);
mxnet::real_t* aptr_x = new mxnet::real_t[128 * 28];
mxnet::real_t* aptr_y = new mxnet::real_t[128];
// we make the data by hand, in 10 classes, with some pattern
for (int i = 0; i < 128; i++) {
for (int j = 0; j < 28; j++) {
aptr_x[i * 28 + j] = i % 10 * 1.0f;
}
aptr_y[i] = i % 10;
}
array_x.SyncCopyFromCPU(aptr_x, 128 * 28);
array_x.WaitToRead();
array_y.SyncCopyFromCPU(aptr_y, 128);
array_y.WaitToRead();
// init the parameters
mxnet::NDArray array_w_1(mshadow::Shape2(512, 28), ctx_dev, false);
mxnet::NDArray array_b_1(mshadow::Shape1(512), ctx_dev, false);
mxnet::NDArray array_w_2(mshadow::Shape2(10, 512), ctx_dev, false);
mxnet::NDArray array_b_2(mshadow::Shape1(10), ctx_dev, false);
// the parameters should be initialized in some kind of distribution,
// so it learns fast
// but here just give a const value by hand
array_w_1 = 0.5f;
array_b_1 = 0.0f;
array_w_2 = 0.5f;
array_b_2 = 0.0f;
// the grads
mxnet::NDArray array_w_1_g(mshadow::Shape2(512, 28), ctx_dev, false);
mxnet::NDArray array_b_1_g(mshadow::Shape1(512), ctx_dev, false);
mxnet::NDArray array_w_2_g(mshadow::Shape2(10, 512), ctx_dev, false);
mxnet::NDArray array_b_2_g(mshadow::Shape1(10), ctx_dev, false);
// Bind the symolic network with the ndarray
std::map<std::string, mxnet::Context> g2c;
// all the input args
std::vector<mxnet::NDArray> in_args;
in_args.push_back(array_x);
in_args.push_back(array_w_1);
in_args.push_back(array_b_1);
in_args.push_back(array_w_2);
in_args.push_back(array_b_2);
in_args.push_back(array_y);
// all the grads
std::vector<mxnet::NDArray> arg_grad_store;
arg_grad_store.push_back(
mxnet::NDArray()); // we don't need the grad of the input
arg_grad_store.push_back(array_w_1_g);
arg_grad_store.push_back(array_b_1_g);
arg_grad_store.push_back(array_w_2_g);
arg_grad_store.push_back(array_b_2_g);
arg_grad_store.push_back(
mxnet::NDArray()); // neither do we need the grad of the loss
// how to handle the grad
std::vector<mxnet::OpReqType> grad_req_type;
grad_req_type.push_back(mxnet::kNullOp);
grad_req_type.push_back(mxnet::kWriteTo);
grad_req_type.push_back(mxnet::kWriteTo);
grad_req_type.push_back(mxnet::kWriteTo);
grad_req_type.push_back(mxnet::kWriteTo);
grad_req_type.push_back(mxnet::kNullOp);
std::vector<mxnet::NDArray> aux_states;
cout << "make the Executor" << endl;
mxnet::Executor* exe =
mxnet::Executor::Bind(sym_out, ctx_dev, g2c, in_args, arg_grad_store,
grad_req_type, aux_states);
cout << "Training" << endl;
int max_iters = 20000;
mxnet::real_t learning_rate = 0.0001;
for (int iter = 0; iter < max_iters; ++iter) {
exe->Forward(true);
if (iter % 100 == 0) {
cout << "epoch " << iter << endl;
const std::vector<mxnet::NDArray>& out = exe->outputs();
mxnet::NDArray c_cpu = out[0].Copy(ctx_cpu);
c_cpu.WaitToRead();
mxnet::real_t* cptr = static_cast<mxnet::real_t*>(c_cpu.data().dptr_);
OutputAccuracy(cptr, aptr_y);
}
// update the parameters
exe->Backward(std::vector<mxnet::NDArray>());
for (int i = 1; i < 5; ++i) {
in_args[i] -= arg_grad_store[i] * learning_rate;
}
}
delete[] aptr_x;
delete[] aptr_y;
}
};
int main(int argc, char** argv) {
MLP mlp;
mlp.Train();
return 0;
}