Redes neuronales¶


Clase7: Modelos lineales¶


Autor: Luis Fernando Apáez Álvarez

Contenido¶

  • Función de pérdida
  • Regresión lineal
  • Entrenamiento por lotes

En esta clase trabajaremos para entrenar un modelo lineal con tensoflow. Para ello comenzaremos por cargar los datos con los cuales estaremos trabajando, de donde utilizaremos Pandas y Numpy para colocar los datos de una manera numérica en un array. Para ello

In [42]:
import pandas as pd
import numpy as np

# Cargamos los datos
data_df = pd.read_csv('kc_house_data.csv')
data_df
Out[42]:
id date price bedrooms bathrooms sqft_living sqft_lot floors waterfront view ... grade sqft_above sqft_basement yr_built yr_renovated zipcode lat long sqft_living15 sqft_lot15
0 7129300520 20141013T000000 221900.0 3 1.00 1180 5650 1.0 0 0 ... 7 1180 0 1955 0 98178 47.5112 -122.257 1340 5650
1 6414100192 20141209T000000 538000.0 3 2.25 2570 7242 2.0 0 0 ... 7 2170 400 1951 1991 98125 47.7210 -122.319 1690 7639
2 5631500400 20150225T000000 180000.0 2 1.00 770 10000 1.0 0 0 ... 6 770 0 1933 0 98028 47.7379 -122.233 2720 8062
3 2487200875 20141209T000000 604000.0 4 3.00 1960 5000 1.0 0 0 ... 7 1050 910 1965 0 98136 47.5208 -122.393 1360 5000
4 1954400510 20150218T000000 510000.0 3 2.00 1680 8080 1.0 0 0 ... 8 1680 0 1987 0 98074 47.6168 -122.045 1800 7503
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
21608 263000018 20140521T000000 360000.0 3 2.50 1530 1131 3.0 0 0 ... 8 1530 0 2009 0 98103 47.6993 -122.346 1530 1509
21609 6600060120 20150223T000000 400000.0 4 2.50 2310 5813 2.0 0 0 ... 8 2310 0 2014 0 98146 47.5107 -122.362 1830 7200
21610 1523300141 20140623T000000 402101.0 2 0.75 1020 1350 2.0 0 0 ... 7 1020 0 2009 0 98144 47.5944 -122.299 1020 2007
21611 291310100 20150116T000000 400000.0 3 2.50 1600 2388 2.0 0 0 ... 8 1600 0 2004 0 98027 47.5345 -122.069 1410 1287
21612 1523300157 20141015T000000 325000.0 2 0.75 1020 1076 2.0 0 0 ... 7 1020 0 2008 0 98144 47.5941 -122.299 1020 1357

21613 rows × 21 columns

donde los dados son sobre las ventas de casas en el condado King en Washington, además vemos que las columnas tienen diferentes tipos de datos, como decimales o enteros, donde algunos de ellos hacen alusión a variables booleanas al tener asigado solo el 1 o el 0. Luego

In [6]:
# Convertimos los datos en un array
data_array = np.array(data_df)
data_array
Out[6]:
array([[7129300520, '20141013T000000', 221900.0, ..., -122.257, 1340,
        5650],
       [6414100192, '20141209T000000', 538000.0, ..., -122.319, 1690,
        7639],
       [5631500400, '20150225T000000', 180000.0, ..., -122.233, 2720,
        8062],
       ...,
       [1523300141, '20140623T000000', 402101.0, ..., -122.299, 1020,
        2007],
       [291310100, '20150116T000000', 400000.0, ..., -122.069, 1410,
        1287],
       [1523300157, '20141015T000000', 325000.0, ..., -122.299, 1020,
        1357]], dtype=object)

Lo cual siempre es recomendable de hacer cuando se quiere entrenar un modelo, esto es, a partir de la carga de datos hecha mediante pandas, luego lo convertimos a un array numpy. Para finalizar la carga de los datos, hablaremos sobre cómo transformar los datos importados para usarlos en Tensorflow.

