Logistic regression#

Simple logistic regression#

Just like in Simple linear regression, if we have only one feature \(x\) and two classes \(0\) (negative) and \(1\) (positive), introduce two parameters: intercept \(w_0\) and slope \(w_1\). Then put

\[ \widehat y = \mathbb P(\text{class }1) = \sigma(w_0 + w_1 x) \]

where \(\sigma(t) = \frac 1{1 + e^{-t}}\)sigmoid function.

../_images/b22d7187b9dce037b857713a41b17222788bbbeb369ca440caf36f92a505fef7.svg

Q. What is \(\mathbb P(\text{class } 0)\)?

Q. How to predict classes if we know predicted probabilities?

Binary logistic regression#

Now suppose that each sample is descirbed by \(d\) numeric features, and we have a dataset \(\mathcal D = \{\boldsymbol x_i, y_i\}_{i=1}^n\), \(y_i \in \{0, 1\}\), \(\boldsymbol x_i \in \mathbb R^d\). Logistic regression model predicts the probability of the positive class:

\[ \widehat y_i = \sigma(\boldsymbol x_i^\mathsf{T} \boldsymbol w) = \mathbb P(\boldsymbol x \in \text{class }1), \]

The linear output \(\boldsymbol x^\mathsf{T} \boldsymbol w\) is also called logit.

Note

As for linear regression model, intercept is included to \(\boldsymbol w\) by putting \(\boldsymbol x_0 = \boldsymbol 1\).

The loss function is binary cross-entropy

(27)#\[\begin{split} \begin{multline*} \mathcal L(\boldsymbol w) = -\frac 1n\sum\limits_{i=1}^n \big(y_i \log \widehat y_i + (1-y_i)\log(1-\widehat y_i)\big) = \\ =-\frac 1n\sum\limits_{i=1}^n \big(y_i \log(\sigma(\boldsymbol x_i^\mathsf{T}\boldsymbol w)) + (1- y_i)\log(1 - \sigma(\boldsymbol x_i^\mathsf{T} \boldsymbol w))\big). \end{multline*}\end{split}\]

Fitting a logistic regression model is achieved by minimizing this loss function with respect to \(\boldsymbol w\).

Question

How will the cross entropy loss change if \(\mathcal Y = \{-1, 1\}\)?

Regularization#

Logistic regression can also suffer from multicollinearity. A possible solution is to add regularization term of the form \(C\Vert \boldsymbol w \Vert\). For example, the loss function for \(L_2\)-regularized logistic regression with \(\mathcal Y = \{-1, 1\}\) is

\[ \mathcal L(\boldsymbol w) = \frac 1n\sum\limits_{i=1}^n \log \big(1 + e^{y_i \boldsymbol x_i^\mathsf{T} \boldsymbol w}\big) + C \boldsymbol w^\mathsf{T} \boldsymbol w. \]

There are also versions of \(L_1\) penalizer or elastic net.

Example: breast cancer dataset#

This is a dataset with \(30\) features and binary target.

from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
data['data'].shape, data['target'].shape
((569, 30), (569,))

Malignant or benign?

data.target_names
array(['malignant', 'benign'], dtype='<U9')
data.feature_names
array(['mean radius', 'mean texture', 'mean perimeter', 'mean area',
       'mean smoothness', 'mean compactness', 'mean concavity',
       'mean concave points', 'mean symmetry', 'mean fractal dimension',
       'radius error', 'texture error', 'perimeter error', 'area error',
       'smoothness error', 'compactness error', 'concavity error',
       'concave points error', 'symmetry error',
       'fractal dimension error', 'worst radius', 'worst texture',
       'worst perimeter', 'worst area', 'worst smoothness',
       'worst compactness', 'worst concavity', 'worst concave points',
       'worst symmetry', 'worst fractal dimension'], dtype='<U23')

Divide the dataset into train and test:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(data.data, data.target, test_size=0.2)

Train and evaluate \(30\) simple logistic regressions — one per each feature:

from sklearn.linear_model import LogisticRegression

for i, feature in enumerate(data.feature_names):
    log_reg = LogisticRegression()
    log_reg.fit(X_train[:, i][:, None], y_train)
    print(feature)
    print("  Train accuracy: {:.2%}".format(log_reg.score(X_train[:, i][:, None], y_train)) + 
          "  test accuracy: {:.2%}".format(log_reg.score(X_test[:, i][:, None], y_test)))
    print(f"  Intercept: {log_reg.intercept_}, coef: {log_reg.coef_[0]}")
Hide code cell output
mean radius
  Train accuracy: 88.35%  test accuracy: 87.72%
  Intercept: [15.22547401], coef: [-1.02574878]
