飞浆PaddlePaddle 猫狗数据大战

news2025/5/18 9:55:28

猫狗数据大战

  • 1 数据集的准备以及处理操作
    • 1.1 数据集
    • 1.2 文件解压操作(python)
  • 1.3 数据的分类
    • 1.4 创建训练集和测试集
  • 2 网络构建
    • CNN版本--
    • DeepID 人脸识别网络结构
    • DeepID 与 CNN 网络结构的差异
  • 3 深度学习模型训练和推理的核心设置
  • 4 制图
  • 5 训练
  • 6 预测+结果

1 数据集的准备以及处理操作

1.1 数据集

在训练模型之前的时候我们需要提前准备数据集
数据集下载链接

1.2 文件解压操作(python)

# 第一步,把原数据解压
import zipfile
import os
import shutil

def unzipFile(source_file, target_dir):
    ''' 解压单个文件到目标文件夹。
    '''
    zf = zipfile.ZipFile(source_file)
    try:
        zf.extractall(path=target_dir)
    except RuntimeError as e:
        print(e)
    zf.close()

sour = 'data/data20541/dogs-vs-cats.zip'
targ = ''
if not os.path.exists('data/dogCat/dog'):   #用os.path.exists来判断'data/dogCat/dog'路径存不存在
    unzipFile(source_file = sour, target_dir = targ)
    os.remove('sample_submission.csv')

首先:我们需要制定一个函数来解压zip文件。
然后:通过python中的库中的文件操作函数ZipFile()来解压
最后:把解压好的文件在通过extractall()函数放在target_dir目录下


第二步:原数据解压之后是 形成 train.zip 和 test.zip 文件还需要解压一遍,让他们解压之后形成训练集和测试集。

if not os.path.exists('data/dogCat/dog'):
    # 先解压训练集
    train_file = 'train.zip'
    train_targetdir = 'data'
    unzipFile(source_file = train_file, target_dir = train_targetdir)
    os.remove(train_file)
    
    # 再解压测试集
    test_file = 'test.zip'
    test_targetdir = 'data'
    unzipFile(source_file = test_file, target_dir = test_targetdir)
    os.remove(test_file)

1.3 数据的分类

在训练计算机识别动物之前,我们需要进行区分,比如:你要让计算机进行猫狗的区分那么你的数据集中有其他的动物的图片,这样的话在训练过程中在识别关键信息的时候就出错!!!。因此我们需要排除干扰选项。

排除干扰项思路:把文件的所有图片都遍历一遍然后进行筛选出猫和狗的图片。

执行代码:

dataRootPath = 'data/dogCat'

def copyFiles(fileDirs, targetDir):
    for fileDir in fileDirs:
        shutil.copy(fileDir, targetDir)
        
def divideData():
    global dataRootPath
    classDirs = os.listdir('data/train')
    dogDirs = [os.path.join('data/train',x) for x in classDirs if x.find('dog')>=0]
    catDirs = [os.path.join('data/train',x) for x in classDirs if x.find('cat')>=0]
    print('总图片数量为:%d'%len(classDirs))
    print('\t猫的图片数量为:%d'%len(catDirs))
    print('\t狗的图片数量为:%d'%len(dogDirs))

    # 遍历图片,转移数据集
    dogPath = os.path.join(dataRootPath, 'dog')
    if not os.path.exists(dogPath):
        os.makedirs(dogPath)
    copyFiles(dogDirs, dogPath)
    
    catPath = os.path.join(dataRootPath, 'cat')
    if not os.path.exists(catPath):
        os.makedirs(catPath)
    copyFiles(catDirs, catPath)
    
if not os.path.exists('data/dogCat/dog'):
    divideData()

接下来我们来介绍以上提到的函数:
1 os.listdir(路径) ==== 这是把路径中的目录展开 。就是把目录展开了,然后就是目录中的子文件或者文件了。这里就是指的是图片!!!
2 os.path.join(路径,路径1)==== 这里就是列表的初始化 ,先是判断你的x有没有dog或者cat的文字(这一步就是先筛选图片名称为dog和cat)。 如果符合那么就把路径1链接到路劲之后。 如:os.path.join(‘data/dogcat/’,dog1.jpg) -> 结果:data/dogcat/dog1.jpg

3 os.path.exists(路径) 就是判断路径存不存在

4 os.mkdir(名称) 就是创建名称目录

5 shutil.copy(dest,source) 就是把文件进行复制

总结:以上代码就是为了把干扰选项给去掉,为了更好的训练出大模型!!!

1.4 创建训练集和测试集

在经过以上操作后:我们就可以开始进行对数据进行划分和创建数据集合测试集了

#Python   os标准库,用于文件操作、  random随机数生成、 JSON解析和系统参数访问。
import os
import random
import json
import sys
#Paddle.fluid,paddle框架的核心模块,用于构建和训练深度学习模型。
import paddle
import paddle.fluid as fluid
#数值计算库,用于处理数组和矩阵.
import numpy

from multiprocessing import cpu_count
from visualdl import LogWriter
#matplotlib.pyplot用于绘图和数据可视化
import matplotlib.pyplot as plt
import numpy as np

#PIL 用于打开和处理图像
from PIL import Image


import paddle
paddle.enable_static()


#定义长度,和宽度
IMG_H =64
IMG_W =64

#缓冲区大小,用来读取数据处理和预处理
BUFFER_SIZE = 1024

#一次批量处理的个数
BATCH_SIZE =64

#是否使用gpu
USE_CUDA = True
#训练次数
TRAIN_NUM = 150
#学习率
LEARNING_RATE = 0.005

DATA_TRAIN = 0.75 #训练集在数据集中占多少数据  # 表示在训练模型时,使用多大规模的数据

#每执行玩一个模型就放在work/model 中,这样方便模型的拿取
model_save_dir = "work/model"

