{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Introduction to AI\n## Auke Schaap\n### 03/06/2020\n\n","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Welkom! \n\nBij deze een korte hands-on introductie wat je precies met Deep Learning kan. Geen probleem als je niet alles in één keer snapt, het is vrij veel informatie in één keer.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# 1. Introductie\n\n\n## 1.1 Wat gaan we doen?\nIn dit notebook bouwen we een neural network om handgeschreven cijfers te herkennen. Een neural network is een machine learning model dat geïnspireerd is door de de biologische netwerken van neuronen in hersenen. Net als mensen kunnen die netwerken 'leren' door voorbeeld taken uit te voeren. Zo hoef je dus geen regels te bedenken waar de cijfers aan moeten voldoen (zoals \"een nul is een open rondje\"). Ze leren dus door trial en error.\n\nEen neural network bestaat dus uit een aantal nodes (neuronen) die verbonden zijn met edges (synapsen). Elke node krijgt en geeft via die edges signalen door van andere nodes, net zoals neuronen signalen door krijgen en geven via synapses in de hersenen.\n\nOm de mate van invloed van zo'n edge aan te geven heeft elke edge een *weight*. Hoe hoger de weight hoe meer invloed die node heeft op het eindresultaat. Tijdens het 'leren' van het netwerk worden de weights beïnvloed. Door die (onzichtbare) interne weights leert het netwerk patronen herkennen; bepaalde neuronen geven signalen af bij bepaalde patronen. Omdat het zoveel neuronen en synapses zijn kunnen er dus heel veel patronen herkend worden, en werkt een neural netwerk heel goed voor klassificatie problemen.\n\n\n## 1.2 Importeren\nDan nu, op naar het programmeren!\n\nAls eerste moeten we even alle software libraries importen die we nodig hebben. Dit is software die andere mensen al geschreven hebben en openbaar is voor iedereen. Als je eerder met Python hebt gewerkt herken je `pandas` en `numpy` vast wel! Dit zijn twee van de bekendste libraries die voor Python geschreven zijn.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport seaborn as sns\n%matplotlib inline\n\nnp.random.seed(2)\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nimport itertools\n\nfrom keras.utils.np_utils import to_categorical # convert to one-hot-encoding\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPool2D\nfrom keras.optimizers import RMSprop\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import ReduceLROnPlateau\n\n\nsns.set(style='white', context='notebook', palette='deep')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2. Data\n\n\nZoals hierboven uitgelegd willen we een (convolutional) neural network bouwen om handschriften van mensen te leren herkennen. Onze data bestaat uit plaatjes van getallen die door mensen zijn geschreven. Elk plaatje is dus net wat anders, maar moet wel een specifiek getal voorstellen.\n\nElk plaatje heeft een formaat van 28 bij 28 pixels, dus 784 pixels in totaal. Elke pixel is een getal van 0 tot 255, wat de donkerheid weergeeft. Het is in zwart-wit, dus 0 is wit en 255 is zwart. Als we de data hebben ingeladen dan laat ik een voorbeeld zien.\n\nMaar hoe gaat ons neural network dit gebruiken om te leren? Van 42.000 plaatjes weten we gelukkig wat voor getal het moet zijn. Dit is onze *training* data, de data die we gaan gebruiken om ons netwerk te trainen. We laten het model simpelweg heel vaak raden welk getal bij welk plaatje hoort, en veranderen de weights iets als het klopt of niet klopt.\n\nAls we dit vaak genoeg gedaan hebben dan is ons network klaar, en zouden we hem in het echt kunnen gaan gebruiken. Hier is de *test* data voor, van die plaatjes weten we niet welk getal het is.\n\nEn dat kan allemaal binnen een paar minuten!","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## 2.1 Inladen\n\nWe laden de *training* en *test* data in vanuit csv bestanden. De training data heeft als eerste kolom de 'labels'. Dat zijn de getallen die dat plaatje horen te zijn. Dit is dus *training* data omdat we het antwoord op het probleem al weten.\n\nKijk maar eens hoeveel van elk getal we hebben in het grafiekje hieronder.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load the data\ntrain = pd.read_csv(\"../input/digit-recognizer/train.csv\")\ntest = pd.read_csv(\"../input/digit-recognizer/test.csv\")\n\ny_train = train[\"label\"]\nx_train = train.drop(labels=[\"label\"], axis = 1)\n\n# Free some space\ndel train\n\ng = sns.countplot(y_train)\n\ny_train.value_counts()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We hebben ongeveer evenveel data voor elk cijfer.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## 2.2 Manipulatie\n\nNu we de data hebben ingeladen moeten we het eerst manipuleren zodat het geschikt is als input. Ons model heeft straks meerdere lagen, waaronder een 'input' en een 'output' laag. We manipuleren de data zodat het bij de architectuur van onze input en output laag past. Dit doen we stap voor stap.\n\nEerst normalizeren we de data zodat de effecten van intensiteit niet zo sterk zijn. Dat is bijvoorbeeld 'hard' of 'zacht' drukken met je pen. Ook werkt ons netwerk sneller op [0...1] data dan op [0...255] data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Normalize\nx_train = x_train / 255.0\ntest = test / 255.0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.2.1 Input\n\nWe hadden al gezegd dat onze input een plaatje van 28 x 28 was, maar onze data is één lange lijst met 784 getallen. Die willen we dus veranderen naar een matrix van 28 bij 28.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"x_train = x_train.values.reshape(-1,28,28,1)\ntest = test.values.reshape(-1,28,28,1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Keras wil een extra dimensie voor de 'channels'. Dit is de kleuren informatie van die kanalen, bijvoorbeeld RGB. Keras accepteert dus ook kleuren plaatjes. Onze plaatjes zijn zwart-wit, dus onze data is daarom 28 x 28 x 1.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### 2.2.2 Output\n\nNu willen we ook nog onze output data veranderen. Onze output laag bestaat uit 10 neuronen, voor de getallen 0 tot 9. We willen dat het netwerk onze input verdeelt in één van deze klasses. Daarom komt het er zo uit te zien.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"y_train = to_categorical(y_train, num_classes = 10)\nprint(y_train[0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.2.3 Training en validation\n\nAls laatste willen we de training data splitten in twee onderdelen: training en validation sets. Zo kunnen we aan het einde de performance van ons netwerk evalueren.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Split the train and the validation set for the fitting\nx_train, x_val, y_train, y_val = train_test_split(x_train, y_train, test_size = 0.1, random_state=2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2.3 Voorbeeld\n\nZo zien de cijfers er uit. We printen ook het getal dat het zou moeten zijn.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"g = plt.imshow(x_train[0][:,:,0], cmap = \"binary\")\n\nprint(y_train[0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Hier zie je dus dat we wat hebben verandert aan de data. Er staat een `1` op plek 8, wat betekent dat dit het getal 8 hoort te zijn. Dat klopt!","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"# 3. Neural Network\n\n## 3.1 Ons model\n\nNu bouwen we ons eigen model. We hebben verschillende type lagen die ik één voor één even toelicht.\n\nDe term `Conv2D` staat voor een 2 dimensionale convolutional layer. Convolution wordt gebruikt voor feature extraction, en 2D omdat het natuurlijk een plaatje is. Dit is voor ons erg belangrijk omdat getallen en handschriften logische features hebben. Ik noemde net bijvoorbeeld al 'heeft een rondje', maar er zijn er natuurlijk veel meer. De 32 staat voor het aantal nodes in de laag, afhankelijk van wat voor features je wilt extracten.\n\n`MaxPool2D` is een pooling layer. Pooling is het downsampelen van 2D data. Met max pooling pakken we de 2x2 feature maps (die we door convolutional layers hebben gemaakt) en kiezen de max value uit. Hierdoor gooien we onbelangrijke features weg en houden we de belangrijkste van de vier over. Als we dit niet zouden doen zou ons netwerk veels te groot blijven en duurt het trainen onmogelijk lang.\n\n`Dropout` layers worden gebruikt om overfitting tegen te gaan. Een fractie van de neuronen wordt weggelaten, in dit geval een vierde deel.\n\nDe `Flatten` layer vormt een brug tussen de 2D layers en de 1D `Dense` layers.\n\nAls laatste hebben we dan de `Dense` layer. Deze verbindt simpelweg alle nodes van de vorige layer met alle nodes van de nieuwe layer. Een vrij standaard laag dus.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Sequential()\n\nmodel.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu', input_shape = (28,28,1)))\nmodel.add(Conv2D(filters = 32, kernel_size = (5,5),padding = 'Same', activation ='relu'))\nmodel.add(MaxPool2D(pool_size=(2,2)))\nmodel.add(Dropout(0.25))\n\n\nmodel.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))\nmodel.add(Conv2D(filters = 64, kernel_size = (3,3),padding = 'Same', activation ='relu'))\nmodel.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))\nmodel.add(Dropout(0.25))\n\n\nmodel.add(Flatten())\nmodel.add(Dense(256, activation = \"relu\"))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(10, activation = \"softmax\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.2 Optimizer en annealer\n\nDe optimizer bepaalt de learning rate. Dit alles is een wiskundig process wat lang duurt om toe te lichten. Je kunt het vergelijken met bijvoorbeeld het vinden van een globale of locale minima in een (2D) grafiek. Je moet een afweging maken tussen snelheid en nauwkeurigheid, en dat alles wordt hier geregeld.\n\nMet annealing wordt de learning rate langzaam kleiner over de tijd. Hierdoor heb je de winst van een snel proces, terwijl je model ook nog nauwkeurig is.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Define the optimizer\noptimizer = RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Compile the model\nmodel.compile(optimizer = optimizer , loss = \"categorical_crossentropy\", metrics=[\"accuracy\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Set a learning rate annealer\nlearning_rate_reduction = ReduceLROnPlateau(monitor='val_accuracy', \n                                            patience=3, \n                                            verbose=1, \n                                            factor=0.5, \n                                            min_lr=0.00001)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Tijdens één epoch ga je één keer over alle data heen. Meer epochs betekend een nauwkeurig model, maar het duurt wel langer om het te maken.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 6 # Turn epochs to 30 to get 0.9967 accuracy\nbatch_size = 86","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.3 Data augmentatie","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# With data augmentation to prevent overfitting (accuracy 0.99286)\n\ndatagen = ImageDataGenerator(\n        featurewise_center=False,  # set input mean to 0 over the dataset\n        samplewise_center=False,  # set each sample mean to 0\n        featurewise_std_normalization=False,  # divide inputs by std of the dataset\n        samplewise_std_normalization=False,  # divide each input by its std\n        zca_whitening=False,  # apply ZCA whitening\n        rotation_range=10,  # randomly rotate images in the range (degrees, 0 to 180)\n        zoom_range = 0.1, # Randomly zoom image \n        width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)\n        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)\n        horizontal_flip=False,  # randomly flip images\n        vertical_flip=False)  # randomly flip images\n\n\ndatagen.fit(x_train)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3.4 Trainen\n\nNa dit alles kunnen we eindelijk gaan trainen.","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# Fit the model\nhistory = model.fit_generator(datagen.flow(x_train,y_train, batch_size=batch_size),\n                              epochs = epochs, validation_data = (x_val,y_val),\n                              verbose = 2, steps_per_epoch=x_train.shape[0] // batch_size\n                              , callbacks=[learning_rate_reduction])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. Resultaat\n## 4.1 Loss en accuracy grafieken","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Plot the loss and accuracy curves for training and validation \nfig, ax = plt.subplots(2,1)\nax[0].plot(history.history['loss'], color='b', label=\"Training loss\")\nax[0].plot(history.history['val_loss'], color='r', label=\"validation loss\",axes =ax[0])\nlegend = ax[0].legend(loc='best', shadow=True)\n\nax[1].plot(history.history['accuracy'], color='b', label=\"Training accuracy\")\nax[1].plot(history.history['val_accuracy'], color='r',label=\"Validation accuracy\")\nlegend = ax[1].legend(loc='best', shadow=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 4.2 Confusion matrix","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Look at confusion matrix \n\ndef plot_confusion_matrix(cm, classes,\n                          normalize=False,\n                          title='Confusion matrix',\n                          cmap=plt.cm.Blues):\n    \"\"\"\n    This function prints and plots the confusion matrix.\n    Normalization can be applied by setting `normalize=True`.\n    \"\"\"\n    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n    plt.title(title)\n    plt.colorbar()\n    tick_marks = np.arange(len(classes))\n    plt.xticks(tick_marks, classes, rotation=45)\n    plt.yticks(tick_marks, classes)\n\n    if normalize:\n        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n\n    thresh = cm.max() / 2.\n    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n        plt.text(j, i, cm[i, j],\n                 horizontalalignment=\"center\",\n                 color=\"white\" if cm[i, j] > thresh else \"black\")\n\n    plt.tight_layout()\n    plt.ylabel('True label')\n    plt.xlabel('Predicted label')\n\n# Predict the values from the validation dataset\ny_pred = model.predict(x_val)\n# Convert predictions classes to one hot vectors \ny_pred_classes = np.argmax(y_pred,axis = 1) \n# Convert validation observations to one hot vectors\ny_true = np.argmax(y_val,axis = 1) \n# compute the confusion matrix\nconfusion_mtx = confusion_matrix(y_true, y_pred_classes) \n# plot the confusion matrix\nplot_confusion_matrix(confusion_mtx, classes = range(10)) ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 4.3 Error results","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Display some error results \n\n# Errors are difference between predicted labels and true labels\nerrors = (y_pred_classes - y_true != 0)\n\ny_pred_classes_errors = y_pred_classes[errors]\ny_pred_errors = y_pred[errors]\ny_true_errors = y_true[errors]\nx_val_errors = x_val[errors]\n\ndef display_errors(errors_index,img_errors,pred_errors, obs_errors):\n    \"\"\" This function shows 6 images with their predicted and real labels\"\"\"\n    n = 0\n    nrows = 2\n    ncols = 3\n    fig, ax = plt.subplots(nrows,ncols,sharex=True,sharey=True)\n    for row in range(nrows):\n        for col in range(ncols):\n            error = errors_index[n]\n            ax[row,col].imshow((img_errors[error]).reshape((28,28)), cmap = \"binary\")\n            ax[row,col].set_title(\"Predicted label :{}\\nTrue label :{}\".format(pred_errors[error],obs_errors[error]))\n            n += 1\n\n# Probabilities of the wrong predicted numbers\ny_pred_errors_prob = np.max(y_pred_errors,axis = 1)\n\n# Predicted probabilities of the true values in the error set\ntrue_prob_errors = np.diagonal(np.take(y_pred_errors, y_true_errors, axis=1))\n\n# Difference between the probability of the predicted label and the true label\ndelta_pred_true_errors = y_pred_errors_prob - true_prob_errors\n\n# Sorted list of the delta prob errors\nsorted_dela_errors = np.argsort(delta_pred_true_errors)\n\n# Top 6 errors \nmost_important_errors = sorted_dela_errors[-6:]\n\n# Show the top 6 errors\ndisplay_errors(most_important_errors, x_val_errors, y_pred_classes_errors, y_true_errors)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}