簡單地說,找到給定方程的最小值被認為是優化。這在現實生活中有很多應用:路徑規劃,工作車間調度,空中交通管理等。優化一直是機器學習的支柱,人們期望這些算法從海量數據中提取知識。
優化在神經網絡中起著重要作用,神經網絡中有數百萬個參數,目標是找到正確的參數集以正確表示數據。儘管優化器的性能已經有了很大的提高,但是還有一個優化所依賴的問題,即初始點。優化的軌跡在很大程度上取決於初始點。
在這篇文章中,我們將看到初始點是如何影響一些優化算法的性能的。雖然我們在這裡使用的是一個二維問題(因為它很容易可視化),但當參數(神經網絡)達到數百萬時,初始化問題就會變得更加普遍。
目標
初始化x、y,使用梯度下降算法找到x、y的最優值,使Beale函數的值為零(或儘可能低)。
優化算法簡介
我們將考慮三種流行的優化算法,因為我們更加關注初始化,這些對於我們的分析就足夠了。
- 隨機梯度下降:隨機梯度下降(SGD)算法每次執行一次更新,計算每一步的梯度。。
- momentum:通過考慮梯度在一段時間內的動量,解決了隨機梯度下降更新緩慢的問題。
- Adam:被認為是最流行的優化算法。。
我們將使用PyTorch的autograd功能來獲得梯度,使用matplotlib來繪製軌跡。首先導入Python庫:
<code>import torch
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from mpl_toolkits.mplot3d
import Axes3D from matplotlib.colors
import LogNorm
import warnings
warnings.filterwarnings("ignore")/<code>
Beale函數
- Beale函數是在二維中定義的多峰非凸連續函數。
- 通常在(x,y)∈[-4.5,4.5]範圍內進行評估。
- 該函數只有一個全局最小值(x,y)=(3,0.5)。
可視化Beale函數
由於Beale函數是一個介於-4.5和4.5之間的雙變量函數,我們可以使用NumPy生成一個網格,將所有可能的x和y值傳遞給該函數。這使我們能夠在每一個可能的點上得到Beale函數的輸出,我們可以使用這些輸出將函數可視化。
當我們將優化問題與神經網絡聯繫起來時,我們將(x,y)稱為(w1,w2)。當使用神經網絡時,我們將目標函數稱為損失函數,並將函數的輸出稱為損失。在這種情況下,我們將Beale函數稱為損失函數,將輸出稱為損失。
<code># Defining function
f = lambda x, y: (1.5 - x + x*y)**2 + (2.25 - x + x*y**2)**2 + (2.625 - x + x*y**3)**2
# Defining the range of w1 and w2, step size
w1_min, w1_max, w1_step = -4.5, 4.5, .2
w2_min, w2_max, w2_step = -4.5, 4.5, .2
# Global minima of the function
minima_ = [3, 0.5]
# generating meshgrid
w1, w2 = np.meshgrid(np.arange(w1_min, w1_max + w1_step, w1_step),
np.arange(w2_min, w2_max + w2_step, w2_step))
losses = f(w1, w2)/<code>
現在,我們將使用以下Python代碼繪製損失。
<code>fig, ax = plt.subplots(figsize=(10, 6))
ax.contour(w1, w2, losses, levels=np.logspace(0, 5, 35),
norm=LogNorm(), cmap=plt.cm.jet, alpha = 0.8)
ax.plot(*minima_, 'r*', color='r',
markersize=10, alpha=0.7, label='minima')
ax.set_xlabel('w1')
ax.set_ylabel('w2')
ax.set_xlim((w1_min, w1_max))
ax.set_ylim((w2_min, w2_max))
ax.legend(bbox_to_anchor=(1.2, 1.))
ax.set_title("Beale Function")
fig.tight_layout(rect=[0, 0.03, 1, 0.95])/<code>
輸出
如圖所示,藍色區域表示Beale函數的值較低,紅色區域表示Beale函數的值較高。最小值(3,0.5)用星號表示。
設定參數
當我們使用PyTorch時,我們需要將要優化的參數放入nn.Module類中。__init__()以(X,Y)作為輸入來初始化參數(W1,W2)。另外,我們將在forward函數中編寫Beale方程。
<code>class Net_Beale(torch.nn.Module):
def __init__(self, x, y):
super(Net_Beale, self).__init__()
self.w1 = torch.nn.Parameter(torch.tensor([x]))
self.w2 = torch.nn.Parameter(torch.tensor([y]))
def forward(self):
# Beale Function Equation
a = (1.5 - self.w1 + self.w1*self.w2)**2
b = (2.25 - self.w1 + self.w1*self.w2**2)**2
c = (2.625 - self.w1 + self.w1*self.w2**3)**2
return a+b+c/<code>
優化和保存軌跡
下面的函數初始化網絡的參數,初始化優化器,並在收集參數路徑的同時針對指定的步驟數運行優化。
<code>def get_trajectory(x, y, optim, lr, epochs, interval=1):
# Initialize Network
net = Net_Beale(x,y)
# Initialize Optimizer
if optim == "sgd":
optim = torch.optim.SGD(net.parameters(), lr)
elif optim == "mom":
optim = torch.optim.SGD(net.parameters(), lr, momentum=0.9)
elif optim == "adam":
optim = torch.optim.Adam(net.parameters(), lr)
# Initialize Trackers
w_1s = []
w_2s = []
# Run Optimization
for i in range(epochs):
optim.zero_grad()
o = net()
o.backward()
if i % interval == 0:
# Append current w1 and w2 to trackers
w_1s.append(net.w1.item())
w_2s.append(net.w2.item())
optim.step()
w_1s.append(net.w1.item())
w_2s.append(net.w2.item())
# Join w1's and w2's into one array
trajectory = np.array([w_1s, w_2s])
return trajectory/<code>
軌跡之間的比較
下面的函數給出了初始位置、優化器列表以及相應的學習率和epoch,並繪製了具有指定設置的算法軌跡。
<code>def compare_trajectories(x, y, epochs, optims, lrs):
colors = ['k', 'g', 'b', 'r', 'y', 'c', 'm']
trajectories = []
names = []
# Loop on all optimizers in list
for ep, optim, lr in zip(epochs, optims, lrs):
trajectory = get_trajectory(float(x), float(y), optim=optim, lr=lr, epochs=ep)
names.append(optim)
trajectories.append(trajectory)
# Plot the Contour plot of Beale Function and trajectories of optimizers
fig, ax = plt.subplots(figsize=(10, 6))
ax.contour(w1, w2, losses, levels=np.logspace(0, 5, 35),
norm=LogNorm(), cmap=plt.cm.jet, alpha = 0.5)
for i, trajectory in enumerate(trajectories):
ax.quiver(trajectory[0,:-1], trajectory[1,:-1], trajectory[0,1:]-trajectory[0,:-1],
trajectory[1,1:]-trajectory[1,:-1], scale_units='xy', angles='xy', scale=1,
color=colors[i], label=names[i], alpha=0.8)
start_ =[x,y]
ax.plot(*start_, 'r*', color='k',markersize=10, alpha=0.7, label='start')
ax.plot(*minima_, 'r*', color='r',markersize=10, alpha=0.7, label='minima')
ax.set_xlabel('w1')
ax.set_ylabel('w2')
ax.set_xlim((w1_min, w1_max))
ax.set_ylim((w2_min, w2_max))
ax.set_title("Initial point - ({},{})".format(x,y))
ax.legend(bbox_to_anchor=(1.2, 1.))
fig.suptitle("Optimization Trajectory")
fig.tight_layout(rect=[0, 0.03, 1, 0.95])/<code>
嘗試不同的初始點
在設置好一切之後,我們現在準備用不同的初始點來比較這三種算法。
學習率設置:
- SGD— 0.0001
- momentum— 0.0001
- Adam-0.01
我們將對不同算法的初始點使用相同的學習率,以保持分析的簡單性,因為我們沒有進行超參數調優。
<code># Settings for optimizers
epochs = [10000] * 3
optims = ['sgd', 'mom', 'adam']
lrs = [0.0001, 0.0001, 0.01]/<code>
情況1:接近極小點
<code># A point closer to minima
x = 2.5
y = 2.
compare_trajectories(x, y, epochs, optims, lrs)/<code>
這三個都達到了全局最小值,讓我們再進一步看看會發生什麼。
情況2:離極小點遠一點
<code># A little away in the same region
x = 1.5
y = 2.5
compare_trajectories(x, y, epochs, optims, lrs)/<code>
如上圖所示,Adam優化器趨向於一個局部的最小值並停滯不前,而sgd和momentum則達到了全局最小值。需要注意的是,我們並沒有改變這裡的學習率,我們關注的是初始點對優化的影響。
情況3:遠離極小值
<code># Lower left region
x = -4
y = -4
compare_trajectories(x, y, epochs, optims, lrs)/<code>
結論
初始點在優化問題中起著至關重要的作用。在這裡,我們試圖解決一個二維問題,與使用大型數據集和上百萬個參數(維度)時找到最小值相比,這很容易。雖然我們在這裡沒有調優超參數,但是我們可以使用正確的超參數集有效地將優化推向正確的方向。
閱讀更多 不靠譜的貓 的文章