【TensorFlow】

综合

tf api
TensorFlow学习笔记1:graph、session和op

Session与变量的关系:变量的具体值是依存于session的,下面第二个sess会报未初始化的错,即使第一个sess里已经初始化了变量,但随着第一个sess的关闭,变量的值会随着sess的关闭而消失,但图上的节点只要定义了就一直存在,这是因为Tensor是只是数据的一个引用,sess关闭,引用虽然存在,但是数据已经不存在了,新开一个sess,是取不到前一个sess里的变量的值

import tensorflow as tf
v1=tf.Variable([[1,2],[3,4]])
v2=tf.Variable([[1,2],[5,6]])
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(tf.global_variables())
    sess.run(tf.assign(v1,[[10,20],[5,6]]))
    print(sess.run(tf.global_variables()))
with tf.Session() as sess:
#     报错,因为没有初始化
    print(sess.run(tf.global_variables()))

TensorFlow基础知识:计算图中的Op,边,和张量

之前搞不清楚op和tensor的关系,op是操作(比如加减乘除),有输入有输出,tensor是对数据(比如输入和计算结果)的引用。下面这句话对理解tf的工作模式非常有帮助
计算图的定义和图的运算是分开的.tensorflow是一个’符号主义的库’.编程模式分为两类,命令式(imperative style)和符号式(symbolic style).命令式的程序很容易理解和调试,它按照原有的逻辑运行.符号式则相反,在现有的深度学习框架中,torch是命令式的,Caffe,Mxnet是两种模式的混合,tensorflow完全采用符号式.符号式编程,一般先定义各种变量,然后建立一个计算图(数据流图),计算图指定了各个变量之间的计算关系,此时对计算图进行编译,没有任何输出,计算图还是一个空壳,只有把需要运算的输入放入后,才会在模型中形成数据流.形成输出.

看下面简单的一段代码,之前我一直会怀疑,run了两次,那是不是就inference这个函数在图上就执行了两次啊?不是的,执行的不是inference函数,而是数据向前流动,inference函数是仅仅在定义图上的计算节点,虽然说最后train_op会被run两次,run是在graph上,数据有两次流入。而inference这个函数也只是在图上定义op而已,其他的活并不是在inference函数里完成的,而是在graph上完成的

import tensorflow as tf
def inference(x):
    net = tf.layers.dense(x, 1)
    return net
x = tf.placeholder(tf.float32, shape=[None, 4])
y = tf.placeholder(tf.float32, shape=[None, 1])
y_ = inference(x)
loss = tf.reduce_mean(y - y_)
train_op = tf.train.AdamOptimizer().minimize(loss)
with tf.Session() as sess:
    sess.run(train_op,feed_dict={x:[[1,2,3,4]],y:[[1]]})
    sess.run(train_op,feed_dict={x:[[1,2,3,4]],y:[[1]]})

Tensorflow一些常用基本概念与函数(2)
tensorflow编程: Inputs and Readers,有place_holder相关和FIFOQueue相关
Inside TF-Slim(13) preprocessing(图像增强相关)

数据基础

在tensorflow里,类型很重要,很多操作里,只接受float或者int,而且很多时候要求两个tensor的类型一致

Variable和get_variable的用法以及区别

tf.reset_default_graph()
a=tf.get_variable('13',shape=[],initializer=tf.constant_initializer(12))
# 下一个语句报错
# a=tf.get_variable('13',shape=[],initializer=tf.constant_initializer(12))
b=tf.Variable(12,name='13')
print(b)
b=tf.Variable(12,name='13')
print(b)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(a))
    print(sess.run(b))

Why do we use tf.name_scope()
tf.variable_scope和tf.name_scope的用法

下面的代码可以看出,name_scope只对op和Variable有效,即对其名称可以添加scope头,get_variable是先尝试获取,如果已经存在并且此时不是复用(没有指定reuse)那就报错,如果没有就创建。

with tf.name_scope('V1'):
    a1 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1))
    a2 = tf.Variable(tf.random_normal(shape=[2, 3], mean=0, stddev=1), name='a2')
    add = tf.add(a1, a2)
    a3 = tf.Variable(tf.random_normal(shape=[2, 3], mean=0, stddev=1), name='a2')
with tf.name_scope('V2'):
    a4 = tf.Variable(tf.random_normal(shape=[2, 3], mean=0, stddev=1), name='a2')
    # a5 = tf.get_variable(name='a1', shape=[1], initializer=tf.constant_initializer(1))报错,因为a1已经存在了
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(a1.name)  # a1:0
    print(a2.name)  # V1/a2:0
    print(add)  # Tensor("V1/Add:0", shape=(2, 3), dtype=float32)
    print(a3.name)  # V1/a2_1:0
    print(a4.name)  # V2/a2:0

下面的代码可以看出,只要外层variable_scope是reuse,内层无论reuse是什么,都是reuse

with tf.variable_scope('name') as scope:
    net = tf.get_variable('net',shape=[],initializer=tf.ones_initializer)
    with tf.variable_scope('in'):
        net1 = tf.get_variable('net1',shape=[],initializer=tf.ones_initializer)
print(net)
print(net1)
with tf.variable_scope('name',reuse=True) as scope:
    net = tf.get_variable('net',shape=[],initializer=tf.ones_initializer)
    with tf.variable_scope('in',reuse=False):
        net1 = tf.get_variable('net1',shape=[],initializer=tf.ones_initializer)
print(net)
print(net1)

tensorflow更改变量的值

tf之中对于数据的处理一定要用tf的操作,比如判断某个tensor的值是否与某个值相等,一定要用tf.equal(tensor,1)这样来比较,不能直接==

