Respondendo a perguntas com o Android

Aplicativo de exemplo de resposta a perguntas no Android

Este tutorial mostra como criar um aplicativo Android usando o TensorFlow Lite para fornecer respostas a perguntas estruturadas em texto em linguagem natural. O aplicativo de exemplo usa a API do respondedor de perguntas BERT ( BertQuestionAnswerer ) na biblioteca de tarefas para linguagem natural (NL) para habilitar modelos de aprendizado de máquina de respostas a perguntas. O aplicativo foi desenvolvido para um dispositivo Android físico, mas também pode ser executado em um emulador de dispositivo.

Se você estiver atualizando um projeto existente, poderá usar o aplicativo de exemplo como referência ou modelo. Para obter instruções sobre como adicionar respostas a perguntas a um aplicativo existente, consulte Atualizando e modificando seu aplicativo .

Visão geral de respostas a perguntas

Responder a perguntas é a tarefa de aprendizado de máquina de responder a perguntas feitas em linguagem natural. Um modelo de resposta a perguntas treinado recebe uma passagem de texto e uma pergunta como entrada e tenta responder à pergunta com base em sua interpretação das informações contidas na passagem.

Um modelo de resposta a perguntas é treinado em um conjunto de dados de respostas a perguntas, que consiste em um conjunto de dados de compreensão de leitura junto com pares de perguntas e respostas com base em diferentes segmentos de texto.

Para obter mais informações sobre como os modelos neste tutorial são gerados, consulte o tutorial BERT Question Answer with TensorFlow Lite Model Maker .

Modelos e conjunto de dados

O aplicativo de exemplo usa o modelo Mobile BERT Q&A ( mobilebert ), que é uma versão mais leve e rápida do BERT (Bidirectional Encoder Representations from Transformers). Para obter mais informações sobre mobilebert , consulte o documento de pesquisa MobileBERT: um BERT agnóstico de tarefa compacto para dispositivos limitados por recursos .

O modelo mobilebert foi treinado usando o conjunto de dados Stanford Question Answering Dataset ( SQuAD ), um conjunto de dados de compreensão de leitura composto por artigos da Wikipedia e um conjunto de pares de perguntas e respostas para cada artigo.

Configure e execute o aplicativo de exemplo

Para configurar o aplicativo de resposta a perguntas, baixe o aplicativo de exemplo do GitHub e execute-o usando o Android Studio .

requisitos de sistema

  • Android Studio versão 2021.1.1 (Bumblebee) ou superior.
  • Android SDK versão 31 ou superior
  • Dispositivo Android com uma versão mínima do SO do SDK 21 (Android 7.0 - Nougat) com o modo de desenvolvedor ativado ou um emulador do Android.

Obtenha o código de exemplo

Crie uma cópia local do código de exemplo. Você usará esse código para criar um projeto no Android Studio e executar o aplicativo de exemplo.

Para clonar e configurar o código de exemplo:

  1. Clone o repositório git
    git clone https://github.com/tensorflow/examples.git
    
  2. Opcionalmente, configure sua instância do git para usar check-out esparso, para que você tenha apenas os arquivos para o aplicativo de exemplo de resposta a perguntas:
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/bert_qa/android
    

Importar e executar o projeto

Crie um projeto a partir do código de exemplo baixado, compile o projeto e execute-o.

Para importar e construir o projeto de código de exemplo:

  1. Inicie o Android Studio .
  2. No Android Studio, selecione File > New > Import Project .
  3. Navegue até o diretório de código de exemplo que contém o arquivo build.gradle ( .../examples/lite/examples/bert_qa/android/build.gradle ) e selecione esse diretório.
  4. Se o Android Studio solicitar um Gradle Sync, escolha OK.
  5. Certifique-se de que seu dispositivo Android esteja conectado ao seu computador e que o modo de desenvolvedor esteja ativado. Clique na seta verde Run .

Se você selecionar o diretório correto, o Android Studio cria um novo projeto e o compila. Esse processo pode levar alguns minutos, dependendo da velocidade do seu computador e se você já usou o Android Studio para outros projetos. Quando a compilação for concluída, o Android Studio exibirá uma mensagem BUILD SUCCESSFUL no painel de status Build Output .

