(8-5-02)盲点检测:基于深度学习的疲劳驾驶和盲点检测系统

8.5.5  疲劳驾驶检测模块:训练疲劳驾驶网络模型

编写文件train_cnn.py,功能是使用Keras训练卷积神经网络(CNN)进行疲劳检测。通过命令行参数设置学习率、批处理大小、训练周期数等,使用ImageDataGenerator进行数据增强,分别对训练集和验证集进行图像预处理。在疲劳驾驶网络模型中构建了包含卷积层、池化层、全连接层和Dropout的CNN模型,训练模型并输出训练和验证的准确性和损失。最后,保存并展示训练过程中的准确性和损失曲线。

import argparse
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import ModelCheckpoint

parser = argparse.ArgumentParser(
    description='使用Keras训练卷积神经网络')

# CNN参数
parser.add_argument('--lr', '--learning-rate', default=0.001, type=float,
                    help='初始学习率')

# 训练参数
parser.add_argument('--b', '--batch-size', default=32, type=int,
                    help='训练批次大小')
parser.add_argument('--e', '--num-epochs', '--epochs', default=100, type=int,
                    help='训练周期数')
parser.add_argument('--ve', '--validation-epochs', default=50, type=int,
                    help='验证周期数间隔')
parser.add_argument('--md', '--model-dir', default='models/cnn/model_cnn.h5',
                    help='保存模型的目录')

args = parser.parse_args()

def main():
    # 图像数据增强
    train_augmentation = ImageDataGenerator(
                    rescale=1./255, 
                    rotation_range=40, 
                    width_shift_range=0.2, 
                    height_shift_range=0.2,
                    shear_range=0.2, 
                    zoom_range=0.2, 
                    horizontal_flip=True, 
                    fill_mode='nearest')                  

    val_augmentation = ImageDataGenerator(
                    rescale=1./255)

    # 训练数据生成器
    train_generator = train_augmentation.flow_from_directory(
                    'data/images/train/', 
                    target_size=(24, 24), 
                    batch_size=args.b, 
                    class_mode='binary',
                    color_mode='grayscale')

    # 验证数据生成器
    val_generator = val_augmentation.flow_from_directory(
                'data/images/val/', 
                target_size=(24, 24), 
                batch_size=args.b,
                class_mode='binary',
                color_mode='grayscale')

    # 构建卷积神经网络模型
    model = Sequential()
    model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(24, 24, 1)))
    model.add(MaxPooling2D((2, 2)))
    model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
    model.add(MaxPooling2D((2, 2)))
    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(MaxPooling2D((2, 2)))
    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(MaxPooling2D((2, 2)))
    model.add(Flatten())
    model.add(Dropout(0.5))
    model.add(Dense(512, activation='relu'))
    model.add(Dense(1, activation='sigmoid'))

    model.summary()
    print('批次大小: ', args.b)
    print('学习率: ', args.lr)
    print('周期数: ', args.e)

    # 编译模型
    model.compile(
        loss='binary_crossentropy', 
        optimizer=Adam(learning_rate=args.lr), 
        metrics=['accuracy'])

    # 训练模型
    history = model.fit(
        train_generator, 
        epochs=args.e, 
        validation_data=val_generator, 
        validation_steps=args.ve)
    
    # 保存模型
    model.save(args.md)
    print("训练完成.................................")

    # 绘制训练和验证的准确性和损失曲线
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    epochs = range(1, len(acc) + 1)
    plt.plot(epochs, acc, 'bo', label='训练准确性')
    plt.plot(epochs, val_acc, 'b', label='验证准确性')
    plt.title('训练和验证准确性')
    plt.legend()
    plt.figure()
    plt.plot(epochs, loss, 'bo', label='训练损失')
    plt.plot(epochs, val_loss, 'b', label='验证损失')
    plt.title('训练和验证损失')
    plt.legend()
    plt.show()

if __name__ == "__main__":
    main()

上述代码的实现流程如下:

  1. 首先,通过argparse模块解析命令行参数,包括学习率、批次大小、训练周期数等。
  2. 然后,使用ImageDataGenerator实现图像数据增强,分别为训练集和验证集创建数据生成器。
  3. 接着,构建了一个卷积神经网络(CNN)模型,包括卷积层、池化层、全连接层和Dropout层,用于疲劳检测。
  4. 随后,编译模型,指定损失函数为二元交叉熵,优化器为Adam,并设置评估指标为准确性。
  5. 然后,使用数据生成器训练模型,设置训练周期数和验证周期数。
  6. 最后,输出训练完成的信息,绘制并展示训练和验证准确性以及损失的曲线,如图5-8所示。

