Le livre Learn Keras for Deep Neural Networks de Jojo Moolayil, publié chez Apress est un livre de 182 pages qui se veut une introduction au Deep Learning (apprentissage profond) en s’appuyant sur la librairie Keras.

Chapitre 1 : An introduction do Deep Learning and Keras

Le chapitre #1 : An introduction do Deep Learning and Keras satisfera les débutants dans le Deep Learning (DL). Une présentation générale des frameworks est présentée et un premier exemple très simple en Python décrit ce qu’est un logiciel développé avec Keras. Dommage qu’il n’y ait pas le code quelque part sur le Web.

Pour exécuter ce premier exemple, si vous le faites sur votre PC/Mac, alors il est préférable d’utiliser Anaconda, dans un environnement où vous avez installé TensorFlow et Keras. Sinon, utilisez GCP, et mon code disponible sur Github.

Cet exemple n’est pas commenté dans le détail dans ce chapitre. Il a juste vocation à montrer les grandes ligne d’un code Keras.

Chapitre 2 : Keras in action

L’installation de l’environnement est tout d’abord décrite. Personnellement, je pense préférable d’utiliser Anaconda ou GCP et d’installer les packages supplémentaires à partir d’Anaconda.

Ce qui est bien dans ce chapitre, c’est l’explication de ce que sont la fonction d’activation, la fonction sigmoïde, ReLU.

Par ailleurs, la description des couches de neurones est très claire (core layer, dense layer). Idem pour le dropout, la fonction de coût, les optimizers et les métriques.

Ce chapitre permet de comprendre facilement quels sont les points importants dans un réseau de neurones.

Le code décrit au § Model training est ici. Ce code n’est pa très intéressant car il s’appuie sur des données aléatoires. Il est préférable de travailler sur des données réelles, ce qui est fait en fin de ce chapitre avec le Boston House Prices dataset et qui est décrit ci-dessous.

Boston House Prices

Les données de cet exercice ont servi à une compétition Kaggle et sont disponibles sur leur site, tout comme sur celui de Keras.

La description des données est la suivante :

The Boston data frame has 506 rows and 14 columns.
This data frame contains the following columns:
crim
per capita crime rate by town.
zn
proportion of residential land zoned for lots over 25,000 sq.ft.
indus
proportion of non-retail business acres per town.
chas
Charles River dummy variable (= 1 if tract bounds river; 0 otherwise).
nox
nitrogen oxides concentration (parts per 10 million).
rm
average number of rooms per dwelling.
age
proportion of owner-occupied units built prior to 1940.
dis
weighted mean of distances to five Boston employment centres.
rad
index of accessibility to radial highways.
tax
full-value property-tax rate per \$10,000.
ptratio
pupil-teacher ratio by town.
black
1000(Bk – 0.63)^2 where Bk is the proportion of blacks by town.
lstat
lower status of the population (percent).
medv
median value of owner-occupied homes in \$1000s.

https://www.kaggle.com/c/boston-housing/overview

Le but de l’exercice est de prédire le prix moyen d’une maison à Boston, dans les années 70, connaissant les informations fournies. Nous sommes dans un cas typique de régression.

Il est utile de consulter la compétition sur Kaggle, d’autant plus qu’elle est terminée depuis longtemps et qu’on connait les performances obtenues par les concurrents.

Dans le cas présent,

The medv variable is the target variable.

medv: median value of owner-occupied homes in \$1000s.

https://www.kaggle.com/c/boston-housing/overview

La méthode d’évaluation est RMSE.

RMSE(y) = \sqrt{\sum_{i=1}^n (y_i - \hat{y}_i)^2}

# Download the data using Keras; this will need an active internet connection from keras.datasets import boston_housing (x_train, y_train), (x_test, y_test) = boston_housing.load_data()

Avec Pandas

Pour faciliter la compréhension des données, je montre aussi comment lire les données avec Pandas et les présenter avec matplotlib et seaborn.

import pandas as pd # conventional alias from sklearn.datasets import load_boston dataset = load_boston() df = pd.DataFrame(dataset.data, columns=dataset.feature_names) df['target'] = dataset.target print(df.shape)
(506, 14)
df.head()
IdCRIMZNINDUSCHASNOXRMAGEDISRADTAXPTRATIOBLSTATtarget
00.0063218.02.310.00.5386.57565.24.09001.0296.015.3396.904.9824.0
10.027310.07.070.00.4696.42178.94.96712.0242.017.8396.909.1421.6
20.027290.07.070.00.4697.18561.14.96712.0242.017.8392.834.0334.7
30.032370.02.180.00.4586.99845.86.06223.0222.018.7394.632.9433.4
40.069050.02.180.00.4587.14754.26.06223.0222.018.7396.905.3336.2

Il y a 506 enregistrements dans le fichier.

Je vous recommande d’exécuter df.describe() qui vous donnera une bonne image de vos données. medv est renommée target dans le tableau.

df.describe()

et ensuite pd.isnull(df).sum() pour savoir s’il y a des données manquantes (ce qui n’est pas le cas ici).

pd.isnull(df).sum()

Les corrélations entre les variables sont les suivantes :

import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(12,10)) sns.heatmap(df.corr().round(2),cmap='coolwarm',annot=True)

Conversion avec Pandas

