[SWIFT] Comment modifier un calque avec Core ML Tools

Lors de la conversion d'un modèle à l'aide de Core ML Tools, vous souhaiterez peut-être ajouter des couches et insérer un traitement de calcul entre les deux, ou couper une partie de la couche, mais cet article vous expliquera comment le faire.

Étant donné que Core ML Tools a peu de documents et peu d'informations sur le net, je dois le rechercher en lisant le code source, mais je pense que l'édition des couches est un cas d'utilisation courant lors de la conversion vers Core ML. J'espère que cet article vous aidera.

Cas du modèle: mettez le remodelage avant la sortie pour changer la forme de la sortie

En tant que cas de modèle, ajoutons un remodelage pour créer la forme de sortie du modèle (19) (1,1,19).

Cela n'a presque aucun sens parce que j'ajoute simplement des dimensions, mais je le fais parce que je veux en faire un cas simple.

Le modèle à utiliser est celui qui prédit (classe) le résultat de l'ajout de deux nombres à un chiffre. Le résultat du classement sera de 19 séquences. En effet, l'ajout de nombres à un chiffre donne un total de 19 types de problèmes de classification de 0 à 18.

Cela ressemble à ceci sur la figure.

Ce modèle est le même que celui utilisé dans l'article précédent. Dans l'article précédent, nous avons donné la sortie jusqu'au nom de l'étiquette, mais cette fois, nous allons simplement afficher 19 probabilités telles qu'elles sont.

Exécutez un modèle simple réalisé avec Keras sur iOS en utilisant CoreML

procédure

Ajoutons maintenant le calque Reshape. Travaillez sur Google Colaboratory.

Le code complet de ce travail peut être trouvé ici. https://gist.github.com/TokyoYoshida/2fada34313385d63b666253490b5f3f4

** 1. Charger le modèle Keras **

J'utiliserai un modèle réalisé avec Keras. La partie création de modèle est omise car elle se trouve dans Article précédent.

notebook


from keras.models import load_model
keras_model = load_model('my_model.h5')

** 2. Convertir en CoreML **

Convertissez en CoreML. Je ne mets pas d'étiquette cette fois, donc je la convertis simplement telle quelle.

notebook


from coremltools.converters import keras as converter
mlmodel = converter.convert(keras_model)

** 3. Charger avec le constructeur **

Chargez-le dans Neural Network Builder dans Core ML Tools.

notebook


import coremltools
spec = coremltools.utils.load_spec(coreml_model_path)
builder = coremltools.models.neural_network.NeuralNetworkBuilder(spec=spec)

Affiche les informations du modèle. À partir de là, vous pouvez voir que la sortie du modèle est nommée output1 et que la forme est 19.

notebook


spec.description.output

#Résultat de sortie
# [name: "output1"
# type {
#   multiArrayType {
#     shape: 19
#     dataType: DOUBLE
#   }
# }
# ]

Affiche les informations sur la couche.

notebook


builder.layers
#production
# ['dense_4',
#  'dense_4__activation__',
#  'dense_5',
#  'dense_5__activation__',
#  'dense_6',
#  'activation_18']

La cible cette fois est activation_18, qui est la couche de sortie. Remodeler la sortie de ce calque.

** 4. Ajouter un calque Rehape **

NeuralNetworkBuilder a une méthode appelée add_reshape qui vous permet d'ajouter une couche de remodelage.

~~ Il est important de noter ici qu'en utilisant le générateur, je pense que je peux ajouter intuitivement une couche au générateur qui représente le modèle d'origine, mais cette méthode ne fonctionne pas. ~~ (Ajouté le 27 octobre 2020, j'ai trouvé que je pouvais ajouter un calque au constructeur qui représente le modèle d'origine, donc je vais le corriger)

Par exemple, ajoutons le calque Reshape tel quel au modèle chargé précédemment dans le générateur.

notebook


