神经网络:Zero2Hero 3 - Gradient calculation

news2024/11/20 9:20:04

Zero2Hero 4 - Gradient

  • 创建一个Value类,属性包含变量的值和梯度信息,并支持梯度计算。
  • 举例说明梯度反向计算过程。
  • 基于Value类构建MLP模型、并实现参数的更新。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

Value类

支持加法、乘法和Tanh的梯度计算

class Value:
  
    def __init__(self, data, _children=(), _op='', label=''):
        self.data = data
        self.grad = 0.0
        self._backward = lambda: None
        self._prev = set(_children)
        self._op = _op
        self.label = label

    def __repr__(self):
        return f"Value(data={self.data})"
  
    def __add__(self, other):
        out = Value(self.data + other.data, (self, other), '+')
    
        def _backward():
            self.grad += 1.0 * out.grad
            other.grad += 1.0 * out.grad
        out._backward = _backward
        return out

    def __mul__(self, other):
        out = Value(self.data * other.data, (self, other), '*')
    
        def _backward():
            self.grad += other.data * out.grad
            other.grad += self.data * out.grad
        out._backward = _backward
        return out
  
    def tanh(self):
        x = self.data
        t = (math.exp(2*x) - 1)/(math.exp(2*x) + 1)
        out = Value(t, (self, ), 'tanh')
    
        def _backward():
            self.grad += (1 - t**2) * out.grad
            out._backward = _backward
        return out
  
    def backward(self):
        # topological order all of the children in the graph
        topo = []
        visited = set()
        def build_topo(v):
            if v not in visited:
                visited.add(v)
                for child in v._prev:
                    build_topo(child)
                topo.append(v)
        build_topo(self)
        self.grad = 1.0
        for node in reversed(topo):
            node._backward()

例子1:加法

a = Value(-4.0)
b = Value(2.0)
c = a + b
c.backward()
print("a.grad : ",a.grad)
print("b.grad : ",b.grad)
a.grad :  1.0
b.grad :  1.0

计算图

a
b
c
+

简单分析以下,反向传播和梯度计算的过程:

c.prev : (Value(data=-4.0), Value(data=2.0))
a.grad = 0.0
b.grad = 0.0
  1. 创建空列表topo,和空集合visited。
  2. 调用build_topo()方法,把c加入列表topo中和集合visited中。
  3. 然后遍历c.prev中的Value对象,(Value(data=-4.0), Value(data=2.0))
    • 对每个Value对象都调用,build_topo()方法
    • 先检查Value是否存在visited集合中,否,加入列表和集合
    • 然后如每个Value对象的prev不为空,继续递归调用下去
  4. 直到遍历完计算图中全部的Value对象,初始化c.grad = 1.0
  5. 逆序遍历topo列,调用_backward()计算每个Value对象的梯度
    • c._backward():
    • a.grad += 1.0 * c.grad
    • b.grad += 1.0 * c.grad
    • a._backward = lambda : None
    • b._backward = lambda : None
  6. a.grad: 1.0,b.grad : 1.0

例子2:加法、乘法混合运算

a = Value(2.0, label='a')
b = Value(-3.0, label='b')
c = Value(10.0, label='c')
e = a*b; e.label = 'e'
d = e + c; d.label = 'd'
f = Value(-2.0, label='f')
L = d * f; L.label = 'L'
L
Value(data=-8.0)

正向传播过程:

a, data=2.0
b, data=-3.0
c, data=10.0
e, data=-6.0
d, data=4.0
f, data=-2.0
L, data=-8.0
*
+
*

反向传播过程:

a, data=2.0, grad=6.0
b, data=-3.0, grad=-4.0
c, data=10.0, grad=-2.0
e, data=-6.0, grad=-2.0
d, data=4.0, grad=-2.0
f, data=-2.0, grad=4.0
L, data=-8.0, grad=1.0
*
+
*
L.backward()
print("d.grad : ",d.grad)
print("f.grad : ",f.grad)
print("c.grad : ",c.grad)
print("e.grad : ",e.grad)
print("a.grad : ",a.grad)
print("b.grad : ",b.grad)
d.grad :  -2.0
f.grad :  4.0
c.grad :  -2.0
e.grad :  -2.0
a.grad :  6.0
b.grad :  -4.0

例子3:MLP

MLP结构如下:

Tanh
x 1
x 2
b
o 1
o 2
o
*
*
+
+
w 1
w 2
h o
n
# inputs x1,x2
x1 = Value(2.0, label='x1')
x2 = Value(0.0, label='x2')
# weights w
w1 = Value(-3.0, label='w1')
w2 = Value(1.0, label='w2')
# bias
b = Value(6.8813735870195432, label='b')
# x1*w1 + x2*w2  + b
o1 = x1*w1; o1.label = 'o1'
o2 = x2*w2; o2.label = 'o2'
ho = o1 + o2; ho.label = 'ho'
n = ho + b; n.label='n'
o = n.tanh(); o.label = 'o'
topo = []
visited = set()
def build_topo(v):
    if v not in visited:
        visited.add(v)
        for child in v._prev:
            build_topo(child)
        topo.append(v)
