Récents articles

loss function perte cout fonction dice jaccard iou binary categorical cross weighted entropy Cours théoriques - Deep learning

Fonctions de perte/coût (loss function)

Distribution-based Loss

Binary Cross-Entropy

Fonction la plus connue, définissant comme mesure de la différence entre deux probabilité de distribution pour une variable prise aléatoirement.

Quand l’utiliser ? 

Utilisé pour de la classification binaire, ou classification multi-classes ou l’on souhaite plusieurs label en sortie. Utilisable aussi pour de la segmentation sémantique. Fonction basé sur la distribution de Bernoulli.

Référence de base à utiliser comme première fonction pour tout nouveau réseau. Non adapté à des datasets biaisé/déséquilibré.

 

Categorical Cross-Entropy

Utilisé pour de la classification multi-classes, mais en souhaitant un seul label en sortie.

Quand l’utiliser ? 

Pour de la classification multi-class à simple label de sortie.

 

Weighted Cross-Entropy

C’est une variante de la Binary Cross-Entropy. Elle permet de rectifier la faiblesse de la BCE pour les datasets déséquilibrés, en ajoutant des poids selon des coefficients, pour les exemples positifs.

Quand l’utiliser ? 

Pour des datasets biaisé/déséquilibré.

 

Balenced Cross-Entropy

Elle est similaire à la Weighted Cross-Entropy.  A la différence prêt, qu’elle pénalise en plus des exemples positives, les exemples négatifs

Quand l’utiliser ? 

Pour des datasets biaisé/déséquilibré.

 

Focal 

Elle est similaire à la Binary Cross-Entropy. Elle excelle dans les datasets déséquilibrés pour la raison qu’elle diminue le poids lors de la prédiction d’exemples facile, pour permettre au réseau de se focaliser pour l’apprentissage des exemples complexes.

Quand l’utiliser ? 

Pour des datasets TRES biaisé/déséquilibré.

 

Region-based Loss

Pixel-Wise Cross-Entropy

Fonction la plus connue pour des tâches d’image segmentation. Celle-ci examine chaque pixel de façon individuelle. On compare la classe prédite du pixel par notre réseau à notre One Hot encoding de notre masque original.

Etant donnée que le calcul se fait selon la moyenne des pixels, on suppose d’avoir des classes en répartition égales sur nos images. Peut donc poser des problèmes si on a des classes non équilibré. Par exemple pour un algo ou l’on souhaite détecter piétons, ciel et route dans des photos, on sait déjà d’avance que les classes ciel et route seront bien plus présente que les piétons. Résultant ainsi dans un réseau avec des prédisposition à détecter d’avantage les classes les plus présentes.

Quand l’utiliser ? 

Référence de base à utiliser comme première fonction pour tout nouveau réseau de segmentation d’image. Non adapté à des datasets biaisé/déséquilibré.

 

Dice 

Inspiré des coefficients Dice, métriques utilisé pour évaluer la performance d’un réseau de segmentation sémantique d’image en mesurant le chevauchement entre deux objets.

Quand l’utiliser ? 

Très apprécié pour des problématiques de segmentation d’image.

 

Soft Dice

Calcul la probabilité de chaque classe de façon indépendante, pour ensuite donner un score final résultant d’une moyenne.

 

Tversky 

Similaire à la Dice. Elle ajoute un poids aux éléments FP (Faux Positif) et FN (Faux Négatif).

Quand l’utiliser ? 

Très apprécié pour des problématiques de segmentation d’image.

 

Focal Tversky

Mélange de la Focal, qui focus l’entrainement sur les exemples complexe en diminuant le poids des exemples simples, comme par exemple pour les petits ROI (Region of Interest) avec la Tversky.

Quand l’utiliser ? 

Très apprécié pour des problématiques de segmentation d’image.

 

BCE-Dice 

Mélange entre la Dice et la Binary Cross-Entropy.

Quand l’utiliser ? 

Très apprécié pour des problématiques de segmentation d’image.

 

Jaccard / Intersection Over Union (IoU)

Inspiré des coefficients IoU ou communement appelé index de Jaccard, métriques utilisé pour évaluer la performance d’un réseau de segmentation sémantique d’image.

Quand l’utiliser ? 

Très apprécié pour des problématiques de segmentation d’image.

 

 

image semantic segmentation keras tensorflow Cours pratiques - Deep learning

Segmentation sémantique d’images

Introduction & code source Github

Je vous met ici l’ensemble du code source sur mon dépôt Github, avec un exemple concret de segmentation sémantique d’images sur la reconnaissance de carries sur des radiographies dentaire.

 

Que-ce que la segmentation sémantique ?

Ce type de segmentation consiste à classifier chaque pixel d’une image en un label. Plus d’informations ici.

 

Types de vos données

En entrée, nous allons envoyer à un réseau de neurones en entrée des images RGB. Plus d’informations sur la composition d’une image ici. Mais de façon concise, notre image RGB à la forme d’un tenseur d’ordre 3 et de dimensions ( hauteur x largeur x 3 ). Le 3 est le nombre de canal. 1 pour une image noir/blanc, 3 pour RGB. Un canal pour le rouge, un pour le vert, et un dernier pour le bleu. Chaque valeur de la matrice représente donc un pixel. Ce pixel qui à une valeur entre 0 et 255, selon l’intensité de la couleur d’un canal spécifique.

En sortie, on souhaite avoir une matrice de dimensions (hauteur x largeur  x 1), avec chaque pixel ayant n’ont pas une intensité entre 0 et 255, mais un nombre correspondant à nos classes. La valeur du pixel est donc compris entre 0 (indice du background) à n (nombre de classe dans nos images).

Une image sera plus parlante :

