tensorflow2.x之由dataset.map引发出的关于tf.py_function以及tf.numpy_function问题

这篇博客介绍了在TensorFlow中遇到的问题,涉及EagerTensor和Tensor的区别,以及如何使用tf.py_function解决在dataset.map中读取文件的错误。EagerTensor可以在python函数中直接交互,而Tensor则需要特定方式。文章通过实例解释了tf.py_function的作用,以及如何包装自定义python函数以适应TensorFlow的数据处理流程。

前言:tensorflow是一个庞大的系统,里面的函数很多,实现了很多常规的一些操作,但是始终没有办法涵盖所有的操作,有时候我们需要定义一些自己的操作逻辑来实现制定的功能,发现没那么简单,本文是在编写tf.data.DataSet的时候出现的一个问题,做了一个集中化的总结,会涉及到以下概念:

EagerTensor和Tensor,tf.py_function以及tf.numpy_function,dataset.map等等

一、问题描述

需要解决的问题,现在有三个文本文件,分别存在files文件夹中,名称分别为file1.txt、file2.txt、file3.txt,里面的内容分别是如下:

file1.txt

1,2,3,4,5

file2.txt

11,22,33,44,55

file3.txt

111,222,333,444,555

每一个文件的标签分别为,1,2,3,现在假设已经经过独热编码,则类别分别为

[ [1,0,0],[0,1,0],[0,0,1] ]

先在我需要通过dataset标准pipeline来读取这三个样本,以便于放入神经网络进行训练,显然,我需要对每一个文本文件进行读取操作,需要使用到datase.map()函数,我的代码如下:

X=["file1.txt","file2.txt","file3.txt"]
Y=[[1,0,0],[0,1,0],[0,0,1]]
 
# 构建dataset对象
dataset = tf.data.Dataset.from_tensor_slices((X,Y))  # 第一步:构造dataset对象 
 
# 对每一个dataset的元素,实际上就是一个example进行解析
dataset = dataset.map(read_file)
 
for features,label in dataset:
    print(features)
    print(label)
    print("===========================================================")

 

解析函数read_file如下

def read_file(filename,label):
    tf.print(type(filename))
    tf.print(type(label))
    
    # filename_ = filename.numpy()
    # label_ = label.numpy()
    
    filename = "./files/" + filename
    tf.print("/")
    
    f =  open(filename,mode="r")
    s =f.readline()
    x_ =s.split(',')
    result =[]
    for i in x_:
        result.append(int(i))
    
    return result,label


代码看起来没什么问题,但是运行实际上显示下面错误:

TypeError: expected str, bytes or os.PathLike object, not Tensor

错误的位置在于

f =  open(filename,mode="r")

意思非常简单,就是说读取文件的这个filename应该是一个str,或者是表示路径的对象,而不应该是一个Tensor对象,

注意:这个问题足足困扰了我有2天之久,在google上面找了很久才找到解决方案,中文搜索几乎没合适的答案。

那怎么办呢?

看起来好像很简单,他既然说了这个filename和label是一个Tensor,那就是我们只要读取到这个Tensor里面的值就可以了啊,不就得到字符串嘛,事实上的确如此,tensorflow2.x中告诉我们获取tensor的值可以使用t.numpy()来获取,但是当我们使用了这两个方法的时候我们发现依然还是错误的,又显示下面的错误:

filename_ = filename.numpy()
label_ = label.numpy()
AttributeError: 'Tensor' object has no attribute 'numpy'

Tensor怎么会没有numpy属性呢?我们不都是通过t.numpy()来获取tensor的值得吗?这实际上引出了下面的一个问题。

 

二、区分tf.EagerTensor和tf.Tensor

2.1 简单的例子

先看几个简单的例子:​​​​​​

 
In [59]: a = tf.constant([1,2,3,4,5])
 
In [60]: a
Out[60]: <tf.Tensor: shape=(5,), dtype=int32, numpy=array([1, 2, 3, 4, 5])>
 
In [61]: type(a)
Out[61]: tensorflow.python.framework.ops.EagerTensor

发现两个问题:

(1)这里的a的确是一个Tensor,而且它有属性numpy,我们可以通过a.numpy()来获取它的值

