Repository: deepjavalibrary/djl
Pull request #1966 (2022-08-26 05:54:42)

[Fix] Closing error in MXNet when indexing results in an empty array
## Description ##

This is to fix the resource closing issue in MXNet engine:
MXNetError: Check failed: delay_alloc:
which happened in the following code
```
        try (NDManager manager = NDManager.newBaseManager()) {
            NDArray dumb1 = manager.create(new Shape(2, 2)).get(":4, 3:4");
            NDArray dumb2 = manager.create(new Shape(2)).get("3:3");
            NDArray dumb3 = manager.create(new Shape(2)).get("2:3");
        }
```
The fix is to check the indices within java.


Patch:
diff --git a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayIndexer.java b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayIndexer.java
index 5ac027484..76ab1b1aa 100644
--- a/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayIndexer.java
+++ b/engines/mxnet/mxnet-engine/src/main/java/ai/djl/mxnet/engine/MxNDArrayIndexer.java
@@ -46,6 +46,15 @@ public class MxNDArrayIndexer extends NDArrayIndexer {
     @Override
     public NDArray get(NDArray array, NDIndexFullSlice fullSlice) {
         array = manager.from(array);
+        long[] min = fullSlice.getMin();
+        long[] max = fullSlice.getMax();
+        long[] s = array.getShape().getShape();
+        for (int i = 0; i < min.length; i++) {
+            if (min[i] >= max[i] || min[i] >= s[i]) {
+                return manager.create(new Shape(0));
+            }
+        }
+
         MxOpParams params = new MxOpParams();
         params.addTupleParam("begin", fullSlice.getMin());
         params.addTupleParam("end", fullSlice.getMax());
diff --git a/extensions/tokenizers/src/main/python/huggingface_converter.py b/extensions/tokenizers/src/main/python/huggingface_converter.py
index 6fdd53f22..747bba7cf 100644
--- a/extensions/tokenizers/src/main/python/huggingface_converter.py
+++ b/extensions/tokenizers/src/main/python/huggingface_converter.py
@@ -35,7 +35,8 @@ class HuggingfaceConverter:
         self.inputs = None
         self.outputs = None
 
-    def save_model(self, model_id: str, output_dir: str, temp_dir: str):
+    def save_model(self, model_info, output_dir: str, temp_dir: str):
+        model_id = model_info.modelId
         if not os.path.exists(temp_dir):
             os.makedirs(temp_dir)
 
@@ -56,7 +57,7 @@ class HuggingfaceConverter:
         if not result:
             return False, reason, -1
 
-        size = self.save_to_model_zoo(model_id, output_dir, temp_dir,
+        size = self.save_to_model_zoo(model_info, output_dir, temp_dir,
                                       hf_pipeline)
 
         return True, None, size
@@ -99,8 +100,9 @@ class HuggingfaceConverter:
         script_module.save(model_file)
         return model_file
 
-    def save_to_model_zoo(self, model_id: str, output_dir: str, temp_dir: str,
+    def save_to_model_zoo(self, model_info, output_dir: str, temp_dir: str,
                           hf_pipeline):
+        model_id = model_info.modelId
         artifact_ids = model_id.split("/")
         model_name = artifact_ids[-1]
 
@@ -128,8 +130,9 @@ class HuggingfaceConverter:
         # Save metadata.json
         sha1 = sha1_sum(zip_file)
         file_size = os.path.getsize(zip_file)
-        metadata = HuggingfaceMetadata(artifact_ids, self.application,
-                                       self.translator, sha1, file_size)
+        metadata = HuggingfaceMetadata(model_info, artifact_ids,
+                                       self.application, self.translator, sha1,
+                                       file_size)
         metadata_file = os.path.join(repo_dir, "metadata.json")
         metadata.save_metadata(metadata_file)
 
diff --git a/extensions/tokenizers/src/main/python/huggingface_models.py b/extensions/tokenizers/src/main/python/huggingface_models.py
index f600944af..7dcf3db06 100644
--- a/extensions/tokenizers/src/main/python/huggingface_models.py
+++ b/extensions/tokenizers/src/main/python/huggingface_models.py
@@ -27,6 +27,19 @@ ARCHITECTURES_2_TASK = {
     "ForMultipleChoice": "text-classification",
     "ForMaskedLM": "fill-mask"
 }
+LANGUAGES = ModelSearchArguments().language
+
+
+def get_lang_tags(model_info):
+    tags = {}
+    for tag in model_info.tags:
+        if tag in LANGUAGES:
+            tags[tag] = "true"
+
+    if not tags:
+        tags["en"] = "true"
+
+    return tags
 
 
 class HuggingfaceModels:
@@ -43,7 +56,6 @@ class HuggingfaceModels:
         self.temp_dir = f"{self.output_dir}/tmp"
 
     def list_models(self, args: Namespace) -> List[dict]:
-        languages = ModelSearchArguments().language
         api = HfApi()
         if args.model_name:
             models = api.list_models(filter="pytorch",
@@ -70,15 +82,8 @@ class HuggingfaceModels:
                 logging.info(f"Skip flair model: {model_id}.")
                 continue
 
-            is_english = "en" in model_info.tags
-            if not is_english:
-                is_english = True
-                for tag in model_info.tags:
-                    if tag in languages and tag != 'en':
-                        is_english = False
-                        break
-
-            if not is_english:
+            languages = get_lang_tags(model_info)
+            if "en" not in languages:
                 logging.warning(f"Skip non-English model: {model_id}.")
                 continue
 
diff --git a/extensions/tokenizers/src/main/python/metadata.py b/extensions/tokenizers/src/main/python/metadata.py
index 36d40711d..90aa1830f 100644
--- a/extensions/tokenizers/src/main/python/metadata.py
+++ b/extensions/tokenizers/src/main/python/metadata.py
@@ -11,17 +11,14 @@
 # BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for
 # the specific language governing permissions and limitations under the License.
 import json
+from huggingface_models import get_lang_tags
 
 
 class HuggingfaceMetadata:
 
-    def __init__(self,
-                 artifact_ids: list[str],
-                 application: str,
-                 translator: str,
-                 sha1: str,
-                 file_size: int,
-                 tags: dict = None):
+    def __init__(self, model_info, artifact_ids: list[str], application: str,
+                 translator: str, sha1: str, file_size: int):
+        self.model_info = model_info
         self.model_name = artifact_ids[-1]
         artifact_ids[0:-1].insert(0, "ai.djl.huggingface")
         self.group_id = ".".join(artifact_ids)
@@ -29,9 +26,10 @@ class HuggingfaceMetadata:
         self.translator = translator
         self.sha1 = sha1
         self.file_size = file_size
-        self.tags = tags
 
     def save_metadata(self, metadata_file: str):
+        properties = get_lang_tags(self.model_info)
+
         metadata = {
             "metadataVersion": "0.2",
             "resourceType": "model",
@@ -52,7 +50,8 @@ class HuggingfaceMetadata:
                 "version": "0.0.1",
                 "snapshot": False,
                 "name": self.model_name,
-                "properties": self.tags,
+                "downloads": self.model_info.downloads,
+                "properties": properties,
                 "arguments": {
                     "translatorFactory": self.translator
                 },
diff --git a/extensions/tokenizers/src/main/python/model_zoo_importer.py b/extensions/tokenizers/src/main/python/model_zoo_importer.py
index bd581b30d..a8c7e6437 100644
--- a/extensions/tokenizers/src/main/python/model_zoo_importer.py
+++ b/extensions/tokenizers/src/main/python/model_zoo_importer.py
@@ -43,11 +43,10 @@ def main():
     for model in models:
         task = model["task"]
         model_info = model["model_info"]
-        model_id = model_info.modelId
         converter = SUPPORTED_TASK[task]
 
-        result, reason, size = converter.save_model(model_id, args.output_dir,
-                                                    temp_dir)
+        result, reason, size = converter.save_model(model_info,
+                                                    args.output_dir, temp_dir)
         if not result:
             logging.error(reason)
 


Test patch:
diff --git a/integration/src/main/java/ai/djl/integration/tests/ndarray/NDIndexTest.java b/integration/src/main/java/ai/djl/integration/tests/ndarray/NDIndexTest.java
index 4920187a3..5c59092f8 100644
--- a/integration/src/main/java/ai/djl/integration/tests/ndarray/NDIndexTest.java
+++ b/integration/src/main/java/ai/djl/integration/tests/ndarray/NDIndexTest.java
@@ -148,6 +148,17 @@ public class NDIndexTest {
         }
     }
 
+    @Test
+    public void testEmptyArrayClosing() {
+        // This is to check the resource closing issue in MXNet engine is circumvented.
+        // MXNetError: Check failed: delay_alloc:
+        try (NDManager manager = NDManager.newBaseManager()) {
+            manager.create(new Shape(2, 2)).get(":4, 3:4");
+            manager.create(new Shape(2)).get("3:3");
+            manager.create(new Shape(2)).get("2:3");
+        }
+    }
+
     @Test
     public void testSetArray() {
         try (NDManager manager = NDManager.newBaseManager()) {