Approaches to biomedical knowledge

Session #5: Logistic regression - sigmoid functions, gradient descent, Python

Peter N Robinson

Free University Berlin

2026-04-26

Overview: Logistic regression

Game plan

Logistic regression shares some characteristics with neural networks including the weighted sum and activation function (for single neurons) and gradient-descent training loop. Studying logistic regression therefore serves as a good preparation for understanding neural networks.

Part 1: Introduction

  • Components of logistic regression
  • Sentiment analysis

Part 2: Sigmoid function

  • Definition
  • First derivative

Part 3: Gradient descent

  • Learning
  • Momentum & Adam
  • Learning rates

Part 4: Python

  • Full implementation

Part 5: Overfitting

Logistic Regression

Logistic Regression (LogReg) is one of the most common machine learning algorithms. It can be used to predict the probability of an event occurring based on a given labeled data set.

  • Email analysis (Is an email spam or not?)
  • Sentiment analysis (is a movie review positive or negative?)
  • Medical diagnostics (is a given diagnosis present or absent?)

We will go over the algorithm in detail, because it is an excellent preparation for understanding neural networks.

The five components of Logistic Regression

1. Feature Representation

For each input \(x\), we have a vector: \(\mathbf{x} = [x_1, x_2, \ldots, x_n]\)

2. Classification Function

Computes the estimated class using the Sigmoid function: \(\sigma(z) = \frac{1}{1 + e^{-z}}\)

3. Objective Function

A Loss function (Cross-Entropy) to measure how well the model is performing ( cross-entropy loss function).

4. Optimization

Gradient Descent: The method used to minimize the loss and find the best weights.

5. Prediction

The learned model can be used to classify new data points.

LogReg: Features

For each input \(x\), we create a vector of features: \(\mathbf{x} = [x_1, x_2, \ldots, x_n]\).

There are many different ways of generating a feature vector

  • Raw numerical data: If the input data is numerical with \(n\) dimensions, each of the dimensions can act as a feature
  • Polynomial & interaction features: We can add interaction terms (\(x_ix_j\)) or polynomial features (e.g., \(x_i^7\)) if this makes sense for the classification task
  • Images: Flatten pixel values (e.g., an \(8\times 8\) image would have 64 features)
  • Text to vector: e.g., count word occurrences across a vocabulary
  • Domain-specific feature engineering

Example

In the Python exercise for this lecture, we will generate two dimensional points with overlapping Gaussian distributions, but in general, the features can also be created (e.g., by counting positive and negative words for sentiment analysis).

Weights and bias

Weights

LogReg learns a vector of a vector of weights and a bias term to solve the classification task

  • Linear combination of input features
  • Each data point is multiplied by weights and a bias is added
  • Here, \(x_{i,j}\) refers to the \(j^{th}\) component of the \(i^{th}\) data point. \[ z_i = \sum_{j=1}^n w_ix_{i,j} + b \tag{1}\]

Equation 1 can be represented identically using dot product notation (where the bold font indicates a vector of weights (\(\mathbf{w}\)) and inputs (\(\mathbf{x}\)))

\[ z_i = \mathbf{w}\cdot \mathbf{x}_i + b \]

This is identical to the sums we will see at individual neurons in neural networks

Sigmoid

Sigmoid function

To create a probability, we transform z through the sigmoid function, \(\sigma(z)\)

  • The sigmoid \(\sigma(z)\) takes a real-valued number \(z\in [-\infty, \infty]\) and effectively squashes it to lie in the range \([0,1]\): \(0<\sigma(z) < 1\).

\[ \sigma(z) = \frac{1}{1+e^{-z}} = \frac{1}{1+\exp(-z)} \]

  • Interpretation: \(P(y=1 | x, w, b)\), i.e., a probability for classification
  • \(\sigma(z) > 0.5 \Leftrightarrow z > 0\)
  • i.e., whenever \(z = \mathrm{w}\cdot \mathrm{x} + b\) is over zero, the model predicts the positive class (\(\hat{y}=1\)).

Sigmoid: A convenient identity

  • \(1−\sigma(x) = \sigma(−x)\)

\[ \begin{align*} 1−\sigma(x) &=& 1- \frac{1}{1+e^{-x}}\\ &=& \frac{1+e^{-x} - 1}{1+e^{-x}} \\ &=& \frac{e^{-x}}{1+e^{-x}} \cdot \frac{e^{x}}{e^{x}}\\ &=& \frac{1}{1+e^{x}} \\ &=& \sigma(-x) \\ \end{align*} \]

