1.数据操作
(jupyter notebook运行快捷键:ctrl+回车)
什么是张量(Tensor)?
看过这篇文章就懂了。
2.数据预处理实现
3.矩阵计算
(1)y是标量,x是列向量,那么y对x求导是一个行向量。计算y关于x每个分量的梯度,梯度跟等高线是正交的,意味着梯度指向的是值增加得最快的那个方向,那么负梯度就是下降得最快得那个方向。
(2)y是向量,x是标量,那么y对x求导是一个列向量。
(3)x,y都是向量
4.自动求导
例子就看视频理解,自动求导是怎么实现的?见下图:
(虽然用pytorch不需要理解计算图,只需要做调包侠,但还是有必要知道它内部的一些工作原理,就如链式求导的原则)
数学上就是这样,先定义公式,再代入值。数学上都是用的显式构造。
隐式构造例如tensor把要用的值给记住
显式构造是指先把整个计算写出来,然后再去给值,你可以认为是用Python来实现一个函数和用数学来实现一个函数是不一样的,真正用的时候,显示构造会很不方便。
构建深度学习模型的基本流程就是:搭建计算图,求得损失函数,然后计算损失函数对模型参数的导数,再利用梯度下降法等方法来更新参数。搭建计算图的过程,称为“正向传播”,这个是需要我们自己动手的,因为我们需要设计我们模型的结构。由损失函数求导的过程,称为“反向传播”,求导是件辛苦事儿,所以自动求导基本上是各种深度学习框架的基本功能和最重要的功能之一,PyTorch也不例外,后面有时间会写一下tensorflow和mxnet框架的自动求导。目前的深度学习框架其都是实现了自动求梯度的功能,你只关注模型架构的设计,而不必关注模型背后的梯度是如何计算的。在深度学习里,向量跟向量或者是向量跟矩阵的求导是很少的。
5.线性回归
5.1基础优化算法
梯度下降见机器学习吴恩达视频讲解部分。之后会讨论更好的、更稳定的优化算法。
在实际中,我们很少用到梯度下降。深度学习最常见的梯度下降版本叫做小批量随机梯度下降。因为梯度下降中,我们每次计算梯度要对整个损失函数求导,这个损失函数是对我们所有样本的平均损失,所以就意味着,求一次梯度我们要把整个样本重新算一遍,这是“很贵”的一件事情,可能需要数分钟或者数小时,一般来说,要走个几百步或者几千步的样子,这样我们的计算量就太大了。
当b很大的时候,近似就会很精确,当b很小时,就不那么精确,但计算梯度就会很容易,因为梯度的计算复杂度跟样本的个数是线性相关的。超参数就需要人工设定。
我们之后是用GPU来进行计算的,GPU动不动就几百上千个核,批量太小,计算资源不好利用。
5.2线性回归的从零开始实现
1.torch.normal(0,1,(num_examples,len(w)))
该函数原型如下:
normal(mean, std, *, generator=None, out=None)
该函数返回从单独的正态分布中提取的随机数的张量,该正态分布的均值是mean,标准差是std。
用法如下:我们从一个标准正态分布N~(0,1),提取一个2x2的矩阵
torch.normal(mean=0.,std=1.,size=(2,2))
2.torch.matmul(X,w)+b
矩阵乘法
3.d2l.set_figsize()
from IPython import display
def use_svg_display():
# 用矢量图显示
display.set_matplotlib_formats('svg')
def set_figsize(figsize=(3.5, 2.5)):
use_svg_display()
# 设置图的尺寸
plt.rcParams['figure.figsize'] = figsize
set_figsize()
# 加分号只显示图
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
已经将上面的 plt 作图函数以及 use_svg_display 函数和 set_figsize 函数定义在 d2lzh 包里。 以后在作图时,我们在作图前只需要调用 d2lzh.set_figsize() 即可打印矢量图并设置图的尺寸。
4.random.shuffle(indices)
随机打乱样本的顺序,构造一个随机样本。注意:shuffle只能作用域list,不能作用于tensor
6.训练模型部分代码
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y) # X和y的小批量损失
# 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
# 并以此计算关于[w,b]的梯度
l.sum().backward()
sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数
with torch.no_grad():
train_l = loss(net(features, w, b), labels)
print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')
其中,l.sum().backward()
是指对l中每个分量求和,一个标量求梯度,也就是求所有偏导数之和。
解释如下:
5.3线性回归的简洁实现
1.读取数据集部分
def load_array(data_arrays, batch_size, is_train=True): #@save
"""构造一个PyTorch数据迭代器"""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
大神们在d2l内部构造了一个API供我们读取数据调用。
*号解释
TensorDataset(*data_arrays)中的"*"号是元组拆包符号,元组解包的过程中会将每一个元素依次放入到位置参数上!
说明:*参数,编写在不同的位置上,会具备不同的功能,当*参数出现在函数定义时,表示可变参数(元组参数),当*参数出现在函数调用时,则表示解包功能!
TensorDataset解释
TensorDataset 可以用来对 tensor 进行打包,就好像 python 中的 zip 功能。该类通过每一个 tensor 的第一个维度进行索引。因此,该类中的 tensor 第一维度必须相等。TensorDataset 中的参数必须是 tensor。把输入的两类数据进行一一对应。用例如下:
DataLoader解释(参考博文:https://blog.csdn.net/lipengfei0427/article/details/109547241)
DataLoader是PyTorch中读取数据的一个重要接口,基本上用PyTorch训练模型都会用到。这个接口的目的是:将自定义的Dataset根据batch size大小、是否shuffle等选项封装成一个batch size大小的Tensor,后续只需要再包装成Variable即可作为模型输入用于训练。
PyTorch中的数据读取主要包含三个类,其过程主要是以下四步:
1.Dataset(就是上文的TensorDataset )
2.DataLoader
3.DataLoaderIter(迭代器)
4.循环DataLoader,进行训练
处理过程伪代码:
dataset = MyDataset()
dataloader = DataLoader(dataset)
num_epoches = 100
for epoch in range(num_epoches):
for img, label in dataloader:
....
前三者的关系可以理解成一个层层封装的关系,Dataset是数据集,DataLoader将数据集进行封装,最终在内部使用DataLoaderIter进行迭代。这时候有必要了解一下迭代器了。
迭代器解释(参考博文:https://blog.csdn.net/qq_45807032/article/details/105219674)
迭代是python中访问集合元素的一种非常强大的一种方式。迭代器是一个可以记住遍历位置的对象,因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源。迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。迭代器有两个方法:iter()和next()方法。一个类(对象)只要含有"__iter__“、”__next__"两个方法,就将其称为迭代器,它解决了基于迭代取值的局限性。
可迭代对象简单理解为可以转换成迭代器的对象。类似于list、tuple、str 等类型的数据可以使用for … in…
的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有**list(列表)、tuple(元组)、dirt(字典)、str(字符串)set(集合)**等。字符串,列表或元组等对象都可用于创建迭代器。例如iter(list)
就是一个迭代器。