2.1.CIFAR-10数据集

epoch: 

是什么:

  • 1 个 epoch = 把整个训练集完整遍历、计算一遍;

为什么:

  • 学完全部避免只学了局部;
  • 方便观察训练进度;
  •  防止:不足时欠拟合(类似于学习次数太少没学透知识);过多时过拟合(学习过多,全部死记硬背下来,在训练集中好,但测试里差)
  • 学习率衰减(前期:大学习率 → 快速下降,快速缩小损失范围;后期:逐步降低学习率 → 微调权重,平稳收敛到最优谷底,提升最终精度)
  • 进行模型保持,方便后续评估

怎么用:

  • 「总样本数 /batch_size」算每 epoch 步数,设定总步数对应 epoch 数,在循环中监控进度。

TensorFlow中的数据读取:

原始输入层-数据集初始化-Epoch循环-数据预处理映射层-迭代-二进制写入本地

import tensorflow as tf
import os
#创文件
if not os.path.exists('read_tf2'):
    os.makedirs('read_tf2')

file_list = ["A.jpg", "B.jpg", "C.jpg"]
# 1. 构建数据集,repeat(5) 等价 num_epochs=5
ds = tf.data.Dataset.from_tensor_slices(file_list).repeat(5)

# 读取整文件二进制
def load_img(path):
    return tf.io.read_file(path)

ds = ds.map(load_img)

# 迭代读取保存
for idx, img_bin in enumerate(ds):
    with open(f"read_tf2/test_{idx+1}.jpg", "wb") as f:
        f.write(img_bin.numpy())
print("5轮遍历完成,自动结束,无异常抛出")

2.1.4实验:将CIFAR-10数据集保存为图片形式

import tensorflow as tf
import os
import numpy as np
from PIL import Image
from cifar10_input import load_bin_file  # 导入修复后的函数

if __name__ == "__main__":
    data_dir = "cifar10_data/cifar-10-batches-bin"
    save_dir = "cifar10_data/raw"
    if not os.path.exists(save_dir):#解析后存放处
        os.makedirs(save_dir)

    # 加载第一个训练bin文件
    first_bin = os.path.join(data_dir, "data_batch_1.bin")
    ds = load_bin_file(first_bin)
    # 只取前30张图片
    ds = ds.take(30)

    save_idx = 0#从0开始命名
    for img_tensor, label_tensor in ds:#图片,标签
        # 浮点转uint8图像
        img_arr = np.clip(img_tensor.numpy(), 0, 255).astype(np.uint8)
                            #转为numpy数组,才能被PIL识别
                    #裁剪像素,防止花屏全黑图
                                                        #强制转型为8位
        img = Image.fromarray(img_arr)#像素数组转为图像
        img.save(os.path.join(save_dir, f"{save_idx}.jpg"))#写入磁盘
        save_idx += 1#文件名+1
    print(f"✅ 成功保存30张CIFAR10图片至 {save_dir}")
"""
TF2 原生tf.data重构CIFAR10二进制读取,替代TF1 string_input_producer、FixedLengthRecordReader队列
完全兼容原接口调用逻辑,无废弃API、无线程手动管理、无Session依赖
"""
import tensorflow as tf
import os

# 基础超参
IMAGE_SIZE = 24
NUM_CLASSES = 10
NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000
NUM_EXAMPLES_PER_EPOCH_FOR_EVAL = 10000
RECORD_BYTES = 1 + 3 * 32 * 32  # 1字节标签 + 3通道32*32图像

def parse_single_cifar_record(raw_record):
    """解析单条CIFAR10二进制样本,等价原read_cifar10读取逻辑"""
    # 拆分标签与图像
    label = tf.cast(tf.slice(raw_record, [0], [1]), tf.int32)
    image_raw = tf.slice(raw_record, [1], [3 * 32 * 32])
    # 通道优先转通道最后 [C,H,W] -> [H,W,C]
    image = tf.reshape(image_raw, [3, 32, 32])
    image = tf.transpose(image, [1, 2, 0])
    image = tf.cast(image, tf.float32)
    return image, label

