GitChat 做者:李嘉璇
原文:从零开始,如何阅读一篇人工智能论文,及构建论文与代码的实现
关注公众号:GitChat 技术杂谈,一本正经的讲技术html
首先讲解如何从零基础开始阅读一篇机器学习方向的论文,以及对待论文中的数学问题。随后,从一篇经典论文入手,讲解如何快速梳理和理解一个深度学习框架及模型。python
最近人工智能和机器学习方向的论文很是多,那么一个有工程背景、学术经验较少或者有必定经验的工程师,如何阅读一篇人工智能相关的论文呢?git
在刚开始的学术探索中,我倾向于全文精读,尤为是深度学习领域的经典论文,但发现这种方式花费时间太多,以致于挤压了个人真正目的——工程实现和工程结合。而且,由于想抓住的东西太多,反而没有抓住一篇文章的核心,致使很容易忘记,好比昨天读的文章就像喝了水同样忘掉了。github
我将和你们从两个方面探讨。算法
这里的从零开始,指的是咱们要从零了解这篇文章作了什么事情、使用了什么方法、获得什么结果,这样的方法和结果对我有没有什么借鉴。编程
而不是说,接触到一个全新的领域,从读论文开始入手。对于没有过接触的陌生领域。个人方法是,先看中文综述,中文博士论文,然后是英文综述。经过中文综述,能够首先了解这个领域的基本名词、实验经常使用方法。数组
不然直接从论文入手的话,做者站的高度和咱们的水平不一致,很容易想固然的理解或者根本看不下去。所以,在阅读这篇文章以前,对于这篇文章中涉及到的基础知识,对应中文基础都理解透彻。bash
这时,回归到从零开始理解这篇文章的状态。服务器
对一篇文章的阅读每每有3个递增的层次:网络
层次1. 读懂这篇文章的概要信息(5-10分钟)
认真读懂标题、摘要、简介(title, abstract, and introduction)。
只读各个部分和子部分(section and sub-section)的标题,跳过具体内容。
读懂结论和讨论(做者一般会在这里论述本研究的不足和缺失,为将来的研究提供建议,指明方向)。
浏览参考文献,记下哪些文献是你已经读过的。
所以,在第一层次事后,应该能回答出如下5个问题:
文章分类:关于实现方法的文章?对于已有系统的分析文章?对于研究理论的描述文章?
内容:有没有对应的相关paper?这篇文章是基于什么样的基础理论?(theoretical bases)
文章的假设(assumptions)是真的正确么?
贡献:这篇文章是在效果上(state of art)有了明显进步?仍是方法上有了创新?仍是完善了基础理论?
清晰度:是一篇描述清晰的文章么?
第一个层次完成你就能够以为是否要深刻第二个层次,它足够作你的某天想用到时的知识储备,而不是如今马上入手。
层次2. 抓住文章的内容,忽略文章细节(1个小时)
第二个层次须要认真读,抓住重点:
对图、表的含义以及他们支持的结论弄懂。
记下参考文献中你认为重要的未读文献,它能让你对这篇文章的背景有深入理解。
完成第2个层次,要达到知道文章用了哪些证据,如何证实了一个什么样的结论。
尤为在这个层次中,若是遇到读不懂(缘由有不少:公式太多、对术语不理解、对实验手段不熟悉、参考文献的文献过多)。说明咱们尚未和做者在一个基础上,建议先从几篇重要的参考文献入手,补充背景知识。
层次3. 深刻细节理解文章(5-6小时)
若是这个文章是你想应用到目前工程中的,则须要第3个层次。目标是可以在相同的假设条件下,重现(re-implement)论文。
同时,要注重论文在GitHub上的对应代码,跳到程序中能加速理解。
比较你重现的结果和原论文,就能真正理解一篇文章的创新点,以及它的隐含前提或假设。而且你能从重现过程当中获得一些你将来工做的方向。
作这三个层次的好处就是,可以让你对读一篇文章的时间有合理的估计,甚至能够根据时间和你的工做须要调整掌握一篇文章的深度。
这个在不少AI论文中很广泛,因此通常来说,在第一个层次中,不须要理解一个公式的全部步骤。尽可能的跳过公式,读文字描述,读实验结果,读结论。
随着你平时工做中数学的积累,在第二个层次中,你也许能直接经过看公式来真正理解做者的目的和步骤。
若是非要进入第三个层次,可能须要跟着文章作一些推导。可是实际上,若是有现成的代码实现,可让你从工程的角度更好的理解数学过程。
最后,建议你们用这种方式,尝试把这128篇论文中本身感兴趣的领域根据本身的须要,调整阅读层次地读完。(笔者刚刚读完啦,欢迎一块儿来交流哦)
下面结合TensorFlow的架构和体系设计论文《TensorFlow:
Large-Scale Machine Learning on Heterogeneous Distributed Systems》来说解如下两点:
####TensorFlow的编程模型和基本概念
结合不到20行代码讲解静态图模型。
TensorFlow的运行方式分以下4步:
加载数据及定义超参数;
构建网络;
训练模型;
评估模型和进行预测。
下面咱们以一个神经网络为例,讲解TensorFlow的运行方式。在这个例子中,咱们构造一个知足一元二次函数y = ax2+b的原始数据,而后构建一个最简单的神经网络,仅包含一个输入层、一个隐藏层和一个输出层。经过TensorFlow将隐藏层和输出层的weights和biases的值学习出来,看看随着训练次数的增长,损失值是否是不断在减少。
生成及加载数据
首先来生成输入数据。咱们假设最后要学习的方程为y = x2 − 0.5,咱们来构造知足这个方程的一堆x和y,同时加入一些不知足方程的噪声点。
import tensorflow as tf
import numpy as np
# 编造知足一元二次方程的函数
x_data = np.linspace(-1,1,300)[:, np.newaxis] # 为了使点更密一些,咱们构建了300个点,分布在-1到1区间,直接采用np生成等差数列的方法,并将结果为300个点的一维数组,转换为300×1的二维数组
noise = np.random.normal(0, 0.05, x_data.shape) # 加入一些噪声点,使它与x_data的维度一致,而且拟合为均值为0、方差为0.05的正态分布
y_data = np.square(x_data) - 0.5 + noise # y = x^2 – 0.5 + 噪声复制代码
接下来定义x和y的占位符来做为将要输入神经网络的变量:
xs = tf.placeholder(tf.float32, [None, 1])
ys = tf.placeholder(tf.float32, [None, 1])复制代码
构建网络模型
这里咱们须要构建一个隐藏层和一个输出层。做为神经网络中的层,输入参数应该有4个变量:输入数据、输入数据的维度、输出数据的维度和激活函数。每一层通过向量化(y = weights×x + biases)的处理,而且通过激活函数的非线性化处理后,最终获得输出数据。
下面来定义隐藏层和输出层,示例代码以下:
def add_layer(inputs, in_size, out_size, activation_function=None):
# 构建权重:in_size×out_size大小的矩阵
weights = tf.Variable(tf.random_normal([in_size, out_size]))
# 构建偏置:1×out_size的矩阵
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
# 矩阵相乘
Wx_plus_b = tf.matmul(inputs, weights) + biases
if activation_function is None:
outputs = Wx_plus_b
else:
outputs = activation_function(Wx_plus_b)
return outputs # 获得输出数据
# 构建隐藏层,假设隐藏层有10个神经元
h1 = add_layer(xs, 1, 20, activation_function=tf.nn.relu)
# 构建输出层,假设输出层和输入层同样,有1个神经元
prediction = add_layer(h1, 20, 1, activation_function=None)复制代码
接下来须要构建损失函数:计算输出层的预测值和真实值间的偏差,对两者差的平方求和再取平均,获得损失函数。运用梯度降低法,以0.1的效率最小化损失:
# 计算预测值和真实值间的偏差
loss = tf.reduce_mean(tf.reduce_sum(tf.square(ys - prediction),
reduction_indices=[1]))
train_step = tf.train.GradientDescentOptimizer(0.1).minimize(loss)复制代码
训练模型
咱们让TensorFlow训练1000次,每50次输出训练的损失值:
init = tf.global_variables_initializer() # 初始化全部变量
sess = tf.Session()
sess.run(init)
for i in range(1000): # 训练1000次
sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
if i % 50 == 0: # 每50次打印出一次损失值
print(sess.run(loss, feed_dict={xs: x_data, ys: y_data}))复制代码
####TensorFlow的基本实现
包括:设备、分布式运行机制、跨设备间通讯、梯度计算。
TensorFlow的分布式有两种模式,数据并行和模型并行,咱们最经常使用的就是数据并行。数据并行的原理很简单,如图所示。其中CPU主要负责梯度平均和参数更新,而GPU1和GPU2主要负责训练模型副本(model replica)。这里称做“模型副本”是由于它们都是基于训练样例的子集训练获得的,模型之间具备必定的独立性。
具体的训练步骤以下。
在GPU1和GPU2上分别定义模型网络结构。
对于单个GPU,分别从数据管道读取不一样的数据块,而后进行前向传播,计算出损失,再计算当前变量的梯度。
把全部GPU输出的梯度数据转移到CPU上,先进行梯度求平均操做,而后进行模型变量的更新。
重复第1步至第3步,直到模型变量收敛为止。
数据并行的目的主要是提升SGD的效率。例如,假如每次SGD的mini-batch大小是1000个样本,那么若是切成10份,每份100个,而后将模型复制10份,就能够在10个模型上同时计算。
可是,由于10个模型的计算速度多是不一致的,有的快有的慢,那么在CPU更新变量的时候,是应该等待这一mini-batch所有计算完成,而后求和取平均来更新呢,仍是让一部分先计算完的就先更新,后计算完的将前面的覆盖呢?
这就引出了同步更新和异步更新的问题。
分布式随机梯度降低法是指,模型参数能够分布式地存储在不一样的参数服务器上,工做节点能够并行地训练数据而且可以和参数服务器通讯获取模型参数。更新参数也分为同步和异步两种方式,即为异步随机梯度降低法(Async-SGD)和同步随机梯度降低法(Sync-SGD)。如图:
同步随机梯度降低法(也称同步更新、同步训练)的含义是在进行训练时,每一个节点上的工做任务须要读入共享参数,执行并行的梯度计算,同步须要等待全部工做节点把局部的梯度算好,而后将全部共享参数进行合并、累加,再一次性更新到模型的参数;下一个批次中,全部工做节点拿到模型更新后的参数再进行训练。
这种方案的优点是,每一个训练批次都考虑了全部工做节点的训练状况,损失降低比较稳定;劣势是,性能瓶颈在于最慢的工做节点上。在异构设备中,工做节点性能经常不一样,这个劣势很是明显。
异步随机梯度降低法(也称异步更新、异步训练)的含义是每一个工做节点上的任务独立计算局部梯度,并异步更新到模型的参数中,不须要执行协调和等待操做。
这种方案的优点优点是,性能不存在瓶颈;劣势是,每一个工做节点计算的梯度值发送回参数服务器会有参数更新的冲突,必定程度上会影响算法的收敛速度,在损失降低过程当中抖动较大。
同步更新和异步更新如何选择?有没有优化方式呢?
同步更新和异步更新的实现区别主要在于更新参数服务器的参数的策略。在数据量小,各个节点的计算能力比较均衡的状况下,推荐使用同步模式;在数据量很大,各个机器的计算性能良莠不齐的状况下,推荐使用异步模式。具体使用哪种还能够看实验结果,通常数据量足够大的状况下异步更新效果会更好。
下面展现将如何建立一个TensorFlow服务器集群,以及如何在该集群中分布式计算一个静态图。
TensorFlow分布式集群的全部节点执行的代码都是相同的。分布式任务代码具备固定的结构:
# 第1步:命令行参数解析,获取集群的信息ps_hosts和worker_hosts,
以及当前节点的角色信息job_name和task_index。例如:
tf.app.flags.DEFINE_string("ps_hosts", "", "Comma-separated list of hostname:port pairs")
tf.app.flags.DEFINE_string("worker_hosts", "", "Comma-separated list of hostname:port pairs")
tf.app.flags.DEFINE_string("job_name", "", "One of 'ps', 'worker'")
tf.app.flags.DEFINE_integer("task_index", 0, "Index of task within the job")
FLAGS = tf.app.flags.FLAGS
ps_hosts = FLAGS.ps_hosts.split(",")
worker_hosts = FLAGS.worker_hosts(",")
# 第2步:建立当前任务节点的服务器
cluster = tf.train.ClusterSpec({"ps": ps_hosts, "worker": worker_hosts})
server = tf.train.Server(cluster, job_name=FLAGS.job_name, task_index=FLAGS.task_index)
# 第3步:若是当前节点是参数服务器,则调用server.join()无休止等待;若是是工做节点,则执行第4步
if FLAGS.job_name == "ps":
server.join()
# 第4步:构建要训练的模型,构建计算图
elif FLAGS.job_name == "worker":
# build tensorflow graph model
# 第5步:建立tf.train.Supervisor来管理模型的训练过程
# 建立一个supervisor来监督训练过程
sv = tf.train.Supervisor(is_chief=(FLAGS.task_index == 0), logdir="/tmp/train_logs")
# supervisor负责会话初始化和从检查点恢复模型
sess = sv.prepare_or_wait_for_session(server.target)
# 开始循环,直到supervisor中止
while not sv.should_stop()
# 训练模型复制代码
采起上面的代码框架,对MNIST数据集进行分布式训练,代码见:
本次Chat的第二部分: 讲解如何将你手上的需求转化为论文的描述并实现出来。
以推荐系统为例:参考文章:
www.textkernel.com/building-la…
咱们以招聘的招聘推荐系统中知识库的构建为例,讲解在哪里以及如何引入NLP和知识图谱的方法。
(1)为何建设知识库?以下就是一个基于知识库的搜索:
也就是咱们但愿把对应的职位描述,结构化为知识图谱:
咱们知道知识图谱包括实体和实体关系,那么结合招聘的场景,实体库中就应该包含:职位库、职业库、简历库、实体词库。而实体关系可能有归属关系、层级关系、关联关系。
咱们来对职位描述作结构化的抽取,来设计实体关系的标签体系,以下:
具体如何来抽取呢?
寻找定位词和标点符号,切分红短句。
职位内容:
(营业员/学徒):负责
吧台,跟随师傅调制 饮料,切配果盘,小吃;待遇:
正式员工底薪3000- 3500元/月+奖金+五险一金,公司包吃住工做地点:
公司根据员工住所安排最近的上班地点
从短句中基于特征字/词定位核心内容
(营业员/学徒)
吧台,跟随师傅调制饮料,切配
果盘,小吃
正式员工底薪3000-3500
元/月+奖金+五
险一
金
公司根据员工住所安排最近
的上班地点
核心词抽取
营业员 学徒
吧台 调制饮料 切配果盘 小吃
底薪3000-3500元/月 奖金 五险一金
最近的上班地点
那么在这个过程当中,如何来寻找定位词呢?通常分为3步:
(1)定位词->种子词->定位词。例如:
(2)基于词性标注。例如:
对文本分词,进行词性标注, 查找里面的动词、数词、量词
,做为定位词
(3)基于语法。例如:
动词后面连续的名词、简称
词组联合共现频率高。动词+形容词,动词+副词的组合
关于词性标注,详见汉语词性标注集:
最终创建起一个招聘知识库:
最后,但愿你们可以多读论文,而且总结和温习读过的文章,结合GitHub上的开源实现,在TensorFlow上多多练习。在一个领域多的论文积累量,能发现不少存在的问题和机会。