YOLO-V5分类实战系列 —— 调优自己的数据集+RK1808部署

news2024/11/26 18:50:24

YOLO-V5分类实战系列 —— 调优自己的数据集

    • 1、保存训练和测试图片
    • 2、数据归一化
    • 3、数据增强
      • 3.1、数据增强库:albumentations
      • 3.2、数据增强库:torchvision
    • 4、ONNX CPU 推理
      • 4.1、Pt 模型转为 ONNX
      • 4.2、ONNX 推理验证
      • 4.3、 ONNX CPU推理(C++)
    • 5、RK 1808 部署
      • 5.1、查看模型输入、输出名字
      • 5.2、转换为RKNN模型
      • 5.3、C++ 芯片部署
    • 6、调优策略
      • 6.1、数据标注
      • 6.2、数据清洗
      • 6.3、网络调优


1、保存训练和测试图片

问题1:训练过程中,如何保证训练集的数据增强和验证集的数据增强是基本一致的?
问题2:训练过程中,如何保证训练集和验证集的标签是正确的?

解决方法:训练测试过程中,保存训练图片和测试图片,并将标签画到图片可视化。

训练过程中,需要保证训练的数据预处理和测试的数据预处理保持一致,这一步还是很重要的,需要优先验证其一致性。常见的【预处理】包括:图片尺寸调整(resize,crop),色彩增强,空间变换(翻转,旋转等),归一化等。

  • 下面的代码片段是数据读取的核心类,包括数据读取和数据增强。代码位置: 【utils/dataloaders.py】,并且需要在 def __init__() 中需要添加 self.augment = augment,保存图像的调用位置 def __getitem__() 中添加 self.save_images(f, sample),下面代码都有添加(红色框内的代码)

在这里插入图片描述

  • 训练和测试图片保存的代码片段如下,将其添加到类 ClassificationDataset 的成员函数即可。通过self.augment 判定是训练图片还是测试图片,进而对图片名字进行命名。保存的路径自己可以设置,

    def save_images(self, f, sample):
        # 保存数据增强后的图片,用于对比训练和验证的图片预处理是否一致
        print('   -------------self.augment: ', self.augment)
        if self.augment:
            print('   -------------f: ', f)
            a = f.split('/train/')[0]+'/train_aug/'+os.path.basename(f)[:-4]+'_train.jpg'
            print('   -------------a: ', a)
            torchvision.utils.save_image(sample, a)
        else:
            print('   -------------f: ', f)
            a = f.split('/test/')[0]+'/train_aug/'+os.path.basename(f)[:-4]+'_test.jpg'
            print('   -------------a: ', a)
            torchvision.utils.save_image(sample, a)
    

    训练过程中,测试和训练预处理保存的图片如下,注意训练集和测试集的图片用同样的图片,这样更方便对比,
    在这里插入图片描述


2、数据归一化

代码中默认的是【ImageNet】数据集的均值和标准差,具体如下

IMAGENET_MEAN = 0.485, 0.456, 0.406  # RGB mean
IMAGENET_STD = 0.229, 0.224, 0.225   # RGB standard deviation

当使用自己的数据集的时候,通常根据数据集是否与ImageNet差异性大小,决定是否重新计算均值和标准差。针对我自己的数据集,需要重新计算,代码如下

def compute_mean_std():
	# 将所有的【路径+图片】读取到列表 all_file 
    images_path = 'datasets/biaozhu_train/train/' # 针对自己的路径,需要修改
    all_file = []
    for i,j,k in os.walk(images_path):
        if len(k)>0:
            print('i: ', i)
            print('j: ', j)
            all_file.append([os.path.join(i,kk) for kk in k])
            print('------------------')
    
    # 统计所有图片的数量
    num_images = len(all_file[0]) + len(all_file[1]) + len(all_file[2])
    
    # 逐张图计算均值和方差
    mean_r = mean_g = mean_b = 0
    std_r = std_g = std_b = 0
    for i in range(3):
        for j in range(len(all_file[i])):
            print('   i: ', i)
            print('   j: ', j)
            img = cv2.imread(all_file[i][j])
            print('img shape: ', img.shape)

            img = np.asarray(img)
            img = img.astype(np.float32) / 255.

            # cv2
            im_mean, im_stds = cv2.meanStdDev(img)
            mean_r += im_mean[0]
            mean_g += im_mean[1]
            mean_b += im_mean[2]

            std_r += im_stds[0]
            std_g += im_stds[1]
            std_b += im_stds[2]
    
    # 计算平均值
    aa_r = mean_r/num_images
    aa_g = mean_g/num_images
    aa_b = mean_b/num_images

    bb_r = std_r/num_images
    bb_g = std_g/num_images
    bb_b = std_b/num_images

    print(' last mean: ', aa_r,aa_g,aa_b)
    print(' last stds: ', bb_r,bb_g,bb_b)

