zoukankan      html  css  js  c++  java
  • 【2020.02.18】反向传播、线性回归

    本期的学习来源是:https://www.bilibili.com/video/BV1Y7411d7Ys?p=4

    代码来源于:https://blog.csdn.net/bit452/article/details/109643481

    反向传播

    简单的线性模型中,X是输入,W是权重,权重是训练的目标,而Y_hat则是最终的输出

    权重可不仅是数字,还可以是矩阵

    更新权重的过程,不是计算Y_hat对其的倒数,而是损失对权重的倒数

    (要使得损失最小而不是Y_hat最小

    image-20210218134239573

    在下图的网络中,每一条连线都是一个权重

    在初始的输入值只有五个的情况下(相当于5*1的矩阵输入),要转入第一层(有6个,相当于6*1的矩阵)

    那么就可以推导出权重矩阵是6*5的矩阵,说明有30个权重

    (例如第一层和第二层的话就是6*7=42个权重

    权重过多所以无法写出解析式

    因此我们要设计反向传播的算法,通过设计图,在图上传播梯度,根据链式法则求出梯度

    image-20210218135045602

    激活函数

    如下图中两层的情况

    图上MM表示的是矩阵乘法,ADD是向量加法

    image-20210218140148908

    但为了防止两个权重矩阵直接化简,如下图所示

    (否则上层的权重就毫无意义了,所有的深度最终只会变为一层

    image-20210218140616358

    因此要在每一次的末尾进行非线性函数的变化

    使得上一层的输出是下一层的输入

    这个非线性函数的变化被称为激活函数

    加了激活函数之后神经网络才有了能够逼近任意函数形式的能力

    其中的过程可以形象比喻成下图

    image-20210218141214819

    具体例子可以见下图

    算出局部的梯度,当拿到损失函数时候,就可以通过梯度传播,算出前部的梯度

    pytorch最后会将梯度存到变量里,而不是存到计算模块里

    image-20210218142229662

    先走前馈的过程得到Loss

    再走反向的过程求出梯度

    image-20210218142936077

    image-20210218143501692

    张量(tensor)

    张量中保存着data和grad,其中data可以是标量向量矩阵等

    grad指的是损失函数对权重的导数

    不论是data还是grad,都是一种tensor,而梯度默认为none

    image-20210218185509952

    因此在创建tensor时候默认是不会计算梯度grad的,所以在创建tensor时候需要在后面加入

    .requires_grad = True

    函数调用l.backward()方法后w.grad为Tensor

    故更新w.data时需使用w.grad.data。如果w需要计算梯度,那构建的计算图中,跟w相关的tensor都默认需要计算梯度

    image-20210218185845089

    从下列代码中就可以比较明晰地看出

    (注意type()那一行的代码

    image-20210218192937687

    import torch
    a = torch.Tensor([1.0])
    print('默认情况下的梯度:
      ',a)
    a.requires_grad = True # 或者 a.requires_grad_()
    print('设置requires_grad=True后的梯度:
      ',a)
    print('只显示数据:
      ',a.data)
    print(a.type())             # a的类型是tensor
    print(a.data.type())        # a.data的类型是tensor
    print(a.grad)
    print(type(a.grad))
    

    使用反向传播的方法例子

    在计算方面上使用.data,在输出时候对tensor对象使用.item()

    import torch
    x_data = [1.0, 2.0, 3.0]
    y_data = [2.0, 4.0, 6.0]
     
    w = torch.Tensor([1.0]) # w的初值为1.0
    w.requires_grad = True # 需要计算梯度
     
    def forward(x):
        return x*w  # w是一个Tensor
     
     
    def loss(x, y):
        y_pred = forward(x)
        return (y_pred - y)**2
    
    # 通过上面的三个样本,此时需要预测input=4时,前向传播的结果
    print("predict (before training)", 4, forward(4).item(), '
    ') 
     
    for epoch in range(100):
        for x, y in zip(x_data, y_data):
            l =loss(x,y) # 前向传播算出损失,l是一个张量,tensor主要是在建立计算图 forward, compute the loss
            # 使用.backward()函数的时候会自动反向传播算出梯度grad值,并记录在变量里
            l.backward() #  backward,compute grad for Tensor whose requires_grad set to True
            print('	grad:', x, y, w.grad.item())
            w.data = w.data - 0.01 * w.grad.data   # 权重更新时,需要用到标量,注意grad也是一个tensor,因此这里要加上.data
     
            w.grad.data.zero_() # after update, remember set the grad to zero
     
        print('progress:', epoch, l.item(), '
    ') # 输出轮数和损失函数
      # 取出loss使用l.item,不要直接使用l(l是tensor会构建计算图)
     
    print("predict (after training)", 4, forward(4).item())
    

    在更新完权重后,需要使用.grad.data.zero_(),清零本轮获得的梯度值

    如果缺乏这一步,实现的效果将会是,加上上一轮损失函数对权重的导数,这就不是我们想要的结果了

    image-20210218195930843

    线性回归

    主要分为以下步骤

    image-20210218205620848

    1. 准备数据集
    2. 设计模型(线性等
    3. 算出损失函数,构建优化器(其中在构建损失函数和计算反向梯度的之间要将梯度清零
    4. 不断训练已达到更新权重值的目的

    module建立

    (构造神经网络,是用来计算Y_hat的,例如Y_hat=w*x+b这个式子就是一个模型)

    列数是维度

    需要同时知道输出值和输入值的维度,才能知道权重值的维度

    但是最后要算出的loss值是标量(即使是tensor类型,也要求和/求平均值,求得平均值,

    模型要定义成一个类,基础的模板如下(会自动构造backward的函数)

    class LinearModel (torch.nn.Module):
    		def _init_(self):
    				super(LinearModel,self).__init__() # 父类的构造,这一步一定要有,just do it
    				self.linear=torch.nn.Linear(1,1) # torch.nn.Linear是pytorch的一个类,类的后面加括号,是在创建一个对象
            # 这一步创建对象包含了权重W和偏置b
    		def forward(self,x):
    				y_pred=self.linear(x)
    				return y_pred
          
    model = LinearModel() # 实例化直接调用
    

    image-20210218214018631

    torch.nn.Linear()具体解析

    image-20210218214743467

    父类的解析:https://www.runoob.com/python/python-func-super.html

    image-20210218213305777

    在通常情况下,y的表示方法为:行作为feature的数量,列作为样本数,在进行计算的时候再进行转置

    image-20210218214656533

    _call_()函数的使用

    如果想要调用函数的话,就得在类中定义__call__(),在默认情况下会在括号内自动补全__call__(self, *args, **kwargs)

    这个*args的意思是,当你未定义形参的时候,多传入的部分会被作为元组进行使用

    这个**kwargs的意思是,带等于号的关键字形参,会被当作词典

    具体的效果可以看下方

    元组列表数组的区别:https://www.cnblogs.com/260554904html/p/8125641.html

    image-20210218220631133

    构造损失函数、优化器

    优化器(optimizer)不会构成计算图

    在构建损失函数和计算反向梯度的之间要将梯度清零

    loss.backward() # 反向传播,计算梯度
    
    optimizer.step() # update 参数,即更新w和b的值
    

    训练过程

    在每一次的循环中要做的几件事

    1. 算出y_hat值

    2. 与实际y值算出损失函数(其中在构建损失函数和计算反向梯度的之间要将梯度清零

    3. 构建反向梯度

    4. 更新权重值

      image-20210218222946833

    image-20210218222801088

    最后在打印权重值的时候

    image-20210218223242437

    之所以这样子打印,是为了调用

    linear是module里面的类,然后经过实例化,item()可以直接取出数值

    image-20210218223203281

    放入测试集时

    image-20210218223430500

    这样子写的目的是要放入一个1*1的矩阵,打印出来也是一个1*1的矩阵

  • 相关阅读:
    DEDECMS里面DEDE函数解析
    dede数据库类使用方法 $dsql
    DEDE数据库语句 DEDESQL命令批量替换 SQL执行语句
    织梦DedeCms网站更换域名后文章图片路径批量修改
    DSP using MATLAB 示例 Example3.12
    DSP using MATLAB 示例 Example3.11
    DSP using MATLAB 示例 Example3.10
    DSP using MATLAB 示例Example3.9
    DSP using MATLAB 示例Example3.8
    DSP using MATLAB 示例Example3.7
  • 原文地址:https://www.cnblogs.com/mokou/p/14411717.html
Copyright © 2011-2022 走看看