从零开始上手深度学习【1】数据操作与数据预处理

【从零开始上手深度学习】


声明

虽然现在市面上有很多关于深度学习原理和代码讲解的视频或者教材,但是大多是着重讲深度学习的原理,代码部分设计很少,还有一部分则讲的是比较高深的代码,对于从零开始的DL小白来说较难上手。对比过现有的众多教程,我找到了DataWhale在2024年三月的组队学习系列“动手学深度学习”(活动已经结束),决定使用其开源的学习资料(包括教材和配套视频),将自己的学习过程整理为笔记,以便和我一样不爱看视频的同学进行学习,也便于在一些没有播放视频的条件的场合下进行DL的系统学习。

本次学习使用的教材是李沐老师的《动手学深度学习》,开源地址:https://zh-v2.d2l.ai/

一、环境配置

关于环境,大家可以访问以下地址进行学习:

https://blog.csdn.net/qq_45956730/article/details/126600028

在本系列中,本人将使用Jupyter Notebook作为主力编程工具,涉及到的.ipynb文件都会上传到本人的github代码库中,地址:

https://github.com/Palpitate74/DL/tree/main

二、数据操作

In: import torch #导入torch的库

张量表示一个数值组成的数组,这个数组可能有多个维度

In: 
x = torch.arange(12)
x

其中:

  • x = torch.arange(12) #表示生成一个从0到11的tensor数组,一共12个数
  • x #jupyter中可以不使用print()函数,直接输入变量名就可以完成打印,但每次只能打印一行

对应的输出为

Out: 
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

张量(tensor)是 PyTorch 中的核心数据结构,类似于 NumPy 数组,但可以在 GPU 上加速计算,常用于深度学习和其他机器学习任务。

我们可以通过张量的shape属性访问其形状和元素总数

In: 
x.shape

其中:

  • shape函数返回x的属性,即一个大小为12的torch

对应的输出为

Out: 
torch.Size([12])

numel()函数仅返回x中元素的数量

In: 
x.numel()
Out: 
12

如果要改变一个张量的形状而不改变元素数量和值,我们可以调用reshape函数

In: 
X = x.reshape(3,4)
X

其中:

  • reshape()函数将x的形状从一个1×12的矩阵改写为一个3×4的矩阵
Out: 
tensor([[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]])

需要注意的是,调用reshape()函数时不会向系统申请新的内存空间,所以对调用后的变量进行操作时,会对调用前的变量造成改变

In: 
a = torch.arange(12)
b = a.reshape(3,4)
b[:] = 1 #[:]表示将b中的所有行上的值都赋值1
a, b #同一行可以打印多个变量
Out: 
(tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
 tensor([[1, 1, 1, 1],
   	     [1, 1, 1, 1],
 	     [1, 1, 1, 1]]))

此外还有:

In: 
torch.zeros((2,3,4))#表示生成2个大小为3×4的全零矩阵,维度为3
Out: 
tensor([[[0., 0., 0., 0.],
     	 [0., 0., 0., 0.],
         [0., 0., 0., 0.]],

        [[0., 0., 0., 0.],
         [0., 0., 0., 0.],
     	 [0., 0., 0., 0.]]])

全1矩阵类推ones()函数

我们可以通过提供包含数值的python列表,为张量中元素赋值

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

常见的标准算术运算符( + 、 − 、 ∗ 、 / 、 ∗∗ ),都可以被升级为按元素运算

In: 
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#均为逐元素计算,例如x+y为[1.0+2, 2+2, 4+2, 8+2]
Out: 
(tensor([ 3.,  4.,  6., 10.]),
 tensor([-1.,  0.,  2.,  6.]),
 tensor([ 2.,  4.,  8., 16.]),
 tensor([0.5000, 1.0000, 2.0000, 4.0000]),
 tensor([ 1.,  4., 16., 64.]))

也可以使用指数函数运算,exp(t)函数表示 e t e^t et,同样逐元素进行运算

In: 
torch.exp(x)
Out: 
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])

连接多个张量

In: 
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)

其中:

  • 参数dtype = torch.float32表示张量中的数值类型为float单精度型
  • cat为连接两个张量的函数,参数dim=0表示按行连接(增加行),dim=1表示按列连接
Out: (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中数值的相等关系,相等返回True,否则返回False*

In: 
X == Y
Out: 
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])

sum()函数表示对张量中所有的values(值)求和

In: 
X.sum()
Out: 
tensor(66.)

广播机制

In: 
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
Out: 
(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))

维度相同,形状不同,按每个维度的最大进行扩张(广播机制),即a,b中行的最大值为3行,列的最大值为2列,则a,b均复制为3×2的张量

