Skip to main content
added 1 character in body; edited title
Source Link
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Possible to speed up neural Neural net? in C++

I wrote a MatrixMatrix struct and a neural net that uses it. Why is this slow? Gprof blames Matrix::operator()(int, int) which I suppose is my constructor, and Matrix::operator*(Matrix)Matrix::operator*(Matrix) and vector::operator[]vector<float>::operator[].

#include <iostream>
//#include <C:/Users/David/Google Drive/Coding/neural_c/Launcher.h>
#include <fstream>
#include <assert.h>
#include <functional>
#include <vector>
#include <math.h>
#include <algorithm>
#include <random>
#include <time.h>

using namespace std;

#define debug(x) cout << #x << " = " << x << endl
#define debug_(x) cout << #x << " = " << x << " : "
#define linearize(r, c) r + rows*c

default_random_engine generator(time(NULL));
normal_distribution<float> _rng(0, 1);

auto rng = [](){return _rng(generator);};
auto logistic = [](float x){return 1/(1+exp(-x));};
auto square = [](float x){return x*x;};

struct Matrix{
    int rows, cols, size;
    int label=-1;
    vector<float> data;

    Matrix(int r=1, int c=1){ //column major
        rows = r;
        cols = c;
        size = r*c;
        data.resize(size);
    }

    Matrix one_to_many(){
        Matrix m(10);
        m.data[label] = 1;
        return m;
    }
    //
    // Matrix(const Matrix &m){
    //  rows = m.rows;
    //  cols = m.cols;
    //  size = m.size;
    //  data.resize(size);
    //  copy(m.data.begin(), m.data.end(), data.begin());
    // }

    void randomize(){
        for (int i=0; i<size; i++)
                data[i] = rng();
    }

    float &operator()(int r, int c) {
        //assert(0<=r && r<rows && 0<=c && c<cols);
        return data[linearize(r,c)];
        //return data[r+rows*c];
    }

    float &operator[](int i){//linear access
        assert(0<=i && i<size);
        return data[i];
    }

    Matrix operator*(Matrix m)  {
        //assert(cols==m.rows);
        Matrix product(rows, m.cols);

        for (int r=0; r<product.rows; r++)
            for (int c=0; c<product.cols; c++)
                for (int i=0; i<cols; i++)
                    product(r,c) += (*this)(r, i) * m(i, c);

        return product;
    }


    Matrix operator*(float f){
        Matrix product(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            product[i] = (*this)[i]*f;
        return product;
    }

    float sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += (*this)[i];
        return sum;
    }

    float abs_sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += abs((*this)[i]);
        return sum;
    }

    Matrix operator+(Matrix &m){
        Matrix sum(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            sum[i] = (*this)[i] + m[i];
        return sum;
    }

    Matrix operator-(Matrix &m){
        //return m;
        Matrix negated=m*-1;
        return (*this) + negated;
    }

    bool operator==(Matrix &m){
        for (int i=0; i<size; i++)
            if (data[i] != m.data[i])
                return false;
        return true;
    }

    template<typename Func>
    void apply(Func f){
        //#pragma omp parallel for
        for (int i=0; i<size; i++)
            (*this)[i] = f((*this)[i]);
    }

    void print(){
        for (int r=0; r<rows; r++){
            for (int c=0; c<cols; c++)
                cout << (*this)(r, c) << " ";
            cout << endl;
        }
        cout << endl;
    }

    void square_print(){
        Matrix square(sqrt(size), sqrt(size));
        square.data = data;
        square.print();
    }
};

struct Net{
    vector<Matrix> weights;

    Net(vector<int> sizes){
        for (int i=0; i<sizes.size()-1; i++){
            weights.push_back(Matrix(sizes[i+1], sizes[i]));
        }
        for (Matrix& weight: weights)
            weight.randomize();
    }

    Matrix operator()(const Matrix &x){
        //return x;
        //for (int i=0; i<10000; i++){x[0]+=.00001;}
        Matrix activation = x;
         for (int i=0; i<weights.size()-1; i++){
             activation = weights[0]*activation;
             activation.apply(logistic);
         }
        return weights[weights.size()-1]*activation;
    }

    float difference_squared(Matrix x, Matrix t){
        Matrix y=(*this)(x); // 5/13
        Matrix difference_squared = t-y; // 4/12
        difference_squared.apply(square);
        return difference_squared.sum();
    }


    float difference_squared(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += difference_squared(X[i], T[i]);
        return total;
    }


