动手学深度学习笔记day2

动手学深度学习笔记day2

数据预处理

读取数据集

1、创建一个人工数据集,并存储在CSV(逗号分隔值)文件 …/data/house_tiny.csv中。

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')
    
# 如果没有安装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

处理缺失值

1、“NaN”项代表缺失值。 为了处理缺失的数据,典型的方法包括插值法和删除法。 在这里,将考虑插值法。

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

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

2、对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和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

**补充:**Pandas.get_dummies 用法简单介绍
Pandas 中的 get_dummies 方法主要用于对类别型特征做 One-Hot 编码(独热编码)。

pandas.get_dummies(data, prefix=None, prefix_sep=’_’, 
			dummy_na=False, columns=None, 
			sparse=False, drop_first=False, dtype=None)

参数说明:

data : array-like, Series, or DataFrame 输入的数据

prefix : string, list of strings, or dict of strings, default None。get_dummies转换后,列名的前缀

columns : list-like, default None。指定需要实现类别转换的列名

dummy_na : bool, default False,增加一列表示空缺值,如果False就忽略空缺值

drop_first : bool, default False,获得k中的k-1个类别值,去除第一个。

转换为张量格式

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

import torch

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

线性代数

求和降维

默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。

A = torch.arange(20, dtype=torch.float32).reshape(5, 4)
A
tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11.],
        [12., 13., 14., 15.],
        [16., 17., 18., 19.]])
A.shape, A.sum()
(torch.Size([5, 4]), tensor(190.))

我们还可以指定张量沿哪一个轴来通过求和降低维度。 以矩阵为例,为了通过求和所有行的元素来降维(轴0),我们可以在调用函数时指定axis=0。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。

A_sum_axis0 = A.sum(axis=0)
A_sum_axis0, A_sum_axis0.shape
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))
A_sum_axis1 = A.sum(axis=1)
A_sum_axis1, A_sum_axis1.shape
(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))

求平均数降维

1、一个与求和相关的量是平均值(mean或average)。 我们通过将总和除以元素总数来计算平均值。

A.mean(), A.sum() / A.numel()
(tensor(9.5000), tensor(9.5000))

2、计算平均值的函数也可以沿指定轴降低张量的维度。

A.mean(axis=0), A.sum(axis=0) / A.shape[0]

(tensor([ 8.,  9., 10., 11.]), tensor([ 8.,  9., 10., 11.]))

非降维求和

1、 keepdims=True保持轴数不变

sum_A = A.sum(axis=1, keepdims=True)
sum_A
tensor([[ 6.],
        [22.],
        [38.],
        [54.],
        [70.]])

2、由于sum_A在对每行进行求和后仍保持两个轴,我们可以通过广播将A除以sum_A。

A / sum_A
tensor([[0.0000, 0.1667, 0.3333, 0.5000],
        [0.1818, 0.2273, 0.2727, 0.3182],
        [0.2105, 0.2368, 0.2632, 0.2895],
        [0.2222, 0.2407, 0.2593, 0.2778],
        [0.2286, 0.2429, 0.2571, 0.2714]])

3、如果我们想沿某个轴计算A元素的累积总和, 比如axis=0(按行计算),我们可以调用cumsum函数。 此函数不会沿任何轴降低输入张量的维度。

A.cumsum(axis=0)

tensor([[ 0.,  1.,  2.,  3.],
        [ 4.,  6.,  8., 10.],
        [12., 15., 18., 21.],
        [24., 28., 32., 36.],
        [40., 45., 50., 55.]])

点积(Dot Product)

给定两个向量x,y∈Rd, 它们的点积(dot product)XTY (或)X,Y 是相同位置的按元素乘积的和在这里插入图片描述

y = torch.ones(4, dtype = torch.float32)
x, y, torch.dot(x, y)

(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))

矩阵-向量积

在代码中使用张量表示矩阵-向量积,我们使用与点积相同的mv函数。 当我们为矩阵A和向量x调用torch.mv(A, x)时,会执行矩阵-向量积。 注意,A的列维数(沿轴1的长度)必须与x的维数(其长度)相同。使用广播机制

A.shape, x.shape, torch.mv(A, x)

(torch.Size([5, 4]), torch.Size([4]), tensor([ 14.,  38.,  62.,  86., 110.]))

矩阵-矩阵乘法

我们可以将矩阵-矩阵乘法A∈Rnk和B∈Rkm看作是简单地执行m次矩阵-向量积,并将结果拼接在一起,形成一个n×m矩阵。
这里的A是一个5行4列的矩阵,B是一个4行3列的矩阵。 两者相乘后,我们得到了一个5行3列的矩阵。

B = torch.ones(4, 3)
torch.mm(A, B)

tensor([[ 6.,  6.,  6.],
        [22., 22., 22.],
        [38., 38., 38.],
        [54., 54., 54.],
        [70., 70., 70.]])

范数

欧几里得距离是一个L2范数:其范数是向量所有元素平方和的平方根

u = torch.tensor([3.0, -4.0])
torch.norm(u)
tensor(5.)

L1范数,它表示为向量元素的绝对值之和

torch.abs(u).sum()
tensor(7.)

梯度

连结一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。

自动微分

通过自动计算导数,即自动微分(automatic differentiation)来求导。

反向传播

1、一个标量函数关于向量x的梯度是向量,并且与x具有相同的形状。
假设我们想对y=2XTX函数关于列向量x求导。
在我们计算y关于x的梯度之前,我们需要一个地方来存储梯度。 重要的是,我们不会在每次对一个参数求导时都分配新的内存。

import torch

x = torch.arange(4.0)
x
tensor([0., 1., 2., 3.])
x.requires_grad_(True)  # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad  # 默认值是None
y = 2 * torch.dot(x, x)
y
tensor(28., grad_fn=)
y.backward()
x.grad
tensor([ 0.,  4.,  8., 12.])

2、现在让我们计算x的另一个函数。在默认情况下,PyTorch会累积梯度,我们需要清除之前的值

# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad

tensor([1., 1., 1., 1.])

矩阵变量的反向传播

# 对矩阵变量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 在我们的例子中,我们只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad

tensor([0., 2., 4., 6.])

分离计算

我们希望将某些计算移动到记录的计算图之外。下面的反向传播函数计算z=ux关于x的偏导数,同时将u作为常数处理, 而不是z=xx*x关于x的偏导数。

x.grad.zero_()
y = x * x
u = y.detach()	#将y分离出来,视u为常数
z = u * x

z.sum().backward()
x.grad == u

tensor([True, True, True, True])

由于记录了y的计算结果,我们可以随后在y上调用反向传播, 得到y=xx关于的x的导数,即2x。

Python控制流的梯度计算

即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度。

单个loss计算前需要梯度清零,多个loss计算需要梯度累积

笔记来源:动手学深度学习在线课程

你可能感兴趣的:(深度学习)