Skip to content

第 2 章 创建张量

学习目标

  • 掌握从列表创建张量:torch.tensor
  • 掌握规则序列创建方法:arangelinspace
  • 掌握占位与特形张量:zerosonesfulleye
  • 掌握随机张量:randrandnrandint 与固定种子
  • 理解 dtypedevice 两个创建时就能指定的属性
  • 理解 torch.tensor 拷贝与 torch.from_numpy 共享内存的区别

2.1 从列表创建:torch.tensor

上一章已经用过 torch.tensor(列表)。它接收任何嵌套的 Python 序列,生成对应的张量:

python
import torch

a = torch.tensor([1, 2, 3])                 # 一维
b = torch.tensor([[1, 2, 3], [4, 5, 6]])    # 二维
print(a)
print(b)

输出:

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

创建时可以直接指定 dtypedevice:

python
import torch

c = torch.tensor([1, 2, 3], dtype=torch.float32, device='cpu')
print(c)
print(c.dtype, c.device)

输出:

tensor([1., 2., 3.])
torch.float32 cpu

device(设备)是张量存放的位置:cpucuda:0(GPU)。CPU 上的张量和 GPU 上的张量不能直接参与同一运算,第 11 章会系统处理设备问题,现在只需要知道创建时可以指定。

2.2 规则序列:arangelinspace

torch.arange(起点, 终点, 步长) 生成等差数列,含起点、不含终点:

python
import torch

print(torch.arange(10))        # 0 到 9
print(torch.arange(2, 10, 2))  # 2 开始,步长 2,小于 10

输出:

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

torch.linspace(起点, 终点, 个数) 生成等间隔的指定个数的数,含起点和终点:

python
import torch

print(torch.linspace(0, 1, 5))

输出:

tensor([0.0000, 0.2500, 0.5000, 0.7500, 1.0000])

arangelinspace 的区别:前者给步长、不问个数,后者给个数、不管步长。浮点运算中 arange 可能因舍入误差少一个数,需要精确等分时优先用 linspace

2.3 占位与特形张量

这些方法按形状生成张量,形状传一个元组,如 (2, 3) 表示 2 行 3 列:

python
import torch

print(torch.zeros(3))          # 全 0,长度 3
print(torch.zeros((2, 3)))     # 全 0,2 行 3 列
print(torch.ones((2, 2)))      # 全 1
print(torch.full((2, 3), 7))   # 全部填 7
print(torch.eye(3))            # 3×3 单位矩阵

输出:

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

注意两点:

  • zerosoneseye 默认是 float32;full 的 dtype 跟随填充值(填 7 得到 int64,填 7.0 得到 float32)。
  • 3 和传 (3,) 效果相同,但二维必须传元组 (2, 3),不能写 torch.zeros(2, 3)(见常见错误)。

torch.empty((2, 3)) 也会分配一块 2×3 的内存,但不初始化里面的值,打印出来是随机垃圾数。它只在确定马上会被覆盖时使用,新手先不要用。

2.4 随机张量

三个最常用的随机生成器:

  • torch.rand(形状):均匀分布,取值在 [0, 1)
  • torch.randn(形状):标准正态分布,均值 0、标准差 1。
  • torch.randint(最小, 最大, 形状):整数均匀分布,含最小、不含最大。
python
import torch

torch.manual_seed(42)
print(torch.rand(3))
print(torch.randn(3))
print(torch.randint(0, 10, (3,)))

输出:

tensor([0.8823, 0.9150, 0.3829])
tensor([-0.8293, -1.6137, -0.2147])
tensor([4, 0, 4])

torch.manual_seed(42) 的作用是固定随机种子:只要设置同一个种子,后续随机数序列完全一样。本书所有涉及随机的示例都先设种子,保证输出可复现——这也是你写深度学习代码时让实验可复现的标准做法。

2.5 与 NumPy 互操作

PyTorch 与 NumPy 可以互相转换。两种创建方式行为不同:

python
import torch
import numpy as np

a = np.arange(3)
t1 = torch.tensor(a)   # 拷贝:后续互不影响
a[0] = 99
print("torch.tensor 是拷贝:", t1)

b = np.arange(3)
t2 = torch.from_numpy(b)   # 共享内存:改一边,另一边跟着变
b[0] = 99
print("from_numpy 共享内存:", t2)

输出:

torch.tensor 是拷贝: tensor([0, 1, 2])
from_numpy 共享内存: tensor([99,  1,  2])

反向转换是 张量.numpy():

python
import torch

t = torch.arange(3)
n = t.numpy()
print(n, type(n))

输出:

[0 1 2] <class 'numpy.ndarray'>

t.numpy() 也是共享内存。共享内存意味着零拷贝、速度快,但改动一侧会影响另一侧;需要独立数据时用 .clone()torch.tensor(...)