def load_bin_file(file_path):
    """读取单个bin文件,解码全部样本(兼容旧版TF2,无decode_fixed_length_records依赖)"""
    # 读取整个bin文件为字节串
    file_content = tf.io.read_file(file_path)
    # 解码为uint8字节张量
    bytes_tensor = tf.io.decode_raw(file_content, tf.uint8)
    # 计算总样本数 = 总字节数 ÷ 单条样本字节数
    total_records = tf.shape(bytes_tensor)[0] // RECORD_BYTES
    # 截断到完整样本长度,避免余数
    bytes_tensor = bytes_tensor[:total_records * RECORD_BYTES]
    # 重塑为 [样本数, 单条样本字节数]
    records = tf.reshape(bytes_tensor, [total_records, RECORD_BYTES])
    # 构建数据集并逐条解析
    ds = tf.data.Dataset.from_tensor_slices(records)
    ds = ds.map(parse_single_cifar_record, num_parallel_calls=tf.data.AUTOTUNE)
    return ds

def distorted_inputs(data_dir, batch_size):
    """
    训练集:带数据增强,等价原版distorted_inputs
    return: image_batch (B,24,24,3), label_batch (B,)
    """
    # 拼接5个训练bin文件
    file_paths = [os.path.join(data_dir, f"data_batch_{i}.bin") for i in range(1, 6)]
    ds = tf.data.Dataset.from_tensor_slices(file_paths)
    # 逐个加载bin文件并合并数据集
    ds = ds.flat_map(load_bin_file)

    # 数据增强
    def augment(image, label):
        # 随机裁剪 32->24
        image = tf.image.random_crop(image, [IMAGE_SIZE, IMAGE_SIZE, 3])
        # 随机水平翻转
        image = tf.image.random_flip_left_right(image)
        # 亮度、对比度
        image = tf.image.random_brightness(image, max_delta=63)
        image = tf.image.random_contrast(image, lower=0.2, upper=1.8)
        # 标准化
        image = tf.image.per_image_standardization(image)
        return image, tf.reshape(label, [])

    ds = ds.map(augment, num_parallel_calls=tf.data.AUTOTUNE)
    # 打乱、批次、预取加速
    min_queue_examples = int(NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN * 0.4)
    ds = ds.shuffle(buffer_size=min_queue_examples)
    ds = ds.batch(batch_size, drop_remainder=False)
    ds = ds.prefetch(tf.data.AUTOTUNE)
    return ds

def inputs(eval_data, data_dir, batch_size):
    """
    测试/验证集:无随机增强,中心裁剪,等价原版inputs
    """
    if not eval_data:
        file_paths = [os.path.join(data_dir, f"data_batch_{i}.bin") for i in range(1, 6)]
    else:
        file_paths = [os.path.join(data_dir, "test_batch.bin")]

    ds = tf.data.Dataset.from_tensor_slices(file_paths)
    ds = ds.flat_map(load_bin_file)

    # 测试预处理:中心裁剪+标准化
    def preprocess_eval(image, label):
        image = tf.image.resize_with_crop_or_pad(image, IMAGE_SIZE, IMAGE_SIZE)
        image = tf.image.per_image_standardization(image)
        return image, tf.reshape(label, [])

    ds = ds.map(preprocess_eval, num_parallel_calls=tf.data.AUTOTUNE)
    ds = ds.batch(batch_size)
    ds = ds.prefetch(tf.data.AUTOTUNE)
    return ds

2.2利用TensorFlow训练CIFAR-10识别模型

数据增强(distorted_inputs()):在训练阶段对原图做随机几何、像素变换,用有限 5 万张训练图生成大量 “新样本”,扩充数据分布,降低过拟合、提升泛化能力,让模型在测试集陌生图片上表现更好。

建立模型识别图像: cifar10_inference_tf2()

