代码之家  ›  专栏  ›  技术社区  ›  Sam Hammamy

无堆分配将结构返回为右值的可能解决方案:用例神经网络

  •  0
  • Sam Hammamy  · 技术社区  · 7 年前

    MNIST是机器学习的hello世界,我用TensorFlow和 pure python 还有努比。

    为了进行更多的练习,我尝试用C语言编写它,只使用标准库,因为我对C语言比较陌生,这是一种很好的学习方法。

    花了三个星期,而且 很多 SEGFAULTS

    最麻烦的当然是 malloc/free 对于矩阵结构中的数据,如下所示:

    typedef struct matrix{
        int rows, cols;
        float *data;
    } matrix;
    

    向前传球和向后传球有如下特点:

    1) matrix dot product
    2) matrix add
    3) matrix subtract
    4) activation function (sigmoid in this case)
    

    为了避免内存泄漏,我传入三个结构,如下所示:

    void matrix_add(matrix *a, matrix *b, matrix *res);
    

    res 需要更改上一层的尺寸,然后 free malloc 像这样:

    void zero_out_data(matrix *res, int rows, int cols)
    {
      if (res->rows != rows || res->cols != cols)
        {
          if ((res->rows*res->cols) != (rows*cols))
        {
          free(res->data);
          res->data = NULL;
          free(res);
          res = NULL;
          res = malloc(sizeof(matrix));
          // make_matrix will calloc the data based on rows*cols
          // any other init stuff that could be needed
          make_matrix(res, rows, cols);
        }
          res->rows = rows;
          res->cols = cols;
        }
      else {
        res->rows = rows;
        res->cols = cols;
        for (int i =0; i < (rows*cols); i++)
          {
        res->data[i] = 0.0;
          }
      }
    }
    

    然后我可以这样使用它:

    void sigmoid(matrix *z, matrix *res)
    {
      zero_out_data(res, z->rows, z->cols); 
      for (int i = 0; i < (z->rows*z->cols); i++)
        {
          res->data[i] = 1.0/(1.0+exp(-z->data[i]));
        }
    }
    

    这是 乱七八糟 因为单次向前传球具有以下特点:

    /* forward pass */
    for (int k=0; k < (network->num_layers-1); k++)
      {
        matrix_dot(network->weights[k], activation, dot);
        matrix_add(dot, network->biases[k], zs[k]);
        sigmoid(zs[k], activation);
        sigmoid(zs[k], activations[k+1]);
    }
    /* end forward pass */
    

    正如你可以想象的那样,背道具 很多 梅西耶。我必须预先创建8个不同的矩阵,加上更多指向矩阵指针的指针,如 activations zs 上面,用于梯度下降。

    matrix_dot 因此,我可以:

    sigmoid(matrix_add(matrix_dot(network->weights[k], activation), network->biases[k]));
    

    这有点像python/numpy的风格。

    当然,我不能从函数返回局部变量,因为一旦函数返回,它就会从堆栈中移除。

    如果我返回一个指针,那么上面的样式将导致严重的内存泄漏。

    1 回复  |  直到 7 年前
        1
  •  0
  •   randomeval    7 年前

    内存泄漏 void zero_out_data(matrix *res, int rows, int cols)

    matrix *res malloc退出函数并传递给 zero_out_data . 在里面 零输出数据 res 是自由的,马洛克又回来了。如果你想改变指针 物件 的值,则需要如下参数 matrix **res .

    如果您想要零输出数据,无需malloc新矩阵,只需malloc data make_matrix 函数可以将malloc内存用于 数据 .

    void zero_out_data(matrix *res, int rows, int col) {
          if (res->data == NULL) {
               make_matrix(res, rows, cols);
          } else if (res->rows != rows || res->cols != cols) {
              if ((res->rows*res->cols) != (rows*cols))
                 {
                   free(res->data);
                   res->data = NULL;
                   make_matrix(res, rows, cols);
                 }
          }
          res->rows = rows;
          res->cols = cols;
          for (int i =0; i < (rows*cols); i++)
          {
                res->data[i] = 0.0;
          } 
    }
    

    sigmoid(matrix_add(matrix_dot(network->weights[k], activation), network->biases[k])); ?

    你可以用 static 或全局变量来实现您想要的。这将不是线程安全和可重入的。示例如下:

    matrix *matrix_dot(matrix *in_a, matrix *in_b)
    {
            static matrix res = {0, 0, NULL};  // static variable
    
            // calculate the res's cols and rows number
            zero_out_data(&res, res_cols, res_rows);    // malloc new data
    
            // do some math.
    
            return &res;
    }
    
    // matrix_add will be just like matrix_dot
    
    // I was wrong about sigmod no need new matrix. sigmod can also do it like matrix_dot.
    

    可以使用全局变量替换静态变量。

    matrix *matrix_dot(matrix *in_a, matrix *in_b, matrix *res) 
    {
          zero_out_data(res, xxx, xxx);
          // do some math
          return res;
    }
    // matrix_add will be the same.
    
    // define local variables.
    matrix add_res, dot_res, sig_res;
    add_res->data = NULL;
    dot_res->data = NULL;
    sig_res->data = NULL;
    
    sigmod(matrix_add(matrix_dot(network->weights[k], activation, &dot_res), network->biases[k], &add_res),  &sig_res)
    
    // Now remember to free data in matrix
    free(add_res->data);
    free(dot_res->data);
    free(sig_res->data);