    float error_normalized(Matrix x, Matrix t){
        float dif_sq = difference_squared(x, t);
        float t_average_magnitude = t.abs_sum() / t.size;
        return pow(dif_sq, .5) / t_average_magnitude;
    }

    float error_normalized(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += error_normalized(X[i], T[i]);
        return total/X.size();
    }

    float learn(Matrix x, Matrix t, float learning_rate=.0001, float dw=.001){
        float error, error_shifted, partial_derivative;
        for (int layer=0; layer<weights.size(); layer++)
            for (int i=0; i<weights[layer].size; i++){
                error = difference_squared(x, t);
                weights[layer].data[i] += dw;
                error_shifted = difference_squared(x, t);
                weights[layer].data[i] -= dw;
                partial_derivative = (error_shifted - error) / dw;
                weights[layer].data[i] -= learning_rate * partial_derivative;
            }
        return error;
    }

    void print(){
        for (Matrix m: weights){
            m.print();
            cout << "\n";
        }
    }
};

vector<Matrix> read_data(int images=1000){
    ifstream in("mnist_train.csv");
    vector<Matrix> data;
    for (int image=0; image<images; image++){
        Matrix datum(28*28);
        int temp=-1;
        in >> datum.label;
        for(int i=0; i<28*28; i++)
                in >> datum.data[i];
        data.push_back(datum);
    }
    return data;
}


int main(){
    vector<Matrix> images = read_data(10);
    vector<Matrix> labels;
    for (Matrix image: images){
        labels.push_back(image.one_to_many());
        assert(labels[labels.size()-1].abs_sum()==1);
        //  image.square_print();
        //labels[labels.size()-1].print();
    }
    printf("data read\n");
    Net brain({28*28, 3, 10});
    printf("brain created!\n");
    //
    int epochs=3;
    debug(brain.difference_squared(images, labels));


    for (int epoch=0; epoch<epochs; epoch++){
        for (int i=0; i<images.size(); i++)
            brain.learn(images[i], labels[i], .01);
        debug_(epoch);
        debug(brain.difference_squared(images, labels));
        //debug(brain.error_normalized(images, labels));
    }

    printf("trained\n");



    printf("done\n");
}
//alias c="g++ neural.cpp -o neural.exe -O3 -fopenmp --std=c++11;time ./neural.exe"`exe"

Possible to speed up neural net? C++

I wrote a Matrix struct and a neural net that uses it. Why is this slow? Gprof blames Matrix::operator()(int, int) which I suppose is my constructor, and Matrix::operator*(Matrix) and vector::operator[].

#include <iostream>
//#include <C:/Users/David/Google Drive/Coding/neural_c/Launcher.h>
#include <fstream>
#include <assert.h>
#include <functional>
#include <vector>
#include <math.h>
#include <algorithm>
#include <random>
#include <time.h>

using namespace std;

#define debug(x) cout << #x << " = " << x << endl
#define debug_(x) cout << #x << " = " << x << " : "
#define linearize(r, c) r + rows*c

default_random_engine generator(time(NULL));
normal_distribution<float> _rng(0, 1);

auto rng = [](){return _rng(generator);};
auto logistic = [](float x){return 1/(1+exp(-x));};
auto square = [](float x){return x*x;};

struct Matrix{
    int rows, cols, size;
    int label=-1;
    vector<float> data;

    Matrix(int r=1, int c=1){ //column major
        rows = r;
        cols = c;
        size = r*c;
        data.resize(size);
    }

    Matrix one_to_many(){
        Matrix m(10);
        m.data[label] = 1;
        return m;
    }
    //
    // Matrix(const Matrix &m){
    //  rows = m.rows;
    //  cols = m.cols;
    //  size = m.size;
    //  data.resize(size);
    //  copy(m.data.begin(), m.data.end(), data.begin());
    // }

    void randomize(){
        for (int i=0; i<size; i++)
                data[i] = rng();
    }

    float &operator()(int r, int c) {
        //assert(0<=r && r<rows && 0<=c && c<cols);
        return data[linearize(r,c)];
        //return data[r+rows*c];
    }

    float &operator[](int i){//linear access
        assert(0<=i && i<size);
        return data[i];
    }

    Matrix operator*(Matrix m)  {
        //assert(cols==m.rows);
        Matrix product(rows, m.cols);

        for (int r=0; r<product.rows; r++)
            for (int c=0; c<product.cols; c++)
                for (int i=0; i<cols; i++)
                    product(r,c) += (*this)(r, i) * m(i, c);

        return product;
    }


