目录

  • 一、相关资料连接
    • 1.1 李沐视频
    • 1.2 代码、PPT
  • 二、代码及笔记(使用Jupyter Notebook)
    • 2.1 线性回归从零开始实现
      • 2.1.1 基本概念
      • 2.1.2 基础优化算法
      • 2.1.3 代码逐行理解
    • 2.2 线性回归的简洁实现
      • 2.2.1 分解代码
      • 2.2.2 整体代码
  • 三、直播问题解答

一、相关资料连接

1.1 李沐视频

1.2 代码、PPT

  1. 链接: 哔哩哔哩李沐课程
  2. 链接: 课程代码及PPT

二、代码及笔记(使用Jupyter Notebook)

2.1 线性回归从零开始实现

2.1.1 基本概念

  1. 线性回归是对n维输入的加权,外加偏差
  2. 使用平方损失来衡量预测值和真实值之间的差异
  3. 线性回归有显示解
  4. 线性回归可以看作单层神经网络

2.1.2 基础优化算法

  1. 梯度下降:
    梯度:使得这个函数增加最快的方向,负梯度:是使得这个函数下降最快的方向
  2. 小批量随机梯度下降:
    在整体训练集上计算梯度太贵了,可以随机采样b个样本来接近损失,b是批量大小是另外一个超参数
  3. 小结:
    (1)梯度下降通过不断沿着反梯度方向更新参数求解
    (2)小批量随机梯度下降是深度学习默认的求解算法
    (3)两个重要的超参数:批量大小和学习率(步长)

2.1.3 代码逐行理解

1. 我们将从零开始实现整个方法,包括数据流水线、模型、损失函数和小批量随机梯度下降优化器

01   !pip install -U d2l    # 首先安装d2l包
02   %matplotlib inlineimport random              #随机梯度下降和随机初始化我们的权重import torchfrom d2l import torch as d2l

(1) 根据带有噪声的线性模型构造一个人造数据集。 我们使用线性模型参数w=[2,−3.4]^⊤、b=4.2和噪声项ϵ生成数据集及其标签:y=Xw+b+ϵ

03   def synthetic_data(w,b,num_examples):    # num_examples:n个样本'''生成 y=Xw+b+噪声'''X = torch.normal(0,1,(num_examples,len(w)))  #生成 X,他是一个均值为0,方差为1的随机数,他的大小: 行为num_examples,列为w的长度表示多少个featurey = torch.matmul(X,w) + by += torch.normal(0,0.01,y.shape)            #加入一些噪音,均值为0 ,方差为0.01,形状和y是一样return X, y.reshape((-1,1))                  #把X和y作为一个列向量返回true_w = torch.tensor([2,-3.4])true_b = 4.2features,labels = synthetic_data(true_w,true_b,1000) #synthetic_data这个函数返回的是特征和标签,相当于分别吧真实的房屋‘关键因素’和对应的‘房价’列出来了# 这里的X指房屋的关键因素集,长度len(w)即列数,表明有len(w)个关键因素,这里是2,比如‘卧室个数’和‘住房面积’两个关键因素,X的行数num_example=房屋的数量
# 以上相当于去市场调研收集真实的房屋数据

(2) 输出显示

04   print('features:', features[0], '\nlabel:', labels[0])# 运行结果
features: tensor([-0.7228,  0.3353])
label: tensor([1.6056])

(3) 可视化显示

