Adaptive gradient descent without descent

Malitsky Y, Mishchenko K. Adaptive gradient descent without descent[J]. arXiv: Optimization and Control, 2019.

本文提出了一种自适应步长的梯度下降方法(以及多个变种方法), 并给了收敛性分析.

主要内容

主要问题:
\tag{1} \min_x \: f(x).
局部光滑的定义:
若可微函数f(x)在任意有界区域内光滑,即
\|\nabla f(x) - \nabla f(y)\| \le L_{\mathcal{C}} \|x-y\|, \quad \forall x, y \in \mathcal{C},
其中\mathcal{C}有界.

本文的一个基本假设是函数f(x)凸且局部光滑.

算法1 AdGD

在这里插入图片描述

定理1 ADGD-L

定理1. 假设f: \mathbb{R}^d \rightarrow \mathbb{R} 为凸函数且局部光滑. 则由算法1生成的序列(x^k)收敛到(1)的最优解, 且
f(\hat{x}^k) - f_* \le \frac{D}{2S_k} = \mathcal{O}(\frac{1}{k}),
其中\hat{x}^k := \frac{\sum_{i=1}^k \lambda_i x^i + \lambda_1 \theta_1 x^1}{S_k}, S_k:= \sum_{i=1}^k \lambda_i + \lambda_1 \theta.

算法2

在这里插入图片描述

L已知的情况下, 我们可以对算法1进行改进.

定理2

定理2 假设f凸且L光滑, 则由算法(2)生成的序列(x^k)同样使得
f(\hat{x}^k)-f_*=\mathcal{O}(\frac{1}{k})
成立.

算法3 ADGD-accel

在这里插入图片描述

这部分没有理论证明, 是作者基于Nesterov中的算法进行的改进.

算法4 Adaptive SGD

在这里插入图片描述

这个算法是对SGD的一个改进.

定理4

在这里插入图片描述

代码

f(x, y) = x^2+50y^2, 起点为(30, 15).

在这里插入图片描述



"""
adgd.py
"""

import numpy as np
import matplotlib.pyplot as plt


State = "Test"

class FuncMissingError(Exception): pass
class StateNotMatchError(Exception): pass

class AdGD:

    def __init__(self, x0, stepsize0, grad, func=None):
        self.func_grad = grad
        self.func = func
        self.points = [x0]
        self.points.append(self.calc_one(x0, self.calc_grad(x0),
                                         stepsize0))
        self.prestepsize = stepsize0
        self.theta = None


    def calc_grad(self, x):
        self.pregrad = self.func_grad(x)
        return self.pregrad

    def calc_one(self, x, grad, stepsize):
        return x - stepsize * grad

    def calc_stepsize(self, grad, pregrad):
        part2 = (
            np.linalg.norm(self.points[-1]
                          - self.points[-2]) /
            (np.linalg.norm(grad - pregrad) * 2)

        )
        if not self.theta:
            return part2
        else:
            part1 = np.sqrt(self.theta + 1) * self.prestepsize
            return min(part1, part2)

    def update_theta(self, stepsize):
        self.theta = stepsize / self.prestepsize
        self.prestepsize = stepsize

    def step(self):
        pregrad = self.pregrad
        prex = self.points[-1]
        grad = self.calc_grad(prex)
        stepsize = self.calc_stepsize(grad, pregrad)
        nextx = self.calc_one(prex, grad, stepsize)
        self.points.append(nextx)
        self.update_theta(stepsize)

    def multi_steps(self, times):
        for k in range(times):
            self.step()

    def plot(self):
        if self.func is None:
            raise FloatingPointError("func is not defined...")
        if State != "Test":
            raise StateNotMatchError()
        xs = np.array(self.points)
        x = np.linspace(-40, 40, 1000)
        y = np.linspace(-20, 20, 500)
        fig, ax = plt.subplots()
        X, Y = np.meshgrid(x, y)
        ax.contour(X, Y, self.func([X, Y]), colors='black')
        ax.plot(xs[:, 0], xs[:, 1], "+-")
        plt.show()


