【自注意力机制必学】BERT类预训练语言模型(含Python实例)

news2024/11/15 19:58:13

在这里插入图片描述

BERT类预训练语言模型

文章目录

  • BERT类预训练语言模型
    • 1. BERT简介
      • 1.1 BERT简介及特点
      • 1.2 传统方法和预训练方法
      • 1.3 BERT的性质
    • 2. BERT结构
      • 2.1 输入层以及位置编码
      • 2.2 Transformer编码器层
      • 2.3 前馈神经网络层
      • 2.4 残差连接层
      • 2.5 输出层
    • 3. BERT类模型简要笔记
    • 4. 代码工程实践

1. BERT简介

1.1 BERT简介及特点

BERT(Bidirectional Encoder Representations from Transformers)是一种预训练模型,它是自然语言处理(NLP)领域的重大里程碑,被认为是当前的State-of-the-Art模型之一。BERT的设计理念和结构基于Transformer模型,通过无监督学习方式进行训练,并且能够适配各种NLP任务。

预训练模型是指在大规模文本数据上进行大量无监督训练,学习得到丰富的语言表示。BERT的预训练任务是通过掩码语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)两个阶段进行的。在MLM中,输入文本的一部分被随机掩码,模型需要预测这些被掩码的词。在NSP中,模型需要判断两个句子是否是原文中的连续句子。

BERT的无监督训练使得它能够学习到丰富的句子级和词级表示,因为它需要理解上下文并进行语言推理。这种能力使得BERT成为适配各种NLP任务的理想选择。在使用BERT进行下游任务时,可以将其作为一个特征提取器或者通过在BERT之上添加一个任务特定的输出层进行微调。

BERT的结构基于Transformer模型,Transformer是一种基于自注意力机制的深度神经网络架构。它能够捕捉句子中的上下文信息,使得模型能够同时考虑句子中的前后词语。这种双向建模的能力是BERT的关键特点之一,也是相对于之前的模型(如基于循环神经网络的模型)的显著改进。

BERT的出现引起了NLP领域的重大变革。它在多个NLP任务上取得了突破性的结果,如问答、文本分类、命名实体识别等。BERT的成功也启发了许多后续模型的设计和改进,包括GPT、RoBERTa等。这些模型在NLP领域取得了巨大的进展,使得研究人员和从业者能够更好地处理自然语言数据,并且在多个任务上取得更高的性能。

1.2 传统方法和预训练方法

对于传统方法,主要集中于:

  • 设计模型结构
  • 收集/标准训练数据
  • 使用标注数据进行模型训练
  • 真实场景预测
    以上流程也称为Fine-tune,也即微调的过程
    预训练的方法相较上面的流程多了Pre-train的过程:
  • 收集海量无标注文本数据
  • 进行模型预训练,并在任务模型中使用

此两步称为Pre-train的过程

  • 设计模型结构
  • 收集/标准训练数据
  • 使用标注数据进行模型训练
  • 真实场景预测
    预训练的方法也即Pre-train+Fine-tune的过程
    在这里插入图片描述

BERT的预训练方式包括两个任务:掩码语言模型(Masked Language Model, MLM)下一句预测(Next Sentence Prediction, NSP)

在掩码语言模型(MLM)任务中,BERT会对输入的文本进行随机的掩码处理。具体来说,对于输入文本中的一部分词,约15%的词会被掩码成特定的标记(通常是"[MASK]"),一小部分词会被替换成随机的词,而其余的词保持不变。模型的目标是通过上下文中的其他词来预测这些被掩码的词的原始内容。

可以通俗理解为完形填空的过程,与n-gram的区别,双向,考虑的是词前后两个方向的信息

下一句预测(NSP)任务旨在训练模型对句子级别的语义进行建模。模型会接收一对句子作为输入,并需要判断这两个句子是否在原文中是连续的。为了生成训练数据,BERT从大规模的文本数据中随机挑选一对句子,并在输入中加入特殊的标记来指示两个句子的边界。

可以浅显的视为句子级别的语言模型

通过这两个任务的预训练,BERT学习到了丰富的语言表示。MLM任务使得模型能够理解上下文并预测被掩码的词,促使模型学会对句子中的语义进行建模。NSP任务则让模型能够理解句子之间的关系和语义连贯性。

