-
Notifications
You must be signed in to change notification settings - Fork 0
/
logistic_regression.py
115 lines (86 loc) · 3.19 KB
/
logistic_regression.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
import numpy as np
import pandas as pd
# IMPORTANT: DO NOT USE ANY OTHER 3RD PARTY PACKAGES
# (math, random, collections, functools, etc. are perfectly fine)
class LogisticRegression:
def __init__(self, learning_rate=.05, epochs=10):
# NOTE: Feel free add any hyperparameters
# (with defaults) as you see fit
#pass
self.theta = None
self.learning_rate = learning_rate
self.epochs = epochs
def fit(self, X, y):
"""
Estimates parameters for the classifier
Args:
X (array<m,n>): a matrix of floats with
m rows (#samples) and n columns (#features)
y (array<m>): a vector of floats containing
m binary 0.0/1.0 labels
"""
# TODO: Implement
y_true = y
self.theta = .5*np.ones(X.shape[1])
#algorithm of gradient descent
for n in range(self.epochs):
# compute h_theta(x)
y_pred = sigmoid(self.theta @ X.T)
# update theta parameters
self.theta += self.learning_rate *(y - y_pred).T @ X
#raise NotImplemented()
def predict(self, X):
"""
Generates predictions
Note: should be called after .fit()
Args:
X (array<m,n>): a matrix of floats with
m rows (#samples) and n columns (#features)
Returns:
A length m array of floats in the range [0, 1]
with probability-like predictions
"""
# TODO: Implement
return sigmoid(self.theta @ X.T)
#raise NotImplemented()
# --- Some utility functions
def binary_accuracy(y_true, y_pred, threshold=0.5):
"""
Computes binary classification accuracy
Args:
y_true (array<m>): m 0/1 floats with ground truth labels
y_pred (array<m>): m [0,1] floats with "soft" predictions
Returns:
The average number of correct predictions
"""
assert y_true.shape == y_pred.shape
y_pred_thresholded = (y_pred >= threshold).astype(float)
correct_predictions = y_pred_thresholded == y_true
return correct_predictions.mean()
def binary_cross_entropy(y_true, y_pred, eps=1e-15):
"""
Computes binary cross entropy
Args:
y_true (array<m>): m 0/1 floats with ground truth labels
y_pred (array<m>): m [0,1] floats with "soft" predictions
Returns:
Binary cross entropy averaged over the input elements
"""
assert y_true.shape == y_pred.shape
y_pred = np.clip(y_pred, eps, 1 - eps) # Avoid log(0)
return - np.mean(
y_true * np.log(y_pred) +
(1 - y_true) * (np.log(1 - y_pred))
)
def sigmoid(x):
"""
Applies the logistic function element-wise
Hint: highly related to cross-entropy loss
Args:
x (float or array): input to the logistic function
the function is vectorized, so it is acceptible
to pass an array of any shape.
Returns:
Element-wise sigmoid activations of the input
"""
return 1. / (1. + np.exp(-x))