Operación aritmética usando Image y un entero como entradas usando red neuronal | de Sajin Payandath | octubre de 2022

Como tratamos con redes neuronales que toman imágenes como entrada en la mayoría de los casos, ¿cómo usamos la red neuronal para agregar un número a otro número que está en formato de imagen?

El objetivo es experimentar y aprender sobre cómo usar la red neuronal para generalizar la operación de agregar un número a una imagen que tiene un número. La salida generada debe ser una imagen (MNIST) que contenga la suma de las entradas.

Esta solución se puede ejecutar en cualquier dispositivo, desde su computadora portátil personal hasta un servidor.

Cuaderno Kaggle para ejecutar el código directamente de principio a fin
https://www.kaggle.com/code/sajinpgupta/add-number-and-numberinimage

Entonces, ¿cómo agregamos un número entero a una imagen con un valor como se muestra en la imagen de abajo?

¡Empecemos! 🙂

# Plot MNIST images
from tensorflow.keras.datasets import mnist
from matplotlib import pyplot as plt
def plot_mnist(trainX, img_ids):
for i,id1 in enumerate(img_ids):
# define subplot
plt.subplot(330 + 1 + i)
# plot raw pixel data
plt.imshow(trainX[id1], cmap=plt.get_cmap('gray'))
# show the figure
plt.show()

Ahora vamos a cargar datos MNIST usando la función

# load dataset
(trainX, trainY), (testX, testY) = mnist.load_data()
# reshape dataset to have a single channel
trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))
testX = testX.reshape((testX.shape[0], 28, 28, 1))

Forma de los conjuntos de datos

trainX.shape, trainY.shape

Limite el rango de números en las entradas (p. ej., número máximo de entrada 3 + número máximo en la imagen 3, lo que significa que el número máximo en la imagen de salida es 6). Así que los dígitos en nuestro experimento van del 0 al 6

digits = np.arange(0,7)
img_ids = np.unique(trainY[np.isin(trainY, digits)])
img_ids

Ahora necesitamos obtener los índices de las imágenes almacenadas en las matrices numpy para estos números del 0 al 6

train_img_ids = np.where((trainY>=digits[0])&(trainY <=digits[-1]))
train_img_ids
test_img_ids = np.where((testY>=digits[0]) & (testY <=digits[-1]))
test_img_ids

Simplemente verifique los números en el conjunto de datos

np.unique(trainY[train_img_ids])

Formemos nuestros conjuntos de datos de entrenamiento y prueba de imágenes para estos números del 0 al 6

trainX1 = trainX[bool_img_ids]
trainY1 = trainY[bool_img_ids]
testX1 = testX[test_img_ids]
testY1 = testY[test_img_ids]

Limite el tamaño de los conjuntos de datos a 10K por el momento. Podría experimentar con un gran conjunto de datos

trainX1 = trainX1[:10000]
trainY1 = trainY1[:10000]
testX1 = testX1[:10000]
testY1 = testY1[:10000]
trainX1.shape, trainY1.shape, testX1.shape, testY1.shape
np.unique(testY1), np.unique(trainY1)

Normalizar los datos de la imagen

train_norm = trainX1.astype('float32')
test_norm = testX1.astype('float32')
# normalize to range 0-1
train_norm = train_norm / 255.0
test_norm = test_norm / 255.0

Combine conjuntos de datos de entrenamiento y prueba en uno para usar en el Generador de datos durante el entrenamiento

dataX_final = np.concatenate((train_norm , test_norm))
dataY_final = np.concatenate((trainY1 , testY1))
dataX_final.shape, dataY_final.shape

Ver imágenes de muestra

plot_mnist(dataX_final, [0,1,2]), dataY_final[0:3]
plot_mnist(testX, [0,1,2]), testY[0:3]

Como tratamos con redes neuronales que toman imágenes como entrada en la mayoría de los casos, ¿cómo usamos la red neuronal para agregar un número a otro número que está en formato de imagen?

El objetivo es experimentar y aprender sobre cómo usar la red neuronal para generalizar la operación de agregar un número a una imagen que tiene un número.

# Plot MNIST images
from tensorflow.keras.datasets import mnist
from matplotlib import pyplot as plt
def plot_mnist(trainX, img_ids):
for i,id1 in enumerate(img_ids):
# define subplot
plt.subplot(330 + 1 + i)
# plot raw pixel data
plt.imshow(trainX[id1], cmap=plt.get_cmap('gray'))
# show the figure
plt.show()

Ahora vamos a cargar datos MNIST usando la función

# load dataset
(trainX, trainY), (testX, testY) = mnist.load_data()
# reshape dataset to have a single channel
trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))
testX = testX.reshape((testX.shape[0], 28, 28, 1))

