【CCF BDCI 2023】多模态多方对话场景下的发言人识别 Baseline 0.71 CNN 部分

【CCF BDCI 2023】多模态多方对话场景下的发言人识别 Baseline 0.71 CNN 部分

  • 概述
  • CNN 简介
  • 数据预处理
    • 查看数据格式
    • 加载数据集
  • 图像处理
    • 限定图像范围
    • 图像转换
    • 加载数据
  • CNN 模型
    • Inception 网络
    • ResNet 残差网络
    • 总结
    • 参数设置
  • 训练 Train
    • 模型初始化
    • 数据加载
    • 训练超参数
    • 训练循环
  • 验证 Valid
    • 验证过程
    • 模型评估
    • 记录 & 保存
  • 测试 Test
    • 加载模型
    • 加载测试数据
    • 预测结果
  • 参考文献

概述

现今技术日新月异, Artificial Intelligence 的发展正在迅速的改变我们的生活和工作方式. 尤其是在自然语言处理 (Natural Linguistic Processing) 和计算机视觉 (Computer Vision) 等领域.

传统的多模态对话研究主要集中在单一用户与系统之间的交互, 而忽视了多用户场景的复杂性. 视觉信息 (Visual Info) 往往会被边缘化, 仅作为维嘉信息而非对话的核心部分. 在实际应用中, 算法需要 “观察” 并与多个用户的交互, 这些用户有可能不是当前的发言人.

【CCF BDCI 2023】多模态多方对话场景下的发言人识别, 核心思想是通过多轮连续对话的内容和每轮对应的帧, 以及对应的人脸 bbox 和 name label, 从每轮对话中识别出发言人 (speaker).

CNN 简介

CNN (Convolutional Neural Network) 能够自动, 有效的从图像数据中学习空间层次结构. CNN 通过一系列的卷积层 (Convolutional Layer)
, 池化层 (Pool Layer) 和全连接层 (Fully Connected Layer) 来处理图像, 每一层专注于提取图像的不同几倍的特征. 卷积层负责提取局部特征, 如: 边缘和纹理; 池化层负责降低特征空间维度, 增强模型泛化能力; 全连接层则用于最终的分类或回归任务.

Baseline 的代码分为三个文件, 分别为convex_optimization.py, dialog_roberta-constrasive.py, finetune_cnn-multiturn.py. 下面小白我就来带大家详解一下各个部分.

数据预处理

再多模态发言人识别中, 数据预处理的目的是将原视频 (帧图片) 转换为我们可以理解和处理的格式. 包括下面三个部分: 加载数据集, 图像转换和数据增强.

查看数据格式

小白我先带大家来看一下数据的格式, 我们先打开train-metada.jason, 下面是文件的样子:

CCF BDCI 多模态发言人识别 数据格式
查看文件长度:

# 读取 jason
train_metadata = json.load(open('../final_dataset-5_turns_chusai/train-metadata.json'))
print(len(train_metadata))
print(train_metadata[0])

输出结果:

13584
[{"frame": "s06e07-000377", "speaker": "phoebe", "content": "Yeah, I know because you have all the good words. What do I get? I get \"it\u2019s,\" \"and\" oh I'm sorry, I have \"A.\" Forget it.", "start": 297, "end": 491, "faces": [[[752, 135, 881, 336], "rachel"], [[395, 111, 510, 329], "leslie"]]}, {"frame": "s06e07-000504", "speaker": "rachel", "content": "Phoebe, come on that's silly.", "start": 498, "end": 535, "faces": [[[466, 129, 615, 328], "phoebe"]]}, {"frame": "s06e07-000552", "speaker": "phoebe", "content": "All right, so let's switch.", "start": 535, "end": 569, "faces": [[[426, 120, 577, 320], "phoebe"]]}, {"frame": "s06e07-000629", "speaker": "rachel", "content": "No, I have all of the good words. OK, fine, fine, we can switch.", "start": 569, "end": 689, "faces": [[[420, 125, 559, 328], "phoebe"], [[652, 274, 771, 483], "rachel"]]}, {"frame": "s06e07-000892", "speaker": "phoebe", "content": "Please...wait, how did you do that?", "start": 879, "end": 906, "faces": [[[424, 133, 573, 334], "phoebe"], [[816, 197, 925, 399], "bonnie"]]}]

