news 2026/9/3 20:31:56

PyTorch Tensor教程:从创建、属性到实战操作的深度学习入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyTorch Tensor教程:从创建、属性到实战操作的深度学习入门

1. 为什么要从 Tensor 开始认识 PyTorch

1.1 Tensor 是什么

如果你接触过深度学习,大概率听过这样一句话:PyTorch 的一切操作都是围绕 Tensor(张量)展开的。

Tensor 的中文翻译是“张量”。用一句话概括:Tensor 是 PyTorch 中最基础的数据结构,它用来存放数据,既可以是标量、向量、矩阵,也可以是更高维度的数组。你可以把 Tensor 简单理解为一个“支持 GPU 加速、能自动记录梯度、专门为深度学习设计的 NumPy 数组”。

举个例子:

  • 单个数字是 0 维张量(标量);
  • 一列数字是 1 维张量(向量);
  • 一个二维表格是 2 维张量(矩阵);
  • 一张彩色图片通常用 3 维张量表示,比如(通道数, 高度, 宽度)
  • 一批彩色图片则是 4 维张量,比如(批量大小, 通道数, 高度, 宽度)

在 PyTorch 中,输入数据、网络权重、中间特征、梯度信息,全部以 Tensor 的形式存储和流转。所以,不管以后你是做 CV、NLP、推荐系统还是强化学习,Tensor 都是绕不开的第一课。

1.2 Tensor 与 NumPy 数组、Python 列表的区别

很多刚入门的读者会问:Python 有 list,NumPy 有 ndarray,为什么 PyTorch 还要设计一个 Tensor?

区别主要体现在三个层面:

  • 设备支持:NumPy 数组只能跑在 CPU 上,Tensor 可以轻松地在 CPU 和 GPU 之间迁移。深度学习训练时数据量巨大,GPU 并行计算能带来几十倍的加速,这是 NumPy 无法直接提供的。
  • 自动求导:Tensor 有一个关键属性requires_grad,打开后 PyTorch 会自动记录张量上的所有运算,并在反向传播时自动计算梯度。NumPy 没有这个能力。
  • 深度集成:PyTorch 的神经网络层(如nn.Linearnn.Conv2d)输入输出都要求是 Tensor,整个生态都以 Tensor 为统一接口。

当然,Tensor 也保留了 NumPy 的很多优点,比如支持向量化运算、广播机制、切片索引等。掌握 Tensor 之后,你会发现在处理数据时比直接用 Python 列表高效得多。

1.3 本文你能学到什么

本文是 PyTorch 系列教程的第 1 课,内容保持“概念 + 代码 + 经验”的结构,读完你应该掌握:

  • Tensor 是什么,适合什么场景;
  • 如何在自己的电脑上搭建 PyTorch 运行环境;
  • 创建 Tensor 的 6 种常用方式;
  • Tensor 的属性如何查看和使用;
  • Tensor 的索引、切片、形状变换、数学运算等基本操作;
  • 一个包含数据生成、预处理、特征构建的完整小案例;
  • 新手最容易踩的坑和工程实践建议。

接下来,我们先把环境准备好,然后用大量可以直接复制的代码带你一步步认识 Tensor。

2. 环境准备:搭建 PyTorch 运行环境

2.1 安装方式选择

PyTorch 的安装方式很灵活,你可以根据习惯选择:

  • 使用 conda / miniforge 创建虚拟环境(推荐);
  • 直接使用 pip 安装;
  • 使用 Docker 镜像。

对于初学者,我最推荐用虚拟环境来隔离项目依赖。因为深度学习项目往往需要不同的 PyTorch 版本和 Python 版本,如果全部装在系统 Python 里,很容易出现依赖冲突。

如果你使用的是 Anaconda 或 Miniforge,可以用下面的命令创建环境:

conda create -n pytorch_env python=3.10 -y conda activate pytorch_env