计算结果如下,与【ImageNet】数据集的均值有差别

IMAGENET_MEAN = 0.3539317, 0.36820036, 0.37243055
IMAGENET_STD = 0.22637626, 0.22498632, 0.22197094

值得注意:使用自己计算的均值和标准差,训练过程更加稳定,波动较小


3、数据增强

官方源代码训练过程中,训练流程和验证流程的数据处理库有两个库(如果开发环境中安装了 albumentations 库),分别是 【albumentations】和【torchvision】,否则默认均使用【torchvision】。下面我们将分别介绍它们:

3.1、数据增强库:albumentations

训练过程中(如果安装了albumentations库),则会执行下图中的代码。如图所示,包括三类的数据增强,也是常用的图像数据增强方法

在这里插入图片描述


下面我们分别介绍上述的几种数据增强方式:

  • 随机裁剪

    A.RandomResizedCrop(height=size, width=size, scale=scale, ratio=ratio)
    

    函数功能:先对图像进行裁剪,然后resize到固定的尺寸
    height:处理完后,最终图像的高度;
    width:处理完后,最终图像的宽度;
    scale:裁剪图占整张图区域面积(h*w)的比例是多少;
    ratio:裁剪区域的宽高比;

    该函数的部分核心源码如下所示,主要计算待裁剪的【区域大小】和【裁剪的起始位置】,具体如下以及部分注释:

    def get_params_dependent_on_targets(self, params):
    	img = params["image"]
    	area = img.shape[0] * img.shape[1]
    	
    	# 当【if】条件不满足时,尝试10次
    	for _attempt in range(10):
    		# 根据scale和ratio参数,得到随机的参数,进而计算裁剪图的大小(w,h)
    	    target_area = random.uniform(*self.scale) * area
    	    log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1]))
    	    aspect_ratio = math.exp(random.uniform(*log_ratio))
    	
    	    w = int(round(math.sqrt(target_area * aspect_ratio)))  # skipcq: PTC-W0028
    	    h = int(round(math.sqrt(target_area / aspect_ratio)))  # skipcq: PTC-W0028
    		
    		# 计算裁剪区域在原图的起始点
    	    if 0 < w <= img.shape[1] and 0 < h <= img.shape[0]:
    	    	i = random.randint(0, img.shape[0] - h)
    	       	j = random.randint(0, img.shape[1] - w)
    			
    			# 进行裁剪+resize操作,底层使用的OpenCV
    	        return {
    	            "crop_height": h,
    	            "crop_width": w,
    	            "h_start": i * 1.0 / (img.shape[0] - h + 1e-10),
    	            "w_start": j * 1.0 / (img.shape[1] - w + 1e-10),
    	        }
    	
    	# 尝试10次后,如果上述的【if】条件依然无法满足,则执行下面的代码进行裁剪
    	# Fallback to central crop
    	in_ratio = img.shape[1] / img.shape[0]
    	if in_ratio < min(self.ratio):
    	    w = img.shape[1]
    	    h = int(round(w / min(self.ratio)))
    	elif in_ratio > max(self.ratio):
    	    h = img.shape[0]
    	    w = int(round(h * max(self.ratio)))
    	else:  # whole image
    	    w = img.shape[1]
    	    h = img.shape[0]
    	i = (img.shape[0] - h) // 2
    	j = (img.shape[1] - w) // 2
    	
    	# 进行裁剪+resize操作,底层使用的OpenCV
    	return {
    	    "crop_height": h,
    	    "crop_width": w,
    	    "h_start": i * 1.0 / (img.shape[0] - h + 1e-10),
    	    "w_start": j * 1.0 / (img.shape[1] - w + 1e-10),
    	}
                    
    

    测试原图

    在这里插入图片描述
    处理后的结果如下图所示:下面三张图,分别使用 0.06,0.5,0.9 的 scale 参数得到的裁剪图,最终 resize 到 256,可以看到被裁剪的范围逐渐增大,由于是随机裁剪,所以裁剪后的区域差别较大

    在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • 空间变换

    A.HorizontalFlip(p=hflip) # 左右翻转
    A.VerticalFlip(p=vflip)   # 上下翻转
    

    处理结果如下图所示(左图:左右翻转,右图:上下翻转),

    在这里插入图片描述 在这里插入图片描述

  • 色彩变换

    A.ColorJitter(*color_jitter, 0) # 随机改变图像的亮度,对比度,饱和度
    

    不作介绍

  • 归一化

    A.Normalize(mean=mean, std=std)  # Normalize and convert to Tensor
    

    对于分类模型,通常需要减去均值,除以标准差,得到如下图所示:

    在这里插入图片描述

  • 调整输入排列

    ToTensorV2()
    

    函数功能:(1)HWC 转为 CHW;(2)numpy 转为 tensor

    函数的核心源码如下:

    def apply(self, img, **params):  # skipcq: PYL-W0613
        if len(img.shape) not in [2, 3]:
            raise ValueError("Albumentations only supports images in HW or HWC format")
    
        if len(img.shape) == 2:
            img = np.expand_dims(img, 2)
    
        return torch.from_numpy(img.transpose(2, 0, 1))
    

