【自然语言处理(NLP)】基于BiLSTM+CRF的事件抽取

news2025/2/28 15:51:48

【自然语言处理(NLP)】基于BiLSTM+CRF的事件抽取


在这里插入图片描述


作者简介:在校大学生一枚,华为云享专家,阿里云专家博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC)志愿者,以及编程爱好者,期待和大家一起学习,一起进步~
.
博客主页ぃ灵彧が的学习日志
.
本文专栏人工智能
.
专栏寄语:若你决定灿烂,山无遮,海无拦
.
在这里插入图片描述

文章目录

  • 【自然语言处理(NLP)】基于BiLSTM+CRF的事件抽取
  • 前言
    • (一)、任务描述
    • (二)、环境配置
  • 一、数据准备
    • (一)、导入相关包
    • (二)、数据集加载
  • 二、定义LSTM模型
  • 三、定义训练框架
      • 评价指标
  • 四、模型训练及预测1
  • 五、模型训练及预测2
  • 总结


前言

(一)、任务描述

事件抽取技术是从非结构化信息中抽取出用户感兴趣的事件,并以结构化呈现给用户。

事件抽取任务可分解为4个子任务:触发词识别、事件类型分类、论元识别和角色分类任务,其中,触发词识别和事件类型分类可合并成事件识别任务。


(二)、环境配置

本示例使用BiLSTM实现两个子任务中的分类,代码运行的环境配置如下:Python版本为3.7,PaddlePaddle版本为2.0.0,操作平台为AI Studio。

import paddle
import numpy as np
import matplotlib.pyplot as plt
print(paddle.__version__)

输出结果如下图1所示:
在这里插入图片描述


一、数据准备


(一)、导入相关包

import os
import json
import paddle.fluid as fluid
import ast
import hashlib
import warnings
import argparse
from functools import partial
import numpy as np
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
from paddlenlp.data import Stack, Tuple, Pad
from paddlenlp.transformers import ErnieTokenizer, ErnieForTokenClassification, LinearDecayWithWarmup
from paddlenlp.metrics import ChunkEvaluator
warnings.filterwarnings('ignore')

(二)、数据集加载

''' ------------------  1.数据预处理及加载  -------------------'''
def cal_md5(str):
    """calculate string md5"""
    str = str.decode("utf-8", "ignore").encode("utf-8", "ignore")
    return hashlib.md5(str).hexdigest()


def read_by_lines(path):
    """read the data by line"""
    result = list()
    with open(path, "r") as infile:
        for line in infile:
            result.append(line.strip())
    return result


def write_by_lines(path, data):
    """write the data"""
    with open(path, "w") as outfile:
        [outfile.write(d + "\n") for d in data]


def text_to_sents(text):
    """text_to_sents"""
    deliniter_symbols = [u"。", u"?", u"!"]
    paragraphs = text.split("\n")
    ret = []
    for para in paragraphs:
        if para == u"":
            continue
        sents = [u""]
        for s in para:
            sents[-1] += s
            if s in deliniter_symbols:
                sents.append(u"")
        if sents[-1] == u"":
            sents = sents[:-1]
        ret.extend(sents)
    return ret


def load_dict(dict_path):
    """load_dict"""
    vocab = {}
    for line in open(dict_path, 'r', encoding='utf-8'):
        value, key = line.strip('\n').split('\t')
        vocab[key] = int(value)
    return vocab


def extract_result(text, labels):
    """extract_result"""
    ret, is_start, cur_type = [], False, None
    if len(text) != len(labels):
        labels = labels[:len(text)]
    for i, label in enumerate(labels):
        if label != u"O":
            _type = label[2:]
            if label.startswith(u"B-"):
                is_start = True
                cur_type = _type
                ret.append({"start": i, "text": [text[i]], "type": _type})
            elif _type != cur_type:
                """
                # 如果是没有B-开头的,则不要这部分数据
                cur_type = None
                is_start = False
                """
                cur_type = _type
                is_start = True
                ret.append({"start": i, "text": [text[i]], "type": _type})
            elif is_start:
                ret[-1]["text"].append(text[i])
            else:
                cur_type = None
                is_start = False
        else:
            cur_type = None
            is_start = False
    return ret


