Pytorch之AlexNet花朵分类

news2024/11/24 17:30:03
  • 💂 个人主页:风间琉璃
  • 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
  • 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)订阅专栏

目录

一、AlexNet

1.卷积模块

2.全连接模块

3.AlexNet创新点

1.更深的神经网络结构

2.ReLU激活函数的使用

3.局部响应归一化(LRN)的使用

4.数据增强和Dropout

5.大规模分布式训练

二、AlexNet实现

1.定义AlexNet网络模型

2.加载数据集

3.训练模型

4.测试模型

三、实现图像分类


一、AlexNet

AlexNet是由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton在2012年ImageNet图像分类竞赛中提出的一种经典的卷积神经网络。当时,AlexNet在 ImageNet 大规模视觉识别竞赛中取得了优异的成绩,把深度学习模型在比赛中的正确率提升到一个前所未有的高度。因此,它的出现对深度学习发展具有里程碑式的意义。

AlexNet输入为RGB三通道的3 x 224 × 224大小的色彩图像。AlexNet 共包含5 个卷积层(包含3个池化)和 3 个全连接层。其中,每个卷积层都包含卷积核、偏置项、ReLU激活函数和局部响应归一化(LRN)模块。第1、2、5个卷积层后面都跟着一个最大池化层,后三个层为全连接层。最终输出层为softmax,将网络输出转化为概率值,得到样本属于 1000 个类别的概率分布,用于预测图像的类别。

为了能够在当时的显卡设备NVIDIA GTX 580(3GB显存)上训练模型, Alex Krizhevsky 将卷积层、前 2 个全连接层等拆开在两块显卡上面分别训练,最后一层合 并到一张显卡上面,进行反向传播更新。AlexNet 在 ImageNet 取得了 15.3%的Top-5 错误 率,比第二名在错误率上降低了 10.9%。

1.卷积模块

AlexNet共有五个卷积层,每个卷积层都包含卷积核、偏置项、ReLU激活函数和局部响应归一化(LRN,Local Response Normalization)模块。

卷积层C1:

输入数据:3x224x224

使用96个核(2个部分之和,每个部分为48,因为输出特征层的通道数等于卷积核个数)对3 x 224 × 224的输入图像进行特征提取,卷积核大小为3 x 11 × 11(输入特征层的通道数等于卷积核的通道数),步长为4。将一对48x55×55的特征图分别放入ReLU激活函数。激活后的图像进行最大池化,size为3×3,stride为2,池化后的特征图size为48x27×27(一部分),总共就是96x27x27。池化后进行LRN处理。

LRN(Local Response Normalization) 局部响应归一化,LRN模拟神经生物学上一个叫做侧抑制(lateral inhibitio)的功能,侧抑制指的是被激活的神经元会抑制相邻的神经元。

LRN局部响应归一化借鉴侧抑制的思想实现局部抑制,使得响应比较大的值相对更大,提高了模型的泛化能力。LRN只对数据相邻区域做归一化处理,不改变数据的大小和维度。LRN概念是在AlexNet模型中首次提出,在GoogLenet中也有应用,但是LRN的实际作用存在争议,如在2015年Very Deep Convolutional Networks for Large-Scale Image Recognition 论文中指出LRN基本没什么用。

卷积层C2:

输入数据:96x27x27

使用卷积层C1的输出作为输入,并使用256个卷积核进行滤波,核大小为48x5 × 5。卷积后数据为256x27x27,做了padding,使得卷积后图像大小不变。其次进行relu,然后进行最大池化,最大池化Maxpool的核大小为3x3,步长为2,池化后的数据为256x13x13([27-3]/2 + 1),下部分:128x13x13。

卷积层C3:

输入数据:256x13x13

使用384个卷积核,核大小为256 x 3 × 3 ,与卷积层C2的输出相连。卷积后的数据:384x13x13,卷积前后图像的大小不变,然后经过relu层,输出数据384x13x13, C3层没有Maxpool层和norm层

卷积层C4:

输入数据:384x13x13

使用384个卷积核,核大小为384 x 3 × 3。卷积核数据384 x 13 x13,做了padding填充,使卷积后图像大小不变,relu后数据384 x 13 x 13。同样没有池化层和norm层

卷积层C5:

输入数据:384 x 13 x13

使用256个卷积核,核大小为384 x 3 × 3。经过relu和池化后256 x 6 x 6(9216),池化核size同样为3×3,stride为2。

其中,卷积层C3、C4、C5互相连接,中间没有接入池化层或归一化层

2.全连接模块

全连接层F6:

输入数据:256x6x6

全连接层输出:因为是全连接层,卷积核size为6×6×256,4096个卷积核生成4096个特征图,尺寸为1×1。然后放入ReLU函数、Dropout处理。值得注意的是AlexNet使用了Dropout层,以减少过拟合现象的发生。

Dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率(一般是50%,这种情况下随机生成的网络结构最多)将其暂时从网络中丢弃(保留其权值),不再对前向和反向传输的数据响应。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而相当于每一个mini-batch都在训练不同的网络,drop out可以有效防止模型过拟合,让网络泛化能力更强,同时由于减少了网络复杂度,加快了运算速度。还有一种观点认为drop out有效的原因是对样本增加来噪声,变相增加了训练样本。

全连接层F7:与F6层相同。

全连接层F8:最后一层全连接层的输出是1000维softmax的输入,softmax会产生1000个类别预测的值

各网络层参数:

较为详细的网络结构:(227x227是经过填充后的图像),这里将分开训练的两部分合在一起啦。

 注意:C3和C4层无池化层哦

3.AlexNet创新点

1.更深的神经网络结构

AlexNet 是首个真正意义上的深度卷积神经网络,它的深度达到了当时先前神经网络的数倍。通过增加网络深度,AlexNet 能够更好地学习数据集的特征,从而提高了图像分类的精度。

2.ReLU激活函数的使用

AlexNet 首次使用了修正线性单元(ReLU)这一非线性激活函数。相比于传统的 sigmoid 和 tanh 函数,ReLU 能够在保持计算速度的同时,有效地解决了梯度消失问题,从而使得训练更加高效

3.局部响应归一化(LRN)的使用

LRN是在卷积层和池化层之间添加的一种归一化操作。在卷积层中,每个卷积核都对应一个特征图(feature map),LRN就是对这些特征图进行归一化。具体来说,对于每个特征图上的每个位置,计算该位置周围的像素的平方和,然后将当前位置的像素值除以这个和

LRN本质是抑制邻近神经元的响应,从而增强了神经元的较大响应。这种技术在一定程度上能够避免过拟合,并提高网络的泛化能力。

4.数据增强和Dropout

为了防止过拟合,AlexNet 引入了数据增强和 Dropout 技术。

数据增强可以通过对图像进行旋转、翻转、裁剪等变换,增加训练数据的多样性,提高模型的泛化能力

Dropout 则是在训练过程中随机删除一定比例的神经元,强制网络学习多个互不相同的子网络,从而提高网络的泛化能力。Dropout简单来说就是在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征。

 5.大规模分布式训练

AlexNet在使用GPU进行训练时,可将卷积层和全连接层分别放到不同的GPU上进行并行计算,从而大大加快了训练速度。像这种大规模 GPU 集群进行分布式训练的方法在后来的深度学习中也得到了广泛的应用。

二、AlexNet实现

1.定义AlexNet网络模型

AlexNet网络模型的构建完全按照前面的各层的参数设置的,包括5层卷积层和3层全连接层。

