牛顿法与拟牛顿法

1,502 阅读3分钟

1. 牛顿法

牛顿法(英语:Newton's method)又称为牛顿-拉弗森方法(英语:Newton-Raphson method),它是一种在实数域和复数域上近似求解方程的方法。

牛顿法的基本思想是使用函数 的泰勒级数的前面几项来寻找方程 的根。

牛顿法主要应用在两个方面,1:求方程的根;2:最优化(求解最值问题)。

1.1 求方程的根

选择一个接近函数零点的, 牛顿法对函数进行一阶泰勒展开:

,得到迭代方式:

迭代后求得方程的根

牛顿法有一个性质,就是能够保证二次收敛到方程的根。论述如下:

假设函数在开区间是二阶可导的,并存在函数的根.定义牛顿迭代法:

假设时,收敛到。若,对于足够大的 ,有:

于是,是二次收敛到

证明: 假设, 即 ,根据泰勒展开公式,

其中,介于之间。 由于,所以有

由于函数连续可导,且,只要足够接近,则有。两边除于得到

根据牛顿迭代法的定义,可以得到下式:

所以有,

一般地,会收敛到,由于介于之间,因此,收敛到收敛到。对应足够大的,有

证毕。

1.2 最优化(求取极值)

解决最优化问题的结构:

给定初始点,

  1. 确定搜索方向,即依照一定规则构造点处的下降方向为搜索方向;
  2. 确定步长因子,使目标函数值有某种意义下降;

a) 若满足某种终止条件,则停止迭代,得到近似最优解,

b) 否则,重复以上步骤。

牛顿法解决最优化问题的基本思想是利用目标函数的二次Taylor展开,并将其极小化。

假设目标函数具有二阶连续偏导数, 为目标函数的极小点,对目标函数在第次迭代值进行二阶泰勒展开:

其中,的一阶导数值,的海森矩阵:

函数 有极值的必要条件是在极值点处一阶导数为0。特别的当 是正定矩阵时,函数的极值为极小值。

对方程,根据上述牛顿迭代法可以求解,也可以二阶泰勒展开公式再进行求导:

其中,记,则有

迭代公式:

对于一元函数,上述迭代公式也可以写成:

1.3 牛顿法最优化的示例

import numpy as np
from sklearn import datasets
from sklearn.linear_model import LinearRegression
class Newton(object):
 def __init__(self,epochs=50):
  self.W = None
  self.epochs = epochs
    
 def get_loss(self, X, y, W,b):
  """
  计算损失 0.5*sum(y_pred-y)^2
  input: X(2 dim np.array):特征
    y(1 dim np.array):标签
    W(2 dim np.array):线性回归模型权重矩阵
  output:损失函数值
  """
  #print(np.dot(X,W))
  loss = 0.5*np.sum((y - np.dot(X,W)-b)**2)
  return loss
  
 def first_derivative(self,X,y):
  """
  计算一阶导数g = (y_pred - y)*x
  input: X(2 dim np.array):特征
    y(1 dim np.array):标签
    W(2 dim np.array):线性回归模型权重矩阵
  output:损失函数值
  """
  y_pred = np.dot(X,self.W) + self.b
  g = np.dot(X.T, np.array(y_pred - y))
  g_b = np.mean(y_pred-y)
  return g,g_b
   
 def second_derivative(self,X,y):
  """
  计算二阶导数 Hij = sum(X.T[i]*X.T[j])
  input: X(2 dim np.array):特征
    y(1 dim np.array):标签
  output:损失函数值
  """
  H = np.zeros(shape=(X.shape[1],X.shape[1]))
  H = np.dot(X.T, X)
  H_b = 1
  return H, H_b
  
 def fit(self, X, y):
    """
  线性回归 y = WX + b拟合,牛顿法求解
  input: X(2 dim np.array):特征
    y(1 dim np.array):标签
  output:拟合的线性回归
  """
  np.random.seed(10)
  self.W = np.random.normal(size=(X.shape[1]))
  self.b = 0
  for epoch in range(self.epochs):
   g,g_b = self.first_derivative(X,y)  # 一阶导数
   H,H_b = self.second_derivative(X,y)  # 二阶导数
   self.W = self.W - np.dot(np.linalg.pinv(H),g)
   self.b = self.b - 1/H_b*g_b
   print("itration:{} ".format(epoch), "loss:{:.4f}".format(
   self.get_loss(X, y , self.W,self.b)))
  

如果迭代次数epoch调大,两者的效果一样:

itration:495  loss:4.935776
itration:496  loss:4.935776
itration:497  loss:4.935776
itration:498  loss:4.935776
itration:499  loss:4.935776
predict_loss 3.20625981684797
3.2062598186600217