Repository: deepjavalibrary/djl
Pull request #3388 (2024-08-06 02:40:10)

[djl-converter] Fixes import text embedding model from local folder
## Description ##

Brief description of what this PR is about

- If this change is a backward incompatible change, why must this change be made?
- Interesting edge cases to note here



Patch:
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index 3c2cdc758..cbac880e6 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -37,7 +37,7 @@ tasks {
     jar {
         manifest {
             attributes(
-                "Notice" to "DJL will collect telemetry to help us better understand our users’" +
+                "Notice" to "DJL will collect telemetry to help us better understand our users'" +
                         " needs, diagnose issues, and deliver additional features. If you would" +
                         " like to learn more or opt-out please go to: " +
                         "https://docs.djl.ai/docs/telemetry.html for more information."
diff --git a/api/src/main/java/ai/djl/translate/DeferredTranslatorFactory.java b/api/src/main/java/ai/djl/translate/DeferredTranslatorFactory.java
index 5f01d3720..4702d3d1c 100644
--- a/api/src/main/java/ai/djl/translate/DeferredTranslatorFactory.java
+++ b/api/src/main/java/ai/djl/translate/DeferredTranslatorFactory.java
@@ -76,7 +76,7 @@ public class DeferredTranslatorFactory implements TranslatorFactory {
             Constructor<? extends TranslatorFactory> constructor = subclass.getConstructor();
             return constructor.newInstance();
         } catch (Throwable e) {
-            logger.trace("Not able to load TranslatorFactory: " + className, e);
+            logger.trace("Not able to load TranslatorFactory: {}", className, e);
         }
         return null;
     }
diff --git a/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java b/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
index 32680b084..866d35785 100644
--- a/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
+++ b/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
@@ -97,7 +97,7 @@ public class ServingTranslatorFactory implements TranslatorFactory {
             Constructor<? extends TranslatorFactory> constructor = subclass.getConstructor();
             return constructor.newInstance();
         } catch (Throwable e) {
-            logger.trace("Not able to load TranslatorFactory: " + className, e);
+            logger.trace("Not able to load TranslatorFactory: {}", className, e);
         }
         return null;
     }
diff --git a/api/src/main/java/ai/djl/util/Ec2Utils.java b/api/src/main/java/ai/djl/util/Ec2Utils.java
index 540818296..8a22cfadb 100644
--- a/api/src/main/java/ai/djl/util/Ec2Utils.java
+++ b/api/src/main/java/ai/djl/util/Ec2Utils.java
@@ -137,7 +137,7 @@ public final class Ec2Utils {
                 logger.debug("telemetry: {} {}", statusCode, conn.getResponseMessage());
             } else {
                 logger.info(
-                        "DJL will collect telemetry to help us better understand our users’ needs,"
+                        "DJL will collect telemetry to help us better understand our users' needs,"
                             + " diagnose issues, and deliver additional features. If you would like"
                             + " to learn more or opt-out please go to:"
                             + " https://docs.djl.ai/docs/telemetry.html for more information.");
diff --git a/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md b/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
index 428b46990..c57fd055c 100644
--- a/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
+++ b/docs/tensorflow/how_to_import_tensorflow_models_in_DJL.md
@@ -50,7 +50,7 @@ Note that you need to click the download model button to find the actual Google
 Please refer to these two examples:
 
 1. [Object Detection with TensorFlow](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/cv/ObjectDetection.java) for loading from TensorFlow Hub url.
-2. [BERT Classification](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/BertClassification.java) for loading from local downloaded model.
+2. [BERT Classification](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/nlp/BertClassification.java) for loading from local downloaded model.
 
 ## How to import TensorFlow models that use [TensorFlow Extensions](https://www.tensorflow.org/resources/libraries-extensions)
 
@@ -68,7 +68,7 @@ This is a text encoder from tensorflow hub that uses ops from the TensorFlow Tex
 - You need to download the library files for the extension so that we can load them for use. [Here](https://github.com/tensorflow/text#install-using-pip) are instructions for downloading TensorFlow text. We'll use version `2.7.0` in this example.
 - After the library files are downloaded, you are ready to load them for use in DJL. You do this by using
 TensorFlow Java `TensorFlow.loadLibrary(...)` API before loading the model in DJL. 
-- We can update the existing [UniversalSentenceEncoder](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/UniversalSentenceEncoder.java) example to use the Multilingual model:
+- We can update the existing [UniversalSentenceEncoder](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoder.java) example to use the Multilingual model:
 
 ```java
 // Build Criteria for Multilingual Universal Sentence Encoder from TensorFlow Hub
diff --git a/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/engine/OrtEngine.java b/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/engine/OrtEngine.java
index 9d312560b..ee843bfdd 100644
--- a/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/engine/OrtEngine.java
+++ b/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/engine/OrtEngine.java
@@ -115,7 +115,7 @@ public final class OrtEngine extends Engine {
                 sessionOptions.addCUDA();
                 return true;
             } catch (OrtException e) {
-                logger.warn("CUDA is not supported OnnxRuntime engine: " + e.getMessage());
+                logger.warn("CUDA is not supported OnnxRuntime engine: {}", e.getMessage());
                 return false;
             }
         }
diff --git a/examples/docs/BERT_question_and_answer.md b/examples/docs/BERT_question_and_answer.md
index 9c65bb317..a66ca2084 100644
--- a/examples/docs/BERT_question_and_answer.md
+++ b/examples/docs/BERT_question_and_answer.md
@@ -2,7 +2,7 @@
 
 In this example, you learn how to use the BERT QA model trained by GluonNLP (Apache MXNet) and PyTorch. 
 You can provide the model with a question and a paragraph containing an answer. The model is then able to find the best answer from the answer paragraph.
-You can find the source code in [BertQaInference.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/BertQaInference.java).
+You can find the source code in [BertQaInference.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java).
 
 Note that Apache MXNet BERT model has a limitation where the max size of the tokens including the question and the paragraph is 384.  
 
@@ -33,5 +33,5 @@ Follow [setup](../../docs/development/setup.md) to configure your development en
 
 ```sh
 cd examples
-./gradlew run -Dmain=ai.djl.examples.inference.BertQaInference
+./gradlew run -Dmain=ai.djl.examples.inference.nlp.BertQaInference
 ```
diff --git a/examples/docs/sentiment_analysis.md b/examples/docs/sentiment_analysis.md
index c83e2f4c1..c18c5cc87 100644
--- a/examples/docs/sentiment_analysis.md
+++ b/examples/docs/sentiment_analysis.md
@@ -2,7 +2,7 @@
 
 In this example, you learn how to use the DistilBERT model trained by HuggingFace using PyTorch. 
 You can provide the model with a question and a paragraph containing an answer. The model is then able to find the best answer from the answer paragraph.
-You can find the source code in [SentimentAnalysis.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/SentimentAnalysis.java).
+You can find the source code in [SentimentAnalysis.java](https://github.com/deepjavalibrary/djl/blob/master/examples/src/main/java/ai/djl/examples/inference/nlp/SentimentAnalysis.java).
 
 Example:
 
@@ -29,5 +29,5 @@ Follow [setup](../../docs/development/setup.md) to configure your development en
 
 ```sh
 cd examples
-./gradlew run -Dmain=ai.djl.examples.inference.SentimentAnalysis
+./gradlew run -Dmain=ai.djl.examples.inference.nlp.SentimentAnalysis
 ```
diff --git a/examples/src/main/java/ai/djl/examples/inference/BertClassification.java b/examples/src/main/java/ai/djl/examples/inference/nlp/BertClassification.java
similarity index 99%
rename from examples/src/main/java/ai/djl/examples/inference/BertClassification.java
rename to examples/src/main/java/ai/djl/examples/inference/nlp/BertClassification.java
index dcea78b25..fd5788123 100644
--- a/examples/src/main/java/ai/djl/examples/inference/BertClassification.java
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/BertClassification.java
@@ -10,7 +10,7 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
 import ai.djl.MalformedModelException;
 import ai.djl.ModelException;
diff --git a/examples/src/main/java/ai/djl/examples/inference/BertQaInference.java b/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
similarity index 89%
rename from examples/src/main/java/ai/djl/examples/inference/BertQaInference.java
rename to examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
index 9538b7c00..6f75ec728 100644
--- a/examples/src/main/java/ai/djl/examples/inference/BertQaInference.java
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/BertQaInference.java
@@ -11,11 +11,10 @@
  * and limitations under the License.
  */
 
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
-import ai.djl.Application;
-import ai.djl.Device;
 import ai.djl.ModelException;
+import ai.djl.huggingface.translator.QuestionAnsweringTranslatorFactory;
 import ai.djl.inference.Predictor;
 import ai.djl.modality.nlp.qa.QAInput;
 import ai.djl.repository.zoo.Criteria;
@@ -65,11 +64,11 @@ public final class BertQaInference {
 
         Criteria<QAInput, String> criteria =
                 Criteria.builder()
-                        .optApplication(Application.NLP.QUESTION_ANSWER)
                         .setTypes(QAInput.class, String.class)
-                        .optFilter("backbone", "bert")
+                        .optModelUrls(
+                                "djl://ai.djl.huggingface.pytorch/deepset/minilm-uncased-squad2")
                         .optEngine("PyTorch")
-                        .optDevice(Device.cpu())
+                        .optTranslatorFactory(new QuestionAnsweringTranslatorFactory())
                         .optProgress(new ProgressBar())
                         .build();
 
diff --git a/examples/src/main/java/ai/djl/examples/inference/SentimentAnalysis.java b/examples/src/main/java/ai/djl/examples/inference/nlp/SentimentAnalysis.java
similarity index 95%
rename from examples/src/main/java/ai/djl/examples/inference/SentimentAnalysis.java
rename to examples/src/main/java/ai/djl/examples/inference/nlp/SentimentAnalysis.java
index 40045f21a..29671fb5d 100644
--- a/examples/src/main/java/ai/djl/examples/inference/SentimentAnalysis.java
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/SentimentAnalysis.java
@@ -11,9 +11,8 @@
  * and limitations under the License.
  */
 
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
-import ai.djl.Application;
 import ai.djl.Device;
 import ai.djl.MalformedModelException;
 import ai.djl.ModelException;
@@ -58,8 +57,8 @@ public final class SentimentAnalysis {
 
         Criteria<String, Classifications> criteria =
                 Criteria.builder()
-                        .optApplication(Application.NLP.SENTIMENT_ANALYSIS)
                         .setTypes(String.class, Classifications.class)
+                        .optModelUrls("djl://ai.djl.pytorch/distilbert")
                         .optEngine("PyTorch")
                         // This model was traced on CPU and can only run on CPU
                         .optDevice(Device.cpu())
diff --git a/examples/src/main/java/ai/djl/examples/inference/UniversalSentenceEncoder.java b/examples/src/main/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoder.java
similarity index 99%
rename from examples/src/main/java/ai/djl/examples/inference/UniversalSentenceEncoder.java
rename to examples/src/main/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoder.java
index 4cc299f42..ac6c1619e 100644
--- a/examples/src/main/java/ai/djl/examples/inference/UniversalSentenceEncoder.java
+++ b/examples/src/main/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoder.java
@@ -10,7 +10,7 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
 import ai.djl.Application;
 import ai.djl.MalformedModelException;
diff --git a/extensions/tokenizers/build.gradle.kts b/extensions/tokenizers/build.gradle.kts
index 2e63b7da5..30fd0e4dc 100644
--- a/extensions/tokenizers/build.gradle.kts
+++ b/extensions/tokenizers/build.gradle.kts
@@ -81,7 +81,7 @@ tasks {
             for (task in tasks) {
                 var file = prefix / task / "ai.djl.huggingface.pytorch.json"
                 if (file.exists())
-                    project.logger.lifecycle("PyTorch model zoo metadata alrady exists: $task")
+                    project.logger.lifecycle("PyTorch model zoo metadata already exists: $task")
                 else {
                     project.logger.lifecycle("Downloading PyTorch model zoo metadata: $task")
                     file.parentFile.mkdirs()
diff --git a/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py b/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
index 0f40c2d77..6776f8d1f 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/huggingface_converter.py
@@ -138,12 +138,7 @@ class HuggingfaceConverter:
             return False, "Failed to save tokenizer", -1
 
         # Save config.json
-        if os.path.exists(model_id):
-            config_file = os.path.join(model_id, "config.json")
-        else:
-            config_file = hf_hub_download(repo_id=model_id,
-                                          filename="config.json")
-
+        config_file = self.get_file(model_id, "config.json")
         shutil.copyfile(config_file, os.path.join(temp_dir, "config.json"))
 
         target = os.path.join(temp_dir, "model.safetensors")
@@ -169,12 +164,10 @@ class HuggingfaceConverter:
                     has_pt_file = True
 
             if has_sf_file:
-                file = hf_hub_download(repo_id=model_id,
-                                       filename="model.safetensors")
+                file = self.get_file(model_id, "model.safetensors")
                 shutil.copyfile(file, target)
             elif has_pt_file:
-                file = hf_hub_download(repo_id=model_id,
-                                       filename="pytorch_model.bin")
+                file = self.get_file(model_id, "pytorch_model.bin")
                 convert_file(file, target)
             else:
                 return False, f"No model file found for: {model_id}", -1
@@ -211,12 +204,7 @@ class HuggingfaceConverter:
             return False, "Failed to save tokenizer", -1
 
         # Save config.json just for reference
-        if os.path.exists(model_id):
-            config_file = os.path.join(model_id, "config.json")
-        else:
-            config_file = hf_hub_download(repo_id=model_id,
-                                          filename="config.json")
-
+        config_file = self.get_file(model_id, "config.json")
         shutil.copyfile(config_file, os.path.join(temp_dir, "config.json"))
 
         # Save jit traced .pt file to temp dir
@@ -372,6 +360,13 @@ class HuggingfaceConverter:
 
         return self.verify_jit_output(hf_pipeline, encoding, out)
 
+    @staticmethod
+    def get_file(model_id: str, file_name: str) -> str:
+        if os.path.exists(model_id):
+            return os.path.join(model_id, file_name)
+        else:
+            return hf_hub_download(repo_id=model_id, filename=file_name)
+
     def get_extra_arguments(self, hf_pipeline, model_id: str,
                             temp_dir: str) -> dict:
         return {}
diff --git a/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py b/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
index 68d6e422a..8c3d394bc 100644
--- a/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
+++ b/extensions/tokenizers/src/main/python/djl_converter/sentence_similarity_converter.py
@@ -20,7 +20,6 @@ import torch
 from transformers import AutoTokenizer, AutoModel, AutoConfig
 
 from djl_converter.huggingface_converter import HuggingfaceConverter, PipelineHolder
-from huggingface_hub import hf_hub_download
 
 
 class SentenceSimilarityConverter(HuggingfaceConverter):
@@ -63,7 +62,7 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
                 'sentence_xlnet_config.json'
         ]:
             try:
-                file = hf_hub_download(repo_id=model_id, filename=config_name)
+                file = self.get_file(model_id, config_name)
                 with open(file) as f:
                     config = json.load(f)
                     if config.get("max_seq_length"):
@@ -75,7 +74,7 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
             except requests.exceptions.HTTPError:
                 pass
 
-        if not "maxLength" in args:
+        if "maxLength" not in args:
             if hasattr(hf_pipeline.model, "config"):
                 config = hf_pipeline.model.config
             else:
@@ -92,7 +91,7 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
         layer_norm_path = None
         normalize = False
         try:
-            file = hf_hub_download(repo_id=model_id, filename="modules.json")
+            file = self.get_file(model_id, "modules.json")
             with open(file, "r") as f:
                 modules = json.load(f)
 
@@ -113,8 +112,7 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
 
         if pooling_path:
             try:
-                file = hf_hub_download(repo_id=model_id,
-                                       filename=f"{pooling_path}/config.json")
+                file = self.get_file(model_id, f"{pooling_path}/config.json")
                 if os.path.exists(file):
                     with open(file, "r") as f:
                         pooling = json.load(f)
@@ -134,8 +132,7 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
 
         if dense_path:
             try:
-                file = hf_hub_download(repo_id=model_id,
-                                       filename=f"{dense_path}/config.json")
+                file = self.get_file(model_id, f"{dense_path}/config.json")
                 with open(file, "r") as f:
                     dense = json.load(f)
                     activation = dense.get("activation_function")
@@ -166,6 +163,5 @@ class SentenceSimilarityConverter(HuggingfaceConverter):
     @staticmethod
     def save_module_weight(model_id: str, temp_dir: str, layer: str,
                            name: str):
-        file = hf_hub_download(repo_id=model_id,
-                               filename=f"{layer}/model.safetensors")
+        file = self.get_file(model_id, f"{layer}/model.safetensors")
         shutil.copyfile(file, os.path.join(temp_dir, f"{name}.safetensors"))
diff --git a/extensions/tokenizers/src/main/python/requirements.txt b/extensions/tokenizers/src/main/python/requirements.txt
index 231427da0..12c703a46 100644
--- a/extensions/tokenizers/src/main/python/requirements.txt
+++ b/extensions/tokenizers/src/main/python/requirements.txt
@@ -1,7 +1,8 @@
-huggingface_hub
+huggingface-hub
 transformers
-sentence_transformers
+sentence-transformers
 torch
 protobuf==3.20.2
 optimum[exporters,onnxruntime]
-safetensors
\ No newline at end of file
+safetensors
+requests
\ No newline at end of file
diff --git a/extensions/tokenizers/src/main/python/setup.py b/extensions/tokenizers/src/main/python/setup.py
index 6264cc145..c89cfed06 100755
--- a/extensions/tokenizers/src/main/python/setup.py
+++ b/extensions/tokenizers/src/main/python/setup.py
@@ -35,8 +35,8 @@ if __name__ == '__main__':
     version = detect_version()
 
     requirements = [
-        'huggingface_hub', 'transformers', 'sentence_transformers', 'torch',
-        'optimum[exporters,onnxruntime]', 'safetensors'
+        'huggingface-hub', 'transformers', 'sentence-transformers', 'torch',
+        'optimum[exporters,onnxruntime]', 'safetensors', 'requests'
     ]
 
     setup(name='djl_converter',


Test patch:
diff --git a/examples/src/test/java/ai/djl/examples/inference/ActionRecognitionTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/ActionRecognitionTest.java
similarity index 92%
rename from examples/src/test/java/ai/djl/examples/inference/ActionRecognitionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/ActionRecognitionTest.java
index 63b9bbc2b..cc7253e6b 100644
--- a/examples/src/test/java/ai/djl/examples/inference/ActionRecognitionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/ActionRecognitionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.ActionRecognition;
 import ai.djl.modality.Classifications;
 import ai.djl.translate.TranslateException;
 
diff --git a/examples/src/test/java/ai/djl/examples/inference/BigGANTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/BigGANTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/BigGANTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/BigGANTest.java
index 8158db050..80aebc619 100644
--- a/examples/src/test/java/ai/djl/examples/inference/BigGANTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/BigGANTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.BigGAN;
 import ai.djl.modality.cv.Image;
 import ai.djl.testing.TestRequirements;
 import ai.djl.translate.TranslateException;
diff --git a/examples/src/test/java/ai/djl/examples/inference/InstanceSegmentationTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/InstanceSegmentationTest.java
similarity index 93%
rename from examples/src/test/java/ai/djl/examples/inference/InstanceSegmentationTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/InstanceSegmentationTest.java
index e04f0f99b..193d2fdf2 100644
--- a/examples/src/test/java/ai/djl/examples/inference/InstanceSegmentationTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/InstanceSegmentationTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.InstanceSegmentation;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.translate.TranslateException;
diff --git a/examples/src/test/java/ai/djl/examples/inference/MaskDetectionTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/MaskDetectionTest.java
similarity index 95%
rename from examples/src/test/java/ai/djl/examples/inference/MaskDetectionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/MaskDetectionTest.java
index 02ef7f6f8..42da8844e 100644
--- a/examples/src/test/java/ai/djl/examples/inference/MaskDetectionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/MaskDetectionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.MaskDetection;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/ObjectDetectionTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionTest.java
similarity index 95%
rename from examples/src/test/java/ai/djl/examples/inference/ObjectDetectionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionTest.java
index 076fda3f9..e83c4881d 100644
--- a/examples/src/test/java/ai/djl/examples/inference/ObjectDetectionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.ObjectDetection;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/ObjectDetectionWithTensorflowSavedModelTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionWithTensorflowSavedModelTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/ObjectDetectionWithTensorflowSavedModelTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionWithTensorflowSavedModelTest.java
index cb8448113..91704905d 100644
--- a/examples/src/test/java/ai/djl/examples/inference/ObjectDetectionWithTensorflowSavedModelTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/ObjectDetectionWithTensorflowSavedModelTest.java
@@ -11,10 +11,9 @@
  * and limitations under the License.
  */
 
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.ObjectDetectionWithTensorflowSavedModel;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/PoseEstimationTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/PoseEstimationTest.java
similarity index 92%
rename from examples/src/test/java/ai/djl/examples/inference/PoseEstimationTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/PoseEstimationTest.java
index 398c71ced..bade178b6 100644
--- a/examples/src/test/java/ai/djl/examples/inference/PoseEstimationTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/PoseEstimationTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.PoseEstimation;
 import ai.djl.modality.cv.output.Joints;
 import ai.djl.translate.TranslateException;
 
diff --git a/examples/src/test/java/ai/djl/examples/inference/StyleTransferTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/StyleTransferTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/StyleTransferTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/StyleTransferTest.java
index 645a88b57..c5159b298 100644
--- a/examples/src/test/java/ai/djl/examples/inference/StyleTransferTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/StyleTransferTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.StyleTransfer;
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.ImageFactory;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/Yolov8DetectionTest.java b/examples/src/test/java/ai/djl/examples/inference/cv/Yolov8DetectionTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/Yolov8DetectionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/cv/Yolov8DetectionTest.java
index de46c8967..f34c0a025 100644
--- a/examples/src/test/java/ai/djl/examples/inference/Yolov8DetectionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/Yolov8DetectionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.cv;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.cv.Yolov8Detection;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/cv/package-info.java b/examples/src/test/java/ai/djl/examples/inference/cv/package-info.java
new file mode 100644
index 000000000..c47d4738a
--- /dev/null
+++ b/examples/src/test/java/ai/djl/examples/inference/cv/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+
+/** Contains tests for the inference examples. */
+package ai.djl.examples.inference.cv;
diff --git a/examples/src/test/java/ai/djl/examples/inference/FeatureComparisonTest.java b/examples/src/test/java/ai/djl/examples/inference/face/FeatureComparisonTest.java
similarity index 91%
rename from examples/src/test/java/ai/djl/examples/inference/FeatureComparisonTest.java
rename to examples/src/test/java/ai/djl/examples/inference/face/FeatureComparisonTest.java
index 7e355ecfa..245acdcfa 100644
--- a/examples/src/test/java/ai/djl/examples/inference/FeatureComparisonTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/face/FeatureComparisonTest.java
@@ -10,11 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.face;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.face.FeatureComparison;
-import ai.djl.examples.inference.face.FeatureExtraction;
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.ImageFactory;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/FeatureExtractionTest.java b/examples/src/test/java/ai/djl/examples/inference/face/FeatureExtractionTest.java
similarity index 93%
rename from examples/src/test/java/ai/djl/examples/inference/FeatureExtractionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/face/FeatureExtractionTest.java
index c5ac6bd11..af50b240a 100644
--- a/examples/src/test/java/ai/djl/examples/inference/FeatureExtractionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/face/FeatureExtractionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.face;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.face.FeatureExtraction;
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.ImageFactory;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/LightFaceDetectionTest.java b/examples/src/test/java/ai/djl/examples/inference/face/LightFaceDetectionTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/LightFaceDetectionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/face/LightFaceDetectionTest.java
index 360a76049..b3d28a36c 100644
--- a/examples/src/test/java/ai/djl/examples/inference/LightFaceDetectionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/face/LightFaceDetectionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.face;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.face.LightFaceDetection;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/RetinaFaceDetectionTest.java b/examples/src/test/java/ai/djl/examples/inference/face/RetinaFaceDetectionTest.java
similarity index 94%
rename from examples/src/test/java/ai/djl/examples/inference/RetinaFaceDetectionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/face/RetinaFaceDetectionTest.java
index 0060cd3e6..39e9737ce 100644
--- a/examples/src/test/java/ai/djl/examples/inference/RetinaFaceDetectionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/face/RetinaFaceDetectionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.face;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.face.RetinaFaceDetection;
 import ai.djl.modality.Classifications;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/face/package-info.java b/examples/src/test/java/ai/djl/examples/inference/face/package-info.java
new file mode 100644
index 000000000..881e4fd93
--- /dev/null
+++ b/examples/src/test/java/ai/djl/examples/inference/face/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+
+/** Contains tests for the inference examples. */
+package ai.djl.examples.inference.face;
diff --git a/examples/src/test/java/ai/djl/examples/inference/BertQaTest.java b/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
similarity index 96%
rename from examples/src/test/java/ai/djl/examples/inference/BertQaTest.java
rename to examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
index 06194ac51..21a70ff67 100644
--- a/examples/src/test/java/ai/djl/examples/inference/BertQaTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/nlp/BertQaTest.java
@@ -10,7 +10,7 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
 import ai.djl.ModelException;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/SentimentAnalysisTest.java b/examples/src/test/java/ai/djl/examples/inference/nlp/SentimentAnalysisTest.java
similarity index 96%
rename from examples/src/test/java/ai/djl/examples/inference/SentimentAnalysisTest.java
rename to examples/src/test/java/ai/djl/examples/inference/nlp/SentimentAnalysisTest.java
index 4091c9152..c18b1f296 100644
--- a/examples/src/test/java/ai/djl/examples/inference/SentimentAnalysisTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/nlp/SentimentAnalysisTest.java
@@ -10,7 +10,7 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
 import ai.djl.ModelException;
 import ai.djl.modality.Classifications;
diff --git a/examples/src/test/java/ai/djl/examples/inference/UniversalSentenceEncoderTest.java b/examples/src/test/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoderTest.java
similarity index 97%
rename from examples/src/test/java/ai/djl/examples/inference/UniversalSentenceEncoderTest.java
rename to examples/src/test/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoderTest.java
index a85564fb6..fb9551be1 100644
--- a/examples/src/test/java/ai/djl/examples/inference/UniversalSentenceEncoderTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/nlp/UniversalSentenceEncoderTest.java
@@ -10,7 +10,7 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.nlp;
 
 import ai.djl.ModelException;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/SuperResolutionTest.java b/examples/src/test/java/ai/djl/examples/inference/sr/SuperResolutionTest.java
similarity index 95%
rename from examples/src/test/java/ai/djl/examples/inference/SuperResolutionTest.java
rename to examples/src/test/java/ai/djl/examples/inference/sr/SuperResolutionTest.java
index e40c2e6ad..4895c8d74 100644
--- a/examples/src/test/java/ai/djl/examples/inference/SuperResolutionTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/sr/SuperResolutionTest.java
@@ -10,10 +10,9 @@
  * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
  * and limitations under the License.
  */
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.sr;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.sr.SuperResolution;
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.ImageFactory;
 import ai.djl.testing.TestRequirements;
diff --git a/examples/src/test/java/ai/djl/examples/inference/sr/package-info.java b/examples/src/test/java/ai/djl/examples/inference/sr/package-info.java
new file mode 100644
index 000000000..60033daa2
--- /dev/null
+++ b/examples/src/test/java/ai/djl/examples/inference/sr/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+
+/** Contains tests for the inference examples. */
+package ai.djl.examples.inference.sr;
diff --git a/examples/src/test/java/ai/djl/examples/inference/TimeSeriesTest.java b/examples/src/test/java/ai/djl/examples/inference/timeseries/TimeSeriesTest.java
similarity index 92%
rename from examples/src/test/java/ai/djl/examples/inference/TimeSeriesTest.java
rename to examples/src/test/java/ai/djl/examples/inference/timeseries/TimeSeriesTest.java
index 614a43560..ee6fce700 100644
--- a/examples/src/test/java/ai/djl/examples/inference/TimeSeriesTest.java
+++ b/examples/src/test/java/ai/djl/examples/inference/timeseries/TimeSeriesTest.java
@@ -11,11 +11,9 @@
  * and limitations under the License.
  */
 
-package ai.djl.examples.inference;
+package ai.djl.examples.inference.timeseries;
 
 import ai.djl.ModelException;
-import ai.djl.examples.inference.timeseries.AirPassengersDeepAR;
-import ai.djl.examples.inference.timeseries.M5ForecastingDeepAR;
 import ai.djl.testing.TestRequirements;
 import ai.djl.translate.TranslateException;
 
diff --git a/examples/src/test/java/ai/djl/examples/inference/timeseries/package-info.java b/examples/src/test/java/ai/djl/examples/inference/timeseries/package-info.java
new file mode 100644
index 000000000..8d8fd25e2
--- /dev/null
+++ b/examples/src/test/java/ai/djl/examples/inference/timeseries/package-info.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2024 Amazon.com, Inc. or its affiliates. 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. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+
+/** Contains tests for the inference examples. */
+package ai.djl.examples.inference.timeseries;