简化NLP:TensorFlow中tf.strings的使用

  • 2020 年 2 月 14 日
  • 笔记

简化NLP:TensorFlow中tf.strings的使用

TensorFlow中很早就包含了tf.strings这个模块,不过实话说,在tf 1.x的固定计算图的情况下,各种操作颇为复杂,我们在迎来了2.0中才更好可以看出tf.strings的威力。

tf.strings的其中一个重要的作用是可以使字符串成为TensorFlow的第一公民,可以直接加入到模型的输入中,在上一篇最简单的BERT调用中,就用到了类似技术 (https://github.com/qhduan/bert-model

详细来说,我们之前在NLP中如果要将字符串进行计算,需要进行下面几步:

  1. 首先需要将字符串分词,例如英文常见用空格、标点分词,中文使用分词器或者干脆按字分词
  2. 其次需要计算一个词表,能将每个词对应到一个对应的数字上,一般还要加入一些例如[pad],[unk]等特殊符号
  3. 在训练前将训练集的所有字符串经过上面的结果,都转换为数字符号。或者使用generator等技术在训练中流式转换

那么tf.strings的目的,就是我们为什么不能直接将字符串输入,避免上面的几步?这样做有几个好处:

  1. 避免了很多多余的代码,比如额外的分词、计算词表等
  2. 保证模型的统一性,例如模型本身就包含了分词和符号转换,就可以直接把模型打包、发布(例如用tensorflow hub),这样别人可以不加载或使用任何第三方代码和程序也能直接用你的模型了
  3. 模型发布也可以直接用tensorflow serve等完成,避免第三方介入

具体来说怎么做呢,需要以下几步:

代替分词

上图可以看到,英文直接使用tf.strings.split就可以按照空格和标点符号分词,下图中文的分词相对tricky,我们先用字符串替换方法tf.strings.regex_replace来把中文符号的后面都加一个空格,然后就可以用split了。

代替词表

TensorFlow提供了一个简单的函数tf.strings.to_hash_bucket_fast可以用来代替词表,从其中hash的字样也可以看出,这是通过字符串hashing的方法来直接映射到对应的数字上。这个函数接受两个参数,一个是字符串后者字符串数组,另一个是词表大小。词表大小是整个模型所要接受的词表大小,当然既然是hashing,就有可能导致碰撞冲突,所以这个词表大小应该设置的比实际单词量大来尽量保证冲突不发生。

实际代码

当我们说我们需要构建一个文本分类模型的时候,例如简单的Bi-LSTM,只需要这样:

就可以完成一个包含tokenizer、字典映射到模型训练的模型。其中的几个细节:

tf.strings.split:分词

tf.strings.to_hash_bucket_fast:词表映射(字典映射,单个字符到整形)

tf.squeeze:用这个是因为我们的输入会产生一个额外的维度,我们需要去掉

tf.to_tensor:实际上我们之前的维度输入的是不定长的(ragged tensor),因为每个句子的词数不一样,我们需要将它转换为定长的普通的tensor,所以这里使用了to_tensor函数并填充0做padding

再之后的代码就与其他所有的TensorFlow/Keras代码无异了。

想要训练时和其他普通的模型一样:

想要预测时更简单:

TensorFlow设计的很多工具也是希望我们能用最简单的代码,最快完成工程实践,提高最多的效率。