CNN-NER论文详解

论文:https://arxiv.org/abs/2208.04534

代码:https://github.com/yhcc/CNN_Nested_NER/tree/master

文章目录

      • 有关工作
        • 前期介绍
        • CNN-NER
        • 模型介绍
      • 代码讲解
        • 主类
        • 多头biaffine
        • CNN
        • Loss
        • 解码
        • 数据传入格式
      • 参考资料

有关工作

前期介绍

过去一共主要有四类方式用来解决嵌套命名实体识别的任务:

  1. 基于序列标注(sequence labeling)
  2. 基于超图(hypergraph)
  3. 基于序列到序列(Seq2Seq)
  4. 基于片段分类(span classification)

本文跟进了《Named Entity Recognition as Dependency Parsing》这一论文的工作,同样采用基于片段分类的方案。

该论文提出采用起始、结束词来指明对应的片段,并利用双仿射(Biaffine Decoder)来得到一个评分矩阵,其元素(i,j)代表对应片段(开始位置为第i个词,结束位置为第j个词)为实体的分数,这一基于片段的方法在计算上易于并行,因此得到了广泛的采用

下图给出一个直观的例子理解评分矩阵。这里由概率可得span(start=2,end=4)=“New York University”最可能是ORG实体

CNN-NER

作者在此基础上注意到了过往的工作忽视了相邻片段间的彼此联系,并通过对评分矩阵的观察分析发现了临近的片段具有非常明显的空间关联。如下图所示

  • o:中心的span
  • a:后端的字符序列与中心span冲突
  • b:前端的字符序列与中心span冲突
  • c:包含中心span
  • d:被中心span包含
  • e:无冲突
针对左上角第一个矩阵:o(2-4),New York University
a(1-3),Is New York
c(1-4),Is New York University
c(1-5),Is New York University in
d(2-3),New York
c(2-5),New York University in
d(3-3),York
d(3-4),York University
b(3-5),York University in

针对右下角第二个矩阵:o(6-6),United
e(5-5),in
c(5-6),in United
c(5-7),in United States
c(6-5),in United
c(6-7),United States
c(7-5),in United States
c(7-6),United States
c(7-7),States

作者把这种针对每一个中心span的张量理解成一种通道数,进一步采用了计算机视觉领域常用的卷积神经网络(CNN)来建模这种空间联系,最终得到一个简单但颇具竞争力的嵌套命名实体解决方案,将其命名为CNN-NER

模型介绍

首先使用编码器(BERT-Encoder)对输入序列进行编码。在获得上下文有关的词嵌入(embedding)后,过去的工作通常将其与静态的词嵌入以及字符级别的嵌入拼接起来送入BiLSTM中获得聚合的词表示,但本文为了让模型架构比较简单,就没有采用更多的嵌入也没有额外引入LSTM层。

然后仿照之前的工作,采用双头仿射解码器(multi-head Biaffine Decoder)获取表示所有可能的片段对应的特征矩阵。

接下来,从维度上考察特征矩阵,将其视作多通道的图片,采用若干个常见的卷积块提取特征矩阵的空间特征。

最后通过FC和sigmoid函数预测对应片段是命名实体的“概率”。训练的损失函数采用的是常见的二元交叉熵(BCE)

本文使用了与之前工作相同的方法解码模型输出的概率,即采用如下的贪心选择:首先丢弃所有预测概率低于0.5的片段,然后按照预测概率从高到低对片段进行排序,依次选择当前预测概率最高的片段,如果其不与之前已经解码出的命名实体冲突,则将该片段解码成一个新的命名实体,否则将其丢弃。如此迭代进行就得到了模型预测的输入序列的所有互不冲突的命名实体

代码讲解

主类

class CNNNer(BaseModel):
    def __init__(self, num_ner_tag, cnn_dim=200, biaffine_size=200,
                 size_embed_dim=0, logit_drop=0, kernel_size=3, n_head=4, cnn_depth=3):
        super(CNNNer, self).__init__()
        self.pretrain_model = build_transformer_model(config_path=config_path, checkpoint_path=checkpoint_path, segment_vocab_size=0)
        hidden_size = self.pretrain_model.configs['hidden_size']

        if size_embed_dim!=0:
            n_pos = 30
            self.size_embedding = torch.nn.Embedding(n_pos, size_embed_dim)
            _span_size_ids = torch.arange(512) - torch.arange(512).unsqueeze(-1)
            _span_size_ids.masked_fill_(_span_size_ids < -n_pos/2, -n_pos/2)
            _span_size_ids = _span_size_ids.masked_fill(_span_size_ids >= n_pos/2, n_pos/2-1) + n_pos/2
            self.register_buffer('span_size_ids', _span_size_ids.long())
            hsz = biaffine_size*2 + size_embed_dim + 2
        else:
            hsz = biaffine_size*2+2
        biaffine_input_size = hidden_size

        self.head_mlp = nn.Sequential(
            nn.Dropout(0.4),
            nn.Linear(biaffine_input_size, biaffine_size),
            nn.LeakyReLU(),
        )
        self.tail_mlp = nn.Sequential(
            nn.Dropout(0.4),
            nn.Linear(biaffine_input_size, biaffine_size),
            nn.LeakyReLU(),
        )

        self.dropout = nn.Dropout(0.4)
        if n_head>0:
            self.multi_head_biaffine = MultiHeadBiaffine(biaffine_size, cnn_dim, n_head=n_head)
        else:
            self.U = nn.Parameter(torch.randn(cnn_dim, biaffine_size, biaffine_size))
            torch.nn.init.xavier_normal_(self.U.data)
        self.W = torch.nn.Parameter(torch.empty(cnn_dim, hsz))
        torch.nn.init.xavier_normal_(self.W.data)
        if cnn_depth>0:
            self.cnn = MaskCNN(cnn_dim, cnn_dim, kernel_size=kernel_size, depth=cnn_depth)

        self.down_fc = nn.Linear(cnn_dim, num_ner_tag)
        self.logit_drop = logit_drop

    def forward(self, input_ids, indexes):
        last_hidden_states = self.pretrain_model([input_ids])
        state = scatter_max(last_hidden_states, index=indexes, dim=1)[0][:, 1:]  # b * l * hidden_size
        lengths, _ = indexes.max(dim=-1)

        head_state = self.head_mlp(state)# b * l * l * biaffine_size
        tail_state = self.tail_mlp(state)# b * l * l * biaffine_size
        if hasattr(self, 'U'):
            scores1 = torch.einsum('bxi, oij, byj -> boxy', head_state, self.U, tail_state)
        else:
            scores1 = self.multi_head_biaffine(head_state, tail_state)#b * cnn_dim * l * l
        head_state = torch.cat([head_state, torch.ones_like(head_state[..., :1])], dim=-1)# b * l * l * biaffine_size + 1
        tail_state = torch.cat([tail_state, torch.ones_like(tail_state[..., :1])], dim=-1)# b * l * l * biaffine_size + 1
        affined_cat = torch.cat([self.dropout(head_state).unsqueeze(2).expand(-1, -1, tail_state.size(1), -1),
                                 self.dropout(tail_state).unsqueeze(1).expand(-1, head_state.size(1), -1, -1)], dim=-1)## b * l * l * 2(biaffine_size + 1)

        if hasattr(self, 'size_embedding'):
            size_embedded = self.size_embedding(self.span_size_ids[:state.size(1), :state.size(1)])# l * l * size_embed_dim
            affined_cat = torch.cat([affined_cat, self.dropout(size_embedded).unsqueeze(0).expand(state.size(0), -1, -1, -1)], dim=-1)# b * l * l * (2(biaffine_size + 1) + size_embed_dim)

        scores2 = torch.einsum('bmnh,kh->bkmn', affined_cat, self.W)  # b x cnn_dim x L x L
        scores = scores2 + scores1# b x cnn_dim x L x L

        if hasattr(self, 'cnn'):            
            batch_size = lengths.shape[0]
            broad_cast_seq_len = torch.arange(int(lengths.max())).expand(batch_size, -1).to(lengths)
            mask = broad_cast_seq_len < lengths.unsqueeze(1)

            mask = mask[:, None] * mask.unsqueeze(-1)
            pad_mask = mask[:, None].eq(0)
            u_scores = scores.masked_fill(pad_mask, 0)
            if self.logit_drop != 0:
                u_scores = F.dropout(u_scores, p=self.logit_drop, training=self.training)
            u_scores = self.cnn(u_scores, pad_mask)# b x cnn_dim x L x L
            scores = u_scores + scores

        scores = self.down_fc(scores.permute(0, 2, 3, 1))

        return scores # b * L * L * num_ner_tag

多头biaffine

class MultiHeadBiaffine(nn.Module):
    def __init__(self, dim, out=None, n_head=4):
        super(MultiHeadBiaffine, self).__init__()
        assert dim%n_head==0
        in_head_dim = dim//n_head
        out = dim if out is None else out
        assert out%n_head == 0
        out_head_dim = out//n_head
        self.n_head = n_head
        self.W = nn.Parameter(nn.init.xavier_normal_(torch.randn(self.n_head, out_head_dim, in_head_dim, in_head_dim)))
        self.out_dim = out

    def forward(self, h, v):
        """
        :param h: bsz x max_len x dim
        :param v: bsz x max_len x dim
        :return: bsz x max_len x max_len x out_dim
        """
        bsz, max_len, dim = h.size()
        h = h.reshape(bsz, max_len, self.n_head, -1)
        v = v.reshape(bsz, max_len, self.n_head, -1)
        w = torch.einsum('blhx,hdxy,bkhy->bhdlk', h, self.W, v)
        w = w.reshape(bsz, self.out_dim, max_len, max_len)
        return w

CNN

class MaskConv2d(nn.Module):
    def __init__(self, in_ch, out_ch, kernel_size=3, padding=1, groups=1):
        super(MaskConv2d, self).__init__()
        self.conv2d = nn.Conv2d(in_ch, out_ch, kernel_size=kernel_size, padding=padding, bias=False, groups=groups)

    def forward(self, x, mask):
        x = x.masked_fill(mask, 0)
        _x = self.conv2d(x)
        return _x


