【LLM】vLLM部署与int8量化

Acceleration & Quantization

image-20231217234546788

vLLM

vLLM是一个开源的大型语言模型(LLM)推理和服务库,它通过一个名为PagedAttention的新型注意力算法来解决传统LLM在生产环境中部署时所遇到的高内存消耗和计算成本的挑战。PagedAttention算法能有效管理注意力机制中的键和值,将它们分割成更小、更易于管理的块,从而减少了vLLM的内存占用,并使其吞吐量超过传统LLM服务方法。

下图展示了三种不同配置下的语言模型(分别为LLaMA-13B、LLaMA-7B配备A100-40GB GPU和LLaMA-7B配备A10G GPU)在处理文本生成推理(TGI)任务时的服务吞吐量(每分钟处理的请求量)。我们可以看到,vLLM在所有配置中都提供了最高的吞吐量,这表明其在处理大规模模型推理任务时的高效性。

Uploaded image

在LLaMA-13B, A100-40GB配置中,vLLM的吞吐量为154.2 req/min,而传统的HuggingFace(HF)模型和TGI分别只有6.4和61.8 req/min。同样,在LLaMA-7B, A100-40GB和LLaMA-7B, A10G配置中,vLLM都显著超过了HF和TGI。

这些数据强调了vLLM在提高服务吞吐量方面的能力,特别是当每个请求需要一个输出完成时,vLLM的吞吐量是HF的24倍,并且是TGI的2.2x到2.5x。当每个请求需要三个输出完成时,vLLM的性能更加突出,其吞吐量是HF的8.5x到15x,并且是TGI的3.3x到3.5x。

可以认为,vLLM因其在服务吞吐量方面的卓越性能,特别适合于生产环境中部署大型语言模型,尤其是在需要高效率处理大量推理请求的场景。

Cache

Cache是目前生成式的Transformer常用的一种加速手段。

下图解释了在有缓存的情况下计算(Q * K^T)* V,即查询(Q)、键的转置(K^T)和值(V)的乘积的过程。这是一个注意力机制计算的典型步骤,常见于自然语言处理中的Transformer架构。

image-20231217235932215

  • 步骤1:查询(Q)与键的转置(K^T)相乘,然后再与值(V)相乘得到结果。在这个过程中,键(K)和值(V)被缓存到缓存内存中,以便将来使用。

  • 步骤N:在一个后续步骤中,从缓存中恢复键(K)和值(V),并只计算当前步骤中所需的部分。这样可以提高计算效率,因为可以重复使用之前步骤中已计算和缓存的部分。

蓝色部分表示在当前步骤中将要计算的值,而紫色部分表示将从缓存中取出的值。这种方法通过减少重复计算和利用缓存中的数据,提高了计算效率。

这两张图比较了在自然语言处理模型中,特别是在Transformer架构的注意力机制中,有和没有KV-Cache(键值缓存)时的处理过程。

Without KV-Cache:

image-20231218000647970

  • 每一个输入Token(例如,“我是一个好”的每个词)都会通过Q、K、V矩阵得到相应的查询(Q)、键(K)和值(V)。
  • 然后进行Attention计算,此过程包括Q和K的点积,然后用这个结果来加权V,得到最后的输出。
  • 由于没有KV-Cache,每次处理新Token时都需要重新计算整个序列。
With KV-Cache:

image-20231218001010611

  • 在处理序列的第一个Token时,也是通过Q、K、V矩阵得到查询、键和值。
  • 计算Attention后,K和V会被存储在缓存中。
  • 当处理下一个Token时(例如,“好”字),模型会使用缓存中的K和V值,而不是重新计算它们。
  • 这样可以提高效率,因为模型不必每次都处理整个序列,而是可以重复使用已经计算过的值。

在这个例子中,“我是一个好人”中的每个字会被依次处理,而使用KV-Cache可以减少重复计算,从而提高模型的效率。

为什么Cache只存储K、V,而不存储Q?

在Transformer架构的注意力机制中,Q(Query)代表当前输入Token的查询信息,它需要与所有先前和当前的K(Key)进行匹配来决定注意力的分配。因为Q是专门针对当前正在处理的Token的,所以它每次都是独一无二的,即便在没有Cache的情况下,对于序列中的每个新Token,Q都会改变。

相反,K(Key)和V(Value)通常与序列中已经处理过的Token相关,它们可以被缓存起来供后续的Token查询使用。存储K和V使得模型可以重用之前Token的信息,而不必重新计算它们,这样可以大大节省计算资源。如果存储Q值,对于每个新的Token,我们仍然需要重新计算整个Q与所有K的匹配度,这不会减少计算量。因此,只缓存KV值而不缓存Q值,是为了优化计算效率。

PageAttention

铺垫了这么多我们来介绍一下vLLm的PageAttention方法。

image-20231218100236221

PageAttention机制是一种优化的注意力机制,通过将键(Key)和值(Value)分块来处理,以减少计算资源的使用。

0. Before generation

image-20231218100602826

上图表示的是文本生成过程开始前的状态,即逻辑KV缓存块和物理KV缓存块的初始状态

逻辑内存是按需分配的 (由PyTorch或者cuda?),而物理内存是实际存储数据的硬件 (GPU) 。

在图中,我们有一个提示(Prompt):“Alan Turing is a computer scientist”,接下来需要生成(Completion)的文本。逻辑KV缓存块还没有填充任何数据,这表示我们还没有开始文本生成过程。物理KV缓存块同样是空的,显示没有任何键(Key)或值(Value)被存储。

1. Allocate space and store the prompt’s KV cache

image-20231218101421180

在这张图中,我们看到了提示(Prompt)“Alan Turing is a computer scientist” 的KV缓存已经被分配和存储。逻辑KV缓存块中的Block 0被填充了单词 “Alan”、“Turing”、“is”、“a”,而Block 1则被填充了 “computer” 和 “scientist”。

同时,物理KV缓存块表格显示Block 0实际上被映射到了物理块7,且有4个槽位被填充;Block 1映射到了物理块1,有2个槽位被填充。这表示系统已经为这些词分配了物理存储空间,并且准备好了用于生成过程中的查询。这是将提示转换为KV对并存储到缓存系统中的第一步,为接下来的文本生成做准备。

2. Generated 1st token

image-20231218101734287

到了这步,文本生成进程已经开始,并且生成了第一个词“and”。在逻辑KV缓存块中,我们可以看到“and”已经被添加到Block 1中,这个块之前已经包含了“computer scientist”。

同时,物理KV缓存块的表格显示Block 1现在填充了3个槽位,这表明“and”已经被添加到了物理块1中。这反映了随着生成过程的进行,逻辑和物理缓存块是如何动态更新以包含新生成的Token的。这允许模型在后续的生成步骤中利用这些新的信息。

3. Generated 2nd token

image-20231218102200166

接下来,第二个词“mathematician”已经被生成。逻辑KV缓存块的Block 1现在包含了“computer”、“scientist”、“and”以及新生成的“mathematician”。

4. Generated 3rd token. Allocate new block.

image-20231218102623397

在这张图中,我们看到了文本生成过程的进一步发展。此时"renowned" 这个词已经生成并被添加到了逻辑KV缓存块的Block 2中。对应地,在物理KV缓存块中,一个新的块(Block 3)被分配来存储这个新词,这显示了缓存系统如何动态地根据需求分配空间。

5. Generated 4th token.

image-20231218102816955

接下来生成的词 “for”,它也被添加到了逻辑KV缓存块的Block 2中。同样,物理KV缓存块的Block 3现在填充了两个槽位,包含了 “renowned” 和 “for”。

这一系列图像描绘了一个动态的、逐步构建的过程,其中每个新生成的Token都被添加到逻辑和物理KV缓存中,以支持连续的文本生成。随着生成的进行,物理缓存块持续更新,以确保快速访问到相关的键和值。

Prompt to Muitiple Outputs

image-20231218135929073

0. Shared prompt: Map logical blocks to the same physical blocks.

image-20231218140007123

在处理多个序列(例如 Seq A 和 Seq B)时vLLM会共享一个共同的提示(Prompt)。这种设置可能用于一个模型同时进行多个任务,或者在多任务学习环境中。逻辑KV缓存块展示了不同序列可以共享相同的提示“ The future of artificial intelligence is”,而这个提示被映射到相同的物理KV缓存块上。

这种共享方式表明,当多个序列处理相同的初始信息时,可以节省资源和时间,因为模型不需要为每个序列单独存储或计算相同的提示信息。这对于优化处理效率和加快推理速度是有益的。

1. Seq A generated 1st token.

image-20231218141549458

此时我们假设 Seq A 生成了一个额外的词 “likely”,并且这个词被添加到了它的逻辑KV缓存块中。物理KV缓存块显示了对应的条目,并有一个引用计数“Ref count: 2”,这意味着有两个不同的逻辑块正在引用这个物理块。

2. Copy-on-Write: Copy to a new block.

这些图展示了“写时复制”(Copy-on-Write)策略在缓存管理中的应用。当Seq A和Seq B共享相同的物理KV缓存块,并且当Seq A更改其内容时(添加了“likely”),引用计数从2减少到1,表明这些物理块不再被Seq B共享。然后,Seq A的更改被复制到一个新的物理块中,以防止对Seq B的逻辑视图产生影响。这样,每个序列可以独立地维护和更新其状态,而不会干扰到其他序列。

image-20231218142539552

image-20231218142625456

image-20231218142651531

这种机制在多任务处理和内存优化中非常有用。

3. Seq B generated 1st token. No copy needed.

image-20231218142726011

接下来当 Seq B 生成时,不会进行 Copy。

4. Seq A and B generated 2nd token.

image-20231218142959326

同理生成第2个token。

5. Seq A and B generated 3rd token.

image-20231218143026307

分配新的 Logical-Physical 映射,生成第3个token。

6. Seq A and B generated 4th token.

image-20231218143141013

继续生成。

Summary

总结一下,vLLM优化了内存使用上的一些浪费,同时也能够兼容huggingface上一些主流的模型。

image-20240107194320244

vLLM推理部署

vLLM的调用

有两种方式可以调用vLLM来加载模型,一种是通过API Server,另一种是离线推理。

  • API Server
!VLLM_USE_MODELSCOPE=True python -m vllm.entrypoints.api_server \
--model="./output/models/llama-"
curl http://localhost:8000/generate \
-d '{
"prompt": "San Francisco is a",
"use_beam_search": true,
"n": 4,
"temperature": 0
}'
from transformers import AutoModelmodel = AutoModel.from_pretrained("../Llama-2-7B-Chat-fp16", load_in_8bit=True)
for name, params in model.named_parameters():print(name, params.dtype)

image-20240107224635255

  • Offline Batched Inference
from vllm import LLM, SamplingParams
import torch# device = torch.device('cuda:2')
llm = LLM(model="../Llama-2-7B-Chat-fp16", gpu_memory_utilization=0.7)
# llm.to(device)

这里的gpu_memory_utilization参数就是vLLM提供的内存优化参数。

可以做个实验

这是我原来的内存,目前只有13M的占用

image-20240108153317911

如果我们直接在24G的现存中加载半精度的Llama-2-7B,会报OM(Out of Memory)

from transformers import AutoModelmodel = AutoModel.from_pretrained("../Llama-2-7B-Chat-fp16")
model = model.to("cuda:0")

事实上,要加载半精度的Llama-2-7B需要大约29152MiB的内存空间(对,很尴尬地卡住了24G内存)。

但是,使用vLLM的gpu_memory_utilization这个参数,我们可以对加载内容进行大小优化

from vllm import LLM, SamplingParamsllm = LLM(model="../Llama-2-7B-Chat-fp16", gpu_memory_utilization=0.7)
image-20240108160842691

可以看到大约用了17G的内存。

当然,空间不可能无限优化下去,如果进一步测试可以发现gpu_memory_utilization=0.3是能够加载一个7B模型的极限(大约15G)。

下面来测试一下

prompts = ["Hello, my name is","The president of the United States is","The capital of France is","The future of AI is","One way to crack a password","I know unsensored swear words such as"
]
sampling_params = SamplingParams(temperature=0.8, top_p=0.95)
outputs = llm.generate(prompts, sampling_params)# Print the outputs.
for output in outputs:prompt = output.promptgenerated_text = output.outputs[0].textprint(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

image-20240107225150561

还是很快的,1秒可以迭代15句。

vLLM with LoRA

vLLM其实一开始并不支持peft,因为显然它在对内存优化的时候改变了一些模型的结构(vllm-vllm-model_executor_models下可以看到它将主要模型的结构都自己重新写了一遍),但这就导致了我们无法对vLLM加载得模型进行LoRA微调。

image-20240108164835286

不过好在Cassia大佬提交了一个repo,加上了vLLM对peft的支持(需要安装以下版本的vllm)

pip install git+https://github.com/troph-team/vllm.git@support_peft

我们用OPT为例看看Cassia大佬究竟做了些什么(lora)

from vllm import LLM, SamplingParams
from vllm.model_executor.adapters import lora# Create an LLM.
llm = LLM(model="../opt-125m", gpu_memory_utilization=0.2)prompts = ["Hello, my name is","The capital of France is","The future of AI is",
]sampling_params = SamplingParams(temperature=0, top_k=-1)  # 不做任何sampling,每次只选择最大概率的tokenoutputs = llm.generate(prompts, sampling_params)

image-20240107225451488

for output in outputs:prompt = output.promptgenerated_text = output.outputs[0].textprint(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

image-20240107225525834

llm.llm_engine.workers[0].model

image-20240107225625595

在这里使用lora库的LoRAModel加载了一个adapter(看名字应该是基于imdb数据finetune的一个opt-adapter)

# Add LoRA adapter
lora.LoRAModel.from_pretrained(llm.llm_engine.workers[0].model, "../opt-125m-imdb-lora")outputs = llm.generate(prompts, sampling_params)for output in outputs:prompt = output.promptgenerated_text = output.outputs[0].textprint(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")

image-20240107225650822

可以看到微调后OPT输出的内容是不一样的。

vllm@support_peft

简单过一下vllm的结构。

最重要的是vllm-vllm-core下的block_manager.py,负责对内存进行动态分配

image-20240108163032619

我们在前两篇文章中讲过,peft是如何加上LoRA这个旁路的:

  1. 遍历原来model的所有层数,找到我们需要加上LoRA的某(几)个module(比如self-attetion);
  2. 把找到的module单独抠出来,放到一个新的module(LoRA module)里面;
  3. LoRA module中包含A、B两个矩阵,输入给到A、B矩阵后得到的输出 与 原来module的输出相加,得到新的输出。

如果要让vLLM支持peft技术,其实就是要让LoRA中的module对应上vLLM中的module,然后把LoRA module挂到module的旁路上面去。

还是以vllm-vllm-model_executor_models下的llama.py为例,仔细审阅一下LlamaAttention模块

image-20240108165901019

hidden_states作为输入计算qkv。显然,我们需要让hidden_states同时作为输入给到LoRA module。这里Cassia大佬的实现是在lora.py中,他在load_adapter方法中将原本的qkv_proj模块抠出来扔到了VllmLoRA这个LoRA module中,然后将原来的module替换成新的

image-20240108171130294

如此一来,当qkv, _ = self.qkv_proj(hidden_states)这句话调用qkv_proj这个模块时,实际上调用的是被lora.py替换后的qkv_proj

最后来看下VllmLoRA这个LoRA module是怎么写的

image-20240108170532064

  1. 类定义

    • VllmLoRA 继承自 LoraLayerColumnParallelLinear
  2. 构造函数

    • 构造函数接收多个参数,包括输入大小(input_size)、输出大小(output_size)、LoRA参数(rlora_alphalora_dropout)和其他关键字参数。
    • init_lora_weights 用于控制是否初始化 LoRA 权重。
    • 使用 ColumnParallelLinearLoraLayer 的构造函数初始化该层。
    • 重置线性层的参数。
    • 使用 update_layer 方法初始化 q_projk_projv_proj 这三个LoRA层。
    • active_adapter
  3. 前向传播(forward 方法)

    • 这个方法首先调用 ColumnParallelLinear 的前向传播方法来处理输入。
    • 将输入数据转换为 LoRA 层所需的数据类型。
    • 对于 q_projk_projv_proj,使用 LoRA 层进行计算,并将结果相加到原始的线性层输出上。
    • 返回最终结果和偏置。

以上,就是vLLM支持LoRA的方法。

Decoding methods

之前在将分页机制的时候讲到一个Prompt to Muitiple Outputs,一个prompt如何生成多种不同的output,这取决于使用的decoding method。

Greedy Search

Greedy search就是贪心,每次取下一步最大的那个token

image-20240108200637709

Beam Search

Beam search需要一个参数num_beams,用于决定往后看几步,每次取后几步最大的beam中第一个token

image-20240108200815028

Sampling

每次对下一个token生成的概率进行采样,每个token都有可能被选择

image-20240108201239411

Top-K Sampling

Sampling存在的问题是,它只考虑了上图中那种比较理想的分布情况。然而在现实中,很多token分布的值可能会很接近,所以Top-K Sampling的做法是对采样区域进行一定的限制,只在前K个token之间进行采样

image-20240108201956475

Top-p (nucleus) Sampling

还有种Sampling方法叫做Top-p (nucleus) sampling,当前p个分布之和大于定值(比如0.9)时,将前p个token作为取样范围

image-20240108202125867

Quantization 量化

网上开源的很多模型本身是全精度的,我们用半精度进行加载量化效果较好,但是如果在进行进一步的量化效果可能就会很差。原因在于全精度意味着更多的参数,在复杂情况下,量化带来的误差会被越放越大,导致模型的表现大幅下降。

下图就是半精度16-bit基准线与8-bit基准线的对比,随着参数量变大8-bit基准线会骤降

image-20240108204437428

于是,量化中最常用的一种技术LLM.int8出现了,它可以用8-bit达到16-bit基准线的效果。

8-bit量化

在介绍LLM.int8技术之前,先看看8-bit量化是如何做的。

8-bit量化的意思是将原来半精度浮点数转换成int_8(int_8的范围是-177~177),举个例子:

Example 1
  • 原始数据: x = [1.52, 2.64, -3.45, 4.32]
  • 量化过程:
    • x_absmax = 4.32 # 找最大
    • scale_factor=127 / x_absmax ≈ 29.4 # 计算量化因素
    • q_x = round([1.52, 2.64, -3.45, 4.32] * scale_factor) =[45, 78, -101, 127] # 将半精度浮点数映射到int_8的数组,占用空间会少一半
  • 反量化过程:
    • x’ = q_x/scale_factor = [1.53, 2.61, 3.44, 4.32] # 除了最大值不变,其他参数会有略微误差
Problem 1

量化过程存在量化误差

  • 原始数据:x=[1.52,2.64,-3.45,4.32]
  • 反量化结果:x’=[1.53,2.61,3.44,4.32]

如何降低量化误差,提高量化精度?

  • 使用更多的量化参数(scale_factor)
  • 矩阵乘法A*B可以看作是A的每一行乘上B的每一列,为A的每每一行和B的每一列单独设置scale_factor,这种方式被称之为Vector-wise量化
Example 2
  • Data: x = [1.42,1.51,1.54,45.3]
  • Quantization process:
    • x_absmax = 45.3
    • scale_factor=127 / x_absmax ≈ 2.8
    • q_x = round([1.42, 1.51, 1.54, 4.32] * scale_factor) = [4, 4,4, 127]
  • Dequantization process:
    • x’ = q_x/scale_factor = [1.43, 1.43, 1.43, 45.36]
Problem 2

很明显,当最大值相对较大时,其他数值的误差就会很大:

  • 离群值:超出某个分布范围的值通常称为离群值
    • x = [1.42, 1.51, 1.54, 45.3]
  • 8位精度的动态范围极其有限,因此量化具有多个大值的向量会产生严重误差
  • 误差在一点点累积的过程中会导致模型的最终性能大幅度下降

LLM.int8()

其实很简单,LLM.int8()方法通过将16位浮点数的特征和权重矩阵分解成小的子矩阵,并将它们转换为8位整数来进行高效的矩阵乘法运算。

该方法特别处理矩阵中的离群值(通过均值和方差),这些值保持为16位浮点数以保证计算的精度。

完成乘法运算后,方法会对结果进行反量化处理,将8位整数输出转换回16位浮点数,然后将离群值结果和常规结果累加以得到最终输出。

image-20240109102147862

这个过程优化了计算效率,同时尽量减少了精度损失。

还是做个实验

from transformers import AutoModelmodel = AutoModel.from_pretrained("../Llama-2-7B-Chat-fp16", load_in_8bit=True)

用int_8来加载,只用了7661M

image-20240109103324840
for name, params in model.named_parameters():print(name, params.dtype)

image-20240109103504476

LLM.int8()会自动处理哪些需要转成int_8,哪些需要保留float_16。

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

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

相关文章

虾皮商品标题:如何创建有效的虾皮商品标题

虾皮(Shopee)平台是一个非常受欢迎的电商平台,为卖家提供了一个广阔的销售渠道。在虾皮上,一个有效的商品标题是吸引潜在买家注意力的关键元素之一。一个好的商品标题能够吸引更多的点击和浏览量,从而提高销售机会。下…

什么是API网关代理?

带有API网关的代理服务显着增强了用户体验和性能。特别是对于那些使用需要频繁创建和轮换代理的工具的人来说,使用 API 可以节省大量时间并提高效率。 了解API API(即应用程序编程接口)充当服务提供商和用户之间的连接网关。通过 API 连接&a…

【PostgreSQL】在DBeaver中实现序列、函数、视图、触发器设计

【PostgreSQL】在DBeaver中实现序列、函数、触发器、视图设计 基本配置一、序列1.1、序列使用1.1.1、设置字段为主键,数据类型默认整型1.1.2、自定义序列,数据类型自定义 1.2、序列延申1.2.1、理论1.2.2、测试1.2.3、小结 二、函数2.1、SQL直接创建2.1.1…

Python教程37:使用turtle画一个戴帽子的皮卡丘

---------------turtle源码集合--------------- Python教程36:海龟画图turtle写春联 Python源码35:海龟画图turtle画中国结 Python源码31:海龟画图turtle画七道彩虹 Python源码30:海龟画图turtle画紫色的小熊 Python源码29&a…

基于宝塔搭建Discuz!论坛

一、安装宝塔 我是在我的虚拟机上安装图的宝塔 虚拟机版本:Ubuntu 18.04 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh 6dca892c安装完成之后在浏览器输入你的地址 https://你的域名(或…

基于JavaWeb+BS架构+SpringBoot+Vue校园一卡通系统的设计和实现

基于JavaWebBS架构SpringBootVue校园一卡通系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 第一章 概述 4 1.1 研究背景 4 1.2研究目的及意义 4 1.3国内外发展现状 4 1…

网络安全之你的浏览器记录真的安全吗?

密码是每个人最私密的东西,轻易是不会展示给他人的,那么我如何能知道你电脑上浏览器里保存的密码呢?浏览器是大家在网上冲浪最常用的软件,在登录一些网站填写账号密码后,浏览器为了方便大家使用,会提示是否…

unity小程序websocket:nginx配置https (wss)转http (ws)及其他问题解决

目录 前言 实际运用场景 处理流程如下 nginx配置ssl和wss 配置过程中遇到的问题 1、无法连接服务器 2、通过IP可以访问,域名却不行 问题描述 解决 3、如何判断该域名是否备案了 前言 为了服务器网络的通用性,我们在实现移动端的游戏转微信小程序…

Python教程38:使用turtle画动态粒子爱心+文字爱心

Turtle库是Python语言中的一个标准库,它提供了一种有趣的方式来介绍编程和图形绘制的基本概念。Turtle库使用一个虚拟的“海龟”来绘制图形。你可以控制海龟的方向、速度和位置,通过向前移动、向左转或向右转等命令来绘制线条、圆弧多边形等图形。 -----…

Java 反射(一)

反射 1.反射的介绍 1.反射机制允话程序在执行期间借助于Refelction API取得任何类的信息(比如成员变量,构造器,成员方法等)并能操作对象的属性及方法,反射在设计模式和框架底层都会用到 2.加载完类之后,在…

API调试怎么做?Apipost快速上手

前言 Apipost是一款支持 RESTful API、SOAP API、GraphQL API等多种API类型,支持 HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外,Apipost 还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程,提高了研发…

CodeWave智能开发平台--03--目标:应用创建--08联系人管理

摘要 本文是网易数帆CodeWave智能开发平台系列的第11篇,主要介绍了基于CodeWave平台文档的新手入门进行学习,实现一个完整的应用,本文主要完成08联系人管理 CodeWave智能开发平台的11次接触 CodeWave参考资源 网易数帆CodeWave开发者社区…

【VRTK】【Unity】【VR开发】Linear Drives

课程配套学习项目源码资源下载 https://download.csdn.net/download/weixin_41697242/88485426?spm=1001.2014.3001.5503 【概述】 前面一篇讨论了角度运动机制,本篇讨论线性运动机制。和角度运动机制类似,线性运动机制提供了更为仿真的互动机制。也分为基于物理的和不基于…

doris部署

doris-2.0.1.1部署安装 一、下载doris安装包二、解压到/data下,修改名称三、修改fe配置文件四、启动doris-fe五、验证doris-fe六、修改be配置文件七、启动doris-be八、mysql中连接be,在Doris中添加后端节点九、设置密码 一、下载doris安装包 wget https…

ArrayBlockingQueue原理探究

类图结构 同样,为了能从全局一览ArrayBlockingQueue的内部构造,先来看它的类图。 ArrayBlockingQueue的内部有一个数组items,用来存放队列元素,putindex变量表示入队元素下标,takelndex是出队下标,count统计队列元素个…

雅意2.0:打造专为中文优化的300亿参数多语言模型

前言 雅意2.0,作为一款专注于中文语境的开源大型语言模型,其在多语言处理方面的能力尤为突出。该模型不仅具有300亿参数规模的庞大体量,还在多个关键领域取得了显著的技术突破。 Huggingface模型下载:https://huggingface.co/wen…

大前端nestjs入门教程系列(四):如何nestjs整合mysql数据库

经过前面的几篇文章,想必大家已经对nestjs有了基础的了解,那么这篇文章就带大家玩玩数据库,学会了这篇,就离大前端又进了一步 Nest与数据库无关,使你可以轻松地与任何 SQL 或 NoSQL 数据库集成。 根据你的喜好&#xf…

【面试高频算法解析】算法练习3 双指针

前言 本专栏旨在通过分类学习算法,使您能够牢固掌握不同算法的理论要点。通过策略性地练习精选的经典题目,帮助您深度理解每种算法,避免出现刷了很多算法题,还是一知半解的状态 专栏导航 二分查找回溯(Backtracking&…

权威mcc mnc 列表网址

MCC-MNC.net 链接: MCC-MNC.net 这个网站提供的mcc mnc 比下面itu文档中更全。 itu.int 2023年发布的mcc mnc分配信息: 链接: PDF T-SP-E.212B-2023 若想获取最新的可以参考如下截图查询:

LeetCode刷题12:贪心算法解决1402.做菜顺序

一个厨师收集了他 n 道菜的满意程度 satisfaction ,这个厨师做出每道菜的时间都是 1 单位时间。 一道菜的 「 like-time 系数 」定义为烹饪这道菜结束的时间(包含之前每道菜所花费的时间)乘以这道菜的满意程度,也就是 time[i]*sa…