semantic segmentation image keras tensorflow

 

Dataset X et Y

Pour réaliser ce genre de classification on utilise de l’entrainement supervisé. Cela consiste à entrainer notre réseau de neurones sur des couples (X, Y). X est l’image à inspecter, Y son étiquette et ce que l’on cherche à avoir. Cela correspond donc à nos masques contenant nos classes à détecter.

 

Labeliser les données

Pour chacune de vos images, vous aller devoir, afin de générer les masques par la suite, les labéliser, les annoter. Via des logiciels spécialisés (VoTT, SuperAnnotate, LabelMe, etc), vous allez pouvoir segmenter vos classes au sein de vos images. Voici un Example de segmentation de Carrie sur des radiographies :

image segmentation tools vott super annotate

Vous aurez la possibilités de faire des carrés, des cubes, des polygones en tout genre. Cela a pour but de générer un fichier JSON. Celui-ci contiendra pour chaque photos un ensemble de coordonnées, de points (X,Y) correspondant aux formes géométriques que vous aurez dessiné sur le logiciel.

segmentation image labeling

 

Génération des masques

Vous devez accordez la configuration de vos masques selon ce que vous souhaitez avoir en sortie du réseau. Vous allez utiliser une fonction d’activation spécifique afin de faire de la prédiction d’une valeur, ou utiliser une autre fonction d’activation pour de la prédiction multi classes. Mais vous pouvez très bien utiliser l’une ou l’autre selon comment vous agencez vos données. On va voir cela dans les prochaines lignes.

Ces différences de configuration de masque concerne la fonction d’activation de la couche final de votre réseau. Et selon elle, vous devez accorder vos métriques et fonction de perte.

 

Génération du masque

Pour générer vos masques, vous allez devoir parcourir l’ensemble de vos radiographies une à une avec le fichier JSON de coordonnées qui lui est associé. Vous allez pouvoir tracer des masques (polygones) via plusieurs procédés. Le but est de remplir des matrices Numpy, avec la classe souhaités contenu dans chaque pixel.

Pour vous donnez des idées de librairies pouvant le faire facilement :

  • Skimage via Polygon2Mask
  • Pillow (PIL) via ImageDraw
  • Scipy
  • OpenCV
  • Matplotlib via points_inside_poly

A FAIRE /// Image toshop photo original input -> masque généré binaire

Exemple d’un masque crée via Skimage :

image segmentation mask polygon 2d numpy

 

Integer ou One hot encoding ?

Comment définir nos classes pour que notre réseau apprennent à les reconnaîtrais ? En effet, on ne peut pas envoyer des mots sous forme de chaine de caractères. Comment donc définir nos classes dans nos matrices ?

 

Integer encoding consiste à donner un entier, un nombre unique qui sert d’identifiant unique pour chaque classe. Pour un exemple avec 3 classes différentes, on peut définir comme ci :

  • Chat, 0
  • Chien, 1
  • Lapin, 2

Dans notre exemple, chaque pixel aura donc une valeur entre 0 et n, correspondant au nombre de classe total présent au sein de notre dataset.

 

One hot encoding consiste à définir un ensemble de colonne selon le nombre de classe possible. Chaque colonne représente une seule classe. Seul la colonne représentant la classe aura comme valeur 1. Les autres colonnes auront comme valeur 0. Si on prends pour trois classe (chat, chien, lapin) on aura un vecteur de la forme suivante :

[probabilité chat, probabilité chien, probabilité lapin], soit [0, 0, 1] par exemple, si on a un lapin à prédire.

Dans notre exemple, chaque pixel aura donc comme valeur un tenseur d’ordre 1, un vecteur. Celui ci sera de taille n, correspondant au nombre de classe total présent au sein de notre dataset.

 

integer labeling vs one hot encoding

 

Pour résumer, si vos données ont des relations entre elles, privilégiez l’integer encoding. Dans le cas contraire ou vos données n’ont pas de relation, privilégiez le one hot encoding.

A FAIRE

 

Préparation des données pour Softmax

Softmax permet de définir une probabilité dans un ensemble de valeurs.

La métrique la plus commune de suivi sera accuracy.

La fonction la plus commune de perte  sera categorical_cross_entropy.

softmax activation function

 

Vous aurez donc des masques de dimensions (Hauteur, Largeur, 1).

Pour les sauvegarder, privilégiez les formats PNG ou TIFF. Car JPG est un format comprimé et vous allez perdre de l’information voir pire avoir des changements de classe. TIFF est un format sans perte, un peu lourd. PNG est un format compressé mais sans perte, donc vous pouvez le choisir sans soucis.

 

De façon concrète vous aurez ceci en sortie de prédictions :

semantic segmentation image keras tensorflow

Préparation des données pour Sigmoid

Sigmoid donne une probabilité entre 0 et 1 pour une seule et unique valeur.

La métrique la plus commune de suivi sera binary_accuracy.

La fonction la plus commune de perte  sera binary_cross_entropy.

sigmoid activation function

 

Vous aurez donc des masques de dimensions ( Hauteur, Largeur, Canal), avec Canal= nombre de classe à identifier. Pensez à ajouter une classe supplémentaire étant le background. En effet elle servira de classe ‘poubelle’ si on ne rencontre aucun de nos classes.

Pour les sauvegarder étant donnée que l’on aura N canal, on ne peut utiliser le TIFF ou PNG car ils ont (je fais un raccourcie et une généralité car c’est plus complexe que cela) que 4 channels (rouge, vert, bleu alpha pour la transparence). Donc vous pourrez les sauvegarder directement sous leur format matricielle Numpy (.NPY).

 

De façon concrète vous aurez ceci en sortie de prédictions :

