Répondre aux questions avec Android

Exemple d'application de réponse aux questions sur Android

Ce didacticiel vous montre comment créer une application Android à l'aide de TensorFlow Lite pour fournir des réponses à des questions structurées en texte en langage naturel. L' exemple d'application utilise l'API du répondeur de questions BERT ( BertQuestionAnswerer ) dans la bibliothèque de tâches pour le langage naturel (NL) pour activer les modèles d'apprentissage automatique de réponse aux questions. L'application est conçue pour un appareil Android physique mais peut également fonctionner sur un émulateur d'appareil.

Si vous mettez à jour un projet existant, vous pouvez utiliser l'exemple d'application comme référence ou modèle. Pour obtenir des instructions sur la façon d'ajouter des questions-réponses à une application existante, reportez-vous à Mise à jour et modification de votre application .

Aperçu des réponses aux questions

La réponse aux questions est la tâche d'apprentissage automatique consistant à répondre à des questions posées en langage naturel. Un modèle de réponse aux questions entraîné reçoit un passage de texte et une question en entrée, et tente de répondre à la question sur la base de son interprétation des informations contenues dans le passage.

Un modèle de réponse aux questions est formé sur un ensemble de données de réponse aux questions, qui consiste en un ensemble de données de compréhension en lecture ainsi que des paires question-réponse basées sur différents segments de texte.

Pour plus d'informations sur la façon dont les modèles de ce didacticiel sont générés, reportez-vous au didacticiel BERT Question Answer with TensorFlow Lite Model Maker .

Modèles et ensemble de données

L'exemple d'application utilise le modèle Mobile BERT Q&A ( mobilebert ), qui est une version plus légère et plus rapide de BERT (Bidirectionnel Encoder Representations from Transformers). Pour plus d'informations sur mobilebert , consultez le document de recherche MobileBERT : un BERT compact indépendant des tâches pour les appareils à ressources limitées .

Le modèle mobilebert a été formé à l'aide de l'ensemble de données Stanford Question Answering Dataset ( SQuAD ), un ensemble de données de compréhension écrite composé d'articles de Wikipédia et d'un ensemble de paires questions-réponses pour chaque article.

Configurer et exécuter l'exemple d'application

Pour configurer l'application de réponse aux questions, téléchargez l'exemple d'application depuis GitHub et exécutez-la à l'aide d' Android Studio .

Configuration requise

  • Android Studio version 2021.1.1 (Bumblebee) ou supérieure.
  • SDK Android version 31 ou supérieure
  • Appareil Android avec une version minimale du système d'exploitation du SDK 21 (Android 7.0 - Nougat) avec le mode développeur activé ou un émulateur Android.

Obtenez l'exemple de code

Créez une copie locale de l'exemple de code. Vous utiliserez ce code pour créer un projet dans Android Studio et exécuter l'exemple d'application.

Pour cloner et configurer l'exemple de code :

  1. Cloner le dépôt git
    git clone https://github.com/tensorflow/examples.git
    
  2. Vous pouvez éventuellement configurer votre instance git pour utiliser une extraction fragmentée, afin de disposer uniquement des fichiers pour l'exemple d'application de réponse aux questions :
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/bert_qa/android
    

Importer et exécuter le projet

Créez un projet à partir de l'exemple de code téléchargé, générez le projet, puis exécutez-le.

Pour importer et créer l'exemple de projet de code :

  1. Démarrez Android Studio .
  2. Depuis Android Studio, sélectionnez Fichier > Nouveau > Importer un projet .
  3. Accédez au répertoire de code d'exemple contenant le fichier build.gradle ( .../examples/lite/examples/bert_qa/android/build.gradle ) et sélectionnez ce répertoire.
  4. Si Android Studio demande une synchronisation Gradle, choisissez OK.
  5. Assurez-vous que votre appareil Android est connecté à votre ordinateur et que le mode développeur est activé. Cliquez sur la flèche verte Run .

Si vous sélectionnez le bon répertoire, Android Studio crée un nouveau projet et le construit. Ce processus peut prendre quelques minutes, selon la vitesse de votre ordinateur et si vous avez utilisé Android Studio pour d'autres projets. Une fois la construction terminée, Android Studio affiche un message BUILD SUCCESSFUL dans le panneau d'état de sortie de construction .

Pour exécuter le projet :

  1. Depuis Android Studio, exécutez le projet en sélectionnant Exécuter > Exécuter… .
  2. Sélectionnez un appareil Android connecté (ou un émulateur) pour tester l'application.