这种预训练方式使得BERT能够从大规模无标签数据中学习到通用的语言表示,这些表示可以应用于各种下游NLP任务,通过微调或作为特征提取器来实现任务特定的性能提升。

1.3 BERT的性质

首先,BERT的本质是一种文本表征(context representation),做的是一个文本->矩阵(max length × \times ×hidden size)或者文本->向量(1 × \times ×hidden size),word2vec也可以做到同样的事,但是word2vec是静态的,而BERT是动态的,因为BERT是将输入考虑语序后经过transformer输出的,一对一的

BERT的表现比RNN优秀,原因概括有两点:

  • 采用了预训练的思想
  • 采用了更有效的网络结构

2. BERT结构

Transformer中最核心的点是注意力机制,核心文章为《Attention is All you Need》,BERT的模型主体结构使用Google在17年提出的Transformer结构。
在这里插入图片描述
BERT的Encoder结构
在这里插入图片描述
BERT的Encoder部分是由多个相同结构的Transformer编码器组成,可以逐层分析其结构。假设BERT包含L个Transformer编码器层。

2.1 输入层以及位置编码

输入层:输入是一个经过词嵌入(Word Embedding)的序列,其中每个词被表示为一个向量。输入序列还包括特殊的标记,如句子的开始([CLS])和分隔符([SEP])。
在这里插入图片描述
Segment Embedding 每一段文本的此部分相同,此部分的参数量为段数 × 768 \times 768 ×768,Token Embedding 将每个词映射为一个向量(包括[CLS]和[SEP]),参数量为词表大小 × 768 \times 768 ×768

位置编码(Positional Encoding):在输入序列的词嵌入中,Transformer编码器需要一种方式来编码单词的位置信息。为此,BERT使用了位置编码,它将位置信息嵌入到词嵌入向量中,使得模型能够考虑单词在序列中的顺序。
Position Embedding 将词所在的位置映射成为一个向量,参数量为 512 × 768 512\times 768 512×768,每个向量均为768维,最大长度限制为512,论文规定,上面三个embedding加和之后会做Layer Normalization

2.2 Transformer编码器层

Transformer编码器层:BERT的Encoder部分由L个Transformer编码器层堆叠而成,堆叠了12次。

自注意力层(Self-Attention Layer):每个Transformer编码器层包含多个自注意力头(self-attention head),这些头可以并行计算。自注意力层的输入包括三个向量序列:查询(query)、键(key)和值(value)。自注意力机制通过计算注意力权重来将查询与键进行交互,然后将注意力权重应用于值向量,最后对加权后的值向量进行求和。这种交互能够捕捉到输入序列中每个位置与其他位置的关系,并为每个位置生成一个更新的表示。
查询(query)(Q):查询向量用于确定当前位置与其他位置的关联程度。在自注意力机制中,通过将查询向量与其他位置的键进行点积操作,得到了每个位置对其他位置的注意力权重。查询向量可以理解为当前位置所关注的信息。

键(key)(K):键向量用于提供其他位置的信息,以便查询向量可以计算与它们的关联程度。通过将查询向量与键向量进行点积操作,可以得到注意力权重。键向量可以理解为其他位置的表示。

