Lab03 - Data Augmentation
Objetivos¶
- Conhecer e praticar Aumento de dados
Data Augmentation¶
O aumento de dados é uma técnica amplamente utilizada no campo do aprendizado profundo e da visão computacional para melhorar a generalização e o desempenho dos modelos de aprendizado de máquina. Essa técnica é especialmente útil em cenários onde os conjuntos de dados são limitados ou desequilibrados, pois ajuda a criar variações nos dados existentes, aumentando assim a quantidade de dados disponíveis para treinamento e reduzindo o overfitting.
O overfitting ocorre quando um modelo de aprendizado de máquina aprende padrões específicos do conjunto de dados de treinamento e não consegue generalizar adequadamente para novos dados. Isso pode levar a um desempenho ruim quando o modelo é exposto a dados não vistos anteriormente. A técnica de aumento de dados aborda esse problema criando exemplos sintéticos, aplicando transformações às imagens originais, como rotação, translação, redimensionamento e inversão. Essas transformações geram variações das imagens originais que podem ajudar o modelo a aprender características mais generalizáveis e a se tornar mais robusto a possíveis variações nos dados de entrada.
De forma geral em imagens pode ser aplicado as transformaçõs:
- Rotação
- Translação (deslocamento horizontal e vertical)
- Zoom (in e out)
- Inversão horizontal e vertical
- Ajuste de brilho e contraste
- Ruído (adicionar ruído gaussiano ou salt-and-pepper)
- Corte aleatório (Random Cropping)
- Existem mais....
Como usar Data Augmentation¶
Para demonstrar, vamos aplicar essa técnica no dataset do Cifar10.
# Carrega os dados
from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
import matplotlib.pyplot as plt
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
# Função para exibir imagens do conjunto de dados
def show_images(images, labels, n_rows=2, n_cols=5):
fig, axes = plt.subplots(n_rows, n_cols, figsize=(10, 4))
for i, ax in enumerate(axes.flat):
ax.imshow(images[i])
ax.set_title(class_names[labels[i][0]])
ax.axis('off')
plt.show()
show_images(x_train, y_train)
No Keras usamos o pacote ImageDataGenerator
.
A partir dele vamos aplicar várias transformações nas imagens do conjunto de dados durante o treinamento do modelo.
rotation_range
= 15
- Isso permitirá que as imagens sejam rotacionadas aleatoriamente em um intervalo de até ±15 graus.
width_shift_range
= 0.1
- Este parâmetro permite que as imagens sejam deslocadas horizontalmente. O valor 0.1 significa que a imagem pode ser deslocada aleatoriamente até 10% de sua largura.
height_shift_range
= 0.1
- Similar ao parâmetro anterior, mas para deslocamento vertical. As imagens podem ser deslocadas aleatoriamente até 10% de sua altura.
shear_range
= 0.1
- Este parâmetro permite que uma distorção de cisalhamento seja aplicada às imagens. Um cisalhamento é uma transformação que desliza uma parte da imagem em uma direção, enquanto a outra parte é deslizada na direção oposta. O valor 0.1 indica a intensidade do cisalhamento.
zoom_range
= 0.1
- Isso permite que as imagens sejam ampliadas ou reduzidas aleatoriamente. O valor 0.1 indica que o zoom pode variar de 0,9 (zoom out) a 1,1 (zoom in).
horizontal_flip
= True
- Isso permite que as imagens sejam espelhadas horizontalmente (ou seja, invertidas de esquerda para direita) com uma probabilidade de 50%.
fill_mode
= 'nearest'
- Durante transformações como rotação ou deslocamento, podem aparecer alguns pixels vazios na imagem. O fill_mode determina como preencher esses pixels. O valor 'nearest' significa que ele usará o valor do pixel mais próximo para preencher os pixels vazios.
rescale
= 1./255
- Este é um passo importante de pré-processamento. As imagens geralmente têm valores de pixel no intervalo [0, 255]. Este parâmetro irá reescalar esses valores para o intervalo [0, 1], dividindo cada pixel por 255. Isso é comumente feito para facilitar a convergência durante o treinamento de redes neurais.
Existem outros parametros e não é obrigatório o uso de todos são necessários, vai depender do problema que está sendo atacado.
from tensorflow.keras.preprocessing.image import ImageDataGenerator
data_gen = ImageDataGenerator(
rotation_range=15, # Rotaciona as imagens em até 15 graus
width_shift_range=0.1, # Desloca as imagens horizontalmente em até 10%
height_shift_range=0.1, # Desloca as imagens verticalmente em até 10%
shear_range=0.1, # Aplica cisalhamento
zoom_range=0.1, # Aplica zoom
horizontal_flip=True, # Inverte as imagens horizontalmente
fill_mode='nearest', # Preenche os pixels vazios após transformações
rescale=1./255) # Normaliza os valores dos pixels para o intervalo [0, 1]
desafio:¶
Avalie os atributos que podem ser usados no ImageDataGenerator para aumentar o conjunto de dados de forma eficiente
import numpy as np
def show_augmented_images(data_gen, image, label, n_rows=2, n_cols=5):
fig, axes = plt.subplots(n_rows, n_cols, figsize=(10, 4))
img_iterator = data_gen.flow(np.array([image]), np.array([label]))
for i, ax in enumerate(axes.flat):
img, lbl = next(img_iterator)
ax.imshow(img[0])
ax.set_title(class_names[lbl[0].item()])
ax.axis('off')
plt.show()
# Selecionar uma imagem do conjunto de dados
image_index = 0
image = x_train[image_index]
label = y_train[image_index]
# Exibir imagens aumentadas
show_augmented_images(data_gen, image, label)
Note nas variações criadas a partir de uma única imagem.
from keras.models import Model
from keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout
# Definição da entrada
inputs = Input(shape=(32, 32, 3))
# Bloco convolucional 1
x = Conv2D(32, (3, 3), activation='relu')(inputs)
x = MaxPooling2D(pool_size=(2, 2))(x)
# Bloco convolucional 2
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D(pool_size=(2, 2))(x)
# Flatten + densas
x = Flatten()(x)
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x)
# Saída (10 classes)
outputs = Dense(10, activation='softmax')(x)
# Criação do modelo funcional
model = Model(inputs=inputs, outputs=outputs)
# Compilação
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
# Resumo da arquitetura
model.summary()
Model: "functional"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ input_layer (InputLayer) │ (None, 32, 32, 3) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d (Conv2D) │ (None, 30, 30, 32) │ 896 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 15, 15, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 13, 13, 64) │ 18,496 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 6, 6, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 2304) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 512) │ 1,180,160 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout (Dropout) │ (None, 512) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 10) │ 5,130 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 1,204,682 (4.60 MB)
Trainable params: 1,204,682 (4.60 MB)
Non-trainable params: 0 (0.00 B)
# Definir o número de imagens aumentadas por imagem original
augmentation_factor = 5
# Criar listas vazias para armazenar as imagens e rótulos aumentados
x_train_augmented = []
y_train_augmented = []
# Aplicar a augmentação de dados
for img, lbl in zip(x_train, y_train):
x_train_augmented.append(img) # Adicionar a imagem original
y_train_augmented.append(lbl) # Adicionar o rótulo original
for _ in range(augmentation_factor - 1):
# Gerar uma imagem aumentada
augmented_img = data_gen.random_transform(img)
# Adicionar a imagem e o rótulo aumentado às listas
x_train_augmented.append(augmented_img)
y_train_augmented.append(lbl)
# Converter as listas em arrays numpy
x_train_augmented = np.array(x_train_augmented)
y_train_augmented = np.array(y_train_augmented)
# Normalizar as imagens
x_train_augmented = x_train_augmented / 255.0
Desafio¶
Comparar o tamanho dos conjuntos de dados original e aumentado
## seu código aqui....
Tamanho do conjunto de dados original: 50000 imagens Tamanho do conjunto de dados aumentado: 250000 imagens
batch_size = 64
steps_per_epoch = len(x_train) // batch_size
history = model.fit(data_gen.flow(x_train, y_train, batch_size=batch_size),
steps_per_epoch=steps_per_epoch,
epochs=20,
validation_data=(x_test / 255, y_test))
Epoch 1/20
/Users/arnaldoalvesvianajunior/cognitivecomputing/.venv/lib/python3.12/site-packages/keras/src/trainers/data_adapters/py_dataset_adapter.py:121: UserWarning: Your `PyDataset` class should call `super().__init__(**kwargs)` in its constructor. `**kwargs` can include `workers`, `use_multiprocessing`, `max_queue_size`. Do not pass these arguments to `fit()`, as they will be ignored. self._warn_if_super_not_called()
476/781 ━━━━━━━━━━━━━━━━━━━━ 15s 51ms/step - accuracy: 0.3018 - loss: 1.8932
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) Cell In[12], line 4 1 batch_size = 64 2 steps_per_epoch = len(x_train) // batch_size ----> 4 history = model.fit(data_gen.flow(x_train, y_train, batch_size=batch_size), 5 steps_per_epoch=steps_per_epoch, 6 epochs=20, 7 validation_data=(x_test / 255, y_test)) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/keras/src/utils/traceback_utils.py:117, in filter_traceback.<locals>.error_handler(*args, **kwargs) 115 filtered_tb = None 116 try: --> 117 return fn(*args, **kwargs) 118 except Exception as e: 119 filtered_tb = _process_traceback_frames(e.__traceback__) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py:377, in TensorFlowTrainer.fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq) 375 for begin_step, end_step, iterator in epoch_iterator: 376 callbacks.on_train_batch_begin(begin_step) --> 377 logs = self.train_function(iterator) 378 callbacks.on_train_batch_end(end_step, logs) 379 if self.stop_training: File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/keras/src/backend/tensorflow/trainer.py:220, in TensorFlowTrainer._make_function.<locals>.function(iterator) 216 def function(iterator): 217 if isinstance( 218 iterator, (tf.data.Iterator, tf.distribute.DistributedIterator) 219 ): --> 220 opt_outputs = multi_step_on_iterator(iterator) 221 if not opt_outputs.has_value(): 222 raise StopIteration File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/util/traceback_utils.py:150, in filter_traceback.<locals>.error_handler(*args, **kwargs) 148 filtered_tb = None 149 try: --> 150 return fn(*args, **kwargs) 151 except Exception as e: 152 filtered_tb = _process_traceback_frames(e.__traceback__) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/polymorphic_function.py:833, in Function.__call__(self, *args, **kwds) 830 compiler = "xla" if self._jit_compile else "nonXla" 832 with OptionalXlaContext(self._jit_compile): --> 833 result = self._call(*args, **kwds) 835 new_tracing_count = self.experimental_get_tracing_count() 836 without_tracing = (tracing_count == new_tracing_count) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/polymorphic_function.py:878, in Function._call(self, *args, **kwds) 875 self._lock.release() 876 # In this case we have not created variables on the first call. So we can 877 # run the first trace but we should fail if variables are created. --> 878 results = tracing_compilation.call_function( 879 args, kwds, self._variable_creation_config 880 ) 881 if self._created_variables: 882 raise ValueError("Creating variables on a non-first call to a function" 883 " decorated with tf.function.") File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/tracing_compilation.py:139, in call_function(args, kwargs, tracing_options) 137 bound_args = function.function_type.bind(*args, **kwargs) 138 flat_inputs = function.function_type.unpack_inputs(bound_args) --> 139 return function._call_flat( # pylint: disable=protected-access 140 flat_inputs, captured_inputs=function.captured_inputs 141 ) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/concrete_function.py:1322, in ConcreteFunction._call_flat(self, tensor_inputs, captured_inputs) 1318 possible_gradient_type = gradients_util.PossibleTapeGradientTypes(args) 1319 if (possible_gradient_type == gradients_util.POSSIBLE_GRADIENT_TYPES_NONE 1320 and executing_eagerly): 1321 # No tape is watching; skip to running the function. -> 1322 return self._inference_function.call_preflattened(args) 1323 forward_backward = self._select_forward_and_backward_functions( 1324 args, 1325 possible_gradient_type, 1326 executing_eagerly) 1327 forward_function, args_with_tangents = forward_backward.forward() File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/atomic_function.py:216, in AtomicFunction.call_preflattened(self, args) 214 def call_preflattened(self, args: Sequence[core.Tensor]) -> Any: 215 """Calls with flattened tensor inputs and returns the structured output.""" --> 216 flat_outputs = self.call_flat(*args) 217 return self.function_type.pack_output(flat_outputs) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/polymorphic_function/atomic_function.py:251, in AtomicFunction.call_flat(self, *args) 249 with record.stop_recording(): 250 if self._bound_context.executing_eagerly(): --> 251 outputs = self._bound_context.call_function( 252 self.name, 253 list(args), 254 len(self.function_type.flat_outputs), 255 ) 256 else: 257 outputs = make_call_op_in_graph( 258 self, 259 list(args), 260 self._bound_context.function_call_options.as_attrs(), 261 ) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/context.py:1688, in Context.call_function(self, name, tensor_inputs, num_outputs) 1686 cancellation_context = cancellation.context() 1687 if cancellation_context is None: -> 1688 outputs = execute.execute( 1689 name.decode("utf-8"), 1690 num_outputs=num_outputs, 1691 inputs=tensor_inputs, 1692 attrs=attrs, 1693 ctx=self, 1694 ) 1695 else: 1696 outputs = execute.execute_with_cancellation( 1697 name.decode("utf-8"), 1698 num_outputs=num_outputs, (...) 1702 cancellation_manager=cancellation_context, 1703 ) File ~/cognitivecomputing/.venv/lib/python3.12/site-packages/tensorflow/python/eager/execute.py:53, in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name) 51 try: 52 ctx.ensure_initialized() ---> 53 tensors = pywrap_tfe.TFE_Py_Execute(ctx._handle, device_name, op_name, 54 inputs, attrs, num_outputs) 55 except core._NotOkStatusException as e: 56 if name is not None: KeyboardInterrupt:
Desafio:¶
Aumente o conjunto de dados de treinamento em 50% usando data augmentation para o treinamento
## seu código aqui....