Keras Tokenizer 方法究竟做了啥?

Posted

技术标签:

【中文标题】Keras Tokenizer 方法究竟做了啥?【英文标题】:What does Keras Tokenizer method exactly do?Keras Tokenizer 方法究竟做了什么? 【发布时间】:2019-01-28 01:46:48 【问题描述】:

有时,情况要求我们执行以下操作:

from keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=my_max)

然后,我们总是念诵这个咒语:

tokenizer.fit_on_texts(text) 
sequences = tokenizer.texts_to_sequences(text)

虽然我(或多或少)了解总体效果是什么,但我无法弄清楚每个人分别做了什么,无论我做了多少研究(显然包括文档)。我想我从来没有见过一个没有另一个。

那么每个人的作用是什么?在任何情况下,您会使用其中一种而不使用另一种吗?如果不是,为什么不将它们简单地组合成类似的东西:

sequences = tokenizer.fit_on_texts_to_sequences(text)

抱歉,如果我遗漏了一些明显的东西,但我对此很陌生。

【问题讨论】:

我发现与直觉相反的是,Tokenizer 的输出是一个整数序列,如单词索引,而不是单个标记的列表。实际上,它可以采用标记化文本(每个内容的标记列表),并输出整数序列tensorflow.org/api_docs/python/tf/keras/preprocessing/text/…。 【参考方案1】:

来自source code:

    fit_on_texts 根据文本列表更新内部词汇表。 此方法根据词频创建词汇表索引。所以如果你给它一些类似的东西,“猫坐在垫子上。”它将创建一个字典 s.t. word_index["the"] = 1; word_index["cat"] = 2 它是单词 -> 索引字典,因此每个单词都有一个唯一的整数值。 0 保留用于填充。所以较低的整数意味着更频繁的词(通常前几个是停用词,因为它们出现很多)。 texts_to_sequences 将 texts 中的每个文本转换为整数序列。 所以它基本上将文本中的每个单词替换为 word_index 字典中对应的整数值。不多也不少,当然不涉及魔法。

为什么不将它们组合起来?因为您几乎总是一次 拟合并多次 转换为序列。您将适合您的训练语料库并在训练/评估/测试/预测时间使用完全相同的word_index 字典将实际文本转换为序列以将它们提供给网络。因此,将这些方法分开是有意义的。

【讨论】:

所以这一切都在源代码中!我想我看起来不够努力......现在我明白了:适合 - 一次,序列 - 很多!像往常一样生活和学习。谢谢。 @nuric 感谢您的回复。但是,有一些注意事项。根据官方文档,“0 是一个保留索引,不会分配给任何单词。”所以在您的示例中,第一个单词索引将是 1 keras.io/preprocessing/text 另一点是默认过滤器会删除大部分标点符号,因此只有当您从过滤器中删除标点符号时,这些符号才会在词汇表中出现。 @Nikita 感谢您指出这一点,我更新了答案来解决这个问题。 谢谢。那么,给定一个时间序列,我们是否必须使用 Tokenizer 对其进行标记? 我们可以在进行 fit_on_sequences 之后提取单词【参考方案2】:

通过示例为上述答案添加更多内容将有助于更好地理解:

示例 1

t  = Tokenizer()
fit_text = "The earth is an awesome place live"
t.fit_on_texts(fit_text)
test_text = "The earth is an great place live"
sequences = t.texts_to_sequences(test_text)

print("sequences : ",sequences,'\n')

print("word_index : ",t.word_index)
#[] specifies : 1. space b/w the words in the test_text    2. letters that have not occured in fit_text

Output :

       sequences :  [[3], [4], [1], [], [1], [2], [8], [3], [4], [], [5], [6], [], [2], [9], [], [], [8], [1], [2], [3], [], [13], [7], [2], [14], [1], [], [7], [5], [15], [1]] 

       word_index :  'e': 1, 'a': 2, 't': 3, 'h': 4, 'i': 5, 's': 6, 'l': 7, 'r': 8, 'n': 9, 'w': 10, 'o': 11, 'm': 12, 'p': 13, 'c': 14, 'v': 15

示例 2

t  = Tokenizer()
fit_text = ["The earth is an awesome place live"]
t.fit_on_texts(fit_text)

#fit_on_texts fits on sentences when list of sentences is passed to fit_on_texts() function. 
#ie - fit_on_texts( [ sent1, sent2, sent3,....sentN ] )

