|
|
e865c9 |
#ifndef LAYER_TEST_INC_CPP
|
|
|
e865c9 |
#define LAYER_TEST_INC_CPP
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
#include <thread></thread>
|
|
|
e865c9 |
|
|
|
e865c9 |
#include "layer.inc.cpp"
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
class Test {
|
|
|
e865c9 |
public:
|
|
|
e865c9 |
class Stage {
|
|
|
e865c9 |
public:
|
|
|
e865c9 |
const int errors;
|
|
|
e865c9 |
inline explicit Stage(const char *name): errors(Test::errors) {
|
|
|
e865c9 |
for(int i = 0; i < level; ++i) printf("- ");
|
|
|
e865c9 |
printf("%s\n", name);
|
|
|
e865c9 |
fflush(stdout);
|
|
|
e865c9 |
++level;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
inline ~Stage() {
|
|
|
e865c9 |
--level;
|
|
|
e865c9 |
if (!*this) {
|
|
|
e865c9 |
for(int i = 0; i < level; ++i) printf("- ");
|
|
|
e865c9 |
printf("FAILED\n");
|
|
|
e865c9 |
}
|
|
|
e865c9 |
fflush(stdout);
|
|
|
e865c9 |
}
|
|
|
e865c9 |
operator bool() { return Test::errors == errors; }
|
|
|
e865c9 |
};
|
|
|
e865c9 |
|
|
|
e865c9 |
private:
|
|
|
e865c9 |
static int level;
|
|
|
e865c9 |
|
|
|
e865c9 |
protected:
|
|
|
e865c9 |
static std::vector<neuron> c_neurons;</neuron>
|
|
|
e865c9 |
static std::vector<neuron> p_neurons;</neuron>
|
|
|
e865c9 |
static std::vector<weight> weights;</weight>
|
|
|
e865c9 |
|
|
|
e865c9 |
public:
|
|
|
e865c9 |
static int errors;
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
static void init(int c_count, int p_count, int w_count) {
|
|
|
e865c9 |
Neuron n = {};
|
|
|
e865c9 |
Weight w = {};
|
|
|
e865c9 |
|
|
|
e865c9 |
c_neurons.clear();
|
|
|
e865c9 |
p_neurons.clear();
|
|
|
e865c9 |
weights.clear();
|
|
|
e865c9 |
c_neurons.resize(c_count, n);
|
|
|
e865c9 |
p_neurons.resize(p_count, n);
|
|
|
e865c9 |
weights.resize(w_count, w);
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
static bool verifyNeurons(const char *name, const Layout &l, const Neuron *neurons, bool ignorePadded = false) {
|
|
|
e865c9 |
Stage st(name);
|
|
|
e865c9 |
for(int y = 0; y < l.sy; ++y)
|
|
|
e865c9 |
for(int x = 0; x < l.sx; ++x)
|
|
|
e865c9 |
for(int z = 0; z < l.sz; ++z) {
|
|
|
e865c9 |
int n = neurons[ (y*l.sx + x)*l.sz + z ].a.i;
|
|
|
e865c9 |
int i = x >= l.x0 && x < l.x1
|
|
|
e865c9 |
&& y >= l.y0 && y < l.y1
|
|
|
e865c9 |
&& z >= l.z0 && z < l.z1;
|
|
|
e865c9 |
if (ignorePadded ? i && n != i : n != i) {
|
|
|
e865c9 |
printf(
|
|
|
e865c9 |
"wrong neuron mark %d, expected %d (%d, %d, %d)\n",
|
|
|
e865c9 |
n, i, y, x, z );
|
|
|
e865c9 |
l.printYXZ("layout");
|
|
|
e865c9 |
++errors;
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
}
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
static bool verifyNeuronIndices(const char *name, const Layout &l, const Neuron *neurons, int base = 1, int stride = 1) {
|
|
|
e865c9 |
Stage st(name);
|
|
|
e865c9 |
for(int y = 0; y < l.sy; ++y)
|
|
|
e865c9 |
for(int x = 0; x < l.sx; ++x)
|
|
|
e865c9 |
for(int z = 0; z < l.sz; ++z) {
|
|
|
e865c9 |
bool active = x >= l.x0 && x < l.x1
|
|
|
e865c9 |
&& y >= l.y0 && y < l.y1
|
|
|
e865c9 |
&& z >= l.z0 && z < l.z1;
|
|
|
e865c9 |
|
|
|
e865c9 |
int n = neurons[ (y*l.sx + x)*l.sz + z ].a.i;
|
|
|
e865c9 |
int i = (((y - l.y0)*l.getW() + x - l.x0)*l.getD() + z - l.z0)*stride + base;
|
|
|
e865c9 |
|
|
|
e865c9 |
if (!active) i = 0;
|
|
|
e865c9 |
|
|
|
e865c9 |
if (n != i) {
|
|
|
e865c9 |
printf(
|
|
|
e865c9 |
"wrong neuron mark %d, expected %d (%d, %d, %d)\n",
|
|
|
e865c9 |
n, i, y, x, z );
|
|
|
e865c9 |
l.printYXZ("layout");
|
|
|
e865c9 |
++errors;
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
}
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
static bool verifyNeuronsAccum(const Layout &l, Neuron *neurons, int accum = 1, bool ignoreBounds = false) {
|
|
|
e865c9 |
for(int y = 0; y < l.sy; ++y)
|
|
|
e865c9 |
for(int x = 0; x < l.sx; ++x)
|
|
|
e865c9 |
for(int z = 0; z < l.sz; ++z) {
|
|
|
e865c9 |
Neuron &n = neurons[ (y*l.sx + x)*l.sz + z ];
|
|
|
e865c9 |
int i = ( x >= l.x0 && x < l.x1
|
|
|
e865c9 |
&& y >= l.y0 && y < l.y1
|
|
|
e865c9 |
&& z >= l.z0 && z < l.z1 )*accum;
|
|
|
e865c9 |
if (ignoreBounds) i = accum;
|
|
|
e865c9 |
if (n.v != 0 && n.v != i) {
|
|
|
e865c9 |
printf(
|
|
|
e865c9 |
"wrong neuron mark %g, expected 0 or %d (%d, %d, %d)\n",
|
|
|
e865c9 |
n.v, i, y, x, z );
|
|
|
e865c9 |
l.printYXZ("layout");
|
|
|
e865c9 |
++errors;
|
|
|
e865c9 |
return false;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
if (n.v) n.a.i = 1;
|
|
|
e865c9 |
n.v = 0;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
return true;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
static bool testLayer(const char *name, Layer &l) {
|
|
|
e865c9 |
Stage st(name);
|
|
|
e865c9 |
|
|
|
e865c9 |
assert(l.next);
|
|
|
e865c9 |
Layer &p = l;
|
|
|
e865c9 |
Layer &c = *l.next;
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
struct H {
|
|
|
e865c9 |
Layer &p;
|
|
|
e865c9 |
Layer &c;
|
|
|
e865c9 |
|
|
|
e865c9 |
std::vector<std::thread*> threads;</std::thread*>
|
|
|
e865c9 |
std::atomic<unsigned int=""> counter;</unsigned>
|
|
|
e865c9 |
|
|
|
e865c9 |
H(Layer &p, Layer &c): p(p), c(c), counter(0) { }
|
|
|
e865c9 |
|
|
|
e865c9 |
void prepareData() {
|
|
|
e865c9 |
memcpy(c.neurons, c_neurons.data(), c.neuronsCount*sizeof(Neuron));
|
|
|
e865c9 |
memcpy(p.neurons, p_neurons.data(), p.neuronsCount*sizeof(Neuron));
|
|
|
e865c9 |
memcpy(c.weights, weights.data(), c.weightsCount*sizeof(Weight));
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
void applyDelta() {
|
|
|
e865c9 |
for(int i = 0; i < c.neuronsCount; ++i)
|
|
|
e865c9 |
c.neurons[i].d *= c_neurons[i].v - c.neurons[i].v;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
void func(int tid) {
|
|
|
e865c9 |
Barrier barrier(counter, tid, threads.size());
|
|
|
e865c9 |
c.pass(barrier);
|
|
|
e865c9 |
barrier.wait();
|
|
|
e865c9 |
if (!tid) applyDelta();
|
|
|
e865c9 |
barrier.wait();
|
|
|
e865c9 |
c.backpassDeltas(barrier);
|
|
|
e865c9 |
barrier.wait();
|
|
|
e865c9 |
c.backpassWeights(barrier);
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
bool test(const char *name, int threadsCount) {
|
|
|
e865c9 |
Stage st(name);
|
|
|
e865c9 |
|
|
|
e865c9 |
assert(threadsCount > 0);
|
|
|
e865c9 |
|
|
|
e865c9 |
counter = 0;
|
|
|
e865c9 |
threads.clear();
|
|
|
e865c9 |
threads.resize(threadsCount, nullptr);
|
|
|
e865c9 |
|
|
|
e865c9 |
prepareData();
|
|
|
e865c9 |
|
|
|
e865c9 |
p.split(threadsCount);
|
|
|
e865c9 |
c.split(threadsCount);
|
|
|
e865c9 |
for(int i = 1; i < threadsCount; ++i) threads[i] = new std::thread(&H::func, this, i);
|
|
|
e865c9 |
func(0);
|
|
|
e865c9 |
for(int i = 1; i < threadsCount; ++i) { threads[i]->join(); delete threads[i]; }
|
|
|
e865c9 |
threads.clear();
|
|
|
e865c9 |
|
|
|
e865c9 |
for(int i = 0; i < c.neuronsCount; ++i) {
|
|
|
e865c9 |
NeuronReal a = c.neurons[i].v;
|
|
|
e865c9 |
NeuronReal b = c_neurons[i + c.neuronsCount].v;
|
|
|
e865c9 |
if (fabs(a - b) > 1e-6)
|
|
|
e865c9 |
{ printf("results differs at neuron %d, was %g, expected %g\n", i, a, b); ++errors; break; }
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
for(int i = 0; i < p.neuronsCount; ++i) {
|
|
|
e865c9 |
NeuronReal a = p.neurons[i].d;
|
|
|
e865c9 |
NeuronReal b = p_neurons[i + p.neuronsCount].d;
|
|
|
e865c9 |
if (fabs(a - b) > 1e-6)
|
|
|
e865c9 |
{ printf("deltas differs at neuron %d, was %g, expected %g\n", i, a, b); ++errors; break; }
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
for(int i = 0; i < c.weightsCount; ++i) {
|
|
|
e865c9 |
WeightReal a = c.weights[i].w;
|
|
|
e865c9 |
WeightReal b = weights[i + c.weightsCount].w;
|
|
|
e865c9 |
if (fabs(a - b) > 1e-6)
|
|
|
e865c9 |
{ printf("weights differs at %d, was %g, expected %g\n", i, a, b); ++errors; break; }
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
if (!st) {
|
|
|
e865c9 |
p.layout.printYXZ("prev layout");
|
|
|
e865c9 |
c.layout.printYXZ("curr layout");
|
|
|
e865c9 |
}
|
|
|
e865c9 |
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
} h(p, c);
|
|
|
e865c9 |
|
|
|
e865c9 |
// make base data
|
|
|
e865c9 |
|
|
|
e865c9 |
init(c.neuronsCount*2, p.neuronsCount*2, c.weightsCount*2);
|
|
|
e865c9 |
for(int i = 0; i < c.neuronsCount; ++i) c_neurons[i].v = rand()/(NeuronReal)RAND_MAX;
|
|
|
e865c9 |
for(int i = 0; i < p.neuronsCount; ++i) p_neurons[i].v = rand()/(NeuronReal)RAND_MAX;
|
|
|
e865c9 |
memcpy(weights.data(), c.weights, c.weightsCount*sizeof(Weight));
|
|
|
e865c9 |
|
|
|
e865c9 |
h.prepareData();
|
|
|
e865c9 |
c.testPass();
|
|
|
e865c9 |
h.applyDelta();
|
|
|
e865c9 |
c.testBackpass();
|
|
|
e865c9 |
|
|
|
e865c9 |
memcpy(&c_neurons[c.neuronsCount], c.neurons, c.neuronsCount*sizeof(Neuron));
|
|
|
e865c9 |
memcpy(&p_neurons[p.neuronsCount], p.neurons, p.neuronsCount*sizeof(Neuron));
|
|
|
e865c9 |
memcpy(&weights[c.weightsCount], c.weights, c.weightsCount*sizeof(Weight));
|
|
|
e865c9 |
|
|
|
e865c9 |
h.test("single-thread", 1);
|
|
|
e865c9 |
h.test("2-threads", 2);
|
|
|
e865c9 |
h.test("7-threads", 7);
|
|
|
e865c9 |
h.test("8-threads", 8);
|
|
|
e865c9 |
//h.test("512-threads", 512);
|
|
|
e865c9 |
|
|
|
e865c9 |
return st;
|
|
|
e865c9 |
}
|
|
|
e865c9 |
};
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
int Test::level = 0;
|
|
|
e865c9 |
std::vector<neuron> Test::c_neurons;</neuron>
|
|
|
e865c9 |
std::vector<neuron> Test::p_neurons;</neuron>
|
|
|
e865c9 |
std::vector<weight> Test::weights;</weight>
|
|
|
e865c9 |
int Test::errors = 0;
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
|
|
|
e865c9 |
#endif
|