|
使用 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) |
|
|
|
|
|
|
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) |
|
## 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` 可以写出可读性更高且不易出错的代码。
希望这份总结能帮你理清它们之间的关系。你具体是在处理什么维度的数据呢?如果对某个特定场景的用法还不确定,可以告诉我,我再帮你具体分析。
|
|
|
|
|
|
|
保存模型字典
torch.save(model.state_dict(), model_param_path)
加载模型字典
model = MyModel() model.load_state_dict(torch.load(PATH)) 再次训练前执行 model.train() 预测前执行 model.eval()
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固定随机数种子