infiniTensor学习笔记(专业阶段2024)

基本概念 

张量(Tensor):

1.基本定义

struct Tensor<T> {
    shape: Vec<usize>,
    data: Vec<T>,
}

张量的形状(Shape)

shape 是一个向量,表示张量每个维度的大小。例如:

  • 一维张量(向量)的 shape[n],其中 n 是向量的长度。
  • 二维张量(矩阵)的 shape[m, n],其中 m 是行数,n 是列数。
  • 三维张量的 shape[p, m, n],其中 p 是层数,m 是行数,n 是列数。

张量的维数(Rank)

张量的维数(或称为 rank)是指张量的 shape 向量的长度。例如:

  • 向量 [n] 是一维张量,其维数是 1。
  • 矩阵 [m, n] 是二维张量,其维数是 2。
  • 三维张量 [p, m, n] 的维数是 3。
data

data 是一个包含张量所有元素的扁平化向量。例如:

  • 对于形状为 [4] 的一维张量,data 可能是 [1, 2, 3, 4]
  • 对于形状为 [2, 3] 的二维张量,data 可能是 [1, 2, 3, 4, 5, 6],表示一个 2x3 的矩阵:
    1 2 3
    4 5 6
    
  • 对于形状为 [2, 2, 2] 的三维张量,data 可能是 [1, 2, 3, 4, 5, 6, 7, 8],表示一个 2x2x2 的立方体结构。

偏移量(Offset)

  • offset 是数据的偏移量,用于处理子张量的情况。例如,如果一个张量是从另一个张量的中间部分截取的,偏移量就可以指向数据的起始位置。这样我们可以避免数据的复制,只需改变偏移量即可创建新的子张量。

  • 示例: 假设我们有一个一维张量,数据为 [1, 2, 3, 4, 5]。如果我们只想创建一个包含 [3, 4, 5] 的子张量,可以设置 offset2

长度(Length)

  • length 表示张量中的元素总数。这个字段可以帮助我们快速获取张量的大小,并在需要时进行验证或其他操作。

  • 示例: 对于一个二维张量,形状为 [2, 3],即包含 2 行 3 列,总共有 6 个元素。此时 length 就是 6

2.方法

reshape 方法

reshape 方法用于重新解释张量的形状,同时保持其总大小不变。

代码示例:
// 重新解释张量的形状,同时保持总大小不变
pub fn reshape(&mut self, new_shape: &Vec<usize>) -> &mut Self {
    let new_length: usize = new_shape.iter().product();
    if new_length != self.length {
        let old_shape = self.shape.clone();
        panic!("New shape {new_shape:?} does not match tensor of {old_shape:?}");
    }
    self.shape = new_shape.clone();
    self
}
功能和使用场景:
  • 功能reshape 方法改变张量的形状,而不改变其数据。它只改变 shape 字段的值。
  • 使用场景:当你需要以不同的维度和形状来查看或操作张量,但数据本身并没有改变时使用。例如,将一个 1x6 的张量重新解释为 2x33x2

slice 方法

slice 方法用于创建一个新的子张量,该子张量包含原张量中的一部分数据。

代码示例:
pub fn slice(&self, start: usize, shape: &Vec<usize>) -> Self {
    let new_length: usize = shape.iter().product();
    assert!(self.offset + start + new_length <= self.length);
    Tensor {
        data: self.data.clone(),
        shape: shape.clone(),
        offset: self.offset + start,
        length: new_length,
    }
}
功能和使用场景:
  • 功能slice 方法根据给定的起始位置和新的形状,创建一个新的张量实例。这个新的张量实例共享原始数据,但通过 offset 字段指向数据的子集。
  • 使用场景:当你需要从原始张量中提取出一个子张量进行操作时使用。例如,从一个 4x4 的张量中提取出一个 2x2 的子张量。

大模型的几个关键算子

1.SiLU函数

(1)Sigmoid函数

其函数图像如下:
这里写图片描述

(2)SiLU 函数逐元素操作(Element-wise Operation)

  • 逐元素操作:这是指对张量中的每一个元素单独进行操作,而不是对整个张量进行整体操作。例如,如果有两个张量 xy,对每一个元素 x[i]y[i],你需要执行 y[i] = sigmoid(x[i]) * x[i] * y[i]

(3)作业定义

1.定义:

2.代码: 
pub fn sigmoid(x: f32) -> f32{
    1.0 / (1.0 + (-x).exp())
}


// y = sigmoid(x) * x * y
// hint: this is an element-wise operation
pub fn silu(y: &mut Tensor<f32>, x: &Tensor<f32>) {
    let len = y.size();
    assert!(len == x.size());

    let y_data = unsafe { y.data_mut() };
    let x_data = x.data();

    // 逐元素计算
    for i in 0..len {
        y_data[i] = y_data[i] * x_data[i] * sigmoid(x_data[i]);
    }
}

2.RMS Normalization 实现 

 定义:

  • 参数说明

    • x_i:位置 i 处的输入张量或向量。
    • w:与 x_i 长度相同的一维权重向量,用于逐元素相乘。
    • ϵ:一个小常数,防止除零错误。
    • y_i:位置 i 处的归一化输出张量或向量。
    • N 是输入向量中元素的个数

代码: 

pub fn rms_norm(y: &mut Tensor<f32>, x: &Tensor<f32>, w: &Tensor<f32>, epsilon: f32) {
    let x_len = x.size();

    let w_len = w.size();

    let x_silce_num = x_len / w_len;

    let y_data = unsafe { y.data_mut() };
    let w_data = w.data();

    for i in 0..x_silce_num{
        let slice = x.slice(w_len*i, &vec![w_len]); // 创建一个更长生命周期的值
        let x_slice = slice.data();
        let sum_of_squares: f32 = x_slice.iter().map(|&xj| xj * xj).sum();
        let rms = (sum_of_squares / w_len as f32 + epsilon).sqrt();

        for j in 0..w_len{
            y_data[w_len*i + j] = x_slice[j] * w_data[j] / rms;
        }
    }
}

3. 算子:矩阵乘

1.转置定义

在多维数组或张量的操作中,转置(Transpose)是指交换某些维度上的数据排列。不同于简单的二维矩阵转置,多维数组的转置可以涉及多个维度的重新排列。以下是几种常见的转置定义:</

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值