结合图片我们可以看出 train 数据由 13584 条数据组成, 每条数据包含 5 段发言, 每段发言包含:

  • frame: 图片对应的帧, 例如: s06e07-000377
  • speaker: 发言人, 共计 11 人, 例如: phoebe
  • content: 发言内容
  • start: 开始帧
  • end: 结束帧
  • faces: 包含多个人脸的列表, 例如: [[[752, 135, 881, 336], "rachel"], [[395, 111, 510, 329], "leslie"]]. 其中每个人脸包含 2 部分数据:
    • 坐标数据: [x1, y1, x2, y2], 分别表示人脸左上到右下的框, 例如: [752, 135, 881, 336]
    • 人脸归属: 坐标中人脸对应的任务, 例如: rachel

CCF BDCI 发言人识别 人脸标注示例

加载数据集

下面我们来讲一下代码中的 FaceDataset 类, 这个类的主要功能是从指定的数据集中加载图像和标签, 并对图像进行必要的预处理. FaceDataset 根据不同的数据集分割加载相应的元数据, 并从中提取出每个帧的面部数据及其对应的标签. 这些信息被用于后续的模型训练和评估.

class FaceDataset:
    def __init__(self, base_folder, transform='default', split='train', debug=False):
        # 设置图像文件的基本路径
        self.frames_path = os.path.join(base_folder, 'images')
        # debug模式用于快速测试(加载较少的数据)
        self.debug = debug
        # 数据集的分割类型(训练、测试或验证)
        self.split = split

        # 根据数据集的分割类型加载相应的元数据文件
        if split == 'test':
            metadata = json.load(open(os.path.join(base_folder, 'test-metadata.json')))
        elif split == 'test-hard':
            metadata = json.load(open(os.path.join(base_folder, 'test-hard-metadata.json')))
        else:
            metadata = json.load(open(os.path.join(base_folder, 'train-metadata.json')))

baseline 选择了 s01 开头的人脸作为厕所集, 其他作为训练集.

        # 初始化存储样本和标签的列表
        self.examples, self.labels = list(), list()
        # 用于记录已处理的帧,避免重复处理
        frames_met = set()
        # 遍历元数据中的每个对话数据
        for dialog_data in metadata:
            # 遍历对话中的每个帧数据
            for frame_data in dialog_data:
                # 如果是验证集,只选择以's01'开头的帧
                if split == 'valid' and not frame_data['frame'].startswith('s01'):
                    continue
                # 如果是训练集,排除以's01'开头的帧
                if split == 'train' and frame_data['frame'].startswith('s01'):
                    continue
                # 检查帧是否已经处理过
                if frame_data['frame'] in frames_met:
                    continue
                # 记录已处理的帧
                frames_met.add(frame_data['frame'])

                # 获取帧中的所有面部数据
                faces = frame_data['faces']      # [(bbox, id), (bbox, id), ...]

图像处理

限定图像范围

计算图片面积, 用于过滤无效图片.

def calculate_area(bbox):
    return (bbox[2] - bbox[0]) * (bbox[3] - bbox[1])

接着讲上面的 FaceDataset 部分, 再上面我们已经获得了每条数据每帧的人脸: Faces, 下面是如何获取 face 对应的 frame, face_label, bbox, label:

