ANN/ORE #6 – Algèbre linéaire en PL/SQL

Dans l’article précédent, on a mis en œuvre manuellement avec R une forward-propagation à partir de poids synaptiques pré-calculés par le package neuralnet. Cela m’a donné l’idée de tenter la même chose avec une procédure PLSQL.

Pour cela, j’ai utilisé le package standard UTL_NLA qui permet la réalisation d’opérations d’algèbre linéaire directement en base. Le package implémente une série d’opérations des librairies spécialisées BLAS et LAPACK.

En particulier, j’ai utilisé la procédure blas_gemm qui permet la réalisation de produits matriciels.

En l’occurrence, cela permet de multiplier la matrice obtenue en sortie d’une couche par les poids synaptiques suivants. Dans l’exemple MNIST, on démarre avec une matrice de 10000*785 (10000 images du dataset de test contenant 784 pixel chacune + une valeur de biais). Les poids synaptiques associés à la couche d’entrée (input layer) forment une matrice de 785*300.

Leur multiplication permet d’obtenir une matrice de 10000*300 aux valeurs de laquelle on applique la fonction d’activation (sigmoïde).

Après ajout d’une valeur de biais, la matrice résultante 10000*301 est à son tour multipliée par les poids synaptiques associés à la première couche cachée – soit 301*100. On parvient à une matrice de 10000*100, à laquelle on applique la fonction d’activation et on ajoute une valeur de biais.

Le résultat est ensuite multiplié par les poids synaptiques associés à la seconde couche cachée – soit 101*10. On arrive alors à une matrice de 10000*10. On y applique la fonction d’activation pour obtenir le résultat final – à savoir la couche de sortie (output layer).

On voit donc bien l’intérêt d’une fonction de produit matriciel optimisée!

Le seul défaut de l’implémentation est, à mon sens, que les procédures utilisent des variable de type UTL_NLA_ARRAY_DBL:

SQL> SELECT type_name, coll_type, upper_bound, elem_type_name
  2    FROM DBA_COLL_TYPES
  3   WHERE type_name = 'UTL_NLA_ARRAY_DBL';

TYPE_NAME            COLL_TYPE            UPPER_BOUND ELEM_TYPE_NAME
-------------------- -------------------- ----------- --------------------
UTL_NLA_ARRAY_DBL    VARYING ARRAY            1000000 BINARY_DOUBLE

SQL>

Cela nécessite donc « l’aplatissement » (sous forme de varrays) des matrices multipliées et ces dernières sont limitées à un million d’éléments (cf. UPPER_BOUND ci-dessus).

C’est une limite élevée mais dans le cas présent, la matrice d’entrée représente 7.8 millions d’entrées (10000 images de 784 pixels). On devra donc réaliser un lotissement pour ne pas dépasser la limite de 1e6.

Le package PL/SQL est disponible ici: FORWARD_PROPAGATION

On peut alors tester l’implémentation en PLSQL à l’aide des données utilisées dans le billet précédent (tables L1, L2 et L3 contenant les poids calculés par NeuralNet):

SQL> set timing on;
SQL> BEGIN
  2      forward_propagation.score ('MNIST_TEST_SET',
  3                                 'L1,L2,L3',
  4                                 'IMG_LBL, IMG_ID',
  5                                 'IMG_ID');
  6  END;
  7  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:45.98
SQL>

Les données de la couche de sortie sont accessibles via la vie V_ANN_OUT$. Pour chaque image, on dispose de 10 neurones de sortie et la prédiction correspond à celui ayant la valeur la plus importante. Ci-dessous, le neurone O7 dispose de la valeur la plus importante pour la première image (on prédit donc un 7), O2 dispose de la valeur la plus importante pour la seconde image (on prédit donc un 2):

SQL> set timing off;
SQL>
SQL> SELECT * FROM v_ann_out$ WHERE recid < 3;

     RECID         O0         O1         O2         O3         O4         O5         O6         O7      O8            O9
---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
         1 5.0343E-21 2.5670E-20 5.5412E-21 1.9100E-23 4.0493E-09 2.8954E-22 4.9004E-17          1 1.0950E-27 8.4151E-12
         2 2.3049E-16 1.3433E-14          1 1.1340E-17 5.3481E-18 1.8879E-14 7.4718E-11 1.2508E-17 3.1266E-17 5.0967E-24

SQL>

On peut alors comparer les valeurs prédites avec les labels du dataset de test:

SQL> WITH
  2      pred
  3      AS
  4          (SELECT recid,
  5                  CASE GREATEST (O0,O1,O2,O3,O4,O5,O6,O7,O8,O9)
  6                      WHEN O0 THEN '0'
  7                      WHEN O1 THEN '1'
  8                      WHEN O2 THEN '2'
  9                      WHEN O3 THEN '3'
 10                      WHEN O4 THEN '4'
 11                      WHEN O5 THEN '5'
 12                      WHEN O6 THEN '6'
 13                      WHEN O7 THEN '7'
 14                      WHEN O8 THEN '8'
 15                      WHEN O9 THEN '9'
 16                  END
 17                      pred_lbl
 18             FROM v_ann_out$),
 19      comparaison_pred_lbl
 20      AS
 21          (SELECT img_lbl,
 22                  CASE WHEN pred_lbl = img_lbl THEN 1 ELSE 0 END correct_pred
 23             FROM pred, mnist_test_set
 24            WHERE recid = img_id)
 25    SELECT img_lbl,
 26           COUNT (*)                                     nb,
 27           SUM (correct_pred)                            nb_correct_pred,
 28           ROUND (100 * SUM (correct_pred) / COUNT (*), 1) correct_pred_pct
 29      FROM comparaison_pred_lbl
 30  GROUP BY img_lbl
 31  ORDER BY 1;

IMG_LBL                                          NB NB_CORRECT_PRED CORRECT_PRED_PCT
---------------------------------------- ---------- --------------- ----------------
0                                               980             966             98.6
1                                              1135            1121             98.8
2                                              1032             977             94.7
3                                              1010             952             94.3
4                                               982             934             95.1
5                                               892             847               95
6                                               958             924             96.5
7                                              1028             987               96
8                                               974             909             93.3
9                                              1009             949             94.1

10 rows selected.

SQL>

Cette approche peut s’avérer intéressante lorsqu’on ne dispose pas de l’option Oracle Advanced Analytics.

En effet, si on créé le modèle sans passer par ORE (Tensorflow, Theano etc…), une fois les poids synaptiques récupérés et chargés en base, on peut réaliser le scoring en base sans option additionnelle.

 

 

 

Laisser un commentaire

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

six × 1 =