class AlexNet(nn.Module):
    def __init__(self, num_classes=1000, init_weights=False):
        super(AlexNet, self).__init__()
        self.features = nn.Sequential(
            # input [3, 224, 224]    output [96, 55 ,55]
            nn.Conv2d(in_channels=3, out_channels=96, kernel_size=11, stride=4, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),  # output[96, 27, 27]

            nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, padding=2),  # output[256, 27, 27]
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),  # output[256, 13, 13]

            nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, padding=1),  # output[384, 13, 13]
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, padding=1),  # output[384, 13, 13]
            nn.ReLU(inplace=True),

            nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, padding=1),  # output[256, 13, 13]
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2)  # output[256, 6, 6]

        )
        self.classifier = nn.Sequential(
            nn.Dropout(p=0.5),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),

            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),

            nn.Linear(4096, num_classes)
        )

        # 初始化参数
        if init_weights:
            self._initialize_weights()

    def forward(self, x):
        x = self.features(x)
        x = torch.flatten(x, start_dim=1)
        # x = x.view(-1, 6*6*256)
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():  # 变量网络所有层
            if isinstance(m, nn.Conv2d):  # 是否为卷积层
                # 使用Kaiming初始化方法来初始化该层的权重
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
                if m.bias is not None:  # 否具有偏差项
                    nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):  # 是否为Linear
                # 正太分布初始化全连接层
                nn.init.normal_(m.weight, 0, 0.01)
                # 将偏项设置为0
                nn.init.constant_(m.bias, 0)

2.加载数据集

这里数据集是自定义的5中花朵数据集,每个文件夹下对应着一种花。

这是总的数据集,但是我们还需要将数据集分为测试集和训练集,分类脚本如下

def mk_file(file_path: str):
    if os.path.exists(file_path):
        # 如果文件夹存在,则先删除原文件夹在重新创建
        rmtree(file_path)
    os.makedirs(file_path)


def main():
    # 保证随机可复现
    random.seed(0)

    # 将数据集中10%的数据划分到验证集中
    split_rate = 0.1

    # 指向你解压后的flower_photos文件夹
    cwd = os.getcwd()
    data_root = os.path.join(cwd, "flower_data")
    origin_flower_path = os.path.join(data_root, "flower_photos")
    assert os.path.exists(origin_flower_path), "path '{}' does not exist.".format(origin_flower_path)

    flower_class = [cla for cla in os.listdir(origin_flower_path)
                    if os.path.isdir(os.path.join(origin_flower_path, cla))]

    # 建立保存训练集的文件夹
    train_root = os.path.join(data_root, "train")
    mk_file(train_root)
    for cla in flower_class:
        # 建立每个类别对应的文件夹
        mk_file(os.path.join(train_root, cla))

    # 建立保存验证集的文件夹
    val_root = os.path.join(data_root, "val")
    mk_file(val_root)
    for cla in flower_class:
        # 建立每个类别对应的文件夹
        mk_file(os.path.join(val_root, cla))

    for cla in flower_class:
        cla_path = os.path.join(origin_flower_path, cla)
        images = os.listdir(cla_path)
        num = len(images)
        # 随机采样验证集的索引
        eval_index = random.sample(images, k=int(num*split_rate))
        for index, image in enumerate(images):
            if image in eval_index:
                # 将分配至验证集中的文件复制到相应目录
                image_path = os.path.join(cla_path, image)
                new_path = os.path.join(val_root, cla)
                copy(image_path, new_path)
            else:
                # 将分配至训练集中的文件复制到相应目录
                image_path = os.path.join(cla_path, image)
                new_path = os.path.join(train_root, cla)
                copy(image_path, new_path)
            print("\r[{}] processing [{}/{}]".format(cla, index+1, num), end="")  # processing bar
        print()

    print("processing done!")

