9. 什么是 Beam Search?深入理解模型生成策略

是不是总感觉很熟悉?Beam Search 是生成任务中常用的一种方法。
在之前第5,7,8篇文章中,我们都曾经用到过与它相关的参数,而对于早就有着实操经验的同学们,想必见到的更多。这篇文章将从示例到数学原理和代码带你进行理解。

Beam Search 对应的中文翻译为“集束搜索”或“束搜索”。你可以将其当作是贪心算法的拓展,其实是很简单的概念:贪心算法每次只选择最好的,而 Beam Search 会在多个候选中进行选择。通过这篇文章,你将了解到:

  • Beam Width(束宽) 的实际作用,常对应于参数名 num_beams
  • 所有候选序列生成结束标记 的含义,常对应于参数名 early_stopping
  • Beam Search 的基本原理和工作机制

强烈建议访问:Beam Search Visualizer,这是一个非常 Amazing 的交互式项目,在即将完成这个文章攥写的时候我通过官方文档发现了它,让理论与实际搭上了桥。

notebook 代码下载

文章目录

    • Beam Search 的工作原理
    • 生成示例
    • 怎么处理 `<eos>` ?
      • 处理示例图(k=3)
    • 进一步深入 Beam Search
      • 使用对数概率
      • 参数解释
    • 代码演示
    • 数学描述
      • 序列概率
      • 评分函数
      • Beam Search 的更新步骤
      • 最终选择
    • 实际应用
      • 代码示例
      • 对比不同束宽的输出
    • 推荐阅读

Beam Search 的工作原理

Beam Search 是一种宽度优先搜索算法,通过保留多个候选序列来探索可能的输出空间,这与贪心算法每次只选择一个当前最优序列不同,可以将贪心算法当成一个候选序列下的 Beam Search。

具体来讲,每一步生成时,Beam Search 会保留束宽 k k k 个最有可能的候选序列(k=1即贪心),并为每个候选序列计算它们的累积概率或对数概率。在每一步搜索时,Beam Search 会生成所有可能的下一个词汇,并从中选择得分最高的 k k k 个序列继续下一步。所以,束宽越大,搜索空间越广,计算成本越高。

以下是 Beam Search 的基本步骤:

  1. 初始化:从一个初始序列(通常为空或特殊起始标记)开始,设定束宽 k k k,初始化候选序列集 B 0 = { start } B_0 = \{ \text{start} \} B0={start}
  2. 迭代生成:对于当前所有候选序列 B t − 1 B_{t-1} Bt1,扩展一个新的词汇或符号,生成所有可能的下一个词汇组合,并计算每个序列的概率。
  3. 选择顶束:从所有扩展的候选序列中,选择得分最高的 k k k 个序列,作为下一步的候选序列 B t B_t Bt
  4. 终止条件:当所有候选序列都生成了结束标记(如 <eos>)或达到设定的最大长度 T T T 时,停止生成。
  5. 选择最终序列:从最终的候选序列集中,选择得分最高的序列作为输出。

:以GPT为例,扩展实际对应于去获取 tokens 的概率。

生成示例

为了清晰,这里使用累积概率进行得分的计算。

  1. 初始化

    • 束宽 ( k k k): 2
    • 当前候选集 ( B 0 B_0 B0): { (空) } \{\text{(空)}\} {(空)}
    • 词汇表 { A , B , C , <eos> } \{A, B, C, \texttt{<eos>}\} {A,B,C,<eos>}
    • 扩展(生成所有可能的下一个词汇):
      扩展结果概率
      A A A 0.4 \textbf{0.4} 0.4
      B B B 0.3 \textbf{0.3} 0.3
      C C C 0.2 0.2 0.2
      <eos> \texttt{<eos>} <eos> 0.1 0.1 0.1
    • 选择顶束 ( k = 2 k=2 k=2):
      • A A A ( 0.4 0.4 0.4)
      • B B B ( 0.3 0.3 0.3)
    • 新的候选集 ( B 1 B_1 B1): { A ( 0.4 ) , B ( 0.3 ) } \{A (0.4), B (0.3)\} {A(0.4),B(0.3)}
  2. 扩展 A A A B B B

    • 扩展 A A A

      • 生成概率: { A : 0.3 , B : 0.1 , C : 0.4 , <eos> : 0.2 } \{A: 0.3, B: 0.1, C: 0.4, \texttt{<eos>}: 0.2\} {A:0.3,B:0.1,C:0.4,<eos>:0.2}
      扩展结果概率计算概率
      A A AA AA 0.4 × 0.3 0.4 \times 0.3 0.4×0.3 0.12 \textbf{0.12} 0.12
      A B AB AB 0.4 × 0.1 0.4 \times 0.1 0.4×0.1 0.04 0.04 0.04
      A C AC AC 0.4 × 0.4 0.4 \times 0.4 0.4×0.4 0.16 \textbf{0.16} 0.16
      A <eos> A\texttt{<eos>} A<eos> 0.4 × 0.2 0.4 \times 0.2 0.4×0.2 0.08 0.08 0.08
    • 扩展 B B B

      • 生成概率: { A : 0.1 , B : 0.1 , C : 0.3 , <eos> : 0.5 } \{A: 0.1, B: 0.1, C: 0.3, \texttt{<eos>}: 0.5\} {A:0.1,B:0.1,C:0.3,<eos>:0.5}
      扩展结果概率计算概率
      B A BA BA 0.3 × 0.1 0.3 \times 0.1 0.3×0.1 0.03 0.03 0.03
      B B BB BB 0.3 × 0.1 0.3 \times 0.1 0.3×0.1 0.03 0.03 0.03
      B C BC BC 0.3 × 0.3 0.3 \times 0.3 0.3×0.3 0.09 \textbf{0.09} 0.09
      B <eos> B\texttt{<eos>} B<eos> 0.3 × 0.5 0.3 \times 0.5 0.3×0.5 0.15 \textbf{0.15} 0.15
    • 所有扩展序列及其概率

      序列概率
      A C AC AC 0.16 \textbf{0.16} 0.16
      A A AA AA 0.12 0.12 0.12
      B <eos> B\texttt{<eos>} B<eos> 0.15 \textbf{0.15} 0.15
      B C BC BC 0.09 0.09 0.09
      A <eos> A\texttt{<eos>} A<eos> 0.08 0.08 0.08
      A B AB AB 0.04 0.04 0.04
      B A BA BA 0.03 0.03 0.03
      B B BB BB 0.03 0.03 0.03
    • 选择顶束 ( k = 2 k=2 k=2):

      • A C AC AC ( 0.16 0.16 0.16)
      • B <eos> B\texttt{<eos>} B<eos> ( 0.15 0.15 0.15)
    • 新的候选集 ( B 2 B_2 B2): { A C ( 0.16 ) , B <eos> ( 0.15 ) } \{AC (0.16), B\texttt{<eos>} (0.15)\} {AC(0.16),B<eos>(0.15)}

    • 完成集合: B <eos> {B\texttt{<eos>}} B<eos> ( 0.15 0.15 0.15)

  3. 仅扩展 A C AC AC

    • 生成概率: { A : 0.1 , B : 0.2 , C : 0.5 , <eos> : 0.2 } \{A: 0.1, B: 0.2, C: 0.5, \texttt{<eos>}: 0.2\} {A:0.1,B:0.2,C:0.5,<eos>:0.2}
    扩展结果概率计算概率
    A C A ACA ACA 0.16 × 0.1 0.16 \times 0.1 0.16×0.1 0.016 0.016 0.016
    A C B ACB ACB 0.16 × 0.2 0.16 \times 0.2 0.16×0.2 0.032 \textbf{0.032} 0.032
    A C C ACC ACC 0.16 × 0.5 0.16 \times 0.5 0.16×0.5 0.080 \textbf{0.080} 0.080
    A C <eos> AC\texttt{<eos>} AC<eos> 0.16 × 0.2 0.16 \times 0.2 0.16×0.2 0.032 \textbf{0.032} 0.032
    • 由于 B <eos> B\texttt{<eos>} B<eos> 已完成,我们选择扩展结果中的顶束:
      • A C C ACC ACC ( 0.080 0.080 0.080)
      • 以某种规则选择 A C B ACB ACB A C <eos> AC\texttt{<eos>} AC<eos> ( 0.032 0.032 0.032)
    • 新的候选集 ( B 3 B_3 B3): { A C C ( 0.080 ) , A C B ( 0.032 ) } \{ACC (0.080), ACB (0.032)\} {ACC(0.080),ACB(0.032)}
    • 完成集合: B <eos> ( 0.15 ) {B\texttt{<eos>} (0.15)} B<eos>(0.15)
  4. 后续步骤

    • 继续扩展:重复上述过程,直到所有候选序列都生成了 <eos> 或达到设定的最大长度。

过程演示

现在是你访问它的最好时机:Beam Search Visualizer

怎么处理 <eos>

在每一步生成过程中,如果某个序列生成了 <eos>,则将其标记为完成,不再进行扩展。以下是处理 <eos> 的举例:

  • 假设在某一步,序列 A C B ACB ACB 扩展出 A C B <eos> ACB\texttt{<eos>} ACB<eos> ( 0.032 × 1 = 0.032 0.032 \times 1 = 0.032 0.032×1=0.032),则:
    • A C B <eos> ACB\texttt{<eos>} ACB<eos> 保留在最终候选集,并不再扩展。
    • Beam Search 继续扩展其他未完成的序列,直到所有序列完成或达到最大长度。

问题如果有一个序列被标记为完成(生成了 <eos>),在下一个扩展步骤中,Beam Search 应该扩展多少个候选序列?

答:束宽 ( k k k) 个

处理示例图(k=3)

你可以在下图中看到,即便有一个序列生成了 <eos>,下一个扩展步骤中还是会扩展 k=3 个候选序列。

image-20240915235014101

进一步深入 Beam Search

使用对数概率

在实际应用中,尤其是在处理长序列时,直接相乘概率会导致数值下溢问题。为了避免这种情况,通常会使用对数概率来累加评分。

示例说明

假设使用对数概率,序列的评分计算如下:

  • 序列 A A A 的概率为 0.4 0.4 0.4,其对数概率为 log ⁡ ( 0.4 ) ≈ − 0.916 \log(0.4) \approx -0.916 log(0.4)0.916
  • 序列 A C AC AC 的概率为 0.16 0.16 0.16,其对数概率为 log ⁡ ( 0.16 ) ≈ − 1.833 \log(0.16) \approx -1.833 log(0.16)1.833

在 Beam Search 中,我们会选择对数概率较高(即绝对值较小)的序列作为顶束。

参数解释

除了 num_beamsearly_stopping,Beam Search 通常还涉及其他参数,以下是常见参数的简要解释:

  • max_length(最大生成长度):限制生成序列的最大长度。
  • length_penalty(长度惩罚):用于调整生成序列的长度偏好,通常用于平衡生成序列的长度与概率评分。值大于 1 时,会惩罚过长的序列,值小于 1 时,会鼓励生成较长的序列。
  • no_repeat_ngram_size:防止生成序列中出现重复的 n-gram,提高生成内容的多样性。
  • num_return_sequences:指定生成的序列数量,允许一次生成多个不同的候选序列,<= num_beams。

代码演示

下面是一个 beam search 演示代码,结果完全对应于之前讨论的示例。为了简单起见,我们进一步假设在序列 ACBACC 之后一定是 <eos>

import math

def beam_search(initial_sequence, beam_width, max_length, vocab, get_next_probs):
    beam = [(initial_sequence, 0.0)]  # (sequence, log_prob)
    completed = []

    for step in range(max_length):
        print(f"\n第 {step + 1} 步:")
        all_candidates = []
        for seq, score in beam:
            if seq.endswith('<eos>'):
                completed.append((seq, score))
                print(f"已完成序列: {seq},得分为 {score}")
                continue
            next_probs = get_next_probs(seq)
            print(f"扩展序列: {seq},当前得分为 {score}")
            for token, prob in next_probs.items():
                new_seq = seq + token
                new_score = score + math.log(prob)
                all_candidates.append((new_seq, new_score))
                print(f"  候选序列: {new_seq},得分为 {new_score}")
        
        # 对所有候选序列按得分降序排列,选择得分最高的 beam_width 个序列
        all_candidates.sort(key=lambda x: x[1], reverse=True)
        beam = all_candidates[:beam_width]

        # 打印选出的顶束序列
        print(f"\n选择的 {beam_width} 个顶束序列:")
        for seq, score in beam:
            print(f"  {seq},得分为 {score}")
        
        # 如果没有更多序列可以扩展,则退出循环
        if not beam:
            break

    # 将当前 beam 中剩下的序列加入完成序列中
    completed += beam

    # 对完成的序列按得分降序排列,选择得分最高的序列
    completed.sort(key=lambda x: x[1], reverse=True)
    
    print("\n已完成的所有序列:")
    for seq, score in completed:
        print(f"  {seq},得分为 {score}")
    
    return completed[0][0]

# 我们之前示例中设置的概率
def get_next_probs(seq):
    probs = {
        "": {"A": 0.4, "B": 0.3, "C": 0.2, "<eos>": 0.1},
        "A": {"A": 0.3, "B": 0.1, "C": 0.4, "<eos>": 0.2},
        "B": {"A": 0.1, "B": 0.1, "C": 0.3, "<eos>": 0.5},
        "AC": {"A": 0.1, "B": 0.2, "C": 0.5, "<eos>": 0.2},
    }
    return probs.get(seq, {"<eos>": 1.0})

initial_sequence = ""
beam_width = 3 # 你可以修改这个参数来感受区别
max_length = 5
vocab = {"A", "B", "C", "<eos>"}

best_sequence = beam_search(initial_sequence, beam_width, max_length, vocab, get_next_probs)
print("\n最佳序列:", best_sequence)

输出:

1 步:
扩展序列: ,当前得分为 0.0
  候选序列: A,得分为 -0.916290731874155
  候选序列: B,得分为 -1.2039728043259361
  候选序列: C,得分为 -1.6094379124341003
  候选序列: <eos>,得分为 -2.3025850929940455

选择的 2 个顶束序列:
  A,得分为 -0.916290731874155
  B,得分为 -1.20397280432593612 步:
扩展序列: A,当前得分为 -0.916290731874155
  候选序列: AA,得分为 -2.120263536200091
  候选序列: AB,得分为 -3.2188758248682006
  候选序列: AC,得分为 -1.83258146374831
  候选序列: A<eos>,得分为 -2.525728644308255
扩展序列: B,当前得分为 -1.2039728043259361
  候选序列: BA,得分为 -3.506557897319982
  候选序列: BB,得分为 -3.506557897319982
  候选序列: BC,得分为 -2.4079456086518722
  候选序列: B<eos>,得分为 -1.8971199848858813

选择的 2 个顶束序列:
  AC,得分为 -1.83258146374831
  B<eos>,得分为 -1.89711998488588133 步:
扩展序列: AC,当前得分为 -1.83258146374831
  候选序列: ACA,得分为 -4.135166556742355
  候选序列: ACB,得分为 -3.4420193761824103
  候选序列: ACC,得分为 -2.525728644308255
  候选序列: AC<eos>,得分为 -3.4420193761824103
已完成序列: B<eos>,得分为 -1.8971199848858813

选择的 2 个顶束序列:
  ACC,得分为 -2.525728644308255
  ACB,得分为 -3.44201937618241034 步:
扩展序列: ACC,当前得分为 -2.525728644308255
  候选序列: ACC<eos>,得分为 -2.525728644308255
扩展序列: ACB,当前得分为 -3.4420193761824103
  候选序列: ACB<eos>,得分为 -3.4420193761824103

选择的 2 个顶束序列:
  ACC<eos>,得分为 -2.525728644308255
  ACB<eos>,得分为 -3.44201937618241035 步:
已完成序列: ACC<eos>,得分为 -2.525728644308255
已完成序列: ACB<eos>,得分为 -3.4420193761824103

选择的 2 个顶束序列:

已完成的所有序列:
  B<eos>,得分为 -1.8971199848858813
  ACC<eos>,得分为 -2.525728644308255
  ACB<eos>,得分为 -3.4420193761824103

最佳序列: B<eos>

数学描述

序列概率

假设我们要生成一个长度为 T T T 的序列 Y = ( y 1 , y 2 , … , y T ) Y = (y_1, y_2, \dots, y_T) Y=(y1,y2,,yT),该序列的生成是逐步进行的,即每个词汇 y t y_t yt 的生成依赖于前面已经生成的词汇 y 1 , y 2 , … , y t − 1 y_1, y_2, \dots, y_{t-1} y1,y2,,yt1。因此,序列 Y Y Y 的联合概率为:

P ( Y ) = ∏ t = 1 T P ( y t ∣ y 1 , y 2 , … , y t − 1 ) P(Y) = \prod_{t=1}^{T} P(y_t | y_1, y_2, \dots, y_{t-1}) P(Y)=t=1TP(yty1,y2,,yt1)

评分函数

由于直接计算概率乘积在处理长序列时容易导致数值下溢问题,通常我们通过取对数来简化计算并稳定数值。取对数后的评分函数(log likelihood)为:

S ( Y ) = log ⁡ P ( Y ) = ∑ t = 1 T log ⁡ P ( y t ∣ y 1 , y 2 , … , y t − 1 ) S(Y) = \log P(Y) = \sum_{t=1}^{T} \log P(y_t | y_1, y_2, \dots, y_{t-1}) S(Y)=logP(Y)=t=1TlogP(yty1,y2,,yt1)

模型的目标是最大化序列的概率:

Y ∗ = arg ⁡ max ⁡ Y P ( Y ) Y^* = \arg\max_Y P(Y) Y=argYmaxP(Y)

Beam Search 的更新步骤

在每一步 t t t,Beam Search 保留束宽 k k k 个最有可能的部分序列。设候选序列集为 B t − 1 = { Y 1 , Y 2 , … , Y k } B_{t-1} = \{Y_1, Y_2, \dots, Y_k\} Bt1={Y1,Y2,,Yk},每个部分序列 Y i Y_i Yi 的概率为 P ( Y i ) P(Y_i) P(Yi)

  1. 扩展:对于每个候选序列 Y i ∈ B t − 1 Y_i \in B_{t-1} YiBt1,从词汇表 V V V 中扩展所有可能的下一个词汇 y y y,生成新的序列 Y i y Y_i y Yiy(即在 Y i Y_i Yi 的末尾添加词汇 y y y),并计算其概率:

P ( Y i y ) = P ( Y i ) × P ( y ∣ Y i ) P(Y_i y) = P(Y_i) \times P(y | Y_i) P(Yiy)=P(Yi)×P(yYi)

同时取对数后得到评分函数的更新:

S ( Y i y ) = log ⁡ P ( Y i y ) = S ( Y i ) + log ⁡ P ( y ∣ Y i ) S(Y_i y) = \log P(Y_i y) = S(Y_i) + \log P(y | Y_i) S(Yiy)=logP(Yiy)=S(Yi)+logP(yYi)

  1. 选择顶束:在每一步中,我们从所有扩展后的候选序列 { Y i y ∣ Y i ∈ B t − 1 , y ∈ V } \{Y_i y \mid Y_i \in B_{t-1}, y \in V\} {YiyYiBt1,yV} 中选择得分最高的 k k k 个序列,组成新的候选集 B t B_t Bt:

B t = arg ⁡ max ⁡ B ′ ⊆ { Y i y ∣ Y i ∈ B t − 1 , y ∈ V } , ∣ B ′ ∣ = k ∑ Y ′ ∈ B ′ S ( Y ′ ) B_t = \arg\max_{B' \subseteq \{Y_i y \mid Y_i \in B_{t-1}, y \in V\}, |B'| = k} \sum_{Y' \in B'} S(Y') Bt=argB{YiyYiBt1,yV},B=kmaxYBS(Y)

最终选择

当生成过程结束时,从最终的候选集 B T B_T BT 中,选择得分最高的序列作为最终输出:

Y ∗ = arg ⁡ max ⁡ Y ∈ B T S ( Y ) Y^* = \arg\max_{Y \in B_T} S(Y) Y=argYBTmaxS(Y)

实际应用

先安装一些演示用到的库:

pip install transformers
pip install torch torchvision torchaudio

代码示例

使用 Hugging Face Transformers 库的简单示例:

import warnings
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

# 忽略 FutureWarning 警告
warnings.filterwarnings("ignore", category=FutureWarning)

# 指定模型名称
model_name = "distilgpt2"

# 加载分词器和模型
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

# 移动模型到设备
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

# 设置模型为评估模式
model.eval()

# 输入文本
input_text = "Hello GPT"

# 编码输入文本,并生成 attention mask
inputs = tokenizer.encode(input_text, return_tensors="pt").to(device)
attention_mask = torch.ones_like(inputs).to(device)

# 生成文本,使用 Beam Search
beam_width = 5
with torch.no_grad():
    outputs = model.generate(
        inputs,
        attention_mask=attention_mask,
        max_length=50,
        num_beams=beam_width,  # 你可以看到 beam_width 对应的参数名为 num_beams
        no_repeat_ngram_size=2,
        early_stopping=True,  # 当所有候选序列生成<eos>停止
        pad_token_id=tokenizer.eos_token_id
    )

# 解码生成的文本
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print("生成的文本:")
print(generated_text)

输出

生成的文本:
Hello GPT.

This article was originally published on The Conversation. Read the original article.

对比不同束宽的输出

# 输入文本
input_text = "Hello GPT"

# 编码输入文本,同时返回 attention_mask
inputs = tokenizer.encode_plus(input_text, return_tensors="pt", padding=True).to(device)

# 设置束宽不同的生成策略
beam_widths = [1, 3, 5]  # 使用不同的束宽

# 生成并打印结果
for beam_width in beam_widths:
    with torch.no_grad():
        outputs = model.generate(
            inputs["input_ids"],
            attention_mask=inputs["attention_mask"],
            max_length=50,
            num_beams=beam_width,
            no_repeat_ngram_size=2,
            early_stopping=True,
            pad_token_id=tokenizer.eos_token_id
        )
    generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    print(f"束宽 {beam_width} 的生成结果:")
    print(generated_text)
    print('-' * 50)

束宽 1 的生成结果:
Hello GPT is a free and open source software project that aims to provide a platform for developers to build and use GPGP-based GPSP based GPCs. GPP is an open-source software development platform that is designed to
--------------------------------------------------
束宽 3 的生成结果:
Hello GPT.

This article is part of a series of articles on the topic, and will be updated as more information becomes available.
--------------------------------------------------
束宽 5 的生成结果:
Hello GPT.

This article was originally published on The Conversation. Read the original article.
--------------------------------------------------

推荐阅读

  • Beam-search decoding
  • Beam Search Visualizer

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言二级考试】循环结构设计

C语言二级考试——循环结构程序设计 五.循环结构程序设计 1.for循环结构 2.while和do-while循环结构 3.continue语句和break语句 4.循环的嵌套 知识点参考【C语言】循环-CSDN博客 文章目录 1.for循环2.while和do-while循环结构3.continue语句和break语句4.循环的嵌套 1.for循环…

智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!

文章目录 1 效果展示2 CogVideoX 前世今生3 CogVideoX 部署实践流程3.1 创建丹摩实例3.2 配置环境和依赖3.3 模型与配置文件3.4 运行4 遇到问题 1 效果展示 A street artist, clad in a worn-out denim jacket and a colorful bandana, stands before a vast concrete wall in …

论文速递!时序预测!DCSDNet:双卷积季节性分解网络,应用于天然气消费预测过程

本期推文将介绍一种新的时序预测方法:双卷积季节性分解网络&#xff08;Dual Convolution withSeasonal Decomposition Network, DCSDNet&#xff09;在天然气消费预测的应用&#xff0c;这项研究发表于《Applied Energy》期刊。 针对天然气消费的多重季节性和非规律性&#x…

C++ —— 关于vector

目录 链接 1. vector的定义 2. vector的构造 3. vector 的遍历 4. vector 的扩容机制 5. vector 的空间接口 5.1 resize 接口 5.2 push_back 5.3 insert 5.4 erase 5.5 流插入与流提取 vector 并不支持流插入与流提取&#xff0c;但是可以自己设计&#xff0c;更…

标准库标头 <barrier>(C++20)学习

此头文件是线程支持库的一部分。 类模板 std::barrier 提供一种线程协调机制&#xff0c;阻塞已知大小的线程组直至该组中的所有线程到达该屏障。不同于 std::latch&#xff0c;屏障是可重用的&#xff1a;一旦到达的线程组被解除阻塞&#xff0c;即可重用同一屏障。与 std::l…

基于SpringBoot项目实现Docker容器化部署

将Spring Boot项目部署到Docker容器中的涉及几个主要步骤&#xff1a; 准备Docker镜像 首先&#xff0c;需要选择一个基础镜像&#xff0c;通常是包含Java运行时环境的镜像&#xff0c;例如OpenJDK。可以从Docker Hub或其他镜像仓库中获取这些镜像。接下来&#xff0c;需要在…

C++库文件移植到QT中一直出错

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

【软考】数据字典(DD)

目录 1. 说明2. 数据字典的内容2.1 说明2.2 数据流条目2.3 数据存储条目2.4 数据项条目2.5 基本加工条目 3. 数据词典管理4. 加工逻辑的描述4.1 说明4.2 结构化语言4.3 判定表4.3 判定树 5. 例题5.1 例题1 1. 说明 1.数据流图描述了系统的分解&#xff0c;但没有对图中各成分进…

一个基于 laravel 和 amis 开发的后台框架, 友好的组件使用体验,可轻松实现复杂页面(附源码)

前言 随着互联网应用的发展&#xff0c;后台管理系统的复杂度不断增加&#xff0c;对于开发者而言&#xff0c;既要系统的功能完备&#xff0c;又要追求开发效率的提升。然而&#xff0c;传统的开发方式往往会导致大量的重复劳动&#xff0c;尤其是在构建复杂的管理页面时。有…

【移动端开发】“明日头条APP”

文章目录 1 系统概述1.1研究背景1.2研究意义 2 系统设计2.1 关键技术2.2 系统设计2.2.1 系统功能模块2.2.2 数据库设计 3 系统实现3.1 数据模型3.1.1 NewsURL3.1.2 NewsType3.1.3 NewsInfo 3.2 数据库操作3.2.1 DBOpenHelper3.2.2 DBManager 3.3 适配器类3.3.1 AddItem3.3.2 In…

Redhat 7,8,9系(复刻系列) 一键部署Oracle19c rpm

Oracle19c前言 Oracle 19c 是甲骨文公司推出的一款企业级关系数据库管理系统,它带来了许多新的功能和改进,使得数据库管理更加高效、安全和可靠。以下是关于 Oracle 19c 的详细介绍: 主要新特性 多租户架构:支持多租户架构,允许多个独立的数据库实例在同一个物理服务器上…

【机器学习】9 ——最大熵模型的直观理解

机器学习9 ——最大熵模型的直观理解 文章目录 机器学习9 ——最大熵模型的直观理解前奏例子硬币垃圾邮件代码 前奏 【机器学习】6 ——最大熵模型 例子 硬币 假设我们有一枚硬币&#xff0c;可能是公平的&#xff0c;也可能是不公平的。我们的任务是估计硬币的正反面出现的…

通过Python代码发送量化交易信号邮件通知

量化交易利用数学模型和计算机算法来分析市场数据,并生成交易信号,本文将介绍如何使用Python编写一个简单的脚本,通过发送邮件通知量化交易信号。 开启SMTP服务 首先要在发件箱的邮件设置中,将POP3/SMPT服务开启,记录下授权密码,在本地可通过此密码登录,注意有效期和保…

微信小程序页面制作——婚礼邀请函(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【网络】TCP/IP 五层网络模型:网络层

最核心的就是 IP 协议&#xff0c;是一个相当复杂的协议 TCP 详细展开讲解&#xff0c;是因为 TCP 确实在开发中非常关键&#xff0c;经常用到&#xff0c;IP 则不同&#xff0c;和普通程序猿联系比较浅。和专门开发网络的程序猿联系比较紧密&#xff08;开发路由器&#xff0…

3款免费的GPT类工具

前言 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;的崛起与发展已经成为我们生活中不可或缺的一部分。它的出现彻底改变了我们与世界互动的方式&#xff0c;并为各行各业带来了前所未有的便利。 一、Kimi 网址&#xff1a;点我前往 国产AI模型Kimi是一…

neo4j安装启动教程+对应的jdk配置

参考这位博主的视频教程&#xff1a;neo4j社区windows版下载 一、官网下载neo4j的安装包 &#xff08;1&#xff09;官网下载页面 &#xff08;2&#xff09;上一步 【download】之后&#xff0c;会自动下载&#xff0c;如果没有&#xff0c;点击【here】 这里可以看到一行字…

Qwen 2.5:阿里巴巴集团的新一代大型语言模型

Qwen 2.5&#xff1a;阿里巴巴集团的新一代大型语言模型 摘要&#xff1a; 在人工智能领域&#xff0c;大型语言模型&#xff08;LLMs&#xff09;的发展日新月异&#xff0c;它们在自然语言处理&#xff08;NLP&#xff09;和多模态任务中扮演着越来越重要的角色。阿里巴巴集…

获取参数

获取querystring参数 querystring 指的是URL中 ? 后面携带的参数&#xff0c;例如&#xff1a;http://127.0.0.1:9090/web?query杨超越。 获取请求的querystring参数的方法如下&#xff1a; 方法1&#xff1a; Query package main// querystringimport ("github.com/…

有毒有害气体检测仪的应用和性能_鼎跃安全

随着现代工业的不断发展和扩张&#xff0c;越来越多的企业涉及到有毒有害气体的生产、使用和处理。工业规模的扩大导致有毒有害气体的排放量增加&#xff0c;同时也增加了气体泄漏的风险。在发生火灾、爆炸或危险化学品泄漏等紧急事件时&#xff0c;救援人员需要迅速了解现场的…