Tensorflow-(4)使用Tensorflow加载csv,pandas dataframes,图像,文本文件

1、使用 tf.data 加载 pandas dataframes

1
2
3
4
from __future__ import absolute_import, division, print_function, unicode_literals

import pandas as pd
import tensorflow as tf

使用 pandas 读取 csv 文件。

1
2
3
4
5
6
csv_file = tf.keras.utils.get_file('heart.csv', 'https://storage.googleapis.com/applied-dl/heart.csv')

df = pd.read_csv(csv_file)
df.head()

df.dtypes

在这里插入图片描述

将 thal 列(数据帧(dataframe)中的 object )转换为离散数值。

1
2
3
4
df['thal'] = pd.Categorical(df['thal'])
df['thal'] = df.thal.cat.codes

df.head()

在这里插入图片描述

1
2
3
4
5
6
target = df.pop('target')

dataset = tf.data.Dataset.from_tensor_slices((df.values, target.values))

for feat, targ in dataset.take(5):
  print ('Features: {}, Target: {}'.format(feat, targ))

在这里插入图片描述

1
train_dataset = dataset.shuffle(len(df)).batch(1)

创建并训练模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_compiled_model():
  model = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
  ])

  model.compile(optimizer='adam',
                loss='binary_crossentropy',
                metrics=['accuracy'])
  return model

model = get_compiled_model()
model.fit(train_dataset, epochs=15)

在这里插入图片描述

2、用 tf.data 加载图片

配置

1
2
3
4
from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
AUTOTUNE = tf.data.experimental.AUTOTUNE

下载数据集

1
2
3
4
5
import pathlib
data_root_orig = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
                                         fname='flower_photos', untar=True)
data_root = pathlib.Path(data_root_orig)
print(data_root)

在这里插入图片描述

1
2
for item in data_root.iterdir():
  print(item)

在这里插入图片描述

1
2
3
4
5
6
7
import random
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
random.shuffle(all_image_paths)

image_count = len(all_image_paths)
image_count

在这里插入图片描述

1
all_image_paths[:10]

在这里插入图片描述

列出可用的标签:

1
2
label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())
label_names

在这里插入图片描述
为每个标签分配索引:

1
2
3
# 为每个标签分配索引:
label_to_index = dict((name, index) for index, name in enumerate(label_names))
label_to_index

在这里插入图片描述
创建一个列表,包含每个文件的标签索引:

1
2
3
4
all_image_labels = [label_to_index[pathlib.Path(path).parent.name]
                    for path in all_image_paths]

print("First 10 labels indices: ", all_image_labels[:10])

在这里插入图片描述
加载和格式化图片

1
2
3
4
5
6
7
8
9
10
11
12
def preprocess_image(image):
  # 将它解码为图像 tensor(张量):
  image = tf.image.decode_jpeg(image, channels=3)

  image = tf.image.resize(image, [192, 192])
  image /= 255.0  # normalize to [0,1] range

  return image

def load_and_preprocess_image(path):
  image = tf.io.read_file(path)
  return preprocess_image(image)
1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt

img_path = all_image_paths[0]
label = all_image_labels[0]

plt.imshow(load_and_preprocess_image(img_path))
plt.grid(False)
plt.xlabel(img_path)
plt.title(label_names[label].title())
print()

在这里插入图片描述
将字符串数组切片,得到一个字符串数据集:

1
2
3
# 构建一个 tf.data.Dataset
path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)
print(path_ds)
1
<TensorSliceDataset shapes: (), types: tf.string>

现在创建一个新的数据集,通过在路径数据集上映射 preprocess_image 来动态加载和格式化图片。

1
2
image_ds = path_ds.map(load_and_preprocess_image, num_parallel_calls=AUTOTUNE)
image_ds
1
<ParallelMapDataset shapes: (192, 192, 3), types: tf.float32>

使用同样的 from_tensor_slices 方法你可以创建一个标签数据集:

1
2
3
4
label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))

for label in label_ds.take(10):
  print(label_names[label.numpy()])
