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,Pricen') # 列名
f.write('NA,Pave,127500n') # 每行表示一个数据样本
f.write('2,NA,106000n')
f.write('4,NA,178100n')
f.write('NA,NA,140000n')
# 如果没有安装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计算需要梯度累积
参考文献
https://zh-v2.d2l.ai/chapter_preliminaries/autograd.html



