Vous êtes sur la page 1sur 22

### START CODE HERE ###

### START CODE HERE ###

3.6 Autres pistes d'amélioration du modèle?


4 Le jeu de données CALTECH-101 (sous-ensemble)

Le jeu de données CALTECH-101 est un jeu de données de 101 catégories d'objets avec 40 à 800 images par
classe. L'objectif de l'ensemble de données est de former un modèle capable de prédire la classe cible.

La raison pour laquelle nous utilisons un sous-ensemble de l'ensemble de données est que vous pouvez
facilement suivre cet exemple et former le réseau à partir de zéro, même si vous n'avez pas de GPU.

4.1 Télécharger et désarchiver le jeu de données CALTECH-101

http://www.vision.caltech.edu/Image_Datasets/Caltech101/
Une fois désarchivé, vous devez retrouvez les 101 catégories d'images:

4.2 Importer tous les packages nécessaires

# import the necessary packages


from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import Activation
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Dense
from tensorflow.keras import backend as K

import matplotlib
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.regularizers import l2
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import cv2
import os

4.3 StridedNet

Le CNN que nous utiliserons aujourd'hui, "StridedNet" qui a trois caractéristiques importantes :

Il utilise des convolutions échelonnées(strides) plutôt que des opérations de regroupement(pooling)

pour réduire la taille du volume

La première couche CONV utilise des filtres 7×7 mais toutes les autres couches du réseau utilisent des
filtres 3×3 (similaires à VGG)

L'algorithme de distribution normale est utilisé pour initialiser tous les poids dans le réseau

(Régularisation L2)
4.3.1 Construisez une fonction build qui prend 6 paramètres:

width : Largeur de l'image en pixels.

height : La hauteur de l'image en pixels.

profondeur : Le nombre de canaux pour l'image.

classes : le nombre de classes que le modèle doit prédire.


reg : Méthode de régularisation.

init : L'initialiseur du noyau.

pour mettre en œuvre le modèle StridedNet.

Les taux de dropout pour les 4 couches de dropout sont:

dropout_1: 0.25

dropout_2: 0.25

dropout_3: 0.25
dropout_4: 0.5

def build(width, height, depth, classes, reg, init="he_normal"):

### START CODE HERE ###


# initialize the model along with the input shape to be
# "channels last" and the channels dimension itself

# our first CONV layer will learn a total of 16 filters, each


# Of which are 7x7 -- we'll then apply 2x2 strides to reduce
# the spatial dimensions of the volume

# here we stack two CONV layers on top of each other where


# each layerswill learn a total of 32 (3x3) filters

# stack two more CONV layers, keeping the size of each filter
# as 3x3 but increasing to 64 total learned filters

# fully-connected layer

# return the constructed network architecture

### END CODE HERE ###

return model
4.4 Préparation base de données

Initialiser le jeu d'étiquettes à partir du jeu de données CALTECH-101 qu'on va former notre réseau sur.

LABELS = set(["Faces", "watch", "Motorbikes", "airplanes"])


data = []
labels = []

Faces: 435 images

watch: 239 images

Motorbikes: 798 images

Airplanes: 800 images

data sera une liste pour contenir nos images sur lesquelles notre réseau sera formé.

labels sera Une liste pour contenir nos étiquettes de classe qui correspondent aux données.

Ecrivez vous un code, pour remplir nos listes de données et d'étiquettes :

Hint:

Utilisez os.listdir() pour trouver la path

Utilisez cv2.imread pour numéraliser l'image

Utilisez cv2.resize pour re-tailler l'image

Et finalement, chaque élément dans la liste date doit être une image de 96 pixels 96 pixels

### START CODE HERE ###


labelsPath = os.listdir("./ObjectCategories")
for label in labelsPath:
# extract the class label from the filename
labelpath = "./ObjectCategories/" + label
# if the label of the current image is not part of of the labels
# are interested in, then ignore the image

# A compléter

### END CODE HERE ###

4.5 Préparation de training

Conversion des données en un tableau NumPy avec chaque image mise à l'échelle dans la plage [0, 1]
et binariser nos étiquettes en « One-Hot Encoding »
### START CODE HERE ###

### END CODE HERE ###

Diviser nos données en train et test

### START CODE HERE ###

### END CODE HERE ###

Initialiser notre ImageDataGenerator pour l'augmentation des données