reshape = builder.add_reshape(name='Reshape', input_name='activation_18', output_name='output', target_shape=(1,1,19), mode=0)

Quand je lis ceci avec Xcode, j'obtiens cette erreur.

Erreur Xcode


There was a problem decoding this CoreML document
validator error: Layer 'Reshape' consumes an input named 'activation_18' which is not present in this network.

Lorsque j'essaye d'effectuer une inférence CoreML sur Python, j'obtiens cette erreur:

Erreur Python


RuntimeError: Error compiling model: "Error reading protobuf spec. validator error: Layer 'Reshape' consumes an input named 'activation_18' which is not present in this network.".

J'ai eu du mal à le trouver sur Internet, mais je suis sûr qu'il y a des gens qui ont du mal à faire la même chose. La raison de cette erreur est que ʻinput_name = 'activation_18'. Ici, vous devez spécifier le nom de l'entrée ou de la sortie affichée dans builder.spec.description` dans le modèle d'origine.

~~ Pour contourner le problème, utilisez NeuralNetworkBuilder pour créer un modèle complet avec une seule couche et l'ajouter au modèle d'origine. ~~ (Corrigé le 27 octobre 2020) Si l'argument nom_entrée de add_reshape correspond au nom_sortie du modèle d'origine, vous pouvez connecter les couches avec succès.

