D2L学习笔记:安装、张量与数据处理

安装

我们需要配置一个环境来运行 Python、Jupyter Notebook、相关库以及运行本书所需的代码,以快速入门并获得动手学习经验。

安装 Miniconda

最简单的方法就是安装依赖Python 3.x的Miniconda。 如果已安装conda,则可以跳过以下步骤。访问Miniconda网站,根据Python3.x版本确定适合的版本。

如果我们使用macOS,假设Python版本是3.9(我们的测试版本),将下载名称包含字符串"MacOSX"的bash脚本,并执行以下操作:

sh 复制代码
# 以Intel处理器为例,文件名可能会更改
sh Miniconda3-py39_4.12.0-MacOSX-x86_64.sh -b

如果我们使用Linux,假设Python版本是3.9(我们的测试版本),将下载名称包含字符串"Linux"的bash脚本,并执行以下操作:

sh 复制代码
# 文件名可能会更改
sh Miniconda3-py39_4.12.0-Linux-x86_64.sh -b

接下来,初始化终端Shell,以便我们可以直接运行conda

sh 复制代码
~/miniconda3/bin/conda init

现在关闭并重新打开当前的shell。并使用下面的命令创建一个新的环境:

sh 复制代码
conda create --name d2l python=3.9 -y

现在激活 d2l 环境:

sh 复制代码
conda activate d2l

安装深度学习框架和d2l软件包

在安装深度学习框架之前,请先检查计算机上是否有可用的GPU。 例如可以查看计算机是否装有NVIDIA GPU并已安装CUDA。 如果机器没有任何GPU,没有必要担心,因为CPU在前几章完全够用。 但是,如果想流畅地学习全部章节,请提早获取GPU并且安装深度学习框架的GPU版本。

我们可以按如下方式安装PyTorch的CPU或GPU版本:

sh 复制代码
pip install torch==1.12.0
pip install torchvision==0.13.0

我们的下一步是安装d2l包,以方便调取本书中经常使用的函数和类:

sh 复制代码
pip install d2l==0.17.6

下载 D2L Notebook

接下来,需要下载这本书的代码。 可以点击本书HTML页面顶部的"Jupyter 记事本"选项下载后解压代码,或者可以按照如下方式进行下载:

sh 复制代码
mkdir d2l-zh && cd d2l-zh
curl https://zh-v2.d2l.ai/d2l-zh-2.0.0.zip -o d2l-zh.zip
unzip d2l-zh.zip && rm d2l-zh.zip
cd pytorch

注意:如果没有安装unzip,则可以通过运行sudo apt install unzip进行安装。

安装完成后我们可以通过运行以下命令打开Jupyter笔记本(在Window系统的命令行窗口中运行以下命令前,需先将当前路径定位到刚下载的本书代码解压后的目录):

复制代码
jupyter notebook

现在可以在Web浏览器中打开http://localhost:8888(通常会自动打开)。 由此,我们可以运行这本书中每个部分的代码。 在运行书籍代码、更新深度学习框架或d2l软件包之前,请始终执行conda activate d2l以激活运行时环境。 要退出环境,请运行conda deactivate

数据操作

为了能够完成各种数据操作,我们需要某种方法来存储和操作数据。 通常,我们需要做两件重要的事:

(1)获取数据;

(2)将数据读入计算机后对其进行处理。

如果没有某种方法来存储数据,那么获取数据是没有意义的。

首先,我们介绍𝑛维数组,也称为张量(tensor)。

无论使用哪个深度学习框架,它的张量类 (在MXNet中为ndarray, 在PyTorch和TensorFlow中为Tensor)都与Numpy的ndarray类似。

但深度学习框架又比Numpy的ndarray多一些重要功能:

首先,GPU很好地支持加速计算,而NumPy仅支持CPU计算;

其次,张量类支持自动微分。 这些功能使得张量类更适合深度学习。

入门

本节的目标是帮助读者了解并运行一些在阅读本书的过程中会用到的基本数值计算工具。

引入torch

首先,我们导入torch。请注意,虽然它被称为PyTorch,但是代码中使用torch而不是pytorch

python 复制代码
import torch

张量

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

具有一个轴的张量对应数学上的向量 (vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。

创建张量

使用 arange 创建一个行向量 x。这个行向量包含以0开始的前12个整数,它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的 元素(element)。

例如,张量 x 中有 12 个元素。除非额外指定,新的张量将存储在内存中,并采用基于CPU的计算。

python 复制代码
x = torch.arange(15)
x

输出:

复制代码
tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
张量的形状
python 复制代码
x.shape

输出:

复制代码
torch.Size([15])
reshape张量

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

例如,可以把张量x从形状为(15,)的行向量转换为形状为(3,5)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行5列的矩阵。

python 复制代码
X = x.reshape(3, 5)
X

输出:

复制代码
tensor([[ 0,  1,  2,  3,  4],
        [ 5,  6,  7,  8,  9],
        [10, 11, 12, 13, 14]])

我们不需要通过手动指定每个维度来改变形状。

也就是说,如果我们的目标形状是(高度,宽度), 那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。

在上面的例子中,为了获得一个3行的矩阵,我们手动指定了它有3行和5列。 幸运的是,我们可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,5)x.reshape(3,-1)来取代x.reshape(3,5)

