【问题标题】:Custom Metrics and Losses: AttributeError: 'Tensor' object has no attribute 'numpy' raised during training自定义指标和损失:AttributeError:“张量”对象在训练期间没有属性“numpy”
【发布时间】:2020-03-29 20:36:19
【问题描述】:

我正在尝试实现自定义度量函数以及自定义损失函数。两种实现都面临同样的问题,所以我将把这篇文章的重点放在其中一个上。

我的目标是在 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 的组合,但不知何故未能成功实现其中任何一个。

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

【问题讨论】:

    标签: tensorflow keras tensorflow2.0 tf.keras


    【解决方案1】:

    对于 tensorflow

    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]))
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 它在自定义函数/模型拟合之外工作,但在模型的编译或拟合中都不起作用
      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2021-11-03
        • 2019-02-20
        • 1970-01-01
        • 2020-07-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多