Logit

  • Odds: The ratio of success to failure: \(p / (1-p)\).
  • Logit: The natural logarithm of those odds (log odds): \(\ln (\frac{p}{1-p})\).
  • The logit is the inverse of the sigmoid function:
  • \(z=\sigma(\mathbf{w}\cdot\mathbf{x}+b)\) can be any real number \((-\infty, \infty)\)
  • \(p\in [0, 1]\) represents a probability
  • These functions are inverses of each other because \(\sigma(\text{logit}(p)) = p\)

Proof

\[ \begin{align*} \sigma(\text{logit}(p)) &= \frac{1}{1 + e^{-\left[ \ln \left( \frac{p}{1-p} \right) \right]}} &&\bullet \text{Substitute Logit into Sigmoid } z \\ &= \frac{1}{1 + e^{\left[ \ln \left( \frac{1-p}{p} \right) \right]}} && \bullet\text{Apply } -\ln(A) = \ln(1/A) \\ &= \frac{1}{1 + \frac{1-p}{p}} && \bullet\text{Identity: } e^{\ln(x)} = x \\ &= \frac{1}{\frac{p + 1 - p}{p}} && \\ &= \frac{1}{1/p} && \bullet\text{Simplify} \\ &= p && \end{align*} \]

  • Therefore :The logit is the inverse of the sigmoid function.

Learning a classification rule

  • We need to learn the parameters of the model \(\mathbf{w}\) and \(b\):

\[ \begin{array}{c c c c c} \mathbf{x}& \longrightarrow & \sigma(z)=\sigma(\mathbf{w}\cdot\mathbf{x}+b) & \longrightarrow & \hat{y}\\ \text{\small Input features} & & \text{\small Output probability} & & \text{\small Prediction of class label} \end{array} \]

Classification Decision Rule

\[ \mathrm{classification}(x) = \begin{cases} 1 & \text{if }P(y = 1|x) > 0.5 \\ 0 & \text{otherwise} \end{cases} \]

Loss function

  • A good model will minimize the difference between our prediction \(\hat{y}\) and the ground truth \(y\)

  • this distance is defined by the loss function (also known as the cost function).

  • The same loss function is commonly used for logistic regression and for neural networks, the cross-entropy loss.

  • Recall that our classifier output is \(\hat{y} = \sigma(\mathbf{w}\cdot\mathbf{x} + b)\)

  • For logistic regression with binary classification, \(y\) can be either 0 or 1, but \(\hat{y}\) is a probability that ranges from 0 to 1.

  • We measure the difference with the loss function \(\mathcal{L}(\hat{y}, y)\)

Loss function

  • Consider the following piecewise loss function \[ \mathcal{L}(\sigma(z), y) = \begin{cases} -\log \sigma(z) & \text{if } y_i = 1 \\ -\log 1- \sigma(z) & \text{if } y_i = 0 \\ \end{cases} \]

As \(\log \sigma(z) \rightarrow 1\), the loss approaches zero

Loss function

  • Using \(\hat{y}=\sigma(z)\), we can concisely write the loss function as: \[ \log p(y|x) = \log[\hat{y}^y(1-\hat{y})^{1-y}] = y\log\hat{y} + (1-y)\log(1-\hat{y}) \]

  • Note this simplifies to either \(\log\hat{y}\) or \(\log(1-\hat{y})\) depending on whether \(y\) is 0 or 1.

  • Since it is convenient to minimize the function, we multiply it by -1: The result is known as the cross-entropy loss

\[ \mathcal{L}_{CE}(\hat{y}, y) = - y\log\hat{y} - (1-y)\log(1-\hat{y}) \]

  • Since we have \(\mathbf{\hat{y}} = \sigma(\mathbf{w}\cdot \mathbf{x} + b)\), this is equivalent to

\[ \mathcal{L}_{CE}(\hat{y}, y) = - y\log\sigma(\mathbf{w}\cdot \mathbf{x} + b) - (1-y)\log(1-\sigma(\mathbf{w}\cdot \mathbf{x} + b)) \]

Loss function (sanity check)

\[ \mathcal{L}_{CE}(\hat{y}, y) = - y\log\sigma(\mathbf{Xw} + b) - (1-y)\log(1-\sigma(\mathbf{Xw} + b)) \]

  • Note that if we predict (correctly) for a true example that \(\hat{y}=1\), then the second term drops out and we have