这里以 Python 3.10 为例,具体版本可以根据你的项目需求调整。PyTorch 对 Python 版本的兼容性比较好,3.8 到 3.12 都能覆盖大部分稳定版本。

2.2 安装 PyTorch

安装 PyTorch 最核心的一点是:一定要到 PyTorch 官网的安装页生成你的专属命令,因为官网会根据你的操作系统、包管理器、CUDA 版本实时给出推荐命令。

需要注意,PyTorch 安装命令中的 CUDA 版本和你电脑实际安装的显卡驱动不是一回事。简单理解:

  • 如果你有 NVIDIA 显卡并希望用 GPU 训练,需要安装对应 CUDA 版本的 PyTorch;
  • 如果你没有 NVIDIA 显卡,或者暂时只做 CPU 学习,选择 CPU 版本即可;
  • AMD 显卡和 Apple Silicon 芯片也有对应的支持方式,同样以官网获取的安装命令为准。

下面给出的是一个没有 CUDA 的 CPU 版安装示例:

pip install torch torchvision torchaudio

如果你需要用 GPU,推荐到官网安装页选择自己的环境后复制命令,这样可以避免版本不匹配。比如选择 Linux + pip + CUDA 12.1 时,命令会类似于:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

由于 PyTorch 版本更新较快,且不同版本对应的 wheel 链接可能变化,不建议硬记命令,每次安装都以官网生成的结果为准。

2.3 验证安装是否成功

在终端激活环境并启动 Python 交互界面,输入以下代码:

import torch print(torch.__version__) print(torch.cuda.is_available())

如果输出类似下面的内容,说明 PyTorch 已经安装成功:

2.3.1 False

torch.__version__输出版本号,torch.cuda.is_available()返回True表示当前环境可以使用 GPU,返回False表示当前是 CPU 环境或 CUDA 配置不对。

注意:就算输出False也不要着急,CPU 环境完全够用来学习本文的 Tensor 操作。真正需要 GPU 的时候,再回头检查驱动和 CUDA 配置即可。

3. 创建 Tensor 的六种核心方式

创建 Tensor 是 PyTorch 中最频繁的操作之一。下面我按使用频率从高到低,把常用的创建方法完整梳理一遍。

3.1 从 Python 列表或 NumPy 数组创建

最直接的方式是用torch.tensor()把已有的 Python 列表转换成一个 Tensor:

import torch # 从列表创建 a = torch.tensor([1, 2, 3]) print(a) # 从嵌套列表创建矩阵 b = torch.tensor([[1, 2], [3, 4]]) print(b) # 指定数据类型 c = torch.tensor([1.2, 3.4], dtype=torch.float32) print(c, c.dtype)

输出结果:

tensor([1, 2, 3]) tensor([[1, 2], [3, 4]]) tensor([1.2000, 3.4000]) torch.float32

需要注意,torch.tensor()会复制数据,生成的新 Tensor 和原始数据不再共享内存。如果你希望 Tensor 和 NumPy 数组共享内存,可以用torch.from_numpy()

import numpy as np arr = np.array([1, 2, 3]) t = torch.from_numpy(arr) t[0] = 100 print(arr) # 输出 [100 2 3],arr 被同步修改了

这个特性在数据处理时很有用,可以避免不必要的内存拷贝,但也容易引起“数据被偷偷改了”的困惑,需要留心。

3.2 全 0、全 1 与单位矩阵

在深度学习中,初始化权重、创建掩码、填充数据时经常需要全 0 或全 1 的矩阵。

# 全 0 矩阵 zeros = torch.zeros(2, 3) print(zeros) # 全 1 矩阵 ones = torch.ones(3, 2) print(ones) # 单位矩阵 eye = torch.eye(3) print(eye)

输出结果:

tensor([[0., 0., 0.], [0., 0., 0.]]) tensor([[1., 1.], [1., 1.], [1., 1.]]) tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])

torch.zerostorch.ones的第一个参数是形状shape,可以传一个元组,也可以传多个整数作为维度的尺寸。torch.eye只接受一个参数 n,生成 n 行 n 列的单位矩阵。

