tensorflow / flutter-tflite

Apache License 2.0
464 stars 125 forks source link

Exception has occurred. _TypeError (type 'List<double>' is not a subtype of type 'List<List<double> #176

Open occasionalcode opened 9 months ago

occasionalcode commented 9 months ago

hi! im trying to create a Object detection application using this repo https://github.com/tensorflow/flutter-tflite. i tried to use my custom model and it gave me a output shape error

Invalid argument(s): Output object shape mismatch, interpreter returned output of shape: [1, 10] while shape of output provided as argument in run is: [1, 10, 4]

i tried to follow the suggestion from #134

At example live object detection in file detector_service.dart we have final output = { 0: [List<List<num>>.filled(10, List<num>.filled(4, 0))], 1: [List<num>.filled(10, 0)], 2: [List<num>.filled(10, 0)], 3: [0.0], }; It doesn't always fit. In my case, it worked with : final output = { 0: [List<num>.filled(10, 0)], 1: [List<List<num>>.filled(10, List<num>.filled(4, 0))], 2: [0.0], 3: [List<num>.filled(10, 0)], }; If we go to Kaggle in Outputs, we will find the right order.

final output = { 0: [List<num>.filled(10, 0)], 1: [List<List<num>>.filled(10, List<num>.filled(4, 0))], 2: [0.0], 3: [List<num>.filled(10, 0)], };

and right now im facing this error

Exception has occurred.
_TypeError (type 'List<double>' is not a subtype of type 'List<List<double>>' in type cast) on line 91

do you have any suggestions on how i can solve this problem?

here is the code https://paste.ofcode.org/4raUUvjbfPPcjZNSQ5Gk6J

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.16.5, on Microsoft Windows [Version 10.0.22621.2715], locale en-PH)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.8.3)
[√] Android Studio (version 2022.3)
[√] Connected device (4 available)
[√] Network resources

• No issues found!
qu-ngx commented 8 months ago

@occasionalcode I would say that the output’s tensors you have are in wrong order. To check for the correct tensors order you can definitely check the name of your model on Kaggle. If you still cannot find your model, please upload your tflite file on Netron. Those two ways will help you reveal the output layer with the correct tensor order. My guess is that 0’s type should be List<List< double >> . That is all the hints so far I can give you. If you have more questions, please let me know!

Fairfieldfred commented 4 months ago

If you add the line debugPrint(outputTensors.toString()); just after the line var outputTensors = getOutputTensors(); in the interpreter.dart file for the function runForMultipleInputs then you will see what is returned as outputs. In the case of the ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8 model the result is: flutter: [Tensor{_tensor: Pointer: address=0x10bcb5450, name: StatefulPartitionedCall:1, type: float32, shape: [1, 10], data: 40}, Tensor{_tensor: Pointer: address=0x10bcb5370, name: StatefulPartitionedCall:3, type: float32, shape: [1, 10, 4], data: 160}, Tensor{_tensor: Pointer: address=0x10bcb54c0, name: StatefulPartitionedCall:0, type: float32, shape: [1], data: 4}, Tensor{_tensor: Pointer: address=0x10bcb53e0, name: StatefulPartitionedCall:2, type: float32, shape: [1, 10], data: 40}]

You can see the StatefulPartitionedCalls are not returned in order but instead are returned in the order 1,3,0,2. If you drop that particular model into Netron , you see what those stateflulPartitionCalls actually are; 0: is for the number of detected boxes 1: is for the scores of the detected boxes 2: is for the categories of the detected boxes 3: is for the locations of the detected boxes

as such final output = { 0: [List<num>.filled(10, 0)], 1: [List<List<num>>.filled(10, List<num>.filled(4, 0))], 2: [0.0], 3: [List<num>.filled(10, 0)], }; as outlined in the above comment by occasionalcode is indeed the correct order for final output tensors... but you are not quite done yet... as the class _DetectorServer within the detector_service.dart file still needs modifications because of the altered order of outputs. Specifically final scores = output.elementAt(2).first as List<double>; needs to change to final scores = output.elementAt(0).first as List<double>; and final locationsRaw = output.first.first as List<List<double>>; need to change to final locationsRaw = output.elementAt(1).first as List<List<double>>; and final numberOfDetectionsRaw = output.last.first as double; needs to change to final numberOfDetectionsRaw = output.elementAt(2).first as double; and final classesRaw = output.elementAt(1).first as List<double>; needs to change to final classesRaw = output.elementAt(3).first as List<double>;

It wasn't until I made these changes that my custom tflite model would actually detect anything.

tustoz commented 1 month ago

If you add the line debugPrint(outputTensors.toString()); just after the line var outputTensors = getOutputTensors(); in the interpreter.dart file for the function runForMultipleInputs then you will see what is returned as outputs. In the case of the ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8 model the result is: flutter: [Tensor{_tensor: Pointer: address=0x10bcb5450, name: StatefulPartitionedCall:1, type: float32, shape: [1, 10], data: 40}, Tensor{_tensor: Pointer: address=0x10bcb5370, name: StatefulPartitionedCall:3, type: float32, shape: [1, 10, 4], data: 160}, Tensor{_tensor: Pointer: address=0x10bcb54c0, name: StatefulPartitionedCall:0, type: float32, shape: [1], data: 4}, Tensor{_tensor: Pointer: address=0x10bcb53e0, name: StatefulPartitionedCall:2, type: float32, shape: [1, 10], data: 40}]

You can see the StatefulPartitionedCalls are not returned in order but instead are returned in the order 1,3,0,2. If you drop that particular model into Netron , you see what those stateflulPartitionCalls actually are; 0: is for the number of detected boxes 1: is for the scores of the detected boxes 2: is for the categories of the detected boxes 3: is for the locations of the detected boxes

as such final output = { 0: [List<num>.filled(10, 0)], 1: [List<List<num>>.filled(10, List<num>.filled(4, 0))], 2: [0.0], 3: [List<num>.filled(10, 0)], }; as outlined in the above comment by occasionalcode is indeed the correct order for final output tensors... but you are not quite done yet... as the class _DetectorServer within the detector_service.dart file still needs modifications because of the altered order of outputs. Specifically final scores = output.elementAt(2).first as List<double>; needs to change to final scores = output.elementAt(0).first as List<double>; and final locationsRaw = output.first.first as List<List<double>>; need to change to final locationsRaw = output.elementAt(1).first as List<List<double>>; and final numberOfDetectionsRaw = output.last.first as double; needs to change to final numberOfDetectionsRaw = output.elementAt(2).first as double; and final classesRaw = output.elementAt(1).first as List<double>; needs to change to final classesRaw = output.elementAt(3).first as List<double>;

It wasn't until I made these changes that my custom tflite model would actually detect anything.

it works for me, thanks!