aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--src/activations.c2
-rw-r--r--src/main.c91
-rw-r--r--src/nn.c81
-rw-r--r--src/nn.h18
5 files changed, 167 insertions, 30 deletions
diff --git a/Makefile b/Makefile
index e56ada6..7cbbd9c 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
diff --git a/src/main.c b/src/main.c
index 30aca1e..aac7e94 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/nn.c b/src/nn.c
index 904fffc..c7b8ac3 100644
--- a/src/nn.c
+++ b/src/nn.c
@@ -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;
}
diff --git a/src/nn.h b/src/nn.h
index 9005364..5402ffa 100644
--- a/src/nn.h
+++ b/src/nn.h
@@ -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
Feel free to download, copy and edit any repo