通过如下命令运行文件train_cnn.py:

Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #
=================================================================
 conv2d (Conv2D)             (None, 24, 24, 32)        320

 max_pooling2d (MaxPooling2  (None, 12, 12, 32)        0
 D)

 conv2d_1 (Conv2D)           (None, 12, 12, 64)        18496

 max_pooling2d_1 (MaxPoolin  (None, 6, 6, 64)          0
 g2D)

 conv2d_2 (Conv2D)           (None, 6, 6, 128)         73856

 max_pooling2d_2 (MaxPoolin  (None, 3, 3, 128)         0
 g2D)

 conv2d_3 (Conv2D)           (None, 3, 3, 128)         147584

 max_pooling2d_3 (MaxPoolin  (None, 1, 1, 128)         0
 g2D)

 flatten (Flatten)           (None, 128)               0

 dropout (Dropout)           (None, 128)               0

 dense (Dense)               (None, 512)               66048

 dense_1 (Dense)             (None, 1)                 513

=================================================================
Total params: 306817 (1.17 MB)
Trainable params: 306817 (1.17 MB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________
批次大小:  32
学习率:  0.001
周期数:  20
Epoch 1/20
WARNING:tensorflow:From C:\Users\37197\AppData\Roaming\Python\Python311\site-packages\keras\src\utils\tf_utils.py:492: The name tf.ragged.RaggedTensorValue is deprecated. Please use tf.compat.v1.ragged.RaggedTensorValue instead.

WARNING:tensorflow:From C:\Users\37197\AppData\Roaming\Python\Python311\site-packages\keras\src\engine\base_layer_utils.py:384: The name tf.executing_eagerly_outside_functions is deprecated. Please use tf.compat.v1.executing_eagerly_outside_functions instead.

12/12 [==============================] - 1s 28ms/step - loss: 0.6881 - accuracy: 0.5056 - val_loss: 0.6956 - val_accuracy: 0.5000
Epoch 2/20
12/12 [==============================] - 0s 16ms/step - loss: 0.6877 - accuracy: 0.5000 - val_loss: 0.6721 - val_accuracy: 0.5000
Epoch 3/20
12/12 [==============================] - 0s 16ms/step - loss: 0.6789 - accuracy: 0.5861 - val_loss: 0.6509 - val_accuracy: 0.5667
Epoch 4/20
12/12 [==============================] - 0s 16ms/step - loss: 0.6437 - accuracy: 0.6444 - val_loss: 0.6079 - val_accuracy: 0.6250
Epoch 5/20
12/12 [==============================] - 0s 16ms/step - loss: 0.5932 - accuracy: 0.7194 - val_loss: 0.5086 - val_accuracy: 0.8500
Epoch 6/20
12/12 [==============================] - 0s 16ms/step - loss: 0.5707 - accuracy: 0.7417 - val_loss: 0.5508 - val_accuracy: 0.7750
Epoch 7/20
12/12 [==============================] - 0s 16ms/step - loss: 0.5186 - accuracy: 0.7639 - val_loss: 0.4606 - val_accuracy: 0.7833
Epoch 8/20
12/12 [==============================] - 0s 16ms/step - loss: 0.5121 - accuracy: 0.7944 - val_loss: 0.3941 - val_accuracy: 0.7750
Epoch 9/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4489 - accuracy: 0.8028 - val_loss: 0.4276 - val_accuracy: 0.8250
Epoch 10/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4742 - accuracy: 0.7611 - val_loss: 0.4979 - val_accuracy: 0.7333
Epoch 11/20
12/12 [==============================] - 0s 17ms/step - loss: 0.4524 - accuracy: 0.7972 - val_loss: 0.5312 - val_accuracy: 0.7000
Epoch 12/20
12/12 [==============================] - 0s 17ms/step - loss: 0.4967 - accuracy: 0.7778 - val_loss: 0.4732 - val_accuracy: 0.7583
Epoch 13/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4272 - accuracy: 0.8222 - val_loss: 0.4538 - val_accuracy: 0.7583
Epoch 14/20
12/12 [==============================] - 0s 17ms/step - loss: 0.4100 - accuracy: 0.8250 - val_loss: 0.3734 - val_accuracy: 0.8333
Epoch 15/20
12/12 [==============================] - 0s 16ms/step - loss: 0.3998 - accuracy: 0.8417 - val_loss: 0.4613 - val_accuracy: 0.8083
Epoch 16/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4166 - accuracy: 0.8111 - val_loss: 0.3786 - val_accuracy: 0.8333
Epoch 17/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4208 - accuracy: 0.8056 - val_loss: 0.3147 - val_accuracy: 0.8750
Epoch 18/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4490 - accuracy: 0.7889 - val_loss: 0.3403 - val_accuracy: 0.8417
Epoch 19/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4231 - accuracy: 0.8028 - val_loss: 0.3696 - val_accuracy: 0.8167
Epoch 20/20
12/12 [==============================] - 0s 16ms/step - loss: 0.4102 - accuracy: 0.8194 - val_loss: 0.3909 - val_accuracy: 0.8167
训练完成.................................
Length of epochs: 20
Length of val_acc: 20

图5-8  训练和验证准确性以及损失的曲线

8.5.6  疲劳驾驶检测模块:模态转换

编写文件cnn_onnx_export.py,将已经训练好的Keras模型(.h5格式)转换为ONNX模型。通过使用tf2onnx库,加载Keras模型后,通过指定输入规格(24x24x1的浮点数张量)和输出路径,将其转换为ONNX模型(opset=13),最后打印出转换完成的信息。

import argparse
import tf2onnx
import onnxruntime as rt
import tensorflow as tf
from tensorflow.keras.models import load_model

parser = argparse.ArgumentParser(
    description='Export to ONNX model')

parser.add_argument('--i', '--input-model', type=str, default='', 
                    help="Path to input Keras model")
parser.add_argument('--o', '--output-model', type=str, default='', 
                    help="Desired path of converted ONNX model")

args = parser.parse_args()

def main():
    # Load model h5
    model = load_model(args.i)

    # Convert to model onnx
    spec = (tf.TensorSpec((None, 24, 24, 1), tf.float32, name="input"),)
    output_path = args.o

    model_proto, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13, output_path=output_path)
    output_names = [n.name for n in model_proto.graph.output]

    print('Exporting complete.....................')

if __name__ == "__main__":
	main()

执行如下命令将.h5模型转换为.onnx模型:

python3 cnn_onnx_export.py --input-model=models/cnn/model_cnn.h5 --output-model=models/cnn/model_cnn.onnx

8.5.7  盲点检测模块:训练(SSD)模型

在本项目中,通过单发多框检测器(SSD)实现盲点检任务。编写文件train_ssd.py,使用PyTorch训练单发多框检测器(SSD)模型的功能。通过命令行参数指定数据集类型、网络架构、学习率等设置,支持VOC和Open Images数据集。代码实现了训练循环,包括模型初始化、数据加载、损失计算、反向传播和模型保存等步骤。支持使用预训练的SSD模型进行初始化,可以选择冻结部分网络层。训练过程中支持学习率衰减策略,如多步骤和余弦退火。训练完成后,保存训练好的SSD模型。

下面代码是一个用PyTorch实现的单发多框检测器(SSD)的训练脚本,支持在VOC和Open Images数据集上进行训练。它提供了丰富的命令行参数,包括网络架构、数据集路径、学习率等,通过SGD优化器进行训练,支持冻结基础网络层和指定预训练模型,同时实现了训练和测试的核心逻辑。

# 创建命令行解析器
parser = argparse.ArgumentParser(
    description='使用PyTorch进行单发多框检测器(SSD)训练')

# 数据集参数
parser.add_argument("--dataset-type", default="open_images", type=str,
                    help='指定数据集类型,当前支持voc和open_images。')
parser.add_argument('--datasets', '--data', nargs='+', default=["data"], help='数据集目录路径')
parser.add_argument('--balance-data', action='store_true',
                    help="通过下采样更频繁的标签来平衡训练数据。")

# 网络参数
parser.add_argument('--net', default="mb1-ssd",
                    help="网络架构,可以是mb1-ssd、mb1-lite-ssd、mb2-ssd-lite或vgg16-ssd之一。")
parser.add_argument('--freeze-base-net', action='store_true',
                    help="冻结基础网络层。")
parser.add_argument('--freeze-net', action='store_true',
                    help="冻结除了预测头之外的所有层。")
parser.add_argument('--mb2-width-mult', default=1.0, type=float,
                    help='MobilenetV2的宽度倍增器')

# 预加载基础网络或检查点的参数
parser.add_argument('--base-net', help='预训练的基础模型')
parser.add_argument('--pretrained-ssd', default='models/mobilenet-v1-ssd-mp-0_675.pth', type=str, help='预训练的基础模型')
parser.add_argument('--resume', default=None, type=str,
                    help='用于恢复训练的检查点state_dict文件')