Pour obtenir des DataFrames, une autre solution est de convertir les données Numpy. Par exemple :

df_x_train = pd.DataFrame.from_records(x_train) df_x_train.shape
(404, 13)

Training set, test set

Lorsque les données sont lues avec Keras, elles sont scindées automatiquement entre un Training Set et un Test set.

# Explore the data structure using basic python commands print("Type of the Dataset:",type(y_train)) print("Shape of training data :",x_train.shape) print("Shape of training labels :",y_train.shape) print("Shape of testing data :",type(x_test)) print("Shape of testing labels :",y_test.shape)

Le testing set a une taille (approximative) de 25% du training set ou dit autrement (approximativement) 20% du dataset. C’est d’ailleurs ce qu’on lit dans le code de Keras (test_split=0.2)

Type of the Dataset: <class 'numpy.ndarray'> Shape of training data : (404, 13) Shape of training labels : (404,) Shape of testing data : <class 'numpy.ndarray'> Shape of testing labels : (102,)

Si vous ne connaissez pas la différence entre Training Set, Validation Set et Test Set, rendez-vous ici.

Training Dataset: The sample of data used to fit the model.

Validation Dataset: The sample of data used to provide an unbiased evaluation of a model fit on the training dataset while tuning model hyperparameters. 

Test Dataset: The sample of data used to provide an unbiased evaluation of a final model fit on the training dataset.

https://towardsdatascience.com/train-validation-and-test-sets-72cb40cba9e7

y_train

y_train correspond à la valeur médiane des maisons occupées (en milliers de dollars).

