Cet article a été écrit dans le but de repousser les personnes qui souhaitent faire du deep learning en Java mais ne peuvent pas faire le premier pas. Vous pouvez facilement découvrir la puissance de l'apprentissage en profondeur, alors veuillez le lire.
Nous supposons que l'une des personnes suivantes est un lecteur.
--Un programmeur Java qui veut essayer le deep learning, mais qui n'a jamais écrit de programme en Python ...
En lisant cet article, vous serez en mesure de créer un environnement de développement d'apprentissage en profondeur en utilisant Deeplearning4j avec IntelliJ et d'identifier les nombres manuscrits. Il vous permet également d'exécuter de nombreux exemples d'apprentissage en profondeur en plus de l'identification des numéros. Cependant, vous ne pouvez pas apprendre la théorie de l'apprentissage profond à partir de cet article. Pour apprendre la théorie du deep learning "[Deep Learning from scratch-The Theory and Implementation of Deep Learning Learn with Python](https://www.amazon.co.jp/%E3%82%BC%E3%83%] AD% E3% 81% 8B% E3% 82% 89% E4% BD% 9C% E3% 82% 8B Apprentissage en profondeur-% E2% 80% 95Python% E3% 81% A7% E5% AD% A6% E3% 81 % B6% E3% 83% 87% E3% 82% A3% E3% 83% BC% E3% 83% 97% E3% 83% A9% E3% 83% BC% E3% 83% 8B% E3% 83% B3 % E3% 82% B0% E3% 81% AE% E7% 90% 86% E8% AB% 96% E3% 81% A8% E5% AE% 9F% E8% A3% 85-% E6% 96% 8E% Nous vous recommandons de lire E8% 97% A4-% E5% BA% B7% E6% AF% 85 / dp / 4873117585).
Je pense que la première étape pour commencer à apprendre le deep learning est d'exécuter le programme et de le «vivre» afin de vous motiver à apprendre. Voir ci-dessous.
Lorsque vous entrez un numéro manuscrit, ce programme affichera le numéro identifié (prédit) dans «Prédiction:» au bas de l'écran.
Si vous voulez écrire un programme qui identifie l'image du nombre manuscrit "7" sans utiliser une bibliothèque spéciale, quel type d'algorithme utiliseriez-vous? Est-ce correct si l'algorithme distingue "7" s'il y a une ligne horizontale en haut de l'image et une ligne descendante à partir du point final? L'algorithme peut-il identifier correctement l'un des éléments suivants comme un «7»? Et comment l'intégrer à votre programme?
Identifier des nombres faciles pour les humains n'est pas si facile pour les ordinateurs (lorsqu'ils essaient de les atteindre avec des algorithmes). Mais l'apprentissage en profondeur peut le faire. Et dans cet article, nous allons exécuter le programme ci-dessus.
La configuration système requise pour Deeplearning4j est la suivante:
--Java 1.7 ou version 64 bits supérieure (définissez également JAVA_HOME) --Maven ou Gradle --IntelliJ ou Eclipse
L'environnement utilisé pour écrire cet article est le suivant.
$ mvn -version
Apache Maven 3.5.0
Maven home: /usr/share/maven
Java version: 1.8.0_171, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-openjdk-amd64/jre
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.13.0-21-generic", arch: "amd64", family: "unix"
$ git --version
git version 2.14.1
Cela prend beaucoup d'espace disque, donc vous voudrez peut-être construire uniquement le sous-projet dl4j-examples
des dl4j-examples
. Si vous souhaitez créer l'ensemble du projet, vous avez besoin d'au moins 15 Go d'espace libre.
: avertissement: Un grand nombre de fichiers JAR seront téléchargés dans les répertoires suivants, donc lorsque vous supprimez le répertoire de Deeplearning4j qui était git clone
, supprimez également ce répertoire.
.m2/repository/org/deeplearning4j/
Pour créer l'environnement de développement, procédez comme suit:
$ git clone https://github.com/deeplearning4j/dl4j-examples.git
$ cd dl4j-examples/dl4j-examples
$ mvn clean install
Cependant, comme il est utilisé pour dessiner l'écran, veuillez également installer OpenJFX (Java FX) si nécessaire. Sans Java FX, vous obtiendrez l'erreur suivante au moment de la construction:
[ERROR] /home/tamura/git/dl4j-examples/dl4j-examples/src/main/java/org/deeplearning4j/examples/recurrent/character/harmonies/Piano.java:[4,24]Paquet javafx.l'animation n'existe pas
Si vous avez Ubuntu 17.10, vous pouvez l'installer avec:
$ sudo apt-get install openjfx
Après avoir construit l'environnement de développement, essayons l'identification des nombres manuscrits (l'exemple que nous avons vu plus tôt) qui se positionnent comme "Hello world!" Dans le deep learning. Dans l'apprentissage profond, une grande quantité de données est d'abord «apprise» pour en déduire les paramètres optimaux. Ensuite, sur cette base, nous faisons une «prédiction». La procédure est la suivante.
Lorsque la génération est terminée, ouvrez le projet dans IntelliJ.
Ouvrez le code source de ʻorg.deeplearning4j.examples.convolution.mnist.MnistClassifier` et exécutez-le (cliquez sur le bouton exécuter du triangle vert sur le côté gauche de l'éditeur) (cette étape fait "apprendre") ..
以下のようなメッセージが出力されます。
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -javaagent:/home/tamura/idea-IC-181.5087.20 ... (omis) ... org.deeplearning4j.examples.convolution.mnist.MnistClassifier
o.d.e.c.m.MnistClassifier - Data load and vectorization...
o.d.i.r.BaseImageRecordReader - ImageRecordReader: 10 label classes inferred using label generator ParentPathLabelGenerator
o.d.i.r.BaseImageRecordReader - ImageRecordReader: 10 label classes inferred using label generator ParentPathLabelGenerator
o.d.e.c.m.MnistClassifier - Network configuration and training...
o.n.l.f.Nd4jBackend - Loaded [CpuBackend] backend
o.n.n.NativeOpsHolder - Number of threads used for NativeOps: 1
o.n.n.Nd4jBlas - Number of threads used for BLAS: 1
o.n.l.a.o.e.DefaultOpExecutioner - Backend used: [CPU]; OS: [Linux]
o.n.l.a.o.e.DefaultOpExecutioner - Cores: [4]; Memory: [0.9GB];
o.n.l.a.o.e.DefaultOpExecutioner - Blas vendor: [MKL]
o.d.n.m.MultiLayerNetwork - Starting MultiLayerNetwork with WorkspaceModes set to [training: ENABLED; inference: ENABLED], cacheMode set to [NONE]
o.d.o.l.ScoreIterationListener - Score at iteration 0 is 2.4694731759178388
o.d.o.l.ScoreIterationListener - Score at iteration 10 is 1.078069156582683
o.d.o.l.ScoreIterationListener - Score at iteration 20 is 0.7327581484283221
... (Omis) ... o.d.o.l.ScoreIterationListener - Score at iteration 1100 is 0.20279510458591593 o.d.o.l.ScoreIterationListener - Score at iteration 1110 is 0.10997898485405874 o.d.e.c.m.MnistClassifier - Completed epoch 0 o.d.e.c.m.MnistClassifier -
========================Evaluation Metrics========================
# of classes: 10
Accuracy: 0.9891
Precision: 0.9891
Recall: 0.9890
F1 Score: 0.9891
Precision, recall & F1: macro-averaged (equally weighted avg. of 10 classes)
=========================Confusion Matrix=========================
0 1 2 3 4 5 6 7 8 9
---------------------------------------------------
973 0 0 0 0 0 2 2 3 0 | 0 = 0
0 1132 0 1 0 1 1 0 0 0 | 1 = 1
2 3 1018 1 0 0 1 6 1 0 | 2 = 2
0 0 1 1000 0 3 0 4 1 1 | 3 = 3
0 0 1 0 973 0 3 0 0 5 | 4 = 4
1 0 0 5 0 882 2 1 1 0 | 5 = 5
5 2 0 0 2 3 944 0 2 0 | 6 = 6
0 2 4 0 0 0 0 1017 2 3 | 7 = 7
3 0 2 1 0 0 1 2 961 4 | 8 = 8
4 2 1 1 3 0 0 6 1 991 | 9 = 9
Confusion matrix format: Actual (rowClass) predicted as (columnClass) N times
==================================================================
Process finished with exit code 0
```
Alors, quel est le mécanisme pour y parvenir? Images numériques manuscrites "d'apprentissage" [code source Mnist Classifier](https://github.com/deeplearning4j/dl4j-examples/blob/master/dl4j-examples/src/main/java/org/deeplearning4j/examples/ Jetons un coup d'œil à convolution / mnist / MnistClassifier.java) d'en haut.
: information_source: Une connaissance de base du deep learning est nécessaire pour comprendre les sections suivantes.
Le premier est la destination de téléchargement de l'image numérique manuscrite, les constantes du répertoire temporaire pour le décompresser et les variables de champ de l'enregistreur.
MnistClassifier
public class MnistClassifier {
private static final Logger log = LoggerFactory.getLogger(MnistClassifier.class);
private static final String basePath = System.getProperty("java.io.tmpdir") + "/mnist";
private static final String dataUrl = "http://github.com/myleott/mnist_png/raw/master/mnist_png.tar.gz";
Et cela devient la méthode main ()
de cette classe. L'appel de cette méthode commencera à apprendre. L'image d'entrée est transmise à la couche d'entrée sous forme de données 3D avec 1 canal et 28 pixels chacun dans les directions verticale et horizontale. Puisqu'il identifie les nombres de 1 à 10, il a 10 couches de sortie, 54 tailles de lots et 1 époque.
public static void main(String[] args) throws Exception {
int height = 28;
int width = 28;
int channels = 1; // single channel for grayscale images
int outputNum = 10; // 10 digits classification
int batchSize = 54;
int nEpochs = 1;
int iterations = 1;
int seed = 1234;
Random randNumGen = new Random(seed);
Ensuite, téléchargez le mnist_png.tar.gz
compressé de 70 000 images numérotées manuscrites depuis GitHub. Et décompressez-le.
log.info("Data load and vectorization...");
String localFilePath = basePath + "/mnist_png.tar.gz";
if (DataUtilities.downloadFile(dataUrl, localFilePath))
log.debug("Data downloaded from {}", dataUrl);
if (!new File(basePath + "/mnist_png").exists())
DataUtilities.extractTarGz(localFilePath, basePath);
Divisez les données d'apprentissage (formation) (60 000) et les données de test (10 000) et stockez-les dans les variables d'itérateur de «trainIter» et «testIter», respectivement.
// vectorization of train data
File trainData = new File(basePath + "/mnist_png/training");
FileSplit trainSplit = new FileSplit(trainData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator(); // parent path as the image label
ImageRecordReader trainRR = new ImageRecordReader(height, width, channels, labelMaker);
trainRR.initialize(trainSplit);
DataSetIterator trainIter = new RecordReaderDataSetIterator(trainRR, batchSize, 1, outputNum);
// pixel values from 0-255 to 0-1 (min-max scaling)
DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
scaler.fit(trainIter);
trainIter.setPreProcessor(scaler);
// vectorization of test data
File testData = new File(basePath + "/mnist_png/testing");
FileSplit testSplit = new FileSplit(testData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);
ImageRecordReader testRR = new ImageRecordReader(height, width, channels, labelMaker);
testRR.initialize(testSplit);
DataSetIterator testIter = new RecordReaderDataSetIterator(testRR, batchSize, 1, outputNum);
testIter.setPreProcessor(scaler); // same normalization for better results
Ensuite, ajoutez le paramètre de taux d'apprentissage au HashMap
avec le nom de variable lrSchedule
. Si le taux d'apprentissage est élevé, l'apprentissage se déroulera rapidement dans la première moitié, mais il ne convergera pas facilement dans la seconde moitié, de sorte que le taux d'apprentissage est abaissé en fonction du nombre de cas traités. Dans ce programme, l'entraînement est répété 1 111 fois (= données d'entraînement: 60 000 / taille du lot: 54). Le taux d'apprentissage est progressivement abaissé en fonction du nombre de répétitions.
log.info("Network configuration and training...");
Map<Integer, Double> lrSchedule = new HashMap<>();
lrSchedule.put(0, 0.06); // iteration #, learning rate
lrSchedule.put(200, 0.05);
lrSchedule.put(600, 0.028);
lrSchedule.put(800, 0.0060);
lrSchedule.put(1000, 0.001);
À partir de là, le traitement principal de la construction du réseau neuronal est effectué. Ajoutez une couche au réseau neuronal en l'appelant avec la méthode layer ()
de NeuralNetConfiguration.Builder ()
. Aucune couche d'entrée supplémentaire n'est requise, donc la première couche à ajouter est la «couche de convolution». Ensuite, nous ajoutons une "couche de sous-échantillonnage". Puis répétez-le, suivi de DenseLayer
(couche entièrement connectée), et enfin ajoutez ʻOutputLayer` (couche de sortie). Il s'agit d'une configuration CNN (réseau de neurones convolutifs) souvent utilisée dans la reconnaissance d'images.
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
.seed(seed)
.l2(0.0005)
.updater(new Nesterovs(new MapSchedule(ScheduleType.ITERATION, lrSchedule)))
.weightInit(WeightInit.XAVIER)
.list()
.layer(0, new ConvolutionLayer.Builder(5, 5)
.nIn(channels)
.stride(1, 1)
.nOut(20)
.activation(Activation.IDENTITY)
.build())
.layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
.layer(2, new ConvolutionLayer.Builder(5, 5)
.stride(1, 1) // nIn need not specified in later layers
.nOut(50)
.activation(Activation.IDENTITY)
.build())
.layer(3, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
.kernelSize(2, 2)
.stride(2, 2)
.build())
.layer(4, new DenseLayer.Builder().activation(Activation.RELU)
.nOut(500).build())
.layer(5, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nOut(outputNum)
.activation(Activation.SOFTMAX)
.build())
.setInputType(InputType.convolutionalFlat(28, 28, 1)) // InputType.convolutional for normal image
.backprop(true).pretrain(false).build();
ʻActivation.IDENTITY est une fonction égale ($ \ scriptsize {f (x) = x} $, c'est-à-dire ne rien faire) pour la fonction d'activation, ʻActivation.RELU
est une fonction ReLU et ʻActivation.SOFTMAX` est souple. Signifie utiliser la fonction Max.
Cela peut être difficile à comprendre avec des mots seuls, j'ai donc essayé d'illustrer la configuration du réseau neuronal. En comparant ce chiffre avec le code source, si vous ne comprenez pas, veuillez consulter Deeplearning4j Cheat Sheet etc. N'expliquera pas).
Allons-nous en. Si vous l'appelez avec la méthode setListeners ()
de MultiLayerNetwork
, l'état d'apprentissage sera affiché périodiquement.
MultiLayerNetwork net = new MultiLayerNetwork(conf);
net.init();
net.setListeners(new ScoreIterationListener(10));
log.debug("Total num of params: {}", net.numParams());
Enfin, appelez la méthode fit ()
pour commencer l'entraînement avec les données d'entraînement. Lorsque la formation est terminée, donnez les données de test à MultiLayerNetwork.evaluate ()
et évaluez-les. Enfin, enregistrez les paramètres dérivés ensemble dans minist-model.zip
.
// evaluation while training (the score should go down)
for (int i = 0; i < nEpochs; i++) {
net.fit(trainIter);
log.info("Completed epoch {}", i);
Evaluation eval = net.evaluate(testIter);
log.info(eval.stats());
trainIter.reset();
testIter.reset();
}
ModelSerializer.writeModel(net, new File(basePath + "/minist-model.zip"), true);
}
}
Une autre classe, «MnistClassifierUI», lit ce «minist-model.zip» pour construire un réseau neuronal et «prédire» des images numériques manuscrites. Une explication détaillée de «MnistClassifierUI» est omise.
Modifions un peu le code source de MnistClassifier
et faisons diverses expériences.
Modifions la classe d'écouteur donnée à la méthode setListeners ()
de MultiLayerNetwork
par autre chose. Définissons la classe d'écouteur introduite dans cette page.
// net.setListeners(new ScoreIterationListener(10));
//Commentez la ligne du haut et ajoutez les 4 lignes du bas
UIServer uiServer = UIServer.getInstance();
StatsStorage statsStorage = new InMemoryStatsStorage();
uiServer.attach(statsStorage);
net.setListeners(Arrays.asList(new ScoreIterationListener(1), new StatsListener(statsStorage)));
Après avoir modifié le code source, exécutez à nouveau le programme. Ce qui suit est émis vers la sortie standard,
o.d.u.p.PlayUIServer - DL4J UI Server started at http://localhost:9000
Lorsque vous accédez à http: // localhost: 9000, vous verrez un graphique qui visualise la situation d'apprentissage actuelle d'une manière facile à comprendre, comme indiqué ci-dessous.
: information_source: Cliquez sur l'onglet "Langue" sur le côté droit de l'écran et sélectionnez japonais.
Vous pouvez voir la configuration de ce réseau de neurones dans un schéma simple en cliquant sur l'onglet "Système".
Ensuite, changeons l'algorithme d'optimisation en méthode Stocastic Gradient Descent (SGD). Remplacez Nesterovs
dans l'argument de la méthode ʻupdater ()
de NeuralNetConfiguration.Builder ()
par Sgd
.
Et quand j'ai lancé le programme, j'ai obtenu le résultat suivant.
========================Evaluation Metrics========================
# of classes: 10
Accuracy: 0.9698
Precision: 0.9696
Recall: 0.9697
F1 Score: 0.9697
Precision, recall & F1: macro-averaged (equally weighted avg. of 10 classes)
=========================Confusion Matrix=========================
0 1 2 3 4 5 6 7 8 9
---------------------------------------------------
969 0 1 0 0 2 3 1 4 0 | 0 = 0
0 1120 3 2 0 1 3 0 6 0 | 1 = 1
6 2 993 4 6 3 3 9 6 0 | 2 = 2
1 0 7 976 0 7 0 9 7 3 | 3 = 3
1 1 2 0 955 0 5 2 2 14 | 4 = 4
2 1 0 11 1 866 5 1 3 2 | 5 = 5
10 3 1 0 6 3 933 0 2 0 | 6 = 6
2 8 16 2 1 0 0 982 3 14 | 7 = 7
6 0 1 4 4 5 4 6 941 3 | 8 = 8
5 7 0 9 11 7 1 5 1 963 | 9 = 9
Confusion matrix format: Actual (rowClass) predicted as (columnClass) N times
==================================================================
La précision a un peu baissé. J'en ai essayé quelques-uns, mais dans ce cas, «Nesterovs» (la méthode de descente de gradient accélérée de Nesterov) semble être un bon choix.
Osons le déplacer avec des paramètres incorrects. Donnez WeightInit.ZERO
à la méthode weightInit ()
deNeuralNetConfiguration.Builder ()
pour mettre le poids initial à zéro.
De cette façon, le score se terminera autour de "2,3" avec presque aucun changement. Et enfin, toutes les images devraient être "1".
=========================Confusion Matrix=========================
0 1 2 3 4 5 6 7 8 9
---------------------------------------------------
0 980 0 0 0 0 0 0 0 0 | 0 = 0
0 1135 0 0 0 0 0 0 0 0 | 1 = 1
0 1032 0 0 0 0 0 0 0 0 | 2 = 2
0 1010 0 0 0 0 0 0 0 0 | 3 = 3
0 982 0 0 0 0 0 0 0 0 | 4 = 4
0 892 0 0 0 0 0 0 0 0 | 5 = 5
0 958 0 0 0 0 0 0 0 0 | 6 = 6
0 1028 0 0 0 0 0 0 0 0 | 7 = 7
0 974 0 0 0 0 0 0 0 0 | 8 = 8
0 1009 0 0 0 0 0 0 0 0 | 9 = 9
Confusion matrix format: Actual (rowClass) predicted as (columnClass) N times
==================================================================
En effet, toutes les valeurs de poids sont mises à jour uniformément.
J'ai donc essayé d'identifier facilement les numéros manuscrits en utilisant Deeplearning4j. Avec cela, je pense que nous avons franchi le premier pas vers l'apprentissage profond en Java. Le code source de git clone
contient de nombreux autres exemples. À l'étape suivante, vous souhaiterez peut-être essayer d'exécuter un autre programme. Ceux qui ne comprennent pas la théorie sont invités à lire les livres mentionnés ci-dessus.