11.1 预备知识
11.1.1 数据操作
形状
shape
输出形状
reshape()
更改形状,
-1
表示自适应。默认按行排列,必要时可先改变形状,后转置得到按列排列的结果numel()
元素数量有多少
拼接
torch.cat()
在已有维度上拼接
torch.stack()
torch.vstack()
torch.stack()
在新的维度上堆叠
逐元素操作
传统运算符
+ - * / **
sum()
若为空则对所有元素求和;
dim
指定轴方向,0,1,2...
表示维度从外到内
x = torch.arange(24).reshape(2,3,-1)
x
"""
tensor([[[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]],
[[12, 13, 14, 15],
[16, 17, 18, 19],
[20, 21, 22, 23]]])
"""
x.sum(dim=0)
"""
tensor([[12, 14, 16, 18],
[20, 22, 24, 26],
[28, 30, 32, 34]])
"""
x.sum(dim=1)
"""
tensor([[12, 15, 18, 21],
[48, 51, 54, 57]])
"""
x.sum(dim=2)
"""
tensor([[ 6, 22, 38],
[54, 70, 86]])
"""
广播机制
维度对齐,从尾部(最右边)开始逐维度比较,形状不足的张量在左边补1个维度
维度大小为1的轴自动”复制”以匹配较大尺寸
A = torch.tensor([[1, 2, 3],
[4, 5, 6]]) # (2, 3)
b = torch.tensor([10, 20, 30]) # (3,)
result = A * b
"""
tensor([[ 10, 40, 90],
[ 40, 100, 180]])
"""
# 广播过程:b → (1,3) → (2,3)
# 由于张量b维度为1,A的维度为2,相当于张量b先复制了一行,再与A逐元素相乘
节省内存
对于形如
X=X+Y
的操作,事实上赋值前的X和赋值后的X占用了两个地方的内存(即使变量名相同),建议改为X[:]=X+Y
,这样前后X的内存地址就一致了
11.1.2 自动微分
深度学习框架能够自动计算导数:先将梯度附加到想要计算偏导数的变量上,然后对目标值进行反向传播backward()
,访问得到的梯度。
x = torch.arange(4)
x.requires_grad_(True) # 等价于x = torch.arange(4, requires_grad=True)
x.grad # 默认值为None
y = 2 * torch.dot(x,x)
y.backward()
x.grad
x.grad.zero_() # 变量会累积梯度,在必要时需要清空
对于复合函数y=f(x), z=g(y,x)
,有时想控制y直接计算z关于x的梯度,则需要将y剥离出来。
- 类型转换
类型 | 方法 | 备注 |
---|---|---|
数组->张量 | torch.from_numpy() | 共享内存 |
数组->张量 | torch.tensor() | 仅复制 |
张量->数组 | .numpy() | 共享内存 |
张量->数组 | .clone().numpy() | 仅复制 |
数据框->数组 | .values | 内存高 |
数据框->数组 | .to_numpy(copy=False) | 内存低 |
数组->数据框 | pd.DataFrame() | - |
默认张量在CPU上,若在GPU上,则先将其移到CPU上,再
.cpu().numpy()
存有梯度的张量不能直接转为数组,应
.detach().numpy()
或.detach().cpu().numpy()
11.1.3 加载数据集
- 创建数据集对象
TensorDataset(*tensor)
用于将内存中的多个张量包装为一个数据集对象。
x = torch.arange(12).reshape(3,4) # 特征
y = torch.tensor([0, 1, 0]) # 标签
dataset = TensorDataset(x,y) # 数据集对象
也可根据抽象类Dataset
自定义数据集对象,切记一定要重写__len__()
和__getitem__()
。
# 自定义数据集类
class MyDataset(Dataset):
def __init__(self, X_data, Y_data):
"""
初始化数据集,X_data 和 Y_data 是两个列表或数组
X_data: 输入特征
Y_data: 目标标签
"""
self.X_data = X_data
self.Y_data = Y_data
def __len__(self):
"""返回数据集的大小"""
return len(self.X_data)
def __getitem__(self, idx):
"""返回指定索引的数据"""
x = torch.tensor(self.X_data[idx], dtype=torch.float32) # 转换为 Tensor
y = torch.tensor(self.Y_data[idx], dtype=torch.float32)
return x, y
- 加载数据集
DataLoader()
用于从数据集中加载数据,并支持打乱、划分批次等操作。
loader = DataLoader(
dataset, # 数据集对象
batch_size=128, # 每个批次的样本数
shuffle=True, # 是否打乱数据顺序
sampler=None, # 抽样策略
num_workers=4, # 用于数据加载的进程数
pin_memory=True, # 是否使用固定内存(CUDA)
drop_last=False # 是否丢弃最后的不完整批次
)
- 划分数据集
random_split()
用于将一整个数据集分割为几个不重合的子集。