DSPy:不需要手写prompt啦,You Only Code Once!

在这里插入图片描述

  论文地址:https://arxiv.org/abs/2310.03714
  项目地址:https://github.com/stanfordnlp/dspy

文章目录

    • 1. 背景
    • 2. 签名
    • 3. 模块
      • 3.1 预测模块
      • 3.2 其他内置模块
    • 4. 提词器
    • 5. 评估目标
    • 6. 代码分析
      • 6.1 _prepare_student_and_teacher
      • 6.2 _prepare_predictor_mappings
      • 6.3 _bootstrap
        • 6.3.1 构造prompt
        • 6.3.2 送入LLM
        • 6.3.3 解析响应结果
      • 6.4 _train
    • 7. 完整代码
    • 预告

1. 背景

  想让 LLM 完成一个任务,需要手动设计 prompt,但 LLM 对 prompt 比较敏感,稍有变动就会导致不同的结果,反复试验会将 prompt 变得越来越长,这对 LLM 在特定任务上的优化带来了极大的挑战。
  DSPy 的目的就是根据任务的样例数据,自动优化 prompt,即任务数据+代码=适用该任务的 prompt。
  DSPy 引入了三个抽象概念(编程模型范式):
   - Signature (签名):抽象模块的输入/输出行为。
   - Module (模块): 取代现有的手动提示技术,并且可以在任意管道中组合。
   - Teleprompter (提词器): 优化管道中的所有模块以最大化指标。

  注:本博客使用的 dspy-ai 版本为2.5.3.

2. 签名

  DSPy 签名是函数的自然语言类型声明,一个简短的声明性规范,告诉 DSPy 文本转换需要做什么(what),而不是提示 LLM 该如何做(how)
  DSPy 签名是 input fieldsoutput fields 以及一个可选的 instruction 组成的元组。一个字段由字段名称和可选的元数据组成。字段的角色由 DSPy 根据字段名称推断。例如,DSPy 编译器将使用上下文学习来解释不同地问题和答案,并会迭代地完善它对这些字段的使用。

class BasicQA(dspy.Signature):"""Answer questions with short factoid answers."""question = dspy.InputField(desc="question about something")answer = dspy.OutputField(desc="often between 1 and 5 words")

  签名比提示有两个好处:它们可以被编译成自适应和适应管道的提示或微调。这主要是通过为每个签名引导有用的示例来实现的。此外,它们处理结构化的格式和解析逻辑,以减少(或理想情况下,避免)用户程序中脆弱的字符串操作。
  实际使用时,也可以使用简写方法(符号表示法):

qa = dspy.Predict("question -> answer")
qa(question="Where is Guaranı spoken?" )
# Out: Prediction(answer="Guaranı is spoken mainly in South America.")

  在符号表示法中,每个字段的名称表明了输入(或输出)字段在转换中扮演的语义角色。DSPy 将解析这个符号表示法,并为 LM 扩展字段名称,以便english_document -> french_translation会提示进行英语到法语的翻译。如果需要,DSPy 还提供了更高级的编程接口来表达对签名的更明确的约束。
  当需要更多控制时,可以将签名表示为 Python 类,以提供转换的明确说明,并更直接地描述每个字段的格式或角色。例如,以下签名使用 context 和可选 question 生成搜索查询:

class GenerateSearchQuery(dspy.Signature):"""Write a simple search query that will help answer a complex question."""context = dspy.InputField(desc="may contain relevant facts")question = dspy.InputField()query = dspy.OutputField(dtype=dspy.SearchQuery)

  使用上述签名,可以指定一个完整的系统,用于生成合成信息检索数据集,其中查询由 LLM 生成的中间问题产生:

query_gen = dspy.Predict(GenerateSearchQuery)
query_gen(context="Language typology")
# Out: Prediction(question="What are the main types of language classification?", query="`language classification` OR `language typology` -wikipedia")

  如果提供了问题,可以像下面这样提供:

query_gen(context="Language typology", question="What are the primary language families of South America?")

  用户可以可选地指定输出字段的类型为 bool、int、float、list 或 dict,而不是默认的字符串类型,如 contexts, question -> answer_found: bool