Tensorflow变量与张量

Tensor不存数据,只是存储数据的来源,因此一切取出tensor的值的操作都要在sess里进行

Spatial Dropout

import numpy as np
import tensorflow as tf

ary = np.arange(70).reshape((2, 7, 5))
inputs = tf.Variable(ary,dtype=tf.float32)
output = tf.nn.dropout(inputs,keep_prob=0.5,noise_shape=[2,1,5])

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(output))

noise_shape:不指定的话,就是随机drop值,如果指定了,比如第二位为1,代表着第二维度的每一列要drop整个第二维全drop,要是不drop那就全留着

tf.gradients()中grad_ys的作用

可以把文中例子改了,改成grad_ys=None看输出有什么变化
grad_ys is a list of tensors of the same length as ys that holds the initial gradients for each y in ys
这个参数的意义在于对xs中的每个元素的求导加权种

关于word2vec的skip-gram模型使用负例采样nce_loss损失函数的源码剖析

  • weights: A Tensor of shape [num_classes, dim], or a list of Tensor objects whose concatenation along dimension 0 has shape [num_classes, dim]. The (possibly-partitioned) class embeddings.
    dim就是embedding_size,嵌入的维度
  • biases: A Tensor of shape [num_classes]. The class biases.
  • labels: A Tensor of type int64 and shape [batch_size, num_true]. The target classes.
    训练数据样本对应的类别矩阵,比如skip-gram里,每个中心词的上下文都是这个中心词的label,以
  • inputs: A Tensor of shape [batch_size, dim]. The forward activations of the input network.
    就是在基于输入的嵌入后的向量,每个样本都是中心词经过嵌入后的向量
    inputs和labels是两两配对的一个格式,比如一个中心词,上下文为1,文本为‘ABC’,那么就可以生成两个样本B->A,B->C;在输入的时候,要将B转化成嵌入向量,而A,C不用,直接是字典中的单次编号就可以,也就是0~num_classes中的一个值
  • num_sampled: An int. The number of classes to randomly sample per batch.
  • num_classes: An int. The number of possible classes.
    num_class是单词表的大小
  • num_true: An int. The number of target classes per training example…
    每个训练数据对应的类别数目,这个我不太理解,难道一个样本里,一个中心词可以对应多个上下文?比如。。B->(A,C)???
tf.reset_default_graph()
g=tf.Graph()
with g.as_default():
    tf.reset_default_graph()

上述代码报错,但如果我要清空当前nest graph怎么办,Do not use tf.reset_default_graph() to clear nested graphs. If you need a cleared graph, exit the nesting and create a new graph.

tf.reset_default_graph()
g=tf.Graph()
with g.as_default():
    pass
g=tf.Graph()
# g里面的东西就清空了

tf.newaxis的用法

#[:, tf.newaxis]操作相当于把这个一维向量变成了一个很高的二维矩阵
arr=tf.constant([1,2])
tf.Session() as sess:
	print(sess.run(arr))
    print(sess.run(arr[:,tf.newaxis]))

TensorFlow queue多线程读取数据

运算操作

tf.control_dependencies()
TensorFlow笔记——(2) tf.group(), tf.tuple 和 tf.identity()
tf.identity的意义以及用例
tensorflow中batch normalization的用法

博主有提到说,“一般来讲,这些参数都是基于channel来做的”,那么也就是说,在CV领域做BN的时候,求平均值是对于通道来求的,因为假设“输入x是一个16*32*32*128(NWHC格式)的feature map”,那么当前层就可以看做一个输入为32*32*128的神经层,每个神经元可以relu或者其他,当对这玩意做bn的时候,是对每个通道求均值和方差,比如第一个通道是一个16*32*32的feature map,对这16*32*32个数求均值和方差,用这个均值和方差处理当前通道的feature map,以上都是个人理解,但我觉得是对的,因为
其中axis的用法,推荐去看tf.layers.BatchNormalization中API的描述,比如输入的是一个batch,这个batch是100*15,也就是100个样本,每个样本15维度,当axis=1的时候,就是说,认为第一个轴是特征轴,对这个轴进行bn,那么算得的均值也是15维度的,就是说有15个均值,这也正是batch_normalization所做的。
至于为什么刚才图片里的是128个均值和方差,我猜是进行了广播吧
6.20得到验证,bn.weights是个列表,有四个Tensor,分别是均值方差以及对应的滑动量,他们的形状都是一维度的长度和channal一样,都是3

import tensorflow as tf
tf.reset_default_graph()
input_x = tf.Variable([[[[1.,2.,3.]],[[4.,5.,6.]]]])
bn = tf.layers.BatchNormalization()
output=bn(input_x)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(output)
    print(bn.weights)
    print(sess.run(output))

tensorflow中的batch_norm以及tf.control_dependencies和tf.GraphKeys.UPDATE_OPS的探究

为什么要updata_op,简单说,就是训练的时候,每次用的是本次batch里的数据计算mean和variance,但是预测的时候,用的是好多个mean和variance的滑动平均值来作为mean和variance的,而根据《google实战》那本书里讲,滑动平均实际上是维护的新的影子变量,因此需要手动更新这些影子变量

TENSORFLOW GUIDE: BATCH NORMALIZATION
tensorflow在函数中用tf.Print输出中间值的方法

