第一节 数据操作+数据处理

本系列文章为李沐老师《动手学深度学习》Pytorch版实践学习笔记,相关课程教学、书籍、代码均为开源,可通过以下链接参考学习:

跟李沐学AI的个人空间-跟李沐学AI个人主页-哔哩哔哩视频 (bilibili.com)

前言 — 动手学深度学习 2.0.0 documentation (d2l.ai)

安装 — 动手学深度学习 2.0.0 documentation (d2l.ai)

一、数据操作

  1. N维数组:N维数组是机器学习和神经网络的主要数据结构。其中0维为标量,例如一个类别;1维为向量,例如一个特征向量;2维为矩阵,例如一个样本的特征矩阵;3维例如一张RGB图片(宽×长×RGB通道);4维例如一个RGB图片批量(批量大小patch×宽×长×RGB通道);5维例如一个视频批量(批量大小×宽×高×通道)
  2. 创建数组需要:①形状;②每个元素的数据类型;③每个元素的值
  3. 深度学习框架比Numpy的ndarray多一些重要功能:首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算;其次,张量类支持自动微分。 这些功能使得张量类更适合深度学习。

数据操作实现

  1. 导入torch:import torch

  2. 张量tensor表示一个数值组成的数组,这个数组可能有多个维度。可以通过张量的shape属性【注意是属性,不是调用函数】来访问张量的形状。调用numel()函数(number of elements)可以查看张量中元素的总数,永远是标量。

  3. 若要改变张量的形状,可以调用reshape()函数,注意该函数生成的是一个新的张量:

    我们不需要通过手动指定每个维度来改变形状。 也就是说,如果我们的目标形状是(高度,宽度), 那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。 我们也可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)x.reshape(3,-1)来取代x.reshape(3,4)b = a.reshape()是对a的引用,操作b会改变a。

    在 PyTorch 中,view() 和 reshape() 方法都用于改变张量(Tensor)的维度布局,它们的主要区别在于处理非连续内存情况下的表现以及内部实现细节:

    1. 视图共享内存
      • view() 和 reshape() 在处理连续内存(contiguous memory)时表现一致,它们都会创建一个新的张量引用,该张量共享原张量的底层数据存储区域,而不会复制数据。
      • 当源张量已经是连续的,view() 和 reshape() 返回的结果都将直接指向原张量的同一块内存空间,更改新张量的值会直接影响原始张量的值。
    2. 非连续内存处理
      • view() 方法在遇到非连续张量时(张量(Tensor)的“连续性”指的是其底层数据在内存中是连续存储的,即张量元素在内存中的物理地址是连续排列的,就像一块连续的内存区域)会抛出错误,因为它假定输入张量是连续的,这样才能保证在改变形状时不改变内存布局。
      • reshape() 方法在尝试对非连续张量进行形状改变时,会先检查新的形状是否与原张量的元素数量一致,并且能否在不破坏数据的前提下转换为连续布局。如果不行,reshape() 会隐式地复制数据,创建一个新的连续张量。
    3. 兼容性和推荐做法
      • PyTorch 的官方文档通常推荐使用 view() 方法,因为它在大多数情况下足够安全,而且性能更好(因为它无需检查连续性并避免不必要的数据复制)。
      • 为了确保 view() 方法成功执行,可以在调用 view() 之前先使用 contiguous() 方法来强制张量变为连续,但这可能导致数据复制。

    总的来说,对于大部分情况,特别是已知张量是连续的情况下,view() 和 reshape() 几乎可以互换使用。但在不确定张量连续性时,使用 reshape() 可能更为稳健,因为它能够处理非连续张量的情况,尽管有可能带来额外的性能开销。在最新的 PyTorch 版本中,有时这两个方法的行为几乎一致,但在旧版本中可能存在上述差异。

  4. 可以调用zeors()和ones()函数等生成具有指定元素值的张量:

    在 PyTorch 中,torch.zeros(2,3,4) 和 torch.zeros((2,3,4)) 实际上是等价的,它们没有区别。两者都是用来创建一个形状为 (2, 3, 4) 的全零张量(Tensor),即该张量共有 2 层,每层有 3 行,每行有 4 列,总共包含 2 * 3 * 4 个元素,并且所有元素初始值都为零。

  5. 通过提供包含数值的Python列表或嵌套列表可以为张量中的每个元素赋值,使用tensor()函数构造向量:

  6. 常见的标准运算符(+、-、*、/、**)都可以被升级为按元素运算。

  7. 可以使用cat()函数将多个向量连结在一起,其中dim表示在第几维度进行合并,0为行,1为列,即第几层中括号

  8. 可以通过逻辑运算符构建二元张量,按元素进行比较:

  9. sum()函数对张量中所有元素进行求和,会产生一个只有一个元素的张量

    1. 若要按照某一维度进行求和,使用X.sum(axis=…)。如果要保持最终结果的维度数不变,在sum()函数中使用参数keepdims=True,便于相同维度使用广播机制。
    2. X.cumsum(axis=…)函数为按维度进行累加求和,也就是维度数不变,将累加结果更新到当前计算到的位置:
  10. 即使形状不同,也可以通过调用广播机制来执行按元素操作:

  11. 和列表一样,可以通过指定索引读取或者修改元素。

  12. 执行原地操作举例:

    对元素进行操作时地址不变,或者使用命令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)读入数据

  1. 人工创建一个数据集:

    import os
    
    os.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')
    

  2. 使用pandas库的read_csv()函数读取数据,data数据为DataFrame类型:

(2)处理缺失值

为了处理缺失的数据,典型的方法包括插值法删除法]其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。

  1. 通过位置索引iloc(index location)将data分为inputs和outputs两部分,前两列为输入,最后一列为输出。对于inputs中缺失的数值,使用同一列的平均值代替。

  2. 对于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,表示该观测值在这个类别上有缺失。

  3. 若要删除具有最多缺失值的列,则需要先统计每列缺失值的个数,找出缺失最多的列,并使用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位浮点数。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值