TensorRt(2)快速入门介绍

news2025/1/20 22:39:04

文章目录

  • 1、使用ONNX部署的示例
    • 1.1、导出模型
    • 1.2、设置batch size批处理大小
    • 1.3、指定数值精度
    • 1.4、转换模型
    • 1.5、部署模型
  • 2、使用ONNX转换为engine再部署的示例
  • 2.1、导出ONNX模型
      • 2.1.1、从TensorFlow中导出ONNX
      • 2.1.1、从PyTorch中导出ONNX
    • 2.2、ONNX 转化为 TensorRT Engine
    • 2.3、在Python运行时中部署TensorRt engine
  • 3、使用tensorRT runtime API
    • 3.1、准备测试容器并构建 TensorRT 引擎
    • 7.2、在c++中运行运行Engine
    • 3.3. 在Python中运行Engine

TensorRt的安装,不同环境下的安装不做介绍,注意不同版本对系统和软件版本的要求。先介绍tensorrt的常规开发周期流程:
在这里插入图片描述
上图对应的主要三个步骤:

训练

这个部分不属于TensorRt的内容范畴。通常训练网络模型使用高性能的服务器,使用的框架常见的有Tensorflow、pytorch、caffe、mxnet等。之后被TendorRt加载或转成换能够加载的模型格式。

编译

加载第三方模型(目前仅支持onnx、caffe、uff)进行模型编译并从多个方面优化,生成序列化的engine模型,以plan形式保存。通常模型加载转换通常有三种方式:

(1)使用 TF-TRT(部署中介绍),提供模型转换和高级运行时 API,并且能够回退到 TensorRT 不支持特定运算符的 TensorFlow 实现。

(2)自动模型转换和部署的一个更高效的选项是使用 ONNX 进行转换。ONNX是一个与框架无关的选项,可与 TensorFlow、PyTorch 等中的模型一起使用。TensorRT 支持使用 TensorRT API 从 ONNX 文件自动转换,或者使用工具 trtexec 。ONNX 转换的结果是单一的engine,比使用 TF-TRT 开销更少。

(3)使用TensorRT 网络定义 API手动构建 TensorRT engine,直接使用TensorRT 操作构建与目标模型相同的网络,之后从框架导出模型的权重并将它们加载到这个TensorRT 网络中。这种方式性能较高,可定制化,但是操作可能比较复杂,适合小型网络。

部署

加载优化后的模型到TensorRt runtime部署进行inference。
在这里插入图片描述
(1)TF-TRT 是 TensorRT 的高级 Python 接口,可直接与 TensorFlow 模型一起使用。TF-TRT 转换会生成一个 TensorFlow 图,其中插入了 TensorRT 操作。可以像运行任何其他使用 Python 的 TensorFlow 模型一样运行 TF-TRT 模型。

(2)TensorRT运行时 API允许最低的开销和最细粒度的控制,但 TensorRT 本身不支持的运算符必须作为插件实现(此处提供了预编写的插件库)。使用运行时 API 进行部署的最常见路径是使用框架中的 ONNX 导出,本指南的下一节将对此进行介绍。

(3)NVIDIA Triton 推理服务器是一款开源推理服务软件,使团队能够从任何框架(TensorFlow、TensorRT、PyTorch、ONNX Runtime 或自定义框架)、本地存储或谷歌云平台或任何 GPU 上的 AWS S3 部署训练有素的 AI 模型,或基于 CPU 的基础架构(云、数据中心或边缘)。它是一个灵活的项目,具有几个独特的功能 - 例如异构模型的并发模型执行和同一模型的多个副本(多个模型副本可以进一步减少延迟)以及负载平衡和模型分析。如果您必须通过 HTTP 为您的模型提供服务,这是一个不错的选择 - 例如在云推理解决方案中。您可以找到 NVIDIA Triton 推理服务器主页这里和这里的文档。