Supongamos que queremos que las columnas price y waterfront sean del tipo de dato float32 y del tipo booleano, respectivamente. Lo anterior lo podemos realizar de dos maneras diferentes:

In [7]:
# Mediante numpy
price_np = np.array(data_df.price, np.float32)
waterfront_np = np.array(data_df.waterfront, np.bool)
waterfront_np
C:\Users\usuario\AppData\Local\Temp\ipykernel_11444\1061770669.py:3: DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
  waterfront_np = np.array(data_df.waterfront, np.bool)
Out[7]:
array([False, False, False, ..., False, False, False])
In [9]:
# Mediante tensorflow
import tensorflow as tf
price_tf = tf.cast(data_df.price, tf.float32)
waterfront_tf = tf.cast(data_df.waterfront, tf.bool)
price_tf
Out[9]:
<tf.Tensor: shape=(21613,), dtype=float32, numpy=
array([221900., 538000., 180000., ..., 402101., 400000., 325000.],
      dtype=float32)>

donde la principal diferencia es que en el primero obtenemos un array numpy y en el segundo un tensor de dimensión 1

Función de pérdida ¶

  • Las funciones de pérdida juegan un papel fundamental en el aprendizaje automático.
  • Necesitamos de ellas para entrenar modelos porque nos dice qué tan bien nuestro modelo explica los datos.
  • Un valor alto de pérdida indica que el ajuste del modelo es deficiente.
  • Por lo general, entrenamos modelos para minimizar la función de pérdida. En algunos casos será necesario, a su vez, maximizar una función en vez de minimizar, lo cual es sencillo de implementar simplemente colocando un signo de menos al inicio de la función y después minimizamos.
  • Tenemos muchas opciones de funciones de pérdida predefinidas en tensorflow. Algunas de las más comunes son:
    • Error cuadrático medio (MSE): tf.keras.losses.mse(). Penaliza fuertemente los valores atípicos y tiene una alta sensibilidad cerca del mínimo.
    • Error absoluto medio (MAE): tf.keras.losses.mae(). Escala linealmente con el tamaño del error y tiene una baja sensibilidad cerca del mínimo.
    • Error Huber: tf.keras.losses.huber(). Es similar al MSE cerca de cero y similar a MAE lejos del cero.

Supongamos que utilizaremos MSE, para ello requeriremos de dos tensores de valores reales u "objetivos", y los valores pronósticados o "predicciones":


# Calculamos la perdida MSE
loss = tf.keras.losses.mse(targets, predictions)

lo que devolverá un valor único: promedio de las diferencias al cuadrado entre los valores reales y los predichos.

En muchos casos, el proceso de entrenamiento requerirá que proporcionemos una función que acepta las variables y datos de nuestro modelo y devuelve una pérdida. Por ejemplo, lo que haremos será definir un modelo de regresión lineal $y=\beta_0+\beta_1x$ que toma:

  • $\beta_0$: el valor del intercepto;
  • $\beta_1$: el valor de la pendiente;
  • $x$: las características.

# Definicion de un modelo de regresion lineal
def linear_regression(intercept, slope = slope, features = features):
    # Devuelve las predicciones del modelo
    return intercept + features * slope

Lo que sigue después es definir una función de pérdida que acepta la pendiente, el intercepto, los objetivos y las características:


# Funcion de perdida calculada mediante mse
def loss_function(intercept, slope = slope, targets = targets, features = features):
    # Calculamos las predicciones para un modelo lineal
    predictions = linear_regression(intercept, slope)
    # Retornamos la perdida
    return tf.keras.losses.mse(targets, predictions)

Así, podemos evaluar la última función definida para evaluar un conjunto de datos dado:


loss_function(intercept, slope, test_targets, test_features)

Regresión lineal ¶

Supongamos que queremos analizar la relación entre el tamaño de las casas, de nuestro conjunto de datos, y el precio

In [11]:
data_df.columns
Out[11]:
Index(['id', 'date', 'price', 'bedrooms', 'bathrooms', 'sqft_living',
       'sqft_lot', 'floors', 'waterfront', 'view', 'condition', 'grade',
       'sqft_above', 'sqft_basement', 'yr_built', 'yr_renovated', 'zipcode',
       'lat', 'long', 'sqft_living15', 'sqft_lot15'],
      dtype='object')
