使用 Android 裝置回答問題

Question answering example app in Android

本教學課程說明如何使用 TensorFlow Lite 建構 Android 應用程式,以針對自然語言文字結構的問題提供解答。範例應用程式使用BERT 問答 (BertQuestionAnswerer) API 在 自然語言 (NL) 工作程式庫中啟用問答機器學習模型。此應用程式專為實體 Android 裝置設計,但也可以在裝置模擬器上執行。

如果您正在更新現有的專案,則可以使用範例應用程式作為參考或範本。如需瞭解如何將問答功能新增至現有應用程式,請參閱更新和修改應用程式

問答總覽

問答是回答以自然語言提出的問題的機器學習工作。經過訓練的問答模型會接收文字段落和問題作為輸入,並嘗試根據其對段落中資訊的解讀來回答問題。

問答模型是在問答資料集上訓練的,該資料集包含閱讀理解資料集以及基於不同文字區段的問題解答配對。

如需瞭解本教學課程中的模型是如何產生的詳細資訊,請參閱 使用 TensorFlow Lite Model Maker 進行 BERT 問答 教學課程。

模型和資料集

範例應用程式使用 Mobile BERT Q&A (mobilebert) 模型,這是 BERT (Bidirectional Encoder Representations from Transformers) 的更輕巧快速版本。如需 mobilebert 的詳細資訊,請參閱 MobileBERT:適用於資源受限裝置的精巧型與工作無關 BERT 研究論文。

mobilebert 模型是使用 Stanford Question Answering Dataset (SQuAD) 資料集訓練的,這是一個閱讀理解資料集,其中包含維基百科的文章以及每篇文章的一組問答配對。

設定並執行範例應用程式

若要設定問答應用程式,請從 GitHub 下載範例應用程式,並使用 Android Studio 執行。

系統需求

  • Android Studio 2021.1.1 (Bumblebee) 或更高版本。
  • Android SDK 31 或更高版本
  • Android 裝置,最低作業系統版本為 SDK 21 (Android 7.0 - Nougat),並啟用開發人員模式,或 Android 模擬器。

取得範例程式碼

建立範例程式碼的本機副本。您將使用此程式碼在 Android Studio 中建立專案並執行範例應用程式。

若要複製並設定範例程式碼

  1. 複製 git 存放區
    git clone https://github.com/tensorflow/examples.git
    
  2. 或者,您可以設定 git 執行個體以使用稀疏簽出,讓您只有問答範例應用程式的檔案
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/bert_qa/android
    

匯入並執行專案

從下載的範例程式碼建立專案、建構專案,然後執行專案。

若要匯入並建構範例程式碼專案

  1. 啟動 Android Studio
  2. 從 Android Studio 中,選取 [File] (檔案) > [New] (新增) > [Import Project] (匯入專案)。
  3. 瀏覽至包含 build.gradle 檔案的範例程式碼目錄 (.../examples/lite/examples/bert_qa/android/build.gradle),然後選取該目錄。
  4. 如果 Android Studio 要求 Gradle Sync,請選擇 [OK] (確定)。
  5. 確認您的 Android 裝置已連線至電腦,且已啟用開發人員模式。按一下綠色的 Run (執行) 箭頭。

如果您選取正確的目錄,Android Studio 會建立新專案並進行建構。此程序可能需要幾分鐘時間,具體取決於電腦速度以及您是否已將 Android Studio 用於其他專案。建構完成後,Android Studio 會在 [Build Output] (建構輸出) 狀態面板中顯示 BUILD SUCCESSFUL (建構成功) 訊息。

若要執行專案

  1. 從 Android Studio 中,選取 [Run] (執行) > [Run…] (執行…) 來執行專案。
  2. 選取已連接的 Android 裝置 (或模擬器) 以測試應用程式。

使用應用程式

在 Android Studio 中執行專案後,應用程式會自動在已連線的裝置或裝置模擬器上開啟。

若要使用問答範例應用程式

  1. 從主題清單中選擇一個主題。
  2. 選擇建議的問題,或在文字方塊中輸入您自己的問題。
  3. 切換橙色箭頭以執行模型。

應用程式會嘗試從段落文字中識別問題的答案。如果模型在段落中偵測到答案,應用程式會為使用者醒目提示相關的文字範圍。

您現在擁有一個可正常運作的問答應用程式。使用以下章節可以更深入瞭解範例應用程式的運作方式,以及如何在您的生產應用程式中實作問答功能

範例應用程式的運作方式

應用程式使用 自然語言 (NL) 工作程式庫套件中的 BertQuestionAnswerer API。MobileBERT 模型是使用 TensorFlow Lite Model Maker 訓練的。應用程式預設在 CPU 上執行,並可選擇使用 GPU 或 NNAPI 委派進行硬體加速。

以下檔案和目錄包含此應用程式的關鍵程式碼

修改您的應用程式

以下章節說明修改您自己的 Android 應用程式以執行範例應用程式中顯示之模型的關鍵步驟。這些說明使用範例應用程式作為參考點。您自己的應用程式所需的特定變更可能與範例應用程式不同。

開啟或建立 Android 專案

您需要在 Android Studio 中有一個 Android 開發專案,才能繼續執行這些指示的其餘部分。依照下列指示開啟現有專案或建立新專案。

若要開啟現有的 Android 開發專案

  • 在 Android Studio 中,選取 [File] (檔案) > [Open] (開啟),然後選取現有專案。

