香橙派 Kunpeng Pro:基于ncnn的深度学习模型量化与部署实践

一 引言

近10年里以深度学习为代表的机器学习技术在图像处理,语音识别,自然语言处理等领域里取得了非常多的突破,其背后的核心算法是深度学习为代表的AI基础模型。

一般来讲,我们进行AI项目研发时,遵循三个步骤。

第一步,我们需要针对目标任务选定一个合适的模型架构,然后训练出满足精度的模型,这就是模型设计;

第二步,我们需要基于第一步训练好的模型,在不显著降低其精度的前提下,对模型的冗余参数进行精简,对高精度计算进行低精度近似,这就是模型压缩;

第三步,将模型运用于实际的生产环境,即面向用户的产品,这就是模型部署。

深度学习模型必须要部署到实际的生产环境中才能产生真正的应用价值。在各类落地场景中,有的是服务端应用,它需要模型有更高的精度、更复杂的功能;有的是嵌入式平台应用,诸如手机等各类移动端设备与车载设备,它需要模型具有体积小、低延迟特性。因此我们在进行模型设计与部署的时候,需要根据应用场景选择不同的模型和工具。当前模型优化和部署的工具非常多,常见的包括TensorRT、NCNN等;当前的硬件计算平台也非常多,包括CPU、GPU,NPU、FPGA等。

现在市面上有各种算力的嵌入式设备,强劲一点的如NVIDIA Jetson,弱一点的如树莓派,本次我们从CSDN社区收到了一块OrangePi Kunpeng Pro(香橙派开发板),正好来试一试这块板子的使用与性能。

下面是这块板子的实拍图与开机界面。

 

二 熟悉板子

拿到板子后当然首先要熟悉一下基本情况,包括软硬件相关信息。

第一步:使用/etc/os-release命令查看系统信息。

该板子的操作系统是openeuler 22.03(LTS-SP3),前身是华为自主研发的服务器操作系统EulerOS,其名字来自于1752年数学家欧拉所发现的欧拉公式。后来华为将其捐赠给开放原子开源基金会(OpenAtom Foundation)孵化及运营,项目地址为https://gitee.com/openeuler

openEuler本质上也是一个Linux系统,每两年推出一个LTS版本,每半年发布一次创新版,支持鲲鹏及其他多种处理器,许多系统底层命令与一般Linux系统无异,一般的文件与目录操作等命令,即ls,cd,cp等均不变,软件安装则与centos一样使用yum或者dnf管理。

第二步:修改一些相关配置

一个全新的Linux系统,总有一些东西需要先改一改,比如默认的软件源。直接打开/etc/yum.repos.d/openEuler.repo,将其内容替换为以下内容。

[OS]name=OSbaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/OS/$basearch/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler[everything]name=everythingbaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/everything/$basearch/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/everything/$basearch/RPM-GPG-KEY-openEuler[EPOL]name=EPOLbaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/EPOL/main/$basearch/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler[debuginfo]name=debuginfobaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/debuginfo/$basearch/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/debuginfo/$basearch/RPM-GPG-KEY-openEuler[source]name=sourcebaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/source/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/source/RPM-GPG-KEY-openEuler[update]name=updatebaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/update/$basearch/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/OS/$basearch/RPM-GPG-KEY-openEuler[update-source]name=update-sourcebaseurl=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/update/source/enabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/openeuler/openEuler-22.03-LTS-SP2/source/RPM-GPG-KEY-openEuler

再运行命令更新

sudo dnf clean all
sudo dnf makecache

第三步:查看硬件配置

由于接下来我们要测试AI项目,因此对于板子的硬件能力心里还是要有底。

 

 可以看到是64bit系统,6个CPU,8GB内存,算力暂时不详。

第四步:安装一些必要的库,包括opencv-cv,protobuf等。

sudo dnf install protobuf-compiler protobuf # 文件格式库
sudo dnf install gtk2-devel gtk2-devel-docs # 桌面显示库
sudo dnf install pkg-config # 编译器的辅助工具,可以帮助 GCC 找到所需要的头文件与库文件路径
sudo dnf install mlocate # 文件查找库
sudo dnf install opencv opencv-devel
sudo pip install opencv-python # opencv库
sudo pip install torch # torch库
sudo pip install torchvision # torchvision库
sudo pip install onnxruntime # onnx库

系统已经预装了python3.9,后续使用如果还缺什么就用pip装就是。

 

三 模型部署

接下来我们使用移动端NCNN框架,以及香橙派,来体验典型的移动端模型部署流程,包括模型的格式转换、模型量化、基于C++的模型推理部署。

框架介绍

本次我们选择的部署框架是NCNN,它是一个在工业界被广为使用的框架,具有非常好的性能。

NCNN是一个纯C++实现的框架,无任何第三方库依赖,不依赖 BLAS/NNPACK 等计算框架,提供了ARM NEON 汇编级良心优化,计算速度极快。NCNN提供了对所有主流操作系统的支持,如图所示;

标题NCNN支持的操作系统 

 

NCNN支持PaddlePaddle/PyTorch/TensorFlow/Caffe/MXNet/DarkNet/OneFlow/ONNX等深度学习框架文件格式,支持CNN、GAN等常用网络模型结构;

NCNN支持Intel架构的CPU与GPU,AMD架构的CPU与GPU,Arm架构的CPU与GPU,高通架构的CPU与GPU,Apple架构的CPU与GPU,其中对高通公司的CPU,ARM公司的CPU以及Apple公司的CPU提供了非常高效率地优化加速;

NCNN支持FP32/FP16/INT8/UINT8等多种运算精度;

NCNN支持C/C++/Python API;

NCNN支持直接内存零拷贝引用加载网络模型,可注册自定义层实现并扩展;

要使用NCNN,首先需要下载源码进行编译安装,相关代码命令如下:

git clone https://github.com/Tencent/ncnn
cd ncnn
mkdir build && cd build
cmake ..
make -j
make install

 

 

 

安装完之后,就可以在build/install目录下看到生成的一系列可执行文件和需要的库文件,它们分别存储于bin子目录和include子目录。如果github访问不了,就换gitee地址。

模型转换与量化

在进行部署之前,需要对模型格式进行转换,相关工具在ncnn根目录/build/install/bin目录下,包括   

caffe2ncnn:caffe模型转换工具
darknet2ncnn: mxnet模型转换工具
mxnet2ncnn: mxnet模型转换工具
onnx2ncnn: onnx模型转换工具
ncnn2table, ncnn2int8: 模型量化工具
ncnn2mem:模型加密可执行文件:
ncnnoptimize:模型优化可执行文件
ncnnmerge:模型合并可执行文件

完整的模型量化流程可以分为3步,以ONNX格式为例:

第一步,将ONNX格式的模型转换为NCNN格式的模型,所使用的模型是一个训练好的图像分类模型。

其模型配置如下:

## 简单模型定义
class simpleconv5(nn.Module):def __init__(self,nclass):super(simpleconv5,self).__init__()self.conv1 = nn.Conv2d(3, 32, 3, 2, 1, bias=False)self.bn1 = nn.BatchNorm2d(32)self.conv2 = nn.Conv2d(32, 64, 3, 2, 1, bias=False)self.bn2 = nn.BatchNorm2d(64)self.conv3 = nn.Conv2d(64, 128, 3, 2, 1, bias=False)self.bn3 = nn.BatchNorm2d(128)self.conv4 = nn.Conv2d(128, 256, 3, 2, 1, bias=False)self.bn4 = nn.BatchNorm2d(256)self.conv5 = nn.Conv2d(256, 512, 3, 2, 1, bias=False)self.bn5 = nn.BatchNorm2d(512)self.fc = nn.Linear(512, nclass)def forward(self , x):x = F.relu(self.bn1(self.conv1(x)))x = F.relu(self.bn2(self.conv2(x)))x = F.relu(self.bn3(self.conv3(x)))x = F.relu(self.bn4(self.conv4(x)))x = F.relu(self.bn5(self.conv5(x)))x = nn.AvgPool2d(7)(x)x = x.view(x.size(0), -1) x = self.fc(x)return x
onnx2ncnn simpleconv5.onnx simpleconv5.param simpleconv5.bin

生成的ncnn格式的模型包括两个文件,simpleconv5.param是网络的配置文件,simpleconv5.bin是网络的权重文件。

第二步,生成int8量化所需要的校准表。