\[ \mathcal{L}_{CE}(\hat{y}, y) = - 1\log 1 = 0 \]

  • Similar things apply for a correct prediction of a negative example as \(\hat{y}=0\)
  • The loss for one example ranges from 0 to infinity (\(-\log 0\))

Optimizing the loss

Optimization Goal

To create a high-performing classifier, we minimize the Cost Function \(J(\theta)\), which is the average loss over the entire training set of \(m\) examples.

  • If we symbolize the parameters (weights, bias) of the logistic regression as \(\theta\), our objective function is:

\[ J(\theta) = \frac{1}{m} \sum_{i=1}^{m} \mathcal{L}(\hat{y}^{(i)}, y^{(i)}) \]

  • We seek \(\hat{\theta}\), the parameters that minimize the loss

\[ \hat{\theta} = \arg\min_{\theta} J(\theta) \]

  • It can be shown that the logistic regression loss function is convex (which enables us to reliably find its minimum)

Gradient descent

Basic idea of gradient descent

The gradient descent algorithm finds the gradient of the loss function at the current point and moves the parameters in the opposite direction.

  • Given a learning rate \(\eta\) and the gradient of the weights and biases:

  • Each iteration of the GD processes Updates the weights and biases of each layer \[ \begin{eqnarray*} \mathbf{W} &\leftarrow &\mathbf{W} - \eta\Delta \mathbf{W} \\ \mathbf{b} &\leftarrow &\mathbf{b} - \eta\Delta \mathbf{b} \end{eqnarray*} \]

  • We will explain how the gradient is calculated and then return to the mechanics of the gradient descent algorithm.

The Gradient

Another commonly encountered notation is the gradient, which by convention is represented as a column vector

\[ \nabla f(\mathbf{x}) = \begin{bmatrix} \dfrac{\partial f}{\partial \mathbf{x}} \end{bmatrix}^T = \begin{bmatrix} \dfrac{\partial f}{\partial x_1}\\ \dfrac{\partial f}{\partial x_2} \\ \cdots \\ \dfrac{\partial f}{\partial x_n} \end{bmatrix} \tag{2}\]

Note

  • \(f(x)\) is a scalar
  • We take the first derivative of \(f(x)\) with respect to each of the \(n\) components of \(\mathbf{w}\)
  • We will have more to say about matrix calculus in a future lecture.

Gradient descent

  • We iteratively take steps towards the minimum along the slope of the function
  • But how do we calculate the slope?

Calculating derivatives

We need to update each component of the weight vector \(\mathbf{w}\), e.g., \(w_j\) based on the firstderivative of the loss \(\mathcal{L}=\mathcal{L}_{CE}(\hat{y}, y)\) with respect to the component.

  • Apply the chain rule twice

\[ \frac{\partial \mathcal{L}}{\partial w_j} = \frac{\partial \mathcal{L}}{\partial \sigma_i} \cdot \frac{\partial \sigma_i}{\partial w_j} = \frac{\partial \mathcal{L}}{\partial \sigma_i} \cdot \frac{\partial \sigma_i}{\partial z_i} \cdot \frac{\partial z_i}{\partial w_j} \]

Derivation and chain rule

  • HOMEWORK To understand the derivations (and to understand backprop in the next lecture!), it is essential to be familiar with the chain rule. We will practice in the practical.
  • If \(f\) and \(g\) are differentiable functions, then \[ \left(f(g(x))\right)^{\prime} = f^{\prime}(g(x))\cdot g^{\prime}(x) \]

  • The chain rule can also be written as follows. If \(y=f(u)\) and \(u=g(x)\), then \[ \dfrac{dy}{dx} =\dfrac{dy}{du}\dfrac{du}{dx} \]

Examples

  • Power Rule: \(y = (5+3x)^5\)
    • Let \(u = 5+3x\), so \(y = u^5\)
    • \(\frac{dy}{du} = 5u^4\) and \(\frac{du}{dx} = 3\) \[\frac{dy}{dx} = 5(5+3x)^4 \cdot 3 = 15(5+3x)^4\]
  • Trig: \(y = \sin(4x+3)\)
    • Let \(u = 4x+3\), so \(y = \sin(u)\)
    • \(\frac{dy}{du} = \cos(u)\) and \(\frac{du}{dx} = 4\) \[\frac{dy}{dx} = \cos(4x+3) \cdot 4 = 4\cos(4x+3)\]

Calculating derivatives