build_topo(o)
for val in reversed(topo):
    print(val.label,":",val,val.grad)
o : Value(data=0.7071067811865476) 0.0
n : Value(data=0.8813735870195432) 0.0
b : Value(data=6.881373587019543) 0.0
ho : Value(data=-6.0) 0.0
o2 : Value(data=0.0) 0.0
w2 : Value(data=1.0) 0.0
x2 : Value(data=0.0) 0.0
o1 : Value(data=-6.0) 0.0
x1 : Value(data=2.0) 0.0
w1 : Value(data=-3.0) 0.0

计算计算图中每个节点的梯度 step by step:

o.grad = 1.0
n.grad = n.grad + (1.0 - o.data**2) * o.grad
n.grad
0.4999999999999999
ho.grad = ho.grad + 1.0 * n.grad
b.grad = b.grad + 1.0 * n.grad
ho.grad, b.grad
(0.4999999999999999, 0.4999999999999999)
o1.grad = o1.grad + 1.0 * ho.grad
o2.grad = o2.grad + 1.0 * ho.grad
o1.grad, o2.grad
(0.4999999999999999, 0.4999999999999999)
x1.grad = x1.grad + w1.data * o1.grad
w1.grad = w1.grad + x1.data * o1.grad
x1.grad, w1.grad
(-1.4999999999999996, 0.9999999999999998)
x2.grad = x2.grad + w2.data * o2.grad
w2.grad = w2.grad + x2.data * o2.grad
x2.grad, w2.grad
(0.4999999999999999, 0.0)
for val in reversed(topo):
    print(val.label,":",val,val.grad)
o : Value(data=0.7071067811865476) 1.0
n : Value(data=0.8813735870195432) 0.4999999999999999
b : Value(data=6.881373587019543) 0.4999999999999999
ho : Value(data=-6.0) 0.4999999999999999
o2 : Value(data=0.0) 0.4999999999999999
w2 : Value(data=1.0) 0.0
x2 : Value(data=0.0) 0.4999999999999999
o1 : Value(data=-6.0) 0.4999999999999999
x1 : Value(data=2.0) -1.4999999999999996
w1 : Value(data=-3.0) 0.9999999999999998

扩展Value类

增加减法和指数运算

class Value:

    def __init__(self, data, _children=(), _op='', label=''):
        self.data = data
        self.grad = 0.0
        self._backward = lambda: None
        self._prev = set(_children)
        self._op = _op
        self.label = label

    def __repr__(self):
        return f"Value(data={self.data})"
  
    def __add__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data + other.data, (self, other), '+')
    
        def _backward():
            self.grad += 1.0 * out.grad
            other.grad += 1.0 * out.grad

        out._backward = _backward
        return out

    def __mul__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data * other.data, (self, other), '*')
    
        def _backward():
            self.grad += other.data * out.grad
            other.grad += self.data * out.grad

        out._backward = _backward 
        return out
  
    def __pow__(self, other):
        assert isinstance(other, (int, float)), "only supporting int/float powers for now"
        out = Value(self.data**other, (self,), f'**{other}')

        def _backward():
            self.grad += other * (self.data ** (other - 1)) * out.grad

        out._backward = _backward
        return out
  
    def __rmul__(self, other): # other * self
        return self * other

    def __truediv__(self, other): # self / other
        return self * other**-1

    def __neg__(self): # -self
        return self * -1

    def __sub__(self, other): # self - other
        return self + (-other)

    def __radd__(self, other): # other + self
        return self + other

    def tanh(self):
        x = self.data
        t = (math.exp(2*x) - 1)/(math.exp(2*x) + 1)
        out = Value(t, (self, ), 'tanh')
    
        def _backward():
            self.grad += (1 - t**2) * out.grad
        
        out._backward = _backward
        return out
  
    def exp(self):
        x = self.data
        out = Value(math.exp(x), (self, ), 'exp')
    
        def _backward():
            self.grad += out.data * out.grad  
        out._backward = _backward
        return out
  
  
    def backward(self):
        topo = []
        visited = set()
        def build_topo(v):
            if v not in visited:
                visited.add(v)
                for child in v._prev:
                    build_topo(child)
                topo.append(v)

        build_topo(self)
        self.grad = 1.0
        for node in reversed(topo):
            node._backward()

例子4