值(value)(V):值向量包含了与每个位置相关的信息。注意力权重会被应用于值向量,以得到每个位置的加权和作为最终的输出。值向量可以理解为当前位置所携带的信息。
在Transformer算法中使用qkv参数的具体步骤:

  • 线性变换:首先,输入序列经过三个独立的线性变换来计算查询(Q)、键(K)和值(V)。对于输入序列中的每个位置,使用不同的权重矩阵对输入进行线性变换,得到相应的查询向量Q、键向量K和值向量V。

  • 计算注意力权重:使用查询向量Q与键向量K进行点积操作,然后通过缩放(一般是将结果除以一个缩放因子)来计算注意力权重。缩放可以使得注意力权重的分布更稳定。注意力权重衡量了每个位置与其他位置的关联程度,决定了每个位置在注意力机制中的重要性。

  • 注意力权重和值的加权求和:将注意力权重与值向量V相乘,并对这些乘积进行加权求和,得到注意力机制的输出。注意力权重决定了每个位置对最终表示的贡献程度,而值向量提供了实际的特征信息。

  • 多头注意力机制:在Transformer中通常采用多头注意力机制,通过并行计算多组qkv参数来提高模型的表示能力。多头注意力机制中,每组qkv参数都会进行独立的线性变换和注意力计算,得到多组注意力输出。最后,将多组注意力输出进行拼接,并通过线性变换得到最终的表示。
    在这里插入图片描述
    在方阵的运算中解决了远字的关系问题,由于是方阵,管你多远,都能计算到,每个字对自己相对应的字的关系,所以是自注意力。
    在这里插入图片描述
    自注意力多头机制:在传统的注意力机制中,通过计算注意力权重来给定一个位置与序列中其他位置的关联程度。而多头注意力机制则通过同时使用多个注意力头,每个头都可以学习到不同的注意力权重,从而提供了更丰富的表达能力。

    具体而言,多头注意力机制将输入序列进行多次线性变换,得到多组查询(query)、键(key)和值(value)向量。然后,在每个注意力头中,通过计算注意力权重并将权重应用于值向量,得到每个头的输出。最后,将多个头的输出进行拼接,并通过线性变换获得最终的注意力表示。

    通过多头注意力机制,模型能够同时学习到不同位置之间的不同关系。不同的注意力头可以关注不同的语义信息,例如一个头可以关注局部的语法结构,另一个头可以关注全局的上下文语义。这样,模型能够从不同角度综合考虑输入序列的信息,提供更全面的表示能力。

    在BERT中,多头注意力机制被广泛应用于每个Transformer编码器层中的自注意力层。通过使用多个注意力头,BERT能够同时捕捉到不同关系的语义信息,提供更丰富的上下文表示。每个头学习到的注意力权重可以被看作是一种关注不同方面的“专家”,它们共同工作来提供全局的上下文理解。
    对于“头”的size计算如下
    a t t e n t i o n _ h e a d _ s i z e = i n t ( h i d d e n _ s i z e / n u m _ a t t e n t i o n _ h e a d s ) attention\_head\_size = int(hidden\_size / num\_attention\_heads) attention_head_size=int(hidden_size/num_attention_heads)
    attention_head_size是个固定值为64,来自谷歌实验,hidden_size为768,num_attention_heads为超参数固定为12,将768砍成12份(将矩阵拧了变形的操作),相当于12个模型并行运行,然后12个模型都去自注意力
    在这里插入图片描述
    除以根号64的原因是为了方便归一化(反映在算法的代码中),防止向量中的数值差距过大导致的非0即1的问题,最终的输出结果在多次变换后维度未发生改变

2.3 前馈神经网络层

前馈神经网络层(Feed-Forward Neural Network Layer):在自注意力层之后,每个Transformer编码器层都包含一个前馈神经网络层。前馈神经网络层由两个全连接层组成,通过一个非线性激活函数进行连接。这个前馈神经网络层能够对自注意力层输出的每个位置的表示进行非线性变换和映射。

2.4 残差连接层

残差连接(Residual Connection)和层归一化(Layer Normalization):在每个子层之后,BERT采用残差连接和层归一化来增强信息流动和减轻梯度消失问题。残差连接将子层的输入与子层的输出相加,从而保留了原始输入的信息。层归一化则对残差连接的结果进行归一化,使得每个子层的输入都有相似的均值和方差。

将输入前和输入后的向量加起来再给下一层输入

2.5 输出层

输出层:BERT的Encoder部分的最后一层输出被用作下游NLP任务的输入。对于分类任务,例如文本分类,可以在输出层之上添加一个全连接层和Softmax激活函数来进行预测。
sequence_output:整句话的每个字符对应的总体矩阵;pooler_output:代表整句话的一个向量

3. BERT类模型简要笔记

在这里插入图片描述

4. 代码工程实践

手动实现Bert结构

import torch
import math
import numpy as np
from transformers import BertModel

'''

通过手动矩阵运算实现Bert结构
模型文件下载 https://huggingface.co/models

'''

