[oneAPI] 基于BERT预训练模型的命名体识别任务

[oneAPI] 基于BERT预训练模型的命名体识别任务

  • Intel® DevCloud for oneAPI 和 Intel® Optimization for PyTorch
  • 基于BERT预训练模型的命名体识别任务
    • 语料介绍
      • 数据集构建
      • 使用示例
    • 命名体识别模型
      • 前向传播
      • 模型训练
    • 结果
  • 参考资料

比赛:https://marketing.csdn.net/p/f3e44fbfe46c465f4d9d6c23e38e0517
Intel® DevCloud for oneAPI:https://devcloud.intel.com/oneapi/get_started/aiAnalyticsToolkitSamples/

Intel® DevCloud for oneAPI 和 Intel® Optimization for PyTorch

在本次实验中,我们在Intel® DevCloud for oneAPI上搭建实验,借助完全虚拟化的环境,专注于模型开发与优化,无需关心底层配置。使用Intel® Optimization for PyTorch,对PyTorch模型进行高效优化。

我们充分发挥了PyTorch和Intel® Optimization for PyTorch的强大功能,经过仔细的优化和拓展。这些优化措施极大地提升了PyTorch在各种任务中的性能,尤其是在英特尔硬件上的表现更为卓越。通过这些优化方法,我们的模型在训练和推断过程中变得更加敏捷高效,大幅缩短了计算时间,从而提升了整体效率。借助深度融合硬件与软件的巧妙设计,我们成功地释放了硬件潜力,使模型的训练和应用变得更加迅速高效。这些优化举措为人工智能应用开辟了崭新的前景,带来了全新的可能性。
在这里插入图片描述

基于BERT预训练模型的命名体识别任务

基于BERT预训练模型的第五个下游任务场景,即如何完成命名体识别(Named Entity Recognition, NER)任务。所谓命名体指的是给模型输入一句文本,最后需要模型将其中的实体(例如人名、地名、组织等等)标记出来。

1 句子:涂伊说,如果有机会他想去黄州赤壁看一看!
2 标签:['B-PER', 'I-PER', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'B-LOC', 'I-LOC', 'B-LOC', 'I-LOC', 'O', 'O', 'O', 'O']
3 实体:涂伊(人名)、黄州(地名)、赤壁(地名)

通常来讲,对于任意一个NLP任务来说模型最后所要完成的基本上都是一个分类任务,尽管表面上看起来可能不太像。根据给出的标签来看,对于原始句子中的每个字符来说其都有一个对应的类别标签,因此对于NER任务来说只需要对原始句子里的每个字符进行分类即可,然后再将预测后的结果进行后处理便能够得到句子从存在的相应实体。

原始数据输入为一个句子,我们只需要在句子的首尾分别加上[CLS]和[SEP],然后输入到模型当中进行特征提取并最终通过一个分类层对输出的每个Token进行分类即可,最后只需要对各个Token的预测结果进行后处理便能够实现整个NER任务。

语料介绍

一个中文命名体识别数据集https://github.com/zjy-ucas/ChineseNER,如下所示便是原始数据的存储形式:

 1 涂 B-PER2 伊 I-PER3 说 O4 , O5 如 O6 果 O7 有 O8 机 O9 会 O
10 他 O
11 想 O
12 去 O
13 黄 B-LOC
14 州 I-LOC
15 赤 B-LOC
16 壁 I-LOC
17 看 O
18 一 O
19 看 O
20 !O

其中每一行包含一个字符和其对应的所属类别,B-表示该类实体的开始标志,I-表示该类实体的延续标志。例如对于13-16行来说其对应了“黄州”和“赤壁”这两个实体。同时,对于这个数据集来说,其一共包含有3类实体(人名、地名和组织),因此其对应的分类总数便为7,如下所示:

1 {'O': 0, 'B-ORG': 1, 'B-LOC': 2, 'B-PER': 3, 'I-ORG': 4, 'I-LOC': 5, 'I-PER': 6}

对于数据预处理部分我们可以继续继承之前文本分类处理中的LoadSingleSentenceClassificationDataset类,然后再稍微修改其中的部分方法即可。

数据集构建

在说完数据集构造的整理思路后,下面我们就来正式编码实现整个数据集的构造过程。同样,对于数据预处理部分我们可以继续继承之前文本分类处理中的LoadSingleSentenceClassificationDataset类,然后再稍微修改其中的部分方法即可。