ncnn2table models/simpleconv5.param models/simpleconv5.bin images.txt simpleconv5.table mean=[127.5,127.5,127.5] norm=[0.00784,0.00784,0.00784] shape=[48,48,3] pixel=RGB

 其中ncnn2table工具默认使用基于KL散度的8bit量化算法,它输入模型文件simpleconv5.param和simpleconv5.bin,校准表图片路径images.txt,预处理均值mean与标准化norm值,输入图片尺寸,RGB图片的格式,输出simpleconv5.table,即校准表。pixel=RGB表示输入网络的图片是RGB格式,当我们使用OpenCV进行图片读取后是BGR格式,两者需要进行区分。由于ncnn框架读取的图片数据像素值范围是0到127,而模型训练时采用的预处理操作包括除以255进行归一化,再减去均值向量[0.5,0.5,0.5],除以方差向量[0.5,0.5,0.5],因此这里对应的预处理操作需要将归一化操作合并到减均值操作和除以方差操作中,mean=255×[0.5,0.5,0.5]=[127.5,127.5,127.5], norm=1/255/[0.5,0.5,0.5]=[0.00784,0.00784,0.00784]

第三步,基于校准表进行量化。

量化前模型大小为6.4MB,量化后模型大小为1.6MB,8bit模型大小为float32模型大小的1/4,减少了存储空间。

模型部署测试

接下来我们使用C++接口对模型进行部署测试,并比较量化前后的模型精度是否受到严重影响,测试的核心C++功能函数代码如下:

#include "net.h"#include <algorithm>
#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <stdio.h>
#include <vector>
#include <cmath>
//推理函数
static int detect_simpleconv5net(const ncnn::Net &simpleconv5net,const cv::Mat& bgr, std::vector<float>& cls_scores)
{ncnn::Mat in = ncnn::Mat::from_pixels_resize(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, bgr.cols, bgr.rows, 224, 224);//读取图片数据const float mean_vals[3] = {0.5f*255.f, 0.5f*255.f, 0.5f*255.f};const float norm_vals[3] = {1/0.5f/255.f, 1/0.5f/255.f, 1/0.5f/255.f};in.substract_mean_normalize(mean_vals, norm_vals); //预处理ncnn::Extractor ex = simpleconv5net.create_extractor();//创建推理引擎ex.input("input.1", in); //填充数据ncnn::Mat out;float start_time = cv::getTickCount(); //计算模型推理时间ex.extract("59", out); //获得模型推理结果float end_time = cv::getTickCount();fprintf(stderr, "%s = %f %s\n", "inference time = ", (end_time-start_time)/cv::getTickFrequency()*1000, " ms");cls_scores.resize(out.w); //取softmax分类概率结果,指数减去固定值防止溢出处理float maxscore = 0.0;for (int j = 0; j < out.w; j++){if(out[j] >= maxscore) maxscore = out[j]; cls_scores[j] = out[j];}float sum = 0.0;for (int j = 0; j < out.w; j++){cls_scores[j] = std::exp(cls_scores[j]-maxscore);sum += cls_scores[j]; }for (int j = 0; j < out.w; j++){cls_scores[j] = cls_scores[j] / sum;}return 0;
}int main(int argc, char** argv)
{if (argc != 5){fprintf(stderr, "Usage: %s%s%s [modelparam modelbin imagepath resultpath]\n", argv[0], argv[1], argv[2], argv[3]);return -1;}const char* modelparam = argv[1];const char* modelbin = argv[2];const char* imagepath = argv[3];const char* resultpath = argv[4];//初始化模型ncnn::Net simpleconv5net;simpleconv5net.opt.use_vulkan_compute = true;simpleconv5net.load_param(modelparam);simpleconv5net.load_model(modelbin);cv::Mat image = cv::imread(imagepath, 1);if (image.empty()){fprintf(stderr, "cv::imread %s failed\n", imagepath);return -1;}//获得topk的分类概率std::vector<float> cls_scores;detect_simpleconv5net(simpleconv5net, image, cls_scores);int topk = 1;int size = cls_scores.size();std::vector<std::pair<float, int> > vec;vec.resize(size);for (int i = 0; i < size; i++){vec[i] = std::make_pair(cls_scores[i], i);}std::partial_sort(vec.begin(), vec.begin() + topk, vec.end(),std::greater<std::pair<float, int> >());for (int i = 0; i < topk; i++){float score = vec[i].first;int index = vec[i].second;fprintf(stderr, "%d = %f\n", index, score);}//绘制结果std::string text;std::string label = "c="+std::to_string(vec[0].second);std::string prob = "prob="+std::to_string(vec[0].first);text.assign(label+"   ");text.append(prob);int font_face = cv::FONT_HERSHEY_COMPLEX; double font_scale = 0.75;int thickness = 2;//将文本框居中绘制cv::Mat showimage = image.clone();cv::resize(showimage,showimage,cv::Size(256,256));cv::Point origin; origin.x = showimage.cols / 20;origin.y = showimage.rows / 2;cv::putText(showimage, text, origin, font_face, font_scale, cv::Scalar(0, 255, 255), thickness, 8, 0);cv::namedWindow("image",0);cv::imshow("image",showimage);//cv::waitKey(0);cv::imwrite(resultpath,showimage);return 0;
}

NCNN中每一层的数据被保存为自定义的Mat类型数据,它使用from_pixels_resize函数将OpenCV读取的Mat矩阵数据进行转换,由于计算使用了汇编,非常高效。网络定义为一个ncnn::Net类,格式与Caffe中的Net类非常相似,包含了layers和blobs成员变量,其中layers储存了每一层的信息,blobs储存了网络的中间数据。

在进行推理时,首先根据net实例化一个ncnn::Extractor类,extractor中的net会被转为const类。我们可以给extractor的任意一层送入数据,如extractor.input("data", in)就是给输入数据层赋值。通过extractor.extract函数可以取出任意层的数据,在extract方法中,它会调用forward_layer方法递归地遍历网络。

我们从20类中每一类随机选取一张图片来进行测试,比较量化前的模型推理结果和量化后的模型推理结果,下图展示了每一张图片的预测类别及经过Softmax映射后的概率,其中奇数行为量化前的模型推理结果,偶数行为量化后的模型推理结果。

 

从图中样本的预测结果可以看出,量化前后模型的预测概率是有差异的,但是差异非常小,大多在1%以内,所选测试图片的预测结果都是正确的,说明该模型经过量化后精度没有精度损失。

下图从上到下分别展示了量化前和量化后的模型推理时间,每一张图片的推理时间是通过重复100次推理后计算出来的平均值,这是为了让推理时间的计算更加稳定。可以看出,对于大部分样本,量化前模型的推理时间约为4.4ms左右,量化后模型的推理时间约为3.7ms左右,量化后模型的推理速度提升了16%,验证了模型量化的加速效果。

量化前推理时间

 

量化后推理时间

 

 我们也用python对onnx模型以及原生pytorch模型进行了计时,计算了平均推理时间约为25ms,相关代码如下:

#coding:utf8
import torch
import torchvision
from torchsummary import summary
import time
import cv2
import sys
import onnxruntime
import numpy as np
import PIL.Image as Image
import os,glob
from simpleconv5 import simpleconv5
torch.manual_seed(0)
os.environ['CUDA_LAUNCH_BLOCKING'] = '1'# 图像预处理函数
def process_image(img):input_size = [224,224]mean = (0.5,0.5,0.5)std = (0.5,0.5,0.5)img = np.asarray(img.resize((input_size[0],input_size[1]),resample=Image.NEAREST)).astype(np.float32) / 255.0img[:,:,] -= meanimg[:,:,] /= stdimage = img.transpose((2,0,1))[np.newaxis, ...]return imageimgdir = "../GHIM-20"'''
##-----------------------test pytorch------------------------##
# 加载模型
model = simpleconv5(20)
modelpath = '../models/model_best.pth.tar'
model.load_state_dict(torch.load(modelpath,map_location='cpu')['state_dict'])
model.eval()
acc = 0.0
nums = 0.0
start_Inference = time.time()
for imgpath in glob.glob(os.path.join(imgdir, "**/*.jpg"),recursive=True):img = Image.open(imgpath).convert('RGB')data_input = process_image(img)input_data = torch.from_numpy(data_input)data_output = model(input_data)output = data_output.squeeze().cpu().detach().numpy()pred1 = output.argmax()label = int(imgpath.split('/')[-2])if label == pred1:acc += 1.0nums += 1.0end_Inference = time.time()
print('Inference use time='+str((end_Inference-start_Inference)*1000/nums)+' ms')
print("acc=",acc/nums)
'''##-----------------------test onnx------------------------##
onnx_path = '../models/simpleconv5.onnx'
result_path = 'onnx_results'
session = onnxruntime.InferenceSession(onnx_path)
inname = [input.name for input in session.get_inputs()]
outname = [output.name for output in session.get_outputs()]
acc = 0.0
nums = 0.0
print("inputs name:",inname,"|| outputs name:",outname)
start = cv2.getTickCount()
for imgpath in glob.glob(os.path.join(imgdir, "**/*.jpg"),recursive=True):img = Image.open(imgpath).convert('RGB')data_input = process_image(img)data_output = session.run(outname, {inname[0]: data_input})output = np.squeeze(data_output[0])pred1 = output.argmax()prob = output[pred1]label = int(imgpath.split('/')[-2])if label == pred1:acc += 1.0nums += 1.0
end = cv2.getTickCount()
print('ONNX Inference Time='+str((end-start)/nums/cv2.getTickFrequency()*1000)+' ms')
print("acc=",acc/nums)

可以看出,C++推理速度相比于python推理速度,有6倍左右的提升。量化后相比于量化前的提升速度并不大,这是因为香橙派本身的硬件足够出色,笔者的MacBook Pro Apple M2上的onnx推理时间也需要8ms左右。

总体来讲,香橙派的性能比我之前的EAIDK-310开发套件性能强多了,同样的模型EAIDK-310量化后的推理时间需要20ms以上,以后做嵌入式项目演示,就用香橙派 Kunpeng Pro了!

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

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

相关文章

LabVIEW步进电机的串口控制方法与实现

本文介绍了在LabVIEW环境中通过串口控制步进电机的方法&#xff0c;涵盖了基本的串口通信原理、硬件连接步骤、LabVIEW编程实现以及注意事项。通过这些方法&#xff0c;用户可以实现对步进电机的精确控制&#xff0c;适用于各种自动化和运动控制应用场景。 步进电机与串口通信…

python3.8环境下安装pyqt5

1.实验目的 测试python可视化工具包pyqt5,为后期做系统前端页面做铺垫 2.实验环境 1.软件 anaconda2.5 pycharm2024.1.1 pyqt5 2.硬件 GPU 4070TI Intel I7 1400K 3. 安装步骤 (base) C:\Users\PC>conda -V conda 23.7.4(base) C:\Users\PC>conda create qttest p…

GIS、GPS、RS综合应用

刘老师&#xff08;副教授&#xff09;&#xff0c;北京重点高校资深专家&#xff0c;拥有丰富的科研及工程技术经验&#xff0c;长期从事3S在环境中的应用等领域的研究和教学工作&#xff0c;具有资深的技术底蕴和专业背景。 第一章、3S 技术及应用简介 1.1、3S 技术及集成简…

跨模型知识融合:大语言模型的知识融合

大语言模型&#xff08;LLMs&#xff09;在多个领域的应用日益广泛&#xff0c;但确保它们的行为与人类价值观和意图一致却充满挑战。传统对齐方法&#xff0c;例如基于人类反馈的强化学习&#xff08;RLHF&#xff09;&#xff0c;虽取得一定进展&#xff0c;仍面临诸多难题&a…

wandb安装与使用 —— 用于跟踪、可视化和协作机器学习实验的工具

文章目录 一、wandb简介二、wandb注册与登陆&#xff08;网页&#xff09; —— 若登录&#xff0c;则支持在线功能三、wandb安装与登陆&#xff08;命令行&#xff09; —— 若不登录&#xff0c;则只保留离线功能四、函数详解4.1、wandb.init() —— 初始化一个新的 wandb 实…

上位机图像处理和嵌入式模块部署(f407 mcu中fatfs中间件使用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们已经实现了spi norflash的驱动&#xff0c;理论上这已经可以实现数据的持久化保存了。为什么还需要一个文件系统呢&#xff1f;主要原因还…

在 Win系统安装 Ubuntu20.04子系统 WSL2 (默认是C盘,第7步开始迁移到D盘,也可以不迁移)

1、简介 WSL在Windows 10上原生运行Linux二进制可执行文件&#xff0c;不用单独安装虚拟机。 WSL2是WSL的第二个版本&#xff0c;提供了与WSL相比的显著性能改进和完全的系统呼叫兼容性。通过运行Linux内核在一个轻量级虚拟机&#xff08;VM&#xff09;中实现。 2、安装 电…

ThingsBoard MQTT 连接认证过程 源码分析+图例

整个连接过程如图所示&#xff1a; 高清图片链接 1、环境准备 thingsboard3.5.1 源码启动。&#xff08;不懂怎么启动的&#xff0c;大家可以看我的博文ThingsBoard3.5.1源码启动&#xff09;MQTTX 客户端&#xff08;用来连接 thingsboard MQTT&#xff09;默认配置。queue.…

7-15 位模式(dump_bits)---PTA实验C++

一、题目描述 为方便调试位运算相关程序&#xff0c;先做个展现位模式的小工具。 建议参照以下接口实现&#xff1a; // 利用函数重载特性&#xff1a;string dump_bits(char x);string dump_bits(short x);string dump_bits(int x);string dump_bits(long long x);// 或用函…

JVM类加载过程

在Java虚拟机规范中&#xff0c;把描述类的数据从class文件加载到内存&#xff0c;并对数据进行校验、转换解析和初始化&#xff0c;最终形成可以被虚拟机直接使用的java.lang.Class对象&#xff0c;这个过程被称作类加载过程。一个类在整个虚拟机周期内会经历如下图的阶段&…

DIYP对接骆驼后台IPTV管理,退出菜单中显示用户名已经网络信息,MAC,剩余天数,套餐名称等

演示&#xff1a;https://url03.ctfile.com/f/1779803-1042599473-4dc000?p8976 (访问密码: 8976) 后台加上EPG&#xff0c;增加一些播放源的动态端口替换。 前台app上&#xff0c;退出菜单中显示用户名已经网络信息&#xff0c;MAC&#xff0c;剩余天数&#xff0c;套餐名称…

Python知识点17---包

提前说一点&#xff1a;如果你是专注于Python开发&#xff0c;那么本系列知识点只是带你入个门再详细的开发点就要去看其他资料了&#xff0c;而如果你和作者一样只是操作其他技术的Python API那就足够了。 Python的包&#xff0c;你可以把它看成是一个大的模块&#xff0c;它…

JAVA基础|多线程

什么是线程&#xff1f; 线程&#xff08;Thread&#xff09;是一个程序内部的一条执行流程。 多线程是什么&#xff1f; 多线程是指从软硬件上实现的多条执行流程的技术&#xff08;多条线程由CPU负责调度执行&#xff09; 一. 如何在程序中创建出多条线程&#xff1f; Ja…

Windows10系统中安装与配置PyTorch(无GPU版本)

文章目录 1. 什么是PyTorch2. PyTorch的安装与配置&#xff08;无GPU&#xff09;2.1 创建环境2.2 安装pytorch库&#xff08;无GPU&#xff09;2.3 验证安装结果 1. 什么是PyTorch PyTorch 是一种用于构建深度学习模型且功能完备的开源框架&#xff0c;通常用于处理图像识别和…

OpenCV 的几种查找图像中轮廓边缘的方法

原始图片&#xff1a; 1、Sobel() Sobel 算子结合了高斯平滑和微分&#xff0c;用于计算图像的梯度&#xff0c;从而突出显示边缘。 import cv2# 读取图像 image cv2.imread(image.png, cv2.IMREAD_GRAYSCALE)# 使用 Sobel 算子查找水平和垂直边缘 sobel_x cv2.Sobel(image…

建筑企业有闲置资质怎么办?

如果建筑企业拥有闲置资质&#xff0c;可以考虑以下几种方式来充分利用这些资质&#xff1a; 1. 租赁或转让资质&#xff1a; 将闲置的建筑资质租赁给其他企业或个人使用&#xff0c;或者通过转让的方式将资质出售给有需要的企业或个人。 2. 提供咨询服务&#xff1a; 利用建…

OneForall工具的下载安装和使用(Windows和Linux)

目录 OneForall的介绍 OneForall的下载 OneForall的安装 安装要求 安装步骤&#xff08;git 版&#xff09; 安装&#xff08;kali&#xff09; OneForall的使用命令 在Windows 在Linux&#xff08;kali&#xff09; OneForall的结果说明 免责声明 本文所提供的文字和…

车辆前向碰撞预警系统性能要求和测试规程

前言 本文整理《GB/T 33577-2017 智能运输系统-车辆前向碰撞预警系统性能要求和测试规程》国标文件关键信息,FCW系统性能和测试右给深层次的认识。 术语和定义 车辆前向碰撞预警系统 forward vehicle collision warning system自车 subject vehicle(SV)目标车辆 target ve…

【Linux】查找和压缩

一、文件查找 1、命令查找 which 2、文件查找、依赖数据库 locate 3、文件查找 find 语法&#xff1a;find [path] [options] [expression] [action] ①按文件名 -name按名 -iname可不区分大小写 ②按文件大小 5M&#xff1a;5M以上文件 5M&#xff1a;5M文件 -…

高中数学:解三角形相关公式总结及用法总结

一、正弦定理 二、余弦定理 三、三角形面积公式 由正弦定理&#xff0c;可以推出三角形的面积公式&#xff1a; S*ab*sinC S*ac*sinB S*bc*sinA 四、使用方法总结 五、练习 例题1 解析 对条件等式进行变形&#xff0c;结合余弦定理&#xff0c;求出∠A的度数&#xff0c;从而…