逐点运算,即对张量的每个元素独立操作。Pytorch 有大量的运算 api 支持逐点运算,这里尽量讲一些常见的运算在 candle 中的等价操作。
逐点运算表格预览: ✅ 表示有对应实现 🚫 表示无对应实现 ☢️ 表示有代替实现
| 操作 | Pytorch | Candle | |
|---|---|---|---|
| 绝对值 | abs、absolute | abs | ✅ |
| 反余弦值/反正弦值/反正切值 | acos(arccos)/asin/atan | 未实现 | 🚫 |
| 余弦值/正弦值/正切值 | cos/sin/tan | cos/sin/ (tan 未实现) | 🚫 |
| 反双曲余弦值 | acosh、arccosh | 未实现 | 🚫 |
| 加/减/乘/除 | add/sub/mul/div | add/sub/mul/div | ✅ |
| 向上取整/向下取整 | ceil/floor | ceil/floor | ✅ |
| 所有元素夹紧到范围 [ min, max ] 内 | clamp、clip | clamp | ✅ |
| 双曲余弦值 | cosh | 未实现 | 🚫 |
| 角度(度)转换为弧度 | deg2rad | 未实现 | 🚫 |
| e的指数 | exp | exp | ✅ |
| 截断整数值 | fix、trunc | 未实现 | 🚫 |
| 浮点数张量的幂 | float_power | powf | ✅ |
| 截断取小数部分 | frac | 未实现 | 🚫 |
| 分解尾数和指数张量 | frexp | 未实现 | 🚫 |
| 自然(e)对数 | log | log | ✅ |
| 其他对数 | log10/log2/log1p/log** | 全部未实现 | 🚫 |
| 负数 | neg、negative | neg | ✅ |
| 倒数 | reciprocal | recip | ✅ |
| 平方根/平方根倒数 | sqrt/rsqrt | sqrt/未实现,有代替方案 | ☢️ |
| 逻辑 sigmoid 函数 | sigmoid、torch.special.expit | candle_nn::ops::sigmoid | ✅ |
| 取符号值 | sign | sign | ✅ |
| softmax | softmax | candle_nn::ops::softmax | ✅ |
| 平方 | square | 未实现,有代替方案 | ☢️ |
绝对值
pytorch
a = torch.tensor([-1,2,-3])
# 输出 tensor([1, 2, 3])
print(a.abs())
candle:
let a_data = vec![-1i64, 2, -3];
let a = Tensor::from_vec(a_data, 3,&Device::Cpu)?;
let y = a.abs()?;
//[-1, 2, -3] -> [1, 2, 3]
println!("{y}");
余弦值/正弦值/正切值
candle 中,只有 cos 和 sin 实现了,tan 未实现。cos 和 sin 默认只支持浮点型。
pytorch
a = torch.tensor([-1,2,-3])
print(a.cos()) #tensor([ 0.5403, -0.4161, -0.9900])
print(a.sin()) #tensor([-0.8415, 0.9093, -0.1411])
print(a.tan()) #tensor([-1.5574, -2.1850, 0.1425])
candle
let a_data = vec![-1., 2., -3.];
let x = Tensor::from_vec(a_data, 3,&Device::Cpu)?;
let a = x.cos()?;
let b = x.sin()?;
println!("{a}");//[ 0.5403, -0.4161, -0.9900]
println!("{b}");//[-0.8415, 0.9093, -0.1411]
虽然 candle 不支持 tan 正切操作,但是通过数学公式我们可知道:
tan(x)=cos(x)/sin(x)
因此 tan 有间接实现:
// 正切操作
let c = (x.sin()? / x.cos()?)?;
println!("{c}"); //[-1.5574, -2.1850, 0.1425]
加/减/乘/除
pytorch 里的加/减/乘/除方法与 candle 加/减/乘/除方法常规用法大体一致。但是,还有一些细节 Canele 未实现:
- pytorch 的 加法和减法有 alpha 缩放,而 candle 没有,也就是当我们不使用 alpha 缩放参数时,其操作才和 candle 中的操作等价。
- pytorch 里的加/减/乘/除方法不但可以支持张量与张量之间,还支持和标量数字之间运算。
pytorch
a = torch.tensor([1,2,3])
print(a.add(10)) #tensor([11, 12, 13])
print(a + 10) # tensor([11, 12, 13])
candle
let a_data = vec![1., 2., 3.];
let x1 = Tensor::from_vec(a_data, 3,&Device::Cpu)?;
// candle 不支持不同尺寸的 Tensor 做加/减/乘/除操作
// 也不支持标量 和 Tensor 做加/减/乘/除操作,因此需要把标量 转成 Tensor
let size = 3;
let b_data = [10f64].repeat(size);
let x2: Tensor = Tensor::from_vec(b_data, size,&Device::Cpu)?;
let y: Tensor = x1.add(&x2)?;
println!("{y}"); //[11., 12., 13.]
let y = (x1 + x2)?;
println!("{y}"); //[11., 12., 13.]
向上取整/向下取整
pytorch
a = torch.tensor([ 0.5403, -0.4161, -0.9900])
print(a.ceil()) #tensor([1., -0., -0.])
print(a.floor()) #tensor([ 0., -1., -1.])
candle
let a_data = vec![ 0.5403, -0.4161, -0.9900];
let x = Tensor::new(a_data, &Device::Cpu)?;
let ceil = x.ceil()?;
let floor = x.floor()?;
println!("ceil: {:?}", ceil); //ceil: Tensor[1, -0, -0; f64]
println!("floor: {:?}", floor);//floor: Tensor[0, -1, -1; f64]
元素夹紧到范围 [ min, max ] 内
公式如下:
pytorch
a = torch.tensor([ 0.5403, -0.4161, -0.9900])
print(a.clamp(-0.5,0.5)) #tensor([ 0.5000, -0.4161, -0.5000])
candle
//x = [ 0.5403, -0.4161, -0.9900]
let y = x.clamp(-0.5, 0.5)?;
println!("{y}"); //[ 0.5000, -0.4161, -0.5000]
e 的指数
公式如下:
pytorch
a = torch.tensor([ 0., -2., -3.])
print(a.exp()) #tensor([1.0000, 0.1353, 0.0498])
candle
//x = [ 0., -2., -3.]
let y = x.exp()?;
println!("{y}"); //[1.0000, 0.1353, 0.0498]
截断整数值
这个操作在 candle 中未实现,可能需要根据具体的 tensor 转成 向量,转换后再返回 tensor,不过这个实现的代价有点大。
let a_data = vec![ 1.0000, -2.9353, -5.0498];
let x = Tensor::new(a_data, &Device::Cpu)?;
let v = x.to_vec1::<f64>()?;
let v:Vec<f64> = v.iter().map(|v| v.trunc()).collect();
let y = Tensor::new(v, &Device::Cpu)?;
println!("{y}");//[ 1., -2., -5.]
浮点数张量的幂
pytorch
a = torch.tensor([ 6.0, 4.0, 7.0, 1.0])
print(a.float_power(2)) #tensor([36., 16., 49., 1.], dtype=torch.float64)
candle
let a_data = vec![6., 4., 7., 1.];
let x = Tensor::new(a_data, &Device::Cpu)?;
let y = x.powf(2f64)?;
println!("{y}");//[36., 16., 49., 1.]
截断取小数部分
和截断整数值 操作一下,candle 中未实现,可以转回向量操作:
let a_data = vec![ 1.0000, -2.9353, -5.0498];
let x = Tensor::new(a_data, &Device::Cpu)?;
let v = x.to_vec1::<f64>()?;
let v:Vec<f64> = v.iter().map(|v| v.fract()).collect();
let y = Tensor::new(v, &Device::Cpu)?;
println!("{y}");//[ 0.0000, -0.9353, -0.0498]
自然对数
公式:
pytorch
a = torch.tensor([ 6.0, 4.0, 7.0, 1.0])
print(a.log()) #tensor([1.7918, 1.3863, 1.9459, 0.0000])
candle
let a_data = vec![ 6.0, 4.0, 7.0, 1.0];
let x = Tensor::new(a_data, &Device::Cpu)?;
let y = x.log()?;
println!("{y}");//[1.7918, 1.3863, 1.9459, 0.0000]
负数
pytorch
a = torch.tensor([ 6.0, 4.0, 7.0, 1.0])
print(a.neg()) #tensor([-6., -4., -7., -1.])
candle
let a_data = vec![ 6.0, 4.0, 7.0, 1.0];
let x = Tensor::new(a_data, &Device::Cpu)?;
let y = x.neg()?;
println!("{y}");//[-6., -4., -7., -1.]
倒数
公式如下:
pytorch
a = torch.tensor([1.0, 2.0,3.0])
print(a.reciprocal()) #tensor([1.0000, 0.5000, 0.3333])
candle
let a_data = vec![1.0, 2.0,3.0];
let x = Tensor::new(a_data, &Device::Cpu)?;
let y = x.recip()?;
println!("{y}");//[1.0000, 0.5000, 0.3333]
平方根/平方根倒数
平方根公式
平方根倒数公式
pytorch
a = torch.tensor([1.0, 2.0,3.0])
print(a.sqrt()) #tensor([1.0000, 1.4142, 1.7321])
print(a.rsqrt()) #tensor([1.0000, 0.7071, 0.5774])
candle 没有 rsqrt, 但是可以通过上面的公式算出来。
//[1.0, 2.0,3.0]
let y = x.sqrt()?;
println!("{y}");//[1.0000, 1.4142, 1.7321]
let y = (1f64 / x.sqrt()?)?;
println!("{y}");//[1.0000, 0.7071, 0.5774]
sigmoid
sigmoid 函数常被用作激活函数,用于将神经元的输出压缩到 (0,1) 之间,从而可以表示概率值。
公式如下:
这个操作 pytorch 和 candle 有一些不一样,而且默认 pytorch 的精度显示也和 candle 不一样。
pytorch
a = torch.tensor([1.0, 2.0,3.0])
print(a.sigmoid()) #tensor([0.7311, 0.8808, 0.9526])
candle
//[1.0, 2.0,3.0]
let y = candle_nn::ops::sigmoid(&x)?;
println!("{:?}",y);//Tensor[0.7310585786300049, 0.8807970779778823, 0.9525741268224334; f64]
取符号值
取符号值的意思是,当x < 0 时,返回 -1,当 x = 0 时,返回 0,当 x > 0 时,返回 1。 数学公式如下:
pytorch
a = torch.tensor([1.0, -2.0,3.0])
print(a.sign()) #tensor([ 1., -1., 1.])
candle
//[1.0, -2.0,3.0]
let y = x.sign()?;
println!("{:?}",y);//Tensor[1, -1, 1; f64]
Softmax
softmax 是一种数学函数,它将一个数值向量转换为一个概率分布向量。softmax 计算后得到一个新的向量,其中每个元素表示对应输入在整个向量中的概率占比。
softmax 常用于分类任务中。
公式如下:
pytorch
a = torch.tensor([1.0, -2.0,3.0])
print(a.softmax(0)) #tensor([0.1185, 0.0059, 0.8756])
candle
//[1.0, -2.0,3.0]
let y = candle_nn::ops::softmax(&x, 0)?;
println!("{:?}",y);//Tensor[0.11849965453500959, 0.005899750401902781, 0.8756005950630876; f64]
平方
pytorch 中平方有两种写法:
a = torch.tensor([1.0, -2.0,3.0])
print(a.square()) #tensor([1., 4., 9.])
print(a.pow(2)) #tensor([1., 4., 9.])
candle 是没有 square 的,只能用 powf 去实现。
//[1.0, -2.0,3.0]
let y = x.powf(2.)?;
println!("{:?}",y);//Tensor[1, 4, 9; f64]