Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tensorflow 1.3 tips - 1 #58

Open
GH1995 opened this issue Mar 16, 2020 · 103 comments
Open

tensorflow 1.3 tips - 1 #58

GH1995 opened this issue Mar 16, 2020 · 103 comments

Comments

@GH1995
Copy link
Owner

GH1995 commented Mar 16, 2020

# 创建模型
# 占位符
X = tf.placeholder("float")
Y = tf.placeholder("float")
# 模型参数
W = tf.Variable(tf.random_normal([1]), name="weight")
b = tf.Variable(tf.zeros([1]), name="bias")
# 前向结构
z = tf.multiply(X, W)+ b
@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

  • tf.add_to_collection:把变量放入一个集合,把很多变量变成一个列表
  • tf.get_collection:从一个集合中取出全部变量,是一个列表
  • tf.add_n:把一个列表的东西都依次加起来
import tensorflow as tf;  
import numpy as np;  
import matplotlib.pyplot as plt;  
 
v1 = tf.get_variable(name='v1', shape=[1], initializer=tf.constant_initializer(0))
tf.add_to_collection('loss', v1)
v2 = tf.get_variable(name='v2', shape=[1], initializer=tf.constant_initializer(2))
tf.add_to_collection('loss', v2)
 
with tf.Session() as sess:
	sess.run(tf.initialize_all_variables())
	print tf.get_collection('loss')
	print sess.run(tf.add_n(tf.get_collection('loss')))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

定义一个获取权重,并自动加入正则项到损失的函数

def get_weight(shape, lambda1):
    w = tf.Variable(tf.random_normal(shape), dtype=tf.float32)
    # tf.add_to_collection将这个新生成的L2正则项损失项加入集合
    tf.add_to_collection('losses', tf.contrib.layers.l2_regularizer(lambda1)(w))
    return w

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 每层节点的个数
layer_dimension = [2, 10, 5, 3, 1]

# 层数
n_layers = len(layer_dimension)

# 这个变量维护前向传播时最深层的节点,开始的时候是输入层
cur_layer = x
in_dimension = layer_dimension[0]

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 循环生成网络结构
for i in range(1, n_layers):
    out_dimension = layer_dimension[i]
    weight = get_weight([in_dimension, out_dimension], 0.003)
    bias = tf.Variable(tf.constant(0.1, shape=[out_dimension]))
    cur_layer = tf.nn.elu(tf.matmul(cur_layer, weight) + bias)
    in_dimension = layer_dimension[i]

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 损失函数的定义
mse_loss = tf.reduce_sum(tf.pow(y_ - y, 2)) / sample_size
tf.add_to_collection('losses', mse_loss)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# tf.get_collection返回一个列表,这个列表是所有这个集合中的元素,将它们加起来就是最终的损失函数
loss = tf.add_n(tf.get_collection('losses'))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

滑动平均模型,它会对每个变量维护一个影子变量(shadow variable),这个影子变量的初始值就是相应变量的初始值,而每次运行变量更新时,影子变量的值会更新为:

$$shadow_variable = decay * shadow_varibale + (1 - decay) * variable$$

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

1.定义变量及滑动平均类

v1 = tf.Variable(0, dtype=tf.float32)   # 注意所有需要计算滑动平均得变量必须是实数型
step = tf.Variable(0, trainable=False)  # 模拟神经网络中迭代轮数,可以动态控制衰减率

ema = tf.train.ExponentialMovingAverage(0.99, step)  # 初始化时定义了衰减率和step
maintain_averages_op = ema.apply([v1])  
# 定义一个更新变量滑动平均的操作,这里需要给定一个列表,每次执行时列表中得变量都会被更新

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

2. 查看不同迭代中变量取值的变化。

with tf.Session() as sess:
    # 初始化
    init_op = tf.global_variables_initializer()
    sess.run(init_op)
    print(sess.run([v1, ema.average(v1)]))
    
    # 更新变量v1的取值到5
    sess.run(tf.assign(v1, 5))
    # 更新v1的滑动平均值。衰减率为 min{0.99, (1+step)/(10+step)=0.1} = 0.1
    # 所以v1的滑动平均会被更新为 0.1 * 0 + 0.9 * 5 = 4.5
    sess.run(maintain_averages_op)
    print(sess.run([v1, ema.average(v1)]))
    
    # 更新step和v1的取值
    sess.run(tf.assign(step, 10000))  
    sess.run(tf.assign(v1, 10))
    # 更新v1的滑动平均值。衰减率为 min{0.99, (1+step)/(10+step)=0.999} = 0.99
    # 更新v1的滑动平均会被更新为 0.99 * 4.5 + 0.01 * 10 = 4.555
    sess.run(maintain_averages_op)
    print(sess.run([v1, ema.average(v1)]))       
    
    # 再次更新v1的滑动平均值,得到 0.99 * 4.555 + 0.01 * 10 = 4.60945
    sess.run(maintain_averages_op)
    print(sess.run([v1, ema.average(v1)]))   

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