# inputs x1,x2
x1 = Value(2.0, label='x1')
x2 = Value(0.0, label='x2')
# weights w1,w2
w1 = Value(-3.0, label='w1')
w2 = Value(1.0, label='w2')
# bias of the neuron
b = Value(6.8813735870195432, label='b')
# x1*w1 + x2*w2 + b
h1 = x1*w1; h1.label = 'h1'
h2 = x2*w2; h2.label = 'h2'
h = h1 + h2; h.label = 'h'
n = h + b; n.label = 'n'
# ----
e = (2*n).exp()
o = (e - 1) / (e + 1)
# ----
o.label = 'o'
o.backward()

计算图

exp
**-1
1
2
x 1,grad=-1.5
x 2,grad=0.5
b,grad=0.5
h 1,grad=0.5
h 2,grad=0.5
o,grad=1.0
*
*
+
+
w 1,grad=1.0
w 2,grad=0.0
h,grad=0.5
n,grad=0.5
*
e,grad=0.0429
+
+
*
-1

简单实现MLP

class Neuron:
  
    def __init__(self, nin):
        self.w = [Value(np.random.uniform(-1,1)) for _ in range(nin)]
        self.b = Value(np.random.uniform(-1,1))
  
    def __call__(self, x):
        # w * x + b
        act = sum((wi*xi for wi, xi in zip(self.w, x)), self.b)
        out = act.tanh()
        return out
  
    def parameters(self):
        return self.w + [self.b]
class Layer:
  
    def __init__(self, nin, nout):
        self.neurons = [Neuron(nin) for _ in range(nout)]
  
    def __call__(self, x):
        outs = [n(x) for n in self.neurons]
        return outs[0] if len(outs) == 1 else outs
  
    def parameters(self):
        return [p for neuron in self.neurons for p in neuron.parameters()]
class MLP:
  
    def __init__(self, nin, nouts):
        sz = [nin] + nouts
        self.layers = [Layer(sz[i], sz[i+1]) for i in range(len(nouts))]
  
    def __call__(self, x):
        for layer in self.layers:
            x = layer(x)
        return x
  
    def parameters(self):
        return [p for layer in self.layers for p in layer.parameters()]
# 初始化模型
m = MLP(3, [4, 4, 1])
# 训练数据
xs = [
  [2.0, 3.0, -1.0],
  [3.0, -1.0, 0.5],
  [0.5, 1.0, 1.0],
  [1.0, 1.0, -1.0],
]
ys = [1.0, -1.0, -1.0, 1.0] # desired targets

训练MLP

loss_history = []
for k in range(10000):
  
    # forward pass
    ypred = [n(x) for x in xs]
    loss = sum((yout - ygt)**2 for ygt, yout in zip(ys, ypred))
  
    # backward pass
    for p in n.parameters():
        p.grad = 0.0
    loss.backward()

    # update
    for p in n.parameters():
        p.data += -0.1 * p.grad
    loss_history.append(loss.data)
plt.figure(figsize=(5,3))
plt.plot(loss_history)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

麻了呀,现在的00后都这么卷了吗?

在程序员职场上,什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事,我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事,可遇不可求,向他学习还来不及呢。 真正让人反感的,是技术平平&…

元宇宙应用领域-工业

元宇宙是指虚拟空间的总称,在这个虚拟空间中,用户可以像在现实世界一样,通过虚拟现实技术进行交互和体验。元宇宙应用领域非常广泛,如工业、游戏、娱乐、教育、医疗、房地产等。 工业领域中,元宇宙可用于在设计阶段帮…

【ThinkPHP6系列学习-1】下载并部署ThinkPHP6

目录 一、下载ThinkPHP6 二、目录结构 三、部署,配置虚拟域名 一、下载ThinkPHP6 在指定目录(www目录)下打开cmd,使用composer下载thinkphp6,命令后面的thinkphp6就是下载的目录名,可以随意修改。 comp…

流媒体传输协议相关小结——NALU、RTP、RTSP、RTMP、SDP等

前言: 本文是博主在学习流媒体时进行的小结,涉及内容较多。 由于流媒体协议说复杂也复杂,说简单也简单,复杂在需要考虑到每一位比特,简单在现成的轮子很多,只是会用往往已经足够。因此此文面向那些希望对流…

微信小程序开发实战课后习题解答————第三章(作业版)

一、填空题 1、微信小程序中用 navigationBar 组件可以实现导航栏 2、 微信小程序中能够实现轮播效果的组件是 swiper 3、 微信小程序中实现滚动条事件的绑定方法是 bindscroll 4、 微信小程序中引入音频的组件是 InnerAudioContext 5、 微信小程序…

ESMM - 完整空间多任务模型(阿里)

文章目录 1、动机2、模型结构 Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion RateESMM: Entire Space Multi-Task Model论文发表在SIGIR-2018,作者来自阿里妈妈盖坤团队。ESMM被提出用于解决pcvr建模中存在的两个非常…

ChatGPT + MindShow 制作PPT