def genDataList(dataRootPath, trainPercent=0.8):
    # 函数会自动检测dataRootPath下的所有文件夹,每一个文件夹为1个类别的图片
    # 然后生成图片的list,即paddlepaddle能获取信息的形式
    # 默认随机抽取20%作为测试集(验证集)
    # random.seed(42)  #设置随机种子。

    
    classDirs = os.listdir(dataRootPath)#os.listdir用来获取dataRootPath的所有文件和文件夹
    #os.isdir 判断是否为目录  #os.path.join 拼接
    classDirs = [x for x in classDirs if os.path.isdir(os.path.join(dataRootPath,x))]
    listDirTest = os.path.join(dataRootPath, "test.list")
    listDirTrain = os.path.join(dataRootPath, "train.list")
    # 清空原来的数据,当一‘w’模式打开文件夹时,1:文件已存在,那么就会清空数据 2:文件不存在,就会新建一个文件
    #避免旧数据干扰
    # with open(listDirTest, 'w') as f:
    #     pass
    # with open(listDirTrain, 'w') as f:
    #     pass
    with open(listDirTest, 'w') as f:
        pass  # 清空测试集文件
    with open(listDirTrain, 'w') as f:
        pass  # 清空训练集文件
   
    # 随机划分训练集与测试集
    classLabel = 0              # 初始化类别标签,从0开始
    class_detail = []           # 记录每个类别的描述
    classList = []              # 记录所有的类别名
    num_images = 0              # 统计图片的总数量
    for classDir in classDirs:
        classPath = os.path.join(dataRootPath,classDir)     # 获取类别为classDir的图片所在的目录,拼接子目录 如:data/DogCat/dog
        imgPaths = os.listdir(classPath)                    # 获取类别为classDir的所有图片名
        # 从中取trainPercent(默认80%)作为训练集
        imgIndex = list(range(len(imgPaths)))               #生成图片索引列表
        random.shuffle(imgIndex)                            #打乱图片索引
        imgIndexTrain = imgIndex[:int(len(imgIndex)*trainPercent)]#划分训练集与测试集
        imgIndexTest = imgIndex[int(len(imgIndex)*trainPercent):]

        #把代码追加到listDirTest 与 listDirTrain 目录下
        with open(listDirTest,'a') as f:
            for i in imgIndexTest:
                imgPath = os.path.join(classPath,imgPaths[i]) #打开目录listDirTest  把刚刚定义的imgIndexTest写入到该目录下 路劲大致如:data/DogCat/dog/dog.1jpg  
                f.write(imgPath + '\t%d' % classLabel + '\n') # 写入格式图片路劲\t类别标签\n
        with open(listDirTrain,'a') as f:
            for i in imgIndexTrain:
                imgPath = os.path.join(classPath,imgPaths[i])
                f.write(imgPath + '\t%d' % classLabel + '\n')        

        num_images += len(imgPaths)
        
        classList.append(classDir) #将类别名称添加到classList中
        class_detail_list = {}   #记录该类别的名称、标签、测试集图片数量和训练集图片数量
        class_detail_list['class_name'] = classDir             #类别名称,如dog
        class_detail_list['class_label'] = classLabel          #类别标签,如cat 的标签是 0     dog的标签是 1
        class_detail_list['class_test_images'] = len(imgIndexTest)       #该类数据的测试集数目
        class_detail_list['class_trainer_images'] = len(imgIndexTrain)   #该类数据的训练集数目
        class_detail.append(class_detail_list)     
        classLabel += 1

    # 说明的json文件信息
    readjson = {} #包含所有类别的名称、总类别数量、总图片数量以及每个类别的详细信息。
    readjson['all_class_name'] = classList                      # 文件父目录
    readjson['all_class_sum'] = len(classDirs)                  # 总类别数量
    readjson['all_class_images'] = num_images                   # 总图片数量
    readjson['class_detail'] = class_detail                     # 每种类别的情况
    jsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))#json.dumps的作用是将Python对象转换成JSON格式的字符串。这里的参数看起来是用于格式化输出的。
    #sort_keys对字典的键(Key)按字母顺序排序。设置缩进为4个空格,使JSON字符串具有层次结构
    with open(os.path.join(dataRootPath,"readme.json"),'w') as f:#把jsons字符串写入到dataRootPath/readme.json中用于保存数据集的元信息(如类别名称、标签、图片数量等)
        f.write(jsons)
    print ('生成数据列表完成!')
    return readjson['all_class_sum']

classNumber = genDataList(dataRootPath)#返回类别数量
print(classNumber)

我们还是一步一步来:
1 变量:

#定义长度,和宽度
IMG_H =64
IMG_W =64
#缓冲区大小,用来读取数据处理和预处理
BUFFER_SIZE = 1024
#一次批量处理的个数
BATCH_SIZE =64
#是否使用gpu
USE_CUDA = True
#训练次数
TRAIN_NUM = 150
#学习率
LEARNING_RATE = 0.005
DATA_TRAIN = 0.75 #训练集在数据集中占多少数据  # 表示在训练模型时,使用多大规模的数据
#每执行玩一个模型就放在work/model 中,这样方便模型的拿取
model_save_dir = "work/model"

这是我们在训练时进行定义的全局变量:
IMG_H IMG_W 定义的输出图像的长宽高度

BUFFER_SIZE 就是缓冲区的大小用于读取数据处理和预处理

BATCH_SIZE 就是在训练时一次读取的个数

USE_CUDA = True 这是使用GPU来进行训练,因为你使用CPU的话这个程序会训练的很慢或者训练不出来。

TRAIN_NUM 训练次数

LEARNING_RATE 学习率

DATA_TRAIN 训练集在数据集中占多少数据 表示在训练模型时,使用多大规模的数据

model_save_dir 每执行玩一个模型就放在work/model 中,这样方便模型的拿取


然后就是我们的genDataList函数:

def genDataList(dataRootPath, trainPercent=0.8):
    # 函数会自动检测dataRootPath下的所有文件夹,每一个文件夹为1个类别的图片
    # 然后生成图片的list,即paddlepaddle能获取信息的形式
    # 默认随机抽取20%作为测试集(验证集)
    # random.seed(42)  #设置随机种子。

    
    classDirs = os.listdir(dataRootPath)#os.listdir用来获取dataRootPath的所有文件和文件夹
    #os.isdir 判断是否为目录  #os.path.join 拼接
    classDirs = [x for x in classDirs if os.path.isdir(os.path.join(dataRootPath,x))]
    listDirTest = os.path.join(dataRootPath, "test.list")
    listDirTrain = os.path.join(dataRootPath, "train.list")
    # 清空原来的数据,当一‘w’模式打开文件夹时,1:文件已存在,那么就会清空数据 2:文件不存在,就会新建一个文件
    #避免旧数据干扰
    # with open(listDirTest, 'w') as f:
    #     pass
    # with open(listDirTrain, 'w') as f:
    #     pass
    with open(listDirTest, 'w') as f:
        pass  # 清空测试集文件
    with open(listDirTrain, 'w') as f:
        pass  # 清空训练集文件
   
    # 随机划分训练集与测试集
    classLabel = 0              # 初始化类别标签,从0开始
    class_detail = []           # 记录每个类别的描述
    classList = []              # 记录所有的类别名
    num_images = 0              # 统计图片的总数量
    for classDir in classDirs:
        classPath = os.path.join(dataRootPath,classDir)     # 获取类别为classDir的图片所在的目录,拼接子目录 如:data/DogCat/dog
        imgPaths = os.listdir(classPath)                    # 获取类别为classDir的所有图片名
        # 从中取trainPercent(默认80%)作为训练集
        imgIndex = list(range(len(imgPaths)))               #生成图片索引列表
        random.shuffle(imgIndex)                            #打乱图片索引
        imgIndexTrain = imgIndex[:int(len(imgIndex)*trainPercent)]#划分训练集与测试集
        imgIndexTest = imgIndex[int(len(imgIndex)*trainPercent):]

        #把代码追加到listDirTest 与 listDirTrain 目录下
        with open(listDirTest,'a') as f:
            for i in imgIndexTest:
                imgPath = os.path.join(classPath,imgPaths[i]) #打开目录listDirTest  把刚刚定义的imgIndexTest写入到该目录下 路劲大致如:data/DogCat/dog/dog.1jpg  
                f.write(imgPath + '\t%d' % classLabel + '\n') # 写入格式图片路劲\t类别标签\n
        with open(listDirTrain,'a') as f:
            for i in imgIndexTrain:
                imgPath = os.path.join(classPath,imgPaths[i])
                f.write(imgPath + '\t%d' % classLabel + '\n')        

        num_images += len(imgPaths)
        
        classList.append(classDir) #将类别名称添加到classList中
        class_detail_list = {}   #记录该类别的名称、标签、测试集图片数量和训练集图片数量
        class_detail_list['class_name'] = classDir             #类别名称,如dog
        class_detail_list['class_label'] = classLabel          #类别标签,如cat 的标签是 0     dog的标签是 1
        class_detail_list['class_test_images'] = len(imgIndexTest)       #该类数据的测试集数目
        class_detail_list['class_trainer_images'] = len(imgIndexTrain)   #该类数据的训练集数目
        class_detail.append(class_detail_list)     
        classLabel += 1

根据我的注释可以得出:
这个函数第一步就是创建出数据集和测试集:

imgIndex = list(range(len(imgPaths)))               #生成图片索引列表
        random.shuffle(imgIndex)                            #打乱图片索引
        imgIndexTrain = imgIndex[:int(len(imgIndex)*trainPercent)]#划分训练集与测试集
        imgIndexTest = imgIndex[int(len(imgIndex)*trainPercent):]



......之后就是把文件写入操作

第二步就是记录属性

 classLabel = 0              # 初始化类别标签,从0开始
    class_detail = []           # 记录每个类别的描述
    classList = []              # 记录所有的类别名
    num_images = 0              # 统计图片的总数量

他们把记录出来的这些属性,用json进行连接。
得出:
在这里插入图片描述

在这里插入图片描述
把狗做标记:为1 (class_label= 1)
然后圈红圈的分别是训练集的个数,和测试集的个数

猫同理。

1.5 数据处理和创建数据处理器

def trainMapper(sample):
    global IMG_H, IMG_W
    img, label = sample
    
    # 图像加载(兼容老版本)
    img = paddle.dataset.image.load_image(img)  # 所有版本通用
    
    # 图像变换(老版本无水平翻转,需手动添加)
    img = paddle.dataset.image.simple_transform(
        im=img, 
        resize_size=IMG_H, 
        crop_size=IMG_W, 
        is_color=True, 
        is_train=True  # 在1.x版本中此参数可能不触发翻转
    )
    img = img.flatten().astype('float32') / 255.0
    return img, label


# 对自定义数据集创建训练集train的reader
def trainReader(train_list, buffered_size=1024):
    global DATA_TRAIN
    def reader():
        with open(train_list, 'r') as f:
            # 将train.list里面的标签和图片的地址放在一个list列表里面,中间用\t隔开'
            # 如data/dogCat/Cat_/1.jpg\t0'
            lines = [line.strip() for line in f]# 读取所有行并去除首尾空格
            np.random.shuffle(lines)#打乱数据顺序增强随机性
            lines = lines[:int(len(lines)*DATA_TRAIN)]
            for line in lines:
                # 图像的路径和标签是以\t来分割的,所以我们在生成这个列表的时候,使用\t就可以了
                img_path, lab = line.strip().split('\t')# 分割路径与标签
                yield img_path, int(lab) # 生成数据元组(路径, 标签)
    # 创建自定义数据训练集的train_reader
    return paddle.reader.xmap_readers(trainMapper, reader, cpu_count(), buffered_size)
    def testMapper(sample):
    global IMG_H
    global IMG_W
    img, label = sample
    img = paddle.dataset.image.load_image(img)
    img = paddle.dataset.image.simple_transform(im=img, 
                                                resize_size=IMG_H, crop_size=IMG_W, 
                                                is_color=True, is_train=False)
    img= img.flatten().astype('float32')/255.0
    return img, label

# 对自定义数据集创建验证集test的reader
def testReader(test_list, buffered_size=1024):
    global DATA_TRAIN
    def reader():
        with open(test_list, 'r') as f:
            lines = [line.strip() for line in f]
            np.random.shuffle(lines)
            lines = lines[int(len(lines)*DATA_TRAIN):]
            for line in lines:
                #图像的路径和标签是以\t来分割的,所以我们在生成这个列表的时候,使用\t就可以了
                img_path, lab = line.strip().split('\t')
                yield img_path, int(lab)

    return paddle.reader.xmap_readers(testMapper, reader, cpu_count(), buffered_size)

在这里呢,我们就讲解一下怎么处理数据的:

img = paddle.dataset.image.simple_transform(
        im=img, 
        resize_size=IMG_H, 
        crop_size=IMG_W, 
        is_color=True, 
        is_train=True  # 在1.x版本中此参数可能不触发翻转
    )
    img = img.flatten().astype('float32') / 255.0

通过simple_transform 这个类成员函数进行处理数据:
resize_size=IMG_H 这里代表的是缩放,就比如:原本3232的照片 缩放成1616。
crop_size=IMG_W 这里代表着中心裁剪
is_color = True 这里就是代表有颜色额图片
is_train=True 如果是True的话,这里的函数有可能对图片进行随机裁剪(增强数据)
最后img = img.flatten().astype(‘float32’) / 255.0 代表这归一化 按(0,1)处理

而在测试的时候is_train=False 这就是不采用随机裁剪


创建数据集Reader