# 遍历每个面部数据
for bbox, face_label in faces:
    # 确保边界框的坐标在合理范围内
    bbox = [max(i, 0) for i in bbox]
    bbox = [min(bbox[0], 1280), min(bbox[1], 720), min(bbox[2], 1280), min(bbox[3], 720)]
    # 如果边界框面积为0,则跳过此面部
    if calculate_area(bbox) <= 0:
        continue
    # 标签为1如果面部是说话者,否则为0
    label = int(face_label == frame_data['speaker'])
    # 添加样本和标签到列表
    self.examples.append((frame_data['frame'], face_label, bbox, label))
    self.labels.append(label)

因为数据存在负数, 使用 max 和 min 限定标注为图片范围.

图像转换

我们需要将原始图像转化为我们需要的格式, 使得 CNN 模型能有效的处理数据. 这一过程包括调整图像大小, 标准化和图像增强.

transforms.RandomHorizontalFlip

代码:

if transform == 'default':
    self.train_transform = transforms.Compose([
        np.float32,
        transforms.ToTensor(),
        transforms.RandomHorizontalFlip(p=0.5),
        transforms.RandomAffine(degrees=20, shear=20),
        transforms.Resize((512, 512)),
        fixed_image_standardization
    ])
    self.valid_transform = transforms.Compose([
        np.float32,
        transforms.ToTensor(),
        transforms.Resize((512, 512)),
        fixed_image_standardization
    ])
else:
    self.train_transform = transform
    self.valid_transform = transform
  1. 调整图像大小: transforms.Resize((512, 512)), 将所有人脸调整为固定的 512*512 像素, 便于后续的 CNN 处理. FYI: 大部分 CNN 模型的输入都会拉成正方形
  2. 数据标准化: fixed_image_standardization, 具体为:
    • 缩放像素值, 通过像素值 / 255, 将像素值范围从 [0, 255] 缩放到 [0, 1]
    • 均值和标准差归一化 (Mean & Standard Deviation Normalization): 使得像素具有特定的均值和标准差. 具体的文档我没找到, 但是 ImageNet 的均值用的是 [0.485, 0.456, 0.406], 标准差是[0.229, 0.224, 0.225]
  3. 数据增强: baseline 使用的数据增强分为 2 部分: 水平翻转和随机仿射变换:
    • 随机水平翻转: transforms.RandomHorizontalFlip(p=0.5), 按照 0.5 的概率进行水平翻转图像. 通过水平翻转的增强, 模型可以学习到不同方向的特征, 增强模型的泛化性. 再人脸任任务中, 此方法特别使用, 因为左右脸是对称的 (差不多对称)
    • 随机仿射变化: transforms.RandomAffine(degrees=20, shear=20), 包括旋转, 缩放, 评议和剪切, degrees=20, 随机旋转上限 20 度, shear=20, 剪切上限 20 度, 通过仿射变化, 对图片进行各种视角和姿态的变化, 可以提高模型对人脸图片的姿势和表情的泛化能力

加载数据

__len__方法和__getitem__方法是继承自 PyTorch 数据集类 (Dataset). 下面小白我来分别解释一下两种方法.

__len__方法:

def __len__(self):
    return len(self.examples) if not self.debug else 32
  • 不在调试模式下: 返回self.example列表长度
  • 调试模式下: 返回 32 样本

_get_item方法, 也是 Dataset 类的一个标准接口:

def __getitem__(self, index):
    frame_name, face_label, bbox, label = self.examples[index]
    image = cv2.imread(os.path.join(self.frames_path, frame_name + '.jpg'))
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    image = image[bbox[1]:bbox[3], bbox[0]:bbox[2]]

    if self.split == 'valid' or self.split.startswith('test'):
        image = self.valid_transform(image)
        return image, label, frame_name, face_label
    else:
        image = self.train_transform(image)
        return image, label
  1. 提取样本信息: self.examples列表中获取指定的样本, 包含: 帧名 (frame_name), 人脸标签 (face_label), 边界框 (bbox), 标签 (label)
  2. 加载和处理图像: 使用 cv2.iamge读取 bbox 中的 人脸, 这个就不细说了
  3. 应用图像转换: 再训练集使用self.train_transform, 验证集和测试集使用slef.valid_transform
  4. 返回处理后的样本