Ajoutez un calque de remodelage à votre modèle. input spécifie la sortie du modèle d'origine, c'est-à-dire ʻoutput1. J'avais l'impression que c'était l'entrée du nouveau modèle, ʻinput2, mais il semble que la sortie du modèle d'origine et l'entrée de la couche de remodelage ne fonctionnent pas bien. output spécifie ʻoutput2`, qui est la sortie du nouveau modèle. target_shape spécifie (1,1,19) quelle est la forme que vous souhaitez convertir cette fois.

notebook


builder.add_reshape(name='Reshape', input_name='output1', output_name='output2', target_shape=(1,1,19), mode=0)

Vous avez maintenant un modèle avec remodelage ajouté.

Vérifiez les informations de la couche.

notebook


builder.layers

#production
# ['dense_4',
#  'dense_4__activation__',
#  'dense_5',
#  'dense_5__activation__',
#  'dense_6',
#  'activation_18',
#  'Reshape']

** 5. Modifiez les informations de sortie du modèle **

Le calque de remodelage a été ajouté à la fin du modèle, mais cela n'a remplacé que le dernier calque du modèle, et les informations de sortie du modèle dans son ensemble n'ont pas changé.

Si vous vérifiez les informations de sortie à titre d'essai, vous pouvez voir qu'il s'agit toujours de (19) au lieu de la forme attendue (1,1,19).

notebook


spec.description.output
# [name: "output1"
# type {
#   multiArrayType {
#     shape: 19
#     dataType: DOUBLE
#   }
# }
# ]

Changer les informations de sortie du modèle est également très bizarre et entraînera une erreur si vous essayez de l'attribuer à builder.spec.description.output [0]. Par conséquent, spécifiez-le lors de l'affichage ou de l'ajout.

notebook


//Supprimez une information de sortie. Puisqu'il n'y avait qu'une seule information de sortie cette fois, les informations de sortie disparaissent
builder.spec.description.output.pop()
//Ajouter une information de sortie
builder.spec.description.output.add()
//Spécifier les attributs des informations de sortie
output = builder.spec.description.output[0]
output.name = "output2"
output.type.multiArrayType.dataType = coremltools.proto.FeatureTypes_pb2.ArrayFeatureType.ArrayDataType.Value('DOUBLE')
//En tant qu'informations sur la forme(1,1,19)Mettre en place
output.type.multiArrayType.shape.append(1)
output.type.multiArrayType.shape.append(1)
output.type.multiArrayType.shape.append(19)

(Supplément) Dans l'exemple ci-dessus, les informations ont été ajoutées après les avoir nettoyées en les supprimant avec pop () et en les ajoutant avec add (), mais il est possible de réécrire directement uniquement la valeur d'attribut de la sortie qui existe à l'origine.

Par exemple, réécrivez la forme comme suit.

Exemple de forme de réécriture


builder.spec.description.output[0].type.multiArrayType.shape[0] = 100

(Fin du supplément)

Si vous vérifiez les informations de sortie, vous pouvez voir que les paramètres ont réussi.

notebook


builder.spec.description.output
# [name: "output2"
# type {
#   multiArrayType {
#     shape: 1
#     shape: 1
#     shape: 19
#     dataType: DOUBLE
#   }
# }
# ]

** 6. Vérifiez le résultat de sortie du modèle avec Jupyter Notebook **

Vérifiez le résultat de sortie du modèle avec Jupyter Notebook s'exécutant sur Mac. Pourquoi Jupyter Notebook sur Mac? Cependant, puisque le back-end de Google Colaboratory est Linux, CoreML ne peut pas être déduit et il n'est pas possible de confirmer si le modèle fonctionne correctement.

Vous pouvez déduire avec Xcode, mais Xcode peut vous donner une erreur lorsque vous lisez un modèle étrange, mais il peut également supprimer l'erreur suivante au moment de la construction.

Erreur Xcode


Command CoreMLModelCompile failed with a nonzero exit code

Si vous obtenez une telle erreur, il est judicieux de l'exécuter dans Jupyter Notebook pour voir les détails de l'erreur.

Alors, démarrez Jupyter Notebook.

Voici le code complet. https://gist.github.com/TokyoYoshida/632b4c8070aa6c937539e4ae261a2740

Donnez [2,3] comme entrée au modèle et essayez d'effectuer l'inférence.

JupyterNoteBook


coreml_model_path= "my_model_with_builder.mlmodel"

import coremltools
spec = coremltools.utils.load_spec(coreml_model_path)
builder = coremltools.models.neural_network.NeuralNetworkBuilder(spec=spec)

mlmodel = coremltools.models.MLModel(spec)

mlmodel.predict({'input1': np.array([2.0,3.0])})

# {'output2': array([[[2.56040733e-21, 7.25779588e-15, 1.99342376e-10, 1.11184195e-09,
#           5.92091055e-05, 9.99939799e-01, 9.72097268e-07, 1.13292452e-14,
#           4.43997455e-23, 5.00404492e-33, 0.00000000e+00, 0.00000000e+00,
#           0.00000000e+00, 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
#           0.00000000e+00, 0.00000000e+00, 0.00000000e+00]]])}

Puisque ce modèle est un modèle qui ajoute deux éléments, la probabilité du cinquième élément est presque de 1, vous pouvez donc voir qu'il peut être déduit correctement.

De plus, puisque le tableau de sortie est «[[[19 tableaux avec probabilité]]]», il est confirmé que la forme est (1,1,19) comme prévu. Je peux le faire.

** 7. Vérifiez le résultat de sortie du modèle avec Xcode & App **

Enfin, placez le résultat de sortie du modèle sur Xcode et vérifiez-le avec l'application.

Le code complet est ici. https://github.com/TokyoYoshida/CoreMLSimpleTest

ViewController.swift


let model = my_model_with_builder()
let inputArray = try! MLMultiArray([2,3])
let inputToModel = my_model_with_builderInput(input1: inputArray)
if let prediction = try? model.prediction(input: inputToModel) {
print(prediction.output2)
try! print(prediction.output2.reshaped(to: [19]))
}

// # Double 1 x 1 x 19 array
// # Double 19 vector
// # [2.422891379885771e-21,7.01752566646674e-15,1.959301054732521e-10,1.089580203839091e-09,5.933549255132675e-05,0.9999396800994873,9.530076567898504e-07,1.087061586308846e-14,4.16250629238845e-23,4.617410135639087e-33,1.401298464324817e-45,1.401298464324817e-45,1.401298464324817e-45,1.401298464324817e-45,1.401298464324817e-45,1.401298464324817e-45,0,0,0]
// 

Vous pouvez voir que c'est également comme prévu. Les nombres de probabilités peuvent être légèrement différents, mais ne vous inquiétez pas, cela est dû au recyclage du modèle.

Edition supplémentaire Comment supprimer un calque

Je n'ai pas confirmé l'opération, mais j'écrirai également comment supprimer le calque.

Supprimez le calque.

notebook


layers = builder.spec.neuralNetwork.layers
#Obtenez le deuxième à l'arrière du calque(activation_18)
item = layers[-2]
#Supprimer cet élément
layers.remove(item)

Si vous regardez les informations de la couche, vous pouvez voir qu'elle a été supprimée.

notebook


for layer in layers:
  print(layer.name)
# dense_4
# dense_4__activation__
# dense_5
# dense_5__activation__
# dense_6
# Reshape

finalement

Note publie régulièrement sur le développement iOS, alors suivez-nous. https://note.com/tokyoyoshida

Il est également publié sur Twitter. https://twitter.com/jugemjugemjugem

Recommended Posts

Comment modifier un calque avec Core ML Tools
Comment écrire un mod de base dans Minecraft Forge 1.15.2
Comment utiliser Lombok au printemps
Comment trouver May'n dans XPath
Comment masquer la barre de défilement dans WebView
Comment exécuter JUnit dans Eclipse
Comment itérer indéfiniment en Ruby
Comment exécuter Ant dans Gradle
Comment maîtriser la programmation en 3 mois
Comment apprendre JAVA en 7 jours
Comment obtenir des paramètres dans Spark
Comment installer Bootstrap dans Ruby
Comment utiliser InjectorHolder dans OpenAM
Comment installer jQuery dans Rails 6
Comment utiliser les classes en Java?
Comment nommer des variables en Java
Comment définir Lombok dans Eclipse
Comment concaténer des chaînes avec Java
Comment installer Swiper in Rails
[swift5] Comment spécifier la couleur en hexadécimal
Comment implémenter la fonctionnalité de recherche dans Rails
Comment implémenter le calcul de la date en Java
Comment implémenter le filtre de Kalman par Java
Prise en charge multilingue de Java Comment utiliser les paramètres régionaux
Comment changer le nom de l'application dans les rails
Comment utiliser le volume nommé dans docker-compose.yml
Comment insérer une vidéo dans Rails
Comment normaliser le pied de page d'en-tête dans Thymeleaf
Comment inclure Spring Tool dans Eclipse 4.6.3?
Comment ajouter un fichier jar dans ScalaIDE
Comment faire une conversion de base en Java
Comment avoir des paramètres dans la méthode link_to
Comment utiliser Docker dans VSCode DevContainer
Comment utiliser MySQL dans le didacticiel Rails
Comment fixer la date système dans JUnit
Comment appliquer les conventions de codage en Java
Comment intégrer Janus Graph dans Java
[rails] Comment configurer le routage dans les ressources
Comment obtenir la date avec Java
Comment implémenter la fonctionnalité de classement dans Rails
Comment utiliser les variables d'environnement dans RubyOnRails
Comment implémenter le traitement asynchrone dans Outsystems
Comment publier une bibliothèque dans jCenter
Comment spécifier l'attribut id dans JSF
Comprendre en 5 minutes !! Comment utiliser Docker
Comment écraser les données Firebase avec Swift
Comment utiliser credentials.yml.enc introduit à partir de Rails 5.2
Comment assembler JSON directement dans Jackson
[Pour les débutants] Comment déboguer avec Eclipse
Comment utiliser ExpandableListView dans Android Studio
Comment afficher les messages d'erreur en japonais
Résumé de la sélection des éléments dans Selenium
Comment obtenir les informations d'identification Keycloak dans la classe d'intercepteur