semantic segmentation image keras tensorflow

 

Réaliser une prédiction

Masque pour classe unique

Etant donnée que l’on souhaite prédire une seule classe, notre masque sera binaire et ne contiendra que des 0 (classe background ou poubelle) ou 1 (notre classe à détecter).

Masque pour multi-classes

Si l’on souhaite prédire plusieurs classes, nous

 

Fonction de perte plus adaptés

Les fonctions de perte de type CrossEntropy sont les fonctions basique pour tout problème de classification.

Alors pourquoi ne pas garder nos fonctions de pertes cités plus haut si elles font le boulot ? Car celles-ci évaluent pour chaque pixel de façon individuelle, pour en faire une moyenne sur l’ensemble des pixels. Etant donnée que l’on va travailler avec des dataset déséquilibrés, nous pouvoir avoir des fonctions qui sont d’avantages étudiés pour ces problématique ci. En effet en gardant ces fonctions de base, on pourrait alors avoirs des chances d’avoir des prédictions penchant vers les classes les plus présente au sein de nos images, car basé sur leurs distributions.

Si vous souhaitez néanmoins rester sur une fonction de type CrossEntropy, sachez qu’il en existe des variantes. En effet, vous pouvez attribuer des poids différents pour chacune de vos classes, selon leur plus ou moins grande présence aux seins de vos images.

Mais nous allons passer en revu certaines autres fonctions de pertes qui peuvent s’avérer bien plus efficaces pour de la segmentation.

 

Dice loss

IoU loss

Pixel-wise cross entropy

 

 

 

dataset loader custom keras tensorflow batch Cours pratiques - Deep learning

Réaliser son propre générateur de données

Pourquoi faire son custom loader de données ?

Nous avons vu dans l’article Charger & entrainer le réseau sur des images : fit() vs fit_generator(), qu’il est préférable d’utiliser un générateur pour charger l’ensemble de ses données à son réseau de neurones via des mini-lots de données, par rapport à envoyer le dataset dans sa globalité via un fichier (numpy par exemple).

Vous pouvez gagner du temps en utilisant les générateurs intégré à Keras :

  • flow : charge des données via une variable
  • flow_from_dataframe : charge des données via un dataframe Pandas
  • flow_from_directory : charge des données via un dossier spécifique sur l’ordinateur

 

Vous répondre à une problématique simple, ils feront largement l’affaire. Mais ils vous imposent certaines choses :

  • Train/Validation dataset : ça peut devenir un peu tricky pour définir ces deux jeux de données via Keras. Par exemple construire ces deux jeux avec des données dans un dossier unique. Vous pouvez à la rigueur vous en sortir comme cela :
  • Réaliser des opérations spécifiques, comme de la data augmentation à la volée, avant qu’elles ne soient envoyé au réseau. Keras en propose quelque une via sa classe ImageDataGenerator, mais reste limiter.

 

Cette solution nous permet donc de nous adapter face à de large dataset plus facilement, et évite tout saturation de RAM ou celle du GPU. On profitera en plus du calcul parallèles via les threads de votre CPU.

Etat actuel des choses

Vous devriez avoir pour le moment quelques choses comme ceci comme cheminement pour charger vos données. Je minimalise l’exemple pour la compréhension :

  1. On charge le dataset dans sa globalité via Numpy
  2. On créer son modèle et le compile selon son optimiser et diverses métriques souhaités
  3. On entraine notre modèle sur nos données précédemment chargé

 

Réalisation du générateur

Squelette de base

Voici le squelette de base que je préconise. Votre classe doit hériter de la classe Keras.utils.Sequence :

Cette classe parente vous assure dans le cas ou vous souhaitez utiliser du calcul parallèle avec vos threads, de garantir de parcourir une seule et unique fois vos données au cours d’une époch, contrairement à pures custom générateurs maison que j’ai déjà vu sur des forums qui ne se synchronisaient pas entre eux. On verra plus tard dans le tutoriel comment utiliser ce multiprocess.

 

Implémentation du constructeur

Obligatoire à écrire, et défini dans l’objet de base de Keras.utils.Sequence. Permet d’instancier un nouvel objet.

Vous aurez besoin au minima besoin de vos données X et Y. Je vous montre ici un exemple :

  • data : nos données dans un dataframe Pandas
  • xLabel : nom de colonne du df contenant nos données X
  • yLabel : nom de colonne du df contenant nos données Y
  • batchSize : taille d’un mini lot de données
  • shuffle : booléen si on souhaite envoyer des données de façon aléatoire, ou dans l’ordre de l’index du dataframe
  • targetSize : afin de resize nos images

 

Implémentation __len__

Obligatoire à écrire, et défini dans l’objet de base de Keras.utils.Sequence. Définie le nombre de batch durant une époch.

 

Implémentation on_epoch_end

Défini dans l’objet de base de Keras.utils.Sequence. Appelé à chaque fin d’epoch. On va s’en servir ici afin de rendre aléatoire l’ordre de la liste des ID des items à envoyer pour constituer le batch de données courant.

 

Implémentation __getitem__

Obligatoire à écrire, et défini dans l’objet de base de Keras.utils.Sequence. Génère un batch de données.

Concrètement je récupère une liste d’ID qui correspond à des items spécifiques (X et Y) contenu dans le dataframe de données, de taille batchSize.  Ici pour l’exemple je ne fais que charger des images dans un tableau Numpy

A vous d’effectuer vos transformation souhaités sur vos données, selon votre problématique. A savoir normaliser les données (diviser par 255 les valeurs des images par exemple pour passer de 0<x<255 à 0<x<1), réaliser de la data augmentation, etc.

 

Appel à notre générateur

