我有以下代码,可以很好地工作.但问题是,它只对一批有效,因为我使用的是next(iter)

我创建了一个TensorFlow数据集,它必须为我的问题返回3个值(即X: [x,y,z]).但我只需要将x值传递给模型.我需要将所有3个值打包在一起,因为我稍后将使用yz.现在,问题是,当我要呼叫fit时,我必须以某种方式将这3个值分开,以便正确地呼叫网络架构.所以,我的问题是在这种情况下如何使用PrefetchDataset拨打fit.

import tensorflow as tf
import numpy as np
from tensorflow.keras.layers import Input, Dense,  Activation, \
    Conv2DTranspose, Conv2D, Reshape
from tensorflow.keras.models import Model

AUTOTUNE = tf.data.experimental.AUTOTUNE

def scale(X, a=-1, b=1, dtype='float32'):
    if a > b:
        a, b = b, a
    xmin = tf.cast(tf.math.reduce_min(X), dtype=dtype)
    xmax = tf.cast(tf.math.reduce_max(X), dtype=dtype)
    X = (X - xmin) / (xmax - xmin)
    scaled = X * (b - a) + a
    return scaled, xmin, xmax

def set_shape_b(x, y, z):
    x = tf.reshape(x,  [16, 16, 2])
    y = tf.reshape(y, [1])
    z = tf.reshape(z, [1])
    return x, y, z

def set_shape_a(x, y, z):
    x = tf.reshape(x,  [4, 4, 2])
    y = tf.reshape(y, [1])
    z = tf.reshape(z, [1])
    return x, y, z

def First(lr):
    inp = Input(lr)
    x = Dense(16)(inp)
    x = Reshape((4, 4, 16))(x)
    x = Conv2DTranspose(2, kernel_size=3, strides=2, padding='same')(x)
    x = Conv2DTranspose(2, kernel_size=3, strides=2, padding='same')(x)
    output = Activation('tanh')(x)
    model = Model(inp, output, name='First')
    return model
    
def Second(hr):
    inp = Input(hr)
    x = Dense(16)(inp)
    x = Conv2D(2, kernel_size=3, strides=2, padding='same')(x)
    x = Conv2D(2, kernel_size=3, strides=2, padding='same')(x)
    output = Dense(1, activation='sigmoid')(x)
    model = Model(inputs=inp, outputs=output, name='Second')
    return model
    

def build_model(First, Second):
    inp = Input(shape=INP)
    gen = First(inp)
    output = Second(gen)
    model = Model(inputs=inp , outputs=[gen, output], name='model')
    return model

# Preproces --------------- #
a = np.random.random((20, 4, 4, 2)).astype('float32')
b = np.random.random((20, 16, 16, 2)).astype('float32')

dataset_a = tf.data.Dataset.from_tensor_slices(a)
dataset_b = tf.data.Dataset.from_tensor_slices(b)

dataset_b = dataset_b.map(lambda x: tf.py_function(scale,
                                                   [x], 
                                                   (tf.float32, tf.float32, tf.float32)))
dataset_b = dataset_b.map(set_shape_b)

dataset_a = dataset_a.map(lambda x: tf.py_function(scale,
                                                   [x], 
                                                   (tf.float32, tf.float32, tf.float32)))
dataset_a = dataset_a.map(set_shape_a)
 
dataset_ones = tf.data.Dataset.from_tensor_slices(tf.ones((len(b), 4, 4, 1)))   

dataset = tf.data.Dataset.zip((dataset_a, (dataset_b, dataset_ones)))

dataset = dataset.cache()
dataset = dataset.batch(2)
dataset = dataset.prefetch(buffer_size=AUTOTUNE)

# Prepare models -------------------- #
INP = (4, 4, 2)
OUT = (16, 16, 2)

first = First(INP)
second = Second(OUT)
model = build_model(first, second)

model.compile(loss=['mse', 'binary_crossentropy'],
              optimizer= tf.keras.optimizers.Adam(learning_rate=1e-4))


train_l, (train_h, train_ones) = next(iter(dataset))


# train ------------------
model.fit(train_l[0],
          [train_h[0], train_ones],
          epochs=2)
              

UPDATE

def rescale(X_scaled, xmin, xmax):
    X = (xmax - xmin) * (X_scaled + 1) / 2.0 + xmin
    return X

class PlotCallback(tf.keras.callbacks.Callback):
    def __init__(self, image, xmin, xmax, model):
        self.image = image
        self.xmin = xmin
        self.xmax = xmax
        self.model = model
        
    def on_epoch_end(self, epoch, logs={}):
        preds = self.model.predict(self.image)
        y_pred = preds[0]
        y_pred = rescale(y_pred, self.xmin, self.xmax)

        
        fig, ax = plt.subplots(figsize=(14, 10))
        ax.imshow(y_pred[0][:, :, 0])
        plt.close()

我正在使用上面的功能,当我试着穿的时候,我想要这样的东西:

