torch设备管理

使用 torch.set_num_threads

 
import torch

# 设置全局线程数
torch.set_num_threads(4)

# 现在导入你的模型和其他 PyTorch 代码...
    

 
注意,torch.set_num_threads 设置的是全局线程数,
它将影响所有的 PyTorch 操作,包括数据加载、模型前向传播等。
    

设置环境变量:

 
import os

# 设置环境变量以限制线程数
os.environ['OMP_NUM_THREADS'] = '4'
os.environ['MKL_NUM_THREADS'] = '4'

# 现在导入 PyTorch 和你的模型
import torch
import torch.nn as nn

# 你的模型定义和加载代码...
    

在数据加载时控制线程数:

 
如果你的瓶颈在于数据加载而不是模型推理,
你可以通过调整 DataLoader 的 num_workers 参数来控制用于数据加载的线程数。
    

 
from torch.utils.data import DataLoader, Dataset

# 假设你有一个自定义的 Dataset 类
dataset = MyDataset()

# 创建 DataLoader 并设置 num_workers
dataloader = DataLoader(dataset, batch_size=32, num_workers=4)
    

 

    

torch 调用GPU


device = "cuda:0" if torch.cuda.is_available() else "cpu"
X = X.to(device=device)
y = y.to(device=device)


USE_CUDA = torch.cuda.is_available()
device = torch.device("cuda" if USE_CUDA else "cpu")

 
import torch
use_gpu = torch.cuda.is_available()
if use_gpu:
    model.cuda()

 

从CPU加载到GPU

 
import torch
x = torch.randn(2, 3)
x
tensor([[ 1.3599,  1.0726, -1.0500],
        [-0.1350, -1.6993, -1.1693]])
    

 
x.device
device(type='cpu')
    

 
这是因为电脑上没有GPU,则默认为CPU 
    

从CPU加载到GPU

 
# Returns a copy of this object in CUDA memory.
x.cuda()

device = "cuda:0" if torch.cuda.is_available() else "cpu"
X = X.to(device=device)

 


 

  

 


pytorch shape解读

 
import torch 
a=torch.linspace(start=1,end=6,steps=6).reshape(2,3)
a
tensor([[1., 2., 3.],
        [4., 5., 6.]])

a.shape
torch.Size([2, 3]),
在pytorch看来,a的shape是(2,3), 应该读作 3个 维度为2的向量

pytorch维度从0开始计数,2维数表在展示给人看的时候,第0维展示为列方向

pytorch中shape:(第0维,第1维,...,第n维)

维度操作

 
a = torch.randn(2, 3, 4)

torch.permute(a, (2, 1, 0))

a.permute(2, 1, 0)

交换两个维度,也只能交换两个维度

 
torch.transpose(a, 0, 1).shape
    

转置

 
import torch
a=torch.tensor([[1,2,3]])
a.shape
torch.Size([1, 3])


b=a.t()
b.shape
torch.Size([3, 1])
    

如果只有2维,可以从行与列的角度去看


shape为(2,3),读作3个维度为2的向量,但这也只是读作,
或者在pytorch看来如此,
或者说对于既定的业务含义有不同的读法,

就数字而言,它也是 2个 维度为3的向量,二者等价
当维度更多时,就是你已经无法一下就说清每个维度的含义,
或者是单纯地从 数学/数字/矩阵 角度去看,
从某个维度取数据时,只改变所取维度,不改变其他维度的大小

 
(2,3) 2行3列,取一行数据,其shape为(3,)
(n1,n2) n1行n2列,取第1维(索引为0)的某个数据,其shape为(n2,)
(n1,n2) n1行n2列,取第2维(索引为1)的某个数据,其shape为(n1,)

(n1,n2,...,nm-1,nm,nm+1,...,nk),
已经无法从行列的角度来说了,但后面的逻辑是一样的

取第nm维后的某个数据,其shape为(n1,n2,...,nm-1,nm+1,...,nk)

pytorch中的维度索引从0开始计数
    

取第2维(索引为1)的某个数

 
```python
import torch

torch.manual_seed(73)
a = torch.ones(1,3,2,2)
print(a)

a = a[:,1,:,:]
print(a.shape)  # torch.Size([1, 2, 2])  这里消失了1维

a = torch.unsqueeze(input=a,dim=1)  # 添加一个维度
print(a.shape) # torch.Size([1, 1, 2, 2])
print(a)

"""
tensor([[[[1., 1.],
          [1., 1.]],

          [[1., 1.],
           [1., 1.]],

          [[1., 1.],
           [1., 1.]]]])
torch.Size([1, 2, 2])
torch.Size([1, 1, 2, 2])
tensor([[[[1., 1.],
          [1., 1.]]]])
"""

```

取某个维度的一个切片

 
a[:,n:m,:]

按维取均值

 
```python 
import torch

torch.manual_seed(73)
a = torch.ones(1,3,2,2)

# 取均值默认会消失一个维度,比如一串数字的均值是一个数值
print(a.mean(dim=1).shape)  # torch.Size([1, 2, 2])
print(a.mean(dim=1,keepdim=True).shape)
print(a)

"""
torch.Size([1, 2, 2])
torch.Size([1, 1, 2, 2])
tensor([[[[1., 1.],
            [1., 1.]],

            [[1., 1.],
            [1., 1.]],

            [[1., 1.],
            [1., 1.]]]])

"""
```

按维取最大值

 
pytorch 取第0维的最大值
values,indices = a.max(dim=0)
values
tensor([4., 5., 6.])

indices
tensor([1, 1, 1])

取第1维的最大值
values,indices = a.max(dim=1)
values
tensor([3., 6.])

 


unsqueeze,在指定dim上增加一维

 
import torch
probs = torch.tensor([[0.9, 0.1], [0.1, 0.9], [0.8, 0.2]]).unsqueeze(0)
probs
tensor([[[0.9000, 0.1000],
            [0.1000, 0.9000],
            [0.8000, 0.2000]]])

 

  

x.view:可兼容onnx转换

 
import torch

x = torch.tensor([[1, 2, 3], [4, 5, 6]])
x_flatten = x.view(2,-1)  # -1 表示自动计算此维度的大小
print(x_flatten)

 
tensor([[1, 2, 3],
        [4, 5, 6]])
     

 
x = torch.randn(3,2,3)
x 
tensor([[[-1.8351, -0.6714, -1.3090],
         [ 1.0558,  1.2539, -0.6256]],

        [[-0.8478,  1.4329, -1.0031],
         [-1.2600,  0.6706,  1.2318]],

        [[ 0.9180,  0.0779, -1.1079],
         [-2.0011, -0.6397, -1.0539]]])

 
x.view(3,-1)
tensor([[-1.8351, -0.6714, -1.3090,  1.0558,  1.2539, -0.6256],
        [-0.8478,  1.4329, -1.0031, -1.2600,  0.6706,  1.2318],
        [ 0.9180,  0.0779, -1.1079, -2.0011, -0.6397, -1.0539]])
        

x.reshape:兼容onnx

 
x.reshape(3,-1)
tensor([[-1.8351, -0.6714, -1.3090,  1.0558,  1.2539, -0.6256],
        [-0.8478,  1.4329, -1.0031, -1.2600,  0.6706,  1.2318],
        [ 0.9180,  0.0779, -1.1079, -2.0011, -0.6397, -1.0539]])

nn.Flatten():兼容onnx

 
# 展平层
self.flatten = nn.Flatten()

x = self.flatten(x)