mean texture
  Train accuracy: 67.69%  test accuracy: 73.68%
  Intercept: [5.03222197], coef: [-0.22715068]
mean perimeter
  Train accuracy: 89.01%  test accuracy: 88.60%
  Intercept: [15.73101082], coef: [-0.1631407]
mean area
  Train accuracy: 88.79%  test accuracy: 85.96%
  Intercept: [8.11064014], coef: [-0.01183704]
mean smoothness
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.66028713], coef: [-1.00675816]
mean compactness
  Train accuracy: 67.47%  test accuracy: 62.28%
  Intercept: [1.11667038], coef: [-5.29806788]
mean concavity
  Train accuracy: 76.04%  test accuracy: 76.32%
  Intercept: [1.216197], coef: [-7.19341908]
mean concave points
  Train accuracy: 65.49%  test accuracy: 62.28%
  Intercept: [0.83783383], coef: [-5.57518259]
mean symmetry
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.89207282], coef: [-1.81787956]
mean fractal dimension
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.56269606], coef: [0.01986425]
radius error
  Train accuracy: 80.22%  test accuracy: 78.07%
  Intercept: [2.90949024], coef: [-5.86425646]
texture error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.41744785], coef: [0.12174087]
perimeter error
  Train accuracy: 79.34%  test accuracy: 78.95%
  Intercept: [4.39973411], coef: [-1.39953561]
area error
  Train accuracy: 87.47%  test accuracy: 84.21%
  Intercept: [5.01455766], coef: [-0.1343004]
smoothness error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.56348868], coef: [0.06513609]
compactness error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.58980254], coef: [-1.04631197]
concavity error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.60690871], coef: [-1.35834348]
concave points error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.56964054], coef: [-0.48747656]
symmetry error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.5617867], coef: [0.10568277]
fractal dimension error
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.564076], coef: [-0.03866078]
worst radius
  Train accuracy: 92.09%  test accuracy: 89.47%
  Intercept: [19.74621311], coef: [-1.18154614]
worst texture
  Train accuracy: 73.41%  test accuracy: 70.18%
  Intercept: [5.68230831], coef: [-0.19463673]
worst perimeter
  Train accuracy: 92.53%  test accuracy: 88.60%
  Intercept: [20.94628998], coef: [-0.18987604]
worst area
  Train accuracy: 91.87%  test accuracy: 89.47%
  Intercept: [10.55749958], coef: [-0.01230494]
worst smoothness
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.82864235], coef: [-2.0121464]
worst compactness
  Train accuracy: 80.22%  test accuracy: 73.68%
  Intercept: [2.26847288], coef: [-6.60558801]
worst concavity
  Train accuracy: 84.40%  test accuracy: 80.70%
  Intercept: [2.47647793], coef: [-6.76251851]
worst concave points
  Train accuracy: 80.00%  test accuracy: 72.81%
  Intercept: [1.49975318], coef: [-7.91253492]
worst symmetry
  Train accuracy: 66.59%  test accuracy: 63.16%
  Intercept: [1.72204249], coef: [-3.98764921]
worst fractal dimension
  Train accuracy: 63.74%  test accuracy: 58.77%
  Intercept: [0.67024595], coef: [-1.27892772]

Now take all \(30\) features at once:

log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)
/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/sklearn/linear_model/_logistic.py:460: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.

Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
LogisticRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

The default value of max_iter is \(100\), and here it is not enough for convergence. However, accuracy is not bad:

print("Train accuracy:", log_reg.score(X_train, y_train))
print("Test accuracy:", log_reg.score(X_test, y_test))
Train accuracy: 0.9582417582417583
Test accuracy: 0.8947368421052632

Now increase max_iter argument:

log_reg = LogisticRegression(max_iter=3000)
log_reg.fit(X_train, y_train)
LogisticRegression(max_iter=3000)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

The improvement of accuracy seems not to be significant:

print("Train accuracy:", log_reg.score(X_train, y_train))
print("Test accuracy:", log_reg.score(X_test, y_test))
Train accuracy: 0.9758241758241758
Test accuracy: 0.9122807017543859

Exercises#

  1. Suppose we collect data for a group of students in a statistics class with variables

    \[ x_1 = \text{hours studied},\quad x_2 = \text{undergrad GPA},\quad y = \text{receive an A}. \]

    We fit a logistic regression and produce estimated coefficients

    \[ w_0 = -6, \quad w_1 = 0.05, \quad w_2 = 1. \]
    • Estimate the probability that a student who studies for \(40\) h and has an undergrad GPA of \(3.5\) gets an A in the class.

    • How many hours would the student need to study to have a \(50\%\) chance of getting an A in the class?

  2. Write down the loss function of logstic regression model with \(\mathcal Y = \{-1, 1\}\) in matrix-vector form.