#Similarly, list of sentences/single sentence in a list must be passed into texts_to_sequences.
test_text1 = "The earth is an great place live"
test_text2 = "The is my program"
sequences = t.texts_to_sequences([test_text1, test_text2])

print('sequences : ',sequences,'\n')

print('word_index : ',t.word_index)
#texts_to_sequences() returns list of list. ie - [ [] ]

Output:

        sequences :  [[1, 2, 3, 4, 6, 7], [1, 3]] 

        word_index :  'the': 1, 'earth': 2, 'is': 3, 'an': 4, 'awesome': 5, 'place': 6, 'live': 7

【讨论】:

例子永远不会说谎!感谢您的精彩回答。 我有一个训练数据集和一个测试数据集都有两列:一个索引和已经清理的文本。我想知道是否可以在两个数据帧之间执行 append() 然后在附加集上执行 Tokenizer.fit_on_text () ,而不仅仅是在训练集上执行。我认为如果我想要的只是将文本转换为一个整数向量,其中每个整数代表所有使用的词汇表中的一个单词,那么首先执行 append() 会更好,因为当我执行 text_to_sequence 时测试集中的向量将具有更多元素()。 @Nachengue 看这里:***.com/questions/47778403/…. 感谢@ajaysinghnegi 提供的有用示例。我有一个问题:一旦完成最常见的单词或字母,基于什么分配给其他单词或字母的整数?例如,在示例 1 中,“l”为 7,“r”为 8。为什么?那个时候是随机的吗?【参考方案3】:

让我们看看这行代码做了什么。

tokenizer.fit_on_texts(text) 

例如,考虑句子" The earth is an awesome place live"

tokenizer.fit_on_texts("The earth is an awesome place live") 适合 [[1,2,3,4,5,6,7]] 其中 3 -> "is" , 6 -> "place",依此类推。

sequences = tokenizer.texts_to_sequences("The earth is an great place live")

返回[[1,2,3,4,6,7]]

你看这里发生了什么。 “great”这个词最初是不合适的,所以它不能识别“great”这个词。意思是,fit_on_text 可以在训练数据上独立使用,然后拟合的词汇索引可以用来表示一组全新的词序列。这是两个不同的过程。因此有两行代码。

【讨论】:

关于缺少“伟大”一词的好消息。但是我们如何处理这些不在词汇表中的词呢?【参考方案4】:

nuric 已经解决了这个问题,但我会补充一点。

请在本例中同时关注单词frequency-based encoding 和OOV:

from tensorflow.keras.preprocessing.text        import Tokenizer

corpus =['The', 'cat', 'is', 'on', 'the', 'table', 'a', 'very', 'long', 'table']

tok_obj = Tokenizer(num_words=10, oov_token='<OOV>')
tok_obj.fit_on_texts(corpus)

[TL;DR] 标记器将包括出现在语料库中的第一个 10 单词。这里10 单词,但只有 8 是唯一的。最常见的10 词将被编码,如果它们超过这个数字,它们将变为 OOV(词汇表外)。

内置词典

请注意频率

'<OOV>': 1, 'the': 2, 'table': 3, 'cat': 4, 'is': 5, 'on': 6, 'a': 7, 'very': 8, 'long': 9

句子处理

processed_seq = tok_obj.texts_to_sequences(['The dog is on the bed'])

这给出了:

>>> processed_seq
    [[2, 1, 5, 6, 2, 1]]

如何检索句子?

构建字典inv_map 并使用它!下面可以使用列表推导来压缩代码。

inv_map = v: k for k, v in tok_obj.word_index.items()

for seq in processed_seq:
    for tok in seq:
        print(inv_map[tok])

给出:

>>> the
<OOV>
is
on
the
<OOV>

因为dogbed不在字典里。

列表推导可用于压缩代码。这里获取一个列表作为输出。

[inv_map[tok] for seq in processed_seq for tok in seq]

给出:

>>> ['the', '<OOV>', 'is', 'on', 'the', '<OOV>']

【讨论】:

以上是关于Keras Tokenizer 方法究竟做了啥?的主要内容,如果未能解决你的问题,请参考以下文章

Gradle 中的transitive = true 究竟做了啥(w.r.t. crashlytics)?

tokenizer.max len 在这个类定义中做了啥?

TimeZone 选项在 Intl.DateTimeFormat() 中究竟做了啥

MissingSchemaAction.AddWithKey 究竟做了啥?

setUseWideViewPort() 和 setLoadWithOverviewMode() 究竟做了啥?

Hasmorepages PrintPageEventArgs 属性究竟做了啥?