WARNING:tensorflow:From E:/txtingwang/python/feature_extraction/zxy_test.py:5: Print (from tensorflow.python.ops.logging_ops) is deprecated and will be removed after 2018-08-20.
Instructions for updating:
Use tf.print instead of tf.Print. Note that tf.print returns a no-output operator that directly prints the output. Outside of defuns or eager mode, this operator will not be executed unless it is directly specified in session.run or used as a control dependency for other operators. This is only a concern in graph mode. Below is an example of how to ensure tf.print executes in graph mode:
就是说,tf.Print已经被删除了,换成tf.print了。tf.print就方便多了,就只是一个打印操作,不像tf.Print那么麻烦了,不过上面博客里讲的道理还是适用的

import tensorflow as tf
def test():
    a = tf.constant(0)
    # 如果把下一行放在了for里面,那么a_print就会打印10次了
    # 想象运算是一个流,在run的时候,a_print只被定义了一次,只流了一次
    a_print = tf.print(['a_value: ', a])
    for i in range(10):
        with tf.control_dependencies([a_print]):
            a = tf.add(a, 1)
    return a
if __name__ == '__main__':
	sess = tf.Session()
    with sess.as_default():
        tensor = tf.range(10)
        print_op = tf.print(tensor)
        with tf.control_dependencies([print_op]):
          out = tf.add(tensor, tensor)
        sess.run(out)
    with tf.Session() as sess:
        print(sess.run(test()))

tile函数的用法,其实就是堆叠,下面的例子就是第一维度扩充重复两次,第二维度重复3次

import tensorflow as tf

a = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float32)
a1 = tf.tile(a, [2, 3])
with tf.Session() as sess:
    print(sess.run(a))
    print(sess.run(a1))

经验干货:使用tf.py_func函数增加Tensorflow程序的灵活性

py_func在v2将会被废弃,更新方法推荐使用py_function,官方给的说明是tf.py_function, which takes a python function which manipulates tf eager tensors instead of numpy arrays. It’s easy to convert a tf eager tensor to a ndarray (just call tensor.numpy()) but having access to eager tensors means tf.py_functions can use accelerators such as GPUs as well as being differentiable using a gradient tape.也就是说,原来py_func处理的是ndarray,而新的py_function处理的是eager tensor,并且也告诉你怎么使用了,就是调用tensor.numpy()就行.其中有个参数叫Tout,如果说你的方法返回了两个list,比如说是[1,2,3],[4,5,6],那么Tout就应该是(tf.int32,tf.int32),代表返回了两个值,第一个值内的数字是tf.int32类型的,因为这两个list最终都会被包裹成tensor,而tensor的dtype就是这块Tout所需要指明的

import tensorflow as tf
import numpy as np
def tile_tensor(tensor_a, tensor_b):
    tile_tensor_a = tf.py_function(_tile_tensor, [tensor_a, tensor_b], tf.float32)
    return tile_tensor_a
def _tile_tensor(a, b):
#这里a和b不再是ndarray了,因为用了py_function
    tile_a = np.tile(a.numpy(), (b.numpy().shape[0], 1))
    return tile_a

损失函数

TensorFlow里的losses包下求loss只能求一个样本的pre和true之间的loss,以hinge loss为例
SVM(支持向量机)之Hinge Loss解释

tf.reset_default_graph()
#下面是个multilabel问题,但仍然是一个样本计算loss
loss = tf.losses.hinge_loss([1,1],[1,1])
loss = tf.losses.hinge_loss([0,1],[1,1])
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(loss))

【TensorFlow】sparse_softmax_cross_entropy_with_logits 和softmax_cross_entropy_with_logits选用技巧

tf.losses.get_loss和tf.losses.get_losses,regularization,从下面可以看出,get_xxx_loss是获取某种损失的列表,get_xxx_losses是获取某种损失的和。然后,正则化损失并不会被添加到loss_collection里,从下面可以看到,但是get_total_losses可以获得全部的损失,包括正则化损失和损失集合里的损失。API里描述get_total_losses有如下一句话

In particular, this adds any losses you have added with tf.add_loss() to any regularization losses that have been added by regularization parameters on layers constructors e.g. tf.layers.
特别强调,这个方法会将任何以tf.add_loss()方式添加的损失,和,所有的在定义神经层里时候定义的正则化损失加起来。

net = tf.Variable([[2.,3.],[1.,2.]])
dense = tf.layers.Dense(1, kernel_regularizer=tf.keras.regularizers.l2(l=0.0005 / 2))
net = dense(net)
print(tf.losses.get_regularization_loss())
# []
print(tf.losses.get_regularization_losses())
# Tensor("total_regularization_loss:0", shape=(), dtype=float32)
print(tf.losses.get_losses())
# []
print(tf.losses.get_total_loss())
# Tensor("total_loss:0", shape=(), dtype=float32)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(tf.losses.get_regularization_loss().eval())
    # 0.000184326 有可能不同
    print(sess.run(tf.losses.get_total_loss()))
    # 0.000184326

下面的代码展示了sparse_softmax_cross_entropysparse_softmax_cross_entropy_with_logits的区别,前者是后者的一个高层封装,默认求和求平均,不加其他参数的话,前者返回一个数,后者返回一个列表分别代表每个样本的损失;前者默认将这个损失加入到tf.GraphKeys.LOSSES这个集合里,后者没这功能。

cross_entropy1 = tf.losses.sparse_softmax_cross_entropy(labels = [1,0,1],logits=[[1/2,1/2],[1/2,1/2],[0.9,1/3]])
cross_entropy2 = tf.nn.sparse_softmax_cross_entropy_with_logits(labels = [1,0,1],logits=[[1/2,1/2],[1/2,1/2],[0.9,1/3]])
total_loss=tf.losses.get_total_loss()
with tf.Session() as sess:
    print(sess.run(cross_entropy1))
    print(sess.run(cross_entropy2))
    print(sess.run(total_loss))

Feature column

