用单因子线性回归演示梯度下降和反向传播

反向传播示例

一、 单层网络的梯度下降和反向传播

单因子线性模型

  1. 线性模型定义

\hat{y}_i=ax_i+b

  1. 损失函数
    Loss = \frac{1}{2}\sum_i^n(\hat{y}_i-y_i)^2=\frac{1}{2}\sum_i^n(ax_i+b-y_i)^2

  2. 损失函数求偏导(偏导代入a的值即为梯度)

\frac{\partial Loss}{\partial a} =\frac{\partial \frac{1}{2}\sum_i^n(ax_i+b-y_i)^2}{\partial a}=\sum_i^n(ax_i+b-y_i)x_i \\ \frac{\partial Loss}{\partial b} =\frac{\partial \frac{1}{2}\sum_i^n(ax_i+b-y_i)^2}{\partial b}=\sum_i^n(ax_i+b-y_i)

  1. 更新参数

    定义学习率lr,代入梯度值,得到a更新后的值
    a=a-lr*\frac{\partial Loss}{\partial a}\\

  1. 代码示例

    1. 构建数据集
    import matplotlib.pyplot as plt
    import torch
    from torch.utils.data import TensorDataset, DataLoader
    
    x = torch.arange(1, 100, 2)
    noise = torch.randn(50)
    y = x * 2 + 10
    # y = y + noise
    
    t_data_set = TensorDataset(x, y)
    
    dl = DataLoader(t_data_set, batch_size=5)
    
    a = torch.tensor(20.0, requires_grad=True)
    b = torch.tensor(30.0, requires_grad=True)
    
    1. epoch 循环

      for epoch in range(100):
          all_loss = 0
          for xt, yt in dl:
              # 损失函数
              y_pred = a * xt + b
              loss = torch.sum((y_pred - yt) ** 2) / 2
              all_loss += loss.data
              # 梯度归零
              if a.grad:
                  a.grad.data.zero_()
                  b.grad.data.zero_()
              # 反向传播      
              loss.backward()
              # 更新数据
              a.data = a.data - a.grad.data * 1e-4
              b.data = b.data - b.grad.data * 1e-3
      

多层网络反向传播

