使用Bert+BiLSTM+CRF训练 NER任务

news2024/10/25 10:44:02

使用的数据集在这里E-Commercial NER Dataset / 电商NER数据集_数据集-阿里云天池

针对面向电商的命名实体识别研究,我们通过爬取搜集了淘宝商品文本的标题,并标注了4大类,9小类的实体类别。具体类型及实体数量如下

针对面向电商的命名实体识别研究,我们通过爬取搜集了淘宝商品文本的标题,并标注了4大类,9小类的实体类别。具体类型及实体数量如下:
 

enter image description here

每个文件数据格式相同,都为根据BI schema标注的NER数据

模型使用BERT + BiLSTM + CRF 该结构通过结合强大的预训练语言表示、序列建模能力和标签依赖建模,能够有效地提升命名实体识别任务的准确性和鲁棒性。这种组合在实际应用中已经显示出优越的性能,尤其是在复杂的语言理解任务中。

BERT + BiLSTM + CRF 是一种常用于命名实体识别(NER)任务的深度学习模型结构。下面是对这个模型结构的详细解释,以及它在 NER 任务中的好处。

模型结构

BERT(Bidirectional Encoder Representations from Transformers)
  • 预训练模型:BERT 是一种基于 Transformer 的预训练语言模型,能够捕捉到上下文信息。它通过双向编码器考虑上下文中的所有单词,从而生成每个单词的上下文嵌入。
  • 词向量表示:在 NER 任务中,BERT 可以生成高质量的词嵌入,帮助模型更好地理解文本中的语义。
BiLSTM(Bidirectional Long Short-Term Memory)
  • 序列建模:BiLSTM 是一种改进的 LSTM(长短期记忆网络),能够同时考虑序列的前向和后向信息。通过双向处理,BiLSTM 可以更好地捕捉上下文依赖关系。
  • 特征提取:在 NER 任务中,BiLSTM 能够从 BERT 输出的嵌入中提取序列特征,并对它们进行时间序列建模。
CRF(Conditional Random Field)
  • 序列标注:CRF 是一种用于序列标注的概率模型,可以考虑标签之间的依赖关系。它在输出层对 BiLSTM 的结果进行后处理,以确保输出标签满足特定的序列约束。
  • 提高准确性:CRF 能够通过学习标签之间的关系(例如,实体标签之间的相互影响)来增强模型的预测能力。

应用在 NER 任务中的好处

1. 上下文理解
  • BERT 的强大能力:BERT 提供丰富的上下文信息,能够更好地理解文本中的语义,从而提高对实体的识别能力。
2. 序列建模
  • BiLSTM 的双向特性:通过双向处理,BiLSTM 能够捕捉到前后文的依赖关系,改进对实体边界的识别。
3. 标签依赖建模
  • CRF 的序列约束:CRF 考虑了输出标签的依赖关系,能够避免产生不合理的标签序列(例如,"B-PER" 后面直接跟 "B-LOC")。
4. 提高性能
  • 综合优势:结合 BERT、BiLSTM 和 CRF 的优点,可以显著提高 NER 任务的性能,尤其是在处理复杂的实体关系和多样的语境时。

 模型model.py内容

# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
from transformers import BertModel
from TorchCRF import CRF


class Bert_BiLSTM_CRF(nn.Module):

    def __init__(self, tag_to_ix, embedding_dim=768, hidden_dim=256):
        super(Bert_BiLSTM_CRF, self).__init__()
        self.tag_to_ix = tag_to_ix
        self.tagset_size = len(tag_to_ix)
        self.hidden_dim = hidden_dim
        self.embedding_dim = embedding_dim

        self.bert = BertModel.from_pretrained('bert-base-chinese')
        self.lstm = nn.LSTM(input_size=embedding_dim,
                            hidden_size=hidden_dim // 2,
                            num_layers=2,
                            bidirectional=True,
                            batch_first=True)
        self.dropout = nn.Dropout(p=0.1)
        self.linear = nn.Linear(hidden_dim, self.tagset_size)
        self.crf = CRF(self.tagset_size)

    def _get_features(self, sentence):
        with torch.no_grad():
            outputs = self.bert(sentence)
        embeds = outputs.last_hidden_state
        enc, _ = self.lstm(embeds)
        enc = self.dropout(enc)
        feats = self.linear(enc)
        return feats

    def forward(self, sentence, tags, mask, is_test=False):
        emissions = self._get_features(sentence)
        if not is_test:  # 训练阶段,返回loss
            loss = -self.crf(emissions, tags, mask)
            return loss.mean()
        else:
            return self.crf.viterbi_decode(emissions, mask)