Para executar o projeto:

  1. No Android Studio, execute o projeto selecionando Run > Run… .
  2. Selecione um dispositivo Android conectado (ou emulador) para testar o aplicativo.

Usando o aplicativo

Após executar o projeto no Android Studio, o aplicativo é aberto automaticamente no dispositivo conectado ou no emulador de dispositivo.

Para usar o aplicativo de exemplo do respondedor de perguntas:

  1. Escolha um tópico da lista de assuntos.
  2. Escolha uma pergunta sugerida ou digite a sua na caixa de texto.
  3. Alterne a seta laranja para executar o modelo.

O aplicativo tenta identificar a resposta para a pergunta do texto da passagem. Se o modelo detectar uma resposta na passagem, o aplicativo destacará o intervalo de texto relevante para o usuário.

Agora você tem um aplicativo de resposta a perguntas em funcionamento. Use as seções a seguir para entender melhor como o aplicativo de exemplo funciona e como implementar recursos de resposta a perguntas em seus aplicativos de produção:

Como o aplicativo de exemplo funciona

O aplicativo usa a API BertQuestionAnswerer dentro da biblioteca de tarefas para o pacote de linguagem natural (NL) . O modelo MobileBERT foi treinado usando o TensorFlow Lite Model Maker . O aplicativo é executado na CPU por padrão, com a opção de aceleração de hardware usando o delegado da GPU ou NNAPI.

Os seguintes arquivos e diretórios contêm o código crucial para este aplicativo:

  • BertQaHelper.kt - Inicializa o respondedor de perguntas e trata do modelo e da seleção de delegados.
  • QaFragment.kt - Manipula e formata os resultados.
  • MainActivity.kt - Fornece a lógica de organização do aplicativo.

Modifique seu aplicativo

As seções a seguir explicam as principais etapas para modificar seu próprio aplicativo Android para executar o modelo mostrado no aplicativo de exemplo. Estas instruções usam o aplicativo de exemplo como ponto de referência. As alterações específicas necessárias para seu próprio aplicativo podem variar do aplicativo de exemplo.

Abra ou crie um projeto Android

Você precisa de um projeto de desenvolvimento Android no Android Studio para seguir o restante destas instruções. Siga as instruções abaixo para abrir um projeto existente ou criar um novo.

Para abrir um projeto de desenvolvimento Android existente:

  • No Android Studio, selecione Arquivo > Abrir e selecione um projeto existente.

Para criar um projeto básico de desenvolvimento Android:

Para obter mais informações sobre como usar o Android Studio, consulte a documentação do Android Studio .

Adicionar dependências do projeto

Em seu próprio aplicativo, adicione dependências de projeto específicas para executar modelos de aprendizado de máquina do TensorFlow Lite e acessar funções do utilitário. Essas funções convertem dados como strings em um formato de dados de tensor que pode ser processado pelo modelo. As instruções a seguir explicam como adicionar as dependências necessárias do projeto e do módulo ao seu próprio projeto de aplicativo Android.

Para adicionar dependências de módulo:

  1. No módulo que usa o TensorFlow Lite, atualize o arquivo build.gradle do módulo para incluir as dependências a seguir.

    No aplicativo de exemplo, as dependências estão localizadas em 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'
    }
    

    O projeto deve incluir a biblioteca de tarefas Text ( tensorflow-lite-task-text ).

    Se você deseja modificar este aplicativo para ser executado em uma unidade de processamento gráfico (GPU), a biblioteca GPU ( tensorflow-lite-gpu-delegate-plugin ) fornece a infraestrutura para executar o aplicativo na GPU e Delegate ( tensorflow-lite-gpu ) fornece a lista de compatibilidade.

  2. No Android Studio, sincronize as dependências do projeto selecionando: File > Sync Project with Gradle Files .

Inicialize os modelos de ML

No seu aplicativo Android, você deve inicializar o modelo de aprendizado de máquina do TensorFlow Lite com parâmetros antes de executar previsões com o modelo.

Um modelo do TensorFlow Lite é armazenado como um arquivo *.tflite . O arquivo de modelo contém a lógica de previsão e normalmente inclui metadados sobre como interpretar os resultados da previsão. Normalmente, os arquivos de modelo são armazenados no diretório src/main/assets do seu projeto de desenvolvimento, como no exemplo de código:

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