处理完后如下:

 然后就可以加载数据集和测试集,并进行相应的预处理操作。

    # 预处理
    data_transform = {
        "train": transforms.Compose([
                                    transforms.RandomResizedCrop(224),     # 随机裁剪
                                    transforms.RandomHorizontalFlip(),   # 随机翻转
                                    transforms.ToTensor(),  # 类型转变并归一化
                                    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
        "val": transforms.Compose([
                                    transforms.Resize((224, 224)),
                                    transforms.ToTensor(),
                                    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

    # 数据集根目录
    data_root = os.path.abspath(os.getcwd())
    print(os.getcwd())
    # 图片目录
    image_path = os.path.join(data_root, "data_set", "flower_data")
    print(image_path)
    assert os.path.exists(image_path), "{} path does not exit.".format(image_path)

    # 准备数据集
    train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),
                                         transform=data_transform["train"])
    train_num = len(train_dataset)

    validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),
                                            transform=data_transform["val"])
    val_num = len(validate_dataset)


    # 定义一个包含花卉类别到索引的字典:雏菊,蒲公英,玫瑰,向日葵,郁金香
    # {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
    # 获取包含训练数据集类别名称到索引的字典,这通常用于数据加载器或数据集对象中。
    flower_list = train_dataset.class_to_idx
    # 创建一个反向字典,将索引映射回类别名称
    cla_dict = dict((val, key) for key, val in flower_list.items())
    # 将字典转换为格式化的JSON字符串,每行缩进4个空格
    json_str = json.dumps(cla_dict, indent=4)
    # 打开名为 'class_indices.json' 的JSON文件,并将JSON字符串写入其中
    with open('class_indices.json', 'w') as json_file:
        json_file.write(json_str)

    batch_size = 32
    nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers
    print("using {} dataloader workers every process".format(nw))

    # 加载数据集
    train_loader = torch.utils.data.DataLoader(train_dataset,
                                               batch_size=batch_size, shuffle=True,
                                               num_workers=nw)

    validate_loader = torch.utils.data.DataLoader(validate_dataset,
                                                  batch_size=4, shuffle=False,
                                                  num_workers=nw)

    print("using {} images for training, {} images for validation.".format(train_num, val_num))

3.训练模型

数据集准备好后就可以实例化网络进行模型的训练。

    net = AlexNet(num_classes=5, init_weights=True)

    net.to(device)
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(net.parameters(), lr=0.0002)

    epochs = 200
    save_path = './AlexNet.pth'
    best_acc = 0.0
    train_steps = len(train_loader)
    for epoch in range(epochs):
        # 将神经网络设置为训练模式
        net.train()
        running_loss = 0.0
        # 使用 tqdm 创建一个进度条以显示训练进度
        # tqdm 用于包装 train_loader,使其在终端中显示一个进度条,以便用户可以实时查看训练进度。file=sys.stdout 参数将进度条输出到终端。
        train_bar = tqdm(train_loader, file=sys.stdout)
        # 迭代训练数据集
        for step, data in enumerate(train_bar):
            images, labels = data
            # 清除梯度
            optimizer.zero_grad()
            # 前向传播
            outputs = net(images.to(device))
            # 计算损失
            loss = loss_function(outputs, labels.to(device))
            # 反向传播
            loss.backward()
            # 更新权重
            optimizer.step()

            # 累积损失值
            running_loss += loss.item()
            train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                     epochs,
                                                                     loss)

4.测试模型

每训练完一个epoch,就进行一次测试,并将比较准确率大小,并根据当前模型的准确率保存模型,如果当前准确率优于之前的最佳准确率,则保存当前模型参数;否则,不变。

        # 将神经网络设置为评估模式
        net.eval()
        acc = 0.0    # 准确率
        # 在评估模式下不进行梯度计算
        with torch.no_grad():
            val_bar = tqdm(validate_loader, file=sys.stdout)
            for val_data in val_bar:
                val_images, val_labels = val_data
                # 前向传播
                outputs = net(val_images.to(device))
                # 预测类别
                predict_y = torch.max(outputs, dim=1)[1]
                acc += torch.eq(predict_y, val_labels.to(device)).sum().item()

        # 计算验证集准确率
        val_accurate = acc / val_num
        print('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %
              (epoch + 1, running_loss / train_steps, val_accurate))

        # 如果当前模型的验证准确率优于之前的最佳准确率,则保存当前模型参数
        if val_accurate > best_acc:
            best_acc = val_accurate
            torch.save(net.state_dict(), save_path)

三、实现图像分类

利用上述训练好的网络模型进行测试,验证是否能完成分类任务。