统计下数据集中出现的多少种类型的标注

# 统计标签类型的Python脚本

def count_labels(file_path):
    labels = set()  # 使用集合来存储唯一标签
    with open(file_path, 'r', encoding='utf-8') as file:
        for line in file:
            parts = line.strip().split()  # 按空格分割每一行
            if len(parts) > 1:  # 确保这一行有标签
                label = parts[-1]  # 标签通常在最后一列
                labels.add(label)  # 将标签添加到集合中
    return labels

# 指定文件路径
file_path = 'dataset/train.txt'
unique_labels = count_labels(file_path)

# 输出结果
print(f"总共有 {len(unique_labels)} 种类型的标签:")
for label in unique_labels:
    print(label)

该数据集里共出现了 9 种类型的标签:
 

I-HCCX
I-HPPX
B-HCCX
B-XH
I-XH
I-MISC
O
B-HPPX
B-MISC

标注遵循了一种常见的命名实体识别标注方案:

  • B- 表示实体的开始。
  • I- 表示实体的内部部分。
  • O 表示非实体部分。
  • 实体类别(如 HCCX、HPPX、XH、MISC)根据具体任务和数据集定义。例如HCCX为产品类型,HPPX是品牌名称,XH 是产品型号

下面开始编写训练需要工具

# -*- coding: utf-8 -*-
'''
@Author: Chenrui
@Date: 2021-9-30
@Description: This file is for implementing Dataset. 
@All Right Reserve
'''

import torch
from torch.utils.data import Dataset
from transformers import BertTokenizer

bert_model = 'bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(bert_model)
'''
<PAD>: 用于填充序列,确保输入长度一致。
[CLS]: 表示序列的开始,常用于分类任务。
[SEP]: 用于分隔不同的输入序列,帮助模型理解输入的结构。
'''
labels = ['B-HCCX',
              'I-HPPX',
              'I-HCCX',
              'I-MISC',
              'B-XH',
              'O',
              'I-XH',
              'B-MISC',
              'B-HPPX']

VOCAB = ('<PAD>', '[CLS]', '[SEP]')

# 将 VOCAB 转换为列表以便修改
vocab_list = list(VOCAB)

# 添加 labels 到 vocab_list
vocab_list.extend(labels)

# 将列表转换回元组
VOCAB = tuple(vocab_list)

tag2idx = {tag: idx for idx, tag in enumerate(VOCAB)}
idx2tag = {idx: tag for idx, tag in enumerate(VOCAB)}
MAX_LEN = 256 - 2


class NerDataset(Dataset):
    ''' Generate our dataset '''

    def __init__(self, f_path):
        self.sents = []
        self.tags_li = []

        with open(f_path, 'r', encoding='utf-8') as f:
            lines = [line.split('\n')[0] for line in f.readlines() if len(line.strip()) != 0]

        tags = [line.split(' ')[1] for line in lines]
        words = [line.split(' ')[0] for line in lines]

        word, tag = [], []
        for char, t in zip(words, tags):
            if char != '。':
                word.append(char)
                tag.append(t)
            else:
                if len(word) > MAX_LEN:
                    self.sents.append(['[CLS]'] + word[:MAX_LEN] + ['[SEP]'])
                    self.tags_li.append(['[CLS]'] + tag[:MAX_LEN] + ['[SEP]'])
                else:
                    self.sents.append(['[CLS]'] + word + ['[SEP]'])
                    self.tags_li.append(['[CLS]'] + tag + ['[SEP]'])
                word, tag = [], []

    def __getitem__(self, idx):
        words, tags = self.sents[idx], self.tags_li[idx]
        token_ids = tokenizer.convert_tokens_to_ids(words)
        label_ids = [tag2idx[tag] for tag in tags]
        seqlen = len(label_ids)
        return token_ids, label_ids, seqlen

    def __len__(self):
        return len(self.sents)