bert = BertModel.from_pretrained(r"D:\badou\pretrain_model\chinese_bert_likes\bert-base-chinese", return_dict=False)
state_dict = bert.state_dict()
bert.eval()
x = np.array([2450, 15486, 15167, 2110]) #通过vocab对应输入:深度学习
torch_x = torch.LongTensor([x])  #pytorch形式输入
# seqence_output, pooler_output = bert(torch_x)
# print(seqence_output.shape, pooler_output.shape)
# print(seqence_output, pooler_output)

# print(bert.state_dict().keys())  #查看所有的权值矩阵名称


#softmax归一化
def softmax(x):
    return np.exp(x)/np.sum(np.exp(x), axis=-1, keepdims=True)

#gelu激活函数
def gelu(x):
    return 0.5 * x * (1 + np.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * np.power(x, 3))))

class DiyBert:
    #将预训练好的整个权重字典输入进来
    def __init__(self, state_dict):
        self.num_attention_heads = 12
        self.hidden_size = 768
        self.num_layers = 1
        self.load_weights(state_dict)

    def load_weights(self, state_dict):
        #embedding部分
        self.word_embeddings = state_dict["embeddings.word_embeddings.weight"].numpy()
        self.position_embeddings = state_dict["embeddings.position_embeddings.weight"].numpy()
        self.token_type_embeddings = state_dict["embeddings.token_type_embeddings.weight"].numpy()
        self.embeddings_layer_norm_weight = state_dict["embeddings.LayerNorm.weight"].numpy()
        self.embeddings_layer_norm_bias = state_dict["embeddings.LayerNorm.bias"].numpy()
        self.transformer_weights = []
        #transformer部分,有多层
        for i in range(self.num_layers):
            q_w = state_dict["encoder.layer.%d.attention.self.query.weight" % i].numpy()
            q_b = state_dict["encoder.layer.%d.attention.self.query.bias" % i].numpy()
            k_w = state_dict["encoder.layer.%d.attention.self.key.weight" % i].numpy()
            k_b = state_dict["encoder.layer.%d.attention.self.key.bias" % i].numpy()
            v_w = state_dict["encoder.layer.%d.attention.self.value.weight" % i].numpy()
            v_b = state_dict["encoder.layer.%d.attention.self.value.bias" % i].numpy()
            attention_output_weight = state_dict["encoder.layer.%d.attention.output.dense.weight" % i].numpy()
            attention_output_bias = state_dict["encoder.layer.%d.attention.output.dense.bias" % i].numpy()
            attention_layer_norm_w = state_dict["encoder.layer.%d.attention.output.LayerNorm.weight" % i].numpy()
            attention_layer_norm_b = state_dict["encoder.layer.%d.attention.output.LayerNorm.bias" % i].numpy()
            intermediate_weight = state_dict["encoder.layer.%d.intermediate.dense.weight" % i].numpy()
            intermediate_bias = state_dict["encoder.layer.%d.intermediate.dense.bias" % i].numpy()
            output_weight = state_dict["encoder.layer.%d.output.dense.weight" % i].numpy()
            output_bias = state_dict["encoder.layer.%d.output.dense.bias" % i].numpy()
            ff_layer_norm_w = state_dict["encoder.layer.%d.output.LayerNorm.weight" % i].numpy()
            ff_layer_norm_b = state_dict["encoder.layer.%d.output.LayerNorm.bias" % i].numpy()
            self.transformer_weights.append([q_w, q_b, k_w, k_b, v_w, v_b, attention_output_weight, attention_output_bias,
                                             attention_layer_norm_w, attention_layer_norm_b, intermediate_weight, intermediate_bias,
                                             output_weight, output_bias, ff_layer_norm_w, ff_layer_norm_b])
        #pooler层
        self.pooler_dense_weight = state_dict["pooler.dense.weight"].numpy()
        self.pooler_dense_bias = state_dict["pooler.dense.bias"].numpy()


    #bert embedding,使用3层叠加,在经过一个embedding层
    def embedding_forward(self, x):
        # x.shape = [max_len]
        we = self.get_embedding(self.word_embeddings, x)  # shpae: [max_len, hidden_size]
        # position embeding的输入 [0, 1, 2, 3]
        pe = self.get_embedding(self.position_embeddings, np.array(list(range(len(x)))))  # shpae: [max_len, hidden_size]
        # token type embedding,单输入的情况下为[0, 0, 0, 0]
        te = self.get_embedding(self.token_type_embeddings, np.array([0] * len(x)))  # shpae: [max_len, hidden_size]
        embedding = we + pe + te
        # 加和后有一个归一化层
        embedding = self.layer_norm(embedding, self.embeddings_layer_norm_weight, self.embeddings_layer_norm_bias)  # shpae: [max_len, hidden_size]
        return embedding

    #embedding层实际上相当于按index索引,或理解为onehot输入乘以embedding矩阵
    def get_embedding(self, embedding_matrix, x):
        return np.array([embedding_matrix[index] for index in x])

    #执行全部的transformer层计算
    def all_transformer_layer_forward(self, x):
        for i in range(self.num_layers):
            x = self.single_transformer_layer_forward(x, i)
        return x

    #执行单层transformer层计算
    def single_transformer_layer_forward(self, x, layer_index):
        weights = self.transformer_weights[layer_index]
        #取出该层的参数,在实际中,这些参数都是随机初始化,之后进行预训练
        q_w, q_b, \
        k_w, k_b, \
        v_w, v_b, \
        attention_output_weight, attention_output_bias, \
        attention_layer_norm_w, attention_layer_norm_b, \
        intermediate_weight, intermediate_bias, \
        output_weight, output_bias, \
        ff_layer_norm_w, ff_layer_norm_b = weights
        #self attention层
        attention_output = self.self_attention(x,
                                q_w, q_b,
                                k_w, k_b,
                                v_w, v_b,
                                attention_output_weight, attention_output_bias,
                                self.num_attention_heads,
                                self.hidden_size)
        #bn层,并使用了残差机制
        x = self.layer_norm(x + attention_output, attention_layer_norm_w, attention_layer_norm_b)
        #feed forward层
        feed_forward_x = self.feed_forward(x,
                              intermediate_weight, intermediate_bias,
                              output_weight, output_bias)
        #bn层,并使用了残差机制
        x = self.layer_norm(x + feed_forward_x, ff_layer_norm_w, ff_layer_norm_b)
        return x

    # self attention的计算
    def self_attention(self,
                       x,
                       q_w,
                       q_b,
                       k_w,
                       k_b,
                       v_w,
                       v_b,
                       attention_output_weight,
                       attention_output_bias,
                       num_attention_heads,
                       hidden_size):
        # x.shape = max_len * hidden_size
        # q_w, k_w, v_w  shape = hidden_size * hidden_size
        # q_b, k_b, v_b  shape = hidden_size
        q = np.dot(x, q_w.T) + q_b  # shape: [max_len, hidden_size]      W * X + B lINER
        k = np.dot(x, k_w.T) + k_b  # shpae: [max_len, hidden_size]
        v = np.dot(x, v_w.T) + v_b  # shpae: [max_len, hidden_size]
        attention_head_size = int(hidden_size / num_attention_heads)
        # q.shape = num_attention_heads, max_len, attention_head_size
        q = self.transpose_for_scores(q, attention_head_size, num_attention_heads)
        # k.shape = num_attention_heads, max_len, attention_head_size
        k = self.transpose_for_scores(k, attention_head_size, num_attention_heads)
        # v.shape = num_attention_heads, max_len, attention_head_size
        v = self.transpose_for_scores(v, attention_head_size, num_attention_heads)
        # qk.shape = num_attention_heads, max_len, max_len
        qk = np.matmul(q, k.swapaxes(1, 2))
        qk /= np.sqrt(attention_head_size)
        qk = softmax(qk)
        # qkv.shape = num_attention_heads, max_len, attention_head_size
        qkv = np.matmul(qk, v)
        # qkv.shape = max_len, hidden_size
        qkv = qkv.swapaxes(0, 1).reshape(-1, hidden_size)
        # attention.shape = max_len, hidden_size
        attention = np.dot(qkv, attention_output_weight.T) + attention_output_bias
        return attention

    #多头机制
    def transpose_for_scores(self, x, attention_head_size, num_attention_heads):
        # hidden_size = 768  num_attent_heads = 12 attention_head_size = 64
        max_len, hidden_size = x.shape
        x = x.reshape(max_len, num_attention_heads, attention_head_size)
        x = x.swapaxes(1, 0)  # output shape = [num_attention_heads, max_len, attention_head_size]
        return x

    #前馈网络的计算
    def feed_forward(self,
                     x,
                     intermediate_weight,  # intermediate_size, hidden_size
                     intermediate_bias,  # intermediate_size
                     output_weight,  # hidden_size, intermediate_size
                     output_bias,  # hidden_size
                     ):
        # output shpae: [max_len, intermediate_size]
        x = np.dot(x, intermediate_weight.T) + intermediate_bias
        x = gelu(x)
        # output shpae: [max_len, hidden_size]
        x = np.dot(x, output_weight.T) + output_bias
        return x

    #归一化层
    def layer_norm(self, x, w, b):
        x = (x - np.mean(x, axis=1, keepdims=True)) / np.std(x, axis=1, keepdims=True)
        x = x * w + b
        return x

    #链接[cls] token的输出层
    def pooler_output_layer(self, x):
        x = np.dot(x, self.pooler_dense_weight.T) + self.pooler_dense_bias
        x = np.tanh(x)
        return x

    #最终输出
    def forward(self, x):
        x = self.embedding_forward(x)
        sequence_output = self.all_transformer_layer_forward(x)
        pooler_output = self.pooler_output_layer(sequence_output[0])
        return sequence_output, pooler_output


