2.2. 数据预处理¶ 在 SageMaker Studio Lab 中打开 Notebook
到目前为止,我们处理的数据都来自于某种合成数据,并已经存入张量格式。 然而,在实践中应用深度学习时,我们通常需要从存储在各种格式文件中的杂乱数据开始,然后进行预处理以满足我们的需求。 幸运的是,pandas 库可以为我们完成大部分繁重的工作。 本节虽然不能替代完整的 pandas 教程,但将为你快速介绍一些最常见的操作。
2.2.1. 读取数据集¶
逗号分隔值(CSV)文件被广泛用于存储表格(类似电子表格)数据。在CSV文件中,每行对应一条记录,由几个(逗号分隔的)字段组成,例如,“Albert Einstein,March 14 1879,Ulm,Federal polytechnic school,field of gravitational physics”。 为了演示如何使用 pandas
加载CSV文件,我们创建一个CSV文件 ../data/house_tiny.csv
。该文件代表一个房屋数据集,其中每行对应一个独立的房屋,列对应于房间数(NumRooms
)、屋顶类型(RoofType
)和价格(Price
)。
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,RoofType,Price
NA,NA,127500
2,NA,106000
4,Slate,178100
NA,NA,140000''')
现在我们导入 pandas
并使用 read_csv
加载数据集。
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms RoofType Price
0 NaN NaN 127500
1 2.0 NaN 106000
2 4.0 Slate 178100
3 NaN NaN 140000
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms RoofType Price
0 NaN NaN 127500
1 2.0 NaN 106000
2 4.0 Slate 178100
3 NaN NaN 140000
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms RoofType Price
0 NaN NaN 127500
1 2.0 NaN 106000
2 4.0 Slate 178100
3 NaN NaN 140000
import pandas as pd
data = pd.read_csv(data_file)
print(data)
NumRooms RoofType Price
0 NaN NaN 127500
1 2.0 NaN 106000
2 4.0 Slate 178100
3 NaN NaN 140000
2.2.2. 数据准备¶
在监督学习中,我们训练模型来预测一个指定的目标值,给定一组输入值。处理数据集的第一步是分离出对应于输入和目标值的列。我们可以通过名称或基于整数位置的索引(iloc
)来选择列。
你可能已经注意到,pandas
将所有值为 NA
的CSV条目替换为一个特殊的 NaN
(not a number)值。当一个条目为空时,例如 “3,,,270000”,也会发生这种情况。这些被称为缺失值,它们是数据科学的“臭虫”,是你整个职业生涯中都会遇到的一个持久的麻烦。根据具体情况,缺失值可以通过插补或删除来处理。插补用其值的估计值替换缺失值,而删除则简单地丢弃包含缺失值的行或列。
以下是一些常见的插补启发式方法。对于类别输入字段,我们可以将 NaN
视为一个类别。由于 RoofType
列取值为 Slate
和 NaN
,pandas
可以将此列转换为两列 RoofType_Slate
和 RoofType_nan
。屋顶类型为 Slate
的行将 RoofType_Slate
和 RoofType_nan
的值分别设置为1和0。对于具有缺失 RoofType
值的行,情况则相反。
inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 NaN False True
1 2.0 False True
2 4.0 True False
3 NaN False True
inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 NaN False True
1 2.0 False True
2 4.0 True False
3 NaN False True
inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 NaN False True
1 2.0 False True
2 4.0 True False
3 NaN False True
inputs, targets = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 NaN False True
1 2.0 False True
2 4.0 True False
3 NaN False True
对于缺失的数值,一个常见的启发式方法是用相应列的平均值替换 NaN
条目。
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 3.0 False True
1 2.0 False True
2 4.0 True False
3 3.0 False True
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 3.0 False True
1 2.0 False True
2 4.0 True False
3 3.0 False True
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 3.0 False True
1 2.0 False True
2 4.0 True False
3 3.0 False True
inputs = inputs.fillna(inputs.mean())
print(inputs)
NumRooms RoofType_Slate RoofType_nan
0 3.0 False True
1 2.0 False True
2 4.0 True False
3 3.0 False True
2.2.3. 转换为张量格式¶
现在 inputs
和 targets
中的所有条目都是数值,我们可以将它们加载到一个张量中(回顾 2.1节)。
import torch
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(targets.to_numpy(dtype=float))
X, y
(tensor([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=torch.float64),
tensor([127500., 106000., 178100., 140000.], dtype=torch.float64))
from mxnet import np
X, y = np.array(inputs.to_numpy(dtype=float)), np.array(targets.to_numpy(dtype=float))
X, y
[22:09:02] ../src/storage/storage.cc:196: Using Pooled (Naive) StorageManager for CPU
(array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=float64),
array([127500., 106000., 178100., 140000.], dtype=float64))
from jax import numpy as jnp
X = jnp.array(inputs.to_numpy(dtype=float))
y = jnp.array(targets.to_numpy(dtype=float))
X, y
No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)
(Array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]], dtype=float32),
Array([127500., 106000., 178100., 140000.], dtype=float32))
import tensorflow as tf
X = tf.constant(inputs.to_numpy(dtype=float))
y = tf.constant(targets.to_numpy(dtype=float))
X, y
(<tf.Tensor: shape=(4, 3), dtype=float64, numpy=
array([[3., 0., 1.],
[2., 0., 1.],
[4., 1., 0.],
[3., 0., 1.]])>,
<tf.Tensor: shape=(4,), dtype=float64, numpy=array([127500., 106000., 178100., 140000.])>)
2.2.4. 讨论¶
你现在知道了如何划分数据列、插补缺失变量以及将 pandas
数据加载到张量中。在 5.7节 中,你将学习更多的数据处理技巧。虽然这个速成课程保持了简单性,但数据处理可能会变得棘手。例如,我们的数据集可能不是存放在一个单独的CSV文件中,而是分散在从关系数据库中提取的多个文件中。例如,在电子商务应用中,客户地址可能存放在一个表中,而购买数据在另一个表中。此外,从业者面临着除了类别和数值之外的多种数据类型,例如文本字符串、图像、音频数据和点云。通常,需要先进的工具和高效的算法来防止数据处理成为机器学习流程中的最大瓶颈。当我们学习计算机视觉和自然语言处理时,这些问题将会出现。最后,我们必须注意数据质量。现实世界的数据集常常受到异常值、传感器故障测量和记录错误等问题的困扰,这些问题必须在将数据输入任何模型之前解决。数据可视化工具,如 seaborn、Bokeh 或 matplotlib,可以帮助你手动检查数据,并对可能需要解决的问题类型形成直觉。
2.2.5. 练习¶
尝试加载数据集,例如来自 UCI机器学习库 的鲍鱼数据集,并检查其属性。其中有多少比例有缺失值?数值、类别或文本变量各占多少比例?
尝试通过名称而不是列号来索引和选择数据列。pandas关于索引的文档有关于如何做到这一点的更多细节。
你认为用这种方式可以加载多大的数据集?可能会有哪些限制?提示:考虑读取数据的时间、表示方式、处理过程和内存占用。在你的笔记本电脑上试试。如果在一台服务器上尝试,会发生什么?
你将如何处理具有大量类别的数据?如果类别标签都是唯一的怎么办?你是否应该包含后者?
你能想到pandas的哪些替代方案?比如从文件中加载NumPy张量?可以看看 Pillow,即Python图像处理库。