pytest 中未涵盖的自定义 keras 层中的调用方法

call method in custom keras layers not covered in pytest

我正在尝试对使用自定义层的 keras 神经网络进行单元测试。当我尝试通过 pytest 和覆盖率 运行 它时,它指出 call 方法中的所有代码都没有被覆盖。

我不知道这是否是由于 pytest 将模型交付给单元测试的方式的特殊性,或者 call 方法是否真的没有在我的神经网络中使用,因此有些东西是错了。

下面是一些示例代码:

示例神经网络

class Linear(keras.layers.Layer):
    def __init__(self, units=32, input_dim=32):
        super(Linear, self).__init__()
        self.w = self.add_weight(
            shape=(input_dim, units), initializer="random_normal", trainable=True
        )
        self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)

def call(self, inputs):
    return tf.matmul(inputs, self.w) + self.b

def build_keras_model():
    
    inputs = keras.layers.Input(shape = (32,))
    x = Linear()(inputs)
    outputs = keras.layers.Dense(1)(x)
    
    model = keras.Model(inputs, outputs)
    return model

示例 PyTest 文件

from model import build_keras_model
import pytest
import numpy as np

def test_keras_model():
    model = build_keras_model()
    
    X = np.random.normal(size = (100, 32))
    y = np.random.normal(size = 100)[:, None]
    
    model.compile(loss = 'mae')
    
    model.fit(X, y, epochs = 1)
    
    assert model.predict(X).shape == (100, 1)

如果我在自己的目录中使用 coverage run -m pytestcoverage report -m 测试此代码,则它显示 call 方法内的行未被覆盖。

是 pytest、keras 还是我的设置方式有问题?

我认为如果您计划拥有 100% 的测试覆盖率,则必须显式调用 model__call__() 方法。有趣的是,model.predict 似乎并没有这样做(至少,不是在您需要的抽象级别。它在内部也使用相同的方法)。试试这个:

%%file test_keras.py
import pytest
import numpy as np
import tensorflow as tf

class Linear(tf.keras.layers.Layer):
    def __init__(self, units=32, input_dim=32):
        super(Linear, self).__init__()
        self.w = self.add_weight(
            shape=(input_dim, units), initializer="random_normal", trainable=True
        )
        self.b = self.add_weight(shape=(units,), initializer="zeros", trainable=True)

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

def build_keras_model():
    
    inputs = tf.keras.layers.Input(shape = (32,))
    x = Linear()(inputs)
    outputs = tf.keras.layers.Dense(1)(x)
    
    model = tf.keras.Model(inputs, outputs)
    return model

def test_keras_model():
    model = build_keras_model()
    
    X = np.random.normal(size = (100, 32))
    y = np.random.normal(size = 100)[:, None]
    
    model.compile(loss = 'mae')
    
    model.fit(X, y, epochs = 1)
    
    assert model(X).shape == (100, 1)
!coverage run -m pytest
============================= test session starts ==============================
platform linux -- Python 3.7.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /content
plugins: cov-3.0.0, typeguard-2.7.1
collected 1 item                                                               

test_keras.py .                                                          [100%]

============================== 1 passed in 6.46s ===============================
!coverage report -m /content/test_keras.py
Name            Stmts   Miss  Cover   Missing
---------------------------------------------
test_keras.py      23      0   100%
---------------------------------------------
TOTAL              23      0   100%

检查此 on the differences between the call method and predict method and this