



\[ \mathbf{v}_{i+1} = \kappa\nabla f(\mathbf{x})_i + \beta \mathbf{v}_i \]
def gd_with_mom(grad, init, n_epochs=5000, eta=10**-4, beta=0.9,gamma=0.9):
params=np.array(init) # Start with initial condition
param_traj=np.zeros([n_epochs+1,2]) # Save the entire trajecotry
param_traj[0,]=init # Also save the initial condition to the trajectory
v=0 # Starting with 0 momentum
# Epochs is borrowing term from machine learning
# Here it means timestep
for j in range(n_epochs):
v=gamma*v+(np.array(grad(params))) # Compute v
params=params-eta*v # Update the location
param_traj[j+1,]=params # Save the trajectory
return param_traj






\[ \mathbf{v}_{i+1} = (1-\gamma)\nabla f(\mathbf{x}_i) + \gamma\mathbf{v}_i \\ G_{s,i+1} = (1-\beta)\nabla f(\mathbf{x}_i)^2 + \beta G_{s,i} \]
def adams(grad, init, n_epochs=5000, eta=10**-4, gamma=0.9, beta=0.99,epsilon=10**-8):
params=np.array(init)
param_traj=np.zeros([n_epochs+1,2])
param_traj[0,]=init
v=0;
grad_sq=0;
for j in range(n_epochs):
g=np.array(grad(params))
v=gamma*v+(1-gamma)*g
grad_sq=beta*grad_sq+(1-beta)*g*g
v_hat=v/(1-gamma**(j+1))
grad_sq_hat=grad_sq/(1-beta**(j+1))
params=params-eta*np.divide(v_hat,np.sqrt(grad_sq_hat+epsilon))
param_traj[j+1,]=params
return param_trajADAM is much more robust to learning rate choices
ADAM is excellent when the gradient is sparse
ADAM is often the best in initial training stages