1.设置输入和输出节点的个数,配置神经网络的参数。

INPUT_NODE = 784     # 输入节点。对于MNIST数据集就等于图片像素
OUTPUT_NODE = 10     # 输出节点。类别数目,对于MNIST为0~9十个数字,即10类
LAYER1_NODE = 500    # 隐藏层数。这里使用只有一个隐藏层的网络结构 
                              
BATCH_SIZE = 100     # 每次batch打包的样本个数。数据越小越接近随机梯度下降;数据越大越接近梯度下降   

# 模型相关的参数
LEARNING_RATE_BASE = 0.8       # 基础的学习率
LEARNING_RATE_DECAY = 0.99     # 学习率的衰减率
REGULARAZTION_RATE = 0.0001    # 正则项的系数
TRAINING_STEPS = 5000          # 训练迭代的总轮数
MOVING_AVERAGE_DECAY = 0.99    # 滑动平均的衰减率

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

2. 定义辅助函数来计算前向传播结果,使用ReLU做为激活函数。

def inference(input_tensor, avg_class, weights1, biases1, weights2, biases2):
    # 不使用滑动平均类
    if avg_class == None:
        layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
        return tf.matmul(layer1, weights2) + biases2  
        # 因为在计算损失函数时会一并计算softmax函数,所以这里不需加入激活函数

    else:
        # 使用滑动平均类
        layer1 = tf.nn.relu(tf.matmul(input_tensor, avg_class.average(weights1))  \
            + avg_class.average(biases1))
        return tf.matmul(layer1, avg_class.average(weights2)) + avg_class.average(biases2)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

3. 定义训练过程。

def train(mnist):
    # 1. 定义神经网络参数,输入输出节点
    x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
    y_ = tf.placeholder(tf.float32, [None, OUTPUT_NODE], name='y-input')
    # 生成隐藏层的参数。
    weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
    biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))
    # 生成输出层的参数。
    weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
    biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))
 
    # 2. 前向传播、损失函数和反向传播
    # a. 前向传播
    # 计算不含滑动平均类的前向传播结果
    y = inference(x, None, weights1, biases1, weights2, biases2)
    
    # 计算使用滑动平均类的前向传播结果
    # 定义存储训练轮数的变量,不需要计算滑动平均值,因此指定为不可训练
    global_step = tf.Variable(0, trainable=False)
    # 给定滑动平均衰减率、训练轮数,初始化滑动平均类。给定训练轮数可以加快训练早期变量的更新速度
    variable_averages = tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY, global_step)
    # 在所有代表神经网络参数的变量上使用滑动平均
    # 即Graphkeys.TRAINABLE_VARIABLES中的元素,也即所有没有指定trainable=False的参数
    variables_averages_op = variable_averages.apply(tf.trainable_variables())
    # 滑动平均不会改变变量本身取值
    # 只是维护一个影子变量来记录其滑动平均值
    # 因此需要使用这个滑动平均值时需要明确调用average函数
    average_y = inference(x, variable_averages, weights1, biases1, weights2, biases2)
    
    # b. 损失函数
    # 计算交叉熵及其平均值
    # 目标类别只有一个正确答案时可使用tf.nn.sparse_softmax_cross_entropy_with_logits来加速交叉熵计算
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=tf.argmax(y_, axis=1))
    # 计算当前batch中所有样例的交叉熵平均值
    cross_entropy_mean = tf.reduce_mean(cross_entropy)
    # 计算L2正则化损失,一般只计算权重部分,不使用偏置项
    regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
    regularaztion = regularizer(weights1) + regularizer(weights2)
    # 总的损失函数
    loss = cross_entropy_mean + regularaztion
    
    # c. 反向传播
    # 设置指数衰减的学习率。
    learning_rate = tf.train.exponential_decay(
        LEARNING_RATE_BASE,
        global_step,
        mnist.train.num_examples / BATCH_SIZE,
        LEARNING_RATE_DECAY,
        staircase=True)
    # 优化损失函数(更新神经网络参数)
    train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step=global_step)
    # 每一遍训练,反向传播既需要更新参数
    # 也需要更新每一个参数的滑动平均值
    # train_op = tf.group(train_step, variables_average_op)等价下面两行
    with tf.control_dependencies([train_step, variables_averages_op]):
        train_op = tf.no_op(name='train')

    # 另;检验使用了滑动平均模型的神经网络前向传播是否正确
    correct_prediction = tf.equal(tf.argmax(average_y, 1), tf.argmax(y_, 1))  
    # tf.equal判断两个张量的每一维是否相等,返回True/False
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))  
    # tf.cast转换数据类型,这里将布尔型转换成实数型
    
    # 3. 建立会话,训练模型
    with tf.Session() as sess:
        tf.global_variables_initializer().run()
        # 准备验证数据,一般在神经网络训练过程中通过其来大致判断停止条件和评判训练的结果
        validate_feed = {x: mnist.validation.images, y_: mnist.validation.labels}
        # 准备验证数据,在实际的应用中,这部分数据在训练时是不可见的,这个数据作为模型优劣的最终评价标准
        test_feed = {x: mnist.test.images, y_: mnist.test.labels} 
        
        # 循环地训练神经网络
        for i in range(TRAINING_STEPS):
            if i % 1000 == 0:
                # 因MNIST数据集较小这里划分为更小的batch
                # 当神经网络比较复杂或验证集比较大时,太大的batch会导致计算时间太长甚至发生内存溢出
                validate_acc = sess.run(accuracy, feed_dict=validate_feed)
                print("After %d training step(s), validation accuracy using average model is %g "\
                % (i, validate_acc))
                
                ##### 5.2.2节适用######
                # test_acc = sess.run(accuracy, feed_dict=test_feed)
                # print(("After %d training step(s), test accuracy using average model is %g" 
                #    %(TRAINING_STEPS, test_acc)))
                #######################
            
            xs, ys = mnist.train.next_batch(BATCH_SIZE)
            sess.run(train_op, feed_dict={x:xs, y_:ys})

        test_acc = sess.run(accuracy, feed_dict=test_feed)
        print(("After %d training step(s), test accuracy using average model is %g" \
            %(TRAINING_STEPS, test_acc)))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

TensorFlow提供了通过变量名称来创建或者获取一个变量的机制,通过这个机制,在不同函数中可以直接通过变量的名字来获取变量,而不需要将变量通过参数的形式到处传递。这主要是通过 tf.get_variabletf.variable_scope 函数实现的,

1. tf.get_variable

v = tf.Variable(tf.constant(1.0, shape=[1]), name='v')
v = tf.get_variable('v', shape=[1], initializer=tf.constant_initializer(1.0))

tf.Variable 函数和 tf.get_variable 函数最大的区别在于指定变量名称的参数:

  • 对于tf.Variable ,变量名称是可选的参数,通过 name='v' 形式给出;
  • 对于tf.get_variable,变量名称是必填的参数,它会根据这个名字去创建或者获取变量,对于上例,它首先会去创建名为v的参数,如果创建失败(如已有同名参数)则会报错,这是为了避免无意识的变量复用造成的错误,比如神经网络中不同层的权重都命名为weights,否则不同层共用一个权重会出现一些难以发现的错误。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

2. tf.variable_scope

如果需要通过 tf.get_variable 来获取一个已经创建的变量,需要通过 tf.variable_scope 函数来生成一个上下文管理器,并明确指定在这个上下文管理器中,tf.get_variable将直接获取已经生成的变量。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 在名为foo的命名空间内创建名为v的变量
with tf.variable_scope("foo"):
    v = tf.get_variable("v", [1], initializer=tf.constant_initializer(1.0))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 在生成上下文管理器时,将参数reuse设置为True,这样tf.get_varibale函数将直接获取已经生命的变量
with tf.variable_scope("foo", reuse=True):
    v1 = tf.get_variable("v", [1])
print(v == v1)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

可以看到,当 tf.variable_scope

  • 使用参数 reuse=None或reuse=False 创建上下文管理器,tf.get_variable 操作将创建新的变量,如果存在同名变量则报错;
  • 使用参数 reuse=True 创建上下文管理器,这个管理器内所有 tf.get_variable 操作将直接获取已创建变量,如果变量不存在则报错;

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

tf.variable_scope 函数生成的上下文管理器也会创建了一个TF中的命名空间,在命名空间内创建的变量名称都会带上这个命名空间作为前缀。所以,tf.variable_scope 函数除了可以控制 tf.get_variable 执行的功能,也提供了一个管理变量命名空间的方法

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

v1 = tf.get_variable("v", [1])
print(v1.name)                                   
# 输出v:0,'v'表示变量名,'0'表示这个变量是生成变量这个运算的第一个结果

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

最后,通过使用 tf.variable_scopetf.get_variable 函数,可以对5.2.1中定义的计算前向传播的函数做出一些改进。如下所示,这样就不再需要将所有的变量都作为参数传递到不同的函数中了,当神经网络复杂、参数更多时,使用这种变量管理方式将大大提高程序的可读性。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