class AdGDL(AdGD):

    def __init__(self, x0, L, grad, func=None):
        super(AdGDL, self).__init__(x0, 1 / L, grad, func)
        self.lipschitz = L


    def calc_stepsize(self, grad, pregrad):
        lk = (
            np.linalg.norm(grad - pregrad) /
            np.linalg.norm(self.points[-1]
                          - self.points[-2])
        )
        part2 = 1 / (self.prestepsize * self.lipschitz ** 2) \
                 + 1 / (2 * lk)
        if not self.theta:
            return part2
        else:
            part1 = np.sqrt(self.theta + 1) * self.prestepsize
            return min(part1, part2)



class AdGDaccel(AdGD):

    def __init__(self, x0, stepsize0, convex0, grad, func=None):
        super(AdGDaccel, self).__init__(x0, stepsize0, grad, func)
        self.preconvex = convex0
        self.Theta = None
        self.prey = self.points[-1]

    def calc_convex(self, grad, pregrad):
        part2 = (
            (np.linalg.norm(grad - pregrad) * 2) /
                np.linalg.norm(self.points[-1]
                        - self.points[-2])
        ) / 2
        if not self.Theta:
            return part2
        else:
            part1 = np.sqrt(self.Theta + 1) * self.preconvex
            return min(part1, part2)

    def calc_beta(self, stepsize, convex):
        part1 = 1 / stepsize
        part2 = convex
        return (part1 - part2) / (part1 + part2)

    def calc_more(self, y, beta):
        nextx = y + beta * (y - self.prey)
        self.prey = y
        return nextx

    def update_Theta(self, convex):
        self.Theta = convex / self.preconvex
        self.preconvex = convex

    def step(self):
        pregrad = self.pregrad
        prex = self.points[-1]
        grad = self.calc_grad(prex)
        stepsize = self.calc_stepsize(grad, pregrad)
        convex = self.calc_convex(grad, pregrad)
        beta = self.calc_beta(stepsize, convex)
        y = self.calc_one(prex, grad, stepsize)
        nextx = self.calc_more(y, beta)
        self.points.append(nextx)
        self.update_theta(stepsize)
        self.update_Theta(convex)

config.json:



{
  "AdGD": {
    "stepsize0": 0.001
  },
  "AdGDL": {
    "L": 100
  },
  "AdGDaccel": {
    "stepsize0": 0.001,
    "convex0": 2.0
  }
}



"""
测试代码
"""



import numpy as np
import matplotlib.pyplot as plt
import json
from adgd import AdGD, AdGDL, AdGDaccel



with open("config.json", encoding="utf-8") as f:
    configs = json.load(f)

partial_x = lambda x: 2 * x
partial_y = lambda y: 100 * y
grad = lambda x: np.array([partial_x(x[0]),
                              partial_y(x[1])])
func = lambda x: x[0] ** 2 + 50 * x[1] ** 2


fig, ax = plt.subplots()
x = np.linspace(-10, 40, 500)
y = np.linspace(-10, 20, 500)
X, Y = np.meshgrid(x, y)
ax.contour(X, Y, func([X, Y]), colors='black')

def process(methods, times=50):
    for method in methods:
        method.multi_steps(times)

def initial(methods, **kwargs):
    instances = []
    for method in methods:
        config = configs[method.__name__]
        config.update(kwargs)
        instances.append(method(**config))
    return instances

def plot(methods):
    for method in methods:
        xs = np.array(method.points)
        ax.plot(xs[:, 0], xs[:, 1], "+-", label=method.__class__.__name__)
    plt.legend()
    plt.show()

x0 = np.array([30., 15.])




methods = [AdGD, AdGDL, AdGDaccel]
instances = initial(methods, x0=x0, grad=grad, func=func)
process(instances)
plot(instances)


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容