Respondendo perguntas com 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 ativar modelos de aprendizado de máquina para resposta a perguntas. O aplicativo foi projetado para um dispositivo Android físico, mas também pode ser executado em um emulador de dispositivo.

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

Visão geral de respostas a perguntas

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

Um modelo de resposta a perguntas é treinado em um conjunto de dados de resposta 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 (Bidirecional Encoder Representations from Transformers). Para obter mais informações sobre mobilebert , consulte o artigo de pesquisa MobileBERT: um BERT compacto independente de tarefas para dispositivos com recursos limitados .

O modelo mobilebert foi treinado usando o conjunto de dados Stanford Question Answering Dataset ( SQuAD ), um conjunto de dados de compreensão de leitura que consiste em 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 no 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 versão mínima do sistema operacional SDK 21 (Android 7.0 - Nougat) com modo de desenvolvedor ativado ou um emulador 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 git para usar checkout esparso, para que você tenha apenas os arquivos do aplicativo de exemplo de resposta a perguntas:
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/bert_qa/android
    

Importe e execute o projeto

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

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

  1. Inicie o Android Studio .
  2. No Android Studio, selecione Arquivo > Novo > Importar Projeto .
  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 uma sincronização do Gradle, escolha OK.
  5. Certifique-se de que seu dispositivo Android esteja conectado ao computador e que o modo de desenvolvedor esteja ativado. Clique na seta verde Run .

Se você selecionar o diretório correto, o Android Studio criará um novo projeto e o compilará. Este processo pode demorar alguns minutos, dependendo da velocidade do seu computador e se você utilizou 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 emulador de dispositivo.

Para usar o aplicativo de exemplo de resposta a perguntas:

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

O aplicativo tenta identificar a resposta à pergunta no texto da passagem. Se o modelo detectar uma resposta na passagem, o aplicativo destacará a extensão 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 funciona o aplicativo de exemplo

O aplicativo usa a API BertQuestionAnswerer dentro do pacote Biblioteca de tarefas para 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 GPU ou delegado NNAPI.

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

  • BertQaHelper.kt - Inicializa o respondente da pergunta e cuida da seleção do modelo e do delegado.
  • 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 em relação ao aplicativo de exemplo.

Abra ou crie um projeto Android

Você precisa de um projeto de desenvolvimento Android no Android Studio para acompanhar 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 específicas do projeto para executar modelos de machine learning do TensorFlow Lite e acessar funções utilitárias. Essas funções convertem dados como strings em um formato de dados tensor que pode ser processado pelo modelo. As instruções a seguir explicam como adicionar as dependências de projeto e módulo necessárias 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 em 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 machine learning 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 predição e normalmente inclui metadados sobre como interpretar os resultados da prediçã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 no 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 do modelo criando um objeto BertQaHelper e construa um objeto TensorFlow Lite com bertQuestionAnswerer .

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

Habilite aceleração de hardware (opcional)

Ao inicializar um modelo do TensorFlow Lite em seu aplicativo, você deve considerar 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 unidades de processamento gráfico (GPUs) ou unidades de processamento de tensores (TPUs).

Para ativar a aceleração de hardware no seu aplicativo:

  1. Crie uma variável para definir o delegado que a aplicação utilizará. No aplicativo de exemplo, esta 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 delegados para executar modelos do TensorFlow Lite é recomendado, mas não obrigatório. Para obter mais informações sobre como usar delegados com o TensorFlow Lite, consulte Delegados do TensorFlow Lite .

Preparar dados para o modelo

No seu aplicativo Android, seu código fornece dados ao modelo para interpretação, transformando dados existentes, como texto bruto, em um formato de dados Tensor que pode ser processado pelo seu modelo. O Tensor que você passa para um modelo deve ter dimensões ou formato específico que corresponda ao formato dos dados usados ​​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 que não sejam do inglês.

Para fornecer dados de texto de passagem ao modelo:

  1. Use o objeto LoadDataSetClient para carregar os dados de texto da passagem no 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 de perguntas e respostas do TFL . No aplicativo de exemplo, isso 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

No seu aplicativo Android, depois de inicializar um objeto BertQuestionAnswerer , você poderá começar a inserir perguntas na forma de texto em linguagem natural no modelo. O modelo tenta identificar a resposta dentro da passagem do texto.

Para executar previsões:

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

    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 ouvinte 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 de acordo com essas previsões, apresentando o resultado ao usuário ou executando lógica adicional.

Próximos passos