全0/全1/概率分布

有时,我们希望[使用全0、全1、其他常量,或者从特定分布中随机采样的数字]来初始化矩阵。 我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下:

python 复制代码
torch.zeros((1, 5, 4))

输出:

复制代码
tensor([[[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]]])

同样,我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为1。代码如下:

python 复制代码
torch.ones((2, 3, 4))

输出:

复制代码
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.]]])

以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。

python 复制代码
torch.randn(3, 4)

输出:

复制代码
tensor([[-0.4546,  0.0417,  2.2459,  0.3215],
        [-0.1053,  1.3135,  0.7135, -0.2748],
        [-0.8907,  0.4276, -0.8387,  2.0068]])

运算符

想在这些数据上执行数学运算,其中最简单且最有用的操作是按元素(elementwise)运算。 它们将标准标量运算符应用于数组的每个元素。

标准算术运算

对于任意具有相同形状的张量, [常见的标准算术运算符(+-\*/\**)都可以被升级为按元素运算]。 我们可以在同一形状的任意两个张量上调用按元素操作。 在下面的例子中,我们使用逗号来表示一个具有5个元素的元组,其中每个元素都是按元素操作的结果。

python 复制代码
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  # **运算符是求幂运算

输出:

复制代码
(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.]))

("按元素"方式可以应用更多的计算),包括像求幂这样的一元运算符。

python 复制代码
torch.exp(x)

输出:

复制代码
tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])

有时,我们想通过逻辑运算符 构建二元张量。 以X == Y为例: 对于每个位置,如果XY在该位置相等,则新张量中相应项的值为1。 这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。

python 复制代码
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]])
X == Y

输出:

复制代码
tensor([[False,  True, False,  True],
        [False, False, False, False],
        [False, False, False, False]])
连结张量(concatenate)

我们也可以把多个张量*连结(concatenate)*在一起, 把它们端对端地叠起来形成一个更大的张量。

我们只需要提供张量列表,并给出沿哪个轴连结。

下面的例子分别演示了当我们沿行(轴-0,形状的第一个元素) 和按列(轴-1,形状的第二个元素)连结两个矩阵时,会发生什么情况。

我们可以看到,第一个输出张量的轴-0长度(6)是两个输入张量轴-0长度的总和(3+3);

第二个输出张量的轴-1长度(8)是两个输入张量轴-1长度的总和(4+4)

python 复制代码
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]])
X,Y,torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)

输出:

复制代码
(tensor([[ 0.,  1.,  2.,  3.],
         [ 4.,  5.,  6.,  7.],
         [ 8.,  9., 10., 11.]]),
 tensor([[2., 1., 4., 3.],
         [1., 2., 3., 4.],
         [4., 3., 2., 1.]]),
 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.]]))

广播机制

在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。 在某些情况下,[即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作]。 这种机制的工作方式如下:

  1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
  2. 对生成的数组执行按元素操作。

在大多数情况下,我们将沿着数组中长度为1的轴进行广播,如下例子:

python 复制代码
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b

输出:

复制代码
(tensor([[0],
         [1],
         [2]]),
 tensor([[0, 1]]))

由于ab分别是3×1和1×2矩阵,如果让它们相加,它们的形状不匹配。 我们将两个矩阵广播 为一个更大的3×2矩阵,如下所示:矩阵a将复制列, 矩阵b将复制行,然后再按元素相加。

python 复制代码
a + b

输出:

复制代码
tensor([[0, 1],
        [1, 2],
        [2, 3]])

节省内存