feature column这个东西真的很好用,把feature columns送入estimator里,然后,我们不用再去关注如何对输入数据进行处理了,这些feature column会自动帮我们进行feature transformation
比如说现在有一个feature column是一个分桶形的feature column
读入dataset的时候,直接读取数字就行,不需要人工对dataset里的数字进行处理手动分桶

Estimators

同样用iris_data为例,定义Estimator需要一个model_fn,model_fn里以前向传播为主要部分
在调用estimator的训练步骤中,即estimator.train,需要一个input_fn,根据官方doc这个input_fn的返回需要是一个dataset,其中每个元素都是(features,labels),这个两个东西直接作为model_fn的features和labels输入
dataset经过batch后,features就是一个(?,feature_shape)的张量元素,如果每个样本是一个28*28的图片,那么feature就是(?,28,28),而label也一样,如果仅仅是一个标记,那label就是(?,)的一个一维向量元素
inference的时候,你直接就可以吧feature想象成placeholder这个东西


def input_fn_closure2(file_path, epoch, batch_size):
    FIELD_DEFAULTS = [[0.0], [0.0], [0.0], [0.0], [0]]

    def input_fn():
        def _parse_line(line):
            # 解析每个元素
            fields = tf.decode_csv(line, FIELD_DEFAULTS)
            # fields的类型是list,与设想的不同,这个函数只会被执行一次
            # 你可以在这个地方打印一个东西,你会发现这个打印操作只进行了1次
            return fields[:-1], fields[-1]

        # 跳过一个元素
        dataset = tf.data.TextLineDataset(file_path).skip(1)
        dataset = dataset.map(_parse_line)
        print(dataset)
        # 下面一行的作用是取类别为1的所有样本
        # dataset = dataset.filter(lambda a, b: tf.equal(b, 1))
        # 注意batch之后,dataset中存储的单位就变了
        dataset = dataset.shuffle(1000).repeat(epoch).batch(batch_size)
        print(dataset)
        return dataset

    return input_fn


def inference2(features, params):
    print(features)
    net = features
    for num_units in params['hidden_units']:
        net = tf.layers.dense(net, num_units, activation=tf.nn.relu)
    logits = tf.layers.dense(net, params['n_classes'], activation=None)
    return logits


def model_fn2(features, labels, mode, params):
    logits = inference2(features, params)

    predicted_class = tf.argmax(logits, axis=1)
    if mode == tf.estimator.ModeKeys.PREDICT:
        predictions = {
            # 请比较带[:, tf.newaxis]和不带[:, tf.newaxis]的区别
            'class_ids': predicted_class[:, tf.newaxis],
            'probabilities': tf.nn.softmax(logits),
            'logits': logits
        }
        return tf.estimator.EstimatorSpec(mode, predictions=predictions)
    loss = tf.losses.sparse_softmax_cross_entropy(labels, logits)

    accuracy = tf.metrics.accuracy(labels, predicted_class, name='acc_op')
    metrics = {
        'accuracy': accuracy
    }
    if mode == tf.estimator.ModeKeys.EVAL:
        return tf.estimator.EstimatorSpec(mode, loss=loss, eval_metric_ops=metrics)

    assert mode == tf.estimator.ModeKeys.TRAIN

    optimizer = tf.train.AdamOptimizer(learning_rate=0.1)
    train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
    return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)


def build_estimator2():
    feature_columns = iris.get_feature_columns()
    # 当然由于这个模型训练的太快= =2秒一存也存不够10个
    my_checkpointing_config = tf.estimator.RunConfig(
        save_checkpoints_secs=2,  # Save checkpoints every 2 seconds.
        keep_checkpoint_max=10,  # Retain the 10 most recent checkpoints.
    )
    # 如果model_dir存在模型,那么就会直接读取文件夹下面最新的模型,并且检查文件模型和代码中模型是否兼容
    classifier = tf.estimator.Estimator(model_fn=model_fn2, model_dir='guide/estimator_test/model/iris',
                                        config=my_checkpointing_config,
                                        params={'feature_columns': feature_columns,
                                                # Two hidden layers of 10 nodes each.
                                                'hidden_units': [10, 10],
                                                # The model must choose between 3 classes.
                                                'n_classes': 3,
                                                })
    return classifier


def train_eval_test2(train_file_path, epoch, batch_size, test_file_path):
    model = build_estimator2()
    model.train(input_fn=input_fn_closure2(train_file_path, epoch, batch_size), steps=1000)
    print(model.evaluate(input_fn=iris.input_fn_closure2(test_file_path, 1, batch_size)))

    expected = ['Setosa', 'Versicolor', 'Virginica']
    predict_x = {
        'SepalLength': [5.1, 5.9, 6.9],
        'SepalWidth': [3.3, 3.0, 3.1],
        'PetalLength': [1.7, 4.2, 5.4],
        'PetalWidth': [0.5, 1.5, 2.1],
    }
    predict_x = [[5.1, 5.9, 6.9],
                 [3.3, 3.0, 3.1],
                 [1.7, 4.2, 5.4],

                 [0.5, 1.5, 2.1]
                 ]
    predict_x = [[l[i] for l in predict_x] for i in range(3)]
    # 由于训练的时候进行了batch,为了保证训练和测试的一致性,因此测试的时候也要batch
    # 因为对于dataset来讲,batch前后存储的数据格式是不一样的
    # 并且测试的时候,dataset里不需要label
    # 训练集的dataset的格式是(dict:[string,1维array],),因为会batch
    predictions = model.predict(
        input_fn=lambda: tf.data.Dataset.from_tensor_slices(predict_x).batch(1))

    template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')

    for pred_dict, expec in zip(predictions, expected):
        # class_id = pred_dict['class_ids'][0]
        # probability = pred_dict['probabilities'][class_id]
        #
        # print(template.format(iris.SPECIES[class_id],
        #                       100 * probability, expec))
        print(pred_dict)
        print(expec)
        
