Tensorflow: 在Android中使用opengl和SSBO进行Tensorflow Lite gpu委托推断

创建于 2019-03-03  ·  102评论  ·  资料来源: tensorflow/tensorflow

系统信息

  • 我是否编写了自定义代码(与使用TensorFlow中提供的股票示例脚本相对):
    是的,使用来自https://www.tensorflow.org/lite/performance/gpu_advanced#android_2的其他代码,从tflite gpu委托android示例中修改了推断代码
  • 操作系统平台和发行版:Android 8.0.0
  • 移动设备:OnePlus 3
  • TensorFlow版本:12.0

描述当前行为
tensorflow lite gpu委托文档提供了一个示例代码,可在android上高效运行tflite推理,并在egl上下文中借助opengl和SSBO避免CPU_GPU内存复制。 但是,这种方法似乎并没有带来任何性能提升。 该文档提到了一种方法-在这种情况下用于运行推理的方法-'interpreter.runInference(null,outputArray)'。此方法与基本运行方法相同,即解释器.run(inputTensor ,outputTensor)。 (当前api中似乎没有方法叫做'interpreter.runInference'。)实验性gpu委托api当前是否支持建议的方法(即直接从opengl ssbo访问输入图像以运行推理)?如何确保模型从GPU内存中的该SSBO接收输入?

*预期行为*
使用opengl ssbo的tflite推理应该比基本的gpu委托推理要快,后者每次将数据从cpu复制到gpu。

其他信息/日志
我们在android studio中测量了'tflite.run'方法的时间。输入采用推荐的ByteBuffer格式。

错误:无法解析方法runInference(null,?)

lite bug

最有用的评论

尚未正式宣布,但是供参考:GPU代码现在可以在以下位置看到:

https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/gpu

如果您需要代码以更好地了解情况,那么会发生什么。

所有102条评论

@ anilsathyan7

感谢您试用GPU委托。

您是否可以提供更多时间方面的信息,即前后多少毫秒?

您正在使用哪种网络? 具体来说,是否支持所有操作?

您是否编写了自定义着色器代码以将相机纹理复制到SSBO中,还是只是自己将CPU内存转储到SSBO中? 如果是前者,则说明您做的不错,而且速度会更快。 如果是后者,它只会变慢。

模型:类似于官方的TF-Lite细分模型(模型推断图作为图像附加).gpu委托似乎不支持最后三个附加节点,输入图像大小为129 * 129。

电话:OnePlus 3,GPU:Adreno 530

时间:
CPU推断:60-70毫秒
GPU推断:40-50毫秒
GPU推断(SSBO):80-90毫秒

即执行“ interpreter.run()”方法的时间。

这是我们用来将相机纹理复制到SSBO的方法:-

//Initialise SSBO
public int[] initializeShaderBuffer(){
    android.opengl.EGLContext eglContext = eglGetCurrentContext();
    int[] id = new int[1];
    GLES31.glGenBuffers(id.length, id, 0);
    GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, id[0]);
    GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, mWidth * mHeight, null, GLES31.GL_STREAM_COPY);
    GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);// unbind
    return id;
}
int inputSsboId = initializeShaderBuffer()[0];

 //After that every time a frame is available OR in onDraFrame(), call 
fillSsboWithCameraImageTexture(inputSsboId,data);

//(Note: Data is Nothing but Camera Frame ByteBuffer)

// Fill Ssbo With CameraImageTexture 

private int fillSsboWithCameraImageTexture(int inputSsboId,ByteBuffer cameraFramme) {

    GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, mWidth * mHeight, cameraFramme, GLES31.GL_STREAM_COPY);
    return inputSsboId;

}

129_80k_dm05

相同的“ Interpreter.run()”方法能否处理来自CPU和SSBO的正常输入? 还是在这种情况下还有其他选项/功能可用于运行推理?

@ anilsathyan7

对于延迟的回复表示歉意。 出于某种原因,我刚刚在收件箱中输入了此内容> _ <

快速提问:您的代码:

不必一定是

GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, 3 * mWidth * mHeight, null, GLES31.GL_STREAM_COPY);

另外,您是否有资格制作形状为1x129x129x4的输入SSBO? 然后,您可以消除其中一个隐藏的内存。

从您共享的图中(顺便说一句,很好的可视化;对此表示赞赏),确实看起来一切都会处理到最后一个ResizeBilinear。 就其形状而言,它的形状也不算太差(129x129x2),它具有太多的通道等。因此,我希望不会出现任何速度下降的情况。

您在ModifyGraphWithDelegate之前正确呼叫BindGlBufferToTensor ModifyGraphWithDelegate吗? 可以共享将纹理转换为SSBO的着色器代码吗? 我正在做类似的事情:

   #version 310 es
   layout(local_size_x = 16, local_size_y = 16) in;
   layout(binding = 0) uniform sampler2D input_texture;
   layout(std430) buffer;
   layout(binding = 1) buffer Output { float elements[]; } output_data;
   void main() {
     ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
     if (gid.x >= 224 || gid.y >= 224) return;
     vec3 pixel = texelFetch(input_texture, gid, 0).xyz;
     int linear_index = 3 * (gid.y * 224 + gid.x);
     output_data.elements[linear_index + 0] = pixel.x;
     output_data.elements[linear_index + 1] = pixel.y;
     output_data.elements[linear_index + 2] = pixel.z;
   }

用于MobileNet。 可能不直接适用,但您大致了解这个主意...

尚未正式宣布,但是供参考:GPU代码现在可以在以下位置看到:

https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/delegates/gpu

如果您需要代码以更好地了解情况,那么会发生什么。

@impjdi
您可以只使用ssbo共享样本分类应用程序,还是至少使用与opengl相关的代码?
我们根据您的输入使用了以下着色器代码,但是遇到一些与着色器版本有关的错误,我们无法解决opengl初学者的问题。

