本系列文章为李沐老师《动手学深度学习》Pytorch版实践学习笔记,相关课程教学、书籍、代码均为开源,可通过以下链接参考学习:
跟李沐学AI的个人空间-跟李沐学AI个人主页-哔哩哔哩视频 (bilibili.com)
前言 — 动手学深度学习 2.0.0 documentation (d2l.ai)
安装 — 动手学深度学习 2.0.0 documentation (d2l.ai)
一、数据操作
- N维数组:N维数组是机器学习和神经网络的主要数据结构。其中0维为标量,例如一个类别;1维为向量,例如一个特征向量;2维为矩阵,例如一个样本的特征矩阵;3维例如一张RGB图片(宽×长×RGB通道);4维例如一个RGB图片批量(批量大小patch×宽×长×RGB通道);5维例如一个视频批量(批量大小×宽×高×通道)
- 创建数组需要:①形状;②每个元素的数据类型;③每个元素的值
- 深度学习框架比Numpy的
ndarray
多一些重要功能:首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算;其次,张量类支持自动微分。 这些功能使得张量类更适合深度学习。
数据操作实现
-
导入torch:
import torch
-
张量tensor表示一个数值组成的数组,这个数组可能有多个维度。可以通过张量的
shape
属性【注意是属性,不是调用函数】来访问张量的形状。调用numel()
函数(number of elements)可以查看张量中元素的总数,永远是标量。 -
若要改变张量的形状,可以调用
reshape()
函数,注意该函数生成的是一个新的张量:我们不需要通过手动指定每个维度来改变形状。 也就是说,如果我们的目标形状是(高度,宽度), 那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。 我们也可以通过
-1
来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)
或x.reshape(3,-1)
来取代x.reshape(3,4)
。b = a.reshape()
是对a的引用,操作b会改变a。 -
可以调用zeors()和ones()函数等生成具有指定元素值的张量:
在 PyTorch 中,
torch.zeros(2,3,4)
和torch.zeros((2,3,4))
实际上是等价的,它们没有区别。两者都是用来创建一个形状为(2, 3, 4)
的全零张量(Tensor),即该张量共有 2 层,每层有 3 行,每行有 4 列,总共包含 2 * 3 * 4 个元素,并且所有元素初始值都为零。 -
通过提供包含数值的Python列表或嵌套列表可以为张量中的每个元素赋值,使用
tensor()
函数构造向量: -
常见的标准运算符(+、-、*、/、**)都可以被升级为按元素运算。
-
可以使用
cat()
函数将多个向量连结在一起,其中dim表示在第几维度进行合并,0为行,1为列,即第几层中括号: -
可以通过逻辑运算符构建二元张量,按元素进行比较:
-
sum()
函数对张量中所有元素进行求和,会产生一个只有一个元素的张量:- 若要按照某一维度进行求和,使用X.sum(axis=…)。如果要保持最终结果的维度数不变,在sum()函数中使用参数keepdims=True,便于相同维度使用广播机制。
- X.cumsum(axis=…)函数为按维度进行累加求和,也就是维度数不变,将累加结果更新到当前计算到的位置:
-
即使形状不同,也可以通过调用广播机制来执行按元素操作:
-
和列表一样,可以通过指定索引读取或者修改元素。
-
执行原地操作举例:
对元素进行操作时地址不变,或者使用命令
X += Y
;使用X = X + Y
地址会改变。X += Y: 这是一个就地操作(in-place operation),意味着它直接修改了张量X的内容而没有创建新的张量。由于张量数据是在同一块内存区域上进行更新,所以其内存地址不会改变。
X = X + Y: 在默认情况下,这个表达式会创建一个新的张量来存储X与Y相加的结果。这是因为Python中的赋值语句实际上是对引用的重新绑定,而不是原地修改对象内容。因此,在执行
X = X + Y
时,首先计算X + Y
得到一个新张量,然后将变量X指向这个新创建的张量,原来的X所指向的内存区域不再被X引用,从而导致X的内存地址发生了变化。在某些情况下,特别是在使用深度学习框架时,为了优化内存使用并减少数据复制,用户可能会选择使用就地操作以提高性能。不过,需要注意的是,频繁使用就地操作可能会影响梯度计算,因为它们破坏了原始数据,无法回溯到之前的计算状态。因此,在实际应用中,需要根据具体需求权衡是否采用就地操作。
二、数据预处理
为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。在Python中常用的数据分析工具中,我们通常使用pandas
软件包,pandas
可以与张量兼容。
(1)读入数据
-
人工创建一个数据集:
import osos.makedirs(os.path.join('..', 'data'), exist_ok=True) # 如果在当前文件夹下,只指明data参数即可 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库的
read_csv()
函数读取数据,data数据为DataFrame类型:
(2)处理缺失值
为了处理缺失的数据,典型的方法包括插值法和删除法,]其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。
-
通过位置索引iloc(index location)将data分为inputs和outputs两部分,前两列为输入,最后一列为输出。对于inputs中缺失的数值,使用同一列的平均值代替。
-
对于inputs中缺失的类别值或者离散值,将”NAN”视为一个类别。由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”,
pandas
可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。在 pandas 库中,
pd.get_dummies()
函数用于将分类变量(通常是字符串类型)转换为虚拟/指示/独热编码形式的数值列。通过这种方式,非数值型类别特征可以被转化为机器学习模型可直接处理的形式。参数
dummy_na=True
意味着包括一个额外的列来表示原始数据集中是否存在缺失值(NaN)。如果某个类别的值是 NaN,则会在生成的 dummy 变量中对应的位置上设置为 1,表示该观测值在这个类别上有缺失。 -
若要删除具有最多缺失值的列,则需要先统计每列缺失值的个数,找出缺失最多的列,并使用
drop()
函数删除:missing_counts = data.isnull().sum() # 统计每列缺失值的数量 # missing_counts = data.isnull().sum(axis=1) 对行统计 print(missing_counts) # Series,索引表示列名 most_missing_column = missing_counts.idxmax() # 返回最多的列,.idxmax() 方法用于返回具有最大值(在这个场景下是最大缺失值数量)的索引 print(most_missing_column) data = data.drop(most_missing_column, axis = 1) print(data)
(3)转换为张量格式
由于inputs和outputs为DataFrame形式,要获取数值内容,需要先转换为numpy数组。
Python默认使用64位浮点数,但是对于深度学习而言比较慢,之后需要转换为32位浮点数。