[ Nota : Para aclarar, esta pregunta se refiere a la teoría y los códigos sólo se utilizan para explicar mejor la cuestión. Esto no es de ninguna manera una programación pregunta].
En sección 5.3 de "Deep learning with python by François Chollet" se explica el proceso de utilización de una red preentrenada para el aprendizaje profundo en pequeños conjuntos de datos de imágenes. Se introducen dos enfoques diferentes para la extracción de características (utilizando únicamente la base convolucional de VGG16):
1. EXTRACCIÓN RÁPIDA DE CARACTERÍSTICAS SIN AUMENTO DE DATOS: en este enfoque, primero se extraen las características de cada imagen del conjunto de datos llamando al predict
método de la conv_base
modelo. Aquí está el código de referencia:
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
include_top=False,
input_shape=(150, 150, 3))
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
base_dir = '/Users/fchollet/Downloads/cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')
datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20
def extract_features(directory, sample_count):
features = np.zeros(shape=(sample_count, 4, 4, 512))
labels = np.zeros(shape=(sample_count))
generator = datagen.flow_from_directory(
directory,
target_size=(150, 150),
batch_size=batch_size,
class_mode='binary')
i = 0
for inputs_batch, labels_batch in generator:
features_batch = conv_base.predict(inputs_batch)
features[i * batch_size : (i + 1) * batch_size] = features_batch
labels[i * batch_size : (i + 1) * batch_size] = labels_batch
i += 1
if i * batch_size >= sample_count:
break
return features, labels
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)
A continuación, estas características se alimentarán a un clasificador densamente conectado para la clasificación que se entrena desde cero:
train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))
from keras import models
from keras import layers
from keras import optimizers
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
loss='binary_crossentropy',
metrics=['acc'])
history = model.fit(train_features, train_labels,
epochs=30,
batch_size=20,
validation_data=(validation_features, validation_labels))
2. EXTRACCIÓN DE CARACTERÍSTICAS CON AUMENTO DE DATOS: En este enfoque (que es mucho más lento), la base convolucional se amplía añadiendo un clasificador densamente conectado sobre ella y el entrenamiento se realiza de principio a fin. Sin embargo, las capas convolucionales se congelan para evitar que sus pesos se actualicen:
from keras import models
from keras import layers
model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
conv_base.trainable = False
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=['acc'])
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
Mis preguntas:
1) No entiendo la diferencia entre el primer y el segundo enfoque (con la excepción de utilizar el aumento de datos en el segundo enfoque y una capa adicional de abandono en el primer enfoque). Los pesos de la base convolucional en el segundo enfoque no se actualizan, por lo que sólo se utiliza en el pase hacia adelante. Por lo tanto, es esencialmente la misma que la base convolucional en el primer enfoque y los clasificadores son idénticos también, así que creo que deberían darnos la misma precisión (y velocidad). ¿Qué me falta?
2) Una de las cosas que más me inquieta es el hecho de que he probado ambos enfoques en mi máquina. El segundo enfoque es mucho más lento pero ambos alcanzan una precisión del 90% en los datos de validación; mientras que en el libro se sugiere que el primer y el segundo enfoque alcancen una precisión del 90% y del 96%, respectivamente, en los datos de validación. (Si los enfoques son diferentes) ¿Por qué ocurre esto?
3) En el libro se sugiere que en el primer enfoque no podríamos utilizar el aumento de datos. No me queda claro por qué es así. En particular, ¿qué nos impide utilizar un ImageDataGenerator
en el primer enfoque como el utilizado en el segundo para generar datos de entrenamiento? (Además, aunque se afirma que el segundo enfoque utiliza el aumento de datos, lo cierto es que, teniendo en cuenta el valor de batch_size
y steps_per_epoch
El número de imágenes utilizadas para el entrenamiento en ambos enfoques es el mismo, es decir, 2000).