In [43]:
import seaborn as sns
import matplotlib.pyplot as plt
plt.style.use('seaborn')
sns.scatterplot(x='sqft_lot15', y='price', data=data_df)

# Graficaremos en una escala logaritmica, lo cual es util cuando sospechamos
# que la relacion es proporcional
plt.xscale('log')
plt.yscale('log')
plt.ylabel('log(price)')
plt.xlabel('log(size)')
plt.show()

donde el modelo de regresión lineal asume que la relación entre las variables en cuestión puede se capturada mediante una línea, esto es, la pendiente y el intercepto caraterizan completamente la relación entre el tamaño y el precio. En nuestro ejemplo hemos asumido que la relación entre los logarítmos del tamaño y los logarítmos de los precios es lineal.

In [51]:
# Agregamos dos columnas nuevas con los logaritmos del tamagnio y los precios
data_df['sqft_lot15_log'] = data_df.sqft_lot15.apply(lambda x: np.log(x))
data_df['price_log'] = data_df.price.apply(lambda x: np.log(x))
sns.regplot(x = 'sqft_lot15_log', 
           y = 'price_log',  
           ci = None, 
           marker='.',
           data = data_df) 
plt.title('Datos con la recta ajustada', size=16)
plt.show()

El entrenamiento del modelo supondrá recuperar la pendiente de la recta y el intercepto, es decir, tendremos dos parámetros dentro de nuestro modelo. Una vez que hayamos entrenado dichos parámetros, podremos tomar un tamaño de una casa y predecir su precio, donde la diferencia entre el precio predicho y el real es el error, que se puede utilizar para construir una función de pérdida.

Procedamos a implementar la regresión lineal en tensorflow

In [52]:
# Definimos los objetivos y las caracteristicas
price = np.array(data_df.price, np.float32)
size = np.array(data_df.sqft_lot15, np.float32)

# Inicializamos el intercepto y la pendiente
# como variables entrenables 
intercepto = tf.Variable(0.1, np.float32)
pendiente = tf.Variable(0.1, np.float32)

Después, definiremos el modelo que usaremos para realizar las predicciones

In [53]:
# Definimos un modelo de regresion lineal
def linear_regression(intercept, slope, features=size):
    return intercept + features * slope

El siguiente paso es definir una función de pérdida

In [54]:
def loss_function(intercept, slope, targets=price, features=size):
    predictions = linear_regression(intercept, slope)
    return tf.keras.losses.mse(targets, predictions)

Con la función de pérdida definida, el siguiente paso es definir una operación de optimización, para este ejemplo utilizaremos el optimizador Adam, donde intuitivamente el optimizador cambiará la pendiente y el intercepto en una dirección que reducirá el valor de la pérdida.

In [55]:
# Podriamos pasar un valor de parametro al optimizador, lo cual
# significa la tasa de aprendizaje
op = tf.keras.optimizers.Adam()

Luego, procederemos a realizar la minimización de la función de pérdida utilizando para ello el optimizador

In [60]:
# Minimizamos la funcion de perdida
# Ejecutaremos 1000 iteraciones
results = []
for j in range(1000):
    #                   funcion a minimizar               lista de variables 
    op.minimize(lambda: loss_function(intercepto, pendiente), var_list=[intercepto, pendiente])
    results.append(loss_function(intercepto, pendiente))
In [61]:
# veamos los ultimos valores obtenidos
results[950:]
Out[61]:
[<tf.Tensor: shape=(), dtype=float32, numpy=389806300000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389797280000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389788240000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389779230000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389770200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389761200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389752200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389743180000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389734170000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389725160000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389716180000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389707170000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389698160000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389689180000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389680140000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389671200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389662200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389653200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389644200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389635200000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389626230000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389617250000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389608280000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389599260000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389590300000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389581340000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389572360000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389563400000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389554370000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389545430000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389536450000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389527470000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389518520000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389509550000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389500600000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389491620000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389482680000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389473700000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389464750000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389455800000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389446860000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389437900000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389428900000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389419960000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389411050000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389402100000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389393150000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389384240000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389375260000.0>,
 <tf.Tensor: shape=(), dtype=float32, numpy=389366380000.0>]