def inference(input_tensor, reuse=False):
    # 定义第一层神经网络的变量和前向传播过程
    with tf.variable_scope('layer1', reuse=reuse):
        # 根据传进来的reuse判断创建新变量还是使用已创建好的。第一次构造网络时需要创建新的,以后每次调用都使用reuse=True就不需要每次传递变量进来
        weights = get_weight_variable([INPUT_NODE, LAYER1_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
        biases = tf.get_variable("biases", [LAYER1_NODE], initializer=tf.constant_initializer(0.0))
        layer1 = tf.nn.relu(tf.matmul(input_tensor, weights) + biases)

    # 定义第二层神经网络的变量和前向传播过程
    with tf.variable_scope('layer2', reuse=reuse):
        weights = get_weight_variable([LAYER1_NODE, OUTPUT_NODE], initializer=tf.truncated_normal_initializer(stddev=0.1))
        biases = tf.get_variable("biases", [OUTPUT_NODE], initializer=tf.constant_initializer(0.0))
        layer2 = tf.matmul(layer1, weights) + biases

    # 返回最后的前向传播结果
    return layer2

x = tf.placeholder(tf.float32, [None, INPUT_NODE], name='x-input')
y = inference(x)

# 如果在程序中需要使用训练好的神经网络进行推倒时,可以直接调用inference(new_x, True)
# 如果需要使用滑动平均模型,可以参考5.2.1中代码,把计算滑动平均的类传到inference函数中即可,创建或获取变量的部分不需要改变
new_x = ...
new_y = inference(new_x, True)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

1. 保存模型

TensorFlow提供了一个非常简单的API来保存和还原一个神经网络模型,这个API就是tf.train.Saver类

# 声明tf.train.Saver类用于保存模型
saver = tf.train.Saver()

with tf.Session() as sess:
    sess.run(init_op)
    saver.save(sess, "Saved_model/model.ckpt")

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

  • model.ckpt.meta,它保存了TensorFlow计算图的结构,可以简单理解为神经网络的网络结构;
  • model.ckpt,它保存了模型中所有变量的取值,实际上分为两个文件:model.ckpt.index 和 model.ckpt.data-*****-of-*****,其中后者是通过SStable格式存储的,可以大致理解为一个(key, value)的列表。TensorFlow提供了tf.train.NewCheckpointReader类来查看保存的变量信息,如下:

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 该类可以读取checkpoint文件中保存的所有变量,注意后面的.data和.index可以省去
reader = tf.train.NewCheckpointReader('Saved_model/model.ckpt')

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 获取所有变量列表,这个是一个从变量名到变量维度的字典
global_varibales = reader.get_variable_to_shape_map()
for variable_name in global_varibales:
    # variable_name为变量名称,global_variables[variable_name]为变量的维度
    print(variable_name, global_varibales[variable_name])

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

# 获取名为v1的变量的取值
print("Value for variable v1 is ", reader.get_tensor("v1"))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

  • checkpoint,这个文件是 tf.train.Saver 类自动生成生成且自动维护的。在checkpoint文件中维护了一个由 tf.train.Saver 类持久化的所有TensorFlow模型文件的文件名。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

2. 加载模型

saver = tf.train.Saver()

with tf.Session() as sess:
    # 加载已经保存的模型,并通过已保存的模型中变量来计算加法
    saver.restore(sess, "Saved_model/model.ckpt")
    print(sess.run(result))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 19, 2020

3. 保存或加载部分变量

为了保存或者加载部分变量,在声明 tf.train.Saver 类时可以提供一个列表来指定需要保存或加载的变量

如在加载模型的代码中将 saver = tf.train.Saver() 改为 saver = tf.train.Saver([v1]),可以看到就会报错FailedPreconditionError: Attempting to use uninitialized value v2,如下:

saver = tf.train.Saver([v1])

with tf.Session() as sess:
    # 加载已经保存的模型,并通过已保存的模型中变量来计算加法
    saver.restore(sess, "Saved_model/model.ckpt")
    print(sess.run(result))

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

# 使用tf.train.shuffle_batch函数来组合样例。
# tf.train.shuffle_batch函数的参数大部分和tf.train.batch函数相似,
# 但是min_after_dequeue参数是tf.train.shuffle_batch函数特有的。
# min_after_dequeue 参数限制了出队时队列中元素的最少个数,
# 因为当队列中元素太少时,随机打乱样例顺序的作用就不太了。
# 当出队函数被调用但是队列中元素个数不够时,出队操作将等待里多的元素入队才会完成。
# 如果min_after_dequeue参数被设定,capacity也应该相应调整来满足性能需求。
example_batch, label_batch = tf.train.shuffle_batch(
    [example, label], batch_size=batch_size,
    capacity=capacity, min_after_dequeue=30)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

# 和tf.train.batch的样例代码一样打印example_batch, label_batch。
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    tf.local_variables_initializer().run()
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    
    # 获取并打印组合之后的样例。真实问题中,这个输出一般会作为神经网络的输入
    for i in range(3):
        cur_example_batch, cur_label_batch = sess.run([example_batch, label_batch])
        print(cur_example_batch, cur_label_batch)
        
    coord.request_stop()
    coord.join(threads)
    
# 从输出可以看到,得到的样例顺序已经被打乱了。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.train.batch函数和tf.train.shuffle_batch函数除了可以将单个训练数据整理成输入batch,也提供了并行化处理输入数据的方法。 二者并行化的方式一致,所以在本节中仅以应用得更多的后者为例:

  • 设置tf.train.shuffle_batch函数中的num_threads参数,可以指定多个线程同时执行入队操作。tf.train.shuffle_batch函数的入队操作就是数据读取以及预处理的过程。当num_threads参数大于1时,多个线程会同时读取一个文件中的不同样例并进行预处理。如果需要多个线程处理不同文件中的样例时,可以使用tf.train.shuffle_batch_join函数,此函数会从输入文件队列中获取不同的文件分配给不同的线程。一般来说,输入文件队列是通过7.3.2中介绍的tf.train.string_input_producer函数生成的,这个函数会平均分配文件以保证不同文件中的数据会被尽量平均地使用。

tf.train.shuffle_batchtf.train.shuffle_batch_join函数都可以完成多线程并行的方式来进行数据的处理,它们各有优劣:

  • 对于tf.train.shuffle_batch 函数,不同线程会读取同一个文件。如果一个文件中的样例比较相似(比如都属于同一个类别),那么神经网络的训练效果有可能会受到影响。所以在使用tf.train.shuffle_batch函数时,需要尽量将同一个TFRecord 文件中的样例随机打乱。
  • 而使用tf.train.shuffle_batch_join函数时,不同线程会读取不同文件。如果读取数据的线程数比总文件数还大,那么多个线程可能会读取同一个文件中相近部分的数据。而且多个线程读取多个文件可能导致过多的硬盘寻址,从而使得读取效率降低。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

输入数据处理框架

# 1. 创建立件列表,并通过文件列表创建输入文件队列。在调用输入数据处理流程前,需要统一
# 所有原始数据的格式并将它们存储到TFRecord文件中。下面给出的文件列表中应该包含所有
# 提供训练数据的TFRecord 文件。
files = tf.train.match_filenames_once("output.tfrecords")
filename_queue = tf.train.string_input_producer(files, shuffle=False) 

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

# 2. 解析TFRecord文件里的数据。这里假设image_raw中存储的是图像
# 的原始数据,pixels代表图片的像素数,label为该样例所对应的标签。
reader = tf.TFRecordReader()
_,serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(
    serialized_example,
    features={
        'image_raw':tf.FixedLenFeature([],tf.string),
        'pixels':tf.FixedLenFeature([],tf.int64),
        'label':tf.FixedLenFeature([],tf.int64)
    })

# 将原始图像数据解析出像素矩阵
decoded_images = tf.decode_raw(features['image_raw'], tf.uint8)
retyped_images = tf.cast(decoded_images, tf.float32)
images = tf.reshape(retyped_images, [784])
labels = tf.cast(features['label'], tf.int32)
#pixels = tf.cast(features['pixels'], tf.int32)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

# 3. 将文件以100个为一组打包
min_after_dequeue = 10000
batch_size = 100
capacity = min_after_dequeue + 3 * batch_size

image_batch, label_batch = tf.train.shuffle_batch([images, labels], 
                                                    batch_size=batch_size, 
                                                    capacity=capacity, 
                                                    min_after_dequeue=min_after_dequeue)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

# 4. 建立NN模型,训练
# 定义模型结构
def inference(input_tensor, weights1, biases1, weights2, biases2):
    layer1 = tf.nn.relu(tf.matmul(input_tensor, weights1) + biases1)
    return tf.matmul(layer1, weights2) + biases2
# 模型相关的参数
INPUT_NODE = 784
OUTPUT_NODE = 10
LAYER1_NODE = 500
REGULARAZTION_RATE = 0.0001   
TRAINING_STEPS = 5000        
weights1 = tf.Variable(tf.truncated_normal([INPUT_NODE, LAYER1_NODE], stddev=0.1))
biases1 = tf.Variable(tf.constant(0.1, shape=[LAYER1_NODE]))

weights2 = tf.Variable(tf.truncated_normal([LAYER1_NODE, OUTPUT_NODE], stddev=0.1))
biases2 = tf.Variable(tf.constant(0.1, shape=[OUTPUT_NODE]))

y = inference(image_batch, weights1, biases1, weights2, biases2)
# 计算交叉熵及其平均值
cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y, labels=label_batch)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
# 损失函数的计算
regularizer = tf.contrib.layers.l2_regularizer(REGULARAZTION_RATE)
regularaztion = regularizer(weights1) + regularizer(weights2)
loss = cross_entropy_mean + regularaztion

# 优化损失函数
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(loss)

# 初始化会话,并开始训练过程。
with tf.Session() as sess:
    # tf.global_variables_initializer().run()
    sess.run((tf.global_variables_initializer(),
              tf.local_variables_initializer()))
    coord = tf.train.Coordinator()
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    # 循环的训练神经网络。
    for i in range(TRAINING_STEPS):
        if i % 1000 == 0:
            print("After %d training step(s), loss is %g " % (i, sess.run(loss)))
                  
        sess.run(train_step)
        
    coord.request_stop()
    coord.join(threads)  

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

image

从图中可以看出,输入数据处理的第一步为通过tf.train.match_filenames_once获取存储训练数据的文件列表,在下图中,这个文件列表为{A,B,C}。

然后通过tf.train.string_input_producer函数,可以选择性地将文件列表中文件的顺序打乱,并加入输入队列(因为是否打乱文件的顺序是可选的,所以在下中通过虚线表示)。tf.train.string_input_producer函数会生成并维护一个输入文件队列,不同线程中的文件读取函数可以共享这个输入文件队列。

在读取样例数据之后,需要将图像进行预处理。图像预处理的过程也会通过tf.train.shuffle_batch提供的机制并行地跑在多个线程中。输入数据处理流程的最后通过tf.train.shuffle_batch函数将处理好的单个输入样例整理成batch提供给神经网络的输入层。

通过这种方式,可以有效地提高数据预处理的效率,避免数据预处理成为神经网络模型训练过程中的性能瓶颈。

@GH1995 GH1995 changed the title tsf tensorflow 1.3 tips Apr 20, 2020
@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.cast()函数的作用是执行 tensorflow 中张量数据类型转换

cast(x, dtype, name=None)

  • 第一个参数 x: 待转换的数据(张量)
  • 第二个参数 dtype: 目标数据类型
  • 第三个参数 name: 可选参数,定义操作的名称

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.greater(x, y, name=None) 返回(x> y)元素的真值

该函数返回一个 bool 类型的张量

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.expand_dims(
    input,
    axis=None,
    name=None,
    dim=None
)

增加一维

前后各增加一维

one_img = tf.expand_dims(one_img, 0)
one_img = tf.expand_dims(one_img, -1) #-1表示最后一维

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.tile(
    input,
    multiples,
    name=None
)

平铺之意,用于在同一维度上的复制

此操作通过复制 input multiples 时间来创建新的张量。输出张量的第 i 维具有 input.dims(i) * multiples[i] 元素,并且沿着 i' 维度 input值被复制multiples[i]` 次。

例如,[a b c d][2] 平铺将得到 [a b c d a b c d].

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.concat ( 
    values, 
    axis,
    name = 'concat' 
)

将张量沿一个维度串联

T1 =  [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] ] 
T2 =  [ [ 7 , 8 , 9 ] , [ 10 , 11 , 12 ] ] 
tf.concat([T1 ,T2] ,0) == >  [[1 , 2 ,3 ],[4 ,5 ,6],[7 ,8 ,9],[10 ,11,12]] 
tf.concat([T1 ,T2] ,1) == >  [[ 1 ,2 ,3 ,7 ,8 ,9 ],[4 ,5 ,6,10 ,11 ,12]]

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.reshape(
    tensor,
    shape,
    name=None
)

