Odpowiadanie na pytania za pomocą Androida

Przykładowa aplikacja odpowiadająca na pytania w systemie Android

W tym samouczku pokazano, jak zbudować aplikację na Androida przy użyciu TensorFlow Lite, aby zapewnić odpowiedzi na pytania ustrukturyzowane w tekście w języku naturalnym. Przykładowa aplikacja wykorzystuje interfejs API odpowiedzi na pytania BERT ( BertQuestionAnswerer ) w bibliotece zadań dla języka naturalnego (NL), aby włączyć modele uczenia maszynowego odpowiadającego na pytania. Aplikacja przeznaczona jest na fizyczne urządzenie z systemem Android, ale można ją także uruchomić na emulatorze urządzenia.

Jeśli aktualizujesz istniejący projekt, możesz użyć przykładowej aplikacji jako odniesienia lub szablonu. Aby uzyskać instrukcje dotyczące dodawania pytań z odpowiedzią do istniejącej aplikacji, zobacz Aktualizowanie i modyfikowanie aplikacji .

Przegląd odpowiedzi na pytania

Odpowiadanie na pytania to zadanie uczenia maszynowego polegające na odpowiadaniu na pytania zadane w języku naturalnym. Wyszkolony model odpowiadania na pytania otrzymuje jako dane wejściowe fragment tekstu i pytanie, a następnie próbuje odpowiedzieć na pytanie w oparciu o swoją interpretację informacji zawartych w tym fragmencie.

Model odpowiadania na pytania jest szkolony na zestawie danych dotyczących odpowiedzi na pytania, który składa się ze zbioru danych dotyczących czytania ze zrozumieniem oraz par pytanie-odpowiedź opartych na różnych segmentach tekstu.

Aby uzyskać więcej informacji na temat generowania modeli w tym samouczku, zapoznaj się z samouczkiem BERT Pytanie Odpowiedź za pomocą TensorFlow Lite Model Maker .

Modele i zbiór danych

Przykładowa aplikacja korzysta z modelu Mobile BERT Q&A ( mobilebert ), który jest lżejszą i szybszą wersją BERT (Bilateral Encoder Representations from Transformers). Więcej informacji na temat mobilebert można znaleźć w artykule badawczym MobileBERT: Compact Task-Agnostic BERT for Resource-Limited Devices .

Model mobilebert został przeszkolony przy użyciu zestawu danych Stanford Pytanie Odpowiedzi Dataset ( SQuAD ), zbioru danych dotyczących czytania ze zrozumieniem składającego się z artykułów z Wikipedii i zestawu par pytanie-odpowiedź dla każdego artykułu.

Skonfiguruj i uruchom przykładową aplikację

Aby skonfigurować aplikację odpowiedzi na pytania, pobierz przykładową aplikację z GitHub i uruchom ją za pomocą Android Studio .

Wymagania systemowe

  • Android Studio w wersji 2021.1.1 (Bumblebee) lub nowszej.
  • Android SDK w wersji 31 ​​lub nowszej
  • Urządzenie z systemem Android z minimalną wersją systemu operacyjnego SDK 21 (Android 7.0 - Nougat) z włączonym trybem programisty lub emulatorem Androida.

Pobierz przykładowy kod

Utwórz lokalną kopię przykładowego kodu. Użyjesz tego kodu, aby utworzyć projekt w Android Studio i uruchomić przykładową aplikację.

Aby sklonować i skonfigurować przykładowy kod:

  1. Sklonuj repozytorium git
    git clone https://github.com/tensorflow/examples.git
    
  2. Opcjonalnie skonfiguruj swoją instancję git tak, aby korzystała z rzadkiej transakcji, aby mieć tylko pliki dla przykładowej aplikacji odpowiadającej na pytanie:
    cd examples
    git sparse-checkout init --cone
    git sparse-checkout set lite/examples/bert_qa/android
    

Zaimportuj i uruchom projekt

Utwórz projekt na podstawie pobranego przykładowego kodu, skompiluj projekt, a następnie uruchom go.

Aby zaimportować i zbudować przykładowy projekt kodu:

  1. Uruchom Android Studio .
  2. W Android Studio wybierz opcję Plik > Nowy > Importuj projekt .
  3. Przejdź do przykładowego katalogu kodu zawierającego plik build.gradle ( .../examples/lite/examples/bert_qa/android/build.gradle ) i wybierz ten katalog.
  4. Jeśli Android Studio zażąda synchronizacji stopniowej, wybierz OK.
  5. Upewnij się, że Twoje urządzenie z Androidem jest podłączone do komputera i włączony jest tryb programisty. Kliknij zieloną strzałkę Run .

Jeśli wybierzesz właściwy katalog, Android Studio utworzy nowy projekt i go zbuduje. Ten proces może zająć kilka minut, w zależności od szybkości komputera i tego, czy korzystałeś z Android Studio w innych projektach. Po zakończeniu kompilacji Android Studio wyświetla komunikat BUILD SUCCESSFUL w panelu stanu wyników kompilacji .