(2)它的类型本质上是一个 EagerTensor,

而上面的Tensor之所以没有numpy属性是因为它是这个样子的 

<class 'tensorflow.python.framework.ops.Tensor'>   # 类型
 
tf.Tensor([102 102 102], shape=(3,), dtype=int64)  # Tensor

所以,在tensorflow2.x中,凡是可以用numpy获取值的都是指的是EagerTensor,虽然打印出来显示依然是下面的这种形式:

 <tf.Tensor: ... ...>

而Tensor到底是什么呢?它实际上是静态图中一种Tensor。虽然我们现在是使用的动态库,但是依然是在后台有一个构建graph的过程,Tensor的值并一定能够及时得到,而是需要为如数据之后才能得到,在tensorflow1.x 静态图中,我们需要采用以下方式来获取Tensor的值:

with tf.Session() as sess:
    result = sess.run([t])  # 获取Tensor t 的值
    print(result)
    
    # 或者是
    result = t.eval()

2.2 使用tensorflow2.x的注意事项

关于EagerTensor和Tensor使用的一些注意事项

(1)希望打印看看运算结果,使用tf.print(tensor)而非print(tensor.numpy())

使用tf.print(tensor)能够无论在动态图还是静态图下都能够打印出张量的内容,而print(tensor.numpy())只能在动态图下使用,而且只能够对EagerTensor使用,以下是一个正确的示范:

(2)使用tf.device而非tensor.gpu()、tensor.cpu()

新版本中创建张量时会自动分配到优先级高的设备上,比如存在gpu时,直接会分配到gpu上:

# 需要GPU版本才能看出创建的张量会直接放置到gpu上!CPU版本不行
import tensorflow as tf
print(tf.test.is_gpu_available())
# True
r = tf.random.normal((3, 4))
print(r.device)
# '/job:localhost/replica:0/task:0/device:GPU:0'

对于新版本的设备指定,EagerTensor可以直接通过.cpu().gpu()方法直接将张量移动到对应的设备上,但是tf.Tensor并没有,兼容两者的方法是在tf.device创建的scope下操作。一个在gpu下创建张量并移动到cpu下进行sin操作的错误例子为:

(3)不要遍历张量,尽量使用向量化的操作

EagerTensor是可以被遍历的,但是tf.Tensor不行,所以尽量不要对张量进行遍历,多想一想应该怎么进行向量化的操作,不光动静态图的兼容性都有,向量化之后的速度的提升也是非常大的。

2.3 分析与理解,

我们可以这样理解,

EagerTensor是实时的,可以在任何时候获取到它的值,即通过numpy获取

Tensor是非实时的,它是静态图中的组件,只有当喂入数据、运算完成才能获得该Tensor的值,

那为什么datastep.map(function)

给解析函数function传递进去的参数,即上面的read_file(filename,label)中的filename和label是Tensor呢?

因为对一个数据集dataset.map,并没有预先对每一组样本先进行map中映射的函数运算,而仅仅是告诉dataset,你每一次拿出来的样本时要先进行一遍function运算之后才使用的,所以function的调用是在每次迭代dataset的时候才调用的,但是预先的参数filename和label只是一个“坑”,迭代的时候采用数据将这个“坑”填起来,而在运算的时候,虽然将数据填进去了,但是filename和label依然还是一个Tensor而不是EagerTensor,所以才会出现上面的问题。

注意:两个问题:

(1)Tensor和EagerTensor没有办法直接转化

(2)Tensor没有办法在python函数中直接使用,因为我没办法在python函数中获取到Tensor的值

三、tensorflow与python代码交互的方式——tf.py_function

我们需要自己定义函数的实现,用python编写的函数没有办法直接来与Tensor交互,那怎么办呢?

tensorflow2.x版本提供了函数tf.py_function来时实现自己定义的功能。

3.1 函数原型

tf.py_function(func, inp, Tout, name=None)

作用:包装Python函数,让Python底阿妈可以与tensorflow进行交互

参数:

func :自己定义的python函数名称

inp :自己定义python函数的参数列表,写成列表的形式,[tensor1,tensor2,tensor3] 列表的每一个元素是一个Tensor对象,

         注意与定义的函数参数进行匹配

