掘金 人工智能 06月02日 12:33
D2L系列-第一篇-2.1数据操作 and 2.2数据预处理
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文是《动手学深度学习》学习笔记的开篇,涵盖了深度学习中的核心概念和实践。文章首先介绍了使用PyTorch进行数据操作的基础,包括张量的创建、运算、索引和切片,以及节省内存的技巧。随后,文章深入探讨了数据预处理的方法,包括处理缺失值和将数据转换为张量格式,为后续的深度学习模型构建奠定了基础。通过Jupyter Notebook的实践,作者分享了代码和笔记,旨在帮助读者快速入门深度学习。

🧱 **张量基础**: 深度学习中,张量(Tensor)是核心数据结构,类似于多维数组,支持GPU计算和自动微分。文章介绍了使用`torch.tensor()`创建张量,以及`reshape()`改变张量形状。理解张量是理解深度学习框架的关键。

➕➖➗ **张量运算**: 张量之间的加减乘除等运算是逐元素进行的,要求参与运算的张量形状需要对齐。文章讲解了广播机制,当两个张量形状不完全匹配时,通过扩展维度来使其能够进行运算,简化了代码。

💾 **内存管理**: PyTorch中,原地操作可以节省内存。文章介绍了两种原地更新的方法:切片和`+=`操作。同时,将张量转换为其他Python对象,如NumPy数组和Python标量,方便数据处理和分析。

📁 **数据预处理**: 深度学习中,数据预处理至关重要。文章介绍了使用Pandas读取CSV文件,处理缺失值(使用均值填充),以及将类别型变量转换为数值型(使用`get_dummies()`)。最后,将预处理后的数据转换为张量,为后续模型训练做好准备。

今天是2025年6月1日,从今天起,我打算新开一个系列,将自己学习《动手学深度学习》的心得和笔记发在掘金上面。

目标是日更,就以这种形式,今天是2.1和2.2,明天就应该是2.3和2.4。我计算过,只有以这种速度更新,才能在两个月内搞定这本书。

本篇文章包括后续的所有文章,都是我在Jupyter Notebook中先自己过了一遍,然后把笔记和代码以markdown的形式放到掘金上。一方面是为了分享,另一方面也是为了督促自己完成。

其实我自己知道真正每天按照计划走很难,达标不容易,但是如果能坚持走下来,相信自己人工智能的实践水平能提升一个档次。

2.1.1 入门

numpy里面的数组只能在CPU上操作,而且不支持自动微分。深度学习框架(Pytorch, Tensorflow, MXnet)支持GPU计算和自动微分因此想做深度学习只用numpy是不够的。

import torchx = torch.arange(12)x

X = x.reshape(3, 4) 只改变形状,不改变元素的值和数量
既然数量不变,因此reshape以后的数量要和原来对的上,否则会报错

X = x.reshape(3, 4)X

无论是一维数组还是二维数组,还是其他维数组,在深度学习框架里面,数组统称为张量Tensor

f"string"这种结构,具体的变量值就放在{}里面,\n放在外面即可

y = torch.arange(22)Y = y.reshape(2, 11)print(f"{y} \n\n {Y}")

无论使不使用-1,用reshape的关键都是前后元素的数量要对齐。

Y2 = y.reshape(-1, 11)Y3 = y.reshape(2, -1)print(f"{Y2}\n\n{Y3}")

torch.tensor()才是本源像什么torch.arange(), torch.randn(), torch.zeros(), torch.ones()这些只是对torch.tensor()的快捷操作罢了。

torch.tensor([[1, 2], [3, 4]])

2.1.2 运算符

''' tensor的加减乘除也是得元素数量的对齐tensor级别的加减乘除本质上就是针对其中相应位置元素的加减乘除。'''x = torch.tensor([3.4, 2.1, 5, 6, 7])y = torch.tensor([1, 2, 3, 4, 5])ans1, ans2, ans3, ans4, ans5 = x+y, x-y, x*y, x/y, x**y print(f"{ans1}\n\n{ans2}\n\n{ans3}\n\n{ans4}\n\n{ans5}")

