LLM - DataCollatorForLanguageModeling 样本生成 by transformers

news2024/11/24 18:45:29

目录

一.引言

二.生成样本 By API

1.样本处理样式

2.DataCollatorForLanguageModeling

2.1 样本准备

2.2 API 生成

三.生成样本 By DIY

1.样本准备

2.data_colloator 实现

3.使用自定义 data_colloator

四.总结


一.引言

前面我们讲了 Baichuan7B 的 lora 微调步骤,我们在 QA 基础上构建了样本集并训练,但是细心的同学肯定发现我们原始样本中只给出了 input_ids 但是没有给出 labels,本文我们简单看下 data_collator 如何生成样本 label 并自定义实现一个简单的 data_collator。

二.生成样本 By API

1.样本处理样式

def preprocess(tokenizer, config, example, max_seq_length, prompt_key, target_key):
    prompt = example[prompt_key]
    target = example[target_key]
    prompt_ids = tokenizer.encode(prompt, max_length=max_seq_length, truncation=True)
    target_ids = tokenizer.encode(target, max_length=max_seq_length, truncation=True, add_special_tokens=False)
    # 最终还是将 instruction 的输入输出都拼在一起,使用经典的 causal-LM 的 next word prediction 方式来训练
    input_ids = prompt_ids + target_ids + [config.eos_token_id] # EOS 用于标识句子结束
    return {"input_ids": input_ids, "seq_len": len(prompt_ids)}

首先加载 tokenizer 对 Q 和 A 分别 token 获取对应 Q_ids 和 A_ids:

Q: 请计算:39 * 0 = 什么?
A: 这是简单的乘法运算,39乘以0得到的是0
TokenQ: [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81]
TokenA: [31106, 3908, 14313, 31640, 31257, 31481, 31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56]

获取 ids 后,会直接将 Q_ids A_ids 连接并在尾部增加 eos_token_id 标识当前句子结束,这里 eos_token_id = 2,合并后的 input_ids 如下:

input_ids = [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81, 31106, 3908, 14313, 31640, 31257, 31481,
           31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56, 2]

除此之外,json 里还用 seq_len 记录了 prompt_ids 即 Q_ids 的长度:

json = {"input_ids": input_ids, "seq_len": 13}

2.DataCollatorForLanguageModeling

data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer,mlm=False)
    # 初始化 trainer, 此处报错: NotImplementedError: Cannot copy out of meta tensor; no data!
    trainer = ModifiedTrainer(
        model=model,
        train_dataset=dataset,
        args=training_args,
        callbacks=[TensorBoardCallback(writer)], # 回调将训练情况写入 tensorboard
        data_collator=data_collator,
    )
    trainer.train()

上文中我们把 json 样本生成的 Dataset 和 DataCollatorForLanguageModeling 直接传给了 Trainer 训练,但是样本中并没有 labels,于是就在想没有 labels 怎么梯度回传,下面用 DataCollatorForLanguageModeling API 看下 data_collator 使用后生成的数据样式。

2.1 样本准备

这里我直接将第一步 tokenizer 后的样本手动生成两个 json 测试后续流程:

# p: [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81]
# t: [31106, 3908, 14313, 31640, 31257, 31481, 31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56]
sample1 = [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81, 31106, 3908, 14313, 31640, 31257, 31481,
           31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56, 2]

# p: [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 31526, 31342, 8835, 31221, 31423, 7156, 55, 31396, 31222, 33370, 31604, 72, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 6851, 11002, 75]
# t: [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 8835, 31221, 31423, 55, 31396, 31222, 33370, 31604, 72, 2926, 31415, 31396, 31222, 33370, 1231, 31221, 11254, 11002, 31380, 1522, 31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135, 31396, 31380, 73, 5, 54, 56, 34399, 55, 31191, 60, 5, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002, 73, 2122, 72, 6787, 31161, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002, 73]
sample2 = [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 31526, 31342, 8835, 31221, 31423, 7156, 55, 31396,
           31222, 33370, 31604, 72, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 6851, 11002, 75, 31106, 33370, 5629,
           16927, 54, 56, 31179, 11002, 72, 8835, 31221, 31423, 55, 31396, 31222, 33370, 31604, 72, 2926, 31415, 31396,
           31222, 33370, 1231, 31221, 11254, 11002, 31380, 1522, 31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135,
           31396, 31380, 73, 5, 54, 56, 34399, 55, 31191, 60, 5, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60,
           31179, 11002, 73, 2122, 72, 6787, 31161, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002,
           73, 2]

json1 = {"input_ids": sample1, "seq_len": 13}
json2 = {"input_ids": sample2, "seq_len": 31}

features = [json1, json2]

每个 json 的 input_ids 都遵循 Q_ids + A_ids + [SEP] 即 Prompt_ids + Target_ids + [SEP] 的规则生成。

2.2 API 生成

from transformers import AutoTokenizer
from transformers import DataCollatorForLanguageModeling

model_checkpoint = "/model/baichuan-7B"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, trust_remote_code=True)
tokenizer.pad_token = tokenizer.unk_token
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer,mlm=False)

features = [json1, json2]
batch = data_collator(features)

print(batch)

调用 API 生成一个 Batch 对应的大 json 共包含 4 个 key:

• attention_mask

对于基于 mask 的语言任务例如 Bert,data_collator 可以帮助生成掩码标签。对于 sample1 其长度较短,所以后面的 PAD_TOKEN 部分的 mask 均为 0,而 sample2 的长度为所有样本中最长的且小于 max_length,所以其 mask 均为 1。

'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
         1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])

• input_ids

与上面同理,data_collator 会就算当前 batch 样本中的 max_length,对于不足 max_length 的样本进行补齐的 padding 操作,所以 sample1 的样本补了很多 padding,sample2 正常。

'input_ids': tensor([[31106, 15875,    77,    57,    53, 31159,    53,    60,    58, 31135,
          6787,  6775,    81, 31106,  3908, 14313, 31640, 31257, 31481, 31742,
            72,    57,    53, 31640, 31187,    53,    60,    58,  9323, 31178,
            52,    79,    54,    59,    56,     2,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
             0,     0,     0,     0,     0,     0],
        [31106, 33370,  5629, 16927,    54,    56, 31179, 11002,    72, 31526,
         31342,  8835, 31221, 31423,  7156,    55, 31396, 31222, 33370, 31604,
            72, 31415, 31396, 31222, 33370,  1231, 31221, 31195,  6851, 11002,
            75, 31106, 33370,  5629, 16927,    54,    56, 31179, 11002,    72,
          8835, 31221, 31423,    55, 31396, 31222, 33370, 31604,    72,  2926,
         31415, 31396, 31222, 33370,  1231, 31221, 11254, 11002, 31380,  1522,
         31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135, 31396, 31380,
            73,     5,    54,    56, 34399,    55, 31191,    60,     5, 31415,
         31396, 31222, 33370,  1231, 31221, 31195,    60, 31179, 11002,    73,
          2122,    72,  6787, 31161, 31415, 31396, 31222, 33370,  1231, 31221,
         31195,    60, 31179, 11002,    73,     2]])

Tips:

这里 pad 的 0 和 mask 的 0 需要区分,这里 padding 为 0 是因为我们定义了 pad_token = unk_token,而 unk_token = <unk> 经过 tokenizer encode 后得到的是 [0]。

code=tokenizer.encode(tokenizer.pad_token, max_length=10000, truncation=True)
print('pad', tokenizer.unk_token, 'token', code)
=> pad <unk> token [0]

• seq_len

这个很好理解,一个 batch 内的多个样本,每个样本的 prompt_id 的长度即 Q_id 的长度。这个长度主要用于 batch 内判断 longest 最长的序列是多长,从而对短的样本进行 padding,保证进入深度模型网络的样本维度一致。

'seq_len': tensor([13, 31])

• labels

'labels': tensor([[31106, 15875,  77,  57,  53, 31159,  53,  60,    58, 31135,
          6787,  6775,    81, 31106,  3908, 14313, 31640, 31257, 31481, 31742,
            72,    57,    53, 31640, 31187,    53,    60,    58,  9323, 31178,
            52,    79,    54,    59,    56,     2,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,  -100,
          -100,  -100,  -100,  -100,  -100,  -100],
        [31106, 33370,  5629, 16927,    54,    56, 31179, 11002,    72, 31526,
         31342,  8835, 31221, 31423,  7156,    55, 31396, 31222, 33370, 31604,
            72, 31415, 31396, 31222, 33370,  1231, 31221, 31195,  6851, 11002,
            75, 31106, 33370,  5629, 16927,    54,    56, 31179, 11002,    72,
          8835, 31221, 31423,    55, 31396, 31222, 33370, 31604,    72,  2926,
         31415, 31396, 31222, 33370,  1231, 31221, 11254, 11002, 31380,  1522,
         31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135, 31396, 31380,
            73,     5,    54,    56, 34399,    55, 31191,    60,     5, 31415,
         31396, 31222, 33370,  1231, 31221, 31195,    60, 31179, 11002,    73,
          2122,    72,  6787, 31161, 31415, 31396, 31222, 33370,  1231, 31221,
         31195,    60, 31179, 11002,    73,     2]])}

最后一个 key 是 labels,这里也可以解决我们前面的疑惑了,为什么样本里只有 input_ids 和 seq_len,但是经过 data_collator 处理送到 trainer 可以正常训练,这里的 label 对应的是 QA 里 A 的 token ids 即 target ids,以第一个 sample 为例,第一个 sample 的 A ids 为:

[31106, 3908, 14313, 31640, 31257, 31481, 31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56] + [2]

最后的 [2] 与前面 <unk> 类似,eos_token 后编码为 [2]:

code=tokenizer.encode(tokenizer.eos_token, max_length=10000, truncation=True)
print('eos', tokenizer.eos_token, 'token', code)
=> eos </s> token [2]

三.生成样本 By DIY

上面使用 Transformer API 实现了 {"input_ids":  xxx, "seq_len": xxx} 形式的数据解析与样本生成,下面我们参考上面逻辑自定义一版 data_collator。首先整理一下思路:

input_ids = Q_ids + A_ids + [SEP]_ids

seq_len = Q_ids.length

labels =  if (max_length) A_ids else A_ids + padding * n

根据 A_ids 的长度判断是否是 longest,然后决定是否补齐 padding 从而生成对应 Label。

1.样本准备

# p: [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81]
# t: [31106, 3908, 14313, 31640, 31257, 31481, 31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56]
sample1 = [31106, 15875, 77, 57, 53, 31159, 53, 60, 58, 31135, 6787, 6775, 81, 31106, 3908, 14313, 31640, 31257, 31481,
           31742, 72, 57, 53, 31640, 31187, 53, 60, 58, 9323, 31178, 52, 79, 54, 59, 56, 2]

# p: [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 31526, 31342, 8835, 31221, 31423, 7156, 55, 31396, 31222, 33370, 31604, 72, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 6851, 11002, 75]
# t: [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 8835, 31221, 31423, 55, 31396, 31222, 33370, 31604, 72, 2926, 31415, 31396, 31222, 33370, 1231, 31221, 11254, 11002, 31380, 1522, 31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135, 31396, 31380, 73, 5, 54, 56, 34399, 55, 31191, 60, 5, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002, 73, 2122, 72, 6787, 31161, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002, 73]
sample2 = [31106, 33370, 5629, 16927, 54, 56, 31179, 11002, 72, 31526, 31342, 8835, 31221, 31423, 7156, 55, 31396,
           31222, 33370, 31604, 72, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 6851, 11002, 75, 31106, 33370, 5629,
           16927, 54, 56, 31179, 11002, 72, 8835, 31221, 31423, 55, 31396, 31222, 33370, 31604, 72, 2926, 31415, 31396,
           31222, 33370, 1231, 31221, 11254, 11002, 31380, 1522, 31482, 11002, 31380, 31640, 31187, 31222, 33370, 31135,
           31396, 31380, 73, 5, 54, 56, 34399, 55, 31191, 60, 5, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60,
           31179, 11002, 73, 2122, 72, 6787, 31161, 31415, 31396, 31222, 33370, 1231, 31221, 31195, 60, 31179, 11002,
           73, 2]

json1 = {"input_ids": sample1, "seq_len": 13}
json2 = {"input_ids": sample2, "seq_len": 31}

features = [json1, json2]

样本继续使用上面构造的 json1 和 json2 并统一放到 features 的 list 中。

2.data_colloator 实现

def data_collator(features: list) -> dict:
    # 序列长度: [36, 106]
    len_ids = [len(feature["input_ids"]) for feature in features]
    # 取最长的序列长度: 106
    longest = max(len_ids)
    input_ids = []
    labels_list = []
    # 降序排列
    for ids_l, feature in sorted(zip(len_ids, features), key=lambda x: -x[0]):
        ids = feature["input_ids"]  # tokenIds
        seq_len = feature["seq_len"]  # seqLen
        # len(prompt) x [-100] + Target + [longest - len(prompt)] * [-100]
        labels = ([-100] * seq_len + ids[seq_len:] + [-100] * (longest - ids_l))
        ids = ids + [pad_token_id] * (longest - ids_l)

        _ids = torch.LongTensor(ids)
        labels_list.append(torch.LongTensor(labels))
        input_ids.append(_ids)
    # tensor([[], []])
    input_ids = torch.stack(input_ids)
    labels = torch.stack(labels_list)
    return {
        "input_ids": input_ids,
        "labels": labels,
    }

data_collator 的逻辑主要是这一句:

# len(prompt) x [-100] + Target + [longest - len(prompt)] * [-100]
labels = ([-100] * seq_len + ids[seq_len:] + [-100] * (longest - ids_l))

即将 Q mask 进行掩码,A 即 Target 保持不变,最后根据 longest 的长度决定是否 padding,这里手动指定了 pad_token_id = 0,实际代码中可以使用 tokenizer 自动指定:

tokenizer.pad_token_id

除此之外,这里和上面 API 生成还有一个区别是是否 mask Q_ids,API 把 Q_ids 全部去掉,上面方法保留了 Q_ids 的位置,但是使用 -100 进行了 mask,实际场景下二者没有区别,只不过 DIY 的 labels 如果 Q_ids 很长则会占用很多无关空间。

3.使用自定义 data_colloator

    trainer = ModifiedTrainer(
        model=model,
        train_dataset=dataset,
        args=training_args,
        callbacks=[TensorBoardCallback(writer)], # 回调将训练情况写入 tensorboard
        data_collator=data_collator,
    )

定义好 Trainer 后,将上面定一个 data_collator 传给 data_collator 参数即可,不过常规情况下我们使用 API 即可,如果自己对样本和 label 的构建有自定义需求,则可以采用后者 DIY 的形式。

四.总结

经过上面的分析,对于样本的处理和 label 的生成流程我们大致清晰了,下面解释下上面的样本如何应用在 LM 大语言模型中以及自己理解的这样构造的含义。

input 为 QA,output 为 A。Input 和 Output 分别在 Embedding lookup 获取输入向量,添加 position 向量后进入 Multi-Head Attention,首先经过 Q、K、V 的 Linear 映射转换,随后经过 Encoder 和 Decoder 的 Transformer 结构,最终通过一个 Linear + Softmax 输出预测概率 logits,输出概率 logits 与 A_ids 对应的 multi_hot 向量计算 batch loss 并回传,这里会忽略 [-100] 的掩码,更完整的 loss 计算逻辑大家可以参考 model.loss 的实现。

    # 根据输入计算 Loss
    def compute_loss(self, model, inputs, return_outputs=False):
        return model(
            input_ids=inputs["input_ids"],
            labels=inputs["labels"],
        ).loss

一般情况下,这里 QA 也分别代表 Prompt 和 Target,上面这种样本和 label 的构造方式意在学习当给定 Prompt 的前提提示下,模型能够预测得到 Target,即语言生成。如果想要学习其他不同的模式,大家也可以根据需求自定义修改上面的 data_collator。

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

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

相关文章

2023年7月广州/惠州/深圳软考信息系统项目管理师报名

信息系统项目管理师是全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff08;简称软考&#xff09;项目之一&#xff0c;是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试&#xff0c;既属于国家职业资格考试&#xff0c;又是职称资…

矩阵的范数和特征值之间的关系

参考&#xff1a; linear algebra - Why is the norm of a matrix larger than its eigenvalue? - Mathematics Stack Exchange

SpringBoot 对象存储 MinIO

SpringBoot 对象存储 MinIO 1.MinIO简介 MinIO 是一个基于 Go 实现的高性能、兼容 S3 协议的对象存储。它采用 GNU AGPL v3 开源协议&#xff0c;项目地址是 https://github.com/minio/minio&#xff0c;官网是 https://min.io。 它适合存储海量的非结构化的数据&#xff0c…

园区能源控制管理系统

园区能源控制管理系统是一种能够实现对园区内能源消耗、供应和分配进行实时监控、管理和控制的系统。该系统通过对园区内各种能源设备的数据采集、处理和分析&#xff0c;为管理者提供实时的能源使用情况和数据分析&#xff0c;从而帮助管理者制定科学的能源管理策略和节能措施…

《向量数据库指南》——传统数据库上的向量搜索插件

传统数据库上的向量搜索插件 很好,现在我们已经知道了向量搜索库和向量数据库之间的区别,下面让我们来看看向量数据库与向量搜索插件有何不同。 很多传统关系型数据库和搜索系统,如 ClickHouse 和 Elasticsearch,都包含内置的向量搜索插件。例如,Elasticsearch 8.0 包…

疫情数据微处理——Numpy实战

注&#xff1a;文章内容参考了莫烦python 一、数据来源 数据来自于Kaggle公开免费数据集&#xff0c;需要的伙伴可以自行到这里下载。 二、展示数据 我们用一个字典存储csv数据的第一行、每一行开头的日期以及除了这两者外的数据。 import csv import numpy as npwith ope…

2023年7月13日,Stream流,Stream流的获取,Stream流中间聚合操作,Stream流终结操作,Calendar时间日期类,包装类

Stream流 1. 单列集合的Stream流获取 Java中的Stream流操作可以分为中间操作和终止操作两种。 中间操作包括&#xff1a; filter&#xff1a;对流中的元素进行筛选。map&#xff1a;对流中的元素进行转换。flatMap&#xff1a;对流中的元素进行扁平化映射。distinct&#x…

重启Oracle数据库

root 用户登录服务器。 1、 以oracle身份登录数据库&#xff0c;命令&#xff1a;su - oracle 2、 进入Sqlplus控制台&#xff0c;命令&#xff1a;sqlplus /nolog 3、 以系统管理员登录&#xff0c;命令&#xff1a;connect / as sysdba可以合并为&#xff1a;sqlplus sys/密码…

Acwing:第 111 场周赛(2023.7.12 C++)

目录 5047. 1序列 题目描述&#xff1a; 实现代码&#xff1a; 5048. 无线网络 题目描述&#xff1a; 实现代码&#xff1a; 二分 贪心 5049. 选人 题目描述&#xff1a; 实现代码&#xff1a; 数学 5047. 1序列 题目描述&#xff1a; 实现代码&#xff1a; #incl…

如何实现浏览器内多个标签页之间的通信?

1、使用 LocalStorage 特点&#xff1a;同域共享存储空间&#xff1b;持久化将数据存储在浏览器&#xff1b;提供事件监听storage变化 实现逻辑&#xff1a; A页面将数据存储在本地。B页面监听storage的变化&#xff0c;同步storage的最新数据&#xff1b; 好处&#xff1a;操…

绘制数据图

读取文件&#xff1a; ( 1960 : 30 64 6 ) (1970 : 24 69 7 ) (1980 : 23 68 9 ) (1990 : 18 70 12) (2000 : 15 68 17 ) (2010 : 13 64 23 ) (2020 : 12 60 28) ( 2030 : 11 59 30 ) ( 2040 : 11 56 33 ) 运行代码&#xff1a; //绘制数据图 #include"std_lib_facil…

2.字体图标

2.1字体图标的产生 字体图标使用场景:主要用于显示网页中通用、常用的一些小图标。 精灵图是有诸多优点的&#xff0c;但是缺点很明显&#xff1a; 1.图片文件比较大 2.图片本身放大和缩小会失真 3.一旦图片制作完毕想要更换非常复杂 字体图标iconfont可以很好的解决以上问题…

Redis特性初识及其安装与配置

目录 1.认识Redis Redis主要特点 主要应用场景 2.MySQL VS NoSQL 3.Redis的安装与配置 redis5的安装 修改配置文件 启动redis 4.Redis客户端 命令行客户端 图形化界面客户端 基于redis的API自行开发客户端 1.认识Redis Redis&#xff08;Remote Dictionary Serve…

Python-Web框架flask使用

目录 1.Web框架 1.1 flask 1.1.1 debug调试 1.1.2 定义参数web服务 获取字符串 ​编辑 1.1.3 html网页渲染 1.13.1 带参数传给网页文件 普通元素 列表元素 字典元素 1.Web框架 1.1 flask python的web框架&#xff0c;目录结构如下&#xff1a; 1.static存放的是css,…

《Linux运维总结:Centos7.6之OpenSSH7.4升级版本至9.3》

一、环境信息 操作系统&#xff1a;Centos7.6.1810 OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 如下图所示&#xff1a; 注意&#xff1a;升级后由于加密算法的区别&#xff0c;低版本的SSH工具可能无法连接&#xff0c;建议改用Xshell7或SecureCRT9.0以上版本。 二、注意事项 1、 …

2023最新版 Navicat 16.2.3安装和试用教程详解:轻松掌握最新版本的数据库管理工具连接Redis

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

kettle开发-Day40-AI分流之case/switch

前言&#xff1a; 前面我们讲到了很多关于数据流的AI方面的介绍&#xff0c;包括自定义组件和算力提升这块的&#xff0c;今天我们来学习一个关于kettle数据分流处理非常重要的组件Switch / Case 。当我们的数据来源于类似日志、csv文件等半结构化数据时&#xff0c;我们需要在…

计算机网络 day6

目录 arp协议 arp病毒\欺骗 arp病毒的运行原理 arp病毒产生的后果&#xff1a; 解决方法&#xff1a; ICMP协议 ICMP用在哪里&#xff1f; ICMP协议数据的封装过程 ​编辑 为什么icmp协议封装好数据后&#xff0c;还要加一个ip包头&#xff0c;再使用ip协议再次进…

【UE4 C++】08-生成抛射物来模拟攻击效果

步骤 新建一个C类&#xff0c;父类为Actor&#xff0c;命名为“ASMagicProjectile” 在“ASMagicProjectile.h”中添加如下代码&#xff1a; 在“ASMagicProjectile.cpp”中添加如下代码&#xff1a; 编译后在虚幻编辑器中新建一个蓝图&#xff0c;选择父类为我们刚创建的C类…

vscode插件开发之终端那些事儿

在开发vscode插件的时候&#xff0c;好几个设计都需要集成终端。 查资料后发现vsocd为开发者提供了丰富的终端API。 结合我自己的需求来展开终端的那些事儿吧&#xff1a; 从treeview中点击触发打开一个终端 无关的代码省略&#xff1a; vscode.window.createTerminal({name…