I've trained a Sequential model in TensorFlow 2.3.1 with Keras in Python resulting in a saved_model.pb file, but when I try to make use of the model in Go, I don't see the input or output layers I specified, only the Bias and Kernel ones.
Python code which trains the model:
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle
from sklearn import preprocessing
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense
# A utility method to create a tf.data dataset from a Pandas Dataframe
def dataframe_to_dataset(dataframe, shuffle=True, batch_size=32):
dataframe = dataframe.copy()
labels = dataframe.pop("URLType")
ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
if shuffle:
ds = ds.shuffle(buffer_size=len(dataframe), seed=0)
ds = ds.batch(batch_size)
return ds, labels
# load full data list from csv file
csvList = pd.read_csv("./training-data.csv", sep='\t')
# split the list into training and test data lists
trainingList, _ = train_test_split(csvList, test_size=0.2, random_state=0)
# split the training data list into validation and test data lists
trainingList, validationList = train_test_split(
trainingList, test_size=0.2, random_state=0
)
# print the length of the individual lists
print("training: %d\nvalidation: %d" % (len(trainingList), len(validationList)))
# set up empty list for feature columns
feature_columns = []
# add numeric columns to feature columns
for header in ["ExcessLength", "PathTokens", "NumericPathTokens", "QueryTokens", "NumericQueryTokens", "LongestNumber"]:
feature_columns.append(feature_column.numeric_column(header))
# create datasets of the training and validation dataframes
batch_size = 32
trainDataset, _ = dataframe_to_dataset(
trainingList, shuffle=False, batch_size=batch_size
)
validationDataset, _ = dataframe_to_dataset(
validationList, shuffle=False, batch_size=batch_size
)
# set up the model layers
model = tf.keras.Sequential(
[
layers.DenseFeatures(feature_columns, name="layer_input"),
layers.Dense(128, activation="elu", name="layer_1"),
layers.Dense(128, activation="elu", name="layer_2"),
layers.Dense(128, activation="elu", name="layer_3"),
layers.Dense(1, name="layer_label"),
]
)
# compile the model
model.compile(
optimizer="adam",
loss=tf.keras.losses.Hinge(),
metrics=["accuracy"],
)
# callback to stop training early if the loss starts to increase
earlyStopCallback = tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)
# train the model
model.fit(trainDataset, validation_data=validationDataset, epochs=5, shuffle=False, callbacks=[earlyStopCallback])
# save the model
model.save("./url_recognizer")
tf.keras.models.save_model(
model=model,
filepath='./url_recognizer',
overwrite=True,
include_optimizer=True,
save_format='tf')
Then when I load the model in Go, and list all the operations, there is no trace of the input layer.
import (
"fmt"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
)
func main() {
model, err := tf.LoadSavedModel("url_recognizer", []string{"serve"}, nil)
if err != nil {
fmt.Printf("Error loading saved model: %s\n", err.Error())
return
}
defer model.Session.Close()
ops := model.Graph.Operations()
for _, op := range ops {
fmt.Println(op.Name())
}
}
Results in:
sequential/layer_1/kernel
sequential/layer_1/kernel/Read/ReadVariableOp
sequential/layer_1/bias
sequential/layer_1/bias/Read/ReadVariableOp
sequential/layer_2/kernel
sequential/layer_2/kernel/Read/ReadVariableOp
sequential/layer_2/bias
sequential/layer_2/bias/Read/ReadVariableOp
sequential/layer_3/kernel
sequential/layer_3/kernel/Read/ReadVariableOp
sequential/layer_3/bias
sequential/layer_3/bias/Read/ReadVariableOp
sequential/layer_label/kernel
sequential/layer_label/kernel/Read/ReadVariableOp
sequential/layer_label/bias
sequential/layer_label/bias/Read/ReadVariableOp
Adam/iter
Adam/iter/Read/ReadVariableOp
Adam/beta_1
Adam/beta_1/Read/ReadVariableOp
Adam/beta_2
Adam/beta_2/Read/ReadVariableOp
Adam/decay
Adam/decay/Read/ReadVariableOp
Adam/learning_rate
Adam/learning_rate/Read/ReadVariableOp
total
total/Read/ReadVariableOp
count
count/Read/ReadVariableOp
total_1
total_1/Read/ReadVariableOp
count_1
count_1/Read/ReadVariableOp
Adam/sequential/layer_1/kernel/m
Adam/sequential/layer_1/kernel/m/Read/ReadVariableOp
Adam/sequential/layer_1/bias/m
Adam/sequential/layer_1/bias/m/Read/ReadVariableOp
Adam/sequential/layer_2/kernel/m
Adam/sequential/layer_2/kernel/m/Read/ReadVariableOp
Adam/sequential/layer_2/bias/m
Adam/sequential/layer_2/bias/m/Read/ReadVariableOp
Adam/sequential/layer_3/kernel/m
Adam/sequential/layer_3/kernel/m/Read/ReadVariableOp
Adam/sequential/layer_3/bias/m
Adam/sequential/layer_3/bias/m/Read/ReadVariableOp
Adam/sequential/layer_label/kernel/m
Adam/sequential/layer_label/kernel/m/Read/ReadVariableOp
Adam/sequential/layer_label/bias/m
Adam/sequential/layer_label/bias/m/Read/ReadVariableOp
Adam/sequential/layer_1/kernel/v
Adam/sequential/layer_1/kernel/v/Read/ReadVariableOp
Adam/sequential/layer_1/bias/v
Adam/sequential/layer_1/bias/v/Read/ReadVariableOp
Adam/sequential/layer_2/kernel/v
Adam/sequential/layer_2/kernel/v/Read/ReadVariableOp
Adam/sequential/layer_2/bias/v
Adam/sequential/layer_2/bias/v/Read/ReadVariableOp
Adam/sequential/layer_3/kernel/v
Adam/sequential/layer_3/kernel/v/Read/ReadVariableOp
Adam/sequential/layer_3/bias/v
Adam/sequential/layer_3/bias/v/Read/ReadVariableOp
Adam/sequential/layer_label/kernel/v
Adam/sequential/layer_label/kernel/v/Read/ReadVariableOp
Adam/sequential/layer_label/bias/v
Adam/sequential/layer_label/bias/v/Read/ReadVariableOp
NoOp
Const
serving_default_Ending
serving_default_ExcessLength
serving_default_HasArticleToken
serving_default_HasDate
serving_default_IsFile
serving_default_LongestNumber
serving_default_NumericPathTokens
serving_default_NumericQueryTokens
serving_default_OriginalURL
serving_default_PathTokens
serving_default_QueryTokens
StatefulPartitionedCall
saver_filename
StatefulPartitionedCall_1
StatefulPartitionedCall_2
All the sample codes I found online show that I should be able to see an operation with the name layer_input or layer_input_input, but I see none of this.
I suspect the issue might be on the Python side, as loading other pre-built models with this method in Go I see the appropriate input layers among the Operations when I list them.
I'm using the following versions of Python and libraries:
- Python 3.8.5
- TensorFlow 2.3.1
- pandas 1.1.2
- numpy 1.18.5