#自制
db = DiyBert(state_dict)
diy_sequence_output, diy_pooler_output = db.forward(x)
#torch
torch_sequence_output, torch_pooler_output = bert(torch_x)

print(diy_sequence_output)
print(torch_sequence_output)

# print(diy_pooler_output)
# print(torch_pooler_output)

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

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

相关文章

利用python绘制端午节的各种图案,例如粽子,赛龙舟等,以及一些端午节的感人小故事

这里写目录标题 1、关于端午节的有趣故事2、关于端午节的趣闻3、利用python绘制龙舟3.1. 代码如下3.2 图形展示 4、利用python绘制大公鸡5、利用python来进行端午节的诗词对弈总结 1、关于端午节的有趣故事 端午节是一个历史悠久的中国传统节日,有很多有趣的故事与…

内存不够用,那你的内存去哪了?

一、前言 近几年开发了一些大型的应用程序,在程序性能调优或者解决一些疑难杂症问题的过程中,遇到最多的还是与内存相关的一些问题。例如glibc内存分配器ptmalloc,google的内存分配器tcmalloc都存在“内存泄漏”,即内存不归还操作…

原来Flutter代码是这样运行在原生系统的!快来了解Flutter标准模板,感受原生系统中Flutter的魅力!

通过Android Studio创建的Flutter应用模板,了解Flutter项目结构,分析Flutter工程与原生Android和iOS工程有哪些联系,体验一个有着基本功能的Flutter应用是如何运转的,从而加深你对构建Flutter应用的关键概念和技术的理解。 Dart只…