#version 310 es
layout(local_size_x = 16, local_size_y = 16) in;
layout(binding = 0) uniform sampler2D u_Texture0;
layout(std430) buffer;
layout(binding = 1) buffer Output { float elements[]; } output_data;
void main() {
    ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
    if (gid.x >= 257 || gid.y >= 257) return;
    vec3 pixel = texelFetch(u_Texture0, gid, 0).xyz;
    int linear_index = 3 * (gid.y * 257 + gid.x);
    output_data.elements[linear_index + 0] = pixel.x;
    output_data.elements[linear_index + 1] = pixel.y;
    output_data.elements[linear_index + 2] = pixel.z;
}
mTextureUniformHandle0 = GLES31.glGetUniformLocation(mProgramHandle,
                "u_Texture0");
// Set the active texture0 unit to texture unit 0.
        GLES31.glActiveTexture(GLES31.GL_TEXTURE0);

        // Bind the texture to this unit.
        GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, mTextureDataHandle0);

        // Tell the texture uniform sampler to use this texture in the shader by
        // binding to texture unit 0.
        GLES31.glUniform1i(mTextureUniformHandle0, 0);
    public int[] initializeShaderBuffer(){
        android.opengl.EGLContext eglContext = eglGetCurrentContext();
        int[] id = new int[1];
        GLES31.glGenBuffers(id.length, id, 0);
        GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, id[0]);
        GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, 257*257*3*4, null, GLES31.GL_STREAM_COPY);
        GLES31.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER,1,id[0]);
        GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);// unbind
        return id;
    }

@ anilsathyan7

我本周休假,办公室网络访问受限,很可能会忘记这一点。 你能下个星期再推我一下吗?

当然了porygon ...😉

@impjdi
您可以为我们解决ssbo tflite的问题吗? 我们无法在android中使用ssbo来运行tflite推理。您能否仅使用ssbo或至少与opengl相关的代码共享示例分类应用程序?在这种情况下,我们可以期望提高多少速度?

@impjdi
我将第二次请求演示SSBO推理的演示。

也许我应该打开一个单独的问题...我们正在尝试在我们的应用程序中与tflite GPUDelegate一起使用GLSurfaceView。 我们的渲染器可以正常工作,直到调用interpreter.modifyGraphWithDelegate(delegate); ,从而导致黑屏。 没有产生glErrors。 即使查看了新发布的GPU委托源,也很难理解对以上行进行注释/取消注释会如何改变行为。

一个可行的示例可能会清除问题...

谢谢!

@ anilsathyan7

呵呵,我早先错过了porygon部分:)

下面是C ++,但在Java中也应该类似。

    glActiveTexture(GL_TEXTURE0 + 0);
    glBindTexture(GL_TEXTURE_2D, /*your gl texture that has the image*/);
    glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, /*your ssbo*/, 0, /*size in bytes*/);
    glUseProgram(/*the program above*/);
    glDispatchCompute(width / 16, height / 16, 1);  // these are work group sizes
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);  // unbind
    glBindTexture(GL_TEXTURE_2D, 0);  // unbind

@ktgordon

嗯,唯一的官方示例代码是TF存储库中的TFLite演示应用程序。 由于Android应用程序不仅包含单个Java文件,还包含许多其他内容,除非我用这些文件启动一个全新的git repo,否则这将很困难。 不幸的是,最重要的是,我不是真正的移动应用程序开发人员。 我在没有相机的情况下在Android C ++中完成了大部分工作。 我将看看是否可以编写一个C ++二进制文件,使它可以在单个C ++文件中完成所有这些工作= /除了讨论之外...

modifyGraphWithDelegate挂起听起来像是您在其他地方遇到了问题。 请确保您的TfLiteGpuDelegateBindBufferToTensor之前被调用modifyGraphWithDelegate ,并且您的SSBO已创建。 带有modifyGraphWithDelegate的程序的流程如下:

Interpreter.modifyGraphWithDelegate (Java)
Interpreter::ModifyGraphWithDelegate (C ++)
tflite::gpu::gl::(anonymous)::DelegatePrepare (C ++)
tflite::gpu::gl::(anonymous)::Delegate::Prepare (C ++)

您可能可以追溯导致挂起的原因。

@ anilsathyan7

事情解决了吗? 这个问题可以解决吗?

代码工作正常; 但是我们无法使用ssbo作为输入来获得正确的输出。输出似乎是黑色的(即输出全为零)。我们无法确保数据已正确复制到ssbo中或tensorflow是否正确访问了数据; 即使它没有错误运行,似乎也无法在android中调试和查看着色器代码(GLSL)。

随附的是包含当我们尝试将SSBO与tflite模型一起使用时的错误的日志文件。
该代码可在带有Adreno-GPU的Mobiles上正常运行,没有任何错误,但看不到任何输出。 但是,在配备Mali-GPU的手机中,甚至在模型出现之前就存在一些问题。

这些错误在Mali设备之间有所不同,而在Adreno Devices中无法看到输出。
以下测试中使用的设备是:

马里(错误记录附有问题:mali-gpu-ssbo-errorlog.txt)
_三星A8 + _
_荣誉发挥_
_Moto C plus_

Adreno (附带错误日志:adreno-gpu-ssbo-errorlog.txt)
_Poco F1_

mali-gpu-ssbo-errorlog.txt

adreno-gpu-ssbo-errorlog.txt

@impjdi您可以看一下。如果您可以与我们分享有效的应用程序代码以供参考,那会更好。

@impjdi SSBO上的任何更新?

@impjdi
我将第二次请求演示SSBO推理的演示。

也许我应该打开一个单独的问题...我们正在尝试在我们的应用程序中与tflite GPUDelegate一起使用GLSurfaceView。 我们的渲染器可以正常工作,直到调用interpreter.modifyGraphWithDelegate(delegate); ,从而导致黑屏。 没有产生glErrors。 即使查看了新发布的GPU委托源,也很难理解对以上行进行注释/取消注释会如何改变行为。

一个可行的示例可能会清除问题...

谢谢!

@ktgordon您是否找到解决此问题的方法/解决方法? 我遇到了完全相同的问题。 调用ModifyGraphWithDelegate()后,所有glDraw调用均以黑色显示。 甚至不需要将SSBO缓冲区关联到TFLite张量。 这很奇怪。 还要更深入地看。

我们确实找到了解决方法。 我假设您正在使用Java API,并通过引入gpu委托
implementation 'org.tensorflow:tensorflow-lite:0.0.1-gpu-experimental'