05   d2l.set_figsize()d2l.plt.scatter(features[:, 1].detach().numpy(),     #detach()分离出数值,不再含有梯度labels.detach().numpy(), 1);         #scatter()函数的最后一个1是绘制点直径大小#把feature的第一列和labels绘出来,是有线性相关的性质#显示结果如下图:

06   d2l.set_figsize()d2l.plt.scatter(features[:, 0].detach().numpy(),    labels.detach().numpy(), 10); #修改为绘制第所有feature的第0列和label的关系,点的大小设置为10#第0列正相关,第1列负相关   w=[2,−3.4]⊤   0列对应w的2,斜率为正的直线;1列对应w的-3.4,斜率为负的直线,所以一个正相关一个负相关#显示结果如下图:


2. 每次读取一个小批量:定义一个data_iter 函数, 该函数接收批量大小、特征矩阵和标签向量作为输入,生成大小为batch_size的小批量

07   def data_iter(batch_size, features, labels):    # data_iter函数接收批量大小、特征矩阵和标签向量作为输入num_examples = len(features)                    indices = list(range(num_examples))         # 生成每个样本的index,随机读取,没有特定顺序。range随机生成0 —(n-1),然后转化成python的listrandom.shuffle(indices)                     # 将下标全都打乱,打乱之后就可以随机的顺序去访问一个样本for i in range(0, num_examples, batch_size):   # 每次从0开始到num_examples,每次跳batch_size个大小batch_indices = torch.tensor(              # 把batch_size的index找出来,因为可能会超出我们的样本个数,所以最后如果没有拿满的话,会取出最小值,所以使用minindices[i:min(i +batch_size, num_examples)]) yield features[batch_indices], labels[batch_indices]batch_size = 10for X, y in data_iter(batch_size, features, labels):          #调用data_iter这个函数返回iterator(迭代器),从中拿到X和yprint(X, '\n', y)                                         #给我一些样本标号每次随机的从里面选取一个样本返回出来参与计算break# 只是indices这个List被打乱了,feature和labels都是顺序的,用循环才能随机的放进去 #(构造一个随机样本。把样本的顺序打乱,然后间隔相同访问,也能达到随机的目的)# yield是构造一个生成器,返回迭代器。yield就是return返回一个值,并且记住返回的位置,下次迭代就从这个开始# 运行结果如下:tensor([[-0.2374,  0.5196],[ 1.6525, -0.3879],[ 0.0729, -0.3433],[-0.1988, -1.0836],[ 0.9156, -0.2383],[ 2.5247,  0.5561],[ 1.5504, -0.8997],[-0.6272, -0.0649],[-0.6743,  0.0073],[-2.0423, -0.2847]]) tensor([[ 1.9519],[ 8.8361],[ 5.5263],[ 7.4791],[ 6.8320],[ 7.3638],[10.3666],[ 3.1665],[ 2.8238],[ 1.0864]])

3. 定义 初始化模型参数

08   w = torch.normal(0, 0.01, size=(2, 1), requires_grad=True)       # w:size为2行1列,随机初始化成均值为0,方差为0.01的正态分布,requires=true是指需要计算梯度b = torch.zeros(1, requires_grad=True) #对于偏差来说直接为0,1表示为一个标量,因为也需要进行更新所以为True#广播机制:当我们用一个向量加一个标量时,标量会被加到每一个分量上

4. 定义模型

09   def linreg(X, w, b):  """线性回归模型。"""return torch.matmul(X, w) + b    #矩阵乘以向量再加上偏差

5. 定义损失函数

10   def squared_loss(y_hat, y):         #y_hat是预测值,y是真实值"""均方损失。"""return (y_hat - y.reshape(y_hat.shape))**2 / 2      #按元素做减法,按元素做平方,再除以2  (这里没有做均方)

6. 定义优化算法

11   def sgd(params, lr, batch_size):   # 优化算法是sgd,他的输入是:params给定所有的参数,这个是一个list包含了w和b,lr是学习率,和batch_size大小"""小批量随机梯度下降。"""with torch.no_grad():      # 这里更新的时候不需要参与梯度计算所以是no_gradfor param in params:   # 对于参数中的每一个参数,可能是w可能是bparam -= lr * param.grad / batch_size   # 参数减去learning rate乘以他的梯度(梯度会存在.grad中)。上面的损失函数中没有求均值,所以这里除以了batch_size求均值,因为乘法对于梯度是一个线性的关系,所以除以在上面损失函数那里定义和这里是一样的效果param.grad.zero_()     # 把梯度设置为0,因为pytorch不会自动的设置梯度为0,需要手动,下次计算梯度的时候就不会与这次相关了# 我们计算的损失是一个批量样本的总和,所以我们用批量大小(batch_size)来归一化步长,这样步长大小就不会取决于我们对批量大小的选择# 更新数据时不需要求导# pytorch 会不断累加变量的梯度,所以每更新一次参数,就要让其对应的梯度清零

7. 训练过程

12   lr = 0.03                  # 首先指定一些超参数:学习率为0.03num_epochs = 3             # epoch为3表示把整个数据扫3遍 net = linreg               # network为linreg前面定义的线性回归模型loss = squared_loss        # loss为均方损失for epoch in range(num_epochs): # 训练的过程基本是两层for循环(loop),第一次for循环是对数据扫一遍for X, y in data_iter(batch_size, features, labels):   # 对于每一次拿出一个批量大小的X和yl = loss(net(X, w, b), y)     # 把X,w,b放进network中进行预测,把预测的y和真实的y来做损失,则损失就是一个长为批量大小的一个向量,是X和y的小批量损失#l(loss)的形状是('batch_size',1),而不是一个标量l.sum().backward()            #对loss求和然后算梯度。计算关于['w','b']的梯度sgd([w, b], lr, batch_size)   #算完梯度之后就可以访问梯度了,使用sgd对w和b进行更新。使用参数的梯度对参数进行更新#对数据扫完一遍之后来评价一下进度,这块是不需要计算梯度的,所以放在no_grad里面with torch.no_grad():train_l = loss(net(features, w, b), labels)      #把整个features,整个数据传进去计算他的预测和真实的labels做一下损失,然后printprint(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')# 求和本身是让l(即loss)以标量的形式表现出来(通过sum()转化标量之后在求梯度)(一般都是对一个标量进行求导,所以我们先对y进行求和再求导:见前面自动求导笔记)。# 不求和是向量,梯度算下来就是变成矩阵了,形状没有办法对应# 求梯度是对于l中每一个分量都是单独求的,l(loss)是一个向量每个元素表示一个样本的误差除以批量数# 如果没有no_grad,在评估模型的时候也进行梯度优化过程了#运行结果如下:epoch 1, loss 0.047467epoch 2, loss 0.000196epoch 3, loss 0.000047

8. 比较真实参数和通过训练学到的参数来评估训练的成功程度

13   print(f'w的估计误差: {true_w - w.reshape(true_w.shape)}')print(f'b的估计误差: {true_b - b}')# 运行结果如下:w的估计误差: tensor([ 0.0003, -0.0010], grad_fn=<SubBackward0>)b的估计误差: tensor([0.0010], grad_fn=<RsubBackward1>)

9. 不同超参数的选择会有什么样的不同的效果(修改超参数)

14   lr = 0.001         # 学习率很小的时候,发现损失很大,即使把epoch调大,loss仍然很大num_epochs = 3net = linregloss = squared_lossfor epoch in range(num_epochs):for X, y in data_iter(batch_size, features, labels):l = loss(net(X, w, b), y)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}')# 重新运行的时候要把上面w和b初始化的代码重新运行一下,重新初始化w和b# 运行结果如下:epoch 1, loss 13.166480epoch 2, loss 10.856715epoch 3, loss 8.952998
15   lr = 10           # 学习率很大,发现loss是一个notnumber,learning rate太大跑飞了num_epochs = 3net = linregloss = squared_lossfor epoch in range(num_epochs):for X, y in data_iter(batch_size, features, labels):l = loss(net(X, w, b), y)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}')# 重新运行的时候要把上面w和b初始化的代码重新运行一下,重新初始化w和b# 运行结果如下:epoch 1, loss nanepoch 2, loss nanepoch 3, loss nan

2.2 线性回归的简洁实现

2.2.1 分解代码

1. 通过使用深度学习框架来简洁的实现 线性回归模型 生成数据集

 import numpy as npimport torchfrom torch.utils import datafrom d2l import torch as d2ltrue_w = torch.tensor([2, -3.4])true_b = 4.2features, labels = d2l.synthetic_data(true_w, true_b, 1000)# 构造一个真实的w和b,然后通过人工数据合成函数生成我们需要的features和labels

2. 调用框架中现有的API来读取数据

 def load_array(data_arrays, batch_size, is_train=True):  """构造一个PyTorch数据迭代器。"""dataset = data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffle=is_train)batch_size = 10data_iter = load_array((features, labels), batch_size)next(iter(data_iter))# 假设我们已经有features和labels了,我们把他做成一个List传到tensor的dataset里面,把我们的X和y传进去,得到pytorch的一个dataset,(也就是说dataset里面是由两部分组成,features和labels)# dataset里面拿到数据集之后我们可以调用dataloader函数每次从里面随机挑选batch_size个样本出来,shuffle是指是否需要随机去打乱顺序,如果是train则是需要的# 构造了一个data_iter(可迭代对象)之后,然后用iter()转成python的一个迭代器,再通过next()函数得到一个X和y# TensorDataset:把输入的两类数据进行一一对应,一个*表示解包(见python书的P175,一个*表示使用一个已经存在的列表作为可变参数)# DataLoader:构建可迭代的数据装载器# enumerate:返回值有两个,一个是序号,一个是数据(包含训练数据和标签)#运行结果如下:[tensor([[-1.6881,  0.5505],[-0.5636, -0.4053],[-0.5162, -1.8817],[-2.0505, -1.2795],[-0.3089, -0.7559],[-1.0222,  0.6298],[-1.4214, -1.4331],[-0.3923,  0.3843],[-0.5184,  0.5693],[-1.1609,  1.0831]]),tensor([[-1.0346e+00],[ 4.4640e+00],[ 9.5674e+00],[ 4.4545e+00],[ 6.1507e+00],[-4.5305e-04],[ 6.2074e+00],[ 2.1123e+00],[ 1.2354e+00],[-1.8010e+00]])]