pytorch矩阵乘法
## matmul ``` import torch a = torch.rand(3,3,7) b = torch.rand(3,7,3) torch.matmul(a,b) ``` ``` tensor([[[1.4016, 1.6263, 1.0781], [1.0632, 1.0419, 0.7032], [0.9459, 1.0396, 0.6318]], [[1.0026, 1.4292, 0.8176], [1.2285, 1.4016, 1.1455], [0.8004, 1.0875, 0.7757]], [[0.7738, 1.4511, 1.5687], [1.5427, 2.4797, 2.1463], [1.3152, 1.6549, 2.1859]]]) ``` ## @ ``` a@b tensor([[[1.4016, 1.6263, 1.0781], [1.0632, 1.0419, 0.7032], [0.9459, 1.0396, 0.6318]], [[1.0026, 1.4292, 0.8176], [1.2285, 1.4016, 1.1455], [0.8004, 1.0875, 0.7757]], [[0.7738, 1.4511, 1.5687], [1.5427, 2.4797, 2.1463], [1.3152, 1.6549, 2.1859]]]) ```
## torch.mm:仅限2维矩阵 ``` import torch torch.manual_seed(73) a = torch.rand(3,7) b = torch.rand(7,3) torch.mm(a,b) tensor([[1.2050, 1.7727, 1.8881], [2.2234, 2.1534, 2.0374], [1.4380, 1.7234, 1.8297]]) ``` - 错误示例 ``` a = a.unsqueeze(dim=0) b = b.unsqueeze(dim=0) torch.matmul(a,b) tensor([[[1.2050, 1.7727, 1.8881], [2.2234, 2.1534, 2.0374], [1.4380, 1.7234, 1.8297]]]) torch.mm(a,b) --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[3], line 1 ----> 1 torch.mm(a,b) RuntimeError: self must be a matrix ```
## torch.bmm ``` import torch torch.manual_seed(73) a = torch.rand(3,3,7) b = torch.rand(3,7,3) torch.matmul(a,b) tensor([[[0.7847, 1.2145, 1.3052], [0.8276, 2.3949, 1.8271], [0.8289, 1.2427, 1.0018]], [[2.5169, 1.9156, 2.4408], [2.0237, 1.2965, 1.1980], [2.3762, 1.9028, 2.2781]], [[0.9137, 1.8393, 1.1406], [0.8456, 1.5484, 1.5531], [1.2973, 2.3349, 2.3504]]]) torch.bmm(a,b) tensor([[[0.7847, 1.2145, 1.3052], [0.8276, 2.3949, 1.8271], [0.8289, 1.2427, 1.0018]], [[2.5169, 1.9156, 2.4408], [2.0237, 1.2965, 1.1980], [2.3762, 1.9028, 2.2781]], [[0.9137, 1.8393, 1.1406], [0.8456, 1.5484, 1.5531], [1.2973, 2.3349, 2.3504]]]) ``` mm仅限2维矩阵 bmm不限维数,等价于matmul,@
- 爱因斯坦求和约定 (Einsum) 是一种用字符串来定义张量运算的极其强大的方式。 - 几乎所有张量运算(点积、外积、转置、求和、矩阵乘法等)都可以用它来实现 ``` a = torch.randn(3, 4) b = torch.randn(4, 5) # 'ik' (a的维度), 'kj' (b的维度) -> 'ij' (输出维度),其中 'k' 是求和索引 result = torch.einsum('ik,kj->ij', a, b) # 输出形状: torch.Size([3, 5]) ``` ``` batch_a = torch.randn(16, 3, 4) batch_b = torch.randn(16, 4, 5) # 'bik' (带批次b), 'bkj' -> 'bij',在 'k' 上求和 result = torch.einsum('bik,bkj->bij', batch_a, batch_b) # 输出形状: torch.Size([16, 3, 5]) ```
PyTorch 中的矩阵乘法接口看似繁多,但核心区别在于**输入维度的灵活性**和**计算模式**。简单来说:`torch.mm` 最“专一”,`torch.matmul`(及其别名`@`)最“通用”,而 `torch.einsum` 则是最“自由”的终极工具。 下面是它们的核心区别速览表: | 操作/函数 | 输入维度限制 | 广播支持 | 主要用途与特点 | | :--- | :--- | :--- | :--- | | **`torch.mm`** | 严格 2D 矩阵 `(n, m)` 和 `(m, p)` | ❌ 不支持 | 最底层的二维矩阵乘法,性能好,但不够灵活。 | | **`torch.bmm`** | 严格 3D 批量矩阵 `(b, n, m)` 和 `(b, m, p)` | ❌ 不支持 | 专门用于批量矩阵计算,要求批次大小(b)一致。 | | **`torch.matmul`** | 任意 >=1D 张量 | ✅ 支持 | **最常用**。会自动根据输入维度处理,功能最全面。 | | **`@` 运算符** | 同 `torch.matmul` | ✅ 支持 | **语法糖**。功能和 `torch.matmul` 完全等价,代码更简洁。 | | **`torch.einsum`** | 任意维度的张量 | ✅ 支持 | **爱因斯坦求和约定**。用字符串定义计算规则,功能最强大、最灵活。 | --- ### 🧠 核心函数详解 #### 1. `torch.mm` - 最基础的二维矩阵乘法 这是最标准的线性代数矩阵乘法。它**仅接受二维张量**,不支持广播,也不支持批量操作。 ```python import torch mat_a = torch.randn(3, 4) mat_b = torch.randn(4, 5) result = torch.mm(mat_a, mat_b) # 输出形状: torch.Size([3, 5]) ``` #### 2. `torch.bmm` - 专门的批量矩阵乘法 当有一批矩阵需要同时相乘时使用。输入必须是**三维张量**,并且批次大小(第一维)必须相同。 ```python batch_a = torch.randn(16, 3, 4) # 16个 3x4 的矩阵 batch_b = torch.randn(16, 4, 5) # 16个 4x5 的矩阵 result = torch.bmm(batch_a, batch_b) # 输出形状: torch.Size([16, 3, 5]) ``` #### 3. `torch.matmul` 与 `@` - 最通用的“瑞士军刀” 这是 PyTorch 推荐的通用矩阵乘法,它会**根据输入张量的维度自动选择合适的计算逻辑**,并支持广播。 * **二维矩阵**:执行标准的矩阵乘法,效果等同于 `torch.mm`。 * **一维和二维混合**:会将一维张量自动视为行向量或列向量进行广播,结果会去除广播添加的维度。例如,一个形状 `(n, m)` 的矩阵和一个形状 `(m)` 的向量相乘,会得到一个形状 `(n)` 的向量(即矩阵与向量的乘法)。 * **高维张量(≥3D)**:将最后两维视为矩阵的维度,前面的维度都视为“批次维度”进行处理。这在 Transformer 等模型中非常常见。 ```python # 矩阵乘向量 mat = torch.randn(3, 4) vec = torch.randn(4) res_vec = torch.matmul(mat, vec) # 输出形状: torch.Size([3]) # 高维张量乘法 a = torch.randn(5, 2, 3) # 5个 2x3 的矩阵 b = torch.randn(5, 3, 4) # 5个 3x4 的矩阵 res_high = torch.matmul(a, b) # 输出形状: torch.Size([5, 2, 4]) ``` **`@` 运算符** 是 `torch.matmul` 的完全等价形式,写起来更简单: ```python # 以下两行完全等价 result = torch.matmul(tensor1, tensor2) result = tensor1 @ tensor2 ``` #### 4. `torch.einsum` - 最自由的操作定义器 爱因斯坦求和约定 (Einsum) 是一种用**字符串**来定义张量运算的极其强大的方式。几乎所有张量运算(点积、外积、转置、求和、矩阵乘法等)都可以用它来实现。 * **基本语法**:用字符串(如 `"ik,kj->ij"`)来描述输入和输出张量的维度(索引)如何映射。 * **矩阵乘法示例**: ```python a = torch.randn(3, 4) b = torch.randn(4, 5) # 'ik' (a的维度), 'kj' (b的维度) -> 'ij' (输出维度),其中 'k' 是求和索引 result = torch.einsum('ik,kj->ij', a, b) # 输出形状: torch.Size([3, 5]) ``` * **批量矩阵乘法**: ```python batch_a = torch.randn(16, 3, 4) batch_b = torch.randn(16, 4, 5) # 'bik' (带批次b), 'bkj' -> 'bij',在 'k' 上求和 result = torch.einsum('bik,bkj->bij', batch_a, batch_b) # 输出形状: torch.Size([16, 3, 5]) ``` 当运算变得复杂(如矩阵转置后相乘、指定轴求和等)时,`einsum` 可以通过一行字符串清晰表达意图,避免使用多个 `reshape` 和 `permute` 操作。 ### 💎 总结与选择建议 1. **日常使用,推荐 `@` 或 `torch.matmul`**:它们是通用性最强的选择,代码简洁,能处理从简单二维到复杂高维的绝大部分情况。 2. **追求极致性能或代码明确性,使用 `torch.mm` 或 `torch.bmm`**:当你明确知道你的输入是标准的二维或三维批量数据时,使用这些专用函数可以最清晰地表达你的计算意图。 3. **操作复杂时,考虑 `torch.einsum`**:当涉及多个维度的转置、指定轴求和、或是实现像多头注意力机制这样的复杂运算时,`einsum` 可以写出可读性更高且不易出错的代码。 希望这份总结能帮你理清它们之间的关系。你具体是在处理什么维度的数据呢?如果对某个特定场景的用法还不确定,可以告诉我,我再帮你具体分析。

 


 


 


pytorch模型保存与加载

保存模型字典

 
torch.save(model.state_dict(), model_param_path)

加载模型字典

 
model = MyModel()
model.load_state_dict(torch.load(PATH))

再次训练前执行
model.train()

预测前执行
model.eval()

torch固定随机数种子

CPU

 
torch.manual_seed(seed) # 为CPU设置随机种子

np.random.seed(seed)  # Numpy module.
random.seed(seed)  # Python random module.	

GPU

 
torch.cuda.manual_seed(seed) # 为当前GPU设置随机种子
torch.cuda.manual_seed_all(seed)  # if you are using multi-GPU,为所有GPU设置随机种子
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True    

参考
    PyTorch固定随机数种子