\[ \frac{\partial \mathcal{L}}{\partial w_j} = \underbrace{\frac{\partial \mathcal{L}}{\partial \sigma_i}}_{\text{Part A}} \cdot \underbrace{\frac{\partial \sigma_i}{\partial z_i}}_{\text{Part B}} \cdot \underbrace{\frac{\partial z_i}{\partial w_j}}_{\text{Part C}} \]

  • In the following slides, we will calculate the three parts separately.

Part A: \(\frac{\partial \mathcal{L}}{\partial \hat{y}}\): Gradient of the Loss with respect to Prediction

\[ \begin{align*} \frac{\partial \mathcal{L}}{\partial \hat{y}} & = -\frac{\partial }{\partial \hat{y}} [y \ln \hat{y} + (1-y) \ln(1-\hat{y})] && \bullet \text{Using } \hat{y}=\sigma(wx+b) \\ & = -y \frac{\partial }{\partial \hat{y}}\ln \hat{y} - (1-y)\frac{\partial }{\partial \hat{y}}\ln(1-\hat{y}) && \bullet \text{Move constants out of derivative}\\ & = -\frac{y}{\hat{y}} - (1-y) \frac{-1}{1-\hat{y}} && \bullet \text{Recall } \frac{d \ln x}{dx} = \frac{1}{x} \\ & = -\frac{y}{\hat{y}} + \frac{1-y}{1-\hat{y}} && \bullet \text{Simplify signs} \\ & = \frac{-y(1-\hat{y}) + \hat{y}(1-y)}{\hat{y}(1-\hat{y})} && \bullet \text{Common denominator} \\ & = \frac{-y + y\hat{y} + \hat{y} - y\hat{y}}{\hat{y}(1-\hat{y})} && \bullet \text{Expand numerator} \\ & = \frac{\hat{y} - y}{\hat{y}(1-\hat{y})} && \bullet \text{Final result} \end{align*} \]

Part B: derivative of the sigmoid activation function

\[ \begin{align*} \dfrac{d\sigma(x)}{dx} & = \dfrac{d}{dx} \dfrac{1}{1+e^{-x}} \\ & = \dfrac{-1}{(1+e^{-x})^2}\cdot (-e^{-x}) && \bullet \text{chain rule} \\ & = \dfrac{e^{-x}}{(1+e^{-x})^2} && \bullet \text{simplify}\\ & = \dfrac{1}{1+e^{-x}} \cdot \dfrac{e^{-x}}{1+e^{-x}} && \bullet \text{rearrange}\\ & = \sigma(x) \cdot \dfrac{e^{-x}}{1+e^{-x}} && \bullet \text{definition of } \sigma(x)\\ & = \sigma(x) \cdot \dfrac{1 + e^{-x} - 1}{1+e^{-x}} && \bullet \text{add and subtract 1}\\ & = \sigma(x) \cdot \left( \dfrac{1 + e^{-x} }{1+e^{-x}} -\dfrac{1}{1+e^{-x}} \right) && \bullet \text{rearrange}\\ & = \sigma(x) \cdot \left( 1 -\sigma(x) \right) && \bullet \text{simplify using definition of } \sigma(x) \end{align*} \]

derivative of the sigmoid activation function

  • Thus we get the fascinating result that

\[ \frac{d\sigma(x)}{dx} = \sigma(x)(1-\sigma(x)) \]

  • Substituing and remembering that we define \[ \hat{y} = \sigma(\mathbf{w}\cdot\mathbf{x} + b) \] we get \[ \underbrace{\frac{\partial \sigma_i}{\partial z_i}}_{\text{Part B}} = \hat{y}(1- \hat{y}) \]

Part C: \(\frac{\partial z}{\partial w_j}\): Gradient of the \(z\) with respect to the Weight \(w_j\)

The Since \(z = w_1x_1 + w_2x_2 + \dots + b\), the derivative with respect to \(w_j\) is simply the feature it is multiplied by: \[ \frac{\partial z}{\partial w_j} = x_j \]

Putting it all together

\[ \frac{\partial \mathcal{L}}{\partial w_j} = \underbrace{\left[ \frac{\hat{y} - y}{\hat{y}(1-\hat{y})} \right]}_{\text{Part A}} \cdot \underbrace{\left[ \hat{y}(1-\hat{y}) \right]}_{\text{Part B}} \cdot \underbrace{x_j}_{\text{Part C}} \]

  • Thanks to cancelations, we are left with

\[ \begin{align*} \frac{\partial \mathcal{L}}{\partial w_j} &= (\hat{y} - y)x_j \end{align*} \]

Intuition

The gradient is a product of the error and the magnitude of the input.

  • The Error (\(\hat{y} - y\)): How wrong were we?
  • The Input (\(x_j\)): How much did this specific feature contribute to that wrong prediction?

