Regresión

Los objetivos de esta clase son:

  • Comprender/recordar regresión lineal.

  • Estimar el error al aplicar modelos matemáticos a los datos.

  • Introducir la librería scikit-learn.

  • Otros tipos de regresión.

Motivación

La regresión lineal es una técnica universalmente utilizada y a pesar de su simpletaza, la derivación de este método entrega importantes consideraciones sobre su implementación, sus hipótesis y sus posibles extensiones.

Para motivar el estudio utilizaremos datos de diabetes disponibles en la biblioteca scikit learn.

import numpy as np
import pandas as pd
import altair as alt

from sklearn import datasets

alt.themes.enable('opaque')  # Para quienes utilizan temas oscuros en Jupyter Lab
ThemeRegistry.enable('opaque')
diabetes_X, diabetes_y = datasets.load_diabetes(return_X_y=True, as_frame=True)
diabetes = pd.concat([diabetes_X, diabetes_y], axis=1)
diabetes.head()
age sex bmi bp s1 s2 s3 s4 s5 s6 target
0 0.038076 0.050680 0.061696 0.021872 -0.044223 -0.034821 -0.043401 -0.002592 0.019908 -0.017646 151.0
1 -0.001882 -0.044642 -0.051474 -0.026328 -0.008449 -0.019163 0.074412 -0.039493 -0.068330 -0.092204 75.0
2 0.085299 0.050680 0.044451 -0.005671 -0.045599 -0.034194 -0.032356 -0.002592 0.002864 -0.025930 141.0
3 -0.089063 -0.044642 -0.011595 -0.036656 0.012191 0.024991 -0.036038 0.034309 0.022692 -0.009362 206.0
4 0.005383 -0.044642 -0.036385 0.021872 0.003935 0.015596 0.008142 -0.002592 -0.031991 -0.046641 135.0

En este conjunto la variable a predecir (target) es una medida cuantitativa de la progresión de la enfermedad un año después de la línea base. Contiene 10 características (features) numéricas, definidas en la siguienta tabla. Cada una de estas ha sido centrada y escalada por su desviación estandar multiplicada por la cantidad de muestras, i.e. la suma de cuadrados de cada columna es 1, que en la prácitca es que tengan norma unitaria.

Feature

Descripción

age

age in years

sex

sex

bmi

body mass index

bp

average blood pressure

s1

tc, T-Cells (a type of white blood cells)

s2

ldl, low-density lipoproteins

s3

hdl, high-density lipoproteins

s4

tch, thyroid stimulating hormone

s5

ltg, lamotrigine

s6

glu, blood sugar level

A continuación exploremos los datos

diabetes.describe().T
count mean std min 25% 50% 75% max
age 442.0 -3.639623e-16 0.047619 -0.107226 -0.037299 0.005383 0.038076 0.110727
sex 442.0 1.309912e-16 0.047619 -0.044642 -0.044642 -0.044642 0.050680 0.050680
bmi 442.0 -8.013951e-16 0.047619 -0.090275 -0.034229 -0.007284 0.031248 0.170555
bp 442.0 1.289818e-16 0.047619 -0.112400 -0.036656 -0.005671 0.035644 0.132044
s1 442.0 -9.042540e-17 0.047619 -0.126781 -0.034248 -0.004321 0.028358 0.153914
s2 442.0 1.301121e-16 0.047619 -0.115613 -0.030358 -0.003819 0.029844 0.198788
s3 442.0 -4.563971e-16 0.047619 -0.102307 -0.035117 -0.006584 0.029312 0.181179
s4 442.0 3.863174e-16 0.047619 -0.076395 -0.039493 -0.002592 0.034309 0.185234
s5 442.0 -3.848103e-16 0.047619 -0.126097 -0.033249 -0.001948 0.032433 0.133599
s6 442.0 -3.398488e-16 0.047619 -0.137767 -0.033179 -0.001078 0.027917 0.135612
target 442.0 1.521335e+02 77.093005 25.000000 87.000000 140.500000 211.500000 346.000000
diabetes.apply(np.linalg.norm)
age          1.000000
sex          1.000000
bmi          1.000000
bp           1.000000
s1           1.000000
s2           1.000000
s3           1.000000
s4           1.000000
s5           1.000000
s6           1.000000
target    3584.818126
dtype: float64

