D2L系列-第一篇-2.1数据操作 and 2.2数据预处理

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

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

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

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

2.1.1 入门

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

ini 复制代码
import torch

x = torch.arange(12)
x

X = x.reshape(3, 4) 只改变形状,不改变元素的值和数量

既然数量不变,因此reshape以后的数量要和原来对的上,否则会报错

ini 复制代码
X = x.reshape(3, 4)
X

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

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

ini 复制代码
y = torch.arange(22)
Y = y.reshape(2, 11)

print(f"{y} \n\n {Y}")

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

ini 复制代码
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()的快捷操作罢了。

lua 复制代码
torch.tensor([[1, 2], [3, 4]])

2.1.2 运算符

python 复制代码
''' 
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}")
python 复制代码
''' 
事实证明,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}")
scss 复制代码
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}")
python 复制代码
''' 
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)
python 复制代码
''' 
使用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 广播机制

lua 复制代码
import torch

x = torch.tensor([[1, 1, 1], [2, 2, 2]])
y = torch.arange(6).reshape(2, 3)

print(f"{x+y}\n\n{x-y}")
python 复制代码
''' 
广播机制有两种应用场景:
1. 至少有一个维度要对齐
2. 行和列相加
'''
x = torch.arange(15).reshape(3, 5)
y = torch.ones(5)
x+y

2.1.4 索引和切片

python 复制代码
''' 
操作逻辑基本上和传统的数组是一样的。
'''
x = torch.arange(12).reshape(3, 4)
x
python 复制代码
''' 
所以tensor的索引有两种方式,一种是x[1, 2],另一种是x[1][2]
这两种效果是等价的。
'''
x[1, 1] = 100
x

2.1.5 节省内存

ini 复制代码
x = torch.tensor([[1, 2], [1, 3]])
y = torch.ones(2, 2)

before_y = id(y)

y = x + y
after_y = id(y)

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

before_y = id(y)

y += x
after_y = id(y)

print(f"{before_y} \n\n{after_y}")
before_y == after_y
ini 复制代码
x = torch.tensor([[1, 2], [1, 3]])
y = torch.ones(2, 2)

before_y = id(y)

y[:, :] = x + y
after_y = id(y)

print(f"{before_y} \n\n{after_y}")
before_y == after_y

2.1.6 转换为其他Python对象

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 False

def 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
python 复制代码
import numpy as np
import 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)}")
python 复制代码
''' 
int(a)的效果是直接下取整。
只有当tensor中只含有一个元素时,才能直接用int()和float()
'''

a = torch.tensor([3.8])
a, a.item(), float(a), int(a)

2.1 练习

ini 复制代码
X = torch.arange(20).reshape(4, 5)
Y = torch.ones(4, 5)

print(f"{X>Y}\n\n\n{X<Y}")

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

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

  1. 相等
  2. 其中一个为一
  3. 不存在
python 复制代码
''' 
我大概能体会为什么要从右到左比较了。
因为这三个条件:等;二选一为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的地位应该是和章节并列的

lua 复制代码
import os

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

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

lua 复制代码
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代表缺失值

kotlin 复制代码
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,列名在上方

scss 复制代码
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类型有用,对数值不起作用

ini 复制代码
inputs = pd.get_dummies(inputs, dummy_na = True) 
print(inputs)

2.2.3 转换为张量格式

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

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

python 复制代码
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

lua 复制代码
import os
import pandas as pd

file_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")通过这种方式可以删除某列

kotlin 复制代码
data = data.drop(columns = "价格")
data
方法二:写代码自动判断哪一列的NaN是最多的

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

ini 复制代码
is_NaN = data.isna()
NaN_num = is_NaN.sum()
Most_NaN_column = NaN_num.idxmax()

data = data.drop(columns = Most_NaN_column)

data 

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

kotlin 复制代码
data = data.fillna(data.mean())
data
kotlin 复制代码
data = pd.get_dummies(data)
data
ini 复制代码
ts = torch.tensor(data.to_numpy(dtype = int))
ts
相关推荐
蹦蹦跳跳真可爱5892 小时前
Python----目标检测(《Fast R-CNN》和Fast R-CNN)
人工智能·python·深度学习·神经网络·目标检测·cnn
mozun20202 小时前
YOLOv7 辅助检测头与重参数化解析2025.6.1
人工智能·yolo·目标检测·机器学习·计算机视觉·目标跟踪
zskj_zhyl4 小时前
从“被动养老”到“主动健康管理”:平台如何重构代际关系?
大数据·人工智能·重构
奔跑吧邓邓子4 小时前
DeepSeek 赋能车路协同:智能交通的破局与重构
人工智能·应用·车路协同·智能交通·deepseek
不剪发的Tony老师4 小时前
sqlite-vec:谁说SQLite不是向量数据库?
数据库·人工智能·sqlite
白熊1885 小时前
【机器学习基础】机器学习入门核心:Jaccard相似度 (Jaccard Index) 和 Pearson相似度 (Pearson Correlation)
人工智能·机器学习
pen-ai5 小时前
【深度学习】17. 深度生成模型:DCGAN与Wasserstein GAN公式深度推导
人工智能·深度学习·生成对抗网络
简简单单做算法6 小时前
基于mediapipe深度学习的虚拟画板系统python源码
人工智能·python·深度学习·mediapipe·虚拟画板
qq_314009836 小时前
Windows+VSCode搭建小智(xiaozhi)开发环境
ide·人工智能·vscode·编辑器·开源软件
技术便签6 小时前
第一篇:揭示模型上下文协议(MCP):AI的通用连接器
人工智能·ai编程·language model·多智能体·智能体