使用推测解码 (Speculative Decoding) 使 Whisper 实现 2 倍的推理加速
Open AI 推出的 Whisper 是一个通用语音转录模型,在各种基准和音频条件下都取得了非常棒的结果。最新的 large-v3 模型登顶了 OpenASR 排行榜,被评为最佳的开源英语语音转录模型。该模型在 Common Voice 15 数据集的 58 种语言中也展现出了强大的多语言性能,在 42 种语言上的单词错误率 (WER) 低于 30%。
尽管转录准确度非常优秀,但推理速度非常缓慢。即使利用 flash attention 、半精度和 分块 等优化推理技术,1 小时长度的音频在 16GB T4 GPU 上也需要超过 6 分钟的转录时间。
在本文中,我们将演示如何运用推测解码将 Whisper 的推理时间缩减 2 倍,同时在数学上确保完全取得与原模型 相同的输出。因此,这种方法可以完美地替换现有的 Whisper 流水线,因为它可以在不降低准确性的情况下免费获得 2 倍的加速。想要看附带有更简洁解释的全部代码,请参阅配套的 Google Colab。
推测解码
推测解码由 Yaniv Leviathan 等人在 Fast Inference from Transformers via Speculative Decoding 中提出。其思想是,一个更快的 辅助模型 通常会生成和更大的 主模型 相同的 token。
首先,辅助模型会通过自回归生成 个 候选 token 序列: 。在下图中,辅助模型生成了一个包含 5 个候选 token 的序列: The quick brown sock jumps
。

尽管这些候选 token 可以快速生成,但它们可能与主模型预测的 token 不同。因此,在第二步中,候选 token 被传入主模型以进行“验证”。主模型将候选 token 作为输入,并执行 单次前馈传播。主模型的输出是每个步骤中“正确”token 的序列 。

在上图中,我们看到主模型预测的前三个 token 与辅助模型的 token 一致: <span style="color:green">
The quick brown 但是,辅助模型的第四个候选 token: “ <span style="color:red">
sock”与主模型的正确 token: “ <span style="color:green">
fox”不一致。
我们知道,所有候选 token 一直到第一个不匹配之前都是正确的 ( <span style="color:green">
The quick brown),因为这些与主模型的预测一致。但是,在第一个不匹配之后,候选 token 开始偏离主模型实际预测的 token。因此,我们可以用主模型的正确 token ( <span style="color:green">
fox) 替换第一个不正确的候选 token ( <span style="color:red">
sock),并放弃之后所有预测的 token,因为这些已经逐渐偏离主模型的预测。经过校正的序列 The quick brown fox
现在成为辅助模型的新输入:

然后,辅助模型再次通过自回归推理,生成一组新的 个候选 token,这些 token 再次通过主模型的单次前馈传播进行验证。

由于我们在生成的时候使用的快速的辅助模型进行自回归,并且缓慢的主模型仅用于验证前馈传播,解码过程将大大加快。此外,经过主模型前馈传播验证后可以确保与仅使用主模型时获得完全相同的输出。这使得推测解码可以完美地替换现有的 Whisper 流水线,因为我们可以确定会取得相同质量的输出。
为了最大限度地减少延迟,辅助模型应该比主模型快得多,同时尽可能频繁地预测相同的 token 分布。实际上,这两个属性之间需要权衡: 模型越快,其准确度越低。然而,由于所有预测 token 中的 70-80% 往往是“较易”的 token,此权衡倾向于选择一个更快的模型,而不是一个更准确的模型。因此,辅助模型应该至少比主模型快 3 倍 (越快越好),同时在示例中正确预测所有较“易”token。剩余的 20-30% 更“难”的 token 可以由更大的主模型进行验证。
选择辅助模型的唯一约束是它必须与主模型使用相同的词汇表。也就是说,辅助模型必须使用与主模型完全一对一相同的分词器。因此,如果我们想对诸如 large-v2 (多语言) 的 Whisper 多语言版本使用推测解码,我们需要选择诸如 tiny 的 Whisper 多语言版本作为辅助模型。而如果我们想对诸如 medium.en 的 Whisper 英文版本使用推测解码,我们需要选择诸如 tiny.en 的 Whisper 英文版本作为辅助模型。目前,large-v3 是唯一一个扩展了词汇量的 Whisper 检查点,因此与以前的 Whisper 检查点不兼容。
现在我们已经了解了推测解码背后的原理,我们准备实际实现它。在 🤗 Transformers 库中,推测解码被实现为“辅助生成 (Assisted Generation)”推理策略。欲了解更多实现细节,建议读者阅读 Joao Gante 关于 辅助生成 的精彩博文。
英文语音转录
基准实现
我们首先使用 Whisper large-v2 进行基准测试,以获得推理速度的基准数值。我们可以通过便捷的 AutoModelForSpeechSeq2Seq
和 AutoProcessor
类加载主模型及其对应的处理器。我们将以 float16
精度加载模型,并通过传递 low_cpu_mem_usage=True
确保加载时间尽可能少。此外,我们要确保模型以 safetensors 格式加载,方法是传递 use_safetensors=True
。最后,我们将传递参数 attn_implementation="sdpa"
,以通过 PyTorch 的 SDPA 注意力内核 进行 Flash 注意力加速。
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessordevice = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32model_id = "openai/whisper-large-v2"model = AutoModelForSpeechSeq2Seq.from_pretrained(model_id,torch_dtype=torch_dtype,low_cpu_mem_usage=True,use_safetensors=True,attn_implementation="sdpa",
)
model.to(device)processor = AutoProcessor.from_pretrained(model_id)
让我们加载将用于基准测试的英语语音转录数据集。我们将加载 LibriSpeech ASR 中验证数据集的 clean 分组中的 73 个样本组成的小型数据集。这大约有 9MB 的数据,因此非常轻量且可以快速下载到设备上。
from datasets import load_datasetdataset = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
对于基准测试,我们只想测量生成时间,所以让我们编写一个简短的辅助函数来测量此步骤运行的时间。下面的函数将同时返回解码的 token 和运行模型所需的时间:
import timedef generate_with_time(model, inputs, **kwargs):start_time = time.time()outputs = model.generate(**inputs, **kwargs)generation_time = time.time() - start_timereturn outputs, generation_time
现在我们可以迭代语音数据集中的音频样本,并统计整体生成时间:
from tqdm import tqdmall_time = 0
predictions = []
references = []for sample in tqdm(dataset):audio = sample["audio"]inputs = processor(audio["array"], sampling_rate=audio["sampling_rate"], return_tensors="pt")inputs = inputs.to(device=device, dtype=torch.float16)output, gen_time = generate_with_time(model, inputs)all_time += gen_timepredictions.append(processor.batch_decode(output, skip_special_tokens=True, normalize=True)[0])references.append(processor.tokenizer._normalize(sample["text"]))print(all_time)
Output:
100%|██████████| 73/73 [01:37<00:00, 1.33s/it]
72.99542546272278
很好!我们看到转录 73 个样本花了 73 秒。让我们检查一下预测的 WER:
from evaluate import loadwer = load("wer")
print(wer.compute(predictions=predictions, references=references))
Output:
0.03507271171941831
我们的最终基准数值为 73 秒,WER 为 3.5%。
推测解码
现在让我们加载推测解码的辅助模型。在此示例中,我们将使用 Whisper 蒸馏后的版本 distil-large-v2。蒸馏模型只使用了 Whisper 中 32 个解码器层中的 2 个编码器。因此,它比 Whisper 快 6 倍,同时在分布测试集上的 WER 性能相比于蒸馏前仅下降了 1%。这使其成为理想的辅助模型,因为它在转录准确性和生成速度方面都非常优秀。
我们即将发布 Distil-Whisper 的改进版本,在 token 分布中具有更佳的对齐性,这将进一步提高推测解码性能。关注 Distil-Whisper 存储库 来追踪最新的更新信息。
由于 Distil-Whisper 使用与 Whisper 模型完全相同的编码器,我们可以在主模型和辅助模型之间共享编码器。然后,我们只需要从 Distil-Whisper 加载 2 层解码器作为“仅解码器”模型。我们可以通过便捷的 AutoModelForCausalLM
自动类实现这一点。在实践中,相比于仅使用主模型,这仅增加了 8%的 VRAM 占用量。
from transformers import AutoModelForCausalLMassistant_model_id = "distil-whisper/distil-large-v2"assistant_model = AutoModelForCausalLM.from_pretrained(assistant_model_id,torch_dtype=torch_dtype,low_cpu_mem_usage=True,use_safetensors=True,attn_implementation="sdpa",
)assistant_model.to(device)
我们可以为推测解码的基准测试定义一个新的函数。与前面的函数唯一的区别是,我们在对 .generate
的调用中传递辅助模型:
def assisted_generate_with_time(model, inputs, **kwargs):start_time = time.time()outputs = model.generate(**inputs, assistant_model=assistant_model, **kwargs)generation_time = time.time() - start_timereturn outputs, generation_time
让我们使用 Distil-Whisper 作为 Whisper 的助手运行推测解码的基准测试:
all_time = 0
predictions = []
references = []for sample in tqdm(dataset):audio = sample["audio"]inputs = processor(audio["array"], sampling_rate=audio["sampling_rate"], return_tensors="pt")inputs = inputs.to(device=device, dtype=torch.float16)output, gen_time = assisted_generate_with_time(model, inputs)all_time += gen_timepredictions.append(processor.batch_decode(output, skip_special_tokens=True, normalize=True)[0])references.append(processor.tokenizer._normalize(sample["text"]))print(all_time)
Outputs:
100%|██████████| 73/73 [00:38<00:00, 1.88it/s]
32.69683289527893
使用推测解码,推理时间仅为 33 秒,比之前快 2.2 倍!让我们验证一下 WER 是否相同:
print(wer.compute(predictions=predictions, references=references))
Outputs:
0.03507271171941831
太完美了!再次达到 3.5%的 WER,因为我们的输出与仅使用主模型的时候完全相同。
推测解码也可以与基础的 🤗 Transformers pipeline API 一起用于推理。下面,我们使用模型和处理器实例化管道,然后使用它来转录测试数据集中的第一个样本。这可以扩展为转录任意长度的音频样本,包括进行批处理:
from transformers import pipelinepipe = pipeline("automatic-speech-recognition",model=model,tokenizer=processor.tokenizer,feature_extractor=processor.feature_extractor,max_new_tokens=128,chunk_length_s=15,batch_size=4,generate_kwargs={"assistant_model": assistant_model},torch_dtype=torch_dtype,device=device,
)sample = dataset[0]["audio"]
result = pipe(sample)
print(result["text"])
Outputs:
Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel.
使用 Whisper 和 Distil-Whisper 运行推测解码的端到端代码示例可在 Distil-Whisper 模型卡 中找到。它将本文中涵盖的推理阶段组合成一个代码示例。
多语言语音转录
Distil-Whisper 是英语语音转录的最佳辅助模型,因为它与原始 Whisper 模型的 WER 误差率仅相差 1%,而对短长语音样本的推理速度提高了 6 倍。然而,官方的 Distil-Whisper 检查点仅支持英语,这意味着它们无法用于多语言语音转录。
要使用推测解码进行多语言语音转录,您可以使用 官方 Whisper 多语言检查点 之一,或者 Whisper 的微调版本。在撰写本文时,Hugging Face Hub 上已有超过 5000 个微调过的 Whisper 检查点,支持超过 100 种语言。这些为选择表现出色的辅助模型提供了极好的起点。在此示例中,我们将使用最小的官方多语言检查点 Whisper tiny。您可以使用任意一个您的语言中微调过的不同检查点!
让我们为新的辅助模型 Whisper tiny 加载权重。由于 Whisper tiny 的编码器与 large-v2 不同,这次我们将使用 AutoModelForSpeechSeq2Seq
类同时加载编码器和解码器:
assistant_model_id = "openai/whisper-tiny"assistant_model = AutoModelForSpeechSeq2Seq.from_pretrained(assistant_model_id,torch_dtype=torch_dtype,low_cpu_mem_usage=True,use_safetensors=True,attn_implementation="sdpa",
)assistant_model.to(device);
我们的基准数据集,将从 VoxPopuli 数据集的荷兰语 (“nl”) 部分中加载 73 个样本:
dataset = load_dataset("sanchit-gandhi/voxpopuli_dummy", "nl", split="validation")
非常好!现在我们可以像前面一样重新运行我们的 Whisper large-v2 模型的基准测试。我们所做的唯一更改是在 generate 函数中传递语言和任务参数,以确保执行语音转录 (而不是语音翻译)。推测解码完全兼容语音转录和翻译任务。只需如下所示设置任务参数即可:
all_time = 0
predictions = []
references = []for sample in tqdm(dataset):audio = sample["audio"]inputs = processor(audio["array"], sampling_rate=audio["sampling_rate"], return_tensors="pt")inputs = inputs.to(device=device, dtype=torch.float16)output, gen_time = generate_with_time(model, inputs, language="nl", task="transcribe")all_time += gen_timepredictions.append(processor.batch_decode(output, skip_special_tokens=True, normalize=True)[0])references.append(processor.tokenizer._normalize(sample["normalized_text"]))wer_result = wer.compute(predictions=predictions, references=references)print("Time:", all_time)
print("WER:", wer_result)
Outputs:
100%|██████████| 73/73 [02:05<00:00, 1.72s/it]
Time: 116.50992178916931
WER: 0.127190136275146
没错!我们的基准时间为 117 秒,WER 为 12.8%。让我们使用推测解码重新运行生成过程:
all_time = 0
predictions = []
references = []for sample in tqdm(dataset):audio = sample["audio"]inputs = processor(audio["array"], sampling_rate=audio["sampling_rate"], return_tensors="pt")inputs = inputs.to(device=device, dtype=torch.float16)output, gen_time = assisted_generate_with_time(model, inputs, language="nl", task="transcribe")all_time += gen_timepredictions.append(processor.batch_decode(output, skip_special_tokens=True, normalize=True)[0])references.append(processor.tokenizer._normalize(sample["normalized_text"]))wer_result = wer.compute(predictions=predictions, references=references)print("Time:", all_time)
print("WER:", wer_result)
Outputs:
100%|██████████| 73/73 [01:08<00:00, 1.06it/s]
Time: 62.10229682922363
WER: 0.127190136275146
Nice!我们达到了 12.8% 的 WER,但这次的推理时间只有 62 秒,表示速度提高了 1.9 倍。考虑到加载辅助模型的低开销和确保获得完全相同输出的数学证明,推测解码为现有的 Whisper 管道提供了完美的即插即用的替代方案。
高效推测解码的策略
在本最终部分,我们将介绍两种策略,以确保使用推测解码时获得可能最快的推理时间。
辅助模型
我们的目标是选择一个至少比主模型快 3 倍 并且 正确转录至少 70-80% 的预测 token (通常是示例中的“更简单”token) 的辅助模型。如果您想要转录某种特定语言,一种有效的策略是训练两个不同大小的 Whisper 模型,并将其中一个用作另一个的辅助模型:
首先,微调 Whisper large-v3 以用作主模型
其次,在同一数据集上蒸馏 Whisper large-v3 以用作快速的辅助模型
微调和蒸馏都可以提高主模型和辅助模型在您选择的语言上的 WER 性能,同时最大化 token 分布的对齐。有关 Whisper 微调的完整指南,请参阅 此处,有关蒸馏的指南请参阅 此处。
批次大小
值得注意的是,使用推测解码获得的最大速度提升来自批次大小为 1。对于批处理推测解码,批处理中的所有候选 token 必须与验证 token 相匹配,才能被接受。如果批处理中给定位置的 token 不一致,则所有在该位置之前的候选 token 将被丢弃。因此,推测解码更倾向于较小的批次大小。在实践中,我们发现推测解码可以提供速度提升,直到批次大小达到 4 为止。当批次大小超过 4 时,推测解码的推理速度比仅用主模型还要慢。有关完整结果,请参阅 Distil-Whisper 论文 的第 D.3 节。
结论
在本博文中,我们介绍了推测解码的推理策略,以及如何将其应用于语音转录的 Whisper 模型。我们展示了如何实现 2 倍的速度提升,同时数学上确保获得与仅使用原始模型相同的输出。我们鼓励您尝试将推测解码用作现有 Whisper 管道的即插即用替代方案,因为使用额外的辅助模型的开销很小,并且可以保证获得相同的转录结果。
致谢
本博客由 Sanchit Gandhi 撰写。非常感谢 Patrick von Platen 和 Pedro Cuenca 的建设性意见,以及 Joao Gante 在 🤗 Transformers 中实现辅助生成的贡献。
🤗 宝子们可以戳 阅读原文 查看文中所有的外部链接哟!
英文原文: https://hf.co/blog/whisper-speculative-decoding
作者: Sanchit Gandhi
译者: Hu Yaoqi (yaoqi)
相关文章:

使用推测解码 (Speculative Decoding) 使 Whisper 实现 2 倍的推理加速
Open AI 推出的 Whisper 是一个通用语音转录模型,在各种基准和音频条件下都取得了非常棒的结果。最新的 large-v3 模型登顶了 OpenASR 排行榜,被评为最佳的开源英语语音转录模型。该模型在 Common Voice 15 数据集的 58 种语言中也展现出了强大的多语言性…...

pi gpio 内存映射
树霉pi gpio内存映射 #include <stdio.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <stdlib.h>#define BCM2835_PERI_BASE 0x20000000 #define GPIO_BASE (BCM2835_PERI_BASE 0x200000) #define PAGE_SIZE…...

[NAND Flash 6.2] NAND 初始化常用命令:复位 (Reset) 和 Read ID 和 Read UID 操作和代码实现
依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解NAND Flash》 <<<< 返回总目录 <<<< 把下文中的字母和数字用`包起来, 中文不变。 全文 4400 字,主要内容 复位的目的和作用? NAND Reset 种类:FFh, FCh, FAh, FDh 区别 Reset 操作步骤 和…...

Multimodal Prototypical Networks for Few-shot Learning
tcGAN is provided with an embedding ϕ T \phi_T ϕT() of the textual description 辅助信息 作者未提供代码...

软件测试|Python requests库的安装和使用指南
简介 requests库是Python中一款流行的HTTP请求库,用于简化HTTP请求的发送和处理,也是我们在使用Python做接口自动化测试时,最常用的第三方库。本文将介绍如何安装和使用requests库,以及一些常见的用例示例。 安装requests库 首…...

HarmonyOS应用开发学习笔记 应用上下文Context 获取文件夹路径
1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器:定义组件重用样式 Extend装饰器:定义扩展组件样式 5、HarmonyOS 应用开发…...

http状态码对照表
状态码含义100客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。…...

金三银四-JVM核心知识高频面试题
又要快到一年一度的金三银四,开始复习啦~! 每天一点点。。 目录 一、JVM中的垃圾收集器有哪些,它们的工作原理是什么? 二、JVM中的类加载器有哪些,它们各自的作用是什么? 三、JVM中垃圾回收的…...

【GitHub项目推荐--谷歌大神又一开源代码调试神器】【转载】
如果调试是 Debug 的必经之路,那么编程应该将它考虑在内。今天我就和大家分享一个代码调试神器 - Cyberbrain。 Cyberbrain是一个免费开源的 Python 代码调试解决方案,它可视化程序执行以及每个变量的变化方式,让程序员免受调试之苦。主要具有…...

Ubuntu pip换源
在 Ubuntu 上使用 pip 更改软件包的下载源可以通过修改 pip.conf 文件来完成。 首先打开终端(Terminal)。 输入以下命令创建或编辑 pip.conf 文件: sudo nano /etc/pip.conf如果提示需要管理员密码,则输入密码并按 Enter 键确认。…...

解锁前端新潜能:如何使用 Rust 锈化前端工具链
前言 近年来,Rust的受欢迎程度不断上升。首先,在操作系统领域,Rust 已成为 Linux 内核官方认可的开发语言之一,Windows 也宣布将使用 Rust 来重写内核,并重写部分驱动程序。此外,国内手机厂商 Vivo 也宣布…...

vite前端工具链,为开发提供极速响应
一、概念 Vite是一个高性能的分布式智能合约平台。它使用了一种名为“异步架构”的设计,能够支持高吞吐量和低延迟的交易处理。Vite采用了基于DAG(有向无环图)的账本结构,可以实现并行处理多个交易,并且具有快速确认的…...

linux系统nginx做负载均衡
负载均衡 作用upstream配置负载均衡算法配置分类热备轮询加权轮询ip_hash 负载均衡配置状态参数nginx配置7层协议及4层协议七层协议做负载均衡四层协议做负载均衡 会话保持ip_hashsticky_cookie_insertjvm_route 作用 负载均衡(Load Balance,简称 LB&am…...

Tensor Core的一些概念理解
英伟达的GPU产品架构发展如下图,Tensor Core是从2017年的Volta架构开始演变的针对AI模型大量乘加运算的特殊处理单元。本文主要梳理一些关于Tensor Core的一些基础概念知识。 什么是混合精度? 混合精度在底层硬件算子层面,使用半精度…...

Git与VScode联合使用详解
目录 Git与VScode联合使用 方式一 1. 用vscode打开文件夹,如图点击初始化仓库,把此仓库初始为git仓库。 2. 提交文件到本地仓库 3. vscode与github账号绑定 4. 在github中建立远程仓库 5. 本地仓库与远程仓库绑定 方式二 1. 在github上建立远程仓…...

SQL Server 加密 view文本
CREATE VIEW dbo.View_building WITH ENCRYPTION AS SELECT * FROM Building_Temp; GO 注意: 加密後就看不到VIEW文本了,修改 ALTER VIEW dbo.View_building WITH ENCRYPTION AS –修改後的VIEW 文本 GO 或者刪除再新增。 所以,要另備份原V…...

Linux查看物理CPU个数、核数、逻辑CPU个数
文章目录 总核数总逻辑CPU数查看物理CPU个数查看每个物理CPU中core的个数(即核数)查看逻辑CPU的个数 总核数 总核数 物理CPU个数 X 每颗物理CPU的核数 总逻辑CPU数 总逻辑CPU数 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 查看物理CPU个数 cat /proc/cpuinfo| grep “…...

python使用单例模式加载config.ini配置文件
在Python中,可以使用单例模式来加载和管理配置文件。下面是一个示例代码: import configparserclass ConfigLoader:__instance Nonedef __init__(self):if ConfigLoader.__instance is not None:raise Exception("ConfigLoader is a singleton cl…...

牛刀小试---二分查找(C语言)
题目:在给定的升序数组中查找指定的数字n,并输出其下标 代码举例: #include <stdio.h> int main() {int arr[] { 1,2,3,4,5,6,7,8,9,10 };//给定的升序数组int left 0;//定义左下标int right sizeof(arr) / sizeof(arr[0]) - 1;//…...

k8s-数据卷
存储卷----数据卷 容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的,delete,k8s用控制创建的pod,delete相当于重启,容器的状态也会恢复到初识状态 一旦容器回到初始状态,所有得分后天编辑的文件…...

Linux学习记录——사십삼 高级IO(4)--- Epoll型服务器
文章目录 1、理解Epoll和对应接口2、实现 1、理解Epoll和对应接口 poll依然需要OS去遍历所有fd。一个进程去多个特定的文件中等待,只要有一个就绪,就使用select/poll系统调用,让操作系统把所有文件遍历一遍,哪些就绪就加上哪些fd…...

6.4、SDN在云数据中心的应用案例分析
云数据中心中的虚拟子网包含网关和IP网段,IP分配给各个服务器,服务器间能够互相通信或通过网关访问外部网络。 在SDN云数据中心内,用户可以随时订购任意网段的虚拟子网,而且这些子网是可以在不同用户之间复用的,也就是说,不同用户可以使用相同的私有网段。 SDN云数据中心…...

SpringBoot整合ES
1.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <version>2.6.3</version> </dependency> 2.config配置文件 Configu…...

Pandas实战100例 | 案例 10: 应用函数 - 使用 `apply`
案例 10: 应用函数 - 使用 apply 知识点讲解 Pandas 的 apply 函数是一个非常强大的工具,允许你对 DataFrame 中的行或列应用一个函数。这对于复杂的数据转换和计算非常有用。你可以使用 apply 来执行任意的函数,这些函数可以是自定义的,也…...

《C++大学教程》4.13汽油哩数
题目: 每位司机都关心自己车辆的行车里程数。有位司机通过记录每次出行所行驶的英里数和用油的加仑数来跟踪他多次出车的情况。请开发一个C程序,它使用一条while语句输入每次出车的行驶英里数和加油量。该程序应计算和显示每次出车所得到的每加仑行驶英里数&#x…...

OpenGL排坑指南—贴图纹理绑定和使用
一、前言 在OpenGL学习 的纹理这一章中讲述了纹理贴图的使用方式,主要步骤是先创建一个纹理的对象,和创建顶点VAO类似,然后就开始绑定这个纹理,最后在循环中使用,有时候可能还要用到激活纹理单元的函数。然而ÿ…...

Electron中 主进程(Main Process)与 渲染进程 (Renderer Process) 通信的方式
1. 渲染进程向主进程通信 修改 html 文件内容 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><!-- 解决控制…...

企业微信forMAC,如何左右翻动预览图片
1、control commandshifd 进入企业微信的debug调试模式 2、按照如下步骤选择 3、重启企业微信...

Android Firebase (FCM)推送接入
官方文档: 向后台应用发送测试消息 | Firebase Cloud Messaging 1、根级(项目级)Gradlegradle的dependencies中添加: dependencies {...// Add the dependency for the Google services Gradle pluginclasspath com.google.gm…...

Neo4j恢复
主要记录windows环境下从备份文件中恢复Neo4j, Linux环境同理 备份在上一篇中有介绍,参考: Neo4j备份-CSDN博客 误删数据 为了模拟误删除场景,我们查询Person,并模拟误操作将其进行删除; match(p:Person) return …...