深入理解深度学习——GPT(Generative Pre-Trained Transformer):GPT-2与Zero-shot Learning

分类目录:《深入理解深度学习》总目录 相关文章: GPT(Generative Pre-Trained Transformer):基础知识 GPT(Generative Pre-Trained Transformer):在不同任务中使用GPT GPT&#x…

软考:软件工程:软件维护与项目管理

软考:软件工程:软件维护与管理 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是需要细心准备的 &…

如何挑选合格的在线教育解决方案?

现在市面上的知识付费系统繁多,你可以说百花齐放,也可以说良莠不齐,如果不具备一定的专业素养,根本就无法从中挑选出真正的好产品,劣币驱逐良币反而成为常态。 本文将从几个常见维度分析一个好产品应该具备的基本要素…

CSS基础总结

CSS基础总结 CSS基础总结基础认知基础选择器**选择器的作用**标签选择器类选择器id选择器**通配符选择器** 字体和文本样式字体样式字体大小:font-size字体粗细:font-weight字体样式:font-style字体类型:font-family字体类型&…

【MYSQL篇】mysql性能优化总结

前言 说到MYSQL性能调优,大部分时候想要实现的目标是让我们的查询更快。一个查询的动作又是由很多个环节组成的,每个环节都会消耗时间,我们要减少查询所消耗的时间,就要从每一个环节入手。 关于MYSQL的sql语句执行流程&#xff0…