### START CODE HERE ###

### END CODE HERE ###

Construire le modèle avec la fonction build

### START CODE HERE ###

### END CODE HERE ###

Compiler le modèle avec avec l'optimiseur Adam et la décroissance du taux d'apprentissage, notre

forme d'entrée spécifiée, le nombre de classes et la régularisation l2.

opt = Adam(lr=1e-4, decay=1e-4 / 50)


model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=
["accuracy"])

Train le CNN par lot de 32, 50 époques.

### START CODE HERE ###

### END CODE HERE ###

Imprimer le résultat
predictions = model.predict(x=x_test, batch_size=32)
print(classification_report(y_test.argmax(axis=1), predictions.argmax(axis=1),
target_names=lb.classes_))

N = 50
plt.figure()
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["acc"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc")
plt.title("Training Loss and Accuracy on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.show()

4.6 Implémentation ResNet-50

En enspirant du chapitre 4.4 du cours, créer un modèle à partir de la structure du modèle ResNet-50,
en l'adaptant avec notre problème actuel.

4.6.1 Importer tous les packages nécessaires


import tensorflow as tf
from tensorflow import keras
from keras import Model
from tensorflow.keras.layers import Input
from tensorflow.keras.layers import ZeroPadding2D
from tensorflow.keras.layers import Add
from tensorflow.keras.layers import AveragePooling2D
from tensorflow.keras.initializers import glorot_uniform

4.6.2 Mise en œuvre du bloc d'identité

def identity_block(X, f, filters, stage, block):


### START CODE HERE ###

### END CODE HERE ###

4.6.3 Mise en œuvre du bloc convolutif

def convolutional_block(X, f, filters, stage, block, s=2):


### START CODE HERE ###

### END CODE HERE ###

4.6.4 Mise en œuvre de ResNet-50

def ResNet50(input_shape=(224, 224, 3)):


### START CODE HERE ###

### END CODE HERE ###

4.6.5 Ajouter les couhches de sorties, Fully Connected. Compiler et entraîner le modèle


Attention, le temps d'exécution va probablement dépasser 1h.
### START CODE HERE ###

### END CODE HERE ###

opt = Adam(learning_rate=1e-4, decay=1e-4 / 50)


model2.compile(loss="categorical_crossentropy", optimizer=opt, metrics=
["accuracy"])

H2 = model2.fit(x=aug.flow(x_train, y_train, batch_size=32),validation_data=


(x_test, y_test), steps_per_epoch=len(x_train) // 32, epochs=50)

Epoch 1/50
53/53 [==============================] - 115s 2s/step - loss: 1.1691 - accuracy:
0.5407 - val_loss: 1.3906 - val_accuracy: 0.1919
Epoch 2/50
53/53 [==============================] - 124s 2s/step - loss: 0.9121 - accuracy:
0.6471 - val_loss: 1.8136 - val_accuracy: 0.1919
...
...
...
Epoch 50/50
53/53 [==============================] - 119s 2s/step - loss: 0.0761 - accuracy:
0.9737 - val_loss: 0.1888 - val_accuracy: 0.9472

4.7 Conclusion
5 Rétropropagation dans les réseaux de neurones convolutifs

5.1 Introduction

Dans les cadres de Deep Learning modernes, vous n'avez qu'à implémenter la passe avant(Forward Pass), et
le cadre prend en charge la passe en arrière, de sorte que la plupart des ingénieurs d'apprentissage en
profondeur n'ont pas besoin de se soucier des détails de la passe en arrière. Le passage en arrière pour les
réseaux convolutifs est compliqué. L'objective de cette partie du séminaire est donc de vous montrer à quoi
ressemble la rétropropagation dans un réseau convolutif.

Dans le cours vous avez implémenté un réseau de neurones simple (entièrement connecté), vous avez
utilisé la rétropropagation pour calculer les dérivées par rapport au coût de mise à jour des paramètres. De
même, dans les réseaux de neurones convolutifs, vous pouvez calculer les dérivées par rapport au coût afin
de mettre à jour les paramètres. Les équations de rétropropagation ne sont pas triviales et nous ne les
avons pas dérivées en cours, mais nous les avons brièvement présentées ci-dessous.

5.2 Rétropropagation de la couche convolutive

Commençons par implémenter la rétropropagation pour une couche CONV.

Rappel des formules:

5.2.1 Calcul de dA

Voici la formule de calcul de par rapport au coût d'un certain filtre et d'un exemple d'apprentissage
donné :

Où est un filtre et est un scalaire correspondant au gradient du coût par rapport à la sortie de la
couche de conv Z à la h ième ligne et à la w ième colonne.

Dans le code, à l'intérieur des boucles for appropriées, cette formule se traduit par :

da_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :] += W[:,:,:,c] *


dZ[i,h,w,c]

5.2.2 Calcul de dW

Ci dessous est la formule pour calculer ( est la dérivée d'un filtre)

Où correspond à la tranche qui a été utilisée pour générer l'activation .


Dans le code, à l'intérieur des boucles for appropriées, cette formule se traduit par:

dW[:,:,:,c] += a_slice * dZ[i,h,w,c]

5.2.3 Calcul de db

Voici la formule de calcul de :

Dans le code, à l'intérieur des boucles for appropriées, cette formule se traduit par :

db[:,:,:,c] += dZ[i,h,w,c]

5.2.4 Implémentation de la rétropropagation de CONV

Implémentez la fonction conv_backward ci-dessous. Vous devez additionner tous les exemples
d'entraînement, les filtres, les hauteurs et les largeurs. Vous devez ensuite calculer les dérivées en utilisant
les formules 1, 2 et 3 ci-dessus.

def conv_backward(dZ, cache):


"""
Implement the backward propagation for a convolution function

Arguments:
dZ -- gradient of the cost with respect to the output of the conv layer (Z),
numpy array of shape (m, n_H, n_W, n_C)
cache -- cache of values needed for the conv_backward(), output of
conv_forward()

Returns:
dA_prev -- gradient of the cost with respect to the input of the conv layer
(A_prev),
numpy array of shape (m, n_H_prev, n_W_prev, n_C_prev)
dW -- gradient of the cost with respect to the weights of the conv layer (W)
numpy array of shape (f, f, n_C_prev, n_C)
db -- gradient of the cost with respect to the biases of the conv layer (b)
numpy array of shape (1, 1, 1, n_C)
"""

### START CODE HERE ###


# Retrieve information from "cache"
(A_prev, W, b, hparameters) = cache

# Retrieve dimensions from A_prev's shape


(m, n_H_prev, n_W_prev, n_C_prev) = A_prev.shape

# Retrieve dimensions from W's shape


(f, f, n_C_prev, n_C) = W.shape

# Retrieve information from "hparameters"


stride = hparameters["stride"]
pad = hparameters["pad"]

# Retrieve dimensions from dZ's shape


(m, n_H, n_W, n_C) = dZ.shape

# Initialize dA_prev, dW, db with the correct shapes


dA_prev = np.zeros((m, n_H_prev, n_W_prev, n_C_prev))

dW = np.zeros((f, f, n_C_prev, n_C))


db = np.zeros((1, 1, 1, n_C))

# Pad A_prev and dA_prev


A_prev_pad = zero_pad(A_prev, pad)
dA_prev_pad = zero_pad(dA_prev, pad)

for i in range(m): # loop over the training examples

# select ith training example from A_prev_pad and dA_prev_pad


a_prev_pad = A_prev_pad[i]
da_prev_pad = dA_prev_pad[i]

for h in range(n_H): # loop over vertical axis of the


output volume
for w in range(n_W): # loop over horizontal axis of the
output volume
for c in range(n_C): # loop over the channels of the
output volume

# Find the corners of the current "slice"


vert_start = h * stride

vert_end = vert_start + f
horiz_start = w * stride

horiz_end = horiz_start + f

# Use the corners to define the slice from a_prev_pad


a_slice = a_prev_pad[vert_start:vert_end,
horiz_start:horiz_end, :]

# Update gradients for the window and the filter's parameters


using the code formulas given above
da_prev_pad[vert_start:vert_end, horiz_start:horiz_end, :] +=
W[:,:,:,c] * dZ[i, h, w, c]
dW[:,:,:,c] += a_slice * dZ[i, h, w, c]
db[:,:,:,c] += dZ[i, h, w, c]
# Set the ith training example's dA_prev to the unpaded da_prev_pad (Hint:
use X[pad:-pad, pad:-pad, :])
dA_prev[i, :, :, :] = da_prev_pad[pad:-pad, pad:-pad, :]
### END CODE HERE ###

# Making sure your output shape is correct


assert(dA_prev.shape == (m, n_H_prev, n_W_prev, n_C_prev))

return dA_prev, dW, db

# où Z et cache_conv sont les deux paramètres que vous avez déini pendant le
chapitre 04 du module 04.
np.random.seed(1)
dA, dW, db = conv_backward(Z, cache_conv)
print("dA_mean =", np.mean(dA))
print("dW_mean =", np.mean(dW))
print("db_mean =", np.mean(db))

Expected Output:

dA_mean = 2.6497022199743
dW_mean = 52.66658438709006
db_mean = 5376.827563159339

5.3 Rétropropagation de la couche de Pooling

Ensuite, implémentons le passage en arrière pour la couche de pooling, en commençant par la couche MAX-
POOL. Même si une couche de pooling n'a pas de paramètres pour la mise à jour de rétropropagation, vous
devez toujours rétro-propager le dégradé à travers la couche de pooling afin de calculer les gradients pour
les couches antérieures à la couche de pooling.

5.3.1 Max pooling

Avant de sauter dans la rétropropagation de la couche de Max_pooling, vous allez créer une fonction
d'assistance appelée create_mask_from_window() qui effectue les opérations suivantes :

Comme vous pouvez le voir, cette fonction crée une matrice "masque" qui garde une trace de l'endroit où se
trouve le maximum de la matrice. Vrai (1) (true) indique la position du maximum dans X, les autres entrées
sont Faux (0) (false). Vous verrez plus tard que la passe arrière pour la Average pooling sera similaire à celle-
ci mais en utilisant un masque différent.
5.3.1.1 Implémentez create_mask_from_window().

Note: Ici, vous n'avez pas besoin de considérer les cas où il y a plusieurs maxima dans une matrice.

def create_mask_from_window(x):
"""
Creates a mask from an input matrix x, to identify the max entry of x.

Arguments:
x -- Array of shape (f, f)

Returns:
mask -- Array of the same shape as window, contains a True at the position
corresponding to the max entry of x.
"""

### START CODE HERE ### (≈1 line)


mask = x == np.max(x)
### END CODE HERE ###

return mask

np.random.seed(1)
x = np.random.randn(2,3)
mask = create_mask_from_window(x)
print('x = ', x)
print("mask = ", mask)

Expected Output:

x = [[ 1.62434536 -0.61175641 -0.52817175]


[-1.07296862 0.86540763 -2.3015387 ]]
mask = [[ True False False]
[False False False]]

5.3.2 Average pooling

Dans la Max pooling, pour chaque fenêtre d'entrée, toute "l'influence" sur la sortie provenait d'une seule
valeur d'entrée - la valeur max. Dans la Average pooling, chaque élément de la fenêtre d'entrée a une
influence égale sur la sortie. Donc, pour implémenter la rétropropagation, vous allez maintenant
implémenter une fonction d'assistance qui reflète cela.

Par exemple, si nous avons fait une average pooling dans la passe avant en utilisant un filtre 2x2, alors le
masque que vous utiliserez pour la passe arrière ressemblera à :
Cela implique que chaque position dans la matrice contribue également à la sortie car dans la passe
avant, nous avons pris une moyenne.

5.3.2.1 Implémentez la fonction ci-dessous pour distribuer également une valeur dz à travers une matrice
de forme dimensionnelle.

def distribute_value(dz, shape):


"""
Distributes the input value in the matrix of dimension shape

Arguments:
dz -- input scalar
shape -- the shape (n_H, n_W) of the output matrix for which we want to
distribute the value of dz

Returns:
a -- Array of size (n_H, n_W) for which we distributed the value of dz
"""

### START CODE HERE ###


# Retrieve dimensions from shape (≈1 line)
(n_H, n_W) = shape

# Compute the value to distribute on the matrix (≈1 line)


average = dz / (n_H * n_W)

# Create a matrix where every entry is the "average" value (≈1 line)
a = np.ones(shape) * average
### END CODE HERE ###

return a

a = distribute_value(2, (2,2))
print('distributed value =', a)

Expected Output:

distributed value = [[0.5 0.5]


[0.5 0.5]]

5.3.3 Pooling backward

Implémentez la fonction pool_backward dans les deux modes ("max" et "average"). Vous utiliserez à
nouveau 4 boucles for (itération sur les exemples d'entraînement, la hauteur, la largeur et les canaux). Vous
devez utiliser une instruction if/elif pour voir si le mode est égal à 'max' ou 'average'. S'il est égal à
"moyenne", vous devez utiliser la fonction distribu_value() que vous avez implémentée ci-dessus pour créer
une matrice de la même forme que a_slice. Sinon, le mode est égal à 'max', et vous allez créer un masque
avec create_mask_from_window() et le multiplier par la valeur correspondante de dZ.

def pool_backward(dA, cache, mode = "max"):


"""
Implements the backward pass of the pooling layer

Arguments:
dA -- gradient of cost with respect to the output of the pooling layer, same
shape as A
cache -- cache output from the forward pass of the pooling layer, contains the
layer's input and hparameters
mode -- the pooling mode you would like to use, defined as a string ("max" or
"average")

Returns:
dA_prev -- gradient of cost with respect to the input of the pooling layer,
same shape as A_prev
"""

### START CODE HERE ###

# Retrieve information from cache (≈1 line)


(A_prev, hparameters) = cache

# Retrieve hyperparameters from "hparameters" (≈2 lines)


stride = hparameters["stride"]
f = hparameters["f"]

# Retrieve dimensions from A_prev's shape and dA's shape (≈2 lines)
m, n_H_prev, n_W_prev, n_C_prev = A_prev.shape
m, n_H, n_W, n_C = dA.shape

# Initialize dA_prev with zeros (≈1 line)


dA_prev = np.zeros(A_prev.shape)

for i in range(m): # loop over the training examples


# select training example from A_prev (≈1 line)
a_prev = A_prev[i]
for h in range(n_H): # loop on the vertical axis
for w in range(n_W): # loop on the horizontal axis
for c in range(n_C): # loop over the channels (depth)
# Find the corners of the current "slice" (≈4 lines)
vert_start = h
vert_end = vert_start + f
horiz_start = w
horiz_end = horiz_start + f

# Compute the backward propagation in both modes.


if mode == "max":
# Use the corners and "c" to define the current slice from
a_prev (≈1 line)
a_prev_slice = a_prev[vert_start:vert_end,
horiz_start:horiz_end, c]
# Create the mask from a_prev_slice (≈1 line)
mask = create_mask_from_window(a_prev_slice)
# Set dA_prev to be dA_prev + (the mask multiplied by the
correct entry of dA) (≈1 line)
dA_prev[i, vert_start:vert_end, horiz_start:horiz_end, c]
+= np.multiply(mask, dA[i, h, w, c])

elif mode == "average":


# Get the value a from dA (≈1 line)
da = dA[i, h, w, c]
# Define the shape of the filter as fxf (≈1 line)
shape = (f, f)
# Distribute it to get the correct slice of dA_prev. i.e.
Add the distributed value of da. (≈1 line)
dA_prev[i, vert_start:vert_end, horiz_start:horiz_end, c]
+= distribute_value(da, shape)

### END CODE ###

# Making sure your output shape is correct


assert(dA_prev.shape == A_prev.shape)

return dA_prev

np.random.seed(1)
A_prev = np.random.randn(5, 5, 3, 2)
hparameters = {"stride" : 1, "f": 2}

dA = np.random.randn(5, 4, 2, 2)

A, cache = pool_forward(A_prev, hparameters, mode = "max")


dA_prev = pool_backward(dA, cache, mode = "max")
print("mode = max")
print('mean of dA = ', np.mean(dA))
print('dA_prev[1,1] = ', dA_prev[1,1])
print()

A, cache = pool_forward(A_prev, hparameters, mode = "average")


dA_prev = pool_backward(dA, cache, mode = "average")
print("mode = average")
print('mean of dA = ', np.mean(dA))
print('dA_prev[1,1] = ', dA_prev[1,1])

Expected Output:
mode = max
mean of dA = 0.14571390272918056
dA_prev[1,1] = [[ 0. 0. ]
[ 5.05844394 -1.68282702]
[ 0. 0. ]]

mode = average
mean of dA = 0.14571390272918056
dA_prev[1,1] = [[ 0.08485462 0.2787552 ]
[ 1.26461098 -0.25749373]
[ 1.17975636 -0.53624893]]

Vous aimerez peut-être aussi