# Chapter 49: Neural Networks and Deep Learning for Economic Data
# Fundamentals of Python for Financial Econometrics - Neural Networks and Deep Learning for Economic Data
# Dataset: Ceteris Lab teaching sample

# Neural Networks and Deep Learning for Economic Data
#
# **Opening question:** How does a stack of simple differentiable units learn a nonlinear mapping, and how do we know it has not merely memorized the training sample?

# %% Cell 2
import numpy as np

rng = np.random.default_rng(49)
X = np.linspace(-1, 1, 200).reshape(-1, 1)
y = 2 * X[:, 0] + 0.3 * np.sin(6 * X[:, 0])
hidden_weights = rng.normal(size=(1, 12))
hidden_bias = rng.normal(scale=0.25, size=12)
hidden = np.tanh(X @ hidden_weights + hidden_bias)
design = np.column_stack([np.ones(len(X)), hidden])
output_weights = np.linalg.lstsq(design, y, rcond=None)[0]
fitted = design @ output_weights
print("Training RMSE:", round(float(np.sqrt(np.mean((y - fitted) ** 2))), 4))

new_x = np.array([[-0.5], [0.0], [0.5]])
new_hidden = np.tanh(new_x @ hidden_weights + hidden_bias)
new_design = np.column_stack([np.ones(len(new_x)), new_hidden])
print("Hidden-layer shape:", hidden.shape)
print("Predictions:", np.round(new_design @ output_weights, 3).tolist())

# **Interpretation check:** Interpretation. The network learns a nonlinear approximation on a small CPU-friendly problem. The exact final loss may vary slightly by software and hardware.

# %% Cell 4
import numpy as np

values = np.array([-2.0, -0.5, 0.0, 0.5, 2.0])
relu = np.maximum(values, 0)
sigmoid = 1 / (1 + np.exp(-values))
print("ReLU:", relu.tolist())
print("Sigmoid:", np.round(sigmoid, 3).tolist())

# **Interpretation check:** Interpretation. Evaluation mode and no_grad disable training-specific behaviour and gradient storage during prediction.

# Verified source output
#
# ```text
# 0.01343
# ```
#
# ```text
# [-1.024999976158142, 0.0, 1.0119999647140503]
# ```
