数据操作
基本概念
N维数组是机器学习和神经网络的主要数据结构
0-d:标量
1-d:向量
2-d:矩阵
3-d:RGB图片(宽×高×通道)
4-d:一个RGB图片批量(批量大小×宽×高×通道)
5-d:一个视频批量(批量大小×时间×宽×高×通道)
数据操作实验
首先需要导入torch
import torch
使用 arange
创建一个行向量 x
。这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的元素(element)。
x = torch.arange(12)
x
通过张量的shape
属性来访问张量(沿每个轴的长度)的形状
x.shape
torch.Size([12])
如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。 因为这里在处理的是一个向量,所以它的shape
与它的size
相同。
x.numel()
12
要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape
函数。
X = x.reshape(3, 4)
X
tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])
有时,我们希望使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。
torch.zeros((2, 3, 4))
torch.ones((2, 3, 4))
有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
torch.randn(3, 4)
tensor([[-0.0135, 0.0665, 0.0912, 0.3212], [ 1.4653, 0.1843, -1.6995, -0.3036], [ 1.7646, 1.0450, 0.2457, -0.7732]])
我们还可以通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值。
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]])
常见的标准算术运算符(+
、-
、*
、/
和 **
)都可以被升级为按元素运算
x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x**y
torch.exp(x)
我们也可以把多个张量连结(concatenate) 在一起
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)
(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.]]), 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.]]))
通过逻辑运算符构建二元张量
X == Y
tensor([[False, True, False, True], [False, False, False, False], [False, False, False, False]])
X.sum()
tensor(66.)
在某些情况下,即使形状不同,我们仍然可以通过调用广播机制(broadcasting mechanism)来执行按元素操作
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a + b
(tensor([[0], [1], [2]]), tensor([[0, 1]]))
运行一些操作可能会导致为新结果分配内存,最好执行原地操作
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
id(Z): 140327634811696 id(Z): 140327634811696
如果在后续计算中没有重复使用X
, 我们也可以使用X[:] = X + Y
或X += Y
来减少操作的内存开销。
before = id(X)
X += Y
id(X) == before
True
数据预处理实现
import os
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv') # 创建文件
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price\n') # 列名
f.write('NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
# 如果没有安装pandas,只需取消对以下行的注释来安装pandas
# !pip install pandas
import pandas as pd
data = pd.read_csv(data_file)
data
NumRooms | Alley | Price | |
---|---|---|---|
0 | NaN | Pave | 127500 |
1 | 2.0 | NaN | 106000 |
2 | 4.0 | NaN | 178100 |
3 | NaN | NaN | 140000 |
注意,“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法和删除法, 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。 在这里,我们将考虑插值法。
通过位置索引iloc
,我们将data
分成inputs
和outputs
, 其中前者为data
的前两列,而后者为data
的最后一列。 对于inputs
中缺少的数值,我们用同一列的均值替换“NaN”项。
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean(numeric_only=True))
print(inputs)
NumRooms Alley
0 3.0 Pave
1 2.0 NaN
2 4.0 NaN
3 3.0 NaN
对于inputs
中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas
可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
inputs = pd.get_dummies(inputs, dummy_na=True,dtype=int) # 将非数值型数据转换为数值型数据
print(inputs)
NumRooms Alley_Pave Alley_nan
0 3.0 1 0
1 2.0 0 1
2 4.0 0 1
3 3.0 0 1
import torch
X = torch.tensor(inputs.values)
y = torch.tensor(outputs.values)
X, y
(tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64), tensor([127500, 106000, 178100, 140000]))
练习
创建包含更多行和列的原始数据集。
import os
import pandas as pd
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
f.write('id,MSSubClass,MSZoning,LotFrontage,NumRooms,Alley,Price\n') # 列名
f.write('1,60,RL,65,NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,70,NA,70,2,NA,106000\n')
f.write('3,NA,NA,80,4,NA,178100\n')
f.write('4,NA,NA,90,NA,NA,140000\n')
original_data = pd.read_csv(data_file)
original_data
- 删除缺失值最多的列。
# 删除缺失值最多的列
most_missing = original_data.isna().sum().idxmax() # 返回缺失值最多的列名
data = original_data.drop(columns=[most_missing]) # 删除缺失值最多的列
data
- 将预处理后的数据集转换为张量格式。
#将预处理后的数据集转换为张量格式。
import torch
data = pd.get_dummies(data, dummy_na=True)
# pd.get_dummies: 将类别数据转换为独热编码
# dummy_na=True: 为缺失值(NA)也创建一个列
data=torch.tensor(data.to_numpy(dtype=float))#将数据转换为张量
data
tensor([[1.0000e+00, 6.0000e+01, 6.5000e+01, nan, 1.2750e+05, 1.0000e+00, 0.0000e+00], [2.0000e+00, 7.0000e+01, 7.0000e+01, 2.0000e+00, 1.0600e+05, 0.0000e+00, 1.0000e+00], [3.0000e+00, nan, 8.0000e+01, 4.0000e+00, 1.7810e+05, 0.0000e+00, 1.0000e+00], [4.0000e+00, nan, 9.0000e+01, nan, 1.4000e+05, 0.0000e+00, 1.0000e+00]], dtype=torch.float64)
线性代数
标量
标量由只有一个元素的张量表示
import torch
x = torch.tensor(3.0)
y = torch.tensor(2.0)
x + y, x * y, x / y, x**y
(tensor(5.), tensor(6.), tensor(1.5000), tensor(9.))
向量
可以将向量视为标量值组成的列表
x = torch.arange(4)
x
tensor([0, 1, 2, 3])
通过张量的索引来访问任一元素
x[3]
访问张量的长度
len(x)
只有一个轴的张量,形状只有一个元素
x.shape
torch.Size([4])
矩阵
对称和反对称矩阵
正定矩阵:对于任何非零向量x,都有:$x^T A x $> 0
通过指定两个分量m和n来创建一个形状为m×n的矩阵
A = np.arange(20).reshape(5, 4)
A
A.T #转置
张量
X = torch.arange(24).reshape(2, 3, 4)
X
给定具有相同形状的任意两个张量,任何按元素二元运算的结果都将是相同形状的张量
A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
B = A.clone() # 通过分配新内存,将A的一个副本分配给B
A, A + B
两个矩阵的按元素乘法称为哈达玛积(Hadamard product)(数学符号⊙)
将张量乘以或加上一个标量不会改变张量的形状,其中张量的每个元素都将与标量相加或相乘。
a = 2
X = torch.arange(24).reshape(2, 3, 4)
a + X, (a * X).shape
计算其元素的和
x = torch.arange(4, dtype=torch.float32)
x, x.sum()
指定张量沿哪一个轴来通过求和降低维度
A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
(tensor([40., 45., 50., 55.]), torch.Size([4]))
一个与求和相关的量是平均值(mean或average)
A.mean(), A.sum() / A.numel()
A.mean(axis=0), A.sum(axis=0) / A.shape[0]
计算总和或均值时保持轴数不变(便于广播)
sum_A = A.sum(axis=1, keepdims=True)
sum_A
某个轴计算A
元素的累积总和
A.cumsum(axis=0)
点积是相同位置的按元素乘积的和
y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)
矩阵向量积 $\mathbf{A}\mathbf{x}$ 是一个长度为 $m$ 的列向量,其第 $i$ 个元素是点积 $\mathbf{a}_i^\top \mathbf{x}$
A.shape, x.shape, torch.mv(A, x)
我们可以将矩阵-矩阵乘法A×B看作是简单地执行m次矩阵-向量积,并将结果拼接在一起,形成一个n×m矩阵
B = torch.ones(4, 3)
torch.mm(A, B)
求范数
u = torch.tensor([3.0, -4.0])
torch.norm(u)
求L1范数(绝对值之和)
torch.abs(u).sum()
矩阵的弗罗贝尼乌斯范数(Frobenius norm)是矩阵元素平方和的平方根:
torch.norm(torch.ones((4, 9)))
按特定轴求和
#按特定轴求和
import torch
x=torch.arange(12).reshape(3,4)
x
x.sum(axis=0)#按列求和
x.sum(axis=1)#按行求和
x.sum()#所有元素求和
#三维
x=torch.arange(24).reshape(2,3,4)
x.sum(axis=0,keepdim=True)#按列求和,keepdim=True保持维度不变
x.sum(axis=0)#按列求和
x.sum(axis=1)#按行求和
x.sum(axis=2)#按页求和
矩阵计算
梯度
将导数拓展到向量
自动求导
快速浏览即可