3. 使用框架的预定义好的层

 from torch import nn     # nn是neural network的缩写,里面有大量定义好的层net = nn.Sequential(nn.Linear(2, 1))# 线性回归用的是nn里面的线性层(或者说是全连接层),它唯一要指定的是输入和输出的维度是多少,此处的输入维度是2,输出维度是1# 线性回归就是简单的单层神经网络,为了以后的方便,放进一个Sequential的容器里面,可以理解为一个list of layers把层按顺序一个一个放在一起# Sequential是一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数# Sequential 是一个容器,里面可以放置任何层,不一定是线性层

4. 初始化模型参数

 net[0].weight.data.normal_(0, 0.01)net[0].bias.data.fill_(0)# 上面定义的net就是只有一个layer,可以通过索引[0]访问到layer,然后用.weight访问到他的w,用.data访问真实data,normal_表示使用正态分布来替换到data的值,使用的是均值为0方差为0.01来替换# 偏差直接设置为0#运行结果如下:tensor([0.])

5. 计算均方误差使用的是MSELoss类,也称平方L2范数

 loss = nn.MSELoss()  #在nn中均方误差叫MSELoss

6. 实例化SGD实例

 trainer = torch.optim.SGD(net.parameters(), lr=0.03)# SGD至少传入两个参数。net.parameters里面就包括了所有的参数,w和b;指定学习率0.03# Optimize.Stochastic Gradient Descent (随机梯度下降法)    optim是指optimize优化,sgd是优化的一种方法# L1范数是算数差,L2范数是算平方差