Tout:它与自定义的python函数的返回值相对应的,

  • 当Tout是一个列表的时候 ,如 [ tf.string,tf,int64,tf.float] 表示自定义函数有三个返回值,即返回三个tensor,每一个tensor的元素的类型与之对应
  • 当Tout只有一个值的时候,如tf.int64,表示自定义函数返回的是一个整型列表或整型tensor
  • 当Tout没有值的时候,表示自定义函数没有返回值

 

3.2 上面所出现的问题的解决方案

(1)定义自己实现的python函数

# dataset.map函数没有直接使用它,而是先用tf.py_function来包装他
def read_file(filename,label):
    tf.print(type(filename))     # 包装之后类型不再是Tensor,而是EagerTensor
    tf.print(type(label))
    
    filename_ = filename.numpy() # 因为是EagerTensor,可以使用numpy获取值,在tensorflow中,字符串以byte存储,所以它的值是  b'xxxxx'  的形式
    label_ = label.numpy()
    
    new_filename = filename_.decode()  # 将byte解码得到str
    new_filename = "./files/" + new_filename
    
    # 先在的new_filename就是纯python字符串了,可以直接打开了
    f =  open(new_filename,mode="r")
    s =f.readline()
    x_ =s.split(',')
    result =[]
    for i in x_:
        result.append(int(i))
    
    return result,label  # 返回,result是一个列表list

(2)定义一个函数来使用tf.py_function来包装自己定义的python函数

z 注意参数的匹配以及类型的匹配
def wrap_function(x,y):
    x, y = tf.py_function(read_file, inp=[x, y], Tout=[tf.int32, tf.int32])
    return x,y

当然我们也可以不用编写包装函数,直接使用lambda表达式一步到位,

如果不使用tf.py_function()来包装这里的读取函数read_file,则read_file的两个参数都是Tensor

而使用了tf.py_function()来包装read_file函数之后,它的参数就变成了EagerTensor,

至于为什么是这样子,我还不是很清楚,望有大神告知!

即如下:

dataset = dataset.map(lambda x, y: tf.py_function(read_file, inp=[x, y], Tout=[tf.int32, tf.int32]))

 

(3)编写dataset的pipeline

X=["file1.txt","file2.txt","file3.txt"]
Y=[[1,0,0],[0,1,0],[0,0,1]]
 
dataset = tf.data.Dataset.from_tensor_slices((X,Y))  # 第一步:构造dataset对象 
 
dataset = dataset.map(wrap_function)
 
dataset=dataset.repeat(3)       # 重复三次                                   
dataset=dataset.batch(3)        # 每次3个样本一个batch
 
 
for features,label in dataset:
    print(features)
    print(label)
    print("=================================================================")

运行结果如下:

tf.Tensor(
[[  1   2   3   4   5]
 [ 11  22  33  44  55]
 [111 222 333 444 555]], shape=(3, 5), dtype=int32)
tf.Tensor(
[[1 0 0]
 [0 1 0]
 [0 0 1]], shape=(3, 3), dtype=int32)
=======================================================================================================
tf.Tensor(
[[  1   2   3   4   5]
 [ 11  22  33  44  55]
 [111 222 333 444 555]], shape=(3, 5), dtype=int32)
tf.Tensor(
[[1 0 0]
 [0 1 0]
 [0 0 1]], shape=(3, 3), dtype=int32)
=======================================================================================================
tf.Tensor(
[[  1   2   3   4   5]
 [ 11  22  33  44  55]
 [111 222 333 444 555]], shape=(3, 5), dtype=int32)
tf.Tensor(
[[1 0 0]
 [0 1 0]
 [0 0 1]], shape=(3, 3), dtype=int32)
=======================================================================================================

可以发现,现在的结果完全吻合!

3.3 关于Tensor与EagerTensor的进一步说明

注意:EagerTensor是可以直接与python代码进行交互的,也可以进行迭代便利操作,不支持与Python直接进行交互的实际上是Tensor,这需要格注意,如下所示的例子:

(1)EagerTensor与python函数的交互

def iterate_tensor(tensor):
    tf.print(type(tensor))  # EagerTensor
    (x1, x2, x3), (x4, x5, x6) = tensor
    return tf.stack([x2, x4, x6])
 
 
