Как сохранить TextVectorization на диск в tensorflow?

#tensorflow #keras #tensorflow2.0 #pickle

#тензорный поток #keras #tensorflow2.0 #маринованный огурец

Вопрос:

Я подготовил слой текстовой векторности (см. Ниже), и я хочу сохранить его на диск, чтобы я мог перезагрузить его в следующий раз? Я уже пробовал pickle и joblib.dump() … Это не работает.

 from tensorflow.keras.layers.experimental.preprocessing import TextVectorization 

text_dataset = tf.data.Dataset.from_tensor_slices(text_clean) 
    
vectorizer = TextVectorization(max_tokens=100000, output_mode='tf-idf',ngrams=None)
    
vectorizer.adapt(text_dataset.batch(1024))
 

Генерируемая ошибка выглядит следующим образом:

 InvalidArgumentError: Cannot convert a Tensor of dtype resource to a NumPy array
 

Как я могу его сохранить?

Комментарии:

1. tensorflow.org/guide/keras/preprocessing_layers

2. Можете ли вы поделиться журналом ошибок при попытке сохранения с помощью pickle.

3. Вы можете попробовать использовать следующий фрагмент кода для сохранения векторизованных данных в формате pickle pickle.dump(vectorized_text, open("vector.pickel", "wb")) и загрузки с помощью vectorizer = pickle.load(open("vector.pickel", "rb")) функции.

4. ну, его нельзя сбросить с помощью pickle.

5. InvalidArgumentError: не удается преобразовать тензор ресурса dtype в числовой массив @TFer

Ответ №1:

Вместо того чтобы мариновать объект, маринуйте конфигурацию и вес. Позже распакуйте его и используйте configuration для создания объекта и загрузки сохраненных весов. Официальные документы здесь.

Код

 text_dataset = tf.data.Dataset.from_tensor_slices([
                                                   "this is some clean text", 
                                                   "some more text", 
                                                   "even some more text"]) 
# Fit a TextVectorization layer
vectorizer = TextVectorization(max_tokens=10, output_mode='tf-idf',ngrams=None)    
vectorizer.adapt(text_dataset.batch(1024))

# Vector for word "this"
print (vectorizer("this"))

# Pickle the config and weights
pickle.dump({'config': vectorizer.get_config(),
             'weights': vectorizer.get_weights()}
            , open("tv_layer.pkl", "wb"))

print ("*"*10)
# Later you can unpickle and use 
# `config` to create object and 
# `weights` to load the trained weights. 

from_disk = pickle.load(open("tv_layer.pkl", "rb"))
new_v = TextVectorization.from_config(from_disk['config'])
# You have to call `adapt` with some dummy data (BUG in Keras)
new_v.adapt(tf.data.Dataset.from_tensor_slices(["xyz"]))
new_v.set_weights(from_disk['weights'])

# Lets see the Vector for word "this"
print (new_v("this"))
 

Вывод:

 tf.Tensor(
[[0.         0.         0.         0.         0.91629076 0.
  0.         0.         0.         0.        ]], shape=(1, 10), dtype=float32)
**********
tf.Tensor(
[[0.         0.         0.         0.         0.91629076 0.
  0.         0.         0.         0.        ]], shape=(1, 10), dtype=float32)
 

Комментарии:

1. Спасибо за ваши инструкции. Это работает. «new_v.adapt(tf.data. Dataset.from_tensor_slices([«xyz»]))» эта команда не нужна, потому что, если я удалю эту команду adapt, она все равно восстановится из сброшенного vec.

2. Спасибо за подробную инструкцию. Я использую tf2.6 и заметил, что таким образом слой векторизации загруженного текста создает неровные тензоры без заполнения. Я думаю, что такое поведение связано с from_config методом.

3. @AritraRoyGosthipaty, есть ли способ получить дополненные последовательности вместо неровных тензоров в TF2.6?

4. @Nacho Я создал токенизатор не из конфигурации, а так, как я это сделал в первый раз. Затем просто использовал set_weights для установки весов токенизатора. Таким образом, я смог сохранить пэды.

Ответ №2:

Позаимствовав трюк @ jakub с моделью транспортного средства, с помощью которого я не смог загрузить модель, я в конце концов прошел по маршруту сериализации JSON следующим образом.

Обратите внимание, что вам нужно иметь tensorflow>= 2.7 для TextVectorization слоя, и вам нужно использовать ту же версию для сохранения и загрузки слоя / модели.

Итак, продолжая с середины блестящего примера @jakub,

 # Save.
model_json = model.to_json()
with open(filepath, "w") as model_json_fh:
    model_json_fh.write(model_json)

# Load.
with open(filepath, 'r') as model_json_fh:
    loaded_model = tf.keras.models.model_from_json(model_json_fh.read())
    vectorization_layer = loaded_model.layers[0]

loaded_model = tf.keras.models.load_model(filepath)
loaded_vectorizer = loaded_model.layers[0]
 

Вот и все.

