最优化方法Python计算:n元函数梯度与Hesse阵的数值计算

对于连续可微的 n n n元函数 f ( x ) f(\boldsymbol{x}) f(x) x = ( x 1 ⋮ x i ⋮ x n ) ∈ R n \boldsymbol{x}=\begin{pmatrix} x_1\\\vdots\\x_i\\\vdots\\x_n \end{pmatrix}\in\text{ℝ}^n x= x1xixn Rn,其梯度是由 f ( x ) f(\boldsymbol{x}) f(x) x \boldsymbol{x} x的各分量 x i x_i xi的偏导数 ∂ f / ∂ x i \partial f/\partial x_i f/xi i = 1 , 2 ⋯   , n i=1,2\cdots,n i=1,2,n)构成。事实上,偏导数 ∂ f / ∂ x i \partial f/\partial x_i f/xi是将 f ( x ) f(\boldsymbol{x}) f(x)中的 x i x_i xi视为唯一变量,其他均为常量的一元函数的导数。所以可利用中心差分(详见博文《最优化方法Python计算:一元函数导数的数值计算》)计算偏导数
∂ f ( x 0 ) ∂ x i ≈ f ( x 1 + e i Δ x / 2 ) − f ( x 1 − e i Δ x / 2 ) Δ x , i = 1 , 2 , ⋯   , n . \frac{\partial f(\boldsymbol{x}_0)}{\partial x_i}\approx\frac{f(\boldsymbol{x}_1+\boldsymbol{e}_i\Delta x/2)-f(\boldsymbol{x}_1-\boldsymbol{e}_i\Delta x/2)}{\Delta x},i=1,2,\cdots,n. xif(x0)Δxf(x1+eiΔx/2)f(x1eiΔx/2),i=1,2,,n.
其中,
e i = ( 0 ⋮ 1 ⋮ 0 ) i \begin{array}{cc} \boldsymbol{e}_i=\begin{pmatrix} 0\\\vdots\\1\\\vdots\\0 \end{pmatrix}&i \end{array} ei= 010 i
即第 i i i个分量为1其他分量均为0的向量,称为第 i i i个基本单位向量, i = 1 , 2 , ⋯   , n i=1,2,\cdots,n i=1,2,,n。利用上式,可定义如下计算 n n n元可微函数 f ( x ) f(\boldsymbol{x}) f(x)在点 x 1 \boldsymbol{x}_1 x1处梯度 ∇ f ( x 1 ) \nabla f(\boldsymbol{x}_1) f(x1)的Python函数。

import numpy as np						#导入numpy
def grad(f,x1):							#f为函数f(x),x1为自变量值
   dx=1e-7								#设置Δx=0.0000001
   n=x1.size							#x的维度
   e=np.eye(n)							#n个基本单位向量
   g0=np.zeros(n)						#梯度初始化为0向量
   for i in range(n):					#逐一计算偏导数
      g0[i]=(f(x1+e[i]*dx/2)-f(x1-e[i]*dx/2))/dx
return g0

程序的第2~9行定义计算n元可微函数梯度的Python函数grad,参数f表示函数 f ( x ) f(\boldsymbol{x}) f(x),x1表示自变量( n n n维向量)。第3行设置自变量增量 Δ x i = 1 1 0 7 \Delta x_i=\frac{1}{10^7} Δxi=1071为dx。第4行读取自变量所含分量个数n。第5行调用numpy的eye函数设置n个单位向量 e 1 , e 2 , ⋯   , e n \boldsymbol{e}_1,\boldsymbol{e}_2,\cdots,\boldsymbol{e}_n e1,e2,,en。第6行调用numpy的zeros函数将梯度g0初始化为0向量。第7~8行的for语句按前式逐一计算偏导数 ∂ f ( x 1 ) ∂ x i \frac{\partial f(\boldsymbol{x}_1)}{\partial x_i} xif(x1) i = 1 , 2 , ⋯   , n i=1,2,\cdots,n i=1,2,,n
相仿地,利用对广义导数的中心差分(详见博文《最优化方法Python计算:一元函数导数的数值计算》),可得二阶可微函数 f ( x ) f(\boldsymbol{x}) f(x)的二阶偏导数的计算公式
∂ 2 f ( x 1 ) ∂ x i ∂ x j ≈ [ f ( x 1 + ( e i + e j ) Δ x 2 ) + f ( x 1 − ( e i + e j ) Δ x 2 ) − f ( x 1 + ( e i − e j ) Δ x 2 ) − f ( x 1 − ( e i − e j ) Δ x 2 ) ] / Δ x 2 , 1 ≤ i , j ≤ n \frac{\partial^2f(\boldsymbol{x}_1)}{\partial x_i\partial x_j}\approx\left[ f\left(\boldsymbol{x}_1+(\boldsymbol{e}_i+\boldsymbol{e}_j)\frac{\Delta x}{2}\right)+f\left(\boldsymbol{x}_1-(\boldsymbol{e}_i+\boldsymbol{e}_j)\frac{\Delta x}{2}\right)\right.\\ \quad-\left.f\left(\boldsymbol{x}_1+(\boldsymbol{e}_i-\boldsymbol{e}_j)\frac{\Delta x}{2}\right)-f\left(\boldsymbol{x}_1-(\boldsymbol{e}_i-\boldsymbol{e}_j)\frac{\Delta x}{2}\right)\right]\big/\Delta x^2,1\leq i,j\leq n xixj2f(x1)[f(x1+(ei+ej)2Δx)+f(x1(ei+ej)2Δx)f(x1+(eiej)2Δx)f(x1(eiej)2Δx)]/Δx2,1i,jn
利用上式,定义计算 n n n元二阶可微函数 f ( x ) f(\boldsymbol{x}) f(x)在点 x 1 \boldsymbol{x}_1 x1处Hesse阵的Python函数如下

