数据操作 + 数据预处理
来源 《动手学习深度学习》 Mu Li
数据操作
N维数组是所有机器学习和神经网络以及深度学习使用到的主要的数据结构
3D 最简单的表现形式就是一张图片,一个RGB的图片。
四维就是n个三维的数组放在一起。因为在深度学习的训练过程中,通常不是一张张图片读入的,而是通过一次性读入多张图片(例如128张图片读入,即batch = 128)
五维的话其实就是一个视频的批量,就是有很多的图片,但是还有一个时间的维度。
上图中
- 第一个表示我需要访问的是第一行(行是从0开始的)第二列(列是从0开始的)的元素,这里只有一个元素,即7。
- 第二个表示我需要访问第一行所有列的元素,(: 表示该行该列的所有元素)即5 6 7 8
- 第三个写错了,应该是 [:,1]
- 第四个表示访问子域1:3理解为 [1:3),即第一行和第二行,1:表示拿到从第一列开始的后面所有的元素
- 第五个表示跳着访问,::3表示从第0行开始,每三行一条,::2表示从第0列开始,每两列一跳。
数据操作实现
首先导入 torch
import torch
张量表示一个由数值组成的数组,这个数组可能有多个维度
可以通过张量的shape
属性来访问张量(沿每个轴的长度)的形状
如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。
因为这里在处理的是一个向量,所以它的shape
与它的size
相同。
[要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape
函数。]
例如,可以把张量x
从形状为(12,)的行向量转换为形状为(3,4)的矩阵。这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。注意,通过改变张量的形状,张量的大小不会改变。
我们不需要通过手动指定每个维度来改变形状。也就是说,如果我们的目标形状是(高度,宽度),那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。在上面的例子中,为了获得一个3行的矩阵,我们手动指定了它有3行和4列。幸运的是,我们可以通过-1
来调用此自动计算出维度的功能。即我们可以用x.reshape(-1,4)
或x.reshape(3,-1)
来取代x.reshape(3,4)
。
有时,我们希望[使用全0、全1、其他常量,或者从特定分布中随机采样的数字]来初始化矩阵。我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下:
同样,我们可以创建一个形状为(2,3,4)
的张量,其中所有元素都设置为1。代码如下:
有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。
例如,当我们构造数组来作为神经网络中的参数时,我们通常会随机初始化参数的值。以下代码创建一个形状为(3,4)的张量。其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
我们还可以[通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值]。在这里,最外层的列表对应于轴0,内层的列表对应于轴1。
我们在外面再嵌套一层列表,并打印其shape,结果如下:
在创建数组后,肯定是需要对上述数组进行一系列的算术运算,这些运算都是按照元素进行的。(下图1.0是为了创建一个浮点数,要是不写.0则会变成一个整数)
(“按元素”方式可以应用更多的计算),包括像求幂这样的一元运算符。
[我们也可以把多个张量连结(concatenate)在一起],把它们端对端地叠起来形成一个更大的张量。我们只需要提供张量列表,并给出沿哪个轴连结。
下面的例子分别演示了当我们沿行(轴-0,形状的第一个元素)和按列(轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。我们可以看到,第一个输出张量的轴-0长度(
6
6
6)是两个输入张量轴-0长度的总和(
3
+
3
3 + 3
3+3);第二个输出张量的轴-1长度(
8
8
8)是两个输入张量轴-1长度的总和(
4
+
4
4 + 4
4+4)。
有时,我们想[通过逻辑运算符构建二元张量]。
以X == Y
为例:对于每个位置,如果X
和Y
在该位置相等,则新张量中相应项的值为1。这意味着逻辑语句X == Y
在该位置处为真,否则该位置为0。
[对张量中的所有元素进行求和,会产生一个单元素张量,可以认为是标量。]
对于形状不同的张量,PyTorch也提供了广播机制进行解决,但是初次接触的话,是很容易犯错的:
广播机制的工作方式如下:
- 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
- 对生成的数组执行按元素操作。
在大多数情况下,将沿着数组中长度为1的轴进行广播,例子如下:
由于a
和b
分别是
3
×
1
3\times1
3×1和
1
×
2
1\times2
1×2矩阵,如果让它们相加,它们的形状不匹配。我们将两个矩阵广播为一个更大的
3
×
2
3\times2
3×2矩阵,如下所示:矩阵a
将复制列,矩阵b
将复制行,然后再按元素相加。
需要注意的是:广播规则允许不同形状的张量进行算术运算,但要求某些维度的大小必须相等或其中一个为1。具体如下:
索引和切片
像在任何其他Python数组中一样,张量中的元素可以通过索引访问。与任何Python数组一样:第一个元素的索引是0,最后一个元素索引是-1;可以指定范围以包含第一个元素和最后一个之前的元素。
如下所示,我们[可以用[-1]
选择最后一个元素,可以用[1:3]
选择第二个和第三个元素]:
[除读取外,我们还可以通过指定索引来将元素写入矩阵。]
当然也可以[为多个元素赋值相同的值,只需要索引所有元素,然后为它们赋值。]
例如,[0:2, :]
访问第1行和第2行,其中“:”代表沿轴1(列)的所有元素。虽然这里是矩阵的索引,但这也适用于向量和超过2个维度的张量。
节省内存
[运行一些操作可能会导致为新结果分配内存]。
例如,如果我们用Y = X + Y
,我们将取消引用Y
指向的张量,而是指向新分配的内存处的张量。
在下面的例子中,我们用Python的id()
函数演示了这一点,它给我们提供了内存中引用对象的确切地址。运行Y = Y + X
后,我们会发现id(Y)
指向另一个位置。这是因为Python首先计算Y + X
,为结果分配新的内存,然后使Y
指向内存中的这个新位置。
这可能是不可取的,原因有两个:
- 首先,不想总是不必要地分配内存。在机器学习中,可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,希望原地执行这些更新;
- 如果不原地更新,其他引用仍然会指向旧的内存位置,这样某些代码可能会无意中引用旧的参数。
不过,执行原地操作非常简单。
可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = <expression>
。为了说明这一点,我们首先创建一个新的矩阵Z
,其形状与另一个Y
相同,使用zeros_like
来分配一个全 0 0 0的块。
如果在后续计算中没有重复使用X
,也可以使用X[:] = X + Y
或X += Y
来减少操作的内存开销。
实际上,python里面最常见的是numpy:
数据预处理实现
数据预处理:对于一个原始数据,如何读取进来是的能够通过机器学习的方法进行处理。
读取数据集
举一个例子,首先(创建一个人工数据集,并存储在CSV(逗号分隔值)文件)../data/house_tiny.csv
中。以其他格式存储的数据也可以通过类似的方式进行处理。下面我们将数据集按行写入CSV文件中。
import os
# 创建一个目录(如果不存在的话)
# os.path.join() 用于组合路径组件
# '..' 表示上一级目录
# 'data' 是我们要创建的子目录
# exist_ok=True 表示如果目录已经存在,则不会抛出异常
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
# 定义CSV文件的路径
# 使用os.path.join()来组合目录和文件名
data_file = os.path.join('..', 'data', 'house_tiny.csv')
# 使用with语句打开文件,确保文件在使用后自动关闭
# 'w' 表示以写入模式打开文件,如果文件已存在则会被覆盖
with open(data_file, 'w') as f:
# 写入列名
f.write('NumRooms,Alley,Price\n')
# 写入数据行
# 第一行数据,其中NumRooms字段是缺失的(用NA表示),Alley字段是Pave,Price是127500
f.write('NA,Pave,127500\n')
# 第二行数据,NumRooms是2,Alley字段是缺失的(用NA表示),Price是106000
f.write('2,NA,106000\n')
# 第三行数据,NumRooms是4,Alley字段是缺失的(用NA表示),Price是178100
f.write('4,NA,178100\n')
# 第四行数据,其中NumRooms和Alley字段都是缺失的(用NA表示),Price是140000
f.write('NA,NA,140000\n')
要[从创建的CSV文件中加载原始数据集],导入pandas
包并调用read_csv
函数。该数据集有四行三列。其中每行描述了房间数量(“NumRooms”)、巷子类型(“Alley”)和房屋价格(“Price”)
处理缺失值
注意,“NaN”项代表缺失值。[为了处理缺失的数据,典型的方法包括插值法和删除法,]
其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。在(这里,我们将考虑插值法)。
通过位置索引iloc
,我们将data
分成inputs
和outputs
,其中前者为data
的前两列,而后者为data
的最后一列。对于inputs
中缺少的数值,我们用同一列的均值替换“NaN”项。
[对于inputs
中的类别值或离散值,我们将“NaN”视为一个类别。]由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”,pandas
可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
对于所有条目都是数值类型,可以转换为张量格式。
数据操作 QA 部分感悟
tensor是一个数学上的概念,是一个张量,有明确的数学上的定义
array是一个计算机的语言。无数学上的定义