train_eval_test2('data/iris/iris_training.csv', None, 100, 'data/iris/iris_training.csv')

Dataset

Tensorflow中数据集的使用方法(tf.data.Dataset)
以iris_train,iris_test两个文件介绍TextlineDataset的用法

FIELD_DEFAULTS = [[0.0], [0.0], [0.0], [0.0], [0]]
def input_fn(file_path):
    def _parse_line(line):
        # 解析每个元素
        fields = tf.decode_csv(line, FIELD_DEFAULTS)
        # fields的类型是list,与设想的不同,这个函数只会被执行一次
        # 你可以在这个地方打印一个东西,你会发现这个打印操作只进行了1次
        return fields[:-1], fields[-1]
    # 跳过一个元素
    dataset = tf.data.TextLineDataset(file_path).skip(1)
    dataset = dataset.map(_parse_line)
    # 注意,filter函数里的接受参数数量一定要和dataset里的每个元素包括的元素数量一致
    # 另外,判断相等的时候,一定要用tf.equal,因为实际上ab都是tensor,不能直接用==
    dataset = dataset.filter(lambda a, b: tf.equal(b, 1))
    #把下面的注释再打开看看结果有什么不同,体会一下
    #dataset = dataset.batch(2)
    return dataset

if __name__ == '__main__':
    data = input_fn('data/iris/iris_test.csv')
    iter = data.make_one_shot_iterator()
    x = iter.get_next()
    # x是(, )
    # 也就是说x里有两个元素
    print(x)
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))

from_tensor_slices代码,第一个是以元组方式输入,第二个是以列表形式输入,第一个dataset的第一个元素就是元组中4个数字里的第一个数字(也就是4个1),而第二个dataset的第一个元素,就是[1,2]

features_dataset = tf.data.Dataset.from_tensor_slices(([1,2], [1,2], [1,2], [1,2]))
print(features_dataset)
features_dataset = tf.data.Dataset.from_tensor_slices([[1,2], [1,2], [1,2], [1,2]])
print(features_dataset)

TFRecordWriter

A tf.Operation that, when run, writes contents of dataset to a file.write方法返回的是一个op,需要run的,官方教程里用了tf.enable_eager_execution(),所以直接就执行了

注意dataset的迭代器的性质,下面两个session的结果是不一样的,每次run都相当于请求数据,每次请求的数据,只要用到了dataset中的数据,都会导致有数据流开始flow,所以第一个session才是正确的访问数据的方式,而第二个session,每次run都会导致get_next的执行

tf.reset_default_graph()
ds = tf.data.Dataset.from_tensor_slices([[1,2],[2,4],[3,6],[4,8],[5,10]])
ds=ds.map(lambda x:(x[0],x[1]))
i=ds.make_one_shot_iterator()
x,y=i.get_next()
a=tf.add(x,1)
b=tf.multiply(y,2)
with tf.Session() as sess:
    try:
        while True:
            print(sess.run([x,y,a,b]))
    except tf.errors.OutOfRangeError:
            print('循环正常结束')
with tf.Session() as sess:
    try:
        while True:
            print(sess.run([x,y]))
            print(sess.run([a,b]))
    except tf.errors.OutOfRangeError:
            print('循环正常结束')

Tensorflow踩坑记之头疼的tf.data 各种iterator

TFRecord

转化为TFRecord

以iris_train为例介绍用法
下面的程序演示了如何将一个文件转化成TFRecord文件,需要注意的是,tf.train.Feature,tf.train.Int64List,tf.train.Example对象构建的时候,他们的传入参数名称都不能省略,比如float_list=,int64_list=,features=等等,并且value=接的参数必须是可迭代对象,比如说列表,并且floatlist的存储方式是tf.float32
写入的时候tf.train.Features的feature参数存的是一个dict,key为特征名,value为Feature对象
下面这种方法是以python_io的方式来写入tf_rec文件

import tensorflow as tf

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                    'PetalLength', 'PetalWidth', 'Species']
SPECIES = ['Setosa', 'Versicolor', 'Virginica']


def _float_feature(value):
    return tf.train.Feature(float_list=tf.train.FloatList(value=[value]))


def _int64_feature(value):
    return tf.train.Feature(int64_list=tf.train.Int64List(value=[int(value)]))


def convert_to_tf_record(file_path='data/iris/iris_training.csv', output_path='data/tf_record/iris_training.tfr'):
    import pandas as pd
    def feature_dict(row: pd.Series):
        d = {CSV_COLUMN_NAMES[i]: _float_feature(row[i]) for i in range(4)}
        d[CSV_COLUMN_NAMES[-1]] = _int64_feature(row[-1])
        return d

    iris = pd.read_csv(file_path, header=None, names=CSV_COLUMN_NAMES, skiprows=1)
    with tf.python_io.TFRecordWriter(output_path) as writer:
        for index, row in iris.iterrows():
            example = tf.train.Example(features=tf.train.Features(feature=feature_dict(row)))
            writer.write(example.SerializeToString())
        writer.close()

过些阵子我补一个用dataset写tfrec文件的代码

用dataset读取tfrecord文件

以下是用dataset读取tfrecord文件的第一种方式,在这里,刚开始读取的dataset里,每个元素都是一个Example对象,并且通过map每次只对一个example进行解析,其中dataset的形状为DatasetV1Adapter shapes: (( ), ( ), (), (), ()), types: (tf.float32, tf.float32, tf.float32, tf.float32, tf.int64)