1
2
3
4
5
6
7
8
9
10
sunflowers
sunflowers
roses
tulips
sunflowers
sunflowers
dandelion
sunflowers
dandelion
roses

由于这些数据集顺序相同,你可以将他们打包在一起得到一个(图片, 标签)对数据集:

1
2
image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))
image_label_ds
1
<ZipDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int64)>
1
2
3
4
5
6
7
8
ds = tf.data.Dataset.from_tensor_slices((all_image_paths, all_image_labels))

# 元组被解压缩到映射函数的位置参数中
def load_and_preprocess_from_path_label(path, label):
  return load_and_preprocess_image(path), label

image_label_ds = ds.map(load_and_preprocess_from_path_label)
image_label_ds
1
<MapDataset shapes: ((192, 192, 3), ()), types: (tf.float32, tf.int32)>

训练的基本方法,使用 tf.data.Dataset.apply 方法和融合过的 tf.data.experimental.shuffle_and_repeat 函数来打乱和重启

1
2
3
4
5
6
7
8
# 训练的基本方法
BATCH_SIZE = 32

ds = image_label_ds.apply(
  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))
ds = ds.batch(BATCH_SIZE)
ds = ds.prefetch(buffer_size=AUTOTUNE)
ds
1
<PrefetchDataset shapes: ((None, 192, 192, 3), (None,)), types: (tf.float32, tf.int32)>

从 tf.keras.applications 取得 MobileNet v2 副本。该模型副本会被用于一个简单的迁移学习例子。设置 MobileNet 的权重为不可训练:

1
2
mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192, 192, 3), include_top=False)
mobile_net.trainable=False
1
2
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet_v2/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_192_no_top.h5
9412608/9406464 [==============================] - 0s 0us/step

在你将输出传递给 MobilNet 模型之前,你需要将其范围从 [0,1] 转化为 [-1,1]:

1
2
3
4
def change_range(image,label):
  return 2*image-1, label

keras_ds = ds.map(change_range)
1
2
3
4
5
# 数据集可能需要几秒来启动,因为要填满其随机缓冲区。
image_batch, label_batch = next(iter(keras_ds))

feature_map_batch = mobile_net(image_batch)
print(feature_map_batch.shape)
1
(32, 6, 6, 1280)

构建模型

1
2
3
4
5
6
7
8
9
10
model = tf.keras.Sequential([
  mobile_net,
  tf.keras.layers.GlobalAveragePooling2D(),
  tf.keras.layers.Dense(len(label_names), activation = 'softmax')])

model.compile(optimizer=tf.keras.optimizers.Adam(),
              loss='sparse_categorical_crossentropy',
              metrics=["accuracy"])

model.summary()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #  
=================================================================
mobilenetv2_1.00_192 (Model) (None, 6, 6, 1280)        2257984  
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0        
_________________________________________________________________
dense_6 (Dense)              (None, 5)                 6405      
=================================================================
Total params: 2,264,389
Trainable params: 6,405
Non-trainable params: 2,257,984
_________________________________________________________________

传递给 model.fit() 之前你会指定 step 的真实数量

1
2
steps_per_epoch=tf.math.ceil(len(all_image_paths)/BATCH_SIZE).numpy()
steps_per_epoch
1
115.0
1
model.fit(ds, epochs=1, steps_per_epoch=115)
1
2
115/115 [==============================] - 10s 86ms/step - loss: 0.6913 - accuracy: 0.7405
<tensorflow.python.keras.callbacks.History at 0x7f9ee776d0f0>

3、使用 tf.data 加载文本数据

将使用相同作品(荷马的伊利亚特)三个不同版本的英文翻译,然后训练一个模型来通过单行文本确定译者。

环境搭建

1
2
3
4
5
6
from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf

import tensorflow_datasets as tfds
import os
1
2
3
4
5
6
7
8
9
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)

parent_dir
1
2
3
4
5
6
7
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt
819200/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step
'/root/.keras/datasets'

将文本加载到数据集中,将迭代数据集中的每一个样本并且返回( example, label )对。

1
2
3
4
5
6
7
8
9
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)

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

1
2
3
4
5
6
7
8
9
10
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 的值。

