北京 | 深度学习与人工智能研修

12月23-24日

再设经典课程 重温深度学习阅读全文>

正文共6615个字,33张图,预计阅读时间:17分钟。

前一章节中介绍的线性回归是一种监督学习算法,我们使用数据与输出值(标签)来建立模型拟合它们。但是我们并不总是有已经打标签的数据,却仍然想去分析它们。这种情况下,我们可以使用无监督的算法如聚类。因为聚类算法是一种很好的方法来对数据进行初步分析,所以它被广泛使用。

本章中,会讲解K-means聚类算法。该算法广泛用来自动将数据分类到相关子集合中,每个子集合中的元素都要比其它集合中的元素更相似。此算法中,我们没有任何目标或结果来预测评估。

本章中依然会介绍TensorFlow的使用,并介绍基础数据结构tensor的更多细节。本章开头介绍tensor的数据类型与分析可在该数据结构上执行的运算变换。接下来展示使用tensor来实现的K-means算法。

基础数据结构—tensor

TensorFlow使用基础数据结构---tensor来表示所有数据。一个tensor可以看成是一个拥用静态数据类型动态大小且多维的数组,它可以从布尔或string转换成数值类型。下表是一些主要类型及在Python中对对应的类型:

另外,每个tensor都有一个秩,也是tensor维度的数量。例如,下面的tensor的秩为2:

t = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

tensor可以拥有任意秩。秩为2的tensor经常被看作矩阵,秩为1的tensor会被看作vector,秩为0的被看作标量值。

TensorFlow文档中使用三种不同称谓来描述tensor的维度:Shape,Rank,Dimension。下面的表展示了它们三者之间的关系,

TensorFlow提供的一系列操作来计算这些tensor,接下来我们会讨论下表中的一些操作。

通过本章,我们会继续讨论更多的细节。在Tensorflow的官方网站上能找到更多的操作列表及每一个操作的细节。

举个例子,假如你想扩展一个2*2000(2D tensor)为立方体(3D tensor)。可以使用tf.expand_dims函数,它可以向tensor中插入一个维度。

tf.expand_dims会向tensor中插入一个维度,插入位置就是参数代表的位置(维度从0开始)。

以可视化来展示的话,上面的转换过程如下图所示:

正如你所看到的,我们得到一个3D tensor,但根据函数参数我们无法判断新维度D0的大小。

如果调用get_shape()来获得tensor的shape,可以看到D0没有大小:

print  expanded_vectors.get_shape()

显示的结果如下:

TensorShape([Dimension(1),Dimension(2000), Dimension(2)])

本章的稍后,我们可以看到,由于TensorFlow的shape传递特性,很多tensor的数学运算函数(正如第一章中提到的)可以自已发现未确定大小维度的大小,并将该值赋给它。

TensorFlow中的数据存储

Tensorflow程序中主要有三种方式来获取数据:

1、从数据文件

2、以常数与变量预加载

3、Python代码提供的数据

下面简要描述这三种方式:

1、数据文件

通常,原始数据从数据文件中下载。这个过程并不复杂,建议读者去TensorFlow官方网站查看如何从不同类型文件中下载数据的细节。你也可以查看input_data.py代码(可以从github上下载),它会从文件中加载MNIST数据(下一章中使用该数据)。

2、变量与常数

当提到小的数据集时,数据可提前加载到内存中;正如之前例子中看到的,有两种基本方式来创建它们:

通过constant()来创建常数

通过Variable()来创建变量

TensorFlow提供了不同的操作来创建常数。下表中对最重要的几个操作进行了总结:

用TensorFlow训练模型的过程中,参数以变量的形式保存在内存中。当变量创建后,可以将其作为初始值(可能是一个常数或随机值)给一个tensor,该tensor可做为参数传给一个函数。Tensorflow提供了一系列操作来产生不同分布的随机tensor:

一个重要的细节是所有这些操作都需要一个确定shape的tensor作为参数,返回的变量拥有同样的shape。总而言之,变量拥有一个固定的shape,但如有需要,Tensorflow提供了reshape的机制。

当创建变量后,它们必须在图创建完之后且调用run()函数之前显示初始化。可以通过调用tf.initialize_all_variables()来进行初始化。在训练过程中与训练完成后,可通过tf.train.Saver()类来将变量保存到磁盘中,该类的相关细节超过了本书的讨论范围。

3、Python代码提供数据

最后,在程序执行过程中,我们可通过叫做“符号变量”或placeholder来操作数据。Placeholder()的调用,包含了元素类型与tensor的shape为参数,还有一个可选参数name。

在Python代码中调用Session.run()或Tensro.eval()的同时,这个tensor与feed_dict参数中指定的数据相关联。第一章中的代码如下:

代码的最后一行中调用sess.run()时,我们通过feed_dict参数给两个tensor赋值。