Forma de los conjuntos de datos

trainX.shape, trainY.shape

Limite el rango de números en las entradas (p. ej., número máximo de entrada 3 + número máximo en la imagen 3, lo que significa que el número máximo en la imagen de salida es 6). Así que los dígitos en nuestro experimento van del 0 al 6

digits = np.arange(0,7)
img_ids = np.unique(trainY[np.isin(trainY, digits)])
img_ids

Ahora necesitamos obtener los índices de las imágenes almacenadas en las matrices numpy para estos números del 0 al 6

train_img_ids = np.where((trainY>=digits[0])&(trainY <=digits[-1]))
train_img_ids
test_img_ids = np.where((testY>=digits[0]) & (testY <=digits[-1]))
test_img_ids

Simplemente verifique los números en el conjunto de datos

np.unique(trainY[train_img_ids])

Formemos nuestros conjuntos de datos de entrenamiento y prueba de imágenes para estos números del 0 al 6

trainX1 = trainX[bool_img_ids]
trainY1 = trainY[bool_img_ids]
testX1 = testX[test_img_ids]
testY1 = testY[test_img_ids]

Limite el tamaño de los conjuntos de datos a 10K por el momento. Podría experimentar con un gran conjunto de datos

trainX1 = trainX1[:10000]
trainY1 = trainY1[:10000]
testX1 = testX1[:10000]
testY1 = testY1[:10000]
trainX1.shape, trainY1.shape, testX1.shape, testY1.shape
np.unique(testY1), np.unique(trainY1)

Normalizar los datos de la imagen

train_norm = trainX1.astype('float32')
test_norm = testX1.astype('float32')
# normalize to range 0-1
train_norm = train_norm / 255.0
test_norm = test_norm / 255.0

Combine conjuntos de datos de entrenamiento y prueba en uno para usar en el Generador de datos durante el entrenamiento

dataX_final = np.concatenate((train_norm , test_norm))
dataY_final = np.concatenate((trainY1 , testY1))
dataX_final.shape, dataY_final.shape

Ver imágenes de muestra

plot_mnist(dataX_final, [0,1,2]), dataY_final[0:3]
plot_mnist(testX, [0,1,2]), testY[0:3]

Como tratamos con redes neuronales que toman imágenes como entrada en la mayoría de los casos, ¿cómo usamos la red neuronal para agregar un número a otro número que está en formato de imagen?

El objetivo es experimentar y aprender sobre cómo usar la red neuronal para generalizar la operación de agregar un número a una imagen que tiene un número.

# Plot MNIST images
from tensorflow.keras.datasets import mnist
from matplotlib import pyplot as plt
def plot_mnist(trainX, img_ids):
for i,id1 in enumerate(img_ids):
# define subplot
plt.subplot(330 + 1 + i)
# plot raw pixel data
plt.imshow(trainX[id1], cmap=plt.get_cmap('gray'))
# show the figure
plt.show()

Ahora vamos a cargar datos MNIST usando la función

# load dataset
(trainX, trainY), (testX, testY) = mnist.load_data()
# reshape dataset to have a single channel
trainX = trainX.reshape((trainX.shape[0], 28, 28, 1))
testX = testX.reshape((testX.shape[0], 28, 28, 1))

Forma de los conjuntos de datos

trainX.shape, trainY.shape

Limite el rango de números en las entradas (p. ej., número máximo de entrada 3 + número máximo en la imagen 3, lo que significa que el número máximo en la imagen de salida es 6). Así que los dígitos en nuestro experimento van del 0 al 6

digits = np.arange(0,7)
img_ids = np.unique(trainY[np.isin(trainY, digits)])
img_ids

Ahora necesitamos obtener los índices de las imágenes almacenadas en las matrices numpy para estos números del 0 al 6

train_img_ids = np.where((trainY>=digits[0])&(trainY <=digits[-1]))
train_img_ids
test_img_ids = np.where((testY>=digits[0]) & (testY <=digits[-1]))
test_img_ids

Simplemente verifique los números en el conjunto de datos

np.unique(trainY[train_img_ids])

Formemos nuestros conjuntos de datos de entrenamiento y prueba de imágenes para estos números del 0 al 6

trainX1 = trainX[bool_img_ids]
trainY1 = trainY[bool_img_ids]
testX1 = testX[test_img_ids]
testY1 = testY[test_img_ids]

Limite el tamaño de los conjuntos de datos a 10K por el momento. Podría experimentar con un gran conjunto de datos

trainX1 = trainX1[:10000]
trainY1 = trainY1[:10000]
testX1 = testX1[:10000]
testY1 = testY1[:10000]
trainX1.shape, trainY1.shape, testX1.shape, testY1.shape
np.unique(testY1), np.unique(trainY1)