7. 训练过程代码与我们从零开始实现时所做的非常相似

 num_epochs = 3         # 迭代3个周期      for epoch in range(num_epochs):    for X, y in data_iter:     # 在data_iter里面一次一次的把minibatch(小批量)拿出来放进net里面l = loss(net(X), y)    # net()这里本身带了模型参数,不需要把w和b放进去了,net(X)是预测值,y是真实值,拿到预测值和真实值做Losstrainer.zero_grad()    # 梯度清零l.backward()           # 计算反向传播,这里pytorch已经做了sum就不需要在做sum了(loss是一个张量,求sum之后是标量)trainer.step()         # 有了梯度之后调用step()函数来进行一次模型的更新。调用step函数,从而分别更新权重和偏差l = loss(net(features), labels)  # 当扫完一遍数据之后,把所有的feature放进network中,和所有的Label作一次Lossprint(f'epoch {epoch + 1}, loss {l:f}')   # {l:f} 是指打印l,格式是浮点型# 运行结果如下:epoch 1, loss 0.000288epoch 2, loss 0.000094epoch 3, loss 0.000093

2.2.2 整体代码

 import numpy as npimport torchfrom torch import nnfrom torch.utils import datafrom d2l import torch as d2ltrue_w = torch.tensor([2, -3.4])true_b = 4.2features, labels = d2l.synthetic_data(true_w, true_b, 1000)# 读取数据def load_array(data_arrays, batch_size, is_train=True):  """构造一个PyTorch数据迭代器。"""dataset = data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffle=is_train)batch_size = 10data_iter = load_array((features, labels), batch_size)next(iter(data_iter))# 选用模型及初始化net = nn.Sequential(nn.Linear(2, 1))net[0].weight.data.normal_(0, 0.01)net[0].bias.data.fill_(0)# 定义损失loss = nn.MSELoss()  #优化trainer = torch.optim.SGD(net.parameters(), lr=0.03)# 开始训练num_epochs = 3               for epoch in range(num_epochs):    for X, y in data_iter:     l = loss(net(X), y)    trainer.zero_grad()    l.backward()           trainer.step()        l = loss(net(features), labels)  print(f'epoch {epoch + 1}, loss {l:f}') # 运行结果如下:epoch 1, loss 0.000238epoch 2, loss 0.000091epoch 3, loss 0.000090

三、直播问题解答

  1. 问:损失为什么要求平均:
    答:求平均和不求平均本质上没有区别,数据是等价的。在一个样本上,不求了平均,梯度数值会比较大,在损失上除以n就等价于在梯度上除以了n,使用随机梯度下降的话,如果loss不除以n,则把学习率除以n就可以了。如下公式:假如L中没有除以n,则梯度会变成以前的n倍大小,若想得到和之前一样,则把学习率除以n即可。学习率是使得这块不要太大也不用太小,所以除以n的好处是指:不管样本多大,不管批量有多大,我的梯度值差不多,使得调学习率的时候比较好调
    W t = W t − 1 − η ∂ L ∂ W t − 1 W_t = {W_{t-1} } -\eta \frac{\partial L}{\partial W_{t-1}} Wt​=Wt−1​−η∂Wt−1​∂L​

  2. 性回归的损失函数一般都是MSE(均方误差)

  3. batchsize越小越容易收敛,因为随机梯度下降理论上是带来了噪音,每次采样越小带来的噪音越多,但是噪音对神经网络是一件好事情,深度神经网络太复杂了,一定的噪音可以防止过拟合,模型的泛化性越好

  4. 梯度是线性的,损失函数是每个样本的损失函数相加,其等价于每个样本求梯度取均值

  5. 随机梯度下降法中的随机:批量大小是不变的(eg:128),每次在样本中随机采样128个元素

  6. 所有的运算子会自动加入计算图求梯度,detach可以把不需要求梯度的运算分离开

