TensorFlow图的构建

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: TensorFlow图的构建

本文就来分析tensorflow图的构建,以及运行图的设备和线程池的创建。TensorFlow使用数据流图将计算表示为独立的指令之间的依赖关系,数据流是一种用于并行计算的常用编程模型。在数据流图中,节点表示计算单元,边缘表示计算使用或产生的数据。

数据流可以为TensorFlow提供多项优势:

  • 并行处理:通过使用明确的边缘来表示操作之间的依赖关系,系统可以轻松识别能够并行执行的操作。
  • 分布式执行:通过使用明确的边缘来表示操作之间流动的值,TensorFlow 可以将您的程序划分到连接至不同机器的多台设备上(CPU、GPU 和 TPU)。
  • 编译:TensorFlow 的 XLA 编译器可以使用数据流图中的信息生成更快的代码。
  • 可移植性:数据流图是一种不依赖于语言的模型代码表示法。您可以使用 Python 构建数据流图,将其存储在 SavedModel 中,并使用 C++ 程序进行恢复,从而实现低延迟的推理。


回目录
类TensorfowTest.cc中有如下代码,它调用c api完成一个图的创建:

	TF_Graph* graph = TF_NewGraph();

在文件c_api.cc中有TF_Graph::TF_Graph构造函数:
graph的类型为Graph,refiner的类型为ShapeRefiner。

TF_Graph::TF_Graph()
    : graph(tensorflow::OpRegistry::Global()),
      refiner(graph.versions().producer(), graph.op_registry()),
      delete_requested(false),
      parent(nullptr),
      parent_inputs(nullptr) {
  // Tell the shape refiner to also run shape inference on functions.
  refiner.set_function_library_for_shape_inference(&graph.flib_def());
}
 
TF_Graph* TF_NewGraph() { return new TF_Graph; }

继续阅读“TensorFlow图的构建”本作品采用知识共享署名 4.0 国际许可协议进行许可。

TensorFlow op和op kernel的注册

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: TensorFlow op和op kernel的注册


回目录

生成ops的定义

TensorFlow Library在加载的时候,其中so里面的静态变量会实例化。
以AddN这个op为例,在文件math_ops.cc里面的REGISTER_OP(“AddN”)其实是个静态变量定义:

REGISTER_OP("AddN")
    .Input("inputs: N * T")
    .Output("sum: T")
    .Attr("N: int >= 1")
    .Attr("T: {numbertype, variant}")
    .SetIsCommutative()
    .SetIsAggregate()
    .SetShapeFn([](InferenceContext* c) {
      ShapeHandle cur = c->input(c->num_inputs() - 1);
      ............
    });

继续阅读“TensorFlow op和op kernel的注册”本作品采用知识共享署名 4.0 国际许可协议进行许可。

基于tensorflow c lib调试的主程序

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: 基于tensorflow c lib调试的主程序


回目录

主程序明细

下面贴上示例代码,程序完成:kone + ktwo = kthree, A + ktwo = plus2, plus2 + B = plusB, plusB + kthree = plusC。

//============================================================================
// Name        : TensorflowTest.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
 
#include <iostream>
#include <tensorflow/c/c_api.h>
#include <tensorflow/c/c_test_util.h>
 
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <memory>
#include <vector>
#include <string.h>
 
using namespace std;
 
int main() {
	cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
	cout << "Hello from TensorFlow C library version" << TF_Version() << endl;
 
	TF_Status* s = TF_NewStatus();
	TF_Graph* graph = TF_NewGraph();
 
	// Construct the graph: A + 2 + B
	TF_Operation* a = Placeholder(graph, s, "A");
	cout << TF_Message(s);
 
	TF_Operation* b = Placeholder(graph, s, "B");
	cout << TF_Message(s);
 
	TF_Operation* one = ScalarConst(1, graph, s, "kone");
	cout << TF_Message(s);
 
	TF_Operation* two = ScalarConst(2, graph, s, "ktwo");
	cout << TF_Message(s);
 
	TF_Operation* three = Add(one, two, graph, s, "kthree");
	cout << TF_Message(s);
 
	TF_Operation* plus2 = Add(a, two, graph, s, "plus2");
	cout << TF_Message(s);
 
	TF_Operation* plusB = Add(plus2, b, graph, s, "plusB");
	cout << TF_Message(s);
 
	TF_Operation* plusC = Add(plusB, three, graph, s, "plusC");
	cout << TF_Message(s);
 
	// Setup a session and a partial run handle.  The partial run will allow
	// computation of A + 2 + B in two phases (calls to TF_SessionPRun):
	// 1. Feed A and get (A+2)
	// 2. Feed B and get (A+2)+B
	TF_SessionOptions* opts = TF_NewSessionOptions();
	TF_Session* sess = TF_NewSession(graph, opts, s);
	TF_DeleteSessionOptions(opts);
 
	TF_Output feeds[] = { TF_Output { a, 0 }, TF_Output { b, 0 } };
	TF_Output fetches[] = { TF_Output { plus2, 0 }, TF_Output { plusB, 0 }, TF_Output { plusC, 0 }  };
 
	const char* handle = nullptr;
	TF_SessionPRunSetup(sess, feeds, TF_ARRAYSIZE(feeds), fetches,
			TF_ARRAYSIZE(fetches), NULL, 0, &handle, s);
	cout << TF_Message(s);
 
	// Feed A and fetch A + 2.
	TF_Output feeds1[] = { TF_Output { a, 0 }, TF_Output { b, 0 } };
	TF_Output fetches1[] = { TF_Output { plus2, 0 }, TF_Output { plusB, 0 }, TF_Output { plusC, 0 } };
	TF_Tensor* feedValues1[] = { Int32Tensor(1), Int32Tensor(3) };
	TF_Tensor* fetchValues1[3];
	TF_SessionPRun(sess, handle, feeds1, feedValues1, 2, fetches1, fetchValues1,
			3, NULL, 0, s);
	cout << TF_Message(s);
	cout << *(static_cast<int*>(TF_TensorData(fetchValues1[0]))) << endl;
	cout << *(static_cast<int*>(TF_TensorData(fetchValues1[1]))) << endl;
	cout << *(static_cast<int*>(TF_TensorData(fetchValues1[2]))) << endl;
 
	// Clean up.
	TF_DeletePRunHandle(handle);
	TF_DeleteSession(sess, s);
	cout << TF_Message(s);
	TF_DeleteGraph(graph);
	TF_DeleteStatus(s);
	return 0;
}
 
</vector></memory></iterator></cstddef></algorithm></iostream>

继续阅读“基于tensorflow c lib调试的主程序”本作品采用知识共享署名 4.0 国际许可协议进行许可。

TensorFlow工程创建及设置

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: TensorFlow工程创建及设置


回目录

创建TensorFlow工程

创建TensorFlow工程的目的是:
1 便于我们查看TensorFlow的代码;
2 代码索引后,即使不在eclipse里面build也可以进行代码跳转,call hierarchy的查看。

设置源代码路径,排除不需要查看代码的目录。

创建TensorFlowTest工程

创建一个Linux GCC工程:

然后设置如下参数:

参数明细:
LD_LIBRARY_PATH=/data/root/tensorflowspace/libtensorflow/lib:/data/root/tensorflowspace/libtensorflow/tensorflow/bazel-bin/tensorflow/core/:/data/root/tensorflowspace/libtensorflow/tensorflow/bazel-bin/tensorflow/cc/:/data/root/tensorflowspace/libtensorflow/tensorflow/bazel-bin/tensorflow/core/kernels/:/data/root/tensorflowspace/libtensorflow/tensorflow/bazel-bin/tensorflow/c/:/data/root/tensorflowspace/libtensorflow/tensorflow/bazel-bin/tensorflow/
LIBRARY_PATH=/data/root/tensorflowspace/libtensorflow/lib

本作品采用知识共享署名 4.0 国际许可协议进行许可。

Tensorflow源码解读

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: Tensorflow源码解读