__getitem__法的实现确保了每次请求数据集时都能获取到正确处理和转换后的图像样本.

CNN 模型

InceptionResnetV1 结合了 Inception 和 ResNet (残差网络) 的特点.

InceptionResnetV1

Inception 网络

Inception 由 GoogleNet 提出, 核心思想是在网络上同一层使用多种不同尺寸的卷积核 (Convolution Kernel), 以提取不同尺度的特征. 较小的卷积核可以捕捉到细节特征, 较大的卷积核则能捕捉大范围的特征.

ResNet 残差网络

ResNet 通过残差连接, 将前一层的输出结果添加到后面的层上, 解决深度学习中的梯度消失问题 (Vanishing Gradient Problem), 使得网络的层数得以加深.

关于梯度消失和梯度爆炸的详解, 参考我这篇博客.

总结

InceptionResnetV1 在 Inception 模块的基础上加入了残差连接. 以保持Inception 模块多尺度特征提取优势的同时, 避免了深层网络可能出现的梯度相关问题.

参数设置

argprase

# 创建一个ArgumentParser对象,用于解析命令行参数
parser = argparse.ArgumentParser()

# 添加'--func'参数,用于指定脚本的功能模式(如'test'或'train')。默认值为'test'。
parser.add_argument('--func', type=str, default='test')

# 这行代码被注释掉了,但它展示了如何添加另一个'--func'参数的例子,其默认值为'train-friends'。
# parser.add_argument('--func', type=str, default='train-friends')

# 添加'--debug'参数,这是一个标志参数。如果在命令行中出现,则其值为True,否则为False。
parser.add_argument('--debug', action='store_true')

# 添加'--load_pretrained'参数,用于控制是否加载预训练模型。它是一个整数类型,1表示加载,0表示不加载。默认值为1。
parser.add_argument('--load_pretrained', type=int, default=1)

# 添加'--data_base_folder'参数,用于指定数据集的基本路径。默认值为'../final_dataset-5_turns_chusai'。
parser.add_argument('--data_base_folder', type=str, default='../final_dataset-5_turns_chusai')

# 添加'--batch_size'参数,用于指定批处理大小。默认值为16。
parser.add_argument('--batch_size', type=int, default=16)

# 添加'--num_workers'参数,用于指定数据加载时使用的工作线程数。默认值为2。
parser.add_argument('--num_workers', type=int, default=2)

# 添加'--num_epochs'参数,用于指定训练的总周期数。默认值为40。
parser.add_argument('--num_epochs', type=int, default=40)

# 添加'--lr'(学习率)参数,用于指定优化器的学习率。默认值为0.001。
parser.add_argument('--lr', type=float, default=0.001)

# 添加'--weight_decay'参数,用于指定优化器的权重衰减。默认值为0。
parser.add_argument('--weight_decay', type=float, default=0.)

# 添加'--decay_interval'参数,用于指定学习率衰减的间隔周期。默认值为3。
parser.add_argument('--decay_interval', type=int, default=3)

# 添加'--output_path'参数,用于指定输出文件的路径。默认值为'out'。
parser.add_argument('--output_path', type=str, default='out')

# 添加'--model_ckpt'参数,用于指定模型检查点的路径。默认值为'../ckpts/5_turns/cnn-best_model.pth'。
parser.add_argument('--model_ckpt', type=str, default='../ckpts/5_turns/cnn-best_model.pth')

# 解析命令行输入的参数
args = parser.parse_args()

# 打印解析得到的参数值
print(args)

训练 Train

模型初始化