输入归一化后的 24×24 彩色图片 → 双层卷积提取视觉特征 → 池化降维 + 归一化增强特征 → 特征扁平化 → 两层全连接提炼高层语义 → 输出 10 分类原始得分

import tensorflow as tf

# 全局常量和原代码保持一致
NUM_CLASSES = 10

def lrn_alternative(x):
   
    return tf.keras.layers.BatchNormalization()(x)

def cifar10_inference_tf2(images, batch_size):
   #TF2 等价原TF1 inference推理函数
   # 第一层卷积conv1:提取底层基础视觉特征:边缘、线条、色块、纹理,是图像最基础像素级特征
   # 权重初始化 stddev=5e-2,L2权重衰减wd=0.0
    conv1 = tf.keras.layers.Conv2D(
        filters=64,kernel_size=(5, 5),activation="relu",
        strides=(1, 1),
        padding="SAME",
        kernel_initializer=tf.keras.initializers.RandomNormal(stddev=5e-2),
        bias_initializer=tf.keras.initializers.Zeros(),
        kernel_regularizer=tf.keras.regularizers.L2(0.0),  
        name="conv1"
    )(images)

    # 第一层最大池化 pool1
    pool1 = tf.keras.layers.MaxPool2D(
        pool_size=(3, 3),
        strides=(2, 2),
        padding="SAME",
        name="pool1"
    )(conv1)

    # 归一化:norm1 LRN 
    norm1 = lrn_alternative(pool1)

    # 第二层卷积 conv2 
    conv2 = tf.keras.layers.Conv2D(
        filters=64,
        kernel_size=(5, 5),
        strides=(1, 1),
        padding="SAME",
        kernel_initializer=tf.keras.initializers.RandomNormal(stddev=5e-2),
        bias_initializer=tf.keras.initializers.Constant(0.1),
        kernel_regularizer=tf.keras.regularizers.L2(0.0),
        activation="relu",
        name="conv2"
    )(norm1)

    #原norm2 LRN
    norm2 = lrn_alternative(conv2)

    #第二层最大池化 pool2
    pool2 = tf.keras.layers.MaxPool2D(
        pool_size=(3, 3),
        strides=(2, 2),
        padding="SAME",
        name="pool2"
    )(norm2)

    # 展平,适配全连接
    flatten = tf.keras.layers.Flatten()(pool2)
    # 固定batch维度,和原代码reshape([batch_size, -1])行为一致
    flatten = tf.reshape(flatten, [batch_size, -1])

    # 全连接 local3  2304 -> 384
    local3 = tf.keras.layers.Dense(
        units=384,
        kernel_initializer=tf.keras.initializers.RandomNormal(stddev=0.04),
        bias_initializer=tf.keras.initializers.Constant(0.1),
        kernel_regularizer=tf.keras.regularizers.L2(0.004),  # 原wd=0.004
        activation="relu",
        name="local3"
    )(flatten)

    #全连接 local4 384 -> 192
    local4 = tf.keras.layers.Dense(
        units=192,
        kernel_initializer=tf.keras.initializers.RandomNormal(stddev=0.04),
        bias_initializer=tf.keras.initializers.Constant(0.1),
        kernel_regularizer=tf.keras.regularizers.L2(0.004),
        activation="relu",
        name="local4"
    )(local3)

    #输出分类层 softmax_linear 192->10
    logits = tf.keras.layers.Dense(
        units=NUM_CLASSES,
        kernel_initializer=tf.keras.initializers.RandomNormal(stddev=1.0 / 192.0),
        bias_initializer=tf.keras.initializers.Zeros(),
        kernel_regularizer=tf.keras.regularizers.L2(0.0),  # 输出层无权重衰减
        activation=None,  # 关键:和原版一致,不提前softmax
        name="softmax_linear"
    )(local4)

    return logits