def createDataReader(BatchSize = 128):
    global BUFFER_SIZE
    # 把图片数据生成reader
    trainer_reader = trainReader(train_list = os.path.join(dataRootPath,"train.list"))
    train_reader = paddle.batch(
        paddle.reader.shuffle(reader=trainer_reader, buf_size=BUFFER_SIZE),
        batch_size=BatchSize)
    
    tester_reader = testReader(test_list = os.path.join(dataRootPath,"test.list"))
    test_reader = paddle.batch(tester_reader, batch_size=BatchSize)
    print('train_reader, test_reader创建完成!')
    return train_reader, test_reader

这里不做解释

2 网络构建

CNN版本–

def convolutional_neural_network(image, classNumber):
    # 第一个卷积-池化层 #修改第一步就是先是卷积池化->bn->激活   查找论文中 可以得出BN在激活前:在激活函数前进行 BN,能更有效地约束激活前的输入分布,
    #使网络更容易学习到合适的参数,ReLU 对负值输入会直接截断(输出0),如果激活后再 BN,可能丢失部分信息;而先 BN 再激活,可以确保激活函数的输入是归一化的正值,梯度更稳定。
    #论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》建议将 BN 放在激活前,以最大化其规范化效果。
    conv_pool_1 = fluid.nets.simple_img_conv_pool(
        input=image,         # 输入图像
        filter_size=5,       # 滤波器的大小
        num_filters=20,      # filter 的数量
        pool_size=2,         # 池化核大小
        pool_stride=2,       # 池化步长
        act=None             # 先不激活
    )
    conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)  # BN
    conv_pool_1 = fluid.layers.relu(conv_pool_1)        # 激活

    # 第二个卷积-池化层
    conv_pool_2 = fluid.nets.simple_img_conv_pool(
        input=conv_pool_1,
        filter_size=5,
        num_filters=50,
        pool_size=2,
        pool_stride=2,
        act=None
    )
    conv_pool_2 = fluid.layers.batch_norm(conv_pool_2)  # BN
    conv_pool_2 = fluid.layers.relu(conv_pool_2)        # 激活

    # 第三个卷积-池化层
    conv_pool_3 = fluid.nets.simple_img_conv_pool(
        input=conv_pool_2,
        filter_size=5,
        num_filters=50,
        pool_size=2,
        pool_stride=2,
        act=None
    )
    conv_pool_3 = fluid.layers.batch_norm(conv_pool_3)  # BN
    conv_pool_3 = fluid.layers.relu(conv_pool_3)        # 激活

    # 全连接输出层
    prediction = fluid.layers.fc(input=conv_pool_3, size=classNumber, act='softmax')
    print('神经网络创建完成!')
    return prediction

~卷积层:nn.Conv2d用于提取特征,参数包括输入通道数、输出通道数、卷积核大小等。

​~池化层:nn.MaxPool2d用于降维,通常使用最大池化。

~​全连接层:nn.Linear用于将特征映射到最终输出空间。

​~激活函数:F.relu是ReLU激活函数,增加非线性。

​~展平操作:x.view将特征图展平为一维向量,以便输入全连接层。

这里就需要我们进行深度学习了!!!

如果是网络构建的话选择CNN就OK了!!!

DeepID 人脸识别网络结构

def DeepID(images, classNumber):
    # 第一个卷积-池化层
    conv_pool_1 = fluid.nets.simple_img_conv_pool(
        input=images,
        filter_size=5,
        num_filters=32,
        pool_size=2,
        pool_stride=2,
        act=None             # 先不激活
    )
    conv_pool_1 = fluid.layers.batch_norm(conv_pool_1)  # BN
    conv_pool_1 = fluid.layers.relu(conv_pool_1)        # 激活

    # 第二个卷积-池化层
    conv_pool_2 = fluid.nets.simple_img_conv_pool(
        input=conv_pool_1,
        filter_size=3,
        num_filters=40,
        pool_size=2,
        pool_stride=1,
        act=None
    )
    conv_pool_2 = fluid.layers.batch_norm(conv_pool_2)  # BN
    conv_pool_2 = fluid.layers.relu(conv_pool_2)        # 激活

    # 第三个卷积-池化层
    conv_pool_3 = fluid.nets.simple_img_conv_pool(
        input=conv_pool_2,
        filter_size=3,
        num_filters=60,
        pool_size=2,
        pool_stride=2,
        act=None
    )
    conv_pool_3 = fluid.layers.batch_norm(conv_pool_3)  # BN
    conv_pool_3 = fluid.layers.relu(conv_pool_3)        # 激活

    # 全连接层 fc160_1
    fc160_1 = fluid.layers.fc(input=conv_pool_3, size=160)

    # 第四个卷积层(无池化)
    conv_4 = fluid.layers.conv2d(
        input=conv_pool_3,
        num_filters=128,
        filter_size=3,
        act=None             # 先不激活
    )
    conv_4 = fluid.layers.batch_norm(conv_4)  # BN
    conv_4 = fluid.layers.relu(conv_4)        # 激活

    # 全连接层 fc160_2
    fc160_2 = fluid.layers.fc(input=conv_4, size=160)

    # 合并全连接层
    fc160 = fluid.layers.elementwise_add(fc160_1, fc160_2, act="relu")  # 必须保留
    # 全连接输出层
    prediction = fluid.layers.fc(input=fc160, size=classNumber, act='softmax')
    return prediction

卷积层:提取人脸的局部特征。
​池化层:降维并增强特征的鲁棒性。
​全连接层:将高维特征映射到类别空间。
​最后一层无激活函数:输出的是人脸特征向量,而不是直接的分类结果。

DeepID 与 CNN 网络结构的差异

特性CNN​DeepID
​网络结构通用结构,适用于多种视觉任务专为人脸识别设计,提取高维特征
​输入数据通用图像数据(如分类任务中的物体图像)固定大小的人脸图像(如 112x112 或 128x128)
​输出类别概率(分类任务)高维特征向量(用于相似性计算)
训练目标最大化正确类别的概率(交叉熵损失等)学习高维、判别性强的人脸特征,结合联合贝叶斯模型优化特征空间
后续处理直接输出结果(如分类标签)特征向量用于人脸验证或聚类,通常结合联合贝叶斯模型
​应用场景图像分类、目标检测、语义分割等人脸识别、人脸验证、人脸聚类等

总结:
​CNN 是一种通用框架,适用于多种视觉任务,网络结构和训练目标灵活多样。
​DeepID 是一种专用网络,专注于人脸识别任务,通过网络提取高维特征并结合联合贝叶斯模型优化特征空间。