3.3 随机张量:均匀分布与正态分布

随机初始化是神经网络训练的基础操作,PyTorch 提供了多种随机张量生成函数。

# 均匀分布 [0, 1) uniform = torch.rand(2, 3) print(uniform) # 标准正态分布 normal = torch.randn(2, 3) print(normal) # 指定上下限的均匀分布 rand_int = torch.randint(0, 10, (3, 3)) print(rand_int)

输出结果(每次运行会略有不同):

tensor([[0.2338, 0.6932, 0.9821], [0.7621, 0.1348, 0.8755]]) tensor([[ 0.3904, -0.1280, 1.0035], [ 0.0231, -0.8912, 0.5761]]) tensor([[3, 7, 0], [9, 2, 5], [4, 8, 1]])

几个函数的记忆方法:

  • torch.rand:random 的缩写,生成 0 到 1 之间的均匀随机数;
  • torch.randn:normal 的缩写,生成均值为 0、方差为 1 的标准正态分布随机数;
  • torch.randint:生成指定范围内的随机整数。

随机种子在机器学习实验中非常重要,后面第 8 节我会专门说如何设置随机种子保证结果可复现。

3.4 等差数列与等间隔序列

生成序列数据时,torch.arangetorch.linspace非常常用。

# 类似 range,生成等差数列 a = torch.arange(0, 10, 2) print(a) # 在 [0, 1] 区间生成 5 个等间隔数 b = torch.linspace(0, 1, 5) print(b)

输出结果:

tensor([0, 2, 4, 6, 8]) tensor([0.0000, 0.2500, 0.5000, 0.7500, 1.0000])

torch.arange(start, end, step)生成从start开始,到end结束(不包含)的等差数列,步长为step

torch.linspace(start, end, steps)则在startend之间均匀生成steps个数,注意这里包含end本身。

两者最直观的区别是:arange关注步长,linspace关注数量。

3.5 指定形状与填充值

有时候需要把一个固定值填充到指定形状的张量中,比如初始化偏置为 0 或者常数。

# 填充 7 full = torch.full((2, 3), 7) print(full) # 生成一个未初始化的张量,内容为内存中的随机值 empty = torch.empty(2, 2) print(empty)

输出结果:

tensor([[7, 7, 7], [7, 7, 7]]) tensor([[6.5738e-33, 4.5746e-41], [0.0000e+00, 0.0000e+00]])

torch.full的用法很直观:第一个参数是形状,第二个参数是填充值。

torch.empty不会对内存做初始化,所以它的默认内容是什么都不确定。在实际项目中,empty往往用于后续马上被覆盖赋值的情景,避免多余的初始化开销。初学者不建议频繁使用,容易产生莫名其妙的数值问题。

3.6 创建张量时的设备与梯度设置

上面创建的 Tensor 默认都在 CPU 上,且关闭了梯度记录。但为了后续训练模型,我们需要掌握两个额外参数。

# 指定设备为 GPU(如果可用) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") a = torch.tensor([1, 2, 3], device=device) print(a.device) # 开启梯度记录 b = torch.tensor([1.0, 2.0], requires_grad=True) print(b.requires_grad)

输出结果:

cuda:0 True

device决定数据在哪个设备上存储和计算,requires_grad决定是否在后续运算中自动构建计算图。这两者会在训练神经网络时频繁出现,现在先建立概念即可。

4. Tensor 的核心属性详解

创建好 Tensor 之后,我们需要学会查看它的各种属性,否则后续处理数据时会一头雾水。

4.1 shape:形状

shape是 Tensor 最常用的属性,表示每个维度的大小。

x = torch.rand(2, 3, 4) print(x.shape)

输出结果:

torch.Size([2, 3, 4])

这里的torch.Size([2, 3, 4])本质是一个元组,表示这个 Tensor 是 3 维的,第一维有 2 个元素,第二维有 3 个元素,第三维有 4 个元素。

