云计算百科
云计算领域专业知识百科平台

第16课:TensorFlow|数据集加载与预处理【TF内置数据集、自定义数据集制作、归一化处理】

在这里插入图片描述

文章目录

    • 1. 课前导读
      • 1.1 本节课学习目标
      • 1.2 知识重难点
      • 1.3 学习前置条件
      • 1.4 学完可掌握能力
      • 1.5 行业应用场景
    • 2. 核心理论精讲
      • 2.1 TensorFlow 内置数据集
      • 2.2 `tf.data.Dataset` 核心概念
      • 2.3 数据归一化
      • 2.4 图像数据增强
      • 2.5 数据流水线性能优化
    • 3. 环境搭建与工具配置
    • 4. 代码实战教学
      • 4.1 加载内置数据集
      • 4.2 从NumPy数组创建数据集
      • 4.3 从CSV文件加载数据(不依赖Pandas)
      • 4.4 从图像文件夹加载数据(使用 image_dataset_from_directory)
      • 4.5 手动构建图像数据集(使用 `tf.data.Dataset.list_files`)
      • 4.6 数据归一化与增强
      • 4.7 性能优化对比
    • 5. 案例实操演练
      • 5.1 准备数据(使用内置数据集模拟)
      • 5.2 构建流水线
      • 5.3 模型训练演示(简单CNN)
    • 6. 常见坑点与排错总结
      • 6.1 数据加载坑点
      • 6.2 数据预处理的常见错误
      • 6.3 数据增强坑点
      • 6.4 性能优化坑点
    • 7. 知识点总结 + 课后作业
      • 7.1 核心知识点梳理
      • 7.2 基础作业
      • 7.3 进阶实操作业
      • 7.4 思考拓展题
  • 🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航

1. 课前导读

1.1 本节课学习目标

  • 掌握使用tf.keras.datasets加载常用内置数据集(MNIST、Fashion-MNIST、CIFAR-10/100、IMDB、波士顿房价等)。
  • 理解tf.data.Dataset的核心概念,能够从不同数据源(NumPy数组、Pandas DataFrame、CSV文件、图像目录)创建数据集。
  • 熟练应用数据预处理操作:归一化(Min-Max、Z-score)、独热编码、图像缩放与数据增强。
  • 掌握tf.data流水线的性能优化技巧:缓存(cache)、混洗(shuffle)、批处理(batch)、预取(prefetch)。
  • 能够构建完整的图像分类数据加载流水线,支持训练/验证/测试集划分。

1.2 知识重难点

类别内容
重点 tf.data.Dataset的创建与转换;归一化的必要性及实现方式;图像数据增强(tf.image)
难点 自定义数据集中的标签生成(从目录名或文件名解析);map函数的并行化及性能影响;大数据集的缓存策略(内存/文件)
易混淆点 shuffle的buffer_size含义;batch与prefetch的顺序;训练集与验证集是否应使用相同的数据增强

1.3 学习前置条件

  • 已掌握第2课的NumPy基础和第5课的张量操作。
  • 能够搭建简单的全连接网络(第12、15课)。
  • 熟悉文件系统基本操作(路径、读取文件)。

1.4 学完可掌握能力

  • 为任何自定义数据集(如图像分类、文本分类)构建高效的数据加载管道。
  • 独立完成数据标准化、增强等预处理步骤,提升模型泛化能力。
  • 诊断数据加载瓶颈(I/O)并使用prefetch、cache等方法优化。
  • 将数据集按需划分为训练、验证、测试集。

1.5 行业应用场景

  • 计算机视觉:从文件夹加载数百万张图像,实施在线数据增强(随机翻转、颜色抖动)。
  • 自然语言处理:从CSV加载评论及标签,进行分词和序列化。
  • 推荐系统:从用户交互日志构建tf.data流水线,处理稀疏特征。
  • 生产部署:使用tf.data服务端进行高效的数据预处理。

2. 核心理论精讲

2.1 TensorFlow 内置数据集

tf.keras.datasets提供了多个经典数据集,适合快速测试模型和算法。常用数据集:

  • MNIST:手写数字(28×28灰度,10类,60000训练+10000测试)
  • Fashion-MNIST:服装图像(同MNIST规格)
  • CIFAR-10/100:彩色图像(32×32×3,10/100类,50000训练+10000测试)
  • IMDB:电影评论(已预处理的词索引序列,二分类)
  • Boston Housing:房价回归(506样本,13特征)

