使用bert模型进行命名实体识别任务

news2024/10/10 7:51:17

一、实验内容

         本实验使用预训练的 BERT 模型进行命名实体识别(NER)任务,并且使用 Hugging Face 的 Transformers 库完成模型的训练、验证和测试。最后,使用测试集评估模型性能,计算NER指标。

二、算法介绍 

         Bert是一种由Google于2018年提出的自然语言处理(NLP)模型。它基于Transformer架构。首先简单介绍transformer模型。2017年,Google翻译团队提出的论文《Attention is All You Need》。在论文中,作者提出了在序列转录领域,只依赖注意力结构的简单的网络架构——Transformer。Transformer是一种基于注意力的编码器-解码器模型。它由若干层编码器和解码器组成,每一层都包含了自注意力机制和前馈神经网络。

        在Transformer提出之前,RNN和CNN一直是人们处理序列问题的前沿方法,但是二者都存在局限性,Transformer的优势也正是对二者的补充。Transformer的提出有效解决了传统的模型在处理可变长序列时遇到的问题。其具有以下优点:

1、长程依赖处理

在CNN模型中,随着序列两个位置之间距离的增大,计算它们之间的依赖难度也随之增加。Transformer所引入的自注意力机制则允许其忽略序列距离的影响,使计算更简单。

2、支持并行计算

以往的RNN模型在t时刻的计算依赖于前t-1时刻的输出,无法并行计算。然而,Transformer仅基于自注意力机制,而完全摒弃了RNN,无需遵循该时序结构,令并行计算成为可能

Transformer结构:

54d2729fbc0b4b6b8e1040d3ee996e11.png

        左侧为编码器,右侧为解码器。编码器包含一个 多头注意力Multi-Head Attention机制,解码器是在编码器 基础上增加了一个有Masked的 Multi-Head Attention。Multi-Head Attention 上方还包括一个 Add & Norm 层,Add 表示残差连接用于防止网络退化,Norm表示层归一化处理,用于对每一层的激活值进行归一化。 

        并且Transformer通过引入位置编码来为attention加入时序信息。

3a2751752c094fc0ad6b30420a0a5345.png

        BERT 的主要创新在于通过预训练大规模语言模型来学习上下文相关的词嵌入。主要由三大部分构成:嵌入层、编码层和输出层。编码层是基于transformer编码层。

7822379a358c4b4bb22c0fe449bc5ffb.png

 

1.嵌入层:

输入向量是由三个向量相加得到,分别是标记嵌入、段嵌入和位置嵌入。

Token Embeddings(标记嵌入): 标识每个token的基本语义

Segment Embeddings(段嵌入):区分不同句子/段的嵌入,表明这个词属于哪个句子

Position Embeddings(位置嵌入):标记token在序列中的位置信息

38d98dd1223d4d14ae78059fcf27a20e.png

 

BERT的预训练任务主要由MLM和NSP两部分组成。

2.MLM

        为了同时利用上下文的信息,Bert将输入序列的部分 token 随机遮挡起来,通过预测这些被遮挡起来的 token,得到文本的双向特征。

        类似于完形填空:给定一句话,随机抹去这句话中的一个或几个词,要求根据剩余词汇预测被抹去的几个词分别是什么,若想正确地填上这些空,就一定需要对整个文本的信息进行学习。

        但由于预训练时存在mask操作但在下游微调任务中不会对文本mask,因此bert仅对15%的词进行mask,对于被选中的token,在80%的时间进行mask,10%的时间使用用随机token替代,10%的时间保持token不变,借此减小预训练与微调之间的差距。

dc084c0a637043fb84736d2f2abcefe8.png

 

        另外一个预训练任务NSP是:给定文本中的两句话,判断第二句话在文本中是否紧跟在第一句话之后,在训练时IsNext和NotNext标签各占50% 

905bed2e75b847c9a8ba096d2b84200a.png

 

         这就要求bert能够学习到文本的大意,NSP与 MLM 任务相结合,让模型能够更准确地刻画语句乃至篇章层面的语义信息。

BERT模型的优越性主要体现在三个方面:

1.双向上下文建模

相较于传统的单向模型,Bert考虑到输入序列中所有位置的上下文信息,能够更好地理解句子中的语境和依赖关系。

2.预训练阶段的无监督学习

BERT在大规模无标签的语料库上进行预训练,通过两个预训练任务MLM和NSP对语料库进行学习,使模型能够学习到通用的语言表示。

3.适用于多种下游任务

由于BERT预训练阶段学到的通用表示,只需在预训练模型的基础上微调,就可以适应不同的任务,无需重新训练整个模型。

三、代码解读

1.数据集准备

使用 Hugging Face 的 datasets 从Hub中加载了一个名为 "lcampillos/ctebmsp" 的生物医学实体识别数据集

a27e5a4d52174a349a8531f22ef30c85.png

2.BERT模型配置

使用了 dccuchile/bert-base-spanish-wwm-cased 作为预训练的 BERT 模型。 

5d8a0a6f8b3b4bf5b9edb2467a2fa46f.png

配置AdamW优化器,设置了学习率和权重衰减

f8c2adf7146d487e95be4327100be200.png

配置一个学习率调度程序,在训练开始时有一个热身阶段并且在训练过程中进行线性调度

6eea4524b50f4358bd518a636196a2bd.png

 

3.数据预处理

        对训练集、验证集和测试集进行了数据预处理,包括分词、填充、创建注意力掩码等。

定义标签映射和BERT分词器

b2d97c67620a4180a68d5cf8c0410152.png

        定义处理数据集的函数 process_dataset:对每个句子进行分词、填充和创建注意力掩码,最终转换为PyTorch张量。目的是将原始文本数据转换为模型可以接受的格式 

8e6e8acebe38412fb5b3d2f957f96671.png

使用上述定义的 process_dataset 函数对训练、验证和测试数据集进行处理

a16a1c3a97f044c9b52af6fb1d6f6c2d.png 

 使用PyTorch的DataLoader将处理后的数据集转换为可用于训练的批次数据。

3770ec0a0fb044f6b79cf6d9bd8757b0.png

4.模型训练

在range()函数中进行多个训练轮次

 2570d9de7476412f80a70e08f3b2bdd1.png

将模型设置为训练模式,启用梯度计算和参数更新

cf40e07441654030a397b73247134440.png

循环遍历训练数据集

7c859c09841f4735bac753e3b31c1ec3.png

 将每个批次的输入数据、注意力掩码和标签转移到GPU设备上

0c2c174908ad4baf80cca84a8d5d03ec.png 

执行前向传播,计算损失

执行反向传播,然后使用优化器更新模型参数。

 6f90104a1e084bc9ac3dee436628daca.png

 

5.学习曲线绘制

使用 Matplotlib 和 Seaborn 绘制了模型的学习曲线,包括训练损失和验证损失的变化趋势。

20c1c803b6844cbb8175cc2790d70926.png

6.模型测试

        在测试集上评估训练后的模型性能。计算测试集的NER性能指标,包括准确性和分类报告。

将模型设置为评估模式,禁用梯度计算和参数更新。

9a98034bbb6547c3915fc9bfde217bb1.png

 执行前向传播并获取预测结果

4cdc5445bfaa4310bdff28b2efa59da8.png

计算评估损失,将模型的预测结果和真实标签保存在相应的列表中

 1c7fe757806f45ae86969cb0e4e56401.png

将模型的预测结果和真实标签转换为嵌套列表格式,并使用classification_report函数输出分类报告,包括准确率、召回率、F1分数等性能指标 

255740f7589c477489b96426628281e2.png 

四、运行结果 

实验环境

PyTorch、Hugging Face Transformers库、Hugging Face Datasets库、scikit-learn库、NumPy、Pandas、Matplotlib、Seaborn库

模型配置

预训练的BERT模型:dccuchile/bert-base-spanish-wwm-cased优化器:AdamW

学习率:3e-5  训练轮数:4 批量大小:8

成功下载,导入数据集

dc5918c0c64049f488fae5da933a810e.png

完成数据预处理

d8e92bb6eb64434aa07a84d9fda3abcd.png

输出模型在训练过程中的性能指标以及在开发集(validation set)上的分类报告

第一轮:

平均训练损失: 0.0306

开发集损失: 0.0155

总体性能:F1-score 为 0.6279

各类别性能差异明显,ANAT 类别的 Precision 和 Recall 较低。

第二轮:

平均训练损失: 0.0127

开发集损失: 0.0143

总体性能提升,F1-score 为 0.6648

ANAT 类别的性能有所改善,但仍然较低。

第三轮:

平均训练损失: 0.0083

开发集损失: 0.0152

总体性能进一步提升,F1-score 为 0.6781

各类别的性能相对均衡。

第四轮:

平均训练损失: 0.0056

开发集损失: 0.0163

总体性能保持稳定,F1-score 为 0.6808

各类别的性能相对稳定,CHEM 和 DISO 类别性能较好。

随着训练轮次的增加,平均训练损失逐渐减小,表明模型在训练数据上逐步学到了特征。开发集损失逐渐减小,说明模型在验证数据上的性能逐渐提升,但在第四轮有轻微上升。模型整体性能在四轮中逐渐提升,最终在开发集上达到了相对稳定的水平。

319fbcf505a347588e1243bc334deeb8.png

76f9d16c22f74fd0bdec21b9df97a1b1.png 

绘制训练过程中的学习曲线,其中包括训练损失(training loss)和验证损失(validation loss)随着训练轮次的变化趋势。图像中的横轴表示训练轮次(Epoch),纵轴表示损失值(Loss)。每个点表示模型在相应训练轮次结束时的损失值。 

d6a91c582ed74f5eaefe3311a4168831.png

随着训练轮次的增加,训练损失逐渐减小。这表明模型在训练数据上逐步学到了特征,取得了收敛。后续训练损失下降但验证损失上升,则可能存在过拟合问题,模型在训练数据上表现好,但在新数据上表现较差。

测试集性能评估:

Accuracy(准确性): 91.78%,表示整体分类的准确性很高。

Macro Avg 和 Weighted Avg: 这两个指标是对所有类别性能的综合评价,显示模型在多类别任务上的整体表现。

模型在大多数类别上都表现良好,尤其是在 B-PROC、I-PROC、B-CHEM、I-CHEM、I-ANAT 等类别上具有较高的精确度和召回率。

55df687250b94c4d8861e6989b0a3856.png

五、参考文献 

  1. Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., ... & Polosukhin, I. (2017). Attention is All You Need. In Advances in Neural Information Processing Systems (NeurIPS), 5998-6008.
  2. Devlin, J., Chang, M. W., Lee, K., & Toutanova, K. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv preprint arXiv:1810.04805.

六、总结 

        本次实验通过搭建基于BERT的命名实体识别系统,深入了解了模型的训练、验证和测试过程。通过使用Hugging Face的数据集库加载了一个西班牙语生物医学文本的命名实体识别数据集,并对数据集进行了预处理,包括分词、标签转索引等。选择了预训练的西班牙语BERT模型(dccuchile/bert-base-spanish-wwm-cased)作为基础模型,并根据任务配置了模型的输入参数、标签集、优化器等。利用训练集对BERT模型进行了训练,并通过设定的损失函数和学习率调度程序进行了模型参数的优化。在训练过程中,使用了GPU加速,并在多个轮次中迭代训练。通过在验证集上进行性能评估,监测模型的训练效果。利用分类报告、损失值等指标来评估模型对各个实体类别的识别性能。最终,利用测试集对训练好的BERT模型进行了测试,评估模型的泛化性能。实验结果表明,BERT模型在生物医学文本命名实体识别任务上具有良好的性能,但也需要对一些特定类别进行进一步的改进。

七、代码附录

# 安装必需的包
!pip install datasets
!pip install seqeval

# 导入必要的库
import torch
import numpy as np
import pandas as pd
from datasets import load_dataset
from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler
import transformers
from transformers import BertTokenizer, BertConfig
from keras.preprocessing.sequence import pad_sequences
from transformers import BertForTokenClassification, AdamW
import seqeval
from seqeval.metrics import f1_score, precision_score, recall_score, accuracy_score, classification_report

# 设置Hugging Face API令牌以访问数据集
access_token = "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"

# 从Hugging Face Hub加载一个命名数据集
dataset = load_dataset("lcampillos/ctebmsp", token=access_token)

# 显示数据集信息
print(dataset)

# 定义与BERT相关的参数
MAX_LEN = 270  # 最大句子长度
bs = 8         # 批量大小
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 选择设备(如果可用,则为GPU)

# 为令牌分类定义所有可能的标签
all_tags = ["O", "B-PROC", "I-PROC", "B-CHEM", "I-CHEM", "B-DISO", "I-DISO", "B-ANAT", "I-ANAT", "PAD"]

# 创建标签到索引的映射字典
tag2idx = {tag: idx for idx, tag in enumerate(all_tags)}
tag_values = {idx: tag for idx, tag in enumerate(all_tags)}

# 加载用于西班牙语的BERT分词器
tokenizer = BertTokenizer.from_pretrained("dccuchile/bert-base-spanish-wwm-cased", token=access_token)

# 定义处理数据集的函数
def process_dataset(dataset):
    tokenized_texts = dataset["tokens"]
    labels = dataset["ner_tags"]

    # 对输入句子和标签进行填充
    input_ids = pad_sequences([tokenizer.convert_tokens_to_ids(txt) for txt in tokenized_texts],
                              maxlen=MAX_LEN, dtype="long", value=0.0,
                              truncating="post", padding="post")

    tags = pad_sequences([[tag2idx.get(l) for l in lab] for lab in labels],
                         maxlen=MAX_LEN, value=tag2idx["PAD"], padding="post",
                         dtype="long", truncating="post")

    # 创建注意力掩码以在测试期间忽略填充的元素
    attention_masks = [[float(i != 0.0) for i in ii] for ii in input_ids]

    # 转换为torch张量
    inputs = torch.tensor(input_ids)
    tags = torch.tensor(tags)
    masks = torch.tensor(attention_masks)

    return TensorDataset(inputs, masks, tags)

# 处理训练、验证和测试数据集
train_data_processed = process_dataset(dataset["train"])
dev_data_processed = process_dataset(dataset["validation"])
test_data_processed = process_dataset(dataset["test"])

# 为每个数据集定义DataLoader
train_dataloader = DataLoader(train_data_processed, batch_size=bs, shuffle=True)
dev_dataloader = DataLoader(dev_data_processed, batch_size=bs)
test_dataloader = DataLoader(test_data_processed, batch_size=bs)

# 实例化用于令牌分类的BERT模型
model = BertForTokenClassification.from_pretrained(
    "dccuchile/bert-base-spanish-wwm-cased",
    num_labels=len(tag2idx),
    output_attentions=False,
    output_hidden_states=False,
    token=access_token
)

# 将模型移到GPU
model.cuda()

# 配置优化器和学习率调度程序
FULL_FINETUNING = True
if FULL_FINETUNING:
    param_optimizer = list(model.named_parameters())
    no_decay = ['bias', 'gamma', 'beta']
    optimizer_grouped_parameters = [
        {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)],
         'weight_decay_rate': 0.01},
        {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)],
         'weight_decay_rate': 0.0}
    ]
else:
    param_optimizer = list(model.classifier.named_parameters())
    optimizer_grouped_parameters = [{"params": [p for n, p in param_optimizer]}]

# Adam优化器
optimizer = AdamW(
    optimizer_grouped_parameters,
    lr=3e-5,
    eps=1e-8
)

# 导入调度程序以减小学习率
from transformers import get_linear_schedule_with_warmup

# 训练轮数
epochs = 4
max_grad_norm = 1.0

# 总训练步骤数
total_steps = len(train_dataloader) * epochs

# 创建学习率调度程序
scheduler = get_linear_schedule_with_warmup(
    optimizer,
    num_warmup_steps=0,
    num_training_steps=total_steps
)

# 用于存储训练和验证损失值的列表
loss_values, development_loss_values = [], []

# 训练循环
for _ in range(epochs):
    # 训练
    model.train()
    total_loss = 0

    for step, batch in enumerate(train_dataloader):
        # 将批次转移到GPU
        batch = tuple(t.to(device) for t in batch)
        b_input_ids, b_input_mask, b_labels = batch

        # 移除之前的梯度
        model.zero_grad()

        # 前向传播
        outputs = model(b_input_ids, token_type_ids=None,
                        attention_mask=b_input_mask, labels=b_labels)
        # 获取损失
        loss = outputs[0]
        # 反向传播
        loss.backward()
        # 训练损失
        total_loss += loss.item()

        # 限制梯度范数以防止梯度爆炸
        torch.nn.utils.clip_grad_norm_(parameters=model.parameters(), max_norm=max_grad_norm)

        # 更新参数
        optimizer.step()

        # 更新学习率
        scheduler.step()

    # 计算平均训练损失
    avg_train_loss = total_loss / len(train_dataloader)
    print("平均训练损失: {}".format(avg_train_loss))

    # 存储损失值以绘制学习曲线
    loss_values.append(avg_train_loss)

    # 开发集评估
    model.eval()
    eval_loss = 0
    predictions, true_labels = [], []

    for batch in dev_dataloader:
        batch = tuple(t.to(device) for t in batch)
        b_input_ids, b_input_mask, b_labels = batch

        with torch.no_grad():
            outputs = model(b_input_ids, token_type_ids=None,
                            attention_mask=b_input_mask, labels=b_labels)

        logits = outputs[1].detach().cpu().numpy()
        label_ids = b_labels.to('cpu').numpy()

        eval_loss += outputs[0].mean().item()
        predictions.extend([list(p) for p in np.argmax(logits, axis=2)])
        true_labels.extend(label_ids)

    eval_loss = eval_loss / len(dev_dataloader)
    development_loss_values.append(eval_loss)
    print("开发集损失: {}".format(eval_loss))

    # 将预测和真实标签转换为嵌套列表格式
    pred_tags = [tag_values[p_i] for p, l in zip(predictions, true_labels)
                 for p_i, l_i in zip(p, l) if tag_values[l_i] != "PAD"]
    dev_tags = [tag_values[l_i] for l in true_labels
                for l_i in l if tag_values[l_i] != "PAD"]

    # 打印分类报告
    print("开发集分类报告:\n{}".format(classification_report(dev_tags, pred_tags, digits=4)))
    print()

# 绘制训练损失
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid')
sns.set(font_scale=1.5)
plt.rcParams["figure.figsize"] = (12, 6)
plt.plot(loss_values, 'b-o', label="训练损失")
plt.plot(development_loss_values, 'r-o', label="验证损失")
plt.title("学习曲线")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.legend()
plt.show()

# 将模型应用于测试集
# 再次将模型设置为评估模式
from sklearn.metrics import classification_report

model.eval()
nb_eval_steps, nb_eval_examples = 0, 0
predictions, true_labels = [], []

input_ids_list = []

for batch in test_dataloader:

    batch = tuple(t.to(device) for t in batch)
    b_input_ids, b_input_mask, b_labels = batch

    # 模型不能计算或存储梯度
    with torch.no_grad():
        outputs = model(b_input_ids, token_type_ids=None,
                            attention_mask=b_input_mask, labels=b_labels)
    # 将logits和标签传输到CPU
    logits = outputs[1].detach().cpu().numpy()
    label_ids = b_labels.to('cpu').numpy()
    input_ids_list.extend(b_input_ids)

    # 计算这批测试句子的准确性
    eval_loss += outputs[0].mean().item()
    predictions.extend([list(p) for p in np.argmax(logits, axis=2)])
    true_labels.extend(label_ids)

pred_tags = [tag_values[p_i] for p, l in zip(predictions, true_labels)
                                 for p_i, l_i in zip(p, l) if tag_values[l_i] != "PAD"]
test_tags = [tag_values[l_i] for l in true_labels
                                  for l_i in l if tag_values[l_i] != "PAD"]
print("测试准确性: {:.4f}".format(accuracy_score(pred_tags, test_tags)))
print("测试分类报告:\n{}".format(classification_report(pred_tags, test_tags, target_names=tag_values)))

 

 

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

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

相关文章

Oracle 11g RAC 节点异常重启问题分析

一、背景 在国庆期间巡检的时候,发现数据库alert日志中出现了异常重启的信息,当即对该报错进行分析处理。 二、处理过程 (1)数据库告警日志分析 node1 alert: Sat Oct 05 13:05:14 2024 Thread 1 advanced to log …

前端模块化进化史:从全局 function 到 ES Modules

目前,前端开发已经离不开由 CommonJS、ES Modules 和 Webpack 构建的模块化开发环境。无论是 JavaScript、CSS、图片还是其他资源,都可以作为一个模块来处理。那么,模块化究竟是如何发展到今天的呢? 全局函数模式 最初的前端模块…

C# 属性和方法

本课要点&#xff1a; 1、属性的使用 2、方法的使用 一 数据安全问题 示例 static void Main(string[] args) {Student stu new Student();stu.age -2; }二 属性 private int age; //年龄 public int Age {get { return age; }set{if (value > 0 && value <…

JavaEE: 深入解析HTTP协议的奥秘(2)

文章目录 HTTP认识 URLURL encode 介绍 认识 "方法"(method)GETPOST其他方法 HTTP JavaEE: 深入解析HTTP协议的奥秘(1) 书接上文~ 认识 URL 平时我们俗称的"网址"其实就是说的 URL .(唯一资源定位符) URL 不是 HTTP 专属的,很多协议都会用到. 其实除了 …

软考-程序设计语言基础

一、程序设计语言概述 (一)程序设计语言的基本概念 1. 程序设计语言的目的 为了书写计算机程序而人为设计的符号语言&#xff0c;用于对计算过程进行描述、组织和推导。 2.低级语言 机器语言&#xff1a; 机器语言是计算机最原始的语言&#xff0c;由0和1的代码构成 计算机…

You must konw JS!!(超详细的javascript套餐,适合计算机专业有基础的,包含常见前端开发面试题)

1.起源 JavaScript 起源于 1995 年&#xff0c;当时它主要是为了满足网页交互的需求而被创建。它最初的设计目的是为了让网页开发者能够在网页中添加一些简单的交互效果和动态内容。在那个时期&#xff0c;网页大多是静态的&#xff0c;而 JavaScript 的出现为网页带来了新的活…

如何将数据从 AWS S3 导入到 Elastic Cloud - 第 1 部分:Elastic Serverless Forwarder

作者&#xff1a;来自 Elastic Hemendra Singh Lodhi 这是多部分博客系列的第一部分&#xff0c;探讨了将数据从 AWS S3 导入 Elastic Cloud 的不同选项。 Elasticsearch 提供了多种从 AWS S3 存储桶导入数据的选项&#xff0c;允许客户根据其特定需求和架构策略选择最合适的方…

助动词的分类及其缩略形式

助动词的分类及其缩略形式 1. 助动词 (auxiliary verb)2. 基本助动词 (primary auxiliary)2.1. 基本助动词 be、do 和 have2.2. 实义动词 be、do 和 have 3. 情态助动词 (modal auxiliary)3.1. 情态助动词取代情态动词 4. 半助动词 (semi-auxiliary)4.1. 不能与 it ... that-cl…

MoveIt2-humble----Planning Around Objects(a)

4.Pick and Place with MoveIt Task Constructor 本节教程会教你创建一个功能包&#xff0c;使用MoveIt Task Constructor规划一个抓取和放置的操作。MoveIt Task Constructor 提供了一种方式&#xff0c;去规划由多种不同子任务&#xff08;也称为阶段&#xff09;所组成的任…

详解Redis分布式锁在SpringBoot的@Async方法中没锁住的坑

背景 Redis分布式锁很有用处&#xff0c;在秒杀、抢购、订单、限流特别是一些用到异步分布式并行处理任务时频繁的用到&#xff0c;可以说它是一个BS架构的应用中最高频使用的技术之一。 但是我们经常会碰到这样的一个问题&#xff0c;那就是我们都按照标准做了但有时运行着、…

分层解耦-05.IOCDI-DI详解

一.依赖注入的注解 在我们的项目中&#xff0c;EmpService的实现类有两个&#xff0c;分别是EmpServiceA和EmpServiceB。这两个实现类都加上Service注解。我们运行程序&#xff0c;就会报错。 这是因为我们依赖注入的注解Autowired默认是按照类型来寻找bean对象的进行依赖注入…

基于Qt的速度仪表盘控件实现

本文将详细讲解一个基于Qt的速度仪表盘控件的实现过程&#xff0c;并对代码进行详细的注释说明。该控件可以模拟汽车仪表盘的外观&#xff0c;并通过滑动条动态改变速度显示。本文将从代码结构、绘制组件到实现细节进行讲解&#xff0c;帮助您理解如何使用Qt框架自定义绘制控件…

CSRF | GET 型 CSRF 漏洞攻击

关注这个漏洞的其他相关笔记&#xff1a;CSRF 漏洞 - 学习手册-CSDN博客 0x01&#xff1a;GET 型 CSRF 漏洞攻击 —— 理论篇 GET 型 CSRF 漏洞是指攻击者通过构造恶意的 HTTP GET 请求&#xff0c;利用用户的登录状态&#xff0c;在用户不知情的情况下&#xff0c;诱使浏览器…

Cortex-M3/M4/M7 芯片 Fault 分析原理与实战

目录 一、简介1、异常类型2、异常优先级3、同步异步问题4、异常具体类型 二、Fault exception registers1、Control registers1.1 CCR1.2 SHP1.3 SHCSR 2、Status and address registers2.1 HardFault Status Register——HSFR2.2 Configurable Fault Status Register——CFSR2…

《Linux从小白到高手》进阶实操篇:用户及权限有关的实际工作场景应用

List item 本篇为《Linux从小白到高手》进阶实操篇的第一篇&#xff0c;主要介绍分享一些用户及权限有关的实际工作场景应用。 场景1&#xff1a; 实际工作中你一定会碰到如下图所示的情景&#xff1a;本部门有5个组&#xff0c;分别为&#xff1a;①Root组&#xff1a;用户…

Python中对象obj类型确定最pythonic的方式——isinstance()函数

python中确定对象obj的类型&#xff0c;isinstance函数最是优雅&#xff0c;type、issubclass等函数也可以&#xff0c;但终究“曲折”。 (笔记模板由python脚本于2024年10月07日 19:42:38创建&#xff0c;本篇笔记适合喜欢python的coder翻阅) 【学习的细节是欢悦的历程】 Pyth…

Vue2电商项目(七)、订单与支付

文章目录 一、交易业务Trade1. 获取用户地址2. 获取订单信息 二、提交订单三、支付1. 获取支付信息2. 支付页面--ElementUI(1) 引入Element UI(2) 弹框支付的业务逻辑(这个逻辑其实没那么全)(3) 支付逻辑知识点小总结 四、个人中心1. 搭建二级路由2. 展示动态数据(1). 接口(2).…

【Kubernetes】常见面试题汇总(六十)

目录 131. pod 一直处于 pending 状态&#xff1f; 132. helm 安装组件失败&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目 69-113 属于…

企业经营异常怎么解除

经营异常是怎么回事&#xff1f;是什么意思&#xff1f;了解异常原因&#xff1a;我们到所属工商营业执照异常的具体原因。原因可能包括未按时提交年报、未履行即时信息公示义务、公示信息隐瞒真实情况或弄xu作jia、失联等。纠正违规行为&#xff1a;查到了异常原因&#xff0c…

洛谷P5723、P5728、P1428、P1319 Python解析

P5723 完整代码 def is_prime(y):if y < 2:return Falsefor i in range(2, int(y**0.5) 1):if y % i 0:return Falsereturn Truen int(input()) sum_primes 0 x 0if n < 2:print("0") elif n 2:print("2\n1") else:for i in range(2, n 1):i…