如果shape的一个分量是特殊值-1,则计算该维度的大小,以使总大小保持不变。特别地情况为,一个[-1]维的shape变平成1维。至多能有一个shape的分量可以是-1。

# tensor 't' is [1, 2, 3, 4, 5, 6, 7, 8, 9]
# tensor 't' has shape [9]
reshape(t, [3, 3]) ==> [[1, 2, 3],
                        [4, 5, 6],
                        [7, 8, 9]]

# tensor 't' is [[[1, 1, 1],
#                 [2, 2, 2]],
#                [[3, 3, 3],
#                 [4, 4, 4]],
#                [[5, 5, 5],
#                 [6, 6, 6]]]
# tensor 't' has shape [3, 2, 3]
# pass '[-1]' to flatten 't'
reshape(t, [-1]) ==> [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6]

# -1 can also be used to infer the shape

# -1 is inferred to be 9:
reshape(t, [2, -1]) ==> [[1, 1, 1, 2, 2, 2, 3, 3, 3],
                         [4, 4, 4, 5, 5, 5, 6, 6, 6]]

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.nn.relu(
    features,
    name=None
)

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.decode_csv(
    records,
    record_defaults,
    field_delim=',',
    use_quote_delim=True,
    name=None,
    na_value='',
    select_cols=None
)

