jacobian

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

commit 5e70fba38c3bce59a155aaea9dadc7809490832c
parent 6f482b7974dcba5042b6dfce7db6453223fda80e
Author: David Freifeld <freifeld.david@gmail.com>
Date:   Sat,  5 Sep 2020 11:25:20 -0700

Minimal updates, better typecasting

Diffstat:
Mchecks.cpp | 49++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/bpnn.cpp | 213+++++++------------------------------------------------------------------------
Msrc/bpnn.hpp | 2+-
Msrc/cnn.cpp | 4+---
Asrc/data.cpp | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/experimental/strassen.cpp | 8++++----
Msrc/optimizers.cpp | 10+++++-----
Msrc/pybind.cpp | 3+--
8 files changed, 142 insertions(+), 212 deletions(-)

diff --git a/checks.cpp b/checks.cpp @@ -236,6 +236,49 @@ void basic_checks() } } -void grad_checks() -{ -} +// Eigen::MatrixXf Network::numerical_grad(int i, float epsilon) +// { +// Eigen::MatrixXf gradient (layers[i].weights->rows(), layers[i].weights->cols()); +// for (int j = 0; j < layers[i].weights->rows(); j++) { +// for (int k = 0; k < layers[i].weights->cols(); k++) { +// float current_cost = cost(); +// std::vector<Layer> backup = layers; +// (*layers[i].weights)(j,k) += epsilon; +// feedforward(); +// float end_cost = cost(); +// gradient(j,k) = (end_cost - current_cost)/epsilon; +// layers = backup; +// batches = 0; +// } +// } +// return gradient; +// } + +// void Network::grad_check() +// { +// std::vector<Layer> backup = layers; +// feedforward(); +// layers = backup; +// batches = 0; +// 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; +// checknan(error(i,j), "gradient of final layer"); +// } +// } +// int counter = 1; +// gradients.push_back(error); +// deltas.push_back((*layers[length-2].contents).transpose() * gradients[0]); +// for (int i = length-2; i >= 1; i--) { +// gradients.push_back(cwise_product(gradients[counter-1] * layers[i].weights->transpose(),*layers[i].dZ)); +// std::cout << layers[i-1].contents->transpose() * gradients[counter]; +// deltas.push_back(layers[i-1].contents->transpose() * gradients[counter]); +// counter++; +// } +// } diff --git a/src/bpnn.cpp b/src/bpnn.cpp @@ -7,6 +7,8 @@ #include "bpnn.hpp" #include "utils.hpp" +#include <atomic> +#include <chrono> #include <ctime> #include <random> @@ -27,14 +29,12 @@ Layer::Layer(int batch_sz, int nodes, float a) dZ = new Eigen::MatrixXf (batch_sz, nodes); int datalen = batch_sz*nodes; for (int i = 0; i < datalen; i++) { - (*contents)((int)i / nodes,i%nodes) = 0; - (*dZ)((int)i / nodes,i%nodes) = 0; + (*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; - } + for (int j = 0; j < batch_sz; j++) (*bias)(j, i) = 0; } } @@ -64,8 +64,8 @@ void Layer::init_weights(Layer next) std::random_device rd; std::mt19937 gen(rd()); (*weights)((int)i / nodes, i%nodes) = d(gen); - (*v)((int)i / nodes, i%nodes) = 0; - (*m)((int)i / nodes, i%nodes) = 0; + (*v)(static_cast<int>(i / nodes), i%nodes) = 0; + (*m)(static_cast<int>(i / nodes), i%nodes) = 0; } } @@ -79,9 +79,7 @@ Network::Network(char* path, int batch_sz, float learn_rate, float bias_rate, in val_data = fopen(VAL_PATH, "r"); instances = total_instances - val_instances; assert(batch_size > 0 || batch_size < instances); - decay = [this]() -> void { - learning_rate = learning_rate; - }; + decay = [this]() -> void {}; update = [this](std::vector<Eigen::MatrixXf> deltas, int i) { *layers[length-2-i].weights -= (learning_rate * deltas[i]); }; @@ -141,76 +139,19 @@ void Network::add_prelu_layer(int nodes, float a) }; } -// Gross code inbound -void Network::add_layer(int nodes, char* name) +void Network::add_layer(int nodes, std::function<float(float)> activation, std::function<float(float)> activation_deriv) { length++; layers.emplace_back(batch_size, nodes); strcpy(layers[length-1].activation_str, name); - if (strcmp(name, "sigmoid") == 0) { - layers[length-1].activation = sigmoid; - layers[length-1].activation_deriv = sigmoid_deriv; - } - else if (strcmp(name, "linear") == 0) { - layers[length-1].activation = linear; - layers[length-1].activation_deriv = linear_deriv; - } - else if (strcmp(name, "step") == 0) { - layers[length-1].activation = step; - layers[length-1].activation_deriv = step_deriv; - } - else if (strcmp(name, "lecun_tanh") == 0) { - layers[length-1].activation = lecun_tanh; - layers[length-1].activation_deriv = lecun_tanh_deriv; - } - else if (strcmp(name, "inverse_logit") == 0) { - layers[length-1].activation = inverse_logit; - layers[length-1].activation_deriv = inverse_logit_deriv; - } - else if (strcmp(name, "cloglog") == 0) { - layers[length-1].activation = cloglog; - layers[length-1].activation_deriv = cloglog_deriv; - } - else if (strcmp(name, "softplus") == 0) { - layers[length-1].activation = softplus; - layers[length-1].activation_deriv = softplus_deriv; - } - else if (strcmp(name, "relu") == 0) { - layers[length-1].activation = rectifier(linear); - layers[length-1].activation_deriv = rectifier(linear_deriv); - } - else if (strcmp(name, "leaky_relu") == 0) { - layers[length-1].activation = leaky_relu; - layers[length-1].activation_deriv = leaky_relu_deriv; - } - else if (strcmp(name, "bipolar_sigmoid") == 0) { - layers[length-1].activation = bipolar_sigmoid; - layers[length-1].activation_deriv = bipolar_sigmoid_deriv; - } - else if (strcmp(name, "tanh") == 0) { - layers[length-1].activation = [](float x) -> float {return tanh(x);}; - layers[length-1].activation_deriv = [](float x) -> float {return 1.0/cosh(x);}; - } - else if (strcmp(name, "hard_tanh") == 0) { - layers[length-1].activation = hard_tanh; - layers[length-1].activation_deriv = hard_tanh_deriv; - } - else if (strcmp(name, "resig") == 0) { - layers[length-1].activation = rectifier(sigmoid); - layers[length-1].activation_deriv = rectifier(sigmoid_deriv); - } - else { - std::cout << "Warning! Incorrect activation specified. Exiting.\n"; - exit(1); - } + layers[length-1].activation = activation; + layers[length-1].activation_deriv = activation_deriv; } void Network::initialize() { 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]); - } + 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) @@ -326,53 +267,6 @@ Eigen::MatrixXf l1_deriv(Eigen::MatrixXf m) return r; } -Eigen::MatrixXf Network::numerical_grad(int i, float epsilon) -{ - Eigen::MatrixXf gradient (layers[i].weights->rows(), layers[i].weights->cols()); - for (int j = 0; j < layers[i].weights->rows(); j++) { - for (int k = 0; k < layers[i].weights->cols(); k++) { - float current_cost = cost(); - std::vector<Layer> backup = layers; - (*layers[i].weights)(j,k) += epsilon; - feedforward(); - float end_cost = cost(); - gradient(j,k) = (end_cost - current_cost)/epsilon; - layers = backup; - batches = 0; - } - } - return gradient; -} - -void Network::grad_check() \ -{ - std::vector<Layer> backup = layers; - feedforward(); - layers = backup; - batches = 0; - 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; - checknan(error(i,j), "gradient of final layer"); - } - } - int counter = 1; - gradients.push_back(error); - deltas.push_back((*layers[length-2].contents).transpose() * gradients[0]); - for (int i = length-2; i >= 1; i--) { - gradients.push_back(cwise_product(gradients[counter-1] * layers[i].weights->transpose(),*layers[i].dZ)); - std::cout << layers[i-1].contents->transpose() * gradients[counter]; - deltas.push_back(layers[i-1].contents->transpose() * gradients[counter]); - counter++; - } -} - void Network::backpropagate() { std::vector<Eigen::MatrixXf> gradients; @@ -390,10 +284,10 @@ void 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: Find nice way to add this + 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); + //grad_calc(gradients, counter, i) gradients.push_back(cwise_product(gradients[counter-1] * layers[i].weights->transpose(), *layers[i].dZ)); deltas.push_back(layers[i-1].contents->transpose() * gradients[counter]); counter++; @@ -408,7 +302,7 @@ void Network::backpropagate() for (int j = 0; j < layers[length-2-i].contents->rows(); j++) { for (int k = 0; k < layers[length-2-i].contents->cols(); k++) { if ((*layers[length-2-i].contents)(j,k)/layers[length-2-i].alpha <= 0) { - // Choice of using index i+1 here is questionable. TODO: REVIEW + // TODO: Review questionable code | -t quality -m Choice of using index i+1 here is sketchy. sum += gradients[i+1](j,k) * (*layers[length-2-i].contents)(j,k)/layers[length-2-i].alpha; } } @@ -431,74 +325,10 @@ void Network::backpropagate() void Network::update_layer(float* vals, int datalen, int index) { - for (int i = 0; i < datalen; i++) (*layers[index].contents)((int)i / layers[index].contents->cols(),i%layers[index].contents->cols()) = vals[i]; -} - -int Network::next_batch() -{ - char line[MAXLINE] = {' '}; - int inputs = layers[0].contents->cols(); - int datalen = batch_size * inputs; - float batch[datalen]; - for (int i = 0; i < batch_size; i++) { - fgets(line, MAXLINE, data); - char *p; - p = strtok(line,","); - for (int j = 0; j < inputs; j++) { - batch[j + (i * inputs)] = strtod(p, NULL); - p = strtok(NULL,","); - } - (*labels)(i, 0) = strtod(p, NULL); - } - float* batchptr = batch; - update_layer(batchptr, datalen, 0); - return 0; + for (int i = 0; i < datalen; i++) (*layers[index].contents)(static_cast<int>(i / layers[index].contents->cols()), i%layers[index].contents->cols()) = vals[i]; } -int prep_file(char* path, char* out_path) -{ - FILE* rptr = fopen(path, "r"); - 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(char* path, int lines, float ratio) -{ - FILE* src = fopen(path, "r"); - 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; -} +#include "data.cpp" float Network::validate(char* path) { @@ -539,17 +369,12 @@ void Network::train() float acc_sum = 0; for (int i = 0; i <= instances-batch_size; i+=batch_size) { if (early_stop == true && get_val_cost() < threshold) return; - if (i != instances-batch_size) { // Don't try to advance batch on final batch. - next_batch(); - } + if (i != instances-batch_size) next_batch(); feedforward(); backpropagate(); cost_sum += cost(); acc_sum += accuracy(); batches++; - // if (i > batch_size * 10) { - // exit(1); - // } } epoch_acc = 1.0/((float) instances/batch_size) * acc_sum; epoch_cost = 1.0/((float) instances/batch_size) * cost_sum; diff --git a/src/bpnn.hpp b/src/bpnn.hpp @@ -26,7 +26,7 @@ public: std::function<float(float)> activation; std::function<float(float)> activation_deriv; char activation_str[1024]; - // PReLU layers shouldn't be Layers but inherit from them! Fix me!! + // TODO: Fix PReLU layer inheritance | -p C -m PReLU layers shouldn't be Layers but inherit from them! float alpha; Layer(int rows, int columns, float a=0); diff --git a/src/cnn.cpp b/src/cnn.cpp @@ -302,8 +302,6 @@ std::vector<Eigen::MatrixXf> gradients; deltas.push_back((*layers[length-2].contents).transpose() * gradients[0]); int counter = 1; for (int i = length-2; i >= 1; i--) { - // TODO: Find nice way to add this - // *layers[i].weights-((learning_rate * *layers[i].weights) + (0.9 * *layers[i].v))).transpose() 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++; @@ -343,7 +341,7 @@ std::vector<Eigen::MatrixXf> gradients; conv_deltas.emplace_back(conv_layers[conv_layers.size()-1].input->rows() - gradients[length-1].rows()+1, conv_layers[conv_layers.size()-1].input->cols() - gradients[length-1].cols()+1); for (int i = 0; i < conv_deltas[0].cols(); i+=conv_layers[conv_layers.size()-1].stride_len) { for (int j = 0; j < conv_deltas[0].rows(); j+=conv_layers[conv_layers.size()-1].stride_len) { - // Transpose here is sketchy + // TODO: Investigate legitimacy of tranpose | -t :quality: conv_deltas[0](j,i) = (gradients[length-1] * (conv_layers[conv_layers.size()-1].input->block(j, i, gradients[length-1].rows(), gradients[length-1].cols())).transpose()).sum(); } } diff --git a/src/data.cpp b/src/data.cpp @@ -0,0 +1,65 @@ +int Network::next_batch() +{ + char line[MAXLINE] = {' '}; + int inputs = layers[0].contents->cols(); + int datalen = batch_size * inputs; + float batch[datalen]; + for (int i = 0; i < batch_size; i++) { + fgets(line, MAXLINE, data); + char *p; + p = strtok(line,","); + for (int j = 0; j < inputs; j++) { + batch[j + (i * inputs)] = strtod(p, NULL); + p = strtok(NULL,","); + } + (*labels)(i, 0) = strtod(p, NULL); + } + float* batchptr = batch; + update_layer(batchptr, datalen, 0); + return 0; +} + +int prep_file(char* path, char* out_path) +{ + FILE* rptr = fopen(path, "r"); + 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(char* path, int lines, float ratio) +{ + FILE* src = fopen(path, "r"); + 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/experimental/strassen.cpp b/src/experimental/strassen.cpp @@ -46,13 +46,13 @@ Eigen::MatrixXf strassen_mul(Eigen::MatrixXf x, Eigen::MatrixXf y) Eigen::MatrixXf m6 = (a.block(a.rows()-block_len,0, block_len, block_len) - a.block(0,0, block_len, block_len)) * (b.block(0,0, block_len, block_len) + b.block(0,b.cols()-block_len, block_len, block_len)); Eigen::MatrixXf m7 = (a.block(0,a.cols()-block_len, block_len, block_len) - a.block(a.rows()-block_len,a.cols()-block_len, block_len, block_len)) * (b.block(a.rows()-block_len,0, block_len, block_len) + b.block(b.rows()-block_len,b.cols()-block_len, block_len, block_len)); - std::cout << m1 + m4 - m5 + m7 << "\n\n" << m3+m5 << "\n\n" << m2+m4 << "\n\n" << m1-m2+m3+m6; + // std::cout << m1 + m4 - m5 + m7 << "\n\n" << m3+m5 << "\n\n" << m2+m4 << "\n\n" << m1-m2+m3+m6; result.block(0,0, block_len, block_len) = m1 + m4 - m5 + m7; result.block(0,result.cols()-block_len, block_len, block_len) = m3 + m5; result.block(result.rows()-block_len,0, block_len, block_len) = m2 + m4; result.block(result.rows()-block_len,result.cols()-block_len, block_len, block_len) = m1 -m2 + m3 + m6; - + std::cout << "\nSTRASSEN_VER\n" << result << "\n\n\n"; return result; } @@ -72,6 +72,6 @@ int main() Eigen::MatrixXf sproduct = strassen_mul(a, b); auto strassen_end = std::chrono::high_resolution_clock::now(); - std::cout << "EIGEN: " << std::chrono::duration_cast<std::chrono::nanoseconds>(eigen_end - eigen_begin).count() / pow(10,9) << " STRASSEN: " << std::chrono::duration_cast<std::chrono::nanoseconds>(strassen_end - strassen_begin).count() / pow(10,9) << "\n"; - std::cout << "A:\n" << a << "\nB:\n" << b << "\nEigen:\n" << product << "\nStrassen:\n" << sproduct << "\n"; + /* std::cout << "EIGEN: " << std::chrono::duration_cast<std::chrono::nanoseconds>(eigen_end - eigen_begin).count() / pow(10,9) << " STRASSEN: " << std::chrono::duration_cast<std::chrono::nanoseconds>(strassen_end - strassen_begin).count() / pow(10,9) << "\n"; */ + /* std::cout << "A:\n" << a << "\nB:\n" << b << "\nEigen:\n" << product << "\nStrassen:\n" << sproduct << "\n"; */ } diff --git a/src/optimizers.cpp b/src/optimizers.cpp @@ -17,7 +17,7 @@ void Network::init_optimizer(char* name, ...) *layers[length-2-i].m = (learning_rate * deltas[i]); }; } - // TODO: split into functions to remove reundant code + // 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); @@ -47,8 +47,8 @@ void Network::init_optimizer(char* name, ...) float beta1 = va_arg(args, double); float beta2 = va_arg(args, double); float epsilon = va_arg(args, double); - // TODO: Add bias correction (requires figuring out measuring t) - // TODO: cwiseProduct here is sketchy, look into me + // 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])); @@ -59,10 +59,10 @@ void Network::init_optimizer(char* name, ...) float beta1 = va_arg(args, double); float beta2 = va_arg(args, double); float epsilon = va_arg(args, double); - // TODO: Add bias correction for m (requires figuring out measuring t) + // 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]); - // FIXME: Use of .sum() here is incredibly questionable. Do this correctly. + // 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(); diff --git a/src/pybind.cpp b/src/pybind.cpp @@ -12,10 +12,9 @@ namespace py = pybind11; PYBIND11_MODULE(mrbpnn, m) { m.doc() = "Fast machine learning in C++"; // optional module docstring - py::class_<Network>(m, "Network") .def(py::init<char*, int, float, float, int, float, float, bool, float>()) - .def("add_layer", &Network::add_layer, py::arg("nodes"), py::arg("activation")) + .def("add_layer", &Network::add_layer, py::arg("nodes"), py::arg("activation"), py::arg("activation_deriv")) //.def("add_prelu_layer", &Network::add_prelu_layer, py::arg("nodes"), py::arg("a")) .def("initialize", &Network::initialize) //.def("init_decay", &Network::init_decay, py::arg("type"), py::arg("a_0"), py::arg("k"))