Custom Metrics and Losses: AttributeError: 'Tensor' object has no attribute 'numpy' raised during training

Custom Metrics and Losses: AttributeError: 'Tensor' object has no attribute 'numpy' raised during training

我正在尝试实现自定义度量函数和自定义损失函数。两种实现都面临着同样的问题,所以我将只关注其中一个 post。

我的目标是在拟合方法期间访问张量的值,以便根据存储在 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()) 代码 returns

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

我尝试了来自多个 Whosebug 和 Github 线程的几种方法,包括 sess = tf.Session().eval()tf.GradientTape 的组合,但不知何故未能实现其中任何一个成功。

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

numpy() 当在 tf2.x 下启用急切执行模式时,张量对象应该存在方法。也许这个 link 对你有帮助:https://www.tensorflow.org/guide/eager#object-oriented_metrics

对于 tensorflow<2.0 图形模式是默认的,为了 运行 在急切模式下你必须在开始时启用它:

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

Eager-tensors 具有 .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]))

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