Периодически прерванный сброс ядра. Может быть, ошибка static_cast?

Я пишу нейронную сеть обратного распространения на основе графа в качестве личного проекта. Все еще на передней опорной ступеньке. Он компилируется. Успешно работает в половине случаев, сбой на самом последнем шаге в половине случаев. Похоже, он умирает на каком-то этапе сборки мусора. Я новичок в виртуальных функциях и static_cast, поэтому мне интересно, виноваты ли эти части. GDB говорит: «Программа получила сигнал SIGABRT, прервана. 0x00000000100404740 в __gnu_cxx::new_allocator::deallocate(double*, unsigned long) ()»

Функции, составляющие первую половину кода или около того, вероятно, не виноваты, так как они работают в более простой старой версии моей нейронной сети (без графиков). Бьюсь об заклад, это где-то в структурах.

Обновление: если я задаю генерацию случайных чисел 123 вместо случайного начального числа на основе времени, оно запускается каждый раз. seed=124 каждый раз терпит неудачу. Удаление случайности в пользу постоянных весов также позволяет запускать его каждый раз. Я сбит с толку!

#include <bits/stdc++.h>
using namespace std;

#define p(x) cout << #x << " = "<< x<< endl
#define min(a,b) a<b ? a : b
typedef vector<double> d1;
typedef vector<d1> d2;
typedef vector<d2> d3;
typedef vector<int> i1;

int argmax(d1 x){
  p(x.size());
  int maxIndex=0;
  double maxValue=x[0];
  for (int i=1; i<x.size(); i++){
    if (x[i] > maxValue){
      maxValue = x[i];
      maxIndex = i;
    }
  }
  return maxIndex;
}

d1 zeros(int n){
  return d1(n);
}

d2 zeros(int rows, int cols){
  return d2(rows, d1(cols, 0));
}

d3 zeros(int x, int rows, int cols){
  return d3(x, d2(rows, d1(cols, 0)));
}

void print(d1 x){
  for (double d: x)
  cout << d << endl;
  cout << endl;
}

void print(d2 x){
  for (auto row: x){
    for (double d: row){
      cout << d << " ";
    }
    cout << endl;
  }
  cout << endl;
}

void print(d3 x){
  for (d2 X: x)
  print(X);
}



void toRank2(d1&x, int rows, d2& y){
  for (int i=0; i<x.size()/rows; i++){
    y.emplace_back();
    for (int row=0; row<rows; row++){
      y[i].push_back(x[i*rows+row]);
    }
  }
}

void toRank3(d1& x, int rows, int cols, d3& y){
  for (int i=0; i<x.size()/rows/cols; i++){
    y.emplace_back();
    for (int row=0; row<rows; row++){
      y[i].emplace_back();
      for (int col=0; col<cols; col++){
        y[i][row].push_back(x[i*rows*cols+row*cols+col]);
      }
    }
  }
}

d1 getRandomDoubles(int size, double mean=0, double standard_deviation=1){
  static normal_distribution<double> distribution(mean, standard_deviation);
  int seed=time(NULL);
  static default_random_engine generator(seed);
  d1 data(size);
  generate(data.begin(), data.end(), []() { return distribution(generator); });
  return data;
}

d2 getRandomDoubles(int rows, int cols, double mean=0, double standard_deviation=1){
  d1 d = getRandomDoubles(rows*cols, mean, standard_deviation);
  d2 e;
  toRank2(d, cols, e);
  return e;
}

d3 getRandomDoubles(int depth, int rows, int cols, double mean=0, double standard_deviation=1){
  d1 d = getRandomDoubles(depth*rows*cols, mean, standard_deviation);;
  d3 e;
  toRank3(d, rows, cols, e);
  return e;
}

struct Node{
  vector<Node*> parents, children;
  bool ready=false;
  //
  // bool check_ready(){
  //   for (Node* n: parents)
  //   if (!n->check_ready())
  //   return false;
  //   return true;
  // }
  //
  void add_child(Node& n){
    children.push_back(&n);
    n.parents.push_back(this);
  }

