Tensorflow2.* 加载和预处理数据之用 tf.data 加载文本数据(5)

Tensorflow2.* 机器学习基础知识篇:

  1. 对服装图像进行分类
  2. 使用Tensorflow Hub对未处理的电影评论数据集IMDB进行分类
  3. Keras 机器学习基础知识之对预处理的电影评论文本分类
  4. Tensorflow2.*教程之使用Auto MPG数据集构建回归模型预测燃油率

Tensorflow2.* 加载和预处理数据篇:

  1. Tensorflow2.* 加载和预处理数据之用 tf.data 加载 CSV 数据
  2. Tensorflow2.* 加载和预处理数据之用 tf.data 加载 Numpy数据
  3. Tensorflow2.* 加载和预处理数据之用 tf.data 加载pandas dataframes类型数据
  4. Tensorflow2.* 加载和预处理数据之用 tf.data 加载磁盘图片数据
  5. Tensorflow2.* 加载和预处理数据之用 tf.data 加载文本数据

Tensorflow2.* 计算机视觉之图像分类篇:

  1. Tensorflow2.* 计算机视觉之图像分类之利用卷积网络对Cifar10数据集进行分类

文本数据是自然语言中处理和使用最多的序列型数据。今天,我想介绍一下如何使用tensorflow2.0对原始纯文本数据进行处理,之后对其进行分类处理。本次我们使用的数据集是Homer的作品,其有三个不同的翻译本,我们通过深度学习的方法来识别每个版本对应的翻译者。

导入库文件

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf

import tensorflow_datasets as tfds
import os

数据集简介

Homer的伊利亚特作品的三个不同版本的英文翻译。每个版本存放在文本文件中,每一行为一个记录。

三个版本的翻译分别来自于:

  • William Cowper — text

  • Edward, Earl of Derby — text

  • Samuel Butler — text

数据集下载

DIRECTORY_URL = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/'
FILE_NAMES = ['cowper.txt', 'derby.txt', 'butler.txt']

for name in FILE_NAMES:
  text_dir = tf.keras.utils.get_file(name, origin=DIRECTORY_URL+name)
  
parent_dir = os.path.dirname(text_dir)

print(parent_dir)

C:\Users\Administrator\.keras\datasets

使用tf.data将数据加载到Dataset对象

tf.data.TextLineDataset 来加载文本文件的示例。TextLineDataset 通常被用来以文本文件构建数据集(原文件中的一行为一个sample) 。这适用于大多数的基于行的文本数据(例如,诗歌或错误日志) 。

迭代整个文件,将整个文件加载到tf.data.Dataset中。每个样本都需要单独标记,所以请使用 tf.data.Dataset.map 来为每个样本设定标签。这将迭代数据集中的每一个样本并且返回( example, label )对。

def labeler(example, index):
  return example, tf.cast(index, tf.int64)  

labeled_data_sets = []

for i, file_name in enumerate(FILE_NAMES):
  lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name))
  labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i))
  labeled_data_sets.append(labeled_dataset)

将这些标记的数据集合并到一个数据集中,然后对其进行随机化操作。

BUFFER_SIZE = 50000
BATCH_SIZE = 64
TAKE_SIZE = 5000
all_labeled_data = labeled_data_sets[0]
for labeled_dataset in labeled_data_sets[1:]:
  all_labeled_data = all_labeled_data.concatenate(labeled_dataset)
  
all_labeled_data = all_labeled_data.shuffle(
    BUFFER_SIZE, reshuffle_each_iteration=False)

使用 tf.data.Dataset.take 与 print 来查看 (example, label) 对的外观。numpy 属性显示每个 Tensor 的值。

for ex in all_labeled_data.take(5):
  print(ex)
(, )
(, )
(, )
(, )
(, )

 将文本编码成数字

机器学习基于的是数字而非文本,所以字符串需要被转化成数字列表。 为了达到此目的,我们需要构建文本与整数的一一映射。

1. 建立词汇表

首先,通过将文本标记为单独的单词集合来构建词汇表。在 TensorFlow 和 Python 中均有很多方法来达成这一目的。具体建立词表的过程:

  • 迭代每个样本的 numpy 值。
  • 使用 tfds.features.text.Tokenizer 来将其分割成 token
  • 将这些 token 放入一个 Python 集合中,借此来清除重复项。
  • 获取该词汇表的大小以便于以后使用。
tokenizer = tfds.features.text.Tokenizer()

vocabulary_set = set()
for text_tensor, _ in all_labeled_data:
  some_tokens = tokenizer.tokenize(text_tensor.numpy())
  vocabulary_set.update(some_tokens)

vocab_size = len(vocabulary_set)

2. 样本编码,将每个句子转换为id

通过传递 vocabulary_set 到 tfds.features.text.TokenTextEncoder 来构建一个编码器。编码器的 encode 方法传入一行文本,返回一个整数列表。

encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)

 现在,在数据集上运行编码器(通过将编码器打包到 tf.py_function 并且传参至数据集的 map 方法的方式来运行)。

def encode(text_tensor, label):
  encoded_text = encoder.encode(text_tensor.numpy())
  return encoded_text, label

def encode_map_fn(text, label):
  # py_func doesn't set the shape of the returned tensors.
  encoded_text, label = tf.py_function(encode, 
                                       inp=[text, label], 
                                       Tout=(tf.int64, tf.int64))

  # `tf.data.Datasets` work best if all components have a shape set
  #  so set the shapes manually: 
  encoded_text.set_shape([None])
  label.set_shape([])

  return encoded_text, label


all_encoded_data = all_labeled_data.map(encode_map_fn)

 将数据集分割为测试集和训练集

使用 tf.data.Dataset.take 和 tf.data.Dataset.skip 来建立一个小一些的测试数据集和稍大一些的训练数据集。

在数据集被传入模型之前,数据集需要被分批。最典型的是,每个分支中的样本大小与格式需要一致。但是数据集中样本并不全是相同大小的(每行文本字数并不相同)。因此,使用 tf.data.Dataset.padded_batch(而不是 batch )将样本填充到相同的大小

padded_shapes = (
    tf.TensorShape([None]),
    #tf.TensorShape([])
)

train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)
train_data = train_data.padded_batch(BATCH_SIZE,padded_shapes = padded_shapes)

test_data = all_encoded_data.take(TAKE_SIZE)
test_data = test_data.padded_batch(BATCH_SIZE,padded_shapes = padded_shapes)

 由于引入了一个新的 token 来编码(填充零),因此词汇表大小增加了一个。

vocab_size += 1

 模型建立

使用了双向RNN:

Tensorflow2.* 加载和预处理数据之用 tf.data 加载文本数据(5)_第1张图片

为何使用双向RNN

有时,你有可能需要从未来的时间步骤中学习表示,以便更好地理解上下文环境并消除歧义。通过接下来的列子,“He said, Teddy bears are on sale” and “He said, Teddy Roosevelt was a great President。在上面的两句话中,当我们看到“Teddy”和前两个词“He said”的时候,我们有可能无法理解这个句子是指President还是Teddy bears。因此,为了解决这种歧义性,我们需要往前查找。这就是双向RNN所能实现的。

双向RNN中的重复模块可以是常规RNN、LSTM或是GRU。双向RNN的结构和连接如图所示。有两种类型的连接,一种是向前的,这有助于我们从之前的表示中进行学习,另一种是向后的,这有助于我们从未来的表示中进行学习。

双向RNN正向传播分两步完成:

从左向右移动,从初始时间步骤开始计算值,一直持续到到达最终时间步骤为止;从右向左移动,从最后一个时间步骤开始计算值,一直持续到到达最终时间步骤为止;

model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(vocab_size, 64))
#  LSTM 层,它允许模型利用上下文中理解单词含义。 LSTM 上的双向包装器有助于模型理解当前数据点与其之#前和之后的数据点的关系。
model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))

# 一个或多个紧密连接的层
for units in [64, 64]:
  model.add(tf.keras.layers.Dense(units, activation='relu'))

# 输出层。第一个参数是标签个数。
model.add(tf.keras.layers.Dense(3, activation='softmax'))
model.summary()

 _________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 64)          736064    
_________________________________________________________________
bidirectional (Bidirectional (None, 128)               66048     
_________________________________________________________________
dense (Dense)                (None, 64)                8256      
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 3)                 195       
=================================================================
Total params: 814,723
Trainable params: 814,723
Non-trainable params: 0

模型编译及训练

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model.fit(train_data, epochs=3, validation_data=test_data)

 Epoch 1/3 697/697 [==============================] - 26s 38ms/step - loss: 0.5212 - accuracy: 0.7495 - val_loss: 0.3996 - val_accuracy: 0.8162

Epoch 2/3 697/697 [==============================] - 22s 31ms/step - loss: 0.2982 - accuracy: 0.8700 - val_loss: 0.4038 - val_accuracy: 0.8204

Epoch 3/3 697/697 [==============================] - 23s 33ms/step - loss: 0.2288 - accuracy: 0.9010 - val_loss: 0.4032 - val_accuracy: 0.8266  

eval_loss, eval_acc = model.evaluate(test_data)

print('\nEval loss: {}, Eval accuracy: {}'.format(eval_loss, eval_acc))
 79/Unknown - 3s 35ms/step - loss: 0.4032 - accuracy: 0.8266
Eval loss: 0.40319899082938326, Eval accuracy: 0.8266000151634216

你可能感兴趣的:(加载和预处理数据,Tensorflow2.0)