美文网首页
Caffe源码分析-math-function分析

Caffe源码分析-math-function分析

作者: 陈泽_06aa | 来源:发表于2020-04-20 19:06 被阅读0次

    math-function定义了一些数学函数,这些函数都是基于BLAS进行实现。在编译时,可以指定Blas实现库,包括Atlas,mkl,openBlas三种,设置如下:

    # atlas for ATLAS (default)
    # mkl for MKL
    # open for OpenBlas
    BLAS := atlas
    

    关于BLAS选型,可以参考:

    大概率结论如下:


    image.png

    math-function中定义的函数也基本按照BLAS分类,分为3种level:

    • Level1:向量-向量计算,比如:点积【dot】,向量加法和乘法【axpy】,绝对值的和【asum】等。
    • Level2:向量-矩阵计算,比如:矩阵向量乘法【gemv】。
    • Level3:矩阵-矩阵计算,比如:矩阵乘法【gemm】。

    关于BLAS的函数定义请参考Intel MKL reference manual or netlib.org

    完整函数归类如下:

    /*
    *类别:Level3
    *功能:计算矩阵乘法的函数之一是 cblas_sgemm,使用单精度实数,另外还有对应双精度实数,单精度复数和双精度复数的函数。在此以 cblas_sgemm为例。
    * 函数定义为:
    void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
    const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
    const int K, const float alpha, const float  *A,
    const int lda, const float  *B, const int ldb,
    const float beta, float  *C, const int ldc)
    得到的结果是:
     C = alpha*op( A )*op( B ) + beta*C
    const enum CBLAS_ORDER Order,这是指的数据的存储形式,在CBLAS的函数中无论一维还是二维数据
    都是用一维数组存储,这就要涉及是行主序还是列主序,在C语言中数组是用 行主序,fortran中是列
    主序。我还是习惯于是用行主序,所以这个参数是用CblasRowMajor,如果是列主序的话就是 CblasColMajor。
    const int M,矩阵A的行,矩阵C的行
    const int N,矩阵B的列,矩阵C的列
    const int K,矩阵A的列,矩阵B的行
    const float alpha, const float beta,计算公式中的两个参数值,如果只是计算C=A*B,则alpha=1,beta=0
    const float  *A, const float  *B, const float  *C,矩阵ABC的数据
    const int lda, const int ldb, const int ldc,在BLAS的文档里,这三个参数分别为ABC的行数,
    但是实际使用发现,在CBLAS里应该是列数。
    */
    template<> void caffe_cpu_gemm<float>(const CBLAS_TRANSPOSE TransA,
        const CBLAS_TRANSPOSE TransB, const int M, const int N, const int K,
        const float alpha, const float* A, const float* B, const float beta,
        float* C) {
      int lda = (TransA == CblasNoTrans) ? K : M;
      int ldb = (TransB == CblasNoTrans) ? N : K;
      cblas_sgemm(CblasRowMajor, TransA, TransB, M, N, K, alpha, A, lda, B,
          ldb, beta, C, N);
    }
    /*
    *类别:Level2
    功能: y=alpha*A*x+beta*y
    其中X和Y是向量,A 是矩阵
    M:A 的行数
    N:A 的列数
    cblas_sgemv 中的 参数1 表示对X和Y的每个元素都进行操作
    */
    template <> void caffe_cpu_gemv<float>(const CBLAS_TRANSPOSE TransA, const int M,
        const int N, const float alpha, const float* A, const float* x,
        const float beta, float* y) {
      cblas_sgemv(CblasRowMajor, TransA, M, N, alpha, A, N, x, 1, beta, y, 1);
    }
    /*
    *类别:Level1
    功能: Y=alpha*X+Y
    N:为X和Y中element的个数
    */
    template <> void caffe_axpy<float>(const int N, const float alpha, const float* X,
        float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }
    /*
    *类别:Level1
    功能:Y= alpha*X+beta*Y
    */
    template <> void caffe_cpu_axpby<float>(const int N, const float alpha, const float* X,
                                const float beta, float* Y) {
      cblas_saxpby(N, alpha, X, 1, beta, Y, 1);
    }
    //功能: 给 Y 的每个 element 加上常数 alpha
    template <> void caffe_add_scalar(const int N, const float alpha, float* Y) {
      for (int i = 0; i < N; ++i) {
        Y[i] += alpha;
      }
    }
    /*
    *类别:Level1
    功能:X = alpha*X
    N: X中element的个数
    */
    template <> void caffe_scal<float>(const int N, const float alpha, float *X) {
      cblas_sscal(N, alpha, X, 1);
    }
     /*
    *类别:Level1
    */
    template <> void caffe_cpu_scale<float>(const int n, const float alpha, const float *x, float* y) {
      cblas_scopy(n, x, 1, y, 1);
      cblas_sscal(n, alpha, y, 1);
    }
    /*
    *类别:Level1
    功能: 返回 vector X 和 vector Y 的内积。
    incx, incy : 步长,即每隔incx 或 incy 个element 进行操作。
    */
    template <> float caffe_cpu_strided_dot<float>(const int n, const float* x, const int incx,
        const float* y, const int incy) {
      return cblas_sdot(n, x, incx, y, incy);
    }
    /*
    *类别:Level1
    功能:返回 x 和 y 之间的海明距离。(两个等长字符串之间的海明距离
    是两个字符串对应位置的不同字符的个数。)
    */
    template <>
    int caffe_cpu_hamming_distance<float>(const int n, const float* x,
                                      const float* y) {
      int dist = 0;
      for (int i = 0; i < n; ++i) {
        dist += __builtin_popcount(static_cast<uint32_t>(x[i]) ^
                                   static_cast<uint32_t>(y[i]));
      }
      return dist;
    }
    /*
    *类别:Level1
    功能:计算 vector x 的所有element的绝对值之和。
    */
    template <> float caffe_cpu_asum<float>(const int n, const float* x) {
      return cblas_sasum(n, x, 1);
    }
    /*
    功能:用常数 alpha 对 Y 进行初始化
    函数 void *memset(void *buffer, char c, unsigned count) 一般为新申请的内存做初始化,
    功能是将buffer所指向内存中的每个字节的内容全部设置为c指定的ASCII值, count为块的大小,
    使用memset函数来初始化数组或者结构体比其他初始化方法更快一点
    */
    template <typename Dtype> void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
      if (alpha == 0) {
        memset(Y, 0, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
        return;
      }
      for (int i = 0; i < N; ++i) {
        Y[i] = alpha;
      }
    }
    /*
    函数 void *memcpy(void *dest, void *src, unsigned int count) 把src所指向
    的内存区域 copy到dest所指向的内存区域, count为块的大小
    表头文件: #include <string.h>
    定义函数: void *memcpy(void *dest, const void *src, size_t n)
    函数说明: memcpy()用来拷贝src所指的内存内容前n个字节到dest所指的内存地址上。与strcpy()不同的是,memcpy()会完整的复制n个字节,不会因为遇到字符串结束'\0'而结束
    返回值:   返回指向dest的指针
    */
    template <typename Dtype>
    void caffe_copy(const int N, const Dtype* X, Dtype* Y) {
      if (X != Y) {
        if (Caffe::mode() == Caffe::GPU) {
    #ifndef CPU_ONLY
          // NOLINT_NEXT_LINE(caffe/alt_fn)
          CUDA_CHECK(cudaMemcpy(Y, X, sizeof(Dtype) * N, cudaMemcpyDefault));
    #else
          NO_GPU;
    #endif
        } else {
          memcpy(Y, X, sizeof(Dtype) * N);  // NOLINT(caffe/alt_fn)
        }
      }
    }
    /*
    功能:这四个函数分别实现element-wise的加减乘除(y[i] = a[i] + - * \ b[i])
    */
    template <> void caffe_add<float>(const int n, const float* a, const float* b,
        float* y) {
      vsAdd(n, a, b, y);
    }
    template <> void caffe_sub<float>(const int n, const float* a, const float* b,
        float* y) {
      vsSub(n, a, b, y);
    }
    template <> void caffe_mul<float>(const int n, const float* a, const float* b,
        float* y) {
      vsMul(n, a, b, y);
    }
    template <> void caffe_div<float>(const int n, const float* a, const float* b,
        float* y) {
      vsDiv(n, a, b, y);
    }
    /*
    功能 : 同样是element-wise操作,分别是y[i] = a[i] ^ b, y[i] = a[i]^2,y[i] = exp(a[i] ),y[i] = |a[i] |
    */
    template <> void caffe_powx<float>(const int n, const float* a, const float b,
        float* y) {
      vsPowx(n, a, b, y);
    }
    template <> void caffe_sqr<float>(const int n, const float* a, float* y) {
      vsSqr(n, a, y);
    }
    template <> void caffe_exp<float>(const int n, const float* a, float* y) {
      vsExp(n, a, y);
    }
    template <> void caffe_log<float>(const int n, const float* a, float* y) {
      vsLn(n, a, y);
    }
    template <> void caffe_abs<float>(const int n, const float* a, float* y) {
        vsAbs(n, a, y);
    }
    /*
    功能:返回一个随机数
    */
    unsigned int caffe_rng_rand() {
      return (*caffe_rng())();
    }
    /*
    功能 : 返回 b 最大方向上可以表示的最接近的数值。
    */
    template <typename Dtype>
    Dtype caffe_nextafter(const Dtype b) {
      return boost::math::nextafter<Dtype>(
          b, std::numeric_limits<Dtype>::max());
    }
    template <typename Dtype>
    void caffe_rng_uniform(const int n, const Dtype a, const Dtype b, Dtype* r) {
      CHECK_GE(n, 0);
      CHECK(r);
      CHECK_LE(a, b);
      boost::uniform_real<Dtype> random_distribution(a, caffe_nextafter<Dtype>(b));
      boost::variate_generator<caffe::rng_t*, boost::uniform_real<Dtype> >
          variate_generator(caffe_rng(), random_distribution);
      for (int i = 0; i < n; ++i) {
        r[i] = variate_generator();
      }
    }
    //
    template <typename Dtype> void caffe_rng_gaussian(const int n, const Dtype a,  const Dtype sigma, Dtype* r) {
      CHECK_GE(n, 0);
      CHECK(r);
      CHECK_GT(sigma, 0);
      boost::normal_distribution<Dtype> random_distribution(a, sigma);
      boost::variate_generator<caffe::rng_t*, boost::normal_distribution<Dtype> >
          variate_generator(caffe_rng(), random_distribution);
      for (int i = 0; i < n; ++i) {
        r[i] = variate_generator();
      }
    }
    //
    template <typename Dtype> void caffe_rng_bernoulli(const int n, const Dtype p, int* r) {
      CHECK_GE(n, 0);
      CHECK(r);
      CHECK_GE(p, 0);
      CHECK_LE(p, 1);
      boost::bernoulli_distribution<Dtype> random_distribution(p);
      boost::variate_generator<caffe::rng_t*, boost::bernoulli_distribution<Dtype> >
          variate_generator(caffe_rng(), random_distribution);
      for (int i = 0; i < n; ++i) {
        r[i] = variate_generator();
      }
    }
    
    }  // namespace caffe
    

    相关文章

      网友评论

          本文标题:Caffe源码分析-math-function分析

          本文链接:https://www.haomeiwen.com/subject/qukjihtx.html