动手实践

  1. torch.linspace 生成 0 到 10 之间(含端点)的 11 个数,打印并核对首尾。
  2. 创建 3×4 的全 1 张量,打印 shapedtype,解释为什么 dtype 是浮点。
  3. torch.full((2, 4), 3.5) 创建张量,打印 dtype;再创建 torch.full((2, 4), 3),对比两者 dtype。
  4. 设种子 42,连续调用两次 torch.rand(3),记录输出;重新设种子 42 再调用一次,观察结果是否相同。
  5. 创建一个 NumPy 数组,用 torch.from_numpy 转成张量后修改原数组,观察张量变化;再用 torch.tensor 重复一遍,对比差异。

常见错误

错误写法现象原因
torch.zeros(2, 3)TypeError: zeros() received an invalid combination of arguments二维形状要传元组 torch.zeros((2, 3))
torch.tensor(1, 2, 3)TypeError: tensor() takes 1 or 2 positional arguments but 3 were given列表要加方括号:torch.tensor([1, 2, 3])
torch.arange(0, 1, 0.1) 直接用于精确等分最后一个元素可能是 0.9 而不是 1.0浮点步长有舍入误差;等分用 linspace
期望 torch.tensor(a) 与 NumPy 数组共享内存修改一边另一边不变torch.tensor 默认拷贝;要共享用 torch.from_numpy
忘记 torch.manual_seed(42)每次运行随机结果不同随机数默认不可复现;训练/示例要设种子
torch.empty((2, 3)) 打印出来的值当真实数据出现乱数empty 不初始化内存,只能用于确定马上被覆盖的场合

章末练习

基础

  1. torch.arange 生成 [0, 5, 10, 15, 20] 并打印。
  2. 创建 2×4 的全零张量,打印它的 shapedtype
  3. torch.linspace 生成 0 到 1 之间的 10 个数,打印第一个和最后一个元素。

提高

  1. 设种子 7,生成 5 个标准正态分布的随机数;再设种子 7,生成 5 个,验证两组完全一样,并解释原因。
  2. 创建 torch.eye(4),打印对角线上的元素(提示:用 torch.diagonal 或索引,第 3 章会教索引)。如果不允许用新方法,先打印整个矩阵观察规律。

挑战

  1. 写一个函数 make_batch(rows, cols),返回一个形状 (rows, cols)、元素为 [0, 1) 均匀随机数的 float32 张量,并保证设置相同种子时结果一致。
  2. 创建一个 NumPy 二维数组并转成张量;在张量上做 +1 运算,再打印原 NumPy 数组,解释结果,并说明哪种转换方式会共享内存。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. torch.arange(2, 10, 2) 的结果是?
    • A. tensor([2, 4, 6, 8, 10])
    • B. tensor([2, 4, 6, 8])
    • C. tensor([2, 4, 6])
    • D. tensor([0, 2, 4, 6, 8])
  2. torch.linspace(0, 1, 5) 的结果是?
    • A. tensor([0., 0.2, 0.4, 0.6, 0.8, 1.])
    • B. tensor([0., 0.25, 0.5, 0.75, 1.])
    • C. tensor([0., 0.5, 1.])
    • D. 报错
  3. torch.zeros((2, 3)).shape 的结果是?
    • A. torch.Size([2, 3])
    • B. torch.Size([3, 2])
    • C. torch.Size([6])
    • D. torch.Size([2])
  4. torch.zeros(3).dtype 的结果是?
    • A. torch.int64
    • B. torch.float64
    • C. torch.float32
    • D. torch.bool
  5. torch.full((2,), 7).dtype 的结果是?
    • A. torch.float32
    • B. torch.int64
    • C. torch.float64
    • D. torch.uint8
  6. torch.eye(3) 生成的是?
    • A. 全 1 的 3×3 矩阵
    • B. 全 0 的 3×3 矩阵
    • C. 对角线为 1、其余为 0 的 3×3 矩阵
    • D. 长度为 3 的向量
  7. torch.manual_seed(42) 的作用是?
    • A. 让随机数更快
    • B. 固定随机种子,结果可复现
    • C. 让随机数更均匀
    • D. 清除所有随机数
  8. 下列哪种方式创建的张量与 NumPy 数组共享内存?
    • A. torch.tensor(arr)
    • B. torch.from_numpy(arr)
    • C. torch.zeros(arr.shape)
    • D. arr.copy()
  9. 创建 2 行 3 列全 1 张量的正确写法是?
    • A. torch.ones(2, 3)
    • B. torch.ones((2, 3))
    • C. torch.ones([2, 3])
    • D. B 和 C 都正确
  10. 关于 torch.randn(3),正确的说法是?
    • A. 生成 3 个 [0, 1) 均匀随机数
    • B. 生成 3 个标准正态分布随机数
    • C. 生成 3 个 [0, 10) 整数
    • D. 生成 3×3 矩阵