torch 的 tensor类

torch的tensor类

导包

import torch
或者
from torch import tensor

构造函数(创建tensor)

(function) def tensor(
    data: Any,
    dtype: _dtype | None = None,
    device: _device | str | None = None,
    requires_grad: _bool = False
) -> Tensor

# 例如
# tensor([1,2])

# tensor([[1,2],[1,2]])

# x = np.array([1,2])
# tensor(x)

创建tensor

  1. arange
def arange(
    start: Number,
    end: Number,
    step: Number,
    *,
    out: Tensor | None = None,
    dtype: _dtype | None = None,
    device: _device | str | None = None,
    requires_grad: _bool = False
) -> Tensor: ...
# 返回一个1维张量,长度为 floor((end−start)/step)
# 。包含从start到end,以step为步长的一组序列值(默认步长为1)。

# 参数:

# 位置参数
# start (Number):

# 起始值(包含在结果中)。可以是整数或浮点数。
# end (Number):

# 终止值(不包含在结果中)。生成的张量中的值会小于 end(如果 step > 0)或大于 end(如果 step < 0)。
# step (Number):

# 步长,默认为 1。表示相邻两个元素之间的差值。
# 关键字参数
# out (Tensor | None, 可选):

# 输出张量。如果提供,则将结果写入此张量中。
# dtype (_dtype | None, 可选):

# 指定返回张量的数据类型(如 torch.float32, torch.int64 等)。如果不指定,PyTorch 会根据 start, end, 和 step 的类型推断数据类型。
# device (_device | str | None, 可选):

# 指定返回张量所在的设备(如 'cpu' 或 'cuda:0')。如果不指定,默认使用当前设备。
# requires_grad (_bool, 可选):

# 如果设置为 True,则返回的张量将被标记为需要梯度计算。默认为 False。

  1. zero / ones
def ones(
    size: _size,
    *,
    out: Tensor | None = None,
    dtype: _dtype = None,
    layout: _layout | None = strided,
    device: _device | str | None = None,
    pin_memory: _bool = False,
    requires_grad: _bool = False
) -> Tensor: ...

3.其他的可见runoob链接

tensor属性

import torch

# 创建一个 2D 张量
tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)

# 张量的属性
print("Tensor:\n", tensor)
print("Shape:", tensor.shape)  # 获取形状
print("Size:", tensor.size())  # 获取形状(另一种方法)
print("Data Type:", tensor.dtype)  # 数据类型
print("Device:", tensor.device)  # 设备
print("Dimensions:", tensor.dim())  # 维度数
print("Total Elements:", tensor.numel())  # 元素总数
print("Requires Grad:", tensor.requires_grad)  # 是否启用梯度
print("Is CUDA:", tensor.is_cuda)  # 是否在 GPU 上
print("Is Contiguous:", tensor.is_contiguous())  # 是否连续存储

# 获取单元素值
single_value = torch.tensor(42)
print("Single Element Value:", single_value.item())

# 转置张量
tensor_T = tensor.T
print("Transposed Tensor:\n", tensor_T)

广播机制

广播(Broadcasting)是 NumPy 和 PyTorch 等库中用于执行不同形状的数组或张量之间的算术运算的一种机制。当两个数组或张量的形状不完全相同时,广播机制允许它们在特定条件下进行操作,而不需要显式地复制数据以使它们的形状一致。

广播规则

广播机制遵循一套严格的规则来决定两个张量是否可以一起操作,并如何扩展较小的张量以匹配较大的张量。以下是广播的基本规则:

  • 从后向前比较维度大小:从最后一个维度开始逐个比较两个张量的维度大小。
  • 相同尺寸或者其中一个为 1:如果两个张量在某个维度上的大小相同,或者其中一个张量在该维度上的大小为 1,则这两个张量在这个维度上是兼容的。否则,无法广播。
  • 自动扩展:对于尺寸为 1 的维度,该维度会被隐式地重复使用以匹配另一个张量的对应维度。
  • 形状对齐:最终结果的形状是由每个维度中的最大值决定的。

具体步骤

  • 检查维度数量:如果两个张量的维度数量不同,则在形状较短的张量前面添加 1,直到两者的维度数量相同。
  • 逐维度比较:从最后一位开始逐位比较维度大小。如果两者之一为 1 或者两者相等,则继续检查下一个维度;否则,广播失败。
  • 确定输出形状:输出张量的形状是输入张量中每个维度的最大值。
import torch

# 形状 (3,)
tensor_a = torch.tensor([1, 2, 3])
# 形状 (3, 1)
tensor_b = torch.tensor([[1], [2], [3]])

result = tensor_a + tensor_b
print(result)
# 输出:
# tensor([[2, 3, 4],
#         [3, 4, 5],
#         [4, 5, 6]])

tensor_a 被视为 (1, 3),tensor_b 是 (3, 1)。
在第一个维度上,tensor_a 的大小为 1,tensor_b 的大小为 3,因此 tensor_a 沿第一个维度被广播。
在第二个维度上,tensor_b 的大小为 1,tensor_a 的大小为 3,因此 tensor_b 沿第二个维度被广播。
结果的形状是 (3, 3)。