def PadBatch(batch):
    maxlen = 512
    token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])
    label_tensors = torch.LongTensor([i[1] + [0] * (maxlen - len(i[1])) for i in batch])
    mask = (token_tensors > 0)
    return token_tensors, label_tensors, mask

训练的主程序

# -*- coding: utf-8 -*-
'''
@Author: Chenrui
@Date: 2023-9-30
@Description: This file is for training, validating and testing.
@All Right Reserve
'''

import argparse
import warnings

import numpy as np
import torch
from sklearn import metrics
from torch.utils import data
from transformers import AdamW, get_linear_schedule_with_warmup

from models import Bert_BiLSTM_CRF
from utils import NerDataset, PadBatch, tag2idx, idx2tag, labels

warnings.filterwarnings("ignore", category=DeprecationWarning)
# os.environ['CUDA_VISIBLE_DEVICES'] = '0'





if __name__ == "__main__":

    best_model = None
    _best_val_loss = 1e18
    _best_val_acc = 1e-18

    parser = argparse.ArgumentParser()
    parser.add_argument("--batch_size", type=int, default=64)
    parser.add_argument("--lr", type=float, default=0.001)
    parser.add_argument("--n_epochs", type=int, default=40)
    parser.add_argument("--trainset", type=str, default="./dataset/train.txt")
    parser.add_argument("--validset", type=str, default="./dataset/dev.txt")
    parser.add_argument("--testset", type=str, default="./dataset/test.txt")

    ner = parser.parse_args()
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model = Bert_BiLSTM_CRF(tag2idx).to(device)

    print('Initial model Done.')
    train_dataset = NerDataset(ner.trainset)
    eval_dataset = NerDataset(ner.validset)
    test_dataset = NerDataset(ner.testset)
    print('Load Data Done.')
    '''
    torch.utils.data.DataLoader 是 PyTorch 中用于加载数据的一个强大工具。它的主要作用是将数据集分成小批量(batches),
    并提供迭代器以便在训练模型时方便地访问数据。以下是 DataLoader 的一些主要功能和使用方法。
    '''
    train_iter = data.DataLoader(dataset=train_dataset,
                                 batch_size=ner.batch_size,
                                 shuffle=True,
                                 num_workers=4,
                                 collate_fn=PadBatch)

    eval_iter = data.DataLoader(dataset=eval_dataset,
                                batch_size=(ner.batch_size) // 2,
                                shuffle=False,
                                num_workers=4,
                                collate_fn=PadBatch)

    test_iter = data.DataLoader(dataset=test_dataset,
                                batch_size=(ner.batch_size) // 2,
                                shuffle=False,
                                num_workers=4,
                                collate_fn=PadBatch)

    # optimizer = optim.Adam(self.model.parameters(), lr=ner.lr, weight_decay=0.01)
    optimizer = AdamW(model.parameters(), lr=ner.lr, eps=1e-6)

    # Warmup
    len_dataset = len(train_dataset)
    epoch = ner.n_epochs
    batch_size = ner.batch_size
    total_steps = (len_dataset // batch_size) * epoch if len_dataset % batch_size == 0 \
        else (len_dataset // batch_size + 1) * epoch

    warm_up_ratio = 0.1  # Define 10% steps

    '''
    get_linear_schedule_with_warmup 是 Hugging Face 的 transformers 库中提供的一个函数,用于创建一个学习率调度器。
    它的主要作用是在训练过程中动态调整学习率,具体通过以下几个步骤实现:
    主要功能
    学习率预热(Warmup):
    在训练的初始阶段,学习率从 0 逐渐增加到设定的最大学习率。这有助于防止模型在训练初期出现较大的更新,从而导致不稳定。
    线性衰减(Linear Decay):
    在预热阶段结束后,学习率会线性地递减到 0。这种衰减方式通常能有效地提高模型的收敛性。
    '''
    scheduler = get_linear_schedule_with_warmup(optimizer=optimizer,
                                                num_warmup_steps=warm_up_ratio * total_steps,
                                                num_training_steps=total_steps)

    print('Start Train...,')
    for epoch in range(1, ner.n_epochs + 1):

        train(epoch, model, train_iter, optimizer, scheduler, device)
        candidate_model, loss, acc = validate(epoch, model, eval_iter, device)

        if loss < _best_val_loss and acc > _best_val_acc:
            best_model = candidate_model
            _best_val_loss = loss
            _best_val_acc = acc

        print("=============================================")

    y_test, y_pred = test(best_model, test_iter, device)
    print(metrics.classification_report(y_test, y_pred, labels=labels, digits=3))

    # 保存模型
    torch.save(model.state_dict(), 'ner_task_model.pth')

导入部分工具的用途

  • argparse:用于解析命令行参数。
  • warnings:用于控制警告信息。
  • numpytorch:用于数值计算和深度学习。
  • sklearn.metrics:用于评估模型性能。
  • transformers:提供 AdamW 优化器和学习率调度器

train训练函数,功能训练模型并输出损失。

def train(e, model, iterator, optimizer, scheduler, device):
    model.train()
    losses = 0.0
    step = 0
    for i, batch in enumerate(iterator):
        step += 1
        x, y, z = batch
        x = x.to(device)
        y = y.to(device)
        z = z.to(device)

        loss = model(x, y, z)
        losses += loss.item()
        loss.backward()
        optimizer.step()
        scheduler.step()
        optimizer.zero_grad()

    print("Epoch: {}, Loss:{:.4f}".format(e, losses / step))
  • e: 当前的训练轮数。
  • model: NER 模型。
  • iterator: 数据加载器。
  • optimizer: 优化器。
  • scheduler: 学习率调度器。
  • device: 设备(CPU 或 GPU)

validate函数,验证集上评估模型性能,计算损失和准确率,并返回模型、损失和准确率。

def validate(e, model, iterator, device):
    # 设置模型为评估模式
    model.eval()
    Y, Y_hat = [], []
    losses = 0
    step = 0
    with torch.no_grad():
        for i, batch in enumerate(iterator):
            step += 1

            x, y, z = batch
            x = x.to(device)
            y = y.to(device)
            z = z.to(device)

            y_hat = model(x, y, z, is_test=True)

            loss = model(x, y, z)
            losses += loss.item()

            for j in y_hat:
                Y_hat.extend(j)
            mask = (z == 1)
            y_orig = torch.masked_select(y, mask)
            Y.append(y_orig.cpu())

    Y = torch.cat(Y, dim=0).numpy()
    Y_hat = np.array(Y_hat)
    acc = (Y_hat == Y).mean() * 100

    print("Epoch: {}, Val Loss:{:.4f}, Val Acc:{:.3f}%".format(e, losses / step, acc))
    return model, losses / step, acc

test函数,在测试集上评估模型性能,返回真实标签和预测标签

def test(model, iterator, device):
    model.eval()
    Y, Y_hat = [], []
    with torch.no_grad():
        for i, batch in enumerate(iterator):
            x, y, z = batch
            x = x.to(device)
            z = z.to(device)
            y_hat = model(x, y, z, is_test=True)
            # Save prediction
            for j in y_hat:
                Y_hat.extend(j)
            # Save labels
            mask = (z == 1).cpu()
            y_orig = torch.masked_select(y, mask)
            Y.append(y_orig)

    Y = torch.cat(Y, dim=0).numpy()
    y_true = [idx2tag[i] for i in Y]
    y_pred = [idx2tag[i] for i in Y_hat]

    return y_true, y_pred

上面代码实现了一个完整的 NER 训练、验证和测试流程,使用了 BERT、BiLSTM 和 CRF 模型结构。通过 argparse 解析输入参数,使用 PyTorch 处理数据和模型训练,使用 DataLoader 创建迭代器以便于批量处理数据,置 AdamW 优化器和学习率调度器,进行多个训练轮次,每个轮次调用训练和验证函数,最终评估模型性能并保存最佳模型。这是一个典型的深度学习项目框架,适用于序列标注任务。

运行训练

python main.py --n_epochs 1000

查看到准确率稍低, 先不管了把模型文件下载下来本地先试试识别提取能力

下载保存的模型文件

测试该模型代码

import os

import torch
from torch.utils.data import Dataset

from models import Bert_BiLSTM_CRF
from utils import tokenizer, tag2idx, MAX_LEN, idx2tag

device = 'cuda' if torch.cuda.is_available() else 'cpu'


class SequenceLabelingDataset(Dataset):
    def __init__(self, lines):
        self.lines = lines
        self.sents, word = [], []

        words = [line[0] for line in lines]
        for char in words:
            if char != '。':
                word.append(char)

            else:
                if len(word) > MAX_LEN:
                    self.sents.append(['[CLS]'] + word[:MAX_LEN] + ['[SEP]'])
                else:
                    self.sents.append(['[CLS]'] + word + ['[SEP]'])
                word = []

    def __len__(self):
        return len(self.sents)

    def item(self):
        words = self.sents[0]
        token_ids = tokenizer.convert_tokens_to_ids(words)
        seqlen = len(token_ids)
        return token_ids, None, seqlen

    def get_token_ids(self, batch):
        maxlen = 512
        token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])
        label_tensors = None
        mask = (token_tensors > 0)
        return token_tensors, label_tensors, mask


