Skip to content

Commit

Permalink
add documentation for nl_classifier
Browse files Browse the repository at this point in the history
  • Loading branch information
am15h committed Aug 19, 2021
1 parent 330a467 commit 3a4481a
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 22 deletions.
75 changes: 73 additions & 2 deletions lib/src/task/text/nl_classifier/nl_classifier.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1,97 @@
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:quiver/check.dart';
import 'package:tflite_flutter_helper/src/common/file_util.dart';
import 'package:tflite_flutter_helper/src/label/category.dart';
import 'package:tflite_flutter_helper/src/task/bindings/text/nl_classifier/nl_classifer.dart';
import 'package:tflite_flutter_helper/src/task/bindings/text/nl_classifier/types.dart';
import 'package:tflite_flutter_helper/src/task/text/nl_classifier/nl_classifier_options.dart';

/// Classifier API for natural language classification tasks, categorizes string into different
/// classes.
///
/// The API expects a TFLite model with the following input/output tensor:
///
/// <ul>
/// <li>Input tensor (kTfLiteString)
/// input of the model, accepts a string.
///
/// <li>Output score tensor
/// (kTfLiteUInt8/kTfLiteInt8/kTfLiteInt16/kTfLiteFloat32/kTfLiteFloat64/kTfLiteBool)
///
/// - output scores for each class, if type is one of the Int types, dequantize it, if it
/// is Bool type, convert the values to 0.0 and 1.0 respectively.
///
/// - can have an optional associated file in metadata for labels, the file should be a
/// plain text file with one label per line, the number of labels should match the number
/// of categories the model outputs. Output label tensor: optional (kTfLiteString) -
/// output classname for each class, should be of the same length with scores. If this
/// tensor is not present, the API uses score indices as classnames. - will be ignored if
/// output score tensor already has an associated label file.
///
/// <li>Optional Output label tensor (kTfLiteString/kTfLiteInt32)
/// - output classname for each class, should be of the same length with scores. If this
/// tensor is not present, the API uses score indices as classnames.
/// - will be ignored if output score tensor already has an associated labe file.
/// </ul>
///
/// By default the API tries to find the input/output tensors with default configurations in
/// [NLClassifierOptions], with tensor name prioritized over tensor index. The option is
/// configurable for different TFLite models.
class NLClassifier {
final Pointer<TfLiteNLClassifier> _classifier;
bool _deleted = false;
Pointer<TfLiteNLClassifier> get base => _classifier;

NLClassifier._(this._classifier);

factory NLClassifier.create(String modelPath, {NLClassifierOptions? options}) {
if(options == null) {
/// Create [NLClassifier] from [modelPath] and optional [options].
///
/// [modelPath] is the path of the .tflite model loaded on device.
///
/// throws [FileSystemException] If model file fails to load.
static NLClassifier create(String modelPath, {NLClassifierOptions? options}) {
if (options == null) {
options = NLClassifierOptions();
}
final classiferPtr =
NLClassifierFromFileAndOptions(modelPath.toNativeUtf8(), options.base);
if (classiferPtr == nullptr) {
throw FileSystemException("Failed to create NLClassifier.", modelPath);
}
return NLClassifier._(classiferPtr);
}

/// Create [NLClassifier] from [modelFile].
///
/// throws [FileSystemException] If model file fails to load.
static NLClassifier createFromFile(File modelFile) {
return create(modelFile.path);
}

/// Create [NLClassifier] from [modelFile] and [options].
///
/// throws [FileSystemException] If model file fails to load.
static NLClassifier createFromFileAndOptions(
File modelFile, NLClassifierOptions options) {
return create(modelFile.path, options: options);
}

/// Create [NLClassifier] directly from [assetPath] and optional [options].
///
/// [assetPath] must the full path to assets. Eg. 'assets/my_model.tflite'.
///
/// throws [FileSystemException] If model file fails to load.
static Future<NLClassifier> createFromAsset(String assetPath,
{NLClassifierOptions? options}) async {
final modelFile = await FileUtil.loadFileOnDevice(assetPath);
return create(modelFile.path, options: options);
}

/// Perform classification on a string input [text],
///
/// Returns classified [Category]s as List.
List<Category> classify(String text) {
final ref = NLClassifierClassify(base, text.toNativeUtf8()).ref;
final categoryList = List.generate(
Expand All @@ -32,6 +102,7 @@ class NLClassifier {
return categoryList;
}

/// Deletes NLClassifier Instance.
void delete() {
checkState(!_deleted, message: 'NLCLassifier already deleted.');
NLClassifierDelete(base);
Expand Down
101 changes: 81 additions & 20 deletions lib/src/task/text/nl_classifier/nl_classifier_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,40 @@ import 'package:ffi/ffi.dart';
import 'package:quiver/check.dart';
import 'package:tflite_flutter_helper/src/task/bindings/text/nl_classifier/types.dart';

/// Options to identify input and output tensors of the model.
///
/// Configure the input/output tensors for NLClassifier:
///
/// <p>- No special configuration is needed if the model has only one input tensor and one
/// output tensor.
///
/// <p>- When the model has multiple input or output tensors, use the following configurations
/// to specifiy the desired tensors: <br>
/// -- tensor names: {inputTensorName}, {outputScoreTensorName}, {@code
/// outputLabelTensorName}<br>
/// -- tensor indices: {inputTensorIndex}, {outputScoreTensorIndex}, {@code
/// outputLabelTensorIndex} <br>
/// Tensor names has higher priorities than tensor indices in locating the tensors. It means
/// the tensors will be first located according to tensor names. If not found, then the tensors
/// will be located according to tensor indices.
///
/// <p>- Failing to match the input text tensor or output score tensor with neither tensor
/// names nor tensor indices will trigger a runtime error. However, failing to locate the
/// output label tensor will not trigger an error because the label tensor is optional.
class NLClassifierOptions {
final Pointer<TfLiteNLClassifierOptions> _options;
bool _deleted = false;

Pointer<TfLiteNLClassifierOptions> get base => _options;
NLClassifierOptions._(this._options);

static const DEFAULT_INPUT_TENSOR_INDEX = 0;
static const DEFAULT_INPUT_TENSOR_NAME = "INPUT";
static const DEFAULT_OUTPUT_SCORE_TENSOR_INDEX = 0;
static const DEFAULT_OUTPUT_SCORE_TENSOR_NAME = "OUTPUT_SCORE";
// By default there is no output label tensor. The label file can be attached
// to the output score tensor metadata.
static const DEFAULT_OUTPUT_LABEL_TENSOR_NAME = "OUTPUT_LABEL";
static const DEFAULT_INPUT_TENSOR_INDEX = 0;
static const DEFAULT_OUTPUT_SCORE_TENSOR_INDEX = 0;
static const DEFAULT_OUTPUT_LABEL_TENSOR_INDEX = -1;

/// Creates a new options instance.
Expand All @@ -30,44 +52,83 @@ class NLClassifierOptions {
return NLClassifierOptions._(optionsPtr);
}

int get inputTensorIndex => base.ref.inputTensorIndex;

set inputTensorIndex(int value) {
base.ref.inputTensorIndex = value;
}

int get outputScoreTensorIndex => base.ref.outputScoreTensorIndex;

set outputScoreTensorIndex(int value) {
base.ref.outputScoreTensorIndex = value;
}

int get outputLabelTensorIndex => base.ref.outputLabelTensorIndex;

set outputLabelTensorIndex(int value) {
base.ref.outputLabelTensorIndex = value;
}

String get inputTensorName => base.ref.inputTensorName.toDartString();

/// Set the name of the input text tensor, if the model has multiple inputs. Only the input
/// tensor specified will be used for inference; other input tensors will be ignored. Dafualt
/// to ["INPUT"].
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
set inputTensorName(String value) {
base.ref.inputTensorName = value.toNativeUtf8();
}

String get outputScoreTensorName =>
base.ref.outputScoreTensorName.toDartString();

/// Set the name of the output score tensor, if the model has multiple outputs. Dafualt to
/// ["OUTPUT_SCORE"].
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
set outputScoreTensorName(String value) {
base.ref.outputScoreTensorName = value.toNativeUtf8();
}

String get outputLabelTensorName =>
base.ref.outputLabelTensorName.toDartString();

/// Set the name of the output label tensor, if the model has multiple outputs. Dafualt to
/// ["OUTPUT_LABEL"].
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
///
/// <p>By default, label file should be packed with the output score tensor through Model
/// Metadata. See the <a
/// href="https://www.tensorflow.org/lite/convert/metadata_writer_tutorial#natural_language_classifiers">MetadataWriter
/// for NLClassifier</a>. NLClassifier reads and parses labels from the label file
/// automatically. However, some models may output a specific label tensor instead. In this
/// case, NLClassifier reads labels from the output label tensor.
set outputLabelTensorName(String value) {
base.ref.outputLabelTensorName = value.toNativeUtf8();
}

int get inputTensorIndex => base.ref.inputTensorIndex;

/// Set the index of the input text tensor among all input tensors, if the model has multiple
/// inputs. Only the input tensor specified will be used for inference; other input tensors
/// will be ignored. Dafualt to 0.
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
set inputTensorIndex(int value) {
base.ref.inputTensorIndex = value;
}

int get outputScoreTensorIndex => base.ref.outputScoreTensorIndex;

/// Set the index of the output score tensor among all output tensors, if the model has
/// multiple outputs. Dafualt to 0.
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
set outputScoreTensorIndex(int value) {
base.ref.outputScoreTensorIndex = value;
}

int get outputLabelTensorIndex => base.ref.outputLabelTensorIndex;

/// Set the index of the optional output label tensor among all output tensors, if the model
/// has multiple outputs.
///
/// <p>See the document above [outputLabelTensorName] for more information about what the
/// output label tensor is.
///
/// <p>See the section, Configure the input/output tensors for NLClassifier, for more details.
///
/// <p>[outputLabelTensorIndex] dafualts to -1, meaning to disable the output label
/// tensor.
set outputLabelTensorIndex(int value) {
base.ref.outputLabelTensorIndex = value;
}

/// Destroys the options instance.
void delete() {
checkState(!_deleted, message: 'NLClassifierOptions already deleted.');
Expand Down

0 comments on commit 3a4481a

Please sign in to comment.