1、使用ONNX部署的示例

ONNX 转换通常是将 ONNX 模型自动转换为 TensorRT engine的最高效方式。本节将在部署预训练 ONNX 模型的背景下介绍 TensorRT 转换的五个基本步骤。

使用 ONNX 格式从 ONNX 模型库转换预训练的 ResNet-50 模型;一种与框架无关的模型格式,可以从大多数主要框架中导出,包括 TensorFlow 和 PyTorch。
在这里插入图片描述

1.1、导出模型

准备一个onnx模型,可以来自网络框架直接导出(见后面章节 3.1 ),也可以是onnx model zoo中直接下载:

wget https://s3.amazonaws.com/download.onnx/models/opset_8/resnet50.tar.gz
tar xzf resnet50.tar.gz

解压得到预训练的模型resnet50/model.onnx。

1.2、设置batch size批处理大小

batch size会对 TensorRT 在我们的模型上执行的优化产生很大影响。在推理优先考虑延迟时,选择小批量batch size;有线考虑吞吐量时,会考虑更大的batch size。较大的batch size需要更长的时间来处理,但由于并行操作会降低每个样本的平均处理时间。

TensorRt能够动态的处理只有运行时才确定的batch size,即固定batch size大小能够被优化。当前示例中指定为64,那么需要在tensorflow或pytorch中指定导出onnx的batch size为64。

BATCH_SIZE=64

这里下载的model.onnx预训练模型的batch size已经是被设置为64。

1.3、指定数值精度

推理通常需要比训练更少的数值精度,较低的精度可以为您提供更快的计算和更低的内存消耗,而不会牺牲有意义的准确性。支持TF32, FP32, FP16, 和 INT8。

FP32是大多数网络框架训练的默认精度,因此在推理前进行指定

import numpy as np
PRECISION = np.float32

1.4、转换模型

有多种工具可将模型从 ONNX 转换为 TensorRT engine,常见是使用附带工具trtexec ,它还能对engine进行其他分析。
通过以下命令,可以将onnx格式转换为engine格式

trtexec --onnx=resnet50/model.onnx --saveEngine=resnet_engine.trt

1.5、部署模型

成功创建engine后,可以选择 python 或 c++ 运行时api部署运行。这里使用python简化的包装器ONNXClassifierWrapper并用随机数据输入进行测试。

from onnx_helper import ONNXClassifierWrapper
import numpy as np

PRECISION = np.float32

N_CLASSES = 1000 # Our ResNet-50 is trained on a 1000 class ImageNet task
trt_model = ONNXClassifierWrapper("resnet_engine.trt", [BATCH_SIZE, N_CLASSES], target_dtype = PRECISION)

BATCH_SIZE=64
dummy_input_batch = np.zeros((BATCH_SIZE, 224, 224, 3))

predictions = trt_model.predict(dummy_input_batch)

注意,wrapper构造时并不会加载engine,只有第一次进行推理时才会进行。因此,第一次处理会有模型初始化、内部构造、硬件资源分配等耗时操作,称为warmup。

2、使用ONNX转换为engine再部署的示例

1、使用ONNX部署的示例 基础上, 增加其他框架转换为)ONNX模型再部署的内容。

ONNX 交换格式提供了一种从许多框架(包括 PyTorch、TensorFlow 和 TensorFlow 2)导出模型的方法,以便与 TensorRT 运行时一起使用。TensorRt 使用 ONNX 导入模型需要其模型中的运算符是被支持的,否则需要提供 不支持的任何运算符的插件实现。(可以在此处连链接找到 TensorRT 的插件库)。

2.1、导出ONNX模型

使用 ONNX 项目中的 keras2onnx 和 tf2onnx 工具,能够轻松地将 TensorFlow 模型转换导出为 ONNX 模型。

在这里插入图片描述

2.1.1、从TensorFlow中导出ONNX

# 从keras.applications中导入 ResNet-50 模型 . 这将加载具有预训练权重的 ResNet-50 副本。
from tensorflow.keras.applications import ResNet50
model = ResNet50(weights='imagenet')

# 将 ResNet-50 模型转换为 ONNX 格式。
import tf2onnx
model.save('my_model')
!python -m tf2onnx.convert --saved-model my_model --output temp.onnx
onnx_model = onnx.load_model('temp.onnx')

# 在 ONNX 文件中设置明确的批量大小。
# 笔记:默认情况下,TensorFlow 不会设置明确的批量大小。
import onnx
BATCH_SIZE = 64
inputs = onnx_model.graph.input
for input in inputs:
    dim1 = input.type.tensor_type.shape.dim[0]
    dim1.dim_value = BATCH_SIZE

# 保存 ONNX 文件。
model_name = "resnet50_onnx_model.onnx"
onnx.save_model(onnx_model, model_name)

2.1.1、从PyTorch中导出ONNX

# 从torchvision中导入 ResNet-50 模型. 这将加载具有预训练权重的 ResNet-50 副本。
import torchvision.models as models
resnext50_32x4d = models.resnext50_32x4d(pretrained=True)

#从 PyTorch 保存 ONNX 文件。
#注意:我们需要一批数据来保存来自 PyTorch 的 ONNX 文件。我们将使用虚拟批次。
import torch
BATCH_SIZE = 64
dummy_input=torch.randn(BATCH_SIZE, 3, 224, 224)

#保存 ONNX 文件。
import torch.onnx
torch.onnx.export(resnext50_32x4d, dummy_input, "resnet50_onnx_model.onnx", verbose=False)

2.2、ONNX 转化为 TensorRT Engine

存在两种转换方式, trtexecTensorRT API, 这里以后者为例,将 resnet50_onnx_model.onnx 转换导出为 resnet_engine.trt :

trtexec --onnx=resnet50_onnx_model.onnx --saveEngine=resnet_engine.trt

2.3、在Python运行时中部署TensorRt engine

TensorRT 有许多可用的runtime运行时。当性能很重要时,TensorRT API 是运行 ONNX 模型的好方法。在下一节中,将使用 C++ 和 Python 中的 TensorRT 运行时 API 部署更复杂的 ONNX 模型。

3、使用tensorRT runtime API

对于模型转换和部署而言,性能最高、可自定义选项的方式是使用TensorRT的运行时api。

TensorRT 包括一个带有 C++ 和 Python 绑定的独立运行时,与使用 TF-TRT 集成并在 TensorFlow 中运行相比,它们通常具有更高的性能和更可定制性。C++ API 的开销较低,但 Python API 可以很好地与 Python 数据加载器和库(如 NumPy 和 SciPy)配合使用,并且更易于用于原型设计、调试和测试。

下面使用具有 ResNet-101 主干的完全卷积模型,对任意输入分辨率的图像进行语义分割为例,说明c++和python两种情况下的runtime api使用。使用主要包含以下三个步骤:

  • 准备:启动测试容器,并将从 PyTorch 模型导出的 ONNX 使用 trtexec 转换为TensorRT engine
  • C++ 运行时 API:使用 engine 和 TensorRT 的 C++ API 运行inference推理
  • Python 运行时 AP:使用 engine 和 TensorRT 的 Python API 运行inference推理

3.1、准备测试容器并构建 TensorRT 引擎

1、从TensorRT 开源软件存储库下载此快速入门教程的源代码。

$ git clone https://github.com/NVIDIA/TensorRT.git
$ cd TensorRT/quickstart

2、将torch.hub中FCN-Resnet-101预训练模型转换为ONNX
使用教程自带的导出脚本生成一个ONNX模型保存到fcn-resnet101.onnx,还会生成一个大小为 1282x1026 的.ppm测试图像。

  • 启动 NVIDIA PyTorch 容器以运行导出脚本
     $ docker run --rm -it --gpus all -p 8888:8888 -v `pwd`:/workspace -w /workspace/SemanticSegmentation nvcr.io/nvidia/pytorch:20.12-py3 bash
    
  • 运行导出脚本以将预训练模型转换为 ONNX
    $ python export.py
    
    注: FCN-ResNet-101只有一个输入维度[batch,3,height,weight],一个输出维度 [batch,21,height,weight],包含对应于 21 个类别标签的预测的非标准化概率。将模型导出到 ONNX 时,附加一个 argmax在输出层产生最高概率的每像素类标签。

3、使用 trtexec 工具从ONNX模型构建为TensorRT engine
trtexec 能够将onnx模型构建为tensorRt engine,并使用 运行时api部署。通过TensorRt ONNX parser加载ONNX模型构建一个TensorRT network graph网络图,之后利用TensorRT Builder API生成优化后的engine。构建过程可能耗时,通常是离线进行的。

trtexec --onnx=fcn-resnet101.onnx --fp16 --workspace=64 --minShapes=input:1x3x256x256 --optShapes=input:1x3x1026x1282 --maxShapes=input:1x3x1440x2560 --buildOnly --saveEngine=fcn-resnet101.engine

成功执行将生成一个engine文件、控制台输出一些有关成功的信息。在 TensorRT Developer Guide 中介绍了trtexec工具构建engine的各种参数。

4、可选的,可以使用trtexec工具对随机输入进行engine验证

trtexec --shapes=input:1x3x1026x1282 --loadEngine=fcn-resnet101.engine

–shapes参数用于设定推力时要求输入的动态尺寸,当成功还行时,会有类似如下输出

&&&& PASSED TensorRT.trtexec # trtexec --shapes=input:1x3x1026x1282 --loadEngine=fcn-resnet101.engine

如果提示输入名称不正确,例如minst的测试

[11/27/2022-13:42:54] [I] [TRT] [MemUsageChange] TensorRT-managed allocation in IExecutionContext creation: CPU +0, GPU +0, now: CPU 0, GPU 0 (MiB)
[11/27/2022-13:42:54] [E] Cannot find input tensor with name "input" in the engine bindings! Please make sure the input tensor names are correct.
[11/27/2022-13:42:54] [E] Invalid tensor names found in --shapes flag.
[11/27/2022-13:42:54] [E] Inference set up failed
&&&& FAILED TensorRT.trtexec [TensorRT v8403] # trtexec.exe --shapes=input:64x1x28x28 --loadEngine=minst.trt

说明网络的输入名称不正确,可以通过Netron查看对应onnx模型的输入和输出等名称和尺寸,
在这里插入图片描述
将输入参数修改为 --shapes=Input3:64x1x28x28 即可正常执行。

7.2、在c++中运行运行Engine

使用测试容器编译运行c++的语义分割示例,

$ make
$ ./bin/segmentation_tutorial

后面步骤秒数如何使用 反序列化Plan (加载优化后的engine文件对象)进行前线推理inference。

1、从文件中发序列化读取TensorRT engine

std::ifstream engineFile(engine, std::ios::binary)
engineFile.seekg(0, engineFile.end);
long int fsize = engineFile.tellg();
engineFile.seekg(0, engineFile.beg);

std::vector<char> engineData(fsize);
engineFile.read(engineData.data(), fsize);

util::UniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger())};

util::UniquePtr<nvinfer1::ICudaEngine> mEngine(runtime->deserializeCudaEngine(engineData.data(), fsize, nullptr))

注意:TensorRT中对象销毁使用成员函数destroy(),在示例程序中使用自定义删除器deleter的智能指针管理生命周期。

struct InferDeleter
{
    template <typename T>
    void operator()(T* obj) const {
        if (obj) obj->destroy();
    }
};

template <typename T>
using UniquePtr = std::unique_ptr<T, util::InferDeleter>;

