NativeVex's picture
assignment-2
92601fa
raw
history blame
9.69 kB
## imports
import numpy as np
import pandas as pd
from scipy.optimize import minimize
from scipy.stats import norm
import math
## Problem 1
data = [4, 5, 7, 8, 8, 9, 10, 5, 2, 3, 5, 4, 8, 9]
data_mean = np.mean(data)
data_variance = np.var(data)
mu = 0.5
sigma = 0.5
w = np.array([mu, sigma])
w_star = np.array([data_mean, data_variance])
mu_star = data_mean
sigma_star = np.sqrt(data_variance)
offset = 10 * np.random.random(2)
w1p = w_star + 0.5 * offset
w1n = w_star - 0.5 * offset
w2p = w_star + 0.25 * offset
w2n = w_star - 0.25 * offset
# Negative Log Likelihood is defined as follows:
# $-\ln(\frac{1}{\sqrt{2\pi\sigma^2}}\exp(-\frac{1}{2}\frac{(x-\mu)}{\sigma}^2))$.
# Ignoring the contribution of the constant, we find that $\frac{\delta}{\delta
# \mu} \mathcal{N} = \frac{\mu-x}{\sigma^2}$ and $\frac{\delta}{\delta \sigma}
# \mathcal{N} = \frac{\sigma^2 + (\mu-x)^2 - \sigma^2}{\sigma^3}$. We apply these as our step functions for our SGD.
loss = lambda mu, sigma, x: np.sum(
[-np.log(norm.pdf(xi, loc=mu, scale=sigma)) for xi in x]
)
loss_2_electric_boogaloo = lambda mu, sigma, x: -len(x) / 2 * np.log(
2 * np.pi * sigma**2
) - 1 / (2 * sigma**2) * np.sum((x - mu) ** 2)
dmu = lambda mu, sigma, x: -np.sum([mu - xi for xi in x]) / (sigma**2)
dsigma = lambda mu, sigma, x: -len(x) / sigma + np.sum([(mu - xi) ** 2 for xi in x]) / (
sigma**3
)
log = []
def SGD_problem1(mu, sigma, x, learning_rate=0.01, n_epochs=1000):
global log
log = []
for epoch in range(n_epochs):
mu += learning_rate * dmu(mu, sigma, x)
sigma += learning_rate * dsigma(mu, sigma, x)
# print(f"Epoch {epoch}, Loss: {loss(mu, sigma, x)}, New mu: {mu}, New sigma: {sigma}")
log.append(
{
"Epoch": epoch,
"Loss": loss(mu, sigma, x),
"Loss 2 Alternative": loss_2_alternative(mu, sigma, x),
"New mu": mu,
"New sigma": sigma,
}
)
return np.array([mu, sigma])
def debug_SGD_1(wnn, data):
print("SGD Problem 1")
print("wnn", SGD_problem1(*wnn, data))
dflog = pd.DataFrame(log)
dflog["mu_star"] = mu_star
dflog["mu_std"] = sigma_star
print(f"mu diff at start {dflog.iloc[0]['New mu'] - dflog.iloc[0]['mu_star']}")
print(f"mu diff at end {dflog.iloc[-1]['New mu'] - dflog.iloc[-1]['mu_star']}")
if np.abs(dflog.iloc[-1]["New mu"] - dflog.iloc[-1]["mu_star"]) < np.abs(
dflog.iloc[0]["New mu"] - dflog.iloc[0]["mu_star"]
):
print("mu is improving")
else:
print("mu is not improving")
print(f"sigma diff at start {dflog.iloc[0]['New sigma'] - dflog.iloc[0]['mu_std']}")
print(f"sigma diff at end {dflog.iloc[-1]['New sigma'] - dflog.iloc[-1]['mu_std']}")
if np.abs(dflog.iloc[-1]["New sigma"] - dflog.iloc[-1]["mu_std"]) < np.abs(
dflog.iloc[0]["New sigma"] - dflog.iloc[0]["mu_std"]
):
print("sigma is improving")
else:
print("sigma is not improving")
return dflog
# _ = debug_SGD_1(w1p, data)
# _ = debug_SGD_1(w1n, data)
# _ = debug_SGD_1(w2p, data)
# _ = debug_SGD_1(w2n, data)
# TODO EXPLAIN WHY += WORKS HERE.
## Problem 2
x = np.array([8, 16, 22, 33, 50, 51])
y = np.array([5, 20, 14, 32, 42, 58])
# $-\frac{n}{\sigma}+\frac{1}{\sigma^3}\sum_{i=1}^n(y_i - (mx+c))^2$
dsigma = lambda sigma, c, m, x: -len(x) / sigma + np.sum(
[(xi - (m * x + c)) ** 2 for xi in x]
) / (sigma**3)
# $-\frac{1}{\sigma^2}\sum_{i=1}^n(y_i - (mx+c))$
dc = lambda sigma, c, m, x: -np.sum([xi - (m * x + c) for xi in x]) / (sigma**2)
# $-\frac{1}{\sigma^2}\sum_{i=1}^n(x_i(y_i - (mx+c)))$
dm = lambda sigma, c, m, x: -np.sum([x * (xi - (m * x + c)) for xi in x]) / (sigma**2)
log2 = []
def SGD_problem2(
sigma: float,
c: float,
m: float,
x: np.array,
y: np.array,
learning_rate=0.01,
n_epochs=1000,
):
global log2
log2 = []
for epoch in range(n_epochs):
sigma += learning_rate * dsigma(sigma, c, m, x)
c += learning_rate * dc(sigma, c, m, x)
m += learning_rate * dm(sigma, c, m, x)
log2.append(
{
"Epoch": epoch,
"New sigma": sigma,
"New c": c,
"New m": m,
"dc": dc(sigma, c, m, x),
"dm": dm(sigma, c, m, x),
"dsigma": dsigma(sigma, c, m, x),
"Loss": loss((m * x + c), sigma, y),
}
)
print(f"Epoch {epoch}, Loss: {loss((m * x + c), sigma, y)}")
return np.array([sigma, c, m])
# def debug_SGD_2(wnn, data):
# print("SGD Problem 2")
# print("wnn", SGD_problem2(*wnn, data))
# dflog = pd.DataFrame(log)
# dflog["m_star"] = m_star
# dflog["c_star"] = c_star
# dflog["sigma_star"] = sigma_star
# print(f"m diff at start {dflog.iloc[0]['New m'] - dflog.iloc[0]['m_star']}")
# print(f"m diff at end {dflog.iloc[-1]['New m'] - dflog.iloc[-1]['m_star']}")
# if np.abs(dflog.iloc[-1]["New m"] - dflog.iloc[-1]["m_star"]) < np.abs(
# dflog.iloc[0]["New m"] - dflog.iloc[0]["m_star"]
# ):
# print("m is improving")
# else:
# print("m is not improving")
# print(f"c diff at start {dflog.iloc[0]['New c'] - dflog.iloc[0]['c_star']}")
# print(f"c diff at end {dflog.iloc[-1]['New c'] - dflog.iloc[-1]['c_star']}")
# if np.abs(dflog.iloc[-1]["New c"] - dflog.iloc[-1]["c_star"]) < np.abs(
# dflog.iloc[0]["New c"] - dflog.iloc[0]["c_star"]
# ):
# print("c is improving")
# else:
# print("c is not improving")
# print(f"sigma diff at start {dflog.iloc[0]['New sigma'] - dflog.iloc[0]['sigma_star']}")
# print(f"sigma diff at end {dflog.iloc[-1]['New sigma'] - dflog.iloc[-1]['sigma_star']}")
# if np.abs(dflog.iloc[-1]["New sigma"] - dflog.iloc[-1]["sigma_star"]) < np.abs(
# dflog.iloc[0]["New sigma"] - dflog.iloc[0]["sigma_star"]
# ):
# print("sigma is improving")
# else:
# print("sigma is not improving")
# return dflog
result = SGD_problem2(0.5, 0.5, 0.5, x, y)
print(f"final parameters: m={result[2]}, c={result[1]}, sigma={result[0]}")
## pset2
# Knowing that the poisson pdf is $P(k) = \frac{\lambda^k e^{-\lambda}}{k!}$, we can find the negative log likelihood of the data as $-\log(\Pi_{i=1}^n P(k_i)) = -\sum_{i=1}^n \log(\frac{\lambda^k_i e^{-\lambda}}{k_i!}) = \sum_{i=1}^n -\ln(\lambda) k_i + \ln(k_i!) + \lambda$. Which simplified, gives $n\lambda + \sum_{i=1}^n \ln(k_i!) - \sum_{i=1}^n k_i \ln(\lambda)$. Differentiating with respect to $\lambda$ gives $n - \sum_{i=1}^n \frac{k_i}{\lambda}$. Which is our desired $\frac{\partial L}{\partial \lambda}$!
import pandas as pd
df = pd.read_csv("../data/01_raw/nyc_bb_bicyclist_counts.csv")
dlambda = lambda l, k: len(k) - np.sum([ki / l for ki in k])
def SGD_problem3(
l: float,
k: np.array,
learning_rate=0.01,
n_epochs=1000,
):
global log3
log3 = []
for epoch in range(n_epochs):
l -= learning_rate * dlambda(l, k)
# $n\lambda + \sum_{i=1}^n \ln(k_i!) - \sum_{i=1}^n k_i \ln(\lambda)$
# the rest of the loss function is commented out because it's a
# constant and was causing overflows. It is unnecessary, and a useless
# pain.
loss = len(k) * l - np.sum(
[ki * np.log(l) for ki in k]
) # + np.sum([np.log(np.math.factorial(ki)) for ki in k])
log3.append(
{
"Epoch": epoch,
"New lambda": l,
"dlambda": dlambda(l, k),
"Loss": loss,
}
)
print(f"Epoch {epoch}", f"Loss: {loss}")
return np.array([l])
l_star = df["BB_COUNT"].mean()
def debug_SGD_3(data, l=1000):
print("SGD Problem 3")
print(f"l: {SGD_problem3(l, data)}")
dflog = pd.DataFrame(log3)
dflog["l_star"] = l_star
print(f"l diff at start {dflog.iloc[0]['New lambda'] - dflog.iloc[0]['l_star']}")
print(f"l diff at end {dflog.iloc[-1]['New lambda'] - dflog.iloc[-1]['l_star']}")
if np.abs(dflog.iloc[-1]["New lambda"] - dflog.iloc[-1]["l_star"]) < np.abs(
dflog.iloc[0]["New lambda"] - dflog.iloc[0]["l_star"]
):
print("l is improving")
else:
print("l is not improving")
return dflog
debug_SGD_3(data=df["BB_COUNT"].values, l=l_star + 1000)
debug_SGD_3(data=df["BB_COUNT"].values, l=l_star - 1000)
## pset 4
# dw = lambda w, x: len(x) * np.exp(np.dot(x, w)) * x - np.sum()
primitive = lambda xi, wi: (x.shape[0] * np.exp(wi * xi) * xi) - (xi**2)
p_dw = lambda w, xi: np.array([primitive(xi, wi) for xi, wi in ])
def SGD_problem4(
w: np.array,
x: np.array,
learning_rate=0.01,
n_epochs=1000,
):
global log4
log4 = []
for epoch in range(n_epochs):
w -= learning_rate * p_dw(w, x)
# custom
# loss = x.shape[0] * np.exp(np.dot(x, w))
loss_fn = lambda k, l: len(k) * l - np.sum(
[ki * np.log(l) for ki in k]
) # + np.sum([np.log(np.math.factorial(ki)) for ki in k])
loss = loss_fn(x, np.exp(np.dot(x, w)))
log4.append(
{
"Epoch": epoch,
"New w": w,
"dw": dw(w, x),
"Loss": loss,
}
)
print(f"Epoch {epoch}", f"Loss: {loss}")
return w
def debug_SGD_3(data, w=np.array([1, 1])):
print("SGD Problem 4")
print(f"w: {SGD_problem4(w, data)}")
dflog = pd.DataFrame(log4)
return dflog
_ = debug_SGD_3(
data=df[["HIGH_T", "LOW_T", "PRECIP"]].to_numpy(),
w=np.array([1.0, 1.0, 1.0]),
)