深度学习在花椒直播的应用(Tensorflow 原理篇)

标签: dev | 发表时间:2019-07-05 00:00 | 作者:
出处:http://itindex.net/relian

殷雅俊,2018年北京邮电大学硕士毕业,自2017年下半年来一直在花椒实习,随后进入花椒直播智能工程组担任算法工程师,主要负责花椒直播个性化推荐算法研发,花椒图像识别算法研发等工作。

一、深度学习的应用

1.花椒直播的深度学习使用

  • 使用Spark进行数据清洗,构建用户画像和物品画像,挖掘数据特征,形成数据集存储在HDFS。

  • 使用TensorFlow作为深度学习计算框架,通过Hbox调度深度学习作业,使用集群分布式训练。训练模型使用TF Serving部署,封装成TF-Web对内提供预测接口,线上使用Go Server提供推荐服务。

2. TensorFlow和TensorSlow

TensorFlow是Google在2015年开源的深度学习框架,是目前最主流的深度学习计算框架。代码库本身有高达100万+的代码量,分为前端代码和后端代码。如此庞大的代码量造成了很多人不理解TensorFlow到底如何工作。

为此,Github上有个名为 TensorSlow的项目,使用纯python的方式重构了TensorFlow底层。该项目旨在帮助理解TensorFlow的工作机制而不在意效率,所以取名TensorSlow。本文就以TensorSlow项目为基础,向大家梳理一下TensorFlow底层到底干了哪些事,对理解深度学习框架底层原理大有裨益。

二、深度学习简介

深度学习是机器学习的重要分支,是为了研究深层神经网络的结构和有效训练而演化来的一系列方法。

神经网络

概念:常见的深度模型使用的是前馈神经网络(多层感知机),它使用一个映射函数

来定义模型,其中为输入,θ为模型参数。由于模型函数可以和一个有向无环图(DAG)等价,所以被称为网络。

隐藏层:映射函数通常是多层的复合函数,例如

输入x是输入层,中间函数对应于隐藏层,输出y是输出层。


代价函数:衡量模型函数和数据集之间的距离的函数(最大似然)。它是模型参数θ的函数,记为J(θ)。


梯度下降:选择模型参数能使代价函数J(θ)最小化的优化方法。以梯度下降为代表的一系列优化方法,通过向参数θ的负梯度方向迭代,来寻找最优的参数θ。


数量级:参数的数量通常有几十万到数百亿不等。


前向传播

当神经网络接收到输入层x后,经过网络不断向前流动,经过每一个隐藏层,最后传播到输出层y的过程,称为 前向传播。在监督学习中,通常输出层y还会进一步得到代价函数J(θ)。

反向传播

从代价函数J(θ)出发,经过网络向后流动,传播到每一个参数上,计算出J(θ)对该参数θ的梯度的过程,称为 方向传播。之后,可以通过梯度下降等优化方法,得到模型的最优参数。

三、 TensorFlow做了什么?

计算图(Computational Graph)

深度学习模型的落地离不开 计算图计算图可以认为是深度模型的描述语言,是神经网络计算的函数描述。计算图被定义为有向图(DAG),其中的节点对应于变量。通过计算图,可以方便的表达复杂计算。

上面的图例展示了一个简单的计算图,它对应于这么一个仿射变换:

其中x是输入节点,A,b 是模型参数。


节点(Vertex)

在计算图中,节点用来表示一个变量。节点的输入和输出称为 Tensor(它可以是标量,向量,矩阵或者更高维的)。根据输入输出的不同,节点可以分为以下三类:

Placeholder节点:整个计算图的输入节点。节点只有输出值,传递给它的子节点 consumers

   classplaceholder:      def__init__(self):      self.consumers = []      _default_graph.placeholders.append(self)


Variable节点:计算图中表示模型参数的节点。Variable节点拥有初始值 value,将输出传递给它的子节点 consumers


   classVariable:      def__init__(self, initial_value = None):      self.value = initial_value   self.consumers = []      _default_graph.variables.append(self)


Operation节点:使用Variable节点或Placeholder节点来组成简单的函数,可以通过复合Operation节点来建立复杂网络。它具有输入和输出,输入由父节点 input_nodes传递,输出传递到子节点 consumers中。而 compute方法需要继承,用来表述Operation节点所表示的函数计算。

   classOperation:      def__init__(self, input_nodes=[]):   self.input_nodes = input_nodes       
self.consumers = []
forinput_nodeininput_nodes: input_node.consumers.append(self)
_default_graph.operations.append(self)
defcompute(self): pass

Addition Operation的具体写法

   classadd(Operation):      def__init__(self, x, y):   super().__init__([x, y])       
defcompute(self, x_value, y_value): self.inputs = [x_value, y_value] returnx_value + y_value

图(Graph)

使用一个 Graph类来绑定计算图中的所有节点(opeartions、placeholders和varaibles)。当创建新的graph的时候,可以调用 as_default方法来设置为这张图的 _default_graph,这样不用显式地将节点绑定到图中。

   classGraph:      def__init__(self):   """Construct Graph"""   self.operations = []   self.placeholders = []   self.variables = []      defas_default(self):   global_default_graph   _default_graph = self

会话(Session)

完成了图定义,如何执行图?在TensorFlow中,通过定义Session实例,Client将计算图传递给后端,通过 Session.run方法传递给master执行。

计算图的输出是特定的Operation节点。而输出节点的计算依赖其他中间节点, 必须保证operations是按拓扑顺序执行的,计算节点o 之前,节点o 的所有输入节点已经完成计算比如,要计算z节点必须先计算出中间节点y。这里通过反向的后序遍历来完成拓扑排序。类似的, Session可以这么定义:

   classSession:      defrun(self, operation, feed_dict = {}):   """Computes the output of an operation   """   ...       

deftraverse_postorder(operation): nodes_postorder = [] defrecurse(node): ifisinstance(node, Operation): forinput_nodeinnode.input_nodes: recurse(input_node) nodes_postorder.append(node)
recurse(operation) returnnodes_postorder

session.run方法首先对节点进行拓扑排序,并根据排序结果依次计算节点输出,完成图的执行。

前向传递算法

再回顾这张计算图:

它对应的放射变换可以用线性变换的方式写出,可以通过之前定义的代码完成前向传递的计算。

   # Create a new graph   Graph().as_default()       
# Create variables A = Variable([[1, 0], [0, -1]]) b = Variable([1, 1])
# Create placeholder x = placeholder()
# Create hidden node y y = matmul(A, x)
# Create output node z z = add(y, b)
session = Session() output = session.run(z, {x: [1, 2]}) print(output)

可以得到结果:

   [2-1]

构建损失函数

深度学习中,分类任务通常使用交叉熵作为损失函数,它的公式如下:

其中c是数据集中的真实分类标签。损失函数的Operation节点J由许多基础 Operation来构建。将输入和参数向量化,并添加节点J,改写后的计算图如下:

   # Create a new graph    
Graph().as_default()

X = placeholder()
c = placeholder()

W = Variable([[1, -1],
[1, -1]])
b = Variable([0,0])
p = softmax( add(matmul(X, W), b) )

# Cross-entropy loss
J = negative(reduce_sum(reduce_sum(multiply(c, log(p)), axis=1)))

session = Session()
print(session.run(J, {
X: np.concatenate((blue_points, red_points)),
c:
[[1,0]] * len(blue_points)
+ [[0,1]] * len(red_points)}))

梯度下降优化算法

通过使用梯度下降方法来最小化代价函数,流程如下:

  1. 模型参数W和b设置随机初始值。

  2. 计算代价函数J对W和b的梯度。

  3. 分别在其负梯度的方向上下降一小步(使用 learning_rate控制步长大小)。

  4. 回到步骤2,继续执行。

   classGradientDescentOptimizer:      def__init__(self, learning_rate):   self.learning_rate = learning_rate      defminimize(self, loss):   learning_rate =self.learning_rate      classMinimizationOperation(Operation):   defcompute(self):   # Compute gradients   grad_table = compute_gradients(loss)      fornodeingrad_table:   iftype(node) ==Variable:   grad = grad_table[node]   node.value -= learning_rate * grad      returnMinimizationOperation()

compute_gradients函数通过反向传递算法计算梯度, grad_table字典存放了代价函数J节点对计算图中所有variable节点的当前梯度。

反向传递算法

链式法则是计算梯度的基本法则。下图和公式显示了e对a的梯度计算:

反向传递的算法用链式法则来计算节点n的梯度:

    1. J节点自身的梯度为1。

    2. 对任意节点z的所有子节点 consumer,计算子节点的梯度。

    3. 将子节点的梯度乘以节点z本身的梯度得到J节点对任意节点z的梯度。