class LoadChineseNERDataset(LoadSingleSentenceClassificationDataset):def __init__(self, entities=None, num_labels=None, ignore_idx=-100, **kwargs):super(LoadChineseNERDataset, self).__init__(**kwargs)self.entities = entitiesself.num_labels = num_labelsself.IGNORE_IDX = ignore_idxif self.entities is None or self.num_labels is None:raise ValueError(f"类 {self.__class__.__name__} 中参数 entities 或 num_labels 不能为空!")@cachedef data_process(self, filepath, postfix='cache'):raw_iter = open(filepath, encoding="utf8").readlines()data = []max_len = 0tmp_token_ids = []tmp_sentence = ""tmp_label = []tmp_entity = []for raw in tqdm(raw_iter, ncols=80):line = raw.rstrip("\n").split(self.split_sep)if len(line) != 1 and len(line) != 2:raise ValueError(f"数据标注有误{line}")if len(line) == 1:  # 表示得到一个完整的token id样本if len(tmp_token_ids) > self.max_position_embeddings - 2:tmp_token_ids = tmp_token_ids[:self.max_position_embeddings - 2]tmp_label = tmp_label[:self.max_position_embeddings - 2]max_len = max(max_len, len(tmp_label) + 2)token_ids = torch.tensor([self.CLS_IDX] + tmp_token_ids +[self.SEP_IDX], dtype=torch.long)labels = torch.tensor([self.IGNORE_IDX] + tmp_label +[self.IGNORE_IDX], dtype=torch.long)data.append([tmp_sentence, token_ids, labels])logging.debug(" ### 样本构造结果为:")logging.debug(f"   ## 句子: {tmp_sentence}")logging.debug(f"   ## 实体: {tmp_entity}")logging.debug(f"   ## input_ids: {token_ids.tolist()}")logging.debug(f"   ## label: {labels.tolist()}")logging.debug(f" ================================\n")assert len(tmp_token_ids) == len(tmp_label)tmp_token_ids = []tmp_sentence = ""tmp_label = []tmp_entity = []continuetmp_sentence += line[0]tmp_token_ids.append(self.vocab[line[0]])tmp_label.append(self.entities[line[-1]])tmp_entity.append(line[-1])return data, max_lendef generate_batch(self, data_batch):batch_sentence, batch_token_ids, batch_label = [], [], []for (sen, token_ids, label) in data_batch:  # 开始对一个batch中的每一个样本进行处理。batch_sentence.append(sen)batch_token_ids.append(token_ids)batch_label.append(label)batch_token_ids = pad_sequence(batch_token_ids,  # [batch_size,max_len]padding_value=self.PAD_IDX,batch_first=False,max_len=self.max_sen_len)batch_label = pad_sequence(batch_label,  # [batch_size,max_len]padding_value=self.IGNORE_IDX,batch_first=False,max_len=self.max_sen_len)# ① 因为label的长度各不相同,所以同一个batch中的label需要padding到相同的长度;# ② 因为进行了padding操作,所以在计算损失的时候需要把padding部分的损失忽略掉;# ③ 又因为label中有0这个类别的存在,所以不能用词表中的PAD_IDX进行padding(PAD_IDX为0),所以要另外取一个IGNORE_IDXreturn batch_sentence, batch_token_ids, batch_labeldef make_inference_samples(self, sentences):if not isinstance(sentences, list):sentences = [sentences]data = []for sen in sentences:tokens = [self.vocab[word] for word in sen]label = [-1] * len(tokens)token_ids = torch.tensor([self.CLS_IDX] + tokens + [self.SEP_IDX], dtype=torch.long)labels = torch.tensor([self.IGNORE_IDX] + label + [self.IGNORE_IDX], dtype=torch.long)data.append([sen, token_ids, labels])return self.generate_batch(data)

使用示例

在完成数据集构造部分的相关代码实现之后,便可以通过如下所示的方式进行使用,代码如下:

class ModelConfig:def __init__(self):self.project_dir = os.path.dirname(os.path.abspath(__file__))self.dataset_dir = os.path.join(self.project_dir, 'ChineseNERdata')self.pretrained_model_dir = os.path.join(self.project_dir, "pretraining")self.vocab_path = os.path.join(self.pretrained_model_dir, 'vocab.txt')self.device = torch.device('xpu' if torch.cuda.is_available() else 'cpu')self.train_file_path = os.path.join(self.dataset_dir, 'example_train.txt')self.val_file_path = os.path.join(self.dataset_dir, 'example_dev.txt')self.test_file_path = os.path.join(self.dataset_dir, 'example_test.txt')self.model_save_dir = os.path.join(self.project_dir, 'cache')self.model_save_name = "ner_model.pt"self.logs_save_dir = os.path.join(self.project_dir, 'logs')self.split_sep = ' 'self.is_sample_shuffle = Trueself.batch_size = 6self.max_sen_len = Noneself.epochs = 10self.learning_rate = 1e-5self.model_val_per_epoch = 2self.entities = {'O': 0, 'B-ORG': 1, 'B-LOC': 2, 'B-PER': 3, 'I-ORG': 4, 'I-LOC': 5, 'I-PER': 6}self.num_labels = len(self.entities)self.ignore_idx = -100logger_init(log_file_name='ner', log_level=logging.DEBUG,log_dir=self.logs_save_dir)if not os.path.exists(self.model_save_dir):os.makedirs(self.model_save_dir)# 把原始bert中的配置参数也导入进来bert_config_path = os.path.join(self.pretrained_model_dir, "config.json")bert_config = BertConfig.from_json_file(bert_config_path)for key, value in bert_config.__dict__.items():self.__dict__[key] = value# 将当前配置打印到日志文件中logging.info(" ### 将当前配置打印到日志文件中 ")for key, value in self.__dict__.items():logging.info(f"###  {key} = {value}")

命名体识别模型

前向传播

我们只需要在原始BERT模型的基础上再加一个对所有Token进行分类的分类层即可,因此这部分代码相对来说也比较容易理解。首先需要在DownstreamTasks目录下新建一个BertForTokenClassification模块,并完成整个模型的初始化和前向传播过程,代码如下:

from ..BasicBert.Bert import BertModel
import torch.nn as nnclass BertForTokenClassification(nn.Module):def __init__(self, config, bert_pretrained_model_dir=None):super(BertForTokenClassification, self).__init__()self.num_labels = config.num_labelsif bert_pretrained_model_dir is not None:self.bert = BertModel.from_pretrained(config, bert_pretrained_model_dir)else:self.bert = BertModel(config)self.dropout = nn.Dropout(config.hidden_dropout_prob)self.classifier = nn.Linear(config.hidden_size, self.num_labels)self.config = configdef forward(self,input_ids=None,attention_mask=None,token_type_ids=None,position_ids=None,labels=None):""":param input_ids: [src_len,batch_size]:param attention_mask: [batch_size, src_len]:param token_type_ids::param position_ids::param labels: [src_len,batch_size]:return:"""_, all_encoder_outputs = self.bert(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids,position_ids=position_ids)  # [batch_size,hidden_size]sequence_output = all_encoder_outputs[-1]  # 取最后一层# sequence_output: [src_len, batch_size, hidden_size]sequence_output = self.dropout(sequence_output)logits = self.classifier(sequence_output)# logit: [src_len, batch_size, num_labels]if labels is not None:  # [src_len,batch_size]loss_fct = nn.CrossEntropyLoss(ignore_index=self.config.ignore_idx)loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))return loss, logitselse:return logits

模型训练

对于模型训练这部分内容来说,首先我们需要在Tasks目录下新建一个TaskForChineseNER.py模块,并新建一个配置类ModelConfig来管理整个模型需要用到的参数,代码实现如下:

class ModelConfig:def __init__(self):self.project_dir = os.path.dirname(os.path.abspath(__file__))self.dataset_dir = os.path.join(self.project_dir, 'ChineseNERdata')self.pretrained_model_dir = os.path.join(self.project_dir, "pretraining")self.vocab_path = os.path.join(self.pretrained_model_dir, 'vocab.txt')self.device = torch.device('xpu' if torch.cuda.is_available() else 'cpu')self.train_file_path = os.path.join(self.dataset_dir, 'example_train.txt')self.val_file_path = os.path.join(self.dataset_dir, 'example_dev.txt')self.test_file_path = os.path.join(self.dataset_dir, 'example_test.txt')self.model_save_dir = os.path.join(self.project_dir, 'cache')self.model_save_name = "ner_model.pt"self.logs_save_dir = os.path.join(self.project_dir, 'logs')self.split_sep = ' 'self.is_sample_shuffle = Trueself.batch_size = 6self.max_sen_len = Noneself.epochs = 10self.learning_rate = 1e-5self.model_val_per_epoch = 2self.entities = {'O': 0, 'B-ORG': 1, 'B-LOC': 2, 'B-PER': 3, 'I-ORG': 4, 'I-LOC': 5, 'I-PER': 6}self.num_labels = len(self.entities)self.ignore_idx = -100logger_init(log_file_name='ner', log_level=logging.DEBUG,log_dir=self.logs_save_dir)if not os.path.exists(self.model_save_dir):os.makedirs(self.model_save_dir)# 把原始bert中的配置参数也导入进来bert_config_path = os.path.join(self.pretrained_model_dir, "config.json")bert_config = BertConfig.from_json_file(bert_config_path)for key, value in bert_config.__dict__.items():self.__dict__[key] = value# 将当前配置打印到日志文件中logging.info(" ### 将当前配置打印到日志文件中 ")for key, value in self.__dict__.items():logging.info(f"###  {key} = {value}")

因为在模型训练过程中需要计算相关的评价指标,如准确率、精确率和召回率等,因此需要对这部分进行实现,代码如下

def accuracy(logits, y_true, ignore_idx=-100):""":param logits:  [src_len,batch_size,num_labels]:param y_true:  [src_len,batch_size]:param ignore_idx: 默认情况为-100:return:e.g.y_true = torch.tensor([[-100, 0, 0, 1, -100],[-100, 2, 0, -100, -100]]).transpose(0, 1)logits = torch.tensor([[[0.5, 0.1, 0.2], [0.5, 0.4, 0.1], [0.7, 0.2, 0.3], [0.5, 0.7, 0.2], [0.1, 0.2, 0.5]],[[0.3, 0.2, 0.5], [0.7, 0.2, 0.4], [0.8, 0.1, 0.3], [0.9, 0.2, 0.1], [0.1, 0.5, 0.2]]])logits = logits.transpose(0, 1)print(accuracy(logits, y_true, -100)) # (0.8, 4, 5)"""y_pred = logits.transpose(0, 1).argmax(axis=2).reshape(-1).tolist()# 将 [src_len,batch_size,num_labels] 转成 [batch_size, src_len,num_labels]y_true = y_true.transpose(0, 1).reshape(-1).tolist()real_pred, real_true = [], []for item in zip(y_pred, y_true):if item[1] != ignore_idx:real_pred.append(item[0])real_true.append(item[1])return accuracy_score(real_true, real_pred), real_true, real_pred

为了能够在模型训练或推理过程中输入模型的预测结果,因此我们需要实现3个辅助函数来完成。首先需要实现根据logits和input_token_ids来得到每个预测值对应的实体标签,代码如下:

def get_ner_tags(logits, token_ids, entities, SEP_IDX=102):""":param logits:  [src_len,batch_size,num_samples]:param token_ids: # [src_len,batch_size]:return:e.g.logits = torch.tensor([[[0.4, 0.7, 0.2],[0.5, 0.4, 0.1],[0.1, 0.2, 0.3],[0.5, 0.7, 0.2],[0.1, 0.2, 0.5]],[[0.3, 0.2, 0.5],[0.7, 0.8, 0.4],[0.1, 0.1, 0.3],[0.9, 0.2, 0.1],[0.1, 0.5,0.2]]])logits = logits.transpose(0, 1)  # [src_len,batch_size,num_samples]token_ids = torch.tensor([[101, 2769, 511, 102, 0],[101, 56, 33, 22, 102]]).transpose(0, 1)  # [src_len,batch_size]labels, probs = get_ner_tags(logits, token_ids, entities)[['O', 'B-LOC'], ['B-ORG', 'B-LOC', 'O']][[0.5, 0.30000001192092896], [0.800000011920929, 0.30000001192092896, 0.8999999761581421]]"""# entities = {'O': 0, 'B-ORG': 1, 'B-LOC': 2, 'B-PER': 3, 'I-ORG': 4, 'I-LOC': 5, 'I-PER': 6}label_list = list(entities.keys())logits = logits[1:].transpose(0, 1)  # [batch_size,src_len-1,num_samples]prob, y_pred = torch.max(logits, dim=-1)  # prob, y_pred: [batch_size,src_len-1]token_ids = token_ids[1:].transpose(0, 1)  # [ batch_size,src_len-1], 去掉[cls]assert y_pred.shape == token_ids.shapelabels = []probs = []for sample in zip(y_pred, token_ids, prob):tmp_label, tmp_prob = [], []for item in zip(*sample):if item[1] == SEP_IDX:  # 忽略最后一个[SEP]字符breaktmp_label.append(label_list[item[0]])tmp_prob.append(item[2].item())labels.append(tmp_label)probs.append(tmp_prob)return labels, probs

