Notas Sobre Redes Neurais Convolucionais
Publicado em qua 22 abril 2020 na categoria notas • 5 min read
Há um tempo atrás ministrei um pequeno crash course para uma turma de graduação da Faculdade Senai sobre classificação de cães e gatos utilizando Redes Neurais Convolucionais. Nesse curso expliquei um pouco do básico do funcionamento de tais redes, como ela aprende e etc. Como o notebook que criei ficou minimamente decente, resolvi utilizar como post inaugural para o blog. Descobri também que criar notebooks utilizando código e teoria no mesmo lugar é uma forma bastante efetiva de se aprender ou relembrar algum assunto.
O código utilizado é baseado nesse excelente kernel do kaggle: https://www.kaggle.com/uysimty/keras-cnn-dog-or-cat-classification
Toda a teoria aplicada aqui é baseada no lendário paper do Yann Lecun: http://yann.lecun.com/exdb/publis/pdf/lecun-99.pdf
import numpy as np
from keras.preprocessing.image import ImageDataGenerator, load_img
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import random
import os
from skimage import io
import pandas as pd
Visualizando Imagens e Preprando os dados¶
#Carregando imagens
img = io.imread('./train/dog.10013.jpg')
img.shape
plt.imshow(img)
filenames = os.listdir("./train/")
categories = []
for filename in filenames:
category = filename.split('.')[0]
if category == 'dog':
categories.append(1)
else:
categories.append(0)
df = pd.DataFrame({
'filename': filenames,
'category': categories
})
df.head()
Redes Neurais Convolucionais¶
Uma CNN (Convolutional Neural Network) é um algoritmo de Deep Learning criado especialmente para se trabalhar com imagens. Esse tipo de arquitetura nos permite fazer o "encoding" de certas propriedades de imagens e reduzir grandemente o número de parâmetros para se treinar o modelo.
Uma ConvNet combina 3 ideias arquiteturais para garantir algum grau de invariância para mudança, escala e distorção em imagens. Essas ideias arquiteturais são:
- Campos receptivos locais;
- Pesos compartilhados;
- E sub-sampling espacial.
Ela é constituída de duas grandes "sub arquiteturas". Elas são:
- Extração de características;
- Classificação de características. </font>
Input¶
Os inputs de uma CNN são imagens RGB (red, green, blue). São, basicamente, matrizes carregando informações de pixels das imagens.
A função de uma ConvNet é reduzir as imagens, sem perder características importantes, de forma a facilitar o processamento e assim realizar a sua tarefa.
Camada Convolucional (Kernels)¶
Cada unidade de uma camada convolucional é conectada a uma pequena região dos dados da camada anterior, dessa forma criando **campos receptivos locais**. Com esses campos, as unidades aprendem a extrair características visuais elementares, tais como: arestas orientadas, bordas e cantos. Essas características são combinadas em camadas posteriores, gerando assim características de mais alto nível.
As unidades em uma camada são organizadas em planos aonde todas as unidades do plano compartilham o mesmo conjunto de pesos. A esses planos é dado o nome de **feature maps**. Unidades dentro de um feature map realizam a mesma operação em diferentes partes da imagem.
O funcionamento das camadas de convolução podem ser visualizadas no gif abaixo:
Uma vez que as operações de convolução são completadas, utilizamos a função de ativação.
Camada de Pooling¶
Uma característica importante desse modelo de rede neural é que a localização exata de uma característica é irrelevante para identificação do padrão na imagem. Por causa disso podemos reduzir a a resolução espacial do feature map. Isso é feito utlizando-se camadas de **sub sambpling espacial**. Através dessa camada reduzimos a sensibilidade da rede para mudanças e distorções, gerando assim uma melhor generalização de aprendizado.
Na figura abaixo podemos ver dois modos de se fazer o sub sampling de um feature map.
Camada Totalmente Conectada (Classificação)¶
Aqui uma rede neural clássica é implementada. Utilizada para classificar as features extraídas pela rede convolucional.
Os inputs são as características extraídas pelas camadas convolucionais, elas são "achatadas" e usadas como input. </font>
Código¶
FAST_RUN = False
IMAGE_WIDTH=128
IMAGE_HEIGHT=128
IMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)
IMAGE_CHANNELS=3
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, Activation, BatchNormalization
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS)))
model.add(BatchNormalization()) #normalização da distribuição de input pra cada camada
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
####### FIM EXTRACAO DE FEATURE #################
####### COMEÇA CLASSIFICACAO #############
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax')) # 2 classes de classificação
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
model.summary()
Função de Ativação Softmax¶
Vai transformar números (logits) em probabilidades que vão se somar a 1. Essa função de ativação tem como output um vetor que representa a distribuição de probabilidade de uma lista de possíveis saídas.
Funções de Perda Categorical Cross Entropy¶
É a função utilizada para avaliar uma solução candidata (um conjunto de pesos). Tipicamente, qunado treinamos redes neurais, buscamos **MINIMIZAR** o erro, ou seja, também queremos **MINIMIZAR** a função de perda.
Então, de forma geral, queremos minimizar a diferença entre a distribuição de probabilidade predita pelo modelo e a distribuição de probabilidade real.
Otimizadores¶
São as funções utlizadas para a **atualização dos pesos de uma rede neural**. Ou seja, os otimizadores são usados para tentar garantir a minimização da função de custo.
$$\theta = \theta - \eta \cdot \nabla J(\theta; \, x, \, y)\Leftrightarrow\theta = \theta - \eta \cdot \frac{\partial C}{\partial \theta}$$
$$\theta= pesos \ da \ rede$$
$$\eta= taxa \ de \ aprendizado$$
$$\partial C= funcao \ de \ custo$$
from keras.callbacks import EarlyStopping, ReduceLROnPlateau
earlystop = EarlyStopping(patience=10)
learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',
patience=2,
verbose=1,
factor=0.5,
min_lr=0.00001)
callbacks = [earlystop, learning_rate_reduction]
Separação em conjuntos de teste e treino:¶
df["category"] = df["category"].replace({0: 'cat', 1: 'dog'})
train_df, validate_df = train_test_split(df, test_size=0.20, random_state=42)
train_df = train_df.reset_index(drop=True)
validate_df = validate_df.reset_index(drop=True)
train_df.head()
total_train = train_df.shape[0]
total_validate = validate_df.shape[0]
batch_size=15
total_validate
train_datagen = ImageDataGenerator(
rotation_range=15,
rescale=1./255,
shear_range=0.1,
zoom_range=0.2,
horizontal_flip=True,
width_shift_range=0.1,
height_shift_range=0.1
)
train_generator = train_datagen.flow_from_dataframe(
train_df,
"./train",
x_col='filename',
y_col='category',
target_size=IMAGE_SIZE,
class_mode='categorical',
batch_size=batch_size
)
validation_datagen = ImageDataGenerator(rescale=1./255)
validation_generator = validation_datagen.flow_from_dataframe(
validate_df,
"./train",
x_col='filename',
y_col='category',
target_size=IMAGE_SIZE,
class_mode='categorical',
batch_size=batch_size
)
epochs=3 if FAST_RUN else 50
history = model.fit_generator(
train_generator,
epochs=epochs,
validation_data=validation_generator,
validation_steps=total_validate//batch_size,
steps_per_epoch=total_train//batch_size,
callbacks=callbacks
)