3.2、数据增强库:torchvision

# 官方的预处理方法
    return T.Compose([CenterCrop(size), ToTensor(), T.Normalize(IMAGENET_MEAN, IMAGENET_STD)])
  • 中心裁剪:CenterCrop(size)
    以最小边的大小裁剪长边的中心区域,核心代码如下

    class CenterCrop:
        # YOLOv5 CenterCrop class for image preprocessing, i.e. T.Compose([CenterCrop(size), ToTensor()])
        def __init__(self, size=640):
            super().__init__()
            self.h, self.w = (size, size) if isinstance(size, int) else size
    
        def __call__(self, im):  # im = np.array HWC
            imh, imw = im.shape[:2]
            m = min(imh, imw)  # min dimension
            top, left = (imh - m) // 2, (imw - m) // 2
            return cv2.resize(im[top:top + m, left:left + m], (self.w, self.h), interpolation=cv2.INTER_LINEAR)
    
  • 调整输入排列
    函数功能:(1)HWC 转为 CHW;(2)BGR 转 RGB;(3)numpy 转为 tensor;
    函数的核心源码如下:

    class ToTensor:
        # YOLOv5 ToTensor class for image preprocessing, i.e. T.Compose([LetterBox(size), ToTensor()])
        def __init__(self, half=False):
            super().__init__()
            self.half = half
    
        def __call__(self, im):  # im = np.array HWC in BGR order
            im = np.ascontiguousarray(im.transpose((2, 0, 1))[::-1])  # HWC to CHW -> BGR to RGB -> contiguous
            im = torch.from_numpy(im)  # to torch
            im = im.half() if self.half else im.float()  # uint8 to fp16/32
    

    T.Normalize(IMAGENET_MEAN, IMAGENET_STD)]:不作介绍


4、ONNX CPU 推理

主要包括三部分内容:

  1. Pt 转为 ONNX 模型;
  2. ONNX 推理验证(Python);
  3. ONNX CPU 推理(C++);

4.1、Pt 模型转为 ONNX

代码位置:yolov5/export.py

# 针对自己的网络输入大小,注意 imgsz 的设置,默认是640
python export.py --weights runs/train-cls/exp24/weights/best.pt --include onnx --imgsz 256

4.2、ONNX 推理验证

代码位置:yolov5/classify/val.py

python classify/val.py --weights best.onnx

4.3、 ONNX CPU推理(C++)

