-
Notifications
You must be signed in to change notification settings - Fork 79
/
Copy pathmlp.cpp
162 lines (140 loc) · 4.53 KB
/
mlp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*!
* Copyright (c) 2015 by Contributors
*/
#include <iostream>
#include <vector>
#include <string>
#include "mxnet-cpp/MxNetCpp.h"
using namespace std;
using namespace mxnet::cpp;
/*
* In this example,
* we make by hand some data in 10 classes with some pattern
* and try to use MLP to recognize the pattern.
*/
void OutputAccuracy(mx_float* pred, mx_float* 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 MLP() {
auto sym_x = Symbol::Variable("X");
auto sym_label = Symbol::Variable("label");
const int nLayers = 2;
vector<int> layerSizes({512, 10});
vector<Symbol> weights(nLayers);
vector<Symbol> biases(nLayers);
vector<Symbol> outputs(nLayers);
for (int i = 0; i < nLayers; i++) {
string istr = to_string(i);
weights[i] = Symbol::Variable(string("w") + istr);
biases[i] = Symbol::Variable(string("b") + istr);
Symbol fc = FullyConnected(string("fc") + istr,
i == 0? sym_x : outputs[i-1],
weights[i], biases[i], layerSizes[i]);
outputs[i] = LeakyReLU(string("act") + istr, fc, LeakyReLUActType::leaky);
}
auto sym_out = SoftmaxOutput("softmax", outputs[nLayers - 1], sym_label);
Context ctx_dev(DeviceType::kCPU, 0);
NDArray array_x(Shape(128, 28), ctx_dev, false);
NDArray array_y(Shape(128), ctx_dev, false);
mx_float* aptr_x = new mx_float[128 * 28];
mx_float* aptr_y = new mx_float[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
NDArray array_w_1(Shape(512, 28), ctx_dev, false);
NDArray array_b_1(Shape(512), ctx_dev, false);
NDArray array_w_2(Shape(10, 512), ctx_dev, false);
NDArray array_b_2(Shape(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
NDArray array_w_1_g(Shape(512, 28), ctx_dev, false);
NDArray array_b_1_g(Shape(512), ctx_dev, false);
NDArray array_w_2_g(Shape(10, 512), ctx_dev, false);
NDArray array_b_2_g(Shape(10), ctx_dev, false);
// Bind the symolic network with the ndarray
// all the input args
std::vector<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<NDArray> arg_grad_store;
arg_grad_store.push_back(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(
NDArray()); // neither do we need the grad of the loss
// how to handle the grad
std::vector<OpReqType> grad_req_type;
grad_req_type.push_back(kNullOp);
grad_req_type.push_back(kWriteTo);
grad_req_type.push_back(kWriteTo);
grad_req_type.push_back(kWriteTo);
grad_req_type.push_back(kWriteTo);
grad_req_type.push_back(kNullOp);
std::vector<NDArray> aux_states;
cout << "make the Executor" << endl;
Executor* exe = new Executor(sym_out, ctx_dev, in_args, arg_grad_store,
grad_req_type, aux_states);
cout << "Training" << endl;
int max_iters = 20000;
mx_float learning_rate = 0.0001;
for (int iter = 0; iter < max_iters; ++iter) {
exe->Forward(true);
if (iter % 100 == 0) {
cout << "epoch " << iter << endl;
std::vector<NDArray>& out = exe->outputs;
float* cptr = new float[128 * 10];
out[0].SyncCopyToCPU(cptr, 128 * 10);
NDArray::WaitAll();
OutputAccuracy(cptr, aptr_y);
delete[] cptr;
}
// update the parameters
exe->Backward();
for (int i = 1; i < 5; ++i) {
in_args[i] -= arg_grad_store[i] * learning_rate;
}
NDArray::WaitAll();
}
delete exe;
delete[] aptr_x;
delete[] aptr_y;
}
int main(int argc, char** argv) {
MLP();
MXNotifyShutdown();
return 0;
}