Una CNN in Keras per il riconoscimento di numeri manoscritti.

Schema Rete Neurale Convoluzionale

Le reti neurali convoluzionali (CNN) sono una particolare categoria di sistemi per il Machine Learning impiegati principalmente per la computer vision e per la speech recognition. Creare un modello di CNN in Python con Keras è abbastanza semplice, in questo articolo costruiremo un classificatore per riconoscere i caratteri numerici manoscritti, avvalendoci di un dataset facilmente reperibile online (MNIST).

Andiamo a creare la classe MNIST_CNN che ci consentirà di processare il dataset, creare il modello, farne il training e poi salvarlo/caricarlo da file. Iniziamo però specificando quale backend usare con keras:

# Set theano as backend
import os
os.environ['KERAS_BACKEND'] = 'theano'
from keras import backend as K
K.set_image_dim_ordering('th')

Successivamente importiamo tutte le librerie necessarie:

import numpy as np
np.random.seed(123)  # for reproducibility

# Import Keras libs
import keras
from keras.models import Sequential, load_model
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils

# Import Dataset
from keras.datasets import mnist

Ora possiamo creare i metodi della nostra classe, il costruttore si occuperà del preprocessing dei dati che useremo per il training e la valutazione del modello:

 def __init__(self):
        ''' Prepare data for the model '''
        
        # Load Data
        print('Date preprocessing . . .')
        (self.X_train, self.y_train), (self.X_test, self.y_test) = mnist.load_data()

        # Reshape data
        self.X_train = self.X_train.reshape(self.X_train.shape[0], 1, 28, 28)
        self.X_test = self.X_test.reshape(self.X_test.shape[0], 1, 28, 28)

        # Convert data type as float32
        self.X_train = self.X_train.astype('float32')
        self.X_test = self.X_test.astype('float32')

        # Normalization (Data in range [0,1])
        self.X_train /= 255
        self.X_test /= 255

        # Convert data to 10-dimensional matrice
        self.Y_train = np_utils.to_categorical(self.y_train, 10)
        self.Y_test = np_utils.to_categorical(self.y_test, 10)

Il metodo fitModel() gestirà la creazione del modello e il relativo training. Al termine salveremo sia il modello che i “pesi” su un file, così da poterlo caricare e utilizzare immediatamente per le elaborazioni successive:

def fitModel(self):
        ''' Model definition and training '''
        
        ## Setup the sequential model
        self.model = Sequential()

        # Declare input layer
        self.model.add(Conv2D(32, kernel_size=(3, 3),activation='relu',input_shape=(1,28,28)))
        self.model.add(Conv2D(64, (3, 3), activation='relu'))
        self.model.add(MaxPooling2D(pool_size=(2, 2)))
        self.model.add(Dropout(0.25))
        self.model.add(Flatten())
        self.model.add(Dense(128, activation='relu'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(10, activation='softmax'))

        # Compile model
        print("Model compilation . . .")
        self.model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.Adadelta(), metrics=['accuracy'])

        # Fit model
        print('Model fitting . . .')
        self.model.fit(self.X_train, self.Y_train, batch_size=128, epochs=12, verbose=1, validation_data=(self.X_test, self.Y_test))

        # Save fitted model
        self.model.save('MNIST_CNN.h5')

Nel caso abbiamo già un modello addestrato (nel repository GitHub trovi anche il file con modello e pesi), servirà un metodo per caricarlo. Eccolo:

def loadModel(self, model_file):
        ''' Load model and weights from file '''
        
        print('Load model from file: {}'.format(model_file))
        self.model = load_model(model_file)

L’ultimo metodo necessario è quello che valuta le performance del modello caricato:

def evaluate(self):
        ''' Evaluate the model accuracy with a test set '''
        
        print('Evaluate test set images')
        score = self.model.evaluate(self.X_test, self.Y_test, verbose=0)
        print('Test loss:', score[0])
        print('Test accuracy:', score[1])

Adesso abbiamo tutto quello che serve. Alla fine del file possiamo anche aggiungere qualche riga di codice per testare la nostra classe senza importarla altrove:

# MNIST CNN CLASS TESTS
if __name__ == "__main__":
    print("Test MNIST CNN CLASS")
    cnn = MNIST_CNN()
    cnn.loadModel('MNIST_CNN.h5')
    cnn.evaluate()

Per utilizzare questo modello ti consiglio di utilizzare un ambiente virtuale di Python3 creato per lo scopo. Per crearlo e installare i pacchetti necessari esegui queste istruzioni da console per unix (su Windows sono comunque molto simili):

python3 -m venv venv
source venv/bin/activate
pip3 install keras theano
python3 MNIST_CNN.py

Il codice completo e il file con la CNN già addestrata è disponibile su questo repository GitHub.