Bias term

  • HOMEWORK You will adapt this example The term for the partial derivative of the bias can be determined analogously to be

\[ \frac{\partial \mathcal{L}}{\partial b} = (\hat{y} - y) \]

  • In the homework, you will be asked to derive this formally.

Gradient descent

Basic idea of gradient descent

  • Update the weights and biases of each layer \[ \begin{eqnarray*} \mathbf{W} &\leftarrow &\mathbf{W} - \eta\Delta \mathbf{W} \\ \mathbf{b} &\leftarrow &\mathbf{b} - \eta\Delta \mathbf{b} \end{eqnarray*} \]

  • Gradient descent is used in a variety of settings outside of back prop. Let’s do one simple example – finding the minimum of a function

\[ f(x) = 7x^2 + 3x-9 \]

  • It is straightforward to find the minimum analytically by setting the derivative to zero

\[ \begin{eqnarray*} \dfrac{df}{dx} (7x^2 + 3x-9) &=& 14x +3 &=& 0 \\ x &=& - \dfrac{3}{14} \end{eqnarray*} \]

  • To use gradient descent to solve this equation, we need to define the update equation based on the first derivative and a step size \(\eta\) (“eta”).

\[ x \leftarrow x-\eta (14x +3 ) \]

Gradient descent example

import numpy as np
import matplotlib.pylab as plt

def f(x):
  return 7*x**2 + 3*x - 9

def d(x):
  return 14*x + 3

# first guess

eta = 0.03
# plot
x = np.linspace(-1, 1.5, 1000)
plt.figure(figsize=(3, 3))
plt.plot(x, f(x))
t = 1
for i in range(15):
  plt.plot(t, f(t), marker='o', color='r')
  t = t - eta*d(t)
print(f"Solution: {t:.4f} (-3/14={-3/14:.4f})")
plt.axvline(x=t, color='blue', linestyle='--', linewidth=0.8)
Solution: -0.2139 (-3/14=-0.2143)

Gradient descent: Overshoot

Final Solution: -0.0199 (Target: -0.2143)
  • Same code as in the previous example, but learning rate increased from \(\eta = 0.03\) to \(\eta=0.13\)

Stochastic gradient descent

  • If we write the loss as

\[ \mathcal{L} = L(\Theta; \mathbf{x},y) \]

  • This means that we are including all training data (\(\mathbf{x},y\))
  • Gradient descent needs \(\frac{\partial L}{\partial \Theta}\), which is obtained by backprop
  • batch training refers to averaging over all training data
  • As the volume of training data increased, it became less sensible to pass all data to the model for each gradient descent step.
  • minibatch training refers to using a subset of all data for each gradient descent step.
  • Because this procedure is noisy compared to full batch training, we refer to it as stochastic gradient descent (SGD).
  • The noisiness may even be an advantage because it may help avoid falling into local minima
  • minibatch size is a hyperparameter, but typically the order of examples is randomized and chunks of data are chosen until all samples have been used.

Updates

If the following is the derivative rule for one example \[ \frac{\partial \mathcal{L}}{\partial w_j} = (\sigma(z_i) - y_i)x_{i,j} \] then the following is the cost across a batch with m examples \[ \frac{\partial C}{\partial w_j} = \frac{1}{m}\sum_{i=1}^m(\sigma(z_i) - y_i)x_{i,j} \] - The update rule for \(w_j\) is thus \[ w_j \leftarrow w_j - \eta \frac{\partial C}{\partial w_j} \]

Stochastic gradient descent

  • We will usually possess multiple labeled examples to train the logistic regression
  • Stochastic gradient descent means that we calculate the gradient after each training example and adjust the parameters (\(\theta\)) by adjusting \(\theta\) in the direction opposite the gradient

\[ \begin{array}{ll} \hline \mathbf{Algorithm:} & \text{Gradient Descent Update} \\ \hline 1: & \text{Initialize } \mathbf{w} \in \mathbb{R}^d, b \in \mathbb{R} \\ 2: & \textbf{for } i \leftarrow 1 \text{ to } \text{iterations } \textbf{do} \\ 3: & \quad \hat{\mathbf{y}} \leftarrow \sigma(\mathbf{Xw} + b) \\ 4: & \quad \mathbf{w} \leftarrow \mathbf{w} - \eta \frac{1}{m} \mathbf{X}^T(\hat{\mathbf{y}} - \mathbf{y}) \\ 5: & \textbf{end for} \\ \hline \end{array} \]