import tensorflow as tf

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                    'PetalLength', 'PetalWidth', 'Species']
def get_iris_tfrecord_dataset_with_single_parse(file_path='data/tf_record/iris_training.tfr'):
    feature_dict = {CSV_COLUMN_NAMES[i]: tf.FixedLenFeature([], tf.float32) for i in range(4)}
    feature_dict[CSV_COLUMN_NAMES[-1]] = tf.FixedLenFeature([], tf.int64)

    def _parse(example):
        features = tf.parse_single_example(example, features=feature_dict)
        return [features[name] for name in CSV_COLUMN_NAMES]

    dataset = tf.data.TFRecordDataset(file_path)
    dataset = dataset.map(_parse)
	print(dataset)
    iter = dataset.make_one_shot_iterator()
    x = iter.get_next()
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))
    return dataset

以下是用dataset读取tfrecord文件的第二种方式,在这里,刚开始读取的dataset里,每个元素都是一个Example对象,然后,先进行了batch操作通过map每次只对每个batch内的多个example进行解析,dataset的形状会变成DatasetV1Adapter shapes: ((?, ), ( ?,), (?, ), (?, ), (?, )), types: (tf.float32, tf.float32, tf.float32, tf.float32, tf.int64),也就是说,parse_example是对每个batch内所有example进行解析操作

import tensorflow as tf

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                    'PetalLength', 'PetalWidth', 'Species']
def get_iris_tfrecord_dataset_with_parse(file_path='data/tf_record/iris_training.tfr'):
    feature_dict = {CSV_COLUMN_NAMES[i]: tf.FixedLenFeature([], tf.float32) for i in range(4)}
    feature_dict[CSV_COLUMN_NAMES[-1]] = tf.FixedLenFeature([], tf.int64)

    def _parse(example):
        features = tf.parse_example(example, features=feature_dict)
        return [features[name] for name in CSV_COLUMN_NAMES]

    dataset = tf.data.TFRecordDataset(file_path)
    dataset = dataset.batch(10)
    dataset = dataset.map(_parse)
	print(dataset)
    iter = dataset.make_one_shot_iterator()
    x = iter.get_next()
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))
    return dataset

FixedLenFeature

从tfrecord文件解析的时候,需要指定features=这个参数,这个参数是一个dict,key为特征名,value为一个FixedLenFeature或者VarLenFeature,通过这两个东西,我们可以确定每个特征的形状以及类型
上一节,在第一种使用parse_single_example解析example的代码中,我使用了FixedLenFeature([], tf.float32),意思就是,某个特征,他的形状是[],这代表的是,该特征里存的就是一个,并且是tf.float32类型的,你把这个FixedLenFeature([], tf.float32)换成FixedLenFeature([1], tf.float32),这样解析的时候,就会认为这个特征里存的是一个1维度长度为1的向量,重新跑第一个程序,dataset的变成DatasetV1Adapter shapes: ((1, ), (1, ), (1, ), (1, ), (1, )), types: (tf.float32, tf.float32, tf.float32, tf.float32, tf.int64)

Tensorflow高阶读写教程,中有提到,example里的一个特征里可以简单的存一个数,也可以存多个数,但这个特征里的多个值的类型一定要相同

但对于第二种使用parse_example解析example的代码中,FixedLenFeature([], tf.float32)换成FixedLenFeature([1], tf.float32),结果会不同,代码如下,dataset的形状同样会变成,和之前的是一个道理
另外,在解析对象的时候,即使文件中的某个特征是int类型存的,读取的时候仍然可以按照float类型来解析,但反过来不行

import tensorflow as tf

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                    'PetalLength', 'PetalWidth', 'Species']
def get_iris_tfrecord_dataset_with_parse(file_path='data/tf_record/iris_training.tfr'):
    feature_dict = {CSV_COLUMN_NAMES[i]: tf.FixedLenFeature([1], tf.float32) for i in range(4)}
    feature_dict[CSV_COLUMN_NAMES[-1]] = tf.FixedLenFeature([1], tf.int64)

    def _parse(example):
        features = tf.parse_example(example, features=feature_dict)
        return [features[name] for name in CSV_COLUMN_NAMES]

    dataset = tf.data.TFRecordDataset(file_path)
    dataset = dataset.batch(10)
    dataset = dataset.map(_parse)
	print(dataset)
    iter = dataset.make_one_shot_iterator()
    x = iter.get_next()
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))
    return dataset

下面的代码演示了,一个特征里存多个数的方法,如代码所示,一个example里只有一个名为data的特征,这个特征里存了两个数,那么读取的时候,就要指定data这个特征里的存的数字的数目是2

def multi_rank_convert_to_tf_record(output_path='data/tf_record/multirank.tfr'):
    data = [[1,2],[3,4]]
    with tf.python_io.TFRecordWriter(output_path) as writer:
        for row in data:
            example = tf.train.Example(features=tf.train.Features(feature={
                'data':tf.train.Feature(int64_list=tf.train.Int64List(value=row))
            }))
            writer.write(example.SerializeToString())
        writer.close()

def get_multi_rank_tf_dataset(file_path='data/tf_record/multirank.tfr'):
    def _parse(example):
        features = tf.parse_single_example(example,features={'data':tf.FixedLenFeature([2],tf.int64)})
        return features
    dataset = tf.data.TFRecordDataset(file_path)
    dataset = dataset.map(_parse)

    print(dataset)
    iter = dataset.make_one_shot_iterator()
    x = iter.get_next()
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))
    return dataset

VarLenFeature

顾名思义,就是变长特征,返回的是SparseTensorValue

def multi_rank_convert_to_tf_record(output_path='data/tf_record/multirank.tfr'):
    # data = [[1,2],[3,4]]
    data = [[1],[3,4,5]]
    with tf.python_io.TFRecordWriter(output_path) as writer:
        for row in data:
            example = tf.train.Example(features=tf.train.Features(feature={
                'data':tf.train.Feature(int64_list=tf.train.Int64List(value=row))
            }))
            writer.write(example.SerializeToString())
        writer.close()

def get_multi_rank_tf_dataset(file_path='data/tf_record/multirank.tfr'):
    def _parse(example):
        # features = tf.parse_single_example(example,features={'data':tf.FixedLenFeature([2],tf.int64)})
        features = tf.parse_single_example(example,features={'data':tf.VarLenFeature(tf.int64)})
        return features
    dataset = tf.data.TFRecordDataset(file_path)
    dataset = dataset.map(_parse)

    print(dataset)
    iter = dataset.make_one_shot_iterator()
    x = iter.get_next()
    with tf.Session() as sess:
        print(sess.run(x))
        print(sess.run(x))
    return dataset

CNN

TensorFlow中CNN的两种padding方式“SAME”和“VALID”

import tensorflow as tf

input = tf.ones([1, 5, 5, 1])
filter_weight = tf.Variable(tf.ones([3, 3, 1, 4]))
conv1 = tf.nn.conv2d(input, filter_weight, strides=[1, 1, 1, 1], padding='SAME')
conv2 = tf.nn.conv2d(input, filter_weight, strides=[1, 1, 1, 1], padding='VALID')
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(conv1))
    print(sess.run(conv2))

你可以打印一下input是啥,这个input和我想象的不太一致,不过input[0,:,:,0]代表的就是第一个图片的第一个channal
conv1的输出和conv2不一样,SAME是因为补充零了,怎么补充的呢,你把input想象成一张55并且只有一个通道的图片,然后用一个33并且输出为4通道的filter卷积,print(sess.run(conv1)[0,:,:,0])就是这张图片的第一层输出,可以看到,在四周都进行了padding的补0操作

RNN

RNNCell使用

tf.layers

下面两个conv2d_layer是参数共享的

net = tf.Variable([[[[2.,3.]]]])
with tf.variable_scope('name') as scope:
    conv2d_layer = tf.layers.Conv2D(2,[1,1])
    out = conv2d_layer(net)
    print(conv2d_layer.variables)
with tf.variable_scope('name',reuse=True) as scope:
    conv2d_layer = tf.layers.Conv2D(2,[1,1])
    out = conv2d_layer(net)
    print(conv2d_layer.variables)

tf.contrib.slim

tf.contrib.slim简介
Slim下的函数介绍(一)
TensorFlow-slim 训练 CNN 分类模型
TensorFlow 使用预训练模型 ResNet-50

get_init_fn(checkpoint_exclude_scopes)是不恢复exclude里的变量
get_trainable_variables(scopes_to_freeze )是获取除了scopes_to_freeze包含的变量,送给minimize,就可以保证不更新scopes_to_freeze里的参数了

Queue与Reader

十图详解tensorflow数据读取机制(附代码)
Tensorflow–tf.FIFOQueue详解
tensorflow队列操作详解

下面这两段代码都会block,第一段是因为,用的是enqueue,每次入队只进一个元素,就是[3,2,1],后面的并没有入队。第二段是因为队列满了,enqueue被block了,所以后面的操作都进行不了

import tensorflow as tf

input_data=[[3.,2.,1.],[11.,22.,33.],[111.,222.,333.]]
q=tf.FIFOQueue(3,dtypes=[tf.float32])
init=q.enqueue(input_data)
output_data=q.dequeue()

with tf.Session() as sess:
    init.run()
    init.run()
    print('1:',sess.run(output_data))
    print('2:',sess.run(output_data))
    print('3:',sess.run(output_data))
    sess.run(q.close(cancel_pending_enqueues=True))
    print(sess.run(q.is_closed()))
with tf.Session() as sess:
	qr = tf.FIFOQueue(capacity=3, dtypes=[tf.uint8], shapes=((), ))
    en_qr = qr.enqueue_many([[1, 2, 3,4,5,6]])
    sess.run(en_qr)
    de_qr = qr.dequeue()
    res = sess.run(de_qr)
    print(res)
    de_qr = qr.dequeue()
    res = sess.run(de_qr)
    print(res)
import tensorflow as tf

if __name__ == '__main__':
    with tf.Session() as sess:
        qr = tf.FIFOQueue(capacity=6, dtypes=[tf.uint8], shapes=((),))
        en_qr = qr.enqueue_many([[1, 2, 3, 4, 5, 6]])
        sess.run(en_qr)
        de_qr = qr.dequeue()
        res = sess.run(de_qr)
        print(res)
        de_qr = qr.dequeue()
        res = sess.run(de_qr)
        print(res)
        
    with tf.Session() as sess:
        qr = tf.FIFOQueue(capacity=6, dtypes=[tf.uint8], shapes=((),))
        en_qr = qr.enqueue([1])
        sess.run(en_qr)
        de_qr = qr.dequeue()
        res = sess.run(de_qr)
        print(res)
        en_qr = qr.enqueue([2])
        sess.run(en_qr)
        de_qr = qr.dequeue()
        res = sess.run(de_qr)
        print(res)

Word2Vec

tensorflow word2vec demo详解

Saver

一个快速完整的教程,以保存和恢复Tensorflow模型

