如何在 C++ 中快速计算向量的归一化 l1 和 l2 范数?

Posted

技术标签:

【中文标题】如何在 C++ 中快速计算向量的归一化 l1 和 l2 范数?【英文标题】:How to fast calculate the normalized l1 and l2 norm of a vector in C++? 【发布时间】:2016-12-30 07:57:42 【问题描述】:

我有一个矩阵 X,它在 d 维空间中有 n 列数据向量。 给定一个向量 xjv[j] 是它的 l1 范数(所有 abs(xji) 的总和),w[j] 是其 l2 范数(所有 xji^2 的总和)的平方,pj[ i] 是条目除以 l1l2 范数的组合。最后,我需要输出:pj, v, w 用于子序列应用程序。

// X = new double [d*n]; is the input.
double alpha = 0.5;
double *pj = new double[d];
double *x_abs = new double[d];
double *x_2 = new double[d];
double *v = new double[n]();
double *w = new double[n]();
for (unsigned long j=0; j<n; ++j) 
        jm = j*m;
        jd = j*d;
        for (unsigned long i=0; i<d; ++i) 
            x_abs[i] = abs(X[i+jd]);
            v[j] += x_abs[i];
            x_2[i] = x_abs[i]*x_abs[i];
            w[j] += x_2[i];    
        
        for (unsigned long i=0; i<d; ++i)
            pj[i] = alpha*x_abs[i]/v[j]+(1-alpha)*x_2[i]/w[j];     
        

   // functionA(pj) ... ...  for subsequent applications
 
// functionB(v, w) ... ... for subsequent applications

我上面的算法需要 O(nd) Flops/Time-complexity,任何人都可以通过使用 Building-functoin 或 C++ 中的新实现来帮助我加快它的速度吗?减少 O(nd) 中的常数值对我也很有帮助。

【问题讨论】:

不是内存分配的瓶颈吗?你不能将预先分配的数组传递给函数吗? 您可以尝试在第二个循环之外计算a = alpha/v[j]b = (1-alpha)/w[j],然后改为计算pj[i] = a*x_abs[i] + b*x_2[i];。但是,编译器可能已经为您进行了优化,并且由于浮点错误,结果可能会略有不同 @Bathsheba 我对内存使用没有任何限制。请继续。谢谢。 你可以为x_abs[i] = abs(X[i + jd]);创建第三个for循环。然后编译器将使用矢量化来加速第二个循环(包含x_2[i] = ... 等的循环)。 @MBo 内部的for (unsigned long i=0; i&lt;d; ++i) x_abs[i] = abs(X[i+jd]); ... etc 应该已经告诉您存储是“主要向量”(向量的所有分量都存储在连续的位置) 【参考方案1】:

让我猜猜:由于您遇到与性能相关的问题,因此您的向量的维度非常大。如果是这种情况,那么值得考虑“CPU 缓存位置” - 关于此 @ 的一些有趣信息987654321@。 如果数据在 CPU 缓存中不可用,那么在 CPU 等待数据时,abs-ing 或平方它一旦可用就相形见绌了。

考虑到这一点,您可能希望尝试以下解决方案(不保证会提高性能 - 编译器在优化代码时实际上可能会应用这些技术)

for (unsigned long j=0; j<n; ++j) 
        // use pointer arithmetic - at > -O0 the compiler will do it anyway
        double *start=X+j*d, *end=X+(j+1)*d;

        // this part avoid as much as possible the competition
        // on CPU caches between X and v/w.
        // Don't store the norms in v/w as yet, keep them in registers
        double l1norm=0, l2norm=0;
        for(double *src=start; src!=end; src++) 
            double val=*src;
            l1norm+=abs(src);
            l2norm+= src*src;
        
        double pl1=alpha/l1norm, pl2=(1-alpha)*l2norm;
        for(double *src=start, *dst=pj; src!=end; src++, dst++) 
          // Yes, recomputing abs/sqr may actually save time by not
          // creating competition on CPU caches with x_abs and x_2
          double val=*src;
          *dst = pl1*abs(val) + pl2*val*val;
            
        // functionA(pj) ... ...  for subsequent applications

        // Think well if you really need v/w. If you really do,
        // at least there are two values to be sent for storage into memory,
        //meanwhile the CPU can actually load the next vector into cache
        v[j]=l1norm; w[j]=l2norm;

// functionB(v, w) ... ... for subsequent applications

【讨论】:

以上是关于如何在 C++ 中快速计算向量的归一化 l1 和 l2 范数?的主要内容,如果未能解决你的问题,请参考以下文章

数据的归一化处理

同一向量的归一化在两种情况下给出不同的值?

关于余弦相似性的取值范围为-1到1的归一化

特征归一化——l2归一化的优势

如何求取一个的图像的归一化直方图?

使用Sklearn的MinMaxScaler做最简单的归一化