张春成
2022/09/07阅读:172主题:默认主题
张量的缩并
张量的缩并
张量,Tensor,是个数学概念。在计算机应用中,它可以用来极大地对计算进行提速。
本实验的结论表明,在样本规划大于 1 万的时候,张量计算快;在样本规模小于 1 万的时候,张量计算又装 X 又快。
什么是张量
严格来说,这个问题我回答不了,因为我不是研究数学的。但可以给出我对它的理解,这个解释与矩阵有关
矩阵是更低阶的张量,张量是更高阶的矩阵
言尽于此,因为再说我就要露馅儿了。
矩阵乘法与张量缩并
下面我们把这个问题“还原”一下,从矩阵乘法的角度来切入这个话题。
矩阵与向量的乘法,或者更具体一点,向量右乘矩阵,或者与它等价的,矩阵左乘向量,这个计算总可以理解成按照向量元素将矩阵的各行进行“线性组合”
其中,向量的维度和矩阵的行数相同。接下来,我们将它们表示成张量。对于向量来说,它是一组实数的线性组合,它的指标是一维的,标示着向量的维度
对于矩阵来说,它也是一组实数的线性组合,它的指标是二维的,标示着元素所在的行和列
其中,可以注意到,两个指标可以出现在上方和下方,在张量的表示习惯中,指标出现在上方时表示“逆变基矢量”,指标出现在下方时表示“协变基矢量”。但这不重要重要的是,类似这样将两个张量放在一起,它们会产生奇妙的化学反应
其中,由于上、下两指标相互偶合,因此在数学上这种相乘再求和的方式可以简记为左端表达方式,称为“爱因斯坦求和约定”。那么,这种求和约定在高维矩阵或张量的计算中同样存在,这里它不再是单纯的矩阵乘法,它开始称为“缩并”,如下例
甚至还有二阶缩并,如下例
计算加速
回到计算机上来,假设你有一个大矩阵
它可以代表 10000 个三角形,每个三角形有 3 个顶点,每个顶点在平面直角坐标系里有 3 个坐标值。如果想要求每个三角形的“内心”,即内接圆圆心,传统的方法是需要重复 10000 次,每次都计算三角形三个端点的算术平均值,求算术平均值的过程可以等价于向量与矩阵的乘法。
进一步地,如果我将求算术平均值时所用的向量换成随机向量,只要这个向量的迹为 1,就能够实现在该三角形内随机取点。搞过 CG 的都会知道,这个操作在三维模型方面使用是多么的频繁。

一堆三角形的散点
这时,如果你注意到了 10000 次这个数量,就会知道这个计算会极其的耗时和低效。于是我们想到的张量。再结合上述分析,这个计算“显然”可以转换成张量的“一阶缩并”。
那么在计算的角度,如果有张量缩并来代替重复的矩阵乘法,这个计算会提速多少呢?我做了个实验。实验结果表明,在 10000 个三角的计算中,张量缩并计算比循环计算矩阵乘法提速了 62 倍。

张量加速的实验结果
而且这个加速是线性的,也就是说数据规模越大,省的时间就越多

张量加速的实验结果(加大规模)
所以说,多学点儿数学还是有用的。
实验代码
import time
import numpy as np
import pandas as pd
# The size of the data
num = 100000
# Generate data
raw_data = np.random.rand(num, 3, 3)
# Generate kernel
kernel = np.random.rand(3, 1)
kernel /= np.sum(kernel)
kt = kernel.transpose()
# Tensor operation
t = time.time()
data = np.tensordot(raw_data, kernel, axes=([1], [0])).squeeze()
t_tensor = time.time() - t
# Loop operation
t = time.time()
d = data * 0
for j, s in enumerate(raw_data):
m = np.matmul(kt, s)
# lst.append(m)
d[j] = m
t_loop = time.time() - t
# Summary
print('The data size is {}'.format(num))
print('The largest different is {}'.format(np.max(np.abs(data - d))))
print('Tensor operation costs {} seconds'.format(t_tensor))
print('Loop operation costs {} seconds'.format(t_loop))
print('Speed up ratio: {}'.format(t_loop / t_tensor))
作者介绍