Adds a Java API for TextClassifier.

PiperOrigin-RevId: 482394706
This commit is contained in:
MediaPipe Team
2022-10-19 23:05:34 -07:00
committed by Copybara-Service
parent 3d588bae8b
commit 41d6f6d005
9 changed files with 629 additions and 1 deletions
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.mediapipe.tasks.text.textclassifiertest"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-sdk android:minSdkVersion="24"
android:targetSdkVersion="30" />
<application
android:label="textclassifiertest"
android:name="android.support.multidex.MultiDexApplication"
android:taskAffinity="">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
android:targetPackage="com.google.mediapipe.tasks.text.textclassifiertest" />
</manifest>
@@ -0,0 +1,19 @@
# Copyright 2022 The MediaPipe Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package(default_visibility = ["//mediapipe/tasks:internal"])
licenses(["notice"])
# TODO: Enable this in OSS
@@ -0,0 +1,154 @@
// Copyright 2022 The MediaPipe Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.mediapipe.tasks.text.textclassifier;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.mediapipe.framework.MediaPipeException;
import com.google.mediapipe.tasks.components.containers.Category;
import com.google.mediapipe.tasks.core.BaseOptions;
import com.google.mediapipe.tasks.core.TestUtils;
import com.google.mediapipe.tasks.text.textclassifier.TextClassifier.TextClassifierOptions;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Test for {@link TextClassifier}/ */
@RunWith(AndroidJUnit4.class)
public class TextClassifierTest {
private static final String BERT_MODEL_FILE = "bert_text_classifier.tflite";
private static final String REGEX_MODEL_FILE =
"test_model_text_classifier_with_regex_tokenizer.tflite";
private static final String STRING_TO_BOOL_MODEL_FILE =
"test_model_text_classifier_bool_output.tflite";
private static final String NEGATIVE_TEXT = "unflinchingly bleak and desperate";
private static final String POSITIVE_TEXT = "it's a charming and often affecting journey";
@Test
public void create_failsWithMissingModel() throws Exception {
String nonExistentFile = "/path/to/non/existent/file";
MediaPipeException exception =
assertThrows(
MediaPipeException.class,
() ->
TextClassifier.createFromFile(
ApplicationProvider.getApplicationContext(), nonExistentFile));
assertThat(exception).hasMessageThat().contains(nonExistentFile);
}
@Test
public void create_failsWithMissingOpResolver() throws Exception {
TextClassifierOptions options =
TextClassifierOptions.builder()
.setBaseOptions(
BaseOptions.builder().setModelAssetPath(STRING_TO_BOOL_MODEL_FILE).build())
.build();
MediaPipeException exception =
assertThrows(
MediaPipeException.class,
() ->
TextClassifier.createFromOptions(
ApplicationProvider.getApplicationContext(), options));
// TODO: Make MediaPipe InferenceCalculator report the detailed.
// interpreter errors (e.g., "Encountered unresolved custom op").
assertThat(exception)
.hasMessageThat()
.contains("interpreter_builder(&interpreter) == kTfLiteOk");
}
@Test
public void classify_succeedsWithBert() throws Exception {
TextClassifier textClassifier =
TextClassifier.createFromFile(ApplicationProvider.getApplicationContext(), BERT_MODEL_FILE);
TextClassificationResult negativeResults = textClassifier.classify(NEGATIVE_TEXT);
assertHasOneHead(negativeResults);
assertCategoriesAre(
negativeResults,
Arrays.asList(
Category.create(0.95630914f, 0, "negative", ""),
Category.create(0.04369091f, 1, "positive", "")));
TextClassificationResult positiveResults = textClassifier.classify(POSITIVE_TEXT);
assertHasOneHead(positiveResults);
assertCategoriesAre(
positiveResults,
Arrays.asList(
Category.create(0.99997187f, 1, "positive", ""),
Category.create(2.8132641E-5f, 0, "negative", "")));
}
@Test
public void classify_succeedsWithFileObject() throws Exception {
TextClassifier textClassifier =
TextClassifier.createFromFile(
ApplicationProvider.getApplicationContext(),
TestUtils.loadFile(ApplicationProvider.getApplicationContext(), BERT_MODEL_FILE));
TextClassificationResult negativeResults = textClassifier.classify(NEGATIVE_TEXT);
assertHasOneHead(negativeResults);
assertCategoriesAre(
negativeResults,
Arrays.asList(
Category.create(0.95630914f, 0, "negative", ""),
Category.create(0.04369091f, 1, "positive", "")));
TextClassificationResult positiveResults = textClassifier.classify(POSITIVE_TEXT);
assertHasOneHead(positiveResults);
assertHasOneHead(positiveResults);
assertCategoriesAre(
positiveResults,
Arrays.asList(
Category.create(0.99997187f, 1, "positive", ""),
Category.create(2.8132641E-5f, 0, "negative", "")));
}
@Test
public void classify_succeedsWithRegex() throws Exception {
TextClassifier textClassifier =
TextClassifier.createFromFile(
ApplicationProvider.getApplicationContext(), REGEX_MODEL_FILE);
TextClassificationResult negativeResults = textClassifier.classify(NEGATIVE_TEXT);
assertHasOneHead(negativeResults);
assertCategoriesAre(
negativeResults,
Arrays.asList(
Category.create(0.6647746f, 0, "Negative", ""),
Category.create(0.33522537f, 1, "Positive", "")));
TextClassificationResult positiveResults = textClassifier.classify(POSITIVE_TEXT);
assertHasOneHead(positiveResults);
assertCategoriesAre(
positiveResults,
Arrays.asList(
Category.create(0.5120041f, 0, "Negative", ""),
Category.create(0.48799595f, 1, "Positive", "")));
}
private static void assertHasOneHead(TextClassificationResult results) {
assertThat(results.classifications()).hasSize(1);
assertThat(results.classifications().get(0).headIndex()).isEqualTo(0);
assertThat(results.classifications().get(0).headName()).isEqualTo("probability");
assertThat(results.classifications().get(0).entries()).hasSize(1);
}
private static void assertCategoriesAre(
TextClassificationResult results, List<Category> categories) {
assertThat(results.classifications().get(0).entries().get(0).categories())
.isEqualTo(categories);
}
}