成都网站建设设计

将想法与焦点和您一起共享

python未知函数梯度 python计算未知数

一文搞懂梯度下降&反向传播

如果把神经网络模型比作一个黑箱,把模型参数比作黑箱上面一个个小旋钮,那么根据通用近似理论(universal approximation theorem),只要黑箱上的旋钮数量足够多,而且每个旋钮都被调节到合适的位置,那这个模型就可以实现近乎任意功能(可以逼近任意的数学模型)。

创新互联专注于库车网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供库车营销型网站建设,库车网站制作、库车网页设计、库车网站官网定制、成都微信小程序服务,打造库车网络公司原创品牌,更为您提供库车网站排名全网营销落地服务。

显然,这些旋钮(参数)不是由人工调节的,所谓的机器学习,就是通过程序来自动调节这些参数。神经网络不仅参数众多(少则十几万,多则上亿),而且网络是由线性层和非线性层交替叠加而成,上层参数的变化会对下层的输出产生非线性的影响,因此,早期的神经网络流派一度无法往多层方向发展,因为他们找不到能用于任意多层网络的、简洁的自动调节参数的方法。

直到上世纪80年代,祖师爷辛顿发明了反向传播算法,用输出误差的均方差(就是loss值)一层一层递进地反馈到各层神经网络,用梯度下降法来调节每层网络的参数。至此,神经网络才得以开始它的深度之旅。

本文用python自己动手实现梯度下降和反向传播算法。 请点击这里 到Github上查看源码。

梯度下降法是一种将输出误差反馈到神经网络并自动调节参数的方法,它通过计算输出误差的loss值( J )对参数 W 的导数,并沿着导数的反方向来调节 W ,经过多次这样的操作,就能将输出误差减小到最小值,即曲线的最低点。

虽然Tensorflow、Pytorch这些框架都实现了自动求导的功能,但为了彻底理解参数调节的过程,还是有必要自己动手实现梯度下降和反向传播算法。我相信你和我一样,已经忘了之前学的微积分知识,因此,到可汗学院复习下 Calculus

和 Multivariable Calculus 是个不错的方法,或是拜读 这篇关于神经网络矩阵微积分的文章 。

Figure2是求导的基本公式,其中最重要的是 Chain Rule ,它通过引入中间变量,将“ y 对 x 求导”的过程转换为“ y 对中间变量 u 求导,再乘以 u 对 x 求导”,这样就将一个复杂的函数链求导简化为多个简单函数求导。

如果你不想涉及这些求导的细节,可以跳过具体的计算,领会其思想就好。

对于神经网络模型: Linear - ReLu - Linear - MSE(Loss function) 来说,反向传播就是根据链式法则对 求导,用输出误差的均方差(MSE)对模型的输出求导,并将导数传回上一层神经网络,用于它们来对 w 、 b 和 x (上上层的输出)求导,再将 x 的导数传回到它的上一层神经网络,由此将输出误差的均方差通过递进的方式反馈到各神经网络层。

对于 求导的第一步是为这个函数链引入中间变量:

接着第二步是对各中间变量求导,最后才是将这些导数乘起来。

首先,反向传播的起点是对loss function求导,即 。 :

mse_grad()之所以用unsqueeze(-1)给导数增加一个维度,是为了让导数的shape和tensor shape保持一致。

linear层的反向传播是对 求导,它也是一个函数链,也要先对中间变量求导再将所有导数相乘:

这些中间变量的导数分别是:

对向量 求导,指的是对向量所有的标量求偏导( ),即: ,这个横向量也称为y的梯度。

这里 ,是一个向量,因此, 求导,指的是y的所有标量(y_1, y_2, ..., y_n)对向量x求偏导,即:

这个矩阵称为雅克比矩阵,它是个对角矩阵,因为 ,因此 。

同理, 。

因此,所有中间导数相乘的结果:

lin_grad() 中的inp.g、w.g和b.g分别是求 的导数,以inp.g为例,它等于 ,且需要乘以前面各层的导数,即 outp.g @ w.t() ,之所以要用点积运算符(@)而不是标量相乘,是为了让它的导数shape和tensor shape保持一致。同理,w.g和b.g也是根据相同逻辑来计算的。

ReLu层的求导相对来说就简单多了,当输入 = 0时,导数为0,当输入 0时,导数为1。

求导运算终于结束了,接下来就是验证我们的反向传播是否正确。验证方法是将forward_backward()计算的导数和Pytorch自动微分得到的导数相比较,如果它们相近,就认为我们的反向传播算法是正确的。

首先,将计算好的参数导数保存到w1g、b1g、w2g和b2g中,再用Pytorch的自动微分来求w11、b11、w22和b22的导数。

最后,用np.allclose()来比较导数间的差异,如果有任何一个导数不相近,assert就会报错。结果证明,我们自己动手实现的算法是正确的。

反向传播是遵循链式法则的,它将前向传播的输出作为输入,输入作为输出,通过递进的方式将求导这个动作从后向前传递回各层。神经网络参数的求导需要进行矩阵微积分计算,根据这些导数的反方向来调节参数,就可以让模型的输出误差的优化到最小值。

欢迎关注和点赞,你的鼓励将是我创作的动力

如何使用python计算常微分方程?

常用形式

odeint(func, y0, t,args,Dfun)

一般这种形式就够用了。

下面是官方的例子,求解的是

D(D(y1))-t*y1=0

为了方便,采取D=d/dt。如果我们令初值

y1(0) = 1.0/3**(2.0/3.0)/gamma(2.0/3.0)

D(y1)(0) = -1.0/3**(1.0/3.0)/gamma(1.0/3.0)

这个微分方程的解y1=airy(t)。

令D(y1)=y0,就有这个常微分方程组。

D(y0)=t*y1

D(y1)=y0

Python求解该微分方程。

from scipy.integrate import odeint

from scipy.special import gamma, airy

y1_0 = 1.0/3**(2.0/3.0)/gamma(2.0/3.0)

y0_0 = -1.0/3**(1.0/3.0)/gamma(1.0/3.0)

y0 = [y0_0, y1_0]

def func(y, t):

... return [t*y[1],y[0]]

def gradient(y,t):

... return [[0,t],[1,0]]

x = arange(0,4.0, 0.01)

t = x

ychk = airy(x)[0]

y = odeint(func, y0, t)

y2 = odeint(func, y0, t, Dfun=gradient)

print ychk[:36:6]

[ 0.355028 0.339511 0.324068 0.308763 0.293658 0.278806]

print y[:36:6,1]

[ 0.355028 0.339511 0.324067 0.308763 0.293658 0.278806]

print y2[:36:6,1]

[ 0.355028 0.339511 0.324067 0.308763 0.293658 0.278806]

得到的解与精确值相比,误差相当小。

=======================================================================================================

args是额外的参数。

用法请参看下面的例子。这是一个洛仑兹曲线的求解,并且用matplotlib绘出空间曲线图。(来自《python科学计算》)

from scipy.integrate import odeint

import numpy as np

def lorenz(w, t, p, r, b):

# 给出位置矢量w,和三个参数p, r, b 计算出

# dx/dt, dy/dt, dz/dt 的值

x, y, z = w

# 直接与lorenz 的计算公式对应

return np.array([p*(y-x), x*(r-z)-y, x*y-b*z])

t = np.arange(0, 30, 0.01) # 创建时间点

# 调用ode 对lorenz 进行求解, 用两个不同的初始值

track1 = odeint(lorenz, (0.0, 1.00, 0.0), t, args=(10.0, 28.0, 3.0))

track2 = odeint(lorenz, (0.0, 1.01, 0.0), t, args=(10.0, 28.0, 3.0))

# 绘图

from mpl_toolkits.mplot3d import Axes3D

