自定义指标和损失:AttributeError:“ Tensor”对象在训练期间没有引发“ numpy”属性

时间:2020-03-29 20:36:19

标签: tensorflow keras tensorflow2.0 tf.keras

我正在尝试实现自定义指标函数以及自定义损失函数。两种实现都面临着相同的问题,因此,我将只关注其中之一。

我的目标是在fit方法期间访问张量的值,以便根据存储在y_true和y_pred中的所述值进行计算。 使用内置的Keras后端功能无法完成这些计算

例如,我们有下面的伪代码:

import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Input, LSTM, Dense
from tensorflow.keras.metrics import Metric

x, y = list(), list()
for _ in range(10):
    x.append(np.arange(10))
    y.append(np.random.randint(0, 2))


x = np.reshape(x, (len(x), 1, len(x[0])))
y = np.asarray(y)

class custom_metric(Metric):
    def __init__(self, name = 'custom_metrics', **kwargs):
        super(custom_metric, self).__init__(name = name, **kwargs)
        self.true_positives = self.add_weight(name = 'tp', initializer = 'zeros')

    def update_state(self, y_true, y_pred, sample_weight = None):
        self.test(y_true, y_pred)
        # In a real application, new_metric would be a function that depends on
        # the values stored in both y_true and y_pred 
        new_metric = 0.1 
        self.true_positives.assign_add(tf.reduce_sum(new_metric))

    def result(self):
        return self.true_positives

    def reset_states(self):
        self.true_positives.assign(0.)

    def test(self, y_true, y_pred):
        tf.print(y_true)
        print(y_true.numpy())

model = Sequential([
    LSTM(5,
         input_shape = (np.asarray(x).shape[1], np.asarray(x).shape[2]),
         return_sequences = True,
         recurrent_initializer = 'glorot_uniform',
         activation = 'tanh',
         recurrent_dropout = 0.2,
         dropout = 0.2
        ),
    Dense(2, activation = 'softmax')
])

model.compile(
    optimizer = 'adam',
    loss = 'sparse_categorical_crossentropy',
    metrics = ['sparse_categorical_accuracy', custom_metric()]
)

model.fit(
    x, y,
    epochs = 1,
    batch_size = 1
)

我写了这个伪函数test只是为了说明问题。如果仅使用tf.print,则执行拟合后,代码将运行,并且张量中的值将打印在stdout上。但是,我是否尝试类似y_true.numpyprint(y_true.numpy())的代码返回

AttributeError: 'Tensor' object has no attribute 'numpy'

我已经尝试了多个StackOverflow和Github线程中的几种方法,包括sess = tf.Session().eval()tf.GradientTape的组合,但是以某种方式未能成功实现它们。

有人知道如何解决这个问题吗?

3 个答案:

答案 0 :(得分:0)

在tf2.x下启用了急切执行模式时,张量对象应该存在

numpy()方法。也许此链接可能对您有所帮助:https://www.tensorflow.org/guide/eager#object-oriented_metrics

答案 1 :(得分:0)

对于tensorflow,默认情况下为<2.0图形模式,要在eager模式下运行,您必须首先通过以下方式启用它:

import tensorflow as tf  #<--- first import 
tf.enable_eager_execution()   #<-- immidiately followed by this, before you start defining any model
.
.
.
...rest of the code

热切张量具有.numpy()函数。

但是即使您执行此操作,也可能是tf.keras.Model.fit()方法在内部将其替换的。因为:

这有效:

def test(self, y_true, y_pred):
    if tf.executing_eagerly():  #<--- This is False
        print(y_true.numpy())
    else:
        print(y_pred)

这也:

def test(self, y_true, y_pred):
    print(y_pred)

但是,这不是:

def test(self, y_true, y_pred):
        tf.print(y_true)
        print(y_true.numpy())

如果要对y_true进行任何进一步的计算,则可以在图形模式下使用tensorflow ops进行:

class custom_metric(Metric):
    def __init__(self, name = 'custom_metrics', **kwargs):
        super(custom_metric, self).__init__(name = name, **kwargs)
        self.true_positives = self.add_weight(name = 'tp', initializer = 'zeros')
        self.lol_value = self.add_weight(name = 'lol', initializer = 'zeros')

    def update_state(self, y_true, y_pred, sample_weight = None):
        self.test(y_true, y_pred)
        # In a real application, new_metric would be a function that depends on
        # the values stored in both y_true and y_pred 
        new_metric = 0.1 
        self.true_positives.assign_add(tf.reduce_sum(new_metric))

    def result(self):
        return self.lol_value

    def reset_states(self):
        self.true_positives.assign(0.)
        self.lol_value.assign(0.)

    def test(self, y_true, y_pred):
        print(y_pred)
        self.lol_value.assign_add(100)

或者,如果您确实是绝对想要numpy,则使用 tf.numpy_function() ,它将正常的numpy计算转换为等效的图形代码。

def func_x(varx):
    #print(x)
    return (varx+1).astype(np.uint8)


class custom_metric(Metric):
    def __init__(self, name = 'custom_metrics', **kwargs):
        super(custom_metric, self).__init__(name = name, **kwargs)
        self.true_positives = self.add_weight(name = 'tp', initializer = 'zeros')
        self.res = self.add_weight(name='loop_counter', initializer='zeros', dtype=tf.uint8)

    def update_state(self, y_true, y_pred, sample_weight = None):
        self.test(y_true, y_pred)
        # In a real application, new_metric would be a function that depends on
        # the values stored in both y_true and y_pred 
        new_metric = 0.1 
        self.true_positives.assign_add(tf.reduce_sum(new_metric))

    def result(self):
        return self.res

    def reset_states(self):
        self.true_positives.assign(0.)

    def test(self, y_true, y_pred):
        self.res.assign(tf.numpy_function(func=func_x, inp=[self.res], Tout=[tf.uint8]))

答案 2 :(得分:0)

最后找到了答案。我不知道为什么,但是代码使用tf-nightly 2.2.0-dev版本。参见https://github.com/tensorflow/tensorflow/issues/38038