我认为正在发生的事情是ModifyGraphWithDelegate()修改了当前上下文,因此我们的显示表面不再是当前的……如果我们可以访问原始状态变量,这不是问题。 但是,由于我们最初尝试使用GLSurfaceView,所以我们无法访问任何这些变量。 实际上,ModifyGraphWithDelegate更改了我们无法恢复的gl状态。

从GLSurfaceView切换到TextureView给了我们更多的控制权,但代价是更加复杂。 我们创建了一个虚拟上下文,初始化了我们的解释器,并调用了ModifyGraphWithDelegate(),然后使用虚拟上下文创建了一个新的共享上下文。 这样,我们可以使显示表面成为当前电流并对其进行渲染。

通过重用Grafika的代码来管理egl上下文。

无论如何,这使我们通过了黑屏问题。

基于TFLite演示(使用TextureView),我所做的正是您在这里所说的。 主要有以下几种:

  1. 创建gl上下文,设置gl视口等。存储eglDisplay,eglSurface,eglContext。
  2. 拨打modifyGraphWithDelegate()
  3. 使用eglMakeCurrent将eglContext,eglSurface,eglDisplay设置为当前值

使用glDrawArrays绘制的结果为黑色。 有趣的是,如果按顺序交换了步骤1和步骤2,则一切正常。

还引用了Grafika代码。

接下来将尝试设置虚拟上下文...

@ktgordon@gnsmrky
您是否建议ssbo方法不适用于普通的GLSurfaceView? 像GLTextureView( link1link2 )这样的东西呢?

最后,与普通的GPU推理相比,您能否实现任何提速? 如果是这样,您可以共享一个基本的演示应用程序吗? 只是为了清理事情...

@ktgordon刚开始工作! 实际上,虚拟共享上下文是使其起作用的关键。 我猜GLES上下文设置/切换可能比人们想象的要复杂得多。

@ anilsathyan7我基于TFLite演示,这是TFLite GPU委托页面提供的主要示例项目。 此示例项目使用TextureView 。 不知道SSBO是否适用于其他曲面类型。 我猜想应该是eglCreateWindowSurface()接受SurfaceViewSurfaceTextureSurfaceHolderSurface ,根据Android eglSurface doc 。 您链接中的GLTextureView扩展了SurfaceTexture,也应该可以正常工作。

性能提升非常明显。 我正在尝试448x448的图片。 (尝试放大图像以扩大复印时间)。 在Snapdragon 808上,不使用SSBO / Image2D复制着色器所花费的时间约为900毫秒。使用复制着色器,时间缩短为<20毫秒!

@gnsmrky您可以共享您的回购信息,这样对于每个人来说,开始使用ssbo可能都是一件更好的事情。

@gnsmrky您可以共享您的回购信息,这样对于每个人来说,开始使用ssbo可能都是一件更好的事情。

@SanthoshRajendiran试图找到时间这样做。 该代码现在非常混乱并且不可读。 我将在获得空闲周期后立即将其清理干净。

@gnsmrky @impjdi关于

@gnsmrky非常感谢您的努力。 当您在此处添加示例代码时,请告诉我们。

@SanthoshRajendiran @ soham24计划在周末发布该回购协议。 仍在做一些调整。 :)

San

我在最近的几次会议上都提出了这个要求。 该示例将添加到TFLite演示应用程序中,但是我有一些截止日期,所以要等到它才花几个月的时间:(

@santhoshRajendiran @ soham24 @impjdi
我只是在tensorflow-lite-ssbo Android分类器demo上放置了GPUmobilenet v1 float可以看到copy time ,将帧复制到SSBO所需的时间。

代码仍然很粗糙。 但是应该以在TFLite GPU代表中开始在SSBO周围开始为目的。

在我的LG G4(Android M,Snapdragon 808)上,复制224x224像素缓冲区所需的时间大大减少。 从180ms〜200ms(Java ByteBuffer putFloat()复制),降至1ms(shader + SSBO)。 由于LG G4是一部相对较旧的手机(现已超过5年),因此在新手机上节省的时间可能不那么重要。 但是,实际上,如果G4可以在<1毫秒内完成帧复制,那么无疑任何其他Android手机都可以做得更好。 :)

基本上它是做什么的:

  1. 初始化GLES上下文A( eglContext )。
  2. 为相机创建表面纹理。
  3. 创建SSBO
  4. 创建将表面纹理复制到SSBO所需的计算着色器。
  5. 使用共享的上下文A初始化GLES上下文B( gpuContext )。
  6. 调用ModifyGraphWithDelegate()
  7. 使用eglMakeCurrent()进行适当的上下文切换

    • 当相机->表面纹理-> SSBO时,切换到上下文A。

    • 调用TFLite Interpreter.run()时切换到上下文B。

注意:我没有创建单独的线程来简化过程。 通常,上下文A和B应该在2个单独的线程中,因此eglMakeCurrent()在一个线程中仅被调用一次。

没有时间编写自述文件。 只是看看提交。 应该很简单找出其中的内容。 希望这有助于阐明有关TFLite + GPU委托+ SSBO的一些信息。

让我知道是否对你们有用...

@gnsmrky恭喜,并感谢您在SSBO上所做的出色工作。 我们在某些手机中试用了该应用程序。 下面讨论了各种电话上的工作方法:

1)Oneplus 3-模型运行时间约为40毫秒,与没有SSBO时相同。 在所有情况下,复制时间约为0或1
2)Poco F1-模型运行时间约为25ms,但是我们无法从应用程序中获取实际输出。
3)Samsung A8 +,Honor Play-应用程序因链接错误而崩溃,提示工作组调用的最大次数。 我们将工作组的大小修改为8,获得了5ms的模型运行时间,但无法从模型中获得正确的输出。

@gnsmrky非常感谢。 实施后,我会通知您。

@gnsmrky太好了。 非常感谢! 您是否尝试过deeplab细分模型?

@gnsmrky恭喜,并感谢您在SSBO上所做的出色工作。 我们在某些手机中试用了该应用程序。 下面讨论了各种电话上的工作方法:

  1. Oneplus 3-模型运行时间约为40毫秒,与没有SSBO时相同。 在所有情况下,复制时间约为0或1

因此,在这些手机中,OnePlus 3只能正常工作并具有正确的输出吗? 让我看看我是否可以握住Snapdragon 845手机。

@gnsmrky太好了。 非常感谢! 您是否尝试过deeplab细分模型?

@junhwanjang我还没有尝试过deeplab。 但是我确实尝试将输出SSBO与其他模型一起使用,该模型也可以正常工作。 您知道Deeplab是否可以与GPU Delegate完全兼容吗?

@SanthoshRajendiran我刚刚更新了存储库。 似乎计算着色器在某些设备上需要真实的显示表面。 我向资产添加了1dp x 1dp视图,以将其与gles曲面关联。 您可以在手机上再次尝试更新的回购吗?

这是最新的提交

顺便说一句,凸轮-> SSBO副本不考虑从updateTexImage()转换。 您可能需要将手机逆时针放置(即手机底部指向右侧),以得出正确的推断结果。

@gnsmrky感谢您的更新。 使用POCO F1(Adreno 630,Snapdragon 845)时,输出速度现在约为20-30ms,复制时间约为0-1ms。
问题仍然存在于Mali GPU设备(在Honor Play上测试)
下面随附的是Honor Play的错误日志:

mali-ssbo-android-errorlog.txt

@SanthoshRajendiran您是否尝试过将Mali设备的工作组设置为8,甚至4? 这是您应将16更改84
计算着色器中的local_size @ L1092
glDispatchCompute @ L1162

@gnsmrky我们尝试在Samsung A8 +

E / Android运行时:致命异常:CameraBackground
流程:android.example.com.tflitecamerademo,PID:23378
java.lang.IllegalArgumentException:内部错误:无法在给定的解释器上运行:GPU委托不支持下一步操作:
挤压:不支持操作。
前29个操作将在GPU上运行,其余2个操作将在CPU上运行。TfLiteGpuDelegate调用:[GL_OUT_OF_MEMORY]:没有足够的内存来执行命令。节点号31(TfLiteGpuDelegate)调用失败。

    at org.tensorflow.lite.NativeInterpreterWrapper.run(Native Method)
    at org.tensorflow.lite.NativeInterpreterWrapper.run(NativeInterpreterWrapper.java:149)
    at org.tensorflow.lite.Interpreter.runForMultipleInputsOutputs(Interpreter.java:275)
    at org.tensorflow.lite.Interpreter.run(Interpreter.java:249)
    at com.example.android.tflitecamerademo.ImageClassifierFloatMobileNet.runInference(ImageClassifierFloatMobileNet.java:101)
    at com.example.android.tflitecamerademo.ImageClassifier.classifyFrameSSBO(ImageClassifier.java:167)
    at com.example.android.tflitecamerademo.Camera2BasicFragment.classifyFrameSSBO(Camera2BasicFragment.java:967)
    at com.example.android.tflitecamerademo.Camera2BasicFragment.access$1200(Camera2BasicFragment.java:91)
    at com.example.android.tflitecamerademo.Camera2BasicFragment$8.run(Camera2BasicFragment.java:785)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:214)
    at android.os.HandlerThread.run(HandlerThread.java:65)

I / Process:正在发送信号。 PID:23378 SIG:9

@gnsmrky默认情况下,在诸如deeplab之类的模型(无法完全在GPU中运行的模型)中,从GPU到CPU的GPU委托会发生回退。 SSBO中的这种行为是否会改变?如果数据回落到CPU,我们如何获取数据?

@gnsmrky谢谢。 在低端设备上,这就像魅力一样。
一个问题,确实是我们必须逆时针旋转手机。 我可以在着色器中添加旋转逻辑吗?
引用链接: https :

@gnsmrky如果我想从tflite模型获取关于SSBO的图像输出,您能否深入了解应用程序代码中必须进行哪些更改。

@gnsmrky我们尝试在Samsung A8 +

@SanthoshRajendiran我刚刚做了一些调整就更新了回购协议。 应该大大降低内存需求。

  1. 使用FP16精度。
  2. 使用8作为工作组大小。
  3. 创建后添加对SSBO缓冲区大小的检查。

您看到的SQUEEZE错误可能是由于创建SSBO缓冲区时失败。 您是否按原样运行仓库?

让我知道更新后的仓库是否适合您。

@gnsmrky默认情况下,在诸如deeplab之类的模型(无法完全在GPU中运行的模型)中,从GPU到CPU的GPU委托会发生回退。 SSBO中的这种行为是否会改变?如果数据回落到CPU,我们如何获取数据?

@SanthoshRajendiran存储库中的SSBO仅用于输入缓冲区。 输出缓冲区没有任何变化。 因此,获取输出数据的代码应与使用CPU的方式相同(即ByteBuffer)。

我尚未接触过deeplab。 您知道哪个操作导致CPU回退吗?

@gnsmrky谢谢。 在低端设备上,这就像魅力一样。
一个问题,确实是我们必须逆时针旋转手机。 我可以在着色器中添加旋转逻辑吗?
引用链接: https :

@ soham24当您将常规的glViewPort,glDraw等与相应的顶点片段着色器一起使用时,将发生转换。 存储库中的SSBO代码是一个简单的内存浮点副本,不涉及任何顶点/片段着色器。 如果我们按浮动进行转换,则很可能会减慢速度。

最好的方法是将相机纹理“绘制”到具有所需变换的另一个纹理,然后执行纹理-> SSBO复制。 这将需要一些努力。 需要花更多的时间来做。

@gnsmrky如果我想从tflite模型获取关于SSBO的图像输出,您能否深入了解应用程序代码中必须进行哪些更改。

@SanthoshRajendiran您想对图像输出做什么? 创建一个SSBO并将其绑定到TFLite GPU委托就像创建一个SSBO并调用bindGlBufferToTensor()到输出张量getOutputTensor()一样容易,如GPU Delegate文档中所述

@gnsmrky谢谢。 在低端设备上,这就像魅力一样。
一个问题,确实是我们必须逆时针旋转手机。 我可以在着色器中添加旋转逻辑吗?
引用链接: https :

@ soham24当您将常规的glViewPort,glDraw等与相应的顶点片段着色器一起使用时,将发生转换。 存储库中的SSBO代码是一个简单的内存浮点副本,不涉及任何顶点/片段着色器。 如果我们按浮动进行转换,则很可能会减慢速度。

最好的方法是将相机纹理“绘制”到具有所需变换的另一个纹理,然后执行纹理-> SSBO复制。 这将需要一些努力。 需要花更多的时间来做。

谢谢@gnsmrky 。 如果您用所需的转换更新样本,那就太好了。

@gnsmrky我们发现了不支持Squeeze操作的问题。 这是因为默认情况下,Squeeze操作在Mali设备上的GPU中不起作用(已通过基准测试工具验证)。 希望,我们为此将打开一个单独的问题,或者因为@impjdi与该线程链接,他将处理该问题。除此之外,回购按原样工作...在我们的情况下,我们正在处理一个完整的GPU支持的模型并获得要在表面上渲染的图像输出,因此,我们也将继续使用SSBO输出。

@ SanthoshRajendiran我有一个疑问。 您是否在将输入大小纹理传递给tflite之前调整其大小?
op将被调整大小。 您如何直接通过纹理渲染它?

@ soham24正在调整大小的模型的输入,以确保模型正在运行。.我们将模型的输出调整为需要渲染的所需大小。

@ soham24正在调整大小的模型的输入,以确保模型正在运行。.我们将模型的输出调整为需要渲染的所需大小。

@SanthoshRajendiran我也听起来很奇怪。 我要说的是,如果SSBO的大小不正确,则GPU代表将说SQUEEZE存在问题,即使不是这种情况。

谢谢@gnsmrky 。 如果您用所需的转换更新样本,那就太好了。

进行中,尽管进度很慢...

在该应用程序的当前版本中,它是使用EGL Surface开发的。 我们尝试使用GL Surface View,但无法正常工作。 是否可以做一些工作来使ssbo输出直接在GL Surface View上呈现?

@gnsmrky我们尝试弄清Output SSBO,但我们无法正确执行。.您能告诉我们进行更改以使其正常工作所需的确切位置吗?

基本上,我们进行了这些修改。
1)通过按照tflite gpu文档设置setAllowBufferHandleOutput(true)来初始化tflite实例。
2)使用gpuDelegate.bindGlBufferToTensor(outputTensor,outputSsboId)将绑定的缓冲区输出输出到模型SSBO。

3)在移动屏幕上呈现输出。

您可以检查SSBO输出是否在您的情况下工作。。或者以前做过的一些更改(例如旋转屏幕)或现在也需要进行某些更改才能在屏幕上可视化输出。

因此,我附加了用于测试输出SSBO的tflite,其中我们什么也没做,只是使用ResizeBilinear操作将图像从197调整为257。

just_resize_ssbo.tflite.zip

您可以检查SSBO输出是否在您的情况下工作。。或者以前做过的一些更改(例如旋转屏幕)或现在也需要进行某些更改才能在屏幕上可视化输出。

@SanthoshRajendiran我在这里发布的仓库中没有对输出SSBO做任何事情。 但是我输出SSBO确实有效。 因此,可能是您的着色器代码中的某些内容将数据从SSBO移动到纹理缓冲区以在屏幕上绘制。

我建议的是尝试一种不会改变任何形状的操作。 sqrt op为例,这是一个不会改变张量形状的一元op。 填写可预测的值,例如100 ,结果应为10 。 从一开始,这就是我处理输入/输出SSBO的方式。

我遇到的大多数问题不是在代码的TFLite GPU委托部分上,而是在Android的OpenGL ES上。 只需逐一剖析代码即可使其从SSBO正常运行到屏幕。

希望这可以帮助...

顺便说一句,尽量不要将非线性调整大小与双线性调整大小一起使用。 尝试像2这样的比例因子。 因此, 157大小将调整为314 。 这可能会帮助...

您好, @gnsmrky
我在两个不同的设备中测试您的代码,它似乎只是随机使用GPU。 大多数时候,在GPU模式下,它什么都不做。 我尝试了更少的工作组(8或4),但没有任何区别...

您是否知道为什么会这样?

提前致谢。

我在两个不同的设备中测试您的代码,它似乎只是随机使用GPU。 大多数时候,在GPU模式下,它什么都不做。 我尝试了更少的工作组(8或4),但没有任何区别...

@jsolves您能详细说明吗? 您是说相机-> SSBO不起作用,还是GPU委托? 您如何观察它是否有效?

我希望我能告诉你更多。 但是应用中的每种模式都可以正常运行,直到运行GPU。 大多数情况下,将所有内容归类为0%或接近0%,并且设备的GPU使用率不会提高。 只有在少数情况下,gpu分类才能正常进行(并且gpu利用率相应地会提高)。

我尝试了其他“ gpu应用程序”,它们按预期工作。 我不知道如何确定问题是Camera-> SSBO或GPU委托还是与着色器有关的问题。 您知道什么我可以尝试查看问题是否是其中之一吗?

感谢您的回答。

我尝试了其他“ gpu应用程序”,它们按预期工作。 我不知道如何确定问题是Camera-> SSBO或GPU委托还是与着色器有关的问题。 您知道什么我可以尝试查看问题是否是其中之一吗?

@jsolves您绝对可以尝试的是原始Tensorflow Lite Android存储库,该存储库已支持GPU。 我的SSBO存储库仅添加相机->基于此存储库的SSBO。 您可以看到TFLite Android存储库中的GPU是否具有更快的推理时间。

啊,对不起,这个问题我有点累了。 是的,GPU Delegate可在原始存储库和我自己的自定义应用程序中正常工作。 它提供比CPU推理更快的推理时间。

那么问题出在相机-> SSBO部分中吗?

那么问题出在相机-> SSBO部分中吗?

@jsolves SSBO的主要目的是减少从相机到TFLite的输入SSBO的像素复制时间。 GPU推理时间完全不会受到影响。

在GPU模式下运行应用程序时,您是否看到“复制时间”?

另外,您如何检查GPU利用率? GPU利用率低时,您是否会获得预期的推理输出?

是的我知道。 在GPU(带有SSBO)中,复制时间非常短(0-2),但是大多数时候没有正确的分类(随机值或全0)。

在设备配置中,有一个类似“显示gpu利用率”的选项,并且在应用正常运行(与GPU配合)的几次情况下,gpu指示灯会升高。

这就像是摄像头不总是进入SSBO或有一些初始化麻烦。 但是我的Android-Fu不够强大... :(

@jsolves当我使用基本的3通道输入模型测试gpu推理时,我无法获得正确的结果。
但是,当我将3通道模型更改为伪4通道输入(使用新的Input和strided_slice ops)时,最终得到正确的结果:)

https://www.tensorflow.org/lite/performance/gpu_advanced#tips_and_tricks

有趣的。 如何制作“假4通道”,在每个像素中将“假” alpha设置为1?

提前致谢。

input_shape = (224, 224, 4)
inputs = Input(input_shape, dtype=np.float32)
x = Lambda(lambda x: x[:, :, :, :3])(inputs)
model_pre = Model(inputs, x)
model_pre.summary()
sess_fake = K.get_session()
graph_def_fake = sess_fake.graph_def
nodes_fake = [n for n in graph_def_fake.node]

我将模型转换如下。

  1. 创建假输入(包括跨步切片操作)
  2. 如图所示,将先前的3通道输入更改为伪输入(如果可能,应注意先前的输入名称)
  3. 转换TFLite模型

我认为https://mediapipe.dev可以做到这一点。

@ soham24我经历了mediapipe,但不了解它是如何工作的。 Mediapipe团队提供的tflite具有TFLite-GPU不支持的操作,甚至它们都不是张量流操作。 谁能提供有关如何训练基于Mediapipe架构的细分模型的建议。

@SanthoshRajendiran甚至我也试图通过查看mediapipe代码来弄清楚管道。
如果tf的人帮助我们,那么IT将会很棒

@santhoshRajendiran @ soham24

是的,MediaPipe可能会使用GPU委托的所有功能,并且是一个不错的起点(我几年前曾在MediaPipe上工作过:D)。 我同意GPU路径并不是超级容易阅读,但仍然是一个不错的起点。 如果首先查看TfLiteInferenceCalculator ,将看到大量的RunInGlContext东西,这确保了您保持在相同的GL上下文中。 然后,它真正要做的就是复制输入SSBO,运行推断并复制输出SSBO。 我认为仍有改进的空间,这将很快发生。 好吧,这是我接下来三个月的菜板:P

对于细分模型,您需要检入MediaPipe github页面并对其进行ping操作。

我们可以对此进行更新吗?

嗯,您能详细说明一下您希望进行的更新吗? 您是否要我们浏览另一个开源软件?

我试图像@gnsmrky那样将我的自定义tflite模型与android中的
(顺便说一句,最新的tflite似乎不支持bindGlBufferToTensor,但是官方的tflite gpu委托文档仍然在使用SSBO时引入bindGlBufferToTensor。)
无论如何,我已经从https://github.com/gnsmrky/tensorflow-lite-ssbo构建了tensorflow,并设法通过SSBO运行图像分类演示。 即使与没有SSBO的CPU版本和官方GPU版本相比,结果显示不同,它也至少可以正常工作-它具有预测值,并且复制时间减少了。
但是当我将提供的mobilenet模型更改为我的自定义模型时(我甚至只尝试了一个简单的添加操作模型),看起来工作正常,但输出全为零,或者有时会产生错误,Tensor不会绑定到缓冲区句柄,具体取决于所使用的模型。
由于我尝试使用与原始演示相同的224x224x3输入的模型,并且除了模型路径之外没有进行任何更改,因此我想知道在更改或制作模型时是否还需要进行其他修改。
以下是我尝试过的一些简单模型的示例。 (由Netron可视化)
image
image
如果TensorFlow提供带有最新tflite的官方SSBO演示,那就太好了。

tensorflow-lite-ssbo / tensorflow / lite / java / demo / app / src / main / java / com / example / android / tflitecamerademo / ImageClassifier。 java:212 :错误:找不到符号
gpuDelegate.bindGlBufferToTensor(inputTensor,inputSsboId);
^
符号:方法bindGlBufferToTensor(Tensor,int)
位置:类型为GpuDelegate的变量gpuDelegate
@jmhodges @gnsmrky

我不在Java领域工作,因此我不知道正在使用哪个委托Java API,但是bindGlBufferToTensor在已弃用的GL委托中被重命名,并在新的GPU委托中被删除。 签出//tf/lite/delegates/gpu/gl_delegate//tf/lite/delegates/gpu/gpu_delegate

@impjdi @jmhodges @gnsmrky
我的模型输入像素值是浮动的,范围为0.0-1.0(1.0 / 255),可以使用ssbo吗?

如何将ssbo buf转储到cpu进行评估?

您正在寻找glMapBufferRange

image
为什么在glDispatchCompute调用后transformedData打印出零值
哪里错了?
如果有更好的方法,我需要检查copyCamtextToSsbo之后的ssbo值吗?
@impjdi @svenstaro @bmabey

对Java ByteBufferFloatBuffer不太熟悉,但是在开始从内存位置读取之前,您是否错过了glFinish

@impjdi @svenstaro @ktgordon @SanthoshRajendiran @gnsmrky
您可以将FloatBuffer视为c ++中的float *指针(缓冲区)
我已经尝试过glFinish但结果相同
但是,如果我添加以下代码:
GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER,ssboSize,ssboData,GL_STREAM_COPY);
为什么我可以通过glMapBufferRange获得ssboData内容?
image
image
image
我要评估out_data.elements内容是否正确? 派遣通勤后。
我用谷歌搜索了两天,但没有找到解决方案

GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER,ssboSize,ssboData,GL_STREAM_COPY);
为什么我可以通过glMapBufferRange获得ssboData内容?

不是在谈论“为什么”部分,但是如果您可以访问ssboData您的问题是否就解决

我还记得,我在网络上找不到足够的示例来取得合理的进展。 由于您正在询问纯OpenGLES计算着色器问题,因此您现在要询问的内容似乎对TFLite GPU支持略有超出范围。 我建议在Khronos论坛上提问和/或遵循TFLIte GPU和MediaPipe中的代码路径; 这两个框架大量使用SSBO和纹理。 我确定您会在其中找到用例。