import numpy as np							#导入numpy
def hess(f,x1):								#f表示函数,x1表示自变量
   dx=7.5e-8								#设置自变量增量Δx
   n=x1.size								#向量维数
   e=np.eye(n)								#基本单位向量
   H0=np.zeros((n,n))						#Hesse阵初始化为0阵
   for i in range(n):						#逐一计算二阶偏导
      for j in range(n):
         H0[i,j]=(f(x1+(e[i]+e[j])*dx/2)+f(x1-(e[i]+e[j])*dx/2)-
	          f(x1+(e[i]-e[j])*dx/2)-f(x1-(e[i]-e[j])*dx/2))/dx**2
   return H0

程序的第2~1行定义 n n n元二阶可微函数 f ( x ) f(\boldsymbol{x}) f(x)在点 x 1 \boldsymbol{x}_1 x1处Hesse矩阵 H ( x 1 ) \boldsymbol{H}(\boldsymbol{x}_1) H(x1)的计算函数hess。第3行设置自变量增量 Δ x = 7.5 ⋅ 1 0 − 8 \Delta x=7.5\cdot10^{-8} Δx=7.5108为dx。第4行读取作为自变量的 x 1 \boldsymbol{x}_1 x1的维数n。第5行调用numpy的eye函数设置n个单位向量 e 1 , e 2 , ⋯   , e n \boldsymbol{e}_1,\boldsymbol{e}_2,\cdots,\boldsymbol{e}_n e1,e2,,en。第6行调用numpy的zeros函数将Hesse阵H0初始化为0阵。第7~10行的两重for循环按上式逐一计算 f ( x ) f(\boldsymbol{x}) f(x) x 1 \boldsymbol{x}_1 x1的二阶偏导数 ∂ 2 f ( x 1 ) ∂ x i ∂ x j \frac{\partial^2f(\boldsymbol{x}_1)}{\partial x_i\partial x_j} xixj2f(x1)并置于H[i,j]。
例1:计算函数 f ( x , y ) = x 1 4 + x 2 4 − 4 x 1 2 x 2 2 , ( x 1 , x 2 ) ∈ R 2 f(x,y)=x_1^4+x_2^4-4x_1^2x_2^2,(x_1,x_2)\in\text{ℝ}^2 f(x,y)=x14+x244x12x22,(x1,x2)R2 x 1 = ( 1 2 1 2 ) \boldsymbol{x}_1=\begin{pmatrix} \frac{1}{2}\\\frac{1}{2} \end{pmatrix} x1=(2121)的梯度和Hesse矩阵。
:下列代码调用上列程序定义的函数grad和hess计算 ∇ f ( x 1 ) \nabla f(\boldsymbol{x}_1) f(x1) H ( x 1 ) \boldsymbol{H}(\boldsymbol{x}_1) H(x1)

import numpy as np											#导入numpy
from utility import grad,hess								#导入grad,hess
f=lambda x:(x[0]**4)+(x[1]**4)-4*(x[0]**2)*(x[1]**2)		#设置函数
x1=np.array([0.5,0.5])										#设置自变量x1
print(grad(f,x1))											#计算梯度
print(hess(f,x1))											#计算Hesse阵

利用代码内部的注释信息不难理解上述程序。运行程序,输出

[-0.5 -0.5]
[[ 1.00166788 -4.00667154]
[-4.00667154  1.00166788]]

输出的第1行,表示 f ( x ) f(\boldsymbol{x}) f(x) x 1 = ( 1 2 1 2 ) \boldsymbol{x}_1=\begin{pmatrix} \frac{1}{2}\\\frac{1}{2} \end{pmatrix} x1=(2121)的梯度 ∇ f ( x 1 ) = ( − 1 2 − 1 2 ) \nabla f(\boldsymbol{x}_1)=\begin{pmatrix} -\frac{1}{2}\\-\frac{1}{2} \end{pmatrix} f(x1)=(2121)的近似,输出的第2~3行表示Hesse矩阵 H ( x 1 ) = ( 1 − 4 − 4 1 ) \boldsymbol{H}(\boldsymbol{x}_1)=\begin{pmatrix} 1&-4\\ -4&1 \end{pmatrix} H(x1)=(1441)的近似。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值