main.cpp

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <onnxruntime_cxx_api.h>
#include "helpers.h"
#include<algorithm>


using namespace cv;
using namespace std;
 
int main(int argc, char* argv[]) {

	if (argc < 4)
	{
		std::cout << ">>>>>>>>>>>>>>>>>>>>>>>>>: Parameters too less" << std::endl;
		return -1;
	}

	std::string class_path = argv[2];
    std::string image_path = argv[3];

    Ort::Env env;
    Ort::Session session(nullptr);
    Ort::SessionOptions sessionOptions{nullptr};

    constexpr int64_t numChannles = 3;
    constexpr int64_t width = 256;
    constexpr int64_t height = 256;
    constexpr int64_t numClasses = 2;
    constexpr int64_t numInputElements = numChannles * height * width;

    const string imgFile = image_path;
    const string labelFile = class_path;
    auto modelPath = argv[1];

    sessionOptions = Ort::SessionOptions();

    //load labels
    Helpers utils;
    vector<string> labels = utils.loadLabels(labelFile);
    if (labels.empty()){
        cout<< "Failed to load labels: " << labelFile << endl;
        return 1;
    }

    //load image
    vector<float> imageVec = utils.loadImage(imgFile, height, width);
    if (imageVec.empty()){
        cout << "Invalid image format. Must be 224*224 RGB image. " << endl;
        return 1;
    }

    // create session
    cout << "create session. " << endl;
    session = Ort::Session(env, modelPath, sessionOptions);

    // get the number of input
    size_t num_input_nodes = session.GetInputCount();
    std::vector<const char*> input_node_names(num_input_nodes);
    std::vector<int64_t> input_node_dims;

    std::cout << "Number of inputs = " << num_input_nodes << std::endl;

    // define shape
    const array<int64_t, 4> inputShape = {1, numChannles, height, width};
    const array<int64_t, 2> outputShape = {1, numClasses};

    // define array
    array<float, numInputElements> input;
    array<float, numClasses> results;

    // define Tensor
    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
    auto inputTensor = Ort::Value::CreateTensor<float>(memory_info, input.data(), input.size(), inputShape.data(), inputShape.size());
    auto outputTensor = Ort::Value::CreateTensor<float>(memory_info, results.data(), results.size(), outputShape.data(), outputShape.size());

    // copy image data to input array
    copy(imageVec.begin(), imageVec.end(), input.begin());

    // define names
    Ort::AllocatorWithDefaultOptions ort_alloc;

    std::vector<const char*> inputNames;
    std::vector<const char*> outputNames;
    inputNames.push_back(session.GetInputName(0, ort_alloc));
    outputNames.push_back(session.GetOutputName(0, ort_alloc));

    std::cout << "Input name: " << inputNames[0] << std::endl;
    std::cout << "Output name: " << outputNames[0] << std::endl;

    // run inference
    cout << "run inference. " << endl;
    try{
        for (size_t i = 0; i < 1; i++)
        {
            auto start = std::chrono::system_clock::now();
            session.Run(Ort::RunOptions{nullptr}, inputNames.data(), &inputTensor, 1, outputNames.data(), &outputTensor, 1);
            auto end = std::chrono::system_clock::now();
            std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() << "ms" << std::endl;
        }
    }
    catch(Ort::Exception& e) {
        cout << e.what() << endl;
        return 1;
    }

    // sort results
    vector<pair<size_t, float>> indexValuePairs;
    cout << "results.size(): " << results[0] << endl;
    for(size_t i = 0; i < results.size(); ++i){
        cout << "results[i]: " << results[i] << endl;
        indexValuePairs.emplace_back(i, results[i]);
    }
    sort(indexValuePairs.begin(), indexValuePairs.end(), [](const auto& lhs, const auto& rhs) 
    { return lhs.second > rhs.second;});

    // show Top5
    for (size_t i = 0; i < 1; ++i) {
        const auto& result = indexValuePairs[i];
        cout << i + 1 << ": " << labels[result.first] << " " << result.second << endl;
    }
}

helpers.h

#pragma once
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>

using namespace std;

class Helpers{
public:
	vector<float> loadImage(const string path, int x, int y);
	vector<string> loadLabels(const string path);
};