**运行一些操作可能会导致为新结果分配内存** \]。 例如,如果我们用`Y = X + Y`,我们将取消引用`Y`指向的张量,而是指向新分配的内存处的张量。 在下面的例子中,我们用Python的`id()`函数演示了这一点, 它给我们提供了内存中引用对象的确切地址。 运行`Y = Y + X`后,我们会发现`id(Y)`指向另一个位置。 这是因为Python首先计算`Y + X`,为结果分配新的内存,然后使`Y`指向内存中的这个新位置。 ```python before = id(Y) Y = X + Y id(Y) == before ``` 输出: False 这可能是不可取的,原因有两个: 1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次更新所有参数。通常情况下,我们希望原地执行这些更新; 2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧的参数。 幸运的是,(**执行原地操作** )非常简单。 我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如`Y[:] = `。 为了说明这一点,我们首先创建一个新的矩阵`Z`,其形状与另一个`Y`相同, 使用`zeros_like`来分配一个全0的块。 ```python Z = torch.zeros_like(Y) print('id(Z):', id(Z)) Z[:] = X + Y print('id(Z):', id(Z)) ``` 输出: id(Z): 1727178695520 id(Z): 1727178695520 \[**如果在后续计算中没有重复使用`X`, 我们也可以使用`X[:] = X + Y`或`X += Y`来减少操作的内存开销。**

python 复制代码
before = id(X)
X += Y
id(X) == before

输出:

复制代码
True

数据预处理

为了能用深度学习来解决现实世界的问题,我们经常从预处理原始数据开始, 而不是从那些准备好的张量格式数据开始。

在Python中常用的数据分析工具中,我们通常使用pandas软件包。 像庞大的Python生态系统中的许多其他扩展包一样,pandas可以与张量兼容。

本节我们将简要介绍使用pandas预处理原始数据,并将原始数据转换为张量格式的步骤。

读取数据集

举一个例子,我们首先(创建一个人工数据集,并存储在CSV(逗号分隔值,Comma-Sperated Values)文件 ) ../data/house_tiny.csv中。 以其他格式存储的数据也可以通过类似的方式进行处理。 下面我们将数据集按行写入CSV文件中。

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

要[从创建的CSV文件中加载原始数据集 ],我们导入pandas包并调用read_csv函数。该数据集有四行三列。其中每行描述了房间数量("NumRooms")、巷子类型("Alley")和房屋价格("Price")。

python 复制代码
# 如果没有安装pandas,只需取消对以下行的注释来安装pandas
# !pip install pandas
import pandas as pd

data = pd.read_csv(data_file)
print(data)

输出:

复制代码
   NumRooms Alley   Price
0       NaN  Pave  127500
1       2.0   NaN  106000
2       4.0   NaN  178100
3       NaN   NaN  140000

处理缺失值

注意,"NaN"项代表缺失值。 [为了处理缺失的数据,典型的方法包括插值法删除法 ] 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。 在(这里,我们将考虑插值法)。

通过位置索引iloc,我们将data分成inputsoutputs, 其中前者为data的前两列,而后者为data的最后一列。 对于inputs中缺少的数值,我们用同一列的均值替换"NaN"项。

python 复制代码
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)

输出:

复制代码
   NumRooms Alley
0       3.0  Pave
1       2.0   NaN
2       4.0   NaN
3       3.0   NaN

**对于`inputs`中的类别值或离散值,我们将"NaN"视为一个类别。** \] 由于"巷子类型"("Alley")列只接受两种类型的类别值"Pave"和"NaN", `pandas`可以自动将此列转换为两列"Alley_Pave"和"Alley_nan"。 巷子类型为"Pave"的行会将"Alley_Pave"的值设置为1,"Alley_nan"的值设置为0。 缺少巷子类型的行会将"Alley_Pave"和"Alley_nan"分别设置为0和1。 ```python inputs = pd.get_dummies(inputs, dummy_na=True) print(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`和`outputs`中的所有条目都是数值类型,它们可以转换为张量格式。** \] 当数据采用张量格式后,可以通过在 `sec_ndarray`中引入的那些张量函数来进一步操作。 ```python import torch X = torch.tensor(inputs.to_numpy(dtype=float)) y = torch.tensor(outputs.to_numpy(dtype=float)) X, y ``` 输出: (tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64), tensor([127500., 106000., 178100., 140000.], dtype=torch.float64)) * `pandas`软件包是Python中常用的数据分析工具,`pandas`可以与张量兼容。 * 用`pandas`处理缺失的数据时,我们可根据情况选择用插值法和删除法。

相关推荐
taoqick2 小时前
FIPO粗读笔记
笔记
chools2 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第一期】
java·人工智能·学习·spring·ai
副露のmagic3 小时前
字符串章节 leetcode 思路&实现
windows·python·leetcode
BackCatK Chen3 小时前
Python安装与VSCode配置完整教程(每步带截图说明)
vscode·python·python安装·vscode配置·python 3.9.10·嵌入式环境配置
半壶清水3 小时前
[软考网规考点笔记]-局域网之以太网标准
网络·笔记·网络协议·考试
忙什么果4 小时前
transformer学习笔记2
笔记·学习·transformer
ZhiqianXia4 小时前
Gem5 学习笔记(2) : Gem5 建模要点与基本思路
笔记·学习
硅基喵4 小时前
Python 入门:从“其他语言”到 Pythonic 思维的完整迁移手册
python
Dxy12393102164 小时前
ECharts折线图入门学习:从基础到实战的完整指南
学习·信息可视化·echarts