Normalizar los datos de la imagen

train_norm = trainX1.astype('float32')
test_norm = testX1.astype('float32')
# normalize to range 0-1
train_norm = train_norm / 255.0
test_norm = test_norm / 255.0

Combine conjuntos de datos de entrenamiento y prueba en uno para usar en el Generador de datos durante el entrenamiento

dataX_final = np.concatenate((train_norm , test_norm))
dataY_final = np.concatenate((trainY1 , testY1))
dataX_final.shape, dataY_final.shape

Ver imágenes de muestra

plot_mnist(dataX_final, [0,1,2]), dataY_final[0:3]
plot_mnist(testX, [0,1,2]), testY[0:3]
import keras;
from keras.models import Model;
from keras.layers import Dense, Conv2D, MaxPooling2D, LSTM, Flatten, Dropout, Input, GRU, Reshape,GlobalMaxPooling2D
,Conv2DTranspose, UpSampling2D, Embedding, concatenate
rnn_size = 50
width, height = 28, 28
comp_vector = 8
inp_int = Input(shape=(1,),name='inp_int1') #vector of size 4 to represent 0,1,2,3
inp_int_vector = Embedding(4, 32 ) (inp_int) # input range limited to 0 to 3, output dim = 32
inp_int_vector = GRU(8)(inp_int_vector)
encoder_input = Input(shape=(width, height, 1), name="img")
x = Conv2D(8, 3, activation="relu")(encoder_input)
x = Conv2D(16, 3, activation="relu")(x)
x = MaxPooling2D()(x)
x = Conv2D(16, 3, activation="relu")(x)
x = Conv2D(comp_vector, 3, activation="relu")(x)
encoder_output = GlobalMaxPooling2D()(x)
#cnn_to_rnn_dims = (4, 4*16) #( width//(pool_size**2), (height//(pool_size**2))*conv_filters )
#encoder_output = Reshape(target_shape=cnn_to_rnn_dims, name='reshape')(encoder_output)
encoder_output = Dense(comp_vector, activation= 'relu', name='dense1')(encoder_output)
#encoder_output = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru1')(encoder_output)
encoder_output_all = concatenate([encoder_output, inp_int_vector])encoder = Model(encoder_input, encoder_output, name="encoder")# encoder.summary()x = Reshape((4, 4, 1))(encoder_output_all)
x = Conv2D(8, kernel_size=(3,3), padding="same")(x)
x = Conv2DTranspose(16, 3, activation="relu")(x)
x = Conv2DTranspose(32, 3, activation="relu")(x)
x = UpSampling2D(3)(x)
x = Conv2DTranspose(16, 3, activation="relu")(x)
decoder_output = Conv2DTranspose(1, 3, activation="relu", name="img_sum")(x)
#decoder_output = Conv2D(1, 3, activation="relu", padding="same", name = "img_sum")(decoder_output)autoencoder = Model(inputs=[encoder_input, inp_int] , outputs = decoder_output, name="autoencoder")
autoencoder.summary()
np.unique(trainY1[:1000], return_counts=True )

Creemos un generador de datos que mezcle las imágenes y los dígitos de entrada y los pase a la red neuronal durante el entrenamiento del modelo.

import numpy as np
import keras
class DataGenerator(keras.utils.all_utils.Sequence):
'Generates data for Keras'
def __init__(self, list_IDs, inp_dig_ids, batch_size=32, dim=(32,32,32), n_channels=1,
repeat_factor=10, shuffle=True):
'Initialization'
self.dim = dim
self.batch_size = batch_size
self.inp_dig_ids = inp_dig_ids
self.list_IDs = list_IDs
self.n_channels = n_channels
self.repeat_factor = repeat_factor
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.list_IDs) / self.batch_size))
def __getitem__(self, index):
'Generate one batch of data'
# Generate indexes of the batch
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
inp_dig_ids_indexes = self.inp_dig_ids_indexes[0:self.batch_size]
# Find list of IDs
list_IDs_temp = [self.list_IDs[k] for k in indexes]
inp_dig_ids_indexes_temp = [self.inp_dig_ids[k] for k in inp_dig_ids_indexes]
# Generate data
X, y = self.__data_generation(list_IDs_temp, inp_dig_ids_indexes_temp)
return X, ydef on_epoch_end(self):
'Updates indexes after each epoch'
self.indexes = np.arange(len(self.list_IDs))
self.inp_dig_ids_indexes = np.arange(len(self.inp_dig_ids))
if self.shuffle == True:
np.random.shuffle(self.indexes)
np.random.shuffle(self.inp_dig_ids_indexes)
def __data_generation(self, list_IDs_temp, inp_dig_ids_indexes_temp):
'Generates data containing batch_size samples' # X : (n_samples, *dim, n_channels)
# Initialization
X = np.empty((self.batch_size, *self.dim, self.n_channels))
y = np.empty((self.batch_size, *self.dim, self.n_channels))
int_inp_list = []
y_label_ids = []
add_res_arr = dataY_final[list_IDs_temp] + inp_int_list[inp_dig_ids_indexes_temp]
#y_label_ids = np.argwhere(np.isin(dataY_final[self.list_IDs],add_res_arr)).flatten()
#np.random.shuffle(y_label_ids)

for digit in add_res_arr: #batch size
ind = np.argwhere(np.isin(dataY_final,digit)).flatten()
#print(digit, ind)
ind = ind[0]
y_label_ids.append(ind)
#print(add_res_arr,dataY_final[y_label_ids], (dataY_final[list_IDs_temp]), (inp_digs[inp_dig_ids_indexes_temp]))
# Generate data
for i, ID in enumerate(zip(list_IDs_temp, inp_dig_ids_indexes_temp, y_label_ids)):
# Store sample
X[i,] = dataX_final[ID[0]]#np.load('data/' + ID + '.npy')
int_inp_list.append(inp_int_list[ID[1]] )
# Store class
y[i] = dataX_final[ID[2]]
#print(y.shape, X.shape, len(int_inp_list))
return ,

Configurar imágenes y números de entrenamiento

batch_size = 8 # this should be a multiple of size of input numbers!
inp_int_list = [0,1,2,3]
repeat_factor = batch_size//len(inp_int_list)
inp_int_list_ids = np.arange(0,len(inp_int_list))
print(batch_size,'//',len(inp_int_list), '==',repeat_factor)
print("Input digits for training:",inp_int_list, inp_int_list_ids)
# Parameters
params =
img_digits_range = [0,1,2,3]trainX_ids = np.argwhere(np.isin(trainY1, img_digits_range)).flatten()print("Input image digits for training:", np.unique(trainY1[trainX_ids]) )
#trainX_ids = np.arange(0,train_norm.shape[0])
testX_ids = len(trainY1)+ np.argwhere(np.isin(testY1, img_digits_range)).flatten()inp_int_list = np.repeat(inp_int_list, repeat_factor) # expends the integer array to the size of batch size
inp_int_list_exp_ids = np.arange(0,len(inp_int_list)) # Generate ids of the expanded array
print('IDs ',inp_int_list_exp_ids, 'for the Input ',inp_int_list)
training_generator = DataGenerator(trainX_ids, inp_int_list_exp_ids, **params)
validation_generator = DataGenerator(testX_ids, inp_int_list_exp_ids, **params)
autoencoder.compile(optimizer='adam', 
loss='mse',
metrics=['mae'])
autoencoder.fit(training_generator, validation_data= validation_generator,epochs=10)
autoencoder.save(curr_dir+'img_dig_sum.h5')

Ahora necesitamos una función de predicción que tome una matriz de números como imagen y una matriz de números como enteros

from tensorflow.keras.models import load_model
def fun_predict_img(integers_as_imgs, inp_int):

img_ids = []
for i,int_img in enumerate(integers_as_imgs):
print([int_img], np.argwhere(np.isin(trainY1,[int_img])).flatten()[0] )
img_ids.append(np.argwhere(np.isin(trainY1,[int_img])).flatten()[0] )
print('Img ids used:',img_ids)
img1 = trainX1[img_ids] # Digit 1
print('Input Image data shape',img1.shape)
print('Input Int data shape', len(inp_int))
img1 = img1.reshape( img1.shape[0], img1.shape[1],img1.shape[2],1)
img1_norm = img1.astype('float32')
# normalize to range 0-1
img1_norm = img1_norm / 255.0
model = load_model(curr_dir+'img_dig_sum.h5')
op1 = autoencoder.predict([img1_norm, np.array(inp_int)]) # image, digit
for i in range(op1.shape[0]):
plt.subplot(330 + 1 + i)
plt.title(str(integers_as_imgs[i])+' + '+str(inp_int[i])+' = ')
#plt.imshow( (trainX1[img_ids[i]]), cmap=plt.get_cmap('gray') )
#plt.subplot(330 + 2 + i + 1)
plt.imshow(op1[i], cmap=plt.get_cmap('gray') )

¡Hagamos una inferencia y probemos el modelo!

fun_predict_img([1,1], [1,2])

Siéntase libre de experimentar diferentes capas ocultas en la arquitectura del modelo. Espero que encuentre esto útil y espero sus valiosas sugerencias / correcciones si las hubiere.

https://becominghuman.ai/

Fuente del artículo

Deja un comentario