import tensorflow as tf
# 代码1,储存模型
v1 = tf.Variable(tf.constant(2.0, shape=[1]), name='v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='v2')
result = v1 + v2
saver = tf.train.Saver()
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver.save(sess,'ckpt_dir/model.ckpt')
import tensorflow as tf

# 代码2 读取数据
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name='v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='v2')
result = v1 + v2
saver = tf.train.Saver()

with tf.Session() as sess:
    saver.restore(sess, 'ckpt_dir/model.ckpt')
    print(sess.run(v1))
    print(sess.run(result))
# [2.]
# [4.]

下面代码如果把初始化放在restore后面,那么结果返回的就是1和3

import tensorflow as tf

# 代码3 读取只读取v1
v1 = tf.Variable(tf.constant(1.0, shape=[1]), name='v1')
v2 = tf.Variable(tf.constant(2.0, shape=[1]), name='v2')
result = v1 + v2
saver = tf.train.Saver([v1])

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, 'ckpt_dir/model.ckpt')
    print(sess.run(v1))
    print(sess.run(result))

# [2.]
# [4.]

tensorflow 只恢复部分模型参数
浅谈Tensorflow模型的保存与恢复加载
我改了一下代码
restore的时候需要指定具体哪个model,所以latest_checkpoint这个方法就很好用了

#存储pb
import tensorflow as tf
import os
from tensorflow.python.framework import graph_util

pb_file_path = 'ckpt_dir/combined_model.pb'
x = tf.placeholder(tf.int32, shape=[1, 3], name='x')
weights = tf.Variable([[2], [2], [1]])
b = tf.Variable(1, name='b')
wx = tf.matmul(x, weights)
# 这里的输出需要加上name属性
op = tf.add(wx, b, name='op_to_store')

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

path = os.path.dirname(os.path.abspath(pb_file_path))
if os.path.isdir(path) is False:
    os.makedirs(path)

# convert_variables_to_constants 需要指定output_node_names,list(),可以多个
constant_graph = graph_util.convert_variables_to_constants(sess, sess.graph_def, ['op_to_store'])
with tf.gfile.FastGFile(pb_file_path, mode='wb') as f:
    f.write(constant_graph.SerializeToString())

# test
feed_dict = {x: [[1, 2, 3]]}
print(sess.run(op, feed_dict))
#读取pb
import tensorflow as tf
from tensorflow.python.platform import gfile

def restore_mode_pb(pb_file_path):
  sess = tf.Session()
  with gfile.FastGFile(pb_file_path, 'rb') as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())
    sess.graph.as_default()
    tf.import_graph_def(graph_def, name='')

  print(sess.run('b:0'))

  input_x = sess.graph.get_tensor_by_name('x:0')
  # input_y = sess.graph.get_tensor_by_name('y:0')

  op = sess.graph.get_tensor_by_name('op_to_store:0')

  ret = sess.run(op, {input_x: [[2,2,3]]})
  print(ret)

restore_mode_pb('ckpt_dir/combined_model.pb')

tensorflow saver和checkpoint总结

image

draw_bounding_boxes

就是一个给图片上画框框的方法,bbox是一个[batch, num_bounding_boxes, 4]的张量,可以画num_bounding_boxes个框框,比如说下面代码,这个bbox就是画两个框框,但是我发现总有时候画不上去,我也不太懂咋回事

bbox = tf.constant([[[0.1, 0.35, 0.6, 0.75],[0.15, 0.3, 0.3, 0.4]]],
                         dtype=tf.float32,
                         shape=[1, 2, 4])
image_with_box = tf.image.draw_bounding_boxes(tf.expand_dims(image, 0),
                                                  bbox)

tf.image.sample_distorted_bounding_box

其实《google实战》那本书里有提到过这个方法,就是随机生成一个框框,至于bounding_boxes,讲得就是图片里哪些范围内是有信息量的,然后min_object_covered参数指的是The cropped area of the image must contain at least this fraction of any bounding box supplied.就是输出的框框至少应该包括百分之多少的bounding_boxes

找不到API的各种方法

tensorflow中的control_flow_ops.switch函数介绍
Tensorflow(4)-control_flow_ops.cond
How to use the function merge and switch of tensorflow?

图片处理

深入学习图像处理——图像相似度算法——基于PILLOW

问题与解决

  1. 报错LossTensor is inf or nan,网上说都是什么学习率太大了,梯度跑飞了,这个可以理解,但是我遇到这个问题,是因为用了tf.losses.sparse_softmax_cross_entropy(labels=label_batch, logits=out)这个方法,在这个方法里,labels是标签,比如可以是[0,1,2],代表三个样本,第一个样本的类标签是0;logits就是输出的几率,比如说可以是[[0.5,0.4,0.1],[0.4,0.5,0.1],[0.2,0.2,0.6]]。(这个方法实际上就在调用tf.nn.sparse_softmax_cross_entropy_with_logits.
    但是我为什么会报上面那个错呢,是因为样本类别的数目是456,就是从0到455,所以labels的输入的类标签就应该是0到455,但是我把输出层的神经元节点数目搞错了,输出层的神经元节点数目变成了455,也就是说,每个样本的logits的维度数目是455。而恰好我又是在GPU上跑的,官方文档对于labels这个参数有说明Tensor of shape [d_0, d_1, ..., d_{r-1}] (where r is rank of labels and result) and dtype int32 or int64. Each entry in labels must be an index in [0, num_classes). Other values will raise an exception when this op is run on CPU, and return NaN for corresponding loss and gradient rows on GPU.,就是说如果labels和logits的类数目对应不上,在CPU上会报错,但是GPU上会导致返回值为NaN,所以就导致了上面的错误

你可能感兴趣的:(机器学习与数据挖掘,Python)