オプティマイザーを視覚化してみました。

私は2024年1月3日現在、AI機械学習フレームワークとしてPyTorchを利用し、Deep Convolutional Neural Network (DCNN)のモデルをディープ ラーニングにより学習させて画像処理、会社で画像検査システムの製作などをしております。

各種オプティマイザーの挙動のプロットの例 1

ここにオプティマイザーの振る舞いをMatplotlibで3Dグラフにプロットして視覚化した図を掲載します。

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 複雑な凹凸の有る3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
MatplotlibによるPyTorchのオプティマイザーの振る舞いを視覚化した図。
SGD, Lion, AdamW, AdaBelief, AdaDerivative の各種オプティマイザーについて。

各種オプティマイザーが x, y の2つのパラメーターを逐次変化させて目的関数の大域的最適解(極小値であり最小値 / global minima, 最も低い地点)への到達を目指します。

上記プロットでは Lion, AdamW, AdaBelief, AdaDerivativeは大域的最適解に到達出来ていますが、 SGD は局所的最適解(最小ではない極小値 / local minima)に嵌ってしまいました。

オプティマイザーについて

以下、オプティマイザーについての簡単な説明です。

機械学習フレームワークでは、まずモデルのネットワークを合成関数として計算グラフ化し、これに何らかのデータを入力して順伝播させます。
ネットワーク内を伝播する情報は、非線形の活性化関数でその一部が切り捨てられる事で加工され洗練されて行きます。
そしてモデルからの出力と所望の出力との誤差を得ます。

次に、順伝播時に記録されたパラメーターの勾配情報に基づいて微分の連鎖律を利用した自動微分機能で誤差を逆伝播してパラメーターを目的に適うように更新して最適化して行きます。
この手法を誤差逆伝播法と言います。

この際、勾配情報からどのようにノードの重み(畳み込みのカーネルの値など)とバイアスのパラメーターを更新するのかという方策を決めるのがオプティマイザーの役割となります。

各種オプティマイザーの挙動のプロットの例 2

オプティマイザーは歴史と共に様々な種類のものが開発されて来ました。
そして私達は悩むのです。
どのオプティマイザーが良いのかと。
普通はバランスの良いAdam (Adaptive Moment Estimation / 適応的モーメント推定)を利用するでしょう。
しかしより良い結果が出るオプティマイザーがあるならば利用せずには居られません。

片っ端から試したいところですが、機械学習には高性能なPCを利用してもかなりの時間が掛かります。

実際の各オプティマイザーの振る舞いを理解する為、Python, PyTorch, Matplotlibにてこれをアニメーション グラフの図にプロットして視覚化する事にしました。
他の方々が既に沢山やって来た事の真似です。

幾つもの関数で、掲載したプロットには無いものも含めて多数のオプティマイザーを試してみた結果としては、 “AdamW” と、 “AdaBelief” 、 “AdaDerivative” が優れているようでした。

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 凹凸の有る3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
Rosenbrock関数上を転がる各種オプティマイザーの挙動。

Lionは1匹だけで遠くへ行ってしまいましたね。
Lionオプティマイザーはステップ サイズが一定で、加速減速は無く、正負の符号だけが更新されます。
その為、学習率をスケジューラーなどで巧く調整してあげれば局所的最適解に嵌まらず素早く大域的最適解に到達出来る可能性は有ります。

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 滑らかな3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
滑らかなSphere関数上を転がる各種オプティマイザーの挙動。

そこそこ加速出来て余りオーヴァーシュートせず、バランスが良いのはAdaBeliefオプティマイザーでしょうか。

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 非常に多数の凹凸の有る3D曲面上を多数の色の着いた点が転がり、その軌跡が表示されている。
多峰性のRastrigin関数上を転がる各種オプティマイザーの挙動。

多峰性である Rastrigin関数上を各種オプティマイザーで探索させてみたところ、これは他の比較的滑らかな曲面上と異なり、学習率が小さいとほとんどの場合に局所最適解(local minima)に嵌って抜け出せなくなり、逆に学習率が大きいと曲面上を x, y パラメーターが暴れまわってどこかへ飛んで行ってしまいました。
人力で学習率の適切な値を見付けるのに苦労しました。

AdaBelief オプティマイザー