    Matrix operator*(float f){
        Matrix product(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            product[i] = (*this)[i]*f;
        return product;
    }

    float sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += (*this)[i];
        return sum;
    }

    float abs_sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += abs((*this)[i]);
        return sum;
    }

    Matrix operator+(Matrix &m){
        Matrix sum(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            sum[i] = (*this)[i] + m[i];
        return sum;
    }

    Matrix operator-(Matrix &m){
        //return m;
        Matrix negated=m*-1;
        return (*this) + negated;
    }

    bool operator==(Matrix &m){
        for (int i=0; i<size; i++)
            if (data[i] != m.data[i])
                return false;
        return true;
    }

    template<typename Func>
    void apply(Func f){
        //#pragma omp parallel for
        for (int i=0; i<size; i++)
            (*this)[i] = f((*this)[i]);
    }

    void print(){
        for (int r=0; r<rows; r++){
            for (int c=0; c<cols; c++)
                cout << (*this)(r, c) << " ";
            cout << endl;
        }
        cout << endl;
    }

    void square_print(){
        Matrix square(sqrt(size), sqrt(size));
        square.data = data;
        square.print();
    }
};

struct Net{
    vector<Matrix> weights;

    Net(vector<int> sizes){
        for (int i=0; i<sizes.size()-1; i++){
            weights.push_back(Matrix(sizes[i+1], sizes[i]));
        }
        for (Matrix& weight: weights)
            weight.randomize();
    }

    Matrix operator()(const Matrix &x){
        //return x;
        //for (int i=0; i<10000; i++){x[0]+=.00001;}
        Matrix activation = x;
         for (int i=0; i<weights.size()-1; i++){
             activation = weights[0]*activation;
             activation.apply(logistic);
         }
        return weights[weights.size()-1]*activation;
    }

    float difference_squared(Matrix x, Matrix t){
        Matrix y=(*this)(x); // 5/13
        Matrix difference_squared = t-y; // 4/12
        difference_squared.apply(square);
        return difference_squared.sum();
    }


    float difference_squared(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += difference_squared(X[i], T[i]);
        return total;
    }


    float error_normalized(Matrix x, Matrix t){
        float dif_sq = difference_squared(x, t);
        float t_average_magnitude = t.abs_sum() / t.size;
        return pow(dif_sq, .5) / t_average_magnitude;
    }

    float error_normalized(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += error_normalized(X[i], T[i]);
        return total/X.size();
    }

    float learn(Matrix x, Matrix t, float learning_rate=.0001, float dw=.001){
        float error, error_shifted, partial_derivative;
        for (int layer=0; layer<weights.size(); layer++)
            for (int i=0; i<weights[layer].size; i++){
                error = difference_squared(x, t);
                weights[layer].data[i] += dw;
                error_shifted = difference_squared(x, t);
                weights[layer].data[i] -= dw;
                partial_derivative = (error_shifted - error) / dw;
                weights[layer].data[i] -= learning_rate * partial_derivative;
            }
        return error;
    }

    void print(){
        for (Matrix m: weights){
            m.print();
            cout << "\n";
        }
    }
};

vector<Matrix> read_data(int images=1000){
    ifstream in("mnist_train.csv");
    vector<Matrix> data;
    for (int image=0; image<images; image++){
        Matrix datum(28*28);
        int temp=-1;
        in >> datum.label;
        for(int i=0; i<28*28; i++)
                in >> datum.data[i];
        data.push_back(datum);
    }
    return data;
}


int main(){
    vector<Matrix> images = read_data(10);
    vector<Matrix> labels;
    for (Matrix image: images){
        labels.push_back(image.one_to_many());
        assert(labels[labels.size()-1].abs_sum()==1);
        //  image.square_print();
        //labels[labels.size()-1].print();
    }
    printf("data read\n");
    Net brain({28*28, 3, 10});
    printf("brain created!\n");
    //
    int epochs=3;
    debug(brain.difference_squared(images, labels));


    for (int epoch=0; epoch<epochs; epoch++){
        for (int i=0; i<images.size(); i++)
            brain.learn(images[i], labels[i], .01);
        debug_(epoch);
        debug(brain.difference_squared(images, labels));
        //debug(brain.error_normalized(images, labels));
    }

    printf("trained\n");



    printf("done\n");
}
//alias c="g++ neural.cpp -o neural.exe -O3 -fopenmp --std=c++11;time ./neural.exe"`

Neural net in C++