In: 
a+b
Out: 
tensor([[0, 1],
       	[1, 2],
        [2, 3]])

[,]为切片操作,逗号前表示行切片,逗号后表示列切片,如[1:3,2:]表示取出第二行到四行(从第0行开始进行切片操作,左闭右开区间)和第3列之后的所有元素

可以使用[-1]选择最后一行元素,用[1:3]选择第二行和第三行元素

In: 
X[-1], X[1:3]
Out: 
(tensor([ 8.,  9., 10., 11.]),
 tensor([[ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]]))

使用切片操作将元素写入矩阵

In: 
X[1, 2] = 9#表示在X矩阵的第二行第三列的位置写入9
X
Out: 
tensor([[ 0.,  1.,  2.,  3.],
       	[ 4.,  5.,  9.,  7.],
        [ 8.,  9., 10., 11.]])

若要将第1行和第二行的所有元素赋值12

In: 
X[0:2,:] = 12
X
Out: 
tensor([[12., 12., 12., 12.],
        [12., 12., 12., 12.],
        [ 8.,  9., 10., 11.]])

运行一些操作会可能会导致为新结果分配内存

In: 
before = id(Y)
Y = Y + X
id(Y) == before

其中:

  • id类似C++里的指针,表示内存地址
  • id(Y) == before逻辑关系表达式,表示Y=Y+X后的Y和一开始的Y的内存地址不相同
Out: 
False

我们可以借助中间变量,以执行原地操作

In: 
Z = torch.zeros_like(Y)
print('id(Z):',id(Z))
Z[:] = X + Y#表示X+Y中的内容赋值给Z中所有的行
print('id(Z):',id(Z))

其中:

  • zeros_like()表示张量的形状等均与Y相同,元素值全为0
  • ’ ’ 里是原封不动打印出来的内容,“,”后面不加’ ’ 的表示打印变量的values
Out: 
id(Z): 1392415756176
id(Z): 1392415756176

如果后续计算中没有重复使用X,我们也可以使用X[:]= X + YX += Y来减少操作的内存开销

In: 
before = id(X)
X += Y #或X[:] = X + Y
id(X) == before
Out: 
True

转换为NumPy张量

In: 
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
Out: 
(numpy.ndarray, torch.Tensor)

其中前者表示是numpy中的ndarray,后者为torch中的tensor

将大小为1的张量转换为Python标量

In: 
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)

其中:

  • item() 函数通常用于从一个单元素数组中提取出该元素,并返回其对应的 Python 标量类型
Out: 
(tensor([3.5000]), 3.5, 3.5, 3)

三、数据预处理

创建一个人工数据集,储存在csv文件中(逗号分隔value)

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

其中:

  • os.makedirs()用于递归的创建目录(包括所有必要的父级目录)。如果路径中某些目录不存在,会自动创建这些目录。
  • os.path.join(‘…’, ‘data’):将data 路径组合起来,生成一个相对路径。这里的 … 表示上一级目录,因此os.path.join(‘…’, ‘data’) 生成的路径是“上一级目录中的 data 文件夹”。
  • exist_ok=True:如果要创建的目录已经存在,设置 exist_ok=True 参数就不会引发错误。这确保了即使目录已经存在,程序也能继续执行。
  • data_file:将生成的路径赋值给变量 data_file,表示 house_tiny.csv 文件的路径。
  • open(data_file, ‘w’):打开文件 data_file 进行写入操作。data_file 是之前定义的 house_tiny.csv 文件的路径,表示 …/data/house_tiny.csv
  • ‘w’ 模式:表示以写入模式打开文件。如果文件不存在,会创建这个文件;如果文件已经存在,它会被清空,然后重新写入内容。
  • with 语句:这是上下文管理器,用于确保文件在打开后能够正确关闭。即使在写入过程中发生错误,with 语句也会确保文件被正确关闭。
  • as f:将打开的文件对象赋值给变量 f,方便后续操作对文件进行写入。
  • f.write():将指定的字符串写入文件 f 中。每次调用 write() 方法,都会将字符串写入文件并继续保持文件打开。
  • ‘NumRooms,Alley,Price\n’:这一行写入了CSV文件的列名,即表示该文件中包含3个字段,分别是“房间数”(NumRooms)、“巷道”(Alley)、“价格”(Price)。
    • \n:表示换行符,写完这一行后换到下一行继续写入。
  • ‘NA,Pave,127500\n’:这一行是数据样本,表示第一个房屋记录:
    • “NA” 表示房间数缺失值(NA)。
    • “Pave” 表示巷道是铺设好的(Paved)。
    • “127500” 表示价格是127500。
  • ‘2,NA,106000\n’:第二个房屋记录:
    • “2” 表示房间数为2。
    • “NA” 表示巷道数据缺失。
    • “178100” 表示价格是178100。
  • 第三、第四行数据类推