In [64]:
# Imprimimos los parametos entrenados
print(intercepto.numpy())
print(pendiente.numpy())
3.0649319
2.8952818

lo cual nos permitirá predecir el valor de una casa dado su tamaño. Podríamos implementar también regresión lineal múltiple

In [67]:
# Definimos los objetivos y las caracteristicas
price = np.array(data_df.price_log, np.float32)
size = np.array(data_df.sqft_lot15_log, np.float32)
bedrooms = np.array(data_df.bedrooms, np.float32)

# Inicializamos los parametros
# como variables entrenables 
beta0 = tf.Variable(0.1, np.float32)
beta1 = tf.Variable(0.1, np.float32)
beta2 = tf.Variable(0.1, np.float32)

# Definimos un modelo de regresion lineal multiple
def linear_regression(params, feature1=size, feature2 = bedrooms):
    return params[0] + feature1 * params[1] + feature2 * params[2]

def loss_function(params, targets=price, feature1=size, feature2 = bedrooms):
    predictions = linear_regression(params, feature1, feature2)
    return tf.keras.losses.mse(targets, predictions)

# Optimizador Adam
op = tf.keras.optimizers.Adam()

# Minimizamos la funcion de perdida
# Ejecutaremos 1000 iteraciones
results = []
for j in range(1000):
    op.minimize(lambda: loss_function(params=(beta0, beta1, beta2)),
                var_list=[beta0, beta1, beta2])
    results.append(loss_function(params=(beta0, beta1, beta2)))
In [68]:
results[950:]
Out[68]:
[<tf.Tensor: shape=(), dtype=float32, numpy=7.9905834>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.9624834>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.934484>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.90659>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.878797>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.8511066>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.823517>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.796027>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.768639>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.7413507>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.714161>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.687072>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.6600847>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.633197>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.6064067>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.579716>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.5531235>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.526629>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.500232>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.4739323>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.4477296>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.4216247>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.395617>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.369704>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.3438873>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.3181677>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.292544>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.2670174>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.2415857>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.2162495>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.191006>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.165859>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.1408057>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.1158476>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.0909796>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.0662065>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.041527>,
 <tf.Tensor: shape=(), dtype=float32, numpy=7.016939>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.9924407>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.9680367>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.943723>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.919502>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.895371>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.871333>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.847385>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.823528>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.799761>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.776084>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.752497>,
 <tf.Tensor: shape=(), dtype=float32, numpy=6.729>]
In [69]:
# Imprimimos los parametos entrenados
print(beta0.numpy())
print(beta1.numpy())
print(beta2.numpy())
0.8046444
0.8013589
0.79487073

Entrenamiento por lotes ¶

El entrenamiento por lotes es utilizado para manejar grandes conjuntos de datos. En dichos casos en donde el conjunto de datos es excesivamente grande, entonces no podrá realizarse el entrenamiento del modelo debido a que dicho conjunto no puede caber en la memoria. En su lugar, deberemos de dividir el conjuntos de datos por lotes de manera secuencial.

Un solo paso sobre todos los lotes se denomina época y el proceso en sí se denomina entrenamiento por lotes. Además de aliviar las limitaciones de memoria, el entrenamiento por lotes también permitirá que actualicemos ponderaciones del modelo y parámetros del optimizador después de cada lote, en lugar de al final de la época.

Además, podremos cargar datos mediante pandas, pero haciéndolo también por lotes. Por ejemplo, si tenemos un conjunto de datos de más de 50 GB, es posible que deseemos evitar cargarlo todo a la vez. Podemos realizar la carga por lotes mediante el parámetro adicional de chunksize. Así, lo que se hará será dividir el conjunto de datos en fragmentos y especificaremos el tamaño de dichos trozos mediante chunksize, lo cual ahorra memoria computacional y mejora la eficiencia del código.