''' 事实证明,reshape(3, 4)和reshape((3, 4))效果是一样的。'''x = torch.arange(12).reshape(3, 4)x2 = torch.arange(12).reshape((3, 4))print(f"{x}\n\n{x2}")

x = torch.arange(12).reshape(3, 4)y = torch.zeros(3, 4)ans1, ans2 = torch.cat((x, y), dim = 0), torch.cat((x, y), dim = 1)print(f"{ans1}\n\n{ans2}")

''' 0竖1横 cat是contactnate的缩写,意思是拼接dim = 0是竖着拼,dim = 1是横着拼注意x和y要用一个小括号括起来,相当于cat()只用两个参数,一个是(x, y),还有一个是dim'''x = torch.arange(12).reshape(3, 4)y = torch.zeros(3, 1)ans = torch.cat((x, y), dim = 1)print(ans)

''' 使用torch.cat()的使用只要拼接处对齐就行,没必要要求tensor形状完全一样。但是使用x == y判断两个tensor中每个位置上的元素是否对应相等,那就形状必须得完全一样了。'''x = torch.tensor([1, 2, 3, 4])y = torch.tensor([1, 1, 3, 3])x == y

2.1.3 广播机制

import torchx = torch.tensor([[1, 1, 1], [2, 2, 2]])y = torch.arange(6).reshape(2, 3)print(f"{x+y}\n\n{x-y}")

''' 广播机制有两种应用场景:1. 至少有一个维度要对齐2. 行和列相加'''x = torch.arange(15).reshape(3, 5)y = torch.ones(5)x+y

2.1.4 索引和切片

''' 操作逻辑基本上和传统的数组是一样的。'''x = torch.arange(12).reshape(3, 4)x

''' 所以tensor的索引有两种方式,一种是x[1, 2],另一种是x[1][2]这两种效果是等价的。'''x[1, 1] = 100x

2.1.5 节省内存

x = torch.tensor([[1, 2], [1, 3]])y = torch.ones(2, 2)before_y = id(y)y = x + yafter_y = id(y)print(f"{before_y} \n\n{after_y}")before_y == after_y

''' 原地更新有两种方法:1. 切片2. +='''x = torch.tensor([[1, 2], [1, 3]])y = torch.ones(2, 2)before_y = id(y)y += xafter_y = id(y)print(f"{before_y} \n\n{after_y}")before_y == after_y

x = torch.tensor([[1, 2], [1, 3]])y = torch.ones(2, 2)before_y = id(y)y[:, :] = x + yafter_y = id(y)print(f"{before_y} \n\n{after_y}")before_y == after_y

2.1.6 转换为其他Python对象

path = []def dfs(self, root, target_node):    path.append(root)    if root.val == target_node.val:        return True                     if root.left: dfs(self, root.left, target_node)    if root.right: dfs(self, root.right, target_node)    path.pop()    return Falsedef dfs(self, root, target_node, path):    if not root:        return False    path.append(root)    if root.val == target_node.val:        return True    if dfs(self, root.left, target_node, path) or dfs(self, root.right, target_node, path):        return True    path.pop()    return False
import numpy as npimport torch''' list不能直接通过numpy()转换为numpy.ndarray, 但是tensor可以直接通过numpy()转换为numpy.ndarray如果想把tensor转换为numpy.ndarray,就直接用torch.tensor'''X = torch.tensor([1, 2, 3])X_numpy = X.numpy()X_torch = torch.tensor(X_numpy)print(f"{type(X_numpy)}\n\n{type(X_torch)}")

''' int(a)的效果是直接下取整。只有当tensor中只含有一个元素时,才能直接用int()和float()'''a = torch.tensor([3.8])a, a.item(), float(a), int(a)

2.1 练习

X = torch.arange(20).reshape(4, 5)Y = torch.ones(4, 5)print(f"{X>Y}\n\n\n{X<Y}")

问题:广播机制的使用到底可以适用于哪些情况?

回答:从右到左比较:每一对维度之间必须满足以下三个条件中的任意一个

    相等其中一个为一不存在
''' 我大概能体会为什么要从右到左比较了。因为这三个条件:等;二选一为1;不存在从右到左和从左到右是不一样的。更深层次的原因是从里往外,内层必须对的上,外层可以扩展'''X = torch.ones(3, 4, 5)Y = torch.ones(1, 5)X+Y


上半截是2.1数据操作,下半截是2.2数据预处理

2.2.1 读取数据集

os.path.join()会根据不同的操作系统创建路径。

os.path.join()不会去创建一个文件或文件夹,创建文件夹用os.makedirs(),创建文件用 with open

data的地位应该是和章节并列的

import osos.makedirs(os.path.join("..", "data"), exist_ok = True)data_file = os.path.join("..", "data", "house_tiny.csv")

用os.path.join()指定路径就是为了使代码在windows和linux之间通用

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')

读csv文件最有效的方法就是pandas里面的read_csv

在用with open()往csv文件里面写东西的时候,第一个写入的就是列名,剩下的就是具体的值。

写入的使用是NA,用pd.read_csv()识别的使用会识别成NaN, NaN代表缺失值

import pandas as pd data = pd.read_csv(data_file)print(data)

2.2.2 处理缺失值

csv文件被read_csv以后,就成了pandas里面的DataFrame

iloc[slice1, slice2],slice1指定行,slice2再指定列

iloc的参数可以是切片,还可以是具体的数字

如果你用inputs.mean()去填充NaN,那么只会影响数值列,不会影响别的列

单列的dataframe会被视为series,列名在下方;多列的dataframe就是dataframe,列名在上方

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]inputs = inputs.fillna(inputs.mean())print(inputs)print()print(outputs)

pd.get_dummies() 根据值的不同生成不同的列,新值是不同值的有无。

dummy_na = True 的意思是NaN也看作是不同的值中的一个dummy_na就决定了是否有Alley_nan这一行。如果dummy_na = True,那么Alley_nan就会单独作为一行。但是如果dummy_na = False,那么Alley_nan就没有这一行

get_dummies()只会对object类型有用,对数值不起作用

inputs = pd.get_dummies(inputs, dummy_na = True) print(inputs)

2.2.3 转换为张量格式

数值类型才能转换为张量形式,因此必须先把Object对象,比如字符串用get_dummies改成数值形式再去转换为tensor

pandas里面有一个函数:to_numpy()可以把dataframe转换为numpynumpy又可以进一步通过torch.tensor转换为tensor

import torch X = torch.tensor(inputs.to_numpy(dtype = float))Y = torch.tensor(outputs.to_numpy(dtype = float))print(f"{X}\n\n{Y}")

练习

1.创建包含更多行和列的原始数据集

定义csv文件时,直接加后缀.csv就行

往csv文件里面逐行写内容,分隔一定要用英文逗号,中文逗号识别不出来,无法起到分隔效果。

太细节了。一般来说写代码的时候英文逗号后面还会额外空一格,但是在往CSV文件里面写东西的时候就不能空格了因为 NA, 这种写法会导致NA无法被正确得被识别为NaN,只有前后无空格才能被识别为NaN

import osimport pandas as pdfile_path = os.path.join("..", "data", "2.2数据预处理_练习.csv")with open(file_path, "w") as f:    f.write("物品,价格,年龄\n")    f.write("iphone10,7999,5\n")    f.write("篮球,NA, 4\n")    f.write("戴尔笔记本,5000,4\n")    f.write("倍思电子笔,NA,NA\n")data = pd.read_csv(file_path)data

2. 删除缺失值最多的列

方法一:肉眼看,直接删除

df2 = df1.drop(columns = "balabalabala")通过这种方式可以删除某列

data = data.drop(columns = "价格")data

方法二:写代码自动判断哪一列的NaN是最多的

isna() 缺失了就是True, 没有缺失就是False

is_NaN = data.isna()NaN_num = is_NaN.sum()Most_NaN_column = NaN_num.idxmax()data = data.drop(columns = Most_NaN_column)data 

3. 将预处理后的数据集转换为张量

data = data.fillna(data.mean())data

data = pd.get_dummies(data)data

ts = torch.tensor(data.to_numpy(dtype = int))ts

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

PyTorch 深度学习 张量 数据预处理
相关文章