返回 登录
7

如何用C++实现自己的Tensorflow

摘要:TensorFlow是由谷歌基于DistBelief进行研发的第二代人工智能学习系统,其命名来源于本身的运行原理,它完全开源,作者通过自己的一个小项目,阐述了如何用C++实现自己的TensorFlow,这篇文章看起来可能会有点晦涩,你需要对相关知识有所了解。以下是译文。

在我们开始之前,以下是代码:

Branch with Eigen backend
Branch that only supports scalars
我和Minh Le一起做了这个项目。

为什么?

如果你是CS专业的人员,可能听过这句“不要使自己陷入_”的话无数次。CS有加密、标准库、解析器等等。我觉得现在还应该包含ML库。

不管事实如何,它仍然是一个值得学习的惊人的教训。人们现在认为TensorFlow和类似的库是理所当然的;把它们当成是一个黑盒子,让其运行。没有多少人知道后台发生了什么。这真是一个非凸的优化问题!不要停止搅拌那堆东西,直到它看起来合适为止(结合下图及机器学习系统知识去理解这句话)。

Tensorflow

TensorFlow是由Google开源的一个深度学习库。在TensorFlow的内核,有一个大的组件,将操作串在一起,行成一个叫做 运算符图 的东西。这个运算符图是一个有向图G=(V,E),在某些节点u1,u2,…,un,v∈V 和 e1,e2,…,en∈E,ei=(ui,v) 存在某些运算符将u1,…,un 映射到 v。

例如,如果我们有x + y = z,那么 (x,z),(y,z)∈E 。

这对于评估算术表达式非常有用。我们可以通过寻找运算符图中的 sinks来得到结果。 Sinks是诸如 v∈V,∄e=(v,u)这样的顶点。换句话说,这些顶点没有到其它顶点的有向边。同样的, sources是v∈V,∄e=(u,v)。

对我们来说, 总是把值放在sources,值会传播到Sinks。

反向模式求导

如果认为我的解释不够好,这里有一些幻灯片。

求导是TensorFlow所需的许多模型的核心要求,因为需要它来运行 梯度下降算法。每个高中毕业的人都知道什么是求导; 它只是获取函数的导数,如果函数是由基本函数组成的复杂组合,那么就做 链式法则。

超级简单的概述

如果有一个这样的函数:

f(x,y) = x * y

那么关于X的求导将产生:

df(x,y)dx=y
关于Y的求导将产生:

df(x,y)dy=x
另外一个例子:

f(x1,x2,…,xn)=f(x)=xTx
这个导数是:

df(x)dxi=2xi
所以梯度就是:

∇xf(x)=2x
链式法则,譬如应用于复杂的函数f(g(h(x))):

df(g(h(x)))dx=df(g(h(x)))dg(h(x))dg(h(x))dh(x)dh(x)x
5分钟内反向模式

现在记住运算符图的DAG结构,以及上一个例子中的链式法则。如果要评估,我们可以看到:

x -> h -> g -> f

作为图表。会给出答案f。但是,我们也可以采取反向求解:

dx <- dh <- dg <- df

这看起来像链式法则!需要将导数相乘在一起,以获得最终结果。

下图是一个运算符图的例子:

所以这基本上退化成图遍历问题。 有谁发觉拓扑排序和DFS / BFS吗?

所以要支持双向拓扑排序的话,需要包含一组父节点和一组子节点,Sinks是另一个方向的Sources, 反之亦然。

实施

在开学之前,Minh Le和我开始设计这个项目。我们决定使用Eigen 库后台进行线性代数运算。它们有一个称为MatrixXd的矩阵类。我们在这里使用它。

每个变量节点由var类表示:

class var {
// Forward declaration
struct impl;

public:
// For initialization of new vars by ptr
var(std::shared_ptr);

var(double);
var(const MatrixXd&);
var(op_type, const std::vector&);

// Access/Modify the current node value
MatrixXd getValue() const;
void setValue(const MatrixXd&);
op_type getOp() const;
void setOp(op_type);

// Access internals (no modify)
std::vector& getChildren() const;
std::vector getParents() const;

private:
// PImpl idiom requires forward declaration of the class:
std::shared_ptr pimpl;
};

struct var::impl{
public:
impl(const MatrixXd&);
impl(op_type, const std::vector&);
MatrixXd val;
op_type op;
std::vector children;
std::vector

评论