TensorFlow是一个灵活的端到端的机器学习框架。通过底层的代码调试更能够了解它的原理。

第一部分 Tensorflow c lib的编译

Tensorflow的下载和编译 本篇介绍编译的过程及注意事项。

第二部分 Tensorflow Eclipse环境的搭建

由于我们是要研究tensorflow的实现,所以用一个简单的c程序来调用tensorflow c library来用gdb调试。
正常的使用python写程序,然后由python调用so文件,但那样没办法调试到tensorflow内部。

TensorFlow工程创建及设置
基于tensorflow c lib调试的主程序
使用VSCode 调试tensorflow c lib的简单方法

第三部分 Tensorflow 源码调试

本部分介绍Tensorflow的实现机制。

  • 启动后,ops和op kernels的定义由静态变量声明
  • 然后根据用户程序生成计算图,查找ops的声明调用ops的factory函数注册ops并付给图中的node
  • 对生成的图进行优化剪裁
  • 创建executor,把root node喂给executor开始计算图的执行,完成后返回给主线程结束程序
TensorFlow op和op kernel的注册 TensorFlow so文件加载时完成TensorFlow op和op kernel的注册。另一篇TF Operation的注册
TensorFlow图的构建 本文就来分析tensorflow图的构建,以及运行图的设备和线程池的创建。TensorFlow使用数据流图将计算表示为独立的指令之间的依赖关系,数据流是一种用于并行计算的常用编程模型。在数据流图中,节点表示计算单元,边缘表示计算使用或产生的数据。
TensorFlow Session的创建 本文介绍TensorFlow Session的创建。TensorFlow使用Session 类来表示客户端程序(通常用Python 程序,但也提供了其他语言的类似接口,这儿就是用C接口)与 C++ 运行时之间的连接。Session 对象使我们能够访问本地机器中的设备和使用分布式 TensorFlow 运行时的远程设备。它还可缓存关于Graph 的信息,使您能够多次高效地运行同一计算。Session接受Graph参数和Options选项参数,Options参数可以指定使用的设备等信息。
TensorFlow Session的Setup TensorFlow Session的Setup完成整个Session的创建,设置输入数据类型(feeds)和输出数据类型(fetches)。然后利用图Graph创建基于Session的基本图,开启线程器(Exectors)等待Session开始。
TensorFlow Executor的创建 Session中的Executeors是一个线程池,用来执行每个节点Node的计算。在上一篇Session的setup中,其中调用了GetOrCreateExecutors类创建Executeors。direct_session.cc中的GetOrCreateExecutors方法用于获取设置callable_options的inputs,fetches,target,然后继续调用CreateExecutors方法创建executors。
tf-operation的创建 以AddNOp为例说明Operation怎样从ops的定义创建具体的kernel实例。
TensorFlow计算图的创建 Tensorflow机器学习任务的核心就是根据用户定义的图graph模型以及参数求解方式进行抽象之后,生成一个由节点和有向边组成,可以确定一个唯一的计算逻辑用有向无环图,称之为计算图。它定义了数据的流转方式,数据的计算方式,以及各种计算之间的相互依赖关系等。节点包括计算节点(Operation)、存储节点(Variable)和数据节点(Placeholder)3类,用于计算数据和存储数据。有向边表示数据的流转方式和依赖。
TensorFlow计算图的优化 Tensorflow计算图的优化相当重要,通过对其优化可以显著降低无用代码对计算资源的消耗,尤其在深度学习时,每一此迭代时间的缩短可以大大加速整个学习结果的求解,降低的资源消耗又可以容纳更多的输入并行计算。常见的优化有常量折叠,公共表达式折叠,内联函数展开,算数优化,修剪不可达节点,调试代码去除,自动并行计算,循环优化,内存优化等。
TF 计算图的执行 TF计算图优化完成后,在Session开始执行后就轮到TF 计算图的执行了。Tensorflow会根据计算图的节点信息,首先找到一个没有输入的节点作为根节点,创建一个task交给线程池取执行。每个节点完成后会根据Edge通知下游节点计算,直到所有节点完成计算,然后输出结果。
Tensorflow C API实现卷积计算 一个基于C API实现的进行卷积计算的示例。
Tensorflow 算法优化器验证 通过一个很简单的程序来验算法优化器的功能。如下程序,通过原图:输入数据->取负数->取负数->取倒数->取倒数->得到结果,经过优化计算图直接变成:输入数据->得到结果,计算节点数目大大减小了。
Tensorflow 函数式编程的测试 本文通过一个简单的程序来验证Tensorflow能够进行函数式编程的底层支持。从此也说明,Tensorflow在2.0发布之前的1.15版本底层已经支持函数式编程,进而能够实现动态图计算。

第四部分 Tensorflow常用实例

本部分介绍Tensorflow一些常用的实例。

Tensorflow Embedding原理 Embedding(嵌入)指的是把低维的流形嵌入到高维空间中。举个简单的例子,三维空间的球体(地球)是一个二维流形嵌入在三维空间(欧几里得空间),即地球上的任意一个点只需一个二维的经纬度就可以表达,但三维空间中用x,y,z。深度学习领域假设“自然的原始数据是低维的流形嵌入到原始数据所在的高维空间中”。所以,深度学习的过程就是把高维原始数据(图像,句子)再回映射到低维流形中,从而是数据变得可分,而这个映射就叫嵌入(Embedding)。比如文档Embedding,就是把每篇文档所组成的单词映射到一个低维的表征向量,使得每篇文档可以用一个表征向量来表示,即Embedding就是从原始数据提取出来的Feature,也就是通过神经网络映射之后的低维向量。
Tensorflow Pooling池化原理 池化(Pooling):也称为欠采样或下采样。主要用于特征降维,在保持旋转、平移、伸缩等不变性的前提下,压缩数据和参数的数量,减小过拟合,同时提高模型的容错性。常用的有按均值池化(mean-pooling):更大地保留图像背景信息,按最大值池化(max-pooling):更多的保留纹理信息。
Tensorflow 文本分类实例
Tensorflow Conv2D和MaxPool2D原理 卷积神经网络(CNN)是指在所有网络中,至少优一层使用了卷积运算运算的神经网络,因此命名为卷积神经网络。那么什么是卷积呢?如果需要卷积一个二位图片,首先定义一个卷积核(kernel),即权重矩阵,它能表面每一次卷积那个方向的值更重要,然后逐步在二维输入数据上“扫描卷积”。当卷积核“滑动”的同时进行卷积:计算权重矩阵和扫描所得的数据矩阵的乘积,求和后得到一个像素输出。
Tensorflow 图像CNN分类解析 自从Yann Lecun最早将CNN用于手写数字识别后,卷积神经网络在多个方向持续火爆:如语音识别、人脸识别、通用物体识别等。与普通神经网络的最大区别为:卷积神经网络包含了由卷积层和池化层构成的特征抽取器。卷积层由卷积核做卷积运算替代全连接神经网络的矩阵计算。卷积核表示一组共享的权值,位于不同地方的权值可以模拟人眼对图像识别的局部感受野:一般认为人对外界的认知是从局部到全局的,而图像的空间联系也是局部的像素联系较为紧密,而距离较远的像素相关性则较弱。共享的权值还可以大大降低权值参数量。所以卷积核的局部感受野可以解决全连接计算将图像展开为向量丢失的空间信息;共享权值减少的参数可以提高训练效率和避免网络过拟合。
Tensorflow LSTM原理 LSTM是RNN的一种cell实现。那么什么是RNN呢?RNN是一种特殊的神经网络结构, 它是根据“人的认知是基于过往的经验和记忆”这一观点而提出的,它使网络对前面的内容的一种“记忆”功能。隐藏层中,一个序列当前的输出与前面的输出也有关,具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出,所有前面的结果可以影响后面的输出。所以它广泛运用于文本生成,机器翻译,机器写小说,语音识别等。

本作品采用知识共享署名 4.0 国际许可协议进行许可。