Por ejemplo, carguemos el mismo conjunto de datos pero en trozos de 10000 filas

In [80]:
# Cargamos los datos mediante lotes de tamagnio 10000,
# el resultado sera un iterador
df = pd.read_csv('kc_house_data.csv', chunksize=10000)
# Veamos la forma de cada lote
for lote in df:
    print(lote.shape)
(10000, 21)
(10000, 21)
(1613, 21)
In [82]:
# Forma del dataframe completo
data_df.shape
Out[82]:
(21613, 23)

Así, los datos fueron cargados en tres lotes, donde los dos primero tuvieron un tamaño de 10000 filas y el último de 1613. Otro ejemplo, carguemos los datos de la columna price pero haciéndolo ahora por lotes de tamaño 100

In [84]:
# Cargamos los datos mediante lotes:
# Creamos dos listas auxiliares para guardar la informacion de los lotes
price_list = []
size_list = []
# Iteramos sobre los datos cargados por lotes
for lote in pd.read_csv('kc_house_data.csv', chunksize=100):
    # Extraeremos la columna price de la informacion que obtengamos 
    # de cada lote y la agregamos a la lista price_list
    price_list.append(lote['price'])
    
    # analogo para el tamagnio
    size_list.append(lote['sqft_lot15'])
# Unimos todos los lotes
price = pd.concat(price_list)
size = pd.concat(size_list)
In [85]:
# Carga por lotes
price
Out[85]:
0        221900.0
1        538000.0
2        180000.0
3        604000.0
4        510000.0
           ...   
21608    360000.0
21609    400000.0
21610    402101.0
21611    400000.0
21612    325000.0
Name: price, Length: 21613, dtype: float64
In [87]:
# Comparacion de la info de la carga por lotes y
# la carga normal
data_df.price == price
Out[87]:
0        True
1        True
2        True
3        True
4        True
         ... 
21608    True
21609    True
21610    True
21611    True
21612    True
Name: price, Length: 21613, dtype: bool

Después, podríamos convertir price y size a numpy arrays para su posterior utilización en el entrenamiento de un modelo:

In [88]:
price_np = np.array(price, np.float32)
size_np = np.array(size, np.float32)

Por otro lado, veamos un ejemplo de entrenamiento de un modelo realizado por lotes, para ello

In [89]:
# Definimos las variables a utilizar el un modelo de regresion 
# lineal simple
intercepto = tf.Variable(0.1, tf.float32)
pendiente = tf.Variable(0.1, tf.float32)

# Definimos un modelo de regresion lineal multiple
def linear_regression(intercept, slope, features):
    return intercept + features * slope
# Definimos la funcion de perdida
def loss_function(intercept, slope, targets, features):
    predictions = linear_regression(intercept, slope, features)
    return tf.keras.losses.mse(targets, predictions)

# Optimizador Adam
op = tf.keras.optimizers.Adam()

lo que haremos después será realizar el entrenamiento por lotes

In [91]:
# Realizamos la carga de los datos por lotes de tamagnio 100
df = pd.read_csv('kc_house_data.csv', chunksize=100)
# Iteramos sobre cada lote en df
for lote in df:
    # Extraemos las columnas de caracteristicas y objetivos
    # de cada lote
    price_lote = np.array(lote['price'], np.float32)
    size_lote = np.array(lote['sqft_lot15'], np.float32)
    # Minimizamos la funcion de perdida
    op.minimize(lambda: loss_function(intercepto, pendiente, price_lote, size_lote),
                var_list=[intercepto, pendiente])

donde dicho ciclo for continuará hasta que se hayan revisado todas las filas de los datos cargados en la variable df. Es importante resaltar que nunca llegamos a necesitar más de 100 filas en la memoria durante todo el proceso. Luego

In [92]:
print(intercepto.numpy(), pendiente.numpy())
0.31784675 0.30197635

Notemos que no usamos valores predeterminados en los argumentos de las función que definimos, debido a que los datos de entrada fueron generados en lotes durante el proceso de entrenamiento.