深度学习之 TensorRT

1 简介

TensorRT是一个高性能的深度学习推理(Inference)优化器,可以为深度学习应用提供低延迟、高吞吐率的部署推理。TensorRT可用于对超大规模数据中心、嵌入式平台或自动驾驶平台进行推理加速。TensorRT现已能支持TensorFlow、Caffe、Mxnet、Pytorch等几乎所有的深度学习框架,将TensorRT和NVIDIA的GPU结合起来,能在几乎所有的框架中进行快速和高效的部署推理。

TensorRT 是一个C++库,从 TensorRT 3 开始提供C++ API和Python API,主要用来针对 NVIDIA GPU进行 高性能推理(Inference)加速。现在最新版TensorRT是4.0版本。

TensorRT 之前称为GIE。

关于推理(Inference):

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

由以上两张图可以很清楚的看出,训练(training)和 推理(inference)的区别:

  • 训练(training)包含了前向传播和后向传播两个阶段,针对的是训练集。训练时通过误差反向传播来不断修改网络权值(weights)。
  • 推理(inference)只包含前向传播一个阶段,针对的是除了训练集之外的新数据。可以是测试集,但不完全是,更多的是整个数据集之外的数据。其实就是针对新数据进行预测,预测时,速度是一个很重要的因素。

一般的深度学习项目,训练时为了加快速度,会使用多GPU分布式训练。但在部署推理时,为了降低成本,往往使用单个GPU机器甚至嵌入式平台(比如 NVIDIA Jetson)进行部署,部署端也要有与训练时相同的深度学习环境,如caffe,TensorFlow等。

由于训练的网络模型可能会很大(比如 inception,resnet等),参数很多,而且部署端的机器性能存在差异,就会导致推理速度慢,延迟高。这对于那些高实时性的应用场合是致命的,比如自动驾驶要求实时目标检测,目标追踪等。

所以为了提高部署推理的速度,出现了很多轻量级神经网络,比如squeezenet,mobilenet,shufflenet等。基本做法都是基于现有的经典模型提出一种新的模型结构,然后用这些改造过的模型重新训练,再重新部署。

而 tensorRT 则是对训练好的模型进行优化。 tensorRT 就只是推理优化器。当你的网络训练完之后,可以将训练模型文件直接丢进tensorRT中,而不再需要依赖深度学习框架(Caffe,TensorFlow等),如下:


在这里插入图片描述

可以认为tensorRT是一个只有前向传播的深度学习框架,这个框架可以将 Caffe,TensorFlow的网络模型解析,然后与tensorRT中对应的层进行一一映射,把其他框架的模型统一全部转换到tensorRT中,然后在tensorRT中可以针对NVIDIA自家GPU实施优化策略,并进行部署加速。

目前TensorRT4.0 几乎可以支持所有常用的深度学习框架,对于caffe和TensorFlow来说,tensorRT可以直接解析他们的网络模型;对于caffe2,pytorch,mxnet,chainer,CNTK等框架则是首先要将模型转为 ONNX 的通用深度学习模型,然后对ONNX模型做解析。而tensorflow和MATLAB已经将TensorRT集成到框架中去了。

ONNX(Open Neural Network Exchange )是微软和Facebook携手开发的开放式神经网络交换工具,也就是说不管用什么框架训练,只要转换为ONNX模型,就可以放在其他框架上面去inference。这是一种统一的神经网络模型定义和保存方式,上面提到的除了tensorflow之外的其他框架官方应该都对onnx做了支持,而ONNX自己开发了对tensorflow的支持。从深度学习框架方面来说,这是各大厂商对抗谷歌tensorflow垄断地位的一种有效方式;从研究人员和开发者方面来说,这可以使开发者轻易地在不同机器学习工具之间进行转换,并为项目选择最好的组合方式,加快从研究到生产的速度。

上面图中还有一个 Netwok Definition API 这个是为了给那些使用自定义的深度学习框架训练模型的人提供的TensorRT接口。举个栗子:比如 YOLO 作者使用的darknet要转tensorrt估计得使用这个API,不过一般网上有很多使用其他框架训练的YOLO,这就可以使用对应的caffe/tensorflow/onnx API了。

ONNX / TensorFlow / Custom deep-learning frame模型的工作方式:

在这里插入图片描述

现在tensorRT支持的层有:

  • Activation: ReLU, tanh and sigmoid
  • Concatenation : Link together multiple tensors across the channel dimension.
  • Convolution: 3D,2D
  • Deconvolution
  • Fully-connected: with or without bias
  • ElementWise: sum, product or max of two tensors
  • Pooling: max and average
  • Padding
  • Flatten
  • LRN: cross-channel only
  • SoftMax: cross-channel only
  • RNN: RNN, GRU, and LSTM
  • Scale: Affine transformation and/or exponentiation by constant values
  • Shuffle: Reshuffling of tensors , reshape or transpose data
  • Squeeze: Removes dimensions of size 1 from the shape of a tensor
  • Unary: Supported operations are exp, log, sqrt, recip, abs and neg
  • Plugin: integrate custom layer implementations that TensorRT does not natively support.

基本上比较经典的层比如,卷积,反卷积,全连接,RNN,softmax等,在tensorRT中都是有对应的实现方式的,tensorRT是可以直接解析的。

但是由于现在深度学习技术发展日新月异,各种不同结构的自定义层(比如:STN)层出不穷,所以tensorRT是不可能全部支持当前存在的所有层的。那对于这些自定义的层该怎么办?

tensorRT中有一个 Plugin 层,这个层提供了 API 可以由用户自己定义tensorRT不支持的层。 如下图:

在这里插入图片描述

这就解决了适应不同用户的自定义层的需求。

2 优化方式

TentsorRT 优化方式:

在这里插入图片描述

TensorRT优化方法主要有以下几种方式,最主要的是前面两种。

  • 层间融合或张量融合(Layer & Tensor Fusion)

    如下图左侧是GoogLeNetInception模块的计算图。这个结构中有很多层,在部署模型推理时,这每一层的运算操作都是由GPU完成的,但实际上是GPU通过启动不同的CUDA(Compute unified device architecture)核心来完成计算的,CUDA核心计算张量的速度是很快的,但是往往大量的时间是浪费在CUDA核心的启动和对每一层输入/输出张量的读写操作上面,这造成了内存带宽的瓶颈和GPU资源的浪费。TensorRT通过对层间的横向或纵向合并(合并后的结构称为CBR,意指 convolution, bias, and ReLU layers are fused to form a single layer),使得层的数量大大减少。横向合并可以把卷积、偏置和激活层合并成一个CBR结构,只占用一个CUDA核心。纵向合并可以把结构相同,但是权值不同的层合并成一个更宽的层,也只占用一个CUDA核心。合并之后的计算图(图4右侧)的层次更少了,占用的CUDA核心数也少了,因此整个模型结构会更小,更快,更高效。
    在这里插入图片描述

  • 数据精度校准(Weight &Activation Precision Calibration)

    大部分深度学习框架在训练神经网络时网络中的张量(Tensor)都是32位浮点数的精度(Full 32-bit precision,FP32),一旦网络训练完成,在部署推理的过程中由于不需要反向传播,完全可以适当降低数据精度,比如降为FP16或INT8的精度。更低的数据精度将会使得内存占用和延迟更低,模型体积更小。

    如下表为不同精度的动态范围:

PrecisionDynamic Range
FP32−3.4×1038 +3.4×1038−3.4×1038 +3.4×1038
FP16−65504 +65504−65504 +65504
INT8−128 +127−128 +127

INT8只有256个不同的数值,使用INT8来表示 FP32精度的数值,肯定会丢失信息,造成性能下降。不过TensorRT会提供完全自动化的校准(Calibration )过程,会以最好的匹配性能将FP32精度的数据降低为INT8精度,最小化性能损失。关于校准过程,后面会专门做一个探究。

  • Kernel Auto-Tuning

    网络模型在推理计算时,是调用GPU的CUDA核进行计算的。TensorRT可以针对不同的算法,不同的网络模型,不同的GPU平台,进行 CUDA核的调整(怎么调整的还不清楚),以保证当前模型在特定平台上以最优性能计算。

    TensorRT will pick the implementation from a library of kernels that delivers the best performance for the target GPU, input data size, filter size, tensor layout, batch size and other parameters.

  • Dynamic Tensor Memory

    在每个tensor的使用期间,TensorRT会为其指定显存,避免显存重复申请,减少内存占用和提高重复使用效率。

  • Multi-Stream Execution

    Scalable design to process multiple input streams in parallel,这个应该就是GPU底层的优化了。

3 安装

这里 是英伟达提供的安装指导,如果有仔细认真看官方指导,基本上按照官方的指导肯定能安装成功。

问题是肯定有很多人不愿意认真看英文指导,比如说我就是,我看那个指导都是直接找到命令行所在,直接敲命令,然后就出了很多问题,然后搜索好长时间,最后才发现,原来官方install guide里是有说明的。

这里使用的是 deb 包安装的方式,以下是安装过程,我是cuda 8.0 ,cuda9.0也是类似的。

进行下面三步时最好先将后面记录的遇到的问题仔细看看,然后回过头来按照 一二三 步来安装。

第一步:

$ sudo dpkg -i nv-tensorrt-repo-ubuntu1604-ga-cuda8.0-trt3.0-20171128_1-1_amd64.deb$ sudo apt-get update
$ sudo apt-get install tensorrt

其中的deb包要换成与自己 cuda和系统 对应的版本。

第二步:

使用python2则安装如下依赖

$ sudo apt-get install python-libnvinfer-doc

这个是为了安装一些依赖的:比如 python-libnvinfer python-libnvinfer-dev swig3.0

如果是python3则安装如下依赖

$ sudo apt-get install python3-libnvinfer-doc

第三步:

$ sudo apt-get install uff-converter-tf

这个是安装通用文件格式转换器,主要用在 TensorRT 与TensorFlow 交互使用的时候。

不过我安装的时候还是出问题了:

  • 安装tensorRT之前要将cuda的两个deb包添加上,因为TensorRT依赖好多cuda的一些东西比如 cuda-cublas-8-0 ,我之前cuda是用runfile安装的,所以TensorRT安装时有些依赖库找不到导致出错,如下图:

    上面提示缺少依赖包,但是实际上 libnvinfer4 的包是tensorRT安装了之后才有的,那现在反而成了依赖包了,不管他,缺什么安装什么,但是还是出错,如下:

    在这里插入图片描述

哇,还是缺少依赖包,这次是缺 cuda-cublas-8-0 ,现在知道了,缺的是cuda的相关组件。

后来把 cuda 的两个deb包安装之后就没问题了,cuda 8.0 的deb包 在这里 ,如下图,下载红框里的两个deb包。

在这里插入图片描述

如果用的是 runfile 的方式安装的cuda的话,很容易出错,因为网上大部分cuda安装教程都是用runfile的方式安装的。所以如果cuda就是用deb包安装的话,就没有这个问题,如果使用runfile安装的话,安装tensorRT之前要把这两个deb包安装上,安装方式如下:

$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-cublas-performance-update_8.0.61-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64.deb

以上是自己摸索出来的,折腾了一番之后才发现原来官方的 install guide已经说明了,如下:

The debian installation automatically installs any dependencies, but:

  • requires sudo root privileges to install
  • provides no flexibility as to which location TensorRT is installed into
  • requires that the CUDA Toolkit has also been installed with a debian package.

注意最后一条,意思是如果用deb包安装TensorRT,那么前提是 你的CUDA也是用deb包安装的。

怪自己没有认真看,要是多花个5分钟仔细看一下,就不用折腾这么久了,由此深有感触,文档还是官方英文原版的最好,而且要认真看。

不过不知道用 runfile cuda+Tar File Installation tensorRT的组合安装方式是怎么样的,没试过。

  • tensorRT 3 支持CUDA 8 和 CUDA 9,但是只支持 cuDNN 7,我第一次安装的时候cuDNN是5.1的,结果总是出错,错误是啥忘记了,反正换成cuDNN 7就好了,这个官方指导也有说明,不过比较隐蔽,他是放在 4.2 Tar File Installation 一节说明的:

    1. Install the following dependencies, if not already present:
      ‣ Install the CUDA Toolkit v8.0, 9.0 or 9.2
      ‣ cuDNN 7.1.3
      ‣ Python 2 or Python 3

    我试过只要大版本是 cudnn7就可以。这个也容易忽略。

安装好后,使用 $ dpkg -l | grep TensorRT 命令检测是否成功,输出如下所示即为成功

在这里插入图片描述

安装后会在 /usr/src 目录下生成一个 tensorrt 文件夹,里面包含 bin , data , python , samples 四个文件夹, samples 文件夹中是官方例程的源码; data , python 文件中存放官方例程用到的资源文件,比如caffemodel文件,TensorFlow模型文件,一些图片等;bin 文件夹用于存放编译后的二进制文件。

可以把 tensorrt 文件夹拷贝到用户目录下,方便自己修改测试例程中的代码。

进入 samples 文件夹直接 make,会在 bin 目录中生成可执行文件,可以一一进行测试学习。

另外tensorRT是不开源的, 它的头文件位于 /usr/include/x86_64-linux-gnu 目录下,共有七个,分别为:

/usr/include/x86_64-linux-gnu/NvCaffeParser.h
/usr/include/x86_64-linux-gnu/NvInfer.h
/usr/include/x86_64-linux-gnu/NvInferPlugin.h
/usr/include/x86_64-linux-gnu/NvOnnxConfig.h
/usr/include/x86_64-linux-gnu/NvOnnxParser.h
/usr/include/x86_64-linux-gnu/NvUffParser.h
/usr/include/x86_64-linux-gnu/NvUtils.h

TensorRT4.0相比于3.0新增了对ONNX的支持。

tensorRT的库文件位于 /usr/lib/x86_64-linux-gnu 目录下,如下(筛选出来的,掺杂了一些其他nvidia库):

/usr/lib/x86_64-linux-gnu/libnvinfer.so
/usr/lib/x86_64-linux-gnu/libnvToolsExt.so
/usr/lib/x86_64-linux-gnu/libnvinfer_plugin.a
/usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so.4
/usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so
/usr/lib/x86_64-linux-gnu/libnvparsers.so.4.1.2
/usr/lib/x86_64-linux-gnu/stubs/libnvrtc.so
/usr/lib/x86_64-linux-gnu/libnvcaffe_parser.a
/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.1
/usr/lib/x86_64-linux-gnu/libnvvm.so
/usr/lib/x86_64-linux-gnu/libnvinfer.a
/usr/lib/x86_64-linux-gnu/libnvvm.so.3
/usr/lib/x86_64-linux-gnu/libnvToolsExt.so.1
/usr/lib/x86_64-linux-gnu/libnvrtc.so.7.5
/usr/lib/x86_64-linux-gnu/libnvparsers.a
/usr/lib/x86_64-linux-gnu/libnvblas.so.7.5
/usr/lib/x86_64-linux-gnu/libnvToolsExt.so.1.0.0
/usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so.4.1.2
/usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so
/usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so
/usr/lib/x86_64-linux-gnu/libnvparsers.so
/usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.7.5.18
/usr/lib/x86_64-linux-gnu/libnvblas.so.7.5.18
/usr/lib/x86_64-linux-gnu/libnvvm.so.3.0.0
/usr/lib/x86_64-linux-gnu/libnvrtc.so
/usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.7.5
/usr/lib/x86_64-linux-gnu/libnvinfer.so.4.1.2
/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.390.30
/usr/lib/x86_64-linux-gnu/libnvrtc.so.7.5.17
/usr/lib/x86_64-linux-gnu/libnvblas.so
/usr/lib/x86_64-linux-gnu/libnvinfer.so.4
/usr/lib/x86_64-linux-gnu/libnvparsers.so.4
/usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so.4.1.2
/usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so.4

编译

/usr/src/tensorrt 文件夹拷贝到用户目录下,假设路径为 <tensorrt_srcpath>

第一个问题:

<tensorrt_srcpath>/tensorrt/samples 文件夹中有个 Makefile.config 文件,里面第4行:

CUDA_VER?=cuda-$(shell dpkg-query -f '$${version}\n' -W 'cuda-cudart-[0-9]*' | cut -d . -f 1,2 | sort -n | tail -n 1)

这一句是为了获取cuda版本的,我的机器是 CUDA 8.0 。我记得我第一次安装时,后面dpkg命令 输出的不是8.0,是一个很奇怪的数字,导致我不能编译 tensorRT 例程。 后来我直接在这句后面添加了一句: CUDA_VER=cuda-8.0 ,简单粗暴解决问题了。

这个问题好像是还是因为我之前安装 cuda 时是用 runfile 的方式安装的,用这种方式安装的cuda不会安装cuda的deb包,所以上面语句输出的是不对的,导致找不到cuda库目录,编译不能进行。

可以使用命令sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64.deb ,安装deb包,就可以了。或者像我那样添加 CUDA_VER=cuda-8.0 也可以。

如果安装cuda就是使用deb包安装的话,就不会出现这个问题。

第二个问题:

如果机器上安装了多个cuda版本,像我这个机器上 cuda8.0,9.0,9.1都装上了,上面语句得到的就只是 CUDA_VER=9.1,如果安装的是其他版本cuda的TensorRT的话肯定是不对的。

可以直接在第4行下面添加:

CUDA_INSTALL_DIR=/usr/local/cuda-9.0

3 TensorRT 使用流程

这是个很简单的流程,先简单了解一下,以后会深入研究更高级的用法。

在使用tensorRT的过程中需要提供以下文件(以caffe为例):

  1. A network architecture file (deploy.prototxt), 模型文件
  2. Trained weights (net.caffemodel), 权值文件
  3. A label file to provide a name for each output class. 标签文件

前两个是为了解析模型时使用,最后一个是推理输出时将数字映射为有意义的文字标签。

tensorRT的使用包括两个阶段, build and deployment:

  • build:Import and optimize trained models to generate inference engines

在这里插入图片描述

build阶段主要完成模型转换(从caffe或TensorFlow到TensorRT),在模型转换时会完成前述优化过程中的层间融合,精度校准。这一步的输出是一个针对特定GPU平台和网络模型的优化过的TensorRT模型,这个TensorRT模型可以序列化存储到磁盘或内存中。存储到磁盘中的文件称之为 plan file。

下面代码是一个简单的build过程:

//创建一个builder
IBuilder* builder = createInferBuilder(gLogger);// parse the caffe model to populate the network, then set the outputs
// 创建一个network对象,不过这时network对象只是一个空架子
INetworkDefinition* network = builder->createNetwork();//tensorRT提供一个高级别的API:CaffeParser,用于解析Caffe模型
//parser.parse函数接受的参数就是上面提到的文件,和network对象
//这一步之后network对象里面的参数才被填充,才具有实际的意义
CaffeParser parser;
auto blob_name_to_tensor = parser.parse(“deploy.prototxt”,
trained_file.c_str(),
*network,
DataType::kFLOAT);// 标记输出 tensors
// specify which tensors are outputs
network->markOutput(*blob_name_to_tensor->find("prob"));// Build the engine
// 设置batchsize和工作空间,然后创建inference engine
builder->setMaxBatchSize(1);
builder->setMaxWorkspaceSize(1 << 30);
//调用buildCudaEngine时才会进行前述的层间融合或精度校准优化方式
ICudaEngine* engine = builder->buildCudaEngine(*network);

上面的过程使用了一个高级别的API:CaffeParser,直接读取 caffe的模型文件,就可以解析,也就是填充network对象。解析的过程也可以直接使用一些低级别的C++API,比如:

ITensor* in = network->addInput(“input”, DataType::kFloat, Dims3{…});
IPoolingLayer* pool = network->addPooling(in, PoolingType::kMAX, …);

解析caffe模型之后,必须要指定输出tensor,设置batchsize,和设置工作空间。设置batchsize就跟使用caffe测试是一样的,设置工作空间是进行前述层间融合和张量融合的必要措施。层间融合和张量融合的过程是在调用builder->buildCudaEngine时才进行的。

  • deploy:Generate runtime inference engine for inference

在这里插入图片描述

deploy阶段主要完成推理过程,Kernel Auto-Tuning 和 Dynamic Tensor Memory 应该是在这里完成的。将上面一个步骤中的plan文件首先反序列化,并创建一个 runtime engine,然后就可以输入数据(比如测试集或数据集之外的图片),然后输出分类向量结果或检测结果。

tensorRT的好处就是不需要安装其他深度学习框架,就可以实现部署和推理。

以下是一个简单的deploy代码:这里面没有包含反序列化过程和测试时的batch流获取

// The execution context is responsible for launching the
// compute kernels 创建上下文环境 context,用于启动kernel
IExecutionContext *context = engine->createExecutionContext();// In order to bind the buffers, we need to know the names of the
// input and output tensors. //获取输入,输出tensor索引
int inputIndex = engine->getBindingIndex(INPUT_LAYER_NAME),
int outputIndex = engine->getBindingIndex(OUTPUT_LAYER_NAME);//申请GPU显存
// Allocate GPU memory for Input / Output data
void* buffers = malloc(engine->getNbBindings() * sizeof(void*));
cudaMalloc(&buffers[inputIndex], batchSize * size_of_single_input);
cudaMalloc(&buffers[outputIndex], batchSize * size_of_single_output);//使用cuda 流来管理并行计算
// Use CUDA streams to manage the concurrency of copying and executing
cudaStream_t stream;
cudaStreamCreate(&stream);//从内存到显存,input是读入内存中的数据;buffers[inputIndex]是显存上的存储区域,用于存放输入数据
// Copy Input Data to the GPU
cudaMemcpyAsync(buffers[inputIndex], input,
batchSize * size_of_single_input,
cudaMemcpyHostToDevice, stream);//启动cuda核计算
// Launch an instance of the GIE compute kernel
context.enqueue(batchSize, buffers, stream, nullptr);//从显存到内存,buffers[outputIndex]是显存中的存储区,存放模型输出;output是内存中的数据
// Copy Output Data to the Host
cudaMemcpyAsync(output, buffers[outputIndex],
batchSize * size_of_single_output,
cudaMemcpyDeviceToHost, stream));//如果使用了多个cuda流,需要同步
// It is possible to have multiple instances of the code above
// in flight on the GPU in different streams.
// The host can then sync on a given stream and use the results
cudaStreamSynchronize(stream);

可见使用了挺多的CUDA 编程,所以要想用好tensorRT还是要熟练 GPU编程。

4 Performance Results

来看一看使用以上优化方式之后,能获得怎样的加速效果:

在这里插入图片描述

可见使用tensorRT与使用CPU相比,获得了40倍的加速,与使用TensorFlow在GPU上推理相比,获得了18倍的加速。效果还是很明显的。

以下两图,是使用了INT8低精度模式进行推理的结果展示:包括精度和速度。

来自:GTC 2017,Szymon Migacz 的PPT

在这里插入图片描述

在这里插入图片描述

可见精度损失很少,速度提高很多。

上面还是17年 TensorRT2.1的性能,这里 是一个最新的TensorRT4.0.1的性能表现,有很详细的数据展示来说明TensorRT在inference时的强劲性能。

后面的博客中会进一步学习 tensorRT,包括官方例程和做一些实用的优化。

参考资料

  1. What’s the Difference Between Deep Learning Training and Inference?
  2. Discover the Difference Between Deep Learning Training and Inference
  3. GTC 2017,Szymon Migacz 的PPT
  4. NVIDIA TensorRT | NVIDIA Developer
  5. Deploying Deep Neural Networks with NVIDIA TensorRT
  6. TensorRT 3: Faster TensorFlow Inference and Volta Support
  7. tensorRT installation guide
  8. cuda installation guide
  9. NVIDIA TensorRT Performance Guide
  10. TensorRT 4 Accelerates Neural Machine Translation, Recommenders, and Speech
  11. ONNX

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

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

相关文章

进制转换中dbho是什么意思_什么是网段?二进制十进制如何互相转换?看完这篇,你就全明白了...

之前的文章讲了ip&#xff0c;子网掩码&#xff0c;网关的关系&#xff0c;今天着重讲一下网段。我们用傻瓜交换机通讯时&#xff0c;一个网段的设备才能互相通讯&#xff0c;怎么能判断两个ip是同一个网段呢&#xff1f;今天就简单的说一下。(这篇文章用语音听可以起到催眠作用…

软件需求与问题解决

&#xff08;一&#xff09; 小满当上项目经理后不久&#xff0c;参与了一个大项目。当时市场签下来的时候&#xff0c;公司里面是欢天喜地的。项目做了一年多。到了交付的时候&#xff0c;用户却很不满意&#xff0c;当初说好的东西&#xff0c;好多都变了卦。用户是上帝&…

flex 换主轴后子元素占满_Chrome72 嵌套 flex 布局修改,你的网站可能会发生布局错乱...

起源2019 年 1 月 29 日&#xff0c;Chrome72 正式版(72.0.3626.81)发布&#xff0c;本次发布带来了一个改变&#xff0c;且没有在更新日志中提及&#xff0c;该改变导致某些网站发生了布局错乱。该改变主要针对的是嵌套的flex布局&#xff0c;下面我们一起看下是怎么回事。问题…

路由到另外一个页面_Nextjs使用解读一(项目搭建与路由系统)

文章说明&#xff1a;1. 之前想搭建个人博客&#xff0c;由于学习的是react技术栈&#xff0c;所以就到处搜罗资料学了nextjs&#xff0c;配合koa就把博客搭起来了。该系列文章基于我的学习笔记&#xff0c;重新整理了一遍&#xff0c;如果有错误之处&#xff0c;还请指正。2. …

微信获取token -1000

最终翻看微信开发api找到需要去配置IP白名单。只需要配置访问来源IP即可。 转载于:https://www.cnblogs.com/yangjinqiang/p/8184663.html

产品技术和管理

为啥纯粹为消费者传递体验的活动可以价格不菲&#xff0c;几为暴利&#xff1f;——谈客户体验作为客户价值提升之源 不论产品还是服务&#xff0c;如果能够为消费者传递有益的体验&#xff0c;其价值就可以在一般的产品服务之上得以体现&#xff1b;附加了体验的产品&#xff…

jquery ztree 设置勾选_047 JAVA-jQuery

jQuery操作元素属性的值表单:<body><input type"button" name"" id"but1" value"测试获得属性值" /><hr />账号&#xff1a;<input type"text" name"sxtzh" id"zhanghao" value&q…

Opencv undefined reference to `cv::imread() Ubuntu编译

Ubuntu下编译一个C文件&#xff0c;C源程序中使用了opencv&#xff0c;opencv的安装没有问题&#xff0c;但是在编译的过程中出现如下错误&#xff1a; undefined reference to cv::imread(std::string const&, int)undefined reference to cv::noArray()undefined referen…

深度学习目标检测之 YOLO v1

这是继 RCNN&#xff0c;fast-RCNN 和 faster-RCNN 之后&#xff0c;rbg&#xff08;RossGirshick&#xff09;针对DL目标检测速度问题提出的另外一种框架。YOLO V1 增强版本GPU中能跑45fps&#xff0c;简化版本155fps。 论文名&#xff1a; 《You Only Look Once&#xff1a;…

编程珠玑番外篇

1.Plan 9 的八卦 在 Windows 下喜欢用 FTP 的同学抱怨 Linux 下面没有如 LeapFTP 那样的方便的工具. 在苹果下面用惯了 Cyberduck 的同学可能也会抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻烦的事情. 其实一点都不麻烦, 因为在 LINUX 系统上压根就不需要用 FTP. 为什么呢? 因…

BT下载原理分析

版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 BitTorrent协议。 BT全名为BitTorrent,是一个p2p软件,你在下载download的同时&#xff0c;也在为其他用户提供上传upload&#xff0c;因为大家是“互相帮助”&#xff0c;所以不会随着用户数的增加而…

表格列求和_excel表格制作,Excel表格的基本操作,包含制作一个表格10方面的知识...

创建表格&#xff0c;插入与删除一行一列或多行多行&#xff0c;一次移动一行一列或多行多列&#xff0c;拆分与合并单元格&#xff0c;单元格内换行&#xff0c;表格求和与求平均值是Excel表格的基本操作&#xff1b;除此之外&#xff0c;Excel表格的基本操作还包括调整行高列…

深度学习之 FPN (Feature Pyramid Networks)

论文题目&#xff1a;Feature Pyramid Networks for Object Detection论文链接&#xff1a;https://arxiv.org/abs/1612.03144论文代码&#xff1a;Caffe版本 https://github.com/unsky/FPN 《Feature Pyramid Networks for Object Detection》这篇论文主要解决的问题是目标检…

ISLR—第二章 Statistical Learning

Statistical Learning Y 和X的关系why estimate f 用来预测 预测的时候可以将f^当成一个black box来用&#xff0c;目的主要是预测对应x时候的y而不关系它们之间的关系。用来推断 推断的时候&#xff0c;f^不能是一个black box&#xff0c;因为我们想知道predictor和response之…

提高编程思想

虚函数和抽象函数有什么区别 虚函数是有代码的并明确允许子类去覆盖&#xff0c;但子类也可不覆盖,就是说可以直接用&#xff0c;不用重写 抽象函数是没有代码&#xff0c;子类继承后一定要重写 ****************************************************************** 在一…

Windows数据库编程接口简介

数据库是计算机中一种专门管理数据资源的系统&#xff0c;目前几乎所有软件都需要与数据库打交道&#xff08;包括操作系统&#xff0c;比如Windows上的注册表其实也是一种数据库&#xff09;&#xff0c;有些软件更是以数据库为核心因此掌握数据库系统的使用方法以及数据库系统…

2019如何转换2010_9102年,你还不知道PPT怎么转换成视频吗?小心落伍了

你在刷抖音的时候有没有刷过这类视频&#xff1a;成为人生赢家必备的书单、5个让你看透人性的电影、6个让你升职加薪的APP...如果你细心观察的话&#xff0c;会发现这类视频的做法基本都是一个样的&#xff0c;像在翻相册一样&#xff0c;一页页过去&#xff0c;所以它们也叫做…

深度学习之RetinaNet

总述 Focal loss主要是为了解决one-stage目标检测中正负样本比例严重失衡的问题。该损失函数降低了大量简单负样本在训练中所占的权重&#xff0c;也可理解为一种困难样本挖掘。 目标识别有两大经典结构: 第一类是以Faster RCNN为代表的两级识别方法&#xff0c;这种结构的第…

python的datetime函数_Python连载8-datetime包函数介绍

一、datetime包(上接连载7内容)1.函数&#xff1a;datetime(1)用法&#xff1a;输入一个日期&#xff0c;来返回一个datetime类?(2)格式&#xff1a;datetime.datetime(年&#xff0c;月&#xff0c;日&#xff0c;hour&#xff0c;minute&#xff0c;second)其中hour,minute,…

深度学习之 soft-NMS

论文&#xff1a;《Improving Object DetectionWith One Line of Code》soft-NMS 英文论文链接&#xff1a;https://arxiv.org/pdf/1704.04503.pdfsoft-NMS github 链接&#xff1a;https://github.com/bharatsingh430/soft-nms 绝大部分目标检测方法&#xff0c;最后都要用到…