3 深度学习模型训练和推理的核心设置

def setPredictor(learning_rate =0.0001):
    
    image = fluid.layers.data(name='image', shape=[3, IMG_H, IMG_W], dtype='float32')#接收形状为 【3,H,W】 的输入图像
    label = fluid.layers.data(name='label', shape=[1], dtype='int64')#接收形状为 1 的标签
    
    # 建立网络
    # predict =  convolutional_neural_network(image, classNumber)
    predict = DeepID(image, classNumber)
    
    
    ###################################################################################
    
    # 获取损失函数和准确率
    cost = fluid.layers.cross_entropy(input=predict, label=label) # 交叉熵
    avg_cost = fluid.layers.mean(cost)                            # 计算cost中所有元素的平均值
    acc = fluid.layers.accuracy(input=predict, label=label)       #使用输入和标签计算准确率
    
    # 定义优化方法
    # optimizer =fluid.optimizer.Adam(learning_rate=learning_rate)
    # optimizer.minimize(avg_cost)
    # 定义带动量的 SGD 优化器
    optimizer = fluid.optimizer.Momentum(
        learning_rate=learning_rate,
        momentum=0.9,
        regularization=fluid.regularizer.L2Decay(regularization_coeff=1e-4)
    )
    optimizer.minimize(avg_cost)
    


   
    # 定义优化器时添加梯度裁剪    正则:原le-4  改成 le-3
    # optimizer = fluid.optimizer.Momentum(
    #     learning_rate=learning_rate,
    #     momentum=0.9,
    #     regularization=fluid.regularizer.L2Decay(1e-4),
    #     grad_clip=fluid.clip.GradientClipByGlobalNorm(clip_norm=5.0)  # 全局范数裁剪
    # )
    # optimizer.minimize(avg_cost)
    
    # 定义使用CPU还是GPU,使用CPU时USE_CUDA = False,使用GPU时USE_CUDA = True
    place = fluid.CUDAPlace(0) if USE_CUDA else fluid.CPUPlace()
    
    # 创建执行器,初始化参数
    exe = fluid.Executor(place) #就像请一个厨师(执行器)按照菜谱(模型结构)在指定的厨房(CPU或GPU)里做菜。
    exe.run(fluid.default_startup_program()) #运行模型的“启动程序”,初始化所有参数(比如神经网络的权重和偏置)
    feeder = fluid.DataFeeder( feed_list=[image, label],place=place)#创建一个“数据喂入器”,负责将输入数据(如图像和标签)传递给模型
    
    # 获取测试程序
    test_program = fluid.default_main_program().clone(for_test=True)
    
    return image, label, predict, avg_cost, acc, exe, feeder, test_program
    ################################################################################### 

image, label, predict, avg_cost, acc, exe, feeder, test_program = setPredictor(LEARNING_RATE)

4 制图

import matplotlib.pyplot as plt
import numpy as np


def draw_figure(dictCostAccdictCostAcc, xlabel, ylabel_1, ylabel_2):
    plt.xlabel(xlabel, fontsize=20)
    plt.plot(dictCostAcc[xlabel], dictCostAcc[ylabel_1],color='red',label=ylabel_1) 
    plt.plot(dictCostAcc[xlabel], dictCostAcc[ylabel_2],color='green',label=ylabel_2) 
    plt.legend()
    plt.grid()

def draw_train_process(epoch, dictCostAcc):
    # train的cost与accuray的变化
    plt.figure(figsize=(10, 3))
    plt.title('epoch - ' + str(epoch), fontsize=24)
    plt.subplot(1,3,1)
    draw_figure(dictCostAcc, 'iteration', 'iter_cost', 'iter_acc')
    plt.subplot(1,3,2)
    draw_figure(dictCostAcc, 'epoch', 'cost_train', 'cost_test')
    plt.subplot(1,3,3)
    draw_figure(dictCostAcc, 'epoch', 'acc_train', 'acc_test')
    plt.show()

draw_figure() 功能:
绘制单个子图,展示两个指标的变化曲线。
通过 dictCostAcc 提供的数据绘制曲线,dictCostAcc 是一个字典,包含训练过程中的各种指标(如损失和准确率)

参数:

dictCostAcc
一个字典,包含训练过程中的数据。
键是指标名称(如 ‘iteration’、‘iter_cost’ 等),值是对应的数值列表

xlabel
X 轴的标签(如 ‘iteration’ 或 ‘epoch’)。
两个 Y 轴的指标名称(如 ‘iter_cost’ 和 ‘iter_acc’)。

draw_train_process()
功能
绘制整个训练过程的指标变化,包括:
每个迭代的损失和准确率。
每个 epoch 的训练和测试损失。
每个 epoch 的训练和测试准确率。
使用 matplotlib 的子图功能,将三个指标绘制在同一张图中。

5 训练

all_train_iter=0
all_train_iters=[]
all_train_costs=[]
all_train_accs=[]
# 记录迭代过程中,每一个epoch的平均cost与accuracy
epoch_train_costs = []
epoch_test_costs = []
epoch_train_accs = []
epoch_test_accs = []

train_reader, test_reader = createDataReader(BATCH_SIZE)

if not os.path.exists(model_save_dir):
    os.makedirs(model_save_dir)

