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"