También es interesante ver como se relaciona cada feature con el target.

base = alt.Chart(diabetes).mark_circle().encode(
    x=alt.X(alt.repeat("row"), type='quantitative'),
    y="target"
).properties(
    width=300,
    height=300
)

base.repeat(row=diabetes_X.columns.tolist()[:4]) | base.repeat(row=diabetes_X.columns.tolist()[4:7]) | base.repeat(row=diabetes_X.columns.tolist()[7:])

Modelo

Supondremos que tenemos \(m\) datos.

Cada dato \(x^{(i)}\), \(i=1,\dots,\) \(m\) tiene \(n\) componentes, \(x^{(i)} = (x^{(i)}_1, ..., x^{(i)}_n)\).

Conocemos además el valor (etiqueta) asociado a \(x^{(i)}\) que llamaremos \(y^{(i)}\), \(i=1,\dots, m\) .

Nuestra hipótesis de modelo lineal puede escribirse como

\[\begin{split}\begin{aligned} h_{\theta}(x) &= \theta_0 + \theta_1 x_1 + \theta_2 x_2 + ... + \theta_n x_n \\ &= \begin{bmatrix}\theta_0 & \theta_1 & \theta_2 & \dots & \theta_n\end{bmatrix} \begin{bmatrix}1 \\ x_1 \\x_2 \\ \vdots \\ x_n\end{bmatrix} \\ &= \theta^T \begin{bmatrix}1\\x\end{bmatrix} = \begin{bmatrix}1 & x^T\end{bmatrix} \theta \end{aligned}\end{split}\]

Definiremos \(x^{(i)}_0 =1\), de modo que \(h_{\theta}(x^{(i)}) = (x^{(i)})^T \theta \) y buscamos el vector de parámetros $\(\theta = \begin{bmatrix}\theta_0 \\ \theta_1 \\ \theta_2 \\ \vdots \\ \theta_n\end{bmatrix}\)$

Definamos las matrices

\[\begin{split}\begin{aligned} Y &= \begin{bmatrix}y^{(1)} \\ y^{(2)} \\ \vdots \\ y^{(m)}\end{bmatrix}\end{aligned}\end{split}\]

y

\[\begin{split}\begin{aligned} X = \begin{bmatrix} 1 & x^{(1)}_1 & \dots & x^{(1)}_n \\ 1 & x^{(2)}_1 & \dots & x^{(2)}_n \\ \vdots & \vdots & & \vdots \\ 1 & x^{(m)}_1 & \dots & x^{(m)}_n \\ \end{bmatrix} = \begin{bmatrix} - (x^{(1)})^T - \\ - (x^{(2)})^T - \\ \vdots \\ - (x^{(m)})^T - \\ \end{bmatrix}\end{aligned}\end{split}\]

Luego la evaluación de todos los datos puede escribirse matricialmente como

\[\begin{split}\begin{aligned} X \theta &= \begin{bmatrix} 1 & x_1^{(1)} & ... & x_n^{(1)} \\ \vdots & \vdots & & \vdots \\ 1 & x_1^{(m)} & ... & x_n^{(m)} \\ \end{bmatrix} \begin{bmatrix}\theta_0 \\ \theta_1 \\ \vdots \\ \theta_n\end{bmatrix} \\ & = \begin{bmatrix} 1 \theta_0 + x^{(1)}_1 \theta_1 + ... + x^{(1)}_n \theta_n \\ \vdots \\ 1 \theta_0 + x^{(m)}_1 \theta_1 + ... + x^{(m)}_n \theta_n \\ \end{bmatrix} \\ & = \begin{bmatrix} h(x^{(1)}) \\ \vdots \\ h(x^{(m)}) \end{bmatrix}\end{aligned}\end{split}\]

Nuestro problema es encontrar un “buen” conjunto de valores \(\theta\) de modo que

\[\begin{split}\begin{aligned} \begin{bmatrix} h(x^{(1)}) \\ h(x^{(2)}) \\ \vdots \\ h(x^{(m)}) \end{bmatrix} \approx \begin{bmatrix}y^{(1)} \\ y^{(2)} \\ \vdots \\ y^{(m)}\end{bmatrix}\end{aligned}\end{split}\]

es decir, que $\(X \theta \approx Y\)$

Para encontrar el mejor vector \(\theta\) podríamos definir una función de costo \(J(\theta)\) de la siguiente manera:

\[J(\theta) = \frac{1}{2} \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right)^2\]

Implementaciones

Aproximación Ingenieril

¿Cómo podemos resolver el problema en el menor número de pasos?

Deseamos resolver el sistema $\(A \theta = b\)\( con \)A \in \mathbb{R}^{m \times n}\( y \)m > n\( (La matrix \)A$ es skinny).

¿Cómo resolvemos?

Bueno, si \(A \in \mathbb{R}^{m \times n}\), entonces \(A^T \in \mathbb{R}^{n \times m}\) y la multiplicación está bien definida y obtengo el siguiente sistema lineal, conocido como Ecuación Normal: \(n \times n\). $\((A^T A) \ \theta = A^T b\)$

Si la matriz \(A^T A\) es invertible, el sistema se puede solucionar “sin mayor reparo”. $\(\theta = (A^T A)^{-1} A^T b\)$

En nuestro caso, obtendríamos $\(\theta = (X^T X)^{-1} X^T Y\)$ Esta respuesta, aunque correcta, no admite interpretaciones y no permite generalizar a otros casos más generales.

En particular…

  • ¿Qué relación tiene con la función de costo (no) utilizada?

  • ¿Qué pasa si \(A^T A\) no es invertible?

Aproximación Machine Learning

¿Cómo podemos obtener una buena aproximación para \(\theta\)?

Queremos encontrar \(\theta^*\) que minimice \(J(\theta)\).

Basta con utilizar una buena rutina de optimización para cumplir con dicho objetivo.

En particular, una elección natural es tomar la dirección de mayor descenso, es decir, el método del máximo descenso (gradient descent).

\[\theta^{(n+1)} = \theta^{(n)} - \alpha \nabla_{\theta} J(\theta^{(n)})\]

donde \(\alpha >0\) es la tasa de aprendizaje.

En nuestro caso, puesto que tenemos $\(J(\theta) = \frac{1}{2} \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right)^2\)$ se tiene que

\[\begin{split}\begin{aligned} \frac{\partial J(\theta)}{\partial \theta_k} &= \frac{\partial }{\partial \theta_k} \frac{1}{2} \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right)^2 \\ &= \frac{1}{2} \sum_{i=1}^{m} 2 \left( h_{\theta}(x^{(i)}) - y^{(i)}\right) \frac{\partial h_{\theta}(x^{(i)})}{\partial \theta_k} \\ &= \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right) x^{(i)}_k\end{aligned}\end{split}\]

Luego, el algoritmo queda como sigue: $\(\begin{aligned} \theta^{(n+1)} & = \theta^{(n)} - \alpha \nabla_{\theta} J(\theta^{(n)}) \\\\ \frac{\partial J(\theta)}{\partial \theta_k} &= \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right) x^{(i)}_k\end{aligned}\)$

Observación: La elección de \(\alpha\) es crucial para la convergencia. En particular, una regla de trabajo es utilizar \(0.01/m\). Notar que el parámetro \(\alpha\) no es un parámetro del modelo como tal, si no que es parte del algoritmo, este tipo de parámetros se suelen llamar hyperparameters. Pudes reconocerlos porque el valor del parámetro es conocido antes de la fase de entrenamiento del modelo.

def lms_regression_slow(X, Y, theta, tol=1E-6):
    m, n = X.shape
    converged = False
    alpha = 0.01 / len(Y)
    while not converged:
        gradient = 0.
        for xiT, yi in zip(X, Y):
            xiT = xiT.reshape(1, n)
            hi = np.dot(xiT, theta)
            gradient += (hi - yi) * xiT.T
        new_theta = theta - alpha * gradient
        converged = np.linalg.norm(theta - new_theta) < tol * np.linalg.norm(theta)
        theta = new_theta
    return theta
