Tensorflow_GPU_2

深度学习训练并行模式

在深度学习中不仅可以利用单个CPU加速学习同时可以利用多个GPU或多台机器并行化地训练深度模型

具有两种模式:

(1) 同步模式

(2)异步模式

深度学习模型的训练是一个迭代的过程。在每一轮迭代中,前向传播算法会根据当前参数的取值计算出在一小部分训练数据上的预测值,然后反向传播算法再根据损失函数计算参数的梯度并更新参数。在并行化第训练深度学习模型时,不同设备(GPU或CPU)可以在不同训练数据上运行这个迭代过程,而不同并行模式的区别在于参数的更新方式

异步模式

在每一轮迭代时,不同设备会读取参数最新的取值,但是因为不同设备之间读取参数时间有差异,取得值可能不一样。根据当前参数的取值和随机获取的一小部分训练数据,不同设备各自运行反向传播的过程并独立更新参数。

同步模式

所有设备同时读取参数的取值,并且当反向传播算法完成后同步更新参数的取值,单个设备不会单独对参数进行更新,而会等待所有设备都完成反向传播后再统一更新参数(计算不同设备上参数的平均值,最后根据参数的平均值对参数进行更新)

# 实例: 利用多GPU实现手写字体的识别
import numpy as np
import tensorflow as tf
import time

# load dataset
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
Extracting MNIST_data/train-images-idx3-ubyte.gz
Extracting MNIST_data/train-labels-idx1-ubyte.gz
Extracting MNIST_data/t10k-images-idx3-ubyte.gz
Extracting MNIST_data/t10k-labels-idx1-ubyte.gz
# 设置超参数
num_gpus = 2 # GPU个数
num_steps = 200
learning_rate = 0.001
batch_size = 1024

num_inputs = 784
num_classes = 10
drop_out = 0.7
# 搭建卷积神经网络
def conv_net(X, n_classes, dropout, reuse, is_training):
    # 测试阶段 不用dropout,所以需要is_training参数
    with tf.variable_scope('ConvNet', reuse=reuse):  # 这里的reuse的参数是干嘛的???
        x = tf.reshape(X, [-1, 28, 28, 1])
        # conv1
        # tf.layers:主要提供的高层的神经网络,主要和卷积相关的,个人感觉是对tf.nn的进一步封装,tf.nn会更底层一些。
        x = tf.layers.conv2d(x, 64, 5, activation=tf.nn.relu) # 参数: 输入tensor, 输出的卷积核个数, 卷积核大小 激活函数
        # 池化
        x = tf.layers.max_pooling2d(x, 2, 2)

        x = tf.layers.conv2d(x, 256, 3, activation=tf.nn.relu)
        x = tf.layers.conv2d(x, 512, 3, activation=tf.nn.relu)
        x = tf.layers.max_pooling2d(x, 2, 2)

        x = tf.contrib.layers.flatten(x) # 将tensor拉伸为1为tensor

        # 全连接
        x = tf.layers.dense(x, 2048) # 输入tensor 输出大小
        x = tf.layers.dropout(x, rate=dropout, training=is_training) #  training若为False 则该句不执行

        x = tf.layers.dense(x, 1024) # 输入tensor 输出大小
        x = tf.layers.dropout(x, rate=dropout, training=is_training) #  training若为False 则该句不执行

        out = tf.layers.dense(x, n_classes)

        if not is_training: 
            return tf.nn.softmax(out)  # 测试阶段需要计算softmax,因为不需要在计算损失,所以要将在损失中计算的softmax提出来。
        else: # 训练阶段is_training=True不要计算softmax
            return out
def average_gradients(tower_grads):
    # 计算参数的平均梯度
    average_grades = []
    for grad_and_vars in zip(*tower_grads):
        # grad_and_vars 的格式为((grad0_gpu0, var0_gpu0),...,()),都是成对的,前面为梯度后面为变量,并且后面变量都相同
        # 例子:
        # a = [(1, 'a'), (2, 'b'), (3, 'c')]
        # list(zip(*a))
        # 输出 [(1,2,3), ('a', 'b', 'c')]
        #  *a 为输入的多个参数
        grads = []
        for g, _ in grad_and_vars:
            expanded_g = tf.expand_dims(g, 0) # 
            grads.append(expanded_g)
        grad = tf.concat(0, grads)
        grad = tf.reduce_mean(grad, 0) # 按列求平均

        v = grad_and_vars[0][1] # 提取出这个变量 
        grad_and_var = (grad, v) #  再组合成 对
        average_grades.append(grad_and_var)
    return average_grades
# 在不同的设备上执行操作
# 只将训练过程放在GPU上
# 这里用“同步模式”
with tf.device('/cpu:0'):
    tower_grads = []
    reuse_vars = False

    # 定义输入占位符
    X = tf.placeholder(tf.float32, [None, num_inputs])
    Y = tf.placeholder(tf.float32, [None, num_classes])

    for i in range(num_gpus):
        with tf.device('/gpu:%d'%i):
            # 分割数据
            # 取batch
            _x = X[i*batch_size : (i+1)*batch_size]
            _y = Y[i*batch_size : (i+1)*batch_size]

            # 前向计算。(训练和测试分开)
            logits_train = conv_net(_x, num_classes, drop_out, reuse=reuse_vars, is_training=True)
            logits_test = conv_net(_x, num_classes, drop_out, reuse=True, is_training=False)

            # 定义损失
            loss = tf.nn.softmax_cross_entropy_with_logits(logits=logits_train, labels=_y) # _y 时one hot形式
            loss_op = tf.reduce_mean(loss)
            optimizer = tf.train.AdamOptimizer(learning=learningrate)

            # minimize函数就是compute_gradients() 和apply_gradients().的结合 先计算后更新
            grads = optimizer.compute_gradients(loss_op) # 计算梯度,因为要等待所有设备完成更新后,求平均值。
            # compute_gradients 这个函数返回一个(梯度,变量)对的列表,其中梯度就是相对应变量的梯度了
            # 

            if i==0: #  将计算测试准确率的操作放在/GPU:0上
                correct_pred = tf.equal(tf.argmax(logits_test, 1), tf.argmax(_y, 1))
                accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
            reuse = True
            tower_grads.append(grads)
    tower_grads = average_gradients(tower_grads) # 计算变量的平均梯度
    # 使用平均梯度更新参数
    train_op = optimizer.apply_gradients(tower_grads)

    # 开始训练
    init  = tf.global_variables_initializer()

    with tf.Session() as sess:
        sess.run(init)
        step = 1
        for step in range(1, num_step+1):
            batch_x, batch_y = mnist.train.next_batch(batch_size*num_gpus)#  为所有GPU的每一个gpu都获取一个batch,所以 batch_size*num_gpus
            ts = time.time()
            sess.run(train_op, feed_dict={X:batch_x, Y:batch_y})
            te = time.time()
            if step % 20 == 0 or step == 1:
                loss, acc = sess.run([loss_op, accuracy], feed_dict={X:batch_x, Y:batch_y})

                print("step " + str(step) + ": Minibatch Loss= {:.4f}".format(loss) + ", training Accuracy = {:.3f}".format(acc) + ", %i Example/sec"%int(len(batch_x)/te))
                step += 1
        print("optimization Finish!")

猜你喜欢

转载自blog.csdn.net/ifruoxi/article/details/78262503