通过简短分析tensor,希望从现在开始读者阅读接下来章节的代码时,没有任何困难。

K-means算法

K-means是一种用来解决聚类问题的无监督算法。该算法依据一个简单容易的方式来对数据集分成一定数目(假设K个类别)的类别。一个类别中的数据点是相似的,不同类别中的数据点是各种各样的,也就是说同一子类别中的元素比其它子类别中的元素更相似。

算法的结果是生成K个点集合,叫做centroids,这是不同组的焦点,标签代表了集合中的点,k个聚类都有自己的tag。一个类中的所有点离centroid要比其它任意centroid要近。

如果我们想直接最小化error function,则生成聚类是非常耗计算的(也就是NP问题);一些算法通过启发式方法来达到局部快速收敛。更通用的算法使用迭代优化技术,仅覆盖几次迭代。

一般来说,这种技术主要有三步:

1.初始化(step 0):初始化K个centroid的集合

2.分配(step 1):将每一个对象赋给最近的组

3.更新(step 2):计算每个新组的新centroid

有多种方法来初始化K个centroid。其中之一就是在数据集中随机选取K个对像并将它们看作centroid;接下来的例子中我们会使用这种方法。

分配(step 1)与更新(step 2)在循环中是可选的,循环直到算法开始收敛,举例来说就是分配点到组后,就不再发生变化。

因为这是一个启发式算法,无法保证算法收敛到最佳目标,结果依赖于初始集合。因为算法通常运行很快,可用不同的初始centroid来多次执行该算法,然后评估结果。

开始在Tensorflow中编码实现K-means算法前,建议先生成一些数据用来进行实验。有一种简单的方式,在2D空间中随机生成2000个点,它们服从两个正态分布,我们可画出空间分布来更好的理解结果。示例代码如下:

如我们在之前章节中所做的,我们可使用Python图库来用图表画出这些点。建议使用matplotlib,这次我们使用基于matplotlib的可视化库Seaborn,操作数据用库pandas,该库能运算更复杂的数据结构。

如果你没有安装这些库,在继续下一步前可能过pip来安装它们。

建议使用如下代码来显示我们随机生成的点:

这段代码生成两维空间下的点图如下:

TensorFlow中实现的K-means算法来对上面生成的点进行分组,假如四个类,求例代码如下(基于Shawn Simister在他博客中发表的模型):

建议读者用如下代码来检查assignment_values tensor中的结果,它会生成一张分布图:

上面代码执行后生成的分布图如下:

新组

也许读者会对上面的K-means代码感到困惑。接下来详细分析每一行代码,我们会特别关注相关tensor的变化及它们在程序中如何运算。

首先需要做的是把我们的数据移到tensor中。我们将所有随机生成的点保存到常量tensor中:

vectors=tf.constant(conjunto_vectors)

根据之前讲解的算法,我们在开始就要决定初始centroids。一种方法就是从输入数据中随机选择K个对像。下面的代码就能达到这个目的,随机排列这些点并选择前K个点作为centroids:

这K个点保存在一个2D tensor中。可通过调用tf.Tensor.get_shape()获得这些tensor的shape:

我们可以看到,vectors是一个数组,D0包含了2000个positions,D1包含了每一个点的坐标x,y。centroids是包含四个元素的矩阵,D0代表每一个形心的位置,D1代表点的坐标x,y。

接下来,算法进入一个循环。第一步就是为每一个点,根据平方欧氏距离(只能被用来比较距离)计算最近的centroid。

为计算该值,需要使用tf.sub(vectors, centroides)。虽然这两个相减的tensor都是2维的,但在第1维度上有不同的大小(2000VS 4 D0中),实际上,这也代表了不同的意义。

为解决这个问题,我们需要使用之前提到的函数,如tf.expand_dims用来在两个tensor中插入一个维度。目的是把这两个tensor从2维转换成3维,使得大小匹配可以进行减法:

tf.expand_dims在每一个tensor中插入一个维度,在vector的tensor中第一维度(D0)插入,在centroides tensor中第二维度(D1)插入。从图片来看,扩展后的tensor中各维度有了同样的含义:

看上去这个问题解决了,实际上,如果仔细来看,两个tensor中都有维度不能确定大小。通过调用get_shape()可以看到:

输出如下:

1代表没有赋予大小。

之前就已经说明TensorFlow允许传递,所以tf.sub函数能够自己发现如何在两个tensor间进行减法。

直观地来看上面的图,两个tensor的形状是匹配的,而且在指定维度上也有相同的大小。这些数学运算就像发生在D2维度上那样。然而,D0中只有expanded_centroides有固定大小。

在这种情况下,TensorFlow假设expanded_vectors拥有同样的大小,如果我们想执行元素对元素的减法。