按照这种方式,直到反向传递到节点n。如果节点J经过多个路径方向传递到该节点,那么对该节点来自不同路径的梯度求和。


compute_gradients方法从J节点开始,使用 BFS的方式执行上面的流程。它会先计算所有子节点的梯度,然后计算当前节点的梯度,直到传递到输出节点。

   fromqueueimportQueue       
defcompute_gradients(loss):
# grad_table[node] will contain the gradient of the loss w.r.t. the node's output ... returngrad_table


节点自身的梯度

   Operation的自身梯度通过预先定义,使用 RegisterGradient注册到全局变量 _gradient_registry中。以 sigmoid函数为例:

   @RegisterGradient("sigmoid")   def_sigmoid_gradient(op, grad):      sigmoid = op.output       
returngrad * sigmoid * (1-sigmoid)


四、实践:搭建MLP

  MLP是经典的神经网络,在深度学习中广泛使用。MLP由多个节点层组成,每一层全连接到下一层。除了输入节点,每个节点都是一个带有非线性激活函数的神经元。下面构建了一个含有3个隐层的MLP来进行分类任务:

   # Create a new graph   ts.Graph().as_default()       
# Create training input placeholder X = ts.placeholder()
# Create placeholder for the training classes c = ts.placeholder()
# Build a hidden layer W_hidden1 = ts.Variable(np.random.randn(2, 4)) b_hidden1 = ts.Variable(np.random.randn(4)) p_hidden1 = ts.sigmoid(ts.add(ts.matmul(X, W_hidden1), b_hidden1))
# Build a hidden layer W_hidden2 = ts.Variable(np.random.randn(4, 8)) b_hidden2 = ts.Variable(np.random.randn(8)) p_hidden2 = ts.sigmoid(ts.add(ts.matmul(p_hidden1, W_hidden2), b_hidden2))
# Build a hidden layer W_hidden3 = ts.Variable(np.random.randn(8, 2)) b_hidden3 = ts.Variable(np.random.randn(2)) p_hidden3 = ts.sigmoid(ts.add(ts.matmul(p_hidden2, W_hidden3), b_hidden3))
# Build the output layer W_output = ts.Variable(np.random.randn(2, 2)) b_output = ts.Variable(np.random.randn(2)) p_output = ts.softmax(ts.add(ts.matmul(p_hidden3, W_output), b_output))
# Build cross-entropy loss J = ts.negative(ts.reduce_sum(ts.reduce_sum(ts.multiply(c, ts.log(p_output)), axis=1)))
# Build minimization op minimization_op = ts.train.GradientDescentOptimizer(learning_rate=0.03).minimize(J)
# Build placeholder inputs feed_dict = { X: np.concatenate((blue_points, red_points)), c: [[1, 0]] * len(blue_points) + [[0, 1]] * len(red_points)
}
# Create session session = ts.Session()
# Perform 100 gradient descent steps for step in range(2000): J_value = session.run(J, feed_dict) if step % 100 == 0: print("Step:", step," Loss:", J_value)     session.run(minimization_op, feed_dict)

通过可视化决策边界,可以发现模型学习到了比较复杂的非线性关系。

五、联系TensorFlow

  1. TensorSlow简单深刻的展示了深度学习框架底层的一般原理,加深理解生产使用的TensorFlow。

  2. TensorFlow后端使用C++高效实现。

  3. TensroFlow优化了算法过程,比如公共表达式消除,常量折叠。

  4. TensorFlow支持分布式,支持GPU等硬件设备。

  5. TensorFlow提供生产化的一系列工具。

六、主要参考资料

  1. https://colah.github.io/posts/2015-08-Backprop/

  2. www.cs.columbia.edu/~mcollins/ff2.pdf

  3. http://www.cs.cornell.edu/courses/cs5740/2017sp/lectures/04-nn-compgraph.pdf

  4. 《TensorFlow 内核剖析》刘光聪

  5. 《深度学习》Ian Goodfellow, Yoshua Bengio

相关 [深度学习 花椒 直播] 推荐:

深度学习在花椒直播的应用(Tensorflow 原理篇)

- - IT瘾-dev
殷雅俊,2018年北京邮电大学硕士毕业,自2017年下半年来一直在花椒实习,随后进入花椒直播智能工程组担任算法工程师,主要负责花椒直播个性化推荐算法研发,花椒图像识别算法研发等工作. 1.花椒直播的深度学习使用. 使用Spark进行数据清洗,构建用户画像和物品画像,挖掘数据特征,形成数据集存储在HDFS.

利用深度学习解决直播支付风控

- - 五四陈科学院
以下内容由 [五四陈科学院]提供. 在直播软件中,典型的过程是A用户充值,送花给B用户,B用户提现. 正是有这样一条变现的道路,无数盗刷、退款、36技术的黑产人盯上了直播,报道见到映客的损失一度到了300万人民币(本文价值至少300万了:P). 外链 https://www.douban.com/group/topic/89441680/.

花椒直播 Kong 应用实践

- - DockOne.io
Kong 是面向现代架构(混合云,混合组织)的下一代 API 网关平台,具有云原生、高性能,易用、可扩展等特性. 适用于 API Gateway,Kubernetes Ingress,Service Mesh Sidecar 等场景. 云原生:与平台无关,Kong 可以从裸机运行到 Kubernetes.

为了解决弹幕喷子,直播平台Twitch研发了一个深度学习系统

- - TECH2IPO/创见
从电子邮件到论坛,从论坛到微博,再从微博到现在的视频直播. 当人们在网上发言的速度越来越接近口语的时候,也就给网络喷子带来了越来越多的便利. 虽然从人类学会说到现在,还没有什么办法能阻止人类之间互相对骂. 但网络空间不比线下,一个人用嘴骂人只能被少数人听到,而在网上却可能影响几千人几万人. 在最新一季的南方公园中,甚至认为网络喷子是川普当选和第三次世界大战以及人类登上火星的主要原因.

深度学习二三事

- - FreeBuf.COM | 关注黑客与极客
我知道博客标题中使用否定词很奇怪,但是前几天有一波讨论正好相应于我正在思考的一些问题. 这一切开始于 Jeff Leek 发表的 一篇有关说明在小样本范围内使用深度学习的文章. 要言之,他认为当样本较小时(生物领域中属于常见现象),带有少许参数的线性模型甚至比带有少量层和隐藏单元的深度网络性能更优.

关于深度学习——Deep Learning

- - 互联网旁观者
转载自: http://blog.csdn.net/abcjennifer/article/details/7826917. Deep Learning是机器学习中一个非常接近AI的领域,其动机在于建立、模拟人脑进行分析学习的神经网络,最近研究了机器学习中一些深度学习的相关知识,本文给出一些很有用的资料和心得.

深度学习的本质探究??

- - ITeye博客
原创 2016-10-07 朱洁 . 标志型事件,阿尔法围棋(AlphaGo)战胜李世石. alphago是一款围棋人工智能程序,由谷歌(Google)旗下DeepMind公司的戴维·西尔弗、艾佳·黄和戴密斯·哈萨比斯与他们的团队开发,这个程序利用“价值网络”去计算局面,用“策略网络”去选择下子. 2015年10月阿尔法围棋以5:0完胜欧洲围棋冠军、职业二段选手樊麾;2016年3月对战世界围棋冠军、职业九段选手李世石,并以4:1的总比分获胜.

深度学习利器:TensorFlow实战

- - 孟飞阳的博客
深度学习及TensorFlow简介. 深度学习目前已经被应用到图像识别,语音识别,自然语言处理,机器翻译等场景并取得了很好的行业应用效果. 至今已有数种深度学习框架,如TensorFlow、Caffe、Theano、Torch、MXNet,这些框架都能够支持深度神经网络、卷积神经网络、深度信念网络和递归神经网络等模型.

深度学习三大框架对比

- -
| 导语 Science is NOT a battle, it is a collaboration. 人工智能的浪潮正席卷全球,诸多词汇时刻萦绕在我们的耳边,如人工智能,机器学习,深度学习等. “人工智能”的概念早在1956年就被提出,顾名思义用计算机来构造复杂的,拥有与人类智慧同样本质特性的机器.

Nimbus: Hulu的深度学习平台

- - 董的博客
Hulu是美国领先的互联网专业视频服务平台,目前在美国拥有超过2500万付费用户. Hulu的目标是帮助用户在任意时刻、任何地点、以任何方式查找并欣赏到高质量的电视剧、电影和电视直播. 实现这一目标离不开各个团队的努力,而AI在其中扮演者越来越重要的角色. 在Hulu, 我们拥有诸多的researcher团队,如广告团队,推荐团队,视频理解团队等ji等.