加载方式统一为:

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

返回的已是NumPy数组,可直接用于训练。但内置数据集规模小,仅适合教学和快速原型。

2.2 tf.data.Dataset 核心概念

tf.data.Dataset是TensorFlow中处理数据流的主力API,支持:

  • 惰性求值:元素在需要时才生成,节省内存。
  • 函数式变换:map、filter、batch、shuffle等。
  • 高性能:与TF图执行结合,自动并行化。

创建数据集的主要方式:

  • 从内存张量:tf.data.Dataset.from_tensor_slices()
  • 从生成器:tf.data.Dataset.from_generator()
  • 从文件(TFRecord、文本、CSV):tf.data.TextLineDataset、tf.data.TFRecordDataset
  • 2.3 数据归一化

    归一化将特征缩放到相似范围(通常[0,1]或均值为0方差1),原因:

    • 梯度下降效率:不同尺度的特征会导致损失函数等高线呈椭圆,梯度下降缓慢且震荡。
    • 防止数值不稳定:大数值特征可能造成梯度爆炸。
    • 提高模型泛化:部分正则化方法对尺度敏感。

    Min-Max归一化: [ x_{\\text{norm}} = \\frac{x – x_{\\min}}{x_{\\max} – x_{\\min}} ] 将数值映射到[0,1],适用于已知边界的特征(如图像像素0-255)。

    Z-score标准化: [ x_{\\text{std}} = \\frac{x – \\mu}{\\sigma} ] 使数据均值为0,标准差为1。适用于特征分布未知或存在异常值。

    在图像任务中,通常简单地将像素值除以255.0(Min-Max)。更高级的标准化是使用ImageNet的均值和标准差([0.485, 0.456, 0.406] / [0.229, 0.224, 0.225])。

    2.4 图像数据增强

    数据增强通过对训练图像施加随机变换(而不改变标签),增加训练样本的多样性,缓解过拟合。常用操作:

    • 几何变换:随机翻转(水平/垂直)、旋转、缩放、裁剪、平移。
    • 颜色变换:亮度、对比度、饱和度、色调的随机调整。
    • 噪声注入:高斯噪声、高斯模糊。

    TensorFlow提供了tf.image模块中的多种增强函数。实践中可组合使用。

    注意事项:

    • 验证集和测试集不应使用数据增强(仅做归一化/中心裁剪)。
    • 数据增强应在map中动态应用,而非预先存储,以节省磁盘空间。

    2.5 数据流水线性能优化

    • shuffle(buffer_size):随机打乱数据集。buffer_size应大于数据集大小(或至少等于一个epoch的样本数),以确保充分混洗。但过大会增加内存。
    • batch(batch_size):将连续元素组合成批次。
    • map(map_func, num_parallel_calls):对每个元素应用预处理。设置num_parallel_calls=tf.data.AUTOTUNE可自动并行化。
    • cache(filename=''):将数据集缓存到内存或文件。若在第一次epoch后数据不再改变,可显著加速后续epoch。
    • prefetch(buffer_size):在GPU训练的同时预取下一批数据,重叠数据加载与计算。推荐prefetch(tf.data.AUTOTUNE)。

    典型流水线模式:

    dataset = dataset.shuffle(10000).map(preprocess, num_parallel_calls=tf.data.AUTOTUNE).batch(128).prefetch(tf.data.AUTOTUNE)

    3. 环境搭建与工具配置

    沿用第15课的环境,需额外安装Pillow库用于图像处理(若需加载自定义图像)。

    conda activate tf213
    pip install pillow

    创建项目目录结构:

    data/
    train/
    cat/
    cat1.jpg

    dog/

    val/
    cat/
    dog/

    导入所需模块:

    import tensorflow as tf
    import numpy as np
    import matplotlib.pyplot as plt
    import pathlib
    import PIL
    from sklearn.model_selection import train_test_split

    4. 代码实战教学

    4.1 加载内置数据集

    # MNIST 示例
    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    print(f"MNIST train shape: {x_train.shape}, test shape: {x_test.shape}")
    # 归一化
    x_train = x_train.astype(np.float32) / 255.0
    x_test = x_test.astype(np.float32) / 255.0

    # 转换为 Dataset
    train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32).shuffle(10000)

    4.2 从NumPy数组创建数据集

    # 模拟数据
    X = np.random.randn(1000, 20).astype(np.float32)
    y = np.random.randint(0, 2, size=(1000, 1)).astype(np.float32)

    # 创建 Dataset
    dataset = tf.data.Dataset.from_tensor_slices((X, y))
    dataset = dataset.batch(64).prefetch(tf.data.AUTOTUNE)

    4.3 从CSV文件加载数据(不依赖Pandas)

    # 创建示例CSV
    import tempfile
    import csv

    with tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False) as f:
    writer = csv.writer(f)
    writer.writerow(['feature1', 'feature2', 'label'])
    for _ in range(100):
    writer.writerow([np.random.rand(), np.random.rand(), np.random.randint(0,2)])
    csv_path = f.name

    # 使用 TextLineDataset 读取
    def parse_csv_line(line):
    # 跳过表头
    defaults = [[0.0], [0.0], [0]] # 默认值
    parsed = tf.io.decode_csv(line, record_defaults=defaults)
    features = tf.stack(parsed[:1])
    label = parsed[1]
    return features, label

    dataset = tf.data.TextLineDataset(csv_path).skip(1) # 跳过表头
    dataset = dataset.map(parse_csv_line).batch(32)
    for feat, lab in dataset.take(1):
    print(f"Features shape: {feat.shape}, label: {lab.shape}")

    4.4 从图像文件夹加载数据(使用 image_dataset_from_directory)

    TensorFlow提供高级API image_dataset_from_directory,自动根据子文件夹名称生成标签。

    # 假设 data/train 下有 cat 和 dog 子文件夹
    train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    'data/train',
    validation_split=0.2,
    subset='training',
    seed=123,
    image_size=(150, 150),
    batch_size=32
    )

    val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    'data/train',
    validation_split=0.2,
    subset='validation',
    seed=123,
    image_size=(150, 150),
    batch_size=32
    )

    # 查看类别名称
    class_names = train_ds.class_names
    print("Classes:", class_names)

    4.5 手动构建图像数据集(使用 tf.data.Dataset.list_files)

    当需要更精细的控制时,可手动遍历文件。

    data_dir = pathlib.Path('data/train')
    image_paths = list(data_dir.glob('*/*.jpg'))
    image_paths = [str(p) for p in image_paths]
    labels = [1 if 'dog' in p.parent.name else 0 for p in image_paths] # 示例

    # 创建 Dataset
    path_ds = tf.data.Dataset.from_tensor_slices(image_paths)
    label_ds = tf.data.Dataset.from_tensor_slices(labels)

    def load_and_preprocess(path, label):
    image = tf.io.read_file(path)
    image = tf.image.decode_jpeg(image, channels=3)
    image = tf.image.resize(image, (150, 150))
    image = tf.cast(image, tf.float32) / 255.0
    return image, label

    dataset = tf.data.Dataset.zip((path_ds, label_ds))
    dataset = dataset.map(load_and_preprocess, num_parallel_calls=tf.data.AUTOTUNE)
    dataset = dataset.shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE)

    4.6 数据归一化与增强

    # 归一化函数
    def normalize(image, label):
    image = tf.cast(image, tf.float32) / 255.0
    return image, label

    # 数据增强函数(仅训练集)
    def augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_brightness(image, max_delta=0.2)
    image = tf.image.random_contrast(image, lower=0.8, upper=1.2)
    return image, label

    # 构建流水线
    train_ds = tf.keras.preprocessing.image_dataset_from_directory('data/train', image_size=(150,150), batch_size=32)
    train_ds = train_ds.map(normalize).map(augment).shuffle(1000).prefetch(tf.data.AUTOTUNE)

    val_ds = tf.keras.preprocessing.image_dataset_from_directory('data/val', image_size=(150,150), batch_size=32)
    val_ds = val_ds.map(normalize).prefetch(tf.data.AUTOTUNE)

    4.7 性能优化对比

    import time

    def create_pipeline_without_prefetch():
    ds = tf.data.Dataset.from_tensor_slices(np.random.rand(10000, 224, 224, 3).astype(np.float32))
    ds = ds.batch(64).map(lambda x: x * 2) # 模拟计算
    return ds

    def create_pipeline_with_prefetch():
    ds = tf.data.Dataset.from_tensor_slices(np.random.rand(10000, 224, 224, 3).astype(np.float32))
    ds = ds.batch(64).map(lambda x: x * 2, num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)
    return ds

    # 测量时间
    start = time.time()
    for _ in create_pipeline_without_prefetch():
    pass
    print(f"Without prefetch: {time.time()start:.2f}s")

    start = time.time()
    for _ in create_pipeline_with_prefetch():
    pass
    print(f"With prefetch: {time.time()start:.2f}s")

    5. 案例实操演练

    案例:猫狗图像分类完整数据流水线

    目标:从包含猫和狗图像的文件夹中构建训练/验证数据集,应用归一化和数据增强,并可视化一批处理后的图像。

    5.1 准备数据(使用内置数据集模拟)

    实际中可下载猫狗数据集,这里我们使用随机生成图像模拟。

    import random
    import os

    # 创建模拟目录结构
    base_dir = './pet_images'
    os.makedirs(os.path.join(base_dir, 'train/cat'), exist_ok=True)
    os.makedirs(os.path.join(base_dir, 'train/dog'), exist_ok=True)
    os.makedirs(os.path.join(base_dir, 'val/cat'), exist_ok=True)
    os.makedirs(os.path.join(base_dir, 'val/dog'), exist_ok=True)

    # 生成随机图像(生产环境应使用真实图像)
    def create_dummy_image(path):
    img = np.random.randint(0, 255, (150, 150, 3), dtype=np.uint8)
    PIL.Image.fromarray(img).save(path)

    for i in range(100):
    create_dummy_image(os.path.join(base_dir, f'train/cat/cat_{i}.jpg'))
    create_dummy_image(os.path.join(base_dir, f'train/dog/dog_{i}.jpg'))
    for i in range(50):
    create_dummy_image(os.path.join(base_dir, f'val/cat/cat_{i}.jpg'))
    create_dummy_image(os.path.join(base_dir, f'val/dog/dog_{i}.jpg'))

    5.2 构建流水线

    IMG_SIZE = (150, 150)
    BATCH_SIZE = 32

    train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    os.path.join(base_dir, 'train'),
    image_size=IMG_SIZE,
    batch_size=BATCH_SIZE,
    shuffle=True,
    validation_split=0.2,
    subset='training',
    seed=42
    )

    val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    os.path.join(base_dir, 'train'),
    image_size=IMG_SIZE,
    batch_size=BATCH_SIZE,
    shuffle=False,
    validation_split=0.2,
    subset='validation',
    seed=42
    )

    # 归一化函数
    def normalize(image, label):
    return tf.cast(image, tf.float32) / 255.0, label

    # 数据增强函数
    def augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_brightness(image, 0.1)
    image = tf.image.random_contrast(image, 0.8, 1.2)
    return image, label

    train_ds = train_ds.map(normalize).map(augment, num_parallel_calls=tf.data.AUTOTUNE)
    val_ds = val_ds.map(normalize)

    train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
    val_ds = val_ds.prefetch(tf.data.AUTOTUNE)

    # 可视化一个batch
    for images, labels in train_ds.take(1):
    plt.figure(figsize=(12, 12))
    for i in range(9):
    plt.subplot(3, 3, i+1)
    plt.imshow(images[i].numpy())
    plt.title(train_ds.class_names[labels[i]])
    plt.axis('off')
    plt.show()

    5.3 模型训练演示(简单CNN)

    model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, 3, activation='relu', input_shape=(150,150,3)),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(64, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
    ])
    model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
    history = model.fit(train_ds, validation_data=val_ds, epochs=5)

    6. 常见坑点与排错总结

    6.1 数据加载坑点

    • 坑1:image_dataset_from_directory 默认将所有子文件夹名作为类别,且按字母顺序分配标签。若文件夹名不是数字,需谨慎处理。

      • 解决:使用class_names参数显式指定顺序,或从返回值获取class_names。
    • 坑2:图片解码失败(格式不支持或损坏)。

      • 解决:在map函数中加入tf.image.decode_image并设置错误处理(try…except),或使用tf.io.decode_jpeg限制格式。
    • 坑3:CSV文件中包含缺失值或特殊字符,导致decode_csv失败。

      • 解决:预处理CSV文件,或使用skip_errors过滤器。

    6.2 数据预处理的常见错误

    • 坑4:忘记将标签转换为独热编码而直接使用交叉熵(categorical_crossentropy)。

      • 解决:若标签为整数,应使用sparse_categorical_crossentropy,或手动转换tf.one_hot。
    • 坑5:归一化时使用tf.cast后再除以255,但忘记设置数据类型为浮点,整数除法导致结果为0。

      • 解决:先cast后除法,或直接用image / 255.0(TensorFlow会自动提升类型)。

    6.3 数据增强坑点

    • 坑6:验证集也应用了随机增强,导致验证指标不稳定且不可复现。

      • 解决:仅对训练集应用增强,验证集只做归一化。
    • 坑7:map函数中使用Python库(如PIL)导致性能极差,无法在num_parallel_calls下受益。

      • 解决:优先使用tf.image中的操作,它们在图内执行,效率高。

    6.4 性能优化坑点

    • 坑8:shuffle的buffer_size设置过小(如32),导致打乱不充分,模型泛化差。

      • 建议:至少与一个epoch的样本数相当,但若内存不足可适当减小。
    • 坑9:重复多次使用cache()且未指定文件名,导致内存占用飙升。

      • 解决:对于超大数据集,使用cache(filename)缓存到磁盘,或仅在小数据集上内存缓存。
    • 坑10:在map中执行IO操作(如读取文件)而未设置num_parallel_calls,造成训练瓶颈。

      • 解决:设置num_parallel_calls=tf.data.AUTOTUNE,并在map后紧跟prefetch。

    7. 知识点总结 + 课后作业

    7.1 核心知识点梳理

    • 内置数据集:tf.keras.datasets快速加载经典数据集。
    • tf.data.Dataset:从多种数据源创建高效流水线,支持函数式变换。
    • 归一化:Min-Max([0,1])和Z-score,加速收敛。
    • 图像数据增强:tf.image随机变换,提升泛化。
    • 流水线优化:cache、shuffle、batch、prefetch、num_parallel_calls。

    7.2 基础作业

  • 使用tf.keras.datasets.cifar10加载数据,将其归一化并转换为tf.data.Dataset,设置批大小为64,训练一个简单的全连接网络(展平后),记录测试准确率。
  • 编写一个从CSV文件读取特征(5个数值特征)和标签(二分类)的数据流水线,并实现Z-score标准化(使用训练集的均值和标准差)。
  • 解释为什么prefetch(tf.data.AUTOTUNE)能提高训练速度。
  • 7.3 进阶实操作业

    任务:实现支持训练/验证/测试划分的通用数据加载器

    要求:

    • 给定一个文件夹,其中包含类别子文件夹(如train/class1/, train/class2/),以及可选的test文件夹。
    • 编写函数load_image_dataset(data_dir, val_split=0.2, test_split=0.1, img_size=(224,224), batch_size=32),返回训练、验证、测试三个tf.data.Dataset对象。
    • 支持数据增强(可配置是否启用)。
    • 输出类别名称和每个集的样本数量。
    • 测试:使用任意图像数据集(如猫狗)验证流水线正确性。

    7.4 思考拓展题

  • 对于非常大的数据集(超出内存),shuffle的buffer_size应如何选择?如果不进行充分混洗(例如顺序读取),会对模型训练产生什么影响?

  • 在图像分类中,如果训练集和验证集来自不同分布(例如训练集为白天拍摄,验证集为夜晚拍摄),数据增强能否完全解决?如果不能,应该采取什么额外措施?

  • 研究tf.data.experimental.AUTOTUNE的内部机制。它是如何动态调整并行度的?在什么情况下自动调优可能不是最优?


  • 下一课预告:模型训练流程全流程——我们将深入探讨训练循环的细节,包括批次训练、迭代控制、学习率调度、早停机制的实现,以及如何使用回调函数自动化管理训练过程。


    🔗《TensorFlow2.x: 深度学习入门到高阶实战教程》系列课程导航

    去订阅

    第一部分:基础入门(1-10 课) 第二部分:神经网络核心(11-25 课) 第三部分:进阶网络与框架高阶(26-40 课) 第四部分:企业实战与项目落地(41-50 课)

    🌟 感谢您耐心阅读到这里! 💡 如果本文对您有所启发欢迎: 👍 点赞📌 收藏 📤 分享给更多需要的伙伴。 🗣️ 期待在评论区看到您的想法, 共同进步。 🔔 关注我,持续获取更多干货内容~ 🤗 我们下篇文章见~

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 第16课:TensorFlow|数据集加载与预处理【TF内置数据集、自定义数据集制作、归一化处理】
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!