3. 模块

  DSPy 签名简单地定义了一个接口,并提供了关于预期行为的类型提示。要使用一个签名,我们必须声明一个具有该签名的模块,就像我们在上面实例化了一个 Predict 模块一样。这样的模块声明返回一个具有该签名的函数。

3.1 预测模块

  在 DSPy 中处理签名的核心模块是 Predict。在内部,Predict 存储了提供的签名、一个可选的 LM(最初为 None,但会覆盖此模块的默认 LM),以及用于提示的演示列表(最初为空)。像 PyTorch 中的层一样,实例化的模块表现为一个可调用的函数:它接受与签名输入字段相对应的关键字参数(例如 question),格式化一个实现签名的提示,包括适当的演示、调用 LM,并解析输出字段。当 Predict 检测到它在编译(compile)模式下使用时,它还将在内部跟踪输入/输出跟踪,以协助 teleprompter 进行引导。

3.2 其他内置模块

  DSPy 模块将提示技术转换为支持任何签名的模块化函数,这与使用任务特定细节(例如,手写的少数示例)提示 LM 的标准方法形成鲜明对比。为此,DSPy 包括一些更复杂的模块,如ChainOfThought、ProgramOfThought、MultiChainComparison 和 ReAct,这些都可以互换使用,以实现 DSPy 签名。例如,简单地将上面的 Predict 更改为 ChainOfThought,就会得到一个逐步思考然后才允许其输出字段的系统。
  重要的是,所有这些模块都以几行代码实现,通过扩展用户定义的签名并根据需要在新签名上多次调用 Predict。例如,下面展示了内置 ChainOfThought 的简化实现:

class ChainOfThought(dspy.Module):def __init__(self, signature):# Modify signature from `*inputs -> *outputs` to `*inputs -> rationale , *outputs`.rationale_field = dspy.OutputField(prefix="Reasoning: Let’s think step by step.")signature = dspy.Signature(signature).prepend_output_field(rationale_field)# Declare a sub-module with the modified signature.self.predict = dspy.Predict(signature)def forward(self, **kwargs): # Just forward the inputs to the sub-module.return self.predict(**kwargs)

4. 提词器

  当编译 DSPy 程序时,我们通常调用一个 teleprompter,这是一个优化器,它接受程序、训练集和度量标准,并返回一个新的优化程序。不同的 teleprompters 采用不同的优化策略。
  在 DSPy 中,训练集可能很小,可能只有几个例子,尽管更大的数据可以支持更强大的优化。训练示例可能是不完整的,即只需要输入值。除非它们需要在度量标准中使用,否则不需要标签。
  度量标准可以是简单的概念,如精确匹配(EM)或 F1,但它们也可以是平衡多个关注点的完整 DSPy 程序。
  例如,我们可以根据问答对 qa_trainset 数据集 和度量 EM 编译上面的 RAG 模块:

# Small training set with only questions and final answers.
qa_trainset = [dspy.Example(question="What is the capital of France?", answer="Paris")]# The teleprompter will bootstrap missing labels: reasoning chains and retrieval contexts.
teleprompter = dspy.BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
compiled_rag = teleprompter.compile(RAG(), trainset=qa_trainset)

  在这个例子中,BootstrapFewShot(teleprompter) 模拟了训练示例中的 RAG。它将收集每个模块的示范(demonstrations)(即其输入-输出行为的示例),这些示范共同导致有效输出(即尊重签名和度量标准)。
  如果想根据检索到的上下文将编译后的程序推送为提取程序,则可以自定义一个指标来代替 dspy.evaluate.answer_exact_match

def answer_and_context_match(example , pred , trace=None):answer_match = dspy.evaluate.answer_exact_match(example, pred)# Is the prediction a substring of some passage?context_match = any((pred.answer.lower() in c) for c in pred.context)return answer_match and context_match

  teleprompter也可以通过指定教师程序来组合,适用教师模型(LLM)来微调学生模型(小LLM),将 BootstrapFewShot 改为 BootstrapFinetune,这里不再介绍。
  编译时会经历三个阶段:
  - 候选生成:编译器会递归地找到程序中所有唯一的预测模块(预测器),包括那些嵌套在其他模块下的预测器。对于每个唯一的预测器 p p p,teleprompter 可以为 p p p的参数生成候选值:指令、字段描述或示例(即输入-输出对)。在当前版本中更专注于示例,并发现简单的拒绝采样方法可以帮助引导高效的多级系统。
  - 参数优化:许多超参数调整算法(例如,随机搜索或 HyperOpt 和 Optuna 中的树结构 Parzen 估计器)可以应用于候选值的选择。另一种优化类型是使用 BootstrapFinetune 进行微调,其中示例用于更新每个预测器的 LM 权重。当应用此方法时,每个模块的 LM 参数将更新为新的 LM 权重。通常,我们使用度量标准和交叉验证来优化训练集或验证集上的平均质量。
  - 更高阶程序优化:DSPy 编译器支持的另一种优化类型是修改程序的控制流。其中最简单的形式之一是集成,集成将引导同一程序的多个副本,然后将程序替换为一个新程序,该程序并行运行所有副本,并通过自定义函数(例如,多数投票)将它们的预测合并为一个。在未来的工作中,这个阶段可以轻松适应更多动态(即测试时)引导以及自动回溯逻辑的技术。

5. 评估目标

  编程框架可以从多个维度进行评估:计算效率、开发者效率、代码和概念的直观性等等。在这篇论文中,作者专注于当前 LM 流水线可能面临的最紧迫问题:手工编写的任务特定提示在实现高性能系统中的作用。评估旨在测试以下假设:
  - 使用DSPy,我们可以用简洁且定义良好的模块替换手工制作的提示字符串,而不会降低质量或表达能力。
  - 通过将模块参数化并将提示视为优化问题,DSPy 能更好地适应不同的 LM,并且可能胜过专家编写的提示。
  - 由此产生的模块化使得更彻底地探索具有有用性能特征或符合细微度量标准的复杂流水线成为可能。
  作者的评估将使用多样的"task–program pairs"来探索这些假设。希望这能从一个转变开始,即从 不同 LM 在任务 T 上的表现如何 这样的未明确指定的问题,转向 在程序 P 上使用策略 S 编译时,它们在任务 T 上的表现如何,这是一个定义明确且可复现的运行。最终,我们的目标是减少现代 AI 中巧妙提示构造的作用,转而支持新模块化、可组合程序和优化器的发展。

6. 代码分析

  本文以地域识别任务为例,使用BootstrapFewShot.compile进行编译优化,来分析一下 PSPy 的优化流程:

def compile(self, student, *, teacher=None, trainset):self.trainset = trainsetself._prepare_student_and_teacher(student, teacher)self._prepare_predictor_mappings()self._bootstrap()self.student = self._train()self.student._compiled = True# set assert_failures and suggest_failures as attributes of student w/ value 0self.student._assert_failures = 0self.student._suggest_failures = 0return self.student

6.1 _prepare_student_and_teacher

  在当前示例中,student 为 RAG 实例,teacher 默认设置为 None,在 _prepare_student_and_teacher 函数中,会将 student 复制一份作为teacher的初始值,然后对 teacher 进行编译优化,具体为:

teleprompter = LabeledFewShot(k=self.max_labeled_demos)
self.teacher = teleprompter.compile(self.teacher.reset_copy(), trainset=self.trainset)

  这里的 compile 主要是对 teacher 随机采样k各训练数据:

predictor.demos = rng.sample(self.trainset, min(self.k, len(self.trainset)))

6.2 _prepare_predictor_mappings

  这里主要是获得两个数据:name2predictor和predictor2name。

name2predictor, predictor2name = {}, {}
for (name1, predictor1), (name2, predictor2) in zip(student.named_predictors(), teacher.named_predictors()):name2predictor[name1] = None  # dict(student=predictor1, teacher=predictor2)predictor2name[id(predictor1)] = name1predictor2name[id(predictor2)] = name2self.name2predictor = name2predictor
self.predictor2name = predictor2name

6.3 _bootstrap

  在这个函数里面对数据进行采样,遍历 trainset 数据集中的每个数据,将这条数据送入到 LLM,如果 LLM 给出的答案与 ground truth 一致,则记录其 idx,然后从未预测正确的训练集中随机采样,作为 validation 数据集。
  LLM 的预测及评估的流程在 _bootstrap_one_example 函数中,先来看下 LLM 的预测流程:

在这里插入图片描述
  具体执行流程是:

在这里插入图片描述

  在Adapter中分为三部分:构造prompt、送入LLM、解析LLM的响应。

6.3.1 构造prompt

  代码位置:ChatAdapter.format()
  - Step1:根据签名来构建 system 指令:messages.append({"role": "system", "content": prepare_instructions(signature)})

# prepare_instructions()
parts = ['Your input fields are:\n1. `question` (str): question about something', 'Your output fields are:\n1. `reasoning` (str)\n2. `answer` (str): default: none', 'All interactions will be structured in the following way, with the appropriate values filled in.', '[[ ## question ## ]]\n{question}', '[[ ## reasoning ## ]]\n{reasoning}\n\n[[ ## answer ## ]]\n{answer}', '[[ ## completed ## ]]', 'In adhering to this structure, your objective is: \n        Recognize location from question and give the reasoning for the same.'
]
return '\n\n'.join(parts).strip()

  - Step2:根据签名和随机采样的数据(demos)来构建 user 指令:messages.append({"role": "user", "content": format_turn(signature, example)})

# format_turn()
content = ['This is an example of the task, though some input or output fields are not supplied.', '[[ ## question ## ]]\n美国留学回来', 'Respond with the corresponding output fields, starting with the field `reasoning`, then `answer`, and then ending with the marker for `completed`.'
]
return '\n\n'.join(content).strip()

  - Step3:根据签名和随机采样的数据(demos)来构建 assistant 指令:messages.append({"role": "assistant", "content": format_turn(signature, example)})

# format_turn()
content = ['[[ ## reasoning ## ]]\nNot supplied for this particular example.\n\n[[ ## answer ## ]]\n美国\n\n[[ ## completed ## ]]'
]
return '\n\n'.join(content).strip()

  - Step4:根据签名和输入数据来构建 user 指令:messages.append({"role": "user", "content": format_turn(signature, inputs)})

# format_turn()
content = ['[[ ## question ## ]]\n担任中职学校班级班主任,是否需要具备教师资格证?', 'Respond with the corresponding output fields, starting with the field `reasoning`, then `answer`, and then ending with the marker for `completed`.'
]
return '\n\n'.join(content).strip()
6.3.2 送入LLM

  DSPy 使用litellm.completion来完成 LLM-API 的调用。

6.3.3 解析响应结果

  将 messages 数据传入 LLM,得到响应结果:

ModelResponse(id='chat-51cf2a4062ef4801bd40e281d600b5ff', choices=[Choices(finish_reason='stop', index=0, message=Message(content='[[ ## reasoning ## ]]\nNot supplied for this particular example.\n\n[[ ## answer ## ]]\nnone\n\n[[ ## completed ## ]]', role='assistant', tool_calls=None, function_call=None))], created=1728648483, model='qwen2dot5-32b-4bit', object='chat.completion', system_fingerprint=None, usage=Usage(completion_tokens=25, prompt_tokens=1613, total_tokens=1638, completion_tokens_details=None, prompt_tokens_details=None), service_tier=None, prompt_logprobs=None
)

  从响应结果中解析出 reasoning 和 answer,然后在Predict.forward()函数中将结果封装到一个 Prediction 实例中,并返回。
  评估流程主要是验证预测结果是否正确,依据是自定义的validate_answer()函数,这里采用的是精确匹配,即将预测的结果与 ground truth 进行比较,看看结果是否一致。
  如果验证结果正确,则将这个训练数据存入到 name2traces 中,作为 augmented_demos:

if success:for step in trace:predictor, inputs, outputs = stepdemo = Example(augmented=True, **inputs, **outputs)try:predictor_name = self.predictor2name[id(predictor)]except KeyError:continuename2traces[predictor_name].append(demo)

6.4 _train

  从 validation 数据集中随机采样数据,并与 augmented_demos 中的数据合并在一起,作为 student 的 demos 数据:

rng = random.Random(0)
raw_demos = self.validationfor name, predictor in self.student.named_predictors():augmented_demos = self.name2traces[name][: self.max_bootstrapped_demos]sample_size = min(self.max_labeled_demos - len(augmented_demos), len(raw_demos))sample_size = max(0, sample_size)raw_demos = rng.sample(raw_demos, sample_size)predictor.demos = augmented_demos + raw_demos

  分析到这,真是离谱他妈给离谱开门,离谱到家了,这训练了啥,这不妥妥的就是内置prompt模板+fewshot来构造prompt吗???

在这里插入图片描述

7. 完整代码

# -*- coding: utf-8 -*-
# Author  : liyanpeng
# Email   : yanpeng.li@cumt.edu.cn
# Datetime: 2024/9/29 15:03
# Filename: location.py
import dspy
from dspy.teleprompt import BootstrapFewShot, BootstrapFewShotWithRandomSearch, BootstrapFewShotWithOptuna, KNNFewShot
from dspy import Exampleimport pandas as pd
import randomclass BuildDataset:def __init__(self):self.xlsx_path = 'data/location_data.xlsx'random.seed(10086)def build(self, val_ratio=0.2):df = pd.read_excel(self.xlsx_path, dtype=str)data_list = []for row in df.values:question, answer = rowdata_list.append(Example(question=question, answer=answer).with_inputs('question'))random.shuffle(data_list)val_num = int(len(data_list) * val_ratio) + 1return data_list[:-val_num], data_list[-val_num:]class CoTSignature(dspy.Signature):"""Recognize location from question and give the reasoning for the same."""question = dspy.InputField(desc="question about something")answer = dspy.OutputField(desc="default: none")class CoTPipeline(dspy.Module):def __init__(self, signature):super().__init__()self.signature = signatureself.predictor = dspy.ChainOfThought(self.signature)def forward(self, question):prediction = self.predictor(question=question)return dspy.Prediction(reasoning=prediction.reasoning, answer=prediction.answer)def validate_answer(example, pred, trace=None):answer_EM = dspy.evaluate.answer_exact_match(example, pred)return answer_EMif __name__ == '__main__':data_builder = BuildDataset()trainset, valset = data_builder.build()llm_model = dspy.LM(model='openai/qwen2dot5-32b-4bit', api_base='http://192.168.3.21:11026/v1', api_key='none',cache=False)dspy.settings.configure(lm=llm_model)teleprompter = BootstrapFewShot(metric=validate_answer,max_bootstrapped_demos=4,max_rounds=1)# teleprompter = BootstrapFewShotWithRandomSearch(metric=validate_answer,#                                                 max_bootstrapped_demos=4,#                                                 max_rounds=1)compiled_cot = teleprompter.compile(CoTPipeline(CoTSignature), trainset=trainset)test_example = '大洛阳有好玩的地方吗?'cot = dspy.ChainOfThought(CoTSignature)pred = cot(question=test_example)print("[uncompiled]Question --> Reasoning --> Predicted Answer")print(f"{test_example} --> {pred.reasoning} --> {pred.answer}")pred = compiled_cot(question=test_example)print("[compiled]Question --> Reasoning --> Predicted Answer")print(f"{test_example} --> {pred.reasoning} --> {pred.answer}")

  运行结果:

 30%|█████████████▏                              | 6/20 [00:23<00:54,  3.86s/it]
Bootstrapped 4 full traces after 7 examples in round 0.
[uncompiled]Question --> Reasoning --> Predicted Answer
大洛阳有好玩的地方吗? --> 从问题中,我们可以看到关键词"大洛阳",这表明问题是关于洛阳这个城市。洛阳是中国河南省的一个城市,有着丰富的历史和文化遗迹,因此问题询问的是洛阳是否有好玩的地方。 --> 洛阳有很多好玩的地方,比如龙门石窟、白马寺等。
[compiled]Question --> Reasoning --> Predicted Answer
大洛阳有好玩的地方吗? --> 问题中提到的"大洛阳"暗示了地点是在中国河南省的洛阳市。 --> 洛阳

预告

  其实啊,在这篇论文中没有提高,另外一篇 DSPy 的论文及项目中有介绍,就是 COPRO 和 MIPROv2 这两个优化器,他们会根据任务的不同动态提出(微调)指令,这些内容将在下一篇博客中介绍。

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

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

相关文章

985研一,转嵌入式好还是后端开发好?

有个老铁问&#xff0c;985研一&#xff0c;转嵌入式好还是后端开发好&#xff1f; 我认为&#xff0c;这学历&#xff0c;两个随便挑&#xff0c;我说的&#xff0c;从趋势来看&#xff0c;更建议嵌入式&#xff0c;走供应链上游&#xff0c;芯片原厂、新能源车企、军工或者搞…

力扣143:重排链表

给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示…

qt creator 转 visual stdio 项目调试

因果 大家在使用qt creator调试程序时&#xff0c;会出现未知错误&#xff0c;比如下图&#xff0c;直接release运行就没有问题。由于调试复杂程序&#xff0c;使用qt creator都感觉不如vs&#xff0c;会报未知中断。 所以有了从qt creator转换到 visual stdio来调试的想法。…

【电子元件】光通量和色温 (欧司朗LED灯珠 KW3 CGLNM1.TG命名规则)

什么是光通量&#xff1f; 光通量&#xff08;Luminous Flux&#xff09;是衡量光源在单位时间内发出的可见光总量的物理量&#xff0c;表示的是光源产生的总光能量&#xff0c;其中只考虑人眼能感知的部分。它通常以流明&#xff08;lumen&#xff0c;符号为 lm&#xff09;为…

如何使用gitlab切换分支

第一步&#xff0c;在gitlab上新建一个远程分支。选择New branch即可新建一个&#xff0c;但是注意往往是在当前分支下新建的分支&#xff0c;所以新分支里会有当前分支的内容。 第二步&#xff0c;在本地当前分支在运行这三行命令&#xff0c;即可得到一个空的新分支。 git c…

springboot2.0x 和springboot 1.0 整合redis 使用自定义CacheManager 问题

问题描述&#xff1a; 在我们深入理解springboot2.0x的缓存机制的时候&#xff0c;发现在springboot1.0 和springboot2.0 中默认的序列化都是使用的jdk的 Serializer 实现这个接口&#xff0c;jdk自带的序列化方法&#xff0c;由此我们需要自己去创建自定义的RedisCacheManager…

《Python游戏编程入门》注-第2章2

《Python游戏编程入门》的“2.2.5 绘制线条”中提到了通过pygame库绘制线条的方法。 1 相关函数介绍 通过pygame.draw模块中的line()函数来绘制线条&#xff0c;该函数的格式如下所示。 line(surface, color, start_pos, end_pos, width1) -> Rect 其中&#xff0c;第一…

AUTOSAR CP 中 BswM 模块功能与使用介绍(2/2)

三、 AUTOSAR BswM 模块详解及 ARXML 示例 BswM 模块的主要功能 BswM&#xff08;Basic Software Mode Manager&#xff09;模块在 AUTOSAR 架构中扮演着模式管理的核心角色。它负责管理车辆的各种模式&#xff08;如启动、运行、停车等&#xff09;&#xff0c;并根据不同的…

基于vue框架的的电子商务网站68pwt(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,商品分类,商品信息 开题报告内容 基于Vue框架的电子商务网站开题报告 一、研究背景与意义 随着互联网技术的不断发展和普及&#xff0c;电子商务已成为现代商业活动的重要组成部分。电子商务网站作为线上交易的主要平台&#xf…

Apple Vision Pro市场表现分析:IDC最新数据揭示的真相

随着AR/VR技术逐渐成熟并被更多消费者接受,2024年第二季度(Q2)成为这一领域的一个重要转折点。根据国际数据公司(IDC)发布的最新报告,整个AR/VR市场在本季度经历了显著的增长。接下来,我们将深入探讨Apple Vision Pro在这股增长浪潮中的具体表现。 市场背景 2024年Q2,…

Excel:vba实现生成随机数

Sub 生成随机数字()Dim randomNumber As IntegerDim minValue As IntegerDim maxValue As Integer 设置随机数的范围(假入班级里面有43个学生&#xff0c;学号是从1→43)minValue 1maxValue 43 生成随机数(在1到43之间生成随机数)randomNumber Application.WorksheetFunctio…

混个1024勋章

一眨眼毕业工作已经一年了&#xff0c;偶然进了游戏公司成了一名初级游戏服务器开发。前两天总结的时候&#xff0c;本来以为自己这一年没学到多少东西&#xff0c;但是看看自己的博客其实也有在进步&#xff0c;虽然比不上博客里的众多大佬&#xff0c;但是回头看也算是自己的…

.net 根据html的input type=“week“控件的值获取星期一和星期日的日期

初始化 "week" 控件值&#xff1a; //MVC部分 public ActionResult WeeklyList() {int weekNo new GregorianCalendar().GetWeekOfYear(System.DateTime.Now, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday);string DefaultWeek DateTime.No…

利用移动式三维扫描技术创建考古文物的彩色纹理网格【上海沪敖3D】

文章来源于蔡司工业质量解决方案&#xff0c;作者蔡司工业质量 在考古环境中&#xff0c;三维扫描技术应用广泛&#xff0c;如存档、保存、复制和分享&#xff08;包括实体和虚拟形式&#xff09;。 文中&#xff0c;通过真实的扫描案例&#xff0c;您将了解到三维光学解决方案…

JavaWeb 23.一文速通npm的配置和使用

目录 一、npm的介绍 二、npm的安装和配置 1.安装 &#xff1a; 2.配置依赖下载使用阿里镜像 3. 配置全局依赖下载后存储位置 4.升级npm版本 5.环境变量配置 三、npm常用命令 1.项目初始化 npm.init npm init -y 2.安装依赖文件 3. 升级依赖 4.卸载依赖 5.查看依赖 查看项目…

Android 原生开发与Harmony原生开发浅析

Android系统 基于Linux ,架构如下 底层 (Linux )> Native ( C层) > FrameWork层 (SystemService) > 系统应用 (闹钟/日历等) 从Android发版1.0开始到现在15,经历了大大小小的变革 从Android6.0以下是个分水岭,6.0之前权限都是直接卸载Manifest中配置 6.0开始 则分普…

Rust的move关键字在线程中的使用

为什么使用 move&#xff1f; 在 Rust 中&#xff0c;move 关键字主要用于闭包。当我们在一个线程中创建一个闭包并将其传递给另一个线程时&#xff0c;如果闭包中使用了某些变量&#xff0c;就需要决定这些变量的所有权归属。 不使用 move&#xff1a; 默认情况下&#xff0…

LabVIEW提高开发效率技巧----事件日志记录

在LabVIEW开发中&#xff0c;集成事件日志记录系统是提升程序调试效率和确保系统运行稳定的关键步骤。通过记录关键操作和异常事件&#xff0c;开发人员可以快速定位问题、优化程序性能&#xff0c;并确保系统的稳定性和可靠性。 1. 事件日志的作用 事件日志是指在程序运行过…

python-PyQt项目实战案例:制作一个视频播放器

文章目录 1. 关键问题描述2. 通过OpenCV读取视频/打开摄像头抓取视频3. 通过PyQt 中的 QTimer定时器实现视频播放4. PyQt 视频播放器实现代码参考文献 1. 关键问题描述 在前面的文章中已经分享了pyqt制作图像处理工具的文章&#xff0c;也知道pyqt通过使用label控件显示图像的…

MySQL 初阶——多版本控制 MVCC

一、版本链&#xff08;undo 日志&#xff09; a. 什么是版本链 版本链就是一条以事务为节点的单链表。其 next 指针指向前一个版本的事务。 b. 版本链的增删 当一个事务被完成时&#xff0c;这个事务就会被加入到版本链里去&#xff1b;当要回滚时&#xff0c;版本链就会删…