将CSV记录转换为张量,每列映射到一个张量。

  • records:一个string类型的Tensor。每个字符串都是csv中的记录/行,所有记录都应具有相同的格式。
  • record_defaults:具有特定类型的Tensor对象列表。可接受的类型有float32,float64,int32,int64,string。输入记录的每列一个张量,具有该列的标量默认值或者如果需要该列则为空向量。
  • field_delim:可选的string。默认为","。用于分隔记录中字段的char分隔符。
  • use_quote_delim:可选的bool。默认为True。如果为false,则将双引号视为字符串字段内的常规字符。
  • name:操作的名称(可选)。
  • na_value:要识别为NA/NaN的附加字符串。
  • select_cols:可选的列索引的可选排序列表。如果指定,则仅解析并返回此列的子集。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.io.matching_files(
    pattern,
    name=None
)

pattern:一个string类型的Tensor。Shell通配符模式。字符串类型的标量或向量。

返回与一个或多个glob模式匹配的文件集。

请注意,此例程仅支持模式的basename部分中的通配符,而不支持目录部分中的通配符。 另请注意,返回的文件名顺序可能是不确定的。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 20, 2020

tf.slice(
    input_,
    begin,
    size,
    name=None
)

“input_”是你输入的tensor,就是被切的那个。
“begin”是每一个维度的起始位置,这个下面详细说。
“size”相当于问每个维度拿几个元素出来。