def data_process(path, model="trigger", is_predict=False):
    """data_process"""

    def label_data(data, start, l, _type):
        """label_data"""
        for i in range(start, start + l):
            suffix = "B-" if i == start else "I-"
            data[i] = "{}{}".format(suffix, _type)
        return data

    sentences = []
    output = ["text_a"] if is_predict else ["text_a\tlabel"]
    with open(path) as f:
        for line in f:
            d_json = json.loads(line.strip())
            _id = d_json["id"]
            text_a = [
                "," if t == " " or t == "\n" or t == "\t" else t
                for t in list(d_json["text"].lower())
            ]
            if is_predict:
                sentences.append({"text": d_json["text"], "id": _id})
                output.append('\002'.join(text_a))
            else:
                if model == "trigger":
                    labels = ["O"] * len(text_a)
                    for event in d_json.get("event_list", []):
                        event_type = event["event_type"]
                        start = event["trigger_start_index"]
                        trigger = event["trigger"]
                        labels = label_data(labels, start,
                                            len(trigger), event_type)
                    output.append("{}\t{}".format('\002'.join(text_a),
                                                  '\002'.join(labels)))
                elif model == "role":
                    for event in d_json.get("event_list", []):
                        labels = ["O"] * len(text_a)
                        for arg in event["arguments"]:
                            role_type = arg["role"]
                            argument = arg["argument"]
                            start = arg["argument_start_index"]
                            labels = label_data(labels, start,
                                                len(argument), role_type)
                        output.append("{}\t{}".format('\002'.join(text_a),
                                                      '\002'.join(labels)))
    return output


def schema_process(path, model="trigger"):
    """schema_process"""

    def label_add(labels, _type):
        """label_add"""
        if "B-{}".format(_type) not in labels:
            labels.extend(["B-{}".format(_type), "I-{}".format(_type)])
        return labels

    labels = []
    for line in read_by_lines(path):
        d_json = json.loads(line.strip())
        if model == "trigger":
            labels = label_add(labels, d_json["event_type"])
        elif model == "role":
            for role in d_json["role_list"]:
                labels = label_add(labels, role["role"])
    labels.append("O")
    tags = []
    for index, label in enumerate(labels):
        tags.append("{}\t{}".format(index, label))
    return tags


def word2id_(lines,vocab,max_len=145):
    # 144 21 0.9505796670630202
    res = []
    lens = []
    for line in lines:
        r = []
        for c in line:
            if c not in vocab:
                r.append(vocab['<pad>'])
            else:
                r.append(vocab[c])
        r =r[:max_len]
        lens.append(len(r))
        r = r+[0]*(max_len-len(r))
        res.append(r)
    return r,lens



def get_vocab():
    train_lines = open('data/DuEE_1_0/train.json','r',encoding='utf-8').readlines()
    dev_lines = open('data/DuEE_1_0/dev.json','r',encoding='utf-8').readlines()
    lines = train_lines + dev_lines
    vocab = set()
    # dic = {}
    for line in lines:
        ll = json.loads(line.strip())
        for c in ll['text']:
            vocab.add(c) 
    vocab = {c:i+2 for i,c in enumerate(list(vocab))}
    vocab['<pad>'],vocab['<unk>']=0,1 
    
    return vocab


print("\n=================  DUEE 1.0 DATASET  ==============")
conf_dir = "./data/DuEE_1_0"
schema_path = "{}/event_schema.json".format(conf_dir)
tags_trigger_path = "{}/trigger_tag.dict".format(conf_dir)
tags_role_path = "{}/role_tag.dict".format(conf_dir)
print("\n=================start schema process==============")
print('input path {}'.format(schema_path))
tags_trigger = schema_process(schema_path, "trigger")
write_by_lines(tags_trigger_path, tags_trigger)
print("save trigger tag {} at {}".format(
    len(tags_trigger), tags_trigger_path))
tags_role = schema_process(schema_path, "role")
write_by_lines(tags_role_path, tags_role)
print("save trigger tag {} at {}".format(len(tags_role), tags_role_path))
print("=================end schema process===============")

# data process
data_dir = "./data/DuEE_1_0"
trigger_save_dir = "{}/trigger".format(data_dir)
role_save_dir = "{}/role".format(data_dir)
print("\n=================start schema process==============")
if not os.path.exists(trigger_save_dir):
    os.makedirs(trigger_save_dir)