Я не уверен в преимуществах одного маршрута над другим.

Это также показывает, как это происходит: https://machinelearningmastery.com/save-load-keras-deep-learning-models

И это помогает с ошибкой JSON, с которой вы можете столкнуться во время путешествий в этих краях:

https://github.com/keras-team/keras/issues/6971

Ответ №3:

Для этого можно использовать небольшой хак. Создайте свой TextVectorization объект, затем поместите его в модель. Сохраните модель, чтобы сохранить векторизатор. Загрузка модели воспроизведет векторизатор. Смотрите пример ниже.

 import tensorflow as tf
from tensorflow.keras.layers.experimental.preprocessing import TextVectorization

data = [
    "The sky is blue.",
    "Grass is green.",
    "Hunter2 is my password.",
]

# Create vectorizer.
text_dataset = tf.data.Dataset.from_tensor_slices(data)
vectorizer = TextVectorization(
    max_tokens=100000, output_mode='tf-idf', ngrams=None,
)
vectorizer.adapt(text_dataset.batch(1024))

# Create model.
model = tf.keras.models.Sequential()
model.add(tf.keras.Input(shape=(1,), dtype=tf.string))
model.add(vectorizer)

# Save.
filepath = "tmp-model"
model.save(filepath, save_format="tf")

# Load.
loaded_model = tf.keras.models.load_model(filepath)
loaded_vectorizer = loaded_model.layers[0]
 

Вот тест, который показывает, что оба векторизатора (исходный и загруженный) выдают одинаковый результат.

 import numpy as np

np.testing.assert_allclose(loaded_vectorizer("blue"), vectorizer("blue"))
 

Комментарии:

1. Как это хак!? 🙂

2. Вы также можете сериализовать слой в JSON, дамп, загрузить и десериализовать. Конечно, дело вкуса?

3. Смотрите мой ответ..

4. @jtlz2 — вы правы, это не взлом 🙂 я не знаю, сохранит ли сериализация в json параметры из vectorizer.adapt . можете ли вы проверить, выдает ли векторизатор, загруженный из json, тот же результат, что и исходный векторизатор?

5. вы правы, это не сохраняет словарный запас! 🙁

Ответ №4:

Во-первых, любой, кто спрашивает себя, как получить dense тензор вместо ragged тензора при загрузке конфигурации TextVectorization слоя, попробуйте явно задать output_mode . Проблема связана с очень недавней ошибкой, из-за которой значение output_mode неправильно установлено, когда оно исходит из сохраненной конфигурации.

В результате dense получается тензор:

 text_dataset = tf.data.Dataset.from_tensor_slices([
                                                   "this is some clean text", 
                                                   "some more text", 
                                                   "even some more text"]) 
vectorizer = TextVectorization(max_tokens=10, output_mode='int', output_sequence_length = 10)   
vectorizer.adapt(text_dataset.batch(1024))

print(vectorizer("this"))
pickle.dump({'config': vectorizer.get_config(),
             'weights': vectorizer.get_weights()}
            , open("tv_layer.pkl", "wb"))

from_disk = pickle.load(open("tv_layer.pkl", "rb"))
new_vectorizer = TextVectorization(max_tokens=from_disk['config']['max_tokens'],
                                          output_mode='int',
                                          output_sequence_length=from_disk['config']['output_sequence_length'])
new_vectorizer.adapt(tf.data.Dataset.from_tensor_slices(["xyz"]))
new_vectorizer.set_weights(from_disk['weights'])

print(new_vectorizer("this"))
 
 tf.Tensor([5 0 0 0 0 0 0 0 0 0], shape=(10,), dtype=int64)
tf.Tensor([5 0 0 0 0 0 0 0 0 0], shape=(10,), dtype=int64)
 

Это приводит к ragged тензору при загрузке:

 import tensorflow as tf

text_dataset = tf.data.Dataset.from_tensor_slices([
                                                   "this is some clean text", 
                                                   "some more text", 
                                                   "even some more text"]) 
vectorizer = TextVectorization(max_tokens=10, output_mode='int', output_sequence_length = 10)   
vectorizer.adapt(text_dataset.batch(1024))

print(vectorizer("this"))
pickle.dump({'config': vectorizer.get_config(),
             'weights': vectorizer.get_weights()}
            , open("tv_layer.pkl", "wb"))

from_disk = pickle.load(open("tv_layer.pkl", "rb"))
new_vectorizer = TextVectorization(max_tokens=from_disk['config']['max_tokens'],
                                          output_mode=from_disk['config']['output_mode'],
                                          output_sequence_length=from_disk['config']['output_sequence_length'])
new_vectorizer.adapt(tf.data.Dataset.from_tensor_slices(["xyz"]))
new_vectorizer.set_weights(from_disk['weights'])

print(new_vectorizer("this"))

 
 tf.Tensor([5 0 0 0 0 0 0 0 0 0], shape=(10,), dtype=int64)
tf.Tensor([5], shape=(1,), dtype=int64)