Utilisation de l'application

Après avoir exécuté le projet dans Android Studio, l'application s'ouvre automatiquement sur l'appareil connecté ou l'émulateur d'appareil.

Pour utiliser l'exemple d'application du répondeur aux questions :

  1. Choisissez un sujet dans la liste des sujets.
  2. Choisissez une question suggérée ou saisissez la vôtre dans la zone de texte.
  3. Basculez la flèche orange pour exécuter le modèle.

L'application tente d'identifier la réponse à la question à partir du texte du passage. Si le modèle détecte une réponse dans le passage, l'application met en évidence la partie du texte pertinente pour l'utilisateur.

Vous disposez désormais d’une application de réponse aux questions fonctionnelle. Utilisez les sections suivantes pour mieux comprendre le fonctionnement de l'exemple d'application et comment implémenter les fonctionnalités de réponse aux questions dans vos applications de production :

Comment fonctionne l'exemple d'application

L'application utilise l'API BertQuestionAnswerer dans le package Bibliothèque de tâches pour langage naturel (NL) . Le modèle MobileBERT a été formé à l'aide de TensorFlow Lite Model Maker . L'application s'exécute sur CPU par défaut, avec la possibilité d'accélération matérielle à l'aide du délégué GPU ou NNAPI.

Les fichiers et répertoires suivants contiennent le code crucial pour cette application :

  • BertQaHelper.kt - Initialise le répondeur de questions et gère la sélection du modèle et des délégués.
  • QaFragment.kt - Gère et formate les résultats.
  • MainActivity.kt - Fournit la logique d'organisation de l'application.

Modifier votre candidature

Les sections suivantes expliquent les étapes clés pour modifier votre propre application Android afin d'exécuter le modèle présenté dans l'exemple d'application. Ces instructions utilisent l'exemple d'application comme point de référence. Les modifications spécifiques nécessaires pour votre propre application peuvent différer de l’exemple d’application.

Ouvrir ou créer un projet Android

Vous avez besoin d'un projet de développement Android dans Android Studio pour suivre le reste de ces instructions. Suivez les instructions ci-dessous pour ouvrir un projet existant ou en créer un nouveau.

Pour ouvrir un projet de développement Android existant :

  • Dans Android Studio, sélectionnez Fichier > Ouvrir et sélectionnez un projet existant.

Pour créer un projet de développement Android de base :

Pour plus d'informations sur l'utilisation d'Android Studio, reportez-vous à la documentation d'Android Studio .

Ajouter des dépendances de projet

Dans votre propre application, ajoutez des dépendances de projet spécifiques pour exécuter des modèles de machine learning TensorFlow Lite et accéder aux fonctions utilitaires. Ces fonctions convertissent des données telles que des chaînes dans un format de données tensoriel qui peut être traité par le modèle. Les instructions suivantes expliquent comment ajouter les dépendances de projet et de module requises à votre propre projet d'application Android.

Pour ajouter des dépendances de module :

  1. Dans le module qui utilise TensorFlow Lite, mettez à jour le fichier build.gradle du module pour inclure les dépendances suivantes.

    Dans l'exemple d'application, les dépendances se trouvent dans app/build.gradle :

    dependencies {
      ...
      // Import tensorflow library
      implementation 'org.tensorflow:tensorflow-lite-task-text:0.3.0'
    
      // Import the GPU delegate plugin Library for GPU inference
      implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.0'
      implementation 'org.tensorflow:tensorflow-lite-gpu:2.9.0'
    }
    

    Le projet doit inclure la bibliothèque de tâches Texte ( tensorflow-lite-task-text ).

    Si vous souhaitez modifier cette application pour qu'elle s'exécute sur une unité de traitement graphique (GPU), la bibliothèque GPU ( tensorflow-lite-gpu-delegate-plugin ) fournit l'infrastructure nécessaire pour exécuter l'application sur GPU, et Déléguer ( tensorflow-lite-gpu ) fournit la liste de compatibilité.

  2. Dans Android Studio, synchronisez les dépendances du projet en sélectionnant : Fichier > Synchroniser le projet avec les fichiers Gradle .

Initialiser les modèles ML

Dans votre application Android, vous devez initialiser le modèle de machine learning TensorFlow Lite avec des paramètres avant d'exécuter des prédictions avec le modèle.

