Saltar al contenido principal

· 3 min de lectura
Darvin Cotrina

¿Como crear una función de perdida en tensorflow?

En este post vamos a ver como crear una función de perdida en tensorflow. Para ello vamos a crear una función de perdida de tipo Huber.

Funcion de perdida Huber

La funcion de perdida Huber es una funcion de perdida robusta que es menos sensible a los valores atipicos en los datos que la funcion de perdida de error cuadratico medio (MSE) y la funcion de perdida de error absoluto medio (MAE). La funcion de perdida Huber es una combinacion de ambas funciones de perdida. En esta publicacion, aprendera como funciona la funcion de perdida Huber y como implementarla en Python.

Equacion de la funcion de perdida Huber

La funcion de perdida Huber se define como:

l={12(yy^)2para yy^δδ(yy^12δ)en otro casol = \begin{cases} \frac{1}{2} (y - \hat{y})^2 & \text{para } |y - \hat{y}| \leq \delta \\ \delta (|y - \hat{y}| - \frac{1}{2} \delta) & \text{en otro caso} \end{cases}

Donde yy es el valor verdadero, y^\hat{y} es el valor predicho y δ\delta es un valor constante que define el umbral entre las dos funciones de perdida. La funcion de perdida Huber es convexa y diferenciable en todas partes, excepto en y=y^y = \hat{y}

Importar librerías

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

Preparar datos

Para nuestro ejemplo crearemos un conjunto de datos de prueba con una distribución normal con media 0 y desviación estándar 1. Para ello usaremos la función normal de la librería numpy.

x = np.random.uniform(-1, 1, 100)
y = 2 * x + np.random.normal(0, 0.1, 100)
print(f'x: {x[:5]}')
print(f'y: {y[:5]}')
x: [ 0.09556661  0.4654635   0.83118325 -0.85447542 -0.31583249]
y: [ 0.2331942 0.786064 1.47523312 -1.85194843 -0.68147018]

Crear la función de pérdida Huber

Ahora vamos a crear la funciones de perdida huber, que resive como parámetros el valor de la predicción y el valor real, y el delta que es el valor que define el punto de inflexión de la función. debemos tener en cuenta que delta hiperparámetro que se debe ajustar para cada problema.

def huber_loss(y_true, y_pred, delta = 1.0):
error = tf.abs(y_true - y_pred)
return tf.where(error <= delta, 0.5 * error ** 2, delta * (error - 0.5 * delta))

Crear un modelo en tensorflow

Ahora crearemos un modelo en tf para poder probar nuestra función de perdida.

model = tf.keras.models.Sequential([
tf.keras.layers.Dense(1, input_shape=(1,))
])
model.compile(optimizer='sgd', loss=lambda y_true, y_pred: tf.keras.losses.Huber()(y_true, y_pred, 2))
history = model.fit(x, y, epochs=100, verbose=0)

model.predict([10])
1/1 [==============================] - 0s 66ms/step
array([[19.569162]], dtype=float32)
plt.plot(history.history['loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epochs')
plt.legend(['Train', 'Val'], loc='upper right')
plt.show()

Como hemos visto crear una funcion de perdida es muy simple y usarla en tensorflow es aun mas simple. En este caso hemos creado una funcion de perdida que es la funcion de perdida de Huber. En el caso que queramos usar la funcion de perdida Huber recomiendo usar la funcion de perdida de Huber de tensorflow que es mas eficiente que la que hemos creado

· 8 min de lectura
Darvin Cotrina

¿Que es una Siamese Network?

Es un tipo especial de arquitecura de red neuronal que permite comparar dos imagenes y determinar si son similares o no, la caracteristica principal de este tipo de red es que comparten los mismos pesos y arquitectura, es decir, son dos redes neuronales que comparten los mismos pesos y arquitectura, esto permite que la red pueda aprender a comparar dos imagenes y determinar si son similares o no.

Importar modulos

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import random

Preparar los datos

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

# cast to float32
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')

print('fashion_mnist train shape:', X_train.shape)
print('fashion_mnist test shape:', X_test.shape)

# normalize data
X_train /= 255.
X_test /= 255.
fashion_mnist train shape: (60000, 28, 28)
fashion_mnist test shape: (10000, 28, 28)

Crear nuestros pares de datos positivos y negativos

  • Pares positivos: Dos imágenes que contienen las mismas caracteristicas
  • Pares negativos: Dos imágenes que contienen diferentes caracteristicas
def create_pairs(X: np.ndarray, y: np.ndarray):
"""
Args:
X (np.ndarray): Array de imagenes
y (np.ndarray): Array de etiquetas
return:
pairs (np.ndarray): Array de pares de imagenes
labels: Etiquetas de las imagenes, 1 si son pares positivos y 0 si son pares negativos
"""

# image indices by class labels: y
num_classes = max(y) + 1
indices = [np.where(y == i)[0] for i in range(num_classes)]

pairs, labels = [], []

# max number of pairs using the min number of images by class
n = min([len(i) for i in indices]) - 1

for c in range(num_classes):
for i in range(n):
# positive pair
img1 = X[indices[c][i]]
# next image of the same class
img2 = X[indices[c][i+1]]
pairs.append((img1, img2))
labels.append(1.)

# negative pair
# select a random class
neg = np.random.choice([_c for _c in range(num_classes) if _c != c])
# select a random image
img1 = X[indices[c][i]]
img2 = X[indices[neg][i]]
pairs.append((img1, img2))
labels.append(0.)

return np.array(pairs), np.array(labels)

X_train_pair, y_train_bin = create_pairs(X_train, y_train)
X_test_pair, y_test_bin = create_pairs(X_test, y_test)
print(f'train pair: {X_train_pair.shape} and type {X_train_pair.dtype} label: {y_train_bin.shape} type {y_train_bin.dtype}')
print(f'test pair: {X_test_pair.shape} and type {X_test_pair.dtype} labe: {y_test_bin.shape} type {y_test_bin.dtype}')
train pair: (119980, 2, 28, 28) and type float32 label: (119980,) type float64
test pair: (19980, 2, 28, 28) and type float32 labe: (19980,) type float64

Ya preparamos los pares de datos para entrenar la red neuronal ahora por cada ejemplo en nuestra input tenemos dos images y nuestro output es un valor 0 o 1 dependiendo si son negativos o positivos respectivamente.

Para poder tener un contexto visual graficaremos las imagenes de ejemplo, una a lado de la otra. En el código usamos un random para seleccionar el par de imagenes a mostrar esto se hace para que se pueda jugar por los datos y ver distintos ejemplos.

def plot_pair(pair: np.ndarray, label: float, pred: float = None):
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].imshow(pair[0])
ax[1].imshow(pair[1])
if label:
title = "Positivo"
else:
title = "Negativo"

if pred is not None:
if pred < 0.5:
title += f" - pred: {pred:.2f} - Positivo"
else:
title += f" - pred: {pred:.2f} - Negativo"
fig.suptitle(title)
plt.show()
num_exam = random.randrange(len(X_train_pair))
plot_pair(X_train_pair[num_exam], y_train_bin[num_exam])

Modelo base

input_shape = (28, 28, )
def build_model_base():
input = tf.keras.Input(shape=input_shape, name="base_input")
x = tf.keras.layers.Flatten(name="flatten_input")(input)
x = tf.keras.layers.Dense(128, activation='relu', name="first_base_dense")(x)
x = tf.keras.layers.Dropout(0.1, name="first_dropout")(x)
x = tf.keras.layers.Dense(128, activation='relu', name="second_base_dense")(x)
x = tf.keras.layers.Dropout(0.1, name="second_dropout")(x)
x = tf.keras.layers.Dense(128, activation='relu', name="third_base_dense")(x)

return tf.keras.Model(inputs=input, outputs=x, name="base_model")
model_base = build_model_base()
tf.keras.utils.plot_model(model_base, show_shapes=True)

Vamos ahora a construir la Siamense Network, donde podremos ver que tenemos dos entradas que se enviaran a al modelo base y esta pasara por una capa personalizada que calculara la distancia entre los dos vectores de salida de la red base, distancia euclidiana

Distancia Euclidiana

la distancia euclidiana es una medida de distancia entre dos puntos que se puede generalizar a cualquier dimensión. En otras palabras, es la distancia entre dos puntos que se puede medir con una regla. En dos dimensiones, la distancia euclidiana entre los puntos

def euclidean_distance(vects):
import keras.backend as K
vect_a, vect_b = vects
sum_square = K.sum(K.square(vect_a - vect_b), axis=1, keepdims=True)
return K.sqrt(K.maximum(sum_square, K.epsilon()))

def ouput_shape(shapes):
shape1, shape2 = shapes
return (shape1[0], 1)
# create input a
input_a = tf.keras.Input(shape=input_shape, name='left_input')
vect_output_a = model_base(input_a)

# create input b
input_b = tf.keras.Input(shape=input_shape, name='right_input')
vect_output_b = model_base(input_b)

# measure the similarity of the two vectorized outputs
output = tf.keras.layers.Lambda(euclidean_distance, name='output_layer', output_shape=ouput_shape)([vect_output_a, vect_output_b])

# create the model
model = tf.keras.Model([input_a, input_b], output)
tf.keras.utils.plot_model(model, show_shapes=True)

Configurar modelo


def contrastive_loss_with_margin(margin):
def contrastive_loss(y_true, y_pred):
'''Contrastive loss from Hadsell-et-al.'06
http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
'''
import tensorflow.keras.backend as K
square_pred = K.square(y_pred)
margin_square = K.square(K.maximum(margin - y_pred, 0))
return (y_true * square_pred + (1 - y_true) * margin_square)
return contrastive_loss
rms = tf.keras.optimizers.RMSprop(learning_rate=0.0001)
model.compile(optimizer=rms, loss=contrastive_loss_with_margin(1), metrics=['accuracy'])

Entrenamos el modelo

history = model.fit([X_train_pair[:, 0], X_train_pair[:, 1]], y_train_bin, epochs=30, batch_size=180, validation_data=([X_test_pair[:, 0], X_test_pair[:, 1]], y_test_bin))
Epoch 1/30
667/667 [==============================] - 6s 7ms/step - loss: 0.1847 - accuracy: 0.2415 - val_loss: 0.1192 - val_accuracy: 0.1572
Epoch 2/30
667/667 [==============================] - 4s 6ms/step - loss: 0.1169 - accuracy: 0.1469 - val_loss: 0.1023 - val_accuracy: 0.1336
Epoch 3/30
667/667 [==============================] - 5s 7ms/step - loss: 0.1039 - accuracy: 0.1306 - val_loss: 0.0955 - val_accuracy: 0.1266
Epoch 4/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0968 - accuracy: 0.1215 - val_loss: 0.0910 - val_accuracy: 0.1217
Epoch 5/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0920 - accuracy: 0.1152 - val_loss: 0.0883 - val_accuracy: 0.1200
Epoch 6/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0880 - accuracy: 0.1096 - val_loss: 0.0851 - val_accuracy: 0.1137
Epoch 7/30
667/667 [==============================] - 4s 5ms/step - loss: 0.0846 - accuracy: 0.1046 - val_loss: 0.0825 - val_accuracy: 0.1064
Epoch 8/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0818 - accuracy: 0.1006 - val_loss: 0.0804 - val_accuracy: 0.1040
Epoch 9/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0794 - accuracy: 0.0970 - val_loss: 0.0786 - val_accuracy: 0.1025
Epoch 10/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0771 - accuracy: 0.0931 - val_loss: 0.0777 - val_accuracy: 0.0988
Epoch 11/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0750 - accuracy: 0.0906 - val_loss: 0.0757 - val_accuracy: 0.0969
Epoch 12/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0733 - accuracy: 0.0877 - val_loss: 0.0746 - val_accuracy: 0.0946
Epoch 13/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0716 - accuracy: 0.0852 - val_loss: 0.0739 - val_accuracy: 0.0930
Epoch 14/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0699 - accuracy: 0.0832 - val_loss: 0.0721 - val_accuracy: 0.0910
Epoch 15/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0684 - accuracy: 0.0813 - val_loss: 0.0720 - val_accuracy: 0.0911
Epoch 16/30
667/667 [==============================] - 4s 5ms/step - loss: 0.0671 - accuracy: 0.0798 - val_loss: 0.0705 - val_accuracy: 0.0858
Epoch 17/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0661 - accuracy: 0.0779 - val_loss: 0.0704 - val_accuracy: 0.0880
Epoch 18/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0648 - accuracy: 0.0764 - val_loss: 0.0699 - val_accuracy: 0.0890
Epoch 19/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0637 - accuracy: 0.0752 - val_loss: 0.0693 - val_accuracy: 0.0877
Epoch 20/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0626 - accuracy: 0.0729 - val_loss: 0.0683 - val_accuracy: 0.0863
Epoch 21/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0616 - accuracy: 0.0724 - val_loss: 0.0683 - val_accuracy: 0.0880
Epoch 22/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0608 - accuracy: 0.0718 - val_loss: 0.0671 - val_accuracy: 0.0845
Epoch 23/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0599 - accuracy: 0.0700 - val_loss: 0.0671 - val_accuracy: 0.0852
Epoch 24/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0591 - accuracy: 0.0698 - val_loss: 0.0670 - val_accuracy: 0.0849
Epoch 25/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0585 - accuracy: 0.0693 - val_loss: 0.0663 - val_accuracy: 0.0843
Epoch 26/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0578 - accuracy: 0.0683 - val_loss: 0.0660 - val_accuracy: 0.0848
Epoch 27/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0568 - accuracy: 0.0671 - val_loss: 0.0659 - val_accuracy: 0.0848
Epoch 28/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0563 - accuracy: 0.0671 - val_loss: 0.0658 - val_accuracy: 0.0852
Epoch 29/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0559 - accuracy: 0.0663 - val_loss: 0.0650 - val_accuracy: 0.0834
Epoch 30/30
667/667 [==============================] - 4s 6ms/step - loss: 0.0553 - accuracy: 0.0663 - val_loss: 0.0655 - val_accuracy: 0.0842

Evaluamos el modelo

pred = model.predict([X_test_pair[:, 0], X_test_pair[:, 1]])
625/625 [==============================] - 1s 2ms/step
examples = np.random.choice(range(len(pred)), size=5, replace=False)

for i in examples:
plot_pair(X_test_pair[i], y_test_bin[i], pred[i][0])

graficar funcion de perdida

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Modelo de perdida')
plt.ylabel('Perdida')
plt.show()

· 3 min de lectura
Darvin Cotrina

Para crear un modelo de deep learning en keras se pueden usar dos API’s: la secuencial y la funcional. La secuencial es la más sencilla de usar, pero la funcional es más flexible y permite crear modelos más complejos. En este notebook se muestra como crear un modelo funcional.

1. Importar modulo

import tensorflow as tf

2. Modelo Sequencial

Crearemos un modelo secuencial, que es una pila lineal de capas. Para ello, usaremos la función Sequential(), en el que nos basaremos para crear nuestro modelo funcional.

def sequential_model():
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation=tf.nn.relu),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
return model

Modelo con el API funcional

Ahora crearemos un modelo usando el api funcional de Keras. Este modelo es un poco más flexible que el modelo secuencial, ya que nos permite crear modelos con múltiples entradas y salidas, y también nos permite crear modelos con capas compartidas.

def functional_model():
# creamos nuestra entrada
input = tf.keras.Input(shape=(28, 28))
# creamos nuestra capas
x = tf.keras.layers.Flatten()(input)
x = tf.keras.layers.Dense(128, activation='relu')(x)
x = tf.keras.layers.Dense(64, activation='softmax')(x)
# definimos nuestro modelo
model = tf.keras.Model(inputs=input, outputs=x)
return model

model_func = functional_model()

3. Entrenar nuestro modelo

Ahora vamos a entrenar nuestro modelo usando los datos de fashion_mnist que es uno de los datasets de ejemplo que vienen con tensorflow.

fashion_mnist = tf.keras.datasets.fashion_mnist

(X_train, y_train), (X_test, y_test) = fashion_mnist.load_data()

# normalizar los datos
X_train = X_train / 255.0
X_test = X_test / 255.0

# configurar y conpilar nuestro modelo
model_func.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])

# entrenar el modelo
model_func.fit(X_train, y_train, epochs=5)
Epoch 1/5
1875/1875 [==============================] - 6s 3ms/step - loss: 0.2289 - accuracy: 0.9147
Epoch 2/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2180 - accuracy: 0.9187
Epoch 3/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2137 - accuracy: 0.9207
Epoch 4/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2063 - accuracy: 0.9226
Epoch 5/5
1875/1875 [==============================] - 5s 3ms/step - loss: 0.2003 - accuracy: 0.9247
<keras.callbacks.History at 0x212d77f6d50>