y_train
array([15.2, 42.3, 50. , 21.1, 17.7, 18.5, 11.3, 15.6, 15.6, 14.4, 12.1, 17.9, 23.1, 19.9, 15.7, 8.8, 50. , 22.5, 24.1, 27.5, 10.9, 30.8, 32.9, 24. , 18.5, ...

Les prix sont entre 10K$ et 50K$ mais c’était les années 70 !

Imports

import numpy as np from keras.models import Sequential from keras.layers import Dense, Activation

Le modèle utilisé ici est le modèle séquentiel de Keras, un modèle relativement simple à comprendre.

Données de validation

Des données de validation sont créées (pour rappel, nous avons déjà des données de test)

# Extract the last 100 rows from the training data to create the validation datasets. x_val = x_train[300:,] y_val = y_train[300:,]

Cette manière de faire est assez étonnante car Keras permet de définir un validation_split, un float qui indique le pourcentage utilisé pour les données de validation.

Architecture

Le modèle est séquentiel. On décrit successivement les différentes couches du réseau.

#Define the model architecture model = Sequential() model.add(Dense(13, input_dim=13, kernel_initializer='normal', activation='relu')) model.add(Dense(6, kernel_initializer='normal', activation='relu')) model.add(Dense(1, kernel_initializer='normal'))

Première couche

Pour la première couche, il est impératif d’indiquer la shape (les dimensions input_dim=13) car elle ne peut pas être déduite contrairement aux autres couches suivantes pour qui c’est possible dans un modèle séquentiel.

The model needs to know what input shape it should expect. For this reason, the first layer in a Sequential model (and only the first, because following layers can do automatic shape inference) needs to receive information about its input shape. There are several possible ways to do this:
Pass an input_shape argument to the first layer. This is a shape tuple (a tuple of integers or None entries, where None indicates that any positive integer may be expected). In input_shape, the batch dimension is not included.
Some 2D layers, such as Dense, support the specification of their input shape via the argument input_dim, and some 3D temporal layers support the arguments input_dim and input_length.

https://keras.io/getting-started/sequential-model-guide/

Pourquoi avoir précisé kernel_initializer=’normal’ ? je ne sais pas, l’auteur ne l’explique pas. Je doute que ça ait une quelconque utilité ici.

La fonction d’activation est ReLu. C’est la fonction la plus commune aujourd’hui mais ce n’est pas la fonction d’activation par défaut, il est donc nécessaire de le préciser.

Deuxième couche

Rien de particulier. Pourquoi passe t-on de 13 à 6 couches et pas à 5 ou 4 ou … il n’y a pas de règles.

Toutefois, étant donné le faible nombre de données, il est préférable de limiter le nombre de couches. Ce qu’en dit F. Chollet (le créateur de Keras) :

Because so few samples are available, you’ll use a very small network with two hidden layers, each with 64 units. In general, the less training data you have, the worse overfit- ting will be, and using a small network is one way to mitigate overfitting.

Deep Learning with Python

Lui utilise, pour le même exemple : 2 couches de 64 neurones suivies d’un output à 1.

Dernière couche

Il n’y a pas d’autre option que de terminer avec un nombre. Nous sommes dans le cas d’une régression. Notez qu’il n’y a pas de fonction d’activation. Une fonction d’activation nous contraindrait sur les valeurs que peut prendre le nombre.

Compilation du modèle

# Compile model model.compile(loss='mean_squared_error', optimizer='adam',metrics=['mean_absolute_percentage_error'])

La fonction de perte est MSE, c’est celle qu’on utilise typiquement dans un problème de régression. L’auteur a choisi Adam pour optimizer. Par défaut, on utilise sgd. Quelle est l’influence de ce choix ? Difficile à dire.

Ma métrique est MAPE. Cette métrique n’a été choisie ici que pour sa vertu pédagogique. Elle donne un pourcentage sur l’erreur.

Apprentissage

# Train the model model.fit(x_train, y_train, batch_size=32, epochs=3, validation_data=(x_val,y_val))

Le nombre d’epochs est très faible (3), par contre le batch_size me semble élevé. Les données de validation ont été discutées précédemment.

Les résultats ne sont pas très bons (et c’est un euphémisme )

Train on 404 samples, validate on 104 samples Epoch 1/3 404/404 [==============================] - 0s 469us/step - loss: 574.4271 - mean_absolute_percentage_error: 98.2716 - val_loss: 652.3900 - val_mean_absolute_percentage_error: 96.5681 Epoch 2/3 404/404 [==============================] - 0s 50us/step - loss: 533.9519 - mean_absolute_percentage_error: 92.5494 - val_loss: 578.9429 - val_mean_absolute_percentage_error: 88.0311 Epoch 3/3 404/404 [==============================] - 0s 49us/step - loss: 435.1889 - mean_absolute_percentage_error: 78.1117 - val_loss: 422.8921 - val_mean_absolute_percentage_error: 67.6374

67% sur l’erreur !

Sur les données de Test

results = model.evaluate(x_test, y_test) for i in range(len(model.metrics_names)): print(model.metrics_names[i]," : ", results[i])
loss : 373.1487354951746 mean_absolute_percentage_error : 66.17471478032131

évidemment le résultat n’est pas meilleur !

En augmentant le nombre d’epochs

# Train the model model.fit(x_train, y_train, batch_size=32, epochs=30, validation_data=(x_val,y_val))
Train on 404 samples, validate on 104 samples Epoch 1/30 404/404 [==============================] - 0s 53us/step - loss: 269.7837 - mean_absolute_percentage_error: 54.8461 - val_loss: 223.8510 - val_mean_absolute_percentage_error: 43.6438 Epoch 2/30 404/404 [==============================] - 0s 52us/step - loss: 150.7172 - mean_absolute_percentage_error: 47.1239 - val_loss: 151.9747 - val_mean_absolute_percentage_error: 45.6067 Epoch 3/30 404/404 [==============================] - 0s 55us/step - loss: 138.3203 - mean_absolute_percentage_error: 51.8210 - val_loss: 143.2883 - val_mean_absolute_percentage_error: 41.8812 Epoch 4/30 404/404 [==============================] - 0s 52us/step - loss: 122.8476 - mean_absolute_percentage_error: 44.5627 - val_loss: 140.4394 - val_mean_absolute_percentage_error: 37.6512 Epoch 5/30 404/404 [==============================] - 0s 57us/step - loss: 115.1191 - mean_absolute_percentage_error: 41.3090 - val_loss: 130.3238 - val_mean_absolute_percentage_error: 36.4818 Epoch 6/30 404/404 [==============================] - 0s 53us/step - loss: 106.4456 - mean_absolute_percentage_error: 40.0573 - val_loss: 121.9103 - val_mean_absolute_percentage_error: 35.3038 Epoch 7/30 404/404 [==============================] - 0s 52us/step - loss: 99.5817 - mean_absolute_percentage_error: 38.4672 - val_loss: 114.8539 - val_mean_absolute_percentage_error: 33.8199 Epoch 8/30 404/404 [==============================] - 0s 48us/step - loss: 93.5690 - mean_absolute_percentage_error: 35.9864 - val_loss: 109.3819 - val_mean_absolute_percentage_error: 32.0800 Epoch 9/30 404/404 [==============================] - 0s 47us/step - loss: 87.5985 - mean_absolute_percentage_error: 35.6523 - val_loss: 102.2023 - val_mean_absolute_percentage_error: 31.9997 Epoch 10/30 404/404 [==============================] - 0s 51us/step - loss: 81.9733 - mean_absolute_percentage_error: 33.9350 - val_loss: 99.2321 - val_mean_absolute_percentage_error: 29.6323 Epoch 11/30 404/404 [==============================] - 0s 56us/step - loss: 78.7858 - mean_absolute_percentage_error: 30.8595 - val_loss: 96.6743 - val_mean_absolute_percentage_error: 27.7957 Epoch 12/30 404/404 [==============================] - 0s 50us/step - loss: 74.1199 - mean_absolute_percentage_error: 31.7667 - val_loss: 90.0601 - val_mean_absolute_percentage_error: 28.9264 Epoch 13/30 404/404 [==============================] - 0s 46us/step - loss: 70.4005 - mean_absolute_percentage_error: 30.3918 - val_loss: 90.0534 - val_mean_absolute_percentage_error: 25.9741 Epoch 14/30 404/404 [==============================] - 0s 51us/step - loss: 67.8720 - mean_absolute_percentage_error: 28.8515 - val_loss: 87.2296 - val_mean_absolute_percentage_error: 25.5040 Epoch 15/30 404/404 [==============================] - 0s 49us/step - loss: 65.3452 - mean_absolute_percentage_error: 27.9279 - val_loss: 86.0044 - val_mean_absolute_percentage_error: 24.6537 Epoch 16/30 404/404 [==============================] - 0s 53us/step - loss: 63.9135 - mean_absolute_percentage_error: 27.1884 - val_loss: 84.1267 - val_mean_absolute_percentage_error: 24.5333 Epoch 17/30 404/404 [==============================] - 0s 65us/step - loss: 62.4643 - mean_absolute_percentage_error: 26.7182 - val_loss: 83.9935 - val_mean_absolute_percentage_error: 23.8654 Epoch 18/30 404/404 [==============================] - 0s 53us/step - loss: 61.2815 - mean_absolute_percentage_error: 26.8983 - val_loss: 81.9291 - val_mean_absolute_percentage_error: 24.5295 Epoch 19/30 404/404 [==============================] - 0s 55us/step - loss: 60.7674 - mean_absolute_percentage_error: 27.1701 - val_loss: 81.8467 - val_mean_absolute_percentage_error: 24.0074 Epoch 20/30 404/404 [==============================] - 0s 59us/step - loss: 60.1599 - mean_absolute_percentage_error: 26.1704 - val_loss: 81.8593 - val_mean_absolute_percentage_error: 23.5683 Epoch 21/30 404/404 [==============================] - 0s 56us/step - loss: 59.6977 - mean_absolute_percentage_error: 26.5049 - val_loss: 81.1265 - val_mean_absolute_percentage_error: 23.7069 Epoch 22/30 404/404 [==============================] - 0s 47us/step - loss: 59.4475 - mean_absolute_percentage_error: 25.5855 - val_loss: 81.7738 - val_mean_absolute_percentage_error: 23.0464 Epoch 23/30 404/404 [==============================] - 0s 58us/step - loss: 58.6671 - mean_absolute_percentage_error: 25.7437 - val_loss: 80.0469 - val_mean_absolute_percentage_error: 23.9294 Epoch 24/30 404/404 [==============================] - 0s 57us/step - loss: 58.8535 - mean_absolute_percentage_error: 26.9202 - val_loss: 80.9957 - val_mean_absolute_percentage_error: 22.9954 Epoch 25/30 404/404 [==============================] - 0s 56us/step - loss: 58.4746 - mean_absolute_percentage_error: 24.8414 - val_loss: 80.6377 - val_mean_absolute_percentage_error: 22.9542 Epoch 26/30 404/404 [==============================] - 0s 61us/step - loss: 58.4065 - mean_absolute_percentage_error: 27.0826 - val_loss: 78.9771 - val_mean_absolute_percentage_error: 23.8978 Epoch 27/30 404/404 [==============================] - 0s 62us/step - loss: 57.8777 - mean_absolute_percentage_error: 25.2186 - val_loss: 79.9620 - val_mean_absolute_percentage_error: 22.8801 Epoch 28/30 404/404 [==============================] - 0s 56us/step - loss: 57.4435 - mean_absolute_percentage_error: 25.3085 - val_loss: 78.4486 - val_mean_absolute_percentage_error: 23.6313 Epoch 29/30 404/404 [==============================] - 0s 69us/step - loss: 57.1338 - mean_absolute_percentage_error: 25.4473 - val_loss: 79.1974 - val_mean_absolute_percentage_error: 22.7873 Epoch 30/30 404/404 [==============================] - 0s 55us/step - loss: 56.7096 - mean_absolute_percentage_error: 24.8387 - val_loss: 78.4174 - val_mean_absolute_percentage_error: 23.0010 <keras.callbacks.History at 0x7f57adf2fe10>

Il y a du progrès : 23%

Sur les données de Test

results = model.evaluate(x_test, y_test) for i in range(len(model.metrics_names)): print(model.metrics_names[i]," : ", results[i])
102/102 [==============================] - 0s 66us/step loss : 61.81917706657858 mean_absolute_percentage_error : 29.708916421030082

On est à 29%

Il y a de la place pour s’améliorer ! mais l’explication est claire.

F. Chollet recommande d’utiliser un K-fold pour la validation et de normaliser les données.

Chapitre 3 – Deep Neural Networks for Supervised Learning : Regression

Cette fois, le dataset est le Rossmann Store sales dataset

You are provided with historical sales data for 1,115 Rossmann stores. The task is to forecast the « Sales » column for the test set. Note that some stores in the dataset were temporarily closed for refurbishment.

https://www.kaggle.com/c/rossmann-store-sales/data

Il s’agit encore une fois d’une régression : prévoir les ventes (le CA) pour un magasin, pour un jour donné.

L’auteur nous présente les frameworks SCR et SCQ que je ne connaissais pas et que je vois ici utilisé pour la première fois dans le Machine Learning. Je ne suis pas sûr que ce soit très utile.

Etude des données

train

Le problème avec le dataset Rossman, c’est qu’il n’est pas pris en compte par Keras (ni sckit-kearn, ni Tensorflow, …). Il faut copier les données sur son disque/serveur. C’est ce que j’ai fait.

import pandas as pd df = pd.read_csv("/Users/...train.csv")

Remplacez : « /Users/… » par votre lien.

df.head()
IdStoreDayOfWeekDateSalesCustomersOpenPromoStateHolidaySchoolHoliday
01531/07/1552635551101
12531/07/1560646251101
23531/07/1583148211101
34531/07/151399514981101
45531/07/1548225591101
df.shape
(1017209, 9)

Le fichier a une taille conséquente, 1 017 209 lignes, et 9 colonnes.

Les champ sont les suivants :

Most of the fields are self-explanatory. The following are descriptions for those that aren’t.
Id – an Id that represents a (Store, Date) duple within the test set
Store – a unique Id for each store
Sales – the turnover for any given day (this is what you are predicting)
Customers – the number of customers on a given day
Open – an indicator for whether the store was open: 0 = closed, 1 = open
StateHoliday – indicates a state holiday. Normally all stores, with few exceptions, are closed on state holidays. Note that all schools are closed on public holidays and weekends. a = public holiday, b = Easter holiday, c = Christmas, 0 = None
SchoolHoliday – indicates if the (Store, Date) was affected by the closure of public schools
StoreType – differentiates between 4 different store models: a, b, c, d
Assortment – describes an assortment level: a = basic, b = extra, c = extended
CompetitionDistance – distance in meters to the nearest competitor store
CompetitionOpenSince[Month/Year] – gives the approximate year and month of the time the nearest competitor was opened
Promo – indicates whether a store is running a promo on that day
Promo2 – Promo2 is a continuing and consecutive promotion for some stores: 0 = store is not participating, 1 = store is participating
Promo2Since[Year/Week] – describes the year and calendar week when the store started participating in Promo2
PromoInterval – describes the consecutive intervals Promo2 is started, naming the months the promotion is started anew. E.g. « Feb,May,Aug,Nov » means each round starts in February, May, August, November of any given year for that store

https://www.kaggle.com/c/rossmann-store-sales/data

store

store = pd.read_csv(".../store.csv") print("Shape of the Dataset:",store.shape)

Ce fichier décrit les magasins.

Shape of the Dataset: (1115, 10)
store.head(5)
IdStoreStoreTypeAssortmentCompetitionDistanceCompetitionOpenSinceMonthCompetitionOpenSinceYearPromo2Promo2SinceWeekPromo2SinceYearPromoInterval
01ca1270.09.02008.00NaNNaNNaN
12aa570.011.02007.0113.02010.0Jan,Apr,Jul,Oct
23aa14130.012.02006.0114.02011.0Jan,Apr,Jul,Oct
34cc620.09.02009.00NaNNaNNaN
45aa29910.04.02015.00NaNNaNNaN

merge

Il nous faut travailler sur un seul ensemble de données. Nous allons fusionner les 2 fichiers précédents en un seul, grâce aux possibilités offertes par Pandas.

df_new = df.merge(store,on=["Store"], how="inner") print(df_new.shape)
(1017209, 18)
print("Distinct number of Stores :", len(df_new["Store"].unique())) print("Distinct number of Days :", len(df_new["Date"].unique())) print("Average daily sales of all stores : ",round(df_new["Sales"].mean(),2))
Distinct number of Stores : 1115 Distinct number of Days : 942 Average daily sales of all stores : 5773.82

Il y a 1115 magasins, ce qu’on savait déjà avec le nombre de lignes de store. Il y a 942 jours pour lesquels on a a des données, et le chiffre d’affaires moyen journalier pour un magasin est de 5773,82 $.

types

df_new.dtypes
DayOfWeek int64 Date object Sales int64 Customers int64 Open int64 Promo int64 StateHoliday object SchoolHoliday int64 StoreType object Assortment object CompetitionDistance float64 CompetitionOpenSinceMonth float64 CompetitionOpenSinceYear float64 Promo2 int64 Promo2SinceWeek float64 Promo2SinceYear float64 PromoInterval object dtype: object

Les données sont de types variés. Nous devons mettre un peu d’ordre dans tout ça.

Nettoyage des données

Dates

Comme souvent, lorsqu’il y a des dates, il faut les réarranger pour leur donner du sens. En effet, il est plus important de savoir qu’on a affaire à un WE qu’au 12/03/2019. Si vous utilisez fastai il y a des routines spécialisées pour cela. Ici ce n’est pas le cas.

df_new["DayOfWeek"].value_counts()
5 145845 4 145845 3 145665 2 145664 7 144730 6 144730 1 144730 Name: DayOfWeek, dtype: int64

Ce que nous voyons déjà, c’est que certains enregistrements ne sont pas datés.

# We can extract all date properties from a datetime datatype import numpy as np df_new['Date'] = pd.to_datetime(df_new['Date'], infer_datetime_format=True) df_new["Month"] = df_new["Date"].dt.month df_new["Quarter"] = df_new["Date"].dt.quarter df_new["Year"] = df_new["Date"].dt.year df_new["Day"] = df_new["Date"].dt.day df_new["Week"] = df_new["Date"].dt.week df_new["Season"] = np.where(df_new["Month"].isin([3,4,5]),"Spring", np.where(df_new["Month"].isin([6,7,8]),"Summer",np.where(df_new["Month"].isin([9,10,11]),"Fall",np.where(df_new["Month"].isin([12,1,2]),"Winter","None"))))
# Using the head command to view (only) the data and the newly engineered features print(df_new[["Date","Year","Month","Day","Week","Quarter","Season"]].head())
Date Year Month Day Week Quarter Season 0 2015-07-31 2015 7 31 31 3 Summer 1 2015-07-30 2015 7 30 31 3 Summer 2 2015-07-29 2015 7 29 31 3 Summer 3 2015-07-28 2015 7 28 31 3 Summer 4 2015-07-27 2015 7 27 31 3 Summer

Je n’insiste pas sur ces conversions qui peuvent être faites automatiquement avec certains outils. Notez que l’auteur pense nécessaire de calculer la saison.

Histogrammes

Histogramme des ventes

# Import matplotlib, python most popular data visualizing library import matplotlib.pyplot as plt %matplotlib inline # Create a histogram to study the Daily Sales for the stores plt.figure(figsize=(15,8)) plt.hist(df_new["Sales"]) plt.title("Histogramme des Ventes") plt.xlabel("bins") plt.xlabel("Fréquence") plt.show()

Autres histogrammes

# Use the histogram function provided by the Pandas object # The function returns a cross-tab histogram plot for all numeric columns in the data df_new.hist(figsize=(20,10))

Valeurs absentes

df_new.isnull().sum()/df_new.shape[0] * 100
Store 0.000000 DayOfWeek 0.000000 Date 0.000000 Sales 0.000000 Customers 0.000000 Open 0.000000 Promo 0.000000 StateHoliday 0.000000 SchoolHoliday 0.000000 StoreType 0.000000 Assortment 0.000000 CompetitionDistance 0.259730 CompetitionOpenSinceMonth 31.787764 CompetitionOpenSinceYear 31.787764 Promo2 0.000000 Promo2SinceWeek 49.943620 Promo2SinceYear 49.943620 PromoInterval 49.943620 Month 0.000000 Quarter 0.000000 Year 0.000000 Day 0.000000 Week 0.000000 Season 0.000000 dtype: float64

Comment traiter les valeurs absentes. Là aussi, fastai propose une solution technique. Ici il faut le faire soi-même. L’auteur a choisi de remplacer les valeurs absentes pas le mode (plutôt que la moyenne). Pourquoi pas !

# Replace nulls with the mode df_new["CompetitionDistance"]=df_new["CompetitionDistance"].fillna(df_new["CompetitionDistance"].mode()[0]) # Double check if we still see nulls for the column df_new["CompetitionDistance"].isnull().sum()/df_new.shape[0] * 100
0.0

Valeurs qualitatives

import seaborn as sns # Seaborn is another powerful visualization library for Python sns.set(style="whitegrid") # Create the bar plot for Average Sales across different Seasons ax = sns.barplot(x="Season", y="Sales", data=df_new)
# Create the bar plot for Average Sales across different Assortments ax = sns.barplot(x="Assortment", y="Sales", data=df_new)
# Create the bar plot for Average Sales across different Store Types ax = sns.barplot(x="StoreType", y="Sales", data=df_new)

L’auteur continue son analyse des données de façon similaire.

Data engineering

Les catégories (pour lesquelles c’est possible) sont transformées en One Hot Encoding.

print("Shape of Data:",temp.shape) print("Distinct Datatypes:",temp.dtypes.unique())
Shape of Data: (1017209, 44) Distinct Datatypes: [dtype('int64') dtype('O') dtype('float64')]

Il faut aussi modifier StateHoliday qui est de type Objet.

print(temp.columns[temp.dtypes=="object"])
Index(['StateHoliday'], dtype='object')
temp["StateHoliday"].unique()
array(['0', 'a', 'b', 'c', 0], dtype=object)

On remplace les a, b, c par 1 et le reste par 0

temp["StateHoliday"]= np.where(temp["StateHoliday"]== '0',0,1) # One last check of the data type temp.dtypes.unique()

Apprentissage /Test / Validation

Les données sont scindées en jeu d’apprentissage, de validation et de test. Il y a une petite erreur dans le code du livre. Au lieu de

from sklearn.cross_validation import train_test_split

il faut :

from sklearn.model_selection import train_test_split
from sklearn.model_selection import train_test_split # Create train and test dataset with an 80:20 split x_train, x_test, y_train, y_test = train_test_split(temp, df_new[target],test_size=0.2,random_state=2018) # Further divide training dataset into train and validation dataset with an 90:10 split x_train, x_val, y_train, y_val = train_test_split(x_train, y_train,test_size=0.1,random_state=2018) # Check the sizes of all newly created datasets print("Shape of x_train:",x_train.shape) print("Shape of x_val:",x_val.shape) print("Shape of x_test:",x_test.shape) print("Shape of y_train:",y_train.shape) print("Shape of y_val:",y_val.shape) print("Shape of y_test:",y_test.shape)
Shape of x_train: (732390, 44) Shape of x_val: (81377, 44) Shape of x_test: (203442, 44) Shape of y_train: (732390, 1) Shape of y_val: (81377, 1) Shape of y_test: (203442, 1)

Modèle

La métrique est MAE (mean absolute error)

# calculate the average score of the train dataset mean_sales = y_train.mean() print("Average Sales :",mean_sales)
Average Sales : Sales 5773.099997 dtype: float64
# Calculate the Mean Absolute Error on the test dataset print("MAE for Test Data:",abs(y_test - mean_sales).mean()[0])
MAE for Test Data: 2883.587604303215

Premier DNN

# Create Deep Neural Network Architecture from keras.models import Sequential from keras.layers import Dense, Dropout model = Sequential() model.add(Dense(150,input_dim = 44,activation="relu")) # The input_dim =44, since the width of the training data=44(refer data engg section) model.add(Dense(1,activation = "linear")) #Configure the model model.compile(optimizer='adam',loss="mean_absolute_error", metrics=["mean_absolute_error"]) #Train the model model.fit(x_train.values,y_train.values, validation_data=(x_val,y_val),epochs=10,batch_size=64)

Le modèle est très classique et fortement inspiré de l’exemple précédent.

Using TensorFlow backend. WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Colocations handled automatically by placer. WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead. Train on 732390 samples, validate on 81377 samples Epoch 1/10 732390/732390 [==============================] - 30s 41us/step - loss: 944.6557 - mean_absolute_error: 944.6557 - val_loss: 819.7478 - val_mean_absolute_error: 819.7478 Epoch 2/10 732390/732390 [==============================] - 29s 39us/step - loss: 773.2035 - mean_absolute_error: 773.2035 - val_loss: 747.8291 - val_mean_absolute_error: 747.8291 Epoch 3/10 732390/732390 [==============================] - 28s 38us/step - loss: 734.3570 - mean_absolute_error: 734.3570 - val_loss: 723.2145 - val_mean_absolute_error: 723.2145 Epoch 4/10 732390/732390 [==============================] - 27s 37us/step - loss: 718.6157 - mean_absolute_error: 718.6157 - val_loss: 710.5568 - val_mean_absolute_error: 710.5568 Epoch 5/10 732390/732390 [==============================] - 29s 39us/step - loss: 704.8254 - mean_absolute_error: 704.8254 - val_loss: 695.5905 - val_mean_absolute_error: 695.5905 Epoch 6/10 732390/732390 [==============================] - 27s 37us/step - loss: 699.6464 - mean_absolute_error: 699.6464 - val_loss: 691.4862 - val_mean_absolute_error: 691.4862 Epoch 7/10 732390/732390 [==============================] - 29s 40us/step - loss: 694.9001 - mean_absolute_error: 694.9001 - val_loss: 705.1612 - val_mean_absolute_error: 705.1612 Epoch 8/10 732390/732390 [==============================] - 29s 39us/step - loss: 692.2150 - mean_absolute_error: 692.2150 - val_loss: 690.5002 - val_mean_absolute_error: 690.5002 Epoch 9/10 732390/732390 [==============================] - 27s 37us/step - loss: 688.5974 - mean_absolute_error: 688.5974 - val_loss: 689.1487 - val_mean_absolute_error: 689.1487 Epoch 10/10 732390/732390 [==============================] - 27s 37us/step - loss: 683.1414 - mean_absolute_error: 683.1414 - val_loss: 692.4914 - val_mean_absolute_error: 692.4914 <keras.callbacks.History at 0x7f337c7a4898>

Mes résultats sont un peu différents mais somme toute assez voisins de ceux du livre.

Et sur le test dataset :

# Use the model's evaluate method to predict and evaluate the test datasets result = model.evaluate(x_test.values,y_test.values) # Print the results for i in range(len(model.metrics_names)): print("Metric ",model.metrics_names[i],":",str(round(result[i],2)))
203442/203442 [==============================] - 5s 26us/step Metric loss : 690.1 Metric mean_absolute_error : 690.1

ça colle aussi !

Amélioration du modèle

Au lieu de mean_absolute_error pour la fonction de coût, utilisons mean_squared_error et augmentons le nombre de couches cachées.

model = Sequential() model.add(Dense(150,input_dim = 44,activation="relu")) model.add(Dense(150,activation="relu")) model.add(Dense(150,activation="relu")) model.add(Dense(1,activation = "linear")) model.compile(optimizer='adam',loss="mean_squared_error",metrics=["mean_absolute_error"]) history = model.fit(x_train,y_train, validation_data=(x_val,y_val),epochs=10,batch_size=64) # result = model.evaluate(x_test,y_test) for i in range(len(model.metrics_names)): print("Metric ",model.metrics_names[i],":",str(round(result[i],2)))
Train on 732390 samples, validate on 81377 samples Epoch 1/10 732390/732390 [==============================] - 35s 48us/step - loss: 1726005.7325 - mean_absolute_error: 853.7699 - val_loss: 1116257.5049 - val_mean_absolute_error: 710.3434 Epoch 2/10 732390/732390 [==============================] - 36s 49us/step - loss: 1149926.0055 - mean_absolute_error: 718.6125 - val_loss: 1043340.8199 - val_mean_absolute_error: 682.8135 Epoch 3/10 732390/732390 [==============================] - 35s 48us/step - loss: 1078362.3809 - mean_absolute_error: 696.9257 - val_loss: 1030583.2103 - val_mean_absolute_error: 684.6889 Epoch 4/10 732390/732390 [==============================] - 35s 48us/step - loss: 1045838.6690 - mean_absolute_error: 686.2809 - val_loss: 995751.8180 - val_mean_absolute_error: 669.2394 Epoch 5/10 732390/732390 [==============================] - 38s 52us/step - loss: 1027480.0447 - mean_absolute_error: 680.0208 - val_loss: 977277.6982 - val_mean_absolute_error: 667.7428 Epoch 6/10 732390/732390 [==============================] - 35s 48us/step - loss: 998382.9407 - mean_absolute_error: 671.7496 - val_loss: 941391.8789 - val_mean_absolute_error: 660.5804 Epoch 7/10 732390/732390 [==============================] - 36s 49us/step - loss: 982592.7586 - mean_absolute_error: 666.6328 - val_loss: 945146.3742 - val_mean_absolute_error: 659.2310 Epoch 8/10 732390/732390 [==============================] - 35s 47us/step - loss: 958592.3347 - mean_absolute_error: 659.9552 - val_loss: 890162.8889 - val_mean_absolute_error: 643.0346 Epoch 9/10 732390/732390 [==============================] - 36s 49us/step - loss: 938630.9130 - mean_absolute_error: 652.7611 - val_loss: 867834.9076 - val_mean_absolute_error: 637.6027 Epoch 10/10 732390/732390 [==============================] - 35s 47us/step - loss: 920924.4739 - mean_absolute_error: 647.1866 - val_loss: 880487.3983 - val_mean_absolute_error: 633.7560 Metric loss : 690.1 Metric mean_absolute_error : 690.1

Les résultats sont meilleurs mais à mon avis pas de façon très significative.

Par la suite, l’auteur essaie différentes options.

Le dernier test se fait avec un réseau plus profond et plus d’epochs.

model = Sequential() model.add(Dense(350,input_dim = 44,activation="relu")) model.add(Dense(350,activation="relu")) model.add(Dense(350,activation="relu")) model.add(Dense(350,activation="relu")) model.add(Dense(350,activation="relu")) model.add(Dense(1,activation = "linear")) model.compile(optimizer='adam',loss="mean_squared_error",metrics=["mean_absolute_error"]) model.fit(x_train,y_train, validation_data=(x_val,y_val),epochs=15,batch_size=64,callbacks=[history]) result = model.evaluate(x_test,y_test) for i in range(len(model.metrics_names)): print("Metric ",model.metrics_names[i],":",str(round(result[i],2)))
Train on 732390 samples, validate on 81377 samples Epoch 1/15 732390/732390 [==============================] - 43s 58us/step - loss: 1668036.1558 - mean_absolute_error: 848.7682 - val_loss: 1391763.4342 - val_mean_absolute_error: 822.2929 Epoch 2/15 732390/732390 [==============================] - 43s 59us/step - loss: 1178223.9480 - mean_absolute_error: 727.8970 - val_loss: 1326927.5146 - val_mean_absolute_error: 767.8719 Epoch 3/15 732390/732390 [==============================] - 42s 57us/step - loss: 1103083.9624 - mean_absolute_error: 702.5887 - val_loss: 1103129.3224 - val_mean_absolute_error: 708.5258 Epoch 4/15 732390/732390 [==============================] - 43s 59us/step - loss: 1060283.2808 - mean_absolute_error: 688.9944 - val_loss: 1014199.5701 - val_mean_absolute_error: 679.0442 Epoch 5/15 732390/732390 [==============================] - 41s 56us/step - loss: 1038514.9163 - mean_absolute_error: 682.2779 - val_loss: 1043350.7875 - val_mean_absolute_error: 693.5402 Epoch 6/15 732390/732390 [==============================] - 44s 61us/step - loss: 1019344.1514 - mean_absolute_error: 676.8103 - val_loss: 978472.8194 - val_mean_absolute_error: 662.1369 Epoch 7/15 732390/732390 [==============================] - 41s 56us/step - loss: 999939.9721 - mean_absolute_error: 670.5147 - val_loss: 1120414.9661 - val_mean_absolute_error: 729.1646 Epoch 8/15 732390/732390 [==============================] - 43s 58us/step - loss: 976066.5027 - mean_absolute_error: 663.1325 - val_loss: 972052.0571 - val_mean_absolute_error: 674.5352 Epoch 9/15 732390/732390 [==============================] - 41s 56us/step - loss: 962188.0513 - mean_absolute_error: 659.0306 - val_loss: 884975.0631 - val_mean_absolute_error: 634.9357 Epoch 10/15 732390/732390 [==============================] - 43s 58us/step - loss: 946094.2717 - mean_absolute_error: 653.3623 - val_loss: 988205.5985 - val_mean_absolute_error: 665.5666 Epoch 11/15 732390/732390 [==============================] - 41s 56us/step - loss: 931117.2877 - mean_absolute_error: 648.1744 - val_loss: 873690.6011 - val_mean_absolute_error: 634.2235 Epoch 12/15 732390/732390 [==============================] - 42s 58us/step - loss: 912910.2298 - mean_absolute_error: 642.1214 - val_loss: 910412.7187 - val_mean_absolute_error: 639.3000 Epoch 13/15 732390/732390 [==============================] - 43s 59us/step - loss: 895185.9466 - mean_absolute_error: 635.9180 - val_loss: 838907.5059 - val_mean_absolute_error: 618.2833 Epoch 14/15 732390/732390 [==============================] - 43s 58us/step - loss: 889846.4490 - mean_absolute_error: 633.7508 - val_loss: 851292.2353 - val_mean_absolute_error: 628.7147 Epoch 15/15 732390/732390 [==============================] - 41s 56us/step - loss: 882237.3479 - mean_absolute_error: 631.4782 - val_loss: 985618.9676 - val_mean_absolute_error: 697.8313 203442/203442 [==============================] - 7s 35us/step Metric loss : 982963.56 Metric mean_absolute_error : 694.82

Je ne constate pas les performances décrites dans le livre. J’y reviendrai.

Les chapitres suivants seront présentés dans un prochain article.

(mon code et ici)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *