前言:前面在介绍使用tensorflow进行data pipeline的时候,遇到了一些问题,特意整理了两篇文章,请参见:
tfrecord文件的map在使用的时候所踩的坑总结(map、py_function、numpy_function)
tensorflow2.x之由dataset.map引发出的关于tf.py_function以及tf.numpy_function问题
本系列文章将深入详解这是哪个函数,即tf.py_function()、tf.py_func、tf.numpy_function(),其中
tf.py_function()、tf.numpy_function()是tensorflow2.x版本的,我使用的版本是tensorflow2.1
tf.py_func () 是tensorflow1.x版本的,我们使用的是tensorflow1.13.1
理解的核心,将上面三者包装的python函数理解成tensorflow的operation,只不过函数的参数与返回值有所限制。
1.1 描述与声明
作用:Wraps a python function into a TensorFlow op that executes it eagerly.
将一个Python函数包装成一个tensorflow的operation操作,然后在eager模式下执行,它的作用是包装自定义的Python函数的,那什么时候回用到呢?
当我们需要进行一些tensorflow没有的操作,需要我们自己编写运算逻辑的时候,我们就需要用到这个函数。
tf.py_function(
func, inp, Tout, name=None
)
参数解析 |
|
---|---|
func |
即需要包装的函数,是一个python函数,这个函数接受一个或者是多个Tensor对象作为函数参数,并且返回一个或者是多个Tensor,当然也可以返回None,需要注意的函数的参数定义的Tensor需要与inp所指定的参数个数相符合;返回的Tensor的元素的类型需要与Tout所指定的元素类型相符合。 |
inp |
A list of Tensor objects. 由一个或者是几个Tensor组成的list |
Tout |
由tensorflow的数据类型组成的list或者是tuple,要与func函数的返回值是匹配的,如果是空的话则代表func函数无返回值,即None。 |
name |
A name for the operation (optional). |
返回值:
该函数的返回值就是定义的python函数func运算之后的结果,在tensorflow2.x中,尅直接获取该返回值,返回值可以是一个或者是多个,但是需要注意的是,不管函数func返回的是什么内容,py_function的返回值总是会自动将其包装成与之对应的匹配的Tensor再返回,在tf2.x中,自动包装成EagerTensor,可以直接获取值。
1.2 简单示例一
问题描述,现在有一个分段函数,函数定义如下:
当x 当x>m的时候,f(x)=m**2(1-2logm)+log(x**2) 当然我们可以直接使用tensorflow的内定义算子来实现上面的运算,但是我们通过下面的方式来实现 1.3 简单示例二 比如我要实现这样一个算子,来对两个张量tensor进行如下操作, 第一,他们的和减去它们的差 第二,他们的和加上他们的差 并且同时返回这两个结果,代码如下: 1.4 简单示例三 现在有一个字符串,这个字符串是一个文件名称,我想使用python方法来读取这个文本文件里面的内容,然后返回它的读取的值,比如文件名称如下:file1.txt,里面的内容是1,2,3,4,5 读取里面的内容,然后返回它们求和的结果。 1.5 注意事项 (1)自定义的python函数func函数体中的内容不能够被序列化为GraphDef,因此这里面的内容不能够通过序列化保存graph结构,也不能够restore,这是很重要的; (2)函数的定义必须与调用包装函数py_function在同一个空间中,如果使用tensorflow的分布式训练,必须要进行相关的设置,参考官方文档如下: https://www.tensorflow.org/api_docs/python/tf/py_function 2.1 描述与声明 作用:依然是将python函数包装成tensorflow的operation Args 是一个Python函数,需要格外注意的是,这个函数的参数必须是一个或者是多个numpy.ndarray类型,不再是Tensor,它的返回值也是一个或者是多个array数组,并且函数参数必须与inp的参数相匹配,返回的array的数据类型必须与Tout指定的类型相匹配。 因为它的参数和返回值都必须是numpy数组类型,所以不再像tf2.x的py_function那么灵活了,使用起来不太好用,下面的例子会说明。 2.2 看一个简单的例子 2.3 再看一个案例 描述,现在我需要自己定义一个这样的功能函数,根据传递的参数,来对一个数组进行按行求和或者是按列求和,代码如下: 这个函数实际上有点类似于py_func,它所包装的函数只能够接受numpy.array作为函数参数,并且返回的也是numpy.array,这里就不再赘述了。 import tensorflow as tf
def log_huber(x, m):
'''
因为是tensorflow2.1,所以默认使用的是EagerTensor,我们可以在里面直接使用python的条件判断,而不用使用
tf.while_loop、tf.cond等条件判断
'''
if tf.abs(x) <= m:
return x**2 # 无论这里返回什么,总是会被包装成默认的与之匹配的Tensor返回,这是自动实现的
else:
return m**2 * (1 - 2 * tf.math.log(m) + tf.math.log(x**2))
x = tf.Variable(initial_value = 1.5)
m = tf.Variable(initial_value = 2.0)
with tf.GradientTape(persistent=True) as g:
g.watch(x)
y = tf.py_function(func=log_huber, inp=[x, m], Tout=tf.float32)
print(y) # 应该是1.5**2 = 2.25
dy_dx = g.gradient(y, x) # y对x求导,应该是2x,即3.0
print(dy_dx)
def add_sub(x, y):
'''
在此函数中使用纯python编程方式
'''
x_ = x.numpy()
y_ = y.numpy()
result1 = x_ + y_ - (x_ - y_)
result2 = x_ + y_ + (x_ - y_)
# 返回的就是普通的python对象,但是它会自动转化成tensor来作为最终的结果,是自动完成的
return result1,result2
x = tf.Variable(initial_value = [10,20,30])
y = tf.Variable(initial_value = [100,200,300])
y1,y2 = tf.py_function(func=add_sub, inp=[x, y], Tout=[tf.int32,tf.int32])
print(y1)
print(y2)
'''
tf.Tensor([200 400 600], shape=(3,), dtype=int32)
tf.Tensor([20 40 60], shape=(3,), dtype=int32)
'''
def read_txt(filename):
filename_ = filename.numpy() # 通过numpy获取每一条样本的文件名称
filename_ = filename_.decode("utf-8") # 需要将bytes转化成str,得到 file1.txt、file2.txt
# 现在可以使用纯python操作了,获取文件路径
filename = "./" + filename_
f = open(filename,mode="r")
s =f.readline()
x_ =s.split(',')
result =[]
for i in x_:
result.append(int(i))
result_sum = sum(result)
return result,result_sum
x = tf.Variable(initial_value = "file1.txt")
y1,y2 = tf.py_function(func=read_txt, inp=[x], Tout=[tf.int32,tf.int32])
print(y1)
print(y2)
'''
tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
tf.Tensor(15, shape=(), dtype=int32)
'''
二、tensorflow1.x的tf.py_func()详解
tf.py_func(
func, inp, Tout, stateful=True, name=None
)
func
inp
A list of
Tensor
objects.由Tensor组成列表,由于func只接受array,所以我们一般传递给inp的都只是placeholder占位符
Tout
返回的Tensor的类型
import tensorflow as tf
import os
import numpy as np
def my_func(x): # 参数X是numpy类型,所以在下面传递的时候不能再传递Tensor给inp参数,只能放一个空的placeholder
print(type(x)) #
def my_func(x,m):
'''
x: 是一个array的二维数组
m: 是一个数值,但要写成array的形式,如[1],因为他只能接受array的参数
'''
if m[0] == 1.0:
return np.sum(x,axis=0)
else:
return np.sum(x,axis=1)
input_x = tf.placeholder(tf.float32,shape=(2,3))
input_m = tf.placeholder(tf.float32,shape=(1,))
y = tf.py_func(my_func, [input_x,input_m], tf.float32)
x_ = np.array([[1.0,2.0,3.0],[4.0,5.0,6.0]])
m_ =np.array([1.0])
with tf.Session() as sess:
y_ = sess.run(y, feed_dict={input_x:x_,input_m:m_})
print(y_)
'''
[5. 7. 9.]
'''
三、tensorflow2.x的tf.numpy_function()