@GH1995
Copy link
Owner Author

GH1995 commented Apr 21, 2020

tf.nn.embedding_lookup(
    params,
    ids,
    partition_strategy='mod',
    name=None,
    validate_indices=True,
    max_norm=None
)

在 embedding 张量列表中查找 ids

url

@GH1995
Copy link
Owner Author

GH1995 commented Apr 24, 2020

tf.strided_slice(
    input_,
    begin,
    end,
    strides=None,
    begin_mask=0,
    end_mask=0,
    ellipsis_mask=0,
    new_axis_mask=0,
    shrink_axis_mask=0,
    var=None,
    name=None
)

提取张量的一个分段切片

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

# 将 TensorFlow 日志信息输出到屏幕
tf.logging.set_verbosity(tf.logging.INFO)

mnist = input_data.read_data_sets(r'C:\Users\tulin\tmp\mnist\data', one_hot=False)

# 指定神经网络的输入层
feature_columns = [tf.feature_column.numeric_column("image", shape=[784])]

estimator = tf.estimator.DNNClassifier(
    feature_columns=feature_columns,
    hidden_units=[500],  # 每层神经网络的结构
    n_classes=10,
    optimizer=tf.train.AdamOptimizer(),
    model_dir=r'C:\Users\tulin\tmp\mnist\log'
)

# 定义数据输入
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"image": mnist.train.image},
    y=mnist.train.labels.astype(np.int32),
    num_epochs=None,
    batch_size=128,
    shuffle=True
)

# 训练模型,没有定义损失函数
estimator.train(input_fn=train_input_fn, steps=10000)

# 定义测试数据的输入
test_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"image": mnist.test.image},
    y=mnist.test.labels.astype(np.int32),
    num_epochs=1,
    batch_size=128,
    shuffle=False
)

# 通过 evaluate 评测模型
accuracy_score = estimator.evaluate(input_fn=test_input_fn)["accuracy"]

print(accuracy_score)

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

global_step 代表全局步数,比如在多少步该进行什么操作,现在神经网络训练到多少轮等等,类似于一个钟表。

global_steps = tf.Variable(0, trainable=False)
 
learning_rate = tf.train.exponential_decay(0.1, global_steps, 10, 2, staircase=False)

loss = tf.pow(w*x-y, 2)
 
train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(
    loss,
    global_step=global_steps
)

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data

# 将 TensorFlow 日志信息输出到屏幕
tf.logging.set_verbosity(tf.logging.INFO)


# 定义模型结构
def lenet(x, is_training):
    # 将输入转化为卷积层需要的形状
    x = tf.reshape(x, shape=[-1, 28, 28, 1])

    net = tf.layers.conv2d(x, 32, 5, activation=tf.nn.relu)
    net = tf.layers.max_pooling2d(net, 2, 2)
    net = tf.layers.conv2d(net, 64, 3, activation=tf.nn.relu)
    net = tf.layers.max_pooling2d(net, 2, 2)
    net = tf.contrib.layers.flatten(net)
    net = tf.layers.dense(net, 1024)
    net = tf.layers.dropout(net, rate=0.4, training=is_training)

    return tf.layers.dense(net, 10)