helpers.cpp

#include <fstream>
#include <iostream>
#include <array>

#include "helpers.h"

using namespace std;
using namespace cv;


const float mean_vals[3] = {0.3539317f, 0.36820036f, 0.37243055f};
const float scale_vals[3] = {1.0 / 0.22637626f, 1.0 / 0.22498632f, 1.0 / 0.22197094f};

void normalize_inplace_kxh(cv::Mat &mat_inplace, const float *mean, const float *scale)
{
  if (mat_inplace.type() != CV_32FC3) mat_inplace.convertTo(mat_inplace, CV_32FC3);
  for (unsigned int i = 0; i < mat_inplace.rows; ++i)
  {
    cv::Vec3f *p = mat_inplace.ptr<cv::Vec3f>(i);
    for (unsigned int j = 0; j < mat_inplace.cols; ++j)
    {
      p[j][0] = (p[j][0] - mean[0]) * scale[0];
      p[j][1] = (p[j][1] - mean[1]) * scale[1];
      p[j][2] = (p[j][2] - mean[2]) * scale[2];
    }
  }
}

vector<float> Helpers::loadImage(const string filename, int sizeX, int sizeY)
{
    Mat image = imread(filename);
    if (image.empty()) {
        cout << "No image found.";
    }

    // convert from BGR to RGB
    cvtColor(image, image, COLOR_BGR2RGB);

    // resize
    resize(image, image, Size(sizeX, sizeY));
    cv::Mat canvas;
    image.convertTo(canvas, CV_32FC3, 1. / 255.f, 0.f);
    normalize_inplace_kxh(canvas, mean_vals, scale_vals);

    // reshape to 1D
    canvas = canvas.reshape(1, 1);

    // uint_8, [0, 255] -> float, [0, 1]
    // Normailze number to between 0 and 1
    // Convert to vector<float> from cv::Mat.
    vector<float> vec;
    canvas.convertTo(vec, CV_32FC1);

    // Transpose (Height, Width, Channel)(224,224,3) to (Chanel, Height, Width)(3,224,224)
    vector<float> output;
    for (size_t ch = 0; ch < 3; ++ch) {
        for (size_t i = ch; i < vec.size(); i += 3) {
            output.emplace_back(vec[i]);
        }
    }
    return output;
}

vector<string> Helpers::loadLabels(const string filename)
{
    vector<string> output;

    ifstream file(filename);
    if (file) {
        string s;
        while (getline(file, s)) {
            output.emplace_back(s);
        }
        file.close();
    }

    return output;
}

编译脚本: build.sh

mkdir build
cd build
cmake ..
make -j4

运行脚本: run.sh

data_path=/home/robot/Project/onnx_demo/onnxruntime_resnet-main/assets/1664450262_1664708729_02597_003.jpg
model_path=/home/robot/Project/onnx_demo/onnxruntime_resnet-main/assets/best.onnx
class_path=/home/robot/Project/onnx_demo/onnxruntime_resnet-main/assets/imagenet_classes.txt

./build/demo $model_path $class_path $data_path

5、RK 1808 部署

主要流程包括:
(1)查看模型的输入和输出节点名称;
(2)PC模拟器模型转换为RKNN,以及推理;
(3)C++部署;

5.1、查看模型输入、输出名字

使用 Netron 打开 ONNX模型,如下图所示,可以得到【输入,输出】节点的名称,分别为【images,output0】,后面转换模型的时候,需要指定输入和输出节点名称,
在这里插入图片描述在这里插入图片描述


5.2、转换为RKNN模型

import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
from rknn.api import RKNN

ONNX_MODEL = 'best.onnx'
RKNN_MODEL = 'best.rknn'


def show_outputs(outputs):
    output = outputs[0][0]
    output_sorted = sorted(output, reverse=True)
    top5_str = 'resnet50v2\n-----TOP 5-----\n'
    for i in range(1):
        value = output_sorted[i]
        index = np.where(output == value)
        for j in range(len(index)):
            if (i + j) >= 5:
              break
            if value > 0:
                topi = '{}: {}\n'.format(index[j], value)
            else:
                topi = '-1: 0.0\n'
            top5_str += topi
    print(top5_str)


if __name__ == '__main__':

    # Create RKNN object
    rknn = RKNN()
    
    # pre-process config
    print('--> Config model')
    rknn.config(mean_values=[[90.253, 93.891, 94.97]], std_values=[[57.726, 57.372, 56.603]], reorder_channel='0 1 2')
    print('done')

    # Load ONNX model
    print('--> Loading model')
    ret = rknn.load_onnx(model=ONNX_MODEL,
                         inputs=['images'],
                         input_size_list=[[3, 256, 256]],
                         outputs=['output0'])
    if ret != 0:
        print('Load resnet50v2 failed!')
        exit(ret)
    print('done')

    # Build model
    print('--> Building model')
    ret = rknn.build(do_quantization=True, dataset='./dataset.txt')
    if ret != 0:
        print('Build resnet50v2 failed!')
        exit(ret)
    print('done')

    # Export RKNN model
    print('--> Export RKNN model')
    ret = rknn.export_rknn(RKNN_MODEL)
    if ret != 0:
        print('Export resnet50v2.rknn failed!')
        exit(ret)
    print('done')
    # Set inputs
    img = cv2.imread('./2_256x256.jpg')
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # init runtime environment
    print('--> Init runtime environment')
    ret = rknn.init_runtime()
    if ret != 0:
        print('Init runtime environment failed')
        exit(ret)
    print('done')
    
    # Inference
    print('--> Running model')
    t1 = time.time()
    outputs = rknn.inference(inputs=[img])
    x = outputs[0]
    output = np.exp(x)/np.sum(np.exp(x))
    outputs = [output]

    t2 = time.time()
    print('inference time: ', t2-t1)

    show_outputs(outputs)
    print('done')

    rknn.release()

5.3、C++ 芯片部署

对于分类模型的部署,可以参考官方的例子,几乎不需要更改太多代码,编译即可使用,这里不作详细解释

RK工具链Demo :https://github.com/rockchip-linux/rknpu/tree/master/rknn/rknn_api/examples/rknn_mobilenet_demo
个人总结的博客:Rockchip RV1126 模型部署(完整部署流程),该例子与1808共用一套工具链,可以作为参考


6、调优策略

调优策略,也不是很方便介绍太细,下面只从几个大的方面介绍一些注意问题,比如数据标注,数据清洗,模型调优等。

6.1、数据标注

当我们面临一个实际场景时,基本的工作流程应该如下:

  1. 采集数据:获取该场景下的数据样本,前期可以先采集一部分,验证整个过程。后续随着算法的调优和遇到的问题,数据采集也可能有一定的要求;
  2. 设计标注规则:比如一张图片应该标注什么标签。有些图片内容对应的标签是模糊不清的,判断可能也不唯一。此时,可能需要看我们关注的点是什么?训练的侧重不同,可能决定一张图片的标签是什么?所以关键是如何定义任务。根据训练的结果,以及项目的侧重点差异,可能会影响标签的标注(比如,我们更关注类别1的分类正确性,那么如果测试集,类别1的预测非常好,但是类别2,3相对较差,并且类别2,3多是相互预测错误,那么根据项目的需要,也是可以接受的);

6.2、数据清洗

通常我们需要先标注一组数据,根据自己数据集的规模,比如取出10%的规模进行训练测试。使用该数据训练网络,并得到初版的效果。此时我们推理测试集,查看测试集的预测情况,错误有哪些,为什么会错?是网络本身的问题,还是该图片本身的标签定义就很模糊呢?

  1. 脏数据清洗:表示数据质量非常差,完全不可用,比如严重曝光,全黑数据,严重模糊,完全遮挡等;
  2. 标签模糊定义:有的数据标签比较难以定义,就分类问题而言,通常提取的是图像的全局+局部特征,有时候图片内同时存在目标特征和非目标特征,就存在定义类别的问题;
  3. 样本划分:当我们训练了一版基本的模型,准确度达到一定的高度。但是,当我们去推理测试集时,总有一些样本预测错误。此时,我们不妨预测一遍训练集,将预测错误的样本提取出来,逐个核对样本,确认是标签本身错误?特征不明显?困难样本?此时,再针对性的解决问题;

6.3、网络调优

实际项目中,重点可能是数据的清洗,网络的调整比较有限,可以调整的有如下几方面,具体流程如下:

  1. 网络结构:根据部署设备的性能,选择合适大小,应用较为广泛,易部署的模型;
  2. 超参数:学习率,batch-size等;
  3. 网络泛化:正则化,dropout,soft-label;

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

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

相关文章

理解Deformable Convolution网络

1.简介 偶然了解到了可形变卷积这篇文章&#xff0c;看了几篇博文后大致了解的差不多了&#xff0c;但是有些细节还是看了代码之后才理解。这里想自己写一下关于这篇论文的了解&#xff0c;希望自己能够讲清楚。这里放一篇写的很好地博文链接&#xff0c;想更深入了解代码的可…

Gateway自定义过滤器——全局过滤器

一、什么是全局过滤器&#x1f349; 首先&#xff0c;我们要知道全局过滤器其实是特殊路由过滤器(特殊的GatewayFilter)&#xff0c;会有条件地作用于所有路由。 为什么要自定义全局过滤器&#xff1f;就好比是看大门的保安大叔&#xff0c;平时主要是做好进出大门外来人员登记…

Chatglm实现agent控制

背景&#xff1a; 这个系列文章&#xff0c;会从LLM搭建应用生态角度来写。从0到1训练一个大的通用的模型对于大部分人和团队来讲是不现实的。重资金&#xff0c;重技术含量、重投入这几个门槛可以把很多团队直接劝退。那么在LLM蓬勃发展的时候我们可以做些什么呢&#xff0c;…

C语言程序设计——字符、字符串、内存函数

一、长度不受限的字符串函数 1. strlen size_t strlen(const char* str); 功能&#xff1a;求字符串长度 &#xff08;1&#xff09;字符串以\0作为结束标志&#xff0c;strlen函数返回的是在字符串中\0之前出现的字符个数&#xff08;不包含\0&#xff09;。 &#xff08…

【每日运维】大文件的分割与合并

产生背景 特殊单位需要将文件刻盘带入&#xff0c;并且刻盘有大小限制&#xff0c;所以有了这个需求 推荐方法 个人电脑上使用 split 命令指定大小分割Linux 服务上使用 cat 命令进行合并使用 md5sum 命令校验包的完整性 方法演示 需要将一个按照100M分割后刻盘导入 在个…

数据结构 ~ 树

什么是树 - tree 一种分层数据的抽象模型&#xff1b; 如&#xff1a;DOM、级联选择、树形控件&#xff0c;js 中没有树 可以用 Object 构建树&#xff1a; const tree {val: a,children: [{val: a-1,children: [{val: a-1-1,children: []}]},{val: a-2,children: [{val: a…

mapbox绘制多边形

1、实现效果 请忽略马赛克 2、实现思路 绘制一个填充的多边形&#xff0c;再描个边框。 3、实现代码 绘制多边形函数 drawPolygon() {map.addSource(maine, {type: geojson,data: https://asc-test1.oss-cn-beijing.aliyuncs.com/2023/07/05/45f6bd80f2f34d79b3e457b31ec5d…

云原生网关如何实现安全防护能力

作者&#xff1a;刘晓瑞(钰诚) 云原生网关&#xff1a;将安全、流量和微服务三合一 作为面向南北向的公网网关&#xff0c;使用 Waf 防护异常流量是很常规的需求&#xff0c;而且随着互联网环境变得越来越复杂&#xff0c;用户对防护的诉求是持续增强的&#xff0c;常规做法是…

需要我怎么帮你?关于维护Nutsdb开源社区的思考

背景 近来有人问我打算怎么继续维护Nutsdb社区&#xff0c;他们当中不乏有开源项目的狂热爱好者&#xff0c;发起了好几个几千star的开源项目&#xff0c;也有对Nutsdb感兴趣的&#xff0c;想来问问后续的计划。这不禁让我回想到从刚开始参与这个项目到最近这段时间一个人维护…

三维重建的工作