# 封装为完整Sequential模型
def create_cifar10_sequential_model(batch_size):
    input_tensor = tf.keras.Input(shape=(24, 24, 3), batch_size=batch_size)
    out = cifar10_inference_tf2(input_tensor, batch_size)
    model = tf.keras.Model(inputs=input_tensor, outputs=out, name="CIFAR10_Official_TF2")
    return model

训练模型:

(没弄清楚代码,累了。)

# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
# ==============================================================================
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

from datetime import datetime
import time
import sys
import os

import cifar10
import cifar10_input
from absl import flags
FLAGS = flags.FLAGS

flags.DEFINE_string('train_dir', 'cifar10_train/', "")
flags.DEFINE_string('data_dir', 'cifar10_data/cifar-10-batches-bin/', "")
flags.DEFINE_integer('batch_size', 128, "")
flags.DEFINE_integer('max_steps', 100000, "")
flags.DEFINE_boolean('log_device_placement', False, "")
flags.DEFINE_integer('log_frequency', 10, "")
flags.DEFINE_float('learning_rate', 0.1, "")

NUM_EXAMPLES_PER_EPOCH_FOR_TRAIN = 50000

def train():
    with tf.Graph().as_default():
        global_step = tf.train.get_or_create_global_step()

        # 数据加载
        train_dataset = cifar10_input.distorted_inputs(
            data_dir=FLAGS.data_dir,
            batch_size=FLAGS.batch_size
        )
        data_iterator = tf.compat.v1.data.make_one_shot_iterator(train_dataset)
        images, labels = data_iterator.get_next()

        # 网络与损失
        logits = cifar10.inference(images)
        loss = cifar10.loss(logits, labels)
        train_op = cifar10.train(loss, global_step)

        # TensorBoard 日志
        tf.summary.scalar('train_loss', loss)
        merged_summary = tf.summary.merge_all()

        # 手动模型保存器
        saver = tf.train.Saver(max_to_keep=5)

        class _LoggerHook(tf.train.SessionRunHook):
            def begin(self):
                self._step = -1
                self._start_time = time.time()
                self.summary_writer = tf.summary.FileWriter(FLAGS.train_dir, tf.get_default_graph())

            def before_run(self, run_context):
                self._step += 1
                return tf.train.SessionRunArgs([loss, global_step, merged_summary])

            def after_run(self, run_context, run_values):
                loss_val, step, summary = run_values.results

                # 写入 TensorBoard
                if self._step % FLAGS.log_frequency == 0:
                    self.summary_writer.add_summary(summary, step)
                    cost_time = time.time() - self._start_time
                    self._start_time = time.time()
                    samples_per_sec = FLAGS.log_frequency * FLAGS.batch_size / cost_time
                    sec_per_batch = cost_time / FLAGS.log_frequency
                    print(f"{datetime.now()}: step {step}, loss={loss_val:.2f}, {samples_per_sec:.1f} samples/sec")

                # 每 500 步手动保存模型
                if step % 500 == 0 and step > 0:
                    saver.save(
                        run_context.session,
                        os.path.join(FLAGS.train_dir, "model.ckpt"),
                        global_step=step
                    )

        # 关键:彻底关闭自动保存,避免参数冲突
        with tf.train.MonitoredTrainingSession(
            checkpoint_dir=FLAGS.train_dir,
            save_checkpoint_secs=None,   # 关闭按时间保存
            save_checkpoint_steps=None,  # 关闭按步数保存
            hooks=[
                tf.train.StopAtStepHook(last_step=FLAGS.max_steps),
                tf.train.NanTensorHook(loss),
                _LoggerHook()
            ],
            config=tf.ConfigProto(log_device_placement=FLAGS.log_device_placement)
        ) as mon_sess:
            while not mon_sess.should_stop():
                mon_sess.run(train_op)

def main(argv=None):
    train_path = FLAGS.train_dir
    if tf.io.gfile.exists(train_path):
        tf.io.gfile.rmtree(train_path)
    tf.io.gfile.makedirs(train_path)
    time.sleep(0.3)
    train()

