[机器学习编程]-序列填充(sequence padding)
1. 序列填充的目的
序列填充对神经网络的训练有哪些影响呢? 当使用神经网络时,我们通常将数据输入到网络中,然后获取输出,然后计算损失(loss),然后反向传播(back propagate),然后对下一个输入进行重复。在实践中,批量处理数据(batches)比一个接一个地处理数据更有效,比如一次输入64组数据,得到64个输出。这时我们使用矩阵 [batch_size x sequence_length] 来完成(batch_size是矩阵的行数,表示有多少组数据;sequence_length 表示矩阵的列数,一般取所有行中长度最大一行数据序列的长度)。如果是可变长度序列,则sequence_length 对应最长序列。此外,在这种情况下,我们用填充标记(通常为0)填充序列以适应矩阵大小。这种特殊的Token然后被掩盖,不计入损失计算等。
2. Python中序列填充的函数
Keras深度学习库中的 pad_sequences() 函数可用于填充可变长度的序列。主要的序列填充方式有:
- Pre-Sequence Padding 前部填充
- Post-Sequence Padding 后部填充
- Pad Sequence To Length 指定长度填充
- Sequence Truncation 截断填充
2.1 Pre-Sequence Padding
from keras.preprocessing.sequence import pad_sequences
# define sequences
sequences = [
[1, 2, 3, 4],
[1, 2, 3],
[1]
]
# pad sequence
padded = pad_sequences(sequences)
print(padded)
# --- output ----
[[1 2 3 4]
[0 1 2 3]
[0 0 0 1]]
2.2 Post-Sequence Padding
from keras.preprocessing.sequence import pad_sequences
# define sequences
sequences = [
[1, 2, 3, 4],
[1, 2, 3],
[1]
]
# pad sequence
padded = pad_sequences(sequences, padding='post')
print(padded)
# --- output ----
[[1 2 3 4]
[1 2 3 0]
[1 0 0 0]]
2.3 Pad Sequence To Length
from keras.preprocessing.sequence import pad_sequences
# define sequences
sequences = [
[1, 2, 3, 4],
[1, 2, 3],
[1]
]
# pad sequence
padded = pad_sequences(sequences, maxlen=5)
print(padded)
# --- output ----
[[0 1 2 3 4]
[0 0 1 2 3]
[0 0 0 0 1]]
2.4 Sequence Truncation
from keras.preprocessing.sequence import pad_sequences
# define sequences
sequences = [
[1, 2, 3, 4],
[1, 2, 3],
[1]
]
# truncate sequence
truncated= pad_sequences(sequences, maxlen=2)
print(truncated)
# --- output ----
[[3 4]
[2 3]
[0 1]]
反向截断
from keras.preprocessing.sequence import pad_sequences
# define sequences
sequences = [
[1, 2, 3, 4],
[1, 2, 3],
[1]
]
# truncate sequence
truncated= pad_sequences(sequences, maxlen=2, truncating='post')
print(truncated)
# --- output ----
[[1 2]
[1 2]
[0 1]]
3. 文本数据处理中的序列填充
在NLP编程过程中,经常遇到对文本语料数据的处理,也涉及到对文本序列数据的填充操作。由于模型训练过程中,model 一般不直接接收文本分词序列,而是需要提前将文本分词得到的 Token 转换成语料库中对应的字符编号,然后再输入模型进行计算。
下面的代码完成三个功能,
- 对一批句子进行分词;
- 将分词结果得到的 Token 转换成数字编码序列;
- 对每个序列进行填充补齐;
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
sentences = [
'I love my dog',
'I love my cat',
'You love my dog!',
'Do you think my dog is amazing?'
]
# 参数说明:
# num_words 根据词频,保留的最大单词数。只保留最常见的 num_words-1 个单词。
# oov_token oov是out-of-vocabulary的缩写。如果给定了oov_token的值,它将被添加到 word_index 中,并用于在text_to_sequence 调用期间替换词汇表外未知的单词
tokenizer = Tokenizer(num_words=100, oov_token="<OOV>")
tokenizer.fit_on_texts(sentences)
word_index = tokenizer.word_index
print(word_index)
sequences = tokenizer.texts_to_sequences(sentences)
print(sequences)
padded = pad_sequences(sequences, padding='post')
print(padded)
程序执行的输出结果
# 按每个 Token 出现的词频从高到低进行编码,例如 my 的编码是 2,未知 Token 的编码是 1
{'<OOV>': 1, 'my': 2, 'love': 3, 'dog': 4, 'i': 5, 'you': 6, 'cat': 7, 'do': 8, 'think': 9, 'is': 10, 'amazing': 11}
# texts_to_sequences 将每句话的文本 Token 转换成对应的编号序列
# 由于每句话的长度不一,序列的长度也不一样
[[5, 3, 2, 4], [5, 3, 2, 7], [6, 3, 2, 4], [8, 6, 9, 2, 4, 10, 11]]
# 为了便于后续模型使用,通过填充操作将序列补齐,使用后部填充
[[ 5 3 2 4 0 0 0]
[ 5 3 2 7 0 0 0]
[ 6 3 2 4 0 0 0]
[ 8 6 9 2 4 10 11]]