文章目录 一、北京&#xff1a;二、广州&#xff1a;三、深圳&#xff1a; 一、北京&#xff1a; 链接 三维重建技术是自动驾驶领域4D真值数据生成的核心基础能力。融合LiDAR、Camera、IMU、轮速计等传感器数据像BlockNeRF一样重建城市级别逼真精细的三维场景&#xff0c;将…

ArcGIS Pro 矢量数据的空间校正

GIS 数据通常来自多个源。当数据源之间出现不一致时,有时需要执行额外的工作以将新数据集与其余数据进行整合。相对于基础数据而言,一些数据会在几何上发生变形或旋转。 在编辑环境中,空间校正提供用于对齐和整合数据的交互式方法。 空间校正可执行的一些任务包括:将数据…

SQLite数据库安装

安装方式一&#xff1a; sudi apt-get install sqlite 安装方式二&#xff1a; https://www.sqlite.org/download.html 1. 把下载的文件 sqlite-autoconf-3420000.tar.gz 上传到开发板 2. tar xvf sqlite-autoconf-3420000.tar.gz 解压 3. cd sqlite-autoconf-3420000 进入…

[Mysql] 索引失效的情况详解~

一条查询语句走了索引和没走索引的查询效率是非常大的&#xff0c;但有很多情况导致我们的索引失去效果。这里总结一下常见的索引失效的情况~ 数据准备 我们准备一张简单的学生来做演示。 CREATE TABLE student (id int NOT NULL COMMENT id,name varchar(255) COLLATE utf8…

基于weka手工实现支持向量机smo算法

关于svm机器学习模型&#xff0c;我主要学习的是周志华老师的西瓜书&#xff08;《机器学习》&#xff09;&#xff1b; 但是西瓜书中对于参数优化&#xff08;即&#xff1a;Sequential Minimal Optimization&#xff0c;smo算法&#xff09;部分讲解的十分简略&#xff0c;看…

Linux运维面试题(三)之shell编程类

Linux运维面试题&#xff08;三&#xff09;之shell编程类 文本截取有一个b.txt文本&#xff0c;要求将所有域名截取出来&#xff0c;并统计重复域名出现的次数统计当前服务器正在连接的IP地址&#xff0c;并按连接次数排序&#xff08;cut不能以空格做分隔符&#xff09; 随机…

Vis相关的期刊会议

中国计算机学会推荐国际学术会议和期刊目录 文档&#xff0c; 下载 link&#xff1a;CCF推荐国际学术刊物目录-中国计算机学会 一.可视化方向文章 1.IEEE VIS&#xff0c;是由 IEEE Visualization and Graphics Technical Committee(VGTC) 主办的数据可视化领域的顶级会议&a…

VMware桥接模式无法识别英特尔AX200无线网卡解决办法

1.先到英特尔网站下载最新驱动&#xff0c;更新网卡驱动适用于 Intel 无线网络卡的 Windows 10 和 Windows 11* Wi-Fi 驱动程序 2.到控制面板查看无线网卡属性是否有下图组件 没有的话&#xff0c;依次操作 安装---服务---添加---从磁盘安装----浏览--进入VMware安装目录&…

Linux系统的目录结构

目录 一、Linux系统使用注意 1、Linux严格区分大小写 2、Linux文件"扩展名" 3、Linux中所有内容以文件形式保存 4、Linux中存储设备都必须在挂载之后才能使用 二、目录结构 1、Linux分区与Windows分区 2、Linux系统文件架构 3、Linux系统的文件目录用途 一、…

Docker【安装与基本使用】

【1】Docker的安装 注意&#xff1a;如果之前安装过docker其他版本&#xff0c;请删除干净。 docker-01 10.0.0.51 2G docker-02 10.0.0.52 2G docker-01 [rootdocker-01 ~]# cp -rp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime cp: overwrite ‘/etc/localtime’? …

(中等)LeetCode146 LRU 缓存 Java

本题有两种实现操作&#xff0c;需要使用到一个哈希表和一个双向链表。在Java语言中&#xff0c;有一种结合了哈希表和双向链表的数据结构&#xff0c;LinkedHashMap import java.util.LinkedHashMap; import java.util.Map;class LRUCache extends LinkedHashMap<Integer,…