Repository: deepjavalibrary/djl
Pull request #2079 (2022-10-12 00:51:55)

[pytorch] Adds image classification resnet18 base model to model zoo
## 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


codecov-commenter: # [Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/2079?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) Report
Base: **72.08**% // Head: **70.19**% // Decreases project coverage by **`-1.89%`** :warning:
> Coverage data is based on head [(`2bed909`)](https://codecov.io/gh/deepjavalibrary/djl/pull/2079?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) compared to base [(`bb5073f`)](https://codecov.io/gh/deepjavalibrary/djl/commit/bb5073f7ffc33f1883621fc1783dbb01e70b418f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).
> Patch coverage: 71.74% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##             master    #2079      +/-   ##
============================================
- Coverage     72.08%   70.19%   -1.90%     
- Complexity     5126     6076     +950     
============================================
  Files           473      605     +132     
  Lines         21970    26924    +4954     
  Branches       2351     2917     +566     
============================================
+ Hits          15838    18898    +3060     
- Misses         4925     6613    +1688     
- Partials       1207     1413     +206     
```


| [Impacted Files](https://codecov.io/gh/deepjavalibrary/djl/pull/2079?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | Coverage Δ | |
|---|---|---|
| [api/src/main/java/ai/djl/modality/cv/Image.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L0ltYWdlLmphdmE=) | `69.23% <ø> (-4.11%)` | :arrow_down: |
| [...rc/main/java/ai/djl/modality/cv/MultiBoxPrior.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L011bHRpQm94UHJpb3IuamF2YQ==) | `76.00% <ø> (ø)` | |
| [...rc/main/java/ai/djl/modality/cv/output/Joints.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L291dHB1dC9Kb2ludHMuamF2YQ==) | `71.42% <ø> (ø)` | |
| [.../main/java/ai/djl/modality/cv/output/Landmark.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L291dHB1dC9MYW5kbWFyay5qYXZh) | `100.00% <ø> (ø)` | |
| [...main/java/ai/djl/modality/cv/output/Rectangle.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L291dHB1dC9SZWN0YW5nbGUuamF2YQ==) | `72.41% <0.00%> (ø)` | |
| [...i/djl/modality/cv/translator/BigGANTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvQmlnR0FOVHJhbnNsYXRvci5qYXZh) | `21.42% <0.00%> (-5.24%)` | :arrow_down: |
| [.../modality/cv/translator/ImageFeatureExtractor.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvSW1hZ2VGZWF0dXJlRXh0cmFjdG9yLmphdmE=) | `0.00% <0.00%> (ø)` | |
| [.../ai/djl/modality/cv/translator/YoloTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1RyYW5zbGF0b3IuamF2YQ==) | `27.77% <0.00%> (+18.95%)` | :arrow_up: |
| [...modality/cv/translator/wrapper/FileTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3Ivd3JhcHBlci9GaWxlVHJhbnNsYXRvci5qYXZh) | `44.44% <ø> (ø)` | |
| [...y/cv/translator/wrapper/InputStreamTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3Ivd3JhcHBlci9JbnB1dFN0cmVhbVRyYW5zbGF0b3IuamF2YQ==) | `44.44% <ø> (ø)` | |
| ... and [526 more](https://codecov.io/gh/deepjavalibrary/djl/pull/2079/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/2079?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).


Patch:
diff --git a/api/src/main/java/ai/djl/modality/cv/output/CategoryMask.java b/api/src/main/java/ai/djl/modality/cv/output/CategoryMask.java
index 4cdc75e21..c7d5414da 100644
--- a/api/src/main/java/ai/djl/modality/cv/output/CategoryMask.java
+++ b/api/src/main/java/ai/djl/modality/cv/output/CategoryMask.java
@@ -130,26 +130,26 @@ public class CategoryMask implements JsonSerializable {
      * Highlights the detected object on the image with random colors.
      *
      * @param image the original image
-     * @param transparency the transparency of the overlay. Value is between 0 and 255 inclusive,
-     *     where 0 means the overlay is completely transparent and 255 means the overlay is
-     *     completely opaque.
+     * @param opacity the opacity of the overlay. Value is between 0 and 255 inclusive, where 0
+     *     means the overlay is completely transparent and 255 means the overlay is completely
+     *     opaque.
      */
-    public void drawMask(Image image, int transparency) {
-        drawMask(image, transparency, COLOR_BLACK);
+    public void drawMask(Image image, int opacity) {
+        drawMask(image, opacity, COLOR_BLACK);
     }
 
     /**
      * Highlights the detected object on the image with random colors.
      *
      * @param image the original image
-     * @param transparency the transparency of the overlay. Value is between 0 and 255 inclusive,
-     *     where 0 means the overlay is completely opaque and 255 means the overlay is completely
-     *     transparent.
+     * @param opacity the opacity of the overlay. Value is between 0 and 255 inclusive, where 0
+     *     means the overlay is completely transparent and 255 means the overlay is completely
+     *     opaque.
      * @param background replace the background with specified background color, use transparent
      *     color to remove background
      */
-    public void drawMask(Image image, int transparency, int background) {
-        int[] colors = generateColors(background, transparency);
+    public void drawMask(Image image, int opacity, int background) {
+        int[] colors = generateColors(background, opacity);
         Image maskImage = getColorOverlay(colors);
         image.drawImage(maskImage, true);
     }
@@ -159,12 +159,14 @@ public class CategoryMask implements JsonSerializable {
      *
      * @param image the original image
      * @param classId the class to draw on the image
-     * @param color the rgb color with transparency
-     * @param transparency the transparency of the overlay
+     * @param color the rgb color with opacity
+     * @param opacity the opacity of the overlay. Value is between 0 and 255 inclusive, where 0
+     *     means the overlay is completely transparent and 255 means the overlay is completely
+     *     opaque.
      */
-    public void drawMask(Image image, int classId, int color, int transparency) {
+    public void drawMask(Image image, int classId, int color, int opacity) {
         int[] colors = new int[classes.size()];
-        colors[classId] = color & 0xFFFFFF | transparency << 24;
+        colors[classId] = color & 0xFFFFFF | opacity << 24;
         Image colorOverlay = getColorOverlay(colors);
         image.drawImage(colorOverlay, true);
     }
@@ -182,14 +184,14 @@ public class CategoryMask implements JsonSerializable {
         return ImageFactory.getInstance().fromPixels(pixels, width, height);
     }
 
-    private int[] generateColors(int background, int transparency) {
+    private int[] generateColors(int background, int opacity) {
         int[] colors = new int[classes.size()];
         colors[0] = background;
         for (int i = 1; i < classes.size(); i++) {
             int red = RandomUtils.nextInt(256);
             int green = RandomUtils.nextInt(256);
             int blue = RandomUtils.nextInt(256);
-            colors[i] = transparency << 24 | red << 16 | green << 8 | blue;
+            colors[i] = opacity << 24 | red << 16 | green << 8 | blue;
         }
         return colors;
     }
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractor.java b/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractor.java
new file mode 100644
index 000000000..8edde7a34
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractor.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 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.
+ */
+package ai.djl.modality.cv.translator;
+
+import ai.djl.ndarray.NDList;
+import ai.djl.translate.TranslatorContext;
+
+import java.util.Map;
+
+/**
+ * A generic {@link ai.djl.translate.Translator} for Image Classification feature extraction tasks.
+ */
+public class ImageFeatureExtractor extends BaseImageTranslator<byte[]> {
+
+    /**
+     * Constructs an Image Classification using {@link Builder}.
+     *
+     * @param builder the data to build with
+     */
+    ImageFeatureExtractor(Builder builder) {
+        super(builder);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public byte[] processOutput(TranslatorContext ctx, NDList list) {
+        return list.get(0).toByteArray();
+    }
+
+    /**
+     * Creates a builder to build a {@code ImageFeatureExtractor}.
+     *
+     * @return a new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Creates a builder to build a {@code ImageFeatureExtractor} with specified arguments.
+     *
+     * @param arguments arguments to specify builder options
+     * @return a new builder
+     */
+    public static Builder builder(Map<String, ?> arguments) {
+        Builder builder = new Builder();
+        builder.configPreProcess(arguments);
+        return builder;
+    }
+
+    /** A Builder to construct a {@code ImageFeatureExtractor}. */
+    public static class Builder extends BaseBuilder<Builder> {
+
+        Builder() {}
+
+        /** {@inheritDoc} */
+        @Override
+        protected Builder self() {
+            return this;
+        }
+
+        /**
+         * Builds the {@link ImageFeatureExtractor} with the provided data.
+         *
+         * @return an {@link ImageFeatureExtractor}
+         */
+        public ImageFeatureExtractor build() {
+            validate();
+            return new ImageFeatureExtractor(this);
+        }
+    }
+}
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractorFactory.java b/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractorFactory.java
new file mode 100644
index 000000000..b89c7a155
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/cv/translator/ImageFeatureExtractorFactory.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 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.
+ */
+package ai.djl.modality.cv.translator;
+
+import ai.djl.Model;
+import ai.djl.modality.Input;
+import ai.djl.modality.Output;
+import ai.djl.modality.cv.Image;
+import ai.djl.modality.cv.translator.wrapper.FileTranslator;
+import ai.djl.modality.cv.translator.wrapper.InputStreamTranslator;
+import ai.djl.modality.cv.translator.wrapper.UrlTranslator;
+import ai.djl.translate.Translator;
+import ai.djl.translate.TranslatorFactory;
+import ai.djl.util.Pair;
+
+import java.io.InputStream;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/** A {@link TranslatorFactory} that creates an {@link ImageClassificationTranslator}. */
+public class ImageFeatureExtractorFactory implements TranslatorFactory {
+
+    private static final Set<Pair<Type, Type>> SUPPORTED_TYPES = new HashSet<>();
+
+    static {
+        SUPPORTED_TYPES.add(new Pair<>(Image.class, byte[].class));
+        SUPPORTED_TYPES.add(new Pair<>(Path.class, byte[].class));
+        SUPPORTED_TYPES.add(new Pair<>(URL.class, byte[].class));
+        SUPPORTED_TYPES.add(new Pair<>(InputStream.class, byte[].class));
+        SUPPORTED_TYPES.add(new Pair<>(Input.class, Output.class));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Set<Pair<Type, Type>> getSupportedTypes() {
+        return SUPPORTED_TYPES;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <I, O> Translator<I, O> newInstance(
+            Class<I> input, Class<O> output, Model model, Map<String, ?> arguments) {
+        ImageFeatureExtractor translator = ImageFeatureExtractor.builder(arguments).build();
+        if (input == Image.class && output == byte[].class) {
+            return (Translator<I, O>) translator;
+        } else if (input == Path.class && output == byte[].class) {
+            return (Translator<I, O>) new FileTranslator<>(translator);
+        } else if (input == URL.class && output == byte[].class) {
+            return (Translator<I, O>) new UrlTranslator<>(translator);
+        } else if (input == InputStream.class && output == byte[].class) {
+            return (Translator<I, O>) new InputStreamTranslator<>(translator);
+        } else if (input == Input.class && output == Output.class) {
+            return (Translator<I, O>) new ImageServingTranslator(translator);
+        }
+        throw new IllegalArgumentException("Unsupported input/output types.");
+    }
+}
diff --git a/api/src/main/java/ai/djl/ndarray/index/NDArrayIndexer.java b/api/src/main/java/ai/djl/ndarray/index/NDArrayIndexer.java
index fe9fa7e2c..ab799069d 100644
--- a/api/src/main/java/ai/djl/ndarray/index/NDArrayIndexer.java
+++ b/api/src/main/java/ai/djl/ndarray/index/NDArrayIndexer.java
@@ -20,9 +20,6 @@ import ai.djl.ndarray.index.full.NDIndexFullPick;
 import ai.djl.ndarray.index.full.NDIndexFullSlice;
 import ai.djl.ndarray.index.full.NDIndexFullTake;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.util.List;
 import java.util.Optional;
 
@@ -80,11 +77,6 @@ public abstract class NDArrayIndexer {
 
         Optional<NDIndexFullTake> fullTake = NDIndexFullTake.fromIndex(index, array.getShape());
         if (fullTake.isPresent()) {
-            Logger logger = LoggerFactory.getLogger(NDArrayIndexer.class);
-            logger.warn(
-                    "The definition of the getter by array NDIndex: get(NDIndex array) has changed"
-                        + " from pick to take.If you still want to use array index as pick, then do"
-                        + " it explicitly by get(new NDIndex().addPickDim(array));");
             return get(array, fullTake.get());
         }
 
diff --git a/api/src/main/java/ai/djl/ndarray/internal/NDFormat.java b/api/src/main/java/ai/djl/ndarray/internal/NDFormat.java
index c60998c13..2bb36da49 100644
--- a/api/src/main/java/ai/djl/ndarray/internal/NDFormat.java
+++ b/api/src/main/java/ai/djl/ndarray/internal/NDFormat.java
@@ -83,6 +83,7 @@ public abstract class NDFormat {
             sb.append(" hasGradient");
         }
         if (!withContent) {
+            sb.append("\nCheck the \"Development Guideline\"->Debug to enable array display.\n");
             return sb.toString();
         }
 
@@ -114,9 +115,7 @@ public abstract class NDFormat {
             int maxDepth,
             int maxRows,
             int maxColumns) {
-        init(array);
         sb.append(LF);
-
         long size = array.size();
         long dimension = array.getShape().dimension();
         if (size == 0) {
@@ -130,6 +129,7 @@ public abstract class NDFormat {
         } else if (dimension > maxDepth) {
             sb.append("[ Exceed max print dimension ]");
         } else {
+            init(array);
             dump(sb, array, 0, true, maxRows, maxColumns);
         }
         return sb.toString();
diff --git a/api/src/main/java/ai/djl/training/listener/TrainingListener.java b/api/src/main/java/ai/djl/training/listener/TrainingListener.java
index 993273bd2..3d81601f2 100644
--- a/api/src/main/java/ai/djl/training/listener/TrainingListener.java
+++ b/api/src/main/java/ai/djl/training/listener/TrainingListener.java
@@ -76,7 +76,7 @@ public interface TrainingListener {
     interface Defaults {
 
         /**
-         * A basic {@link TrainingListener} set with minimal recommended functionality.
+         * A basic {@link TrainingListener} set with the minimal recommended functionality.
          *
          * <p>This contains:
          *
diff --git a/api/src/main/java/ai/djl/translate/Batchifier.java b/api/src/main/java/ai/djl/translate/Batchifier.java
index 7f21a696f..fd8446f9c 100644
--- a/api/src/main/java/ai/djl/translate/Batchifier.java
+++ b/api/src/main/java/ai/djl/translate/Batchifier.java
@@ -18,8 +18,8 @@ import java.util.Arrays;
 import java.util.stream.IntStream;
 
 /**
- * An interface that provides methods to convert an un-batched {@link NDList} into a batched {@link
- * NDList} and vice versa.
+ * An interface for different strategies to convert between {@link ai.djl.training.dataset.Record}
+ * {@link NDList}s and {@link ai.djl.training.dataset.Batch} {@link NDList}.
  *
  * <p>Different implementations of {@code Batchifier} represent different ways of creating batches.
  * The most common would be the {@link StackBatchifier} that batchifies by creating a new batch axis
@@ -49,7 +49,8 @@ public interface Batchifier {
     }
 
     /**
-     * Converts an array of {@link NDList} into an NDList.
+     * Converts an array of {@link ai.djl.training.dataset.Record} {@link NDList}s into a combined
+     * {@link ai.djl.training.dataset.Batch} NDList.
      *
      * <p>The size of the input array is the batch size. The data in each of the {@link NDList} are
      * assumed to be the same, and are batched together to form one batched {@link NDList}.
@@ -60,7 +61,10 @@ public interface Batchifier {
     NDList batchify(NDList[] inputs);
 
     /**
-     * Reverses the {@link #batchify(NDList[]) batchify} operation.
+     * Splits a combined {@link ai.djl.training.dataset.Batch} {@link NDList} into it's constituent
+     * {@link ai.djl.training.dataset.Record} {@link NDList}s.
+     *
+     * <p>This reverses the {@link #batchify(NDList[]) batchify} operation.
      *
      * @param inputs the {@link NDList} that needs to be 'unbatchified'
      * @return an array of NDLists, of size equal to batch size, where each NDList is one element
@@ -69,7 +73,11 @@ public interface Batchifier {
     NDList[] unbatchify(NDList inputs);
 
     /**
-     * Splits the given {@link NDList} into the given number of slices.
+     * Partitions the given {@link ai.djl.training.dataset.Batch} {@link NDList} into multiple
+     * {@link ai.djl.training.dataset.Batch} lists with smaller batch size.
+     *
+     * <p>As an example, this function might be used for multi-GPU training where it takes the main
+     * batch and splits it into sub-batches that can be run on each GPU.
      *
      * <p>This function unbatchifies the input {@link NDList}, redistributes them into the given
      * number of slices, and then batchify each of the slices to form an array of {@link NDList}.
diff --git a/djl-zero/README.md b/djl-zero/README.md
index 518a0be78..6df910e26 100644
--- a/djl-zero/README.md
+++ b/djl-zero/README.md
@@ -2,14 +2,29 @@
 
 ## Overview
 
-This module is a zero deep learning knowledge required wrapper over DJL. Instead of worrying about finding a model or how to train, this will provide a simple recommendation for your deep learning application. It is the easiest way to get started with DJL and get a solution for your deep learning problem.
+This module is a zero deep learning knowledge required wrapper over DJL.
+Instead of worrying about finding a model or how to train, this will provide a simple recommendation for your deep learning application.
+It is the easiest way to get started with DJL and get a solution for your deep learning problem.
+
+DJL Zero is based on the [Application](https://javadoc.io/doc/ai.djl/api/latest/ai/djl/Application.html) or the common deep learning task you wish to train.
+It features an automatic training method (if currently implemented) and an automatic recommendation for pre-trained models (if applicable for the application).
+
+The automatic training method is designed to be simple and requires only two parameters.
+First is a `Dataset`, which all can be implemented with helpers found on our [guide to creating a dataset](../docs/development/how_to_use_dataset.md).
+And second, it requires a desired `Performance`: one of `FAST`, `BALANCED`, or `ACCURATE` depending on your desired place in the model size curve.
+In general, larger models are more accurate in exchange for requiring more disk space, cost, and latency.
+
+Some applications also feature a pre-trained method.
+This will pull a model for the application from the DJL model zoo that is recommended by the DJL team.
+Depending on the application, there may also be options in terms of what performance level, classifications, output type, etc.
 
 ## List of Applications
 
 This module contains the following applications:
 
-- Image Classification - take an image and classify the main subject of the image.
-
+- [Image Classification](https://javadoc.io/doc/ai.djl/djl-zero/latest/ai/djl/zero/cv/ImageClassification.html) - take an image and classify the main subject of the image.
+- [Object Detection](https://javadoc.io/doc/ai.djl/djl-zero/latest/ai/djl/zero/cv/ObjectDetection.html) - take an image and find each object (car, bike, plant, etc.) in the image along with the bounding box around it's location
+- [Tabular Regression](https://javadoc.io/doc/ai.djl/djl-zero/latest/ai/djl/zero/tabular/TabularRegression.html) - takes input features from a row in a table and predict the value of a numeric column
 
 ## Documentation
 
diff --git a/djl-zero/src/main/java/ai/djl/zero/tabular/Tabular.java b/djl-zero/src/main/java/ai/djl/zero/tabular/TabularRegression.java
similarity index 95%
rename from djl-zero/src/main/java/ai/djl/zero/tabular/Tabular.java
rename to djl-zero/src/main/java/ai/djl/zero/tabular/TabularRegression.java
index 634466a0d..28d621005 100644
--- a/djl-zero/src/main/java/ai/djl/zero/tabular/Tabular.java
+++ b/djl-zero/src/main/java/ai/djl/zero/tabular/TabularRegression.java
@@ -32,10 +32,10 @@ import ai.djl.zero.Performance;
 
 import java.io.IOException;
 
-/** Tabular takes a NDList as input and output an NDList (for supervised learning). */
-public final class Tabular {
+/** TabularRegression takes a NDList as input and output an NDList (for supervised learning). */
+public final class TabularRegression {
 
-    private Tabular() {}
+    private TabularRegression() {}
 
     /**
      * Trains a Model on a custom dataset. Currently, trains a TabNet Model.
diff --git a/docs/development/development_guideline.md b/docs/development/development_guideline.md
index 1b043de3b..a4d8e42b3 100644
--- a/docs/development/development_guideline.md
+++ b/docs/development/development_guideline.md
@@ -156,12 +156,11 @@ IntelliJ allows you to [customize the data view in the Debug Tools](https://www.
 You can create your own NDArray renderer as follows:
 ![](img/custom_debug_view.png)
 
-Please make sure to:
-
-- Check the "On-demand" option, which causes IntelliJ to only render the NDArray when you click on the variable.
-- Change the "Use following expression" field to `toDebugString(true)` to show NDArray content
-- Change the "Use following expression" field to something like `toDebugString(1000, 10, 10, 20, true)`
+Opening the IDE Settings/Preferences (`⌘ ,`), in *Java Type Renderer* section, you can enable array display by:
+- Change the "Use following expression" field to `toDebugString(true)` to show NDArray content.
+- Change the "Use following expression" field to something like `toDebugString(100, 10, 10, 20, true)`.
 if you want to adjust the range of NDArray's debug output.
+- Check the "On-demand" option, if you want the IntelliJ to render the NDArray only on clicking the variable.
 
 ## Common Problems
 
diff --git a/docs/development/memory_management.md b/docs/development/memory_management.md
index 867348c82..470bb5f25 100644
--- a/docs/development/memory_management.md
+++ b/docs/development/memory_management.md
@@ -1,44 +1,96 @@
 # Memory Management
 
-Memory is one of the biggest challenge in area of deep learning. There are several challenges in Java to manage the memory.
-Firstly, GC(Garbage Collector) doesn't have the control over the native memory.
-Secondly, to close every AutoClosable manually makes the code boilerplate and not practical.
-Thirdly, the system lacks in support of releasing a group of native resources.
-As a result, we create the [NDManager](https://javadoc.io/doc/ai.djl/api/latest/ai/djl/ndarray/NDManager.html)
-to help us release the native memory.
-
-We design the NDManager in tree structure. It provides fine-grained control of native resource and manage the resource scope in more effectively way.
-NDManager can make any kind of tree. However, using the Predictor/Trainer classes will automatically create a certain kind of tree.
-The structure of the NDManager for the classic inference case is like ![structure of the NDManager](https://raw.githubusercontent.com/deepjavalibrary/djl/master/docs/development/img/ndmanager_structure_for_inference.png).
-The structure of the NDManager for the classic training case is like ![structure of the NDManager](https://github.com/deepjavalibrary/djl/blob/master/docs/development/img/ndmanager_structure_for_training.png?raw=true).
-The topmost is System NDManager. The model, which is one layer below, contains the weight and bias of the Neural Network.
-The bottommost NDManager takes care of the intermediate NDArrays we would like to close as soon as the program exit the scope of the functions that use them.
-
-If the native resources is no longer used, we allow GC collect it's native memory before NDManager close them.
-However, there are still some limitations. For example, it may cause OOM(Out of memory) if preprocessing and postprocessing create a large amount of NDArrays in the training loop before GC kicks in.
- 
-Here are the rule of thumb:
-
-* The output of the NDArray operation should be attached to the same manager as the input one. The order matters as we use first NDArray's manager on the NDArray operation by default.
-* If intermediate NDArray get into upper level NDManager, e.g from Trainer -> Model, the memory leak will happen.
+Memory is one of the biggest challenge in the area of deep learning, especially in Java.
+The greatest issue is that the garbage collector doesn't have control over the native memory.
+It doesn't know how much is used and how to free it.
+Beyond that, it can be too slow for high-memory usages such as training on a GPU.
+
+Without the automatic memory management, it is still possible to manually free the memory.
+However, there are too many NDArrays that are created for such a thing to be practical.
+
+For this reason, DJL uses the [NDManager](https://javadoc.io/doc/ai.djl/api/latest/ai/djl/ndarray/NDManager.html).
+The NDManager behaves as both a factory and a scope.
+All arrays created by a manager are part of its scope.
+By freeing the manager, it can then free all the arrays attached to it.
+This also includes arrays produced by operations such as `NDArray.add(other)` or `Activation.relu(array)`.
+They are attached to the manager of the input argument.
+If it takes multiple `NDArray` inputs, it is attached to the manager for the first one.
+
+It is also common to have a hierarchy of NDManagers.
+Each of them represent a different level of abstraction and point at which the data are freed.
+As an example, let's look at some common usages:
+
+### Inference Use Case
+
+The structure of the NDManager for the classic inference case is like:
+
+![structure of the NDManager](https://raw.githubusercontent.com/deepjavalibrary/djl/master/docs/development/img/ndmanager_structure_for_inference.png).
+
+At the top of the hierarchy is the system manager.
+It contains memory which is global, and the system manager can't be closed.
+When you create a new manager by calling `NDManager.newBaseManager()`, it is created as a child of the system manager.
+
+Under the system manager, there is the manager for the model and then the predictor.
+Those are both likely to be long-lasting and contain the parameters to run prediction.
+The model would contain the standard copy and the predictor may contain additional copies of the parameters depending on the engine and wheter multiple devices are used.
+
+Each time data is passed into a call to `predict()`, it is added to a `PredictorContext` manager.
+This data is temporary and only lasts as long as the call to predict.
+It ensures that all the temporary arrays created during the `predict()` call are promptly freed.
+As long as the input and output to the `Translator` for the predictor are standard Java classes and not resources like `NDArray` or `NDList`, this will handle the memory automatically.
+If instead an `NDResource` is used for either an input or output, the user must make sure that the memory is attached to a specific manager and freed when no longer needed.
+
+Make sure that within the preprocessing, post-processing, and model, data is created only within the `PredictorContext`.
+Memory created in other managers (especially the model or predictor) would not be freed and would constitute a memory leak.
+
+### Training Use Case
+
+The structure of the NDManager for the classic training case is like:
+
+![structure of the NDManager](https://github.com/deepjavalibrary/djl/blob/master/docs/development/img/ndmanager_structure_for_training.png?raw=true).
+
+Like the inference use case, it contains the same system manager and model.
+The `Trainer` behaves similarly to the `Predictor`, although only one `Trainer` should be created rather than one per device.
+
+Then, there is also the `Batch`.
+It contains memory which should be used for a training step, and freed directly afterwards.
+Here, the use of the `NDManager` makes it possible to take up the whole GPU for each batch without straining the GC.
+Note that a **`Batch` must be manually closed at the end of the training step**.
+
+Make sure that within the training steps, loss, and model, data is created only within the `Batch` manager.
+Memory created in other managers (especially the model or trainer) would not be freed and would constitute a memory leak.
+If you remember to close the `Batch` and memory is still growing during the training step, some memory is likely being attached to the wrong manager.
+
+## Rules of Thumb:
+
+* The output of an NDArray operation should be attached to the same manager as the input NDArray. The order matters as we use first NDArray's manager on the NDArray operation by default.
+* If an intermediate NDArray gets into an upper level NDManager (e.g. from Trainer -> Model), this is a memory leak
 * You can use `NDManager.debugDump()` to see if any of NDManager's resource count is keep increasing.
 * If a large amount of intermediate NDArrays are needed, it is recommended to create your own subNDManager or close them manually.
 
-## Inference case
-For the majority of the inference cases, you would be working on the ProcessInput and ProcessOutput.
-Make sure all temporary NDArrays are attached to the NDManager in TranslatorContext.
-Note that if you don't specify NDManager in a NDArray operation, it uses the NDManger from the input NDArray.
-
-## Training
-The intermediate NDArrays involving in training case are usually
- 
-1. a batch of the dataset
-2. custom operation you write
-
-In general, all the parameters in the model should be associated with Model level NDManager.
-All of the input and output NDArrays should be associated with one NDManager which is one level down to the model NDManager.
-Please check if you call [batch.close()](https://javadoc.io/static/ai.djl/api/0.19.0/ai/djl/training/dataset/Batch.html#close--)
-to release one batch of the dataset at the end of each batch.
-If you still see the memory grows as the training process goes, it is most likely that intermediate NDArrays are attached to the Model(Block) parameter level.
-As a result, those NDArrays would not closed until the training is finished.
-On the other hand, if you implement your own block, be aware of not attaching the parameter to batch level NDManager.
+## Advanced Memory Management
+
+There are also a few tools for more sophisticated memory management.
+The first is by using `ndManager.tempAttachAll()`.
+This will allow a manager to "borrow" some resources (NDManager or NDList) from another manager.
+When the borrower is closed, the resources are returned to the original manager.
+
+The most common usage of temp attach is to create a scope for performing some computations.
+A dedicated manager is created for the computation and the inputs are temporarily attached to it.
+After the result is computed, the result is attached to a higher manager either manually or by using `computationManager.ret(resource)`.
+Then, the computation manager is closed which frees all intermediate computation memory and returns the input arrays back to their original manager.
+
+## Troubleshooting
+
+If you find your memory increasing, here are some things to look for.
+If you are doing training, make sure that you close the `Batch`.
+If you are doing inference, make sure that the translator either uses standard Java classes (not `NDManager` or `NDList`) or that the prediction input/output are freed manually.
+
+If none of these are the problem, one test is to try calling `ndManager.cap()`.
+Calling `cap` will block a manager from having additional data attached to it.
+Try to `cap` the manager for the Model and/or the Trainer/Predictor before a training batch or prediction call.
+This will throw an exception if any memory is attached to these higher managers accidentally.
+
+You can also use `BaseNDManager.debugDump(...)` to help see what managers and resources are attached to a given manager.
+Calling it on a manager repeatedly will let you know if the resource count is increasing.
+It may also help to set names for the arrays or managers to help identify them within the dump.
\ No newline at end of file
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index fdaedd333..5fa83cba4 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -147,6 +147,7 @@ nav:
           - 'docs/serving/serving/docs/management_api.md'
           - 'docs/serving/serving/docs/plugin_management.md'
       - 'docs/serving/wlm/README.md'
+      - DJL Zero: 'djl-zero/README.md'
   - Demos:
       - Demos: 'docs/demos/README.md'
       - AWS:
diff --git a/engines/dlr/dlr-native/CMakeLists.txt b/engines/dlr/dlr-native/CMakeLists.txt
index f8a3e6a7c..4b9e77b38 100644
--- a/engines/dlr/dlr-native/CMakeLists.txt
+++ b/engines/dlr/dlr-native/CMakeLists.txt
@@ -4,6 +4,8 @@ project(djl_dlr)
 set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+set(JAVA_AWT_LIBRARY NotNeeded)
+set(JAVA_AWT_INCLUDE_PATH NotNeeded)
 find_package(JNI REQUIRED)
 
 add_subdirectory(neo-ai-dlr)
diff --git a/engines/paddlepaddle/paddlepaddle-native/CMakeLists.txt b/engines/paddlepaddle/paddlepaddle-native/CMakeLists.txt
index c94e33f5b..567f66c36 100644
--- a/engines/paddlepaddle/paddlepaddle-native/CMakeLists.txt
+++ b/engines/paddlepaddle/paddlepaddle-native/CMakeLists.txt
@@ -4,6 +4,8 @@ project(djl_paddle)
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+set(JAVA_AWT_LIBRARY NotNeeded)
+set(JAVA_AWT_INCLUDE_PATH NotNeeded)
 find_package(JNI REQUIRED)
 find_library(PADDLE_LIBRARY
         NAMES paddle_inference
diff --git a/engines/pytorch/pytorch-model-zoo/src/main/java/ai/djl/pytorch/zoo/PtModelZoo.java b/engines/pytorch/pytorch-model-zoo/src/main/java/ai/djl/pytorch/zoo/PtModelZoo.java
index c53aeedd5..b49dd6a62 100644
--- a/engines/pytorch/pytorch-model-zoo/src/main/java/ai/djl/pytorch/zoo/PtModelZoo.java
+++ b/engines/pytorch/pytorch-model-zoo/src/main/java/ai/djl/pytorch/zoo/PtModelZoo.java
@@ -33,6 +33,8 @@ public class PtModelZoo extends ModelZoo {
 
     PtModelZoo() {
         addModel(REPOSITORY.model(CV.IMAGE_CLASSIFICATION, GROUP_ID, "resnet", "0.0.1"));
+        addModel(
+                REPOSITORY.model(CV.IMAGE_CLASSIFICATION, GROUP_ID, "resnet18_embedding", "0.0.1"));
         addModel(REPOSITORY.model(CV.OBJECT_DETECTION, GROUP_ID, "ssd", "0.0.1"));
         addModel(REPOSITORY.model(NLP.QUESTION_ANSWER, GROUP_ID, "bertqa", "0.0.1"));
         addModel(REPOSITORY.model(NLP.SENTIMENT_ANALYSIS, GROUP_ID, "distilbert", "0.0.1"));
diff --git a/engines/pytorch/pytorch-native/CMakeLists.txt b/engines/pytorch/pytorch-native/CMakeLists.txt
index 0db265312..a8d8e362b 100644
--- a/engines/pytorch/pytorch-native/CMakeLists.txt
+++ b/engines/pytorch/pytorch-native/CMakeLists.txt
@@ -11,6 +11,9 @@ endif()
 if(NOT BUILD_ANDROID)
     message("Building djl_torch with the host...")
     find_package(Torch REQUIRED)
+
+    set(JAVA_AWT_LIBRARY NotNeeded)
+    set(JAVA_AWT_INCLUDE_PATH NotNeeded)
     find_package(JNI REQUIRED)
 
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
diff --git a/engines/tensorrt/CMakeLists.txt b/engines/tensorrt/CMakeLists.txt
index c48f21727..21c1e64d9 100644
--- a/engines/tensorrt/CMakeLists.txt
+++ b/engines/tensorrt/CMakeLists.txt
@@ -4,6 +4,8 @@ project(djl_trt)
 set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+set(JAVA_AWT_LIBRARY NotNeeded)
+set(JAVA_AWT_INCLUDE_PATH NotNeeded)
 find_package(JNI REQUIRED)
 #find_library(TRT_ONNX_PARSER
 #        NAMES nvonnxparser
diff --git a/extensions/fasttext/CMakeLists.txt b/extensions/fasttext/CMakeLists.txt
index a4fdfa5c4..7997d9e6f 100644
--- a/extensions/fasttext/CMakeLists.txt
+++ b/extensions/fasttext/CMakeLists.txt
@@ -4,6 +4,8 @@ project(jni_fasttext)
 set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
+set(JAVA_AWT_LIBRARY NotNeeded)
+set(JAVA_AWT_INCLUDE_PATH NotNeeded)
 find_package(JNI REQUIRED)
 find_path(UTILS_INCLUDE_DIR NAMES djl/utils.h PATHS ${PROJECT_SOURCE_DIR}/../../api/src/main/native REQUIRED)
 add_subdirectory(fastText)
diff --git a/extensions/sentencepiece/CMakeLists.txt b/extensions/sentencepiece/CMakeLists.txt
index 7aea0fd21..f3068ce65 100644
--- a/extensions/sentencepiece/CMakeLists.txt
+++ b/extensions/sentencepiece/CMakeLists.txt
@@ -17,6 +17,8 @@ if (MSVC)
   add_definitions("/wd4267 /wd4244 /wd4305 /Zc:strictStrings /utf-8")
 endif()
 
+set(JAVA_AWT_LIBRARY NotNeeded)
+set(JAVA_AWT_INCLUDE_PATH NotNeeded)
 find_package(JNI REQUIRED)
 find_path(UTILS_INCLUDE_DIR NAMES djl/utils.h PATHS ${PROJECT_SOURCE_DIR}/../../api/src/main/native REQUIRED)
 
diff --git a/tools/gradle/publish.gradle b/tools/gradle/publish.gradle
index 23afe8041..86d3fc686 100644
--- a/tools/gradle/publish.gradle
+++ b/tools/gradle/publish.gradle
@@ -1,6 +1,7 @@
 configure([
         project(':api'),
         project(':basicdataset'),
+        project(':djl-zero'),
         project(':engines:dlr:dlr-engine'),
         project(':engines:ml:xgboost'),
         project(':engines:ml:lightgbm'),


Test patch:
diff --git a/api/src/test/java/ai/djl/ndarray/internal/NDFormatTest.java b/api/src/test/java/ai/djl/ndarray/internal/NDFormatTest.java
index f629f51b4..9b3f0294c 100644
--- a/api/src/test/java/ai/djl/ndarray/internal/NDFormatTest.java
+++ b/api/src/test/java/ai/djl/ndarray/internal/NDFormatTest.java
@@ -136,4 +136,14 @@ public class NDFormatTest {
             Assert.assertEquals(str, "ND: (3) cpu() float64" + LF + "[  1.,   2., 100.]" + LF);
         }
     }
+
+    @Test
+    public void testLargeArray() {
+        try (NDManager manager = NDManager.newBaseManager()) {
+            NDArray large = manager.arange(0f, 100000000f).reshape(100, 100, 100, 100);
+            String str = large.toString();
+            Assert.assertEquals(
+                    str, "ND: (100, 100, 100, 100) cpu() float32\n[ Exceed max print size ]");
+        }
+    }
 }
diff --git a/engines/pytorch/pytorch-model-zoo/src/test/resources/mlrepo/model/cv/image_classification/ai/djl/pytorch/resnet18_embedding/metadata.json b/engines/pytorch/pytorch-model-zoo/src/test/resources/mlrepo/model/cv/image_classification/ai/djl/pytorch/resnet18_embedding/metadata.json
new file mode 100644
index 000000000..dbf438b50
--- /dev/null
+++ b/engines/pytorch/pytorch-model-zoo/src/test/resources/mlrepo/model/cv/image_classification/ai/djl/pytorch/resnet18_embedding/metadata.json
@@ -0,0 +1,42 @@
+{
+  "metadataVersion": "0.1",
+  "resourceType": "model",
+  "application": "cv/image_classification",
+  "groupId": "ai.djl.pytorch",
+  "artifactId": "resnet18_embedding",
+  "name": "resnet18_embedding",
+  "description": "A pretrained resnet18 model as an embedding base model",
+  "website": "http://www.djl.ai/engines/pytorch/model-zoo",
+  "licenses": {
+    "license": {
+      "name": "The Apache License, Version 2.0",
+      "url": "https://www.apache.org/licenses/LICENSE-2.0"
+    }
+  },
+  "artifacts": [
+    {
+      "version": "0.0.1",
+      "snapshot": false,
+      "name": "resnet18_embedding",
+      "arguments": {
+        "width": 224,
+        "height": 224,
+        "resize": 256,
+        "centerCrop": true,
+        "normalize": true,
+        "translatorFactory": "ai.djl.modality.cv.translator.ImageFeatureExtractorFactory"
+      },
+      "options": {
+        "mapLocation": "true"
+      },
+      "files": {
+        "model": {
+          "uri": "0.0.1/resnet18_embedding.zip",
+          "name": "",
+          "sha1Hash": "e37db339e87dc13ae0831e45818fca454f526ffb",
+          "size": 41574720
+        }
+      }
+    }
+  ]
+}