导入pandas库,读取csv文件

Pandas 是一个功能强大的 Python 数据分析库,专为处理和分析结构化数据而设计。它基于 NumPy 构建,并提供了高效的数据操作工具,适合处理类似于 Excel 表格和 SQL 数据库的表格数据。Pandas 的核心数据结构是 DataFrame 和 Series,分别表示二维和一维数据结构。

In: 
import pandas as pd
data = pd.read_csv(data_file)
data

使用pandas库中的read_csv()函数读取csv文件,生成一个Dataframe,命名为data

DataFrame 是 Pandas 库中的核心数据结构,用于表示二维的表格数据,类似于 Excel 表格或 SQL 数据表。它由行和列组成,每一列可以包含不同类型的数据(如整数、浮点数、字符串等)。DataFrame 提供了丰富的功能来进行数据的选择、过滤、清洗、转换、聚合和导入导出操作,支持多种数据源(如 CSV、Excel、SQL 数据库等),并且能够高效地处理大规模数据,是数据分析和处理的重要工具。

Out:

IndexNumRoomsAlleyPrice
0NaNPave127500
12.0NaN106000
24.0NaN178100
3NaNNaN140000

为了处理缺失的数据NaN,典型的方法包括插值和删除,这里我们考虑插值
我们先展示第一种方法

In: 
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
  • 当然这是报错的(可能是版本关系,李沐老师配套的视频里这句代码是对的,本人运行的时间为2024.9.22,可能版本不支持这样的操作)。
  • 报错的原因是inputs包含NumRoomsAlley两列数据,其中Alley是非数值型数据,而在使用mean()函数(求平均值)时不支持非数值型数据。

于是我们把NumRooms数据单独拿出来进行求均值操作,即先使用切片把第一列取出来,均值插值之后再与第二列进行连接

In: 
inputs, outputs = data.iloc[:, 0:1], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
inputs = pd.concat([inputs, data.iloc[:,1]],axis=1)
print(inputs)
  • 其中axis=1表示按列进行连接(即连接后会增加列的数量)

Out:

IndexNumRoomsAlley
03.0Pave
12.0NaN
24.0NaN
33.0NaN

对于inputs中的类别值或离散值,我们将“NaN”视为一个类别

In:
inputs = pd.get_dummies(inputs, dummy_na = True, dtype=int)#dtype=int
inputs

其中:

  • get_dummies() 函数用于将分类变量(如字符串或布尔值,本题中为“NaN”)转换为独热编码(one-hot encoding),也就是将每个类别值转换成一列二进制数(0或1)。这样做是为了让分类变量能够用于机器学习模型等场景中。
  • inputs是要转换的输入数据(通常是一个 DataFrame),它包含了可能的分类变量。inputs 中的分类列将被转换成多个二进制列。
  • dummy_na=True 会为缺失值(NaN)创建一个额外的哑变量列(即Alley_nan列)。这可以帮助处理含有缺失值的数据。生成的列会显示是否该位置是缺失值(NaN),值为1表示是缺失值,0表示不是。
  • dtype=int这个参数指定输出的哑变量的数据类型为 int。默认情况下,哑变量的值是浮点数(float),但在这里将其转换为整数(int)。这样可以节省内存,并且在某些场景下使用整数可能更合适。

Out:

IndexNumRoomsAlley_PaveAlley_nan
03.010
12.001
24.001
33.001

现在inputs和outputs中所有条目都是数值类型,它们可以转换为张量格式

In:
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
X, y

其中:

  • inputs.values 提取 inputs 中的所有值,返回一个 NumPy 数组。
  • outputs.values 提取 outputs 中的所有值,也返回一个 NumPy 数组。
  • torch.tensor() 是 PyTorch 中用于将数据转换为张量(tensor)的函数。
Out:
(tensor([[3., 1., 0.],
         [2., 0., 1.],
         [4., 0., 1.],
         [3., 0., 1.]], dtype=torch.float64),
 tensor([127500, 106000, 178100, 140000]))

总结

本文对深度学习中会使用到的一些常见的数据操作和预处理的方法进行了基础的了解,后续我们会逐渐开始学习深度学习中使用到的各类模型,以帮助我们更好的理解深度学习的内核。
感谢阅读!希望本文对你理解深度学习有所帮助。如有收获,欢迎点赞、评论与分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值