Notes
深度学习
Pytorch 基础

Pytorch

Pytorch 是一个基于 Python 的深度学习框架,它提供了最大的灵活性和速度,以及强大的计算能力。

导入

import torch

张量(Tensor)

张量表示一个由数值组成的数组,这个数组可能有多个维度。张量可以用来存储数据和进行数学运算。

创建张量

# 行向量
x = torch.arange(12)
print(x)
# tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

通过 shape 属性可以获取张量的形状。

print(x.shape)
# torch.Size([12])

可以使用 reshape 方法改变张量的形状。

x = x.reshape(3, 4)
print(x)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])

如果希望创建全 0 或全 1 的张量,可以使用 zeros 和 ones 函数。

x = torch.zeros(2, 3)
print(x)
# tensor([[0., 0., 0.],
#         [0., 0., 0.]])
x = torch.ones(2, 3)
print(x)
# tensor([[1., 1., 1.],
#         [1., 1., 1.]])

可以使用 randn 函数创建一个张量,其中的元素是从均值为 0,标准差为 1 的标准高斯(正态)分布中随机采样而来。

x = torch.randn(2, 3)
print(x)
# tensor([[ 0.3365, -0.1132, -0.6787],
#         [ 0.3177, -0.0755, -0.2871]])

也可以直接提供张量的数值来创建张量。

x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x)
# tensor([[1, 2, 3],
#         [4, 5, 6]])

运算

可以对张量进行标准的数学运算。

加法

张量的运算遵循按元素操作的原则。

x = torch.tensor([1, 2, 3])
y = torch.tensor([4, 5, 6])
print(x + y)
# tensor([5, 7, 9])

减法

print(x - y)
# tensor([-3, -3, -3])

乘法

print(x * y)
# tensor([ 4, 10, 18])

除法

print(x / y)
# tensor([0.2500, 0.4000, 0.5000])

求幂

x = torch.tensor([1, 2, 3])
print(x ** 2)
# tensor([1, 4, 9])

拼接

x = torch.tensor([[1, 2, 3], [4, 5, 6]])
y = torch.tensor([[7, 8, 9], [10, 11, 12]])
print(torch.cat((x, y), dim=0)) # dim=0 表示按行拼接,即增加行数
# tensor([[ 1,  2,  3],
#         [ 4,  5,  6],
#         [ 7,  8,  9],
#         [10, 11, 12]])
print(torch.cat((x, y), dim=1)) # dim=1 表示按列拼接,即增加列数
# tensor([[ 1,  2,  3,  7,  8,  9],
#         [ 4,  5,  6, 10, 11, 12]])

求和

x = torch.tensor([1, 2, 3])
print(x.sum())
# tensor(6)

广播机制

当对两个形状不同的张量进行按元素运算时,可能会触发广播机制。

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

广播机制会先将 x 和 y 扩展到相同的形状,然后再按元素运算。

x' = [[0, 0],
      [1, 1],
      [2, 2]] # 复制 x 原有的列,扩展到和 y 的列数相同
y' = [[0, 1],
      [0, 1],
      [0, 1]] # 复制 y 原有的行,扩展到和 x 的行数相同
print(x + y)
# tensor([[0, 1],
#         [1, 2],
#         [2, 3]])

索引和切片

可以使用索引来访问张量的元素。

x = torch.arange(12).reshape(3, 4)
print(x)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])
print(x[1, 2])  # 表示第 2 行,第 3 列的元素
# tensor(6)
print(x[1:3])  # 表示第 2 行和第 3 行
# tensor([[ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])
print(x[1:3, 0:2])  # 表示第 2 行和第 3 行,第 1 列和第 2 列
# tensor([[ 4,  5],
#         [ 8,  9]])

节省内存

运行一些操作可能会导致为新结果分配内存。例如,如果我们用 y = x + y,我们将取消对 y 的引用,以便开始使用 y 指向的新张量。我们可以使用索引运算符将结果分配给先前分配的数组,例如:

x = torch.tensor([1, 2, 3])
y = torch.tensor([4, 5, 6])
y[:] = x + y
print(y)
# tensor([2, 4, 6])

转换为其他 Python 对象

可以使用 numpy 方法将张量转换为 numpy 数组。

x = torch.tensor([1, 2, 3])
y = x.numpy()
print(y)
# [1 2 3]

数据预处理

读取数据集

首先创建一个人工数据集。

import os
 
os.makedirs(os.path.join('..', 'data'), exist_ok=True)      # 保存在上一级目录下的 data 文件夹
data_file = os.path.join('..', 'data', 'house_tiny.csv')    # 保存在上一级目录下的 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')

其中每行表示一个数据样本。在这个数据集中,有两个特征,即 'NumRooms' 和 'Alley',以及一个标签,即 'Price'。NA 表示缺失值。

NumRoomsAlleyPrice
NAPave127500
2NA106000
4NA178100
NANA140000

如果要从 CSV 文件中读取数据,可以使用 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

处理缺失值

通常,缺失值可能是数据预处理中的一个问题。为了处理缺失的数据,我们经常使用插值法或者删除法。

插值法是指用已知的值估计未知的值。常用的插值方法有:最近邻插值、线性插值、多项式插值、样条插值等。

删除法是指删除缺失值所在的行或列。

在上述数据中,我们的目的是为了预测房价,因此我们把数据分为 input 和 output。其中 input 是特征,output 是标签。

inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]

对于 inputs 中的缺失值,我们使用插值法。我们使用同一列的非缺失值的平均值来替换缺失值。

inputs = inputs.fillna(inputs.mean()) # mean() 方法返回每一列的平均值
print(inputs)
#    NumRooms Alley
# 0       3.0  Pave
# 1       2.0   NaN
# 2       4.0   NaN
# 3       3.0   NaN

但是对于缺少的非数值特征呢?我们可以将非数值特征替换为一个指示特征。因为在这里,'Alley' 只有两个可能的取值 'Pave' 和 'NA',我们可以将 'Alley' 变为两个特征 'Alley_Pave' 和 'Alley_nan',如果 'Alley' 为 'Pave',则 'Alley_Pave' 为 1,'Alley_nan' 为 0;如果 'Alley' 为 'NA',则 'Alley_Pave' 为 0,'Alley_nan' 为 1。

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 转换为张量。

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