使用 tf.keras.estimator.model_to_estimator 将 keras 模型转换为估计器模型后使用 estimator.train() 的问题

Posted

技术标签:

【中文标题】使用 tf.keras.estimator.model_to_estimator 将 keras 模型转换为估计器模型后使用 estimator.train() 的问题【英文标题】:Problem using estimator.train() after converting keras model to estimator model using tf.keras.estimator.model_to_estimator 【发布时间】:2020-11-04 12:09:46 【问题描述】:

我正在学习如何使用 tensorflow 2.0 并尝试使用 keras.estimator.model_to_estimator 将 keras 模型转换为估计器模型。

我使用来自https://storage.googleapis.com/tf-datasets/titanic/train.csv 和https://storage.googleapis.com/tf-datasets/titanic/eval.csv 的泰坦尼克数据集作为示例。

在 keras 模型中,我使用 DenseFeatures 层 keras.layers.DenseFeatures() 自动将分类特征转换为 one-hot 编码。幸运的是,我只能使用 model.fit() 来训练我的模型。

但是,当我尝试使用estimator = keras.estimator.model_to_estimator(model) 并使用estimator.train() 训练模型时,程序会报错ValueError: Unexpectedly found an instance of type `<class 'dict'>`. Expected a symbolic tensor instance.

我认为是因为函数make_dataset()使用dict()导致错误,但我不知道如何修改代码来修复错误。

完整代码如下

import tensorflow as tf
import matplotlib as mpl
import numpy as np
import sklearn
import pandas as pd
import os
import sys
from tensorflow import keras

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

print(sys.version_info)
for module in mpl, np, pd, sklearn, tf, keras:
    print(module.__name__, module.__version__)

# https://storage.googleapis.com/tf-datasets/titanic/train.csv
# https://storage.googleapis.com/tf-datasets/titanic/eval.csv
train_file = './data/titanic/train.csv'
eval_file = './data/titanic/eval.csv'

train_df = pd.read_csv(train_file)
eval_df = pd.read_csv(eval_file)

train_y = train_df.pop('survived')
eval_y = eval_df.pop('survived')

categorical_columns = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 'embark_town', 'alone']
numeric_columns = ['age', 'fare']

feature_columns = []

for categorical_column in categorical_columns:
    vocab = train_df[categorical_column].unique()
    feature_columns.append(
        tf.feature_column.indicator_column(
            tf.feature_column.categorical_column_with_vocabulary_list(categorical_column, vocab)
        )
    )

for numeric_column in numeric_columns:
    feature_columns.append(tf.feature_column.numeric_column(numeric_column))


def make_dataset(data_df, label_df, epochs=10, shuffle=True, batch_size=32):
    dataset = tf.data.Dataset.from_tensor_slices((dict(data_df), label_df))
    if shuffle:
        dataset = dataset.shuffle(10000)
    dataset = dataset.repeat(epochs).batch(batch_size)
    return dataset


train_dataset = make_dataset(train_df, train_y, epochs=100, batch_size=5)
for x, y in train_dataset.take(1):
    print(keras.layers.DenseFeatures(feature_columns, dtype=tf.float32)(x).numpy())

model = keras.models.Sequential([
    keras.layers.DenseFeatures(feature_columns, dtype=tf.float32),
    keras.layers.Dense(100, activation='relu', dtype=tf.float64),
    keras.layers.Dense(100, activation='relu', dtype=tf.float64),
    keras.layers.Dense(2, activation='softmax', dtype=tf.float64)
])
model.compile(loss='sparse_categorical_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy'])

batch_size = 32
train_dataset = make_dataset(train_df, train_y, epochs=100, batch_size=batch_size)
eval_dataset = make_dataset(eval_df, eval_y, epochs=1, shuffle=False, batch_size=batch_size)

# 1. model.fit()
history = model.fit(
    train_dataset,
    validation_data=eval_dataset,
    steps_per_epoch=627 // batch_size,
    validation_steps=264 // batch_size,
    epochs=100
)

# 2. model -> estimator -> train
estimator = keras.estimator.model_to_estimator(model)
estimator.train(input_fn=lambda: make_dataset(train_df, train_y, epochs=100))

各个库的版本如下

matplotlib 3.2.0
numpy 1.16.3
pandas 1.0.1
sklearn 0.23.1
tensorflow 2.2.0
tensorflow.keras 2.3.0-tf

【问题讨论】:

【参考方案1】:

这周我遇到了同样的问题。事实证明,转换器无法推断顺序模型的输入名称。您可以通过显式定义输入层来解决此问题(有关github issues 和此github pr 问题的更多详细信息)。

您需要对 sn-p 进行两项核心更改。首先,您需要为所有特征列创建输入层:

# build an input map for all features
input_features = 
for f in categorical_columns:
    if f == 'n_siblings_spouses' or f == 'parch':
        input_features[f] = tf.keras.layers.Input(
            name=f, shape=(1,), dtype=tf.int64)
    else:
        input_features[f] = tf.keras.layers.Input(
            name=f, shape=(1,), dtype=tf.string)
for f in numeric_columns:
    input_features[f] = tf.keras.layers.Input(
        name=f, shape=(1,), dtype=tf.float32)

然后,您可以使用函数式 api 来定义您的 tf.keras 模型:

# use the functional api to build the model
dense_features = tf.keras.layers.DenseFeatures(feature_columns)(input_features)
hid = keras.layers.Dense(100, activation='relu', dtype=tf.float64)(dense_features)
hid = keras.layers.Dense(100, activation='relu', dtype=tf.float64)(hid)
output_layer = keras.layers.Dense(2, activation='softmax', dtype=tf.float64)(hid)
model = keras.Model(inputs=input_features, outputs=output_layer)
model.compile(loss='sparse_categorical_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy'])

以下是完整示例的要点:https://gist.github.com/justincosentino/f7f63642cb04f18ac7b896d3381db058。

【讨论】:

以上是关于使用 tf.keras.estimator.model_to_estimator 将 keras 模型转换为估计器模型后使用 estimator.train() 的问题的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)