Saving and doing Inference with Tensorflow BERT model - python

I have created a binary classifier with Tensorflow BERT language model. Here is the link. After the model is trained, it saves the model and produces the following files.
Prediction code.
from tensorflow.contrib import predictor
#MODEL_FILE = 'graph.pbtxt'
with tf.Session() as sess:
predict_fn = predictor.from_saved_model(f'/content/drive/My Drive/binary_class/bert/graph.pbtxt')
predictions = predict_fn(pred_sentences)
print(predictions)
Error
OSError: SavedModel file does not exist at: /content/drive/My Drive/binary_class/bert/graph.pbtxt/{saved_model.pbtxt|saved_model.pb}
After digging in this issue. I came across tf.train.Saver() class for saving the model. I changed the estimator train code to following to save the model. I referred this link. I guess tensorflow estimators can be saved with it.
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
# Do some work with the model.
saver = tf.train.Saver()
estimator.train(input_fn=train_input_fn, max_steps=num_train_steps)
# Save the variables to disk.
save_path = saver.save(sess, f'/content/drive/My Drive/binary_class/bert/tmp/model.ckpt')
And here is the error.
Beginning Training!
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-38-0c9f9b70d76b> in <module>()
9 sess.run(init_op)
10 # Do some work with the model.
---> 11 saver = tf.train.Saver()
12 estimator.train(input_fn=train_input_fn, max_steps=num_train_steps)
13 # Save the variables to disk.
2 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/training/saver.py in _build(self, checkpoint_path, build_save, build_restore)
860 return
861 else:
--> 862 raise ValueError("No variables to save")
863 self._is_empty = False
864
ValueError: No variables to save
The variables, weights are created in create_model function. What should I change to save my trained model?
Updates:
Code to save the model. I am not sure about the feature_spec and feature tensor.
feature_spec = {'x': tf.VarLenFeature(tf.string)}
def serving_input_receiver_fn():
serialized_tf_example = tf.placeholder(dtype=tf.string,
shape=[1], # batch size
name='input_example_tensor')
receiver_tensors = {'examples': serialized_tf_example}
features = tf.parse_example(serialized_tf_example, feature_spec)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
# Export the estimator
export_path = f'/content/drive/My Drive/binary_class/bert/'
estimator.export_saved_model(
export_path,
serving_input_receiver_fn=serving_input_receiver_fn)
I got this error :-
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-55-209298910d1e> in <module>()
16 estimator.export_saved_model(
17 export_path,
---> 18 serving_input_receiver_fn=serving_input_receiver_fn)
4 frames
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in export_saved_model(self, export_dir_base, serving_input_receiver_fn, assets_extra, as_text, checkpoint_path, experimental_mode)
730 as_text=as_text,
731 checkpoint_path=checkpoint_path,
--> 732 strip_default_attrs=True)
733
734 def experimental_export_all_saved_models(
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _export_all_saved_models(self, export_dir_base, input_receiver_fn_map, assets_extra, as_text, checkpoint_path, strip_default_attrs)
854 builder, input_receiver_fn_map, checkpoint_path,
855 save_variables, mode=ModeKeys.PREDICT,
--> 856 strip_default_attrs=strip_default_attrs)
857 save_variables = False
858
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _add_meta_graph_for_mode(self, builder, input_receiver_fn_map, checkpoint_path, save_variables, mode, export_tags, check_variables, strip_default_attrs)
927 labels=getattr(input_receiver, 'labels', None),
928 mode=mode,
--> 929 config=self.config)
930
931 export_outputs = export_lib.export_outputs_for_mode(
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _call_model_fn(self, features, labels, mode, config)
1144
1145 logging.info('Calling model_fn.')
-> 1146 model_fn_results = self._model_fn(features=features, **kwargs)
1147 logging.info('Done calling model_fn.')
1148
<ipython-input-17-119a3167bf33> in model_fn(features, labels, mode, params)
5 """The `model_fn` for TPUEstimator."""
6
----> 7 input_ids = features["input_ids"]
8 input_mask = features["input_mask"]
9 segment_ids = features["segment_ids"]
KeyError: 'input_ids'

The create_model function present in notebook takes some arguments. These features are passed to the model.
By updating the serving_input_fn function to following, the serving function works as expected.
Updated Code
def serving_input_receiver_fn():
feature_spec = {
"input_ids" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"input_mask" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"segment_ids" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"label_ids" : tf.FixedLenFeature([], tf.int64)
}
serialized_tf_example = tf.placeholder(dtype=tf.string,
shape=[None],
name='input_example_tensor')
print(serialized_tf_example.shape)
receiver_tensors = {'example': serialized_tf_example}
features = tf.parse_example(serialized_tf_example, feature_spec)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
export_path = '/content/drive/My Drive/binary_class/bert/'
estimator._export_to_tpu = False # this is important
estimator.export_saved_model(export_dir_base=export_path,serving_input_receiver_fn=serving_input_receiver_fn)

Using the feature_spec dict in the serving_input_receiver_fn did not work for me. I used the serving_input_fn below from someone else asking the same question.
To use the loaded estimator:
def serving_input_fn():
label_ids = tf.placeholder(tf.int32, [None], name='label_ids')
input_ids = tf.placeholder(tf.int32, [None, MAX_SEQ_LEN], name='input_ids')
input_mask = tf.placeholder(tf.int32, [None, MAX_SEQ_LEN], name='input_mask')
segment_ids = tf.placeholder(tf.int32, [None, MAX_SEQ_LEN], name='segment_ids')
input_fn = tf.estimator.export.build_raw_serving_input_receiver_fn(
{
'label_ids': label_ids,
'input_ids': input_ids,
'input_mask': input_mask,
'segment_ids': segment_ids
}
)()
return input_fn
export_path = '../testing'
estimator._export_to_tpu = False # this is important
estimator.export_saved_model(export_dir_base=export_path,serving_input_receiver_fn=serving_input_fn)
from tensorflow.contrib import predictor
predict_fn = predictor.from_saved_model('../testing/1589420991')
def predict(sentences, predict_fn):
labels = [0, 1]
input_examples = [
run_classifier.InputExample(
guid="",
text_a = x,
text_b = None,
label = 0
) for x in sentences] # here, "" is just a dummy label
input_features = run_classifier.convert_examples_to_features(
input_examples, labels, MAX_SEQ_LEN, tokenizer
)
all_input_ids = []
all_input_mask = []
all_segment_ids = []
all_label_ids = []
for feature in input_features:
all_input_ids.append(feature.input_ids)
all_input_mask.append(feature.input_mask)
all_segment_ids.append(feature.segment_ids)
all_label_ids.append(feature.label_id)
pred_dict = {
'input_ids': all_input_ids,
'input_mask': all_input_mask,
'segment_ids': all_segment_ids,
'label_ids': all_label_ids
}
predictions = predict_fn(pred_dict)
return [
(sentence, prediction, label)
for sentence, prediction, label in zip(pred_sentences, predictions['probabilities'], predictions['labels'])
]
pred_sentences = [
"That movie was absolutely awful",
"The acting was a bit lacking",
"The film was creative and surprising",
"Absolutely fantastic!",
]
predictions = predict(pred_sentences, predict_fn)
print(predictions)
[('That movie was absolutely awful',
array([-0.26713806, -1.4505868 ], dtype=float32),
0),
('The acting was a bit lacking',
array([-0.23832974, -1.5508994 ], dtype=float32),
0),
('The film was creative and surprising',
array([-0.2784096, -1.4146391], dtype=float32),
0),
('Absolutely fantastic!',
array([-0.29031944, -1.3784236 ], dtype=float32),
0),
('The patient has diabetes',
array([-0.33836085, -1.2480571 ], dtype=float32),
0),
('The patient does not have diabetes',
array([-0.29378486, -1.3682064 ], dtype=float32),
0)]

Related

How to replace an LSTM with BERT in a PyTorch classification model

Context
I have an LSTM model with pre-trained word embeddings for a multiclass classification problem with 10 output classes.
# LSTM model
hidden_dim = 64
n_layers=1
embed_len = 100
class LSTMClassifier(nn.Module):
def __init__(self, embeddings, n_layers):
super(LSTMClassifier, self).__init__()
#self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.n_layers = n_layers
self.weights = torch.FloatTensor(embeddings)
self.embedding_layer = nn.Embedding.from_pretrained(self.weights)
self.lstm = nn.LSTM(input_size=embed_len, hidden_size=hidden_dim,
num_layers=self.n_layers, batch_first=True)
self.linear = nn.Linear(hidden_dim, 10)
self.activation = nn.Tanh()
self.dropout = nn.Dropout(0.4)
def forward(self, X_batch):
embeddings = self.embedding_layer(X_batch)
embeddings = self.dropout(embeddings)
output, (hidden, cell) = self.lstm(embeddings, (torch.randn(self.n_layers, len(X_batch), hidden_dim),
torch.randn(self.n_layers, len(X_batch), hidden_dim)))
output = self.activation(output)
output = self.dropout(output)
return self.linear(output[:,-1])
Problem
Now I would like to change the LSTM layer to BERT. I tried it the following way, but when I try to train the BERT model, I get a value error ValueError: too many values to unpack (expected 2). This probably means that the input shape of the embeddings for self.bert(embeddings) pass is not of the right shape. The embeddings that are passed to self.bert() have the shape torch.Size([256, 64, 100]). What do I need to change so that my BERT model works?
class BERTClassifier(nn.Module):
def __init__(self, embeddings):
super(BERTClassifier, self).__init__()
# Load the BERT model
self.bert = BertModel.from_pretrained('bert-base-uncased')
# Embeddings
self.weights = torch.FloatTensor(embeddings)
self.embedding_layer = nn.Embedding.from_pretrained(self.weights)
# Final layer for classifcation
self.linear = nn.Linear(self.bert.config.hidden_size, 10)
self.dropout = nn.Dropout(0.4)
def forward(self, X_batch):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
X_batch = X_batch.to(device)
embeddings = self.embedding_layer(X_batch)
output, (hidden, cell) = self.bert(embeddings)
hidden = self.dropout(hidden[:, 0, :])
logits = self.linear(hidden)
return logits
Training lines
epochs = 50
learning_rate = 1e-4
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
bert_classifier = BERTClassifier(word_embeddings_with_stopwords)
bert_classifier = bert_classifier.to(device)
# Freeze the BERT layer
for param in bert_classifier.bert.parameters():
param.requires_grad = False
# Freeze the embedding layer
for param in bert_classifier.embedding_layer.parameters():
param.requires_grad = False
loss_fn = nn.CrossEntropyLoss()
optimizer = Adam(bert_classifier.parameters(), lr=learning_rate, weight_decay=1e-5)
results_bert_classifier = train_model(bert_classifier, loss_fn, optimizer, train_loader_with_stopwords, val_loader_with_stopwords, epochs)
Training functions
# Create validation evaluation function
def calc_val_loss_accuracy(model, loss_fn, val_loader, val_losses_list, val_accuracy_list):
"""
Calculates the validation loss and accuracy during training.
Adds the calculated validation loss and accuracy to a predefined list.
"""
model.eval()
with torch.no_grad():
Y_shuffled = []
Y_preds = []
losses = []
for X, Y in val_loader:
preds = model(X)
loss = loss_fn(preds, Y)
losses.append(loss.item())
Y_shuffled.append(Y)
Y_preds.append(preds.argmax(dim=-1))
Y_shuffled = torch.cat(Y_shuffled)
Y_preds = torch.cat(Y_preds)
val_accuracy = accuracy_score(Y_shuffled.detach().numpy(), Y_preds.detach().numpy())
print("Valid Loss : {:.3f}".format(torch.tensor(losses).mean()))
print("Valid Acc : {:.3f}".format(val_accuracy))
val_losses_list.append(np.array(losses).mean())
val_accuracy_list.append(val_accuracy)
# Create training function
def train_model(model, loss_fn, optimizer, train_loader, val_loader, epochs=10):
"""
Trains a model for a given set of epochs, with a given loss function and optimizer.
Returns a dictionary consisting of the train loss and accuracy per epoch
and val loss and accuracy per epoch.
"""
model.train()
train_losses = []
train_accuracy = []
val_losses = []
val_accuracy = []
Y_shuffled_list = []
Y_preds_list = []
for i in range(1, epochs+1):
print(f"EPOCH {i}")
losses = []
for X, Y in tqdm(train_loader):
preds = model(X)
loss = loss_fn(preds, Y)
losses.append(loss.item())
Y_shuffled_list.append(Y)
Y_preds_list.append(preds.argmax(dim=-1))
optimizer.zero_grad()
loss.backward()
optimizer.step()
Y_shuffled = torch.cat(Y_shuffled_list)
Y_preds = torch.cat(Y_preds_list)
train_accuracy_score = accuracy_score(Y_shuffled.detach().numpy(), Y_preds.detach().numpy())
print("Train Loss : {:.3f}".format(torch.tensor(losses).mean()))
print("Train Acc : {:.3f}".format(train_accuracy_score))
calc_val_loss_accuracy(model, loss_fn, val_loader, val_losses, val_accuracy)
# Save training history
train_losses.append(np.array(losses).mean())
train_accuracy.append(train_accuracy_score)
# Create results
results = {'train_loss': train_losses,
'train_acc': train_accuracy,
'val_loss': val_losses,
'val_acc': val_accuracy}
return results
Full Traceback
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-113-c162e15ac6eb> in <module>
17 optimizer = Adam(bert_classifier.parameters(), lr=learning_rate, weight_decay=1e-5)
18
---> 19 results_bert_classifier = train_model(bert_classifier, loss_fn, optimizer, train_loader_with_stopwords, val_loader_with_stopwords, epochs)
4 frames
<ipython-input-76-af9148b7ec20> in train_model(model, loss_fn, optimizer, train_loader, val_loader, epochs)
21 for X, Y in tqdm(train_loader):
22
---> 23 preds = model(X)
24
25 loss = loss_fn(preds, Y)
/usr/local/lib/python3.8/dist-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1188 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1189 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1190 return forward_call(*input, **kwargs)
1191 # Do not call functions when jit is used
1192 full_backward_hooks, non_full_backward_hooks = [], []
<ipython-input-112-cbe2b66bfaf1> in forward(self, X_batch)
25 embeddings = embeddings.reshape(batch_size, sequence_length, hidden_size)
26
---> 27 output, hidden = self.bert(embeddings)
28
29 hidden = self.dropout(hidden[:, 0, :])
/usr/local/lib/python3.8/dist-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1188 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1189 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1190 return forward_call(*input, **kwargs)
1191 # Do not call functions when jit is used
1192 full_backward_hooks, non_full_backward_hooks = [], []
/usr/local/lib/python3.8/dist-packages/transformers/models/bert/modeling_bert.py in forward(self, input_ids, attention_mask, token_type_ids, position_ids, head_mask, inputs_embeds, encoder_hidden_states, encoder_attention_mask, past_key_values, use_cache, output_attentions, output_hidden_states, return_dict)
973 raise ValueError("You have to specify either input_ids or inputs_embeds")
974
--> 975 batch_size, seq_length = input_shape
976 device = input_ids.device if input_ids is not None else inputs_embeds.device
977
ValueError: too many values to unpack (expected 2)

ValueError: The model did not return a loss from the inputs, only the following keys: logits,past_key_values

I'm using Pytorch to do huggingface model finetuning with transformers library. I have torch version '1.13.0+cu117' with python 3.7.8 and CUDA 11.8. But with some copying and pastinng of others' code, I'm getting ValueError: The model did not return a loss from the inputs, only the following keys: logits,past_key_values. For reference, the inputs it received are input_ids,token_type_ids,attention_mask.. I've seen this question but as a text generation task, I cannot find a proper function like GPT2+text generation so I used AutoModel+Casual LM.
The traceback info is like this:
ValueError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_8240\3982389964.py in <module>
47 )
48
---> 49 trainer.train()
c:\users\fchen\appdata\local\programs\python\python37\lib\site-packages\transformers\trainer.py in train(self, resume_from_checkpoint, trial, ignore_keys_for_eval, **kwargs)
1503 resume_from_checkpoint=resume_from_checkpoint,
1504 trial=trial,
-> 1505 ignore_keys_for_eval=ignore_keys_for_eval,
1506 )
1507
c:\users\fchen\appdata\local\programs\python\python37\lib\site-packages\transformers\trainer.py in _inner_training_loop(self, batch_size, args, resume_from_checkpoint, trial, ignore_keys_for_eval)
1747 tr_loss_step = self.training_step(model, inputs)
1748 else:
-> 1749 tr_loss_step = self.training_step(model, inputs)
1750
1751 if (
c:\users\fchen\appdata\local\programs\python\python37\lib\site-packages\transformers\trainer.py in training_step(self, model, inputs)
2506
2507 with self.compute_loss_context_manager():
-> 2508 loss = self.compute_loss(model, inputs)
2509
2510 if self.args.n_gpu > 1:
c:\users\fchen\appdata\local\programs\python\python37\lib\site-packages\transformers\trainer.py in compute_loss(self, model, inputs, return_outputs)
2552 if isinstance(outputs, dict) and "loss" not in outputs:
2553 raise ValueError(
-> 2554 "The model did not return a loss from the inputs, only the following keys: "
2555 f"{','.join(outputs.keys())}. For reference, the inputs it received are {','.join(inputs.keys())}."
2556 )
ValueError: The model did not return a loss from the inputs, only the following keys: logits,past_key_values. For reference, the inputs it received are input_ids,token_type_ids,attention_mask.
The entire code is like this. The train.csv and test.csv has only lines of sentences in natural language. One sentence per line.
from transformers import TrainingArguments, Trainer
from transformers import AutoTokenizer, AutoModelForCausalLM
from transformers import TrainingArguments
from datasets import load_dataset
import evaluate
MAX_LEN=100
def tokenize_function(examples):
return tokenizer(examples["sentences"], padding='max_length', truncation=True,max_length=MAX_LEN)
pretrained = "./models/gpt2-chinese-cluecorpussmall/"
tokenizer = AutoTokenizer.from_pretrained(pretrained)
model = AutoModelForCausalLM.from_pretrained(pretrained)
data_files = {"train": "train.csv", "test": "test.csv"}
dataset = load_dataset("csv", data_files=data_files)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
train_dataset = tokenized_datasets["train"].shuffle(seed=42)
eval_dataset = tokenized_datasets["test"].shuffle(seed=42)
training_args = TrainingArguments(
output_dir='./test_trainer',
num_train_epochs=1,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
learning_rate= 5e-05,
warmup_steps=500,
weight_decay=0.01,
logging_dir='./logs',
load_best_model_at_end=True,
logging_steps=400,
save_steps=400,
evaluation_strategy="steps",
report_to=None
)
training_args = TrainingArguments(output_dir="test_trainer", evaluation_strategy="epoch")
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset =eval_dataset
)
trainer.train()