m = 1000
t = np.linspace(0, 1, m)
x = 2 + 2 * t
y = 300 + 100 * t
X = np.array([np.ones(m), x]).T
Y = y.reshape(m, 1)
theta_0 = np.array([[0.0], [0.0]])
theta_slow = lms_regression_slow(X, Y, theta_0)
print(theta_slow)

Validamos si nuestro resultado es el indicado con una tolerancia dada.

np.allclose(X @ theta_slow, Y, atol=0.5)
True
np.allclose(X @ theta_slow, Y, atol=1e-3)
False

Implementación Vectorial

¿Cómo podemos obtener una justificación para la ecuación normal?

Necesitamos los siguientes ingredientes:

\[\begin{split}\begin{aligned} \nabla_x &(x^T A x) = A x + A^T x \\ \nabla_x &(b^T x) = b \end{aligned}\end{split}\]

Se tiene

\[\begin{split}\begin{aligned} J(\theta) &= \frac{1}{2} \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right)^2 \\ &= \frac{1}{2} \sum_{i=1}^{m} \left( h_{\theta}(x^{(i)}) - y^{(i)}\right) \left( h_{\theta}(x^{(i)}) - y^{(i)}\right) \\ &= \frac{1}{2} \left( X \theta - Y \right)^T \left( X \theta - Y \right) \\ &= \frac{1}{2} \left( \theta^T X^T - Y^T \right) \left( X \theta - Y \right) \\ &= \frac{1}{2} \left( \theta^T X^T X \theta - \theta^T X^T Y - Y^T X \theta + Y^T Y \right) \\ &= \frac{1}{2} \left( \theta^T X^T X \theta - 2 (Y^T X) \theta + Y^T Y \right)\end{aligned}\end{split}\]

Aplicando a cada uno de los términos, obtenemos:

\[\begin{split}\begin{aligned} \nabla_\theta ( \theta^T X^T X \theta ) &= X^T X \theta + (X^T X)^T \theta \\ & = 2 X^T X \theta\end{aligned}\end{split}\]

también se tiene

\[\begin{split}\begin{aligned} \nabla_\theta ( Y^T X \theta ) &= (Y^T X) ^T\\ &= X^T Y\end{aligned}\end{split}\]

y por último

\[\begin{aligned} \nabla_\theta ( Y^T Y ) = 0\end{aligned}\]

Por lo tanto se tiene que

\[\begin{split}\begin{aligned} \nabla_\theta J(\theta) & = \nabla_\theta \frac{1}{2} \left( \theta^T X^T X \theta - 2 (Y^T X) \theta + Y^T Y \right) \\ &= \frac{1}{2} ( 2 X^T X \theta - 2 X^T Y + 0 ) \\ &= X^T X \theta - X^T Y \end{aligned}\end{split}\]

Esto significa que el problema $\(\min_\theta J(\theta)\)\( se resuelve al hacer todas las derivadas parciales iguales a cero (ie, gradiente igual a cero) \)\(\nabla_\theta J(\theta) = 0\)\( lo cual en nuestro caso se convierte convenientemente a la ecuación normal \)\(X^T X \theta = X^T Y\)\( y se tiene \)\(\hat{\theta} = (X^T X)^{-1} X^T Y\)\( Aquí \)\hat{\theta}\( es una estimación de \)\theta$.

def lms_regression_fast(X, Y, theta, tol=1E-6):
    converged = False
    alpha = 0.01 / len(Y)
    theta = theta.reshape(X.shape[1], 1)
    A = np.dot(X.T, X)
    b = np.dot(X.T, Y)
    while not converged:
        gradient = np.dot(A, theta) - b
        new_theta = theta - alpha * gradient
        converged = np.linalg.norm(theta - new_theta) < tol * np.linalg.norm(theta)
        theta = new_theta
    return theta
theta_fast = lms_regression_fast(X, Y, theta_0)
print(theta_fast)
[[199.39672176]
 [ 50.19457286]]

Validación

np.allclose(X @ theta_fast, Y, atol=0.5)
True
np.allclose(X @ theta_fast, Y, atol=1e-3)
False

También es posible usar la implementación de resolución de sistemas lineales dispoinible en numpy.

def matrix_regression(X, Y, theta, tol=1E-6):
    A = np.dot(X.T,X)
    b = np.dot(X.T,Y)
    sol = np.linalg.solve(A,b)
    return sol
theta_npsolve = matrix_regression(X, Y, theta_0)
print(theta_npsolve)
[[200.]
 [ 50.]]

Interpretación Probabilística

Consideremos el modelo lineal

\[ Y = X \theta + \varepsilon \]

donde \(\varepsilon\) es un vector de errores aleatorios de media cero y matriz de dispersión \(\sigma^2 I\), donde \(I\) es la matriz identidad. Es usual añadir el supuesto de normalidad al vector de errores, por lo que se asume que

\[\varepsilon \sim \mathcal{N}(0, \sigma^2 I)\]

Cabe destacar que:

  • \(\theta\) no es una variable aleatoria, es un parámetro (desconocido).

  • \(Y \ | \ X; \theta \sim \mathcal{N}(X \theta, \sigma^2 I)\)

La función de verosimilitud \(L(\theta)\) nos permite entender que tan probable es encontrar los datos observados, para una elección del parámetro \(\theta\).

\[ L(\theta) = \left( 2 \pi \sigma^2 \right)^{-n/2} \, \exp\left(- \frac{1}{2 \sigma ^2} || Y - X \theta ||^2 \right) \]

Sea \(l(\theta) = \log L(\theta)\) la log-verosimilitud. Luego, ignorando los términos constantes se tiene

\[ l(\theta) = -\frac{n}{2} \log \sigma^2 - \frac{1}{2 \sigma ^2} || Y - X \theta ||^2 \]

Luego, derivando respecto a \(\theta\):

\[\begin{split} \begin{aligned} \frac{\partial l(\theta)}{\partial \theta} &= - \frac{1}{2 \sigma ^2} \left( - 2 X^T Y + 2 X^T X \theta \right) \\ &= - \frac{1}{\sigma ^2} \left( X^T Y + X^T X \theta \right) \\ \end{aligned} \end{split}\]

Luego podemos usar toda nuestra artillería de optimización despejando \(\partial l(\theta) / \partial \theta = 0\) y demostrando que es un máximo. Nuevamente llegamos a

\[\hat{\theta} = (X^T X)^{-1} X^T Y\]

Scikit-learn

Scikit-learn Machine Learning in Python

Scikit-learn is an open source machine learning library that supports supervised and unsupervised learning. It also provides various tools for model fitting, data preprocessing, model selection and evaluation, and many other utilities.

  • Simple and efficient tools for predictive data analysis

  • Accesible to everybody, and reusable in various contexts

  • Built on numpy, scipy and matplotlib

  • Open source, commercially usable - BSD license

Scikit-learn cuenta con enorme cantidad de herramientas de regresión, siendo la regresión lineal la más simple de estas. Ajustar una es tan sencillo como:

from sklearn.linear_model import LinearRegression

reg = LinearRegression(fit_intercept=False)
reg.fit(X, Y)
theta_sklearn = reg.coef_.T
print(theta_sklearn)
[[200.]
 [ 50.]]

Nota que primero se crea un objeto LinearRegression en que se declaran algunos parámetros, por ejemplo, en nuestro caso la matriz de diseño X ya posee una columna de intercepto, por lo que no es necesario incluirla en el modelo de scikit-learn. Luego se ajusta el modelo reg con el método fit().

Benchmark

Implementación simple

%%timeit
lms_regression_slow(X, Y, theta_0)
1min 33s ± 1.2 s per loop (mean ± std. dev. of 7 runs, 1 loop each)
theta_slow
array([[199.39672176],
       [ 50.19457286]])

Implementación vectorizada

%%timeit
lms_regression_fast(X, Y, theta_0)
241 ms ± 7.73 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
theta_fast
array([[199.39672176],
       [ 50.19457286]])

Implementación numpy

%%timeit
matrix_regression(X, Y, theta_0)
17.1 µs ± 475 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
theta_npsolve
array([[200.],
       [ 50.]])

Implementación scikit-learn