张量操作

  1. mv, mm, bmm, matmul
## mv  matrix * vector
A = torch.ones(3,4)
b = torch.ones(4)
A, b, torch.mv(A,b)

## mm matrix * matrix
A = torch.ones(3,4)
B = torch.ones(4,3)
A, B, torch.mm(A,B)

## bmm Batch mm
A = torch.ones(2,3,4)
B = torch.ones(2,4,3)
A, B, torch.bmm(A,B)

## matmul 
A = torch.ones(2,3,4)
B = torch.ones(1,4,3)
A, B, torch.matmul(A,B), torch.matmul(A,B) == A@B  
  1. 切片
### 普通切片
A = torch.ones(3,4)
A[0,0], A[:,0], A[0,:], A[:], A[0:2, 1:3]
### 传入bool数组切片
A = torch.ones(3,4)
B = torch.zeros_like(A)
B[:,0] = 1
print(A)
print(B)


B = B>0
print(A)
print(B)


A[B] = 2
print(A)
print(B)

节省内存/原地操作/深浅拷贝

运行一些操作可能会导致为新结果分配内存

例如,如果我们用Y = X + Y,我们将取消引用Y指向的张量,而是指向新分配的内存处的张量。

这可能是不可取的,原因有两个:

  1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新;
  2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。

幸运的是,(执行原地操作)非常简单。
我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>

如果在后续计算中没有重复使用X
我们也可以使用X[:] = X + YX += Y来减少操作的内存开销。

## 浅拷贝 改变B也改变A
A = torch.ones(3,4)
B = A

B+=1
B, A

## 深拷贝 复制一次内存,改变B不改变A
A = torch.ones(3,4)
B = A.clone()

B+=1
B, A




## 指向新地址,非原地操作

B = torch.ones(3,4)
ori_id = id(B)
A = torch.zeros_like(B)
B = A+B

ori_id == id(B)


## 原地操作
B = torch.ones(3,4)
ori_id = id(B)
A = torch.zeros_like(B)
B[:] = A+B

ori_id == id(B)


L2和 L1范数

## l2
torch.norm(A)
## L1
torch.abs(A).sum()

求和/平均/降维


### 求和
(method)
def sum(
    *,
    dtype: _dtype | None = None
) -> Tensor: ...

def sum(
    dim: _size | _int,
    keepdim: _bool = False,
    *,
    dtype: _dtype | None = None
) -> Tensor: ...

def sum(
    dim: Sequence[str | ellipsis | None],
    keepdim: _bool = False,
    *,
    dtype: _dtype | None = None
) -> Tensor: ...


A = torch.ones(2,3,4)
A.sum(), A.sum(0,True), A.sum([0,1])

###平均
A = torch.ones(2,3,4)
A.mean(), A.mean(0) , A.mean(0, True), A.mean([0,1])

###平均的另一种实现
A = torch.ones(3,4)
A.mean(), A.sum() / A.numel()
A.mean(0),  A.sum(0)/ A.shape[0]

转换成numpy

import torch
import numpy as np

# 1. NumPy 数组转换为 PyTorch 张量
print("1. NumPy 转为 PyTorch 张量")
numpy_array = np.array([[1, 2, 3], [4, 5, 6]])
print("NumPy 数组:\n", numpy_array)

# 使用 torch.from_numpy() 将 NumPy 数组转换为张量
tensor_from_numpy = torch.from_numpy(numpy_array)
print("转换后的 PyTorch 张量:\n", tensor_from_numpy)

# 修改 NumPy 数组,观察张量的变化(共享内存)
numpy_array[0, 0] = 100
print("修改后的 NumPy 数组:\n", numpy_array)
print("PyTorch 张量也会同步变化:\n", tensor_from_numpy)

# 2. PyTorch 张量转换为 NumPy 数组
print("\n2. PyTorch 张量转为 NumPy 数组")
tensor = torch.tensor([[7, 8, 9], [10, 11, 12]], dtype=torch.float32)
print("PyTorch 张量:\n", tensor)

# 使用 tensor.numpy() 将张量转换为 NumPy 数组
numpy_from_tensor = tensor.numpy()
print("转换后的 NumPy 数组:\n", numpy_from_tensor)

# 修改张量,观察 NumPy 数组的变化(共享内存)
tensor[0, 0] = 77
print("修改后的 PyTorch 张量:\n", tensor)
print("NumPy 数组也会同步变化:\n", numpy_from_tensor)

# 3. 注意:不共享内存的情况(需要复制数据)
print("\n3. 使用 clone() 保证独立数据")
tensor_independent = torch.tensor([[13, 14, 15], [16, 17, 18]], dtype=torch.float32)
numpy_independent = tensor_independent.clone().numpy()  # 使用 clone 复制数据
print("原始张量:\n", tensor_independent)
tensor_independent[0, 0] = 0  # 修改张量数据
print("修改后的张量:\n", tensor_independent)
print("NumPy 数组(不会同步变化):\n", numpy_independent)
posted @ 2025-04-14 17:07  玉米面手雷王  阅读(38)  评论(0)    收藏  举报