const = tf.constant(range(6), shape=(2, 3)) # EagerTensor
o = iterate_tensor(const)
print(o)
'''运行结果为:
<class 'tensorflow.python.framework.ops.EagerTensor'>
tf.Tensor([1 3 5], shape=(3,), dtype=int32)
'''

(2)Tensor与python函数的交互

使用tf.function来修饰函数,如下:

@tf.function
def iterate_tensor(tensor):
    tf.print(type(tensor))  # Tensor
    (x1, x2, x3), (x4, x5, x6) = tensor
    return tf.stack([x2, x4, x6])
 
 
const = tf.constant(range(6), shape=(2, 3)) # EagerTensor
o = iterate_tensor(const)
print(o)

因为使用了tf.function来修饰Python函数,会将其编译为静态图的操作,此时的tensor变为了Tensor,所以上面的代码会出错:

OperatorNotAllowedInGraphError: iterating over `tf.Tensor` is not allowed: AutoGraph did not convert this function. Try decorating it directly with @tf.function.

由此可见tensor变成了Tensor,不允许对其进行迭代操作,会出现错误。

总结:一定要注意区分EagerTensor和tf.Tensor

在动态图下创建的张量是EagerTensor(引用方式为from tensorflow.python.framework.ops import EagerTensor),在静态图下创建的张量是tf.TensorEagerTensortf.Tensor虽然非常相似,但是不完全一样,如果依赖于EagerTensor特有的一些方法,会导致转换到静态图时tf.Tensor没有这些方法而报错

我们很多时候不知道一个tensor到底是EagerTensor还是Tensor呢?最简单的方式就是使用

tf.print(type(tensor_name))

进行查看

 

四、补充——关于tf.py_function和tf.numpy_function

必须承认是的TensorFlow的存在的这么多(len(dir(tf.raw_ops))个,大约1227个)的Op依然不足以完全覆盖numpy所有的功能,因此在一些情况下找不到合适的Op(或者Op组合)表达运算逻辑时,能用上numpy的函数也是挺好的,因此可能会有人会想到先EagerTensor转换成numpy然后用numpy运算完再转换成Tensor,tf.function可不允许这么做,还是老老实实用tf.numpy_function吧。(当然可以自己写Op Kernel然后编译使用,后续看看有没有额外的时间做自定义Op的总结,目前还是把早年立的填2.0的总结的坑的flag搞定再说> <)

关于更多

tf.py_function

tf.numpy_function

的使用请参见后面的例子吧