4. Evalular el modelo

Por ultimo vamos a evaluar el modelo con el conjunto de test. Para ello vamos a utilizar la función evaluate del modelo. Esta función nos devuelve el valor de la función de perdida y el valor de la métrica que hemos definido.

model_func.evaluate(X_test, y_test)
313/313 [==============================] - 1s 2ms/step - loss: 0.3422 - accuracy: 0.8854
[0.34223565459251404, 0.8853999972343445]

Como hemos visto crear un modelo usando el API funcional de keras, no es complicado, pero si es un poco más complejo que usando el API secuencial. Sin embargo, debemos tener en cuenta que el API funcional nos permite crear modelos más complejos, con más de una entrada y más de una salida, lo cual no es posible con el API secuencial.

· 4 min de lectura
Darvin Cotrina

Modelo con mutiples salidas

En este notebook, crearemos un modelo con multiples salidas usando el API funcional de keras.

Para el modelo usaremos lo datos de Energy efficiency

Importar modulos

::: {.cell _cell_guid=‘bc1c482a-3443-4deb-bfbe-35775986216c’ _uuid=‘3869d47c-4312-4dae-8587-4c76fe28cb21’ execution=‘{“iopub.execute_input”:“2023-08-11T20:34:57.287840Z”,“iopub.status.busy”:“2023-08-11T20:34:57.286929Z”,“iopub.status.idle”:“2023-08-11T20:34:57.295622Z”,“shell.execute_reply”:“2023-08-11T20:34:57.293802Z”,“shell.execute_reply.started”:“2023-08-11T20:34:57.287795Z”}’ jupyter=‘{“outputs_hidden”:false}’ trusted=‘true’ execution_count=1}

import tensorflow as tf
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
import requests
from io import BytesIO
import zipfile

:::

Preparar los datos

Descargamos el dataset y preparamos nuestros datos de entrenamiento y pruebas

# download file
url = 'https://archive.ics.uci.edu/static/public/242/energy+efficiency.zip'
r = requests.get(url)
zip_data = BytesIO(r.content)

# unzip
with zipfile.ZipFile(zip_data, 'r') as zip_file:
with zip_file.open('ENB2012_data.xlsx') as excel_file:
df = pd.read_excel(excel_file)

# random sort
df = df.sample(frac=1)

df.head()
X1X2X3X4X5X6X7X8Y1Y2
4700.66759.5318.5220.53.540.25412.8616.17
3790.64784.0343.0220.53.550.25217.1120.43
310.71710.5269.5220.53.550.0006.4011.67
7470.74686.0245.0220.53.550.40514.3916.70
7370.79637.0343.0147.07.030.40541.9637.70