model.fit(
    dataset,
    validation_data=dataset,
    epochs=2,
    callbacks=[PlotCallback(here_the_dataset_a_scaled_values,
                            xmin_from_dataset_a,
                            xmax_from_dataset_b, model)]
)

推荐答案

在上面的注释之后,要解决您的问题,您可以应用自定义函数来仅返回目标值.另外,请看一下tf.data.Dataset.map个参考资料.

def set_shape(x, y, z, dims):
    x = tf.reshape(x,  dims)
    y = tf.reshape(y, [1])
    z = tf.reshape(z, [1])
    return x, y, z

dataset_a = dataset_a.map(lambda x, y, z: set_shape(x, y, z, dims=[4, 4, 2]))
dataset_b = dataset_b.map(lambda x, y, z: set_shape(x, y, z, dims=[16, 16, 2]))

def only_scale(x, y, z):
    return x

dataset_a = dataset_a.map(only_scale)
dataset_b = dataset_b.map(only_scale)

对数据进行压缩和批量处理.

dataset = tf.data.Dataset.zip(
    (dataset_a, (dataset_b, dataset_ones))
)
dataset = dataset.cache()
dataset = dataset.batch(2)
dataset = dataset.prefetch(buffer_size=AUTOTUNE)

a, b = next(iter(dataset)) 
a.shape, b[0].shape, b[1].shape
(TensorShape([2, 4, 4, 2]),
 TensorShape([2, 16, 16, 2]),
 TensorShape([2, 4, 4, 1]))

现在,我们可以将其传递给Fit方法.

# train ------------------
model.fit(
    dataset,
    epochs=2
)

Epoch 1/2
2s 6ms/step - loss: 1.0283 - First_loss: 0.3368 - Second_loss: 0.6914
Epoch 2/2
0s 4ms/step - loss: 1.0228 - First_loss: 0.3367 - Second_loss: 0.6860

更新1

正如 comments 中提到的,在训练过程中,.map(only_scale)不能用来接收(scale, xmin, xmax)来进行伸缩.但是,我们不能将这样的数据格式传递给不需要这种特定输入的模型.换句话说,模型代码对xminxmax一无所知.

在这种情况下,有两种解决方案.一种是在KERAS中使用自定义训练循环,另一种是覆盖fit方法的train_step函数.我们来试试第二个吧.在这种情况下,我们不需要使用来自数据API的.map(only_scale)个方法.下面是关于覆盖Fit方法的references条.

让我们构建一个定制模型来覆盖trian_step和(对于验证数据也是test_step).仅供参考,还有predict_step个.

class CustomFitter(keras.Model):
    def __init__(self, model, **kwargs):
        super().__init__(**kwargs)
        self.model = model 
    
    def call(self, inputs):
        return self.model(inputs)
    
    def unpack(self, data):
        x, y = data
        # x: dataset_a
        # y: (dataset_b, dataset_ones)
        # dataset_a / datast_b: (scale, xmin, xmax)
        scale_y = y[0][0]
        ones_y = y[1]
        y = (scale_y, ones_y)
        x = x[0]
        return x, y
    
    def train_step(self, data):
        x, y = self.unpack(data)
        return super().train_step((x, y))
    
    def test_step(self, data):
        x, y = self.unpack(data)
        return super().test_step((x, y))

接下来,我们可以做

model = build_model(first, second)
model = CustomFitter(model)
model.compile(
    loss=['mse', 'binary_crossentropy'],
    optimizer= tf.keras.optimizers.Adam(learning_rate=1e-4)
)

接下来,我们现在可以对数据进行拟合(不使用only_scale方法).

model.fit(
    dataset,
    validation_data=dataset,
    epochs=2
)
Epoch 1/2
45ms/step - loss: 1.0278 - output_1_loss: 0.3358 - output_2_loss: 0.6919 - val_loss: 1.0262 - val_output_1_loss: 0.3357 - val_output_2_loss: 0.6905
Epoch 2/2
8ms/step - loss: 1.0249 - output_1_loss: 0.3356 - output_2_loss: 0.6893 - val_loss: 1.0234 - val_output_1_loss: 0.3355 - val_output_2_loss: 0.6879

更新2

关于在回调中使用xminxmax来重新zoom 预测数组和绘图,我们可以做一些如下的事情.

  1. 我们将在训练时存储xminxmax的值.目前,我们将存储来自验证数据集的这些值.
  2. 在稍后的回调中,我们使用此值为on_epoch_end,并将其重置为on_epoch_begin以用于下一个纪元.

首先,我们将做以下工作:

from tensorflow.experimental import numpy as tnp

with tf.device('/CPU:0'):
    scaling_xmin = tf.Variable(
        tnp.empty((0, 1), dtype=tf.float32), shape=[None, 1], trainable=False
    )
    scaling_xmax = tf.Variable(
        tnp.empty((0, 1), dtype=tf.float32), shape=[None, 1], trainable=False
    )