# 示例数据
data_string = '2005复刻正版戏曲光盘1dvd视频碟片河南曲剧优秀剧目集锦。'


print('data_string', data_string)
data = [(char,) for char in data_string]
print('data', data)

# 创建数据集和 DataLoader
dataset = SequenceLabelingDataset(data)

current_directory = os.getcwd()
model_path = os.path.join(current_directory, 'model', 'ner_task_model.pth')

model = Bert_BiLSTM_CRF(tag2idx).to(device)
model.load_state_dict(torch.load(model_path, map_location=device))
model.eval()


def predict(model, batch, device):
    x, y, z = batch[0]

    maxlen = 512
    token_tensors = torch.LongTensor([i[0] + [0] * (maxlen - len(i[0])) for i in batch])
    mask = (token_tensors > 0)

    predict = model(token_tensors, None, mask, is_test=True)  # 使用生成的 mask
    return predict


try:
    batch = [dataset.item()]
    predict = predict(model, batch, device)
    print('predict', predict)
    label_ids = [idx2tag[tag] for tag in predict[0]]
    print('label_ids', label_ids)
except Exception as e:
    print('predict throw exception', e)

# 提取实体
entities = {
    'HCCX': [],
    'HPPX': [],
    'MISC': [],
    'XH': [],
    'O': []
}

# 当前实体存储
current_entities = {
    'HCCX': [],
    'HPPX': [],
    'MISC': [],
    'XH': [],
    'O': []
}

# 按照标签提取实体
for i, label in enumerate(label_ids):
    word = data_string[i] if i < len(data_string) else ''

    if 'B-' in label:
        entity_type = label[2:]  # 获取实体类型
        current_entities[entity_type].append(word)
    elif 'I-' in label:
        entity_type = label[2:]  # 获取实体类型
        if current_entities[entity_type]:  # 确保当前实体存在
            current_entities[entity_type].append(word)
    else:
        # 处理当前实体
        for entity_type in current_entities:
            if current_entities[entity_type]:
                entities[entity_type].append("".join(current_entities[entity_type]))
                current_entities[entity_type] = []

# 处理最后一个实体
for entity_type in current_entities:
    if current_entities[entity_type]:
        entities[entity_type].append("".join(current_entities[entity_type]))

# 输出结果
for entity_type, entity_list in entities.items():
    print(f"提取的{entity_type}实体:", entity_list)

返回结果

实体识别出来一些

现在NER任务基本都是大模型去做,相比大语言模型小模型越来越势微越来越不受重视,前公司在知识图谱上搞得globalpointer 任务做NER得产品已经彻底放弃,以至于我们还有些用该产品得客户已经无法得到支持了,但作为NER 入门得例子还是值得拿出来写写

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

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

相关文章

解决:如何在opencv中得到与matlab立体标定一样的矫正图?(python版opencv)

目的&#xff1a;采用一样的标定参数&#xff0c;matlab中和opencv中的立体矫正图像是一样的吗&#xff1f;不一样的话怎么让它们一样&#xff1f; 结论&#xff1a;不一样。后文为解决方案。 原因&#xff1a;注意matlab的标定结果在matlab中的用法和在opencv中的用法不一样&a…

OpenCv-01

使用opencv对图像进行一些简单的处理 首先知道自己的工作目录 import os cwdos.getcwd() 命名一张图片 my_imagelenna.png 获得图片路径 image_pathos.path.join(cwd,my_image) import cv2 imagecv2.imread(my_image) #imread()函数将图片转换为ndarray数组 image.sh…

大数据新视界 --大数据大厂之 Snowflake 在大数据云存储和处理中的应用探索

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

探索华为云DataArts Insight:数据智能的新引擎

在快速发展的数字化时代&#xff0c;数据已经成为企业最宝贵的资产。如何有效地管理和利用这些数据&#xff0c;以实现商业价值&#xff0c;是每个企业需要面对的重要挑战。华为云DataArts Insight平台应运而生&#xff0c;作为一款强大的数据智能解决方案&#xff0c;它帮助企…

STANFORD SR570 斯坦福 SR570 前置放大器

斯坦福&#xff08;Stanford&#xff09;STANFORD SR570前置放大器&#xff0c;作为音频与电子测量领域的璀璨明珠&#xff0c;以其无与伦比的性能与精湛的工艺&#xff0c;赢得了全球专业人士的广泛赞誉。这款前置放大器不仅是技术的集大成者&#xff0c;更是艺术与科学的完美…

论文学习 | 《电动汽车锂离子电池健康状态估计及寿命预测方法研究》

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要对上述论文《电动汽车锂离子电池健康状态估计及寿命预测方法研究》进行学习与阅读总结&#xff0c;便于后续科研进一步学习&#xff08;纯小白自读汇总版&#xff09;。总体感觉这篇有点偏向数理方面&#xff0c;不过后…

【动手学深度学习】8.2. 文本预处理(个人向笔记)

本节将解析文本的常见预处理步骤包括&#xff1a;将文本作为字符串加载到内存中。将字符串拆分为词元&#xff08;如单词和字符&#xff09;。建立一个词表&#xff0c;将拆分的词元映射到数字索引。将文本转换为数字索引序列&#xff0c;方便模型操作。 1. 读取数据集 我们下…

leetcode-73-矩阵置零

题解&#xff1a; 1、获取矩阵的行数M与列数N&#xff1b; 2、 代码实现&#xff1a;

yolo目标检测和姿态识别和目标追踪

要检测摄像头画面中有多少人&#xff0c;人一排排坐着&#xff0c;像教室那样。由于摄像头高度和角度的原因&#xff0c;有的人会被遮挡。 yolo v5 首先需要下载yolo v5官方代码&#xff0c;可以克隆或下载主分支的代码&#xff0c;或者下载release中发布的。 简单说一下环境…

【python实战】利用代理ip爬取Alibaba海外版数据

引言 在跨境电商的业务场景中&#xff0c;数据采集是分析市场、了解竞争对手以及优化经营策略的重要环节。然而&#xff0c;随着越来越多企业依赖数据驱动决策&#xff0c;许多跨境电商平台为了保护自身数据&#xff0c;采取了更严格的防护措施。这些平台通过屏蔽大陆IP地址或部…

Idea、VS Code 如何安装Fitten Code插件使用

简介 Fitten Code是由非十大模型驱动的AI编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试Bug&#xff0c;节省您的时间。还可以对话聊天&#xff0c;解决您编程碰到的问题。免费且支持80多种语言&#xff1a;Python、C、Javascript、Typ…

python实战(一)——iris鸢尾花数据集分类

一、任务背景 本文是python实战系列专栏的第一篇文章&#xff0c;我们将从分类开始由浅入深逐步学习如何使用python完成常规的机器学习/深度学习任务。iris数据集是经典的机器学习入门数据集&#xff0c;许多分类任务教程都会以这个数据集作为示例&#xff0c;它的数据量是150条…

No.21 笔记 | WEB安全 - 任意文件绕过详解 part 3

&#xff08;一&#xff09;空格绕过 原理 Windows系统将文件名中的空格视为空&#xff0c;但程序检测代码无法自动删除空格&#xff0c;使攻击者可借此绕过黑名单限制。基于黑名单验证的代码分析 代码未对上传文件的文件名进行去空格处理&#xff0c;存在安全隐患。相关代码逻…

【软考高级架构】关于分布式数据库缓存redis的知识要点汇总

一.分布式数据库的含义 分布式数据库缓存指的是在高并发的环境下&#xff0c;为了减轻数据库的压力和提高系统响应时间&#xff0c;在数据库系统和应用系统之间增加一个独立缓存系统。 二.常见的缓存技术 &#xff08;1&#xff09;MemCache: Memcache是一个高性能的分布式的内…

openlayers 封装加载本地geojson数据 - vue3

Geojson数据是矢量数据&#xff0c;主要是点、线、面数据集合 Geojson数据获取&#xff1a;DataV.GeoAtlas地理小工具系列 实现代码如下&#xff1a; import {ref,toRaw} from vue; import { Vector as VectorLayer } from ol/layer.js; import { Vector as VectorSource } fr…

html全局属性、框架标签

常用的全局属性&#xff1a; 属性名含义id 给标签指定唯一标识&#xff0c;注意&#xff1a;id是不能重复的。 作用&#xff1a;可以让label标签与表单控件相关联&#xff1b;也可以与css、JavaScript配合使用。 注意&#xff1a;不能再以下HTML元素中使用&#xff1a;<hea…

Unity3D学习FPS游戏(4)重力模拟和角色跳跃

前言&#xff1a;前面两篇文章&#xff0c;已经实现了角色的移动和视角转动&#xff0c;但是角色并没有办法跳跃&#xff0c;有时候还会随着视角移动跑到天上。这是因为缺少重力系统&#xff0c;本篇将实现重力和角色跳跃功能。觉得有帮助的话可以点赞收藏支持一下&#xff01;…

社区养老实训室解决方案

一、实训室建设理念与目标 1.1 培养高质量养老专业人才 随着人口老龄化的不断加剧&#xff0c;对养老专业人才的需求呈现出日益增长的趋势。社区养老实训室的建设理念&#xff0c;正是基于这一背景&#xff0c;致力于培养一支既具备专业技能又拥有综合服务能力的高质量养老人…

gitlab不同账号间·仓库转移

背景&#xff1a;公司业务调整&#xff0c;原先在海外仓库的代码转移回国内 诉求&#xff1a;完整的保留项目记录 操作&#xff1a; 步骤一: 定位到需要迁移的原项目地址 步骤二&#xff1a;创建新项目 步骤三&#xff1a;打开命令行&#xff0c;创建好文件路径为需要clo…

Anchor DETR论文笔记

原文链接 [2109.07107] Anchor DETR: Query Design for Transformer-Based Object Detection (arxiv.org)https://arxiv.org/abs/2109.07107 原文笔记 What 提出了一种新的基于锚点的查询设计&#xff0c;即将锚点编码为对象查询。 Why 对象检测任务是预测图像中每个对象…