I wrote a Matrix struct and a neural net that uses it. Why is this slow? Gprof blames Matrix::operator()(int, int) which I suppose is my constructor, and Matrix::operator*(Matrix) and vector<float>::operator[].

#include <iostream>
//#include <C:/Users/David/Google Drive/Coding/neural_c/Launcher.h>
#include <fstream>
#include <assert.h>
#include <functional>
#include <vector>
#include <math.h>
#include <algorithm>
#include <random>
#include <time.h>

using namespace std;

#define debug(x) cout << #x << " = " << x << endl
#define debug_(x) cout << #x << " = " << x << " : "
#define linearize(r, c) r + rows*c

default_random_engine generator(time(NULL));
normal_distribution<float> _rng(0, 1);

auto rng = [](){return _rng(generator);};
auto logistic = [](float x){return 1/(1+exp(-x));};
auto square = [](float x){return x*x;};

struct Matrix{
    int rows, cols, size;
    int label=-1;
    vector<float> data;

    Matrix(int r=1, int c=1){ //column major
        rows = r;
        cols = c;
        size = r*c;
        data.resize(size);
    }

    Matrix one_to_many(){
        Matrix m(10);
        m.data[label] = 1;
        return m;
    }
    //
    // Matrix(const Matrix &m){
    //  rows = m.rows;
    //  cols = m.cols;
    //  size = m.size;
    //  data.resize(size);
    //  copy(m.data.begin(), m.data.end(), data.begin());
    // }

    void randomize(){
        for (int i=0; i<size; i++)
                data[i] = rng();
    }

    float &operator()(int r, int c) {
        //assert(0<=r && r<rows && 0<=c && c<cols);
        return data[linearize(r,c)];
        //return data[r+rows*c];
    }

    float &operator[](int i){//linear access
        assert(0<=i && i<size);
        return data[i];
    }

    Matrix operator*(Matrix m)  {
        //assert(cols==m.rows);
        Matrix product(rows, m.cols);

        for (int r=0; r<product.rows; r++)
            for (int c=0; c<product.cols; c++)
                for (int i=0; i<cols; i++)
                    product(r,c) += (*this)(r, i) * m(i, c);

        return product;
    }


    Matrix operator*(float f){
        Matrix product(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            product[i] = (*this)[i]*f;
        return product;
    }

    float sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += (*this)[i];
        return sum;
    }

    float abs_sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += abs((*this)[i]);
        return sum;
    }

    Matrix operator+(Matrix &m){
        Matrix sum(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            sum[i] = (*this)[i] + m[i];
        return sum;
    }

    Matrix operator-(Matrix &m){
        //return m;
        Matrix negated=m*-1;
        return (*this) + negated;
    }

    bool operator==(Matrix &m){
        for (int i=0; i<size; i++)
            if (data[i] != m.data[i])
                return false;
        return true;
    }

    template<typename Func>
    void apply(Func f){
        //#pragma omp parallel for
        for (int i=0; i<size; i++)
            (*this)[i] = f((*this)[i]);
    }

    void print(){
        for (int r=0; r<rows; r++){
            for (int c=0; c<cols; c++)
                cout << (*this)(r, c) << " ";
            cout << endl;
        }
        cout << endl;
    }

    void square_print(){
        Matrix square(sqrt(size), sqrt(size));
        square.data = data;
        square.print();
    }
};

struct Net{
    vector<Matrix> weights;

    Net(vector<int> sizes){
        for (int i=0; i<sizes.size()-1; i++){
            weights.push_back(Matrix(sizes[i+1], sizes[i]));
        }
        for (Matrix& weight: weights)
            weight.randomize();
    }

    Matrix operator()(const Matrix &x){
        //return x;
        //for (int i=0; i<10000; i++){x[0]+=.00001;}
        Matrix activation = x;
         for (int i=0; i<weights.size()-1; i++){
             activation = weights[0]*activation;
             activation.apply(logistic);
         }
        return weights[weights.size()-1]*activation;
    }

    float difference_squared(Matrix x, Matrix t){
        Matrix y=(*this)(x); // 5/13
        Matrix difference_squared = t-y; // 4/12
        difference_squared.apply(square);
        return difference_squared.sum();
    }


    float difference_squared(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += difference_squared(X[i], T[i]);
        return total;
    }


    float error_normalized(Matrix x, Matrix t){
        float dif_sq = difference_squared(x, t);
        float t_average_magnitude = t.abs_sum() / t.size;
        return pow(dif_sq, .5) / t_average_magnitude;
    }