class CustomFitter(keras.Model):
    ....
    
    def unpack(self, data, data_src='valid'):
        x, y = data
        # x: dataset_a
        # y: (dataset_b, dataset_ones)
        # dataset_a / datast_b: (scale, xmin, xmax)
        
        if data_src == 'valid':
            scaling_xmin.assign(
                tf.concat([scaling_xmin, x[1]], axis=0)
            )
            scaling_xmax.assign(
                tf.concat([scaling_xmax, x[2]], axis=0)
            )
        
        scale_y = y[0][0]
        ones_y = y[1]
        y = (scale_y, ones_y)
        x = x[0]
        return x, y

    def train_step(self, data):
        x, y = self.unpack(data, data_src='train')
        return super().train_step((x,y))
    
    def test_step(self, data):
        x, y = self.unpack(data, data_src='valid')
        return super().test_step((x, y))

现在,在回调中,我们将做

class PlotCallback(tf.keras.callbacks.Callback):
    def __init__(self, image):
        self.image = image
        
    def on_epoch_begin(self, epoch, logs=None):
        scaling_xmin.assign(
            tf.Variable(
                tnp.empty((0,1), dtype=tf.float32), shape=[None,1]
            )
        )
        scaling_xmax.assign(
            tf.Variable(
                tnp.empty((0,1), dtype=tf.float32), shape=[None,1]
            )
        )

    def on_epoch_end(self, epoch, logs={}):
        preds = self.model.predict(self.image)
        y_pred = preds[0]
        
        # assuming y_pred.shape[0] == xmin.shape[0] == xmax.shape[0]
        for yp, xmin, xmax in zip(
            y_pred, scaling_xmin.numpy(), scaling_xmax.numpy()
        ):
            yp = rescale(
                yp, xmin, xmax
            )
            fig, ax = plt.subplots(figsize=(14, 10))
            ax.imshow(yp[:, :, 0])
            break
        plt.show()

接下来,我们可以调用此回调.请注意,我们传递的是2D单输入.如果PlotCallback(dataset)通过了,请确保实现predict_step,这将与上面模型代码中的test_step几乎相同.

a = np.random.random((20, 4, 4, 2)).astype('float32')
custom_model.fit(
    dataset,
    validation_data=dataset,
    callbacks=[PlotCallback(a)],
    epochs=2
)
107ms/step - loss: 1.0251 - output_1_loss: 0.3387 - output_2_loss: 0.6864 - val_loss: 1.0239 - val_output_1_loss: 0.3386 - val_output_2_loss: 0.6853

[plot will be displayed]

更新3

正如您在 comments 中提到的,最初显示的日志(log)名称是First_lossSecond_loss,在更新1/2之后,它变成了output_1_lossoutput_2_loss.要解决这个问题,我们可以稍微更改一下模型代码.首先,我们会做

def build_model(First, Second):
    inp = Input(shape=INP)
    gen = First(inp)
    output = Second(gen)
    return inp, [gen, output]

接下来,我们对CustomFitter执行如下操作,移除init并调用不再需要的方法.

class CustomFitter(keras.Model):
    def unpack(self, data, data_src='valid'):
        ...
        return x, y
    
    def train_step(self, data):
        x, y = self.unpack(data, data_src='train')
        return super().train_step((x,y))
    
    def test_step(self, data):
        x, y = self.unpack(data, data_src='valid')
        return super().test_step((x, y))


first = First(INP)
second = Second(OUT)
inputs, outputs = build_model(first, second)
custom_model = CustomFitter(inputs, outputs)
custom_model.compile(
    loss=[
        'mse', 
        'binary_crossentropy'
    ],
    optimizer= tf.keras.optimizers.Adam(learning_rate=1e-4)
)
loss: 1.0337 - First_loss: 0.3405 - Second_loss: 0.6932 - val_loss: 1.0330 - val_First_loss: 0.3404 - val_Second_loss: 0.6926

Python-3.x相关问答推荐

海象表达可以放在方括号中而不是括号中吗?

使用Polars阅读按日期键分区的最新S3镶木地板文件

CONNEXION.EXCEPTIONS.ResolverError:运行pyz文件时未命名模块

Pandas :从元组字典创建数据帧

将列表转换为 pandas 数据框,其中列表包含字典

在特定条件下从 DataFrame 中提取特定组

需要找到完全匹配并使用正则表达式替换

当参数名称与 typing.Protocol 中定义的名称不同时发生 mypy 错误

如何在不使用循环的情况下根据另一个数组的索引值将 numpy 数组中不同通道的值设置为零?

Python Regex 查找给定字符串是否遵循交替元音、辅音或辅音、元音的连续模式

Snakemake 'run' 指令不产生错误信息

判断 gekko 中的表达式

Python pandas将单元格值移动到同一行中的另一个单元格

每个数据行中每个数据帧值的总和

使用 Python 解析 JSON 嵌套字典

Python 3 变量名中接受哪些 Unicode 符号?

如何在 Selenium 和 Python 中使用类型查找元素

创建集合的 Python 性能比较 - set() 与 {} 文字

如何在python中创建代码对象?

如何在 Pandas 中的超 Big Data 框上创建数据透视表