【深度学习训练之Batch】

一、Batch概念

什么是batch,准备了两种解释,看君喜欢哪种?

  1. 对于一个有 2000 个训练样本的数据集。将 2000 个样本分成大小为 500 的 batch,那么完成一个 epoch 需要 4 个 iteration。
  2. 如果把准备训练数据比喻成一块准备打火锅的牛肉,那么epoch就是整块牛肉,batch就是切片后的牛肉片,iteration就是涮一块牛肉片(饿了吗?)。

    【深度学习训练之Batch】_第1张图片

    【深度学习训练之Batch】_第2张图片

     

二、Batch用来干什么

不是给人吃,是喂给模型吃。在搭建了“模型-策略-算法”三大步之后,要开始利用数据跑(训练)这个框架,训练出最佳参数。

  1. 理想状态,就是把所有数据都喂给框架,求出最小化损失,再更新参数,重复这个过程,但是就像煮一整块牛肉那样,不知道什么时候才有得吃。----全量数据的梯度下降算法
  2. 另一个极端的状态,就是每次只给模型喂一条数据,立马就熟了,快是够快了,但是一个不小心也会直接化掉,吃都没得吃(可能无法得到局部最优)----随机梯度下降算法(stochastic gradient descent)
  3. 平衡方案,综合考虑又要快,又要有得吃,那么选用切片涮牛肉的方法,把数据切成batch大小的一块,每次(iteration)只吃一块。每次只计算一小部分数据的损失函数,并更改参数。

三、Batch的实现

再次提供两种方法

1. yield→generator

具体的语法知识,请点链接。

# --------------函数说明-----------------
# sourceData_feature :训练集的feature部分
# sourceData_label   :训练集的label部分
# batch_size  : 牛肉片的厚度
# num_epochs  : 牛肉翻煮多少次
# shuffle : 是否打乱数据

def batch_iter(sourceData_feature,sourceData_label, batch_size, num_epochs, shuffle=True):
    
    data_size = len(sourceData_feature)
    
    num_batches_per_epoch = int(data_size / batch_size)  # 样本数/batch块大小,多出来的“尾数”,不要了
    
    for epoch in range(num_epochs):
        # Shuffle the data at each epoch
        if shuffle:
            shuffle_indices = np.random.permutation(np.arange(data_size))
            
            shuffled_data_feature = sourceData_feature[shuffle_indices]
            shuffled_data_label   = sourceData_label[shuffle_indices]
        else:
            shuffled_data_feature = sourceData_feature
            shuffled_data_label = sourceData_label

        for batch_num in range(num_batches_per_epoch):   # batch_num取值0到num_batches_per_epoch-1
            start_index = batch_num * batch_size
            end_index = min((batch_num + 1) * batch_size, data_size)

            yield (shuffled_data_feature[start_index:end_index] , shuffled_data_label[start_index:end_index])

batchSize = 100 # 定义具体的牛肉厚度
Iterations = 0  #  记录迭代的次数

# sess
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# 迭代 必须注意batch_iter是yield→generator,所以for语句有特别
for (batchInput, batchLabels) in batch_iter(mnist.train.images, mnist.train.labels, batchSize, 30, shuffle=True):
    trainingLoss = sess.run([opt,loss], feed_dict = {X: batchInput, y:batchLabels})
    if Iterations%1000 == 0:  # 每迭代一千次,输出一次效果
        train_accuracy = sess.run(accuracy, feed_dict={X:batchInput, y:batchLabels})
        print("step %d, training accuracy %g"%(Iterations,train_accuracy))
    Iterations=Iterations+1    

2. slice_input_producer + batch

又涉及到一些背景知识,这篇文章和这篇文章。以下是图解slice_input_producer。

【深度学习训练之Batch】_第3张图片

def get_batch_data(images, label, batch_Size):
    input_queue = tf.train.slice_input_producer([images, label], shuffle=True, num_epochs=20)    # 见图解
    image_batch, label_batch = tf.train.batch(input_queue, batch_size=batch_Size, num_threads=2,allow_smaller_final_batch=True)
    return image_batch,label_batch

batchSize = 100 #  记录迭代的次数

batchInput, batchLabels = get_batch_data(mnist.train.images, mnist.train.labels, batchSize)

Iterations = 0 # 定义具体的牛肉厚度

# sess
sess = tf.Session()
sess.run(tf.global_variables_initializer())
sess.run(tf.local_variables_initializer())#就是这一行

coord = tf.train.Coordinator()
# 真正将文件放入文件名队列,还需要调用tf.train.start_queue_runners 函数来启动执行文件名队列填充的线程,
# 之后计算单元才可以把数据读出来,否则文件名队列为空的,
threads = tf.train.start_queue_runners(sess,coord)

try:
    while not coord.should_stop():
        BatchInput,BatchLabels = sess.run([batchInput, batchLabels])
        trainingLoss = sess.run([opt,loss], feed_dict = {X:BatchInput, y:BatchLabels})
        if Iterations%1000 == 0:
            train_accuracy = accuracy.eval(session = sess, feed_dict={X:BatchInput, y:BatchLabels})
            print("step %d, training accuracy %g"%(Iterations,train_accuracy))
        Iterations = Iterations + 1
except tf.errors.OutOfRangeError:
    train_accuracy = accuracy.eval(session = sess, feed_dict={X:BatchInput, y:BatchLabels})
    print("step %d, training accuracy %g"%(Iterations,train_accuracy))
    print('Done training')
finally:
    coord.request_stop()
coord.join(threads)
# sess.close()

四、两种方式的对比

方式1: yield→generator 30个epoch
试验效果,开始前python.exe进程占了402M内存。
试验中,内存基本维持在865M左右
试验后,30个epoch耗时需要49.8s

方式2: slice_input_producer + batch
进行slice_input_producer这步,占用内存由410M提升到了583M
训练的时候,内存占用比较飘忽,有时1G多。
20个epoch耗时需要199s

小结:方式1的效率暂时比方式2快不少。



作者:StarsOcean
链接:https://www.jianshu.com/p/71f31c105879
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(深度学习,python,深度学习,tensorflow)