Advertisement

人脸识别技术在人脸检测领域的应用

阅读量:

作者:禅与计算机程序设计艺术

1.简介

人脸识别(Face Recognition)技术可提取多个人脸信息。该技术通过机器视觉(Computer Vision)的方式,在图像或视频流中自动识别并定位不同对象的面部特征,并可获取多个人脸数据。基于此方法开发的应用程序已能在多个安全领域及智能安防系统中得到广泛应用。随着深度学习算法的快速发展与普及,在人脸识别方面取得显著进展的同时也催生了大量新型产品与服务。本文将重点探讨计算机视觉(Computer Vision)中的单目目标检测技术——SSD (Single Shot MultiBox Detector),并深入分析其实现原理及相关优化策略。

2.基本概念术语说明

2.1 什么是单体框检测器?

基于深度学习的人脸检测系统(face detection system)是一种用于识别并定位面部特征的计算机视觉模型。该系统主要通过分析图像中的关键特征来确定目标位置,并结合筛选机制确保检测准确性。具体而言,在这个系统中利用神经网络架构(如CNN)对输入图像进行处理后能够自动识别出目标脸区域,并生成相应的边界框以框定目标位置(如图所示)。

facedetector

按照分类标准, 人脸检测器主要可分为单体框型、基于区域型以及深度学习型三种主要类型. 其中, 单体框检测器即本文重点介绍的SSD检测器, 其基本架构为一个单一网络架构, 在实际应用中通过预先设定的尺寸先验来快速定位目标.

2.2 SSD检测器概览

SSD检测器主要由基础网络和多级探测器构成。其中的基础网络通常采用卷积神经网络(CNN),具体包括VGG、ResNet和Inception等模型架构。这些模型能够有效地提取图像的空间特征信息。在每一次预测过程中,在每次迭代中将输入图像缩放到多个不同的尺寸比例,并分别在各个尺寸层面上执行特征提取操作以生成相应尺寸范围内的候选框集合。同时,在各个尺寸层面上进行特征提取后所得的结果会被整合成最终的整体检测框集合。通过这种方式不仅能够覆盖不同大小的人脸区域实现全面的人脸定位功能,并且能够有效避免传统固定模板匹配方法所存在的不足之处。

2.2.1 基础网络

基础网络通常采用深度残差网络架构(如ResNet及其类似设计),其核心特征在于通过多级特征提取模块实现高效的表示能力。具体而言,该架构由一系列卷积操作构成,在每一步操作中均会经历前馈神经元的激活与调整。其中包含多种关键组件:卷积操作用于特征提取与空间降维;下采样操作通过最大池化实现信息压缩;参数标准化则有助于加速训练并提升模型稳定性;激活函数则引入非线性变换以增强模型的表达能力。

2.2.2 多尺度探测器

针对不同尺寸的输入图像进行处理的是多-scale探测器。依据各个比例下的候选框,在每个比例下分别应用卷积神经网络进行位置信息及尺寸参数的解码。位置信息解码器负责解码各候选框的位置信息及尺寸参数;而分类分支层则判断各候选取存在是否为人脸。多-scale检测模块基于多种输入分辨率设计了多个特征提取网络模块,在各个特征图中提取关键的人脸特征并进行分类识别。这种设计使得SSD架构在面对不同分辨率图像时表现出更强的适应性和稳定性

2.2.3 检测锚点

检测锚点(anchor points)是预先设定于不同尺度下的候选框中心位置,以指导候选框的生成过程。如图所示,在多个尺度上生成候选框时使用的检测锚点能够显著提升网络的整体鲁棒性,并有效降低对无关背景区域的误判。

anchors

2.3 单体框检测器的优缺点

单体框检测器具有以下优点:

运行迅速:单体框检测器仅依赖一个网络结构,在运行效率上表现优异,并且具有较高的实时处理能力;
具备良好的自适应能力:相较于其他同类检测方法,在应对不同形态特征时表现出色;
模型设计简洁:单体框检测器仅由少数几个稳定且可靠的模块构成,在功能实现上既全面又不复杂。此外,在轻量级设计下实现部署过程更加便捷。