@impjdi @svenstaro @ktgordon @SanthoshRajendiran @gnsmrky

我是opengl es的新手。

我已经查看了MediaPipe和Tflite GPU尝试解决它,但是失败了

我很好奇如何在Android中调试计算着色器

网络上关于ssbo的资料很少

该代码由https://github.com/gnsmrky/tensorflow-lite-ssbo提供

对不起,我的英语不好

别再强调我了。

@svenstaro非常抱歉打扰您

@impjdi
我终于找到了为什么glmapbufferrange返回全零的原因。
GL_OES_EGL_image_external_essl3无法在某些Android设备上使用
https://community.arm.com/developer/tools-software/graphics/f/discussions/9432/is-extension-gl_oes_egl_image_external_essl3-not-working-properly-in-compute-shader-on-mali-g71-gpu

啊,谢谢你的更新和分享!

我遵循了GPU委托的android官方文档,也陷入了bindBuffer步骤。

我不在Java领域工作,因此我不知道正在使用哪个委托Java API,但是bindGlBufferToTensor在已弃用的GL委托中被重命名,并在新的GPU委托中被删除。 签出//tf/lite/delegates/gpu/gl_delegate//tf/lite/delegates/gpu/gpu_delegate

我签出了当前的主服务器,没有gpu_delegate(.cc?),只有gpu_delegate_jni(.cc)。 你是那个意思吗

无论如何,我发现TfLiteGpuDelegateBindBufferToTensor似乎是库的导出符号,我们可以获取委托的本机句柄,因此我们可以直接从Java调用该方法。

抱歉,最后一个文件应该是//tf/lite/delegates/gpu/delegate.cc 。 我们在内部尝试使用bindBuffer (不使用委托API,但直接使用GPU内部函数),并且发现新API有点破损,因此无法与新API一起使用。 有人正在解决这些问题。 现在,如果您想使用bindBuffer ,我想您仍然会使用旧的API,即gl_delegate

@impjdi感谢您的更新。 这是否意味着SSBO路由当前仅可用于C绑定或根本不可用?

我尚未检查Java,但是如果Java已迁移到新的API(delegate.cc),则您的评估是正确的。

对于C ++,仅在v1(gl_delegate.cc)中可用,而在v2(delegate.cc)中不可用。

@impjdi是否解决了v2委托中的SSBO bindBuffer问题?

当前的计划是在委托v2中不支持bindBuffer。

@impjdi我们将图像帧保存在GPU内存中。 我们是否应该将其移至CPU只是为了开始推理,然后又将其移至GPU? 在许多情况下,花时间进行此操作会浪费gpu推理的好处。

@impjdi您是否可以共享有关为何委托v2不支持bindBuffer的任何信息? 我相信它可以消除memcpy动作,从而改善gpu端到端推理时间。 tflite团队会遇到一些无法解决的问题,还是仅由产品要求来决定?

移动GPU推断有许多高级用法,对于每种GPU委托,GPU委托都需要诸如bindBuffer类的辅助函数,因为它不适合委托框架。 通过辅助功能或选项添加了对扩展用法的大量支持后,我们认为组合增长不再可维护,即使在GPU委托(OpenCL,OpenGL,Metal等)中,其外观也不一致。 请注意,我们必须使用Java API对其进行包装。 由于大多数用户都希望GPU委托只是一种快速的黑箱加速器,因此我们最终决定委托API将保持简洁。 对于支持简化的GPU执行管道的高级用法,我们仍将提供示例代码,例如MediaPipe的TfLiteInferenceCalculator 。 请注意,它尚不存在,因为它仍使用v1委托,因此可以访问bindBuffer

@impjdi此信息很有帮助。 另一个问题是MediaPipe委托v2集成何时发布? 谢谢。

有人在努力:)

是否有人设法将缓冲区与v2委托绑定?

在我看来,mediapipe已经在使用它,请参见mediapipe / tflite_gpu_runner.h 。 该运行器用impjdi提到的use_advanced_gpu_api_标志下的计算器中。 它代替了解释器/委托流程,而是使用低级组件。

对于那些想在不维护自己的解释器的情况下拥有SSBO实用程序的人来说,这是非常不友好的,但是更深入地讲,绑定逻辑位于mediapipe / tflite_gpu_runner.cc中,只需调用InferenceRunner::SetInputObject

v2委托人自己拥有InferenceRunner因此对v2委托人的一个小补丁可能会添加所需的SetInputObject (或输出)调用。 但是我还没有测试,设置此刻对我来说很困难。

@impjdi ,任何指导性的话在这里都会有所帮助。 这个对吗? 我们可以简单地使用InferenceRunner :: SetInputObject调用修补v2委托,然后调用它而不是v1 bindBuffer吗? 我认为我走的路不对,但是我确实认为,如果我们能够获得补丁文件并在此处共享,它将对社区非常有用。

@ natario1我认为@impjdi解释了bindBuffer API不适合v2委托设计。 v1和v2委托之间的主要区别是v2同时支持OpenCL和OpenGL后端,而v1仅支持OpenGL。 这将影响Tflite处理数据所有权交换的方式。 而且,市场上的许多设备并不完全支持OpenCL-OpenGL互操作性。 我也尝试在MediaPipe中使用use_advanced_gpu_api_标志,当我打开应用程序时会崩溃。 因此,我认为v2代理支持bindBuffer功能不是一个简单的补丁。 如果您需要此功能,我认为最简单的解决方案是坚持使用opengl后端的mediapipe。

感谢您的评论@ brucechou1983 。 对我来说,一个更简单的解决方案是坚持使用v1委托,但是老实说,它似乎并没有像mediapipe运行程序那样做任何复杂的事情,除了在准备时调用InferenceRunner::SetInputObjectInferenceRunner::SetInputObjectDef 。 我知道它可能还没有准备好,因为它处于标记之下。

v2委托也执行相同的object / objectdef调用,但是不同之处在于,它使用ObjectType::CPU_MEMORY而不是像mediapipe那样使用ObjectType::OPENGL_SSBO