Un modèle TensorFlow Lite est stocké sous forme de fichier *.tflite . Le fichier modèle contient la logique de prédiction et inclut généralement des métadonnées sur la manière d'interpréter les résultats de prédiction. Généralement, les fichiers de modèle sont stockés dans le répertoire src/main/assets de votre projet de développement, comme dans l'exemple de code :

  • <project>/src/main/assets/mobilebert_qa.tflite

Pour plus de commodité et de lisibilité du code, l'exemple déclare un objet compagnon qui définit les paramètres du modèle.

Pour initialiser le modèle dans votre application :

  1. Créez un objet compagnon pour définir les paramètres du modèle. Dans l'exemple d'application, cet objet se trouve dans BertQaHelper.kt :

    companion object {
        private const val BERT_QA_MODEL = "mobilebert.tflite"
        private const val TAG = "BertQaHelper"
        const val DELEGATE_CPU = 0
        const val DELEGATE_GPU = 1
        const val DELEGATE_NNAPI = 2
    }
    
  2. Créez les paramètres du modèle en créant un objet BertQaHelper et construisez un objet TensorFlow Lite avec bertQuestionAnswerer .

    Dans l'exemple d'application, cela se trouve dans la fonction setupBertQuestionAnswerer() dans BertQaHelper.kt :

    class BertQaHelper(
        ...
    ) {
        ...
        init {
            setupBertQuestionAnswerer()
        }
    
        fun clearBertQuestionAnswerer() {
            bertQuestionAnswerer = null
        }
    
        private fun setupBertQuestionAnswerer() {
            val baseOptionsBuilder = BaseOptions.builder().setNumThreads(numThreads)
            ...
            val options = BertQuestionAnswererOptions.builder()
                .setBaseOptions(baseOptionsBuilder.build())
                .build()
    
            try {
                bertQuestionAnswerer =
                    BertQuestionAnswerer.createFromFileAndOptions(context, BERT_QA_MODEL, options)
            } catch (e: IllegalStateException) {
                answererListener
                    ?.onError("Bert Question Answerer failed to initialize. See error logs for details")
                Log.e(TAG, "TFLite failed to load model with error: " + e.message)
            }
        }
        ...
        }
    

Activer l'accélération matérielle (facultatif)

Lors de l'initialisation d'un modèle TensorFlow Lite dans votre application, vous devez envisager d'utiliser les fonctionnalités d'accélération matérielle pour accélérer les calculs de prédiction du modèle. Les délégués TensorFlow Lite sont des modules logiciels qui accélèrent l'exécution de modèles d'apprentissage automatique à l'aide de matériel de traitement spécialisé sur un appareil mobile, tel qu'une unité de traitement graphique (GPU) ou des unités de traitement tensoriel (TPU).

Pour activer l'accélération matérielle dans votre application :

  1. Créez une variable pour définir le délégué que l'application utilisera. Dans l'exemple d'application, cette variable se trouve au début de BertQaHelper.kt :

    var currentDelegate: Int = 0
    
  2. Créez un sélecteur de délégués. Dans l'exemple d'application, le sélecteur de délégués se trouve dans la fonction setupBertQuestionAnswerer dans BertQaHelper.kt :

    when (currentDelegate) {
        DELEGATE_CPU -> {
            // Default
        }
        DELEGATE_GPU -> {
            if (CompatibilityList().isDelegateSupportedOnThisDevice) {
                baseOptionsBuilder.useGpu()
            } else {
                answererListener?.onError("GPU is not supported on this device")
            }
        }
        DELEGATE_NNAPI -> {
            baseOptionsBuilder.useNnapi()
        }
    }
    

L'utilisation de délégués pour exécuter des modèles TensorFlow Lite est recommandée, mais pas obligatoire. Pour plus d'informations sur l'utilisation de délégués avec TensorFlow Lite, consultez Délégués TensorFlow Lite .

Préparer les données pour le modèle

Dans votre application Android, votre code fournit des données au modèle à des fins d'interprétation en transformant les données existantes telles que le texte brut en un format de données Tensor pouvant être traité par votre modèle. Le Tensor que vous transmettez à un modèle doit avoir des dimensions, ou une forme, spécifiques qui correspondent au format des données utilisées pour entraîner le modèle. Cette application de réponse aux questions accepte les chaînes comme entrées pour le passage de texte et la question. Le modèle ne reconnaît pas les caractères spéciaux ni les mots non anglais.