if args.func.split('-')[0] == 'train':
    if args.load_pretrained:
        resnet = InceptionResnetV1(classify=True, pretrained='../pretrain_model/torch/20180402-114759-vggface2.pt', num_classes=2).to(device)
    else:
        resnet = InceptionResnetV1(classify=True, num_classes=2).to(device)
    softmax = torch.nn.Softmax()
  1. 判断是否为 train
  2. 判断是否加载预训练模型 vggfacenet2
  3. 初始化 softmax 激活函数, 将最终概率限制到 [0, 1]

数据加载

train_dataset = FaceDataset(args.data_base_folder, split='train', debug=args.debug)
valid_dataset = FaceDataset(args.data_base_folder, split='valid', debug=args.debug)
train_loader = DataLoader(train_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=True, drop_last=True)
valid_loader = DataLoader(valid_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)

使用 Dataloader 加载训练和验证数据.

训练超参数

loss_fn = torch.nn.CrossEntropyLoss()
metrics = {'fps': training.BatchTimer(), 'acc': training.accuracy}
writer = SummaryWriter(os.path.join(args.output_path, 'tensorboard_logs'))
optimizer = optim.Adam(resnet.parameters(), lr=args.lr, weight_decay=args.weight_decay)
scheduler = MultiStepLR(optimizer, list(range(args.decay_interval, args.num_epochs, args.decay_interval)))
  • loss_fn: 损失函数, 交叉熵
  • metrics: 评估标准, acc
  • optimizer: 优化器, Adam
  • scheduler: 学习率递减

训练循环

for epoch_i in range(args.num_epochs):
    print('\nEpoch {}/{}'.format(epoch_i + 1, args.num_epochs))
    resnet.train()
    train_loss, train_metrics = training.pass_epoch(
        resnet, loss_fn, train_loader, optimizer, scheduler,
        batch_metrics=metrics, show_running=False, device=device,
        writer=writer
    )
  • training.pass_epoch: 是封装好的函数, 用于训练数据

验证 Valid

验证过程

with torch.no_grad():
    resnet.eval()
    test_output = dict()
    for images, labels, frame_names, face_labels in valid_loader:
        images = images.to(device)
        labels = labels.to(device)
        y_preds = softmax(resnet(images))[:, 1]
        for y_pred, frame_name, face_label, label in zip(y_preds, frame_names, face_labels, labels):
            y_pred = float(y_pred.cpu())
            label = int(label.cpu())
            if frame_name not in test_output:
                test_output[frame_name] = dict()
            test_output[frame_name][face_label] = {'pred': y_pred, 'label': label}
  1. 禁用梯度: with torch.no_grad()
  2. 模型评估模式: resnet.eval(), 忽略 Droupout 和 Batch Normalization 层, 具体表现为eval()的 Dropout 不会关闭神经元, Batch Normalization 会使用训练的全局均值和方差, 而不是根据每个 batch 调整

模型评估

计算帧准确率 (acc):

val_metrics = dict()
frame_acc_dict, dialog_acc_list = dict(), list()
for frame_id, frame_pred in test_output.items():
    y_preds = [i['pred'] for i in frame_pred.values()]
    y_golds = [i['label'] for i in frame_pred.values()]
    if not any(y_golds):  # 都是0,说明说话人不在frame中
        frame_acc_dict[frame_id] = 0
    else:
        frame_acc_dict[frame_id] = (np.argmax(y_preds) == np.argmax(y_golds))

val_metrics['frame_acc'] = sum(frame_acc_dict.values()) / len(frame_acc_dict)

计算对话准确率 (acc):

val_metadata = json.load(open(os.path.join(args.data_base_folder, 'train-metadata.json')))
for dialog_data in val_metadata:
    for frame_data in dialog_data:
        if frame_data['frame'] in frame_acc_dict:
            dialog_acc_list.append(frame_acc_dict[frame_data['frame']])
val_metrics['dialog_acc'] = sum(dialog_acc_list) / len(dialog_acc_list)

记录 & 保存

for key in val_metrics:
    writer.add_scalar('valid/' + key, val_metrics[key], epoch_i)