  void forward_propagate(){
    cout << "starting r2 forward" <<endl;
  //  if (parents.size()==0 || updated_parents == parents.size()-1)
    for (Node* n: children){
      cout << "loop" << endl;
      n->update_state();
      //  cout << "root child forward" << endl;
    }
    cout << "exiting r2 forward" << endl;
    //updated_parents++;
  }

  virtual void update_state(){
    //if (parents.size()==0 || updated_parents == parents.size() - 1)
    forward_propagate();
  }
};

struct r1:Node{
  vector<double> state;
  int r;

  r1(){}

  r1(int R){
    r=R;
    state = vector<double>(r);
  }
};

struct r2:Node{
  vector<vector<double>> state;
  int r,c;

  r2(){}
  r2(int R, int C){
    r=R;
    c=C;
    state = zeros(r, c);
  }
};

struct r3:Node{
  d3 state;
  int r, c, d;
  r3(){}
  r3(int R, int C, int D){
    r=R;
    c=C;
    d=D;
    state = zeros(R,C,D);
  }
};

struct MatrixProduct1_1: r1{
  MatrixProduct1_1(int n):r1(n){}

  void update_state() override{
    cout << "mat11" << endl;
    d2& W = static_cast<r2*>(parents[0])->state;
    d1& x = static_cast<r1*>(parents[1])->state;
    state = zeros(r);
    for (int i=0; i<W.size(); i++)
    for (int j=0; j<W[0].size(); j++)
    state[i] += W[i][j]*x[j];
    forward_propagate();
  }
};

struct MatrixProduct2_1: r1{
  MatrixProduct2_1(int n):r1(n){}

  void update_state() override{
    cout << "matt21" << endl;
    d3& W = static_cast<r3*>(parents[0])->state;
    d2& x = static_cast<r2*>(parents[1])->state;
    state = zeros(r);
    for (int i=0; i<W.size(); i++)
    for (int j=0; j<W[0].size(); j++)
    for (int k=0; k<W[0][0].size(); k++)
    state[k] += W[i][j][k]*x[i][j];
    forward_propagate();
  }
};

struct Convolution: r2{
  Convolution(int r, int c): r2(r, c){}
  void update_state() override{
    cout << "convolving" << endl;
    state = zeros(r, c);
    d2& W = static_cast<r2*>(parents[0])->state;
    d2& x = static_cast<r2*>(parents[1])->state;

    int wCenterX = W[0].size() / 2;
    int wCenterY = W.size() / 2;
    int rows = x.size(), cols = x[0].size();
    int wRows = W.size(), wCols = W[0].size();

    //#pragma omp parallel for
    for(int i=0; i < rows; i++)
    for(int j=0; j < cols; j++)
    for(int m=0; m < W.size(); m++){
      int mm = W.size() - 1 - m;
      for(int n=0; n < wCols; n++){
        int nn = wCols - 1 - n;
        int ii = i + (m - wCenterY);
        int jj = j + (n - wCenterX);
        if (ii >= 0 && ii < rows && jj >= 0 && jj < cols)
        state[i][j] += x[ii][jj] * W[mm][nn];
      }
    }
    forward_propagate();
  }
};


struct RELU: r2{
  RELU(int r, int c):r2(r, c){}
  void update_state() override{
    cout << "relu2" << endl;
    state = zeros(r,c);
    d2& x = static_cast<r2*>(parents[0])->state;
    for (int i=0; i<state.size(); i++)
    for (int j=0; j<state[0].size(); j++)
    if (x[i][j] > 0)
    state[i][j] = x[i][j];
    forward_propagate();
  }
};

struct Softmax: r1{
  Softmax(int r):r1(r){}
  void update_state() override{
    cout << "softmax" << endl;
    state = zeros(r);
    p(parents.size());
    d1& x = static_cast<r1*>(parents[0])->state;
    cout << "got state" << endl;
    //p(x.size());
    //print(x);

    p(x.size());
    cout << "argmax " << argmax(x) << endl;
    double largest = x[argmax(x)];
    double lndenom = largest;
    double expsum = 0;
    cout << "starting expsum" << endl;
    for (int i=0; i<x.size(); i++)
    //expsum += exp(x[i]-largest);
    expsum += x[i] - largest;
    cout << "next loop " << endl;
    for (int i=0; i<x.size(); i++)
  //  state[i] = exp(x[i]-largest) / expsum;
    state[i] = x[i]-largest;
    cout << "forward proping" << endl;
    cout << "weird" << endl;
  //  forward_propagate();
    cout << "done with softmax" <<endl;
  }
};

struct Add1: r1{
  Add1(int r):r1(r){}
  void update_state() override{
    cout << "add1ing" << endl;
    d1& x = static_cast<r1*>(parents[0])->state;
    d1& y = static_cast<r1*>(parents[1])->state;
    for (int i=0; i<r; i++)
    state[i] = x[i]+y[i];
    forward_propagate();
  }
};

struct Add2: r2{
  Add2(int r, int c): r2(r, c){}
  void update_state() override{
    d2& x = static_cast<r2*>(parents[0])->state;
    d2& y = static_cast<r2*>(parents[1])->state;
    for (int i=0; i<x.size(); i++)
    for (int j=0; j<x[0].size(); j++)
    state[i][j] = x[i][j] + y[i][j];
    forward_propagate();
  }
};

struct MaxPool: r2{
  MaxPool(int r, int c): r2(r, c){}
  void update_state() override{
    d2& x = static_cast<r2*>(parents[0])->state;
    for (int i=0; i<x.size(); i+=2)
    for (int j=0; j<x[0].size(); j+=2)
    state[i/2][j/2] = max(max(x[i][j], x[i+1][j]), max(x[i+1][j], x[i+1][j+1]));
    forward_propagate();
  }
};

int main(){
  Node root;
  r2 x;
  x.state = getRandomDoubles(28,28);
  r2 wConv;
  wConv.state = getRandomDoubles(10, 10);
  root.add_child(x);
  root.add_child(wConv);
  Convolution c(28,28);
  wConv.add_child(c);
  x.add_child(c);
  Add2 a(28,28);
  r2 bConv(28,28);
  bConv.state = getRandomDoubles(28,28);
  c.add_child(a);
  bConv.add_child(a);
  RELU r(28,28);
  a.add_child(r);
  MaxPool max(14, 14);
  r.add_child(max);
  r3 wFull(10,28,28);
  wFull.state = getRandomDoubles(10,28,28);
  // print(wFull.state);
  // return 0;
  MatrixProduct2_1 full(10);
  wFull.add_child(full);
  max.add_child(full);
  r1 bFull(10);
  bFull.state = getRandomDoubles(10);
  Add1 aFull(10);
  aFull.state[0] = 123;
  full.add_child(aFull);
  bFull.add_child(aFull);
  Softmax s(10);
  aFull.add_child(s);
  // d1& x =         static_cast<r1*>(parents[0])->state;
  // d1& asdf = static_cast<r1*>(s.parents[0])->state;
  // print(asdf);
  //root.forward_propagate();
  x.forward_propagate();
  //print(s.state);
  cout << "returning main";
}

person David Lerner    schedule 16.11.2016    source источник
comment
Макросы min известны своей неудачей, и это плохая версия. Это также бессмысленно, так как у нас есть прекрасное std::min. Вы изобретаете не только колесо — argmax — это всего лишь std::max_element.   -  person MSalters    schedule 16.11.2016


Ответы (2)


static_cast нужно довольно редко. Это не исключение. Ваши узлы действительно должны знать, какой тип имеют их соседи.

Я не могу сразу определить конкретную проблему, но я знаком с нейронными сетями. И такой код, как struct MatrixProduct1_1: r1, в значительной степени является красным предупреждением. Почему это структура и почему она наследуется от r1? В теории нейронных сетей матричные произведения — это то, как вы выражаете полную связь между двумя слоями узлов. Опять же, узел обычно имеет скалярную активацию.