def main():
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    data_transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),
        transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
    ])

    # 加载图片
    img_path = 'tulips.jpg'
    assert os.path.exists(img_path), "file: '{}' does not exist.".format(img_path)
    image = Image.open(img_path)

    # img.show()
    image.show()
    # [N, C, H, W]
    img = data_transform(image)
    # 扩展维度
    img = torch.unsqueeze(img, dim=0)

    # 获取标签
    json_path = 'class_indices.json'
    assert os.path.exists(json_path), "file: '{}' does not exist.".format(json_path)
    with open(json_path, 'r') as f:
        # 使用json.load()函数加载JSON文件的内容并将其存储在一个Python字典中
        class_indict = json.load(f)

    # 加载网络
    model = AlexNet(num_classes=5).to(device)

    # 加载模型文件
    weights_path = "./AlexNet.pth"
    assert os.path.exists(weights_path), "file: '{}' dose not exist.".format(weights_path)
    model.load_state_dict(torch.load(weights_path))

    model.eval()
    with torch.no_grad():
        # 对输入图像进行预测
        output = torch.squeeze(model(img.to(device))).cpu()
        # 对模型的输出进行 softmax 操作,将输出转换为类别概率
        predict = torch.softmax(output, dim=0)
        # 得到高概率的类别的索引
        predict_cla = torch.argmax(predict).numpy()

    res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)], predict[predict_cla].numpy())
    draw = ImageDraw.Draw(image)
    # 文本的左上角位置
    position = (10, 10)
    # fill 指定文本颜色
    draw.text(position, res, fill='red')
    image.show()
    for i in range(len(predict)):
        print("class: {:10}   prob: {:.3}".format(class_indict[str(i)], predict[i].numpy()))

预测结果:

 

结束语

感谢阅读吾之文章,今已至此次旅程之终站 🛬。

吾望斯文献能供尔以宝贵之信息与知识也 🎉。

学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。

然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。

愿尔之学习之路风平浪静,充满希望💐。再次感谢尔之阅读与关注,吾期望再次相见!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习技巧应用28-强化学习的原理介绍与运用技巧实践

大家好,我是微学AI,今天给大家介绍一下深度学习技巧应用28-强化学习的原理介绍与运用技巧实践, 强化学习是一种机器学习的子领域,它使得一个智能体在与环境的交互中学习如何行动以最大化某种数值奖励信号。强化学习模型的关键特性是它的试错搜索和延迟奖励。 一、强化学习…

React 全栈体系(十四)

第七章 redux 六、react-redux 7. 代码 - react-redux 数据共享版 7.1 效果 7.2 App /* src/App.jsx */ import React, { Component } from "react"; import Count from "./containers/Count"; import Person from "./containers/Person";ex…

opencv dnn模块 示例(17) 目标检测 object_detection 之 yolo v5

在前文【opencv dnn模块 示例(16) 目标检测 object_detection 之 yolov4】介绍的yolo v4后的2个月,Ultralytics发布了YOLOV5 的第一个正式版本,其性能与YOLO V4不相伯仲。 文章目录 1、Yolo v5 和 Yolo v4 的区别说明1.1、Data Augmentation - 数据增强1…

Shader中的渲染路径LightMode

文章目录 前言一、在Shader中如何区分不同的渲染路径1、Pass Tag2、LightMode的不同类型 二、在Frame Debug下查看渲染路径之间的区别1、在摄像机可以切换渲染路径2、前向渲染路径3、延迟渲染路径4、顶点照明渲染路径(可以看出效果很差) 前言 Shader中的…

网络竞品分析:用爬虫技术洞悉竞争对手

概述 网络竞品分析是指通过互联网收集、分析和比较竞争对手的信息,以了解他们的优势和劣势,找出自己的差距和机会,制定有效的竞争策略。网络竞品分析涉及的信息包括竞争对手的产品、价格、渠道、营销、用户反馈等方面。爬虫技术是一种自动化…

电子商务交易产品质量监测实施指南

声明 本文是学习GB-T 42893-2023 电子商务交易产品质量监测实施指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本文件提供了开展电子商务交易的有形产品质量监测的总则,监测准备、监测实施、监测效果评价 与反馈等过程指导…

git的ssh协议走代理拉取代码

1.首先要自己搭建一个代理 https://blog.csdn.net/Jessica_hhh/article/details/133276101https://blog.csdn.net/Jessica_hhh/article/details/133276101 2. 确认机器装过nc,若没有,用yum install -y nc安装 centos 6使用yum安装软件_duang_huang的博…

Machine Learning(study notes)

There is no studying without going crazy Studying alwats drives us crazy 文章目录 DefineMachine LearningSupervised Learning(监督学习)Regression problemClassidication Unspervised LearningClustering StudyModel representation&#xff08…

多层感知机——MLP

源代码在此处:https://github.com/wepe/MachineLearning/tree/master/DeepLearning Tutorials/mlp 一、多层感知机(MLP)原理简介 多层感知机(MLP,Multilayer Perceptron)也叫人工神经网络(ANN&…

2023-9-25 JZ24 反转链表

题目链接:反转链表 import java.util.*;/** public class ListNode {* int val;* ListNode next null;* public ListNode(int val) {* this.val val;* }* }*/public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改&…

顺序读写函数的介绍:fgetc fputc

目录 前提须知: 函数介绍: fputc: fpuct写到文件中,这个可以叫做文件流。 文件效果: 若要将fputc写入屏幕中,可以采取以下代码操作: 屏幕效果: fgetc: 使用f…

基于KubeFATE的FATE-LLM任务实战

随着大型语言模型的不断蓬勃发展,相关新模型,新应用和新范式也在不断涌现,自 4 月发布以来,FATE-LLM 已经迭代发布了多个版本,不断完善大语言模型在联邦学习场景下的支持,以解决构建、使用大模型时的数据隐…

基于图像形态学处理的路面裂缝检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................... %1:从文件夹中读取多个…

Linux基础知识 总结

Linux基础知识 总结 1、Clion的简单介绍 CLion是以IntelliJ为基础,专为开发C及C所设计的跨平台IDE,可以在Windows、Linux及MacOS使用,这里我是在ubuntu 16.0.4基础上安装。2、下载 Linux版Clion的.tar.gz的压缩包 wget https://download.j…

常用数据库validationQuery语句

常用数据库validationQuery语句 validationQuery是用来验证数据库连接的查询语句,这个查询语句必须是至少返回一条数据的SELECT语句。每种数据库都有各自的验证语句, 下表中收集了几种常见数据库的validationQuery。DataBase validationQueryhsqldb …

【voe】channel receive 和 acm 关联走读

每次看服务端的owt的各种adpater都看得懵懵懂懂翻出三年前的客户端webrtc的代码,才觉得舒坦终于知道为啥owt adapter要这么调用了。ChannelReceiveInterface 是AudioReceiveStream 测试需要的 RtpPacketSinkInterface RtpPacketSinkInterface : This class represents a rece…

系统架构设计(最重要的章节)

系统架构设计 软件架构的概述构件软件架构风格 软件架构的概述 架构设计是在需求分析和软件设计之间的过渡阶段 软件架构设计与生命周期 需求分析:问题空间 架构设计SA:解空间 需求->软件架构设计->系统设计 构件 对象 模块 构件 服务 粒度是越来…

RFID智能档案柜助力各大银行实现RFID智能档案管理

在过去的档案管理过程中,银行常常需要进行繁琐的手工操作,包括分类、排序、装钉、手写档案盒信息等。档案存放无序,查找困难,档案管理效率低下。 问题分析 档案工作流程繁琐低效 银行的档案整理过程繁琐,耗时长&…

flask服务鉴权

基本认证(Basic Authentication): 这是一种简单的鉴权方式,需要客户端发送用户名和密码,服务器验证后允许或拒绝访问。可以使用 Flask-BasicAuth 扩展来实现。首先,安装扩展: pip install Fla…

JS 拖拽事件

1.drag等拖拽事件 拖放是由拖动与释放两部分组成,拖放事件也分为被拖动元素的相关事件,和容器的相关事件。 被拖动元素的相关事件如下所示: 被拖动元素相关事件: 事件描述dragstart用户开始拖动元素时触发drag元素正在拖动时触发dragend用户…