Pour fournir des données de texte de passage au modèle :

  1. Utilisez l'objet LoadDataSetClient pour charger les données de texte du passage dans l'application. Dans l'exemple d'application, cela se trouve dans LoadDataSetClient.kt

    fun loadJson(): DataSet? {
        var dataSet: DataSet? = null
        try {
            val inputStream: InputStream = context.assets.open(JSON_DIR)
            val bufferReader = inputStream.bufferedReader()
            val stringJson: String = bufferReader.use { it.readText() }
            val datasetType = object : TypeToken<DataSet>() {}.type
            dataSet = Gson().fromJson(stringJson, datasetType)
        } catch (e: IOException) {
            Log.e(TAG, e.message.toString())
        }
        return dataSet
    }
    
  2. Utilisez l'objet DatasetFragment pour répertorier les titres de chaque passage de texte et démarrer l'écran Questions et réponses TFL . Dans l'exemple d'application, cela se trouve dans DatasetFragment.kt :

    class DatasetFragment : Fragment() {
        ...
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            val client = LoadDataSetClient(requireActivity())
            client.loadJson()?.let {
                titles = it.getTitles()
            }
            ...
        }
       ...
    }
    
  3. Utilisez la fonction onCreateViewHolder dans l'objet DatasetAdapter pour présenter les titres de chaque passage de texte. Dans l'exemple d'application, cela se trouve dans DatasetAdapter.kt :

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding = ItemDatasetBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return ViewHolder(binding)
    }
    

Pour soumettre des questions d'utilisateur au modèle :

  1. Utilisez l'objet QaAdapter pour fournir la question au modèle. Dans l'exemple d'application, cela se trouve dans QaAdapter.kt :

    class QaAdapter(private val question: List<String>, private val select: (Int) -> Unit) :
      RecyclerView.Adapter<QaAdapter.ViewHolder>() {
    
      inner class ViewHolder(private val binding: ItemQuestionBinding) :
          RecyclerView.ViewHolder(binding.root) {
          init {
              binding.tvQuestionSuggestion.setOnClickListener {
                  select.invoke(adapterPosition)
              }
          }
    
          fun bind(question: String) {
              binding.tvQuestionSuggestion.text = question
          }
      }
      ...
    }
    

Exécuter des prédictions

Dans votre application Android, une fois que vous avez initialisé un objet BertQuestionAnswerer , vous pouvez commencer à saisir des questions sous forme de texte en langage naturel dans le modèle. Le modèle tente d'identifier la réponse dans le passage de texte.

Pour exécuter des prédictions :

  1. Créez une fonction answer , qui exécute le modèle et mesure le temps nécessaire pour identifier la réponse ( inferenceTime ). Dans l'exemple d'application, la fonction answer se trouve dans BertQaHelper.kt :

    fun answer(contextOfQuestion: String, question: String) {
        if (bertQuestionAnswerer == null) {
            setupBertQuestionAnswerer()
        }
    
        var inferenceTime = SystemClock.uptimeMillis()
    
        val answers = bertQuestionAnswerer?.answer(contextOfQuestion, question)
        inferenceTime = SystemClock.uptimeMillis() - inferenceTime
        answererListener?.onResults(answers, inferenceTime)
    }
    
  2. Transmettez les résultats de answer à l’objet écouteur.

    interface AnswererListener {
        fun onError(error: String)
        fun onResults(
            results: List<QaAnswer>?,
            inferenceTime: Long
        )
    }
    

Gérer la sortie du modèle

Après avoir saisi une question, le modèle propose un maximum de cinq réponses possibles dans le passage.

Pour obtenir les résultats du modèle :

  1. Créez une fonction onResult pour que l'objet écouteur gère la sortie. Dans l'exemple d'application, l'objet écouteur se trouve dans BertQaHelper.kt

    interface AnswererListener {
        fun onError(error: String)
        fun onResults(
            results: List<QaAnswer>?,
            inferenceTime: Long
        )
    }
    
  2. Mettez en surbrillance les sections du passage en fonction des résultats. Dans l'exemple d'application, celui-ci se trouve dans QaFragment.kt :

    override fun onResults(results: List<QaAnswer>?, inferenceTime: Long) {
        results?.first()?.let {
            highlightAnswer(it.text)
        }
    
        fragmentQaBinding.tvInferenceTime.text = String.format(
            requireActivity().getString(R.string.bottom_view_inference_time),
            inferenceTime
        )
    }
    

Une fois que le modèle a renvoyé un ensemble de résultats, votre application peut agir sur ces prédictions en présentant le résultat à votre utilisateur ou en exécutant une logique supplémentaire.

Prochaines étapes