# SGD优化器的参数
parser.add_argument('--lr', '--learning-rate', default=0.01, type=float,
                    help='初始学习率')
parser.add_argument('--momentum', default=0.9, type=float,
                    help='优化器的动量值')
parser.add_argument('--weight-decay', default=5e-4, type=float,
                    help='SGD的权重衰减')
parser.add_argument('--gamma', default=0.1, type=float,
                    help='SGD的Gamma更新')
parser.add_argument('--base-net-lr', default=0.001, type=float,
                    help='基础网络的初始学习率,或者使用--lr')
parser.add_argument('--extra-layers-lr', default=None, type=float,
                    help='不在基础网络和预测头中的层的初始学习率')

# 学习率调度器
parser.add_argument('--scheduler', default="cosine", type=str,
                    help="SGD的调度器。可以是multi-step或cosine之一")

# Multi-step调度器的参数
parser.add_argument('--milestones', default="80,100", type=str,
                    help="MultiStepLR的里程碑")

# Cosine Annealing调度器的参数
parser.add_argument('--t-max', default=100, type=float,
                    help='Cosine Annealing调度器的T_max值')

# 训练参数
parser.add_argument('--batch-size', default=4, type=int,
                    help='训练批次大小')
parser.add_argument('--num-epochs', '--epochs', default=30, type=int,
                    help='训练周期数')
parser.add_argument('--num-workers', '--workers', default=2, type=int,
                    help='数据加载中使用的工作线程数')
parser.add_argument('--validation-epochs', default=1, type=int,
                    help='运行验证的周期数间隔')
parser.add_argument('--debug-steps', default=10, type=int,
                    help='设置调试日志输出频率。')
parser.add_argument('--use-cuda', default=True, type=str2bool,
                    help='使用CUDA进行模型训练')
parser.add_argument('--checkpoint-folder', '--model-dir', default='models/',
                    help='保存检查点模型的目录')

# 配置日志
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
                    format='%(asctime)s - %(message)s', datefmt="%Y-%m-%d %H:%M:%S")

# 解析命令行参数
args = parser.parse_args()
DEVICE = torch.device("cuda:0" if torch.cuda.is_available() and args.use_cuda else "cpu")

# 使用CUDA(如果可用)
if args.use_cuda and torch.cuda.is_available():
    torch.backends.cudnn.benchmark = True
    logging.info("使用CUDA...")

# 训练函数
def train(loader, net, criterion, optimizer, device, debug_steps=100, epoch=-1):
    net.train(True)
    running_loss = 0.0
    running_regression_loss = 0.0
    running_classification_loss = 0.0
    for i, data in enumerate(loader):
        images, boxes, labels = data
        images = images.to(device)
        boxes = boxes.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()
        confidence, locations = net(images)
        regression_loss, classification_loss = criterion(confidence, locations, labels, boxes)  # TODO 更改BOXES
        loss = regression_loss + classification_loss
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        running_regression_loss += regression_loss.item()
        running_classification_loss += classification_loss.item()
        if i and i % debug_steps == 0:
            avg_loss = running_loss / debug_steps
            avg_reg_loss = running_regression_loss / debug_steps
            avg_clf_loss = running_classification_loss / debug_steps
            logging.info(
                f"Epoch: {epoch}, Step: {i}/{len(loader)}, " +
                f"Avg Loss: {avg_loss:.4f}, " +
                f"Avg Regression Loss {avg_reg_loss:.4f}, " +
                f"Avg Classification Loss: {avg_clf_loss:.4f}"
            )
            running_loss = 0.0
            running_regression_loss = 0.0
            running_classification_loss = 0.0

# 测试函数
def test(loader, net, criterion, device):
    net.eval()
    running_loss = 0.0
    running_regression_loss = 0.0
    running_classification_loss = 0.0
    num = 0
    for _, data in enumerate(loader):
        images, boxes, labels = data
        images = images.to(device)
        boxes = boxes.to(device)
        labels = labels.to(device)
        num += 1

        with torch.no_grad():
            confidence, locations = net(images)
            regression_loss, classification_loss = criterion(confidence, locations, labels, boxes)
            loss = regression_loss + classification_loss

        running_loss += loss.item()
        running_regression_loss += regression_loss.item()
        running_classification_loss += classification_loss.item()
    return running_loss / num, running_regression_loss / num, running_classification_loss / num

未完待续

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值