Skip to content

Why is the TensorRT input binding set to FP16 for FP16 engines by default?Β #6777

Closed

Description

@DavidBaldsiefen

Search before asking

  • I have searched the YOLOv5 issues and found no similar bug report.

YOLOv5 Component

No response

Bug

Right now, when exporting a model to TensorRT using export.py with the --half argument, the input binding is set to datatype "half" while the output is set to datatype "float":

TensorRT: Network Description:
TensorRT:	input "images" with shape (1, 3, 640, 640) and dtype DataType.HALF
TensorRT:	output "output" with shape (1, 25200, 6) and dtype DataType.FLOAT

However, this means that whenever the resulting engine is used, its inputs also need to be converted to FP16, as can be seen in detect.py:

im = im.half() if half else im.float() # uint8 to fp16/32

Now I was wondering: is this intentional? TensorRT can handle an input in FP32 without problem even if the remaining engine is in half precision. However, having TensorRT handle the conversion means less overhead for the developer, while most likely also being faster as the conversion can be handled by the GPU instead of CPU. The datatype of the input binding when creating a new engine can be easily changed with a single line of code:

network.get_input(0).dtype = trt.DataType.FLOAT

Are you willing to submit a PR?

  • Yes I'd like to help by submitting a PR!

Activity

  1. glenn-jocher commented on Feb 25, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen thanks for raising this issue!

    The basic idea is that --half export will create a TRT model expecting --half inputs, i.e.

    python export.py --half
    python detect.py --half
    

    If you want FP32 inputs simply use default settings:

    python export.py
    python detect.py
    

    Does this resolve your question or do you still think there's an optimization/simplification we could do in the workflow?

  2. DavidBaldsiefen commented on Feb 25, 2022

    @DavidBaldsiefen
    ContributorAuthor

    I believe that it makes more sense to set the default input binding to float even for FP16 engines. As mentioned above, I do belive that a) it is faster to have TensorRT handle the input conversion and b) it is more intuitive for developers. For example, C++ does not have any FP16 datatype by default, which means that the engine exported by export.py cannot be used for C++ inference without modifications. In addition, it would no longer be necessary to append the --half option to val.py or detect.py. However, I am not sure how changing the input-tensor to float precision would affect ONNX models that are converted to FP16

  3. glenn-jocher commented on Feb 25, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen hmm interesting. I just tested a new workflow without issue, exporting at --half and running inference with FP32 inputs. The TRT model seems to convert on it's own (so flag already appears unnecessary).

    python export.py --half
    python detect.py
    
  4. glenn-jocher commented on Feb 25, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen BTW the ONNX exports made during TRT export are just a byproduct of the TRT exports, so I wouldn't worry about them, they likely won't be used by users. You can always export them directly with --include onnx also.

  5. DavidBaldsiefen commented on Feb 25, 2022

    @DavidBaldsiefen
    ContributorAuthor

    I just tested a new workflow without issue, exporting at --half and running inference with FP32 inputs. The TRT model seems to convert on it's own (so flag already appears unnecessary).

    How so? When I create an engine using the default export.py, I need to set the --half flag for detect.py and val.py, as otherwise nothing is detected. If I modify export.py so that the input binding is FP32, the opposite is true.

  6. glenn-jocher commented on Feb 25, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen ah yes, you are correct! Silent errors (no detections) are produced with improper FP16-FP32 pairing, but no error is shown unfortunately.

  7. glenn-jocher commented on Feb 25, 2022

    @glenn-jocher
    Member

    I get these results when testing all 4 combinations:

    Screenshot 2022-02-25 at 22 12 00

  8. DavidBaldsiefen commented on Feb 26, 2022

    @DavidBaldsiefen
    ContributorAuthor

    The results make sense. A start could be to check the engines input datatype in detect.py (or common.py) and print a warning if it does not match the presence (or absence) of --half. That would prevent issues like #6766

  9. glenn-jocher commented on Feb 26, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen do you know of a way to determine the quantization of a TRT model after loading? Perhaps we should have an assert but I don't know how to get the actual value, i.e.:

    bits = get_TRT_quantization(trt_model)
    assert bits == (16 if half else 32), 'model ' + ('requires' if bits == 16 else 'incompatible with') + ' --half'
  10. DavidBaldsiefen commented on Feb 27, 2022

    @DavidBaldsiefen
    ContributorAuthor

    @glenn-jocher Yes, I have created a pull request that would do the assertion in val.py and detect.py : #6798

  11. glenn-jocher commented on Feb 28, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen I was thinking after seeing the above issues perhaps we want to default TRT exports to FP16 and then add the line in your original post since you said this could accommodate both FP32 and FP16 inputs?

    network.get_input(0).dtype = trt.DataType.FLOAT
    

    Part of what we want to do is default to the best settings, and based on the above speed results --half TRT export seems like the best choice. I still need to do a mAP comparison, but usually (i.e. with PyTorch) FP32 and FP16 inference return almost identical accuracies.

    EDIT: where exactly would we slot the above line into export.py?

  12. glenn-jocher commented on Mar 4, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen any comment on what I asked earlier in #6777 (comment) ?

    Would it make sense to default to FP16 and add your line to be robust to FP32 inputs by default?

  13. DavidBaldsiefen commented on Mar 4, 2022

    @DavidBaldsiefen
    ContributorAuthor

    @glenn-jocher I agree that it makes sense to default TensorRT exports to FP16; I suppose that in most cases, TensorRT deployment is performed for the sole purpose of increasing performance through that method. I actually tested YOLOv5s and YOLOv5m TensorRT engines recently on the validation dataset with these results (please ignore YOLOv4-CSP):

    grafik

    This would indicate a accuracy loss of just around 1% at a performance gain of around 100% (for batch size one, throughput more than doubled while latency halved. Tested on a NVIDIA Jetson AGX).

    Do you mean that you would default the engine to FP16 and make the input precision depend on the --half flag?

  14. 2 remaining items

  15. DavidBaldsiefen commented on Mar 4, 2022

    @DavidBaldsiefen
    ContributorAuthor

    @glenn-jocher I have updated my pull request to include these changes

  16. glenn-jocher commented on Mar 4, 2022

    @glenn-jocher
    Member

    @DavidBaldsiefen awesome! Ran some benchmarks of my own on Colab. See no reason not to default to FP16 as long as we can run inference without the --half flag (so users don't get confused on crash without it).

    YOLOv5 πŸš€ v6.1-13-g601dbb8 torch 1.10.0+cu111 CUDA:0 (Tesla V100-SXM2-16GB, 16160MiB)

    !pip install -U nvidia-tensorrt --index-url https://pypi.ngc.nvidia.com  # install
    
    !python export.py --weights yolov5s.pt --include engine --imgsz 640 640 --device 0  # export
    %mv yolov5s.engine yolov5s_fp32.engine
    !python export.py --weights yolov5s.pt --include engine --imgsz 640 640 --device 0 --half  # export
    %mv yolov5s.engine yolov5s_fp16.engine
    
    !python val.py --weights yolov5s.pt --data coco.yaml --img 640 --iou 0.65 --device 0 --batch 1
    !python val.py --weights yolov5s.pt --data coco.yaml --img 640 --iou 0.65 --device 0 --batch 1 --half
    
    !python val.py --weights yolov5s_fp32.engine --data coco.yaml --img 640 --iou 0.65 --device 0 --batch 1
    !python val.py --weights yolov5s_fp16.engine --data coco.yaml --img 640 --iou 0.65 --device 0 --batch 1 --half
    YOLOv5s v6.1 Inference time (ms) COCO mAP
    PyTorch FP32 12.5 37.5
    PyTorch FP16 10.5 37.5
    TensorRT FP32 3.4 37.4
    TensorRT FP16 1.9 37.4
  17. linked a pull request that will close this issueDefault FP16 TensorRT export #6798on Mar 4, 2022
  18. added a commit that references this issue on Mar 6, 2022
  19. henry-zwart commented on May 4, 2022

    @henry-zwart

    I've run into the above-mentioned issue when working with the model programmatically (exported at half precision, inputs at full precision, no detections found). I'm currently passing my inputs as a list of PIL.Image objects, and it doesn't seem that there's a simple way to cast these inputs to the right dtype. Do you have any recommendations?

  20. glenn-jocher commented on May 4, 2022

    @glenn-jocher
    Member

    @henry-zwart πŸ‘‹ hi, thanks for letting us know about this possible problem with YOLOv5 πŸš€. TensorRT export and PIL inference with PyTorch Hub works correctly in our tests:

    Screen Shot 2022-05-04 at 12 30 10 PM

    We've created a few short guidelines below to help users provide what we need in order to start investigating a possible problem.

    How to create a Minimal, Reproducible Example

    When asking a question, people will be better able to provide help if you provide code that they can easily understand and use to reproduce the problem. This is referred to by community members as creating a minimum reproducible example. Your code that reproduces the problem should be:

    • βœ… Minimal – Use as little code as possible to produce the problem
    • βœ… Complete – Provide all parts someone else needs to reproduce the problem
    • βœ… Reproducible – Test the code you're about to provide to make sure it reproduces the problem

    For Ultralytics to provide assistance your code should also be:

    • βœ… Current – Verify that your code is up-to-date with GitHub master, and if necessary git pull or git clone a new copy to ensure your problem has not already been solved in master.
    • βœ… Unmodified – Your problem must be reproducible using official YOLOv5 code without changes. Ultralytics does not provide support for custom code ⚠️.

    If you believe your problem meets all the above criteria, please close this issue and raise a new one using the πŸ› Bug Report template with a minimum reproducible example to help us better understand and diagnose your problem.

    Thank you! πŸ˜ƒ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions