jacobian

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

commit 0be5798cdcde98abeed8ca818d312cfcde51e410
parent 52c389b017298f07a363362afd73ecf54be816af
Author: David Freifeld <freifeld.david@gmail.com>
Date:   Sun, 28 Mar 2021 18:17:21 -0700

Removed activation name property, fixed optimizers

Diffstat:
Mexample.cpp | 9+++++----
Msrc/bpnn.cpp | 36+++++++++++++++++++++---------------
Msrc/bpnn.hpp | 23++++++++++++++---------
Msrc/optimizers.cpp | 96+++++++++++++++++++++++++++++++------------------------------------------------
4 files changed, 77 insertions(+), 87 deletions(-)

diff --git a/example.cpp b/example.cpp @@ -20,11 +20,12 @@ double bench(int batch_sz, int epochs) { auto start = std::chrono::high_resolution_clock::now(); Network net ("./data_banknote_authentication.txt", batch_sz, 0.0155, 0.03, L2, 0, 0.9); - net.add_layer(4, "linear", linear, linear_deriv); - net.add_layer(5, "lecun", lecun_tanh, lecun_tanh_deriv); - net.add_layer(2, "linear", linear, linear_deriv); + net.add_layer(4, linear, linear_deriv); + net.add_layer(5, lecun_tanh, lecun_tanh_deriv); + net.add_layer(2, linear, linear_deriv); + // net.init_optimizer(optimizers::demon(0.1, 50)); net.initialize(); - for (int i = 0; i < epochs; i++) { + for (int i = 0; i < epochs; i++) { net.train(); } auto end = std::chrono::high_resolution_clock::now(); diff --git a/src/bpnn.cpp b/src/bpnn.cpp @@ -56,11 +56,11 @@ Network::Network(const char* path, int batch_sz, float learn_rate, float bias_ra 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 = [this]() -> void {}; - update = [this](std::vector<Eigen::MatrixXf> deltas, int i) { - *layers[length-2-i].weights -= (learning_rate * deltas[i]); + decay = []() -> void {}; + update = [](Layer& layer, Eigen::MatrixXf delta, float learning_rate) { + *layer.weights -= (learning_rate * delta); }; - // File descriptors are nonnegative integers and open() returns -1 on failure. + // File descriptors are nonnegative integers and open() returns -1 on failure. Ensures(batch_size < instances && data > 0 && val_data > 0); } @@ -70,12 +70,11 @@ Network::~Network() close(val_data); } -void Network::add_layer(int nodes, const char* name, std::function<float(float)> activation, std::function<float(float)> activation_deriv) +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); - strcpy(layers[length-1].activation_str, name); layers[length-1].activation = activation; layers[length-1].activation_deriv = activation_deriv; } @@ -116,7 +115,6 @@ void Network::feedforward() { for (int i = 0; i < length-1; i++) { for (int j = 0; j < layers[i].contents->rows(); j++) { - if (strcmp(layers[i].activation_str, "linear") == 0) break; 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)); @@ -126,7 +124,6 @@ void Network::feedforward() *layers[i+1].contents += *layers[i+1].bias; } for (int j = 0; j < layers[length-1].contents->rows(); j++) { - if (strcmp(layers[length-1].activation_str, "linear") == 0) break; 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)); @@ -138,12 +135,19 @@ void Network::feedforward() void Network::list_net() { Expects(length > 1); - std::cout << "-----------------------\nINPUT LAYER (LAYER 0)\n-----------------------\n\n\u001b[31mGENERAL INFO:\x1B[0;37m\nActivation Function: " << layers[0].activation_str << "\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"; + 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\n\u001b[31mGENERAL INFO:\x1B[0;37m\nActivation Function: " << layers[i].activation_str; - std::cout << "\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 << "-----------------------\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\n\u001b[31mGENERAL INFO:\x1B[0;37m\nActivation Function: " << layers[length-1].activation_str <<"\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"; + 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() @@ -213,8 +217,8 @@ Eigen::MatrixXf Network::backpropagate() 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. + 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)); @@ -222,7 +226,7 @@ Eigen::MatrixXf Network::backpropagate() counter++; } for (int i = 0; i < length-1; i++) { - update(deltas, 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]; @@ -249,6 +253,8 @@ void Network::validate(const char* path) Ensures(lseek(val_data, 0, SEEK_CUR) == 0); } +#include "optimizers.cpp" + void Network::train() { float cost_sum = 0; diff --git a/src/bpnn.hpp b/src/bpnn.hpp @@ -3,8 +3,6 @@ #include <eigen3/Eigen/Dense> -//#include "../../mapreduce/mapreduce.h" - #include <vector> #include <iostream> #include <string> @@ -16,7 +14,6 @@ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> -// #include <gsl/gsl_assert> #include <lz4.h> #define BUFFER_SIZE 600*1024 @@ -34,7 +31,7 @@ public: std::function<float(float)> activation; std::function<float(float)> activation_deriv; char activation_str[32]; - + Layer(int rows, int columns); Layer(float* vals, int rows, int columns); void operator=(const Layer& that); @@ -52,8 +49,8 @@ protected: float val_cost; std::function<void(void)> decay; std::function<void(std::vector<Eigen::MatrixXf>, int, int)> grad_calc; - std::function<void(std::vector<Eigen::MatrixXf>, int)> update; - void next_batch(int fd); + std::function<void(Layer&, Eigen::MatrixXf, float)> update; + void next_batch(int fd); public: int data; int val_data; @@ -72,20 +69,21 @@ public: 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, const char* name, std::function<float(float)> activation, std::function<float(float)> activation_deriv); + 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 set_activation(int index, std::function<float(float)> custom, std::function<float(float)> custom_deriv); void feedforward(); void softmax(); void list_net(); float cost(); float accuracy(); - Eigen::MatrixXf backpropagate(); + Eigen::MatrixXf backpropagate(); void validate(const char* path); void train(); float get_acc() {return epoch_acc;} @@ -101,6 +99,13 @@ void prep(const char* rname, const char* wname); void compress(const char* rname, const char* wname); Eigen::MatrixXf l1_deriv(Eigen::MatrixXf m); +namespace optimizers { +std::function<void(Layer&, Eigen::MatrixXf, float)> momentum(float beta); +std::function<void(Layer&, Eigen::MatrixXf, float)> demon(float beta_init, int max_ep); +std::function<void(Layer&, Eigen::MatrixXf, float)> adam(float beta1, float beta2, float epsilon); +std::function<void(Layer&, Eigen::MatrixXf, float)> adamax(float beta1, float beta2, float epsilon); +} + #define MAXLINE 1024 #if (!RECKLESS) diff --git a/src/optimizers.cpp b/src/optimizers.cpp @@ -5,68 +5,46 @@ // Created by David Freifeld // -void Network::init_optimizer(const char* name, ...) -{ - va_list args; - va_start(args, name); - if (strcmp(name, "momentum") == 0) { - float beta = va_arg(args, double); - va_end(args); - update = [this, beta](std::vector<Eigen::MatrixXf> deltas, int i) { - *layers[length-2-i].weights -= (beta * *layers[length-2-i].m) + (learning_rate * deltas[i]); - *layers[length-2-i].m = (learning_rate * deltas[i]); - }; - } - // TODO: Remove redundant code | -t quality -m Attempt split into functions to remove reundant code - if (strcmp(name, "nesterov") == 0) { - float beta = va_arg(args, double); - va_end(args); - grad_calc = [this](std::vector<Eigen::MatrixXf> gradients, int i, int counter) -> void { - gradients.push_back((gradients[counter-1] * (*layers[i].weights-((learning_rate * *layers[i].weights) + (0.9 * *layers[i].v))).transpose()).cwiseProduct(*layers[i].dZ)); - }; - update = [this, beta](std::vector<Eigen::MatrixXf> deltas, int i) { - *layers[length-2-i].weights -= (beta * *layers[length-2-i].m) + (learning_rate * deltas[i]); - *layers[length-2-i].m = (learning_rate * deltas[i]); + +std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::momentum(float beta) { + return [beta](Layer& layer, Eigen::MatrixXf delta, float learning_rate) { + *layer.weights -= (beta * *layer.m) + (learning_rate * delta); + *layer.m = (learning_rate * delta); }; - } - else if (strcmp(name, "demon") == 0) { - float beta_init = va_arg(args, double); - float max_ep = va_arg(args, int); - float beta = beta_init; - int prev_epoch = -1; - update = [this, max_ep, prev_epoch, beta_init, beta](std::vector<Eigen::MatrixXf> deltas, int i) mutable { - if (epochs > prev_epoch) { +} + +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](Layer& layer, Eigen::MatrixXf delta, float learning_rate) mutable { beta = beta_init * (1-(epochs/max_ep)) / ((beta_init * (1-(epochs/max_ep))) + (1-beta_init)); - prev_epoch = epochs; - } - *layers[length-2-i].weights -= (beta * *layers[length-2-i].m) + (learning_rate * deltas[i]); - *layers[length-2-i].m = (learning_rate * deltas[i]); + *layer.weights -= (beta * *layer.m) + (learning_rate * delta); + *layer.m = (learning_rate * delta); + epochs++; }; - } - else if (strcmp(name, "adam") == 0) { - float beta1 = va_arg(args, double); - float beta2 = va_arg(args, double); - float epsilon = va_arg(args, double); - // TODO: Add bias correction to adam | -t coding -m (requires figuring out measuring t) - // TODO: Investigate cwiseProduct in code | -t quality -m cwiseProduct here is sketchy, look into me - update = [this, beta1, beta2, epsilon](std::vector<Eigen::MatrixXf> deltas, int i) { - *layers[length-2-i].m = (beta1 * *layers[length-2-i].m) + ((1-beta1)*deltas[i]); - *layers[length-2-i].v = (beta2 * *layers[length-2-i].v) + (1-beta2)*(deltas[i].cwiseProduct(deltas[i])); - *layers[length-2-i].weights -= learning_rate * ((layers[length-2-i].v->cwiseSqrt()).array()+epsilon).pow(-1).cwiseProduct(layers[length-2-i].m->array()).matrix(); +} + +std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::adam(float beta1, float beta2, float epsilon) { + return [beta1, beta2, epsilon](Layer& layer, Eigen::MatrixXf delta, 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(); }; - } - else if (strcmp(name, "adamax") == 0) { - float beta1 = va_arg(args, double); - float beta2 = va_arg(args, double); - float epsilon = va_arg(args, double); - // TODO: Add bias correction for adamax | -t coding -m (requires figuring out measuring t) - update = [this, beta1, beta2, epsilon](std::vector<Eigen::MatrixXf> deltas, int i) { - *layers[length-2-i].m = (beta1 * *layers[length-2-i].m) + ((1-beta1)*deltas[i]); - // TODO: Fix Adamax calculations | -p C -t quality -m Use of .sum() here is incredibly questionable. Do this correctly. - if ((beta2 * *layers[length-2-i].v).sum() > deltas[i].array().abs().sum()) *layers[length-2-i].v = (beta2 * *layers[length-2-i].v); - else *layers[length-2-i].v = deltas[i].array().abs().matrix(); - *layers[length-2-i].weights -= learning_rate * (layers[length-2-i].v->array().pow(-1).cwiseProduct(layers[length-2-i].m->array())).matrix(); +} + +std::function<void(Layer&, Eigen::MatrixXf, float)> optimizers::adamax(float beta1, float beta2, float epsilon) { + return [beta1, beta2, epsilon](Layer& layer, Eigen::MatrixXf delta, 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(); }; - } - va_end(args); +} + +void Network::init_optimizer(std::function<void(Layer&, Eigen::MatrixXf, float)> f) +{ + update = f; }