李沐动手学深度学习:08 线性回归(代码逐行理解)相关推荐

  1. 李沐动手学深度学习v2/总结1

    总结 编码过程 数据 数据预处理 模型 参数,初始化参数 超参数 损失函数,先计算损失,清空梯度(防止有累积的梯度),再对损失后向传播计算损失关于参数的梯度 优化算法,使用优化算法更新参数 训练求参数 ...

  2. 李沐动手学深度学习V2-全卷积网络FCN和代码实现

    一.全卷积网络FCN 1. 介绍 语义分割是对图像中的每个像素分类,全卷积网络(fully convolutional network,FCN)采用卷积神经网络实现了从图像像素到像素类别的变换 ,与前 ...

  3. 李沐动手学深度学习(pytorch版本)d2lzh_pytorch包的缺少安装问题

    学习深度学习时候,很多人参考的是李沐的动手学深度学习Pytorch版本(附上官方地址:https://tangshusen.me/Dive-into-DL-PyTorch/#/). 在学习3.5.1节 ...

  4. 【李沐动手学深度学习】读书笔记 01前言

    虽然之前已经学过这部分内容和深度学习中的基础知识,但总觉得学的不够系统扎实,所以希望再通过沐神的课程以及书籍,系统条理的学习一遍.在读书过程中,利用导图做了一下梳理,形成了这个读书笔记.如有侵权,请联 ...

  5. 关于李沐动手学深度学习(d2l)pytorch环境本地配置

    本地安装d2l 由于之前试了很多次d2l课本的安装方法失败了,这里提供一种我可以成功安装d2l包的方法. pytorch安装 首先安装cuda.cudnn.pytroch(gpu版本).可以参考这篇文 ...

  6. windows上配置深度学习(李沐-动手学深度学习)

    1.安装miniconda windows下安装,去清华大学开源镜像下载,速度比较快. 选中Miniconda3-latest-Windos-x86_64.exe下载安装包(目前最新的是py3.9) ...

  7. 李沐动手学深度学习第四章-4.9.环境和分布偏移

    我们从来没有想过数据最初从哪里来?以及我们计划最终如何处理模型的输出? 根据测试集的精度衡量,模型表现得非常出色. 但是当数据分布突然改变时,模型在部署中会出现灾难性的失败. 解决方案很简单(要求&q ...

  8. 3.4 Softmax回归【李沐动手学深度学习】

    目录 1. 从回归到多分类--均方损失 Softmax回归 2. Softmax和交叉熵损失 损失 梯度 3. 损失函数 3.1 均方损失(L2 Loss) 3.2 绝对值损失函数(L1 Loss) ...

  9. 李沐动手学深度学习V2-实战Kaggle比赛:狗的品种识别(ImageNet Dogs)和代码实现

    一. 实战Kaggle比赛:狗的品种识别(ImageNet Dogs) 1. 介绍 在这场比赛中,将识别120类不同品种的狗,这个数据集是ImageNet的数据集子集,与 CIFAR-10数据集中的图 ...

最新文章

  1. 函数的四种调用模式.上下文调用.call.apply
  2. 在家办公这些天整理的Kafka知识点大全
  3. mysql binary 查询_MYSQL的binary解决mysql数据大小写敏感问题 《转载》
  4. 迭代DOM集合的几种方法
  5. HMM隐马尔可夫模型(HMM)攻略
  6. vue-router学习第一天
  7. 记录利用CSS完美解决前端图片变形问题
  8. 不同图像的噪声,选用什么滤波器去噪,图像处理的噪声和处理方法
  9. OutputFormat类——Hadoop
  10. java基础提升(二):多线程、线程安全、线程状态、等待唤醒机制、线程池
  11. 在计算机系统中对文件执行彻底删除,怎么让电脑删除文件时可以直接彻底进行删除...
  12. 35.滚动 scroll
  13. 手把手教你:基于TensorFlow的语音识别系统
  14. 电脑派位系统(新生入学摇号) v2016
  15. 将一坨很大数据保存为json,然后读取json。
  16. 写文章时,你是不是也有这4个困惑?
  17. 公司的机票返利项目总结
  18. java selenium一一操作定位元素
  19. Tomcat 启动闪退问题解决集(八大类详细)
  20. ext-3 怎么将PDK的库包添加到CCS工程中

热门文章

  1. 华为nova到底有多持久? 我亲自测试了一下!
  2. 联通3G手机怎么用短信查询套餐包使用情况?
  3. 如何取消WPS的弹窗广告
  4. 【※世界上最丑最恶心的鱼※】
  5. C++之获取本机Ip地址 CString 可直接使用
  6. 电话销售实战经验总结
  7. 【评测】胎牛血清/血清怎么选?
  8. 四川信息职业技术学院计算机考纲,四川省普通高校对口招生统一考试大纲--信息技术二类(2019年版)...
  9. 分手之后可以挽回爱情吗?
  10. TinyMCE 富文本编辑器 ━━ 一键排版功能所需正则表达式整理及学习