单因子多层网络梯度下降和反向传播

  1. 多层线性模型定义
    复合函数:\hat{y}_i=a_2(a_1x_i+b_1)+b_2\\ 第一层:g(x_i) = a_1x_i+b_1\\ 第二层:f(x_i) = a_2g(x_i)+b_2\\ 需要注意的是,在这里需要把x_i理解为常量,即g(x_1)和g(x_2)是针对于a_1和b_1的不同的方程

  2. 损失函数
    Loss=\frac{1}{2}\sum_i^n(\hat{y}_i-y_i)^2=\frac{1}{2}\sum_i^n(a_2g(x_i)+b_2-y_i)^2=\frac{1}{2}\sum_i^n(a_2(a_1x_i+b_1)+b_2-y_i)^2

  3. 先对a2、b2求导
    \frac{\partial Loss}{\partial a_1} =\frac{\partial \frac{1}{2}\sum_i^n(a_2g(x_i)+b_2-y_i)^2}{\partial a_2}=\sum_i^n(a_2g(x_i)+b_2-y_i)g(x_i)
    \frac{\partial Loss}{\partial b} =\frac{\partial \frac{1}{2}\sum_i^n(a_2g(x_i)+b_2-y_i)^2}{\partial b_2}=\sum_i^n(a_2g(x_i)+b_2-y_i)

  4. 链式法则说明
    \frac{\partial y}{\partial x}=\frac{\partial y}{\partial u}\frac{\partial u}{\partial x}\\ 若函数\mu=\varphi(t),\nu=\psi(t)在点t可导,z=f(\mu, \nu),在点(\mu, \nu)处偏导连续\\ 则复合函数z=f(\varphi(t),\psi(t))在点t可导,且有链式法则:\\ \frac{dz}{dt} =\frac{\partial z}{\partial \mu}\frac{\partial \mu}{\partial t}+\frac{\partial z}{\partial \nu}\frac{\partial \nu}{\partial t}

  5. 基于链式法则对a1、b1求导
    Loss = \frac{1}{2}\sum_i^n(f(g(x_i))-y_i)^2\\ \frac{\partial Loss}{\partial a_1}=\sum_i^n[(f(g(x_i))-y_i)\frac{\partial f(g(x_i))}{\partial g(x_i)}\frac{\partial g(xi)}{\partial a_1}]=\sum_i^n(f(g(x_i))-y_i)a_2x_i\\ \frac{\partial Loss}{\partial b_1}=\sum_i^n[(f(g(x_i))-y_i)\frac{\partial f(g(x_i))}{\partial g(x_i)}\frac{\partial g(xi)}{\partial b_1}]=\sum_i^n(f(g(x_i))-y_i)a_2

  6. 代码示例

    import seaborn as sns
    import matplotlib.pyplot as plt
    import torch
    from torch.utils.data import TensorDataset, DataLoader
    import pandas as pd
    
    x = torch.arange(1, 100, 2)
    noise = torch.randn(50)
    y = x * 2 + 10
    # y = y + noise
    
    t_data_set = TensorDataset(x, y)
    
    dl = DataLoader(t_data_set, batch_size=5)
    
    # 两层神经网络
    a1 = torch.tensor(20.0, requires_grad=True)
    b1 = torch.tensor(30.0, requires_grad=True)
    
    a2 = torch.tensor(20.0, requires_grad=True)
    b2 = torch.tensor(30.0, requires_grad=True)
    flag = 0
    for epoch in range(1):
        all_loss = 0
        for xt, yt in dl:
            # 损失函数
            liner1 = a1 * xt + b1
            y_pred = a2 * liner1 + b2
            loss = torch.sum((y_pred - yt) ** 2) / 2
            all_loss += loss.data
            # 梯度归零
            if flag != 0:
                a1.grad.data.zero_()
                b1.grad.data.zero_()
                a2.grad.data.zero_()
                b2.grad.data.zero_()
            else:
                flag = 1
            loss.backward()
            print(f"a1:{a1.data}, a2:{a2.data}, b1:{b1.data},b2:{b2.data}")
            print(f"自动计算的梯度:a1_grad:{a1.grad}, a2_grad:{a2.grad}, b1_grad:{b1.grad},b_grad:{b2.grad}")
            print(f"x: {xt}, y: {yt}, 第一层结果:{liner1}, 第二层结果:{y_pred}")
            print(f"计算对a2梯度:{torch.sum(torch.mul(y_pred - yt, liner1))}")
            print(f"loss对liner1的梯度:{a2 * (y_pred - yt)}")
            print(f"liner1对a1的梯度:{xt}")
            print(f"loss对a1的梯度:{torch.sum(torch.mul(xt, a2 * (y_pred - yt)))}")
            print(f"loss对b1的梯度:{torch.sum(a2 * (y_pred - yt))}")
    
            # 更新数据
            a1.data = a1.data - a1.grad.data * 1e-4
            b1.data = b1.data - b1.grad.data * 1e-3
            a2.data = a2.data - a2.grad.data * 1e-4
            b2.data = b2.data - b2.grad.data * 1e-3
            break
        print(f"epoch:{epoch}, now a:{a1}, now b:{b1}, now loss: {all_loss / len(dl)}")
    
    # y_pre = a * x + b
    # plt.plot(x.detach().numpy(), y.detach().numpy(), 'go', label='data', alpha=0.3)
    # plt.plot(x.detach().numpy(), y_pre.detach().numpy(),
    #          label='predicted', alpha=1)
    # plt.legend()
    # plt.show()
    

多层网络多因子梯度下降和反向传播

  1. 代码示例
# 构建数据集
x = torch.randn(100, 2)
noise = torch.randn(100)

# y=10*x1+20*x2+3
y = torch.matmul(x, torch.tensor([2, 1], dtype=torch.float32)) + 3
y = y + noise

t_data_set = TensorDataset(x, y)
dl = DataLoader(t_data_set, batch_size=5)

def dy_lr(epoch_num):
    if epoch_num < 120:
        return 3e-3
    # if 100 <= epoch_num < 1000:
    #     return 1e-5
    else:
        return 1e-3


def cal_grad(line1_output, line2_grad, line1_para):
    """
    求导
    :param line1_output: 上一层的输出 batch_size * 上一层神经元树
    :param line2_grad: 本层的导数 batch_size * 本层 神经元树
    :param line1_para: 本层的参数,即上层神经元数*本层神经元数 line1_output*line1_para = line2
    :return: 本层的导数和参数的导数
    """
    line1_grad = torch.matmul(line1_para, line2_grad.unsqueeze(2))
    line1_a_grad = torch.matmul(line1_output.unsqueeze(2), line2_grad.unsqueeze(1))
    return line1_grad.squeeze(), line1_a_grad.squeeze()