Por conveniência e legibilidade do código, o exemplo declara um objeto complementar que define as configurações do modelo.

Para inicializar o modelo em seu aplicativo:

  1. Crie um objeto complementar para definir as configurações do modelo. No aplicativo de exemplo, este objeto está localizado em 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. Crie as configurações para o modelo criando um objeto BertQaHelper e construa um objeto TensorFlow Lite com bertQuestionAnswerer .

    No aplicativo de exemplo, isso está localizado na função setupBertQuestionAnswerer() dentro de 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)
            }
        }
        ...
        }
    

Ativar aceleração de hardware (opcional)

Ao inicializar um modelo do TensorFlow Lite em seu aplicativo, considere o uso de recursos de aceleração de hardware para acelerar os cálculos de previsão do modelo. Os delegados do TensorFlow Lite são módulos de software que aceleram a execução de modelos de aprendizado de máquina usando hardware de processamento especializado em um dispositivo móvel, como unidade de processamento gráfico (GPUs) ou unidades de processamento de tensor (TPUs).

Para habilitar a aceleração de hardware em seu aplicativo:

  1. Crie uma variável para definir o delegado que o aplicativo usará. No aplicativo de exemplo, essa variável está localizada no início de BertQaHelper.kt :

    var currentDelegate: Int = 0
    
  2. Crie um seletor de delegado. No aplicativo de exemplo, o seletor de delegado está localizado na função setupBertQuestionAnswerer em 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()
        }
    }
    

O uso de representantes para executar modelos do TensorFlow Lite é recomendado, mas não obrigatório. Para obter mais informações sobre como usar representantes com o TensorFlow Lite, consulte Delegados do TensorFlow Lite .

Preparar dados para o modelo

Em seu aplicativo Android, seu código fornece dados ao modelo para interpretação, transformando dados existentes, como texto bruto, em um formato de dados do Tensor que pode ser processado por seu modelo. O tensor que você passa para um modelo deve ter dimensões específicas, ou forma, que correspondam ao formato de dados usado para treinar o modelo. Este aplicativo de resposta a perguntas aceita strings como entradas para a passagem de texto e a pergunta. O modelo não reconhece caracteres especiais e palavras não inglesas.

Para fornecer dados de texto de passagem ao modelo:

  1. Use o objeto LoadDataSetClient para carregar os dados do texto de passagem para o aplicativo. No aplicativo de exemplo, isso está localizado em 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. Use o objeto DatasetFragment para listar os títulos de cada passagem de texto e iniciar a tela TFL Question and Answer . No aplicativo de exemplo, ele está localizado em 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. Use a função onCreateViewHolder dentro do objeto DatasetAdapter para apresentar os títulos de cada passagem de texto. No aplicativo de exemplo, isso está localizado em DatasetAdapter.kt :

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

Para fornecer perguntas do usuário ao modelo:

  1. Use o objeto QaAdapter para fornecer a pergunta ao modelo. No aplicativo de exemplo, isso está localizado em 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
          }
      }
      ...
    }
    

Executar previsões

Em seu aplicativo Android, depois de inicializar um objeto BertQuestionAnswerer , você pode começar a inserir perguntas na forma de texto em linguagem natural para o modelo. O modelo tenta identificar a resposta dentro da passagem de texto.

Para executar previsões:

  1. Crie uma função de answer , que executa o modelo e mede o tempo necessário para identificar a resposta ( inferenceTime ). No aplicativo de exemplo, a função de answer está localizada em 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. Passe os resultados de answer para o objeto listener.

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

Lidar com a saída do modelo

Depois de inserir uma pergunta, o modelo fornece no máximo cinco respostas possíveis na passagem.

Para obter os resultados do modelo:

  1. Crie uma função onResult para o objeto listener para manipular a saída. No aplicativo de exemplo, o objeto ouvinte está localizado em BertQaHelper.kt

    interface AnswererListener {
        fun onError(error: String)
        fun onResults(
            results: List<QaAnswer>?,
            inferenceTime: Long
        )
    }
    
  2. Destaque seções da passagem com base nos resultados. No aplicativo de exemplo, isso está localizado em 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
        )
    }
    

Depois que o modelo retornar um conjunto de resultados, seu aplicativo poderá agir nessas previsões apresentando o resultado ao usuário ou executando lógica adicional.

Próximos passos