若要建立基本的 Android 開發專案

如需使用 Android Studio 的詳細資訊,請參閱 Android Studio 文件

新增專案依附元件

在您自己的應用程式中,新增特定的專案依附元件以執行 TensorFlow Lite 機器學習模型並存取公用程式函式。這些函式會將字串等資料轉換為模型可以處理的張量資料格式。以下指示說明如何將必要的專案和模組依附元件新增至您自己的 Android 應用程式專案。

若要新增模組依附元件

  1. 在使用 TensorFlow Lite 的模組中,更新模組的 build.gradle 檔案以包含下列依附元件。

    在範例應用程式中,依附元件位於 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'
    }
    

    專案必須包含 Text 工作程式庫 (tensorflow-lite-task-text)。

    如果您想要修改此應用程式以在圖形處理單元 (GPU) 上執行,GPU 程式庫 (tensorflow-lite-gpu-delegate-plugin) 提供在 GPU 上執行應用程式的基礎架構,而 Delegate (tensorflow-lite-gpu) 提供相容性清單。

  2. 在 Android Studio 中,選取 [File] (檔案) > [Sync Project with Gradle Files] (將專案與 Gradle 檔案同步) 來同步專案依附元件。

初始化機器學習模型

在您的 Android 應用程式中,您必須先使用參數初始化 TensorFlow Lite 機器學習模型,才能使用模型執行預測。

TensorFlow Lite 模型儲存為 *.tflite 檔案。模型檔案包含預測邏輯,通常包含關於如何解讀預測結果的中繼資料。通常,模型檔案會儲存在開發專案的 src/main/assets 目錄中,如程式碼範例所示

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

為了方便起見和程式碼可讀性,範例宣告了一個同伴物件,用於定義模型的設定。

若要在您的應用程式中初始化模型

  1. 建立一個同伴物件來定義模型的設定。在範例應用程式中,此物件位於 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. 藉由建構 BertQaHelper 物件來建立模型的設定,並使用 bertQuestionAnswerer 建構 TensorFlow Lite 物件。

    在範例應用程式中,這位於 BertQaHelper.kt 中的 setupBertQuestionAnswerer() 函式中

    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)
            }
        }
        ...
        }
    

啟用硬體加速 (選用)

在您的應用程式中初始化 TensorFlow Lite 模型時,您應考慮使用硬體加速功能來加快模型預測計算速度。TensorFlow Lite 委派是軟體模組,可使用行動裝置上的專用處理硬體 (例如圖形處理單元 (GPU) 或張量處理單元 (TPU)) 加速機器學習模型的執行。

若要在您的應用程式中啟用硬體加速

  1. 建立一個變數來定義應用程式將使用的委派。在範例應用程式中,此變數位於 BertQaHelper.kt 的開頭

    var currentDelegate: Int = 0
    
  2. 建立委派選取器。在範例應用程式中,委派選取器位於 BertQaHelper.kt 中的 setupBertQuestionAnswerer 函式中

    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()
        }
    }
    

建議使用委派來執行 TensorFlow Lite 模型,但並非必要。如需瞭解將委派與 TensorFlow Lite 搭配使用的詳細資訊,請參閱 TensorFlow Lite 委派

準備模型的資料

在您的 Android 應用程式中,您的程式碼會透過將現有資料 (例如原始文字) 轉換為模型可以處理的 張量資料格式,來提供資料給模型進行解讀。您傳遞至模型的張量必須具有特定的維度或形狀,以符合用於訓練模型的資料格式。此問答應用程式接受 字串 作為文字段落和問題的輸入。模型無法辨識特殊字元和非英文字詞。

若要提供段落文字資料給模型

  1. 使用 LoadDataSetClient 物件將段落文字資料載入應用程式。在範例應用程式中,這位於 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. 使用 DatasetFragment 物件列出每個文字段落的標題,並啟動 [TFL Question and Answer] (TFL 問答) 畫面。在範例應用程式中,這位於 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. 使用 DatasetAdapter 物件中的 onCreateViewHolder 函式來呈現每個文字段落的標題。在範例應用程式中,這位於 DatasetAdapter.kt

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

若要提供使用者問題給模型

  1. 使用 QaAdapter 物件將問題提供給模型。在範例應用程式中,這位於 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
          }
      }
      ...
    }
    

執行預測

在您的 Android 應用程式中,一旦您初始化 BertQuestionAnswerer 物件,您就可以開始以自然語言文字的形式將問題輸入到模型中。模型會嘗試在文字段落中識別答案。

若要執行預測

  1. 建立一個 answer 函式,執行模型並測量識別答案所需的時間 (inferenceTime)。在範例應用程式中,answer 函式位於 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. answer 的結果傳遞至接聽器物件。

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

處理模型輸出

在您輸入問題後,模型會在段落中提供最多五個可能的答案。

若要從模型取得結果

  1. 為接聽器物件建立一個 onResult 函式來處理輸出。在範例應用程式中,接聽器物件位於 BertQaHelper.kt

    interface AnswererListener {
        fun onError(error: String)
        fun onResults(
            results: List<QaAnswer>?,
            inferenceTime: Long
        )
    }
    
  2. 根據結果醒目提示段落的區段。在範例應用程式中,這位於 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
        )
    }
    

一旦模型傳回一組結果,您的應用程式就可以根據這些預測採取行動,方法是向使用者呈現結果或執行其他邏輯。

後續步驟