jacobian

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 9fc32c95da5344e8b735b0fecae7e632d47c7aa6
parent 4b5cbf01fd46f774ee23aadbb334d47514a45e2e
Author: David Freifeld <freifeld.david@gmail.com>
Date:   Fri,  9 Apr 2021 20:05:08 -0700

Remove pointer indirection

Diffstat:
Msrc/bpnn.cpp | 451+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/bpnn.hpp | 136+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/data.cpp | 182++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/optimizers.cpp | 54+++++++++++++++++++++++++++---------------------------
Msrc/pybind.cpp | 12++++++------
5 files changed, 413 insertions(+), 422 deletions(-)

diff --git a/src/bpnn.cpp b/src/bpnn.cpp @@ -10,321 +10,320 @@ #include <random> Layer::Layer(int batch_sz, int nodes) - :weights(nullptr), v(nullptr), m(nullptr) { - contents = new Eigen::MatrixXf (batch_sz, nodes); - dZ = new Eigen::MatrixXf (batch_sz, nodes); - int datalen = batch_sz*nodes; - for (int i = 0; i < datalen; i++) { - (*contents)(static_cast<int>(i / nodes),i%nodes) = 0; - (*dZ)(static_cast<int>(i / nodes),i%nodes) = 0; - } - bias = new Eigen::MatrixXf (batch_sz, nodes); - for (int i = 0; i < nodes; i++) { - for (int j = 0; j < batch_sz; j++) (*bias)(j, i) = 0; - } + contents = Eigen::MatrixXf(batch_sz, nodes); + dZ = Eigen::MatrixXf(batch_sz, nodes); + int datalen = batch_sz*nodes; + for (int i = 0; i < datalen; i++) { + contents(static_cast<int>(i / nodes),i%nodes) = 0; + dZ(static_cast<int>(i / nodes),i%nodes) = 0; + } + bias = Eigen::MatrixXf (batch_sz, nodes); + for (int i = 0; i < nodes; i++) { + for (int j = 0; j < batch_sz; j++) bias(j, i) = 0; + } } void Layer::init_weights(Layer next) { - v = new Eigen::MatrixXf (contents->cols(), next.contents->cols()); - m = new Eigen::MatrixXf (contents->cols(), next.contents->cols()); - weights = new Eigen::MatrixXf (contents->cols(), next.contents->cols()); - int nodes = weights->cols(); - int n = contents->cols() + next.contents->cols(); - std::normal_distribution<float> d(0,sqrt(1.0/n)); - for (int i = 0; i < (weights->rows()*weights->cols()); i++) { - std::random_device rd; - std::mt19937 gen(rd()); - (*weights)(static_cast<int>(i / nodes), i%nodes) = d(gen); - (*v)(static_cast<int>(i / nodes), i%nodes) = 0; - (*m)(static_cast<int>(i / nodes), i%nodes) = 0; - } + v = Eigen::MatrixXf (contents.cols(), next.contents.cols()); + m = Eigen::MatrixXf (contents.cols(), next.contents.cols()); + weights = Eigen::MatrixXf (contents.cols(), next.contents.cols()); + int nodes = weights.cols(); + int n = contents.cols() + next.contents.cols(); + std::normal_distribution<float> d(0,sqrt(1.0/n)); + for (int i = 0; i < (weights.rows()*weights.cols()); i++) { + std::random_device rd; + std::mt19937 gen(rd()); + weights(static_cast<int>(i / nodes), i%nodes) = d(gen); + v(static_cast<int>(i / nodes), i%nodes) = 0; + m(static_cast<int>(i / nodes), i%nodes) = 0; + } } Network::Network(const char* path, int batch_sz, float learn_rate, float bias_rate, Regularization regularization, float l, float ratio, bool early_exit, float cutoff) - :batch_size(batch_sz), learning_rate(learn_rate), bias_lr(bias_rate), reg_type(regularization), - lambda(l), early_stop(early_exit), threshold(cutoff) + :batch_size(batch_sz), learning_rate(learn_rate), bias_lr(bias_rate), reg_type(regularization), + lambda(l), early_stop(early_exit), threshold(cutoff) { - Expects(batch_size > 0 && learning_rate > 0 && - bias_rate > 0 && l >= 0 && ratio >= 0 && ratio <= 1); - int total_instances = prep_file(path, SHUFFLED_PATH); - val_instances = split_file(SHUFFLED_PATH, total_instances, ratio); - prep(TRAIN_PATH, TRAIN_BIN_PATH); - prep(VAL_PATH, VAL_BIN_PATH); - data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); - val_data = open(VAL_BIN_PATH, O_RDONLY | O_NONBLOCK); - instances = total_instances - val_instances; - decay = [](float& learning_rate) -> void {}; - update = [](const Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { - *layer.weights -= (learning_rate * delta); - }; - // File descriptors are nonnegative integers and open() returns -1 on failure. - Ensures(batch_size < instances && data > 0 && val_data > 0); + Expects(batch_size > 0 && learning_rate > 0 && + bias_rate > 0 && l >= 0 && ratio >= 0 && ratio <= 1); + int total_instances = prep_file(path, SHUFFLED_PATH); + val_instances = split_file(SHUFFLED_PATH, total_instances, ratio); + prep(TRAIN_PATH, TRAIN_BIN_PATH); + prep(VAL_PATH, VAL_BIN_PATH); + data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); + val_data = open(VAL_BIN_PATH, O_RDONLY | O_NONBLOCK); + instances = total_instances - val_instances; + decay = [](float& learning_rate) -> void {}; + update = [](Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { + layer.weights = (learning_rate * delta); + }; + // File descriptors are nonnegative integers and open() returns -1 on failure. + Ensures(batch_size < instances && data > 0 && val_data > 0); } Network::~Network() { - close(data); - close(val_data); + close(data); + close(val_data); } void Network::add_layer(int nodes, std::function<float(float)> activation, std::function<float(float)> activation_deriv) { - Expects(nodes > 0); - length++; - layers.emplace_back(batch_size, nodes); - layers[length-1].activation = activation; - layers[length-1].activation_deriv = activation_deriv; + Expects(nodes > 0); + length++; + layers.emplace_back(batch_size, nodes); + layers[length-1].activation = activation; + layers[length-1].activation_deriv = activation_deriv; } void Network::initialize() { - Expects(length > 1); - labels = new Eigen::MatrixXf (batch_size,layers[length-1].contents->cols()); - for (int i = 0; i < length-1; i++) layers[i].init_weights(layers[i+1]); + Expects(length > 1); + labels = new Eigen::MatrixXf (batch_size,layers[length-1].contents.cols()); + for (int i = 0; i < length-1; i++) layers[i].init_weights(layers[i+1]); } void Network::set_activation(int index, std::function<float(float)> custom, std::function<float(float)> custom_deriv) { - Expects(index >= 0 && index < length); - layers[index].activation = custom; - layers[index].activation_deriv = custom_deriv; + Expects(index >= 0 && index < length); + layers[index].activation = custom; + layers[index].activation_deriv = custom_deriv; } void Network::softmax() { - for (int i = 0; i < layers[length-1].contents->rows(); i++) { - Eigen::MatrixXf m = layers[length-1].contents->block(i,0,1,layers[length-1].contents->cols()); - Eigen::MatrixXf::Index maxRow, maxCol; - float max = m.maxCoeff(&maxRow, &maxCol); - m = (m.array() - max).matrix(); - float sum = 0; - for (int j = 0; j < layers[length-1].contents->cols(); j++) { - sum += exp(m(0,j)); - } - for (int j = 0; j < layers[length-1].contents->cols(); j++) { - m(0,j) = exp(m(0,j))/sum; - } - layers[length-1].contents->block(i,0,1,layers[length-1].contents->cols()) = m; - } + for (int i = 0; i < layers[length-1].contents.rows(); i++) { + Eigen::MatrixXf m = layers[length-1].contents.block(i,0,1,layers[length-1].contents.cols()); + Eigen::MatrixXf::Index maxRow, maxCol; + float max = m.maxCoeff(&maxRow, &maxCol); + m = (m.array() - max).matrix(); + float sum = 0; + for (int j = 0; j < layers[length-1].contents.cols(); j++) { + sum += exp(m(0,j)); + } + for (int j = 0; j < layers[length-1].contents.cols(); j++) { + m(0,j) = exp(m(0,j))/sum; + } + layers[length-1].contents.block(i,0,1,layers[length-1].contents.cols()) = m; + } } void Network::feedforward() { - for (int i = 0; i < length-1; i++) { - for (int j = 0; j < layers[i].contents->rows(); j++) { - for (int k = 0; k < layers[i].contents->cols(); k++) { - (*layers[i].dZ)(j,k) = layers[i].activation_deriv((*layers[i].contents)(j,k)); - (*layers[i].contents)(j,k) = layers[i].activation((*layers[i].contents)(j,k)); - } - } - *layers[i+1].contents = (*layers[i].contents) * (*layers[i].weights); - *layers[i+1].contents += *layers[i+1].bias; - } - for (int j = 0; j < layers[length-1].contents->rows(); j++) { - for (int k = 0; k < layers[length-1].contents->cols(); k++) { - (*layers[length-1].dZ)(j,k) = layers[length-1].activation_deriv((*layers[length-1].contents)(j,k)); - (*layers[length-1].contents)(j,k) = layers[length-1].activation((*layers[length-1].contents)(j,k)); - } - } - softmax(); + for (int i = 0; i < length-1; i++) { + for (int j = 0; j < layers[i].contents.rows(); j++) { + for (int k = 0; k < layers[i].contents.cols(); k++) { + layers[i].dZ(j,k) = layers[i].activation_deriv(layers[i].contents(j,k)); + layers[i].contents(j,k) = layers[i].activation(layers[i].contents(j,k)); + } + } + layers[i+1].contents = layers[i].contents * layers[i].weights; + layers[i+1].contents += layers[i+1].bias; + } + for (int j = 0; j < layers[length-1].contents.rows(); j++) { + for (int k = 0; k < layers[length-1].contents.cols(); k++) { + layers[length-1].dZ(j,k) = layers[length-1].activation_deriv(layers[length-1].contents(j,k)); + layers[length-1].contents(j,k) = layers[length-1].activation(layers[length-1].contents(j,k)); + } + } + softmax(); } std::function<void(float&)> decays::step(float a_0, float k) { - return [a_0, k](float& learning_rate) -> void { - learning_rate = a_0 * learning_rate/k; - }; + return [a_0, k](float& learning_rate) -> void { + learning_rate = a_0 * learning_rate/k; + }; } std::function<void(float&)> decays::exponential(float a_0, float k) { - int epochs = 0; - return [a_0, k, epochs](float& learning_rate) mutable -> void { - learning_rate = a_0 * exp(-k * epochs); - epochs++; - }; + int epochs = 0; + return [a_0, k, epochs](float& learning_rate) mutable -> void { + learning_rate = a_0 * exp(-k * epochs); + epochs++; + }; } std::function<void(float&)> decays::fractional(float a_0, float k) { - int epochs = 0; - return [a_0, k, epochs](float& learning_rate) mutable -> void { - learning_rate = a_0 / (1+(k * epochs)); - epochs++; - }; + int epochs = 0; + return [a_0, k, epochs](float& learning_rate) mutable -> void { + learning_rate = a_0 / (1+(k * epochs)); + epochs++; + }; } std::function<void(float&)> decays::linear(int max_ep) { - int epochs = 0; - return [max_ep, epochs](float& learning_rate) mutable -> void { - learning_rate = 1 - epochs/max_ep; - epochs++; - }; + int epochs = 0; + return [max_ep, epochs](float& learning_rate) mutable -> void { + learning_rate = 1 - epochs/max_ep; + epochs++; + }; } void Network::init_decay(std::function<void(float&)> f) { - decay = f; + decay = f; } void Network::list_net() { - Expects(length > 1); - std::cout << "-----------------------\nINPUT LAYER (LAYER 0)\n" - << "\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << *layers[0].contents - << "\n\n\u001b[31mWEIGHTS:\x1B[0;37m\n" << *layers[0].weights - << "\n\n\u001b[31mBIASES:\x1B[0;37m\n" << *layers[0].bias << "\n\n\n"; - for (int i = 1; i < length-1; i++) { - std::cout << "-----------------------\nLAYER " << i - << "\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << *layers[i].contents - << "\n\n\u001b[31mBIASES:\x1B[0;37m\n" << *layers[i].bias - << "\n\n\u001b[31mWEIGHTS:\x1B[0;37m\n" << *layers[i].weights << "\n\n\n"; - } - std::cout << "-----------------------\nOUTPUT LAYER (LAYER " << length-1 - <<"\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << *layers[length-1].contents - << "\n\n\u001b[31BIASES:\x1B[0;37m\n" << *layers[length-1].bias << "\n\n\n"; + Expects(length > 1); + std::cout << "-----------------------\nINPUT LAYER (LAYER 0)\n" + << "\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << layers[0].contents + << "\n\n\u001b[31mWEIGHTS:\x1B[0;37m\n" << layers[0].weights + << "\n\n\u001b[31mBIASES:\x1B[0;37m\n" << layers[0].bias << "\n\n\n"; + for (int i = 1; i < length-1; i++) { + std::cout << "-----------------------\nLAYER " << i + << "\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << layers[i].contents + << "\n\n\u001b[31mBIASES:\x1B[0;37m\n" << layers[i].bias + << "\n\n\u001b[31mWEIGHTS:\x1B[0;37m\n" << layers[i].weights << "\n\n\n"; + } + std::cout << "-----------------------\nOUTPUT LAYER (LAYER " << length-1 + <<"\n\n\u001b[31mACTIVATIONS:\x1B[0;37m\n" << layers[length-1].contents + << "\n\n\u001b[31BIASES:\x1B[0;37m\n" << layers[length-1].bias << "\n\n\n"; } float Network::cost() { - float sum = 0; - float reg = 0; // Regularization term - for (int i = 0; i < layers[length-1].contents->rows(); i++) { - float tempsum = 0; - for (int j = 0; j < layers[length-1].contents->cols(); j++) { - float truth; - if (j==(*labels)(i,0)) truth = 1; - else truth = 0; - if ((*layers[length-1].contents)(i,j) == 0) (*layers[length-1].contents)(i,j) += 0.00001; - tempsum += truth * log((*layers[length-1].contents)(i,j)); - } - sum-=tempsum; - } - for (unsigned long i = 0; i < layers.size()-1; i++) { - if (reg_type == L2) reg += layers[i].weights->cwiseProduct(*layers[i].weights).sum(); - else if (reg_type == L1) reg += (layers[i].weights->array().abs().matrix()).sum(); - } - return ((1.0/batch_size) * sum) + (1/2*lambda*reg); + float sum = 0; + float reg = 0; // Regularization term + for (int i = 0; i < layers[length-1].contents.rows(); i++) { + float tempsum = 0; + for (int j = 0; j < layers[length-1].contents.cols(); j++) { + float truth; + if (j==(*labels)(i,0)) truth = 1; + else truth = 0; + if (layers[length-1].contents(i,j) == 0) layers[length-1].contents(i,j) += 0.00001; + tempsum += truth * log(layers[length-1].contents(i,j)); + } + sum-=tempsum; + } + for (unsigned long i = 0; i < layers.size()-1; i++) { + if (reg_type == L2) reg += layers[i].weights.cwiseProduct(layers[i].weights).sum(); + else if (reg_type == L1) reg += (layers[i].weights.array().abs().matrix()).sum(); + } + return ((1.0/batch_size) * sum) + (1/2*lambda*reg); } float Network::accuracy() { - float correct = 0; - for (int i = 0; i < layers[length-1].contents->rows(); i++) { - float ans = -INFINITY; - float index = -1; - for (int j = 0; j < layers[length-1].contents->cols(); j++) { - if ((*layers[length-1].contents)(i, j) > ans) { - ans = (*layers[length-1].contents)(i, j); - index = j; - } - } - if ((*labels)(i, 0) == index) correct += 1; - } - return (1.0/batch_size) * correct; + float correct = 0; + for (int i = 0; i < layers[length-1].contents.rows(); i++) { + float ans = -INFINITY; + float index = -1; + for (int j = 0; j < layers[length-1].contents.cols(); j++) { + if (layers[length-1].contents(i, j) > ans) { + ans = layers[length-1].contents(i, j); + index = j; + } + } + if ((*labels)(i, 0) == index) correct += 1; + } + return (1.0/batch_size) * correct; } Eigen::MatrixXf l1_deriv(Eigen::MatrixXf m) { - Eigen::MatrixXf r(m.rows(), m.cols()); - for (int i = 0; i < m.rows(); i++) { - for (int j = 0; j < m.cols(); j++) { - if (m(i,j) == 0) r(i,j) = 0; - else r(i,j) = 1; - } - } - return r; + Eigen::MatrixXf r(m.rows(), m.cols()); + for (int i = 0; i < m.rows(); i++) { + for (int j = 0; j < m.cols(); j++) { + if (m(i,j) == 0) r(i,j) = 0; + else r(i,j) = 1; + } + } + return r; } Eigen::MatrixXf Network::backpropagate() { - std::vector<Eigen::MatrixXf> gradients; - std::vector<Eigen::MatrixXf> deltas; - Eigen::MatrixXf error (layers[length-1].contents->rows(), layers[length-1].contents->cols()); - for (int i = 0; i < error.rows(); i++) { - for (int j = 0; j < error.cols(); j++) { - float truth; - if (j==(*labels)(i,0)) truth = 1; - else truth = 0; - error(i,j) = (*layers[length-1].contents)(i,j) - truth; - } - } - gradients.push_back(error); - deltas.push_back((*layers[length-2].contents).transpose() * gradients[0]); - int counter = 1; - for (int i = length-2; i >= 1; i--) { - // TODO: Add nesterov momentum | -p B -t conundrum -t coding -m Without causing segmentation faults. - // (*layers[i].weights-((learning_rate * *layers[i].weights) + (0.9 * *layers[i].v))).transpose() - //grad_calc(gradients, counter, i) - gradients.push_back((gradients[counter-1] * layers[i].weights->transpose()).cwiseProduct(*layers[i].dZ)); - deltas.push_back(layers[i-1].contents->transpose() * gradients[counter]); - counter++; - } - for (int i = 0; i < length-1; i++) { - update(layers[length-2-i], deltas[i], learning_rate); - if (reg_type == L2) *layers[length-2-i].weights -= ((lambda/batch_size) * (*layers[length-2-i].weights)); - else if (reg_type == L1) *layers[length-2-i].weights -= ((lambda/(2*batch_size)) * l1_deriv(*layers[length-2-i].weights)); - *layers[length-1-i].bias -= bias_lr * gradients[i]; - } - return gradients.back(); + std::vector<Eigen::MatrixXf> gradients; + std::vector<Eigen::MatrixXf> deltas; + Eigen::MatrixXf error (layers[length-1].contents.rows(), layers[length-1].contents.cols()); + for (int i = 0; i < error.rows(); i++) { + for (int j = 0; j < error.cols(); j++) { + float truth; + if (j==(*labels)(i,0)) truth = 1; + else truth = 0; + error(i,j) = layers[length-1].contents(i,j) - truth; + } + } + gradients.push_back(error); + deltas.push_back(layers[length-2].contents.transpose() * gradients[0]); + int counter = 1; + for (int i = length-2; i >= 1; i--) { + // TODO: Add nesterov momentum | -p B -t conundrum -t coding -m Without causing segmentation faults. + // (*layers[i].weights-((learning_rate * *layers[i].weights) + (0.9 * *layers[i].v))).transpose() + //grad_calc(gradients, counter, i) + gradients.push_back((gradients[counter-1] * layers[i].weights.transpose()).cwiseProduct(layers[i].dZ)); + deltas.push_back(layers[i-1].contents.transpose() * gradients[counter]); + counter++; + } + for (int i = 0; i < length-1; i++) { + update(layers[length-2-i], deltas[i], learning_rate); + if (reg_type == L2) layers[length-2-i].weights -= ((lambda/batch_size) * (layers[length-2-i].weights)); + else if (reg_type == L1) layers[length-2-i].weights -= ((lambda/(2*batch_size)) * l1_deriv(layers[length-2-i].weights)); + layers[length-1-i].bias -= bias_lr * gradients[i]; + } + return gradients.back(); } #include "data.cpp" void Network::validate(const char* path) { - if (val_instances == 0) return; - float costsum = 0; - float accsum = 0; - for (int i = 0; i <= val_instances-batch_size; i+=batch_size) { - next_batch(val_data); - feedforward(); - costsum += cost(); - accsum += accuracy(); - } - val_acc = 1.0/(static_cast<float>(val_instances/batch_size)) * accsum; - val_cost = 1.0/(static_cast<float>(val_instances/batch_size)) * costsum; - val_data = open(VAL_BIN_PATH, O_RDONLY | O_NONBLOCK); - Ensures(lseek(val_data, 0, SEEK_CUR) == 0); + if (val_instances == 0) return; + float costsum = 0; + float accsum = 0; + for (int i = 0; i <= val_instances-batch_size; i+=batch_size) { + next_batch(val_data); + feedforward(); + costsum += cost(); + accsum += accuracy(); + } + val_acc = 1.0/(static_cast<float>(val_instances/batch_size)) * accsum; + val_cost = 1.0/(static_cast<float>(val_instances/batch_size)) * costsum; + val_data = open(VAL_BIN_PATH, O_RDONLY | O_NONBLOCK); + Ensures(lseek(val_data, 0, SEEK_CUR) == 0); } #include "optimizers.cpp" void Network::interactive_next_batch() { - if (batches < instances/batch_size-batch_size) next_batch(data); - else { - batches = 0; - data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); - decay(learning_rate); - } - batches++; + if (batches < instances/batch_size-batch_size) next_batch(data); + else { + batches = 0; + data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); + decay(learning_rate); + } + batches++; } void Network::train() { - float cost_sum = 0; - float acc_sum = 0; - for (int i = 0; i <= instances-batch_size; i+=batch_size) { - if (i != instances-batch_size) next_batch(data); - feedforward(); - backpropagate(); - cost_sum += cost(); - acc_sum += accuracy(); - batches++; - } - epoch_acc = 1.0/(static_cast<float>(instances/batch_size)) * acc_sum; - epoch_cost = 1.0/(static_cast<float>(instances/batch_size)) * cost_sum; - validate(VAL_PATH); - if (silenced == false) printf("Epoch %i complete - cost %f - acc %f - val_cost %f - val_acc %f\n", epochs, epoch_cost, epoch_acc, val_cost, val_acc); - batches=1; - data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); - decay(learning_rate); - epochs++; - Ensures(lseek(data, 0, SEEK_CUR) == 0); + float cost_sum = 0; + float acc_sum = 0; + for (int i = 0; i <= instances-batch_size; i+=batch_size) { + if (i != instances-batch_size) next_batch(data); + feedforward(); + backpropagate(); + cost_sum += cost(); + acc_sum += accuracy(); + batches++; + } + epoch_acc = 1.0/(static_cast<float>(instances/batch_size)) * acc_sum; + epoch_cost = 1.0/(static_cast<float>(instances/batch_size)) * cost_sum; + validate(VAL_PATH); + if (silenced == false) printf("Epoch %i complete - cost %f - acc %f - val_cost %f - val_acc %f\n", epochs, epoch_cost, epoch_acc, val_cost, val_acc); + batches=1; + data = open(TRAIN_BIN_PATH, O_RDONLY | O_NONBLOCK); + decay(learning_rate); + epochs++; + Ensures(lseek(data, 0, SEEK_CUR) == 0); } diff --git a/src/bpnn.hpp b/src/bpnn.hpp @@ -15,88 +15,80 @@ #define BUFFER_SIZE 600*1024 #define LARGE_BUF 600*1024*15 -#define MATRIX_NULL Eigen::MatrixXf::Constant(0,1,1) enum Regularization {L1, L2}; class Layer { public: - Eigen::MatrixXf* contents; - Eigen::MatrixXf* weights; - Eigen::MatrixXf* bias; - Eigen::MatrixXf* dZ; - Eigen::MatrixXf* v; - Eigen::MatrixXf* m; - std::function<float(float)> activation; - std::function<float(float)> activation_deriv; - - Layer(int rows, int columns); - Layer(float* vals, int rows, int columns); - void operator=(const Layer& that); - void init_weights(Layer next); - - Eigen::MatrixXf get_contents() {return *contents;} - Eigen::MatrixXf get_weights() {if (weights == nullptr) return MATRIX_NULL; else return *weights;} - Eigen::MatrixXf get_bias() {return *bias;} - Eigen::MatrixXf get_dZ() {return *v;} - Eigen::MatrixXf get_v() {if (weights == nullptr) return MATRIX_NULL; else return *v;} - Eigen::MatrixXf get_m() {if (weights == nullptr) return MATRIX_NULL; else return *m;} + Eigen::MatrixXf contents; + Eigen::MatrixXf weights; + Eigen::MatrixXf bias; + Eigen::MatrixXf dZ; + Eigen::MatrixXf v; + Eigen::MatrixXf m; + std::function<float(float)> activation; + std::function<float(float)> activation_deriv; + + Layer(int rows, int columns); + Layer(float* vals, int rows, int columns); + void operator=(const Layer& that); + void init_weights(Layer next); }; class Network { - char buf[BUFFER_SIZE]; - char* p; + char buf[BUFFER_SIZE]; + char* p; protected: - int instances; - float epoch_acc; - float epoch_cost; - float val_acc; - float val_cost; - std::function<void(float&)> decay; - std::function<void(std::vector<Eigen::MatrixXf>, int, int)> grad_calc; - std::function<void(Layer&, Eigen::MatrixXf, float)> update; - void next_batch(int fd); + int instances; + float epoch_acc; + float epoch_cost; + float val_acc; + float val_cost; + std::function<void(float&)> decay; + std::function<void(std::vector<Eigen::MatrixXf>, int, int)> grad_calc; + std::function<void(Layer&, Eigen::MatrixXf, float)> update; + void next_batch(int fd); public: - int data; - int val_data; - int val_instances; - int test_instances; - std::vector<Layer> layers; - int length = 0; - int batch_size; - float learning_rate; - float bias_lr; - Regularization reg_type; - float lambda; - bool early_stop; - float threshold; - bool silenced = false; - int epochs = 0; - int batches = 0; - Eigen::MatrixXf* labels; - - Network(const char* path, int batch_sz, float learn_rate, - float bias_rate, Regularization regularization, - float l, float ratio, bool early_exit=true, float cutoff=0); - ~Network(); - void add_layer(int nodes, std::function<float(float)> activation, std::function<float(float)> activation_deriv); - void initialize(); - void init_optimizer(std::function<void(Layer&, Eigen::MatrixXf, float)> f); - void init_decay(std::function<void(float&)> f); - void set_activation(int index, std::function<float(float)> custom, std::function<float(float)> custom_deriv); - void feedforward(); - void softmax(); - void list_net(); - void interactive_next_batch(); - float cost(); - float accuracy(); - Eigen::MatrixXf backpropagate(); - void validate(const char* path); - void train(); - float get_acc() {return epoch_acc;} - float get_val_acc() {return val_acc;} - float get_cost() {return epoch_cost;} - float get_val_cost() {return val_cost;} + int data; + int val_data; + int val_instances; + int test_instances; + std::vector<Layer> layers; + int length = 0; + int batch_size; + float learning_rate; + float bias_lr; + Regularization reg_type; + float lambda; + bool early_stop; + float threshold; + bool silenced = false; + int epochs = 0; + int batches = 0; + Eigen::MatrixXf* labels; + + Network(const char* path, int batch_sz, float learn_rate, + float bias_rate, Regularization regularization, + float l, float ratio, bool early_exit=true, float cutoff=0); + ~Network(); + void add_layer(int nodes, std::function<float(float)> activation, std::function<float(float)> activation_deriv); + void initialize(); + void init_optimizer(std::function<void(Layer&, Eigen::MatrixXf, float)> f); + void init_decay(std::function<void(float&)> f); + void set_activation(int index, std::function<float(float)> custom, std::function<float(float)> custom_deriv); + void feedforward(); + void softmax(); + void list_net(); + void interactive_next_batch(); + float cost(); + float accuracy(); + Eigen::MatrixXf backpropagate(); + void validate(const char* path); + void train(); + float get_acc() {return epoch_acc;} + float get_val_acc() {return val_acc;} + float get_cost() {return epoch_cost;} + float get_val_cost() {return val_cost;} }; int prep_file(const char* path, const char* out_path); diff --git a/src/data.cpp b/src/data.cpp @@ -1,111 +1,111 @@ typedef float val_t; inline float scan(char **p) { - float n; - int neg = 1; - while (!isdigit(**p) && **p != '-' && **p != '.') ++*p; - if (**p == '-') neg = -1, ++*p; - for (n=0; isdigit(**p); ++*p) (n *= 10) += (**p-'0'); - if (*(*p)++ != '.') return n*neg; - float d = 1; - for (; isdigit(**p); ++*p) n += (d /= 10) * (**p-'0'); - return n*neg; + float n; + int neg = 1; + while (!isdigit(**p) && **p != '-' && **p != '.') ++*p; + if (**p == '-') neg = -1, ++*p; + for (n=0; isdigit(**p); ++*p) (n *= 10) += (**p-'0'); + if (*(*p)++ != '.') return n*neg; + float d = 1; + for (; isdigit(**p); ++*p) n += (d /= 10) * (**p-'0'); + return n*neg; } void prep(const char* rname, const char* wname) { - FILE* wptr = fopen(wname, "wb"); - FILE* rptr = fopen(rname, "rb"); - if(!wptr) throw std::runtime_error{"prep() could not write to the output file."}; - if(!rptr) throw std::runtime_error{"prep() could not read file for binary translation."}; - float tmp; - char buf[BUFFER_SIZE+1]; - while(fgets(buf, BUFFER_SIZE+1, rptr)) { - char* p = buf; - for (int i=0; i<5; ++i) { - tmp = scan(&p); - fwrite(static_cast<void*>(&tmp), sizeof(float), 1, wptr); - } - } - fclose(wptr); - fclose(rptr); + FILE* wptr = fopen(wname, "wb"); + FILE* rptr = fopen(rname, "rb"); + if(!wptr) throw std::runtime_error{"prep() could not write to the output file."}; + if(!rptr) throw std::runtime_error{"prep() could not read file for binary translation."}; + float tmp; + char buf[BUFFER_SIZE+1]; + while(fgets(buf, BUFFER_SIZE+1, rptr)) { + char* p = buf; + for (int i=0; i<5; ++i) { + tmp = scan(&p); + fwrite(static_cast<void*>(&tmp), sizeof(float), 1, wptr); + } + } + fclose(wptr); + fclose(rptr); } void Network::next_batch(int fd) { - Expects(fd > 0); // File descriptor must be valid. - uintmax_t lines = 0; - while(size_t bytes_read = read(fd, buf, BUFFER_SIZE)) { - if (!bytes_read) break; - p = buf; - while(p < buf+BUFFER_SIZE) { - if (lines >= 10) return; - for (int i=0; i<layers[0].contents->cols(); ++i) { - (*layers[0].contents)(lines,i) = *(reinterpret_cast<float*>(p)); - p += sizeof(float); - } - (*labels)(lines,0) = *(reinterpret_cast<float*>(p)); - p += sizeof(float); - ++lines; - } - } - if (p < buf+BUFFER_SIZE) { - while(p < buf+BUFFER_SIZE) { - if (lines >= 10) return; - for (int i=0; i<layers[0].contents->cols(); ++i) { - (*layers[0].contents)(lines,i) = *(reinterpret_cast<float*>(p)); - p += sizeof(float); - } - (*labels)(lines,0) = *(reinterpret_cast<float*>(p)); - p += sizeof(float); - ++lines; - } - } + Expects(fd > 0); // File descriptor must be valid. + uintmax_t lines = 0; + while(size_t bytes_read = read(fd, buf, BUFFER_SIZE)) { + if (!bytes_read) break; + p = buf; + while(p < buf+BUFFER_SIZE) { + if (lines >= 10) return; + for (int i=0; i<layers[0].contents.cols(); ++i) { + layers[0].contents(lines,i) = *(reinterpret_cast<float*>(p)); + p += sizeof(float); + } + (*labels)(lines,0) = *(reinterpret_cast<float*>(p)); + p += sizeof(float); + ++lines; + } + } + if (p < buf+BUFFER_SIZE) { + while(p < buf+BUFFER_SIZE) { + if (lines >= 10) return; + for (int i=0; i<layers[0].contents.cols(); ++i) { + layers[0].contents(lines,i) = *(reinterpret_cast<float*>(p)); + p += sizeof(float); + } + (*labels)(lines,0) = *(reinterpret_cast<float*>(p)); + p += sizeof(float); + ++lines; + } + } } int prep_file(const char* path, const char* out_path) { - FILE* rptr = fopen(path, "r"); - if (!rptr) throw std::runtime_error{"prep_file() could not open file for shuffle/read."}; - char line[MAXLINE]; - std::vector<std::string> lines; - int count = 0; - while (fgets(line, MAXLINE, rptr) != NULL) { - lines.emplace_back(line); - count++; - } - lines[lines.size()-1] = lines[lines.size()-1] + "\n"; - std::random_device rd; - std::mt19937 g(rd()); - std::shuffle(lines.begin(), lines.end(), g); - fclose(rptr); - FILE* wptr = fopen(out_path, "w"); - for (std::string & i : lines) { - const char* cstr = i.c_str(); - fprintf(wptr,"%s", cstr); - } - fclose(wptr); - return count; + FILE* rptr = fopen(path, "r"); + if (!rptr) throw std::runtime_error{"prep_file() could not open file for shuffle/read."}; + char line[MAXLINE]; + std::vector<std::string> lines; + int count = 0; + while (fgets(line, MAXLINE, rptr) != NULL) { + lines.emplace_back(line); + count++; + } + lines[lines.size()-1] = lines[lines.size()-1] + "\n"; + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(lines.begin(), lines.end(), g); + fclose(rptr); + FILE* wptr = fopen(out_path, "w"); + for (std::string & i : lines) { + const char* cstr = i.c_str(); + fprintf(wptr,"%s", cstr); + } + fclose(wptr); + return count; } int split_file(const char* path, int lines, float ratio) { - FILE* src = fopen(path, "r"); - if (!src) throw std::runtime_error{"split_file() could not open file to split."}; - FILE* test = fopen(VAL_PATH, "w"); - FILE* train = fopen(TRAIN_PATH, "w"); - int switch_line = round(ratio * lines); - char line[MAXLINE]; - int tests = 0; - for (int i = 0; fgets(line, MAXLINE, src) != NULL; i++) { - if (i > switch_line) { - fprintf(test, "%s", line); - tests++; - } - else fprintf(train, "%s", line); - } - fclose(src); - fclose(test); - fclose(train); - return tests; + FILE* src = fopen(path, "r"); + if (!src) throw std::runtime_error{"split_file() could not open file to split."}; + FILE* test = fopen(VAL_PATH, "w"); + FILE* train = fopen(TRAIN_PATH, "w"); + int switch_line = round(ratio * lines); + char line[MAXLINE]; + int tests = 0; + for (int i = 0; fgets(line, MAXLINE, src) != NULL; i++) { + if (i > switch_line) { + fprintf(test, "%s", line); + tests++; + } + else fprintf(train, "%s", line); + } + fclose(src); + fclose(test); + fclose(train); + return tests; } diff --git a/src/optimizers.cpp b/src/optimizers.cpp @@ -7,44 +7,44 @@ std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::momentum(float beta) { - return [beta](const Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { - *layer.weights -= (beta * *layer.m) + (learning_rate * delta); - *layer.m = (learning_rate * delta); - }; + return [beta](Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { + layer.weights -= (beta * layer.m) + (learning_rate * delta); + layer.m = (learning_rate * delta); + }; } std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::demon(float beta, int max_ep) { - float beta_init = beta; - float prev_epoch = -1; - float epochs = 0; - return [max_ep, epochs, beta_init, beta](const Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) mutable { - beta = beta_init * (1-(epochs/max_ep)) / ((beta_init * (1-(epochs/max_ep))) + (1-beta_init)); - *layer.weights -= (beta * *layer.m) + (learning_rate * delta); - *layer.m = (learning_rate * delta); - epochs++; - }; + float beta_init = beta; + float prev_epoch = -1; + float epochs = 0; + return [max_ep, epochs, beta_init, beta](Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) mutable { + beta = beta_init * (1-(epochs/max_ep)) / ((beta_init * (1-(epochs/max_ep))) + (1-beta_init)); + layer.weights -= (beta * layer.m) + (learning_rate * delta); + layer.m = (learning_rate * delta); + epochs++; + }; } std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::adam(float beta1, float beta2, float epsilon) { - return [beta1, beta2, epsilon](const Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { - *layer.m = (beta1 * *layer.m) + ((1-beta1)*delta); - *layer.v = (beta2 * *layer.v) + (1-beta2)*(delta.cwiseProduct(delta)); - *layer.weights -= learning_rate * - ((layer.v->cwiseSqrt()).array()+epsilon).pow(-1).cwiseProduct(layer.m->array()).matrix(); - }; + return [beta1, beta2, epsilon](Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { + layer.m = (beta1 * layer.m) + ((1-beta1)*delta); + layer.v = (beta2 * layer.v) + (1-beta2)*(delta.cwiseProduct(delta)); + layer.weights -= learning_rate * + ((layer.v.cwiseSqrt()).array()+epsilon).pow(-1).cwiseProduct(layer.m.array()).matrix(); + }; } std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::adamax(float beta1, float beta2, float epsilon) { - return [beta1, beta2, epsilon](const Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { - *layer.m = (beta1 * *layer.m) + ((1-beta1)*delta); - if ((beta2 * *layer.v).sum() > delta.array().abs().sum()) *layer.v = (beta2 * *layer.v); - else *layer.v = delta.array().abs().matrix(); - *layer.weights -= learning_rate * - (layer.v->array().pow(-1).cwiseProduct(layer.m->array())).matrix(); - }; + return [beta1, beta2, epsilon](Layer& layer, const Eigen::MatrixXf delta, const float learning_rate) { + layer.m = (beta1 * layer.m) + ((1-beta1)*delta); + if ((beta2 * layer.v).sum() > delta.array().abs().sum()) layer.v = (beta2 * layer.v); + else layer.v = delta.array().abs().matrix(); + layer.weights -= learning_rate * + (layer.v.array().pow(-1).cwiseProduct(layer.m.array())).matrix(); + }; } void Network::init_optimizer(std::function<void(Layer&, Eigen::MatrixXf, float)> f) { - update = f; + update = f; } diff --git a/src/pybind.cpp b/src/pybind.cpp @@ -23,12 +23,12 @@ PYBIND11_MODULE(_jacobian, m) .export_values(); py::class_<Layer>(m, "Layer") .def(py::init<int, int>()) - .def("get_contents", &Layer::get_contents) - .def("get_weights", &Layer::get_weights) - .def("get_bias", &Layer::get_bias) - .def("get_v", &Layer::get_v) - .def("get_m", &Layer::get_m) - .def("get_dZ", &Layer::get_dZ) + .def_readonly("contents", &Layer::contents) + .def_readonly("weights", &Layer::weights) + .def_readonly("bias", &Layer::bias) + .def_readonly("v", &Layer::v) + .def_readonly("m", &Layer::m) + .def_readonly("dZ", &Layer::dZ) .def_readonly("activation", &Layer::activation) .def_readonly("activation_deriv", &Layer::activation); py::class_<Network>(m, "Network")