Aby uruchomić projekt:

  1. W Android Studio uruchom projekt, wybierając opcję Uruchom > Uruchom… .
  2. Wybierz podłączone urządzenie z Androidem (lub emulator), aby przetestować aplikację.

Korzystanie z aplikacji

Po uruchomieniu projektu w Android Studio aplikacja automatycznie otworzy się na podłączonym urządzeniu lub emulatorze urządzenia.

Aby skorzystać z przykładowej aplikacji odpowiadającej na pytania:

  1. Wybierz temat z listy tematów.
  2. Wybierz sugerowane pytanie lub wpisz własne w polu tekstowym.
  3. Przełącz pomarańczową strzałkę, aby uruchomić model.

Aplikacja próbuje znaleźć odpowiedź na pytanie na podstawie tekstu fragmentu. Jeśli model wykryje odpowiedź w fragmencie, aplikacja podświetla użytkownikowi odpowiedni zakres tekstu.

Masz teraz działającą aplikację odpowiadającą na pytania. Skorzystaj z poniższych sekcji, aby lepiej zrozumieć, jak działa przykładowa aplikacja i jak zaimplementować funkcje odpowiadania na pytania w aplikacjach produkcyjnych:

Jak działa przykładowa aplikacja

Aplikacja korzysta z interfejsu API BertQuestionAnswerer w ramach biblioteki zadań dla pakietu języka naturalnego (NL) . Model MobileBERT został przeszkolony przy użyciu narzędzia TensorFlow Lite Model Maker . Aplikacja domyślnie działa na procesorze, z możliwością akceleracji sprzętowej za pomocą delegata GPU lub NNAPI.

Następujące pliki i katalogi zawierają kluczowy kod tej aplikacji:

  • BertQaHelper.kt — inicjuje osobę odpowiadającą na pytanie oraz obsługuje wybór modelu i delegata.
  • QaFragment.kt — obsługuje i formatuje wyniki.
  • MainActivity.kt — zapewnia logikę organizowania aplikacji.

Zmodyfikuj swoją aplikację

W poniższych sekcjach wyjaśniono najważniejsze kroki umożliwiające zmodyfikowanie własnej aplikacji dla systemu Android w celu uruchomienia modelu pokazanego w przykładowej aplikacji. W niniejszych instrukcjach punktem odniesienia jest przykładowa aplikacja. Konkretne zmiany potrzebne w Twojej aplikacji mogą różnić się od przykładowej aplikacji.

Otwórz lub utwórz projekt na Androida

Aby wykonać pozostałe instrukcje, potrzebujesz projektu programistycznego dla systemu Android w Android Studio. Postępuj zgodnie z poniższymi instrukcjami, aby otworzyć istniejący projekt lub utworzyć nowy.

Aby otworzyć istniejący projekt programistyczny na Androida:

  • W Android Studio wybierz opcję Plik > Otwórz i wybierz istniejący projekt.

Aby utworzyć podstawowy projekt programistyczny dla systemu Android:

Więcej informacji na temat korzystania z Android Studio znajdziesz w dokumentacji Android Studio .

Dodaj zależności projektu

Dodaj we własnej aplikacji określone zależności projektu, aby uruchamiać modele uczenia maszynowego TensorFlow Lite i uzyskać dostęp do funkcji narzędziowych. Funkcje te konwertują dane, takie jak ciągi znaków, na format danych tensorowych, który może być przetwarzany przez model. Poniższe instrukcje wyjaśniają, jak dodać wymagane zależności projektu i modułu do własnego projektu aplikacji dla systemu Android.

Aby dodać zależności modułów:

  1. W module korzystającym z TensorFlow Lite zaktualizuj plik build.gradle modułu, aby uwzględnić następujące zależności.

    W przykładowej aplikacji zależności znajdują się w 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'
    }
    

    Projekt musi zawierać bibliotekę zadań Text ( tensorflow-lite-task-text ).

    Jeśli chcesz zmodyfikować tę aplikację tak, aby działała na procesorze graficznym (GPU), biblioteka GPU ( tensorflow-lite-gpu-delegate-plugin ) zapewnia infrastrukturę do uruchamiania aplikacji na GPU, a Delegate ( tensorflow-lite-gpu ) zawiera listę kompatybilności.

  2. W Android Studio zsynchronizuj zależności projektu, wybierając: Plik > Synchronizuj projekt z plikami Gradle .

Zainicjuj modele ML

W aplikacji na Androida musisz zainicjować model uczenia maszynowego TensorFlow Lite z parametrami, zanim uruchomisz prognozy z modelem.

Model TensorFlow Lite jest przechowywany jako plik *.tflite . Plik modelu zawiera logikę przewidywania i zazwyczaj zawiera metadane dotyczące sposobu interpretacji wyników przewidywań. Zazwyczaj pliki modeli są przechowywane w katalogu src/main/assets projektu deweloperskiego, jak w przykładzie kodu:

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