1
2
for ex in all_labeled_data.take(5):
  print(ex)
1
2
3
4
5
(<tf.Tensor: shape=(), dtype=string, numpy=b"In boxing, Clytomedes, OEnops' son,">, <tf.Tensor: shape=(), dtype=int64, numpy=1>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'in your heart, and this, all about one single girl, whereas we now'>, <tf.Tensor: shape=(), dtype=int64, numpy=2>)
(<tf.Tensor: shape=(), dtype=string, numpy=b"With angry taunts he drove the gather'd crowds.">, <tf.Tensor: shape=(), dtype=int64, numpy=0>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'bravest of the Achaeans."'>, <tf.Tensor: shape=(), dtype=int64, numpy=2>)
(<tf.Tensor: shape=(), dtype=string, numpy=b"Olympian over-arch'd with clouds of gold">, <tf.Tensor: shape=(), dtype=int64, numpy=0>)

建立词汇表

首先,通过将文本标记为单独的单词集合来构建词汇表。

  • 迭代每个样本的 numpy 值。
  • 使用 tfds.features.text.Tokenizer 来将其分割成 token。
  • 将这些 token 放入一个 Python 集合中,借此来清除重复项。
  • 获取该词汇表的大小以便于以后使用。
1
2
3
4
5
6
7
8
9
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)
vocab_size
1
17178

样本编码

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

尝试运行这一行代码并查看输出的样式。

1
2
3
4
encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)

example_text = next(iter(all_labeled_data))[0].numpy()
print(example_text)
1
b"In boxing, Clytomedes, OEnops' son,"
1
2
encoded_example = encoder.encode(example_text)
print(encoded_example)
1
[6870, 1006, 14062, 7080, 16501]

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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)
all_encoded_data
1
<MapDataset shapes: ((None,), ()), types: (tf.int64, tf.int64)>

将数据集分割为测试集和训练集且进行分支

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

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

1
2
train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)
train_data
1
<ShuffleDataset shapes: ((None,), ()), types: (tf.int64, tf.int64)>
1
2
3
4
train_data = train_data.padded_batch(BATCH_SIZE, padded_shapes=(tf.TensorShape([None,]),tf.TensorShape([])))

test_data = all_encoded_data.take(TAKE_SIZE)
test_data = test_data.padded_batch(BATCH_SIZE, padded_shapes=(tf.TensorShape([None,]),tf.TensorShape([])))

建立模型

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
model = tf.keras.Sequential()
model.add(tf.keras.layers.Embedding(vocab_size+1, 64))

# LSTM 层,它允许模型利用上下文中理解单词含义。
model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))
# 一个或多个紧密连接的层
# 编辑 `for` 行的列表去检测层的大小
for units in [64, 64]:
  model.add(tf.keras.layers.Dense(units, activation='relu'))

# 输出层。第一个参数是标签个数。
model.add(tf.keras.layers.Dense(3, activation='softmax'))
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

model.fit(train_data, epochs=3, validation_data=test_data)
1
2
3
4
5
6
7
Epoch 1/3
697/697 [==============================] - 18s 25ms/step - loss: 0.5225 - accuracy: 0.7457 - val_loss: 0.3818 - val_accuracy: 0.8258
Epoch 2/3
697/697 [==============================] - 17s 24ms/step - loss: 0.3010 - accuracy: 0.8678 - val_loss: 0.3646 - val_accuracy: 0.8340
Epoch 3/3
697/697 [==============================] - 17s 24ms/step - loss: 0.2316 - accuracy: 0.8990 - val_loss: 0.3820 - val_accuracy: 0.8384
<tensorflow.python.keras.callbacks.History at 0x7f9f45767b00>
1
2
3
eval_loss, eval_acc = model.evaluate(test_data)

print('\nEval loss: {}, Eval accuracy: {}'.format(eval_loss, eval_acc))
1
2
3
79/79 [==============================] - 2s 27ms/step - loss: 0.3820 - accuracy: 0.8384

Eval loss: 0.3820337951183319, Eval accuracy: 0.8384000062942505