李沐动手学深度学习V2
文章内容说明
本文主要是自己学习过程中的随手笔记,需要自取
课程参考B站:https://space.bilibili.com/1567748478?spm_id_from=333.788.0.0
课件等信息原视频简介中有
CSV文件修改读取成张量tensor
数据预处理
首先(创建一个人工数据集,并存储在CSV(逗号分隔值)文件) ../data/house_tiny.csv
中,在csv文件中写入数据
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')
要进行读取数据集,我们导入pandas包并调用read_csv
函数。该数据集有四行三列。其中每行描述了房间数量(“NumRooms”)、巷子类型(“Alley”)和房屋价格(“Price”)。
# 如果没有安装pandas,只需取消对以下行的注释来安装pandas
# !pip install pandas
import pandas as pd
data = pd.read_csv(data_file)
print(data)
结果如下
处理缺失值
为了处理缺失的数据,典型的方法包括插值法和删除法,下面以插值法作为示例
通过位置索引iloc
,我们将data分成inputs和outputs, 其中前者为data的前两列,而后者为data的最后一列。 对于inputs中缺少的数值,我们用同一列的均值替换“NaN”项。【第二列求不出均值所以不改变】
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2]
inputs = inputs.fillna(inputs.mean())
print(inputs)
结果如下
对于inputs中的类别值或离散值,我们将“NaN”视为一个类别,使用独热编码,NAN值赋0,其他赋1
inputs = pd.get_dummies(inputs, dummy_na=True)
print(inputs)
结果如下
转为张量格式
现在inputs和outputs中的所有条目都是数值类型,它们可以转换为张量格式。
import torch
X = torch.tensor(inputs.to_numpy(dtype=float))
y = torch.tensor(outputs.to_numpy(dtype=float))
X, y
结果如下
python默认类型float64,这样比较慢,机器学习一般改float32
范数
向量的范数是表示一个向量有多大。 这里考虑的大小(size)概念不涉及维度,而是分量的大小。
L1范数
L1范数,它表示为向量元素的绝对值之和
# 向量(1维张量)
vector = torch.tensor([1.0, 2.0, 3.0, 4.0, -5.0])
# 求向量的L1范数
norm1 = torch.norm(vector, p=1, dim=0)
结果如下
L2范数
L2范数是向量元素平方和的平方根
可以用以下代码实现
u = torch.tensor([3.0, -4.0]) #或u = torch.tensor([3.0, -4.0],p=2)
torch.norm(u)
结果如下
Frobenius范数
Frobenius范数(Frobenius norm)是矩阵元素平方和的平方根
#Frobenius范数是矩阵,Lp范数是向量
torch.norm(torch.ones((4, 9)))
结果如下
自动求导实现
在我们计算y关于x的梯度之前,需要一个地方来存储梯度。使用如下代码
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad # 默认值是None
现在计算y
y = 2 * torch.dot(x, x)
y
结果如下
通过调用反向传播函数来自动计算y关于x每个分量的梯度
y.backward()
x.grad
结果如下,存储x每个分量的梯度(即切线/导数)
验证一下是否正确
矩阵的反向传播
分离计算
将某些计算移动到记录的计算图之外,使用y.detach()将u赋为标量,及x*x
x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
z.sum().backward()
x.grad == u
结果如下
由于记录了y的计算结果,我们可以随后在y上调用反向传播, 得到y=xx关于的x的导数,即2x
x.grad.zero_()
y.sum().backward()
x.grad == 2 * x
结果如下
Python控制流的梯度计算
即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度
while循环的迭代次数和if语句的结果都取决于输入a的值
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
a.grad == d / a
结果如下