summaryrefslogtreecommitdiff
path: root/neural-net/main.c
blob: 3001bde6460a831fb620592d0d7262f70ccad596 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "nn.h"

static size_t IN_DIMS, OUT_DIMS, HIDDEN_DIMS, N_LAYERS;

void append_bias(struct vec *vec) {
    vec->data[vec->n++] = 1.;
}

int read_vec(size_t n, struct vec *out) {
    out->n = n;
    for (size_t i = 0; i < n; i++)
        if (scanf(" %f ", &out->data[i]) != 1)
            return 0;
    return 1;
}

// neural net described by @layers. the input vector is @intermediates[0].
// intermediate layer values are placed in @intermediates.
void forward(struct mat layers[], struct vec intermediates[], size_t n_layers) {
    for (size_t i = 0; i < n_layers; i++) {
        intermediates[i + 1] = mv(layers[i], intermediates[i]);
        if (i + 1 != n_layers)
            intermediates[i + 1] = v_relu(intermediates[i + 1]);
    }
}

// computes the derivative for the output nodes. @desired is the true labels;
// @out is the current output of the model. the derivative is computed
// in-place, by overwriting @out.
// note: 'derivative' here is a bit of a rough description ...
void loss_bp(struct vec *out, struct vec desired, float learn_rate) {
    for (size_t i = 0; i < out->n; i++)
        out->data[i] = (desired.data[i] - out->data[i]) * learn_rate;
}

// backpropagates in-place. assumes @intermediates[n_layers] contains the
// derivative for the last layer. during @ith loop iteration, we compute the
// derivative for the @i-1 layer from the derivative of the @ith layer.
// derivative is computed in-place.
void backward(struct mat layers[], struct vec intermediates[], size_t n_layers) {
    for (size_t i = n_layers; i > 0; i--) {
        // need to recompute the forward pass because we've already overwritten
        // @intermediates[i].
        struct vec pre_relu = mv(layers[i - 1], intermediates[i - 1]);
        struct vec pre_relu_deltas = v_relu_bp(pre_relu, intermediates[i]);
        if (i == n_layers)
            pre_relu_deltas = intermediates[i];
        struct mat layer_delta = mv_bp_m(layers[i - 1], intermediates[i - 1], pre_relu_deltas);
        struct vec in_delta = mv_bp_v(layers[i - 1], intermediates[i - 1], pre_relu_deltas);
        intermediates[i - 1] = in_delta;
        add_mat(&layers[i - 1], layer_delta);
    }
}

int main() {
    assert(scanf(" in size %lu out size %lu hidden size %lu n layers %lu ",
                 &IN_DIMS, &OUT_DIMS, &HIDDEN_DIMS, &N_LAYERS) == 4);
    assert(IN_DIMS < MAX_DIMS && OUT_DIMS < MAX_DIMS && HIDDEN_DIMS < MAX_DIMS
            && N_LAYERS < MAX_LAYERS);
    struct mat layers[MAX_LAYERS];
    layers[0] = m_random(HIDDEN_DIMS, IN_DIMS + 1);
    for (size_t i = 1; i < N_LAYERS; i++)
        layers[i] = m_random(HIDDEN_DIMS, HIDDEN_DIMS);
    layers[N_LAYERS - 1] = m_random(OUT_DIMS, HIDDEN_DIMS);

    // Read in the training points.
    struct vec train_inputs[128];
    struct vec train_outputs[128];
    size_t n_train_points = 0;
    for (; read_vec(IN_DIMS, &train_inputs[n_train_points]); n_train_points++) {
        append_bias(&train_inputs[n_train_points]);
        read_vec(OUT_DIMS, &train_outputs[n_train_points]);
    }
    printf("Read %lu training points.\n", n_train_points);

    // Do the training.
    size_t n_iters;
    float learn_rate;
    assert(scanf(" train %lu %f ", &n_iters, &learn_rate) == 2);
    for (size_t _iter = 0; _iter < n_iters; _iter++) {
        for (size_t i = 0; i < n_train_points; i++) {
            struct vec intermediates[N_LAYERS + 1];
            intermediates[0] = train_inputs[i];
            forward(layers, intermediates, N_LAYERS);
            loss_bp(&intermediates[N_LAYERS], train_outputs[i], learn_rate);
            backward(layers, intermediates, N_LAYERS);
        }
    }

    // Do the testing.
    struct vec input;
    while (!feof(stdin) && read_vec(IN_DIMS, &input)) {
        append_bias(&input);

        struct vec intermediates[N_LAYERS + 1];
        intermediates[0] = input;
        forward(layers, intermediates, N_LAYERS);
        print_vec(intermediates[N_LAYERS]);
    }

    return 0;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback