04动手学深度学习(李沐)-数据操作+数据预处理-超详细笔记

本节视频链接:

  1. p1 https://www.bilibili.com/video/BV1CV411Y7i4?p=1&vd_source=901b5111a53e52641fb11df13be3b7d1
  2. p2 https://www.bilibili.com/video/BV1CV411Y7i4?p=2&vd_source=901b5111a53e52641fb11df13be3b7d1

p1:

1.N维数组

N维数组是机器学习和神经网络的主要数据结构

  1. 0dim(标量):1.0:一个类别
  2. 1dim(向量):[1.0,2.7,3.4]:一个特征向量
  3. 2dim(矩阵)(矩阵是二维张量,张量不一定是矩阵):[[1.0,2.7,3.4][5.0,0.2,4.6][4.3,8.5,0.2]]:一个样本——特征矩阵

在这里插入图片描述

2.创建数组

创建数组需要:

  1. 形状:例如3*4矩阵
  2. 每个元素的数据类型:例如32位浮点数
  3. 每个元素的值。例如全是0,或者随机数

3.访问元素

  1. 访问一个元素:[1,2]:访问第2行第3个元素

  2. 访问一行元素:[1,:]:访问第2行的所有列(就是访问第2行元素)

  3. 访问一列元素:[:,1]:访问第2列的所有行(就是访问第2列元素)

  4. 访问子区域:[1:3,1:]:访问第2到3(左闭右开)行,第2列开始往后的所有列
    在这里插入图片描述

  5. 访问子区域:[::3,::2]:访问第0行到最后一行,每3行取一次,访问第0列到最后一列,每2列取一次
    在这里插入图片描述

p2:数据操作

导入torch库

import torch
  1. 张量:表示一个数值组成的数组,这个数组可能有很多个维度
x=torch.arange(12)
print(x)
#tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
  1. 通过shape属性来访问张量的形状
print(x.shape)
#torch.Size([12])
  1. 张量中元素的总数(永远是一个标量)
print(x.numel())
#12
  1. 通过reshape函数改变张量的形状而不改变元素数量和元素值
y=x.reshape(3,4)
print(y)
tensor([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
  1. 使用0、全1、其他常量或者从特定分布中随机采样的数字
x=torch.zeros((2, 3, 4))
print(x)
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.]]])
x=torch.ones(2, 3, 4)
print(x)
tensor([[[1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]],

        [[1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]]])

关于x=torch.zeros((2, 3, 4))x=torch.ones(2, 3, 4)的区别:

  • 在PyTorch中,torch.ones函数用于创建一个填充了1的张量。对于你提到的两个用法,x=torch.ones(2,3,4)x=torch.ones((2,3,4)),它们在功能上是等效的,没有实质性的区别。

  • torch.ones(2,3,4)中的参数是以逗号分隔的多个整数,表示张量的形状。这种方式比较直观,将形状的各个维度依次传递给函数。

  • torch.ones((2,3,4))中的参数是一个元组 (2,3,4),表示张量的形状。使用元组的方式可以将形状以一个整体的方式传递给函数,这在一些情况下更加方便,特别是当形状较复杂或者通过变量动态指定形状时。

  • 两种用法最终都会创建一个形状为(2, 3, 4)的张量,并且将其中的元素都设置为1。所以,在这种情况下,可以根据个人的喜好和代码的可读性来选择使用哪种方式。

  1. 通过提供包含数值的Python列表(或嵌套列表)来为所需张量中的每个元素赋值
x=torch.tensor([[1,2,3],[4,5,6],[7,8,9]])
print(x)
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])

如果再加一个[]就变成3

x=torch.tensor([[[1,2,3],[4,5,6],[7,8,9]]])
print(x.shape)
torch.Size([1, 3, 3])
  1. 常见的标准算术运算符(+、-、*、/、**)都可以升级为按元素运算
x=torch.tensor([1,2,3,4.0])
y=torch.tensor([2,2,2,2])
print(x+y)
print(x-y)
print(x*y)
print(x/y)
print(x**y)
tensor([3., 4., 5., 6.])
tensor([-1.,  0.,  1.,  2.])
tensor([2., 4., 6., 8.])
tensor([0.5000, 1.0000, 1.5000, 2.0000])
tensor([ 1.,  4.,  9., 16.])
  1. 指数运算
x=torch.tensor([1,2,3,4.0])
print(torch.exp(x))
tensor([ 2.7183,  7.3891, 20.0855, 54.5981])
  1. 把多个张量拼接在一起
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
print(torch.cat((x, y), dim=0))

dim=0表示在y轴方向上拼接

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.]])
y=torch.tensor([[2.0,1,4,3,],[1,2,3,4],[4,3,2,1]])
print(torch.cat((x, y), dim=1))

dim=1表示x轴方向上的拼接

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.]])
  1. 通过逻辑运算符构建二元张量
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]])
print(y==x)
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])
  1. 即使形状不同,我们仍然可以通过调用广播机制来执行按元素操作
a=torch.arange(3).reshape((3,1))
b=torch.arange(2).reshape((1,2))
print(a)
print(b)
tensor([[0],
        [1],
        [2]])
tensor([[0, 1]])
print(a + b)

a复制成3*2的矩阵

tensor([[0],[0]
        [1],[1]
        [2],[2]])

b复制成3*2的矩阵

tensor([[0, 1]
        [0, 1]
        [0, 1]])

然后再相加,得

tensor([[0, 1],
        [1, 2],
        [2, 3]])
  1. [-1]访问最后一个元素
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
print(x[-1])

访问最后一行

tensor([ 8.,  9., 10., 11.])
  1. [1:3]访问第2个和第3个元素
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
print(x[1:3])

访问第2行和第3

tensor([[ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.]])
  1. 通过索引将指定的元素写入矩阵
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
x[1,2]=9
print(x)

2行第3个元素改为9

tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  9.,  7.],
        [ 8.,  9., 10., 11.]])
  1. 为多个元素赋相同的值
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
x[0:2,:]=12
print(x)

1行和第2行,所有列,赋值为12

tensor([[12., 12., 12., 12.],
        [12., 12., 12., 12.],
        [ 8.,  9., 10., 11.]])
  1. 运行一些操作可能会导致为新结果分配内存
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]])
before=id(y)
y=y+x
print(id(y) == before)

id()是取地址操作

False

新的yid不等于以前的,产生了一个新的y,因为这里y=y+x的等式左右都边使用到了y,变量名重复使用了。所以不是原地操作

  1. 执行原地操作
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]])
z=torch.zeros_like(y)
print('id(z):',id(z))
z[:]=x+y
print('id(z):',id(z))

z=torch.zeros_like(y)表示zyshape和数据类型是一样的,所有的元素值为0

这里必须是z[:]=,该操作只改变了z中的元素值,未改变z的地址

id(z): 1515049846816
id(z): 1515049846816
  1. 原地操作
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]])
before=id(x)
x+=y
print(id(x) == before)
True
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]])
before=id(x)
x[:]=x+y
print(id(x) == before)
True

x[:]=x+y只改变了值

当执行 x = x + y 时,它实际上是创建了一个新的张量对象来存储 x + y 的结果,并将该新张量赋值给变量 x。因此,x 引用的是一个全新的张量对象,其身份标识发生了变化。

相比之下,x[:] = x + y 是一种原地操作。它将 x + y 的结果存储回 x 张量,而不创建新的张量对象。因此,x 仍然引用同一个张量对象,其身份标识保持不变。

  1. 转换为Numpy张量
import numpy
x=torch.arange(12,dtype=torch.float32).reshape((3,4))
a=x.numpy()
b=toorch.tensor(a)
print(type(a))
print(type(b))
<class 'numpy.ndarray'>
<class 'torch.Tensor'>
  1. 将大小为1的张量转换为Python标量
a=torch.tensor([3.5])
print(a,a.item(),float(a),int(a))
tensor([3.5000]) 3.5 3.5 3

P3:数据预处理

  1. 创建一个人工数据集,并存储在csv(逗号分隔值)文件中
import torch
import numpy
import os
import pandas as pd
os.makedirs(os.path.join('D:\PycharmProject\TorchStudy', 'data'), exist_ok=True)
data_file = os.path.join('D:\PycharmProject\TorchStudy', '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')
data = pd.read_csv(data_file)
print(data)
  NumRooms Alley    Price
0       NA  Pave  127500 
1        2   NaN   106000
2        4   NaN   178100
3       NA   NaN   140000

这段代码的主要功能是创建一个名为 house_tiny.csvCSV文件,并写入一些数据。

  1. 首先,os.makedirs(os.path.join('D:\PycharmProject\TorchStudy', 'data'), exist_ok=True) 用于创建一个名为 data 的文件夹,其相对路径是上一级目录('D:\PycharmProject\TorchStudy)下的 dataexist_ok=True 表示如果文件夹已经存在,不会引发错误。

  2. 接下来,data_file = os.path.join(''D:\PycharmProject\TorchStudy', 'data', 'house_tiny.csv') 将文件路径 'D:\PycharmProject\TorchStudy/data/house_tiny.csv 存储在变量 data_file 中。

  3. 然后,使用 open(data_file, 'w') as f 打开文件 data_file,以写入模式('w')。这将创建一个文件对象 f,可以通过该对象进行写入操作。

  4. 在 with 语句的代码块中,进行以下写入操作:
    f.write('NumRooms,Alley,Price\n') 写入一行包含列名的标题行。
    f.write('N,Pave,127500 \n ') 写入第一个数据样本的行,包含三个值:NumRooms= N,Alley= Pave,Price= 127500。
    f.write(' 2,NA,106000\n ') 写入第二个数据样本的行,包含三个值:NumRooms= 2,Alley= NA,Price= 106000。
    f.write(' 4,NA,178100\n ') 写入第三个数据样本的行,包含三个值:NumRooms= 4,Alley= NA,Price= 178100。
    f.write('NA,NA,14000o\n ') 写入第四个数据样本的行,包含三个值:NumRooms= NA,Alley= NA,Price= 140000。
    通过这些写入操作,数据文件 house_tiny.csv 被创建并填充了一些数据。每一行表示一个数据样本,包含三个特征(NumRooms、Alley、Price)和对应的值。

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:
  1. 首先通过 os.makedirs 创建了一个名为 data 的文件夹(如果该文件夹不存在的话),然后将文件路径 house_tiny.csv 存储在变量 data_file 中。

  2. 下来使用 open(data_file, 'w') 打开文件,模式设置为写入模式。这将创建一个空的 house_tiny.csv 文件,如果该文件已经存在,会被覆盖。

  3. 因此,运行代码时,文件夹 data 和文件 house_tiny.csv 会被自动创建,然后数据会被写入 house_tiny.csv 文件中。

with open(data_file, 'w') as f: 是Python中用于处理文件的常见语法,它被称为上下文管理器(Context Manager)。这种用法可以确保在处理完文件后,无论是否发生异常,都会自动关闭文件,释放相关资源。

具体解释如下:

  1. open(data_file, 'w') 打开一个文件,返回一个文件对象。data_file 是要打开的文件的路径,'w' 表示以写入模式打开文件。

  2. as f 将文件对象赋值给变量 f,以便在代码块中使用。

  3. 冒号表示开始一个代码块,下面的代码将在文件对象 f 的上下文中执行。

  4. with 代码块中,可以执行读取或写入文件的操作,例如使用 f.write() 写入数据。

  5. 当代码块结束时,无论是否发生异常,文件对象 f 将自动关闭,确保文件资源被正确释放。

使用 with open() as f 的好处是,不需要手动调用 f.close() 来关闭文件。当代码块退出时,文件会自动关闭,即使发生异常也不会影响文件的关闭操作。这种方式更加简洁和安全,同时也减少了出错的可能性。


2. 为了处理缺失的数据,典型的方法包括插值和删除,这里我们将考虑插值
机器学习就是为了处理缺失的数据(预测未来)

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean(numeric_only=True))
print(inputs)
   NumRooms Alley
0       3.0  Pave
1       2.0   NaN
2       4.0   NaN
3       3.0   NaN

data.iloc[:, 0:2] 表示从 data 数据框中选取所有行和第 0 列至第 1 列(总共两列)的数据,作为 inputs 的值。
data.iloc[:, 2] 表示从 data 数据框中选取所有行和第 2 列的数据,作为 outputs 的值。
inputs.fillna(inputs.mean()) 使用 inputs 中的均值来填充缺失值。
fillna() 是一个Pandas函数,用于填充缺失值。
这里使用 inputs.mean() 计算 inputs 列的均值,并将缺失值用均值填充。
numeric_only=True 参数表示仅考虑数值类型的列来计算均值。

对于inputs中的类别值或离散值(不是数值),我们将‘NaN’视为一个类别

inputs=pd.get_dummies(inputs,dummy_na=True)
print(inputs)
   NumRooms  Alley_Pave  Alley_nan
0       3.0        True      False
1       2.0       False       True
2       4.0       False       True
3       3.0       False       True

pd.get_dummies() 函数用于对分类变量进行独热编码(One-Hot Encoding),创建虚拟变量。
独热编码是将分类变量转换为二进制向量的过程,使得机器学习模型能够更好地处理分类数据。
具体解释如下:

  • pd.get_dummies(inputs, dummy_na=True)inputs 数据框中的分类变量进行独热编码。get_dummies() 是 Pandas 的函数,接受一个数据框或数据列作为输入,并返回独热编码后的结果。

  • inputs 数据框中的分类变量会被转换为一组二进制变量,每个变量表示一个可能的分类值。如果 dummy_na=True,还会为缺失值创建一个虚拟变量。

  • 最后,将独热编码后的结果赋值给 inputs

 若要得到以下结果:

   NumRooms  Alley_Pave  Alley_nan
0       3.0           1          0
1       2.0           0          1
2       4.0           0          1
3       3.0           0          1

 则代码为:

inputs=pd.get_dummies(inputs,dummy_na=True)
inputs = inputs.replace({True: 1, False: 0})
print(inputs)

 使用 replace() 方法将布尔值 True 替换为 1,将布尔值 False 替换为 0
replace() 方法接受一个字典作为参数,其中键表示要替换的值,值表示替换后的值。
 在这里,使用 {True: 1, False: 0} 字典将布尔值替换为整数值。

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

x,y=torch.tensor(inputs),torch.tensor(outputs)
print(x)
print(y)

会报错:ValueError: could not determine the shape of object type 'DataFrame'

原因:torch.tensor() 函数用于将数据转换为张量形式,但它需要处理的是具有确定形状的数据,而不是 DataFrame 对象。

正确代码:

x,y=torch.tensor(inputs.values),torch.tensor(outputs.values)
print(x)
print(y)
tensor([[3., 1., 0.],
        [2., 0., 1.],
        [4., 0., 1.],
        [3., 0., 1.]], dtype=torch.float64)
tensor([127500, 106000, 178100, 140000])

64位浮点数计算较慢,深度学习一般用32位

NaN 是一个缩写,表示 “Not a Number”,即"不是一个数字"。
它是一种特殊的数值表示,在计算机科学和数据分析中常常用于表示缺失值或无效的数值。

QA:

a=torch.arange(12)
b=a.reshape((3,4))
b[:]=2
print(a)
tensor([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

为什么改ba也会变?

 在PyTorch中,张量对象是通过引用传递的,而不是通过值传递。当你创建一个张量,并将其赋值给另一个变量时,它们实际上共享相同的底层数据存储。这种共享存储的机制导致了这种情况。

 在代码中,执行 b = a.reshape((3, 4)) 时,b 实际上成为了 a 的一个视图(view)。这意味着 ba 引用相同的存储空间,它们是同一个张量的不同表示。

 因此,当修改 b 中的元素时,实际上也会修改共享的底层存储,从而影响到 a。这是因为 b[:] = 2 语句将所有元素都设置为 2,同时在底层存储中进行了相应的更改。由于 ab 共享相同的存储,因此对 b 所做的更改也会反映在 a 上。

 如果想要创建一个与 a 独立的副本,而不共享底层存储,可以使用 clone() 方法来复制张量。例如,可以使用 b = a.clone().reshape((3, 4)) 来创建一个新的张量 b,它与 a 拥有相同的形状,但是是独立的对象,修改 b 不会影响到 a

 总结起来,当你对一个张量进行切片或重塑操作时,要注意是否会影响到原始张量,因为它们可能共享相同的底层存储。如果需要避免这种共享,可以使用 clone() 方法来创建副本。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值