这是 TensorFlow Lite C++ 源代码中的一段实现,用于 Interpreter::Invoke()
函数。这段代码执行了模型的推理过程,并对一些性能和安全性进行了处理。下面是对这段代码功能的逐步解读:
1. 函数的主要作用 Interpreter::Invoke()
是 TensorFlow Lite 的核心方法,用于执行模型推理。通过调用它,可以处理输入数据并获取模型的输出。
2. 代码分析 ScopedRuntimeInstrumentationProfile 1 ScopedRuntimeInstrumentationProfile scoped_runtime_event (root_profiler_.get(), "invoke" ) ;
作用 :创建一个性能跟踪器,记录推理过程的性能指标(如运行时间)。
**root_profiler_
**:这是用于跟踪模型推理性能的分析器。
**"invoke"
**:指定当前分析器记录的事件名称。
处理取消标志 1 if (cancellation_enabled_) (void )continue_invocation_.test_and_set ();
作用 :在支持取消推理功能的场景下,重置取消标志以确保本次推理能够正常执行。
**cancellation_enabled_
**:表示是否启用取消功能。
**continue_invocation_
**:是一个原子标志,用于检查推理是否被取消。
抑制非正规浮点数(Denormal Floating Point Numbers) 1 ruy::ScopedSuppressDenormals suppress_denormals;
作用 :抑制非正规浮点数(Denormals)的影响。
原因 :在某些架构(如 x86)上,处理非正规浮点数可能会导致严重的性能下降。
**ruy::ScopedSuppressDenormals
**:确保推理期间所有浮点运算不会生成非正规值。
执行模型主图的推理 1 2 TF_LITE_ENSURE_STATUS_WITH_SCOPED_INSTRUMENTATION ( scoped_runtime_event, primary_subgraph ().Invoke ());
**primary_subgraph().Invoke()
**:调用模型的主子图进行推理。
**TF_LITE_ENSURE_STATUS_WITH_SCOPED_INSTRUMENTATION
**:检查推理状态,并将状态与性能分析工具关联。
处理输出张量数据 1 2 3 4 5 6 7 if (!allow_buffer_handle_output_) { for (int tensor_index : outputs ()) { TF_LITE_ENSURE_STATUS_WITH_SCOPED_INSTRUMENTATION ( scoped_runtime_event, primary_subgraph ().EnsureTensorDataIsReadable (tensor_index)); } }
返回状态
表示推理执行成功,返回 kTfLiteOk
状态。
3. 函数中的关键点
性能优化
:
使用 ScopedRuntimeInstrumentationProfile
记录性能数据。
抑制非正规浮点数以提高计算速度。
取消功能支持
:
提供了取消推理执行的机制,适用于长时间运行的模型。
安全性检查
:
模块化设计
:
调用主子图的推理逻辑,使得模型支持多子图和模块化扩展。
4. 适用场景
移动和嵌入式设备 :适合资源受限环境,优化了性能并支持运行时监控。
大规模推理系统 :支持性能分析和取消操作,用于高效调试和部署。
这段代码体现了 TensorFlow Lite 在性能、安全性和功能支持上的深度设计。如果需要修改此函数(如自定义推理逻辑),需确保遵循 TensorFlow Lite 的架构和 API 规范。
TfLiteStatus Subgraph::Invoke() { auto status = InvokeImpl(); telemetry::TelemetryReportEvent(&context_, “Invoke”, status); return status; }
这段代码是 TensorFlow Lite 的核心实现之一,Subgraph::InvokeImpl()
,用于执行子图的推理过程(子图是 TensorFlow Lite 中支持多子图模型运行的核心结构之一)。以下是逐步分析和解读:
1. 函数的主要作用 Subgraph::InvokeImpl()
是 TFLite 的底层实现,负责按照执行计划依次执行子图中的每个节点操作,并管理相关的资源分配、错误处理和性能跟踪。
2. 代码分解与详细解读 2.1. 初步一致性检查 1 2 3 4 if (!consistent_) { ReportError ("Invoke called on model that is not consistent." ); return kTfLiteError; }
检查子图是否处于一致状态(consistent_
)。
如果子图不一致,说明模型结构可能被破坏,立即返回错误。
2.2. 状态检查 1 2 3 4 5 6 7 if (state_ == kStateUninvokable) { ReportError ("Invoke called on model that is not ready." ); return kTfLiteError; } else if (memory_planner_ && !memory_planner_->HasNonPersistentMemory ()) { ReportError ("Non-persistent memory is not available." ); return kTfLiteError; }
模型状态检查 :kStateUninvokable
表示子图未准备好运行,可能因为没有完成内存分配。
内存检查 :确保内存规划器存在且已分配非持久性内存(动态内存)。
2.3. 性能跟踪 1 TFLITE_SCOPED_TAGGED_DEFAULT_PROFILE (profiler_.get (), "Invoke" );
2.4. 执行计划的遍历 1 2 3 for (int execution_plan_index = 0 ; execution_plan_index < execution_plan_.size (); execution_plan_index++) {
遍历子图的执行计划 :execution_plan_
是一组节点索引的列表,定义了模型操作的执行顺序。
对每个节点进行如下处理:
2.4.1. 节点准备 1 2 3 if (execution_plan_index == next_execution_plan_index_to_prepare_) { TF_LITE_ENSURE_STATUS (PrepareOpsAndTensors ()); }
如果当前节点需要重新准备资源(例如张量或操作),调用 PrepareOpsAndTensors()
准备。
2.4.2. 读取节点和操作 1 2 3 int node_index = execution_plan_[execution_plan_index]; TfLiteNode& node = nodes_and_registration_[node_index].first;const TfLiteRegistration& registration = nodes_and_registration_[node_index].second;
通过 node_index
获取节点和其对应的操作注册信息。
2.4.3. 数据完整性检查 1 2 3 4 5 6 7 for (int i = 0 ; i < node.inputs->size; ++i) { int tensor_index = node.inputs->data[i]; if (tensor->data.raw == nullptr && tensor->bytes > 0 ) { ReportError ("Input tensor %d lacks data" , tensor_index); return kTfLiteError; } }
确保节点输入张量的内存数据完整。
如果发现某个输入张量缺少必要数据,则报错返回。
2.4.4. 执行节点操作 1 2 3 4 if (auto s = OpInvoke (registration, &node); s != kTfLiteOk) { auto err = ReportOpError (&context_, node, registration, node_index, "failed to invoke" ); return s == kTfLiteCancelled ? s : err; }
调用 OpInvoke()
,执行注册的操作。
如果操作失败,报告错误并返回状态。
2.4.5. 动态张量调整 1 2 3 4 if (tensor_resized_since_op_invoke_ && HasDynamicTensor (context_, node.outputs, nullptr )) { next_execution_plan_index_to_prepare_ = execution_plan_index + 1 ; }
检测动态张量大小是否发生变化。
如果发生变化,更新执行计划以重新准备相关节点。
2.5. 取消操作的支持 1 2 3 4 if (check_cancelled_func_ != nullptr && check_cancelled_func_ (cancellation_data_)) { ReportError ("Client requested cancel during Invoke()" ); return kTfLiteError; }
如果用户请求取消当前推理操作,通过回调函数处理取消逻辑并终止执行。
3. 关键特性总结
执行计划管理 :通过遍历执行计划,依次调用子图中的每个操作。
动态内存管理 :支持动态张量的大小调整,并重新规划内存。
错误和取消处理 :在推理过程中随时检查错误状态和取消请求。
性能监控 :内置性能跟踪机制,帮助开发者优化推理性能。
4. 适用场景
嵌入式设备推理 :适合资源受限环境下的高效执行。
分布式推理框架 :支持多子图和动态内存的灵活运行。
调试与优化 :通过内置的错误报告和性能跟踪,便于开发者发现问题和优化性能。
Conv2D实现 在 TensorFlow Lite 中,Conv2D
操作并不是直接定义在 interpreter
类本身,而是由 TensorFlow Lite 的操作集合(operations)来实现。TensorFlow Lite 的 Interpreter
类是用于加载和执行 .tflite
模型的,它通过解析模型中的操作(例如 Conv2D
)来执行推理。
1. Conv2D
操作在 TensorFlow Lite 中的位置 Conv2D注册 tensorflow/core/kernels/conv_ops.cc
1 2 3 4 5 6 7 8 9 10 11 12 13 REGISTER_OP ("Conv2D" ) .Input ("input: T" ) .Input ("filter: T" ) .Output ("output: T" ) .Attr ("T: {half, bfloat16, float, double, int32}" ) .Attr ("strides: list(int)" ) .Attr ("use_cudnn_on_gpu: bool = true" ) .Attr (GetPaddingAttrStringWithExplicit ()) .Attr (GetExplicitPaddingsAttrString ()) .Attr (GetConvnetDataFormatAttrString ()) .Attr ("dilations: list(int) = [1, 1, 1, 1]" ) .SetShapeFn (shape_inference::Conv2DShapeWithExplicitPadding);
Conv2D实现
1 2 3 REGISTER_KERNEL_BUILDER ( Name ("Conv" ).Device (DEVICE_CPU).TypeConstraint <bfloat16>("T" ), ConvOp<CPUDevice, bfloat16>);
1 2 3 4 5 6 7 8 9 10 11 tensorflow-master/tensorflow/core/kernels/conv_ops_bfloat16.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<bfloat16>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_bfloat16.cc: Name("Conv2D" ).Device(DEVICE_GPU).TypeConstraint<Eigen::bfloat16>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_double.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<double>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_double.cc: Name("Conv2D" ).Device(DEVICE_GPU).TypeConstraint<double>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_float.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<float >("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_float.cc: Name("Conv2D" ).Device(DEVICE_GPU).TypeConstraint<float >("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_half.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<Eigen::half>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_half.cc: Name("Conv2D" ).Device(DEVICE_GPU).TypeConstraint<Eigen::half>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_int32.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<int32>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_int32.cc: Name("Conv2D" ).Device(DEVICE_GPU).TypeConstraint<int32>("T" ), tensorflow-master/tensorflow/core/kernels/conv_ops_using_gemm.cc: Name("Conv2D" ).Device(DEVICE_CPU).TypeConstraint<T>("T" ), \
在 TensorFlow Lite 中,卷积操作 Conv2D
是一个内部实现的操作,它是由 TFLite
操作库提供的。具体来说,TensorFlow Lite 会根据模型中 .tflite
文件的结构,自动解析卷积操作,并调用对应的底层实现。卷积操作的实现通常会通过一个 操作委托(delegate) 来完成(如 GPU、EdgeTPU、CPU 上的优化)。
2. 卷积操作的实现机制 Conv2D
操作的实现通常会依赖于 操作节点 (operation node),每个操作节点定义了要执行的计算。在 TensorFlow Lite 中,这些操作节点在模型的解析和推理过程中被逐一执行。卷积层的实现涉及以下几个步骤:
在解析 .tflite
模型时 ,TensorFlow Lite 会解析出一个或多个 Conv2D
操作,并为每个操作分配相应的输入张量(图像数据)和权重(卷积核)。
Interpreter
类会管理模型执行的流程 ,它会在调用 invoke()
方法时,根据每个操作的定义,依次执行卷积操作。这些操作会触发内部的执行机制,卷积操作会通过优化的数学算法来计算卷积输出。
卷积操作的底层实现 :具体的实现和优化(如矩阵乘法、Winograd 算法、手写优化等)是在 TensorFlow Lite 中的 操作实现函数 中定义的。这些函数在执行时会被调用,并通过 invoke()
执行模型计算。
3. 查找 Conv2D
操作定义 在 TensorFlow Lite 的源代码中,Conv2D
的具体实现通常位于 tensorflow/lite/kernels
文件夹中的实现文件里。例如:
conv_2d.cc
文件:包含 Conv2D
操作的具体实现。
kernels
子模块 :TensorFlow Lite 在 kernels
文件夹中定义了各种操作(包括 Conv2D
),这些操作用于处理低级别的张量计算。
例如,你可以在 TensorFlow Lite 的源代码中看到像以下这样的函数:
1 2 3 4 5 6 7 8 TfLiteStatus Conv2DPrepare (TfLiteContext* context, TfLiteNode* node) { }TfLiteStatus Conv2DInvoke (TfLiteContext* context, TfLiteNode* node) { }
4. 调用机制 当你在 Interpreter
中调用 invoke()
时,TensorFlow Lite 会依次遍历每一层,识别每个操作(例如 Conv2D
)。对于 Conv2D
操作,它会调用底层的执行函数(如 Conv2DInvoke
)来完成卷积运算。这个函数会执行以下任务:
提取输入张量和卷积核。
使用卷积算法(例如,直接卷积、GEMM、Winograd 等)执行卷积计算。
计算并存储输出结果。
5. 总结
Conv2D
操作并不在 interpreter
类本身定义,而是在 TensorFlow Lite 的 kernels
目录下的实现文件中(例如 conv_2d.cc
)。
Interpreter
类 会解析 .tflite
模型并执行对应的操作(如 Conv2D
)。它会在 invoke()
方法中触发卷积操作的执行,调用相应的底层实现函数来完成计算。
TensorFlow Lite 中的卷积操作会通过优化的算法执行,如 GEMM、Winograd 或硬件加速(GPU、EdgeTPU)来提高性能。
要在 TensorFlow 中注册自己实现的 Conv2D
操作,可以参考以下步骤:
注册自己的Conv2D 1. 定义 Op 的接口 首先,需要定义一个新的 Op 接口。这通常涉及到编写一个 .cc
文件,其中包含对 REGISTER_OP
宏的调用,以定义操作的输入、输出和属性。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/shape_inference.h" using namespace tensorflow;REGISTER_OP ("CustomConv2D" ) .Input ("input: T" ) .Input ("filter: T" ) .Output ("output: T" ) .Attr ("T: {half, bfloat16, float, double, int32}" ) .Attr ("strides: list(int)" ) .Attr ("padding: {'SAME', 'VALID'}" ) .Attr ("use_cudnn_on_gpu: bool = true" ) .Attr ("data_format: {'NHWC', 'NCHW'} = 'NHWC'" ) .Attr ("dilations: list(int) = [1, 1, 1, 1]" ) .SetShapeFn ([](shape_inference::InferenceContext* c) { return Status::OK (); });
2. 实现 Kernel 接下来,需要实现该 Op 的具体计算逻辑。这通常涉及到编写一个继承自 OpKernel
的类,并重写 Compute
方法。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class CustomConv2DKernel : public OpKernel {public : explicit CustomConv2DKernel (OpKernelConstruction* ctx) : OpKernel(ctx) { } void Compute (OpKernelContext* ctx) override { const Tensor& input = ctx->input (0 ); const Tensor& filter = ctx->input (1 ); int32 strides[4 ]; OP_REQUIRES_OK (ctx, ctx->GetAttr ("strides" , &strides)); string padding; OP_REQUIRES_OK (ctx, ctx->GetAttr ("padding" , &padding)); bool use_cudnn_on_gpu; OP_REQUIRES_OK (ctx, ctx->GetAttr ("use_cudnn_on_gpu" , &use_cudnn_on_gpu)); string data_format; OP_REQUIRES_OK (ctx, ctx->GetAttr ("data_format" , &data_format)); int32 dilations[4 ]; OP_REQUIRES_OK (ctx, ctx->GetAttr ("dilations" , &dilations)); Tensor* output = nullptr ; OP_REQUIRES_OK (ctx, ctx->allocate_output (0 , TensorShape ({}), &output)); } };REGISTER_KERNEL_BUILDER (Name ("CustomConv2D" ).Device (DEVICE_CPU), CustomConv2DKernel);
3. 编译为共享库 将上述代码编译为一个共享库(.so
文件)。这通常涉及到编写一个 BUILD
文件,使用 Bazel 进行编译。例如:
1 2 3 4 5 6 7 8 9 10 cc_binary( name = "custom_conv2d_op.so" , srcs = ["custom_conv2d_op.cc" , "custom_conv2d_kernel.cc" ], copts = ["-fPIC" ], linkshared = 1 , deps = [ "//tensorflow/core:framework" , "//tensorflow/core:lib" , ], )
4. 加载共享库 在 Python 代码中,使用 tf.load_op_library
加载编译好的共享库:
1 2 3 4 import tensorflow as tf custom_conv2d_module = tf.load_op_library('./custom_conv2d_op.so' ) custom_conv2d = custom_conv2d_module.custom_conv2d
5. 使用自定义 Op 现在可以在 TensorFlow 图中使用自定义的 Conv2D
操作了:
1 2 3 input_tensor = tf.random.normal([1 , 28 , 28 , 1 ]) filter_tensor = tf.random.normal([3 , 3 , 1 , 32 ]) output_tensor = custom_conv2d(input =input_tensor, filter =filter_tensor, strides=[1 , 1 , 1 , 1 ], padding='SAME' )
总结 通过上述步骤,你可以成功注册并使用自己实现的 Conv2D
操作。这涉及到定义 Op 接口、实现 Kernel、编译为共享库、加载共享库以及在 TensorFlow 图中使用自定义 Op。希望这些步骤能帮助你实现自定义的卷积操作。