对于expended_centroides的D1同样如此,Tensorflow推断出expanded_vectors的D1大小。

在分配步骤(step 1),算法可实现为如下四行代码,用来计算平方欧氏距离:

如果我们察看tensor的形状,diff, sqr,distances and assignments的大小分别为:

tf.sub返回一个tensor,包含了两个tensor相减的值(vector表明D1的大小,centroid表明D0的大小。D2中表明了x,y)。sqr tensor包含了它们的平方。在distance tensor中,已经减少了一个维度,减少的维度在tf.reduce_sum函数中表明。

通过这个例子来表明TensorFlow提供了一些操作来进行运算,就像tf.reduce_sum来减少tensor的维度。下面的表中总结了一些很重要的操作:

最后,通过tf.argmin来赋值,它返回tensor某一维度中的最小值索引(此处为D0,代表centroid)。同样也有tf.argmax操作:

实际上,上面的四行代码可以总结成一行代码中:

assignments=tf.argmin(tf.reduce_sum(tf.square(tf.sub(expanded_vectors,expanded_centroides)),2),0)

不管如何,定义结点与执行内部图的那些内部tensor与操作都跟我们之前提到一样。

计算新形心

一旦在迭代中创建了新组,需要记住算法的新步骤中包含了计算组的新形心。正如我们之前看到的代码;

means=tf.concat(0,[tf.reduce_mean(tf.gather(vectors,tf.reshape(tf.where(tf.equal(assignments,c)),[1,-1])),reduction_indices=[1])forcinxrange(k)])

这段代码中,means tensor是连接k个tensor的结果,这k个tensor都是由那些平均值属于每一个k类的点组成的。

接下来,会详细分析计算每一个点属于哪个cluster的代码:

A.通过equal获得一个布尔tensor(Dimension(2000)),true代表了assignment tensor与K cluster相匹配的位置,同时我们也计算了点的平均值

B.where根据传进来的布尔tensor中元素值为true的位置来构造一个tensor(Dimension(1) x Dimension(2000))

C.reshape根据vectors tensor内部那些属于c cluster的点的索引来构建一个tensor(Dimension(2000) x Dimension(1))

D.gather从c cluster中收集所有点的坐标并创建tensor(Dimension(1) x Dimension(2000))

E.reduce_mean则是根据c cluster中所有点的平均值来创建tensor(Dimension(1) x Dimension(2))

如果读者想了解代码的更多细节,可访问TensorFlow api页面,通过解说例子来了解所有操作的细节。

图执行

最后,我们来描述循环相关的代码部分与用新计算的平均值tensor来更新centroide的部分。

当run()方法被调用时,我们要在更新的centroids值在下轮迭代使用前,先创建一个赋值操作符用means tensor值更新centroids值:

update_centroides=tf.assign(centroides,means)

我们同样需要在运行图之前创建一个操作来初始化所有变量:

init_op=tf.initialize_all_variables()

到现在为止,所有都准备就绪,可以开始运行图:

sess=tf.Session()

sess.run(init_op)

for step in xrange(num_steps):

_,centroid_values,assignment_values=sess.run([update_centroides,centroides,assignments])

在这段代码中,每次迭代时,centroids与为每个点新分配的cluster都会被更新。

代码中指定了三个操作,同时需要查看run()的执行状态,并按顺序来运行这三个操作。因为有三个值需要查找,sess.run()返回了三个numpy数组,每个数组分别包含了训练过程中相应的内容。

因为update_centroides这个操作的结果并不需要返回,在返回的turple中相应元素内容为空,用“_”表示不接收该参数。

对于另外两个值,centroids与将点赋给每一个cluster,一旦完成所有迭代计算后,我们可以将这两个变量打印在屏幕上。

使用简单的打印命令,输出如下:

希望读者的电脑上也有接近的值,这说明读者已经成功执行了本章中的相关代码。

建议读者在继续进行下一步之前,先尝试修改某些值。例如num_points,尤其聚类的数量,然后通过生成结果图来查看assignment_values如何变化。

为了测试本章中的代码,可通过github下载本代码。包含本章代码的文件为Kmeans.py,

本章中已经了解了一些TensorFlow的知识,尤其通过TensorFlow中实现一个聚类算法K-means来学习基础数据结构tensor。

了解了tensor后,我们在下章中可以一步步建立一个单层神经网络。

原文链接:http://www.jianshu.com/p/cf44ecf64efe

查阅更为简洁方便的分类文章以及最新的课程、产品信息,请移步至全新呈现的“LeadAI学院官网”:

www.leadai.org

请关注人工智能LeadAI公众号,查看更多专业文章

大家都在看

LSTM模型在问答系统中的应用

基于TensorFlow的神经网络解决用户流失概览问题

最全常见算法工程师面试题目整理(一)

最全常见算法工程师面试题目整理(二)