然而,单体框检测器也存在一些缺点:

  1. 模糊现象:单体框 detector 依赖于单一网络架构,在局部环境变化时可能导致识别精度下降或出现模糊现象及误检情况;
  2. 高误检率:基于单一对象识别的 single-box detector 仅能捕捉单一类型的人脸特征,在复杂场景下容易出现高误检率的问题;
  3. 缺乏抗变形能力:该类 single-box detector 在面对面部表情变化或尺寸缩放时无法准确识别。

3.核心算法原理及具体操作步骤

3.1 数据集准备

首先获取该WIDER FACE数据集,在其内部包含了有293,203张训练图片的数量中的一部分共有32,203张图片被用作测试样本

WIDER FACE dataset可通过官方网站获取:http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/

下载后的文件结构如下:

复制代码
    wider_face/
    ├── wider_face_split/
    │   ├── wider_face_train_bbx_gt.txt
    │   └──...
    ├── images/
    │   ├── 0--Parade/
    │   │   ├── 0_Parade_marchingband_1_849.jpg
    │   │   ├──...
    │   ├── 10--Family_Group/
    │   │   ├── 10_Family_Group_Concert_848.jpg
    │   │   ├──...
    
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

该数据集则按人物姓名进行了分类划分,并即此而言,训练集与测试集都属于同一个目录结构。针对训练集的要求而言,则需要制作准备三个文件:

  1. train.csv 数据表存储了训练数据中各类别及其样本数量信息。
  2. label.json 数据文件中包含了各类别对应的标签信息。
  3. 该图像目录存储了训练数据集中所有原始图片的完整文件。

3.2 数据增强

常用的数据增强方式有两种:

  • 进行图像处理(Data augmentation):涉及平移操作、图像翻转、裁剪处理以及旋转调整等多种方式。
    • 采用合成数据集(Synthetic data):这是一種更为先进的方法,能够有效生成多样化的训练样本。这种策略不仅丰富了数据多样性还能避免过拟合的风险,并且可能導致数据分布发生变化以及引入潜在的偏差因素。

该库提供了一种高效的方法来实现数据增强。

3.3 生成训练数据

训练数据可被提取出来,并构建四维数组用于存储训练样本的相关信息。其中包含了图片路径、标注框位置坐标以及对应的标签信息。