    float error_normalized(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += error_normalized(X[i], T[i]);
        return total/X.size();
    }

    float learn(Matrix x, Matrix t, float learning_rate=.0001, float dw=.001){
        float error, error_shifted, partial_derivative;
        for (int layer=0; layer<weights.size(); layer++)
            for (int i=0; i<weights[layer].size; i++){
                error = difference_squared(x, t);
                weights[layer].data[i] += dw;
                error_shifted = difference_squared(x, t);
                weights[layer].data[i] -= dw;
                partial_derivative = (error_shifted - error) / dw;
                weights[layer].data[i] -= learning_rate * partial_derivative;
            }
        return error;
    }

    void print(){
        for (Matrix m: weights){
            m.print();
            cout << "\n";
        }
    }
};

vector<Matrix> read_data(int images=1000){
    ifstream in("mnist_train.csv");
    vector<Matrix> data;
    for (int image=0; image<images; image++){
        Matrix datum(28*28);
        int temp=-1;
        in >> datum.label;
        for(int i=0; i<28*28; i++)
                in >> datum.data[i];
        data.push_back(datum);
    }
    return data;
}


int main(){
    vector<Matrix> images = read_data(10);
    vector<Matrix> labels;
    for (Matrix image: images){
        labels.push_back(image.one_to_many());
        assert(labels[labels.size()-1].abs_sum()==1);
        //  image.square_print();
        //labels[labels.size()-1].print();
    }
    printf("data read\n");
    Net brain({28*28, 3, 10});
    printf("brain created!\n");
    //
    int epochs=3;
    debug(brain.difference_squared(images, labels));


    for (int epoch=0; epoch<epochs; epoch++){
        for (int i=0; i<images.size(); i++)
            brain.learn(images[i], labels[i], .01);
        debug_(epoch);
        debug(brain.difference_squared(images, labels));
        //debug(brain.error_normalized(images, labels));
    }

    printf("trained\n");



    printf("done\n");
}
//alias c="g++ neural.cpp -o neural.exe -O3 -fopenmp --std=c++11;time ./neural.exe"
Source Link

Possible to speed up neural net? C++

I wrote a Matrix struct and a neural net that uses it. Why is this slow? Gprof blames Matrix::operator()(int, int) which I suppose is my constructor, and Matrix::operator*(Matrix) and vector::operator[].

#include <iostream>
//#include <C:/Users/David/Google Drive/Coding/neural_c/Launcher.h>
#include <fstream>
#include <assert.h>
#include <functional>
#include <vector>
#include <math.h>
#include <algorithm>
#include <random>
#include <time.h>

using namespace std;

#define debug(x) cout << #x << " = " << x << endl
#define debug_(x) cout << #x << " = " << x << " : "
#define linearize(r, c) r + rows*c

default_random_engine generator(time(NULL));
normal_distribution<float> _rng(0, 1);

auto rng = [](){return _rng(generator);};
auto logistic = [](float x){return 1/(1+exp(-x));};
auto square = [](float x){return x*x;};

struct Matrix{
    int rows, cols, size;
    int label=-1;
    vector<float> data;

    Matrix(int r=1, int c=1){ //column major
        rows = r;
        cols = c;
        size = r*c;
        data.resize(size);
    }

    Matrix one_to_many(){
        Matrix m(10);
        m.data[label] = 1;
        return m;
    }
    //
    // Matrix(const Matrix &m){
    //  rows = m.rows;
    //  cols = m.cols;
    //  size = m.size;
    //  data.resize(size);
    //  copy(m.data.begin(), m.data.end(), data.begin());
    // }

    void randomize(){
        for (int i=0; i<size; i++)
                data[i] = rng();
    }

    float &operator()(int r, int c) {
        //assert(0<=r && r<rows && 0<=c && c<cols);
        return data[linearize(r,c)];
        //return data[r+rows*c];
    }

    float &operator[](int i){//linear access
        assert(0<=i && i<size);
        return data[i];
    }

    Matrix operator*(Matrix m)  {
        //assert(cols==m.rows);
        Matrix product(rows, m.cols);

        for (int r=0; r<product.rows; r++)
            for (int c=0; c<product.cols; c++)
                for (int i=0; i<cols; i++)
                    product(r,c) += (*this)(r, i) * m(i, c);

        return product;
    }


    Matrix operator*(float f){
        Matrix product(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            product[i] = (*this)[i]*f;
        return product;
    }

    float sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += (*this)[i];
        return sum;
    }