在深度学习框架中,我们经常会看到一个叫“维度”的概念。比如:

  • 全连接层的输入通常是 2 维张量:(batch_size, feature_dim)
  • 卷积层的输入通常是 4 维张量:(batch_size, channel, height, width)
  • Transformer 的输入通常是 3 维张量:(batch_size, seq_len, hidden_dim)

理解shape是理解一切张量操作的基础。

4.2 ndim、size()、numel()

除了shape,还有几个高频属性需要掌握。

x = torch.rand(2, 3, 4) print(x.ndim) # 维度数量,输出 3 print(x.size()) # 等价于 x.shape print(x.size(0)) # 第 0 维大小,输出 2 print(x.numel()) # 元素总数 2*3*4 = 24

输出结果:

3 torch.Size([2, 3, 4]) 2 24

ndim返回维度数,你可以把它理解为“这个张量有几层方括号”;size()shape等价;numel()返回所有元素的数量,常用于统计参数量或计算损失时进行归一化。

4.3 dtype:数据类型

dtype表示 Tensor 中元素的类型。常见的类型包括:

  • torch.float32(默认浮点类型,深度学习中最常用)
  • torch.float64(双精度浮点,精度高但更慢、更占内存)
  • torch.int32torch.int64(整数类型)
  • torch.bool(布尔类型)
a = torch.tensor([1, 2, 3]) b = torch.tensor([1.0, 2.0, 3.0]) print(a.dtype) # torch.int64 print(b.dtype) # torch.float32

PyTorch 的默认整数类型是int64,默认浮点类型是float32。这也是深度学习中最常见的配置。GPU 对float32的优化最好,内存占用也更小。

新手经常犯的一个错误是:把整型 Tensor 和浮点型 Tensor 混在一起计算,导致类型不匹配报错。建议在创建 Tensor 时就显式指定dtype,或者通过.float().int().long()等方法转换。

4.4 device:设备

device表示 Tensor 存储在 CPU 还是 GPU 上。

cpu_tensor = torch.rand(2, 2) gpu_tensor = cpu_tensor.to("cuda") # 如果 GPU 可用 print(cpu_tensor.device) # cpu print(gpu_tensor.device) # cuda:0

在训练过程中,最常见的报错之一就是“device mismatch”:一个 Tensor 在 CPU 上,另一个在 GPU 上,直接运算就会报错。后面排错章节会更详细展开。

4.5 requires_grad:自动梯度开关

requires_grad是 PyTorch 自动求导的开关。当它为True时,PyTorch 会记录这个张量上的所有操作,并构建一个计算图,之后可以通过反向传播计算梯度。

x = torch.tensor([2.0], requires_grad=True) y = x ** 2 y.backward() print(x.grad) # tensor([4.])

上面代码中,y = x^2,对x求导结果是2x = 4,PyTorch 正确计算出了梯度。

这里我们只需要记住:训练神经网络时,网络权重.weight.bias默认requires_grad=True;普通数据默认是False,不需要手动修改,除非你要实现自定义梯度逻辑。

4.6 stride 与内存布局(进阶)

stride是 Tensor 内存布局的重要概念,它表示在每个维度上,移动到下一个元素需要跨越的内存步长。

x = torch.rand(3, 4) print(x.stride()) # (4, 1)

对于这样连续的二维张量,第 0 维的步长是 4(即跳过一行 4 个元素),第 1 维的步长是 1。

stride对张量性能影响很大,因为非连续张量在运算时可能触发额外的拷贝。不过对初学者来说,更重要的是知道transposepermute等操作会改变内存布局,导致张量变成“非连续”状态,如果后面要强制改变形状,需要用contiguous()先转回连续内存布局。

5. Tensor 的基本操作入门

5.1 索引与切片

Tensor 的索引和切片与 NumPy 非常像,甚至和 Python 列表也有相似之处。

