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的矩阵

  • 相关阅读:
    python_paramiko_SSHException Invalid requirement, parse error at
    python_实现发送邮件功能
    人生格言-羊皮卷
    xshell的Solarized Dark配色方案
    vim常用的配置
    Hadoop1.2.1 HDFS原理
    Hadoop1.x目录结构及Eclipse导入Hadoop源码项目
    Hadoop1.2.1 出现Warning: $HADOOP_HOME is deprecated.的解决方案
    Hadoop1.2.1 启停的Shell 脚本分析
    Hadoop1.2.1 配置文件详解
  • 原文地址:https://www.cnblogs.com/mokou/p/14411717.html
Copyright © 2011-2022 走看看