%%timeit
LinearRegression(fit_intercept=False).fit(X, Y).coef_.T
278 µs ± 2.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
theta_sklearn
array([[200.],
       [ 50.]])

Algunos comentarios:

  • La implementación simple es miles de veces más lenta que la más rápida, que en este caso es la implementación de numpy.

  • La implementación de numpy es sin duda la más rápida, pero no es posible utilizarla con matrices singulares.

  • Las implementaciones de gradient descent implementadas from scratch no son lo sufiecientemente precisas.

  • scikit-learn demora más pues es más flexible, además de realizar validaciones al momento de ajustar los modelos.

Aspectos Prácticos

Al realizar regresión, algunos autores indican que es conveniente normalizar/estandarizar los datos, es decir transformarlos para que tengan una escala común:

  • Utilizando la media y la desviación estándar $\(\frac{x_i-\overline{x_i}}{\sigma_{x_i}}\)$

  • Utilizando mínimos y máximos $\(\frac{x_i-\min{x_i}}{\max{x_i} - \min{x_i}}\)$

¿Porqué normalizar?

  • Los valores numéricos poseen escalas de magnitud distintas.

  • Las variables tienen distintos significados físicos.

  • Algoritmos funcionan mejor.

  • Interpretación de resultados es más sencilla.

Algunos problemas potenciales

  • Normalizar los datos puede producir colinealidad en los datos, produciendo inestabilidad numérica en la implementación.

En particular, scikit-learn ofrece herramientas para transformar datos. Por ejemplo, para escalar la forma más fácil y directa es utilizar sklearn.preprocessing.scale

from sklearn import preprocessing

diabetes_X_scaled = preprocessing.scale(diabetes_X)
diabetes_X_scaled
array([[ 0.80050009,  1.06548848,  1.29708846, ..., -0.05449919,
         0.41855058, -0.37098854],
       [-0.03956713, -0.93853666, -1.08218016, ..., -0.83030083,
        -1.43655059, -1.93847913],
       [ 1.79330681,  1.06548848,  0.93453324, ..., -0.05449919,
         0.06020733, -0.54515416],
       ...,
       [ 0.87686984,  1.06548848, -0.33441002, ..., -0.23293356,
        -0.98558469,  0.32567395],
       [-0.9560041 , -0.93853666,  0.82123474, ...,  0.55838411,
         0.93615545, -0.54515416],
       [-0.9560041 , -0.93853666, -1.53537419, ..., -0.83030083,
        -0.08871747,  0.06442552]])

Sin embargo, para parovechar todas las bondades de scikit-learn se recomienda hacer uso de los objetos Transformer.

scaler = preprocessing.StandardScaler().fit(diabetes_X)
scaler.transform(diabetes_X)
array([[ 0.80050009,  1.06548848,  1.29708846, ..., -0.05449919,
         0.41855058, -0.37098854],
       [-0.03956713, -0.93853666, -1.08218016, ..., -0.83030083,
        -1.43655059, -1.93847913],
       [ 1.79330681,  1.06548848,  0.93453324, ..., -0.05449919,
         0.06020733, -0.54515416],
       ...,
       [ 0.87686984,  1.06548848, -0.33441002, ..., -0.23293356,
        -0.98558469,  0.32567395],
       [-0.9560041 , -0.93853666,  0.82123474, ...,  0.55838411,
         0.93615545, -0.54515416],
       [-0.9560041 , -0.93853666, -1.53537419, ..., -0.83030083,
        -0.08871747,  0.06442552]])

Aplicación

Para aplicar a nuestros datos de diabetes es tan fácil como

reg = LinearRegression(fit_intercept=True).fit(diabetes_X, diabetes_y)

Para obtener los coeficientes de regresión y el intercepto debes acceder a los atributos de la instancia

reg.coef_
array([ -10.01219782, -239.81908937,  519.83978679,  324.39042769,
       -792.18416163,  476.74583782,  101.04457032,  177.06417623,
        751.27932109,   67.62538639])
reg.intercept_
152.1334841628965

También es posible predecir u obtener el score asociado a datos.