这是报错信息:Traceback (most recent call last): File "main_SemanticKITTI.py", line 206, in <module> dataset.init_input_pipeline() File "main_SemanticKITTI.py", line 176, in init_input_pipeline self.batch_train_data = self.batch_train_data.map(map_func=map_func) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 1861, in map return MapDataset(self, map_func, preserve_cardinality=True) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4985, in __init__ use_legacy_function=use_legacy_function) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4218, in __init__ self._function = fn_factory() File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3151, in get_concrete_function *args, **kwargs) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3116, in _get_concrete_function_garbage_collected graph_function, _ = self._maybe_define_function(args, kwargs) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3463, in _maybe_define_function graph_function = self._create_graph_function(args, kwargs) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/eager/function.py", line 3308, in _create_graph_function capture_by_value=self._capture_by_value), File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/framework/func_graph.py", line 1007, in func_graph_from_py_func func_outputs = python_func(*func_args, **func_kwargs) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4195, in wrapped_fn ret = wrapper_helper(*args) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/data/ops/dataset_ops.py", line 4125, in wrapper_helper ret = autograph.tf_convert(self._func, ag_ctx)(*nested_args) File "/root/miniconda3/envs/randlanet/lib/python3.6/site-packages/tensorflow/python/autograph/impl/api.py", line 695, in wrapper raise e.ag_error_metadata.to_exception(e) AttributeError: in user code: main_SemanticKITTI.py:145 tf_map * up_i = tf.py_func(DP.knn_search, [sub_points, batch_pc, 1], tf.int32) AttributeError: module 'tensorflow' has no attribute 'py_func' 这是代码:from helper_tool import DataProcessing as DP from helper_tool import ConfigSemanticKITTI as cfg from helper_tool import Plot from os.path import join from RandLANet import Network from tester_SemanticKITTI import ModelTester import tensorflow as tf import numpy as np import os, argparse, pickle class SemanticKITTI: def __init__(self, test_id): self.name = 'SemanticKITTI' self.dataset_path = '/root/autodl-tmp/RandLA-Net-master/data/semantic_kitti/dataset/sequences_0.06' self.label_to_names = {0: 'unlabeled', 1: 'car', 2: 'bicycle', 3: 'motorcycle', 4: 'truck', 5: 'other-vehicle', 6: 'person', 7: 'bicyclist', 8: 'motorcyclist', 9: 'road', 10: 'parking', 11: 'sidewalk', 12: 'other-ground', 13: 'building', 14: 'fence', 15: 'vegetation', 16: 'trunk', 17: 'terrain', 18: 'pole', 19: 'traffic-sign'} self.num_classes = len(self.label_to_names) self.label_values = np.sort([k for k, v in self.label_to_names.items()]) self.label_to_idx = {l: i for i, l in enumerate(self.label_values)} self.ignored_labels = np.sort([0]) self.val_split = '08' self.seq_list = np.sort(os.listdir(self.dataset_path)) self.test_scan_number = str(test_id) self.train_list, self.val_list, self.test_list = DP.get_file_list(self.dataset_path, self.test_scan_number) self.train_list = DP.shuffle_list(self.train_list) self.val_list = DP.shuffle_list(self.val_list) self.possibility = [] self.min_possibility = [] # Generate the input data flow def get_batch_gen(self, split): if split == 'training': num_per_epoch = int(len(self.train_list) / cfg.batch_size) * cfg.batch_size path_list = self.train_list elif split == 'validation': num_per_epoch = int(len(self.val_list) / cfg.val_batch_size) * cfg.val_batch_size cfg.val_steps = int(len(self.val_list) / cfg.batch_size) path_list = self.val_list elif split == 'test': num_per_epoch = int(len(self.test_list) / cfg.val_batch_size) * cfg.val_batch_size * 4 path_list = self.test_list for test_file_name in path_list: points = np.load(test_file_name) self.possibility += [np.random.rand(points.shape[0]) * 1e-3] self.min_possibility += [float(np.min(self.possibility[-1]))] def spatially_regular_gen(): # Generator loop for i in range(num_per_epoch): if split != 'test': cloud_ind = i pc_path = path_list[cloud_ind] pc, tree, labels = self.get_data(pc_path) # crop a small point cloud pick_idx = np.random.choice(len(pc), 1) selected_pc, selected_labels, selected_idx = self.crop_pc(pc, labels, tree, pick_idx) else: cloud_ind = int(np.argmin(self.min_possibility)) pick_idx = np.argmin(self.possibility[cloud_ind]) pc_path = path_list[cloud_ind] pc, tree, labels = self.get_data(pc_path) selected_pc, selected_labels, selected_idx = self.crop_pc(pc, labels, tree, pick_idx) # update the possibility of the selected pc dists = np.sum(np.square((selected_pc - pc[pick_idx]).astype(np.float32)), axis=1) delta = np.square(1 - dists / np.max(dists)) self.possibility[cloud_ind][selected_idx] += delta self.min_possibility[cloud_ind] = np.min(self.possibility[cloud_ind]) if True: yield (selected_pc.astype(np.float32), selected_labels.astype(np.int32), selected_idx.astype(np.int32), np.array([cloud_ind], dtype=np.int32)) gen_func = spatially_regular_gen gen_types = (tf.float32, tf.int32, tf.int32, tf.int32) gen_shapes = ([None, 3], [None], [None], [None]) return gen_func, gen_types, gen_shapes def get_data(self, file_path): seq_id = file_path.split('/')[-3] frame_id = file_path.split('/')[-1][:-4] kd_tree_path = join(self.dataset_path, seq_id, 'KDTree', frame_id + '.pkl') # Read pkl with search tree with open(kd_tree_path, 'rb') as f: search_tree = pickle.load(f) points = np.array(search_tree.data, copy=False) # Load labels if int(seq_id) >= 11: labels = np.zeros(np.shape(points)[0], dtype=np.uint8) else: label_path = join(self.dataset_path, seq_id, 'labels', frame_id + '.npy') labels = np.squeeze(np.load(label_path)) return points, search_tree, labels @staticmethod def crop_pc(points, labels, search_tree, pick_idx): # crop a fixed size point cloud for training center_point = points[pick_idx, :].reshape(1, -1) select_idx = search_tree.query(center_point, k=cfg.num_points)[1][0] select_idx = DP.shuffle_idx(select_idx) select_points = points[select_idx] select_labels = labels[select_idx] return select_points, select_labels, select_idx @staticmethod def get_tf_mapping2(): def tf_map(batch_pc, batch_label, batch_pc_idx, batch_cloud_idx): features = batch_pc input_points = [] input_neighbors = [] input_pools = [] input_up_samples = [] for i in range(cfg.num_layers): neighbour_idx = tf.py_func(DP.knn_search, [batch_pc, batch_pc, cfg.k_n], tf.int32) sub_points = batch_pc[:, :tf.shape(batch_pc)[1] // cfg.sub_sampling_ratio[i], :] pool_i = neighbour_idx[:, :tf.shape(batch_pc)[1] // cfg.sub_sampling_ratio[i], :] up_i = tf.py_func(DP.knn_search, [sub_points, batch_pc, 1], tf.int32) input_points.append(batch_pc) input_neighbors.append(neighbour_idx) input_pools.append(pool_i) input_up_samples.append(up_i) batch_pc = sub_points input_list = input_points + input_neighbors + input_pools + input_up_samples input_list += [features, batch_label, batch_pc_idx, batch_cloud_idx] return input_list return tf_map def init_input_pipeline(self): print('Initiating input pipelines') cfg.ignored_label_inds = [self.label_to_idx[ign_label] for ign_label in self.ignored_labels] gen_function, gen_types, gen_shapes = self.get_batch_gen('training') gen_function_val, _, _ = self.get_batch_gen('validation') gen_function_test, _, _ = self.get_batch_gen('test') self.train_data = tf.data.Dataset.from_generator(gen_function, gen_types, gen_shapes) self.val_data = tf.data.Dataset.from_generator(gen_function_val, gen_types, gen_shapes) self.test_data = tf.data.Dataset.from_generator(gen_function_test, gen_types, gen_shapes) self.batch_train_data = self.train_data.batch(cfg.batch_size) self.batch_val_data = self.val_data.batch(cfg.val_batch_size) self.batch_test_data = self.test_data.batch(cfg.val_batch_size) map_func = self.get_tf_mapping2() self.batch_train_data = self.batch_train_data.map(map_func=map_func) self.batch_val_data = self.batch_val_data.map(map_func=map_func) self.batch_test_data = self.batch_test_data.map(map_func=map_func) self.batch_train_data = self.batch_train_data.prefetch(cfg.batch_size) self.batch_val_data = self.batch_val_data.prefetch(cfg.val_batch_size) self.batch_test_data = self.batch_test_data.prefetch(cfg.val_batch_size) iter = tf.data.Iterator.from_structure(self.batch_train_data.output_types, self.batch_train_data.output_shapes) self.flat_inputs = iter.get_next() self.train_init_op = iter.make_initializer(self.batch_train_data) self.val_init_op = iter.make_initializer(self.batch_val_data) self.test_init_op = iter.make_initializer(self.batch_test_data) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--gpu', type=int, default=0, help='the number of GPUs to use [default: 0]') parser.add_argument('--mode', type=str, default='train', help='options: train, test, vis') parser.add_argument('--test_area', type=str, default='14', help='options: 08, 11,12,13,14,15,16,17,18,19,20,21') parser.add_argument('--model_path', type=str, default='None', help='pretrained model path') FLAGS = parser.parse_args() os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" os.environ['CUDA_VISIBLE_DEVICES'] = str(FLAGS.gpu) os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' Mode = FLAGS.mode test_area = FLAGS.test_area dataset = SemanticKITTI(test_area) dataset.init_input_pipeline() if Mode == 'train': model = Network(dataset, cfg) model.train(dataset) elif Mode == 'test': cfg.saving = False model = Network(dataset, cfg) if FLAGS.model_path is not 'None': chosen_snap = FLAGS.model_path else: chosen_snapshot = -1 logs = np.sort([os.path.join('results', f) for f in os.listdir('results') if f.startswith('Log')]) chosen_folder = logs[-1] snap_path = join(chosen_folder, 'snapshots') snap_steps = [int(f[:-5].split('-')[-1]) for f in os.listdir(snap_path) if f[-5:] == '.meta'] chosen_step = np.sort(snap_steps)[-1] chosen_snap = os.path.join(snap_path, 'snap-{:d}'.format(chosen_step)) tester = ModelTester(model, dataset, restore_snap=chosen_snap) tester.test(model, dataset) else: ################## # Visualize data # ################## with tf.Session() as sess: sess.run(tf.global_variables_initializer()) sess.run(dataset.train_init_op) while True: flat_inputs = sess.run(dataset.flat_inputs) pc_xyz = flat_inputs[0] sub_pc_xyz = flat_inputs[1] labels = flat_inputs[17] Plot.draw_pc_sem_ins(pc_xyz[0, :, :], labels[0, :]) Plot.draw_pc_sem_ins(sub_pc_xyz[0, :, :], labels[0, 0:np.shape(sub_pc_xyz)[1]])
最新发布
07-30
<think>我们正在解决TensorFlow中出现的AttributeError: module 'tensorflow' has no attribute 'py_func'错误。 根据引用[5]的提示,我们可以将`tf.py_func`替换为`tf.compat.v1.py_func`。 因为从TensorFlow 2.x开始,一些1.x的API被移到了`compat.v1`模块下。所以,我们需要使用兼容模式下的函数。 具体步骤: 1. 检查TensorFlow版本,确认是否为2.x。 2. 将代码中的`tf.py_func`替换为`tf.compat.v1.py_func`。 注意:引用[5]中给出了一个示例,将`tf.py_func`改为`tf.compat.v1.py_func`,并且注意函数参数的传递方式。 示例代码修改: 原代码可能如下: result = tf.py_func(func, [input], Tout) 修改后: result = tf.compat.v1.py_func(func, [input], Tout) 但是,请注意,在TensorFlow 2.x中,推荐使用`tf.py_function`(注意有下划线)来代替,因为`tf.compat.v1.py_func`有一些限制(例如,它不能很好地与计算图一起工作,且不支持GPU)。不过,如果我们需要保持与旧代码的兼容性,可以使用`tf.compat.v1.py_func`。 另外,引用[5]中给出的写法是`tf.compat.v1.pyfunc`(没有下划线),但根据TensorFlow文档,正确的函数名是`tf.compat.v1.py_func`(有下划线)。所以我们需要使用`tf.compat.v1.py_func`。 因此,我们有两种解决方案: 方案一:使用`tf.compat.v1.py_func`(兼容1.x的API) 方案二:使用`tf.py_function`(TensorFlow 2.x推荐的方式) 两种方案的区别: - `tf.compat.v1.py_func`:与TensorFlow 1.x的`tf.py_func`相同,它接受一个numpy函数,并且返回numpy数组。它不保留梯度信息,且不能在GPU上运行。 - `tf.py_function`:接受一个Python函数,该函数可以操作和返回TensorFlow张量,并且可以保留梯度(如果函数中的操作是TensorFlow操作的话)。但是,它也有性能上的损失,因为涉及Python解释器的调用。 根据需求选择: 如果只是需要调用一个numpy函数,并且不需要梯度,那么可以使用`tf.compat.v1.py_func`。 如果需要在函数中使用TensorFlow操作,并且需要梯度,那么使用`tf.py_function`。 下面给出两种方法的示例: 方法一(使用`tf.compat.v1.py_func`): import tensorflow as tf # 假设我们有一个numpy函数 def my_func(x): # x是numpy数组 return x * 2 # 在TensorFlow中使用 input_tensor = tf.constant([1, 2, 3]) result = tf.compat.v1.py_func(my_func, [input_tensor], tf.int32) 方法二(使用`tf.py_function`): import tensorflow as tf def my_func(x): # 这里x是TensorFlow张量,我们可以使用TensorFlow操作 return x * 2 input_tensor = tf.constant([1, 2, 3]) result = tf.py_function(func=my_func, inp=[input_tensor], Tout=tf.int32) # 注意:使用tf.py_function时,函数内部的形状信息可能会丢失,需要手动设置 result.set_shape(input_tensor.shape) # 如果形状不变,可以这样设置 因此,根据你的具体情况选择合适的方法。 另外,如果代码中多处出现`tf.py_func`,并且你确定要使用兼容模式,那么可以全局替换。同时,为了确保代码在TensorFlow 2.x上运行,你可能需要在代码开头加入: import tensorflow.compat.v1 as tf tf.disable_v2_behavior() 但这种方法会禁用2.x的特性,不推荐。推荐的方法是逐步迁移到2.x的API。 总结:解决AttributeError: module 'tensorflow' has no attribute 'py_func'的方法有两种: 1. 使用`tf.compat.v1.py_func`(注意函数名有下划线) 2. 使用`tf.py_function`(TensorFlow 2.x推荐) 根据实际需求选择。</think>### TensorFlow AttributeError: module 'tensorflow' has no attribute 'py_func' 解决方法 在 TensorFlow 2.x 中,`tf.py_func` 已被弃用并移动到 `tf.compat.v1` 模块中。以下是两种解决方案: #### 方法一:使用兼容模式(推荐) 直接将 `tf.py_func` 替换为 `tf.compat.v1.py_func`: ```python import tensorflow as tf # 原代码:result = tf.py_func(your_function, [input], output_type) # 修改为 ↓ result = tf.compat.v1.py_func(your_function, [input], output_type) ``` #### 方法二:使用 TensorFlow 2.x 推荐方式 迁移到 `tf.py_function`(支持自动微分和 GPU): ```python import tensorflow as tf # 定义函数(参数为 Eager Tensor) def your_func_wrapper(input_tensor): # 将 Tensor 转为 NumPy 数组 numpy_array = input_tensor.numpy() # 执行自定义操作 result = your_custom_logic(numpy_array) return tf.convert_to_tensor(result, dtype=output_type) # 在计算图中使用 result = tf.py_function( func=your_func_wrapper, inp=[input_tensor], Tout=output_type ) # 显式设置形状(可选) result.set_shape(input_tensor.shape) ``` #### 关键区别说明: | **特性** | `tf.compat.v1.py_func` | `tf.py_function` | |------------------------|-------------------------------------------|--------------------------------------| | **梯度支持** | ❌ 不可微分 | ✅ 支持自动微分 | | **GPU 兼容性** | ❌ 仅在 CPU 运行 | ✅ 支持 GPU | | **输入/输出类型** | NumPy 数组 | Eager Tensor | | **TensorFlow 2.x 推荐** | ⚠️ 兼容旧代码 | ✅ 首选方案 | #### 示例场景(AUROC 计算) ```python from sklearn.metrics import roc_auc_score # 使用兼容模式 def auroc(y_true, y_pred): return tf.compat.v1.py_func( roc_auc_score, (y_true, y_pred), tf.double ) # 使用新API def auroc_v2(y_true, y_pred): return tf.py_function( lambda yt, yp: roc_auc_score(yt.numpy(), yp.numpy()), [y_true, y_pred], tf.double ) ``` > **注意**:如果项目中大量使用 TensorFlow 1.x API,建议在文件开头启用兼容模式: > ```python > import tensorflow.compat.v1 as tf > tf.disable_v2_behavior() # 禁用 TF2 特性 > ``` --- ### 相关问题 1. TensorFlow 2.x 中如何处理 `tf.contrib` 模块被移除的问题?[^1] 2. 如何将 TensorFlow 1.x 的 `tf.Session()` 代码迁移到 TensorFlow 2.x 的 Eager Execution 模式?[^2] 3. TensorFlow 2.x 中遇到 `module 'tensorflow' has no attribute 'variable_scope'` 错误该如何解决?[^3] 4. 为什么 TensorFlow 2.x 会报错 `AttributeError: module 'tensorflow.python.framework.ops' has no attribute '_TensorLike'`?如何修复?[^4] 5.TensorFlow 2.x 中使用自定义 Python 函数时,`tf.py_function` 和 `tf.numpy_function` 有何区别?
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值