🍏🍐🍊🍑🍒🍓🫐🥑🍋🍉🥝 ChatGPT MindShow 制作PPT 文章目录 🍐具体操作🐳结语 🍐具体操作 ChatGP…

异步线程:CompletableFuture、@Async

区别: 1.CompletableFuture是java中提供的一个异步执行类,Async是Spring提供的异步执行方法,当调用方法单独开启一个线程进行调用。 2.Async通常指定一个方法使用的异步方法调用,而CompletableFuture可以一个方法体内对请求体进行排序组合成…

SQL方式对hudi表进行操作

插入数据 查询数据 更新数据 删除数据 覆盖数据 修改表结构 修改分区 插入数据 默认情况下,如果提供了preCombineKey,则insert into的写操作类型为upsert,否则使用insert。 向非分区表插入数据 insert into hudi_cow_nonpcf_tbl sel…

通达信获取行情主站ip地址方法

最近使用通达信的pytdx来获取股票行情,需要通达信的行情站的ip地址与端口,这里做个记录防止忘了 1 测试代码 pip install pytdx from pytdx.hq import TdxHq_API api TdxHq_API() with api.connect(‘121.36.81.195’, 7709):#通达信行情站地址、端口…

摸摸索索总结下项目生命周期经验

业务调研 主要是完全跟技术无关,站在业务的角度去定义系统要干嘛 组织结构图 部门岗位 业务流程图 泳道图,一级业务流程二级业务流程 系统多个模块的整体业务流程 每个模块内部的业务流程 业务需求 需求分析 站在技术的角度,去分析系…

JavaWeb-JSP的学习

JSP 今日目标: 理解 JSP 及 JSP 原理能在 JSP中使用 EL表达式 和 JSTL标签理解 MVC模式 和 三层架构能完成品牌数据的增删改查功能 1、JSP 概述 JSP(全称:Java Server Pages):Java 服务端页面。是一种动态的网页技术…

深拷贝和浅拷贝-M

深拷贝和浅拷贝 数据类型分为:基本数据类型和引用类型 首先基本数据类型分为:number,string,boolean,null,undefined,symbol以及未来ES10新增的BigInt(任意精度整数)七类。 引用类型分为Array,Object,Function,正则等…

Charles 流量配置(弱网测试)、断点调试

一、流量配置 流量配置主要是用来检测软件(APP)在不同的网络环境下的一个表现,例如出现丢包闪退等情况. 流量配置主要有以下四步: 在 Charles 窗口中点击菜单 “Proxy” ,选择 "Throttle Setting" 进行网络…

ChatGPT副业赚钱·Midjourney之logo设计《猪八戒网站接单制作Logo》- 第10篇

历史文章 文心一言 PK ChatGPT,二者究竟谁更胜一筹 - 第7篇 用Midjourney画个美女,AI绘画也太强大了!!! - 第8篇 推荐一款idea神级代码插件【Bito-ChatGPT】而且免费!- 第9篇 ​ 悟纤:师傅&a…

合理利用Optional 来避免NPE

一、什么是Optional 在Java中什么异常最容易出现,那肯定是NullPointerException,空指针就像一个定时炸弹,总给我们带来些麻烦,在开发过程中都会碰到需要判断Null值以防止空指针的情况,以往的方式要么是抛异常&#xf…

(3)---STM32通信

目录 【1】通信的基础知识 【2】USART 【3】串口通信协议 【4】相关寄存器 串口控制寄存器 波特率寄存器 中断和状态寄存器 数据发送寄存器 数据接收寄存器 【5】 USART功能框图 【6】串口发送实验 实验要求 1.观察实物 2.分析原理图 3.STM32CubeMX配置 4、寄存器方式编写…

选择性搜索算法(Selective Search )——SS算法

文章目录 一、前言二、object Detection VS object Recognition(Selective Search的提出)2.1object recognition与object detection的关系2.2滑动窗口方法的局限性2.3Selective search算法的提出 三、Selective Search算法3.1什么是Selective Search&…

ChatGPT工作提效之遇强则强

ChatGPT工作提效之遇强则强 前言一、如何使用ChatGPT二、ChatGPT实战应用三、ChatGPT会叫的小孩有奶吃工具类的交互问答类的交互开发类的交互 前言 读《笑傲江湖》西湖比剑时,对于独孤九剑1的解读印象颇为深刻。令狐冲被任我行这个高手激发出许多精妙的剑招。这独孤…

消息队列-RabbitMQ

文章目录 1.什么是MQ1.1 特点1.2 MQ产品分类 2.RabbitMQ2.1.RabbitMQ介绍2.2.使用Docker安装RabbitMQ 3.SpringBoot中使用RabbitMQ3.1.SpringAMQP3.2使用步骤 1.什么是MQ RabbitMQ官方文档 消息队列(Message Queue,简称MQ):是在消息的传输过程中保存消…