print('开始训练...')
for pass_id in range(TRAIN_NUM):
    # train_reader, test_reader = createDataReader(BATCH_SIZE)
    print("epoch %d -------------" % pass_id)
    train_accs = []                                                          #训练的损失值
    train_costs = []                                                          #训练的准确率
    for batch_id, data in enumerate(train_reader()):                         #遍历train_reader的迭代器,并为数据加上索引batch_id
        train_cost, train_acc = exe.run(
            program=fluid.default_main_program(),                            #运行主程序
            feed=feeder.feed(data),                                          #喂入一个batch的数据
            fetch_list=[avg_cost, acc])                                      #fetch均方误差和准确率

        all_train_iter=all_train_iter+BATCH_SIZE
        all_train_iters.append(all_train_iter)
        all_train_costs.append(train_cost[0])
        all_train_accs.append(train_acc[0])
        train_costs.append(train_cost[0])
        train_accs.append(train_acc[0])
        if batch_id % 50 == 0:                                               #每10次batch打印一次训练、进行一次测试
            print("\tPass %d, Step %d, Cost %f, Acc %f" % 
            (pass_id, batch_id, train_cost[0], train_acc[0]))
    
    epoch_train_costs.append(sum(train_costs) / len(train_costs))            #每个epoch的cost
    epoch_train_accs.append(sum(train_accs)/len(train_accs))                 #每个epoch的acc
    print('\t\tTrain:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, epoch_train_costs[-1], epoch_train_accs[-1]))
    
    # 开始测试
    test_accs = []                                                            #测试的损失值
    test_costs = []                                                           #测试的准确率
    # 每训练一轮 进行一次测试
    
    for batch_id, data in enumerate(test_reader()):                           # 遍历test_reader
         test_cost, test_acc = exe.run(program=test_program,  # #运行测试主程序
                                       feed=feeder.feed(data),                #喂入一个batch的数据
                                       fetch_list=[avg_cost, acc])            #fetch均方误差、准确率
         test_accs.append(test_acc[0])                                        #记录每个batch的误差
         test_costs.append(test_cost[0])                                      #记录每个batch的准确率

    epoch_test_costs.append(sum(test_costs) / len(test_costs))
    epoch_test_accs.append(sum(test_accs) / len(test_accs))
    print('\t\tTest:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, epoch_test_costs[-1], epoch_test_accs[-1]))
    if pass_id < 3:
        continue
    else:
        dictCostAcc = {}
        dictCostAcc['iteration'] = all_train_iters
        dictCostAcc['iter_cost'] = all_train_costs
        dictCostAcc['iter_acc'] = all_train_accs
        dictCostAcc['epoch'] = list(range(pass_id+1))
        dictCostAcc['cost_train'] = epoch_train_costs
        dictCostAcc['cost_test'] = epoch_test_costs
        dictCostAcc['acc_train'] = epoch_train_accs
        dictCostAcc['acc_test'] = epoch_test_accs
        draw_train_process(pass_id, dictCostAcc)
    # draw_train_process("training",all_train_iters,all_train_costs,all_train_accs,"trainning cost","trainning acc","iter",'cost/acc')

    print('\n')
    
    if pass_id % 5 == 0:
        # 每5个epoch保存一个模型
        model_dir = os.path.join(model_save_dir,str(pass_id))
        if not os.path.exists(model_dir):
            os.makedirs(model_dir)
        fluid.io.save_inference_model(model_dir, ['image'], [predict], exe)
        print('第%d个epoch的训练模型保存完成!'%pass_id)

1 变量初始化

all_train_iter = 0
all_train_iters = []
all_train_costs = []
all_train_accs = []
epoch_train_costs = []
epoch_test_costs = []
epoch_train_accs = []
epoch_test_accs = []

​功能:初始化变量,用于记录训练和测试过程中的损失和准确率。

​变量说明:
all_train_iter:记录总的迭代次数。
all_train_iters:记录所有训练迭代的索引(累积的 batch 数)。
all_train_costs:记录每个 batch 的训练损失。
all_train_accs:记录每个 batch 的训练准确率。
epoch_train_costs:记录每个 epoch 的平均训练损失。
epoch_test_costs:记录每个 epoch 的平均测试损失。
epoch_train_accs:记录每个 epoch 的平均训练准确率。
epoch_test_accs:记录每个 epoch 的平均测试准确率。

2 数据读取器

train_reader, test_reader = createDataReader(BATCH_SIZE)

功能:创建训练和测试数据读取器。

createDataReader(BATCH_SIZE)
自定义函数,返回训练和测试数据的迭代器。
​BATCH_SIZE:每个 batch 的样本数量。

3. 模型保存目录

if not os.path.exists(model_save_dir):
    os.makedirs(model_save_dir)

​功能:检查模型保存目录是否存在,如果不存在则创建。 ​model_save_dir:模型保存的路径。

4. 训练过程

for pass_id in range(TRAIN_NUM):
    print("epoch %d -------------" % pass_id)
    train_accs = []
    train_costs = []

功能:开始训练过程,TRAIN_NUM 是总的训练轮数(epoch)。

变量说明:
pass_id:当前是第几个 epoch。
train_accs:记录当前 epoch 的所有 batch 的训练准确率。
train_costs:记录当前 epoch 的所有 batch 的训练损失。

4.1 遍历训练数据

for batch_id, data in enumerate(train_reader()):
    train_cost, train_acc = exe.run(
        program=fluid.default_main_program(),
        feed=feeder.feed(data),
        fetch_list=[avg_cost, acc]
    )

功能:遍历训练数据读取器,逐 batch 进行训练。

train_reader()
返回训练数据的迭代器,每次迭代返回一个 batch 的数据。
exe.run
运行主程序,执行前向传播和反向传播。
program=fluid.default_main_program():指定运行的程序。
feed=feeder.feed(data):将当前 batch 的数据喂入模型。
fetch_list=[avg_cost, acc]:获取训练的损失和准确率。
train_cost 和 train_acc
train_cost:当前 batch 的训练损失。
train_acc:当前 batch 的训练准确率。

4.2 记录训练数据

all_train_iter += BATCH_SIZE
all_train_iters.append(all_train_iter)
all_train_costs.append(train_cost[0])
all_train_accs.append(train_acc[0])
train_costs.append(train_cost[0])
train_accs.append(train_acc[0])

功能:记录每个 batch 的训练数据。

all_train_iter:累积迭代次数,每次增加一个 batch 的样本数(BATCH_SIZE)。
all_train_iters:记录总的迭代次数。
all_train_costs 和 all_train_accs:记录所有 batch 的训练损失和准确率。
train_costs 和 train_accs:记录当前 epoch 的所有 batch 的训练损失和准确率。

4.3 打印训练信息

if batch_id % 50 == 0:
    print("\tPass %d, Step %d, Cost %f, Acc %f" % 
          (pass_id, batch_id, train_cost[0], train_acc[0]))

​功能:每 50 个 batch 打印一次训练信息。

​打印内容:
当前 epoch (pass_id)。
当前 batch 的索引 (batch_id)。
当前 batch 的训练损失 (train_cost[0])。
当前 batch 的训练准确率 (train_acc[0])。

4.4 计算并记录每个 epoch 的平均损失和准确率

epoch_train_costs.append(sum(train_costs) / len(train_costs))
epoch_train_accs.append(sum(train_accs) / len(train_accs))

​功能:计算当前 epoch 的平均训练损失和准确率,并记录下来。

5. 测试过程

for batch_id, data in enumerate(test_reader()):
    test_cost, test_acc = exe.run(
        program=test_program,
        feed=feeder.feed(data),
        fetch_list=[avg_cost, acc]
    )
    test_accs.append(test_acc[0])
    test_costs.append(test_cost[0])

功能:遍历测试数据读取器,逐 batch 进行测试。

test_reader():返回测试数据的迭代器,每次迭代返回一个 batch 的数据。
exe.run:运行测试程序,获取测试的损失和准确率。
test_cost 和 test_acc:当前 batch 的测试损失和准确率。
test_costs 和 test_accs:记录所有 batch 的测试损失和准确率。

5.1 计算并记录每个 epoch 的平均测试损失和准确率

epoch_test_costs.append(sum(test_costs) / len(test_costs))
epoch_test_accs.append(sum(test_accs) / len(test_accs))

功能:计算当前 epoch 的平均测试损失和准确率,并记录下来。

6 打印训练和测试信息

print('\t\tTrain:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, epoch_train_costs[-1], epoch_train_accs[-1]))
print('\t\tTest:%d, Cost:%0.5f, ACC:%0.5f' % (pass_id, epoch_test_costs[-1], epoch_test_accs[-1]))

​功能:打印当前 epoch 的平均训练损失和准确率,以及测试损失和准确率。

7. 可视化训练过程

if pass_id < 3:
    continue
else:
    dictCostAcc = {
        'iteration': all_train_iters,
        'iter_cost': all_train_costs,
        'iter_acc': all_train_accs,
        'epoch': list(range(pass_id+1)),
        'cost_train': epoch_train_costs,
        'cost_test': epoch_test_costs,
        'acc_train': epoch_train_accs,
        'acc_test': epoch_test_accs
    }
    draw_train_process(pass_id, dictCostAcc)

​功能:从第 4 个 epoch 开始,定期可视化训练过程。

dictCostAcc
构造一个字典,包含训练和测试的所有数据。
​键值对:
‘iteration’:总的迭代次数。
‘iter_cost’:每个 batch 的训练损失。
‘iter_acc’:每个 batch 的训练准确率。
‘epoch’:当前的 epoch 数。
‘cost_train’:每个 epoch 的平均训练损失。
‘cost_test’:每个 epoch 的平均测试损失。
‘acc_train’:每个 epoch 的平均训练准确率。
‘acc_test’:每个 epoch 的平均测试准确率。

2.draw_train_process
调用可视化函数,绘制训练过程的曲线。

8. 定期保存模型

if pass_id % 5 == 0:
    model_dir = os.path.join(model_save_dir, str(pass_id))
    if not os.path.exists(model_dir):
        os.makedirs(model_dir)
    fluid.io.save_inference_model(model_dir, ['image'], [predict], exe)
    print('第%d个epoch的训练模型保存完成!' % pass_id)

​功能:每 5 个 epoch 保存一次模型。

model_dir
模型保存的路径,包含当前 epoch 的编号。
fluid.io.save_inference_model
保存推理模型,供后续推理使用。
​参数:
model_dir:模型保存的路径。
[‘image’]:输入数据的名称。
[predict]:需要保存的模型变量。
exe:执行器。
​打印信息:
打印模型保存完成的提示。

6 预测+结果

def createInfer():
    global USE_CUDA
    place = fluid.CUDAPlace(0) if USE_CUDA else fluid.CPUPlace()
    infer_exe = fluid.Executor(place)
    inference_scope = fluid.core.Scope()
    return infer_exe, inference_scope
        
def load_image(path):
    global IMG_H, IMG_W
    img = paddle.dataset.image.load_and_transform(path,IMG_H,IMG_W, False).astype('float32')
    img = img / 255.0 
    img = np.expand_dims(img, axis=0)
    return img

def getClassList(path):
    with open(path,'r') as load_f:
        new_dict = json.load(load_f)
        return new_dict['all_class_name']
        
def predImgs(pathImgList, optimalEpoch):
    pred_label_list = []
    pred_class_list = []
    modelpath = os.path.join(model_save_dir, str(optimalEpoch))
    for pathImg in pathImgList:
        infer_exe, inference_scope = createInfer()
        with fluid.scope_guard(inference_scope):
            #从指定目录中加载 推理model(inference model)
            [inference_program, # 预测用的program
             feed_target_names, # 是一个str列表,它包含需要在推理 Program 中提供数据的变量的名称。 
             fetch_targets] = fluid.io.load_inference_model(modelpath,#fetch_targets:是一个 Variable 列表,从中我们可以得到推断结果。
                                                            infer_exe)     #infer_exe: 运行 inference model的 executor
            
            img = Image.open(pathImg)
            plt.imshow(img)   
            plt.show()    
            
            img = load_image(pathImg)
        
            results = infer_exe.run(inference_program,                 #运行预测程序
                                    feed={feed_target_names[0]: img},  #喂入要预测的img
                                    fetch_list=fetch_targets)          #得到推测结果
            # print('results',results)
            # print('results[0]',np.argmax(results[0]))
            label_list = getClassList(os.path.join(dataRootPath,'readme.json'))
            pred_label = np.argmax(results[0])
            pred_class = label_list[np.argmax(results[0])]
            print("infer results: %s" % label_list[np.argmax(results[0])])
            pred_label_list.append(pred_label)
            pred_class_list.append(pred_class)
    return pred_label_list, pred_class_list



pathcat = 'data/test/100.jpg'
pathdog = 'data/test/1000.jpg'
predImgList = ['data/test/'+str(x)+'.jpg' for x in range(1,29)]
predImgs(predImgList,95)

训练时候的图像:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

好啦基本的就介绍到这里啦!!!

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

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

相关文章

使用高德api实现天气查询

创建应用获取 Key 天气查询-基础 API 文档-开发指南-Web服务 API | 高德地图API 代码编写 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wid…

Could not find artifact com.microsoft.sqlserver:sqljdbc4:jar:4.0 in central

具体错误 [ERROR] Failed to execute goal on project datalink-resource: Could not resolve dependencies for project com.leon.datalink:datalink-resource:jar:1.0.0: Could not find artifact com.microsoft.sqlserver:sqljdbc4:jar:4.0 in central (https://repo.maven…

Express学习笔记(三)——使用 Express 写接口

目录 1. 创建基本的服务器 2. 创建 API 路由模块 3. 编写 GET 接口 4. 编写 POST 接口 5. CORS 跨域资源共享 5.1 接口的跨域问题 5.2 使用 cors 中间件解决跨域问题 5.3 什么是 CORS 5.4 CORS 的注意事项 5.5 CORS 响应头部 - Access-Control-Allow-Origin 5.6 COR…

【HarmonyOS Next之旅】DevEco Studio使用指南(十)

目录 1 -> Optimize Imports功能 2 -> 父/子类快速跳转 3 -> 查看接口/类的层次结构 4 -> 代码自动补全 1 -> Optimize Imports功能 使用编辑器提供的Optimize Imports&#xff0c;可以快速清除未使用的import&#xff0c;并根据设置的规则对import进行合并…

java并发编程-并发容器

并发容器 CopyOnWriteArrayListCopyOnWriteArraySetConcurrentHashMapConcurrentSkipListMap迭代器的fail-fast与fail-safe机制应用场景 CopyOnWriteArrayList 线程不安全容器&#xff1a;ArrayList代替Vector、synchronizedList适用于读多写少的场景&#xff0c;对读操作不加…

PPT助手:一款集计时、远程控制与多屏切换于一身的PPT辅助工具

PPT助手&#xff1a;一款集计时、远程控制与多屏切换于一身的PPT辅助工具 &#x1f4dd;&#x1f3a4; 在现代化的演讲和演示中&#xff0c;如何高效地控制PPT进程、保证展示的流畅性与精准性&#xff0c;成为了每个演讲者必须面对的挑战。无论是商务汇报、学术演讲&#xff0…

大模型应用初学指南

随着人工智能技术的快速发展&#xff0c;检索增强生成&#xff08;RAG&#xff09;作为一种结合检索与生成的创新技术&#xff0c;正在重新定义信息检索的方式&#xff0c;RAG 的核心原理及其在实际应用中的挑战与解决方案&#xff0c;通用大模型在知识局限性、幻觉问题和数据安…

如何通过管理系统提升团队协作效率

在现代企业管理中&#xff0c;团队协作效率的高低直接关系到企业的竞争力和运营效率。随着信息技术的不断发展&#xff0c;管理系统作为提升团队协作效率的重要工具&#xff0c;逐渐受到企业的重视。本文将深入探讨如何通过管理系统提升团队协作效率&#xff0c;为企业提供实用…

云手机如何防止设备指纹被篡改

云手机如何防止设备指纹被篡改 云手机作为虚拟化设备&#xff0c;其设备指纹的防篡改能力直接关系到账户安全、反欺诈和隐私保护。以下以亚矩阵云手机为例&#xff0c;讲解云手机防止设备指纹被篡改的核心技术及实现方式&#xff1a; 系统层加固&#xff1a;硬件级安全防护 1…

XT1870 同步升压 DC-DC 变换器

1、 产品概述 XT1870 系列产品是一款低功耗、高效率、低纹波、工 作频率高的 PFM 控制升压 DC-DC 变换器。 XT1870 系列产品仅需要 3 个外部元器 , 即可完成低输 入的电池电压输入。 2、用途 数码相机、电子词典 LED 手电筒、 LED 灯 血压计、MP3 、遥控玩具 …

seaweedfs分布式文件系统

seaweedfs https://github.com/seaweedfs/seaweedfs.git go mod tidy go -o bin ./… seaweed占不支持smb服务&#xff0c;只能用fuse的方式mount到本地文件系统 weed master 默认端口&#xff1a;9333&#xff0c;支持浏览器访问 weed volume 默认端口&#xff1a;8080 weed …

Spring Boot后端开发全攻略:核心概念与实战指南

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、全栈领域优质创作者、高级开发工程师、高级信息系统项目管理师、系统架构师&#xff0c;数学与应用数学专业&#xff0c;10年以上多种混合语言开发经验&#xff0c;从事DICOM医学影像开发领域多年&#xff0c;熟悉DICOM协议及…

通过 Markdown 改进 RAG 文档处理

通过 Markdown 改进 RAG 文档处理 作者&#xff1a;Tableau 原文地址&#xff1a;https://zhuanlan.zhihu.com/p/29139791931 通过 Markdown 改进 RAG 文档处理https://mp.weixin.qq.com/s/LOBOKNA71dANXHuwxe7yxw 如何将 PDF 转换为 Markdown 以获得更好的 LLM RAG 结果 Mar…

高速电路 PCB 设计要点一

3 高速电路 PCB 设计要点 3.1 PCB设计与信号完整性 随着电子技术的发展&#xff0c;电路的规模越来越大&#xff0c;单个器件集成的功能越来越多&#xff0c;速率越来越高&#xff0c;而器件的尺寸越来越小。由于器件尺寸的减小&#xff0c;器件引脚信号变化沿的速率变得越来…

【Centos】centos7内核升级-亲测有效

相关资源 通过网盘分享的文件&#xff1a;脚本升级 链接: https://pan.baidu.com/s/1yrCnflT-xWhAPVQRx8_YUg?pwd52xy 提取码: 52xy –来自百度网盘超级会员v5的分享 使用教程 将脚本文件上传到服务器的一个目录 执行更新命令 yum install -y linux-firmware执行脚本即可 …

Opencv计算机视觉编程攻略-第八节 检测兴趣点

目录 1.检测图像中的角点 2.快速检测特征 3.尺度不变特征的检测 4.多尺度FAST 特征的检测 在计算机视觉领域&#xff0c;兴趣点&#xff08;也称关键点或特征点&#xff09;应用包括目标识别、图像配准、视觉跟踪、三维重建等。这个概念的原理是&#xff0c;从图像中选取某…

基于微信小程序的医院挂号预约系统设计与实现

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本微信小程序医院挂号预约系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大…

如何保障话费api接口的稳定性?

保障话费接口的稳定性是确保服务高效运行的关键。以下是基于最新信息的建议&#xff1a; 1. 选择可靠的API服务提供商 信誉和稳定性&#xff1a;选择有良好声誉和稳定服务记录的提供商&#xff0c;查看其服务水平协议&#xff08;SLA&#xff09;以确保高可用性。技术支持&…

video标签播放mp4格式视频只有声音没有图像的问题

video标签播放mp4格式视频只有声音没有图像的问题 这是由于视频格式是hevc(H265)编码的&#xff0c;这种编码格式视频video播放有问题主要是由于以下两种原因导致的&#xff1a; 1、浏览器没有开启硬加速模式&#xff1a; 开启方法&#xff08;以谷歌浏览器为例&#xff09;&a…

解决docker部署的容器第二天访问报错139的问题

前阵子我部署项目&#xff0c;把数据库放宿主机上&#xff0c;结果电脑一重启&#xff0c;Docker 直接把数据库删了个精光&#xff01;我当时的表情 be like &#x1f631;&#xff1a;"我的数据呢&#xff1f;&#xff1f;&#xff1f;" 连备份都没来得及做&#xf…