为视觉语言多模态模型进行偏好优化

news2024/9/20 7:56:13

训练模型使得它能够理解并预测人类偏好是一项比较复杂的任务。诸如 SFT (Supervised finetuning) 的传统的方法一般都需要耗费较大成本,因为这些算法需要对数据打上特定的标签。而偏好优化 (Preference Optimization) 作为一种替代选项,通常可以简化这一过程,并产出更准确的结果。通过对候选回答的对比和排序,而不是赋予固定的标签,偏好优化使得模型能更高效地捕捉人类偏好中的细微差别。

偏好优化已经在大语言模型中广泛使用了,但现在,它也可以用在视觉语言模型 (VLM) 上。得益于 TRL 的开发,现在我们可以 使用 TRL 对 VLM 进行直接偏好优化 (Direct Preference Optimization)。本文将会介绍使用 TRL 和 DPO 对视觉语言模型进行训练的全过程。

偏好数据集

进行偏好优化,首先我们需要有一个能体现用户偏好的数据集。在双项选择的设定下,相应的数据一般包含一个提示词 (Prompt) 和两个候选回答,两个回答中一个被记为选中 (chosen),另一个被记为淘汰 (rejected)。模型将要去学习着给出选中的回答,而不是被淘汰的那个。下图就是一个例子:

在这里插入图片描述

图片来自 openbmb/RLAIF-V-Dataset 数据集

❔ Question: How many families?

  • ❌ Rejected: The image does not provide any information about families.
  • ✅ Chosen: The image shows a Union Organization table setup with 18,000 families.

需要注意的是,尽管选中的回答也不是完全正确的 (回答 18000 个家庭还是不对,应该是 18000000),但它也好于那个被淘汰的回答。

本文将使用 openbmb/RLAIF-V-Dataset 作为示例数据集,它包含了超过 83000 条标注的数据。可以通过下面代码查看一下数据集:

>>> from datasets import load_dataset
>>> dataset = load_dataset("openbmb/RLAIF-V-Dataset", split="train[:1%]")
>>> sample = dataset[1]
>>> sample["image"].show()
>>> sample["question"]
'how many families?'
>>> sample["rejected"]
'The image does not provide any information about families.'
>>> sample["chosen"]
'The image shows a Union Organization table setup with 18,000 families.'

我们将要训练的 VLM 模型需要文本和图像同时作为输入,所以这里的第一步还是要对数据集格式进行改造。一条数据应该被结构化成能模拟人机对话的形式。用户提供一个提示语,其中包含一张图片和一个问题,然后模型需要能够给出一个回答。我们用以下代码实现格式转换:

from datasets import features
from transformers import AutoProcessor

processor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)

def format(example):
    # Prepare the input for the chat template
    prompt = [
        {
            "role": "user",
            "content": [{"type": "image"}, {"type": "text", "text": example["question"]}],
        },
    ]
    chosen = [
        {
            "role": "assistant",
            "content": [{"type": "text", "text": example["chosen"]}],
        },
    ]
    rejected = [
        {
            "role": "assistant",
            "content": [{"type": "text", "text": example["rejected"]}],
        },
    ]
    # Apply the chat template
    prompt = processor.apply_chat_template(prompt, tokenize=False)
    chosen = processor.apply_chat_template(chosen, tokenize=False)
    rejected = processor.apply_chat_template(rejected, tokenize=False)
    # Resize the image to ensure it fits within the maximum allowable
    # size of the processor to prevent OOM errors.
    max_size = processor.image_processor.size["longest_edge"]
    example["image"].thumbnail((max_size, max_size))
    return {"images": [example["image"]], "prompt": prompt, "chosen": chosen, "rejected": rejected}

# Apply the formatting function to the dataset,
# remove columns to end up with only "images", "prompt", "chosen", "rejected" columns
dataset = dataset.map(format, remove_columns=dataset.column_names)

# Make sure that the images are decoded, it prevents from storing bytes.
# More info here https://github.com/huggingface/blog/pull/2148#discussion_r1667400478
f = dataset.features
f["images"] = features.Sequence(features.Image(decode=True)) # to avoid bytes
dataset = dataset.cast(f)

完成了格式转换,我们来看看第一条数据:

>>> dataset[1]
{'images': [<PIL.JpegImagePlugin.JpegImageFile image mode=L size=980x812 at 0x154505570>],
 'prompt': 'User:<image>how many families?<end_of_utterance>\n',
 'rejected': 'Assistant: The image does not provide any information about families.<end_of_utterance>\n',
 'chosen': 'Assistant: The image shows a Union Organization table setup with 18,000 families.<end_of_utterance>\n'}

OK!接下来准备好 GPU,训练马上开始。

训练

我们将使用 Idefics2-8b 作为我们的示例模型,但 TRL 里的 DPO 也是能用在像 Llava 1.5 和 PaliGemma 这样的模型上的 (可参考这篇文章: Finetuning Llava 1.5, PaliGemma and others)。不过训练之前,我们先检查一下我们的 GPU 显存是否够用:

训练需要多大的 GPU 显存?

一个 80GB VRAM 的 GPU 足够用来对 Idefics2-8b 进行 DPO 训练吗?我们可以先计算一下:

我们用 NNN 表示参数的数量,用 PPP 表示训练使用的精度。训练过程中,下列部分需要共同放入显存中:

  • 要训练的模型: N×PN \times PN×P
  • 用以防止模型产生偏离的参考模型: 和要训练的模型一样大,所以也是 N×PN \times PN×P
  • 梯度: 我们对所有参数都进行训练,所以每个参数都有梯度: N×PN \times PN×P
  • 优化器的状态量: 我们使用 AdamW,一个参数会保存两个状态量,所以需要: 2×N×P2 \times N \times P2×N×P

Idefics2-8b 有 80 亿 (8B) 参数,我们使用 float32 精度,每个参数占 4 个字节。所以总的显存需求是:

参数来源计算公式显存需求
要训练的模型8×109×48 \times 10^9 \times 48×109×432 GB
参考模型8×109×48 \times 10^9 \times 48×109×432 GB
梯度8×109×48 \times 10^9 \times 48×109×432 GB
优化器状态量2×8×109×42 \times 8 \times 10^9 \times 42×8×109×464 GB
合计160 GB

这远超我们前面说的 80GB 显存了!幸运的是,我们可以使用量化、LoRA 等技术来大幅度地减少显存需求,让训练可以进行。接下来我们将介绍这些技术。

量化

量化会降低模型权重和激活值的精度,但也同时显著减少内存需求。将精度从 float32 改为 bfloat16 ,会让每个参数需要的比特数从 4 比特减少到 2 比特。这一策略不仅能减少内存使用,还会显著加速训练,确保以最小代价保证足够高的性能。具体做法如下:

import torch
from transformers import AutoModelForVision2Seq

model = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b", torch_dtype=torch.bfloat16)

通过如下 bf16=True 的设置, bfloat16 也可以被用在优化器上:

from transformers import TrainingArguments

training_args = TrainingArguments(..., bf16=True)

LoRA

LoRA 对参数矩阵进行低秩分解; 在训练时,固定住原参数矩阵,仅训练分解出的两个矩阵。是一种大规模减少 LLM 训练参数的方法。LoRA 已被集成在了 PEFT 库里,使用非常方便:

  from transformers import AutoModelForVision2Seq
+ from peft import get_peft_model, LoraConfig

  model = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b")
+ peft_config = LoraConfig(target_modules="all-linear")
+ model = get_peft_model(model, peft_config)

PEFT 像是给原模型进行了一次封装 (代码中称为 adapter )。训练时,实际上是这个 adapter 在被训练,而原有的模型保持不动。我们现在算算 LoRA 帮我们减少了多少要训练的参数:

>>> model.print_trainable_parameters()
trainable params: 55,348,736 || all params: 8,458,116,848 || trainable%: 0.6543860411799315

它帮我们把要训练的参数从八十亿降到了五千五百万!差距真大!这将显著减少显存需求。

使用 bfloat16 和 LoRA 后的显存需求

现在我们来算算新的显存需求:

参数来源计算公式显存需求
要训练的模型8G×28 \mathrm{G} \times 28G×216 GB
参考模型8G×28 \mathrm{G} \times 28G×216 GB
梯度55M×255 \mathrm{M} \times 255M×20.1 GB
优化器状态量2×55M×22 \times 55 \mathrm{M} \times 22×55M×20.2 GB
合计32.3 GB

现在我们仅需 32GB 的显存就可以训练我们的 Idefics2-8b 模型了。这合理多了,用 80GB 显存的 GPU 就可以训练了。

训练时 batch size 怎么设定?

上述关于显存占用的计算还不算准确,因为实际训练时,激活值也需要占用显存。激活值是神经网络各层的输出。作为中间产物,它们的显存占用量取决于模型结构和训练时的 batch size。准确计算这些显存需求还是很困难的,我们一般依赖实验观察。

若想找到一个合适的 batch size ( per_device_train_batch_size ),你可以先随便选取一个你认为合适的数值 (比如 64) 然后试着开始训练。当然这大多数情况下会爆显存 (OOM)。如果这样,你可以减半 batch size,同时将 gradient_accumulation_steps 翻倍,以获得和原先 batch size 设定相同的效果。反复重复这一过程,最终当 OOM 不再出现时,你就可以训练了。我们的实验参数是: per_device_train_batch_size 设为 2, gradient_accumulation_steps 设为 32。

你还可以使用 gradient_checkpointing 来减少激活值所需的内存。这一技术在计算梯度时,会重新计算一遍前向过程,而不是在前向过程中保存用于计算梯度的中间结果。需要使用时,设置 gradient_checkpointing=True 即可。

完整训练代码

一切就绪,我们可以开始训练了。下面是我们的完整训练代码。除了上面提到的部分外,我们还设置了 dataset_num_procdataloader_num_workers 等参数,用于加速数据预处理。

# dpo_idefics2-8b.py
from datasets import features, load_dataset
from transformers import AutoModelForVision2Seq, AutoProcessor
import torch
from trl import DPOConfig, DPOTrainer
from peft import LoraConfig

def main():
    # Load the model and processor
    model = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b", torch_dtype=torch.bfloat16)
    processor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)

    # Load the dataset
    dataset = load_dataset("openbmb/RLAIF-V-Dataset", split="train")

    def format(example):
        # Prepare the input for the chat template
        prompt = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": example["question"]}]}]
        chosen = [{"role": "assistant", "content": [{"type": "text", "text": example["chosen"]}]}]
        rejected = [{"role": "assistant", "content": [{"type": "text", "text": example["rejected"]}]}]
        # Apply the chat template
        prompt = processor.apply_chat_template(prompt, tokenize=False)
        chosen = processor.apply_chat_template(chosen, tokenize=False)
        rejected = processor.apply_chat_template(rejected, tokenize=False)
        # Resize the image to ensure it fits within the maximum allowable
        # size of the processor to prevent OOM errors.
        max_size = processor.image_processor.size["longest_edge"]// 2
        example["image"].thumbnail((max_size, max_size))
        return {"images": [example["image"]], "prompt": prompt, "chosen": chosen, "rejected": rejected}

    # Apply the formatting function to the dataset
    dataset = dataset.map(format, remove_columns=dataset.column_names, num_proc=32)

    # Make sure that the images are decoded, it prevents from storing bytes.
    # More info here https://github.com/huggingface/blog/pull/2148#discussion_r1667400478
    f = dataset.features
    f["images"] = features.Sequence(features.Image(decode=True))
    dataset = dataset.cast(f)

    # Train the model
    training_args = DPOConfig(
        output_dir="idefics2-8b-dpo",
        bf16=True,
        gradient_checkpointing=True,
        per_device_train_batch_size=2,
        gradient_accumulation_steps=32,
        num_train_epochs=1,
        dataset_num_proc=32, # tokenization will use 32 processes
        dataloader_num_workers=32, # data loading will use 32 workers
        logging_steps=10,
    )
    trainer = DPOTrainer(
        model,
        ref_model=None, # not needed when using peft
        args=training_args,
        train_dataset=dataset,
        tokenizer=processor,
        peft_config=LoraConfig(target_modules="all-linear"),
    )

    trainer.train()

if __name__ == "__main__":
    main()

启动脚本开始训练,接下来就等待结果吧 🚀

accelerate launch dpo_idefics2-8b.py

结果

训练需要几小时的时间。当训练完成后,我们可以看看训练相关指标的变化曲线:

在这里插入图片描述

In DPO, we focus on several metrics to assess the quality of the training:

在 DPO 中,为了评估训练,我们关注这几个指标:

  • 精度 (Accuracy): 在训练样本中,模型更愿意输出被选中的回答而不是被淘汰的回答,这个比率有多少。我们可以看到随着训练,精度在提升,这是个好的信号。
  • 奖励 (Rewards): 这一指标与一个回答 (选中或淘汰) 被选中的概率呈正相关,读者可以参考 DPO 论文 , 第 5 部分。我们希望被选中的回答对应的奖励高于被淘汰的回答。我们可以通过两者奖励的差值 ( reward margin ) 来看: 图中这一差值逐渐变大, 这也是个好的信号。

评测

推理代码

训练完成后,我们接下来就要在一些样本上评测一下了。这会让我们了解模型学习得怎么样、预测有效性如何。下面的代码可以用来在测试样本上进行评测:

from transformers import AutoModelForVision2Seq, AutoProcessor
from PIL import Image

model = AutoModelForVision2Seq.from_pretrained("HuggingFaceM4/idefics2-8b").to("cuda")
processor = AutoProcessor.from_pretrained("HuggingFaceM4/idefics2-8b", do_image_splitting=False)
model.load_adapter("HuggingFaceH4/idefics2-8b-dpo-rlaif-v-v0.3") # <-- Load the adapter we've just trained

# Process
user_message = ...
image_path = ...
data = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": user_message}]}]
prompts = processor.apply_chat_template(data, add_generation_prompt=True) # add_generation_prompt=True to end the prompt with "ASSISTANT:"
images = [Image.open(image_path)]
inputs = processor(prompts, images, return_tensors="pt")
inputs = {k: v.to("cuda") for k, v in inputs.items()}

# Generate
generated_ids = model.generate(**inputs, max_new_tokens=500)
response_text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
print(response_text)

前面提到的 openbmb/RLAIF-V-Dataset 这个数据集是用来减少大模型幻觉的。但真实训练效果如何呢?我们可以使用 AMBER benchmark 这个评测基准,该数据集专门被用来评估 VLM 的幻觉情况。我们列出 Idefics2 和 Idefics2+DPO 的结果,并和其它模型对比。

AccuracyF1
GPT-4o88.891.6
Idefics2+DPO85.989.4
Idefics285.889.1
GPT-4v83.487.4
MiniGemini82.687.6
LLaVA-NeXT81.485.4
QWEN-VL81.986.4
LURE73.577.7
OPERA75.278.3
Less-is-more72.475.8
VCD71.874.9

总的来看,有点作用!幻觉似乎减少了点。训练看来还是成功的。

下面我们也列出一些可视化结果出来:

ImageQuestionIdefics2Idefics2+DPO
Are there two ships in this image?YesNo
在这里插入图片描述
Is the ground uneven in this image?NoYes
在这里插入图片描述
Is there one shovel in this image?YesNo
在这里插入图片描述

你也可以自己找些例子来测试一下这个模型!

在这里插入图片描述

微调 Llava 1.5 和 PaliGemma 等模型

截至本文完稿时,TRL 的 DPO 实现已支持 Idefics2、Llava 1.5 和 PaliGemma,同时 TRL 也在努力支持更多的模型。最简单的调用方法还是使用 TRL 提供的 示例脚本。例如,如果你想微调 PaliGemma,你可以这样:

accelerate launch examples/scripts/dpo_visual.py \
    --dataset_name HuggingFaceH4/rlaif-v_formatted \
    --model_name_or_path google/paligemma-3b-pt-224 \
    --per_device_train_batch_size 2 \
    --gradient_accumulation_steps 32 \
    --dataset_num_proc 32 \
    --output_dir dpo_paligemma_rlaif-v \
    --bf16 \
    --torch_dtype bfloat16 \
    --gradient_checkpointing \
    --use_peft \
    --lora_target_modules=all-linear

更多关于 PaliGemma 微调的信息可以在 smol-vision 这个项目里看到。

🚀🚀 好了!你现在已经会使用 DPO 微调 VLM 模型了!

如何学习AI大模型?

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

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

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

相关文章

【10-21】设计原则

目录 一.开闭原则 二.里氏代换原则 三.依赖倒转原则 四.接口隔离原则 五.迪米特法则 六.合成复用原则 前言&#xff1a; 在软件开发中&#xff0c;为了提高软件系统的可维护性和可复用性&#xff0c;增加软件的可拓展性和灵活性&#xff0c;程序员要尽量根据6条原则来开发…

关于WebSocket必知必会的知识点

什么是WebSocket WebSocket是一种网络传输协议&#xff0c;可以在单个TCP连接上进行全双工通信&#xff0c;位于OSI模型的应用层。 WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;服务器可以主动向客户端发送消息。在WebSocket API中&#xff0c;浏览器和…

20240820让飞凌的OK3588-C的核心板在Linux R4下挂载1TB的NTFS格式的TF卡

fdisk -l df -h df -t df -T mount 20240820让飞凌的OK3588-C的核心板在Linux R4下挂载1TB的NTFS格式的TF卡 2024/8/20 18:03 【默认挂载到/run/media/mmcblk1&#xff0c;不用任何操作&#xff01;】 插入1TB的TF卡之后的dmesg信息&#xff1a; [ 876.551728] fdee0000.hdmi…

pandans读写分析csv文件

1.什么是pandans Pandas 是一个强大的 Python 库&#xff0c;主要用于数据处理和数据分析。它为 Python 提供了高效的数据结构和数据分析工具&#xff0c;使得数据处理变得更加简单和直观。 2.什么事csv文件 CSV 文件&#xff08;Comma-Separated Values&#xff0c;逗号分隔值…

点亮宇宙的温暖之光 —— COSMOSPANDA星际熊猫“寞”降临!

在这个科技日新月异的时代&#xff0c;我们往往在追求进步的同时&#xff0c;忽略了心中那份最真挚的情感与温暖。但今天&#xff0c;让我们共同迎接一位来自宇宙深处的温暖使者——COSMOSPANDA星际熊猫“寞”&#xff0c;它将用独特的方式&#xff0c;连接过去与未来&#xff…

3:2比例的程序员专业显示器,效率提升显著,摸鱼时间又多了

对于我们程序员来说&#xff0c;显示器的重要性不言而喻&#xff0c;作为我们与代码交流的直接工具&#xff0c;他影响着我们的工作效率、舒适度和整体编程体验。我在家用的是自己笔记本的屏幕&#xff0c;简单写写代码还行&#xff0c;涉及到多任务协同或者大代码量开发就有点…

Linux 下安装miniconda(少走弯路)

Miniconda 和 Conda 都是用于管理 Python&#xff08;及其他语言&#xff09;环境和包的工具。 conda对于我来说是太臃肿了&#xff0c;很多的包我不会使用&#xff0c;所以选择安装miniconda是一个较好的选择。 下面是linux安装miniconda的实际操作。 在以下的网站&#xf…

复现 LET-NET

摘要 稀疏光流法是计算机视觉中的一项基本任务。然而&#xff0c;它依赖于恒定的假设限制了其在高动态范围&#xff08;HDR&#xff09;场景中的适用性。在本研究中&#xff0c;我们提出了一种新的方法&#xff0c;旨在通过学习一个对光照变化具有鲁棒性的特征映射来超越图像的…

LabVIEW轨距实时动态检测系统

轨距实时动态检测系统解决铁路轨距不平顺现象&#xff0c;提高铁路运行安全性。系统利用高精度的激光位移传感器与数据同步采集技术&#xff0c;结合LabVIEW软件进行数据处理&#xff0c;有效提高了轨距检测的准确性与效率。 项目背景 随着铁路运输业的快速发展&#xff0c;轨…

速度+耐力,希迪智驾引领中国商用车自动驾驶发展

作者 | 魏启扬 来源 | 洞见新研社 资本市场对自动驾驶的投资热情依然不减。 不久前&#xff0c;科技巨头Alphabet重申了对自动驾驶未来的承诺&#xff0c;并表示将向其自动驾驶子公司Waymo投资50亿美元&#xff0c;分多年进行。 更早一点的5月份&#xff0c;孙正义旗下的…

如何使用 JavaScript 动态创建下拉框?

在现代 Web 开发中&#xff0c;动态生成页面元素是一个常见的需求&#xff0c;比如在用户选择某个选项后&#xff0c;根据选择的内容动态生成新的下拉框。今天&#xff0c;我们来聊一聊如何使用 JavaScript 动态创建一个带选项的下拉框&#xff0c;并用一个具体的场景带大家进入…

第十九讲 python中的异常本质-异常定位-异常解决思路

目录 1.异常是什么&#xff1f; 2.异常机制的本质 2.1 本质 2.2 异常对象 2.3 抛出异常 2.4 捕捉异常 2.5 最终处理 3.异常的解决思路 3.1 异常定位 3.2 异常解决 1.异常是什么&#xff1f; 异常是指程序在运行时发生的错误或不正常情况。 工作中&#xff0c;程序遇到的情况不…

数据结构中的双向链表

1.链表的分类 链表的结构非常多样&#xff0c;以下情况组合起来就是8种&#xff08;2x2x2&#xff09;链表结构&#xff1a; 在带头链表中&#xff0c;除了头结点&#xff0c;其他结点均存储有效的数据。 头结点是占位子的&#xff0c;也叫做“哨兵位”。head结点就是头结点。…

【实战场景】如何优雅实现分页

【实战场景】如何优雅实现分页 开篇词&#xff1a;干货篇&#xff1a;1.添加PageHelper依赖2.添加PageHelper配置3.使用 PageHelper4.自定义Pageable注解 总结&#xff1a;1.执行查询2.处理分页结果3.注意事项 我是杰叔叔&#xff0c;一名沪漂的码农&#xff0c;下期再会&#…

代码随想录算法训练营day49 | 42. 接雨水、84.柱状图中最大的矩形

碎碎念&#xff1a; 参考&#xff1a;代码随想录 42. 接雨水 题目链接 42. 接雨水 思想 如图可以按照列来计算&#xff0c;这样宽度一定是1&#xff0c;只需要计算每一列的雨水的高度接口。而每一列的雨水高度取决于该列左侧最高的主子和右侧最高柱子中最矮的那个柱子的高度…

如何使用Java SpringBoot+Vue搭建半成品配菜平台,实现家庭烹饪新体验

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

用Python编写一个超级玛丽游戏|附源码

​ 编写一个超级玛丽游戏是一个复杂的任务&#xff0c;涉及到多个方面的编程知识和技巧。下面&#xff0c;我将详细讲解如何用Python编写一个简化版的超级玛丽游戏&#xff0c;包括所需的库、游戏逻辑、角色控制、关卡设计、碰撞检测等方面。 所需库 为了编写这个游戏&#…

猫咪去浮毛能一劳永逸吗?手动不行宠物空气净化器是真能做到

现在啊&#xff0c;越来越多的家庭选择养宠物来增添生活乐趣。但宠物带来的快乐背后&#xff0c;也有那么点“小困扰”&#xff1a;下班回家&#xff0c;迎接你的可能是满屋子的“特殊香味”和无处不在的毛发。这样的环境&#xff0c;真的不会对我们的健康产生不良影响吗&#…

照片整理专家,照片整理大师,照片图库整理,智能图片整理软件

前言 业务痛点&#xff1a; 就是我从2015年拥有自己的智能手机之后&#xff0c;就会刻意的对自己拍过的照片、视频&#xff0c;收藏的视频等&#xff0c;媒体元素&#xff0c;进行收集归纳&#xff0c;尝试过很多的存储方案&#xff0c;归纳整理方案 2015年 百度网盘 2016年 时…

电子厂车间的客流统计需要集成哪些硬件设备

在电子厂车间中&#xff0c;准确的客流统计对于生产管理和安全保障至关重要。要实现有效的客流统计&#xff0c;需要集成一系列硬件设备。 首先&#xff0c;客流统计系统主要由以下硬件组成。一是人数采集设备&#xff0c;通常采用红外传感器、双目摄像头等&#xff0c;安装在车…