Tensorflow——循环神经网络(五)subword文本分类

1,502 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情


在上一篇文章中,我们介绍了LSTM模型,并且使用LSTM模型完成了文本分类和文本生成的实战。

今天我们将使用tensorflow_datasets中的imdb数据使用subword_level来进行文本分类的方法


  • 在这个实战中,我们使用的是imdb的数据集,在这个数据集中,我们是使用词为单位来表达所有的句子和语料,对词来做embedding,对embedding来输入我们的循环神经网络,去做文本分类。

  • 但是,在NLP领域,我们很多时候是不使用词为单位的,因为词为单位有两个缺点,

    • 一个是词的词表比较大,因而我们就需要一个比较大的词表,那么我们的model_size就会比较大;
    • 第二个原因就是,我的词表再大,它还是有一个上限的,而对于语言来说,它的词语会非常多,很多次都不在词表中,我遇到它就只能用UNK表示。
  • 那么怎么解决这个问题呢?

    • 一个方法就是使用char-level model, char-level就是字符级别的,对于英文来说就是a-z这26个字母加0-9这10个数字
    • 一种就是使用subword-level model,subword-level是介于char-level和word-level之间的一个level;例如:对于‘hello’就使用‘he’、‘ll’、‘o'三个subword-level来表示

接下来,我们就来看一个如何使用subword-level来训练一个文本分类模型。

  • 5.1 我们先介绍一个新的库:Tensorflow_datasets在这个库中,tensorflow是帮我们定义好了许许多多的公开的数据集,这些数据集都是以dataset的格式去存储的,可以通过一个string字符串去载入进来

  • 5.1.1 载入数据
    • as_supervised:数据集是否有监督
import tensorflow_datasets as tfds

dataset, info = tfds.load('imdb_reviews/subwords8k', with_info = True,
                          as_supervised=True)

train_dataset, test_dataset = dataset['train'], dataset['test']

5.1.2 对数据集进行操作:

  • encoder:可以将词语转成subword
tokenizer = info.features['text'].encoder
print('vocabulary size: {}'.format(tokenizer.vocab_size))

运行结果:

vocabulary size: 8185

5.1.3 接下来,我们测试一下对于一个句子来说tokenizer会把它变成什么样子:

  • 使用tokenizer.encode方法对句子进行处理
  • 使用tokenizer.decode在把句子转回去
sample_string = 'Tensorflow is cool.'
tokenized_string = tokenizer.encode(sample_string)
print('Tokenized string is {}'.format(tokenized_string))

original_string = tokenizer.decode(tokenized_string)
print('Original string is {}'.format(original_string))

assert original_string == sample_string

运行结果:

Tokenized string is [6307, 2327, 2934, 7961, 9, 2724, 7975]
Original string is Tensorflow is cool.

看一下句子对应的字母是什么:

for token in tokenized_string:
    print('{} --> {}'.format(token, tokenizer.decode([token])))

运行结果:

6307 --> Ten
2327 --> sor
2934 --> flow
7961 -->  
9 --> is 
2724 --> cool
7975 --> .

5.1.4 对数据集进行变换,包括shuffle,batch

padded_batch:对于每一个batch分别做padding,找到batch中最长的样本,根据最长的样本做padding,当然,你也可以自己设置最长的长度

buffer_size = 10000
batch_size = 64

print(tf.compat.v1.data.get_output_shapes(train_dataset))
print(tf.compat.v1.data.get_output_shapes(test_dataset))

train_dataset = train_dataset.shuffle(buffer_size)
train_dataset = train_dataset.padded_batch(batch_size, tf.compat.v1.data.get_output_shapes(train_dataset))
test_dataset = test_dataset.padded_batch(batch_size, tf.compat.v1.data.get_output_shapes(test_dataset))

5.1.5 建立模型:

vocab_size = tokenizer.vocab_size
embedding_dim = 16
batch_size = 512

bi_rnn_model = keras.models.Sequential([
    keras.layers.Embedding(vocab_size, embedding_dim),
    keras.layers.Bidirectional(
        keras.layers.LSTM(
            units = 32, return_sequences = False)),
    keras.layers.Dense(32, activation = 'relu'),
    keras.layers.Dense(1, activation='sigmoid'),
])

bi_rnn_model.summary()
bi_rnn_model.compile(optimizer = 'adam',
                     loss = 'binary_crossentropy',
                     metrics = ['accuracy'])

运行结果:

Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 16)          130960    
_________________________________________________________________
bidirectional (Bidirectional (None, 64)                12544     
_________________________________________________________________
dense (Dense)                (None, 32)                2080      
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 33        
=================================================================
Total params: 145,617
Trainable params: 145,617
Non-trainable params: 0
_________________________________________________________________

训练模型,打印学习曲线:

history = bi_rnn_model.fit(
    train_dataset,
    epochs = 10,
    validation_data = test_dataset)
def plot_learning_curves(history, label, epochs, min_value, max_value):
    data = {}
    data[label] = history.history[label]
    data['val_'+label] = history.history['val_'+label]
    pd.DataFrame(data).plot(figsize=(8, 5))
    plt.grid(True)
    plt.axis([0, epochs, min_value, max_value])
    plt.show()
    
plot_learning_curves(history, 'accuracy', 10, 0, 1)
plot_learning_curves(history, 'loss', 10, 0, 1)

运行结果:

W`HPCFDHH65}ZLPM_UFSZCV.png

在tfds中导入的数据和keras中导入的数据有些区别,所以并不能和之前的模型进行对比,但是经过图像可以发现,我们的模型过拟合现象比之前要弱一些。

因而,我们可以看到,通过使用subword这种机制可以是词表变小,词表变小参数就会变少,参数变少过拟合的风险就变低了一些。

9JQ4ZCQY3M({Q$KEN%9BFQX.png