How do I use `loader.asMatrix` with multiple threads?

Question linkage Using model predictions with multiple threads can cause results to be overwritten

I tried to use the model prediction under multi-threaded and found that some results were overwritten, then I tested and found that it seems that the results of loader.asMatrix are overwritten under multi-threaded, he is not thread safe right, how can I load the image under multi-threaded.

I was using NativeImageLoader and it was messing up with multiple threads, but then I tried using ImageLoader and I don’t have a problem. I don’t know if this is a solution.

@visionki could you clarify? Is the input data overridden? Models themselves are not thread safe. You need to use parallelinference.

@agibsonccc Yes, I used parallelinference, Here is my code snippet:

@Slf4j
@Component
public class NsfwUtil {
    ComputationGraph model;
    private final ParallelInference parallelInference;
    private final ImageLoader imgLoader;
    NativeImageLoader loader;

    public NsfwUtil() throws IOException, UnsupportedKerasConfigurationException, InvalidKerasConfigurationException {
        model = KerasModelImport.importKerasModelAndWeights(new ClassPathResource("model/nsfw.299x299.h5").getInputStream());
        parallelInference = new ParallelInference.Builder(model)
                .inferenceMode(InferenceMode.BATCHED)
                .batchLimit(32)
                .workers(5)
                .build();
        loader = new NativeImageLoader(299, 299, 3, new ColorConversionTransform(COLOR_BGR2RGB));
        imgLoader = new ImageLoader(299, 299, 3);
    }



    @Async("loadExecutor")
    public CompletableFuture<AnalysisResult> getResultAsync(String url){
        try (InputStream inputStream = Unirest.get(url)
                .asObjectAsync(RawResponse::getContent)
                .get()
                .getBody()){
            INDArray matrix = loader.asMatrix(inputStream, false);
            log.info("{}", matrix.hashCode());
            INDArray output = parallelInference.output(matrix.div(255));
            return CompletableFuture.completedFuture(new AnalysisResult(output.data().asDouble()));
        }catch (Exception e){
            throw new RuntimeException();
        }
    }
}

The above example uses NativeImageLoader to load images, and the output of matrix.hashCode() is occasionally duplicated (common when first started), and the log is as follows:

735038670
735038670
988874855
988874855
2103345588
-822422519
920533466
-1450966807
-1952245710
-2122104256
-82726789

When I replace it with ImageLoader to load the image, this does not happen.

@visionki I would recommend a threadlocal for the NativeImageLoader. That should help your issue.

@agibsonccc I want to know what is the difference between NativeImageLoader and ImageLoader, I can’t find the relevant documentation. looking at the comments it seems NativeImageLoader is to load images with opencv, ImageLoader is to load images with BufferedImage, do you mean you don’t recommend using ImageLoader?

@visionki NativeImageLoader uses opencv and is the recommended way of loading images. There’s not really much to document there.

@agibsonccc Ok, thanks for your reply, I’ll try what you said.

@agibsonccc Hello, still similar code as above, I found that after running for a while the memory footprint will keep increasing, memory footprint is:

When I periodically execute system.gc() manually, the memory footprint seems to become normal:

What do I need to release so that memory doesn’t keep growing?

@visionki that’s pretty common with threadlocals if not managed properly. Are you creating new threads every time?