AdaBelief はBelief(信念)によって波に翻弄される事無く大域的最適解に向かう性質が有ります。
このBeliefはMomentum(慣性, 勢いvector)として働く勾配のEMA(Exponential Moving Average / 指数移動平均)を指針とし、現在の勾配の値との差を取り、これをRMSE(Root Mean Square Error / 誤差二乗平均平方根)として学習率の補正の分母に使用するようです。

この仕組みにより、勾配の偏差(平均との差)が大きな場合は学習率が下がり、偏差が小さい場合は勢い良く進む事が出来ます。
詳しくはAdaBelief Optimizerの論文著者のページを御覧下さい。
https://juntang-zhuang.github.io/adabelief/

Ubuntu PCにTeX LiveTexmakerをインストールして簡単な疑似コードでAdaBeliefの更新式を書いてみました。
元の論文の式とは記号や式が異なっておりますので御注意の上、元の論文を御確認下さい。

AdaFastオプティマイザーのアルゴリズムが疑似コードで記述された画像。
AdaBeliefオプティマイザーのアルゴリズム。

AdaBC オプティマイザー(AdaBeliefの一部改変)

2024年1月3日現在、私はResidual in Residual構造のDeep Convolutional Neural Network(DCNN)を試すなどしています。
しかし、residual blockについて480ch, 128段, 256層くらいに増やしてモデル サイズを大きくし、パラメーター数を増大させると、たとえResidual in Residual構造で勾配が伝播し易くても学習が巧く進まなくなって来ます。

そこで私は元々のAdaBeliefオプティマイザーを一部改変する事により、その様な大きなモデルでも学習出来るようにしました。

