hugging face 使用教程———快速入门

news2024/9/20 10:28:16

概述

    本篇存在的意义是快速介绍hugging face使用,梳理主要部件,梳理易混淆概念。原因是:目前hugging face的使用,官方放在了3个地方(参考链接部分):使用文档、NLP教程、Transformers git的readme 文件,很多重叠内容比较浪费时间,很容易看懵。等大家有了主要概念再去看需要具体看某个函数或功能。

    本篇主要从以下维度来快速介绍hugging face使用:

    1、是什么:我觉得这么讲更适合当前(20240603)初学者,hugging face本身集成了常用的三大功能:model hub、data hub、space,和一个著名源代码库Transformers。

    2、怎么用:这里除了上面的几大部件,最需要知道的几个易混淆名词:pipeline、AutoTokenizer、AutoConfig、AutoModel、AutoModelForXXX、accelerate、Trainer、peft、Agents、optimum。

一、是什么

1、model hub

    登录官方网站(https://huggingface.co/models)可以看到如下的页面(我们选择models 选项卡)。

    其中蓝色和绿色,就是后面怎么用的时候代码里面对应的参数名,分别代表任务类型和模型名字,其中任务类型在代码中全部换为小写。

    模型内部如下,这个是一个比较复杂的代码,并且当前没有被hugging face 的Transformers git代码集成,所以有自定义的一些源码,就需要也上传到对应的hub上,调用逻辑,主要是通过config json 里面的map 字段。

2、databub

    整体和model hub类似, 登录官方网站(https://huggingface.co/datasets)

3、space

    其实就是对应的服务的一个demo地址,可以先简单试一下模型效果。登录官方网站(https://huggingface.co/spaces)

4、docs

    就是对应的一些文档,这部分和源码里面的readme 有重叠。官方网址(https://huggingface.co/docs)

5、Transformers

    这是整个工程的开源代码库,这个要区别Transform(这是一个架构名称,区别与CNN),这个git里面集成了一些知名的结构的源代码,比如llama,并且没有做过多的抽象,可以方便快速查看源代码

    比如:https://github.com/huggingface/transformers/tree/main/src/transformers/models

    这里就要区别使用model bub,这是两种不同的使用方式,一般而言 model bub会更新和更全。官方也有文档写怎么提交这两种方式,参见这里https://github.com/huggingface/transformers/blob/main/docs/source/zh/custom_models.md

二、怎么用

    这里以pytorch和LLM类为例,图像和声音的可以参考官方对应文档,整体流程差不多。涉及的python安装包(建议使用Python虚拟环境):

    pip install transformers datasets evaluate accelerate torch

1、AutoTokenizer、AutoModel、AutoModelForXXX、AutoConfig

首先这里还是建议使用Auto类别,AutoModel、AutoModelForXXX、特定类区别:

AutoModel:加载基础的预训练模型,不带特定任务头部。

AutoModelFor 系列:为特定任务(如分类、标注、问答等)加载预训练模型,并添加适当的任务头部。

直接使用特定模型类(如 BertModel.from_pretrained):提供明确性和特定模型功能,适合需要特定模型特性的场景。

PS:

*这里AutoTokenizer需要和model绑定保持一致,因为不同的模型使用的Tokenizer可能不一样。需要基本了解BPE算法,还有就是hugging face tokenizer默认为快速版本使用rust编写,也可以选择满速版本使用Python原生实现,只需要在参数中将 use_fast=False 即可。

*这里AutoConfig需要和model等绑定保持一致,并且AutoConfig并不能加载权重。

使用范例:

from transformers import AutoModel, AutoModelForSequenceClassification, AutoTokenizer, AutoConfig, BertModel, BertTokenizer


model_name = "bert-base-uncased"

# 使用AutoModel加载基础模型
model = AutoModel.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 使用AutoModelFor加载适用于特定任务的模型
model_for_classification = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

# 直接使用BertModel加载模型,等价使用AutoModel加载基础模型
model = BertModel.from_pretrained(model_name)
tokenizer = BertTokenizer.from_pretrained(model_name)

# 从预训练模型名称加载配置
config = AutoConfig.from_pretrained(model_name)
model = AutoModel.from_config(config)  # 使用配置初始化模型(仅创建模型,不加载权重)
model = AutoModel.from_pretrained(model_name)  # 从预训练模型名称加载模型(包括配置和权重)
tokenizer = AutoTokenizer.from_pretrained(model_name)

2、pipeline

上面代码其实已经很简洁,但使用pipeline后可以仅需要一行代码。

其中pipeline支持的任务:https://huggingface.co/docs/transformers/main/en/main_classes/pipelines#transformers.pipeline.task

from transformers import pipeline


# 加载文本分类pipeline
classifier = pipeline("sentiment-analysis", model="bert-base-uncased")

# 示例文本
text = "I love using transformers library!"

# 进行情感分析
result = classifier(text)
print(result)


#其中pipeline参数可以为多种

# 使用默认
classifier = pipeline("sentiment-analysis")

# 指定model
classifier = pipeline("sentiment-analysis", model="bert-base-uncased")
model_name = "bert-base-uncased"
model = AutoModelForSequenceClassification.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
classifier = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)

自定义pipeline

官方:https://github.com/huggingface/transformers/blob/main/docs/source/zh/add_new_pipeline.md

简单的修改后处理的代码如下:

class MyPipeline(TextClassificationPipeline):
    def postprocess():
        # Your code goes here
        scores = scores * 100
        # And here

my_pipeline = MyPipeline(model=model, tokenizer=tokenizer, ...)
# or if you use *pipeline* function, then:
my_pipeline = pipeline(model="xxxx", pipeline_class=MyPipeline)

完整的一个示例如下:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import Pipeline
import torch


class CustomTextClassificationPipeline(Pipeline):
    def __init__(self, model, tokenizer, **kwargs):
        super().__init__(model=model, tokenizer=tokenizer, **kwargs)

    def _sanitize_parameters(self, **kwargs):
        preprocess_kwargs = {}
        forward_kwargs = {}
        postprocess_kwargs = {}
        return preprocess_kwargs, forward_kwargs, postprocess_kwargs

    def preprocess(self, inputs):
        # 自定义预处理步骤
        return self.tokenizer(inputs, padding=True, truncation=True, return_tensors="pt")

    def _forward(self, model_inputs):
        # 自定义前向传播步骤
        with torch.no_grad():
            outputs = self.model(**model_inputs)
        return outputs

    def postprocess(self, model_outputs):
        # 自定义后处理步骤
        logits = model_outputs.logits
        predictions = torch.nn.functional.softmax(logits, dim=-1)
        return predictions

# 加载模型和分词器
model_name_or_path = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)
model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path)

# 创建自定义 pipeline
custom_pipeline = CustomTextClassificationPipeline(model=model, tokenizer=tokenizer)

# 示例文本
texts = [
    "I love using the transformers library!",
    "I had a terrible experience with this product."
]

# 进行分类
results = custom_pipeline(texts)

# 输出结果
for text, result in zip(texts, results):
    print(f"Text: {text}\nClassification: {result}\n")

3、accelerate

    这里对等的相关概念还有:megtron、PyTorch FSDP、deepspeed。这里就不对比详细区别,因为整体的技术都是zero系列的思路,只不过不同的时刻支持的方案有区别,当你用的时候可以查看所使用版本支持哪些并行方案。整体适合生态而言:

*Accelerate:适合希望快速实现分布式训练的用户,尤其是使用 Hugging Face 生态系统的用户。

*DeepSpeed:适合需要训练非常大规模模型的用户,对性能和资源利用率有高要求的场景。

*PyTorch FSDP:适合需要在多 GPU 上进行大规模模型训练的用户,尤其是已经在使用 PyTorch 的用户。

    使用方式也相对简单,安装accelerate、简单设置、简单修改训练源码、启动训练,参照:https://github.com/huggingface/transformers/blob/main/docs/source/zh/accelerate.md

    # 安装库

pip install accelerate

    # 简单配置

accelerate config

    修改训练源码(查看+、-部分)  

 + from accelerate import Accelerator
  from transformers import AdamW, AutoModelForSequenceClassification, get_scheduler
+ accelerator = Accelerator()
  model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
  optimizer = AdamW(model.parameters(), lr=3e-5)
- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
- model.to(device)
+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
+     train_dataloader, eval_dataloader, model, optimizer

+ )
  num_epochs = 3
  num_training_steps = num_epochs * len(train_dataloader)
  lr_scheduler = get_scheduler(
      "linear",
      optimizer=optimizer,
      num_warmup_steps=0,
      num_training_steps=num_training_steps
  )
  progress_bar = tqdm(range(num_training_steps))
  model.train()
  for epoch in range(num_epochs):
      for batch in train_dataloader:
-         batch = {k: v.to(device) for k, v in batch.items()}
          outputs = model(**batch)
          loss = outputs.loss
-         loss.backward()
+         accelerator.backward(loss)
          optimizer.step()
          lr_scheduler.step()
          optimizer.zero_grad()
          progress_bar.update(1)

    启动训练

accelerate launch train.py

4、peft

    Transformers原生支持一些PEFT方法,这意味着你可以加载本地存储或在Hub上的adapter权重,并使用几行代码轻松运行或训练它们。以下是受支持的方法:Low Rank Adapters、IA3、AdaLoRA。参考:https://github.com/huggingface/transformers/blob/main/docs/source/zh/peft.md

    要从huggingface的Transformers库中加载并使用PEFTadapter模型,请确保Hub仓库或本地目录包含一个adapter_config.json文件和adapter权重,如上例所示。然后,您可以使用AutoModelFor类加载PEFT adapter模型。

    1)要为因果语言建模加载一个PEFT adapter模型:可以使用AutoModelFor类或基础模型类(如OPTForCausalLM或LlamaForCausalLM)来加载一个PEFT adapter;也可以通过load_adapter方法来加载 PEFT adapter。 

    2)添加新的adapter,切换adapter,启用禁用adapter。这里需要区别:

    *enable_adapters 影响的是整个模型的适配器功能开关。

    *set_active_adapters 影响的是具体的适配器激活状态。默认使用最后一次调用该函数的适配器,所以,当需要同时使用多个适配器的时候,同时传给该函数,切不要分次传入(只有最后一个生效)。

    *load_adapter:从外部资源加载预训练的适配器,适用于已经有预训练适配器的情况。

    *add_adapter:在模型中添加一个新的适配器,适用于需要在新任务上训练适配器的情况。

    * 调用关系链:enable_adapters:如果你希望在推理或训练过程中使用适配器,那么你需要调用 enable_adapters 方法。如果你不调用这个方法,模型将不使用适配器。set_active_adapters:如果你只有一个适配器,并且不需要切换适配器,那么你不需要调用 set_active_adapters 方法。如果你有多个适配器并需要在它们之间切换,那么你需要调用这个方法。

    *get_peft_model & add_adapter: get_peft_model是 PEFT 库的一部分,专注于参数高效微调技术。add_adapter 是 AdapterHub 或 Transformers 库的一部分。

from transformers import AutoModelForCausalLM, AutoTokenizer


# 定义模型和适配器的ID
model_id = "facebook/opt-350m"
peft_model_id = "ybelkada/opt-350m-lora"

# 加载基础模型
model = AutoModelForCausalLM.from_pretrained(model_id)

# 加载PEFT适配器
model.load_adapter(peft_model_id, adapter_name="adapter_1")

# 再次加载相同的PEFT适配器,作为不同的适配器名称
model.load_adapter(peft_model_id, adapter_name="adapter_2")

# 启用适配器
model.enable_adapters()  # 禁用: model.disable_adapters()

# 设置使用的适配器,展示切换
model.set_active_adapters("adapter_1")  # 使用 adapter_1
print("Using adapter_1:")
inputs = tokenizer("Hello, how are you?", return_tensors="pt")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

model.set_active_adapters("adapter_2")  # 使用 adapter_2
print("Using adapter_2:")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

# 同时使用多个适配器(如果支持)
model.set_active_adapters(["adapter_1", "adapter_2"])  # 同时使用 adapter_1 和 adapter_2
print("Using adapter_1 and adapter_2:")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

# 只使用 adapter_1
model.set_active_adapters("adapter_1")
print("Using adapter_1 again:")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

# 只使用 adapter_2
model.set_active_adapters("adapter_2")
print("Using adapter_2 again:")
outputs = model.generate(**inputs)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

5、Trainer

    对于pytorch,当你的模型是模型都是标准的 torch.nn.Module,然后可以使用trainer来替代自己手写训练循环,初次之外trainer:包含了基础的训练循环并且为诸如分布式训练(也就是可以理解默认使用了acclerate),混合精度等特性增加了额外的功能。需要注意你可以使用回调来将trainer与其他库集成,查看训练循环来报告进度或提前结束训练。回调不会修改训练循环。如果想自定义损失函数等,就需要子类化 Trainer。 这里补充一点:Trainer 类支持的损失函数主要取决于所使用的模型。大多数预训练模型在其 forward 方法中已经定义了适合其任务的损失函数。如果需要自定义损失函数,可以通过继承 Trainer 类并重写 compute_loss 方法来实现。

from transformers import Trainer, TrainingArguments, AutoModelForSequenceClassification, AutoTokenizer,  PreTrainedModel, PretrainedConfig
from datasets import load_dataset
import numpy as np
from sklearn.metrics import accuracy_score, precision_recall_fscore_support
from peft import LoraConfig


# 加载数据集
dataset = load_dataset("glue", "mrpc")

# 加载预训练模型和分词器
model_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=2)

#  ———————————————————————————————————— 如下是自定义模型——————————————————————————————————————————————————  #

"""
class CustomBertModel(PreTrainedModel):
    def __init__(self, config):
        super().__init__(config)
        self.bert = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", config=config)
        self.custom_layer = nn.Linear(config.hidden_size, config.num_labels)

    def forward(self, input_ids=None, attention_mask=None, token_type_ids=None, labels=None):
        outputs = self.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)
        logits = self.custom_layer(outputs[1])  # 使用 BERT 的池化输出
        loss = None
        if labels is not None:
            loss_fct = nn.CrossEntropyLoss()
            loss = loss_fct(logits.view(-1, self.config.num_labels), labels.view(-1))
        return (loss, logits) if loss is not None else logits

# 加载配置和模型
config = PretrainedConfig.from_pretrained("bert-base-uncased", num_labels=2)
model = CustomBertModel(config)
"""

# ———————————————————————————————————— 如下是使用LoRa —————————————————————————————————————————————————— #
"""
peft_config = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    task_type="SEQ_CLS",
)
model.add_adapter(peft_config)
model.enable_adapters()  # 启用适配器,禁用: model.disable_adapters()

# peft_model = get_peft_model(model, peft_config) # 上面两行代码也可以使用这行代码替换
"""

# 预处理数据
def preprocess_function(examples):
    return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True)
encoded_dataset = dataset.map(preprocess_function, batched=True)

# 定义计算指标
def compute_metrics(pred):
    labels = pred.label_ids
    preds = np.argmax(pred.predictions, axis=1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='binary')
    acc = accuracy_score(labels, preds)
    return {
        'accuracy': acc,
        'f1': f1,
        'precision': precision,
        'recall': recall
    }

# 设置训练参数
training_args = TrainingArguments(
    output_dir='./results',
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    num_train_epochs=3,
    weight_decay=0.01,
)

# 初始化 Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=encoded_dataset['train'],
    eval_dataset=encoded_dataset['validation'],
    compute_metrics=compute_metrics
)

# 开始训练
trainer.train()

# 评估模型
trainer.evaluate()

# 保存模型
save_dir = ''
model.save_pretrained(save_dir)
model = AutoModelForSequenceClassification.from_pretrained(save_dir)

6、optimum

支持的平台比较多(Intel、amd、NVIDIA等):https://huggingface.co/docs/optimum/index

鉴于目前还是都使用NVIDIA,所以重点看这里:https://github.com/huggingface/optimum-nvidia

然后发现底层其实还是TensorRT-LLM: https://github.com/NVIDIA/TensorRT-LLM,支持模型列表:Support Matrix — tensorrt_llm documentation

7、agent

目前(20240604)hugging face还在开发中,后续持续跟进,建议目前还是使用longchain 框架。

参考链接

https://huggingface.co/docs/transformers/v4.41.3/zh/index

https://huggingface.co/learn/nlp-course/zh-CN/chapter1/1

https://github.com/huggingface/transformers/tree/main/docs/source/zh

https://github.com/huggingface/transformers/tree/main/docs/source/zh/main_classes

swiGLU: Index - 笔记 

rope:一文通透位置编码:从标准位置编码、旋转位置编码RoPE到ALiBi、LLaMA 2 Long-CSDN博客

Yarn:大模型长度扩展综述:从直接外推ALiBi、插值PI、NTK-aware插值、YaRN到S2-Attention-CSDN博客

长文本:https://zhuanlan.zhihu.com/p/640641794 

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

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

相关文章

洛谷P1910题解

思路 一个变型的DP 这道题增加了一个维度&#xff0c;其他地方和其他的01背包没有任何区别 只是状态转移方程由变成了 dp[j][k]max(dp[j][k],dp[j-c[i]][k-b[i]]a[i]); 还要注意一点&#xff0c;不能开三维的数组&#xff0c;会MLE。 代码 #include<bits/stdc.h>…

【llama3.1】ollama的使用--本地部署使用llama3.1模型

快速入门 安装完成ollama后,在命令行窗口输入 ollama run llama3 上图表示 Ollama 正在下载 llama3 任务所需的资源文件,并显示了当前的下载进度、速度和预计剩余时间。这是 Ollama 在准备运行 llama3 任务之前所需的步骤。 上面的步骤完成后,就可以在本地进行聊天了,…

USB枚举过程记录和个人认识以及设备程序的框架简述

主机和从机认识 参考 主机 从机 从usb设备连接usb口整个过程概述 参考&#xff1b;参考1 枚举过程简单说就是usb设备插入电脑接口后建立最初的识别设备通信的过程 参考 参考下面的分析是从开始对默认地址第一次发送Get_Descriptors开始的 主机和设备断开或连接以及高速…

Arduino学习笔记1——IDE安装与起步

一、IDE安装 去浏览器直接搜索Arduino官网&#xff0c;点击Software栏进入下载界面&#xff0c;选择Windows操作系统&#xff1a; 新版IDE下载不需要提前勾选所下载的拓展包&#xff0c;下载好后直接点击安装即可。 安装好后打开Arduino IDE&#xff0c;会自动开始下载所需的…

【精通Redis】Redis入门介绍

引言 本文作为笔者研究学习Redis的开篇之作&#xff0c;主要是对redis做一个简单系统的介绍&#xff0c;日常开发中都只是集成使用其缓存的功能&#xff0c;没有更深入的学习了解它的特性。笔者作为一个有五年Java开发经验的程序员&#xff0c;把大量时间都花在了编码上&#…

从零入手人工智能(6)—— 聚类

1.小故事 有一家零售连锁店&#xff0c;他们以其精准的市场定位和个性化的顾客服务而闻名&#xff0c;随着市场竞争的加剧和顾客需求的多样化&#xff0c;他们的管理层开始意识到&#xff0c;只有更加深入地了解他们的顾客群体&#xff0c;以便更好地满足他们的需求。 他们定…

渗透测试——利用公网反弹shell到本地的两种方式,vmware虚拟机与主机的端口转发,本地ssh无法上线的问题解决

解决问题&#xff1a; 因长期使用本地模拟靶场&#xff0c;实战护网时并非模拟靶场&#xff0c;shell反弹需要利用公网测试。解决目标站无法反弹到本地的情况。解决本地是windows&#xff0c;虚拟机是kail、linux&#xff0c;无法相互转换流量的情况。 环境搭建 靶机 centOS7 …

HarmonyOS(45) 控件拖动或者拖拽PanGesture

PanGesture实现控件拖动的效果&#xff0c;通过拖动的坐标位置调用position或者translate方法来更新UI的位置。效果见下图&#xff1a; 具体代码如下&#xff1a; // xxx.ets Entry Component struct PanGestureExample {State offsetX: number 0State offsetY: number 0pos…

做视频混剪都是去哪里找高清素材的?分享10个高清视频素材库

提升视频混剪质感的10个高清素材库推荐 在这个视觉体验至上的时代&#xff0c;视频的视觉质量对吸引观众至关重要。如果你正在寻找高清素材以提升视频混剪作品的层次&#xff0c;那么你来对地方了。今天&#xff0c;我将为你揭秘10个视频混剪达人常用的高清素材库&#xff0c;…

html+css+js前端作业 王者荣耀官网5个页面带js

htmlcssjs前端作业 王者荣耀官网5个页面带js 下载地址 https://download.csdn.net/download/qq_42431718/89574989 目录1 目录2 目录3 项目视频 王者荣耀5个页面&#xff08;带js&#xff09; 页面1 页面2 页面3 页面4 页面5

大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

前端文件下载word乱码问题

记录一次word下载乱码问题&#xff1a; 用的请求是axios库&#xff0c;然后用Blob去接收二进制文件 思路&#xff1a;现在的解决办法有以下几种&#xff0c;看看是对应哪种&#xff0c;可以尝试解决 1.将响应类型设为blob&#xff0c;这也是最重要的&#xff0c;如果没有解决…

LeetCode 2766题: 重新放置石块(原创)

【题目描述】 给你一个下标从 0 开始的整数数组 nums &#xff0c;表示一些石块的初始位置。再给你两个长度 相等 下标从 0 开始的整数数组 moveFrom 和 moveTo 。 在 moveFrom.length 次操作内&#xff0c;你可以改变石块的位置。在第 i 次操作中&#xff0c;你将位置在 moveF…

C++STL详解(一)——String接口详解(上)!!!

目录 一.string类介绍 二.string类的构造赋值 2.1string类的拷贝和构造函数 2.2深拷贝 三.string类的插入 3.1push_back 3.2append 3.3操作符 3.4insert 四.string的删除 4.1pop_back 4.2erase 五.string的查找 5.1find 5.2rfind 六.string的比较 6.1compare函…

LeetCode 热题 HOT 100 (011/100)【宇宙最简单版】

【图论】No. 0200 岛屿数量 【中等】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#xf…

使用AOP优化Spring Boot Controller参数:自动填充常用字段的技巧

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 使用AOP优化Spring Boot Controller参数&#xff1a;自动填充常用字段的技巧 前言为什么使用AOP为…

web网站组成

web网站由四部分组成&#xff1a;浏览器 前端服务器 后端服务器 数据库服务器 流程&#xff1a; 1.浏览器输入网站后&#xff0c;向前端服务器发送请求&#xff0c;前端服务器响应&#xff0c;静态的数据给浏览器。 2.前端代码中script中有url,这个是向后台发送请求的网…

项目标红,识别不了maven项目,解决办法

首先&#xff0c;检查 preferences 其次&#xff0c;检查IDEA 的 jdk。File-》Project Structure 最后&#xff1a; 1. 2. mvn clean install -Dmaven.test.skiptrue 跳过单元测试 maven跳过单元测试-maven.test.skip和skipTests的区别-CSDN博客

vue3+g2plot实现词云图

词云图 效果预览: 核心代码: import {WordCloud } from @antv/g2plot;fetch(https://gw.alipayobjects.com/os/antfincdn/jPKbal7r9r/mock.json).then((res) => res.json()).then((data) => {const wordCloud = new WordCloud(container, {data,wordField: x,weigh…

细说MCU用DMA实现DAC输出的方法

目录 一、建立新工程 1.项目依赖的硬件 2.配置DAC 3.配置DMA 4.配置TIM3 5.选择时钟源和Debug 6.配置系统时钟 二、代码修改 1. 启动定时器和DMA 2.定义输出波形数据 3.通过MATLAB产生这个波形数据的方法 三、查看结果 用DMA的方式将位于存储器(数组)中的数据传递…