# 使用模型
def model_fn(features, labels, mode, params):
    # 得到前向传播的结果
    predict = lenet(
        features["image"],
        mode == tf.estimator.ModeKeys.TRAIN
    )

    # 预测模式
    if mode == tf.estimator.ModeKeys.PREDICT:
        return tf.estimator.EstimatorSpec(
            mode=mode,
            predictions={"result": tf.argmax(predict, 1)}
        )

    # 1. 定义损失函数
    loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=predict,
        labels=labels,
    ))

    # 定义优化函数
    optimizer = tf.train.GradientDescentOptimizer(
        learning_rate=params["learning_rate"]
    )

    # 2. 定义训练过程
    train_op = optimizer.minimize(
        loss=loss,
        global_step=tf.train.get_global_step()
    )

    # 3. 定义测评标准
    eval_metric_ops = {
        "my_metric": tf.metrics.accuracy(
            tf.arg_max(predict, 1),
            labels
        )
    }

    # 返回模型训练过程中使用的三个对象
    return tf.estimator.EstimatorSpec(
        mode,
        loss=loss,
        train_op=train_op,
        eval_metric_ops=eval_metric_ops
    )


mnist = input_data.read_data_sets('mnist/data', one_hot=False)
model_params = {"learning_rate": 0.01}

# 生成 Estimator 类
estimator = tf.estimator.Estimator(
    model_fn=model_fn,
    params=model_params,
)

# 训练和测评模型
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"image": mnist.train.images},
    y=mnist.train.labels.astype(np.int32),
    num_epochs=None,
    batch_size=128,
    shuffle=True
)
estimator.train(input_fn=train_input_fn, steps=30000)

test_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"image": mnist.test.images},
    y=mnist.test.labels.astype(np.int32),
    num_epochs=1,
    batch_size=128,
    shuffle=False
)
test_results = estimator.evaluate(input_fn=test_input_fn)

accuracy_score = test_results["my_metric"]
print(accuracy_score)

# 使用训练好的模型在新数据上预测
predict_input_fn = tf.estimator.inputs.numpy_input_fn(
    x={"image": mnist.test.images[:10]},
    num_epochs=1,
    shuffle=False
)

predictions = estimator.predict(input_fn=predict_input_fn)

for i, p in enumerate(predictions):
    print("Prediction %s: %s" % (i + 1, p["result"]))

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

# 输入函数
def my_input_fn(file_path, perform_shuffle=False, repeat_count=1):
    def decode_csv(line):
        # 将一行中的数据解析出来,前四列为特征,最后一列为标签
        parsed_line = tf.decode_csv(line, [[0.], [0.], [0.], [0.], [0]])

        # Estimator 的输入要求特征是一个字典
        return {"x": parsed_line[:-1]}, parsed_line[-1]

    dataset = (tf.contrib.data.TextLineDataset(file_path).skip(1).map(decode_csv))

    if perform_shuffle:
        dataset = dataset.shuffle(buffer_size=256)
    dataset = dataset.repeat(repeat_count)
    iterator = dataset.make_one_shot_iterator()

    batch_features, batch_labels = iterator.get_next()
    return batch_features, batch_labels

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

# 线程中运行的程序,每隔 1 秒判断是否需要停止所有线程并打印自己的 ID
def MyLoop(coord, worker_id):
    while not coord.should_stop():
        if np.random.rand() < 0.1:
            print("Stop from id: %s\n" % worker_id)
            coord.request_stop()  # 停止所有线程
        else:
            print("Working on id: %s\n" % worker_id)
        time.sleep(1)


coordinator = tf.train.Coordinator()

threads = [threading.Thread(target=MyLoop, args=(coordinator, i,)) for i in range(5)]

for t in threads:
    t.start()
coordinator.join(threads)

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

# 启动多个线程操作同一个队列

# 声明一个队列
fifo_queue = tf.FIFOQueue(100, "float")

# 定义入队操作
enqueue_op = fifo_queue.enqueue([tf.random_normal([1])])

# 启动五个线程,每个线程中运行都是 enqueue_op 操作
qr = tf.train.QueueRunner(queue=fifo_queue, enqueue_ops=[enqueue_op] * 5)

# qr 加入计算图指定集合
tf.train.add_queue_runner(qr)

# 定义出队操作
dequeue_op = fifo_queue.dequeue()

with tf.Session() as sess:
    # 协调器
    coord = tf.train.Coordinator()
    # 启动所有线程
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    for _ in range(3):
        print(sess.run(dequeue_op)[0])

    coord.request_stop()  # coord 停止所有线程
    coord.join(threads)

@GH1995
Copy link
Owner Author

GH1995 commented May 5, 2020

input_files = ["file1", "file2"]
dataset = tf.data.TextLineDataset(input_files)

iterator = dataset.make_one_shot_iterator()


x = iterator.get_next()
with tf.Session() as sess:
    for i in range(3):
        print(sess.run(x))

@GH1995
Copy link
Owner Author

GH1995 commented May 6, 2020

@GH1995 GH1995 changed the title tensorflow 1.3 tips tensorflow 1.3 tips - 1 May 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant