diff options
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | src/activations.c | 2 | ||||
-rw-r--r-- | src/main.c | 91 | ||||
-rw-r--r-- | src/nn.c | 81 | ||||
-rw-r--r-- | src/nn.h | 18 |
5 files changed, 167 insertions, 30 deletions
@@ -22,10 +22,13 @@ build: $(OBJS) ${CC} ${DLIBS} -o ${BIN} ${OBJS} run: build - ./${BIN} train -a 1e-6 data/sample_data.json -e 150 + @./${BIN} train data/sample_data.json | tee data/train_history.txt + @./${BIN} predict data/sample_data.json | jq -r '.[] | [values[] as $$val | $$val] | @tsv' > ./data/net_data.tsv + @gnuplot -p utils/plot.gpi debug: build gdb -x utils/commands.gdb --tui --args ${BIN} train -a 230 data/sample_data.json -e 150 + gdb -x utils/commands.gdb --tui --args ${BIN} predict data/sample_data.json clean: @rm $(OBJS) $(OBJDIR) -rv diff --git a/src/activations.c b/src/activations.c index e2704ee..4e17cc1 100644 --- a/src/activations.c +++ b/src/activations.c @@ -1,3 +1,5 @@ +#include <math.h> + #include "nn.h" double leaky_relu(double x); @@ -1,5 +1,6 @@ #include <stdio.h> #include <stdint.h> +#include <stdbool.h> #include <string.h> #include <stdarg.h> #include <errno.h> @@ -19,17 +20,25 @@ typedef struct Array { #define ARRAY_SIZE(x, type) sizeof(x) / sizeof(type) -static void json_read(const char *filepath, - Array *input, Array *out, - char *out_key, - char *in_keys[], - size_t in_keys_size); - -void json_read(const char *filepath, - Array *input, Array *out, - char *out_key, - char *in_keys[], - size_t n_input_keys) +static void json_read( + const char *filepath, + Array *input, Array *out, + char *out_keys[], size_t out_keys_size, + char *in_keys[], size_t in_keys_size, + bool read_output); + +static void json_write( + const char *filepath, + Array input, Array out, + char *out_keys[], size_t out_keys_size, + char *in_keys[], size_t in_keys_size); + +void json_read( + const char *filepath, + Array *input, Array *out, + char *out_keys[], size_t n_out_keys, + char *in_keys[], size_t n_input_keys, + bool read_output) { FILE *fp = NULL; char *fp_buffer = NULL; @@ -68,15 +77,24 @@ void json_read(const char *filepath, input->data = calloc(input->shape[0] * input->shape[1], sizeof(input->data[0])); out->shape[0] = (size_t)json_obj_length; - out->shape[1] = 1; + out->shape[1] = n_out_keys; out->data = calloc(out->shape[0] * out->shape[1], sizeof(out->data[0])); + if (!input->data || !out->data) goto json_read_error; + for (int i = 0; i < json_object_array_length(json_obj); i++) { json_object *item = json_object_array_get_idx(json_obj, i); - out->data[i] = json_object_get_double(json_object_object_get(item, out_key)); for (int j = 0; j < n_input_keys; j++) { - input->data[n_input_keys * i + j] = json_object_get_double(json_object_object_get(item, in_keys[j])); + size_t index = n_input_keys * i + j; + input->data[index] = json_object_get_double(json_object_object_get(item, in_keys[j])); + } + + if (!read_output) continue; + + for (int j = 0; j < n_out_keys; j++) { + size_t index = n_out_keys * i + j; + out->data[index] = json_object_get_double(json_object_object_get(item, out_keys[j])); } } @@ -90,6 +108,39 @@ json_read_error: exit(1); } +void json_write( + const char *filepath, + Array input, Array out, + char *out_keys[], size_t out_keys_size, + char *in_keys[], size_t in_keys_size) +{ + FILE *fp = (!filepath) ? fopen("/dev/stdout", "w") : fopen(filepath, "w"); + if (!fp) die("json_read() Error:"); + fprintf(fp, "[\n"); + + for (size_t i = 0; i < input.shape[0]; i++) { + fprintf(fp, " {\n"); + + for (size_t j = 0; j < input.shape[1]; j++) { + size_t index = input.shape[1] * i + j; + fprintf(fp, " \"%s\": %lf,\n", in_keys[j], input.data[index]); + } + + for (size_t j = 0; j < out.shape[1]; j++) { + size_t index = out.shape[1] * i + j; + fprintf(fp, " \"%s\": %lf", out_keys[j], out.data[index]); + + if (j == out.shape[1] - 1) fprintf(fp, "\n"); + else fprintf(fp, ",\n"); + } + + if (i == input.shape[0] - 1) fprintf(fp, " }\n"); + else fprintf(fp, " },\n"); + } + fprintf(fp, "]\n"); + fclose(fp); +} + void load_config(struct Configs *cfg, int n_args, ...) { char *filepath; @@ -145,6 +196,7 @@ int main(int argc, char *argv[]) { .alpha = 1e-5, .config_filepath = "utils/settings.cfg", .network_size = 0, + .out_filepath = NULL, }; // Try different config paths @@ -156,8 +208,8 @@ int main(int argc, char *argv[]) { Array X, y; if (!strcmp("train", argv[0])) { - json_read(argv[1], &X, &y, ml_configs.label_keys[0], ml_configs.input_keys, ml_configs.n_input_keys); - nn_network_init_weights(network, ml_configs.network_size, X.shape[1]); + json_read(argv[1], &X, &y, ml_configs.label_keys, ml_configs.n_label_keys, ml_configs.input_keys, ml_configs.n_input_keys, true); + nn_network_init_weights(network, ml_configs.network_size, X.shape[1], true); nn_network_train( network, ml_configs.network_size, X.data, X.shape, @@ -165,7 +217,14 @@ int main(int argc, char *argv[]) { load_loss(ml_configs), ml_configs.epochs, ml_configs.alpha); + nn_network_write_weights(ml_configs.weights_filepath, network, ml_configs.network_size); + fprintf(stderr, "weights saved on '%s'\n", ml_configs.weights_filepath); } else if (!strcmp("predict", argv[0])) { + json_read(argv[1], &X, &y, ml_configs.label_keys, ml_configs.n_label_keys, ml_configs.input_keys, ml_configs.n_input_keys, false); + nn_network_init_weights(network, ml_configs.network_size, X.shape[1], false); + nn_network_read_weights(ml_configs.weights_filepath, network, ml_configs.network_size); + nn_network_predict(y.data, y.shape, X.data, X.shape, network, ml_configs.network_size); + json_write(ml_configs.out_filepath, X, y, ml_configs.label_keys, ml_configs.n_label_keys, ml_configs.input_keys, ml_configs.n_input_keys); } else usage(1); nn_network_free_weights(network, ml_configs.network_size); @@ -1,3 +1,14 @@ +#include <stdlib.h> +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <unistd.h> +#include <openblas/cblas.h> + +#include "util.h" #include "nn.h" static void fill_random_weights(double *weights, double *bias, size_t rows, size_t cols); @@ -267,7 +278,71 @@ void nn_layer_forward(Layer layer, double *zout, size_t zout_shape[2], double *i 1.0, zout, layer.neurons); // beta B } -void nn_network_init_weights(Layer layers[], size_t nmemb, size_t n_inputs) +void nn_network_read_weights(char *filepath, Layer *network, size_t network_size) +{ + FILE *fp = fopen(filepath, "rb"); + if (fp == NULL) die("nn_network_read_weights Error():"); + + size_t net_size, shape[2], ret; + ret = fread(&net_size, sizeof(size_t), 1, fp); + if (net_size != network_size) goto nn_network_read_weights_error; + + for (size_t i = 0; i < network_size; i++) { + fread(shape, sizeof(size_t), 2, fp); + if (shape[0] != network[i].input_nodes + || shape[1] != network[i].neurons) { + goto nn_network_read_weights_error; + } + + if (!network[i].weights || !network[i].bias) { + die("nn_network_read_weights() Error: " + "the weights on layer %zu haven't been initialized", i); + } + + ret = fread(network[i].weights, sizeof(double), shape[0] * shape[1], fp); + if (ret != shape[0] * shape[1]) goto nn_network_read_weights_error; + + ret = fread(network[i].bias, sizeof(double), shape[1], fp); + if (ret != shape[1]) goto nn_network_read_weights_error; + } + + return; + +nn_network_read_weights_error: + die("nn_network_read_weights() Error: " + "number of read objects does not match with expected ones"); +} + +void nn_network_write_weights(char *filepath, Layer *network, size_t network_size) +{ + FILE *fp = fopen(filepath, "wb"); + if (fp == NULL) die("nn_network_write_weights() Error:"); + + fwrite(&network_size, sizeof(size_t), 1, fp); + + size_t ret; + for (size_t i = 0; i < network_size; i++) { + size_t shape[2] = {network[i].input_nodes, network[i].neurons}; + size_t size = shape[0] * shape[1]; + + ret = fwrite(shape, sizeof(size_t), 2, fp); + if (ret != 2) goto nn_network_write_weights_error; + + ret = fwrite(network[i].weights, sizeof(double), size, fp); + if (ret != size) goto nn_network_write_weights_error; + + ret = fwrite(network[i].weights, sizeof(double), network[i].neurons, fp); + if (ret != network[i].neurons) goto nn_network_write_weights_error; + } + fclose(fp); + return; + +nn_network_write_weights_error: + die("nn_network_write_weights() Error: " + "number of written objects does not match with number of objects"); +} + +void nn_network_init_weights(Layer layers[], size_t nmemb, size_t n_inputs, bool fill_random) { int i; size_t prev_size = n_inputs; @@ -280,7 +355,9 @@ void nn_network_init_weights(Layer layers[], size_t nmemb, size_t n_inputs) if (layers[i].weights == NULL || layers[i].bias == NULL) { goto nn_layers_calloc_weights_error; } - fill_random_weights(layers[i].weights, layers[i].bias, prev_size, layers[i].neurons); + + if (fill_random) fill_random_weights(layers[i].weights, layers[i].bias, prev_size, layers[i].neurons); + layers[i].input_nodes = prev_size; prev_size = layers[i].neurons; } @@ -1,14 +1,8 @@ #ifndef __NN__ #define __NN__ -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <math.h> -#include <unistd.h> -#include <openblas/cblas.h> +#include <stdbool.h> +#include <stddef.h> struct Cost { double (*func)(double labels[], double net_out[], size_t shape); @@ -26,7 +20,9 @@ typedef struct Layer { size_t neurons, input_nodes; } Layer; -void nn_network_init_weights(Layer *network, size_t nmemb, size_t input_cols); +void nn_network_write_weights(char *filepath, Layer *network, size_t network_size); +void nn_network_read_weights(char *filepath, Layer *network, size_t network_size); +void nn_network_init_weights(Layer *network, size_t nmemb, size_t input_cols, bool fill_random); void nn_network_free_weights(Layer *network, size_t nmemb); void nn_network_predict( @@ -77,10 +73,10 @@ void nn_layer_backward( void nn_layer_out_delta( double *delta, double *dcost_out, double *zout, size_t cols, - double (*activation_derivative)(double));//TODO + double (*activation_derivative)(double)); void nn_layer_hidden_delta( double *delta, double *delta_next, double *zout, double *weights_next, size_t weights_next_shape[2], - double (*activation_derivative)(double));//TODO + double (*activation_derivative)(double)); #endif |