if not os.path.exists(role_save_dir):
    os.makedirs(role_save_dir)
print("\n----trigger------for dir {} to {}".format(data_dir,
                                                   trigger_save_dir))
train_tri = data_process("{}/train.json".format(data_dir), "trigger")
write_by_lines("{}/train.tsv".format(trigger_save_dir), train_tri)
dev_tri = data_process("{}/dev.json".format(data_dir), "trigger")
write_by_lines("{}/dev.tsv".format(trigger_save_dir), dev_tri)
test_tri = data_process("{}/test.json".format(data_dir), "trigger")
write_by_lines("{}/test.tsv".format(trigger_save_dir), test_tri)
print("train {} dev {} test {}".format(
    len(train_tri), len(dev_tri), len(test_tri)))
print("\n----role------for dir {} to {}".format(data_dir, role_save_dir))
train_role = data_process("{}/train.json".format(data_dir), "role")
write_by_lines("{}/train.tsv".format(role_save_dir), train_role)
dev_role = data_process("{}/dev.json".format(data_dir), "role")
write_by_lines("{}/dev.tsv".format(role_save_dir), dev_role)
test_role = data_process("{}/test.json".format(data_dir), "role")
write_by_lines("{}/test.tsv".format(role_save_dir), test_role)
print("train {} dev {} test {}".format(
    len(train_role), len(dev_role), len(test_role)))
print("=================end schema process==============")
vocab = get_vocab()
vocab_size = len(list(vocab))
# print(vocab)
print(vocab_size)

输出结果如下图2所示:

在这里插入图片描述


二、定义LSTM模型

随着深度学习的发展,目前主流的序列化标注任务基于词向量(word embedding)进行表示学习。下面介绍模型的整体训练流程如下:

在这里插入图片描述


序列标注任务常用的模型是RNN+CRF。GRU和LSTM都是常用的RNN单元。这里我们以Bi-LSTM+CRF模型为例,介绍如何使用 PaddlePaddle 定义序列化标注任务的网络结构。如下图所示,LSTM的输出可以作为 CRF 的输入,最后 CRF 的输出作为模型整体的预测结果。

在这里插入图片描述


class LSTM_Model(nn.Layer):
    def __init__(self,vocab_num, emb_size, hidden_size, num_layers, num_labels, dropout):
        super(LSTM_Model, self).__init__()
        self.embedding = nn.Embedding(vocab_num, emb_size)
        self.lstm = nn.LSTM(emb_size, hidden_size, num_layers=num_layers, direction='bidirect', dropout=dropout)
        self.attention_linear = nn.Linear(hidden_size * 2, hidden_size)
        self.linear = nn.Linear(hidden_size * 2, num_labels)
        self.dropout = nn.Dropout(dropout)

    def forward(self,input_ids,target=None):
        token_emb = self.embedding(input_ids)
        sequence_output, (hidden, cell) = self.lstm(token_emb)  # [batch_size,time_steps,num_directions * hidden_size]
        sequence_output = self.dropout(sequence_output)
        logits = self.linear(sequence_output)

        # feature_out = fluid.layers.fc(input=hidden_1, size=len(label_dict), act='tanh')
        # 调用内置 CRF 函数并做状态转换解码.
        # if target is not None:
        #     crf_cost = fluid.layers.linear_chain_crf(
        #         input=paddle.reshape(logits,[-1,logits.shape[-1]]), label=paddle.reshape(target,[-1,1]),
        #         param_attr=fluid.ParamAttr(name='crfw1', learning_rate=0.0001))
        #     avg_cost = fluid.layers.mean(crf_cost)
        # else:
        #     avg_cost = 0
        avg_cost = 0
        return logits, avg_cost

三、定义训练框架

定义网络结构后,需要配置优化器、损失函数、评价指标。


评价指标

针对每条序列样本的预测结果,序列标注任务将预测结果按照语块(chunk)进行结合并进行评价。评价指标通常有 Precision、Recall 和 F1。

  1. Precision,精确率,也叫查准率,由模型预测正确的个数除以模型总的预测的个数得到,关注模型预测出来的结果准不准
  2. Recall,召回率,又叫查全率, 由模型预测正确的个数除以真实标签的个数得到,关注模型漏了哪些东西
  3. F1,综合评价指标,计算公式如下, F 1 = 2 ∗ P r e c i s i o n ∗ R e c a l l P r e c i s i o n + R e c a l l F1 = \frac{2*Precision*Recall}{Precision+Recall} F1=Precision+Recall2PrecisionRecall,同时考虑 Precision 和 Recall ,是 Precision 和 Recall 的折中。

paddlenlp.metrics中集成了ChunkEvaluator评价指标,并逐步丰富中,


# 定义训练框架
num_epoch = 10
learning_rate=0.0001 


tag_path = './data/DuEE_1_0/'
data_dir = './data/DuEE_1_0/trigger'
train_data = './data/DuEE_1_0/trigger/train.tsv'
dev_data = './data/DuEE_1_0/trigger/dev.tsv'
test_data = './data/DuEE_1_0/trigger/test.tsv'
predict_data = './data/DuEE_1_0/duee_test.json'
checkpoints = './data/DuEE_1_0/trigger/'
init_ckpt = './data/DuEE_1_0/trigger/best.pdparams'
weight_decay=0.01
warmup_proportion=0.1
max_seq_len=145
valid_step=100
skip_step=50
batch_size=32 
predict_save_path=None
seed=1000

@paddle.no_grad()
def evaluate(model, criterion, metric, num_label, data_loader):
    """evaluate"""
    model.eval()
    metric.reset()
    losses = []
    for input_ids, labels, seq_lens in data_loader:
        logits,_ = model(input_ids,labels)
        loss = paddle.mean(criterion(logits.reshape([-1, num_label]), labels.reshape([-1])))
        losses.append(loss.numpy())
        preds = paddle.argmax(logits, axis=-1)
        n_infer, n_label, n_correct = metric.compute(None, seq_lens, preds, labels) #  metric.compute(None, seq_lens, preds, labels)
        metric.update(n_infer.numpy(), n_label.numpy(), n_correct.numpy())
        precision, recall, f1_score = metric.accumulate()
    avg_loss = np.mean(losses)
    model.train()

    return precision, recall, f1_score, avg_loss

def word2id(line,vocab,max_len=145): 
    r = []
    for c in line:
        if c not in vocab:
            r.append(vocab['<unk>'])
        else:
            r.append(vocab[c])
    r =r[:max_len]
    lens = len(r)
    r = r+[0]*(max_len-len(r))
    # print('----------------')
    # print(line,r)
    return r,lens

def convert_example_to_feature(example, label_vocab=None, max_seq_len=145, no_entity_label="O",
                               ignore_label=-1, is_test=False):
    tokens, labels, seq_len = example 
    input_ids,seq_lens = word2id(tokens,vocab)

    if is_test:
        return input_ids,seq_lens
    elif label_vocab is not None:
        encoded_label = labels[:seq_lens] 
        encoded_label = [label_vocab[x] for x in encoded_label]
        encoded_label = encoded_label + [-1]*(max_seq_len-min(seq_lens,145) )
        # print('++++++++++++++++++++++++++++')
        # print(labels,encoded_label)
        # print('++++++++++++++++++++++++++++')
        return input_ids, encoded_label, seq_lens


class DuEventExtraction(paddle.io.Dataset):
    """DuEventExtraction"""
    def __init__(self, data_path, tag_path):
        self.label_vocab = load_dict(tag_path)
        self.word_ids = []
        self.label_ids = []
        self.seq_lens = []
        with open(data_path, 'r', encoding='utf-8') as fp:
            # skip the head line
            next(fp)
            for line in fp.readlines():
                words, labels = line.strip('\n').split('\t')
                words = words.split('\002')
                labels = labels.split('\002')
                self.word_ids.append(words)
                self.label_ids.append(labels)
                self.seq_lens.append(len(words[:145]))
        self.label_num = max(self.label_vocab.values()) + 1

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

    def __getitem__(self, index):
        return self.word_ids[index], self.label_ids[index], self.seq_lens[index]


def do_train():
    paddle.set_device('cpu') 
    no_entity_label = "O"
    ignore_label = -1
    label_map = load_dict(tag_path)
    id2label = {val: key for key, val in label_map.items()} 
    vocab_num, emb_size, hidden_size, num_layers, num_labels, dropout = \
        vocab_size,256,256,2,len(list(id2label)),0.1
        
    model = LSTM_Model(vocab_num, emb_size, hidden_size, num_layers, num_labels, dropout)
    
    print("============start train==========")
    train_ds = DuEventExtraction(train_data, tag_path)
    dev_ds = DuEventExtraction(dev_data, tag_path)
    test_ds = DuEventExtraction(test_data, tag_path)

    trans_func = partial(
        convert_example_to_feature, 
        label_vocab=train_ds.label_vocab,
        max_seq_len=max_seq_len,
        no_entity_label=no_entity_label,
        ignore_label=ignore_label,
        is_test=False)
    batchify_fn = lambda samples, fn=Tuple(
        Pad(axis=0, pad_val=0),  # input ids
        Pad(axis=0, pad_val=ignore_label),  # labels
        Stack()                # seq_lens
    ): fn(list(map(trans_func, samples)))

    batch_sampler = paddle.io.DistributedBatchSampler(train_ds, batch_size=batch_size, shuffle=True)
    train_loader = paddle.io.DataLoader(
        dataset=train_ds,
        batch_sampler=batch_sampler,
        collate_fn=batchify_fn)
    dev_loader = paddle.io.DataLoader(
        dataset=dev_ds,
        batch_size=batch_size,
        collate_fn=batchify_fn)
    test_loader = paddle.io.DataLoader(
        dataset=test_ds,
        batch_size=batch_size,
        collate_fn=batchify_fn)

    num_training_steps = len(train_loader) * num_epoch
    # Generate parameter names needed to perform weight decay.
    # All bias and LayerNorm parameters are excluded.
    decay_params = [
        p.name for n, p in model.named_parameters()
        if not any(nd in n for nd in ["bias", "norm"])
    ]
    optimizer = paddle.optimizer.AdamW(
        learning_rate=learning_rate,
        parameters=model.parameters(),
        weight_decay=weight_decay,
        apply_decay_param_fun=lambda x: x in decay_params)

    metric = ChunkEvaluator(label_list=train_ds.label_vocab.keys(), suffix=False)
    criterion = paddle.nn.loss.CrossEntropyLoss(ignore_index=ignore_label)
    # print(ignore_label)

    step, best_f1 = 0, 0.0
    model.train()
    for epoch in range(num_epoch):
        for idx, (input_ids,labels,seq_lens) in enumerate(train_loader):
            # print(input_ids[0],labels[0])
            logits,_= model(input_ids,labels)
            probs_ids = paddle.argmax(logits, -1).numpy()
            # print(logits.shape,labels.shape)
            logits = logits.reshape([-1, train_ds.label_num])
            loss = paddle.mean(criterion(logits, labels.reshape([-1])))
            loss.backward()
            optimizer.step()
            optimizer.clear_grad()
            loss_item = loss.numpy().item()
            if step > 0 and step % skip_step == 0: 
                # print(probs_ids )
                print(f'train epoch: {epoch} - step: {step} (total: {num_training_steps}) - loss: {loss_item:.6f}')
            if step > 0 and step % valid_step == 0:
                p, r, f1, avg_loss = evaluate(model, criterion, metric, len(label_map), dev_loader)
                print(f'dev step: {step} - loss: {avg_loss:.5f}, precision: {p:.5f}, recall: {r:.5f}, ' \
                      f'f1: {f1:.5f} current best {best_f1:.5f}')
                if f1 > best_f1:
                    best_f1 = f1
                    print(f'==============================================save best model ' \
                          f'best performerence {best_f1:5f}')
                    paddle.save(model.state_dict(), '{}/best.pdparams'.format(checkpoints))
            step += 1

    # save the final model
    paddle.save(model.state_dict(), '{}/final.pdparams'.format(checkpoints))


def do_predict():
    paddle.set_device('cpu')
    no_entity_label = "O"
    ignore_label = -1
    label_map = load_dict(tag_path)
    id2label = {val: key for key, val in label_map.items()}
    vocab_num, emb_size, hidden_size, num_layers, num_labels, dropout = \
        vocab_size,256,256,2,len(list(id2label)),0.1
        
    model = LSTM_Model(vocab_num, emb_size, hidden_size, num_layers, num_labels, dropout)

    print("============start predict==========")
    if not init_ckpt or not os.path.isfile(init_ckpt):
        raise Exception("init checkpoints {} not exist".format(init_ckpt))
    else:
        state_dict = paddle.load(init_ckpt)
        model.set_dict(state_dict)
        print("Loaded parameters from %s" % init_ckpt)

    # load data from predict file
    sentences = read_by_lines(predict_data)  # origin data format
    sentences = [json.loads(sent) for sent in sentences]

    encoded_inputs_list = []
    for sent in sentences:
        sent = sent["text"].replace(" ", "\002")
        input_ids = convert_example_to_feature([list(sent), [],len(sent)], max_seq_len=max_seq_len, is_test=True)
        encoded_inputs_list.append((input_ids))

    batchify_fn = lambda samples, fn=Tuple(
        Pad(axis=0, pad_val=0),  # input_ids 
        Stack()
    ): fn(samples)
    # Seperates data into some batches.
    batch_encoded_inputs = [encoded_inputs_list[i: i + batch_size]
                            for i in range(0, len(encoded_inputs_list),batch_size)]
    results = []
    model.eval()
    for batch in batch_encoded_inputs:
        input_ids,seq_lens= batchify_fn(batch)
        input_ids = paddle.to_tensor(input_ids)
        # token_type_ids = paddle.to_tensor(token_type_ids)
        logits,_ = model(input_ids)
        probs = F.softmax(logits, axis=-1)
        probs_ids = paddle.argmax(probs, -1).numpy()
        probs = probs.numpy()
        for p_list, p_ids, seq_len in zip(probs.tolist(), probs_ids.tolist(), seq_lens.tolist()):
            prob_one = [p_list[index][pid] for index, pid in enumerate(p_ids[1: seq_len - 1])]
            label_one = [id2label[pid] for pid in p_ids[1: seq_len - 1]]
            results.append({"probs": prob_one, "labels": label_one})
    assert len(results) == len(sentences)
    print(results[:10])
    for sent, ret in zip(sentences, results):
        sent["pred"] = ret
    sentences = [json.dumps(sent, ensure_ascii=False) for sent in sentences]
    print(sentences[:10])
    # write_by_lines(predict_save_path, sentences)
    # print("save data {} to {}".format(len(sentences), predict_save_path))


四、模型训练及预测1

## 训练ner
num_epoch = 10
base_dir = 'DuEE_1_0'
tag_path = './data/{}/trigger_tag.dict'.format(base_dir)
data_dir = './data/{}/trigger'.format(base_dir)
train_data = './data/{}/trigger/train.tsv'.format(base_dir)
dev_data = './data/{}/trigger/dev.tsv'.format(base_dir)
test_data = './data/{}/trigger/test.tsv'.format(base_dir)
predict_data = './data/{}/test.json'.format(base_dir)
checkpoints = './data/{}/trigger/'.format(base_dir)
init_ckpt = './data/{}/trigger/final.pdparams'.format(base_dir)

do_train()
do_predict()

输出结果如下图4所示:

在这里插入图片描述


五、模型训练及预测2

代码如下:

## 训练ee
num_epoch = 10
tag_path = './data/{}/role_tag.dict'.format(base_dir)
data_dir = './data/{}/role'.format(base_dir)
train_data = './data/{}/role/train.tsv'.format(base_dir)
dev_data = './data/{}/role/dev.tsv'.format(base_dir)
test_data = './data/{}/role/test.tsv'.format(base_dir)
predict_data = './data/{}/test.json'.format(base_dir)
checkpoints = './data/{}/role/'.format(base_dir)
init_ckpt = './data/{}/role/final.pdparams'.format(base_dir)


do_train()
do_predict()

输出结果如下图5所示:

在这里插入图片描述


总结

本系列文章内容为根据清华社出版的《自然语言处理实践》所作的相关笔记和感悟,其中代码均为基于百度飞桨开发,若有任何侵权和不妥之处,请私信于我,定积极配合处理,看到必回!!!

最后,引用本次活动的一句话,来作为文章的结语~( ̄▽ ̄~)~:

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。

ps:更多精彩内容还请进入本文专栏人工智能,进行查看,欢迎大家支持与指教啊~( ̄▽ ̄~)~

在这里插入图片描述

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

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

相关文章

Centos 7 安装 wget

文章目录1. wget命令详解:2.查看帮助手册3.使用 wget 下载单个文件4.使用 wget -O 下载并以不同的文件名保存5.使用 wget -c 断点续传6.使用 wget -b 后台下载1. wget命令详解: wget是Linux中的一个下载文件的工具&#xff0c;wget是在Linux下开发的开放源代码的软件&#xff…

tomcat线程池-深度分析tomcat线程池设计与现实

1.概述 在正式进入Tomcat线程池之前&#xff0c;小伙伴们可以先回顾一下JDK中的线程池相关特性&#xff0c;对于JDK线程池的总结和源码的解析感兴趣的童鞋&#xff0c;也可参考博主的层层剖析线程池源码的这篇文章&#xff0c;文章主要讲述对线程池的生命周期&#xff0c;核心参…

五、事件系统-原生事件-react中的合成事件-详解事件的冒泡和捕获机制

目标 深入理解和掌握事件的冒泡及捕获机制理解react中的合成事件的本质在react组件中合理的使用原生事件 知识点 在原生的dom模型上触发的事件会进行事件传递。而所谓的事件传递指的是当在A元素上触发某一事件的时候&#xff0c;B元素如果满足了和A元素一定的关系的话&#xf…

freeswitch如何判断挂机方

概述 freeswitch作为VOIP的软交换平台&#xff0c;需要对呼叫的信息做判断和归类。 常见的呼叫信息中&#xff0c;挂机方向的信息对于话单统计有很大的用处。 但是fs的原始话单和日志中并没有挂机方向的信息。 环境 centos&#xff1a;CentOS7 freeswitch&#xff1a;v1.…

火遍国内外IT技术圈,豆瓣 9.7!这本技术书籍直接封神了

这是一本在国内外都被称为技术书籍巅峰之作的神书&#xff01; 国内的豆瓣评分 9.7&#xff08;满分 10.00&#xff09;&#xff0c;接近 90% 的人为这本书打了五星好评。 国外的 Goodreads&#xff08;类似于豆瓣&#xff09; 评分 4.72&#xff08;满分 5.00&#xff09; &a…

元宇宙:下一代互联网的“美丽新世界”

8月6日到8日,美国超人气歌手Ariana Grande在全球不同地区,连续举办了5场演唱会。与众不同的是,演出的举办地点在游戏《堡垒之夜》(Fortnite)中。全球上千万的玩家,使用自己的游戏角色盛装出席。演出中,一系列的小游戏贯穿了主线,最后粉丝在绚丽的场景中和“Ariana”一对…

42、Spring AMQP DirectExchange

1、DirectExchange 2、案例 3、通过配置类实现 1、配置类 2、Publisher 3、Consumer 4、测试结果 4、通过注解实现 1、Consumer添加Linstener 2、Publisher 执行测试方法 3、测试结果 4、总结分析 学习了DirectExchange之后&#xff0c;发现与FanoutExchange有些许差异&#…

腾讯程序员的手码K8S+Jenkins笔记

最近在GitHub上认识一个腾讯大佬&#xff0c;从他手上得到一份K8SJenkins笔记&#xff0c;好东西还是要共享的&#xff01;希望这份笔记能让你技术更上一层&#xff01; 从理论到实战深入K8S 学习K8S的学习笔记很少有比较齐全的&#xff0c;而这份K8S学习笔记&#xff0c;尽量…

谈了这么久的无代码到底是什么?

尽管我们一直在谈论关于“无代码”的方方面面&#xff0c;但却并没有专门谈及“无代码”是什么。不仅许多刚听到这个名词的人会感到疑惑&#xff0c;就算熟悉这一名词的可能也无法给出一个准确的定义&#xff0c;又或者常常将其与另一个相近的名词“低代码”弄混。 今天我们专…

Revit插件实现【快速扣减】的两种操作

翻模的过程中&#xff0c;我们在处理细节问题的时候&#xff0c;如果只是两个构件之间的重合&#xff0c;我们可以利用“连接”命令进行扣减。而如果是想让多个构件进行扣减操作&#xff0c;而不是整层或者整栋楼都按照同样的扣减顺序进行的话&#xff0c;我们可以利用橄榄山和…

Windows安全日志分析

Windows安全日志分析 0x00 引言 在应急响应初步阶段&#xff0c;我们会对系统日志、中间件安全日志、恶意文件等进行收集。接下来便是要进一步对这些文件进行分析&#xff1a;对恶意文件逆向、日志文件分析、梳理入侵时间线和入侵路径等。本文主要对Windows安全日志进行举例分…

Mybatis整合达梦数据库

陈老老老板&#x1f9b8;&#x1f468;‍&#x1f4bb;本文专栏&#xff1a;国产数据库-达梦数据库&#xff08;主要讲一些达梦数据库相关的内容&#xff09;&#x1f468;‍&#x1f4bb;本文简述&#xff1a;本文讲一下SpringBoot整合Mybatis与达梦数据库&#xff0c;就是简单…

全网最详细|Oracle12C安装和卸载图文教程

一. 数据库的引入 以前将数据用变量、数组、对象存在内存&#xff0c;而内存只能短暂存储数据。如果我们想长久存数据用文件将数据存在磁盘上&#xff0c;不方便存取和管理数据&#xff0c;因此可以使用数据库来存数据。 二. 数据库基础概念 2.1数据库(database,简称DB) 以…

QRegExpValidator(正则验证器)

QRegExpValidator QRegExpValidator 使用正则表达式 &#xff08;regexp&#xff09; 来确定输入字符串是可接受、中间还是无效的。正则表达式可以在构建 QRegExpValidator 时提供&#xff0c;也可以在以后提供。 构造函数&#xff1a; ​ 常用的函数&#xff1a; setRegE…

在Windows上使用Docker搭建ChirpStack私有LoRa服务端

在Windows上使用Docker容器部署ChirpStack服务&#xff0c;首先若要在Windows 10或11 上运行容器&#xff0c;需要以下条件&#xff1a; 一个运行 Windows 10 或 11 专业版或企业版的物理计算机系统。Hyper-V 功能已启用。 Windows下的Docker安装 1.Docker为Windows提供了一款…

如何设计一个支撑数亿用户的系统

要设计出一套能支撑几十亿人的系统是很困难的。对于软件架构师来说&#xff0c;这一直是一项很大的挑战&#xff0c;但是&#xff0c;从现在开始&#xff0c;看完我的文章&#xff0c;你就会觉得容易很多了。 下面是我在本文中提到的几个话题&#xff1a; 从最简单的开始&…

关于大数据测试,你一定要试试python的fake库

一.背景 对于大数据相关项目的测试&#xff0c;往往需要大量的测试数据&#xff0c;而场内测试时很难获取那么大批量的真实数据&#xff0c;这个时候需要测试人员能够快速模拟出符合要求的测试数据。这个时候要是有一个自动化工具或测试脚本简直不要太爽~ 二.问题引入 对于互…

【Ajax进阶】跨域和JSONP的学习

✍️ 作者简介: 前端新手学习中。 &#x1f482; 作者主页: 作者主页查看更多前端教学 &#x1f393; 专栏分享&#xff1a;css重难点教学 Node.js教学 从头开始学习 ajax学习 文章目录了解同源策略和跨域  同源策略    什么是同源    什么是同源策略跨域    什么是…

python基础07——函数,想重复使用自己的代码就写个函数吧

目录1 函数定义与调用1.1 自定义模块函数1.2 函数的返回值 return2 参数的传递方式2.1 位置参数2.2 默认值参数2.3 关键参数2.4 可变长度参数(收集和分配参数)2.4.1 收集位置参数2.4.2 分配位置参数,函数参数的序列解包2.4.3 收集关键字参数2.4.4 分配关键字参数3 变量的作用域…

腾讯云GPU云服务器在选择实例配置时应该注意哪些事项?

腾讯云异构计算服务器搭载GPU、FPGA等异构硬件&#xff0c;具有实时高速的并行计算和浮点计算能力&#xff0c;适合于深度学习、科学计算、视频编解码和图形工作站等高性能应用&#xff0c;下面分享腾讯云NVIDIA GPU云服务器配置CPU内存性能注意事项&#xff1a; NVIDIA GPU云…