On a plus qu’a créer un générateur pour le jeu d’entrainement, et un second pour le jeu de validation à partir de notre classe custom. On lui fourni un dataframe avec un nombre d’élément spécifique à chaque type de jeu de données spécifié avec un ratio de 0,8/0,2.

On fourni ainsi nos deux générateurs au modèle via la méthode fit_generator(). On peut spécifier si on souhaite utiliser nos threads afin de paralléliser le chargement des données du disque dur vers le GPU.

 

Data augmentation à la volée

Vous pouvez très bien réaliser une data augmentation sur votre fichier de données en même temps que vos masques, en local, au préalable. L’utilisation du générateur prendra en compte l’ensemble des fichiers de vos deux dossiers.

Vous pouvez néanmoins réaliser de la data augmentation au sein même du générateur, à la volée. Cela permet de stocker votre dataset de base, sans avoir des dizaines et dizaines de giga-octets supplémentaire et ainsi d’économiser du stockage sur votre disque dur. Cependant vous aurez alors un points négatif dans cette affaire. Vous perdrez légèrement en temps de chargement des données vers le GPU. En effet, en plus de lire et charger les images en local, on ajoute une petit étape ici, de créer de nouveaux échantillons à la volée avant de tout envoyer au GPU. A vous de voir quel moyen au final est le plus adapté selon votre type de dataset, votre hardware et votre approche pour répondre à votre problématique.

Ici je vous montre les quelques changements à réaliser pour adapter notre précédent générateur, afin d’y ajouter de la data augmentation à la volée.

 

Modification du constructeur

On va initialiser ici un nouvel attribut, batchSizeAugmented :

  • batchSizeAugmented : nombre d’image total du batch
  • batchSize : nombre d’image lu en local de notre dataset d’origine

Ici comme exemple, je prends batchSize =2 et batchSizeAugmented =32. Dans mon raisonnement, je souhaite que pour une image lu en local, je crée 15 nouvelles images augmentées avec divers effets. Donc si je lis 2 images, j’aurais 30 images augmentés. Ce qui fait 2 + 30 = 32 images au total.

 

Modification de __len__

Rappelez vous cette méthode défini le nombre de batch par époch. On remplace alors batchSize par batchSizeAugmented :

 

Modification de __getitem__

On souhaite générer des images augmentés (30) à partir d’image local (2), aucun changement donc pour l’objet currentBatchIdsRow. 

On change cependant la première dimension de xTrain et yTrain de batchSize à batchSizeAugmented, qui correspond au nombre total d’image par batch ( donc les images locales en plus des augmentés).

On ajoute une nouvelle boucle dans la première , et c’est ici que vous allez opérez vos transformations pour générer de nouvelles images.

classification localisation segmentation detection Cours théoriques - Deep learning

Différences entre classification d’image, localisation/détection & segmentation d’objets

Image classification

image segmentation object detection localization deep learning cnn keras tensorflow

Dans la tâche la plus courante lorsque l’on parle de traitement d’image, on parle de classification d’image. Cela résulte à identifier une classe au sein de l’image. 

 

 

Détection d’objet

image segmentation object detection localization deep learning cnn keras tensorflow

La détection d’objet se superpose à la simple classification d’image, en ajoutant de la localisation d’objet. Cela fonctionne encore avec une classe au sein de l’image, en plus d’avoir sa position. Celle-ci est souvent définie par des rectangles, appelés bounding box.

 

Segmentation d’objets

image segmentation object detection localization deep learning cnn keras tensorflow

Segmentation sémantique

C’est le processus permettant de classifier chaque pixel d’une image en un label particulier. On ne peut faire de différence entre deux instances d’un même objet. Par exemple, si on a deux voitures sur une image, ce type de segmentation donnera le même label sur l’ensemble des pixels des deux voitures.

Segmentation d’instance

La segmentation d’instance va attribuer un unique label sur chaque instance d’un même objet au sein de l’image. En reprenant le précédent exemple, si nous avons à nouveau deux voitures sur une image, chacune d’entre elles se verront attribuer une couleur différente. À l’inverse de la segmentation sémantique, qui aurait défini la même couleur pour les deux voitures.

 

fit vs fit_generator keras tensorflow numpy generator Cours pratiques - Deep learning

Charger & entrainer le réseau sur des images :…

Petit comparatif sur deux méthodes permettant de chargeur un dataset pour entrainer un réseau de neurones via Tensorflow et Keras.

 

Fit()

Cela permet d’envoyer en un seul coup l’ensemble du dataset au réseau de neurones. En conséquence, on doit être sur que le dataset puisse rentrer en RAM.

C’est donc plutôt adapté pour les petits dataset.

 

Générer les fichiers numpy en local

On commence par définir nos tableau qui vont contenir nos images. Pour cela je vous donne deux méthodes différentes :

 

Maintenant on va parcourir un dossier supposé contenant nos images que l’on souhaite ajouter à nos deux précédents tableau. Je vous montre selon les deux types d’initialisation faîte précédemment. Pour chaque image que l’on aura, on va devoir les ouvrir et les transformer en tenseur de taille 3 (largeur x hauteur x canal, ou canal=3 si RGB ou canal=1 si Noir/Blanc). Une couleur peut avoir un gradient allant de 0 à 255, selon son intensité. Pour chaque pixel, on aura alors des triplets (0<Intensité rouge<255, 0<intensité vert<255, 0<intensité bleu<255). Pour des raisons d’optimisation, les réseaux apprennent plus facilement sur des valeurs normalisés. Pour cela, je vais divisé par 255 les valeurs, pour avoir des valeurs comprises entre 0 et 1.

 

Maintenant on les enregistre en local :

 

Charger les fichiers numpy

