1.极简入门TensorFlow C++源码
2.TFlite 源码分析(一) 转换与量化
3.TensorFlow 源码大坑(2) Session
4.从源码build Tensorflow2.6.5的源码阅读记录
5.探索TensorFlow核心组件系列之Session的运行源码分析
6.tf.embedding_lookup(sparse)详解
极简入门TensorFlow C++源码
前一段时间,我专注在框架开发上,源码阅读并偶尔协助业务同学优化使用TensorFlow的源码阅读代码。在观看dmlc/relay、源码阅读nnvm的源码阅读代码时,我发现了它们的源码阅读正品的溯源码中盏哪里去买有趣之处。我也对TensorFlow的源码阅读Graph IR、PaddlePaddle的源码阅读Graph IR产生了兴趣,上周五在阅读代码时,源码阅读无意间听到了一个数据竞赛群讨论框架的源码阅读底层实现。几位算法大佬提到了看底层源码可能较为繁琐,源码阅读因为这类代码通常相对容易理解。源码阅读在与群内伙伴的源码阅读交流后,我萌生了撰写一篇关于如何阅读TensorFlow或其他框架底层源码的源码阅读文章。
选择合适版本的源码阅读bazel,对于阅读TensorFlow源码至关重要。应使用版本为0..0的bazel来拉取TF2.0代码,因为太高的版本或太低的版本可能影响阅读体验。在安装了合适的bazel版本后,使用clion上的bazel插件进行导入,然后配置编译,导入项目,等待clion编译整个项目。完成编译后,就能愉快地阅读代码,甚至于protobuf生成的文件也能轻松跳转。
使用c++编译模型是TensorFlow的另一面。尝试使用c++编写模型代码,可以深入理解TensorFlow的底层机制。主要函数包括CreateGraphDef、ConcurrentSteps、ConcurrentSessions等。通过这些函数,可以构建计算图,定义节点、常量变量、操作符等。这为理解TensorFlow的linux内核4.0 源码逻辑提供了直观的视角。
深入分析代码后,可以了解到TensorFlow的GraphDef机制、Square类的实现、注册到特定op的过程、functor的使用以及最终的实现逻辑。这有助于理解TensorFlow的核心原理,并在阅读源码时进行更深入的思考。
除了阅读源码,还可以通过编写测试用例来增强理解。TensorFlow提供了丰富的测试用例,如在client_session_test.cc中运行测试程序,可以验证代码的正确性。这不仅有助于理解代码,还能提高对TensorFlow框架的掌握程度。
阅读源码只是理解TensorFlow原理的开始,深入行业论文和请教行业专家是进一步深入学习的关键。网络上关于机器学习系统的资料丰富多样,但缺少系统性的课程。希望官方能够分享更多框架的干货,并期待在学习过程中总结和分享更多资源。阅读源码虽然复杂,但其背后蕴含的原理和逻辑十分有趣。
TFlite 源码分析(一) 转换与量化
TensorFlow Lite 是 Google 推出的用于设备端推断的开源深度学习框架,其主要目的是将 TensorFlow 模型部署到手机、嵌入式设备或物联网设备上。它由两部分构成:模型转换工具和模型推理引擎。
TFLite 的核心组成部分是转换(Converter)和解析(interpreter)。转换主要负责将模型转换成 TFLite 模型,并完成优化和量化的过程。解析则专注于高效执行推理,在端侧设备上进行计算。
转换部分,主要功能是通过 TFLiteConverter 接口实现。转换过程涉及确定输入数据类型,如是否为 float、int8 或 uint8。优化和转换过程主要通过 Toco 完成,mvc 单点登录源码包括导入模型、模型优化、转换以及输出模型。
在导入模型时,`ImportTensorFlowGraphDef` 函数负责确定输入输出节点,并检查所有算子是否支持,同时内联图的节点进行转换。量化过程则涉及计算网络中单层计算的量化公式,通常针对 UINT8(范围为 0-)或 INT8(范围为 -~)。量化功能主要通过 `CheckIsReadyForQuantization`、`Quantize` 等函数实现,确保输入输出节点的最大最小值存在。
输出模型时,根据指定的输出格式(如 TensorFlow 或 TFLite)进行。TFLite 输出主要分为数据保存和创建 TFLite 模型文件两部分。
量化过程分为选择量化参数和计算量化参数两部分。选择量化参数包括为输入和权重选择合适的量化参数,这些参数在 `MakeInitialDequantizeOperator` 中计算。计算参数则使用 `ChooseQuantizationParamsForArrayAndQuantizedDataType` 函数,该函数基于模板类模板实现。
TFLite 支持的量化操作包括 Post-training quantization 方法,实现相关功能的代码位于 `tools\optimize\quantize_model.cc`。
TensorFlow 源码大坑(2) Session
深入探讨TensorFlow源码中的Session机制,揭示其运行机制和复杂性。从Python和C++两端的Session API入手,解析其调用栈,解析内部工作流程。Python端的tf.Session().run()方法,通过初始化调用栈,实现计算图的执行。C++端的ClientSession.run()同样展示了Session运行机制,揭示了底层实现细节。对比之下,DirectSession作为Session的基类,展示了如何构建Executor并具体运行计算图,为理解TensorFlow的高效计算逻辑提供了深入视角。
深入解析Python端tf.Session().run()方法的淘金农场php源码调用栈,揭示了其如何通过初始化调用栈来执行计算图的全过程。从创建Session到调用run方法,每一次调用都紧锣密鼓地执行一系列操作,确保计算图能够正确运行,这使得理解TensorFlow的执行流程变得清晰。
同时,C++端的ClientSession.run()方法提供了另一种视角,展示了Session运行机制在底层语言中的实现。通过对比Python和C++端的实现,可以更深入地理解TensorFlow在不同环境下的兼容性和性能优化。
DirectSession作为Session的基类,展示了如何构建Executor并具体运行计算图。通过分析DirectSession的run方法和构建过程,可以理解TensorFlow在执行计算图时的灵活性和高效性,以及如何通过Executor优化计算流程。
总之,深入研究TensorFlow源码中的Session机制,不仅能够揭示其复杂性,还能为开发者提供优化计算图执行流程、提升模型训练效率的策略,是理解TensorFlow内核机制的关键。
从源码build Tensorflow2.6.5的记录
.从源码编译Tensorflow2.6.5踩坑记录,笔者经过一天的努力,失败四次后终于成功。Tensorflow2.6.5是截至.时,能够从源码编译的最新版本。
0 - 前期准备
为了对Tensorflow进行大规模修改并完成科研工作,笔者有从源码编译Tensorflow的需求。平时更常用的做法是在conda环境中pip install tensorflow,有时为了环境隔离方便打包,会用docker先套住,再上conda + pip安装。
1 - 资料汇总
教程参考:
另注:bazel的编译可以使用换源清华镜像(不是必要)。整体配置流程的根本依据还是官方的教程,但它的教程有些点和坑没有涉及到,所以多方材料了解。
2 - 整体流程
2.1 确定配置目标
官网上给到了配置目标,qt 线程池源码和对应的版本匹配关系(这张表里缺少了对numpy的版本要求)。笔者最后(在docker中)配置成功的版本为tensorflow2.6.5 numpy1..5 Python3.7. GCC7.5.0 CUDA.3 Bazel3.7.2。
2.2 开始配置
为了打包方便和编译环境隔离,在docker中进行了以下配置:
2. 安装TensorFlow pip软件包依赖项,其编译过程依赖于这些包。
3. Git Tensorflow源代码包。
4. 安装编译工具Bazel。
官网的介绍:(1)您需要安装Bazel,才能构建TensorFlow。您可以使用Bazelisk轻松安装Bazel,并且Bazelisk可以自动为TensorFlow下载合适的Bazel版本。为便于使用,请在PATH中将Bazelisk添加为bazel可执行文件。(2)如果没有Bazelisk,您可以手动安装Bazel。请务必安装受支持的Bazel版本,可以是tensorflow/configure.py中指定的介于_TF_MIN_BAZEL_VERSION和_TF_MAX_BAZEL_VERSION之间的任意版本。
但笔者尝试最快的安装方式是,到Github - bazelbuild/build/releases上下载对应的版本,然后使用sh脚本手动安装。比如依据刚才的配置目标,笔者需要的是Bazel3.7.2,所以下载的文件为bazel-3.7.2-installer-linux-x_.sh。
5. 配置编译build选项
官网介绍:通过运行TensorFlow源代码树根目录下的./configure配置系统build。此脚本会提示您指定TensorFlow依赖项的位置,并要求指定其他构建配置选项(例如,编译器标记)。
这一步就是选择y/N基本没啥问题,其他参考里都有贴实例。笔者需要GPU的支持,故在CUDA那一栏选择了y,其他部分如Rocm部分就是N(直接按enter也可以)。
6.开始编译
编译完成应输出
7.检查TF是否能用
3 - 踩坑记录
3.1 cuda.0在编译时不支持sm_
笔者最初选择的docker是cuda.0的,在bazel build --config=cuda //tensorflow/tools/pip_package:build_pip_package过程中出现了错误。所以之后选择了上面提到的cuda.3的docker。
3.2 问题2: numpy、TF、python版本匹配
在配置过程中,发现numpy、TF、python版本需要匹配,否则会出现错误。
4 - 启示
从源码编译Tensorflow2.6.5的过程,虽然经历了多次失败,但最终还是成功。这个过程也让我对Tensorflow的编译流程有了更深入的了解,同时也提醒我在后续的工作中要注意版本匹配问题。
探索TensorFlow核心组件系列之Session的运行源码分析
TensorFlow作为一个前后端分离的计算框架,旨在实现前端在任何设备、任何位置上使用API构建模型,而不受硬件资源限制。那么,TensorFlow是如何建立前后端的连接呢?在这一过程中,Session起着关键桥梁作用,它连接前后端通道,并通过session.run()触发计算,将前端的计算图转化为graphdef pb格式发送至后端。后端接收此格式,将计算图重建、剪枝、分裂,并分配到设备上,最终在多个Executor上执行计算。
Session管理着计算图、变量、队列、锁、设备和内存等多种资源,确保资源安全、高效地使用。在Session生命周期中,包含创建、运行、关闭和销毁四个阶段,确保模型运行的正确性和效率。
在Session创建时,使用BaseSession初始化,通过调用TF_NewSessionRef创建实例。此过程涉及确定图实例、判断混合精度设置以及创建Session。在分布式框架中,Python通过swig自动生成的函数符号映射关系调用C++层实现。
Session运行主要通过session.run()触发,该方法在BaseSession的run()中实现,涉及创建fetch处理器、获取最终fetches和targets,调用_do_run方法启动计算,并输出结果。在本地模式下,Session初始化会生成DirectSession对象。
综上所述,Session在TensorFlow架构中扮演着核心角色,连接前后端,管理资源,并确保模型高效、安全地运行。
tf.embedding_lookup(sparse)详解
嵌入查找是一种从矩阵中根据ID索引对应值的方法,适用于处理离散特征。
假设embw1为一个行5列的矩阵,即表示一个拥有个类别的单值离散特征(例如商品ID)的初始化权重嵌入矩阵,嵌入大小为5。如果feature1是一个序列多值稀疏特征,批量大小为4,序列特征长度为3,经过嵌入查找后,转换为(4,3,5)的张量。这种方法在DIN源码中有所应用。
如果feature2是单值稀疏特征,批量大小为4,经过嵌入查找后,转换为(4,5)的张量。这表示是从emb_w1的特定行进行行索引。
第二部分,嵌入查找稀疏主要参考博客,引入了从CSV文件中读取和解析数据的操作。需要注意在CSV解析时,确保每一行前有固定的索引值,否则可能会报错。假设CSV解析的index是固定的使用方法,若采用逐行解析的reader形式,则index是自带的。目前尚未实现使用reader形式解析的博客链接。
总体而言,嵌入查找和嵌入查找稀疏在处理稀疏数据时,提供了高效的方法来转换和处理特征,为模型训练提供了有力的支持。
序列化推荐中的GRU与Transformer源码解析之一
GRU4Rec源码(TF版本):github.com/Songweiping/...
Transformer源码:github.com/kang/SASR...
序列化推荐领域中,GRU4Rec成功地将循环神经网络(NLP和时序预测常用)应用至推荐领域,此模型取得了良好效果。紧随其后的是"SASR",基于注意力机制的自适应序列推荐模型,实验表明其性能超越了GRU4Rec。
两篇论文的作者均在源码公开阶段,为研究者提供参考。我们深入剖析源码,后续系列文章将比较GRU4Rec与SASR的差异、联系与优缺点。
GRU4Rec模型结构简洁,采用门限循环神经网络,Embedding层处理item_id的one_hot编码,降低维度,便于优化。
并行化训练数据集优化了模型训练速度,构建了training_batch,便于使用GPU加速矩阵运算。
负采样技术提高了训练频率,利用同一时刻不同session中的item作为负样本。
模型设计了贝叶斯排序和TOP1等pairwise方法计算排序损失,认为pairwise结果优于pointwise。
实验数据集包括RSC和私有VIDEO集,结果表明GRU4Rec模型性能优秀,测试集评价指标包括召回率(recall)和倒序排名得分(mrr)。
深入分析模型的Tensorflow版本代码,主要从main.py和model.py文件开始,重点解析模型定义、损失函数、GRU4Rec核心代码、数据集初始化、模型训练与预测以及评估函数。
GRU4Rec的代码分析暂告一段落,后续将详细梳理SASR代码,目标是通过三篇文章全面探讨两个模型的细节。感谢关注。
tf.metrices.auc源码解读
auc指标在机器学习二分类问题中广泛应用,反映了分类器对正负样本排序的能力。常用的计算方法有tensorflow库中的tf.metrics.auc函数和sklearn中的roc_auc_score()函数。二者均采用极限逼近原理,计算roc曲线下的小梯形面积总和,得到auc值。然而,这两个函数计算的auc值有时会出现较大差异,本文将解析tf.metrics.auc的实现机制。
tf.metrics.auc函数的定义包含了三个关键参数:labels和predictions为必要的输入,分别代表二分类问题的类别集合和模型预测的得分集合;num_thresholds参数控制计算小梯形的数量,其默认值为,可根据数据集大小进行调整。
在tf.metrics.auc的实现中,函数返回了auc_value和update_op两个值。auc_value为最终计算得到的auc值,但需先执行sess.run(update_op)后才能获取。这一设计的逻辑在于,update_op是一个操作符,auc_value是标量结果,它们的计算依赖于一个名为compute_auc的函数,该函数基于混淆矩阵的四个值计算auc。
compute_auc函数内部,真正例率rec和假正例率fp_rate分别对应roc曲线的横坐标和纵坐标。通过计算小梯形之和得到roc曲线下的面积,即auc值。
在-行,auc_value和update_op的区别在于values和update_ops变量。update_op为操作符,auc_value为标量结果。这两个值的计算在行的_confusion_matrix_at_thresholds函数中进行,该函数接收labels、predictions、thresholds和weights(默认值None)作为输入参数。通过thresholds在[0-1]范围内划分成num_thresholds个段,计算混淆矩阵。
以values[‘tp’]为例,其计算过程涉及创建本地变量、计算真正例is_true_positive,然后通过assign_add操作更新真正例数量。assign_add返回操作符,因此获取真正例数量需先执行update_ops[‘tp’]。这一过程解释了为何需要执行update_op后才能获取auc_value。
真正例is_true_positive的计算涉及label_is_pos和pred_is_pos的逻辑与操作。通过数组操作生成阈值矩阵,使用逻辑判断确定预测为正例的样本。最终,通过逻辑与操作得到真正例的判定结果。
总结,tf.metrics.auc通过计算混淆矩阵和小梯形面积,实现对auc值的高效计算,其内部机制涉及操作符和变量的交互,以及针对不同阈值的真正例和假正例率的计算。