Contrariamente a mi intuición, la introducción de ruido aleatorio y distorsiones en mi conjunto de entrenamiento en realidad mejoró mi clasificación correcta dramáticamente.
Primero, normalicé cada dígito con respecto al tamaño y la escala. Luego hice copias distorsionadas de cada dígito de entrenamiento. La distorsión incluía agregar ruido, estirar y rotar los dígitos.
La idea se presenta aquí:
http://arxiv.org/pdf/1003.0358.pdf
- ¿Por qué los entrenamientos CNN desequilibrados afectan tanto la clasificación?
- ¿Cuáles son buenos recursos para aprender sobre la ejecución distribuida en redes neuronales profundas (MPI, allreduce, etc.)?
- Cómo pasar del desarrollo de pila completa al aprendizaje automático en el trabajo independiente
- ¿Cuál es el mejor foro de red neuronal en Internet?
- ¿Cómo podría una máquina / sistema de IA identificar una oración sarcástica?
Opencv en python hace que todo esto sea bastante fácil. Aquí hay un código de Python (probablemente malo) que puede usar para normalizar los dígitos, que sería el enfoque tradicional, y luego distorsionarlos.
### IMPORTACIONES ############################################## ######################
importar os
importar cv2
importar csv
importar numpy
matemáticas de importación
### ARGS OPERACIONALES ############################################# ##############
### MODO DEPURACIÓN: IMPRIME EL NÚMERO DE IMÁGENES ESPECIFICADAS POR BEDUG_CUTOFF
DEPURACIÓN = Falso
DEBUG_CUTOFF = 10
### MODO DE TREN
TREN = Verdadero # AGREGA ETIQUETAS A LA PRIMERA COLUMNA
NORMALIZE_ONLY = False # NO AGREGA REGISTROS DISTORTADOS
### IO LOCATION – FILE_IN AND FILE_OUT DEBE ESTAR EN DIR
DIR = ‘C: / Ruta / a / digitsData’
FILE_IN = ‘train.csv’
FILE_OUT = ‘train_augmented.csv’
### NÚMEROS MÁGICOS GLOBALES PARA IMÁGENES … LO SÉ, OK … #########################
INPUT_SIZE = (28, 28) # TAMAÑO DE LA IMAGEN DE ENTRADA, 2-TUPLE
OUT_SIZE = (27, 27) # TAMAÑO DE LA IMAGEN DE SALIDA, 2-TUPLE
NORM_SIZE = (21, 21) # TAMAÑO FINAL DE LA CAJA DE LÍMITES PARA IMÁGENES NORMALIZADAS, 2-TUPLE, <OUT_SIZE
NORM_EXPAND_SIZE = int ((27-NORM_SIZE [0]) / 2)
LARGE_SIZE = (25,25) # TAMAÑO FINAL DE LA CAJA DE LÍMITES PARA IMÁGENES AMPLIADAS, 2-TUPLE, <OUT_SIZE
LARGE_EXPAND_SIZE = int ((27-LARGE_SIZE [0]) / 2)
RAND_PERCENT = .15 # INYECCIÓN DE RUIDO, <1.0
RAND_THRESHOLD = int (OUT_SIZE [0] * OUT_SIZE [0] * RAND_PERCENT)
GRADO = 15 # GRADO DE ROTACIÓN
### MÁS COMPLICADO …
# CONVERTIR UN NÚMERO 1, 7 U OTRO SKINNY EN UN CUADRADO DURANTE LA NORMALIZACIÓN ES DUMB
# EVITE HACERLO, NO VUELVA A AJUSTAR LOS NÚMEROS CUYA DEJÓ MÁS PÍXELES SE ENCUENTRA
# EN UN ÍNDICE> = A SKINNY_THRESHOLD
SKINNY_THRESHOLD = 10
# DIFÍCIL DE VER (Y POR LO TANTO PRUEBA) LA CAJA DE LÍMITES SIN ESTO, 0-255
TO_BLACK_THRESHOLD = 50
### ESCRIBIR IMAGEN PARA GRABAR ########################################### ###########
def write_image_to_record (src, out_csv, row_label = Ninguno):
out = numpy.array (src) .flatten ()
if (row_label! = None): out = numpy.insert (out, 0, row_label)
out_csv.writerow (fuera)
### NORMALIZAR ESCALA ############################################## ###############
def normalize_scale (src, out_size = OUT_SIZE, norm_size = NORM_SIZE, \
norm_expand_size = NORM_EXPAND_SIZE, skinny_threshold = SKINNY_THRESHOLD, \
to_black_threshold = TO_BLACK_THRESHOLD):
src [src <to_black_threshold] = 0
bottom, top = numpy.min (numpy.nonzero (src) [0]), numpy.max (numpy.nonzero (src) [0])
izquierda, derecha = numpy.min (numpy.nonzero (src.T) [0]), numpy.max (numpy.nonzero (src.T) [0])
bounding_box = src [abajo: arriba + 1, izquierda: derecha + 1]
if (left> = skinny_threshold): skinny = True
más: flaco = Falso
si es delgado: devuelve cv2.resize (src, (out_size))
más:
norma = cv2.resize (bounding_box, (norm_size))
return cv2.copyMakeBorder (norm, norm_expand_size, norm_expand_size, norm_expand_size, norm_expand_size, 0)
### RUIDO DE INYECCIÓN ############################################# ##################
def inject_noise (src, out_size = OUT_SIZE, rand_threshold = RAND_THRESHOLD):
ruido = numpy.copy (src) # COPIA PROFUNDA NECESARIA
ruido [numpy.random.randint (out_size [0] -1, tamaño = rand_threshold), numpy.random.randint (out_size [0] -1, tamaño = rand_threshold)] = 0
ruido de retorno
### AMPLIAR ############################################## ######################
def enlarge (src, skinny_threshold = SKINNY_THRESHOLD, input_size = INPUT_SIZE, \
out_size = OUT_SIZE, large_size = LARGE_SIZE, large_expand_size = LARGE_EXPAND_SIZE):
bottom, top = numpy.min (numpy.nonzero (src) [0]), numpy.max (numpy.nonzero (src) [0])
izquierda, derecha = numpy.min (numpy.nonzero (src.T) [0]), numpy.max (numpy.nonzero (src.T) [0])
bounding_box = src [abajo: arriba + 1, izquierda: derecha + 1]
if (left> = skinny_threshold): skinny = True
más: flaco = Falso
si (flaco):
tall_bounding_box = src [bottom-1: top + 2, 0: input_size [0] -1]
return cv2.resize (tall_bounding_box, (out_size))
más:
large = cv2.resize (bounding_box, large_size)
return cv2.copyMakeBorder (large, large_expand_size, large_expand_size, large_expand_size, large_expand_size, 0)
### ROTAR (Y TRADUCIR) ########################################## ###########
def rotate_about_center (src, angle, scale = 1.):
w = src.shape [1]
h = src.shape [0]
rangle = numpy.deg2rad (ángulo) # ANGLE IN RADS
# CALCULAR NUEVAS DIMENSIONES DE IMAGEN
nw = (abs (numpy.sin (rangle) * h) + abs (numpy.cos (rangle) * w)) * scale
nh = (abs (numpy.cos (rangle) * h) + abs (numpy.sin (rangle) * w)) * escala
# OBTENER MATRIZ DE ROTACIÓN
rot_mat = cv2.getRotationMatrix2D ((nw * 0.5, nh * 0.5), ángulo, escala)
# CENTROS ANTIGUOS Y NUEVOS COMBINADOS CON ROTACIÓN
rot_move = numpy.dot (rot_mat, numpy.array ([(nw-w) * 0.5, (nh-h) * 0.5,0]))
# ACTUALIZAR TRADUCCIÓN
rot_mat [0,2] + = rot_move [0]
rot_mat [1,2] + = rot_move [1]
return cv2.warpAffine (src, rot_mat, (int (math.ceil (nw)), int (math.ceil (nh))), flags = cv2.INTER_LANCZOS4)
def main ():
### IO
file_in = open (DIR + ‘/’ + FILE_IN, ‘rb’)
im_in_csv = csv.reader (file_in, dialect = ‘excel’)
file_out = open (DIR + ‘/’ + FILE_OUT, ‘wb’)
im_out_csv = csv.writer (file_out, dialect = ‘excel’)
para i, fila en enumerate (im_in_csv):
si i> 0: # ROW 0 ES SOLO ETIQUETAS
imprimir ‘Procesando imagen’ + str (i) + ‘…’
### GUARDAR ETIQUETA Y LEER LA FILA EN LA IMAGEN
row_array = numpy.asarray (fila)
row_array = row_array.astype (numpy.float32)
row_label = Ninguno
si (TREN):
row_label = row_array [0]
img = numpy.reshape (row_array [1:], (INPUT_SIZE))
más:
img = numpy.reshape (row_array, (INPUT_SIZE))
#if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘raw’ + str (i) + ‘.jpg’, img)
### NORMALIZAR ESCALA
norma = normalizar_escala (img)
write_image_to_record (norma, im_out_csv, row_label)
if (DEPURACIÓN): cv2.imwrite (DIR + ‘/’ + ‘norm’ + str (i) + ‘.jpg’, norm)
if (NORMALIZE_ONLY): continuar
si (TREN):
ruido = ruido_inyectar (norma)
write_image_to_record (noise, im_out_csv, row_label)
if (DEPURACIÓN): cv2.imwrite (DIR + ‘/’ + ‘noise’ + str (i) + ‘.jpg’, noise)
grande = agrandar (norma)
#if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘large’ + str (i) + ‘.jpg’, large)
ruido_grande = agrandar (ruido)
#if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘large_noise’ + str (i) + ‘.jpg’, large_noise)
# rotar + grados
plus = cv2.resize (rotate_about_center (grande, GRADO), OUT_SIZE)
write_image_to_record (plus, im_out_csv, row_label)
if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘rotate_p’ + str (DEGREE) + ‘_’ + str (i) + ‘.jpg’, más)
plus_noise = cv2.resize (rotate_about_center (large_noise, DEGREE), OUT_SIZE)
write_image_to_record (plus_noise, im_out_csv, row_label)
if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘rotate_noise_p’ + str (DEGREE) + ‘_’ + str (i) + ‘.jpg’, plus_noise)
# rotar – grados
menos = cv2.resize (rotate_about_center (grande, -DEGREE), (OUT_SIZE))
write_image_to_record (minus, im_out_csv, row_label)
if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘rotate_m’ + str (DEGREE) + ‘_’ + str (i) + ‘.jpg’, menos)
menos ruido = cv2.resize (rotate_about_center (large_noise, -DEGREE), (OUT_SIZE))
write_image_to_record (minus_noise, im_out_csv, row_label)
if (DEBUG): cv2.imwrite (DIR + ‘/’ + ‘rotate_noise_m’ + str (DEGREE) + ‘_’ + str (i) + ‘.jpg’, minus_noise)
si (DEPURACIÓN):
if (i> = DEBUG_CUTOFF):
descanso
más: im_out_csv.writerow (fila [0: OUT_SIZE [0] * OUT_SIZE [0] +1]) # ESCRIBE ETIQUETAS DE LA FILA
file_in.close ()
file_out.close ()
imprimir ‘Listo’
if __name__ == “__main__”:
principal()