Python 算例实现Levenberg-Marquardt算法

    第一次写技术博文,有错误的地方欢迎指点。
    本博文是通过一个算例对LM算法的学习进行总结,编程语言是python。
    理论就不讲了,网上一大堆。
    拟合函数 y(x) = exp(a*x^2 + b * x + c) 中的参数 a, b, c 。废话不多说,直接上代码:

# -*- coding:utf-8 -*-
# autor:HuangYuliang
import numpy as np
from numpy import matrix as mat
from matplotlib import pyplot as plt
import random

n = 100
a1,b1,c1 = 1,3,2      # 这个是需要拟合的函数y(x) 的真实参数
h = np.linspace(0,1,n)       # 产生包含噪声的数据
y = [np.exp(a1*i**2+b1*i+c1)+random.gauss(0,4) for i in h]
y = mat(y)   # 转变为矩阵形式
 
def Func(abc,iput):   # 需要拟合的函数,abc是包含三个参数的一个矩阵[[a],[b],[c]]
    a = abc[0,0]
    b = abc[1,0]
    c = abc[2,0]
    return np.exp(a*iput**2+b*iput+c)

def Deriv(abc,iput,n):  # 对函数求偏导
    x1 = abc.copy()
    x2 = abc.copy()
    x1[n,0] -= 0.000001
    x2[n,0] += 0.000001
    p1 = Func(x1,iput)
    p2 = Func(x2,iput)
    d = (p2-p1)*1.0/(0.000002)
    return d
         
J = mat(np.zeros((n,3)))      #雅克比矩阵
fx = mat(np.zeros((n,1)))     # f(x)  100*1  误差
fx_tmp = mat(np.zeros((n,1)))
xk = mat([[0.8],[2.7],[1.5]]) # 参数初始化
lase_mse = 0
step = 0
u,v= 1,2
conve = 100

while (conve):
       
    mse,mse_tmp = 0,0
    step += 1  
    for i in range(n):
        fx[i] =  Func(xk,h[i]) - y[0,i]    # 注意不能写成  y - Func  ,否则发散
        mse += fx[i,0]**2
        
        for j in range(3): 
            J[i,j] = Deriv(xk,h[i],j) # 数值求导                                                    
    mse /= n  # 范围约束

    H = J.T*J + u*np.eye(3)   # 3*3
    dx = -H.I * J.T*fx        # 注意这里有一个负号,和fx = Func - y的符号要对应
    xk_tmp = xk.copy()
    xk_tmp += dx
    
    for j in range(n):
        fx_tmp[i] =  Func(xk_tmp,h[i]) - y[0,i]  
        mse_tmp += fx_tmp[i,0]**2
    mse_tmp /= n
     
    q = (mse - mse_tmp)/((0.5*dx.T*(u*dx - J.T*fx))[0,0])
    
    if q > 0:
        s = 1.0/3.0
        v = 2
        mse = mse_tmp
        xk = xk_tmp
        temp = 1 - pow(2*q-1,3)

        if s > temp:
            u = u*s
        else:
            u = u*temp
    else:
        u = u*v
        v = 2*v
        xk = xk_tmp

    print "step = %d,abs(mse-lase_mse) = %.8f" %(step,abs(mse-lase_mse))  
    if abs(mse-lase_mse)<0.000001:
        break
       
    lase_mse = mse  # 记录上一个 mse 的位置
    conve -= 1
print xk
 
z = [Func(xk,i) for i in h] #用拟合好的参数画图

plt.figure(0)
plt.scatter(h,y,s = 4)
plt.plot(h,z,'r')
plt.show()

拟合效果如下图所示:

拟合结果如下:

step = 1,abs(mse-lase_mse) = 6427.823881782
step = 2,abs(mse-lase_mse) = 1348.739146779
step = 3,abs(mse-lase_mse) = 7725.330011166
step = 4,abs(mse-lase_mse) = 51.226834643
step = 5,abs(mse-lase_mse) = 0.013664543
step = 6,abs(mse-lase_mse) = 0.000026830
step = 7,abs(mse-lase_mse) = 0.000000194
参数:[[ 0.97256842]
          [ 3.03242402]
          [ 1.99001528]]

可以看到,算法迭代了七次就收敛了。并且和真实值 [1, 3, 2] 很接近。

本博文参考了下篇博文:

http://blog.csdn.net/jinshengtao/article/details/53310804


  • 15
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
很抱歉,我之前给出的代码中使用的是LBFGS优化器,而不是Levenberg-Marquardt算法。目前PyTorch并没有内置的Levenberg-Marquardt优化器,但你可以使用PyTorch提供的自动微分功能手动实现Levenberg-Marquardt算法。下面是一个使用Levenberg-Marquardt算法训练BP神经网络的示例代码: ```python import torch import torch.nn as nn from torch.autograd import Variable # 定义BP神经网络模型 class Net(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(Net, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.fc2 = nn.Linear(hidden_size, output_size) def forward(self, x): x = torch.sigmoid(self.fc1(x)) x = self.fc2(x) return x # 定义损失函数和数据 input_size = 10 hidden_size = 20 output_size = 1 model = Net(input_size, hidden_size, output_size) criterion = nn.MSELoss() # 定义训练函数 def train(input, target): params = list(model.parameters()) num_params = sum(p.numel() for p in params if p.requires_grad) J = torch.zeros(target.size(0), num_params) e = torch.zeros(target.size()) def closure(): optimizer.zero_grad() output = model(input) loss = criterion(output, target) loss.backward() return loss optimizer = torch.optim.LBFGS(model.parameters(), lr=0.1, max_iter=20) def eval_Jacobian(e, J): for i in range(num_params): model.zero_grad() grad_params = torch.autograd.grad(e, params[i], retain_graph=True)[0] J[:, i] = grad_params.view(-1) for _ in range(10): output = model(input) e = output - target eval_Jacobian(e, J) H = torch.matmul(J.t(), J) diag_H = torch.diag(H) lambda_ = torch.max(diag_H) * 1e-2 H += lambda_ * torch.eye(num_params) delta = torch.matmul(torch.pinverse(H), torch.matmul(J.t(), e)) model.zero_grad() for param, delta_param in zip(params, delta): param.data -= delta_param.view(param.size()) optimizer.step(closure) # 训练模型 input = Variable(torch.randn(100, input_size)) target = Variable(torch.randn(100, output_size)) for i in range(10): train(input, target) # 使用训练好的模型进行预测 test_input = Variable(torch.randn(1, input_size)) print(model(test_input)) ``` 在上面的代码中,我们手动实现了Levenberg-Marquardt算法的关键步骤。首先,我们定义了BP神经网络模型、损失函数和数据。然后,在训练函数`train`中,我们使用自动微分功能计了Jacobian矩阵和误差项e,并利用这些信息更新了模型的参数。最后,我们使用训练好的模型进行了一个简单的预测。 希望这个示例代码能满足你的需求!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值