Функции активации могут быть реализованы с использованием наследования, но вы унаследуете это от Node. Это означает, что у вас также не может быть этих типов r1..r3, но я все равно их не понял.

TLDR: типы перепутаны, вы скрываете это с помощью static_cast, но это только заставляет его компилироваться, но не делает его правильным.

person MSalters    schedule 16.11.2016

Решено! Ошибка была вызвана использованием неправильных индексов в MatrixProd2_1. Я поймал это, удалив узлы, начиная с конца нейронной сети, определив, что один из них был источником ошибки, и вставив утверждения об аргументах vector::operator[]. Я обращался за пределы границ, вызывая неопределенное поведение. Мне непонятно, почему некоторые семена вообще запускались (предположительно с несколько неправильными результатами).

Я также внес изменения в формы при создании wFull в main и в аргументы getRandomDoubles, используемые в MatrixProd2_1. Полная новая версия:

#include <bits/stdc++.h>
using namespace std;

#define p(x) cout << #x << " = "<< x<< endl
//#define min(a,b) a<b ? a : b
typedef vector<double> d1;
typedef vector<d1> d2;
typedef vector<d2> d3;
typedef vector<int> i1;
int seed;
bool time_seed = true;

int argmax(d1 x){
  p(x.size());
  int maxIndex=0;
  double maxValue=x[0];
  for (int i=1; i<x.size(); i++){
    if (x[i] > maxValue){
      maxValue = x[i];
      maxIndex = i;
    }
  }
  return maxIndex;
}

d1 zeros(int n){
  return d1(n);
}

d2 zeros(int rows, int cols){
  return d2(rows, d1(cols, 0));
}

d3 zeros(int x, int rows, int cols){
  return d3(x, d2(rows, d1(cols, 0)));
}

void print(d1 x){
  for (double d: x)
  cout << d << endl;
  cout << endl;
}

void print(d2 x){
  for (auto row: x){
    for (double d: row){
      cout << d << " ";
    }
    cout << endl;
  }
  cout << endl;
}

void print(d3 x){
  for (d2 X: x)
  print(X);
}



void toRank2(d1&x, int rows, d2& y){
  for (int i=0; i<x.size()/rows; i++){
    y.emplace_back();
    for (int row=0; row<rows; row++){
      y[i].push_back(x[i*rows+row]);
    }
  }
}

void toRank3(d1& x, int rows, int cols, d3& y){
  for (int i=0; i<x.size()/rows/cols; i++){
    y.emplace_back();
    for (int row=0; row<rows; row++){
      y[i].emplace_back();
      for (int col=0; col<cols; col++){
        y[i][row].push_back(x[i*rows*cols+row*cols+col]);
      }
    }
  }
}

d1 getRandomDoubles(int size, double mean=1, double standard_deviation=1){
  static normal_distribution<double> distribution(mean, standard_deviation);
  if (time_seed)
  seed=time(NULL);
  //int seed=123; //123 works, 124 fails
  static default_random_engine generator(seed);
  d1 data(size);
  generate(data.begin(), data.end(), []() { return distribution(generator); });
  //  generate(data.begin(), data.end(), [](){return -.1;});
  return data;
}

d2 getRandomDoubles(int rows, int cols, double mean=0, double standard_deviation=1){
  d1 d = getRandomDoubles(rows*cols, mean, standard_deviation);
  d2 e;
  toRank2(d, cols, e);
  return e;
}

d3 getRandomDoubles(int depth, int rows, int cols, double mean=0, double standard_deviation=1){
  d1 d = getRandomDoubles(depth*rows*cols, mean, standard_deviation);;
  d3 e;
  toRank3(d, rows, cols, e);
  return e;
}

struct Node{
  vector<Node*> parents, children;
  bool ready=false;
  //
  // bool check_ready(){
  //   for (Node* n: parents)
  //   if (!n->check_ready())
  //   return false;
  //   return true;
  // }
  //
  void add_child(Node& n){
    children.push_back(&n);
    n.parents.push_back(this);
  }