def test_backward():
    """
    计算梯度
    :return:
    """
    line1_a = torch.randn(2, 2, requires_grad=True)
    line1_b = torch.randn(1, 2, requires_grad=True)

    line2_a = torch.randn(2, 2, requires_grad=True)
    line2_b = torch.randn(1, 2, requires_grad=True)

    line3_a = torch.randn(2, 1, requires_grad=True)
    line3_b = torch.randn(1, requires_grad=True)
    for xt, yt in dl:
        line1_out = torch.matmul(xt, line1_a) + line1_b
        line2_out = torch.matmul(line1_out, line2_a) + line2_b
        line3_out = torch.matmul(line2_out, line3_a) + line3_b
        output = line3_out.squeeze()
        loss = torch.sum((output - yt) ** 2)**2 / 2
        loss.backward()
        print("x:{}, y:{}, y_pred:{}".format(xt, yt, output))
        print("line1_a:{},\nline1_b:{},\nline2_a:{},\nline2_b:{},\nline3_a:{},\nline3_b:{}".format(
            line1_a, line1_b, line2_a, line2_b, line3_a, line3_b
        ))
        print("*" * 20)
        print("line1_out:{},\nline2_out:{},\nline3_out:{}".format(
            line1_out, line2_out, line3_out))
        print("*" * 20)
        print("loss:{}".format(loss))
        print("*" * 20)
        print("grad:\nline1_a:{},\nline1_b:{},\nline2_a:{},\nline2_b:{},\nline3_a:{},\nline3_b:{}".format(
            line1_a.grad, line1_b.grad, line2_a.grad, line2_b.grad, line3_a.grad, line3_b.grad
        ))
        print("*" * 20)
        # 1. Loss 对 y_pred求导
        # grad_loss_y_pre = output.detach() - yt
        grad_loss_y_pre = (torch.sum((output - yt) ** 2)*2*(output - yt)).detach()
        print(grad_loss_y_pre)
        # 逐层求导
        grad_loss_line3, grad_loss_line3_a = cal_grad(line2_out, grad_loss_y_pre.unsqueeze(1), line3_a)
        grad_loss_line2, grad_loss_line2_a = cal_grad(line1_out, grad_loss_line3, line2_a)
        grad_loss_line1, grad_loss_line1_a = cal_grad(xt, grad_loss_line2, line1_a)
        print(
            f"grad:line1_a:{grad_loss_line1_a.sum(dim=0)},\nline1:{grad_loss_line1.sum(dim=0)}")
        print(
            f"grad: line2_a:{grad_loss_line2_a.sum(dim=0)},\nline2:{grad_loss_line2.sum(dim=0)}")
        print(
            f"grad: line3_a:{grad_loss_line3_a.sum(dim=0)},\n line3:{grad_loss_line3.sum(dim=0)}")
        break
test_backward()
  1. pytorch 简易写法
# 构建数据集
x = torch.randn(100, 2)
noise = torch.randn(100)

# y=10*x1+20*x2+3
y = torch.matmul(x, torch.tensor([2, 1], dtype=torch.float32)) + 3
y = y + noise

t_data_set = TensorDataset(x, y)
dl = DataLoader(t_data_set, batch_size=5)


def test_by_torch_model():
    """
    通过torch自带模型计算结果
    :return:
    """
    model = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2), nn.Linear(2, 1))
    optim = Adam(model.parameters(), lr=1e-2)
    criterion = nn.MSELoss()
    for epoch in range(50):
        all_loss = 0
        for xt, yt in dl:
            outputs = model(xt)
            optim.zero_grad()
            loss = criterion(yt, outputs.squeeze())
            loss.backward()
            optim.step()
            all_loss += loss.detach().data
        print(f"epoch:{epoch},  now loss: {all_loss / len(dl)}")
    y_pred = model(x).squeeze().detach().numpy()
    plt.plot([i for i in range(len(y_pred))],
             y.detach().numpy(), 'go', label='data', alpha=0.3)
    plt.plot([i for i in range(len(y_pred))],
             y_pred, label='predicted', alpha=1)
    plt.legend()
    plt.show()

# test_backward()
test_by_torch_model()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容