Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
greyhatguy007
GitHub Repository: greyhatguy007/Machine-Learning-Specialization-Coursera
Path: blob/main/C2 - Advanced Learning Algorithms/week2/C2W2A1/lab_utils_common.py
3520 views
1
"""
2
lab_utils_common
3
contains common routines and variable definitions
4
used by all the labs in this week.
5
by contrast, specific, large plotting routines will be in separate files
6
and are generally imported into the week where they are used.
7
those files will import this file
8
"""
9
import copy
10
import math
11
import numpy as np
12
import matplotlib.pyplot as plt
13
from matplotlib.patches import FancyArrowPatch
14
from ipywidgets import Output
15
from matplotlib.widgets import Button, CheckButtons
16
17
np.set_printoptions(precision=2)
18
19
dlc = dict(dlblue = '#0096ff', dlorange = '#FF9300', dldarkred='#C00000', dlmagenta='#FF40FF', dlpurple='#7030A0', dldarkblue = '#0D5BDC', dlmedblue='#4285F4')
20
dlblue = '#0096ff'; dlorange = '#FF9300'; dldarkred='#C00000'; dlmagenta='#FF40FF'; dlpurple='#7030A0'; dldarkblue = '#0D5BDC'; dlmedblue='#4285F4'
21
dlcolors = [dlblue, dlorange, dldarkred, dlmagenta, dlpurple]
22
plt.style.use('./deeplearning.mplstyle')
23
24
def sigmoid(z):
25
"""
26
Compute the sigmoid of z
27
28
Parameters
29
----------
30
z : array_like
31
A scalar or numpy array of any size.
32
33
Returns
34
-------
35
g : array_like
36
sigmoid(z)
37
"""
38
z = np.clip( z, -500, 500 ) # protect against overflow
39
g = 1.0/(1.0+np.exp(-z))
40
41
return g
42
43
##########################################################
44
# Regression Routines
45
##########################################################
46
47
def predict_logistic(X, w, b):
48
""" performs prediction """
49
return sigmoid(X @ w + b)
50
51
def predict_linear(X, w, b):
52
""" performs prediction """
53
return X @ w + b
54
55
def compute_cost_logistic(X, y, w, b, lambda_=0, safe=False):
56
"""
57
Computes cost using logistic loss, non-matrix version
58
59
Args:
60
X (ndarray): Shape (m,n) matrix of examples with n features
61
y (ndarray): Shape (m,) target values
62
w (ndarray): Shape (n,) parameters for prediction
63
b (scalar): parameter for prediction
64
lambda_ : (scalar, float) Controls amount of regularization, 0 = no regularization
65
safe : (boolean) True-selects under/overflow safe algorithm
66
Returns:
67
cost (scalar): cost
68
"""
69
70
m,n = X.shape
71
cost = 0.0
72
for i in range(m):
73
z_i = np.dot(X[i],w) + b #(n,)(n,) or (n,) ()
74
if safe: #avoids overflows
75
cost += -(y[i] * z_i ) + log_1pexp(z_i)
76
else:
77
f_wb_i = sigmoid(z_i) #(n,)
78
cost += -y[i] * np.log(f_wb_i) - (1 - y[i]) * np.log(1 - f_wb_i) # scalar
79
cost = cost/m
80
81
reg_cost = 0
82
if lambda_ != 0:
83
for j in range(n):
84
reg_cost += (w[j]**2) # scalar
85
reg_cost = (lambda_/(2*m))*reg_cost
86
87
return cost + reg_cost
88
89
90
def log_1pexp(x, maximum=20):
91
''' approximate log(1+exp^x)
92
https://stats.stackexchange.com/questions/475589/numerical-computation-of-cross-entropy-in-practice
93
Args:
94
x : (ndarray Shape (n,1) or (n,) input
95
out : (ndarray Shape matches x output ~= np.log(1+exp(x))
96
'''
97
98
out = np.zeros_like(x,dtype=float)
99
i = x <= maximum
100
ni = np.logical_not(i)
101
102
out[i] = np.log(1 + np.exp(x[i]))
103
out[ni] = x[ni]
104
return out
105
106
107
def compute_cost_matrix(X, y, w, b, logistic=False, lambda_=0, safe=True):
108
"""
109
Computes the cost using using matrices
110
Args:
111
X : (ndarray, Shape (m,n)) matrix of examples
112
y : (ndarray Shape (m,) or (m,1)) target value of each example
113
w : (ndarray Shape (n,) or (n,1)) Values of parameter(s) of the model
114
b : (scalar ) Values of parameter of the model
115
verbose : (Boolean) If true, print out intermediate value f_wb
116
Returns:
117
total_cost: (scalar) cost
118
"""
119
m = X.shape[0]
120
y = y.reshape(-1,1) # ensure 2D
121
w = w.reshape(-1,1) # ensure 2D
122
if logistic:
123
if safe: #safe from overflow
124
z = X @ w + b #(m,n)(n,1)=(m,1)
125
cost = -(y * z) + log_1pexp(z)
126
cost = np.sum(cost)/m # (scalar)
127
else:
128
f = sigmoid(X @ w + b) # (m,n)(n,1) = (m,1)
129
cost = (1/m)*(np.dot(-y.T, np.log(f)) - np.dot((1-y).T, np.log(1-f))) # (1,m)(m,1) = (1,1)
130
cost = cost[0,0] # scalar
131
else:
132
f = X @ w + b # (m,n)(n,1) = (m,1)
133
cost = (1/(2*m)) * np.sum((f - y)**2) # scalar
134
135
reg_cost = (lambda_/(2*m)) * np.sum(w**2) # scalar
136
137
total_cost = cost + reg_cost # scalar
138
139
return total_cost # scalar
140
141
def compute_gradient_matrix(X, y, w, b, logistic=False, lambda_=0):
142
"""
143
Computes the gradient using matrices
144
145
Args:
146
X : (ndarray, Shape (m,n)) matrix of examples
147
y : (ndarray Shape (m,) or (m,1)) target value of each example
148
w : (ndarray Shape (n,) or (n,1)) Values of parameters of the model
149
b : (scalar ) Values of parameter of the model
150
logistic: (boolean) linear if false, logistic if true
151
lambda_: (float) applies regularization if non-zero
152
Returns
153
dj_dw: (array_like Shape (n,1)) The gradient of the cost w.r.t. the parameters w
154
dj_db: (scalar) The gradient of the cost w.r.t. the parameter b
155
"""
156
m = X.shape[0]
157
y = y.reshape(-1,1) # ensure 2D
158
w = w.reshape(-1,1) # ensure 2D
159
160
f_wb = sigmoid( X @ w + b ) if logistic else X @ w + b # (m,n)(n,1) = (m,1)
161
err = f_wb - y # (m,1)
162
dj_dw = (1/m) * (X.T @ err) # (n,m)(m,1) = (n,1)
163
dj_db = (1/m) * np.sum(err) # scalar
164
165
dj_dw += (lambda_/m) * w # regularize # (n,1)
166
167
return dj_db, dj_dw # scalar, (n,1)
168
169
def gradient_descent(X, y, w_in, b_in, alpha, num_iters, logistic=False, lambda_=0, verbose=True, Trace=True):
170
"""
171
Performs batch gradient descent to learn theta. Updates theta by taking
172
num_iters gradient steps with learning rate alpha
173
174
Args:
175
X (ndarray): Shape (m,n) matrix of examples
176
y (ndarray): Shape (m,) or (m,1) target value of each example
177
w_in (ndarray): Shape (n,) or (n,1) Initial values of parameters of the model
178
b_in (scalar): Initial value of parameter of the model
179
logistic: (boolean) linear if false, logistic if true
180
lambda_: (float) applies regularization if non-zero
181
alpha (float): Learning rate
182
num_iters (int): number of iterations to run gradient descent
183
184
Returns:
185
w (ndarray): Shape (n,) or (n,1) Updated values of parameters; matches incoming shape
186
b (scalar): Updated value of parameter
187
"""
188
# An array to store cost J and w's at each iteration primarily for graphing later
189
J_history = []
190
w = copy.deepcopy(w_in) #avoid modifying global w within function
191
b = b_in
192
w = w.reshape(-1,1) #prep for matrix operations
193
y = y.reshape(-1,1)
194
last_cost = np.Inf
195
196
for i in range(num_iters):
197
198
# Calculate the gradient and update the parameters
199
dj_db,dj_dw = compute_gradient_matrix(X, y, w, b, logistic, lambda_)
200
201
# Update Parameters using w, b, alpha and gradient
202
w = w - alpha * dj_dw
203
b = b - alpha * dj_db
204
205
# Save cost J at each iteration
206
ccost = compute_cost_matrix(X, y, w, b, logistic, lambda_)
207
if Trace and i<100000: # prevent resource exhaustion
208
J_history.append( ccost )
209
210
# Print cost every at intervals 10 times or as many iterations if < 10
211
if i% math.ceil(num_iters / 10) == 0:
212
if verbose: print(f"Iteration {i:4d}: Cost {ccost} ")
213
if verbose ==2: print(f"dj_db, dj_dw = {dj_db: 0.3f}, {dj_dw.reshape(-1)}")
214
215
if ccost == last_cost:
216
alpha = alpha/10
217
print(f" alpha now {alpha}")
218
last_cost = ccost
219
220
return w.reshape(w_in.shape), b, J_history #return final w,b and J history for graphing
221
222
def zscore_normalize_features(X):
223
"""
224
computes X, zcore normalized by column
225
226
Args:
227
X (ndarray): Shape (m,n) input data, m examples, n features
228
229
Returns:
230
X_norm (ndarray): Shape (m,n) input normalized by column
231
mu (ndarray): Shape (n,) mean of each feature
232
sigma (ndarray): Shape (n,) standard deviation of each feature
233
"""
234
# find the mean of each column/feature
235
mu = np.mean(X, axis=0) # mu will have shape (n,)
236
# find the standard deviation of each column/feature
237
sigma = np.std(X, axis=0) # sigma will have shape (n,)
238
# element-wise, subtract mu for that column from each example, divide by std for that column
239
X_norm = (X - mu) / sigma
240
241
return X_norm, mu, sigma
242
243
#check our work
244
#from sklearn.preprocessing import scale
245
#scale(X_orig, axis=0, with_mean=True, with_std=True, copy=True)
246
247
######################################################
248
# Common Plotting Routines
249
######################################################
250
251
252
def plot_data(X, y, ax, pos_label="y=1", neg_label="y=0", s=80, loc='best' ):
253
""" plots logistic data with two axis """
254
# Find Indices of Positive and Negative Examples
255
pos = y == 1
256
neg = y == 0
257
pos = pos.reshape(-1,) #work with 1D or 1D y vectors
258
neg = neg.reshape(-1,)
259
260
# Plot examples
261
ax.scatter(X[pos, 0], X[pos, 1], marker='x', s=s, c = 'red', label=pos_label)
262
ax.scatter(X[neg, 0], X[neg, 1], marker='o', s=s, label=neg_label, facecolors='none', edgecolors=dlblue, lw=3)
263
ax.legend(loc=loc)
264
265
ax.figure.canvas.toolbar_visible = False
266
ax.figure.canvas.header_visible = False
267
ax.figure.canvas.footer_visible = False
268
269
def plt_tumor_data(x, y, ax):
270
""" plots tumor data on one axis """
271
pos = y == 1
272
neg = y == 0
273
274
ax.scatter(x[pos], y[pos], marker='x', s=80, c = 'red', label="malignant")
275
ax.scatter(x[neg], y[neg], marker='o', s=100, label="benign", facecolors='none', edgecolors=dlblue,lw=3)
276
ax.set_ylim(-0.175,1.1)
277
ax.set_ylabel('y')
278
ax.set_xlabel('Tumor Size')
279
ax.set_title("Logistic Regression on Categorical Data")
280
281
ax.figure.canvas.toolbar_visible = False
282
ax.figure.canvas.header_visible = False
283
ax.figure.canvas.footer_visible = False
284
285
# Draws a threshold at 0.5
286
def draw_vthresh(ax,x):
287
""" draws a threshold """
288
ylim = ax.get_ylim()
289
xlim = ax.get_xlim()
290
ax.fill_between([xlim[0], x], [ylim[1], ylim[1]], alpha=0.2, color=dlblue)
291
ax.fill_between([x, xlim[1]], [ylim[1], ylim[1]], alpha=0.2, color=dldarkred)
292
ax.annotate("z >= 0", xy= [x,0.5], xycoords='data',
293
xytext=[30,5],textcoords='offset points')
294
d = FancyArrowPatch(
295
posA=(x, 0.5), posB=(x+3, 0.5), color=dldarkred,
296
arrowstyle='simple, head_width=5, head_length=10, tail_width=0.0',
297
)
298
ax.add_artist(d)
299
ax.annotate("z < 0", xy= [x,0.5], xycoords='data',
300
xytext=[-50,5],textcoords='offset points', ha='left')
301
f = FancyArrowPatch(
302
posA=(x, 0.5), posB=(x-3, 0.5), color=dlblue,
303
arrowstyle='simple, head_width=5, head_length=10, tail_width=0.0',
304
)
305
ax.add_artist(f)
306
307
308
#-----------------------------------------------------
309
# common interactive plotting routines
310
#-----------------------------------------------------
311
312
class button_manager:
313
''' Handles some missing features of matplotlib check buttons
314
on init:
315
creates button, links to button_click routine,
316
calls call_on_click with active index and firsttime=True
317
on click:
318
maintains single button on state, calls call_on_click
319
'''
320
321
#@output.capture() # debug
322
def __init__(self,fig, dim, labels, init, call_on_click):
323
'''
324
dim: (list) [leftbottom_x,bottom_y,width,height]
325
labels: (list) for example ['1','2','3','4','5','6']
326
init: (list) for example [True, False, False, False, False, False]
327
'''
328
self.fig = fig
329
self.ax = plt.axes(dim) #lx,by,w,h
330
self.init_state = init
331
self.call_on_click = call_on_click
332
self.button = CheckButtons(self.ax,labels,init)
333
self.button.on_clicked(self.button_click)
334
self.status = self.button.get_status()
335
self.call_on_click(self.status.index(True),firsttime=True)
336
337
#@output.capture() # debug
338
def reinit(self):
339
self.status = self.init_state
340
self.button.set_active(self.status.index(True)) #turn off old, will trigger update and set to status
341
342
#@output.capture() # debug
343
def button_click(self, event):
344
''' maintains one-on state. If on-button is clicked, will process correctly '''
345
#new_status = self.button.get_status()
346
#new = [self.status[i] ^ new_status[i] for i in range(len(self.status))]
347
#newidx = new.index(True)
348
self.button.eventson = False
349
self.button.set_active(self.status.index(True)) #turn off old or reenable if same
350
self.button.eventson = True
351
self.status = self.button.get_status()
352
self.call_on_click(self.status.index(True))
353
354