Dla wygody i czytelności kodu w przykładzie zadeklarowano obiekt towarzyszący, który definiuje ustawienia modelu.

Aby zainicjować model w aplikacji:

  1. Utwórz obiekt towarzyszący, aby zdefiniować ustawienia modelu. W przykładowej aplikacji obiekt ten znajduje się w 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. Utwórz ustawienia modelu, budując obiekt BertQaHelper i skonstruuj obiekt TensorFlow Lite za pomocą bertQuestionAnswerer .

    W przykładowej aplikacji znajduje się to w funkcji setupBertQuestionAnswerer() w 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)
            }
        }
        ...
        }
    

Włącz akcelerację sprzętową (opcjonalnie)

Inicjując model TensorFlow Lite w aplikacji, należy rozważyć użycie funkcji akceleracji sprzętowej, aby przyspieszyć obliczenia predykcyjne modelu. Delegaci TensorFlow Lite to moduły oprogramowania, które przyspieszają wykonywanie modeli uczenia maszynowego przy użyciu specjalistycznego sprzętu przetwarzającego na urządzeniu mobilnym, takiego jak jednostka przetwarzania grafiki (GPU) lub jednostka przetwarzania tensor (TPU).

Aby włączyć akcelerację sprzętową w aplikacji:

  1. Utwórz zmienną, aby zdefiniować delegata, którego będzie używać aplikacja. W przykładowej aplikacji zmienna ta znajduje się na początku BertQaHelper.kt :

    var currentDelegate: Int = 0
    
  2. Utwórz selektor delegata. W przykładowej aplikacji selektor delegata znajduje się w funkcji setupBertQuestionAnswerer w 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()
        }
    }
    

Zalecane jest używanie delegatów do uruchamiania modeli TensorFlow Lite, ale nie jest to wymagane. Aby uzyskać więcej informacji na temat używania delegatów z TensorFlow Lite, zobacz Delegaty TensorFlow Lite .

Przygotuj dane do modelu

W aplikacji na Androida kod dostarcza dane do modelu w celu interpretacji, przekształcając istniejące dane, takie jak nieprzetworzony tekst, w format danych Tensor , który może być przetwarzany przez model. Tensor przekazywany do modelu musi mieć określone wymiary lub kształt pasujący do formatu danych używanych do uczenia modelu. Ta aplikacja odpowiadająca na pytania akceptuje ciągi znaków jako dane wejściowe zarówno dla fragmentu tekstu, jak i pytania. Model nie rozpoznaje znaków specjalnych ani słów w języku innym niż angielski.

Aby dostarczyć dane tekstowe fragmentu do modelu:

  1. Użyj obiektu LoadDataSetClient , aby załadować dane tekstowe fragmentu do aplikacji. W przykładowej aplikacji znajduje się to w 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. Użyj obiektu DatasetFragment , aby wyświetlić tytuły każdego fragmentu tekstu i uruchomić ekran pytań i odpowiedzi TFL . W przykładowej aplikacji znajduje się to w 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. Użyj funkcji onCreateViewHolder w obiekcie DatasetAdapter , aby zaprezentować tytuły każdego fragmentu tekstu. W przykładowej aplikacji znajduje się to w DatasetAdapter.kt :

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

Aby zadać modelowi pytania użytkownika:

  1. Użyj obiektu QaAdapter , aby zadać pytanie modelowi. W przykładowej aplikacji znajduje się to w 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
          }
      }
      ...
    }
    

Uruchom prognozy

Po zainicjowaniu obiektu BertQuestionAnswerer w aplikacji na Androida możesz rozpocząć wprowadzanie do modelu pytań w formie tekstu w języku naturalnym. Model próbuje zidentyfikować odpowiedź w obrębie fragmentu tekstu.

Aby uruchomić prognozy:

  1. Utwórz funkcję answer , która uruchamia model i mierzy czas potrzebny na identyfikację odpowiedzi ( inferenceTime ). W przykładowej aplikacji funkcja answer znajduje się w 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. Przekaż wyniki z answer do obiektu słuchacza.

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

Obsługuj dane wyjściowe modelu

Po wprowadzeniu pytania model udostępnia w fragmencie maksymalnie pięć możliwych odpowiedzi.

Aby uzyskać wyniki z modelu:

  1. Utwórz funkcję onResult dla obiektu słuchacza, która będzie obsługiwać dane wyjściowe. W przykładowej aplikacji obiekt słuchacza znajduje się w BertQaHelper.kt

    interface AnswererListener {
        fun onError(error: String)
        fun onResults(
            results: List<QaAnswer>?,
            inferenceTime: Long
        )
    }
    
  2. Podkreśl fragmenty fragmentu na podstawie wyników. W przykładowej aplikacji znajduje się to w 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
        )
    }
    

Gdy model zwróci zestaw wyników, aplikacja może zastosować się do tych przewidywań, prezentując wynik użytkownikowi lub wykonując dodatkową logikę.

Następne kroki