内积运算
内积(inner product)是一种在向量空间中定义的二元运算,也称为点积、内积或标量积。
内积可以用来表示两个向量之间的相似程度、夹角以及两个向量之间的夹角余弦值。
如果两个向量a和b的内积是正数,则它们夹角小于90°;如果内积是负数,则它们夹角大于90°;如果内积是0
内积与机器学习
在机器学习中,内积运算可以用来表示向量之间的相似程度。内积运算还可以用来表示向量之间的距离。

例如,在文本分类任务中,我们可以将每篇文章看作一个向量,并使用内积运算来计算两篇文章之间的相似程度。
在推荐系统中,我们也可以使用内积运算来计算用户和物品之间的相似程度,从而推荐相似度高的物品给用户。
内积计算加速
内积计算需要大量的浮点数运算,常规计算非常耗时,可以考虑以下加速方法:
使用C++实现
C++和Python是两种流行的编程语言,在许多方面都有所不同。其中一个显著的差异是执行速度。一般来说,C++的执行速度比Python快得多。
使用SSE加速
SSE(Streaming SIMD Extensions)是一种由Intel提出的用于提升CPU运算速度的指令集。SSE提供了一组专门用于执行浮点运算的指令,可以用来加速向量运算、矩阵乘法以及内积运算等。
SSE提供了一组128位的浮点数寄存器,每个寄存器可以存储4个32位浮点数。SSE指令可以同时对4个数进行运算,因此可以大大提升运算速度。
使用GPU加速
使用GPU进行内积运算可以大大提升计算速度。GPU具有大量的浮点运算单元,可以同时执行大量的向量运算。
内积运算实践
C++ 内积运算
如下代码所示,我们分别定义两个函数,分别进行手动计算内积和使用SSE计算内积。smmintrin.h是一个C/C++头文件,包含了SSE4.1指令集的函数声明。
#include <smmintrin.h>
#include <iostream>
#include <ctime>
// 手动计算内积
float inner_product(const float* x, const float* y, const long & len){
float prod = 0.0f;
long i;
for (i=0;i<len;i++){
prod+=x[i]*y[i];
}
return prod;
}
// SSE计算内积
float dot_product(const float* x, const float* y, const long & len){
float prod = 0.0f;
const int mask = 0xff;
__m128 X, Y;
float tmp;
long i;
for (i=0;i<len;i+=4){
X=_mm_loadu_ps(x+i); //_mm_loadu_ps把float转为__m128
Y=_mm_loadu_ps(y+i);
_mm_storeu_ps(&tmp,_mm_dp_ps(X,Y,mask));
prod += tmp;
}
return prod;
}
Go 内积运算
GO语言由Google开发,广泛应用于后端开发和大规模分布式系统的开发中。GO语言有许多优秀的特性,其中一个显著的优势是执行速度。
如下代码所示,我们实现了使用GO语言进行内积计算的逻辑:
import (
"fmt"
"time"
)
func InnerProduct(x []float32, y []float32) float32{
var rect float32=0
for i:=0;i<len(x);i++{
rect+=x[i]*y[i]
}
return rect
}
Python 内积运算
Python是最常见的编程语言,接下来我们将手动完成向量内积运算。
array1=np.array(arr)
array2=np.array(brr)
for i in range(LEN):
prod+=arr[i]*brr[i]
Numpy 内积运算
NumPy可以使用线性代数库,例如BLAS和LAPACK,加速向量运算、矩阵乘法和内积运算等。
array1=np.array(arr)
array2=np.array(brr)
prod=np.dot(array1,array2)
Jax 内积运算
Jax支持NumPy的大多数函数和API,可以方便地将NumPy代码转换为Jax代码。Jax也可以使用GPU加速计算,提升运算速度。
import jax
prod = jax.numpy.dot(array1, array2)
PyTorch 内积运算
在PyTorch中,可以使用Tensor类型进行向量运算。Tensor可以在CPU或者GPU上进行计算,在GPU上可以加速运算。
import torch
array1 = torch.from_numpy(array1)
array2 = torch.from_numpy(array2)
# CPU计算
prod = torch.dot(array1, array2)
# GPU计算
array1 = array1.cuda().float()
array2 = array2.cuda().float()
prod = torch.dot(array1, array2)
速度对比
如下我们将不同的工具在不同的向量维度下进行了速度的对比,这里我们都是运算需要的秒。
| 工具 | 1w维度 | 100w维度 |
|---|---|---|
| C++ | 2.4e-05 | 0.002279 |
| C++ + SSE | 2.1e-05 | 0.001742 |
| Go | 0.000020 | 0.001098 |
| Python | 0.001637 | 0.156543 |
| Numpy | 0.000216 | 0.001519 |
| Jax CPU | 0.021019 | 0.004413 |
| Pytorch GPU | 0.000723 | 0.000690 |
从计算结果可以看出,C++和Go语言本身速度很快,SSE可以带来一定的加速。Python很慢,但Numpy很快。Numpy内置了向量和矩阵加速库,加速后和C++差不多。
Jax和Pytorch在CPU下速度差不多,但GPU加速很多,是所有工具中速度最快的。
# 竞赛交流群 邀请函 #

与 28000+来自竞赛爱好者一起交流~