我不知道Android对OpenCL的支持是什么,但是OpenGL可以正常工作,因此我们可以在v2委托选项中有一个标志,告诉委托不要尝试OpenCL并使用OpenGL。 我认为,TF团队可以添加一些内容来简化v1-v2的过渡,因为使用v1的用户可能会设置SSBO。

@ natario1如果仅需要使用OpenGL的标志,尽管已经处于试验阶段但它已经TFLITE_GPU_EXPERIMENTAL_FLAGS_GL_ONLY

但是,当您需要在200美元的手机上运行实时(>> 30fps)语义分段和/或运行面部网格时,在tflite运行时中选择合适的GPU后端以有效执行实际上并不是一个小问题。 我确实看到了对某些MALI gpu设备使用OpenCL的价值。 invoke()执行速度比OpenGL ES快2到3倍。 尽管我必须将数据复制到张量/从张量复制,但总体性能还是更好的。 我认为tflite团队正在尝试将v2委托设计为通用/任意IoT设备/易于使用的黑箱加速器,同时为MediaPipe等其他框架创建接口以针对特定用途进行优化,例如在移动设备/台式机上简化GPU执行。

@ natario1我看到你在那里做了作业,很好

您可能已经注意到,但是TFLite正在为各种加速器或API添加一堆委托。 他们每个人都具有自定义的帮助程序功能并没有帮助使用,但是对于希望使用TFLite GPU委托作为魔术盒进行GPU加速推理的99%的用户来说,这更加令人困惑。 因此,我们做出的最终决定是使TFLite GPU代表尽可能简单,但为想要做真正高性能的高级用户留出空间。

提供TFLite GPU和MediaPipe的团队是姐妹团队,共享一位经理。 话虽如此,TFLite GPU不会破坏MediaPIpe,这是一个保证。 从这个意义上讲,更深入地使用MediaPipe的高级内部API(例如InferenceRunner::SetInputObject是安全的。 当然,因为它不是公共API,而是高级的内部API,所以可能会发生API更改,使您有时不时遇到麻烦,但是您将始终拥有MediaPipe的参考实现。

我了解@impjdi的情况。 您会考虑类似V2Delegate::GetInferenceRunner()吗? 这样我们就可以调用InferenceRunner::SetInputObject或代表外部的其他任何内容。

您说SetInput/OutputObjectSetInput/OutputObjectDef API是“高级的”,并且它们在某种程度上是合理的,但同时,将张量绑定到“某物”是很有意义的,必须指定其数据布局,大小,对象类型等。 就BindGlBufferToTensor ,它们实际上是非常优雅且易于理解的,从我的角度来看,这只是在引擎盖下做了一些我无法真正把握的魔术。

这些API也将隐藏在GetInferenceRunner()API的后面,您可以将其记录为“自担风险”功能,并保持黑匣子表面清洁。 我认为这种方法确实可以像您所说的那样“让房间开着”。 (也许为您做的工作不仅仅是为推理运行者添加getter,但您明白了—能够从外部控制委托对象)

除此之外,我将在本周末尝试使用这些低级API,以查看是否能够使v2正常工作。 感谢您的帮助!

编辑:在度过了一个周末之后,我意识到这个建议是不可能的,但是我希望您可以考虑像我最终所做的那样,这很干净,并且使委托标头保持不变。

@impjdi关于如何解决此错误的任何建议? BHWC> BHWC4转换似乎是一个问题,但是我不知道如何解决。 它发生在ToTensorConverter中。

E/tflite:
    TfLiteGpuDelegate Invoke: Missing output in converter
    Node number 1 (TfLiteGpuDelegateV2) failed to invoke.

我创建对象def和张量对象,如下所示:

// object def
tflite::gpu::ObjectDef object_def;
object_def.data_type = tflite::gpu::DataType::FLOAT32;
object_def.data_layout = tflite::gpu::DataLayout::BHWC;
object_def.object_type = tflite::gpu::ObjectType::OPENGL_SSBO;
object_def.user_provided = true;

// tensor object
tflite::gpu::OpenGlBuffer tensor_object;
tensor_object.id = ssbo;

然后将两者都传递给ModifyGraphWithDelegate之前的委托人。 它们已正确传递给推理运行器和运行器构建器,但是我收到该转换器错误。

TF版本为2.2.0,我使用的模型非常简单,拍摄了400x400x1的图像并计算平均强度,返回一个浮点数。 我正在尝试仅将SSBO对象用于输入。

另外,我正在运行OpenGL后端,我的手机上没有OpenCL。

许多小时后,我认为我遇到了一个仍在2.2.0中存在的错误,但已通过以下提交在master中修复: https : https://github.com / tensorflow / tensorflow / commit / dffe6a0e810f4c3d9968ddb56fd58c8f405eb846

简而言之,对于那些感兴趣的人,我正在使用具有1个颜色通道(而不是4个颜色通道)的BHWC的事实,要求gl引擎执行一次转换和这种转换(在https://github.com/tensorflow/之前) tensorflow / commit / 4000a5c75cdbe49d77bcac93a7f21070a31c4cce和https://github.com/tensorflow/tensorflow/commit/dffe6a0e810f4c3d9968ddb56fd58c8f405eb846)已完全损坏,因为user_provided / flow / tensor硬编码为true blob / v2.2.0 / tensorflow / lite / delegates / gpu / gl / api2.cc#L595),但是当user_provided为true时,引擎不会费心创建输出GL缓冲区(https:// github .com / tensorflow / tensorflow / blob / v2.2.0 / tensorflow / lite / delegates / gpu / gl / api2.cc#L199-L202),因此无法进行C-> C4转换。

通过将https://github.com/tensorflow/tensorflow/commit/4000a5c75cdbe49d77bcac93a7f21070a31c4ccehttps://github.com/tensorflow/tensorflow/commit/dffe6a0e810f4c3d9968ddb56fd58c8f405eb846插入到v2。使用v2委托执行SSBO I / O。 这些提交已经很老了,所以我希望他们可以将其提交到下一个版本中。

这些是我必须进行的更改以公开必需的API: https :

此页面是否有帮助?
0 / 5 - 0 等级