print('epoch %d' % epoch_i)
print('dialog level acc: {}'.format(val_metrics['dialog_acc']))
print('val acc: {}, best_acc: {}'.format(val_metrics['frame_acc'], best_acc))
if val_metrics['frame_acc'] > best_acc:
    best_acc = val_metrics['frame_acc']
    torch.save(resnet.state_dict(), os.path.join(args.output_path, 'best_model.pth'))
    json.dump(test_output, open(os.path.join(args.output_path, 'valid_output.json'), 'w'))
  • 记录评估指标
  • 控制台输出评估指标
  • 保存模型

测试 Test

加载模型

elif args.func.startswith('test'):
    resnet = InceptionResnetV1(classify=True, num_classes=2).to(device)
    resnet.eval()
    print('loading checkpoint from %s' % args.model_ckpt)
    resnet.load_state_dict(torch.load(args.model_ckpt))
    softmax = torch.nn.Softmax()
  1. 初始化模型: InceptionResnetV1
  2. 设置评估模式: reset.eval()
  3. 加载训练好的模型: resnet.load_state_dict(torch.load(args.model_ckpt))

加载测试数据

for split in ['test', 'test-hard']:
    test_dataset = FaceDataset(args.data_base_folder, split=split, debug=args.debug)
    test_loader = DataLoader(test_dataset, num_workers=args.num_workers, batch_size=args.batch_size, shuffle=False)
  • 分别加载 test / test-hard 测试数据

预测结果

with torch.no_grad():
    resnet.eval()
    test_output = dict()
    for images, labels, frame_names, face_labels in test_loader:
        images = images.to(device)
        labels = labels.to(device)
        y_preds = softmax(resnet(images))[:, 1]
        for y_pred, frame_name, face_label, label in zip(y_preds, frame_names, face_labels, labels):
            y_pred = float(y_pred.cpu())
            label = int(label.cpu())
            if frame_name not in test_output:
                test_output[frame_name] = dict()
            test_output[frame_name][face_label] = {'pred': y_pred, 'label': label}

test_metrics = dict()
frame_acc_dict, dialog_acc_list = dict(), list()
for frame_id, frame_pred in test_output.items():
    y_preds = [i['pred'] for i in frame_pred.values()]
    y_golds = [i['label'] for i in frame_pred.values()]
    if not any(y_golds):        # 都是0,说明说话人不在frame中
        frame_acc_dict[frame_id] = 0
    else:
        frame_acc_dict[frame_id] = (np.argmax(y_preds) == np.argmax(y_golds))

test_metrics['frame_acc'] = sum(frame_acc_dict.values()) / len(frame_acc_dict)
if split.startswith('test'):
    test_metadata = json.load(open(os.path.join(args.data_base_folder, 'test-metadata.json')))
else:
    test_metadata = [example for example in json.load(open(os.path.join(args.data_base_folder, 'train-metadata.json'))) if example[0]['frame'].startswith('s01')]

for dialog_data in test_metadata:
    for frame_data in dialog_data:
        if frame_data['frame'] in frame_acc_dict:
            dialog_acc_list.append(frame_acc_dict[frame_data['frame']])
        else:
            dialog_acc_list.append(0)       # 没有人脸,因此本模型无从判断
test_metrics['dialog_acc'] = sum(dialog_acc_list) / len(dialog_acc_list)

print('dialog level acc: {}'.format(test_metrics['dialog_acc']))
print('frame level acc: {}'.format(test_metrics['frame_acc']))
json.dump(test_output, open(os.path.join(args.output_path, '%s_output.json' % split), 'w'))

将测试过程中的输出保存为 json 文件.

参考文献

比赛链接

Baseline 完整代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/235268.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实现Django Models的数据mock