class MaskCNN(nn.Module):
    def __init__(self, input_channels, output_channels, kernel_size=3, depth=3):
        super(MaskCNN, self).__init__()

        layers = []
        for _ in range(depth):
            layers.extend([
                MaskConv2d(input_channels, input_channels, kernel_size=kernel_size, padding=kernel_size//2),
                LayerNorm((1, input_channels, 1, 1), dim_index=1),
                nn.GELU()])
        layers.append(MaskConv2d(input_channels, output_channels, kernel_size=3, padding=3//2))
        self.cnns = nn.ModuleList(layers)

    def forward(self, x, mask):
        _x = x  # 用作residual
        for layer in self.cnns:
            if isinstance(layer, LayerNorm):
                x = x + _x
                x = layer(x)
                _x = x
            elif not isinstance(layer, nn.GELU):
                x = layer(x, mask)
            else:
                x = layer(x)
        return _x

Loss

class Loss(object):
    def __call__(self, scores, y_true):
        matrix, _ = y_true
        assert scores.shape[-1] == matrix.shape[-1]
        flat_scores = scores.reshape(-1)
        flat_matrix = matrix.reshape(-1)
        mask = flat_matrix.ne(-100).float().view(scores.size(0), -1)
        flat_loss = F.binary_cross_entropy_with_logits(flat_scores, flat_matrix.float(), reduction='none')
        loss = ((flat_loss.view(scores.size(0), -1)*mask).sum(dim=-1)).mean()
        return loss

解码

class Evaluator(Callback):
    """评估与保存
    """
    def __init__(self):
        self.best_val_f1 = 0.

    def on_epoch_end(self, steps, epoch, logs=None):
        f1, p, r, e_f1, e_p, e_r = self.evaluate(valid_dataloader)
        if e_f1 > self.best_val_f1:
            self.best_val_f1 = e_f1
            # model.save_weights('best_model.pt')
        print(f'[val-token  level] f1: {f1:.5f}, p: {p:.5f} r: {r:.5f}')
        print(f'[val-entity level] f1: {e_f1:.5f}, p: {e_p:.5f} r: {e_r:.5f} best_f1: {self.best_val_f1:.5f}\n')

    def evaluate(self, data_loader, threshold=0.5):
        def cal_f1(c, p, r):
            if r == 0 or p == 0:
                return 0, 0, 0
            r = c / r if r else 0
            p = c / p if p else 0
            if r and p:
                return 2 * p * r / (p + r), p, r
            return 0, p, r

        pred_result = []
        label_result = []

        total_ent_r = 0
        total_ent_p = 0
        total_ent_c = 0
        for data_batch in tqdm(data_loader, desc='Evaluate'):
            (tokens_ids, indexes), (matrix, ent_target) = data_batch
            scores = torch.sigmoid(model.predict([tokens_ids, indexes])).gt(threshold).long()
            scores = scores.masked_fill(matrix.eq(-100), 0)  # mask掉padding部分
            
            # token粒度
            mask = matrix.reshape(-1).ne(-100)
            label_result.append(matrix.reshape(-1).masked_select(mask).cpu())
            pred_result.append(scores.reshape(-1).masked_select(mask).cpu())

            # 实体粒度
            ent_c, ent_p, ent_r = self.decode(scores.cpu().numpy(), ent_target)
            total_ent_r += ent_r
            total_ent_p += ent_p
            total_ent_c += ent_c

        label_result = torch.cat(label_result)
        pred_result = torch.cat(pred_result)

        p, r, f1, _ = precision_recall_fscore_support(label_result.numpy(), pred_result.numpy(), average="macro")
        e_f1, e_p, e_r = cal_f1(total_ent_c, total_ent_p, total_ent_r)
        return f1, p, r, e_f1, e_p, e_r

    def decode(self, outputs, ent_target):
        ent_c, ent_p, ent_r = 0, 0, 0
        for pred, label in zip(outputs, ent_target):
            ent_r += len(label)
            pred_tuple = []
            for item in range(pred.shape[-1]):
                if pred[:, :, item].sum() > 0:
                    _index = np.where(pred[:, :, item]>0)
                    tmp = [(i, j, item) if j >= i else (j, i, item) for i, j in zip(*_index)]
                    pred_tuple.extend(list(set(tmp)))
            ent_p += len(pred_tuple)
            ent_c += len(set(label).intersection(set(pred_tuple)))
            
        return ent_c, ent_p, ent_r

数据传入格式

初步处理

class MyDataset(ListDataset):
    @staticmethod
    def get_new_ins(bpes, spans, indexes):
        bpes.append(tokenizer._token_end_id)
        cur_word_idx = indexes[-1]
        indexes.append(0)
        # int8范围-128~127
        matrix = np.zeros((cur_word_idx, cur_word_idx, len(label2idx)), dtype=np.int8)
        ent_target = []
        for _ner in spans:
            s, e, t = _ner
            matrix[s, e, t] = 1
            matrix[e, s, t] = 1
            ent_target.append((s, e, t))
        assert len(bpes)<=maxlen, len(bpes)
        return [bpes, indexes, matrix, ent_target]

    def load_data(self, filename):
        D = []
        word2bpes = {}
        with open(filename, encoding='utf-8') as f:
            f = f.read()
            for l in tqdm(f.split('\n\n'), desc='Load data'):
                if not l:
                    continue
                _raw_words, _raw_ents = [], []
                for i, c in enumerate(l.split('\n')):
                    char, flag = c.split(' ')
                    _raw_words += char
                    if flag[0] == 'B':
                        _raw_ents.append([i, i, flag[2:]])
                    elif flag[0] == 'I':
                        _raw_ents[-1][1] = i
                if len(_raw_words) > maxlen - 2:
                    continue
                
                bpes = [tokenizer._token_start_id]
                indexes = [0]
                spans = []
                ins_lst = []
                _indexes = []
                _bpes = []

                for idx, word in enumerate(_raw_words, start=0):
                    if word in word2bpes:
                        __bpes = word2bpes[word]
                    else:
                        __bpes = tokenizer.encode(word)[0][1:-1]
                        word2bpes[word] = __bpes
                    _indexes.extend([idx]*len(__bpes))
                    _bpes.extend(__bpes)
                next_word_idx = indexes[-1]+1
                if len(bpes) + len(_bpes) <= maxlen:
                    bpes = bpes + _bpes
                    indexes += [i + next_word_idx for i in _indexes]
                    spans += [(s+next_word_idx-1, e+next_word_idx-1, label2idx.get(t), ) for s, e, t in _raw_ents]
                else:
                    new_ins = self.get_new_ins(bpes, spans, indexes)
                    ins_lst.append(new_ins)
                    indexes = [0] + [i + 1 for i in _indexes]
                    spans = [(s, e, label2idx.get(t), ) for s, e, t in _raw_ents]
                    bpes = [tokenizer._token_start_id] + _bpes

                D.append(self.get_new_ins(bpes, spans, indexes))
        return D

传入的是:

  • bpes:对应input_ids
  • indexes:“CLS”、"SEP"为0,其他字符按照所在句子的位置的索引
  • matrix:[cur_word_idx, cur_word_idx, len(label2idx)],第三个维度表明若是某个实体,则设为1
  • ent_target:在当前句子中存在实体的的[start,ent,ent_type]
def collate_fn(data):
    tokens_ids, indexes, matrix, ent_target = map(list, zip(*data))
    tokens_ids = torch.tensor(sequence_padding(tokens_ids), dtype=torch.long, device=device)
    indexes = torch.tensor(sequence_padding(indexes), dtype=torch.long, device=device)
    seq_len = max([i.shape[0] for i in matrix])
    matrix_new = np.ones((len(tokens_ids), seq_len, seq_len, len(label2idx)), dtype=np.int8) * -100
    for i in range(len(tokens_ids)):
        matrix_new[i, :len(matrix[i][0]), :len(matrix[i][0]), :] = matrix[i]
    matrix = torch.tensor(matrix_new, dtype=torch.long, device=device)

    return [tokens_ids, indexes], [matrix, ent_target]
  • 对tokens_ids、indexes进行填充为0
  • 对matrix填充为-100

参考资料

https://zhuanlan.zhihu.com/p/565824221

参照代码:

https://github.com/Tongjilibo/bert4torch/blob/master/examples/sequence_labeling/task_sequence_labeling_ner_CNN_Nested_NER.py

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构初阶--二叉树的顺序结构之堆

目录 一.堆的概念及结构 1.1.堆的概念 1.2.堆的存储结构 二.堆的功能实现 2.1.堆的定义 2.2.堆的初始化 2.3.堆的销毁 2.4.堆的打印 2.5.堆的插入 向上调整算法 堆的插入 2.6.堆的删除 向下调整算法 堆的删除 2.7.堆的取堆顶元素 2.8.堆的判空 2.9.堆的求堆的…

MyBatis-Plus实现分页查询

目录 MyBatis-Plus实现分页查询 代码 定义一个MyBatis-Plus拦截器 在连接数据库的配置文件中添加MyBatis-Plus日志查看MyBatis-Plus的SQL语句 测试 运行结果 MyBatis-Plus实现分页查询 代码 定义一个MyBatis-Plus拦截器 package com.dong.config;import com.baomidou.my…

webpack基础知识二:说说webpack的构建流程?

一、运行流程 webpack 的运行流程是一个串行的过程&#xff0c;它的工作流程就是将各个插件串联起来 在运行过程中会广播事件&#xff0c;插件只需要监听它所关心的事件&#xff0c;就能加入到这条webpack机制中&#xff0c;去改变webpack的运作&#xff0c;使得整个系统扩展…

【学习笔记】Java安全之反序列化

文章目录 反序列化方法的对比PHP的反序列化Java的反序列化Python反序列化 URLDNS链利用链分析触发DNS请求 CommonCollections1利用链利用TransformedMap构造POC利用LazyMap构造POCCommonsCollections6 利用链 最近在学习Phith0n师傅的知识星球的Java安全漫谈系列&#xff0c;随…

分布式限流方案及实现

优质博文&#xff1a;IT-BLOG-CN 一、限流的作用和意义 限流是对高并发访问进行限制&#xff0c;限速的过程。通过限流来限制资源&#xff0c;可以提高系统的稳定性和可靠性&#xff0c;控制系统的负载&#xff0c;削峰填谷&#xff0c;保证服务质量。 服务限流后的常见处理…

电动汽车设计、制造、研发的学科、技术和前沿科技综述

引言&#xff1a;电动汽车作为替代传统燃油汽车的一种先进交通工具&#xff0c;不仅具有环保、低噪音等优势&#xff0c;而且对于能源消耗和气候变化等全球性问题也具有重要意义。本文将综述与电动汽车设计、制造、研发相关的学科、技术和前沿科技&#xff0c;以期对电动汽车领…

MATLAB /Simulink 快速开发STM32(使用st官方工具 STM32-MAT/TARGET),以及开发过程

配置好环境以后就是开发&#xff1a; stm32cube配置芯片&#xff0c;打开matlab添加ioc文件&#xff0c;写处理逻辑&#xff0c;生成代码&#xff0c;下载到板子中去。 配置需要注意事项&#xff1a; STM32CUBEMAX6.5.0 MABLAB2022BkeilV5.2 Matlab生成的代码CTRLB 其中关键的…

海外版金融理财系统源码 国际投资理财系统源码 项目投资理财源码

海外版金融理财系统源码 国际投资理财系统源码 项目投资理财源码

iPhone 8 Plus透明屏应用范围详解

iPhone 8 Plus是苹果公司于2017年推出的一款智能手机&#xff0c;它采用了全新的玻璃机身设计&#xff0c;支持无线充电&#xff0c;并且搭载了更强大的A11仿生芯片。 而透明屏则是一种新型的屏幕技术&#xff0c;可以使手机屏幕呈现出透明的效果。 透明屏是一种将屏幕背后的元…

百度智能云“千帆大模型平台”最新升级:接入Llama 2等33个模型!

今年3月&#xff0c;百度智能云推出“千帆大模型平台”。作为全球首个一站式的企业级大模型平台&#xff0c;千帆不但提供包括文心一言在内的大模型服务及第三方大模型服务&#xff0c;还提供大模型开发和应用的整套工具链&#xff0c;能够帮助企业解决大模型开发和应用过程中的…

在Ruoyi中采用Ajax动态生成Echarts图表实践

前言 在之前博文中&#xff0c;我们讲解了如何使用java在后台进行Echarts的图表生成组件&#xff0c;博文如下&#xff1a; 序号 博客连接1一款基于JAVA开发的Echarts后台生成框架2Ruoyi单体项目与Echarts4.2.1地图集成时的思路及解决办法3解决Ruoyi单体版本集成Echarts多图表时…

RocketMQ生产者和消费者都开启Message Trace后,Consume Message Trace没有消费轨迹

一、依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.0.3</version> </dependency>二、场景 1、生产者和消费者所属同一个程序 2、生产者开启消…

Xcode protobuf2.5添加arm64编译器补丁生成静态库

项目需求&#xff0c;protobuf源码编成静态库使用 但是&#xff0c;github上的protobuf源码没有对应arm64的编译器定义&#xff0c;编译出来的静态库使用时报错。 下面的连接是arm64编译器代码补丁包&#xff0c;把编译器代码放到src/google/protobuf/stubs/atomicops_intern…

二、JVM-深入运行时数据区

深入运行时数据区 计算机体系结构 JVM的设计实际上遵循了遵循冯诺依曼计算机结构 CPU与内存交互图&#xff1a; 硬件一致性协议&#xff1a; MSI、MESI、MOSI、Synapse、Firely、DragonProtocol 摩尔定律 摩尔定律是由英特尔(Intel)创始人之一戈登摩尔(Gordon Moore)提出来…

基于Caffe的静默活体检测识别分析系统

周末的时候看到一个好玩的项目就想着实际拿来使用一下&#xff0c;这个项目主要是做的是开源的跟人脸活体检测相关的内容&#xff0c;这里主要采用的是静默活体检测的方式。 人脸静默活体检测是一种用于验证人脸是真实、活体的技术&#xff0c;而不需要进行任何口头指令或特定…

自监督去噪:Noise2Noise原理及实现(Pytorch)

文章地址&#xff1a;https://arxiv.org/abs/1803.04189 ICML github 代码: https://github.com/NVlabs/noise2noise 本文整理和参考代码: https://github.com/shivamsaboo17/Deep-Restore-PyTorch 文章目录 1. 理论背景2. 实验结果3. 代码实现(1) 网络结构(2) 数据加载(3) 网络…

Elasticsearchr入门

首先在官网下载elasticsearch8.9版本&#xff0c;以及8.9版本的kibana。 解压&#xff0c;点击es8.9bin目录下的elasticsearch.bat文件启动es 如图所示即为成功。 启动之后打开idea&#xff0c;添加依赖 <dependency><groupId>com.fasterxml.jackson.core</g…

后端进阶之路——Spring Security构建强大的身份验证和授权系统(四)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

嵌入式:C高级 Day3

一、整理思维导图 二、判断家目录下&#xff0c;普通文件的个数和目录文件的个数 三、输入一个文件名&#xff0c;判断是否为shell脚本文件&#xff0c;如果是脚本文件&#xff0c;判断是否有可执行权限&#xff0c;如果有可执行权限&#xff0c;运行文件&#xff0c;如果没有可…

中科昊芯28034上手(1)--环境搭建

官网信息还是比较完整&#xff0c;不过需要下载阿里云盘。 1. 官网链接&#xff1a; start28034湖人开发板_北京中科昊芯科技有限公司 官网图片长这样&#xff1a; 实物有些出入&#xff1a; 基本功能差异不大。 2. 资料还是齐备的&#xff0c;不过案例好像都是平头哥的…