    float abs_sum(){
        float sum=0;
        //#pragma omp parallel for reduction(+:sum)
        for (int i=0; i<size; i++)
            sum += abs((*this)[i]);
        return sum;
    }

    Matrix operator+(Matrix &m){
        Matrix sum(rows, cols);
        #pragma omp parallel for
        for (int i=0; i<size; i++)
            sum[i] = (*this)[i] + m[i];
        return sum;
    }

    Matrix operator-(Matrix &m){
        //return m;
        Matrix negated=m*-1;
        return (*this) + negated;
    }

    bool operator==(Matrix &m){
        for (int i=0; i<size; i++)
            if (data[i] != m.data[i])
                return false;
        return true;
    }

    template<typename Func>
    void apply(Func f){
        //#pragma omp parallel for
        for (int i=0; i<size; i++)
            (*this)[i] = f((*this)[i]);
    }

    void print(){
        for (int r=0; r<rows; r++){
            for (int c=0; c<cols; c++)
                cout << (*this)(r, c) << " ";
            cout << endl;
        }
        cout << endl;
    }

    void square_print(){
        Matrix square(sqrt(size), sqrt(size));
        square.data = data;
        square.print();
    }
};

struct Net{
    vector<Matrix> weights;

    Net(vector<int> sizes){
        for (int i=0; i<sizes.size()-1; i++){
            weights.push_back(Matrix(sizes[i+1], sizes[i]));
        }
        for (Matrix& weight: weights)
            weight.randomize();
    }

    Matrix operator()(const Matrix &x){
        //return x;
        //for (int i=0; i<10000; i++){x[0]+=.00001;}
        Matrix activation = x;
         for (int i=0; i<weights.size()-1; i++){
             activation = weights[0]*activation;
             activation.apply(logistic);
         }
        return weights[weights.size()-1]*activation;
    }

    float difference_squared(Matrix x, Matrix t){
        Matrix y=(*this)(x); // 5/13
        Matrix difference_squared = t-y; // 4/12
        difference_squared.apply(square);
        return difference_squared.sum();
    }


    float difference_squared(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += difference_squared(X[i], T[i]);
        return total;
    }


    float error_normalized(Matrix x, Matrix t){
        float dif_sq = difference_squared(x, t);
        float t_average_magnitude = t.abs_sum() / t.size;
        return pow(dif_sq, .5) / t_average_magnitude;
    }

    float error_normalized(vector<Matrix> X, vector<Matrix> T){
        float total=0;
        for (int i=0; i<X.size(); i++)
            total += error_normalized(X[i], T[i]);
        return total/X.size();
    }

    float learn(Matrix x, Matrix t, float learning_rate=.0001, float dw=.001){
        float error, error_shifted, partial_derivative;
        for (int layer=0; layer<weights.size(); layer++)
            for (int i=0; i<weights[layer].size; i++){
                error = difference_squared(x, t);
                weights[layer].data[i] += dw;
                error_shifted = difference_squared(x, t);
                weights[layer].data[i] -= dw;
                partial_derivative = (error_shifted - error) / dw;
                weights[layer].data[i] -= learning_rate * partial_derivative;
            }
        return error;
    }

    void print(){
        for (Matrix m: weights){
            m.print();
            cout << "\n";
        }
    }
};

vector<Matrix> read_data(int images=1000){
    ifstream in("mnist_train.csv");
    vector<Matrix> data;
    for (int image=0; image<images; image++){
        Matrix datum(28*28);
        int temp=-1;
        in >> datum.label;
        for(int i=0; i<28*28; i++)
                in >> datum.data[i];
        data.push_back(datum);
    }
    return data;
}


int main(){
    vector<Matrix> images = read_data(10);
    vector<Matrix> labels;
    for (Matrix image: images){
        labels.push_back(image.one_to_many());
        assert(labels[labels.size()-1].abs_sum()==1);
        //  image.square_print();
        //labels[labels.size()-1].print();
    }
    printf("data read\n");
    Net brain({28*28, 3, 10});
    printf("brain created!\n");
    //
    int epochs=3;
    debug(brain.difference_squared(images, labels));


    for (int epoch=0; epoch<epochs; epoch++){
        for (int i=0; i<images.size(); i++)
            brain.learn(images[i], labels[i], .01);
        debug_(epoch);
        debug(brain.difference_squared(images, labels));
        //debug(brain.error_normalized(images, labels));
    }

    printf("trained\n");



    printf("done\n");
}
//alias c="g++ neural.cpp -o neural.exe -O3 -fopenmp --std=c++11;time ./neural.exe"`