2.TensorRT 执行上下文 context封装了执行状态,例如用于在推理期间保存中间激活张量的持久设备内存
由于分割模型是在启用动态形状的情况下构建的,因此必须为推理执行指定输入的形状。可以查询network输出形状以确定输出缓冲区的相应维度。

auto input_idx = mEngine->getBindingIndex("input");
assert(mEngine->getBindingDataType(input_idx) == nvinfer1::DataType::kFLOAT);

auto input_dims = nvinfer1::Dims4{1, 3 /* channels */, height, width};

context->setBindingDimensions(input_idx, input_dims);

auto input_size = util::getMemorySize(input_dims, sizeof(float));

auto output_idx = mEngine->getBindingIndex("output");
assert(mEngine->getBindingDataType(output_idx) == nvinfer1::DataType::kINT32);

auto output_dims = context->getBindingDimensions(output_idx);

auto output_size = util::getMemorySize(output_dims, sizeof(int32_t));

注意:network中 I/O 的绑定索引可以按名称查询。

3. 在准备推理时,为所有输入和输出分配 CUDA 设备内存,处理图像数据并将其复制到输入内存中,并生成引擎绑定列表
对于语义分割,输入图像数据通过使用均值归一化[0.485, 0.456, 0.406]和标准偏差 [0.229, 0.224, 0.225] 拟合到[0, 1]。 请参阅输入预处理要求火炬视觉模型在这里。当前模型输入的预处理要求可以参看torchvision models。 此操作由实用程序类RGBImageReader抽象实现。

void* input_mem{nullptr};
cudaMalloc(&input_mem, input_size);

void* output_mem{nullptr};
cudaMalloc(&output_mem, output_size); 

const std::vector<float> mean{0.485f, 0.456f, 0.406f};
const std::vector<float> stddev{0.229f, 0.224f, 0.225f};

auto input_image{util::RGBImageReader(input_filename, input_dims, mean, stddev)};
input_image.read();

auto input_buffer = input_image.process();

cudaMemcpyAsync(input_mem, input_buffer.get(), input_size, cudaMemcpyHostToDevice, stream);

4. 使用上下文context的启动推理执行 executeV2 或者enqueueV2 方法
执行完成后,我们将结果复制回主机缓冲区并释放所有设备内存分配。

void* bindings[] = {input_mem, output_mem};
bool status = context->enqueueV2(bindings, stream, nullptr);
auto output_buffer = std::unique_ptr<int>{new int[output_size]};
cudaMemcpyAsync(output_buffer.get(), output_mem, output_size, cudaMemcpyDeviceToHost, stream);
cudaStreamSynchronize(stream);

cudaFree(input_mem);
cudaFree(output_mem);

5. 使用伪彩色可视化并保存到.ppm文件
此操作由实用程序类ArgmaxImageWriter抽象实现。

const int num_classes{21};
const std::vector<int> palette{
   (0x1 << 25) - 1, (0x1 << 15) - 1, (0x1 << 21) - 1};
auto output_image{util::ArgmaxImageWriter(output_filename, output_dims, palette, num_classes)};
output_image.process(output_buffer.get());
output_image.write();

前面的测试图像进行语义分割后的处理结果为

3.3. 在Python中运行Engine

安装python依赖包

$ pip install pycuda

启动 Jupyter 并使用提供的令牌通过浏览器登录 http://<host-ip-address>:8888

$ jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root

打开tutorial-runtime.ipynb笔记本并按照其步骤操作。

Python环境下的运行时 runtime api 是 c++ api的隐射,参见Running an Engine in C++。

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

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

相关文章

27个超实用Chrome控制台调试技巧 Source 全局搜索(关注更新)

谷歌开发者工具提供了一系列的功能来帮助开发者高效 Debug 网页应用&#xff0c;让他们可以更快地查找和修复 bug。在谷歌的开发者工具中&#xff0c;有非常多有用的小工具&#xff0c;但是很多开发者并不知道。通过这篇文章&#xff0c;我把我常用的那些高效 Debug 的 Chrome …

ARM 汇编指令集1_2

一、两个概念&#xff1a;指令与伪指令 &#xff08;汇编&#xff09;指令&#xff0c;是 CPU 机器指令的助记符&#xff0c;经过编译后会得到一串10组成的机器码&#xff0c;可以由 CPU 读取执行。&#xff08;汇编&#xff09;伪指令&#xff0c;本质上不是指令&#xff08;…

版本控制利器——changelog

问题描述 当前&#xff0c;我们项目需要进行版本的确定&#xff0c;人工审核代码已接近尾声&#xff0c;但为了防止后续继续出现该问题&#xff0c;我希望能够做到在每次push到master时&#xff0c;更新changelog 将每一个版本的commit记录下来&#xff0c;类似于下列 解决…

K8s 集成 Jenkins 部署Go Web项目

风离不摆烂学习日志 Day9 K8s 集成 Jenkins 部署Go Web项目 Dockerfile FROM golang:alpine as builder # 需要go环境 MAINTAINER fl1906WORKDIR /work # 源 RUN go env -w GOPROXYhttps://goproxy.cn && go env -w CGO_ENABLED0 && go env -w GO111MODULEon C…

Splunk UBA 从 Ldap 成功导入 HR 数据

1: 今天在配置Splunk UBA 的HRdata 和asset data 的时候,都要用到ldap, 所以还非要装add-on:\ add-on 的名字: Splunk Supporting Add-on for Active Directory (SA-LDAPSearch) (SA-LDAPSearch) 这个add-on 的配置: Configure the Splunk Supporting Add-on for Activ…

使用腾讯云cos搭建图片服务器

背景 当我们能不希望把图片上传到自己的服务器时&#xff0c;可以采用腾讯云cos帮我们免费代图片&#xff0c;我们只需要在自己的数据库里保存一个图片地址就行了 二、注册账号免费试用 1、注册腾讯云账号 按照腾讯云的注册方式&#xff0c;注册自己的账号 2、实名认证 选择个…

blender boxCutter插件

boxCutter可以用来进行bool运算 打开boxCutter 方法1&#xff1a;物体模式在舞台左侧选择boxCutter图标 方法2&#xff1a;物体模式按N&#xff0c;在舞台右侧的侧边栏里选择boxCutter 方法3 物体模式alta 打开BoxCutter后&#xff0c;舞台上方&#xff0c;N键的弹出菜单&…

进行 Spring 6 迁移的最佳方式

介绍 在本文中&#xff0c;我们将了解如何将现有应用程序迁移到Spring 6以及如何充分利用此升级。 本文中的提示基于我在Hypersistence Optimizer和高性能 Java Persistence 项目中添加对 Spring 6 的支持所做的工作。 爪哇 17 首先&#xff0c;Spring 6 将最低 Java 版本提…

2022.11.27 学习周报

文章目录摘要文献阅读1.题目2.摘要3.介绍4.基于后向传播算法的多时间尺度RNN4.1 循环神经网络&#xff08;RNN&#xff09;4.2 基于多时间尺度的RNN预测模型5.实验5.1 数据简介5.2 数据分析5.3 评价指标5.4 实验结果6.结论深度学习1.循环神经网络&#xff08;RNN&#xff09;1.…

Flutter 5 大本地数据库解决方案

Flutter 5 大本地数据库解决方案 原文 https://levelup.gitconnected.com/top-5-local-database-solutions-for-flutter-development-6351cd494070 前言 这里列出了最流行的数据库解决方案以及代码示例。 选择正确的数据管理系统对于提高效率和可 extension 性以及影响可用性和…

3DConvCaps:3DUNet与卷积胶囊编码器用于医学图像分割

摘要 卷积神经网络需要大量的训练数据&#xff0c;无法处理物体的姿态和变形。此外其中的池化层也倾向于丢弃位置等重要的信息。CNN对旋转和仿射变换非常敏感。 胶囊网络是最近出现的一种新型体系结构&#xff0c;其通过动态路由和卷积步长代替池化层&#xff0c;在部分整体表…

steam搬砖项目的核心内容解答

大家好&#xff0c;我是阿阳 如果你有个几千块钱积蓄&#xff0c;想做点小生意&#xff0c;又不知道做啥&#xff0c;那我建议你来做steam搬砖&#xff0c;steam搬砖是什么呢&#xff1f;steam搬砖怎么赚钱的呢&#xff1f;你听我一步步跟你说来 steam搬砖是什么呢&#xff1f…

详解GMM高斯混合模型EM模型

一般讲到GMM就会讲到EM。 我不过多的介绍EM算法。这里只是举一些例子来看看真实的GMM怎么用EM算的。 一、GMM的作用 记住GMM的作用&#xff0c;就是聚类&#xff01; 二、GMM有hard和soft两种 hard-GMM和soft-GMM是为了对标k-means和soft k-means。在中文互联网上搜索到的GM…

【SVM分类】基于matlab哈里斯鹰算法优化支持向量机SVM分类【含Matlab源码 2243期】

⛄一、哈里斯鹰算法简介 HHO算法用数学公式来模拟现实中哈里斯鹰在不同机制下捕捉猎物的策略.在HHO中&#xff0c;哈里斯鹰是候选解&#xff0c;猎物随迭代逼近最优解.HHO算法包括两个阶段&#xff1a;全局探索阶段、局部开采阶段。 1 全局探索阶段 在这一阶段中&#xff0c;哈…

springboot thymeleaf使用

导入依赖 <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <a…

springboot集成swagger并更换主题

前言 swagger对于web一个开发人员&#xff0c;可以说是非常熟悉的了&#xff0c;之前都是用的公司搭好的框架拿来就用&#xff0c;自己也没有研究过&#xff0c;最近想搞一个自己拿来练手的基础框架&#xff0c;因此来记录一下springboot集成swagger的过程和注意事项。 swagg…

云南白药正在度过“中年危机”

​文丨熔财经 作者|kinki 今年&#xff0c;正是云南白药创制的120周年&#xff0c;虽然是一个百年老字号品牌&#xff0c;但在过去的数年间&#xff0c;云南白药在新品研制、跨界发展乃至品牌推广上都做了不少新尝试。今年&#xff0c;云南白药更提出了“141”战略&#xff0…

备战 2023 春招,P7大咖位手打 26 大后端面试专题神技,1500+题解析助力offer

年过后&#xff0c;不少人已经蓄势待发&#xff0c;信心满满地准备投递简历&#xff0c;到处面试&#xff0c;在不同的 Offer 之中择优而栖。 与此同时&#xff0c;也有人会悔恨自己这半年进步不大&#xff0c;每天噼里啪啦敲代码&#xff0c;但面对那些不能再熟悉的 Java 面试…

分布式解决方案 Percolator--详解

Percolator简介 Google在2012年将Percolator的架构设计作为论文发表&#xff0c;其目的是构建于BigTalbe的基础上&#xff0c;主要用于网页搜索索引等服务。由于BigTable只支持单行级别的事务&#xff0c;不支持多行事务等更复杂的事务&#xff0c;因此Percolator的诞生就是为了…

数据增强:Simple Questions Generate Named Entity Recognition Datasets

数据增强的方式一般是无标注数据集的情形的一种解决方式&#xff0c;今天的讲座报告中对这问题做了梳理。11.27学术报告文章&#xff0c;应该是韩旭的报告。 文章目录问题背景一、论文核心二、文章内容三、experiments总结问题背景 还是在于方法的创新&#xff0c;虽然是数据增…