{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": [
"remove-cell"
]
},
"outputs": [],
"source": [
"import sys\n",
"import os\n",
"if not any(path.endswith('textbook') for path in sys.path):\n",
" sys.path.append(os.path.abspath('../../..'))\n",
"from textbook_utils import *"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(sec:linear_multi_fit)=\n",
"# Fitting the Multiple Linear Model"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the previous section we considered the case of two explanatory variables; one of these was called $x$ and the other $v$. Now we want to generalize the approach to $p$ explanatory variables. Our approach of choosing new letters to represent additional variables quickly fails us. Instead, we use a more formal and general approach that represents multiple predictors as a matrix, as depicted in {numref}`Figure %s `. We call $\\textbf{X}$ the *design matrix*. Notice that $\\textbf{X}$ has shape $ n \\times (p + 1)$. Each column of $\\textbf{X}$ represents a variable, and each row represents an observation. That is, $x_{i,j}$ is the measurement taken on observation $i$ of variable $j$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{figure} figures/design-matrix.svg\n",
"---\n",
"name: fig:design-matrix\n",
"---\n",
"\n",
"Each row and column of $X$ represent an observation and a feature.\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
":::{note}\n",
"\n",
"One technicality: the design matrix is defined as a mathematical matrix,\n",
"not a dataframe, so you might notice that a matrix doesn't include the column or row labels that the dataframe has.\n",
"\n",
"That said, we usually don't have to worry about converting dataframes into matrices\n",
"since most Python libraries for modeling treat dataframes of numbers as if they were matrices.\n",
"\n",
":::"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a given observation, say, the second row in $\\textbf{X}$, we represent the outcome $y_2$ by the linear approximation:\n",
"\n",
"$$\n",
"\\begin{aligned}\n",
"y_2 \\approx {\\hat{y}_2} = \\theta_0 + \\theta_1 x_{2,1} + \\ldots + \\theta_p x_{2,p} \n",
"\\end{aligned}\n",
"$$\n",
"\n",
"Similar to the simple linear model, our\n",
"multiple linear model predicts $\\hat{y}_2$ as a linear combination of the values $[x_{2,1}, \\ldots,x_{2,p}]$.\n",
"\n",
"Also, we can write the model parameters as a $p+1$ column vector ${\\boldsymbol{\\theta}}$, \n",
"\n",
"$$\n",
"\\boldsymbol{\\theta} = \n",
"\\begin{bmatrix}\n",
"\\theta_0 \\\\\n",
"\\theta_1 \\\\\n",
"\\vdots \\\\\n",
"\\theta_p\n",
"\\end{bmatrix}\n",
"$$\n",
"\n",
"\n",
"\n",
"Putting these notational definitions together, we can write the vector of $\\mathbf{\\hat{y}}$ predictions for the entire dataset using matrix multiplication:\n",
"\n",
"$$\n",
"\\begin{aligned}\n",
"\\hat{\\mathbf{y}} &= {\\textbf{X}} {\\boldsymbol{\\theta}}\n",
"\\end{aligned}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Additionally, the errors in using $\\hat{\\mathbf{y}}$ to predict $\\mathbf{y}$ can be expressed as the $n$-dimensional column vector:\n",
"\n",
"$$ \\mathbf{e} = \\mathbf{y} - \\hat{\\mathbf{y}}.$$\n",
"\n",
"If we check the dimensions of $\\textbf{X}$ and $\\boldsymbol{\\theta}$, it confirms that $\\hat{\\mathbf{y}}$ is an $n$-dimensional column vector. \n",
"Each element in this vector is the model's prediction for one observation.\n",
"\n",
"and we also represent $\\mathbf{y}$ as a column vector of outcomes:\n",
"\n",
"$$ \n",
"\\mathbf{y} = \n",
"\\begin{bmatrix}\n",
"y_1 \\\\\n",
"y_2 \\\\\n",
"\\vdots \\\\\n",
"y_n\n",
"\\end{bmatrix}\n",
"$$\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This matrix representation of the multiple linear model can help us fit the model.\n",
"Similar to the simple linear model, we find the best model using squared loss.\n",
"That is, we want to find the model parameters $\\hat{\\boldsymbol{\\theta}}$ that minimize the average squared loss:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\begin{aligned}\n",
"L({\\boldsymbol{\\theta}}, \\textbf{X}, {\\mathbf{y}})\n",
" &= \\frac{1}{n} \\sum_i [y_i - (\\theta_0 + \\theta_1 x_{i,1} + \\cdots + \\theta_p x_{i,p})]^2 \\\\\n",
" &= \\frac{1}{n} \\lVert \\mathbf{y} - {\\textbf{X}} {\\boldsymbol{\\theta}} \\rVert^2\n",
"\\end{aligned}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here, we use the notation $ \\lVert \\mathbf{v} \\rVert^2 $ for a vector $\\mathbf{v}$ as a\n",
"shorthand for the sum of each vector element squared [^ell2]:\n",
"$\\lVert \\mathbf{v} \\rVert^2 = \\sum_i v_i^2$.\n",
"\n",
"We can fit our model using calculus as we did for the simple linear model.\n",
"However, this approach gets cumbersome, and instead we use a geometric argument that is more intuitive and easily leads to useful properties of the design matrix, errors, and predicted values.\n",
"\n",
"[^ell2]: $\\lVert \\mathbf{v} \\rVert^2$ is also called the $\\ell_2$ norm of a vector $\\mathbf{v}$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A Geometric Problem"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Again, our goal is the find the $\\boldsymbol{\\hat{\\theta}}$ that minimizes our loss\n",
"function---we want to make $L({\\boldsymbol{\\theta}}, \\textbf{X}, \\mathbf{y})$ as small as possible for a given $\\textbf{X}$ and $\\mathbf{y}$.\n",
"The key insight is that we can restate this goal in a geometric way.\n",
"Since the model predictions $ f_{\\theta}(\\mathbf{X}) $ and the true outcomes\n",
"$\\mathbf{y}$ are both vectors, we can think of them as points in *variable space*.\n",
"When we change our model parameters $ {\\boldsymbol{\\theta}} $, the model makes \n",
"different predictions $ f_{\\theta}(\\mathbf{X}) $, and the space of all possible model predictions is $ \\text{span}(\\mathbf{X}) $, as illustrated in\n",
"{numref}`Figure %s `."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{figure} figures/spanX.svg\n",
"---\n",
"name: fig:spanX\n",
"width: 80%\n",
"---\n",
"\n",
"In this simplified diagram, the space of all possible model prediction vectors $ \\text{span}(\\mathbf{X}) $ is illustrated as a gray plane in 3-dimensional space, and the observed $ \\mathbf{y} $ as a point.\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
":::{note}\n",
"\n",
"As {numref}`Figure %s ` shows, the true outcome vector $ y $ doesn't usually lie in $ \\text{span}(\\mathbf{X}) $. This means that the model can't get perfect accuracy on the training set. Although our loss can't be exactly zero, we can instead find the point in $ \\text{span}(\\mathbf{X}) $ that lies as close to $ y $ as possible.\n",
"\n",
":::"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The error vector $ \\mathbf{e} = \\mathbf{y} - f_{\\theta}(\\mathbf{X}) $ represents the distance between the true outcomes and our model's predictions.\n",
"Visually, $ \\mathbf{e} $ has the smallest magnitude when it is *perpendicular* to the subspace $ \\text{span}(\\mathbf{X}) $, as shown in\n",
"{numref}`Figure %s `.\n",
"The proof of this fact is omitted for brevity."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{figure} figures/error-vector-optimal.svg\n",
"---\n",
"name: fig:error-vector-optimal\n",
"width: 80%\n",
"---\n",
"\n",
"The error is minimized when it lies perpendicular to $ \\text{span}(\\mathbf{X}) $.\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In other words, there are many possible model parameters $ \\boldsymbol{\\theta} $, but only one $ \\hat{\\boldsymbol{\\theta}} $ minimizes the loss by making the error vector perpendicular to $ \\text{span}(\\mathbf{X}) $."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This fact lets us derive a formula for $ \\hat{\\boldsymbol{\\theta}} $ as follows:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\n",
"\\begin{aligned}\n",
"\\textbf{X} \\boldsymbol{\\hat{\\theta}} + \\mathbf{e} &= \\mathbf{y} \\\\\n",
"{\\textbf{X}}^\\top \\textbf{X} \\hat{\\boldsymbol{\\theta}} + {\\textbf{X}}^\\top \\mathbf{e} &= {\\textbf{X}}^\\top \\mathbf{y}\n",
" & (\\text{left-multiply by } {\\textbf{X}}^\\top) \\\\\n",
"{\\textbf{X}}^\\top \\textbf{X} \\hat{\\boldsymbol{\\theta}} &= {\\textbf{X}}^\\top \\mathbf{y}\n",
" & (\\mathbf{e} \\perp \\text{span}(\\textbf{X})) \\\\\n",
"\\boldsymbol{\\hat{\\theta}} &= ({\\textbf{X}}^\\top \\textbf{X})^{-1} {\\textbf{X}}^\\top \\mathbf{y}\n",
" & (\\text{left-multiply by } ({\\textbf{X}}^\\top \\textbf{X})^{-1})\n",
"\\end{aligned}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this derivation, we used the fact that $ {\\textbf{X}}^\\top \\mathbf{e} = 0 $\n",
"when $ \\mathbf{e} $ is perpendicular to $ \\text{span}(\\mathbf{X}) $."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" \n",
"This general approach to derive $\\boldsymbol{\\hat{\\theta}}$ for the multiple linear model also gives\n",
"us $\\hat{\\theta}_0$ and $\\hat{\\theta}_1$ for the simple linear model. If we set\n",
"${\\textbf{X}}$ to contain the intercept column and one column of features, using this\n",
"formula for $\\boldsymbol{\\hat{\\theta}}$ and some linear algebra, we can get the intercept and slope of the least-squares-fitted simple linear model. In fact, if ${\\textbf{X}}$ is simply a single column of $1$s, then we can use this formula to show that $\\boldsymbol{\\hat{\\theta}}$ is just the mean of $\\mathbf{x}$. This ties back to the constant model that we introduced in {numref}`Chapter %s `. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
":::{note}\n",
"\n",
"While, we can write a simple function to derive the $\\boldsymbol{\\hat{\\theta}}$ based on the formula, \n",
"\n",
"$$\n",
"\\boldsymbol{\\hat{\\theta}} = ({\\textbf{X}}^\\top \\textbf{X})^{-1} {\\textbf{X}}^\\top \\mathbf{y},\n",
"$$\n",
"\n",
"we recommend leaving the calculation of $\\boldsymbol{\\hat{\\theta}}$ to the optimally tuned methods provided in the `scikit-learn` or `statsmodels` libraries. They handle cases where the design matrix is sparse, highly co-linear, and not invertible. \n",
"\n",
":::"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This solution for $\\boldsymbol{\\hat{\\theta}}$ (along with the pictures) reveal some useful properties of the fitted coefficients and the predictions:\n",
"\n",
"+ The residuals are orthogonal to the predicted values. \n",
"+ The average of the residuals is $0$, if the model has an intercept term.\n",
"+ The variance of the residuals is just the ASE. \n",
"\n",
"These properties explain why we examine plots of the residuals against the predictions. When we fit a multiple linear model, we also plot the residuals against variables that we are considering adding to the model. If they show a linear pattern, then we would add them to the model. \n",
"\n",
"In addition to examining the SD of the errors, the ratio of the ASE for a multiple linear model to the ASE for the constant model gives a measure of the model fit. This is called the *Multiple $R^2$* and is defined as: \n",
"\n",
"$$ \n",
"R^2 = 1 - \\frac {\\lVert \\mathbf{y} - {\\textbf{X}}{\\boldsymbol{\\hat{\\theta}}} \\rVert^2}\n",
" {\\lVert {\\mathbf{y}} - \\bar{y} \\rVert^2}.\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As the model fits the data closer and closer, the multiple $R^2$ gets nearer to $1$. That might seem like a good thing, but there are problems with this approach because $R^2$ continues to grow even as we add meaningless features to our model, as long as the features fill out the $\\text{span}(\\textbf{X})$. To account for the size of a model, we often adjust the numerator and denominator by the number of fitted coefficients in the models. That is, we would normalize the numerator by $1/[n-(p+1)]$ and the denominator by $1/(n-1)$. Better approaches to selecting a model are covered in {numref}`Chapter %s `."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}