元のAdam系のオプティマイザーは上記疑似コードの様に1次と2次のEMAの両方にBias Correctionが適用されていますが、AdamD論文(https://arxiv.org/abs/2110.10828)と同様に、1次のEMAのバイアス補正を削除する事により初期ステップでの学習率を小さな値から緩やかに増大させるようにしてウォームアップにしています。

加えて、AdaBoundオプティマイザーが作られた理由でもありますが、学習率は大きくなり過ぎても小さくなり過ぎても良くないとの事ですので、momentumの下限を0.1、上限を2.0にclippingする事により学習の安定性を高めています。

AdaBCオプティマイザーのアルゴリズムが疑似コードで記述された画像。
AdaBC(AdaBeliefの一部改変)オプティマイザーの疑似コード。
AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 滑らかな3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
Sphere関数と各種オプティマイザーの挙動。

1次のバイアス補正を削除したAdaBeliefと、それにClippingを施したAdaBCの挙動を比べると、最適解付近で学習率が下がって来たところからLionオプティマイザーの様に角ばった動きになって来ますが差は僅かです。
尚、AdaBCはLion同様にパラメーターの更新幅に下限が有るので止まる事は有りません。

因みに、AdaBiliefの計算式のEMADEMA(Double EMA)やTEMA(Triple EMA)に変えたりしてみたところ、滑らかな曲面上での挙動は最良でしたが、多峰性のRastrigin関数曲面上では暴れるか局所的最適解に嵌るかしてしまい、具合が良くありませんでした。

各種オプティマイザーの挙動のプロットの例 3

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 複雑に波打った凹凸の有る3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
波がうねるような3D曲面上を x, y のパラメーターがオプティマイザーによって転がって行く様子。
AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 細かく複雑に波打った凹凸の有る3D曲面上を多数の色の着いた点が転がり落ち、その軌跡が表示されている。
細かく波がうねるような3D曲面上を x, y のパラメーターがオプティマイザーによって転がって行く様子。

波面のうねりを細かくしたら AdaBelief も局所的最適解に捕われてしまいました。

AdaDerivative オプティマイザー

因みに AdaDerivative はそれについての論文を元に、GitHubにコードが掲載されていた AdaBeliefの、勾配のEMAと現在の勾配との差分を取るところを、現在の勾配と1ステップ前の勾配との差分を取るように書き換えて使用しております。

“AdaDerivative optimizer: Adapting step-sizes by the derivative term in past gradient information – ScienceDirect” のURL:
https://www.sciencedirect.com/science/article/abs/pii/S095219762200745X

AdaDerivativeはその挙動は優れているのですが、残念な事に前回の勾配の値を保存する必要が有り、保存する状態値がLionが1つ、AdamW、AdaBeliefが2つに対して、AdaDelivativeは3つとなり、メモリーの使用量が1.5倍になってしまいます。
なるべく大きなモデルを学習させたいのでメモリー使用量が多いのは短所と言えます。

局所最小値と大域的最小値

Backpropagation微分の連鎖律により計算グラフの勾配を求め、勾配に基づいて最小値を目指す勾配降下法が 2025 年 7 月 10 日現在でも主流の最適化方法ですが、局所最小値に嵌って抜け出せず、大域的最小値まで到達出来ない場合が多々有ります。

温度パラメーターを利用してタイミング良くステップ サイズやノイズの量を調整して局所最適解からの脱出を促す仕組みも有りますが、これはハイパーパラメーターの調整が難儀です。

AI機械学習のオプティマイザーの振る舞いを視覚化したグラフの図。 凹凸の有る青色の 3D 螺旋曲面上を色とりどりの複数の点が転がり落ち、その軌跡が表示されている。 何れの点も最終的に螺旋の最も低い窪みの緑色の領域には到達出来ず、手前の浅い窪みに捕らわれている。
MatplotlibによるPyTorchのオプティマイザーの振る舞いを視覚化した図。 SGD, Lion, AdamW, AdaBelief, AdaDerivative, AdaBC の各種オプティマイザー。 局所最小値に捕らわれ、大域的最小値には到達出来ません。

上の画像は従来の勾配降下法のオプティマイザーの挙動で、やはり局所最小値に嵌っている様子が分かります。

下の画像は私の自作の Zeroth オプティマイザーで、Backpropagation をせず、詰まり微分をしていません
そして巧く局所最小値から脱出して大域的最小値まで到達出来ている様子が分かります。

自作の 0次オプティマイザーの振る舞いを視覚化したグラフの図。 凹凸の有る青色の 3D 螺旋曲面上を橙色の点が転がり落ち、その軌跡が表示されている。 最終的に螺旋の最も低い窪みの緑色の領域に点が辿り着いている。
Zeroth と名付けたオプティマイザーの挙動。 最終的に大域的最小値に到達出来ている。

因みにこの曲面は、Himmelblau 関数に螺旋曲面を足したものです。

サンプル コード

以下にオプティマイザーの振る舞いをプロットする為に書いたPythonスクリプトの拙いコードを掲載いたします。
また、GitHubにもプロット用のコードを掲載してあります。
https://github.com/ImpactCrater/OptimizerVisualizer/tree/main

#! /usr/bin/python3
# -*- coding: utf8 -*-

import os, time, random, re, glob
from os.path import expanduser
from pathlib import Path
import math
import random
import numpy
import torch
from torch.utils.data import DataLoader, Dataset
import torchvision
from torchvision import transforms, utils
import SGD
import Lion
import AdamW
import AdaDerivative
import AdaBelief
import AdaBC
import matplotlib
from matplotlib import pyplot
from matplotlib.colors import LogNorm
from matplotlib.animation import FuncAnimation



# Execution
'''
cd ~/Program/OptimizerVisualizer/
source venv3.12/bin/activate
python3 ~/Program/OptimizerVisualizer/optimizerVisualizerZeroth.py
deactivate
'''


# Mini Batch
miniBatchSize = 1

#Paths
# Home Path
homePath = expanduser("~")
plotImagePath = homePath + "/Program/OptimizerVisualizer/Plots"

objectiveFunctionNames = ["Rosenbrock", "SixHumpCamel", "Sphere", "Rastrigin", "Custom", "Custom2", "Custom3", "Custom4"]

numberOfFrames = 100


pi = torch.tensor(math.pi)
e = torch.tensor(math.e)
deltaZ = 1e-5




def rosenbrockFunction(x, y):
    scaleX = torch.tensor(2)
    scaleY = torch.tensor(2)
    shiftY = torch.tensor(1)
    scaleZ = torch.tensor(2509) # Maxima: 2509.00000000, Minima: 0.00000000
    x = x * scaleX
    y = - y * scaleY + shiftY
    a = 1
    b = 100
    z = a * (x - 1) ** 2 + b * (y - x ** 2) ** 2
    z = z / scaleZ * (1 - deltaZ) + deltaZ
    return z


def sixHumpCamelFunction(x, y):
    scaleX = torch.tensor(2)
    scaleY = torch.tensor(- 1)
    rangeZ = 5.49558687 + 1.03008306 # Maximum: 5.49558687, Minimum: - 1.03008306
    scaleZ = torch.tensor(rangeZ)
    shiftZ = torch.tensor(1.03008306)
    x = x * scaleX
    y = y * scaleY
    term1 = (4 - 2.1 * x ** 2 + (x ** 4) / 3) * x ** 2
    term2 = x * y
    term3 = (- 4 + 4 * y ** 2) * y ** 2
    z = term1 + term2 + term3
    z = (z + shiftZ) / scaleZ * (1 - deltaZ) + deltaZ
    return z


def sphereFunction(x, y):
    shiftY = torch.tensor(- 0.4)
    rangeZ = 2.46000004 # Maxima: 2.46000004, Minima: 0.00000000
    scaleZ = torch.tensor(rangeZ)
    y = - y + shiftY
    z = 0.5 * x ** 2 + y ** 2
    z = z / scaleZ * (1 - deltaZ) + deltaZ
    return z


def rastriginFunction(x, y):
    scaleX = torch.tensor(5.12)
    scaleY = torch.tensor(5.12)
    rangeZ = 79.98309326 # Maximum: 79.98309326, Minimum: 0.00000000
    scaleZ = torch.tensor(rangeZ)
    x = x * scaleX
    y = y * scaleY
    a = 2
    z = a + (x ** 2 - a * torch.cos(2 * pi * x)) + a + (y ** 2 - a * torch.cos(2 * pi * y))
    z = z / scaleZ * (1 - deltaZ) + deltaZ
    return z


def customFunction(x, y):
    scaleX1 = torch.tensor(2)
    scaleY1 = torch.tensor(- 1)
    rangeZ1 = 5.49558687 + 1.03008306 # Maximum: 5.49558687, Minimum: - 1.03008306
    scaleZ1 = torch.tensor(rangeZ1)
    shiftZ1 = torch.tensor(1.03008306)
    x1 = x * scaleX1
    y1 = y * scaleY1
    term1 = (4 - 2.1 * x1 ** 2 + (x1 ** 4) / 3) * x1 ** 2
    term2 = x1 * y1
    term3 = (- 4 + 4 * y1 ** 2) * y1 ** 2
    z1= term1 + term2 + term3
    z1 = (z1 + shiftZ1) / scaleZ1 * (1 - deltaZ) + deltaZ

    shiftX2 = torch.tensor(- 1)
    shiftY2 = torch.tensor(1)
    rangeZ2 = 7.90062523 - 0.00062500 # Maximum: 7.90062523, Minimum: 0.00062500
    scaleZ2 = torch.tensor(rangeZ2)
    shiftZ2 = torch.tensor(- 0.00062500)
    x2 = x + shiftX2
    y2 = y + shiftY2
    z2 = x2 ** 2 + y2 ** 2
    z2 = (z2 + shiftZ2) / scaleZ2 * (1 - deltaZ) + deltaZ

    rangeZ = 1.00000000 - 0.07055350 # Maximum: 1.00000000, Minimum: 0.07055350
    scaleZ = torch.tensor(rangeZ)
    shiftZ = torch.tensor(- 0.07055350)

    z = 0.5 * z1 + 0.5 * z2
    z = (z + shiftZ) / scaleZ * (1 - deltaZ) + deltaZ
    return z


def custom2Function(x, y):
    scaleX1 = torch.tensor(3)
    scaleY1 = torch.tensor(3)
    rangeZ1 = 6.80937624 + 7.99237823 # Maximum: 6.80937624, Minimum: -7.99237823
    scaleZ1 = torch.tensor(rangeZ1)
    shiftZ1 = torch.tensor(7.99237823)
    x1 = x * scaleX1
    y1 = y * scaleY1

    term1 = x1 - torch.sin(2 * x1 + 3 * y1) - torch.cos(3 * x1 - 5 * y1)
    term2 = y1 - torch.sin(x1 - 2 * y1) + torch.cos(x1 + 3 * y1)
    z1 = term1 + term2
    z1 = (z1 + shiftZ1) / scaleZ1 * (1 - deltaZ) + deltaZ

    shiftX2 = torch.tensor(- 0.5)
    shiftY2 = torch.tensor(0.5)
    rangeZ2 = 1.48755252 # Maximum: 1.48755252, Minimum: 0.00000000
    scaleZ2 = torch.tensor(rangeZ2)
    x2 = x + shiftX2
    y2 = y + shiftY2
    z2 = torch.sqrt((x2 ** 2 + y2 ** 2) / 2)
    z2 = z2 / scaleZ2 * (1 - deltaZ) + deltaZ

    rangeZ = 0.90223962 - 0.16485822 # Maximum: 0.90223962, Minimum: 0.16485822
    scaleZ = torch.tensor(rangeZ)
    shiftZ = torch.tensor(- 0.16485822)

    z = 0.3 * z1 + 0.7 * z2
    z = (z + shiftZ) / scaleZ * (1 - deltaZ) + deltaZ
    return z


def custom3Function(x, y):
    scaleX1 = torch.tensor(6)
    scaleY1 = torch.tensor(6)
    rangeZ1 = 13.22151566 + 13.42661095 # Maximum: 13.22151566, Minimum: -13.42661095
    scaleZ1 = torch.tensor(rangeZ1)
    shiftZ1 = torch.tensor(13.42661095)
    x1 = x * scaleX1
    y1 = y * scaleY1

    term1 = x1 - torch.sin(2 * x1 + 3 * y1) - torch.cos(3 * x1 - 5 * y1)
    term2 = y1 - torch.sin(x1 - 2 * y1) + torch.cos(x1 + 3 * y1)
    z1 = term1 + term2
    z1 = (z1 + shiftZ1) / scaleZ1 * (1 - deltaZ) + deltaZ

    shiftX2 = torch.tensor(- 0.5)
    shiftY2 = torch.tensor(0.5)
    rangeZ2 = 1.48755252 # Maximum: 1.48755252, Minimum: 0.00000000
    scaleZ2 = torch.tensor(rangeZ2)
    x2 = x + shiftX2
    y2 = y + shiftY2
    z2 = torch.sqrt((x2 ** 2 + y2 ** 2) / 2)
    z2 = z2 / scaleZ2 * (1 - deltaZ) + deltaZ

    rangeZ = 0.86382282 - 0.15205750 # Maximum: 0.86382282, Minimum: 0.15205750
    scaleZ = torch.tensor(rangeZ)
    shiftZ = torch.tensor(- 0.15205750)

    z = 0.3 * z1 + 0.7 * z2
    z = (z + shiftZ) / scaleZ * (1 - deltaZ) + deltaZ
    return z
    return z


def custom4Function(x, y):
    scaleX1 = torch.tensor(- 5)
    scaleY1 = torch.tensor(- 5)
    rangeZ1 = 0.97691298 - 0.00002000 # Maximum: 0.97691298, Minimum: 0.00002000
    scaleZ1 = torch.tensor(rangeZ1)
    shiftZ1 = torch.tensor(- 0.00002000)
    x1 = x * scaleX1
    y1 = y * scaleY1
    z1 = (x1 ** 2 + y1 - 11) ** 2 + (x1 + y1 ** 2 - 7) ** 2
    z1 = torch.tanh(z1 / 400)
    z1 = (z1 + shiftZ1) / scaleZ1 * (1 - deltaZ) + deltaZ

    shiftX2 = torch.tensor(0.)
    shiftY2 = torch.tensor(0.)
    rangeZ2 = 4.97492075 + 1.35411644 # Maximum: 4.97492075, Minimum: -1.35411644
    scaleZ2 = torch.tensor(rangeZ2)
    shiftZ2 = torch.tensor(1.35411644)
    x2 = x + shiftX2
    y2 = y + shiftY2
    a = 0.9
    r = torch.sqrt(x2 ** 2 + y2 ** 2)
    Theta = torch.arctan2(y2, x2) - torch.pi / 2 # [- π, π]
    Theta = torch.where(Theta < 0, Theta + 2 * torch.pi, Theta)
    z2 = (r - a * Theta) + 4.
    z2 = (z2 + shiftZ2) / scaleZ2 * (1 - deltaZ) + deltaZ

    rangeZ = 2.39048314 - 0.34079576 # Maximum: 2.39048314, Minimum: 0.34079576
    scaleZ = torch.tensor(rangeZ)
    shiftZ = torch.tensor(- 0.34079576)

    z = z1 + 1.5 * z2
    #z = z2
    z = (z + shiftZ) / scaleZ * (1 - deltaZ) + deltaZ
    return z



def objectiveFunction(objectiveFunctionName, x, y):
    if objectiveFunctionName == 'Rosenbrock':
        return rosenbrockFunction(x, y)

    elif objectiveFunctionName == 'SixHumpCamel':
        return sixHumpCamelFunction(x, y)

    elif objectiveFunctionName == 'Sphere':
        return sphereFunction(x, y)

    elif objectiveFunctionName == 'Rastrigin':
        return rastriginFunction(x, y)

    elif objectiveFunctionName == 'Custom':
        return customFunction(x, y)

    elif objectiveFunctionName == 'Custom2':
        return custom2Function(x, y)

    elif objectiveFunctionName == 'Custom3':
        return custom3Function(x, y)

    elif objectiveFunctionName == 'Custom4':
        return custom4Function(x, y)




print("Checking the existence of the directory to save plotted images.")
if os.path.isdir(plotImagePath):
    print("Okay.")
else:
    print("Making the directory.")
    os.mkdir(plotImagePath)
    print(plotImagePath + " has been created.")




optimizerDictionary = {'SGD': {}, 'Lion': {}, 'AdamW': {}, 'AdaBelief': {}, 'AdaDerivative': {}, 'AdaBC': {}}

optimizerDictionary['SGD']['color'] = 'magenta'
optimizerDictionary['Lion']['color'] = 'darkorange'
optimizerDictionary['AdamW']['color'] = 'blue'
optimizerDictionary['AdaBelief']['color'] = 'red'
optimizerDictionary['AdaDerivative']['color'] = 'yellow'
optimizerDictionary['AdaBC']['color'] = 'lawngreen'




for objectiveFunctionName in objectiveFunctionNames:
    optimizerDictionary['SGD']['learningRate'] = 1e-1
    optimizerDictionary['Lion']['learningRate'] = 1e-1
    optimizerDictionary['AdamW']['learningRate'] = 1e-1
    optimizerDictionary['AdaBelief']['learningRate'] = 1e-1
    optimizerDictionary['AdaDerivative']['learningRate'] = 1e-1
    optimizerDictionary['AdaBC']['learningRate'] = 1e-1




    for key in optimizerDictionary:
        if objectiveFunctionName == 'Rastrigin':
            x = torch.tensor(- 0.7, requires_grad=True)
            y = torch.tensor(0.8, requires_grad=True)
        elif objectiveFunctionName == 'Custom3':
            x = torch.tensor(- 0.9, requires_grad=True)
            y = torch.tensor(0.8, requires_grad=True)
        else:
            x = torch.tensor(- 0.9, requires_grad=True)
            y = torch.tensor(0.9, requires_grad=True)
        optimizerDictionary[key]['parameters'] = [x, y]
        optimizerDictionary[key]['xList'] = []
        optimizerDictionary[key]['yList'] = []
        optimizerDictionary[key]['zList'] = []


        if key == 'SGD':
            optimizerDictionary['SGD']['optimizer'] = SGD.SGD(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['SGD']['learningRate'], weight_decay=1e-16)
        elif key == 'Lion':
            optimizerDictionary['Lion']['optimizer'] = Lion.Lion(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['Lion']['learningRate'], betas=(0.9, 0.99), weight_decay=1e-16)
        elif key == 'AdamW':
            optimizerDictionary['AdamW']['optimizer'] = AdamW.AdamW(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['AdamW']['learningRate'], betas=(0.9, 0.999), eps=1e-16, weight_decay=1e-16)
        elif key == 'AdaBelief':
            optimizerDictionary['AdaBelief']['optimizer'] = AdaBelief.AdaBelief(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['AdaBelief']['learningRate'], betas=(0.9, 0.999), eps=1e-16, weight_decay=1e-16)
        elif key == 'AdaDerivative':
            optimizerDictionary['AdaDerivative']['optimizer'] = AdaDerivative.AdaDerivative(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['AdaDerivative']['learningRate'], betas=(0.9, 0.999), eps=1e-16, weight_decay=1e-16)
        elif key == 'AdaBC':
            optimizerDictionary['AdaBC']['optimizer'] = AdaBC.AdaBC(params=optimizerDictionary[key]['parameters'], lr=optimizerDictionary['AdaBC']['learningRate'], betas=(0.9, 0.999), eps=1e-16, weight_decay=1e-16)





    X = numpy.arange(-1, 1, 0.025)
    Y = numpy.arange(-1, 1, 0.025)
    X, Y = numpy.meshgrid(X, Y)
    X = torch.from_numpy(X.astype(numpy.float32)).clone()
    Y = torch.from_numpy(Y.astype(numpy.float32)).clone()
    Z = objectiveFunction(objectiveFunctionName, X, Y)
    maximum = torch.max(Z)
    print("Maximum: {:.8f}".format(maximum))
    minimum = torch.min(Z)
    print("Minimum: {:.8f}".format(minimum))
     
    figure1 = pyplot.figure(figsize=(20, 10))
    figure1.subplots_adjust(left=0., right=1., bottom=0., top=1., wspace=0.)
    gridspec1 = figure1.add_gridspec(20, 40)
    axis1 = figure1.add_subplot(gridspec1[1:19, 0:20], projection='3d', elev=45, azim=330, zorder=1, facecolor=(0.9, 0.9, 0.9, 0.25))
    axis1.xaxis.pane.set_facecolor((0.75, 0.75, 0.75, 0.5))
    axis1.yaxis.pane.set_facecolor((0.75, 0.75, 0.75, 0.5))
    axis1.zaxis.pane.set_facecolor((0.75, 0.75, 0.75, 0.5))
    axis2 = figure1.add_subplot(gridspec1[4:16, 27:39], facecolor=(0.9, 0.9, 0.9, 0.5))
    #contour1.clabel(fmt='%1.1f', fontsize=8)


    def update(i):
        print('Step: {:3d}'.format(i + 1))

        axis1.cla()
        axis2.cla()
        plotWireframe1 = axis1.plot_wireframe(X, Y, Z, rstride=2, cstride=2, zorder=1)
        plotSurface1 = axis1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='ocean', norm=LogNorm(), alpha=0.8, zorder=1)
        plot2dContour = axis2.pcolormesh(X, Y, Z, cmap='ocean', norm=LogNorm(), zorder=1)
        contour1 = axis2.contour(X, Y, Z,  levels=16, colors=['black'])

        for key in optimizerDictionary:
            optimizerDictionary[key]['optimizer'].zero_grad()
            outputs = objectiveFunction(objectiveFunctionName, optimizerDictionary[key]['parameters'][0], optimizerDictionary[key]['parameters'][1])
            outputs.backward()
            optimizerDictionary[key]['xList'].append(optimizerDictionary[key]['parameters'][0].item())
            optimizerDictionary[key]['yList'].append(optimizerDictionary[key]['parameters'][1].item())
            optimizerDictionary[key]['zList'].append(outputs.item())
            optimizerDictionary[key]['optimizer'].step()
            labelString = key + ': lr ' + str(optimizerDictionary[key]['learningRate'])
            plot1 = axis1.plot(optimizerDictionary[key]['xList'], optimizerDictionary[key]['yList'], optimizerDictionary[key]['zList'], color=optimizerDictionary[key]['color'], marker='',  linestyle='-', alpha=0.8, markersize=1, zorder=4)
            plot1 = axis1.plot(optimizerDictionary[key]['xList'][-1], optimizerDictionary[key]['yList'][-1], optimizerDictionary[key]['zList'][-1], color=optimizerDictionary[key]['color'], marker='o',  linestyle='-', alpha=1.0, markersize=8, zorder=4, label=labelString)
            plotScatter1 = axis2.plot(optimizerDictionary[key]['xList'], optimizerDictionary[key]['yList'], color =optimizerDictionary[key]['color'], marker='',  linestyle='-', alpha=0.8, markersize=1, zorder=2)
            plotScatter1 = axis2.plot(optimizerDictionary[key]['xList'][-1], optimizerDictionary[key]['yList'][-1], color =optimizerDictionary[key]['color'], marker='o',  linestyle='-', alpha=1.0, markersize=8, zorder=2)

            #axis1.set_xlabel('x', fontsize=8)
            #axis1.set_ylabel('y', fontsize=8)
            #axis1.set_zlabel('z', fontsize=8)

            axis1.set_xlim(- 1, 1)
            axis1.set_ylim(- 1, 1)
            axis2.set_xlim(- 1, 1)
            axis2.set_ylim(- 1, 1)

            axis1.legend(bbox_to_anchor=(1., 1.), loc='upper left', fontsize=16)

    #pyplot.savefig(plotImagePath + '/{}_optim_{}.png'.format(objectiveFunctionName, key))
    animation = FuncAnimation(fig=figure1, func=update, frames=numberOfFrames, interval=100, repeat=False)
    animation.save(plotImagePath + '/Optimizers (' + objectiveFunctionName + ').gif')

記事やコードの内容に不備や誤りがあるかもしれませんのが、悪しからず。

コメント

タイトルとURLをコピーしました