ARM-驱动/总结一

Linux设备驱动 驱动:能够控制硬件实现特定功能的软件代码就是驱动 ARM裸机驱动和驱动区别? ARM裸机驱动是不基于操作系统的软件代码,通常这份代码都是有开发者独立编写完成的。 驱动是基于内核(Linux)架构的基础上的…

chatGPT 指南:秒变 Excel 大神

Excel 是一款功能强大的电子表格软件,而 ChatGPT 则是一种智能语言模型,可以为 Excel 用户提供帮助和指导。本文将探讨 Excel 与 ChatGPT 的关系,并从初级、中级和高级 Excel 用户三个层次,介绍如何利用 ChatGPT 来提升 Excel 技能…

leetcode416. 分割等和子集(动态规划-java)

分割等和子集 leetcode416. 分割等和子集题目描述 暴力递归代码演示 动态规划解题思路代码演示 动态规划专题 leetcode416. 分割等和子集 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/partition-equal-subset-sum 题目…

高级数据结构——平衡二叉树(AVL树)

目录 1. 底层结构 2. AVL数的概念 3. AVL树节点的定义 4. 基本框架 5. AVL树的插入 6. AVL树的旋转 6.1 左单旋 6.2 右单旋 6.3 左右双旋 6.4 右左双旋 7. AVL树的验证 8. AVL树的查找 9. AVL树的删除 10. AVL树的性能 11. 总代码 11.1 AVLTree 11.2 Test.c…

mac本地创建ssh key连接github

起因 今天克隆自己github上面的笔记到新电脑上,用http连接进行克隆,然后要我输入账号密码,输入完报了个提示“remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.”…

JavaScript 手写代码 第一期

文章目录 1.为什么要手写代码?2.手写代码2.1 手写Object.create()方法2.1.1 基本使用2.1.2 使用实例2.1.3 手写实现 2.2 手写实现instanceof方法2.2.1 基本使用2.2.2 使用实例2.2.3 手写实现 2.3 手写实现new操作符2.3.1 基本使用2.3.2 使用实例2.3.3 手写实现 1.为…

分享一个下载按钮

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>下载按钮</title><link href"https://fonts.googleapis.com/css2?familyHind&amp;d…

Redisson源码-单线程加解锁流程

Redisson源码-单线程加解锁流程 以下源码分析基于redisson-3.17.6版本&#xff0c;不同版本源码会有些许不同需注意。 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.17.6</version>&l…

推荐5 款好用的 Linux 音乐播放器

目前 Linux 上有几十个音乐播放器&#xff0c;这使得找到一个最好用的变成很困难。之前我们已经回顾了其中的一些播放器&#xff0c;如 Cantata&#xff0c;Exaile&#xff0c;甚至不那么出名的 Clementine&#xff0c;Nightingale 和 Quod Libet。 在本篇文章中我将涵盖更多的…

python学习——pandas数据处理 时间序列案例 matplotlib绘图案例

目录 pandas数据处理1.合并数据1) 堆叠合并2) 主键合并3) 重叠合并 2.分组和聚合3.索引和符合索引4.去除重复值5.处理缺失值6.处理离群值7.标准化数据1) 离差标准化函数2) 标准差标准化函数3) 小数定标差标准化函数 8.转换数据--离散处理9.时间序列【案例】时间序列案例案例1&a…

C++测试

开始对C嘎嘎下手&#xff01; 1.有关char数组 定义长度为5&#xff0c;但是实际长度是定义长度减1 突然就想到计网安全中的栈溢出问题了&#xff0c;C语言是不检查你是否越界的&#xff0c;如果通过让实参溢出覆盖掉原程序的返回地址&#xff0c;通过精心控制是可以让计算机执…

高级数据结构——红黑树

目录 1. 红黑树的概念 2. 红黑树的性质 3. 红黑树 6. 红黑树的验证 7. 红黑树的删除 8. 红黑树与AVL数的比较 9. 红黑树的应用 10. 完整代码 10.1 RBTree.h 10.2 test.cpp 1. 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存…