目录 一、创建测试数据 二、使用随机数据 三、使用第三方库生成数据 四、编写测试用例 五、总结 在 Django 中&#xff0c;Model 是用于定义数据库表的结构的类。有时候&#xff0c;我们需要在测试或者开发过程中&#xff0c;模拟 Model 的数据&#xff0c;而不是直接从数…

springboot基础(80):redis geospatial的应用

文章目录 前言redis geospatial如何从地图上获取经纬度springboot 的相关方法调用准备redis服务器引用的依赖预设位置的keyGEOADD 添加位置GEORADIUS 获取指定经纬度附件的停车场&#xff08;deprecated&#xff09;GEORADIUS 获取指定成员附件的停车场&#xff08;deprecated&…

SLMi8230BDCG-DG兼容Si8230BD-BS-IS 通过CQC认证隔离驱动方案探索

SLMi8230BDCG-DG 双通道1A 5.0kVRMS隔离栅极驱动器是一种具有不同配置的隔离双通道栅极驱动器。配置为高侧/低侧驱动器&#xff0c;峰值 电源 输出电流为1.0A&#xff0c;具有可编程死区&#xff08;DT&#xff09;功能。将DIS引脚拉高会同时关闭两个输出&#xff0c;并允许在D…

多合一iPhone 解锁工具:iMyFone LockWiper iOS

多合一iPhone 解锁工具 无需密码解锁 iPhone/iPad/iPod touch 上所有类型的屏幕锁定 在几分钟内解锁 iPhone Apple ID、Touch ID 和 Face ID 立即绕过 MDM 并删除 iPhone/iPad/iPod touch 上的 MDM 配置文件 支持所有 iOS 版本和设备&#xff0c;包括最新的 iOS 17 和 iPhone 1…

XML学习及应用

介绍XML语法及应用 1.XML基础知识1.1什么是XML语言1.2 XML 和 HTML 之间的差异1.3 XML 用途 2.XML语法2.1基础语法2.2XML元素2.3 XML属性2.4XML命名空间 3.XML验证3.1xml语法验证3.2自定义验证3.2.1 XML DTD3.2.2 XML Schema3.2.3PCDATA和CDATA区别3.2.4 参考 1.XML基础知识 1…

python:五种算法(DBO、WOA、GWO、PSO、GA)求解23个测试函数(python代码)

一、五种算法简介 1、蜣螂优化算法DBO 2、鲸鱼优化算法WOA 3、灰狼优化算法GWO 4、粒子群优化算法PSO 5、遗传算法GA 二、5种算法求解23个函数 &#xff08;1&#xff09;23个函数简介 参考文献&#xff1a; [1] Yao X, Liu Y, Lin G M. Evolutionary programming made…

[java学习日记]反射、动态代理

目录 一.反射的简单解释与获取字节码文件对象 二.获取构造方法对象Constructor 三.反射获取字节码文件中的成员变量Field 四.反射获取字节码文件中的成员方法&#xff1a;Method 五.反射练习&#xff1a;保存信息 六.反射练习&#xff1a;利用配置文件&#xff08;存储类名…

学习通【劳动通论】2023章节练习题

社会主义核心价值观24个字是&#xff1a;富强、民主、文明、和谐&#xff0c;自由、平等、公正、法治&#xff0c;爱国、敬业、诚信、友善。 1、“富强、民主、文明、和谐”&#xff0c;是我国社会主义现代化国家的建设目标&#xff0c;也是从价值目标层面对社会主义核心价值观…

超级实用的防止商品超卖的 7 种实现方式,非常好用!

高并发场景在现场的日常工作中很常见&#xff0c;特别是在互联网公司中&#xff0c;这篇文章就来通过秒杀商品来模拟高并发的场景。 本文环境&#xff1a; SpringBoot 2.5.7 MySQL 8.0 X MybatisPlus Swagger2.9.2 模拟工具&#xff1a; Jmeter 模拟场景&#xff1a; 减库…

2000字总结—Redis常见命令

