Closed madprogrammer closed 2 years ago
@madprogrammer
Do you mind share your project?
@frankfliu I've isolated the case to a small project forked from the djl-demo/quarkus/example
https://github.com/madprogrammer/djl-issue-prj
It can be run by executing mvnw quarkus:dev
, and then going to http://127.0.0.1:8080/detect
I found that the created tensor is deallocated as soon as the PointerScope block is exited
I'm not sure but somehow this doesn't seem right to me.
After some more digging and comparison to TensorFlow Java API, I found that the working code from TensorFlow Java creates a DT_STRING tensor with shape [1], and numBytes
= Loader.sizeof(TF_TString.class)
, and apparently there is no way to do it with the current API present in TfNDManager
and JavacppUtils
@madprogrammer Currently TfNDManager.create(String[]) was not implemented in DJL, you cannot create String tensor other than scalar. But you should be easily get shape (1) tensor by nd.expandDims(0):
String data = new String(input.array(), StandardCharsets.UTF_8);
NDArray imageBytes = ctx.getNDManager().create(data).expandDims(0);
imageBytes.setName("image_bytes");
@madprogrammer I created PR that allows you to create String tensor with specified shape: #1251
@frankfliu Thank you, but the problem is also that calling String data = new String(input.array(), StandardCharsets.UTF_8)
will corrupt the binary data after decoding back to byte array in JavacppUtils
, or even probably throw an exception in case an invalid byte sequence for UTF-8 encoding is encountered. TensorFlow defines DT_STRING as variable-length byte arrays. There probably should be an overload that allows to pass a byte[] array directly down to createStringTensor
in JavacppUtils
, or something like that, to avoid calling str.getBytes
inside JavacppUtils
to make it work.
I just tested that encoding and decoding with StandardCharsets.US_ASCII
provides same byte array that was before encoding to String, but inside of JavacppUtils
UTF-8 is used anyway, so it's not going to work, and looks more like a dirty hack than a good code.
@frankfliu Implementation proposal
@SuppressWarnings({"unchecked", "try"})
public static Pair<TF_Tensor, TFE_TensorHandle> createStringTensor(long[] dims, String[] src) {
ByteBuffer[] byteBuffers = new ByteBuffer[src.length];
for (int i = 0; i < src.length; i++)
byteBuffers[i] = ByteBuffer.wrap(src[i].getBytes(StandardCharsets.UTF_8));
return createStringTensor(dims, byteBuffers);
}
@SuppressWarnings({"unchecked", "try"})
public static Pair<TF_Tensor, TFE_TensorHandle> createStringTensor(long[] dims, ByteBuffer[] src) {
int dType = TfDataType.toTf(DataType.STRING);
long numBytes = (long) Loader.sizeof(TF_TString.class) * src.length;
try (PointerScope ignored = new PointerScope()) {
/*
* String tensor allocates a separate TF_TString memory. The TF_TString will
* be deleted when the string tensor is closed. We have to track TF_TString
* memory by ourselves and make sure thw TF_TString lifecycle align with
* TFE_TensorHandle. TF_Tensor already handles TF_TString automatically, We
* can just keep a TF_Tensor reference in TfNDArray.
*/
TF_Tensor tensor = AbstractTF_Tensor.allocateTensor(dType, dims, numBytes);
Pointer pointer = tensorflow.TF_TensorData(tensor).capacity(numBytes);
TF_TString data = new TF_TString(pointer).capacity(pointer.position() + src.length);
for (int i = 0; i < src.length; ++i) {
TF_TString tstring = data.getPointer(i);
tensorflow.TF_TString_Copy(tstring, new BytePointer(src[i]), src[i].remaining());
}
TF_Status status = TF_Status.newStatus();
TFE_TensorHandle handle = AbstractTFE_TensorHandle.newTensor(tensor, status);
status.throwExceptionIfNotOK();
handle.retainReference();
tensor.retainReference();
return new Pair<>(tensor, handle);
}
}
@madprogrammer Thanks for your inputs.
Now you can create String tensor using either charset or use ByteBuffer:
TfNDManager manager = ((TfNDManager)ctx.getNDManager());
NDArray imageBytes = manager.createStringTensor(new Shape(1), ByteBuffer.wrap(input));
imageBytes.setName("image_bytes");
The ByteBuffer only available in TfNDManager.
This is fixed by https://github.com/deepjavalibrary/djl/pull/1251. You can try 0.13.0-SNAPSHOT version now.
Description
I'm trying to feed a Google AutoML Object detection saved_model.pb, which accepts a JPEG/PNG-encoded file as DT_STRING(-1) tensor on the input. It worked well in Java TensorFlow, but I'm struggling to make it work in DJL. Can't find a way to create binary tensor with type String. After few hours of reading DJL code, tried the following, without success:
The same
saved_model.pb
works fine from Java TensorFlow API, but with DJL TensorFlow complains, and the tensor handle inimageBytes
seems to havetensor
withaddress = 0
which seems not right to me (see screenshot).The shape of the AutoML model:
Working code from Java TensorFlow API to create the input tensors:
Expected Behavior
There is a way to create a binary string tensor, as it's possible with TensorFlow Java, Python and other APIs
Error Message
What have you tried to solve it?
Spent hours of reading the code of TfNDManager, JavacppUtils etc. to try to understand how to do it.
Environment Info
DJL version: 0.12.0