Tensorflow——多线程输入数据处理框架(完整代码)

我们学习使用TensorFlow对图像数据进行预处理的方法。虽然使用这些图像数据预处理的方法可以减少无关因素对图像识别模型效果的影响,但这些复杂的预处理过程也会减慢整个训练过程。为了避免图像预处理成为神经网络模型训练效率的瓶颈,TensorFlow提供了一套多线程处理输入数据的框架。

  下面总结了一个经典的输入数据处理的流程:

   下面我们首先学习TensorFlow中队列的概念。在TensorFlow中,队列不仅是一种数据结构,它更提供了多线程机制。队列也是TensorFlow多线程输入数据处理框架的基础。然后再学习上面的流程。最后这个流程将处理好的单个训练数据整理成训练数据 batch,这些batch就可以作为神经网络的输入。

准备知识:多线程的简单介绍

  在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程。线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于CPU),而一条流水线必须属于一个车间,一个车间就是一个进程,车间负责把资源整合到一起,是一个资源单位,而一个车间内至少有一条流水线。所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是CPU上的执行单位。

  多线程(即多个控制线程)的概念就是:在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当于一个车间内有多条流水线,都共用一个车间的资源。比如成都地铁和西安地铁是不同的进程,而成都地铁3号线是一个线程,成都地铁所有的线程共享成都所有的资源,比如成都所有的乘客可以被所有线拉。

  开启多线程的方式:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

import time

import random

from threading import Thread

def study(name):

    print("%s is learning" % name)

    time.sleep(random.randint(1, 3))

    print("%s is playing " % name)

if __name__ == '__main__':

    t = Thread(target=study, args=('james', ))

    t.start()

    print("主线程开始运行")

'''

结果展示:

james is learning

主线程开始运行

james is playing

'''

    t.start() 将开启进程的信号发给操作系统后,操作系统要申请内存空间,让好拷贝父进程地址空间到子进程,开销远大于线程。

1,队列与多线程

  在TensorFlow中,队列和变量类似,都是计算图上有状态的节点。其他的计算节点可以修改他们的状态。对于变量,可以通过赋值操作修改变量的取值。对于队列,修改队列状态的操作主要有Enqueue,EnqueueMany和Dequeue。下面程序展示了如何使用这些函数来操作一个队列。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

#_*_coding:utf-8_*_

import tensorflow as tf

# 创建一个先进先出的队列,指定队列中最多可以保存两个元素,并指定类型为整数

q = tf.FIFOQueue(2, 'int32')

# 使用enqueue_many 函数来初始化队列中的元素。

# 和变量初始化类似,在使用队列之前需要明确的调用这个初始化过程

init = q.enqueue_many(([0, 10], ))

# 使用Dequeue 函数将队列中的第一个元素出队列。这个元素的值将被存在变量x中

x = q.dequeue()

# 将得到的值加1

y = x + 1

# 将加 1 后的值在重新加入队列

q_inc = q.enqueue([y])

with tf.Session() as sess:

    # 运行初始化队列的操作

    init.run()

    for in range(6):

        #运行q_inc 将执行数据出队列,出队的元素 +1 ,重新加入队列的整个过程

        v, _ = sess.run([x, q_inc])

        # 打印出队元素的取值

        print('%s'%v)

'''

队列开始有[0, 10] 两个元素,第一个出队的为0, 加1之后为[10, 1]

第二次出队的为10, 加1之后入队的为11, 得到的队列为[1, 11]

以此类推,最后得到的输出为:

0

10

1

11

2

'''

  TensorFlow中提供了FIFOQueue 和 RandomShuffleQueue 两种队列。在上面的程序中,已经展示了如何使用FIFOQueue,它的实现的一个先进先出队列。 RandomShuffleQueue 会将队列中的元素打乱,每次出队操作得到的是从当前队列所有元素中随机选择的一个。在训练审计网络时希望每次使用的训练数据尽量随机。 RandomShuffleQueue 就提供了这样的功能。

  在TensorFlow中,队列不仅仅是一种数据结构,还是异步计算张量取值的一个重要机制。比如多个线程可以同时向一个队列中写元素,或者同时读取一个队列中的元素。在后面我们会学习TensorFlow是如何利用队列来实现多线程输入数据处理的。

  TensorFlow提供了 tf.Coordinator 和 tf.QueueRunner 两个类来完成多线程协同的功能。tf.Coordinator 主要用于协同多个线程一起停止,并提供了 should_stop, request_stop 和 join 三个函数。在启动线程之前,需要先声明一个 tf.Coordinator 类,并将这个类传入每一个创建的线程中。启动的线程需要一直查询 tf.Coordinator 类中提供的 should_stop 函数,当这个函数的返回值为 True时,则当前线程也需要退出。每一个启动的线程都可以通过调用 request_stop 函数来通知其他线程退出。当某一个线程调用  request_stop 函数之后, should_stop 函数的返回值将被设置为 TRUE,这样其他的线程就可以同时终止了。下面程序展示了如何使用 tf.Coordinator。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

#_*_coding:utf-8_*_

import tensorflow as tf

import numpy as np

import threading

import time

# 线程中运行的程序,这个程序每隔1秒判断是否停止并打印自己的ID

def MyLoop(coord, worker_id):

    # 使用 tf.Coordinator 类提供的协同工具判断当前是否需要停止

    while not coord.should_stop():

        # 随机停止所有的线程

        if np.random.rand() < 0.1:

            print("Stopping from id: %d\n" % worker_id)

            # 调用 coord.request_stop() 函数来通知其他线程停止

            coord.request_stop()

        else:

            # 打印当前线程的 ID

            print("Working on id: %d\n" % worker_id)

        # 暂停1 s

        time.sleep(1)

# 声明一个  tf.train.Coordinator 类来协同多个线程

coord = tf.train.Coordinator()

# 声明创建 5 个线程

threads = [

    threading.Thread(target=MyLoop, args=(coord, i, )) for in range(5)

]

# 启动所有的线程

for in threads:

    t.start()

# 等待所有线程退出

coord.join(threads)

'''

Working on id: 0

Working on id: 1

Working on id: 2

Working on id: 3

Working on id: 4

Working on id: 0

Working on id: 1

Working on id: 3

Working on id: 2

Working on id: 4

Working on id: 0

Working on id: 2

Working on id: 1

Working on id: 3

Working on id: 4

Working on id: 2

Working on id: 1

Working on id: 0

Working on id: 3

Working on id: 4

Working on id: 3

Working on id: 0

Working on id: 1

Working on id: 2

Working on id: 4

Working on id: 1

Stopping from id: 0

'''

  当所有线程启动之后,每个线程会打印各自的ID,于是前面4行打印出了他们的ID。然后在暂停1秒之后,所有的线程又开始第二遍打印ID。在这个时候有一个线程推出的条件达到,于是调用了coord.request_stop 函数来停止所有其他的线程。然而在打印Stoping_from_id:4之后,可以看到有线程仍然在输出。这是因为这些线程已经执行完 coord.should_stop 的判断,于是仍然会继续输出自己的ID。但在下一轮判断是否需要停止时将推出线程。于是在打印一次ID之后就不会再有输出了。

  tf.QueueRunner 主要用于启动多个线程来操作同一个队列,启动的这些线程可以通过上面介绍的 tf.Coordinator 类来统一管理,下面代码展示了如何使用 tf.QueueRunner 和 tf.Coordinator 来管理多线程队列操作。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36<

  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值