复制代码
    import os
    from PIL import Image
    import numpy as np
    import imgaug as ia
    import imgaug.augmenters as iaa
    
    
    def load_images(root):
    """
    Load all the image paths and bounding boxes for a directory
    
    Args:
        root: The path to the directory containing the images
    
    Returns: A tuple of two lists, one with the image filepaths
             and another with their corresponding bounding boxes
    """
    files = []
    bboxes = []
    # Load each image in turn from the directory
    for filename in sorted(os.listdir(root)):
        if not filename.lower().endswith('.jpg'):
            continue
    
        filepath = os.path.join(root, filename)
        im = Image.open(filepath).convert('RGB')
    
        width, height = im.size
    
        # Generate some example bounding boxes for this image
        boxes = [ia.BoundingBox(x1=np.random.uniform() * width,
                                 y1=np.random.uniform() * height,
                                 x2=(np.random.uniform() +.5) * width,
                                 y2=(np.random.uniform() +.5) * height)
                 for _ in range(2)]
        # Augment the image using bounding box transformations
        seq = iaa.Sequential([iaa.Affine(rotate=(-10, 10)),
                              iaa.Resize((width // 2, height // 2))])
        image_aug, bbs_aug = seq(image=im, bounding_boxes=boxes)
    
        # Convert the resulting image and bounding boxes back to arrays
        arr = np.array(image_aug)
        bbox_arr = np.array([[bb.x1 / width, bb.y1 / height,
                              bb.x2 / width, bb.y2 / height]
                             for bb in bbs_aug], dtype='float32')
    
        files.append(filename)
        bboxes.append(bbox_arr)
    
    return files, bboxes
    
    
    if __name__ == '__main__':
    # Generate training data
    train_files, train_bboxes = [], []
    for dirname in ['0--Parade', '10--Family_Group']:
        fnames, bboxes = load_images(os.path.join('images', dirname))
        train_files += fnames
        train_bboxes += bboxes
    
    # Save the generated data so we can use it later
    np.savez('data/train.npz', files=train_files, bboxes=train_bboxes)
    
    print("Done!")
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

上面的例子说明了如何应用库imgaug来进行图片的数据增强,并生成相应的训练数据。在执行脚本之前,请确保已安装所需依赖库:

复制代码
    pip install -U imgaug
    
    
    AI写代码

这样就可以运行脚本,生成训练数据的四维数组。

3.4 创建SSD模型

在开始构建SSD模型之前,我们需要设置超参数。

复制代码
    class Config:
    # Model hyperparameters
    model_input_shape = (300, 300, 3)           # Input shape of the model
    num_classes = len(train_dataset.labels)     # Number of classes in the dataset
    priors = generate_priors(model_input_shape, config.min_sizes, config.max_sizes, 
                             config.aspect_ratios, config.num_layers)    # Prior boxes
    
    # Training parameters
    batch_size = 32                             # Batch size during training
    initial_lr = 1e-3                           # Initial learning rate
    decay_steps = int(epoch_size / batch_size)   # Learning rate decay steps
    lr_decay = 0.1                              # Learning rate decay rate
    
    config = Config()
    
    def create_ssd_model():
    """Create an SSD model"""
    inputs = keras.Input(shape=config.model_input_shape)
    feature_extractor = create_feature_extractor(inputs)
    cls_outputs, loc_outputs = build_predictor(inputs, num_classes=config.num_classes, 
                                               layer_shapes=feature_extractor.output_shape[1:], 
                                               num_default_boxes=len(config.priors),
                                               aspect_ratios=config.aspect_ratios,
                                               variances=[0.1, 0.1, 0.2, 0.2])
    predictions = Concatenate(axis=-2)([cls_outputs, loc_outputs])
    model = Model(inputs=inputs, outputs=predictions)
    compile_model(model)
    return model
    
    
    def create_feature_extractor(inputs):
    """Create the base convolutional neural network used to extract features"""
    convnet = keras.applications.MobileNetV2(include_top=False, input_tensor=inputs, alpha=0.5)
    convnet.trainable = False
    layers = [convnet.get_layer(name).output for name in ["Conv_1", "Conv_2", "Conv_3", "Conv_4"]]
    model = keras.Model(inputs=convnet.inputs, outputs=layers)
    return model
    
    
    def build_predictor(inputs, num_classes, layer_shapes, num_default_boxes, aspect_ratios, variances):
    """Build the SSD prediction head on top of the extracted features"""
    mbox_conf = Conv2D(num_classes * num_default_boxes, kernel_size=(3, 3), padding="same")(inputs)
    mbox_loc = Conv2D(num_default_boxes * 4, kernel_size=(3, 3), padding="same")(inputs)
    mbox_loc = Reshape((-1, 4))(mbox_loc)
    mbox_conf = Reshape((-1, num_classes))(mbox_conf)
    ssd_outputs = concatenate([mbox_loc, mbox_conf], axis=-1)
    outputs = detection_decoder(ssd_outputs, num_classes=num_classes,
                                prior_boxes=config.priors,
                                variances=variances,
                                nms_threshold=0.5, score_threshold=0.01)
    return outputs
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

此脚本构建了一个SSD模型作为主要组件之一,并包含一个特征提取器和一个预测器作为其两个主要组成部分。

特征提取器是基于基础网络(MobileNetV2)的特征抽取模块。

该预测器由两个卷积层构成,在其架构中包括两类功能模块:一类是负责输出各类别的置信度(即类别概率),另一类则是用于生成回归目标坐标的偏移量(即定位信息)。在模型设计阶段,在完成上述两步运算后会将这些结果进一步整合处理以满足后续需求。

预测头的输出由一个列表构成,该列表包含两个Tensor变量。第一个Tensor变量用于表示回归坐标的预测值,第二个Tensor变量则用于表示类别预测值。每个Tensor变量的维度均为(batch\_size, total\_num\_default\_boxes, ...)。其中total\_num\_default\_boxes等于(priors的数量 \times 2) + (priors的数量 \times (aspect ratios的数量 - 1) \times 2)

SSD模型利用引入一个检测解码器(detection decoder)层,在基于预测分支的输出中还原出人脸位置坐标及其分类得分,并采用NMS(非极大值抑制)方法去除重复检测结果,最终输出定位框。

复制代码
    def compile_model(model):
    """Compile the SSD model"""
    optimizer = Adam(lr=config.initial_lr)
    loss = losses.MultiboxLoss(neg_pos_ratio=3, negatives_for_hard=100, alpha=1.0)
    model.compile(optimizer=optimizer, loss=loss.compute, metrics=[loss.negative_log_likehood,
                                                                       loss.localization_loss,
                                                                       loss.classification_loss])
    
      
      
      
      
      
      
    
    AI写代码

上面的脚本设置了超参数、编译模型,并指定损失函数。

3.5 训练SSD模型

在训练SSD模型的过程中可借鉴之前的做法,即首先设置损失函数,并分别对训练和验证阶段进行定义,在完成一次完整的训练循环后即可完成模型的参数更新。

复制代码
    # Initialize the weights of the model
    create_ssd_model().load_weights('checkpoints/init.h5', by_name=True)
    
    # Train the model
    history = model.fit(train_dataset,
                    epochs=100,
                    validation_data=val_dataset,
                    callbacks=[ModelCheckpoint(filepath='checkpoints/{epoch}.h5', verbose=1, save_best_only=True)])
    
    # Evaluate the final model on test set
    test_loss, test_nms, test_cla = evaluate(model, val_dataset, mode='test')
    print(f"Test Loss: {test_loss:.4f}, Test NMS: {test_nms:.4f}, Test Cla: {test_cla:.4f}")
    
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

该脚本初始化了初始权重之后,并通过调用fit()函数来进行后续的模型训练初始化工作。在模型进行训练的过程中,在每轮迭代结束后会自动保存当前最优的模型参数配置信息。当完成所有轮次的训练任务后,则会调用evaluate()函数对测试集进行评估分析,并最终计算得到了最终损失函数值。

3.6 扩展训练数据

SSD模型依赖于获取大量的训练数据以获得良好的效果;然而,在实际应用中,现有的训练样本数量不足。为此可以通过以下几种方法扩充训练数据:

  1. 在不同光照条件下采集图像:考虑到不同的环境照明条件会对图像质量产生影响,在不同光照条件下采集同一对象的图像能够有效补充训练样本。
  2. 从具有更少标记对象的图像中采样:当训练集中仅有少量样本带有标记信息而其余样本无标时,可以通过从包含更多标记对象的图像中随机选取一些图片来扩展训练集。
  3. 通过训练增强技术增强训练数据:利用强化学习技术使训练数据更加健壮并具有更强的多样性。我们可以通过调整色彩、对比度、裁剪、缩放、旋转等多种方式对手册样本进行增强以提升模型性能。

这些策略都可以通过修改模型结构、调整训练参数来实现。

4.具体代码实例

在本节中

4.1 安装依赖库

在开始阶段,我们必须安装各种工具包以支持该系统运行:如numpy、tensorflow、keras、imgaug和matplotlib。

复制代码
    pip install tensorflow==2.3.0 keras==2.4.3 imgaug matplotlib
    
    
    AI写代码

4.2 设置路径

然后,我们需要设置数据集存放路径。

复制代码
    DATA_DIR = '../WIDERFACE/'  # Set your own path here
    IMAGE_DIR = DATA_DIR + 'WIDER_val'
    ANNOTATION_FILE = DATA_DIR + 'wider_face_val.mat'
    OUTPUT_DIR = './output/'  # Output directory where results will be saved
    
      
      
      
    
    AI写代码

4.3 解析标注文件

随后我们希望解析标注文件wider_face_val.mat并收集训练图片及其对应的图像数据及定位框坐标

复制代码
    import scipy.io as sio
    
    annotation = sio.loadmat(ANNOTATION_FILE)['event_list'][0][0]['file_list'][0][0]
    
    annotations = {}
    file_names = []
    bboxes = []
    
    for item in annotation:
    file_names.append(item['filename'][0][0][0])
    bboxes.append([])
    annotations[item['filename'][0][0][0]] = {'size': {'width': item['width'][0][0],
                                                        'height': item['height'][0][0]},
                                                'objects': [{'bbox': {'xmin': obj['bbox']['xmin'],
                                                                      'ymin': obj['bbox']['ymin'],
                                                                      'xmax': obj['bbox']['xmax'],
                                                                      'ymax': obj['bbox']['ymax']}}
                                                            for obj in item['object']]
                                            }
    
    for obj in item['object']:
        xmin = max(obj['bbox']['xmin'], 0)
        ymin = max(obj['bbox']['ymin'], 0)
        xmax = min(obj['bbox']['xmax'], annotations[item['filename'][0][0][0]]['size']['width'])
        ymax = min(obj['bbox']['ymax'], annotations[item['filename'][0][0][0]]['size']['height'])
    
        if ((xmax - xmin) > 0) and ((ymax - ymin) > 0):
            bboxes[-1].append([xmin, ymin, xmax, ymax])
    
    del annotation
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

该代码对标注文件的结构进行了分析,并记录了每张图片的尺寸以及相应的注释框信息。需要注意的是,在这一过程中,默认会排除注释框超出图像范围的部分。具体实现时,则采用了max()和min()函数来限定注释框的有效区域。

4.4 数据增强

接着,我们要实现数据增强,这里使用的库是imgaug。具体的实现如下:

复制代码
    import imgaug as ia
    import imgaug.augmenters as iaa
    
    seq = iaa.Sequential([
    iaa.Sometimes(0.5, iaa.Fliplr()),          # Flip half of the images horizontally
    iaa.Flipud(),                            # Flip images vertically
    iaa.OneOf([                               # Apply affine transformations randomly
        iaa.Affine(scale={"x": (0.9, 1.1), "y": (0.9, 1.1)}),
        iaa.Affine(shear={"x": (-10, 10), "y": (-10, 10)}),
        iaa.Affine(translate_percent={"x": (-0.1, 0.1), "y": (-0.1, 0.1)})
    ])
    ])
    
    image_aug, bbs_aug = seq(image=image, bounding_boxes=bbs)
    
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

这段代码创建一个序列,并涵盖五种不同的变换方案。在此处的具体场景下,在线随机抽取一种变换方案并完整流程中,并对输入图像及其对应的标注框执行该操作

4.5 定义模型

然后,我们定义模型,包括一个特征提取器和一个预测器。

复制代码
    import tensorflow as tf
    from tensorflow import keras
    from tensorflow.keras import layers
    from models import create_ssd_model
    
    model = create_ssd_model(num_classes=len(annotations))
    
      
      
      
      
      
    
    AI写代码

这里,我们调用函数create_ssd_model(),该函数创建了一个SSD模型。

4.6 定义损失函数

接着,我们定义损失函数。

复制代码
    from models import CustomLossLayer
    from utils import compute_iou, center_form_to_corner_form, corner_form_to_center_form
    
    custom_loss = CustomLossLayer(neg_pos_ratio=3, pos_alpha=1.0, neg_alpha=0.5,
                              center_variance=0.1, size_variance=0.2)
    
    true_confidence = tf.expand_dims(tf.reduce_sum(true[..., :4], axis=-1), axis=-1)
    pred_confidence = pred[..., 4:]
    mask = true_confidence >= custom_loss._neg_pos_ratio
    true_confidence = tf.boolean_mask(true_confidence, mask)
    pred_confidence = tf.boolean_mask(pred_confidence, mask)
    
    true_locs = center_form_to_corner_form(true[..., :4])[..., :2]
    pred_locs = center_form_to_corner_form(pred[..., :4])[..., :2]
    
    true_areas = tf.squeeze(tf.sqrt(tf.abs(true[..., :2] - true[..., 2:]) ** 2), axis=-1)
    pred_areas = tf.squeeze(tf.sqrt(tf.abs(pred[..., :2] - pred[..., 2:]) ** 2), axis=-1)
    
    true_scores = true[..., 4]
    pred_scores = pred[..., 4]
    
    ious = compute_iou(true_bboxes, pred_bboxes)
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

在本研究中,我们开发并实现了 CustomLossLayer 损失函数。该损失函数主要承担计算回归坐标误差、类别区分误差以及整体损失的任务。其中参数 neg_pos_ratio 表示背景类占正样本的比例,在模型训练过程中起到平衡两类样本的作用;而 pos_alpha 和 neg_alpha 则分别代表正样本与负样本之间的权重系数,在优化目标函数时起到区分两类重要性的作用;最后 center_variance 和 size_variance 则用于调节回归坐标结果的方差程度。

复制代码
    class CustomLossLayer(tf.keras.layers.Layer):
    def __init__(self, neg_pos_ratio, pos_alpha, neg_alpha, center_variance, size_variance):
        super(CustomLossLayer, self).__init__()
        self._neg_pos_ratio = neg_pos_ratio
        self._pos_alpha = pos_alpha
        self._neg_alpha = neg_alpha
        self._center_variance = center_variance
        self._size_variance = size_variance
    
    def call(self, y_true, y_pred):
        num_batches = tf.cast(tf.shape(y_pred)[0], tf.int64)
        grid_size = tf.cast(tf.shape(y_pred)[1:-1], tf.float32)
        y_true = tf.reshape(y_true, [-1, tf.shape(y_true)[-1]])
        y_pred = tf.reshape(y_pred, [-1, tf.shape(y_pred)[-1]])
    
        # 取出正样本和负样本
        positive_indices = tf.where(tf.greater_equal(y_true[:, 4], 1))
        negative_indices = tf.where(tf.less(y_true[:, 4], 1))
        ignore_indices = tf.where(tf.logical_and(tf.less(y_true[:, 4], 0),
                                                  tf.not_equal(y_true[:, 4], -1)))
    
        # 计算正样本的loss
        pos_count = tf.minimum(tf.shape(positive_indices)[0],
                               self._neg_pos_ratio * tf.shape(negative_indices)[0])
        neg_count = tf.minimum(tf.shape(negative_indices)[0],
                               self._neg_pos_ratio * tf.shape(positive_indices)[0])
    
        confidence_losses = tf.nn.sigmoid_cross_entropy_with_logits(labels=y_true[:, 4], logits=y_pred[:, 4])
        confidence_loss = tf.reduce_sum(confidence_losses) / tf.cast(num_batches * grid_size[0] * grid_size[1], tf.float32)
    
        y_pred = tf.gather_nd(y_pred, indices=positive_indices)
        labels = tf.ones_like(y_pred[:, :1])
        regression_losses = tf.concat([
            smooth_l1_loss(y_pred[:, :2], y_true[:, :2]),
            tf.square(tf.exp(y_pred[:, 2:4])) / tf.square(tf.exp(y_true[:, 2:4])),
            tf.zeros_like(y_pred[:, 4:5])
        ], axis=-1)
        regression_loss = tf.reduce_sum(regression_losses) / tf.cast(tf.shape(positive_indices)[0], tf.float32)
    
        # 计算负样本的loss
        if tf.math.greater(tf.size(ignore_indices), 0):
            y_pred = tf.concat([
                y_pred[:self._neg_pos_ratio*tf.shape(positive_indices)[0]],
                y_pred[(self._neg_pos_ratio+1)*tf.shape(positive_indices)[0]:]
            ], axis=0)
    
            count = tf.shape(negative_indices)[0]
            false_positives = tf.zeros((count,), dtype=tf.float32)
            false_negatives = tf.ones((count,), dtype=tf.float32)
    
            _, conf_idx = tf.nn.top_k(y_pred[:, 4], k=count, sorted=True)
            y_pred = tf.gather(y_pred, indices=conf_idx)
    
            labels = tf.concat([labels[:self._neg_pos_ratio*tf.shape(positive_indices)[0]],
                                labels[(self._neg_pos_ratio+1)*tf.shape(positive_indices)[0]:]], axis=0)
            labels = tf.stop_gradient(labels)
    
            regression_losses = tf.concat([regression_losses[:self._neg_pos_ratio*tf.shape(positive_indices)[0]],
                                           regression_losses[(self._neg_pos_ratio+1)*tf.shape(positive_indices)[0]:]], axis=0)
            regression_losses = tf.stop_gradient(regression_losses)
    
            confidence_losses = tf.nn.sigmoid_cross_entropy_with_logits(labels=false_positives, logits=y_pred[:, 4]) \
                                + tf.nn.sigmoid_cross_entropy_with_logits(labels=false_negatives, logits=y_pred[:, 4]*0.0)
    
            confidence_loss = tf.reduce_sum(confidence_losses) / tf.cast(grid_size[0] * grid_size[1], tf.float32)
            regression_loss = tf.reduce_sum(regression_losses) / tf.cast(tf.shape(negative_indices)[0], tf.float32)
    
        # 计算总体loss
        loss = (self._pos_alpha * regression_loss + self._neg_alpha * confidence_loss) / tf.maximum(1., float(pos_count + neg_count))
        return loss
    
    def smooth_l1_loss(y_pred, y_true):
    absolute_errors = tf.abs(y_pred - y_true)
    square_errors = 0.5 * (y_pred - y_true)**2
    l1_loss = tf.where(tf.less(absolute_errors, 1.), square_errors, absolute_errors - 0.5)
    return l1_loss
    
    def compute_iou(true_boxes, pred_boxes):
    """Compute the Intersection over Union between predicted and ground truth boxes."""
    intersections = tf.maximum(0., tf.minimum(true_boxes[:, 2:], pred_boxes[:, 2:]) - tf.maximum(true_boxes[:, :2], pred_boxes[:, :2]))
    intersection_area = intersections[:, 0] * intersections[:, 1]
    union_area = tf.maximum(true_boxes[:, 2] - true_boxes[:, 0] + 1.,
                            pred_boxes[:, 2] - pred_boxes[:, 0] + 1.) * tf.maximum(true_boxes[:, 3] - true_boxes[:, 1] + 1.,
                                                                                  pred_boxes[:, 3] - pred_boxes[:, 1] + 1.)
    return tf.clip_by_value(intersection_area / union_area, clip_value_min=0., clip_value_max=1.)
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

这里我们制定了smooth_l1_loss()这一函数 用于计算L1损失 这个函数将返回一个张量变量 该变量包含每个预测值与真实值之间的L1距离 并且当这个距离值低于或等于一的时候 将采用平方误差作为其对应的损失评估标准

4.7 训练模型

最后,我们训练模型。

复制代码
    import os
    import shutil
    import cv2
    
    epochs = 100
    checkpoint_dir = OUTPUT_DIR + '/models/checkpoints/'
    log_dir = OUTPUT_DIR + '/logs/'
    
    if os.path.exists(log_dir):
    shutil.rmtree(log_dir)
    if os.path.exists(checkpoint_dir):
    shutil.rmtree(checkpoint_dir)
    
    os.makedirs(log_dir)
    os.makedirs(checkpoint_dir)
    
    callbacks = [
    keras.callbacks.EarlyStopping(patience=10, verbose=1),
    keras.callbacks.ReduceLROnPlateau(factor=0.1, patience=5, min_lr=0.00001, verbose=1),
    keras.callbacks.ModelCheckpoint(checkpoint_dir + '{epoch:03d}.h5'),
    keras.callbacks.TensorBoard(log_dir=log_dir, write_graph=True, update_freq='epoch', profile_batch=0)
    ]
    
    model.fit(X_train, Y_train,
          batch_size=BATCH_SIZE,
          epochs=epochs,
          validation_data=(X_test, Y_test),
          shuffle=True,
          callbacks=callbacks)
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI写代码

在这里,我们设置了训练周期的数量,并在每个周期结束后生成了模型的检查点文件。在训练过程中,通过EarlyStopping和ReduceLROnPlateau回调函数来完成停止训练以及优化学习速率。此外,在整个实验过程中,我们采用了Tensorboard用于记录与模型相关的指标数据。

全部评论 (0)

还没有任何评论哟~