进一步,在得到每个输入句子的预测结果后,还需要将其进行格式化处理得到最终的预测结果,实现代码如下:

def pretty_print(sentences, labels, entities):""":param sentences::param labels::param entities::return:e.g.labels = [['B-PER','I-PER', 'O','O','O','O','O','O','O','O','O','O','B-LOC','I-LOC','B-LOC','I-LOC','O','O','O','O'],['B-LOC','I-LOC','O','B-LOC','I-LOC','O','B-LOC','I-LOC','I-LOC','O','B-LOC','I-LOC','O','O','O','B-PER','I-PER','O','O','O','O','O','O']]sentences=["涂伊说,如果有机会他想去赤壁看一看!","丽江、大理、九寨沟、黄龙等都是涂伊想去的地方!"]entities = {'O': 0, 'B-ORG': 1, 'B-LOC': 2, 'B-PER': 3, 'I-ORG': 4, 'I-LOC': 5, 'I-PER': 6}句子:涂伊说,如果有机会他想去黄州赤壁看一看!涂伊:  PER黄州:  LOC赤壁:  LOC句子:丽江、大理、九寨沟、黄龙等都是涂伊想去的地方!丽江:  LOC大理:  LOC九寨沟:  LOC黄龙:  LOC涂伊:  PER"""sep_tag = [tag for tag in list(entities.keys()) if 'I' not in tag]result = []for sen, label in zip(sentences, labels):logging.info(f"句子:{sen}")last_tag = Nonefor item in zip(sen + "O", label + ['O']):if item[1] in sep_tag:  #if len(result) > 0:entity = "".join(result)logging.info(f"\t{entity}:  {last_tag.split('-')[-1]}")result = []if item[1] != 'O':result.append(item[0])last_tag = item[1]else:result.append(item[0])last_tag = item[1]

输出结果如下:

1 句子:涂伊说,如果有机会他想去黄州赤壁看一看!2 涂伊:  PER3 黄州:  LOC4 赤壁:  LOC5 句子:丽江、大理、九寨沟、黄龙等都是涂伊想去的地方!6 丽江:  LOC7 大理:  LOC8 九寨沟:  LOC9 黄龙:  LOC
10 涂伊:  PER

在完成上述所有铺垫之后,便可以来实现模型的训练部分,代码如下(下面只摘录核心部分进行介绍):

def train(config):model = BertForTokenClassification(config,config.pretrained_model_dir)model_save_path = os.path.join(config.model_save_dir,config.model_save_name)global_steps = 0if os.path.exists(model_save_path):checkpoint = torch.load(model_save_path)global_steps = checkpoint['last_epoch']loaded_paras = checkpoint['model_state_dict']model.load_state_dict(loaded_paras)logging.info("## 成功载入已有模型,进行追加训练......")model = model.to(config.device)optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)'''Apply Intel Extension for PyTorch optimization against the model object and optimizer object.'''model, optimizer = ipex.optimize(model, optimizer=optimizer)model.train()data_loader = LoadChineseNERDataset(entities=config.entities,num_labels=config.num_labels,ignore_idx=config.ignore_idx,vocab_path=config.vocab_path,tokenizer=BertTokenizer.from_pretrained(config.pretrained_model_dir).tokenize,batch_size=config.batch_size,max_sen_len=config.max_sen_len,split_sep=config.split_sep,max_position_embeddings=config.max_position_embeddings,pad_index=config.pad_token_id,is_sample_shuffle=config.is_sample_shuffle)train_iter, test_iter, val_iter = \data_loader.load_train_val_test_data(train_file_path=config.train_file_path,val_file_path=config.val_file_path,test_file_path=config.test_file_path,only_test=False)max_acc = 0for epoch in range(config.epochs):losses = 0start_time = time.time()for idx, (sen, token_ids, labels) in enumerate(train_iter):token_ids = token_ids.to(config.device)labels = labels.to(config.device)padding_mask = (token_ids == data_loader.PAD_IDX).transpose(0, 1)loss, logits = model(input_ids=token_ids,  # [src_len, batch_size]attention_mask=padding_mask,  # [batch_size,src_len]token_type_ids=None,position_ids=None,labels=labels)  # [src_len, batch_size]# logit: [src_len, batch_size, num_labels]optimizer.zero_grad()loss.backward()optimizer.step()losses += loss.item()global_steps += 1acc, _, _ = accuracy(logits, labels, config.ignore_idx)if idx % 20 == 0:logging.info(f"Epoch: {epoch}, Batch[{idx}/{len(train_iter)}], "f"Train loss :{loss.item():.3f}, Train acc: {round(acc, 5)}")if idx % 100 == 0:show_result(sen[:10], logits[:, :10], token_ids[:, :10], config.entities)end_time = time.time()train_loss = losses / len(train_iter)logging.info(f"Epoch: [{epoch + 1}/{config.epochs}],"f" Train loss: {train_loss:.3f}, Epoch time = {(end_time - start_time):.3f}s")if (epoch + 1) % config.model_val_per_epoch == 0:acc = evaluate(config, val_iter, model, data_loader)logging.info(f"Accuracy on val {acc:.3f}")if acc > max_acc:max_acc = accstate_dict = deepcopy(model.state_dict())torch.save({'last_epoch': global_steps,'model_state_dict': state_dict},model_save_path)

结果

在这里插入图片描述

在这里插入图片描述

参考资料

基于BERT预训练模型的中文文本分类任务: https://mp.weixin.qq.com/s/bbeN95mlLaE05dFndUAxgA

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

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

相关文章

Unity - 制作package 插件包

1.将制作的插件包代码放置一个根目录下 2.在跟目录下创建package.json文件 //package.json {"name": "com.unity.customlibrary", //插件包名:com.组织名.包名"displayName": "CustomLibrary", //显示的插件名"v…

OpenCV项目开发实战--基于Python/C++实现鼠标注释图像和轨迹栏来控制图像大小

鼠标指针是图形用户界面 (GUI) 中的关键组件。没有它,您就无法真正考虑与 GUI 进行交互。那么,让我们深入了解 OpenCV 中鼠标和轨迹栏的内置函数。我们将演示如何使用鼠标来注释图像,以及如何使用轨迹栏来控制图像的大小 我们将使用下图来演示 OpenCV 中鼠标指针和轨迹栏功能…

保护函数返回的利器——Linux Shadow Call Stack

写在前面 提到内核栈溢出的漏洞缓解,许多朋友首先想到的是栈内金丝雀(Stack Canary)。今天向大家介绍一项在近年,于Android设备中新增,且默默生效的安全机制——影子调用栈:SCS(Shadow Call St…

Kafka单节点部署

🎈 作者:互联网-小啊宇 🎈 简介: CSDN 运维领域创作者、阿里云专家博主。目前从事 Kubernetes运维相关工作,擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、开源软件部署维护…

iptables的使用规则

环境中为了安全要限制swagger的访问,最简单的方式是通过iptables防火墙设置规则限制。 在测试服务器中设置访问swagger-ui.html显示如下,区分大小写: iptables设置限制访问9783端口的swagger字段的请求: iptables -A INPUT -p t…

leetcode304. 二维区域和检索 - 矩阵不可变(java)

前缀和数组 二维区域和检索 - 矩阵不可变题目描述前缀和代码演示 一维数组前缀和 二维区域和检索 - 矩阵不可变 难度 - 中等 原题链接 - 二维区域和检索 - 矩阵不可变 题目描述 给定一个二维矩阵 matrix,以下类型的多个请求: 计算其子矩形范围内元素的总…

Java抽象类

Java中的抽象类(Abstract Class)是一种特殊类型的类,它无法被实例化,只能被用作其他类的基础。抽象类用于定义具有共同特征和行为的一组相关类的共同结构和方法。抽象类可以包含抽象方法(没有具体实现的方法&#xff0…

VR防地质灾害安全教育:增强自然灾害知识,提高自我保护意识

VR防地质灾害安全教育系统是一种虚拟仿真技术,可以通过虚拟现实技术模拟地震、泥石流、滑坡等地质灾害的发生和应对过程,帮助人们提高应对突发自然灾害的能力。这种系统的优势在于可以增强自然灾害知识,提高自我保护意识,锻炼人们…

MyBatis分页插件PageHelper的使用及特殊字符的处理

目录 一、PageHelper简介 1.什么是分页 2.PageHelper是什么 3.使用PageHelper的优点 二、PageHelper插件的使用 原生limit查询 1. 导入pom依赖 2. Mybatis.cfg.xml 配置拦截器 3. 使用PageHelper进行分页 三、特殊字符的处理 1.SQL注入: 2.XML转义&#…

一、Kafka概述

目录 1.1 定义1.2 消息队列1、传统消息队列的应用场景2、消息队列的两种模式 1.3 Kafka的基础架构 1.1 定义 Kafka传 统定义:Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。 K…

ACL2023 Prompt 相关文章速通 Part 1

Accepted Papers link: ACL2023 main conference accepted papers 文章目录 Accepted PapersPrompter: Zero-shot Adaptive Prefixes for Dialogue State Tracking Domain AdaptationQuery Refinement Prompts for Closed-Book Long-Form QAPrompting Language Models for Lin…

【Redis】什么是缓存击穿,如何预防缓存击穿?

【Redis】什么是缓存击穿,如何预防缓存击穿? 缓存击穿是指一个 Key 非常热点,大并发集中对这一个点进行访问,当这个Key 在失效的瞬间,持续的大并发就会穿破缓存,直接请求数据库。缓存击穿和缓存雪崩的区别…

基于FPGA视频接口之HDMI2.0编/解码

简介 为什么要特别说明HDMI的版本,是因为HDMI的版本众多,代表的HDMI速度同样不同,当前版本在HDMI2.1速度达到48Gbps,可以传输4K及以上图像,但我们当前还停留在1080P@60部分,且使用的芯片和硬件结构有很大差别,故将HDMI分为两个部分说明1080@60以下分辨率和4K以上分辨率(…

【WebSocket】前端使用WebSocket实时通信

目录 前言什么是WebSocketWebSocket的工作原理WebSocket与HTTP的关系HTTP建立持久化连接WebSocket类封装 前言 最近写项目,需要实现消息通知和实时聊天的功能,就去了解了一些关于websocket的知识,总结如下。 什么是WebSocket WebSocket 是一…

vscode C++17便捷配置教程(懒人版)

环境链接 以上是已经配置好的c17环境链接,直接下载解压即可(注意文件路径上不要带有中文) 下载解压之后按照msys64-mingw64-bin路径打开 然后单击该路径右方空白区域可直接复制路径 然后点击开始菜单搜索“环境变量“并打开(如…

SQL阶段性优化

😜作 者:是江迪呀✒️本文关键词:MySQL、SQL优化、阶段性优化☀️每日 一言:我们要把懦弱扼杀在摇篮中。 一、前言 我们在做系统的过程中,难免会遇到页面查询速度慢,性能差的问题,…

数据结构基础:P3-树(上)----编程作业02:List Leaves

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,系列文章链接如下: 数据结构(陈越、何钦铭)学习笔记 文章目录 一、题目描述二、整体思路与实现代码 一、题目描述 题目描述: 给定一棵树,按照从上到下、从左到右的顺序列出所有…

Compressor For Mac强大视频编辑工具 v4.6.5中文版

Compressor for Mac是苹果公司推出的一款视频压缩工具,可以将高清视频、4K视频、甚至是8K视频压缩成适合网络传输或存储的小文件。Compressor支持多种视频格式,包括H.264、HEVC、ProRes和AVC-Intra等,用户可以根据需要选择不同的压缩格式。 …

ModaHub魔搭社区:WinPlan经营大脑预算编制

目录 WinPlan经营大脑预算编制介绍 WinPlan经营大脑预算编制模版 WinPlan经营大脑预算模版管理 WinPlan经营大脑预算数据录入 WinPlan经营大脑预算编制介绍 预算编制时面向企业经营管理场景,创建各个业务单位的目标,包括销售目标、财务目标、人事目标等,实现各个业务单…

华为OD机试 - 最佳植树距离 - 二分查找(Java 2023 B卷 100分)

目录 一、题目描述二、输入描述三、输出描述四、备注说明五、二分查找六、解题思路七、Java算法源码八、效果展示1、输入2、输出3、说明 一、题目描述 按照环保公司要求,小明需要在沙化严重的地区进行植树防沙工作,初步目标是种植一条直线的树带。 由于…