如何在张量流中将 TextVectorization 保存到磁盘?

Posted

技术标签:

【中文标题】如何在张量流中将 TextVectorization 保存到磁盘?【英文标题】:How to save TextVectorization to disk in tensorflow? 【发布时间】:2021-03-14 03:26:30 【问题描述】:

我已经训练了一个 TextVectorization 层(见下文),我想将它保存到磁盘,以便下次重新加载?我试过picklejoblib.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

如何保存?

【问题讨论】:

tensorflow.org/guide/keras/preprocessing_layers 您可以分享使用pickle保存时的错误日志吗? 您可以尝试使用以下代码sn-p将矢量化数据保存为pickle格式pickle.dump(vectorized_text, open("vector.pickel", "wb"))并使用vectorizer = pickle.load(open("vector.pickel", "rb"))函数加载。 好吧,它不能用泡菜转储。 InvalidArgumentError: 无法将 dtype 资源的张量转换为 NumPy 数组 @TFer 【参考方案1】:

可以使用一些技巧来做到这一点。构造您的 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"))

【讨论】:

这算什么黑客攻击!? :) 您还可以将层序列化为 JSON、转储、加载和反序列化。不过肯定是口味问题? 看我的回答.. @jtlz2 - 你说得对,这不是 hack :) 我不知道序列化为 json 是否会维护来自vectorizer.adapt 的参数。你能测试从 json 加载的向量化器是否提供与原始向量化器相同的输出? 你是对的,它不保存词汇! :(【参考方案2】:

不是腌制对象,而是腌制配置和权重。稍后解开它并使用配置来创建对象并加载保存的权重。官方文档here.

代码

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)

【讨论】:

感谢您的指导。有用。 "new_v.adapt(tf.data.Dataset.from_tensor_slices(["xyz"]))" 这个命令是没有必要的,因为如果我删除这个adapt命令,它仍然会从转储的vec​​中恢复。 感谢您的详细指导。我正在使用tf2.6 并注意到以这种方式,加载的文本矢量化层会产生不规则的张量而没有填充。我这个行为是由于from_config 方法。 @AritraRoyGosthipaty,有没有办法在 TF2.6 中获得填充序列而不是参差不齐的张量? @Nacho 我不是从配置构建标记器,而是按照我第一次做的方式。然后只需使用set_weights 设置标记器的权重。这样我就可以保留垫子了。【参考方案3】:

借用 @jakub 的模型车辆技巧 - 我无法加载模型 - 我最终通过 JSON 序列化路线进行,如下所示。

注意TextVectorization层需要tensorflow>=2.7,并且需要使用相同的版本来保存和加载层/模型。

所以,从@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

【讨论】:

【参考方案4】:

如果有人在加载TextVectorization 层的配置时询问自己如何获得dense 张量而不是ragged 张量,请尝试显式设置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)

【讨论】:

以上是关于如何在张量流中将 TextVectorization 保存到磁盘?的主要内容,如果未能解决你的问题,请参考以下文章

在张量流中将 SSD 转换为冻结图。必须使用哪些输出节点名称?

sh 它使张量流推理本机库中的选择性注册意味着在该库中将只是模型歌剧

如何在 Keras 中将标量添加到张量或从标量创建张量?

如何在 Keras / Tensorflow 中将(无,)批量维度重新引入张量?

如何在张量流中对张量进行子集化?

如何在张量流中张量的某些索引处插入某些值?