【动手学深度学习】2.1. 数据操作
目录
- 2. 预备知识
- 2.1. 数据操作
- 1) 入门
- 2)运算符
- 3)广播机制(broadcasting mechanism)
- 4)索引和切片
- 5)节省内存
- 6)转换为其他Python对象
- 7)小结
2. 预备知识
学习深度学习需掌握以下基础:
-
数据处理:涵盖存储、操作与预处理,核心技能为高效管理表格数据(样本为行,属性为列)。
-
线性代数:矩阵运算是处理多维数据的基础,重点理解基本原理与实现,如矩阵乘法与操作。
-
优化与微积分:通过调整模型参数优化性能,微积分指导参数更新方向,实用工具(如
autograd
)可自动求导简化计算。 -
概率论:用于不确定场景下的推断与预测,提供量化不确定性的数学框架。
-
官方文档利用:熟练查阅框架文档以扩展知识,解决实际问题。
.
2.1. 数据操作
为了能够完成各种数据操作,我们需要某种方法来存储和操作数据。 通常,我们需要做两件重要的事:
-
(1)获取数据;
-
(2)将数据读入计算机后对其进行处理。
首先,我们介绍维数组,也称为张量(tensor)。张量类(在MXNet中为ndarray
, 在PyTorch和TensorFlow中为Tensor
)都与Numpy的ndarray
类似。 但深度学习框架又比Numpy的ndarray
多一些重要功能: 首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算; 其次,张量类支持自动微分。
.
1) 入门
核心概念:
-
张量维度:1轴为向量,2轴为矩阵,更高维度无特定数学名称。
-
形状变换时元素总数不变,
-1
可自动推导维度。 -
初始化方法(全0/1、随机)常用于模型参数初始化。
基础操作:
-
导入库:
import torch
(注意包名为torch
,非pytorch
)。 -
创建张量:
x = torch.arange(12) # 创建整数行向量 [0, 1, ..., 11]
x = torch.arange(12, dtype=torch.float32) # 指定浮点类型
- 形状与元素数:
x.shape # 形状 → torch.Size([12])
x.numel() # 元素总数 → 12
- 重塑形状:
reshape
不改变元素值与总数,支持自动维度计算(-1
):
X = x.reshape(3, 4) # 显式指定形状 → 3行4列矩阵
X = x.reshape(-1, 4) # 自动计算行数 → 同(3,4)
- 全0/全1张量:
torch.zeros((2, 3, 4)) # 形状(2,3,4)的全0张量
torch.ones((2, 3, 4)) # 形状(2,3,4)的全1张量
- 随机初始化(标准正态分布):
torch.randn(3, 4) # 形状(3,4),元素服从N(0,1)
- 直接转换嵌套列表:外层列表对应轴0,内层对应轴1:
torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])# 输出:
tensor([[2, 1, 4, 3],[1, 2, 3, 4],[4, 3, 2, 1]])
.
2)运算符
核心概念:
-
按元素运算:要求输入张量形状相同,支持
+
,-
,*
,/
,**
及标量函数(如exp
)。 -
张量连结:
torch.cat
按指定轴拼接,dim=0
扩展行,dim=1
扩展列。 -
逻辑运算:生成布尔张量,用于条件筛选或掩码操作。
-
聚合操作:如
sum()
对张量所有元素求和,常用于数值计算与损失函数。
(1)按元素运算
- 算术运算符(相同形状张量):
x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2.0, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y # **运算符是求幂运算# 输出
(tensor([ 3., 4., 6., 10.]), tensor([-1., 0., 2., 6.]), tensor([ 2., 4., 8., 16.]), tensor([0.5000, 1.0000, 2.0000, 4.0000]), tensor([ 1., 4., 16., 64.]))
- 一元运算符(如指数运算):
torch.exp(x) # 计算 e^x # 输出:
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])
(2)张量连结
- 沿指定轴拼接(
dim=0
按行,dim=1
按列):
X = torch.arange(12, dtype=torch.float32).reshape(3, 4)
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
- 输出:
# dim=0 结果(6行4列)
tensor([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [ 2., 1., 4., 3.], [ 1., 2., 3., 4.], [ 4., 3., 2., 1.]]) # dim=1 结果(3行8列)
tensor([[ 0., 1., 2., 3., 2., 1., 4., 3.], [ 4., 5., 6., 7., 1., 2., 3., 4.], [ 8., 9., 10., 11., 4., 3., 2., 1.]])
(3)逻辑运算
- 按元素比较(生成布尔张量):
# 以X == Y为例: 对于每个位置,如果X和Y在该位置相等,则新张量中相应项的值为1。 这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。X == Y # 输出
tensor([[False, True, False, True], [False, False, False, False], [False, False, False, False]])
(4)求和操作
- 张量元素总和(返回单元素张量):
X.sum() # 输出:
tensor(66.)
.
3)广播机制(broadcasting mechanism)
原理: 允许不同形状的张量进行按元素运算,自动扩展至相同形状。
-
通过复制元素扩展张量,使其形状一致;
-
扩展通常沿长度为1的轴进行(如标量与张量运算);
示例
a = torch.arange(3).reshape((3, 1)) # 形状 (3,1)
b = torch.arange(2).reshape((1, 2)) # 形状 (1,2)
# 即:
a → tensor([[0], [1], [2]])
b → tensor([[0, 1]]) # 广播过程:
# a 沿列(轴1)复制两次 → 形状变为 (3,2)。
# b 沿行(轴0)复制三次 → 形状变为 (3,2)。# 按元素相加结果:
a + b
# 输出:
tensor([[0, 1], [1, 2], [2, 3]])
.
4)索引和切片
-
元素访问:张量元素通过索引访问,第一个元素索引0,最后一个元素索引-1。
-
切片操作:使用范围索引访问多个元素,如
X[-1]
获取最后一个元素,X[1:3]
获取第2到第3个元素。 -
修改元素:直接通过索引赋值修改张量元素,如
X[1, 2] = 9
。 -
批量修改:通过索引组合批量修改元素,如
X[0:2, :] = 12
修改前两行所有列的值。
示例代码:
X = torch.tensor([[0., 1., 2., 3.], [4., 5., 6., 7.], [8., 9., 10., 11.]]) # 访问最后一个元素
X[-1] → tensor([ 8., 9., 10., 11.]) # 访问第2、3行
X[1:3] → tensor([[ 4., 5., 6., 7.], [ 8., 9., 10., 11.]]) # 修改单个元素
X[1, 2] = 9
# 结果:
tensor([[ 0., 1., 2., 3.], [ 4., 5., 9., 7.], [ 8., 9., 10., 11.]]) # 批量修改前两行所有元素
X[0:2, :] = 12
# 结果:
tensor([[12., 12., 12., 12.], [12., 12., 12., 12.], [ 8., 9., 10., 11.]])
.
5)节省内存
运行一些操作可能会导致为新结果分配内存。 例如,如果我们用Y = X + Y
,我们将取消引用Y
指向的张量,而是指向新分配的内存处的张量。下面介绍一些节省内存的操作:
-
避免不必要内存分配:使用原地操作减少内存分配,如
Y[:] = X + Y
。 -
原地更新操作:使用
X += Y
或X[:] = X + Y
进行原地更新,减少内存开销。 -
内存地址验证:通过
id()
函数验证内存地址是否改变,如检查id(Y) == before
。
示例代码:
# 方法1:通过切片赋值
Z = torch.zeros_like(Y)
Z[:] = X + Y # 不改变Z的内存地址 # 方法2:运算符简写
X += Y # 直接原地更新X,内存地址不变 # 验证内存地址:
before = id(X)
X += Y
id(X) == before → True # 内存地址未变
.
6)转换为其他Python对象
将深度学习框架定义的张量转换为NumPy张量(ndarray
)很容易,反之也同样容易。 torch张量和numpy数组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。
A = X.numpy()
B = torch.tensor(A)type(A), type(B)
# 输出:
(numpy.ndarray, torch.Tensor)
要将大小为1的张量转换为Python标量,我们可以调用item
函数或Python的内置函数。
a = torch.tensor([3.5])a, a.item(), float(a), int(a)
# 输出:
(tensor([3.5000]), 3.5, 3.5, 3)
.
7)小结
深度学习存储和操作数据的主要接口是张量(维数组)。它提供了各种功能,包括基本数学运算、广播、索引、切片、内存节省和转换其他Python对象。
.
声明:资源可能存在第三方来源,若有侵权请联系删除!