x = torch.arange(12).reshape(3, 4) print(x)

输出结果:

tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])

接下来看几个常用操作:

print(x[0]) # 第 0 行 print(x[:, 1]) # 所有行的第 1 列 print(x[1:, :2]) # 从第 1 行开始,前 2 列 print(x[-1]) # 最后一行

输出结果:

tensor([0, 1, 2, 3]) tensor([1, 5, 9]) tensor([[4, 5], [8, 9]]) tensor([ 8, 9, 10, 11])

切片返回的是原始 Tensor 的视图,不是拷贝。也就是说,修改切片结果会同步影响原始数据。如果需要独立副本,要调用.clone()

5.2 形状变换:view、reshape、transpose

形状变换在实际开发中非常高频,因为数据和网络层之间的形状常常需要对齐。

x = torch.arange(12) # view 改变形状 y = x.view(3, 4) print(y) # reshape 改变形状 z = x.reshape(2, 6) print(z)

输出结果:

tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]]) tensor([[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])

view只适用于内存连续的张量,reshape更通用。如果view遇到不连续的张量会直接报错,此时可以先调用.contiguous()view。在新手阶段,可以优先用reshape,减少踩坑。

再看一个转置操作:

x = torch.rand(2, 3) print(x.shape) # torch.Size([2, 3]) y = x.transpose(0, 1) print(y.shape) # torch.Size([3, 2])

transpose(0, 1)交换第 0 维和第 1 维。需要注意,transpose得到的是视图,内存布局会变成非连续状态。如果之后要view改形状,需要先.contiguous()

5.3 数学运算与广播机制

Tensor 支持所有常见数学运算,比如加减乘除、幂运算、指数和对数等。

a = torch.tensor([1, 2, 3], dtype=torch.float32) b = torch.tensor([4, 5, 6], dtype=torch.float32) print(a + b) # tensor([5., 7., 9.]) print(a - b) # tensor([-3., -3., -3.]) print(a * b) # tensor([ 4., 10., 18.]) print(a / b) # tensor([0.2500, 0.4000, 0.5000]) print(torch.pow(a, 2)) # tensor([1., 4., 9.]) print(torch.sqrt(a)) # tensor([1.0000, 1.4142, 1.7321])

还有一个非常重要的机制叫“广播”(Broadcasting)。当两个张量形状不一致时,PyTorch 会尝试自动扩展维度使它们可以运算。

a = torch.tensor([1, 2, 3]) b = torch.tensor(10) print(a + b) # tensor([11, 12, 13])

标量10被广播到和[1, 2, 3]相同形状后再相加。再举个矩阵和向量的例子:

m = torch.rand(2, 3) v = torch.tensor([1, 2, 3]) # m 的形状是 (2, 3),v 的形状是 (3,),可以广播 print(m + v)

广播机制让代码更简洁,但也容易让新手误判形状。如果两个张量形状完全不兼容,PyTorch 会抛出 RuntimeError,此时优先去检查维度是否对齐。

5.4 拼接与拆分

在处理批量数据或合并特征时,拼接操作非常常见。

a = torch.tensor([[1, 2], [3, 4]]) b = torch.tensor([[5, 6], [7, 8]]) # 按第 0 维拼接,相当于上下拼接 c = torch.cat([a, b], dim=0) print(c.shape) # torch.Size([4, 2]) # 按第 1 维拼接,相当于左右拼接 d = torch.cat([a, b], dim=1) print(d.shape) # torch.Size([2, 4])

torch.stackcat不同,它会创建一个新的维度:

e = torch.stack([a, b], dim=0) print(e.shape) # torch.Size([2, 2, 2])

cat拼接时不会增加维度,stack会在指定位置新增一个维度。在实际项目里,cat用于合并特征,stack用于把多个样本堆叠成批次。

拆分操作可以使用torch.chunktorch.split

x = torch.rand(4, 6) # 平均分成 2 块 chunks = torch.chunk(x, chunks=2, dim=0) print(len(chunks)) # 2 # 按指定大小切分 splits = torch.split(x, split_size_or_sections=2, dim=1) print(len(splits)) # 3

5.5 与 NumPy 互转及深浅拷贝

PyTorch 和 NumPy 的互转非常方便。

import numpy as np # Tensor 转 NumPy t = torch.tensor([1, 2, 3]) arr = t.numpy() print(arr, arr.dtype) # NumPy 转 Tensor arr2 = np.array([4, 5, 6]) t2 = torch.from_numpy(arr2) print(t2)

输出结果:

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

需要特别注意的是:t.numpy()torch.from_numpy()生成的转换结果,默认与原始对象共享内存。如果修改其中一个,另一个也会变化。如果希望完全独立,需要用.clone().copy()

t = torch.tensor([1, 2, 3]) arr = t.numpy() arr[0] = 100 print(t) # tensor([100, 2, 3])

6. 实战:从数据到特征的完整 Tensor 流程

6.1 场景描述

学完基本操作后,我们通过一个小案例把它们串起来。

假设我们有一个身高和体重的数据列表,需要计算 BMI 指数,然后把数据归一化到 0 到 1 之间,最后从张量中筛选出 BMI 正常范围(18.5 到 24.9)的样本,并转换成 NumPy 数组用于后续存储。

这个场景覆盖了:创建 Tensor、类型转换、数学运算、索引筛选、NumPy 互转等高频操作。

6.2 完整代码

import torch # 1. 原始数据:身高(m),体重(kg) heights = [1.65, 1.75, 1.80, 1.60, 1.70] weights = [55.0, 70.0, 82.0, 50.0, 68.0] # 2. 创建浮点 Tensor h = torch.tensor(heights, dtype=torch.float32) w = torch.tensor(weights, dtype=torch.float32) # 3. 计算 BMI = 体重 / 身高^2 bmi = w / (h ** 2) print("BMI:", bmi) # 4. 手动归一化: (x - min) / (max - min) bmi_min = bmi.min() bmi_max = bmi.max() bmi_norm = (bmi - bmi_min) / (bmi_max - bmi_min) print("归一化 BMI:", bmi_norm) # 5. 筛选 BMI 正常范围样本 mask = (bmi >= 18.5) & (bmi <= 24.9) print("正常掩码:", mask) normal_bmi = bmi[mask] print("正常 BMI 值:", normal_bmi) # 6. 转成 NumPy 数组 normal_bmi_np = normal_bmi.numpy() print("NumPy 结果:", normal_bmi_np)

6.3 运行结果与说明

输出结果:

BMI: tensor([20.2020, 22.8571, 25.3086, 19.5312, 23.5294]) 归一化 BMI: tensor([0.1315, 0.6151, 1.0000, 0.0000, 0.7387]) 正常掩码: tensor([ True, True, False, True, True]) 正常 BMI 值: tensor([20.2020, 22.8571, 19.5312, 23.5294]) NumPy 结果: [20.20202 22.857143 19.53125 23.52941 ]

这个例子虽然简单,但你已经用上了 Tensor 的创建、计算、统计、掩码索引和 NumPy 转换。在真实项目中,类似的数据预处理流程还会配合torch.utils.data.Dataset来完成,那是后续教程要讲的内容。

7. 常见问题与排查思路

刚接触 PyTorch,遇到报错是很正常的。把下面这些高频问题收藏起来,能帮你减少很多排查时间。

问题现象常见原因解决思路
RuntimeError: Expected object of scalar type Long but got scalar type Float数据类型不匹配,比如索引需要整数类型,你传了浮点类型.long().int().float()显式转换
RuntimeError: Expected all tensors to be on the same device一个 Tensor 在 CPU,另一个在 GPU,直接参与运算将所有张量统一移动到同一设备,使用.to(device)
RuntimeError: view size is not compatible with input tensor's size and stride对非连续张量使用view,且新形状与内存布局冲突先调用.contiguous()view,或直接改用reshape
Tensor在训练过程中梯度不断累积每个 batch 未清空梯度在每轮反向传播前调用optimizer.zero_grad()
.numpy()报错Tensor 的requires_grad=True,或当前在 GPU 上.detach().cpu()再转 NumPy
改了切片结果后原始 Tensor 也变了切片返回的是视图,不是副本如果需要独立数据,使用.clone()
CUDA out of memoryGPU 显存不足减小 batch size、降低输入尺寸、清理不需要的中间变量、使用torch.no_grad()推理

下面重点说两个最常见的问题。

7.1 设备不一致报错

在 GPU 环境下,最常见的错误就是设备不一致:

# 模拟错误:一个张量在 CPU,一个在 GPU cpu_tensor = torch.rand(3, 3) gpu_tensor = torch.rand(3, 3, device="cuda") # 下面这行会报错 # result = cpu_tensor + gpu_tensor

正确的做法是统一设备:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu") cpu_tensor = cpu_tensor.to(device) result = cpu_tensor + gpu_tensor

7.2 非连续张量的 view 报错

# 创建一个连续张量 x = torch.arange(12).reshape(3, 4) # 转置后变成非连续 y = x.transpose(0, 1) # 下面这行可能报错 # y.view(12) # 正确做法 y = y.contiguous() print(y.view(12))

在编写模型时,transposepermutesqueezeunsqueeze都可能改变内存布局,遇到view报错时优先用.contiguous()修复。

8. 最佳实践与工程建议

8.1 明确 dtype 和 device

在创建 Tensor 时,尽量显式指定dtypedevice。特别是在处理来自不同来源的数据时,类型和设备的隐式转换可能带来性能损耗或直接报错。

推荐统一使用浮点类型torch.float32,这是 PyTorch 在深度学习中默认且优化最好的类型。整数做索引时用torch.int64即可。

实际项目里建议封装一个设备工具函数:

import torch device = torch.device("cuda" if torch.cuda.is_available() else "cpu") def to_device(data): if isinstance(data, torch.Tensor): return data.to(device) return data

8.2 正确处理梯度和原地操作

训练神经网络时,有几个习惯可以尽早养成:

  • 每轮反向传播前调用optimizer.zero_grad(),否则梯度会累加;
  • 推理阶段用torch.no_grad()包裹,既省内存又提高速度;
  • 对不需要梯度的数据,可以设置requires_grad=False,或者用.detach()把张量从计算图中分离。

另外,在开启requires_grad的 Tensor 上尽量避免使用原地操作,比如t += 1t.add_(1)。因为原地操作会修改变量的值,而计算图记录的是操作历史,这可能导致梯度计算错误。用非原地写法t = t + 1更安全。

8.3 可复现性设置

深度学习实验非常依赖随机初始化。为了结果可复现,最好在脚本开头统一设置随机种子:

import random import numpy as np import torch def set_seed(seed=42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)

注意:即使设置了随机种子,由于 GPU 并行计算本身存在不确定性,完全复现仍然有难度,但设置种子能大幅降低实验之间的随机差异。

8.4 内存管理与性能优化

对于大张量,有几个实用建议:

  • .clone()tensor.copy_()时要注意是否真的需要副本,不必要的拷贝会浪费内存;
  • 大批量数据不要一次性全部加载到内存,推荐配合torch.utils.data.DataLoader分批读取;
  • 中间变量如果不再使用,Python 的垃圾回收会处理,但在循环中显式删除大变量也能帮助释放显存;
  • 如果显存不够,优先减小 batch size,而不是降低模型复杂度。

8.5 编写可读代码

Tensor 操作很容易写出一长串难以阅读的链式调用。推荐把复杂操作拆成小函数,并加上类型注释。比如:

def normalize_tensor(x: torch.Tensor) -> torch.Tensor: """将张量归一化到 [0, 1] 区间。""" return (x - x.min()) / (x.max() - x.min())

这样既方便复用,也方便测试。

9. 下一步学习路线

到这里,第 1 课的内容就算完整结束了。回顾一下,你已经掌握了 PyTorch 中最核心的数据结构 Tensor:如何创建、如何查看属性、如何做基本操作,以及常见的坑和工程建议。

但 Tensor 只是 PyTorch 的起点。要真正开始训练神经网络,还有几个关键知识需要继续学习:

  • 自动求导机制autograd是如何工作的,计算图是什么,梯度如何流动;
  • torch.nn模块:如何搭建全连接网络、卷积网络、循环网络;
  • 数据集与数据加载DatasetDataLoader、数据增强;
  • 训练循环:损失函数、优化器、评估指标、模型保存与加载;
  • GPU 训练与分布式训练:多卡训练常用技巧。

如果你有自己的数据,建议先拿一批小数据,把“数据预处理 → 模型搭建 → 训练 → 评估”的完整流程跑通,再逐步加深理论。亲手敲代码比只看教程有效得多,遇到报错也可以去官方文档和社区搜索,结合本文第 7 节的排错思路,大部分问题都能很快定位。

后续我会继续更新这个 PyTorch 系列教程,下一课计划讲自动求导与线性回归实战,帮助你完全理解梯度下降是如何在 PyTorch 中自动完成的。如果你在跟着实操时遇到问题,欢迎在评论区留言,我会根据大家反馈调整后续内容的侧重点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 19:36:07

T3 Code用量分析实战:3个维度看懂AI编程Token成本与缓存节省

T3 Code用量分析实战&#xff1a;3个维度看懂AI编程Token成本与缓存节省 【免费下载链接】t3code 项目地址: https://gitcode.com/GitHub_Trending/t3/t3code T3 Code 是一款开源的"Agent 控制面"工具&#xff0c;可统一管理你电脑上的 Claude Code、Codex、…

作者头像 李华
网站建设 2026/9/3 21:13:57

大模型产品选型先算任务成本

大模型产品选型先算任务成本上季度准备给公司的客服与工单系统做 AI 大模型升级时&#xff0c;团队在选型阶段犯过一个典型错误。当时大家直接参考了市场上几款标杆竞品的选型方案&#xff0c;看到某大厂宣称其最新的 70B 模型在 MMLU 和 GSM8K 榜单上得分第一&#xff0c;支持…

作者头像 李华
网站建设 2026/9/2 10:36:49

Spring AOP切点表达式execution深度解析:从语法到实战避坑指南

1. 从一次线上告警说起&#xff1a;为什么我们需要精确的切点表达式那天下午&#xff0c;我正在处理一个需求&#xff0c;突然钉钉群里弹出一条告警&#xff1a;“用户积分变更服务响应时间超过2秒”。点开监控一看&#xff0c;问题出在一个记录积分变更日志的切面上。这个切面…

作者头像 李华
网站建设 2026/9/3 22:53:27

AI编程Skills机制解析:用结构化工程规范约束代码生成质量

GitHub 上围绕 AI 编程的 Skills 类项目热度很高&#xff0c;类似“不写屎山代码”的标题背后&#xff0c;是大量开发者开始意识到同一个问题&#xff1a;模型生成代码的速度越来越快&#xff0c;但生成结果的质量却经常停留在“能跑”而不是“能维护”。Skills 这种机制&#…

作者头像 李华
网站建设 2026/9/3 21:18:26

ST25D系列NFC动态标签开发全解析:从协议原理到量产调优

做NFC开发这行时间不算短了&#xff0c;从最早接触NXP的NTAG系列&#xff0c;到后来帮客户做工业配置、智能家居标签&#xff0c;再到这两年重点折腾ST25D系列动态标签&#xff0c;踩过的坑攒了满满一箩筐。今天不聊虚的&#xff0c;把ST25D系列NFC动态标签的开发流程、协议要点…

作者头像 李华