On a plus qu’a charger les fichiers précédemment sauvegardé en local, et de les fournir directement au réseau via la méthode fit():

 

Fit_Generator()

On se sert de générateurs afin d’envoyer des mini-lots (batch) de notre dataset au réseau.

C’est donc plutôt adapté pour les grands dataset et convient donc mieux aux problématiques rencontré dans la vie réel. Cela permet en plus d’ajouter de la data augmentation à la volée, donc pratique !

 

Besoin de data augmentation ?

C’est ici que l’on défini notre data augmentation. Vous pouvez laisser vide si vous n’en souhaitez pas. On laisse juste la normalisation des données, comme vu précédemment :

 

Chargement des données

On peut charger les données directement via un dossier spécifique ou via un dataframe de pandas. Gardez un seed identique permettant de synchro les deux générateurs sur les mêmes images en entrée entre sa donnée et son label.

 

Via un dossier spécifique

 

Via un datafame pandas

 

Train & Validation set depuis un même dossier commun

Simple exemple pour de la segmentation d’image. Mais selon votre problématique vous devrez changez dans la méthode flow_from_directory/dataframe le class_mode (type de vos données en Y, catégorie, binaire, etc.) et le classes (liste de vos classes [chiens, chats, etc.])

react native expo android ios stack navigator Cours web Mobile – React Native

Chap 0 : Présentation de ReactNative & Expo

Vous commencez le développement mobile et vous hésitez à choisir ReactNative ou la surcouche Expo ? Je vous donne quelques indices afin d’y voir plus clair sur quel framework vous devriez choisir en fonction de vos compétences, du temps, et de vos besoins.

 

Présentation de React

Framework pour le développement web frontend, développé par Facebook et concurrent direct de Angular et de Vue.js.

 

Dom Réel et virtuel

React est à la mode. Il est performant, et ce grave à un point important, est qu’il utilise un DOM (Document Object Model) virtuel.

Le DOM (réel) est l’arbre de nœuds, la représentation textuelle de toute votre application web. Quand vous effectuez un changement d’état au sein de votre application, l’intégralité du DOM est recréer. Cela comprend alors l’ensembles des éléments fils de l’application. Et ceci peut être couteux en terme de performance pour des applications grandissantes, surtout si vous y effectuez des modifications fréquentes de votre UI.

Le DOM virtuel quand à lui est un second arbre, correspondant à l’état réel de la globalité de votre application. Vous pouvez alors vous demandez, n’est-ce pas doublon d’avoir deux arbres à l’identique ? Et bien non.

On imagine que vous effectuiez une modification de couleur, sur un texte par exemple. Dans le cas d’une application avec Angular, celui-ci va redessiner la globalité de l’application. Avec React, seul le DOM virtuel va être modifié. React va alors réaliser une comparaison avec des algorithmes spécifiques entre le DOM virtuel fraichement actualisé, et le DOM réel. En fonction des différences entre les deux arbres, seul les nœuds qui ont un nouvel état, vont être redessiner dans le DOM réel. Cela permet de redessiner seulement un seul nœud, dans notre exemple.

Voici une image plus parlante :

virutal dom vs real dom

 

Présentation de React Native (Vanilla)

C’est un framework développé par Facebook pour le développement d’applications cross-platform (iOS et Android).

 

Développement natif vs développement hybride

Le développement natif est l’utilisation de SDK spécifique. Xcode pour iOS, et Android studio pour Android.

 

Pourquoi favoriser le dév. natif
  • Le développement natif utilise ses modules et bibliothèques natives, avec son langage natif. Il sera toujours plus performant que les solutions hybrides, même si au cours du temps nous observons de moins en moins de différences
  • L’accès aux modules spécifique facile (notification push par exemple)
  • Poids des apk/ipa très léger

 

Pourquoi favoriser le dév. hybride
  • Un seul langage de développement pour deux plateformes
  • Un code unique. Donc 2x moins de bug, en 2x moins de temps
  • Une interface UI/UX identique, évitant des différences

Présentation de Expo

Expo embarque ReactNative, c’est une sorte de wrapper, de surcouche. Vous retrouvez donc l’ensemble des items graphiques de ReactNative.

En voici les principales caractéristiques :

Projet déjà configuré ! En une minute vous avez votre Helloworld de fonctionnel ! Cela évite des paramétrages en tout genre et fastidieux sous ReactNative Vanilla pour paramétrer Xcode et AndroidStudio.

Pas besoin de mac pour tester votre app sur les deux OS mobile ! Et ça, ça fait plaisir. Ayant un vielle Iphone et mon Android de tout les jours, je peux tester via l’application Expo (disponible sur les stores iOS et Play), avec hot-reload, et sans avoir de Mac, l’application que je développe sur mes deux smartphones. Même si le dév. hybride promet d’avoir un UI/UX 100% identique entre les deux plateformes, il m’est arrivé que mes checkbox ne s’affichait pas correctement sur iOS. J’ai donc forcé pour mes deux plateforme, l’utilisation de la checkbox Android. Soucis auquel je me serais rendu compte une fois seulement uploadé sur les stores. Sous ReactNative Vanilla, vous pouvez seulement tester sur votre iOS si vous tournez sous macOS,  et seulement sur votre Android si vous êtes sur Windows pour développer.

Pas besoin de mac pour builder votre app ! Vous pouvez utiliser les serveurs expo pour cela. Attention, il vous faudra obligatoirement un compte développeur chez les deux sociétés, et acheter la licence qui va avec. Autre piège, votre build se fera à distance et non en local, attention donc si vous développer une app qui doit rester à l’abris des regards.. Mais dans tout les cas si vous souhaitez build en local et sans mac, vous pouvez utiliser une VM sous macOS Catalina avec QEMU/KVM sans soucis. Sous ReactNative Vanilla il vous faudra forcement les deux OS pour builder sur les deux plateformes, mais qui se résous aussi bien par la VM.