Batch and Mini-batch training

  • Stochastic gradient descent is called stochastic because it chooses a single random example at a time
  • Batch training trains over all examples before adjusting parenters
  • Since the total Cost \(J\) is the average of all individual losses (\(\frac{1}{m} \sum \mathcal{L}\)), the derivative of the total cost is simply the average of the individual derivatives:\[\frac{\partial J}{\partial w_j} = \frac{1}{m} \sum_{i=1}^{m} (\hat{y}^{(i)} - y^{(i)}) x_j^{(i)}\]
  • In Mini-batch training, we train on a group of examples at a time, and take the average loss of these examples.
  • Modern hardware lets mini-batch training be vectorized for efficiency

Vectorized representation

Sigmoid function

It is convenient to stack many examples into a matrix

\[ \mathbf{\hat{y}} = \sigma(\mathbf{Xw} + b) \]

  • Dimensions

\[ \underbrace{\hat{\mathbf{y}}}_{m \times 1} = \sigma \left( \underbrace{\mathbf{X}}_{m \times n} \cdot \underbrace{\mathbf{w}}_{n \times 1} + b \right) \]

  • Rows of \(X\) = Observations (e.g., \(m\) Patients)
  • Columns of \(X\): Features (\(n\) items in this example)

\[ \mathbf{X} = \begin{pmatrix} x_{1,1} & x_{1,2} & \cdots & x_{1,n} \\ x_{2,1} & x_{2,2} & \cdots & x_{2,n} \\ \vdots & \vdots & \ddots & \vdots \\ x_{m,1} & x_{m,2} & \cdots & x_{m,n} \end{pmatrix} \]

Softmax

  • The softmax is a generalization of the sigmoid

\[ \begin{align*} \sigma(z) & = \frac{1}{1+e^{-z}} \\ \mathrm{softmax}(z_i) & = \frac{e^{z_i}}{\sum_{j=1}^K e^{-z_j}} \end{align*} \]

  • The softmax function takes a vector \(z = [z_1, z_2,\ldots, z_K]\) of \(K\) arbitrary values and maps them to a probability distribution, with each value in the range [0,1], and all the values summing to 1.
  • \(z\), the vector of scores that is the input to the softmax are called logits (as with sigmoid)
  • This allows us to perform logistic regression for \(>2\) outcomes
  • Softmax will be important for neural networks and transformers, we will discuss more later.

Part 4: Python implementation from scratch

  • We will show how to perform LR “from scratch” to promote understanding
  • Usually, it is recommendable to use library code (scikit-learn etc.) for actual analysis
  • HOMEWORK You will adapt this example
  • Here we have created points two classes and want to learn LR parameters for classification

Sigmoid

  • To code up a simple version of logistic regression from scratch, we will need five main items. The sigmoid is the simplest.

\[ \sigma(z) = \frac{1}{1+e^{-z}} = \frac{1}{1+\exp(-z)} \]

import numpy as np
import numpy.typing as npt

def sigmoid(z: npt.NDArray) -> npt.NDArray:
    return 1 / (1 + np.exp(-z))
  • NumPy N-Dimensional Array: the function takes an array and returns an array
  • The function takes the data (X,y) and the current values of the parameters w and b

Cost function

def cost_function(
    X: npt.NDArray, 
    y: npt.NDArray, 
    w: npt.NDArray, 
    b: float
) -> float:
    m = len(y)  # Define m based on the number of labels
    cost_sum = 0.0

    for i in range(m):
        # np.dot between vectors returns a scalar, b is a float
        z = float(np.dot(w, X[i]) + b)
        g = sigmoid(z)

        cost_sum += -y[i] * np.log(g) - (1 - y[i]) * np.log(1 - g)

    return (1 / m) * cost_sum
  • i.e., we calculate the Binary Cross-Entropy Cost Function \[ C(w,b) = \frac{1}{m} \mathcal{L}(\sigma(z_i), y_i) \]

Gradient Function

  • compute derivatives of the cost function
def gradient_function(
    X: npt.NDArray, 
    y: npt.NDArray, 
    w: npt.NDArray, 
    b: float
) -> typing.Tuple[float, npt.NDArray]:
    m, n = X.shape  # m = examples, n = features
    grad_w = np.zeros(n)
    grad_b = 0.0

    for i in range(m):
        # Calculate prediction error for the i-th example
        z = float(np.dot(w, X[i]) + b)
        g = sigmoid(z)
        error = g - y[i]

        # Accumulate bias gradient
        grad_b += error
        
        # Accumulate weight gradients for each feature j
        for j in range(n):
            grad_w[j] += error * X[i, j]

    # Average the gradients over all examples
    grad_b = (1 / m) * grad_b
    grad_w = (1 / m) * grad_w

    return grad_b, grad_w

\[ \frac{\partial C}{\partial w_j} = \frac{1}{m}\sum_{i=1}^m(\sigma(z_i) - y_i)x_{i,j} \quad\text{ and }\quad \frac{\partial C}{\partial b} = \frac{1}{m}\sum_{i=1}^m(\sigma(z_i) - y_i) \]

Gradient descent

def gradient_descent(X: npt.NDArray, 
    y: npt.NDArray, 
    alpha: float, 
    iterations: int
) -> typing.Tuple[npt.NDArray, float]:
    w = np.zeros(n)
    b = 0

    for i in range(iterations):
        grad_b, grad_w = gradient_function(X, y, w, b)

        w = w - alpha * grad_w
        b = b - alpha * grad_b

        if i % 1000 == 0:
            print(f"Iteration {i}: Cost {cost_function(X, y, w, b)}")
    
    return w, b

\[ w_j \leftarrow w_j - \eta \frac{\partial C}{\partial w_j} \quad\text{ and }\quad b \leftarrow b - \eta \frac{\partial C}{\partial b} \]

Prediction

def predict(
    X: npt.NDArray, 
    w: npt.NDArray, 
    b: float
) -> npt.NDArray:
    preds = np.zeros(m)

    for i in range(m):
        z = np.dot(w, X[i]) + b
        g = sigmoid(z)

        preds[i] = 1 if g >= 0.5 else 0
    
    return preds

\[ pred(i) = \begin{cases} 1& \text{if }\sigma(z_i) >= 0.5 \\ 0& \text{if }\sigma(z_i) < 0.5 \\ \end{cases} \]

Running

learning_rate = 0.01
iterations = 10000

final_w, final_b = gradient_descent(X_train, y_train, learning_rate, iterations)

predictions = predict(X_train, final_w, final_b)
accuracy = np.mean(predictions == y_train) * 100
print(f"training accuracy: {accuracy:.2f}%")
Iteration 0: Cost 0.6863856844505929
Iteration 1000: Cost 0.24893304312617473
Iteration 2000: Cost 0.19455725857907238
Iteration 3000: Cost 0.17109379033874833
Iteration 4000: Cost 0.15808906650231236
Iteration 5000: Cost 0.1498628871067555
Iteration 6000: Cost 0.14421883627612167
Iteration 7000: Cost 0.14012775640570907
Iteration 8000: Cost 0.13704269179323025
Iteration 9000: Cost 0.13464584006217875
training accuracy: 95.60%

Decision decision_boundary

  • As mentioned above, the decision boundary for the output of the sigmoid function is typically taken to be 0.5: \[ \begin{align*} \sigma(z) = \sigma(w_1x_{1} + w_2x_2 + b) &= 0.5 \\ w_1x_{1} + w_2x_2 + b &= 0\\ x_2 &= -\frac{w_1}{w_2}x_1 - \frac{b}{w_2}\\ \end{align*} \] Therefore, the slope of the decision boundary is \(-\frac{w_1}{w_2}\) and the intercept is \(-\frac{b}{w_2}\).

Plotting this, we get

Overfitting

  • Here we generate data from two classes with a somewhat more complex decision boundary
  • Underfitting: Logistic regression with x,y values (same as above)
  • Overfitting: 15th-degree polynomial features as input to the logistic regression model
  • Balanced fit: Applying L2 regularization to second model

Part 5: Overfitting

“With four parameters I can fit an elephant and with five I can make him wiggle his trunk.” — John von Neumann

Measuring Model Performance

Model fit can be assessed using the difference between predictions and reality:

  • Prediction Error: Difference between the model’s predictions and new data.
  • Estimation Error: Difference between the estimated and true parameter values.
  • Both types of error are driven by two competing forces, which are heavily dictated by model complexity1:

The Bias-Variance Tradeoff

Bias

  • Error introduced by using a predictive model that is incapable of capturing the underlying structure.
  • Underfitting: Models that are too simple have high bias and low variance.

Variance

  • Error due to excessive sensitivity to small fluctuations and noise in the training data.
  • Overfitting: Overly complex models typically have low bias and high variance.

Regularization

a technique used to enhance the generalization ability of machine learning models by adding a penalty term to the loss function.

  • We will focus on L2 regularization here (but see also Lasso and Elastic Net)
  • For simplicity, let’s focus for now on linear regression with a standard linear model1:

\[ Y = \beta_0 + \beta_1X_1 + \ldots + \beta_pX_p \]

  • If the true relationship between the response and the predictors is approximately linear, the least squares estimates will have low bias.
  • If \(n \gg p\) — that is, if \(n\), the number of observations, is much larger than \(p\), the number of variables—then the least squares estimates tend to also have low variance, and hence will perform well on test observations.
  • if \(n\) is not much larger than \(p\), then there can be a lot of variability in the least squares fit, resulting in overfitting and consequently poor predictions on future observations not used in model training.
  • If \(p>n\), here is no longer a unique least squares coefficient estimate: there are infinitely many solutions. Each of these least squares solutions gives zero error on the training data, but typically very poor test set performance due to extremely high variance

Shrinkage

Shrinkage

Shrinkage (also known as regularization) has the effect of reducing variance.

  • Today, we will focus on L2 regularization (Ridge regression)
  • Lasso regression and elastic net regression are also important techniques to learn about

🧮 The Regularized Cost Function

We add an L2 penalty term to the standard cross-entropy loss function:

\[ J(\mathbf{w}) = -\frac{1}{m} \sum_{i=1}^{m} \left[ y^{(i)} \log(a^{(i)}) + (1 - y^{(i)}) \log(1 - a^{(i)}) \right] + \frac{\lambda}{2m} \|\mathbf{w}\|_2^2 \tag{3}\]

Where the L2 norm penalty is defined as: \[ \|\mathbf{w}\|_2^2 = \sum_{j=1}^{n} w_j^2 \]

🔑 Key Parameters

  • \(\lambda\) (Lambda): The regularization strength hyperparameter.
  • If \(\lambda = 0\): Standard logistic regression (potential overfitting).
  • If \(\lambda \to \infty\): Weights are driven toward zero (underfitting).

Gradient of Log-Loss with L2 Regularization

To train our regularized model, we need the gradient of the objective function \(J(\mathbf{w})\), which combines our standard cross-entropy loss \(\mathcal{L}\) and the L2 penalty.

Using the sum rule for differentiation, we take the derivative of both parts with respect to weight \(w_j\):

\[ \frac{\partial J(\mathbf{w})}{\partial w_j} = \underbrace{\frac{\partial \mathcal{L}}{\partial w_j}}_{\text{Standard Gradient}} + \underbrace{\frac{\partial}{\partial w_j} \left( \frac{\lambda}{2m} \sum_{k=1}^{n} w_k^2 \right)}_{\text{L2 Derivative}} \tag{4}\]

Evaluating the derivative of the penalty term (where \(\frac{\partial}{\partial w_j}(w_j^2) = 2w_j\)), we get:

\[ \frac{\partial J(\mathbf{w})}{\partial w_j} = \underbrace{(\hat{y} - y)x_j}_{\text{Prediction Error}} + \underbrace{\frac{\lambda}{m} w_j}_{\text{Weight Penalty}} \tag{5}\]


💡 Intuition for Gradient Descent Updates

When we update our weights (\(w_j \leftarrow w_j - \alpha \frac{\partial J}{\partial w_j}\)) using Equation 5, the L2 term (i.e., weight penalty) alters the step:

  • The Core Error \((\hat{y} - y)x_j\): Same as above!
  • The Weight Penalty \(\frac{\lambda}{m} w_j\): The larger a weight grows, the harder gradient descent penalizes it, effectively preventing any single feature from dominating the model.


  • The Sum of Convex Functions is Convex

  • The above total cost function \(J(\mathbf{w})\) is composed of two distinct components \[J(\mathbf{w}) = \mathcal{L}_{\text{cross-entropy}}(\mathbf{w}) + \mathcal{L}_{\text{L2}}(\mathbf{w})\]

  • THe Standard negative log-likelihood (cross-entropy loss) for logistic regression is convex.

  • L2 Penalty (\(\frac{\lambda}{2m}\|\mathbf{w}\|_2^2\)) is a quadratic function (“bowl shaped”), which is strictly convex.

  • Thus logistic regression with L2 penalty has a unique solution

  • We still need to decide which value to use for \(\lambda\), which is a hyperparameter.

  • The optimal value of \(\lambda\) can be estimated with \(k\)-Fold Cross-Validation

  • HOMEWORK You will receive code for “do-it-yourself” logisitic regression in Python and be askeed to add L2 regularization.