与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 Redis常见命令通用命令String类型key的层级格式Hash类型List类型Set类型SortedSet类型 &#x1f48e;总结 Redis常见命令 通用命令 通过help[command]可以查看一个命令的具体用法 127.0.0.1:6379> help keysK…

鸿蒙应用开发ArkTS容器组件的使用

语雀知识库地址&#xff1a;语雀HarmonyOS知识库 飞书知识库地址&#xff1a;飞书HarmonyOS知识库 本文示例代码地址&#xff1a;Gitee 仓库地址 嗨&#xff0c;大家好&#xff0c;我是小白 上篇文章向大家介绍了 ArkTS 中我们经常使用到的几种基础组件&#xff0c;Text、Butt…

智能优化算法应用:基于教与学算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于教与学算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于教与学算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.教与学算法4.实验参数设定5.算法结果6.参考文…

【Python必做100题】之第三题(找出100以内的奇数并打印)

思路&#xff1a; 1、定义一个空列表来存储所有的奇数 2、判断是奇数就追加到列表的末尾 3、打印所有的奇数 代码如下&#xff1a; list [ ] #定义一个列表来存储所有的奇数 for i in range (1,100):if i % 2 ! 0: #判断是否为奇数list.append(i) #追加到列表的末尾 prin…

仿照MyBatis手写一个持久层框架学习

首先数据准备&#xff0c;创建MySQL数据库mybatis&#xff0c;创建表并插入数据。 DROP TABLE IF EXISTS user_t; CREATE TABLE user_t ( id INT PRIMARY KEY, username VARCHAR ( 128 ) ); INSERT INTO user_t VALUES(1,Tom); INSERT INTO user_t VALUES(2,Jerry);JDBC API允…

《信息技术时代》期刊杂志论文发表投稿

《信息技术时代》期刊收稿方向&#xff1a;通信工程、大数据、计算机、办公自动化、信息或计算机教育、电子技术、系统设计、移动信息、图情信息研究、人工智能、智能技术、信息技术与网络安全等。 刊名&#xff1a;信息技术时代 主管主办单位&#xff1a;深圳湾科技发展有限…

【sgAutocomplete】自定义组件:基于elementUI的el-autocomplete组件开发的自动补全下拉框组件(带输入建议的自动补全输入框)

特性&#xff1a; 1、支持本地保存选中过的记录 2、支持动态接口获取匹配下拉框内容 3、可以指定对应的显示label和字段组件key 4、自动生成速记符字段&#xff08;包含声母和全拼两种类型&#xff09;&#xff0c;增强搜索匹配效率 sgAutocomplete源码 <template><!…

API接口并发测试:如何测试API接口的最大并发能力?

本文将深入探讨API接口并发测试&#xff0c;介绍并比较不同的API并发测试工具&#xff0c;并分享如何有效测量和提高API接口在最大并发情况下的性能。了解如何应对高并发压力是保证系统稳定性和用户满意度的关键&#xff0c;让我们一起来探索这个重要的话题。 随着互联网的迅速…

selenium库的使用

来都来了给我点个赞收藏一下再走呗&#x1f339;&#x1f339;&#x1f339;&#x1f339;&#x1f339; 目录 一、下载需要用到的python库selenium 二、selenium的基本使用 1.在python代码引入库 2.打开浏览器 3.元素定位 1&#xff09;通过id定位 2&#xff09;通过标…

计算机组成原理-指令寻址

指令寻址 指令 寻址下一条欲执行指令的地址&#xff08;始终由程序计数器PC给出) 顺序寻址 &#xff08;PC&#xff09;“1”-> PC 这里的1理解为1个指令字长&#xff0c;实际加的值会因指令长度、编址方式而不同 **跳跃寻址 **由转移指令指出 数据寻址 确定 本条指令 的…

AttributeError: module ‘lib‘ has no attribute ‘X509_V_FLAG_CB_ISSUER_CHECK‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…