-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfindMin.py
148 lines (111 loc) · 4 KB
/
findMin.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import numpy as np
from numpy.linalg import norm
def findMin(funObj, w, maxEvals, *args, verbose=0):
"""
Uses gradient descent to optimize the objective function
This uses quadratic interpolation in its line search to
determine the step size alpha
"""
# Parameters of the Optimization
optTol = 1e-2
gamma = 1e-4
# Evaluate the initial function value and gradient
f, g = funObj(w,*args)
funEvals = 1
alpha = 1.
while True:
# Line-search using quadratic interpolation to find an acceptable value of alpha
gg = g.T.dot(g)
while True:
w_new = w - alpha * g
f_new, g_new = funObj(w_new, *args)
funEvals += 1
if f_new <= f - gamma * alpha*gg:
break
if verbose > 1:
print("f_new: %.3f - f: %.3f - Backtracking..." % (f_new, f))
# Update step size alpha
alpha = (alpha**2) * gg/(2.*(f_new - f + alpha*gg))
# Print progress
if verbose > 0:
print("%d - loss: %.3f" % (funEvals, f_new))
# Update step-size for next iteration
y = g_new - g
alpha = -alpha*np.dot(y.T,g) / np.dot(y.T,y)
# Safety guards
if np.isnan(alpha) or alpha < 1e-10 or alpha > 1e10:
alpha = 1.
if verbose > 1:
print("alpha: %.3f" % (alpha))
# Update parameters/function/gradient
w = w_new
f = f_new
g = g_new
# Test termination conditions
optCond = norm(g, float('inf'))
if optCond < optTol:
if verbose:
print("Problem solved up to optimality tolerance %.3f" % optTol)
break
if funEvals >= maxEvals:
if verbose:
print("Reached maximum number of function evaluations %d" % maxEvals)
break
return w, f
def findMinL1(funObj, w, L1_lambda, maxEvals, *args, verbose=0):
"""
Uses the L1 proximal gradient descent to optimize the objective function
The line search algorithm divides the step size by 2 until
it find the step size that results in a decrease of the L1 regularized
objective function
"""
# Parameters of the Optimization
optTol = 1e-2
gamma = 1e-4
# Evaluate the initial function value and gradient
f, g = funObj(w,*args)
funEvals = 1
alpha = 1.
proxL1 = lambda w, alpha: np.sign(w) * np.maximum(abs(w)- L1_lambda*alpha,0)
L1Term = lambda w: L1_lambda * np.sum(np.abs(w))
while True:
gtd = None
# Start line search to determine alpha
while True:
w_new = w - alpha * g
w_new = proxL1(w_new, alpha)
if gtd is None:
gtd = g.T.dot(w_new - w)
f_new, g_new = funObj(w_new, *args)
funEvals += 1
if f_new + L1Term(w_new) <= f + L1Term(w) + gamma*alpha*gtd:
# Wolfe condition satisfied, end the line search
break
if verbose > 1:
print("Backtracking... f_new: %.3f, f: %.3f" % (f_new, f))
# Update alpha
alpha /= 2.
# Print progress
if verbose > 0:
print("%d - alpha: %.3f - loss: %.3f" % (funEvals, alpha, f_new))
# Update step-size for next iteration
y = g_new - g
alpha = -alpha*np.dot(y.T,g) / np.dot(y.T,y)
# Safety guards
if np.isnan(alpha) or alpha < 1e-10 or alpha > 1e10:
alpha = 1.
# Update parameters/function/gradient
w = w_new
f = f_new
g = g_new
# Test termination conditions
optCond = norm(w - proxL1(w - g, 1.0), float('inf'))
if optCond < optTol:
if verbose:
print("Problem solved up to optimality tolerance %.3f" % optTol)
break
if funEvals >= maxEvals:
if verbose:
print("Reached maximum number of function evaluations %d" % maxEvals)
break
return w, f