How to predict from loaded BERT tensorflow model

I was trying to make a prediction from a loaded tensorflow model. Though I'm not sure if it's correct how I previously saved it, specifically I have doubts about code inside serving_input_fn() function (MAX_SEQ_LENGTH=128):
def serving_input_fn():
feature_spec = { "input_ids" : tf.FixedLenFeature([None,MAX_SEQ_LENGTH], tf.int64),
"input_mask" : tf.FixedLenFeature([None,MAX_SEQ_LENGTH], tf.int64),
"segment_ids" : tf.FixedLenFeature([None,MAX_SEQ_LENGTH], tf.int64),
"label_ids" : tf.FixedLenFeature([None], tf.int64) }
serialized_tf_example = tf.placeholder(dtype=tf.string,shape=[None],name='input_example_tensor')
receiver_tensors = {'example': serialized_tf_example}
features = tf.parse_example(serialized_tf_example, feature_spec)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
estimator.export_saved_model('gs://bucket/trained_model', serving_input_receiver_fn=serving_input_fn)
When I try to predict from loaded model:
from tensorflow.contrib import predictor
predict_fn = predictor.from_saved_model(LOAD_PATH)
input_features_test = convert_examples_to_features( test_examples,label_list, MAX_SEQ_LENGTH, tokenizer)
predictions = predict_fn({'example':input_features_test[0]})
it returns this error:
ValueError: Cannot feed value of shape () for Tensor
'input_example_tensor:0', which has shape '(?,)'
How should I change serving_input_fn() method?
If you want to reproduce it: github_repo (you should download variables from here and put it in trained_model/1608370941/ folder)
This is the tutorial I followed to fine tuned BERT model on google cloud TPU.
I saved the model using following serving_input_fn() function (found in this tutorial):
def serving_input_fn():
label_ids = tf.placeholder(tf.int32, [None], name='label_ids')
input_ids = tf.placeholder(tf.int32, [None, MAX_SEQ_LENGTH], name='input_ids')
input_mask = tf.placeholder(tf.int32, [None, MAX_SEQ_LENGTH], name='input_mask')
segment_ids = tf.placeholder(tf.int32, [None, MAX_SEQ_LENGTH], name='segment_ids')
input_fn = tf.estimator.export.build_raw_serving_input_receiver_fn({
'label_ids': label_ids,
'input_ids': input_ids,
'input_mask': input_mask,
'segment_ids': segment_ids,
})()
return input_fn
Then I loaded it using the code mentioned below:
from tensorflow.contrib import predictor
predict_fn = predictor.from_saved_model(LOAD_PATH_GCP)
I converted input string to BERT model input features with following method:
def convert_single_string_to_input_dict(example_string_prep, vocab_file_path, max_seq_length):
#Inizialize BERT tokenizer
tokenizer = tokenization.FullTokenizer(vocab_file_path, do_lower_case=True)
token_a = tokenizer.tokenize(example_string_prep)
tokens = []
segments_ids = []
segment_ids = []
tokens.append("[CLS]")
segment_ids.append(0)
for token in token_a:
tokens.append(token)
segment_ids.append(0)
tokens.append('[SEP]')
segment_ids.append(0)
input_ids = tokenizer.convert_tokens_to_ids(tokens)
input_mask = [1] * len(input_ids)
while len(input_ids) < max_seq_length:
input_ids.append(0)
input_mask.append(0)
segment_ids.append(0)
label_id = [0]
padding = [0] * max_seq_length
print(len(input_ids),len(input_mask),len(segment_ids),len(label_id))
return {"input_ids":[input_ids,padding], "input_mask":[input_mask,padding], "segment_ids":[segment_ids,padding], "label_ids":label_id}
Finally I made a prediction using loaded model:
MAX_SEQ_LENGTH = 128
VOCAB_FILE_PATH = 'path/to/vocab'
example_features = convert_single_string_to_input_dict(example_string, VOCAB_FILE_PATH, MAX_SEQ_LENGTH)
prediction = predict_fn(example_features)['probabilities'][0]
prediction_dict = {'POS': round(prediction[1],4), 'NEG': round(prediction[0],4)}
pprint(f"prediction: {prediction_dict}")