Accès à l’ensemble des modules natifs compliqué voir impossible… A un moment donnée, votre application grandissante va prendre en fonctionnalités. Etant donnée que Expo est une surcouche, l’accès aux modules natifs n’est pas possible pour réaliser certaines fonctionnalités. Je prends comme exemple connu comme les notifications push, ou achats-in app. Vous n’y aurez simplement pas accès.

… mais vous pouvez Ejecter Expo ! Vous pouvez cependant ‘éjecter’ Expo pour palier au problème précédent. Mais à en lire certaines expériences sur le net, selon la taille et le versionning de votre projet, que ça puisse très bien se passer comme mal se passer.

Poids supérieur des apk/ipa ! J’ai publié sur les stores ma première application avec des fonctionnalités très simple. Elle fait 50mo à télécharger sur les stores.

 

Quel framework choisir au final ?

  • Vous débutez sur le développement mobile ?
    • Oui ? Foncez sur ReactNative + Expo

 

  • Votre application requiert des accès natifs à des modules particuliers (achats in-apps ou push-notifications) ?
    • Oui ? : Foncez sur ReactNative vanilla
    • Non ? : Foncez ReactNative + Expo

 

Si votre choix se confirme à développer sous Expo, je vous propose de commencer par le premier chapitre, afin d’installer l’environnement de développement. Hello World sur mon smartphone en moins d’une minute garantie ! 😉

 

 

mac os apple installation catalina vm virtual machine qemu kvm Système d'exploitation

macOS Catalina sans macbook, gratuit & fluide

J’ai eu récemment besoin de Catalina afin de builder une application ReactNative pour iOS. Les solutions traditionnels Virtualbox ou encore Vmware m’ont donnée des OS inutilisable, résultant en d’important lag même sur ma config à 4000€. Au bout de deux jours de recherche et d’installations en tout genre pour activer l’accélération graphique via GPU, je suis tombé ENFIN sur une option viable, gratuite, et donnant d’excellent résultat sur mon laptop à 500€ qui datent des années 2015 avec un vieux dual-core !

Vous aurez accès à l’ensemble des outils de Apple, XCode, les app stores, etc.

 

Prérequis

Désolé les amis, il vous faudra lâché Windows au profit d’une distrib Linux. Donc ayez une machine en dual-boot sous la main, c’est obligatoire.

 

Dépôt

J’ai trouvé deux répo intéressant:

 

Le premier de Foxlet est le plus simple. Mais celui de Kholia est un poil plus performant, et nous partirons sur celui-ci dans la suite du tuto.

 

Pourquoi ?

Foxlet utilise le bootmanager Clover, alors que Kholia Open-core. Open-core est plus jeune, moins robuste, mais semble être plus prometteur et performant. Clover semble être délaissé au profit du jeune OpenCore, la plupart des gros développeur sont partie pour ce dernier.

Vous pouvez y ajouter votre GPU, votre carte son, périphériques USB pour développer sous XCode, etc.

 

Installation

On va suivre les infos du dépôts :

  • On paramètre KVM
    $ echo 1 | sudo tee /sys/module/kvm/parameters/ignore_msrs
    

    On rend la modification permanente

    $ sudo cp kvm.conf /etc/modprobe.d/kvm.conf
    
  • Installation des packages nécessaires
    sudo apt-get install qemu uml-utilities virt-manager git wget libguestfs-tools -y
    
  • Clone le répo de Kholia
    cd ~
    
    git clone --depth 1 https://github.com/kholia/OSX-KVM.git
    
    cd OSX-KVM
    
  • Lancez le scripts suivant :
    ./fetch-macOS.py
    

    Selectionnez la version de macOS que vous souhaitez installer.

    $ ./fetch-macOS.py
     #    ProductID    Version   Post Date  Title
     1    061-26578    10.14.5  2019-10-14  macOS Mojave
     2    061-26589    10.14.6  2019-10-14  macOS Mojave
     3    041-91758    10.13.6  2019-10-19  macOS High Sierra
     4    041-88800    10.14.4  2019-10-23  macOS Mojave
     5    041-90855    10.13.5  2019-10-23  Install macOS High Sierra Beta
     6    061-86291    10.15.3  2020-03-23  macOS Catalina
     7    001-04366    10.15.4  2020-05-04  macOS Catalina
     8    001-15219    10.15.5  2020-06-15  macOS Catalina
     9    001-36735    10.15.6  2020-08-06  macOS Catalina
    10    001-36801    10.15.6  2020-08-12  macOS Catalina
    11    001-51042    10.15.7  2020-09-24  macOS Catalina
    12    001-57224    10.15.7  2020-10-27  macOS Catalina
    13    001-68446    10.15.7  2020-11-11  macOS Catalina
    14    001-79699     11.0.1  2020-11-12  macOS Big Sur
    
    Choose a product to download (1-14): 14
    

    On a récupérer le BaseSystem.dmg, on va le convertir

    qemu-img convert BaseSystem.dmg -O raw BaseSystem.img
    
  • On créer un disque dur virtuel, ou notre distri de macOS sera installé. Attribuez la valeur de stockage comme vous le souhaitez. Sachez que Xcode prends pas mal de place, donc au minimum une partition de 64go me semble correct…
    qemu-img create -f qcow2 mac_hdd_ng.img 128G
    

     

Démarrer Catalina

Pour démarrer notre futur macbook pour la première ainsi que les autres sessions, lancez le script suivant :

./OpenCore-Boot.sh

 

On commence par booter sur le disque de base :

installer apple mac os catalina gratuit qemu kvm

 

Sélectionnez Disk Utiliy :

installer apple mac os catalina gratuit qemu kvm

 

Sélectionnez le disque que vous venez de créer dans les étapes précédentes :

installer apple mac os catalina gratuit qemu kvm

 

On va cliquer sur Erase. Donnez lui un nom à notre disque, macOS Catalina par exemple. Validez en cliquant une nouvelle fois sur Erase :

installer apple mac os catalina gratuit qemu kvm

 

Tout est fini. Fermez l’utilitaire de disque afin de revenir sur le menu de départ.

installer apple mac os catalina gratuit qemu kvm

 

Sélectionnez Reinstall macOS :

installer apple mac os catalina gratuit qemu kvm

 

Un tas de chose vont vous être demander, laissez vous guidez à travers l’installation. Acceptez les divers conditions, remplissez votre profil, enregistrer votre compte Apple afin d’avoir accès au store, etc. Je vous explique pas chaque détails, c’est plutôt intuitif :

installer apple mac os catalina gratuit qemu kvm

 

Après une vingtaine de minute, votre installation est fini

installer apple mac os catalina gratuit qemu kvm

 

Pour utiliser le plein écran : Shift + Alt + F

Pour quitter le focus et revenir sur votre linux : Shit + Alt + G

react native expo android ios sqlite Cours web Mobile – React Native

Chap 6 : Stockage de données complexes (SQLite)

Nous avons vu dans le chapitre précédent comment stocker en local des données simples.

On va s’attarder cette fois-ci à comment stocker des informations plus nombreuses, plus complexes et ce de façon plus ordonnée, en utilisant des bases de données SQL.

On va réaliser une simple to-do app, avec possibilité d’ajouter une note, ou d’en supprimer, avec une liste permettant de toute les afficher.

 

On ne peut utiliser n’importe quel outils pour persister des données car nous sommes sur un projet Expo, nous empêchant d’accéder aux modules natifs de iOS et Android. SQLite est un moyen fonctionnant avec des projets Expo.

 

Objectifs

  • Stocker localement des informations via une base de donnée SQL

 

react native sqlite

 

Prérequis

Installez le package pour utiliser SQLite :

expo install expo-sqlite

 

Nous l’importerons dans nos fichiers de la manière suivante :

import * as SQLite from 'expo-sqlite';

Base de données & CRUD

CRUD : diminutif correspondant aux requêtes basique, à savoir Create, Read, Update et Delete.

Création de la base de données

Rien de plus simple, une seule ligne suffit :

On donne un nom à notre base, en argument. Celle-ci n’est crée qu’une seule et unique fois. Si on rappelle cette même méthode, on récupère la base de donnée crée auparavant, aucun risque de doublon.

 

Création de la table NOTE

On va maintenant créer notre table. Je vous montre la façon la plus simple de réaliser des transaction SQL vers notre base :

Requête SQL des plus basique avec le mot clé CREATE TABLE. On lui donne un nom de table, ainsi que la définitions de nos colonnes. Un ID auto-incrémenté pour garantir l’unicité de nos données, ainsi qu’un attribut TEXT qui contiendra le contenu de nos notes.

Nos deux attributs ne peuvent être null, et on leur défini un type, soit Text ou Integer.

 

Optimiser les requêtes

Je préfère une autre annotation que la précédente pour questionner la base de données, récupérer nos objets et leurs affecter des transformations. Je trouve plus clair et simple à l’utilisation. On définit notre modèle de requête avec une promesse :

On pourra utiliser ce modèle dans des fonctions async, via un appel par un await. On pourra utiliser des then() et catch() à l’appel du service dans nos vues, permettant par exemple d’afficher à l’utilisateur dans une popup si une note à bien été ajouté ou si dans le cas inverse afficher un message d’erreur avec son origine.

 

Récupérer toute les notes

On définit une interface pour les objets que l’on va récupérer en base. Cela nous facilitera leurs manipulations au sein de notre application :

 

On fait appel à notre modèle pour questionner la base :

On récupère nos objets via la requête. On va itérer sur notre résultat de requête pour re-typer correctement nos objets.

 

Ajouter une note

Seule différence, on va ici passer en argument le contenu de ma note que l’on souhaite persister en base. Pas besoin de lui passer un ID pour la note, car celle-ci est généré automatiquement en base. Nous avons défini cette option tout à l’heure, lors de la création de la table.

Supprimer une note

Quasiment identique au point précédent, sauf qu’ici on lui passe un ID de note à notre fonction, étant donné que c’est l’attribut qui défini l’unicité de mes items dans ma table NOTE :

 

Récupération des items dans la vue

Nous venons de créer notre base ainsi que des opérations CRUD permettant d’interagir avec elle. On va désormais créer une nouvelle vue, avec un champ de texte permettant de donner du contenu à une note, un bouton pour ajouter cette note, une liste scrollable permettant de naviguer sur l’ensemble de nos notes en base, et leur associé à chacun un bouton pour les supprimer.

 

Définition du state de notre vue

On commence par créer le squelette de base de notre vue :

On utilise ici un composant de classe et non un composant fonctionnel. On a besoin d’utiliser un state pour l’affichage dynamique du contenu de notre liste scrollable. On défini un attribut myNoteList correspondant à une liste de l’ensemble de nos notes en base, ainsi qu’un second attribut note correspondant au champ de texte remplissable par l’utilisateur pour créer une nouvelle note. On initialise les deux attributs dans le constructeur.

 

Définition des méthodes de notre vue

On défini les méthodes pour mettre à jour nos éléments, pour ajouter une note, ainsi que la supprimer :

La méthode componentDidMount() est une méthode standardisé de React, permettant d’être appelé une seule et unique fois et ce à la fin de la création du composant. On lui demande à l’ouverture de notre page, d’initialiser notre liste avec le contenu de notre base de données.

 

Définition des éléments visuels de notre vue

Ici rien de bien complexe. J’ai créer un titre de page, un champ de texte avec un bouton pour ajouter une note. Une liste scrollable présentant l’ensemble des notes en base, ainsi qu’une option permettant de les supprimer :

J’ai utilisé des icones Ionicons inclus dans Expo, qui s’adapte en fonction de si vous êtes sur Android ou iOS via la méthode Platform.OS, encapsulé dans des TouchableOpacity, pour rendre un peu plus esthétique mes boutons d’interactions, ainsi que des flexbox pour avoir une touche de responsive design.

 

Conclusion

Vous venez de voir comment créer très simplement une base de donnée pour votre application React Native tournant sous Expo, comment l’interroger et surtout comment récupérer et afficher le résultats dans une belle vue responsive.

 

react native expo android ios async storage Cours web Mobile – React Native

Chap 5 : Stockage de données simples (AsyncStorage)

Nous venons de voir au chapitre précédent comment organiser une navigation complexe et imbriquée pour une application avec une architecture grandissante.

Nous allons voir aujourd’hui comment stocker des données plutôt simple, par exemple des paramètres de l’application comme l’activation ou non d’un thème clair ou sombre.

 

On ne peut utiliser n’importe quel outils pour persister des données car nous sommes sur un projet Expo, nous empêchant d’accéder aux modules natifs de iOS et Android. AsyncStorage est un moyen fonctionnant avec des projets Expo.

 

Objectifs

  • Stocker localement des informations via JSON

async storage chap 5

 

Prérequis

Installez le packet requis via une console :

expo install @react-native-async-storage/async-storage

Utilisez l’import qui correspond dans vos fichiers :

import AsyncStorage from '@react-native-async-storage/async-storage';

Que-ce que AsyncStorage

AsyncStorage nous permet de persister des données en local sur le smartphone, de façon asynchrone et non crypté. Les informations sont sauvegardé en clair, donc éviter de stocker des données sensibles tel que des mots de passes.

Le stockage s’effectue sous forme de couple tel quel : <Clé, Valeur>.

On ne peut stocker que des string, donc pour des éléments plus complexe tel que des objets, on devra utiliser JSON.stringify() pour la conversion en JSON lors de la sauvegarde d’une donnée, et utiliser JSON.parse() pour lire un objet.

Sauvegarder des données

Pour un string

Pour un objet

 

Lire des données

Pour un string

Pour un objet

 

En pratique

On va faire un exemple tout bête, à savoir charger le nom d’un utilisateur à l’appui d’un bouton.

 

Méthode d’écriture & lecture de donnée

On commence par définir nos deux méthodes pour charger et sauvegarder une donnée :

On appellera la méthode initProfileName() dans l’entrée de notre l’application (App.tsx) pour initialiser notre donnée.

 

Mise à jour de notre vue

On crée un état initialisé à ‘Invité’. On aura une méthode appelant notre fonction pour lire notre donnée via AsyncStorage, définit précédemment. Et enfin une méthode render() pour afficher un champ de texte, et un bouton bindé avec la fonction de chargement de notre donnée locale :

 

Conclusion

On vient de voir comment stocker des informations simple en local. Nous allons voir au prochain chapitre comment stocker des informations plus complexe et surtout en quantités plus importantes.

react native expo android ios stack navigator Cours web Mobile – React Native

Chap 4 : Stack navigator ( navigation imbriquée )

Nous venons de voir dans le chapitre précèdent comment faire une barre de navigation principal. Mais comment faire si nous souhaitons naviguer dans de nouveaux composant à travers nos vues actuelle ?

Pour réaliser une navigation imbriqué de vue dans d’autres vues, nous allons ajouter un nouvel élément, une StackNavigation.

 

Objectifs

  • Créer de nouvelles vues
  • Réaliser une navigation imbriqué

stack navigator and tabs navigator react native

Prérequis

Installez la lib qui gère la stack :

npm install @react-navigation/stack

 

Créer une nouvelle vue

Pour faire de la navigation imbriquée, il va nous falloir dans une première partie créer un nouveau composant, une nouvelle page. Faisons une nouvelle page Profil, qui affiche mon prénom, par exemple :

 

Mise a jour des ROUTES

On va ajouter une nouvelle route dans notre constante globale des routes, amenant à notre nouvelle vue précédemment crée :

 

Création de la stack navigator 

On va créer notre composant qui va gérer la navigation par pile. On doit lui définir nos deux composants (nos deux vues), leurs routes respectifs pour y accéder, et la route initiale lorsque le composant est crée :

 

Mise a jour de notre tab navigator

On va désormais fournir à notre navigation par tab, non plus mon composant WelcomePage comme initial, mais directement notre stackNavigator, crée précédemment :

 

Appel du changement de route par un bouton

On met à jour notre composant WelcomePage, via la méthode onPress, qui est appelé lors d’un clique sur notre item, permettant d’appeler le changement de vue en lui donnant la route que l’on souhaite. Je vous montre comment faire cet appel selon le type de composant que vous utilisez au sein de votre application :

 

Navigation via composant fonctionnel

 

Navigation via composant de classe

 

react native stack tab navigator
Résultat du cours

Conclusion

Nous venons de voir comment naviguer à l’infinie entre nos vues. Dans le prochain chapitre, nous nous attarderons à comment sauvegarder, à la fermeture de notre application, des données simples, comme des paramètres de l’application.