if __name__ == '__main__':
    FLAGS(sys.argv)
    main()

启动Anaconda Navigator命令窗口

  • conda activate dl_21project
  • D:
  • cd Deep-Learning-21-Examples\chapter_2
  • python cifar10_train.py --train_dir cifar10_train/ --data_dir cifar10_data/cifar-10-batches-bin/

这里使用的是cpu训练,输入修改def train()后可以查看:

def train():
    # ========== 新增:设备检测,启动就打印CPU/GPU状态 ==========
    physical_devices = tf.config.list_physical_devices()
    print("===== 训练硬件设备检测结果 =====")
    for dev in physical_devices:
        print(f"检测到设备: {dev}")
    gpu_devices = tf.config.list_physical_devices('GPU')
    if len(gpu_devices) > 0:
        print("✅ 识别到GPU,将使用GPU加速训练")
    else:
        print("❌ 无可用GPU,将使用CPU训练")
    print("==================================\n")

    with tf.Graph().as_default():
        global_step = tf.train.get_or_create_global_step()
        # 下方原有代码完全不变
        train_dataset = cifar10_input.distorted_inputs(
            data_dir=FLAGS.data_dir,
            batch_size=FLAGS.batch_size
        )
        data_iterator = tf.compat.v1.data.make_one_shot_iterator(train_dataset)
        images, labels = data_iterator.get_next()

        logits = cifar10.inference(images)
        loss = cifar10.loss(logits, labels)
        train_op = cifar10.train(loss, global_step)

        tf.summary.scalar('train_loss', loss)
        merged_summary = tf.summary.merge_all()
        saver = tf.train.Saver(max_to_keep=5)

        class _LoggerHook(tf.train.SessionRunHook):
            def begin(self):
                self._step = -1
                self._start_time = time.time()
                self.summary_writer = tf.summary.FileWriter(FLAGS.train_dir, tf.get_default_graph())

            def before_run(self, run_context):
                self._step += 1
                return tf.train.SessionRunArgs([loss, global_step, merged_summary])

            def after_run(self, run_context, run_values):
                loss_val, step, summary = run_values.results
                if self._step % FLAGS.log_frequency == 0:
                    self.summary_writer.add_summary(summary, step)
                    cost_time = time.time() - self._start_time
                    self._start_time = time.time()
                    samples_per_sec = FLAGS.log_frequency * FLAGS.batch_size / cost_time
                    sec_per_batch = cost_time / FLAGS.log_frequency
                    print(f"{datetime.now()}: step {step}, loss={loss_val:.2f}, {samples_per_sec:.1f} samples/sec")
                if step % 500 == 0 and step > 0:
                    saver.save(run_context.session, os.path.join(FLAGS.train_dir, "model.ckpt"), global_step=step)

        with tf.train.MonitoredTrainingSession(
            checkpoint_dir=FLAGS.train_dir,
            save_checkpoint_secs=None,
            save_checkpoint_steps=None,
            hooks=[
                tf.train.StopAtStepHook(last_step=FLAGS.max_steps),
                tf.train.NanTensorHook(loss),
                _LoggerHook()
            ],
            config=tf.ConfigProto(log_device_placement=FLAGS.log_device_placement)
        ) as mon_sess:
            while not mon_sess.should_stop():
                mon_sess.run(train_op)

在TensorFlow中查看训练进度

在Anaconda Navigator命令窗口执行:

执行训练模型后不关闭,新开窗口:

  • D:
  • cd Deep-Learning-21-Examples\chapter_2
  • conda activate dl_21project
  • tensorboard --logdir cifar10_train/ --port 6006

浏览器输入网址: http://localhost:6006/ 

summary_writer.add_summary() 没有被正确执行,loss 数据没写入日志文件:

改后依旧有问题......

每秒训练步数的情况:

代码train有问题,只能看到global_step.......,没看到total -loss -1和learning -rate

测试模型效果:

cifar10_eval.py没运行出来.....

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