How to make features for serving_input_receiver_fn BERT Tensorflow

I have created a binary classifier with Tensorflow BERT language model. Here is the link to sample code. I am able to do predictions. Now I want to export this model. I am not sure if I have defined feature_spec correctly.
Code to export model.
feature_spec = {'x': tf.VarLenFeature(tf.string)}
def serving_input_receiver_fn():
serialized_tf_example = tf.placeholder(dtype=tf.string, shape=[1],name='input_example_tensor')
receiver_tensors = {'examples': serialized_tf_example}
features = tf.parse_example(serialized_tf_example, feature_spec)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)
# Export the estimator
export_path = f'/content/drive/My Drive/binary_class/bert/export'
estimator.export_saved_model(
export_path,
serving_input_receiver_fn=serving_input_receiver_fn)
Error
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-71-56ff3fb3e002> in <module>()
16 estimator.export_saved_model(
17 export_path,
---> 18 serving_input_receiver_fn=serving_input_receiver_fn)
4 frames
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in export_saved_model(self, export_dir_base, serving_input_receiver_fn, assets_extra, as_text, checkpoint_path, experimental_mode)
730 as_text=as_text,
731 checkpoint_path=checkpoint_path,
--> 732 strip_default_attrs=True)
733
734 def experimental_export_all_saved_models(
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _export_all_saved_models(self, export_dir_base, input_receiver_fn_map, assets_extra, as_text, checkpoint_path, strip_default_attrs)
854 builder, input_receiver_fn_map, checkpoint_path,
855 save_variables, mode=ModeKeys.PREDICT,
--> 856 strip_default_attrs=strip_default_attrs)
857 save_variables = False
858
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _add_meta_graph_for_mode(self, builder, input_receiver_fn_map, checkpoint_path, save_variables, mode, export_tags, check_variables, strip_default_attrs)
927 labels=getattr(input_receiver, 'labels', None),
928 mode=mode,
--> 929 config=self.config)
930
931 export_outputs = export_lib.export_outputs_for_mode(
/usr/local/lib/python3.6/dist-packages/tensorflow_estimator/python/estimator/estimator.py in _call_model_fn(self, features, labels, mode, config)
1144
1145 logging.info('Calling model_fn.')
-> 1146 model_fn_results = self._model_fn(features=features, **kwargs)
1147 logging.info('Done calling model_fn.')
1148
<ipython-input-17-119a3167bf33> in model_fn(features, labels, mode, params)
5 """The `model_fn` for TPUEstimator."""
6
----> 7 input_ids = features["input_ids"]
8 input_mask = features["input_mask"]
9 segment_ids = features["segment_ids"]
KeyError: 'input_ids'
The create_model function present in notebook takes some arguments. Those are the features which will be passed to the model.
By updating the serving_input_fn function to following, the serving function works properly.
Updated Code
def serving_input_fn():
feature_spec = {
"input_ids" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"input_mask" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"segment_ids" : tf.FixedLenFeature([MAX_SEQ_LENGTH], tf.int64),
"label_ids" : tf.FixedLenFeature([], tf.int64)
}
serialized_tf_example = tf.placeholder(dtype=tf.string,
shape=[None],
name='input_example_tensor')
receiver_tensors = {'example': serialized_tf_example}
features = tf.parse_example(serialized_tf_example, feature_spec)
return tf.estimator.export.ServingInputReceiver(features, receiver_tensors)

Setting different batch size for training and validation using Tensorflow's tf.data API

I am migrating from the older queue-based data pipeline to the newer tf.data API. Suppose I have a code like the following, how can I explicitly set different batch sizes for my training and validation iterators.
filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
# Initialize `iterator` with training data.
training_filenames = ["/var/data/file1.tfrecord",
"/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames:
training_filenames})
# Initialize `iterator` with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames:
validation_filenames})
EDIT:
Thank you. Based on the reply, my implementation is as follows:
My implementation is like follows, but I'm not able to figure out why I'm getting this error:
import tensorflow as tf
def _parse(filename, label):
image_string = tf.read_file(filename)
image_decoded = tf.image.decode_jpeg(image_string)
image_resized = tf.image.resize_images(image_decoded, [224, 224])
image_resized.set_shape([224,224,3])
return image_resized, label
def input_pipeline(imglist,labellist, batch_size):
dataset = tf.data.Dataset.from_tensor_slices((imglist, labellist))
dataset = dataset.map(_parse) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(batch_size)
return dataset
imglist = glob.glob('/var/temp/*.jpg')
train_imgs=imglist[0:100]
train_labels = [i for i in range(100)]
val_imgs=imglist[200:250]
val_labels = [i for i in range(50)]
training_batch_size = 4
validation_batch_size = 1
training_ds = input_pipeline(train_imgs, train_labels, training_batch_size)
validation_ds = input_pipeline(val_imgs, val_labels, validation_batch_size)
handle = tf.placeholder(tf.string, shape=[])
iterator = tf.data.Iterator.from_string_handle(
handle, training_ds.output_types, training_ds.output_shapes)
input_batch = iterator.get_next()
train_iter = training_ds.make_initializable_iterator()
val_iter = validation_ds.make_initializable_iterator()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Define training and validation handlers
training_handle = sess.run(train_iter.string_handle())
validation_handle = sess.run(val_iter.string_handle())
# Initialize training and validation dataset
sess.run(train_iter)
sess.run(val_iter)
# If we use training_handle, then input_batch tensor comes from training tfrecords
training_batch = sess.run(input_batch, feed_dict={handle: training_handle})
# If we use validation_handle, then input_batch tensor comes from validation tfrecords
validation_batch = sess.run(input_batch, feed_dict={handle: validation_handle})
But I end up getting the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in __init__(self, fetches, contraction_fn)
281 self._unique_fetches.append(ops.get_default_graph().as_graph_element(
--> 282 fetch, allow_tensor=True, allow_operation=True))
283 except TypeError as e:
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py in as_graph_element(self, obj, allow_tensor, allow_operation)
3589 with self._lock:
-> 3590 return self._as_graph_element_locked(obj, allow_tensor, allow_operation)
3591
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/framework/ops.py in _as_graph_element_locked(self, obj, allow_tensor, allow_operation)
3678 raise TypeError("Can not convert a %s into a %s." % (type(obj).__name__,
-> 3679 types_str))
3680
TypeError: Can not convert a Iterator into a Tensor or Operation.
During handling of the above exception, another exception occurred:
TypeError Traceback (most recent call last)
<ipython-input-31-50c4f3464d03> in <module>()
47
48 # Initialize training and validation dataset
---> 49 sess.run(train_iter)
50 sess.run(val_iter)
51
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in run(self, fetches, feed_dict, options, run_metadata)
898 try:
899 result = self._run(None, fetches, feed_dict, options_ptr,
--> 900 run_metadata_ptr)
901 if run_metadata:
902 proto_data = tf_session.TF_GetBuffer(run_metadata_ptr)
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in _run(self, handle, fetches, feed_dict, options, run_metadata)
1118 # Create a fetch handler to take care of the structure of fetches.
1119 fetch_handler = _FetchHandler(
-> 1120 self._graph, fetches, feed_dict_tensor, feed_handles=feed_handles)
1121
1122 # Run request and get response.
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in __init__(self, graph, fetches, feeds, feed_handles)
425 """
426 with graph.as_default():
--> 427 self._fetch_mapper = _FetchMapper.for_fetch(fetches)
428 self._fetches = []
429 self._targets = []
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in for_fetch(fetch)
251 if isinstance(fetch, tensor_type):
252 fetches, contraction_fn = fetch_fn(fetch)
--> 253 return _ElementFetchMapper(fetches, contraction_fn)
254 # Did not find anything.
255 raise TypeError('Fetch argument %r has invalid type %r' % (fetch,
~/anaconda3/lib/python3.6/site-packages/tensorflow/python/client/session.py in __init__(self, fetches, contraction_fn)
284 raise TypeError('Fetch argument %r has invalid type %r, '
285 'must be a string or Tensor. (%s)' %
--> 286 (fetch, type(fetch), str(e)))
287 except ValueError as e:
288 raise ValueError('Fetch argument %r cannot be interpreted as a '
TypeError: Fetch argument <tensorflow.python.data.ops.iterator_ops.Iterator object at 0x7fa2c0697c88> has invalid type <class 'tensorflow.python.data.ops.iterator_ops.Iterator'>, must be a string or Tensor. (Can not convert a Iterator into a Tensor or Operation.)
I would create 2 tf.data.Dataset, one for training and one for validation subsets. Once you have both datasets pipelines defined (where you are able to define 2 different batch sizes), you can join them in the graph by creating a single tf.data.Iterator with a handler (in my case, the tf.placeholder handle).
import tensorflow as tf
def input_pipeline(filenames, batch_size):
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # Parse the record into tensors.
dataset = dataset.repeat() # Repeat the input indefinitely.
dataset = dataset.batch(batch_size)
return dataset
training_filenames = ["/var/data/file1.tfrecord",
"/var/data/file2.tfrecord"]
training_batch_size = 32
validation_filenames = ["/var/data/validation1.tfrecord",
"/var/data/validation2.tfrecord"]
validation_batch_size = 16
training_ds = input_pipeline(training_filenames, training_batch_size)
validation_ds = input_pipeline(validation_filenames, validation_batch_size)
handle = tf.placeholder(tf.string, shape=[])
iterator = tf.data.Iterator.from_string_handle(
handle, training_ds.output_types, training_ds.output_shapes)
input_batch = iterator.get_next()
Before requesting batches from any of both datasets, you can get correponding handlers from each dataset using string_handle(). After that, when you run input_batch, you can decide if it comes from training or validation by defining it on the handle placeholder.
train_iter = training_ds.make_initializable_iterator()
val_iter = validation_ds.make_initializable_iterator()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Define training and validation handlers
training_handle = sess.run(train_iter.string_handle())
validation_handle = sess.run(val_iter.string_handle())
# Initialize training and validation dataset
sess.run(train_iter.initializer)
sess.run(val_iter.initializer)
# If we use training_handle, then input_batch tensor comes from training tfrecords
trainaing_batch = sess.run(input_batch, feed_dict={handle: training_handle})
# If we use validation_handle, then input_batch tensor comes from validation tfrecords
validation_batch = sess.run(input_batch, feed_dict={handle: validation_handle})
Hope it helps!
EDIT:
Your current error seems to be due to trying to do a sess.run() on a tf.data.Iterator. Try to replace sess.run(train_iter) for sess.run(train_iter.initializer) (and same for validation iterator). train_iter.initializer is the tf.Operation that initializes train_iter iterator. Everything should work now.
Slight modification needed to get the right answer:
import tensorflow as tf
imglist = glob.glob('/var/temp/*.jpg')
train_imgs=imglist[0:100]
train_labels = [i for i in range(100)]
val_imgs=imglist[200:250]
val_labels = [i for i in range(50)]
training_ds = tf.data.Dataset.from_tensor_slices((train_imgs,train_labels)).batch(4)
validation_ds = tf.data.Dataset.from_tensor_slices((val_imgs,val_labels)).batch(1)
handle = tf.placeholder(tf.string, shape=[])
iterator = tf.data.Iterator.from_string_handle(
handle, training_ds.output_types, training_ds.output_shapes)
input_batch = iterator.get_next()
train_iter = training_ds.make_initializable_iterator()
val_iter = validation_ds.make_initializable_iterator()
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# Define training and validation handlers
training_handle = sess.run(train_iter.string_handle())
validation_handle = sess.run(val_iter.string_handle())
sess.run(train_iter.initializer)
# If we use training_handle, then input_batch tensor comes from training tfrecords
training_batch = sess.run(input_batch, feed_dict={handle: training_handle})
print("Training...")
print(training_batch)
sess.run(val_iter.initializer)
# If we use validation_handle, then input_batch tensor comes from validation tfrecords
print("Validation")
validation_batch = sess.run(input_batch, feed_dict={handle: validation_handle})
print(validation_batch)

Categories