  void forward_propagate(){
    cout << "starting r2 forward" <<endl;
    //  if (parents.size()==0 || updated_parents == parents.size()-1)
    for (Node* n: children){
      cout << "loop" << endl;
      n->update_state();
      //  cout << "root child forward" << endl;
    }
    cout << "exiting r2 forward" << endl;
    //updated_parents++;
  }

  virtual void update_state(){
    //if (parents.size()==0 || updated_parents == parents.size() - 1)
    forward_propagate();
  }
};

struct r1:Node{
  vector<double> state;
  int r;

  r1(){}

  r1(int R){
    r=R;
    state = vector<double>(r);
  }
};

struct r2:Node{
  vector<vector<double>> state;
  int r,c;

  r2(){}
  r2(int R, int C){
    r=R;
    c=C;
    state = zeros(r, c);
  }
};

struct r3:Node{
  d3 state;
  int r, c, d;
  r3(){}
  r3(int R, int C, int D){
    r=R;
    c=C;
    d=D;
    state = zeros(R,C,D);
  }
};

struct MatrixProduct1_1: r1{
  MatrixProduct1_1(int n):r1(n){}

  void update_state() override{
    cout << "mat11" << endl;
    d2& W = static_cast<r2*>(parents[0])->state;
    d1& x = static_cast<r1*>(parents[1])->state;
    state = zeros(r);
    for (int i=0; i<W.size(); i++)
    for (int j=0; j<W[0].size(); j++)
    state[i] += W[i][j]*x[j];
    forward_propagate();
  }
};

struct MatrixProduct2_1: r1{
  MatrixProduct2_1(int n):r1(n){}

  void update_state() override{
    cout << "matt21" << endl;
    d3& W = static_cast<r3*>(parents[0])->state;
    d2& x = static_cast<r2*>(parents[1])->state;
    p(x.size());
    p(W.size());
    p(x[0].size());
    p(W[0].size());
    p(W[0][0].size());
    p(state.size());
    // assert (x.size()==W.size());
    // assert (x[0].size()==W[0].size());
    // assert (state.size()==W[0][0].size());
    assert (state.size() == W.size());

    state = zeros(r);
    for (int i=0; i<W.size(); i++)
    for (int j=0; j<W[0].size(); j++)
    for (int k=0; k<W[0][0].size(); k++)
    state[i] += W[i][j][k]*x[j][k];
    forward_propagate();
  }
};

struct Convolution: r2{
  Convolution(int r, int c): r2(r, c){}
  void update_state() override{
    cout << "convolving" << endl;
    state = zeros(r, c);
    d2& W = static_cast<r2*>(parents[0])->state;
    d2& x = static_cast<r2*>(parents[1])->state;

    int wCenterX = W[0].size() / 2;
    int wCenterY = W.size() / 2;
    int rows = x.size(), cols = x[0].size();
    int wRows = W.size(), wCols = W[0].size();

    //#pragma omp parallel for
    for(int i=0; i < rows; i++)
    for(int j=0; j < cols; j++)
    for(int m=0; m < W.size(); m++){
      int mm = W.size() - 1 - m;
      for(int n=0; n < wCols; n++){
        int nn = wCols - 1 - n;
        int ii = i + (m - wCenterY);
        int jj = j + (n - wCenterX);
        if (ii >= 0 && ii < rows && jj >= 0 && jj < cols)
        state[i][j] += x[ii][jj] * W[mm][nn];
      }
    }
    forward_propagate();
  }
};


struct RELU: r2{
  RELU(int r, int c):r2(r, c){}
  void update_state() override{
    cout << "relu2" << endl;
    state = zeros(r,c);
    d2& x = static_cast<r2*>(parents[0])->state;
    for (int i=0; i<state.size(); i++)
    for (int j=0; j<state[0].size(); j++)
    if (x[i][j] > 0)
    state[i][j] = x[i][j];
    forward_propagate();
  }
};