::: {.cell _cell_guid=‘966384d2-52f6-43fe-a9a7-30a34005525c’ _uuid=‘246e7b03-c479-425e-b12e-0d31b6caa74c’ execution=‘{“iopub.execute_input”:“2023-08-11T20:35:01.995196Z”,“iopub.status.busy”:“2023-08-11T20:35:01.994656Z”,“iopub.status.idle”:“2023-08-11T20:35:02.010268Z”,“shell.execute_reply”:“2023-08-11T20:35:02.008886Z”,“shell.execute_reply.started”:“2023-08-11T20:35:01.995155Z”}’ jupyter=‘{“outputs_hidden”:false}’ trusted=‘true’ execution_count=3}

# split data
train, test = train_test_split(df, test_size=0.2)

def format_data(df: pd.DataFrame):
y1 = df['Y1'].values
y2 = df['Y2'].values
X = df.drop(['Y1', 'Y2'], axis=1)
return X, (y1, y2)

X_train, Y_train = format_data(train)
X_test, Y_test = format_data(test)

:::

::: {.cell _cell_guid=‘f867a2c3-849d-476b-8068-deea62ff3ea8’ _uuid=‘3717aebb-1951-47bc-aeb9-ef1ee7af3ced’ execution=‘{“iopub.execute_input”:“2023-08-11T20:35:06.917556Z”,“iopub.status.busy”:“2023-08-11T20:35:06.917099Z”,“iopub.status.idle”:“2023-08-11T20:35:06.925773Z”,“shell.execute_reply”:“2023-08-11T20:35:06.924386Z”,“shell.execute_reply.started”:“2023-08-11T20:35:06.917524Z”}’ jupyter=‘{“outputs_hidden”:false}’ trusted=‘true’ execution_count=4}

# normalize data
scaler = preprocessing.StandardScaler()
X_train_norm = scaler.fit_transform(X_train)
X_test_norm = scaler.transform(X_test)

:::

El modelo

Ahora construiremos nuestro modelo teniendo en cuenta que va a tener dos salidas

def build_model():
input_layer = tf.keras.layers.Input(shape=(8,))
hidden_layer = tf.keras.layers.Dense(128, activation='relu')(input_layer)
hidden_layer = tf.keras.layers.Dense(128, activation='relu')(hidden_layer)

# ouput 1
y1 = tf.keras.layers.Dense(1, name='y1')(hidden_layer)

# output 2
hidden_layer = tf.keras.layers.Dense(64, activation='relu')(hidden_layer)

y2 = tf.keras.layers.Dense(1, name='y2')(hidden_layer)

return tf.keras.models.Model(inputs=input_layer, outputs=[y1, y2])

Graficar nuestro modelo

Vamos a graficar nuestro modelo donde podemos observar de forma mas simple nuestras dos salidas y1 y y2, tambien podemos ver que y2 tiene una capa extra, esta es la de tf.keras.layers.Dense(units=1, name='y2', activation='linear')(X)

model = build_model()
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 8)] 0 []

dense (Dense) (None, 128) 1152 ['input_1[0][0]']

dense_1 (Dense) (None, 128) 16512 ['dense[0][0]']

dense_2 (Dense) (None, 64) 8256 ['dense_1[0][0]']

y1 (Dense) (None, 1) 129 ['dense_1[0][0]']

y2 (Dense) (None, 1) 65 ['dense_2[0][0]']

==================================================================================================
Total params: 26,114
Trainable params: 26,114
Non-trainable params: 0
__________________________________________________________________________________________________

Configurar parametros

model.compile(
optimizer=tf.optimizers.SGD(learning_rate=0.001),
loss={
'y1': tf.keras.losses.MeanSquaredError(),
'y2': tf.keras.losses.MeanSquaredError(),
},
metrics={
'y1': tf.keras.metrics.RootMeanSquaredError(),
'y2': tf.keras.metrics.RootMeanSquaredError(),
}
)

Entrenar el modelo

model.fit(
X_train_norm, Y_train,
epochs=500, batch_size=10,
validation_data=(X_test_norm, Y_test),
verbose=0
)
<keras.callbacks.History at 0x1e161d1ed10>

Evaluar el modelo

Ahora vamos a evaluar el modelo y graficar los datos para tener una mejor idea de lo que esta pasando

model.evaluate(X_test_norm, Y_test)
5/5 [==============================] - 0s 2ms/step - loss: 0.9150 - y1_loss: 0.2067 - y2_loss: 0.7082 - y1_root_mean_squared_error: 0.4547 - y2_root_mean_squared_error: 0.8416
[0.9149883389472961,
0.2067471146583557,
0.7082412838935852,
0.454694539308548,
0.8415707349777222]

Comparar datos reales y predichos

pred = model.predict(X_test_norm)
print(pred[0][:5])
Y_test[0][:5]
5/5 [==============================] - 0s 1ms/step
[[33.359097]
[37.002422]
[23.915794]
[35.689697]
[24.020735]]
array([33.16, 37.26, 24.03, 35.94, 24.24])
def plot_values(ax, y_true, y_pred, title):
ax.scatter(y_true, y_pred, alpha=0.5)
ax.plot([y_true.min(), y_true.max()], [y_true.min(), y_true.max()], '--')
ax.set_xlabel('True value')
ax.set_ylabel('Predicted value')
ax.set_title(title)

# Y1 vs pred
fig, ax = plt.subplots(1, 2, figsize=(15, 5))
plot_values(ax[0], Y_test[0], pred[0], 'Y1')
plot_values(ax[1], Y_test[1], pred[1], 'Y2')

· 3 min de lectura
Darvin Cotrina

En este articulo vamos a ver como hacer resampling de series de tiempo con pandas. El resampling es un proceso de conversión de series de tiempo de una frecuencia a otra. La frecuencia puede ser diaria, mensual, trimestral, anual, etc. Por ejemplo, podemos convertir una serie de tiempo con frecuencia diaria a una serie de tiempo con frecuencia mensual. También podemos convertir una serie de tiempo con frecuencia mensual a una serie de tiempo con frecuencia anual.

1. Frecuencias de series de tiempo

Las series de tiempo pueden tener diferentes frecuencias, ahora vamos a ver las frecuencias que podemos encontrar en pandas.

CódigoDescripción
BFrecuencia de negocios
CFrecuencia personalizada
DFrecuencia diaria
WFrecuencia semanal
MFrecuencia mensual
QFrecuencia trimestral
AFrecuencia anual
HFrecuencia horaria
TFrecuencia minutal
SFrecuencia segundal

2. Importar librerías

Primero empesaremos importando las librerías que vamos a utilizar.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

3. Cagar y preparar los datos

Ahora vamos a crear un DataFrame con datos usando una frecuencia diaria.

date_range = pd.date_range('01/01/2020', periods=365, freq='D')
# crearemos datos para la demostración
data = np.random.randn(len(date_range))
df = pd.DataFrame(data, index=date_range, columns=['Value'])
# graficamos los datos
df.plot()
plt.show()

4. Resampling a una frecuencia mas baja (downsampling)

Ahora vamos a convertir la serie de tiempo con frecuencia diaria a una serie de tiempo con frecuencia mensual. Para hacer esto vamos a usar el método resample(), al tener una frecuencia mas baja tenemos que especificar como queremos que se agreguen los datos, vamos a tener que ingresar una función de agregación. En este caso vamos a usar la función mean() para calcular el promedio de los datos, peru tu puedes usar cualquier función de agregación que necesites.

df_monthly_mean = df.resample('M').mean()
df_monthly_mean.head(5)
# graficamos los datos
df_monthly_mean.plot()
plt.show()

5. Resampling a una frecuencia mas alta (upsampling)

Ya vimos como disminuir la frecuencia, ahora veremos como aumentar la frecuencia. Para hacer esto vamos a usar el método resample() y especificar la frecuencia que queremos. En este caso vamos a aumentar la frecuencia de diaria a horaria, para esto vamos a usar el código H que significa frecuencia horaria.

df_hourly = df.resample('H').ffill() # otros metodos: bfill, interpolate

Para poder ver con mas detalle los datos haremos un acercamiento a un periodo de tiempo especifico.

df_hourly.loc['2020-01-01':'2020-01-30'].plot()
plt.show()

6. Manejar los datos faltantes

Como pudimos ver en el ejemplo anterior cuando aumentamos la frecuencía de diaria a horaria, muchos valores se convirtieron en NaN. Para solucionar esto vamos a usar el método interpolate() para interpolar los valores faltantes.

df_hourly = df.resample('H').interpolate()
df_hourly.loc['2020-01-01':'2020-01-30'].plot()
plt.show()

7. Resampling con multiples metodos de agregación

Tambien podemos hacer un resampling con multiples métodos de agregación. Para hacer esto vamos a usar el método agg() y especificar los métodos de agregación que queremos usar.

df_weekly = df.resample('W').agg(['mean', 'std', 'min', 'max'])
df_weekly.plot()
plt.show()

Eso es todo por ahora, espero que este articulo te haya sido de ayuda

· 3 min de lectura
Darvin Cotrina

Comparar series de tiempo con pandas

Comparar series de tiempo con pandas, de algunas acciones de las principales empresas tecnológicas.

1. Importar librerías

Para este ejercicio, se necesitara de las siguientes librerías:

pip install pandas
pip install matplotlib
pip install yfinance

Usaremos la librería yfinance para obtener los datos de las acciones de las empresas tecnológicas, hay otras librearías que también pueden ayudar con esta tarea como pandas_datareader o quandl.

import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf

2. Obtener datos

Para este ejemplo, se obtendrán los datos de las acciones de las empresas tecnológicas desde el 2015 de google, amazon, facebook, apple y microsoft.

tickets = ['GOOG', 'AMZN', 'META', 'AAPL', 'MSFT']
start_date = '2015-01-01'
end_date = '2023-01-01'

df = yf.download(tickets, start=start_date, end=end_date)['Adj Close']
df.head()
[*********************100%***********************]  5 of 5 completed
AAPLAMZNGOOGMETAMSFT
Date
2015-01-0224.53176315.426026.16865378.44999740.620667
2015-01-0523.84066615.109525.62315277.19000240.247116
2015-01-0623.84291314.764525.02928276.15000239.656406
2015-01-0724.17723814.921024.98640176.15000240.160259
2015-01-0825.10618415.023025.06518478.18000041.341694

3. Normalizar datos

Para poder comparar los datos vamos a normalizarlos, para esto se usará la siguiente fórmula:

$$ \frac{P_t}{P_0} * 100 $$ Donde PtP_t es el precio en el tiempo tt y P0P_0 es el precio inicial.

normalized_df = df / df.iloc[0] * 100
normalized_df.head()
AAPLAMZNGOOGMETAMSFT
Date
2015-01-02100.000000100.000000100.000000100.000000100.000000
2015-01-0597.18284797.94827197.91543898.39388899.080393
2015-01-0697.19200695.71178595.64604397.06820297.626183
2015-01-0798.55483496.72630595.48217997.06820298.866569
2015-01-08102.34154097.38752895.78323899.655836101.775026

4. Graficar datos

Por ultimo grafiaremos los datos para poder compararlos y ver como se han comportado en el tiempo.

normalized_df.plot(figsize=(15, 10))
plt.show()

5. Conclusiones

Como hemos podido ver hacer una comparación de series de tiempo es muy sencillo con pandas, y nos permite ver como se han comportado las acciones de las empresas tecnológicas en los últimos años, las concluciones respecto al comportamiento de las acciones de las empresas tecnológicas se las dejo a ustedes.

· 2 min de lectura
Darvin Cotrina

¿Como ver los días de la semana usando pandas?`

Con pandas podemos ver de forma muy sencilla los días de la semana de una fecha en específico, para esto usaremos la función weekday_name y con dayofweek podemos ver el número del día de la semana.

import pandas

day = pandas.to_datetime('2023-07-10')
print(day.dayofweek, day.day_name())
0 Monday
# todos los días de la semana
week = pandas.date_range(start='2023-07-10', periods=7, freq='D')
for day in week:
print(day.dayofweek, day.day_name())
0 Monday
1 Tuesday
2 Wednesday
3 Thursday
4 Friday
5 Saturday
6 Sunday
# df con los dias de la semana
df = pandas.DataFrame(week, columns=['date'])
df['dayofweek'] = df['date'].dt.dayofweek
df['dayname'] = df['date'].dt.day_name()
df.set_index('date', inplace=True)
df
dayofweekdayname
date
2023-07-100Monday
2023-07-111Tuesday
2023-07-122Wednesday
2023-07-133Thursday
2023-07-144Friday
2023-07-155Saturday
2023-07-166Sunday

· 9 min de lectura
Darvin Cotrina

En esta sección vamos a ver cómo podemos extraer información de páginas web usando la librería BeautifulSoup de Python, veremos el poder de esta librería y cómo podemos usarla para extraer información de páginas web.

1. Requerimientos

Antes de empezar, debemos instalar las siguientes librerías:

pip install requests
pip install beautifulsoup4

2. Importar librerías

Como en todo codigo de Python, lo primero que debemos hacer es importar las librerías que vamos a usar, en este caso

from bs4 import BeautifulSoup
import requests

3. Obtener el contenido de una página web

BeautifulSoup no puede optener el contenido directamente desde una url, por lo que nos vamos ayudar en el módulo requests

url = 'https://peps.python.org/pep-0020/'
r = requests.get(url)
html = r.text

4. Ver el contenido de la página

BeautifulSoup nos permite ver el contenido de la página de una forma más amigable, para ello usamos el método prettify()

soup = BeautifulSoup(html, 'html')
print(soup.prettify())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<meta content="light dark" name="color-scheme"/>
<title>
PEP 20 – The Zen of Python | peps.python.org
</title>
<link href="../_static/py.png" rel="shortcut icon"/>
<link href="https://peps.python.org/pep-0020/" rel="canonical"/>
<link href="../_static/style.css" rel="stylesheet" type="text/css"/>
<link href="../_static/mq.css" rel="stylesheet" type="text/css"/>
<link href="../_static/pygments.css" id="pyg-light" media="(prefers-color-scheme: light)" rel="stylesheet" type="text/css"/>
<link href="../_static/pygments_dark.css" id="pyg-dark" media="(prefers-color-scheme: dark)" rel="stylesheet" type="text/css"/>
<link href="https://peps.python.org/peps.rss" rel="alternate" title="Latest PEPs" type="application/rss+xml"/>
<meta content="Python Enhancement Proposals (PEPs)" name="description"/>
</head>
<body>
<svg style="display: none;" xmlns="http://www.w3.org/2000/svg">
<symbol id="svg-sun-half" pointer-events="all" viewbox="0 0 24 24">
<title>
Following system colour scheme
</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9">
</circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85">
</path>
</svg>
</symbol>
<symbol id="svg-moon" pointer-events="all" viewbox="0 0 24 24">
<title>
Selected dark colour scheme
</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" stroke="none">
</path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z">
</path>
</svg>
</symbol>
<symbol id="svg-sun" pointer-events="all" viewbox="0 0 24 24">
<title>
Selected light colour scheme
</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="5">
</circle>
<line x1="12" x2="12" y1="1" y2="3">
</line>
<line x1="12" x2="12" y1="21" y2="23">
</line>
<line x1="4.22" x2="5.64" y1="4.22" y2="5.64">
</line>
<line x1="18.36" x2="19.78" y1="18.36" y2="19.78">
</line>
<line x1="1" x2="3" y1="12" y2="12">
</line>
<line x1="21" x2="23" y1="12" y2="12">
</line>
<line x1="4.22" x2="5.64" y1="19.78" y2="18.36">
</line>
<line x1="18.36" x2="19.78" y1="5.64" y2="4.22">
</line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>
Python Enhancement Proposals
</h1>
<ul class="breadcrumbs">
<li>
<a href="https://www.python.org/" title="The Python Programming Language">
Python
</a>
»
</li>
<li>
<a href="../pep-0000/">
PEP Index
</a>
»
</li>
<li>
PEP 20
</li>
</ul>
<button id="colour-scheme-cycler" onclick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto">
<use href="#svg-sun-half">
</use>
</svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark">
<use href="#svg-moon">
</use>
</svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light">
<use href="#svg-sun">
</use>
</svg>
<span class="visually-hidden">
Toggle light / dark / auto colour theme
</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">
PEP 20 – The Zen of Python
</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">
Author
<span class="colon">
:
</span>
</dt>
<dd class="field-odd">
Tim Peters &lt;tim.peters at gmail.com&gt;
</dd>
<dt class="field-even">
Status
<span class="colon">
:
</span>
</dt>
<dd class="field-even">
<abbr title="Currently valid informational guidance, or an in-use process">
Active
</abbr>
</dd>
<dt class="field-odd">
Type
<span class="colon">
:
</span>
</dt>
<dd class="field-odd">
<abbr title="Non-normative PEP containing background, guidelines or other information relevant to the Python ecosystem">
Informational
</abbr>
</dd>
<dt class="field-even">
Created
<span class="colon">
:
</span>
</dt>
<dd class="field-even">
19-Aug-2004
</dd>
<dt class="field-odd">
Post-History
<span class="colon">
:
</span>
</dt>
<dd class="field-odd">
22-Aug-2004
</dd>
</dl>
<hr class="docutils"/>
<section id="contents">
<details>
<summary>
Table of Contents
</summary>
<ul class="simple">
<li>
<a class="reference internal" href="#abstract">
Abstract
</a>
</li>
<li>
<a class="reference internal" href="#the-zen-of-python">
The Zen of Python
</a>
</li>
<li>
<a class="reference internal" href="#easter-egg">
Easter Egg
</a>
</li>
<li>
<a class="reference internal" href="#references">
References
</a>
</li>
<li>
<a class="reference internal" href="#copyright">
Copyright
</a>
</li>
</ul>
</details>
</section>
<section id="abstract">
<h2>
<a class="toc-backref" href="#abstract" role="doc-backlink">
Abstract
</a>
</h2>
<p>
Long time Pythoneer Tim Peters succinctly channels the BDFL’s guiding
principles for Python’s design into 20 aphorisms, only 19 of which
have been written down.
</p>
</section>
<section id="the-zen-of-python">
<h2>
<a class="toc-backref" href="#the-zen-of-python" role="doc-backlink">
The Zen of Python
</a>
</h2>
<div class="highlight-text notranslate">
<div class="highlight">
<pre><span></span>Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
</pre>
</div>
</div>
</section>
<section id="easter-egg">
<h2>
<a class="toc-backref" href="#easter-egg" role="doc-backlink">
Easter Egg
</a>
</h2>
<div class="highlight-pycon notranslate">
<div class="highlight">
<pre><span></span><span class="gp">&gt;&gt;&gt; </span><span class="kn">import</span> <span class="nn">this</span>
</pre>
</div>
</div>
</section>
<section id="references">
<h2>
<a class="toc-backref" href="#references" role="doc-backlink">
References
</a>
</h2>
<p>
Originally posted to
<a class="reference external" href="mailto:comp.lang.python/python-list%40python.org">
comp
<span>
.
</span>
lang
<span>
.
</span>
python/python-list
<span>
@
</span>
python
<span>
.
</span>
org
</a>
under a
thread called
<a class="reference external" href="https://groups.google.com/d/msg/comp.lang.python/B_VxeTBClM0/L8W9KlsiriUJ">
“The Way of Python”
</a>
</p>
</section>
<section id="copyright">
<h2>
<a class="toc-backref" href="#copyright" role="doc-backlink">
Copyright
</a>
</h2>
<p>
This document has been placed in the public domain.
</p>
</section>
</section>
<hr class="docutils"/>
<p>
Source:
<a class="reference external" href="https://github.com/python/peps/blob/main/pep-0020.txt">
https://github.com/python/peps/blob/main/pep-0020.txt
</a>
</p>
<p>
Last modified:
<a class="reference external" href="https://github.com/python/peps/commits/main/pep-0020.txt">
2022-03-15 17:40:34+00:00 GMT
</a>
</p>
</article>
<nav id="pep-sidebar">
<h2>
Contents
</h2>
<ul>
<li>
<a class="reference internal" href="#abstract">
Abstract
</a>
</li>
<li>
<a class="reference internal" href="#the-zen-of-python">
The Zen of Python
</a>
</li>
<li>
<a class="reference internal" href="#easter-egg">
Easter Egg
</a>
</li>
<li>
<a class="reference internal" href="#references">
References
</a>
</li>
<li>
<a class="reference internal" href="#copyright">
Copyright
</a>
</li>
</ul>
<br/>
<a href="https://github.com/python/peps/blob/main/pep-0020.txt" id="source">
Page Source (GitHub)
</a>
</nav>
</section>
<script src="../_static/colour_scheme.js">
</script>
<script src="../_static/wrap_tables.js">
</script>
<script src="../_static/sticky_banner.js">
</script>
</body>
</html>

Una vez que tenemos el contenido de la página, podemos navegar por el contenido usando los métodos find() y find_all().

a = soup.find_all('a')
for link in a:
print(link.get('href'))
https://www.python.org/
../pep-0000/
#abstract
#the-zen-of-python
#easter-egg
#references
#copyright
#abstract
#the-zen-of-python
#easter-egg
#references
mailto:comp.lang.python/python-list%40python.org
https://groups.google.com/d/msg/comp.lang.python/B_VxeTBClM0/L8W9KlsiriUJ
#copyright
https://github.com/python/peps/blob/main/pep-0020.txt
https://github.com/python/peps/commits/main/pep-0020.txt
#abstract
#the-zen-of-python
#easter-egg
#references
#copyright
https://github.com/python/peps/blob/main/pep-0020.txt

Para navegar por el contenido del documento, BeautifulSoup tiene varios métodos que nos permiten navegar por el contenido del documento, los más usados son:

  • find(): Nos permite encontrar el primer elemento que cumpla con las condiciones especificadas.
  • find_all(): Nos permite encontrar todos los elementos que cumplan con las condiciones especificadas.
  • select(): Nos permite encontrar todos los elementos que cumplan con las condiciones especificadas usando selectores CSS.
  • select_one(): Nos permite encontrar el primer elemento que cumpla con las condiciones especificadas usando selectores CSS.
  • find_parent(): Nos permite encontrar el elemento padre que cumpla con las condiciones especificadas.
  • find_parents(): Nos permite encontrar todos los elementos padres que cumplan con las condiciones especificadas.

Entre otros, para mas información ir a la documentación

# buscar elementos a por class
elements = soup.find_all('a', {'class': 'reference internal'})
for element in elements:
print(element.get('href'))
#abstract
#the-zen-of-python
#easter-egg
#references
#copyright
#abstract
#the-zen-of-python
#easter-egg
#references
#copyright
# buscar elementos por id
elements = soup.find_all(id = 'copyright')
print(elements)
[<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain.</p>
</section>]
# buscar elementos por texto
elements = soup.find_all(string='PEP 20')
elements
[]
# buscar elementos por texto con expresiones regulares
import re
elements = soup.find_all(re.compile('^sy'))
elements
[<symbol id="svg-sun-half" pointer-events="all" viewbox="0 0 24 24">
<title>Following system colour scheme</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>,
<symbol id="svg-moon" pointer-events="all" viewbox="0 0 24 24">
<title>Selected dark colour scheme</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" stroke="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>,
<symbol id="svg-sun" pointer-events="all" viewbox="0 0 24 24">
<title>Selected light colour scheme</title>
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" x2="12" y1="1" y2="3"></line>
<line x1="12" x2="12" y1="21" y2="23"></line>
<line x1="4.22" x2="5.64" y1="4.22" y2="5.64"></line>
<line x1="18.36" x2="19.78" y1="18.36" y2="19.78"></line>
<line x1="1" x2="3" y1="12" y2="12"></line>
<line x1="21" x2="23" y1="12" y2="12"></line>
<line x1="4.22" x2="5.64" y1="19.78" y2="18.36"></line>
<line x1="18.36" x2="19.78" y1="5.64" y2="4.22"></line>
</svg>
</symbol>]

6. Conclusiones

Como hemos visto, BeautifulSoup es una librería muy poderosa que nos permite extraer información de páginas web de una forma rapida y sencilla, pero tiene sus limitaciones y no es la mejor opción para extraer información de páginas web complejas, para ello existen otras librerías como Scrapy que nos permiten extraer información de páginas web de una forma más eficiente.

Cuando hagamos scraping de páginas web, debes de tener en cuenta que tienes que respetar las políticas de privacidad de la página web, para ello debes de leer los términos y condiciones de la página web, asi como el acceso a los robots de búsqueda, del archivo robots.txt de la página web.

· 4 min de lectura
Darvin Cotrina

¿Que es un árbol de decisión?

Un árbol de decisión es un modelo de predicción utilizado en el ámbito de la inteligencia artificial. Dada una base de datos se construye un árbol de decisión para poder llegar a la conclusión deseada. Es una herramienta de apoyo para la toma de decisiones.

¿Que es un método de ensamblaje?

Los métodos de ensamblaje son métodos que combinan varios algoritmos de aprendizaje automático para obtener un mejor rendimiento predictivo que un solo algoritmo de aprendizaje automático. Los métodos de ensamblaje funcionan mejor cuando los predictores individuales están correlacionados entre sí.

Muestras con reemplazo

En estadística, el muestreo con reemplazo es un método de muestreo en el que, para cada extracción, el elemento elegido se devuelve a la población y se mezcla con el resto de elementos. El muestreo con reemplazo es un método de muestreo no exhaustivo.

P(xi)=1NP(x_i) = \frac{1}{N}

En arboles de decisión se utiliza el muestreo con reemplazo para generar los árboles de decisión que se utilizaran para el ensamblaje, es decir, se generan varios árboles de decisión con muestras de la base de datos original, y se combinan para generar un modelo más robusto.

Random Forest

Random Forest es un método de ensamblaje que combina varios árboles de decisión, cada uno de los cuales se genera con una muestra de la base de datos original, y se combinan para generar un modelo más robusto. esteme metodo usa el muestreo con reemplazo para generar los árboles de decisión.

Tenemos un datos de entrenamiento de tamaño mm

  • para b = 1 hasta B: Utilizamos el muestreo con reemplazo para generar una muestra de tamaño mm de la base de datos original. Entrenamos un árbol de decisión TbT_b con la muestra generada.
  • Se obtiene el modelo final combinando los BB árboles de decisión generados.

Cuando usamos este algorithmo, muchas veces tenemos la misma división en el nodo raíz, por lo que podemos modificar un poco el algorithmo para que esto no suceda, y así obtener un mejor modelo.

Elección de características aleatorias

En cada nodo, se elige un subconjunto aleatorio de kk características de todo el conjunto de características. si nn es el número total de características, se recomienda k=nk = \sqrt{n} para la regresión y k=n3k = \frac{n}{3} para la clasificación, debe de tener en cuenta que esto es recomendado para un gran número de características

XGBoost ( ExTreme Gradient Boosting)

XGBoost es un método de ensamblaje que combina varios árboles de decisión, cada uno de los cuales se genera con una muestra de la base de datos original, y se combinan para generar un modelo más robusto. este metodo usa el muestreo con reemplazo para generar los árboles de decisión.

Pero a diferencia de Random Forest, XGBoost utiliza un algorithmo de optimización para generar los árboles de decisión, En vez de utilizar el muestreo con reemplazo para generar los árboles de decisión con una probabilidad uniforme 1/m1/m, XGBoost utiliza un algorithmo de optimización para generar los árboles de decisión con una probabilidad pip_i que depende de la pérdida de la iteración anterior.

pi=eΔLiλi=1meΔLiλp_i = \frac{e^{\frac{-\Delta L_i}{\lambda}}}{\sum_{i=1}^{m} e^{\frac{-\Delta L_i}{\lambda}}}

Donde:

  • ΔLi\Delta L_i es la pérdida de la iteración ii
  • λ\lambda es un parámetro de regularización

La idea de esto es que el algorithmo de optimización se enfoque en las muestras que tienen una pérdida mayor, y así generar un mejor modelo.

Ventajas de XGBoost

  • Implementaciónes open source en varios lenguajes de programación
  • Rapidez en el entrenamiento
  • Buena elección de divisiónes criticas por defecto y criterio para cuando parar de dividir
  • Regularización para evitar el sobreajuste

Implementación en Python

Para la implementación en Python, se utilizara la librería XGBoost, la cual se puede instalar con el comando:

from xgboost import XGBClassifier
model = XGBClassifier() # XGBRegressor para regresión
model.fit(X_train, y_train)
y_pred = model.predict(X_test)

Cuando usar Arboles de decisión y métodos de ensamblaje

  • Trabajan bien con datos tabulares (estructurados)
  • No se recomienda para datos no estructurados (imágenes, texto, audio, etc)
  • Es muy rápido en entrenamiento y predicción
  • Pequeños arboles de decisión son fáciles de interpretar (visualizar)

Cuando usar neural networks

  • Trabaja bien con todo tipo de datos tabulares "estructurados" y "no estructurados"
  • Puede ser lento en entrenamiento y predicción
  • Trabaja con transfer learning
  • Cuando trabajamos con multiples modelos juntos, puede ser mas sencillo encadenarlos con una red neuronal

· 11 min de lectura
Darvin Cotrina

Broadcasting con NumPy

image.png

El broadcasting es una forma de hacer operaciones entre arrays de diferentes tamaños, que usualmente no son compatibles. El broadcasting es posible cuando se cumplen ciertas reglas.

  • El tamaño de cada dimensión es igual.
  • Una de las dimensiones es 1.

El broadcasting se realiza en la dimensión que tiene tamaño 1. El array con tamaño 1 se extiende para que tenga el mismo tamaño que el otro array.

Ejemplos de broadcasting

Array 1Array 2Resultado
10 x 51 x 510 x 5
10 x 5510 x 5
10 x 510 x 110 x 5
10 x 5scalar10 x 5
import numpy as np
from rich import print
# matris de 10x5
m10_5 = np.random.randint(0, 10, (10, 5))
m1_5 = np.random.randint(0, 10, (1, 5))
m5 = np.random.randint(0, 10, (5))
m10_1 = np.random.randint(0, 10, (10, 1))
scalar = 5

print('Broadcasting de 10x5 + 1x5')
print(m10_5 + m1_5)
print('Broadcasting de 10x5 + 5')
print(m10_5 + m5)
print('Broadcasting de 10x5 + 10x1')
print(m10_5 + m10_1)
print(m10_5 + scalar)
Broadcasting de 10x5 + 1x5
[[ 9  2  6  5  1]
 [ 5  9 10 10  8]
 [ 6  9  3 10  3]
 [ 8  8  7  4  8]
 [ 5  2  6 10  5]
 [ 7  9  9  8  3]
 [ 7  5  3  5  7]
 [ 4  7  7  3  1]
 [ 2  8  6 12  7]
 [ 9  9 12 10 10]]
Broadcasting de 10x5 + 5
[[ 7 11  9  2  5]
 [ 3 18 13  7 12]
 [ 4 18  6  7  7]
 [ 6 17 10  1 12]
 [ 3 11  9  7  9]
 [ 5 18 12  5  7]
 [ 5 14  6  2 11]
 [ 2 16 10  0  5]
 [ 0 17  9  9 11]
 [ 7 18 15  7 14]]
Broadcasting de 10x5 + 10x1
[[14  9 10  9  7]
 [ 5 11  9  9  9]
 [ 5 10  1  8  3]
 [11 13  9  6 12]
 [ 9  8  9 13 10]
 [11 15 12 11  8]
 [ 6  6  1  3  7]
 [ 3  8  5  1  1]
 [ 8 16 11 17 14]
 [ 7  9  9  7  9]]
[[12  7  8  7  5]
 [ 8 14 12 12 12]
 [ 9 14  5 12  7]
 [11 13  9  6 12]
 [ 8  7  8 12  9]
 [10 14 11 10  7]
 [10 10  5  7 11]
 [ 7 12  9  5  5]
 [ 5 13  8 14 11]
 [12 14 14 12 14]]

Ejemplos de no broadcasting

Array 1Array 2Resultado
10 x 55 x 10Error
10 x 510Error
m10_5 = np.random.randint(0, 10, (10, 5))
m5_10 = np.random.randint(0, 10, (5, 10))
m10 = np.random.randint(0, 10, (10))

print('Broadcasting de 10x5 + 5x10')
print(m10_5 + m5_10)
Broadcasting de 10x5 + 5x10
ValueError: operands could not be broadcast together with shapes (10,5) (5,10) 
print('Broadcasting de 10x5 + 10')
print(m10_5 + m10)
Broadcasting de 10x5 + 10
ValueError: operands could not be broadcast together with shapes (10,5) (10,)