import matplotlib.pyplot as plt

fig = plt.figure()

ax = Axes3D(fig)

ax.plot(track1[:,0], track1[:,1], track1[:,2])

ax.plot(track2[:,0], track2[:,1], track2[:,2])

plt.show()

===========================================================================

scipy.integrate.odeint(func, y0, t, args=(), Dfun=None, col_deriv=0, full_output=0, ml=None, mu=None, rtol=None, atol=None, tcrit=None, h0=0.0, hmax=0.0, hmin=0.0, ixpr=0, mxstep=0, mxhnil=0, mxordn=12, mxords=5, printmessg=0)

计算常微分方程(组)

使用 FORTRAN库odepack中的lsoda解常微分方程。这个函数一般求解初值问题。

参数:

func : callable(y, t0, ...) 计算y在t0 处的导数。

y0 : 数组 y的初值条件(可以是矢量)

t : 数组 为求出y,这是一个时间点的序列。初值点应该是这个序列的第一个元素。

args : 元组 func的额外参数

Dfun : callable(y, t0, ...) 函数的梯度(Jacobian)。即雅可比多项式。

col_deriv : boolean. True,Dfun定义列向导数(更快),否则Dfun会定义横排导数

full_output : boolean 可选输出,如果为True 则返回一个字典,作为第二输出。

printmessg : boolean 是否打印convergence 消息。

返回: y : array, shape (len(y0), len(t))

数组,包含y值,每一个对应于时间序列中的t。初值y0 在第一排。

infodict : 字典,只有full_output == True 时,才会返回。

字典包含额为的输出信息。

键值:

‘hu’ vector of step sizes successfully used for each time step.

‘tcur’ vector with the value of t reached for each time step. (will always be at least as large as the input times).

‘tolsf’ vector of tolerance scale factors, greater than 1.0, computed when a request for too much accuracy was detected.

‘tsw’ value of t at the time of the last method switch (given for each time step)

‘nst’ cumulative number of time steps

‘nfe’ cumulative number of function evaluations for each time step

‘nje’ cumulative number of jacobian evaluations for each time step

‘nqu’ a vector of method orders for each successful step.

‘imxer’index of the component of largest magnitude in the weighted local error vector (e / ewt) on an error return, -1 otherwise.

‘lenrw’ the length of the double work array required.

‘leniw’ the length of integer work array required.

‘mused’a vector of method indicators for each successful time step: 1: adams (nonstiff), 2: bdf (stiff)

其他参数,官方网站和文档都没有明确说明。相关的资料,暂时也找不到。

OpenCV-Python系列四:图像分割(2)--梯度

上一期提到的图像阈值处理,不仅可以实现获取你想要的目标区域(作为mask使用),还可以帮你获取图像的边缘信息,那关于图像边缘,本期将从另外的角度来处理。

对边缘信息与背景差异较大的场景,你也可以使用threshold分割,不过若阈值不好选取,Laplacian梯度算子就不失为一直尝试方案,而且上网看看,关于Laplacian算子还可以用来判断图像的模糊程度,这个在相机的自动对焦当中,是否可以尝试判断下?

不过处理的效果并不理想,图像低灰阶部分边缘信息丢失严重。

对于sobel,laplacian算子我们可以使用cv2.filter2D()来实现,配置相应的核模板即可,如实现提取水平方向边缘信息:

你可以依据实际的应用需求来配置提取边缘的角度信息,这里以45度角(垂直向下逆时针旋转45度)为例:

对此,你可以采用下面的方式来解决:

python如何实现求函数的在一个连续区间的最值?

如果函数是确定的,可以用导数的方法进行计算,但是如果函数是不确定的,就需要用优化的方法来处理了,比如常用的梯度上升法,模拟退火等,希望可以帮到你。


分享文章:python未知函数梯度 python计算未知数
当前地址:http://chengdu.cdxwcx.cn/article/doipdoh.html