struct Softmax: r1{
  Softmax(int r):r1(r){}
  void update_state() override{
    cout << "softmax" << endl;
    state = zeros(r);
    p(parents.size());
    d1& x = static_cast<r1*>(parents[0])->state;
    cout << "got state" << endl;
    //p(x.size());
    //print(x);

    p(x.size());
    cout << "argmax " << argmax(x) << endl;
    double largest = x[argmax(x)];
    double lndenom = largest;
    double expsum = 0;
    cout << "starting expsum" << endl;
    for (int i=0; i<x.size(); i++)
    expsum += exp(x[i]-largest);
    //expsum += x[i] - largest;
    cout << "next loop " << endl;
    for (int i=0; i<x.size(); i++)
    state[i] = exp(x[i]-largest) / expsum;
    //state[i] = x[i]-largest;
    //  state[i] = 3;
    cout << "forward proping" << endl;
    cout << "weird" << endl;
    forward_propagate();
    cout << "done with softmax" <<endl;
  }
};

struct Add1: r1{
  Add1(int r):r1(r){}
  void update_state() override{
    cout << "add1ing" << endl;
    d1& x = static_cast<r1*>(parents[0])->state;
    d1& y = static_cast<r1*>(parents[1])->state;
    for (int i=0; i<r; i++)
    state[i] = x[i]+y[i];
    forward_propagate();
  }
};

struct Add2: r2{
  Add2(int r, int c): r2(r, c){}
  void update_state() override{
    d2& x = static_cast<r2*>(parents[0])->state;
    d2& y = static_cast<r2*>(parents[1])->state;
    for (int i=0; i<x.size(); i++)
    for (int j=0; j<x[0].size(); j++)
    state[i][j] = x[i][j] + y[i][j];
    forward_propagate();
  }
};

struct MaxPool: r2{
  MaxPool(int r, int c): r2(r, c){}
  void update_state() override{
    d2& x = static_cast<r2*>(parents[0])->state;
    for (int i=0; i<x.size(); i+=2)
    for (int j=0; j<x[0].size(); j+=2)
    state[i/2][j/2] = max(max(x[i][j], x[i+1][j]), max(x[i+1][j], x[i+1][j+1]));
    forward_propagate();
  }
};

int main( int argc, char *argv[] ){
  if (argc>1){
    seed = atoi(argv[1]);
    time_seed = false;
  }
  Node root;
  r2 x;
  x.state = getRandomDoubles(28,28);
  //x.state[0][0]-=1000;
  r2 wConv;
  wConv.state = getRandomDoubles(10, 10);
  root.add_child(x);
  root.add_child(wConv);
  Convolution c(28,28);
  wConv.add_child(c);
  x.add_child(c);
  Add2 a(28,28);
  r2 bConv(28,28);
  bConv.state = getRandomDoubles(28,28);
  c.add_child(a);
  bConv.add_child(a);
  RELU r(28,28);
  a.add_child(r);
  MaxPool max(14, 14);
  r.add_child(max);
//  print(max.state);
  r3 wFull(10,14,14);
  wFull.state = getRandomDoubles(10,14,14);
  //print(wFull.state);
  // return 0;
  MatrixProduct2_1 full(10);
  wFull.add_child(full);
  max.add_child(full);
  //print(full.state); //suspiciously zero
  r1 bFull(10);
  bFull.state = getRandomDoubles(10);
  Add1 aFull(10);
  aFull.state[0] = 123;
  full.add_child(aFull);
  bFull.add_child(aFull);
  Softmax s(10);
  aFull.add_child(s);
  // d1& x =         static_cast<r1*>(parents[0])->state;
  // d1& asdf = static_cast<r1*>(s.parents[0])->state;
  // print(asdf);
  //root.forward_propagate();
  x.forward_propagate();
  //print(aFull.state);
  print(s.state);
  cout << "returning main";
}
person David Lerner    schedule 16.11.2016