TensorFlow从1到2 | 第三章 深度学习革命的开端:卷积神经网络

装饰器 | Python高级编程

今天不如来复习下Python基础

点击“阅读原文”直接打开报名链接

译文 | 与TensorFlow的第一次接触 第三章:聚类相关推荐

  1. 译文 | 与TensorFlow的第一次接触 第六章:并发

    北京 | 深度学习与人工智能研修 12月23-24日 再设经典课程 重温深度学习 阅读全文 > 正文共1434个字,21张图,预计阅读时间:4分钟. 第一版TensorFlow第一版发布于201 ...

  2. 译文 | 与TensorFlow的第一次接触 第五章:多层神经网络

    北京 深度学习与人工智能研修12月23-24日 再设经典课程 重温深度学习阅读全文> 正文共5270个字,15张图,预计阅读时间14分钟. 本章中,我们继续使用之前章节中的MNIST数字识别问题 ...

  3. 译文 | 与TensorFlow的第一次接触 第四章:单层神经网络

    北京 | 深度学习与人工智能研修 12月23-24日 再设经典课程 重温深度学习阅读全文> 正文共7865个字,27张图,预计阅读时间:20分钟. 在前言中,已经提到经常使用深度学习的领域就是模 ...

  4. 译文 | 与TensorFlow的第一次接触(一)

    北京 | 深度学习与人工智能研修 12月23-24日 再设经典课程 重温深度学习阅读全文> 正文共8444个字,8张图,预计阅读时间22分钟. 目录 译者序 前言 序 实践练习 1.Tensor ...

  5. 译文 | 与TensorFlow的第一次接触第二篇:线性回归

    北京 | 深度学习与人工智能研修 12月23-24日 再设经典课程 重温深度学习阅读全文> 正文共3655个字,21张图,预计阅读时间:10分钟. 本章中,将会利用TensorFlow实现一个简 ...

  6. Win32学习笔记 第三章 HelloWin 选择自 villager 的 Blog

    Win32学习笔记 作者: 姜学哲(netsail0@163.net) 教材: Windows程序设计(第五版)北京大学出版社  [美]Charles Petzold 著  北京博彦科技发展有限公司 ...

  7. vue中常碰见的坑_Vue 与 Vuex 的第一次接触遇到的坑

    在 Vue.js 的项目中,如果项目结构简单, 父子组件之间的数据传递可以使用  props 或者 $emit 等方式 但是如果是大型项目,很多时候都需要在子组件之间传递数据,使用之前的方式就不太方便 ...

  8. Project Pacific的第一次接触(转)

    这是己亥年的最后一篇公众号更新,想谈谈自己与VMware Pacific产品的第一次接触,提供一些配置的参考,感兴趣的朋友们可以一起对照着在自己的环境中进行模拟. 首先我们来看几张演示用例: 这可能是 ...

  9. 百度地图API的第一次接触

    因为项目的需求,第一次接触了百度API. 第一步:引用百度地图API的脚本 如果在局域网环境中,要把地图文件和js文件都要下载下来 <script type="text/javascr ...

最新文章

  1. python入门经典例题-Python入门经典练习题
  2. 鸟哥的Linux私房菜(基础篇)- 第七章、Linux 文件与目录管理
  3. 那些关于区块链革命的事情
  4. 面向对象C语言编程--抽象数据类型-AbstractDataTypes
  5. 【转】jquery 注册事件的方法
  6. header的安全配置指南
  7. google摄像头查找办法
  8. 95-260-058-源码-检查点-CheckpointBarrierHandler
  9. java的Junit单元测试
  10. AcWing 1270. 数列区间最大值(RMQ问题)
  11. python文件读写到list_Python文件读写
  12. python去除列表指定位置元素_Python实现判断并移除列表指定位置元素的方法
  13. c++ 队列_RabbitMQ的死信与延迟队列,你真的会用吗?
  14. 快播之父卷土重来 发布升级版快播,首个区块链视频Bliface
  15. ubuntu无法定位软件包的问题
  16. 内网代理流量:Socks5协议原理分析和编程
  17. Tensorflow nmt的整体结构
  18. 听说你在做斗鱼APP?
  19. Mac安装、配置Maven
  20. http-server

热门文章

  1. php字符串以符号截取,PHP按符号截取字符串的指定部分的实现方法
  2. nginx 判断手机端跳转_Nginx系列:配置跳转的常用方式
  3. 【windows】windows允许 ICMP协议(允许ping)
  4. autopep8_Autopep8的使用
  5. android 判断当前窗口,Android判断当前界面是否是home界面
  6. c语言定义的几种易错的说明
  7. c#學習筆記--Visual Studio 使用
  8. 在IDEA中设置Java的堆大小
  9. apm驱动与板级驱动
  10. Matlab绘图添加直角坐标轴