reg.predict(diabetes_X)
array([206.11706979,  68.07234761, 176.88406035, 166.91796559,
       128.45984241, 106.34908972,  73.89417947, 118.85378669,
       158.81033076, 213.58408893,  97.07853583,  95.1016223 ,
       115.06673301, 164.67605023, 103.07517946, 177.17236996,
       211.75953205, 182.84424343, 147.99987605, 124.01702527,
       120.33094632,  85.80377894, 113.11286302, 252.44934852,
       165.48821056, 147.72187623,  97.12824075, 179.09342974,
       129.05497324, 184.78138552, 158.71515746,  69.47588393,
       261.50255826, 112.81897436,  78.37194762,  87.66624129,
       207.92460213, 157.87686037, 240.84370686, 136.93372685,
       153.48187659,  74.15703284, 145.63105805,  77.8280105 ,
       221.0786645 , 125.22224022, 142.60147066, 109.4926324 ,
        73.14037106, 189.87368742, 157.93636782, 169.55816531,
       134.18186217, 157.72356219, 139.1077439 ,  72.73252701,
       207.8289973 ,  80.10834588, 104.08562488, 134.57807971,
       114.23779529, 180.67760064,  61.12644508,  98.7215441 ,
       113.79626149, 189.96141244, 148.98263155, 124.33457266,
       114.83969622, 122.00224605,  73.91315064, 236.70948329,
       142.31366526, 124.51427625, 150.84273716, 127.75408702,
       191.16674356,  77.05921006, 166.82129568,  91.00741773,
       174.75026808, 122.83488194,  63.27214662, 151.99895968,
        53.73407848, 166.00134469,  42.65030679, 153.04135861,
        80.54493791, 106.9048058 ,  79.94239571, 187.1634566 ,
       192.60115666,  61.07125918, 107.40466928, 125.04038427,
       207.72180472, 214.21749964, 123.47505642, 139.16396617,
       168.21035724, 106.9267784 , 150.64502809, 157.92231541,
       152.75856279, 116.22255529,  73.03090141, 155.66898717,
       230.14278537, 143.50191007,  38.0947967 , 121.860737  ,
       152.79569851, 207.99651918, 291.23082717, 189.17431487,
       214.02871163, 235.18090808, 165.3872774 , 151.25000032,
       156.57626783, 200.44154589, 219.35211772, 174.79049427,
       169.23161767, 187.8719893 ,  57.49473392, 108.55110499,
        92.68518048, 210.87365701, 245.47433558,  69.84529943,
       113.0351432 ,  68.42945176, 141.69628649, 239.46177949,
        58.3802079 , 235.47268158, 254.91986281, 253.31042713,
       155.50813249, 230.55904185, 170.44063216, 117.99200943,
       178.55548636, 240.07155813, 190.3398776 , 228.66100769,
       114.24162642, 178.36570405, 209.09273631, 144.85567253,
       200.65791056, 121.34184881, 150.50918174, 199.02165018,
       146.2806806 , 124.02443772,  85.26036769, 235.16536625,
        82.17255475, 231.29266191, 144.36634395, 197.04778326,
       146.99720377,  77.18477545,  59.3728572 , 262.67891084,
       225.12578458, 220.20506312,  46.59691745,  88.1040833 ,
       221.77623752,  97.24900614, 164.48869956, 119.90114263,
       157.79986195, 223.08505437,  99.5885471 , 165.84341641,
       179.47571002,  89.83382843, 171.82492808, 158.36337775,
       201.47857482, 186.39202728, 197.47094269,  66.57241937,
       154.59826802, 116.18638034, 195.92074021, 128.04740268,
        91.20285628, 140.56975398, 155.23013996, 169.70207476,
        98.75498537, 190.1453107 , 142.5193942 , 177.26966106,
        95.31403505,  69.0645889 , 164.16669511, 198.06460718,
       178.26228169, 228.58801706, 160.67275473, 212.28682319,
       222.48172067, 172.85184399, 125.27697688, 174.7240982 ,
       152.38282657,  98.58485669,  99.73695497, 262.29658755,
       223.73784832, 221.3425256 , 133.61497308, 145.42593933,
        53.04259372, 141.81807792, 153.68369915, 125.21948824,
        77.25091512, 230.26311068,  78.90849563, 105.20931175,
       117.99633487,  99.06361032, 166.55382825, 159.34391027,
       158.27612808, 143.05658763, 231.55938678, 176.64144413,
       187.23572317,  65.38504165, 190.66078824, 179.74973878,
       234.91022512, 119.15540438,  85.63464409, 100.85860205,
       140.4174259 , 101.83836332, 120.66138775,  83.06599161,
       234.58754656, 245.16192142, 263.26766492, 274.87431887,
       180.67699732, 203.05474761, 254.21769367, 118.44122343,
       268.44988948, 104.83643442, 115.87172349, 140.45788952,
        58.46850453, 129.83264097, 263.78452618,  45.01240356,
       123.28697604, 131.08314499,  34.89018315, 138.35659686,
       244.30370588,  89.95612306, 192.07094588, 164.32674962,
       147.74783541, 191.89381753, 176.44296313, 158.34707354,
       189.19183226, 116.58275843, 111.44622859, 117.45262547,
       165.79457547,  97.80241129, 139.54389024,  84.17453643,
       159.9389204 , 202.4011919 ,  80.48200416, 146.64621068,
        79.05274311, 191.33759392, 220.67545196, 203.75145711,
        92.87093594, 179.15570241,  81.80126162, 152.82706623,
        76.79700486,  97.79712384, 106.83424483, 123.83477117,
       218.13375502, 126.02077447, 206.76300555, 230.57976636,
       122.0628518 , 135.67694517, 126.36969016, 148.49621551,
        88.07082258, 138.95595037, 203.86570118, 172.55362727,
       122.95773416, 213.92445645, 174.88857841, 110.07169487,
       198.36767241, 173.24601643, 162.64946177, 193.31777358,
       191.53802295, 284.13478714, 279.30688474, 216.0070265 ,
       210.08517801, 216.22213925, 157.01489819, 224.06561179,
       189.05840605, 103.56829281, 178.70442926, 111.81492124,
       290.99913121, 182.64959461,  79.33602602,  86.33287509,
       249.15238929, 174.51439576, 122.10645431, 146.27099383,
       170.6555544 , 183.50018707, 163.36970989, 157.03563376,
       144.42617093, 125.30179325, 177.50072942, 104.57821235,
       132.1746674 ,  95.06145678, 249.9007786 ,  86.24033937,
        62.00077469, 156.81087903, 192.3231713 , 133.85292727,
        93.67456315, 202.49458467,  52.53953733, 174.82926235,
       196.9141296 , 118.06646574, 235.3011088 , 165.09286707,
       160.41863314, 162.37831419, 254.05718804, 257.23616403,
       197.50578991, 184.06609359,  58.62043851, 194.3950396 ,
       110.77475548, 142.20916765, 128.82725506, 180.12844365,
       211.26415225, 169.59711427, 164.34167693, 136.2363478 ,
       174.50905908,  74.67649224, 246.29542114, 114.14131338,
       111.54358708, 140.02313284, 109.99647408,  91.37269237,
       163.01389345,  75.16389857, 254.05755095,  53.47055785,
        98.48060512, 100.66268306, 258.58885744, 170.67482041,
        61.91866052, 182.3042492 , 171.26913027, 189.19307553,
       187.18384852,  87.12032949, 148.37816611, 251.35898288,
       199.69712357, 283.63722409,  50.85577124, 172.14848891,
       204.06179478, 174.16816194, 157.93027543, 150.50201654,
       232.9761832 , 121.5808709 , 164.54891787, 172.67742636,
       226.78005938, 149.46967223,  99.14026374,  80.43680779,
       140.15557121, 191.90593837, 199.27952034, 153.63210613,
       171.80130949, 112.11314588, 162.60650576, 129.8448476 ,
       258.02898298, 100.70869427, 115.87611124, 122.53790409,
       218.17749233,  60.94590955, 131.09513588, 119.48417359,
        52.60848094, 193.01802803, 101.05169913, 121.22505534,
       211.8588945 ,  53.44819015])
reg.score(diabetes_X, diabetes_y)
0.5177494254132934