Transformer详解(附代码实现及翻译任务实现)

一:了解背景和动机

  1. 阅读Transformer论文:

    阅读原始的Transformer论文:“Attention is All You Need”,由Vaswani等人于2017年提出,是Transformer模型的开创性工作。
    在这里插入图片描述

二:理解基本构建块

  1. 注意力机制:

    Transformer的核心在于自注意力机制。它允许模型在处理每个词时考虑句子中的所有其他词,从而有效捕获长距离依赖关系。这是通过计算查询(Q)、键(K)和值(V)之间的关系实现的,其中注意力分数是通过以下公式计算得出的:
    Attention(Q, K, V) = softmax ( QK T d k ) V \text{Attention(Q, K, V)} = \text{softmax}\left(\frac{\text{QK}^T}{\sqrt{d_k}}\right)\text{V} Attention(Q, K, V)=softmax(dk QKT)V
    在这里插入图片描述

  2. 位置编码:

    在Transformer模型中,由于自注意力机制并不关注输入序列中元素的顺序,为了使模型能够处理序列数据的顺序信息,引入了位置编码(Positional Encoding)。

    位置编码的主要目的是为模型提供一些关于输入序列中元素相对位置的信息。由于Transformer没有内置的对序列顺序的理解,位置编码的添加有助于模型区分不同位置的词或输入。位置编码通常是通过在输入嵌入向量中添加一个与位置相关的向量来实现的。这个向量的设计通常遵循一些规律,以便模型能够通过学习位置编码来理解输入序列的顺序。一种常见的方式是使用正弦和余弦函数:

    位置 p o s pos pos 和嵌入维度 i i i 之间的位置编码 P E ( p o s , 2 i ) PE(pos, 2i) PE(pos,2i) P E ( p o s , 2 i + 1 ) PE(pos, 2i+1) PE(pos,2i+1) 分别计算如下:

    P E ( p o s , 2 i ) = sin ⁡ ( p o s 1000 0 2 i / d ) P E ( p o s , 2 i + 1 ) = cos ⁡ ( p o s 1000 0 2 i / d ) PE(pos, 2i) = \sin\left(\frac{{pos}}{{10000^{2i/d}}}\right) \\ PE(pos, 2i+1) = \cos\left(\frac{{pos}}{{10000^{2i/d}}}\right) PE(pos,2i)=sin(100002i/dpos)PE(pos,2i+1)=cos(100002i/dpos)
    其中,(pos) 是位置,(i) 是维度的索引,(d) 是嵌入的维度。这样设计的位置编码能够使得不同位置之间的编码有一些规律性的差异,以便模型学习到序列的顺序信息。

    在实际实现中,这个位置编码会被直接添加到输入嵌入向量中。这样,通过学习嵌入向量和位置编码之间的权重,模型就可以同时利用嵌入向量的语义信息和位置编码的顺序信息。以下是一个简化的Python代码示例,演示了如何生成位置编码:

    import torch
    import torch.nn as nn
    import math
    
    class PositionalEncoding(nn.Module):
        def __init__(self, d_model, max_len=512):
            super(PositionalEncoding, self).__init__()
            self.encoding = torch.zeros(max_len, d_model)
            position = torch.arange(0, max_len).unsqueeze(1).float()
            div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
            self.encoding[:, 0::2] = torch.sin(position * div_term)
            self.encoding[:, 1::2] = torch.cos(position * div_term)
            self.encoding = self.encoding.unsqueeze(0)
    
        def forward(self, x):
            return x + self.encoding[:, :x.size(1)].detach()
    
    # Example usage
    d_model = 512
    max_len = 100
    positional_encoding = PositionalEncoding(d_model, max_len)
    
    # Assuming input is a tensor of shape (batch_size, sequence_length, d_model)
    input_sequence = torch.rand((32, 50, d_model))
    output_sequence = positional_encoding(input_sequence)
    

    这个示例中,PositionalEncoding 模块可以添加到Transformer的输入嵌入中。你可以根据实际任务和模型的需要调整嵌入维度和序列的最大长度。

三:学习Transformer模型的结构

  1. Encoder和Decoder结构:

    • 理解Transformer模型的整体结构,包括Encoder和Decoder。了解Encoder中多头注意力(Multi-Head Attention)和前馈神经网络(Feedforward Network)的作用,以及Decoder中的掩码多头注意力。
    Encoder结构:
    1. 多层自注意力机制(Multi-Head Self Attention):
      • 每个注意力头学习不同的注意力表示,从而捕捉输入序列中不同位置的信息。
      • 每个注意力头的输出通过线性层进行变换和组合。
    2. 前馈神经网络(Feedforward Neural Network):
      • 每个注意力头的输出通过一个全连接前馈神经网络进行非线性映射。
    3. 层归一化(Layer Normalization)和残差连接(Residual Connection):
      • 在每个子层(自注意力和前馈神经网络)的输出后都应用层归一化和残差连接。
      • 这有助于梯度的流动和训练稳定性。

    Decoder结构:

    1. 多层自注意力机制(Multi-Head Self Attention):
      • 类似于Encoder,但在Decoder中,自注意力机制要注意到输入序列和输出序列的不同位置。
    2. 多层编码-解码注意力机制(Multi-Head Encoder-Decoder Attention):
      • 将Encoder的输出用作Query,将Decoder的自注意力输出用作Key和Value。这允许Decoder关注输入序列的不同部分。
    3. 前馈神经网络(Feedforward Neural Network):
      • 与Encoder中的结构类似,用于处理解码器的注意力输出。
    4. 层归一化(Layer Normalization)和残差连接(Residual Connection):
      • 同样,在每个子层后应用层归一化和残差连接。
  2. Layer Normalization 和残差连接:

    • 学习如何在Transformer的层中使用Layer Normalization和残差连接。

    1. Layer Normalization(层归一化):

    层归一化是一种用于神经网络中的归一化技术,其目的是减少内部协变量转移(Internal Covariate Shift)。在每个训练小批量上,层归一化对每个特征进行归一化,使其均值为零,标准差为一。

    在Transformer中,Layer Normalization通常在每个子层的输出上应用,例如自注意力层或前馈神经网络层。其数学表达式如下:
    L a y e r N o r m ( x ) = γ ( ( x − μ ) / σ ) + β LayerNorm(x)=γ((x−μ)/σ)+β LayerNorm(x)=γ((xμ)/σ)+β
    其中,x 是输入张量,μσ 分别是其均值和标准差,γβ 是可学习的缩放和平移参数。

    2. 残差连接(Residual Connection):

    残差连接是一种通过将输入直接添加到输出上的机制,用于解决梯度消失问题。在Transformer的每个子层的输出后都使用了残差连接。其数学表达式如下:
    O u t p u t = I n p u t + S u b l a y e r ( I n p u t ) Output=Input+Sublayer(Input) Output=Input+Sublayer(Input)
    其中,Sublayer(⋅) 是子层的变换,可以是自注意力、编码-解码注意力或前馈神经网络等。

四:使用现有的实现进行实践

  1. PyTorch或TensorFlow实现:

    • 使用PyTorch或TensorFlow,了解如何实现一个简单的Transformer模型。可以参考开源实现或教程。
    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    
    class MultiHeadAttention(nn.Module):
        def __init__(self, d_model, num_heads):
            super(MultiHeadAttention, self).__init__()
            self.d_model = d_model
            self.num_heads = num_heads
    
            self.Q = nn.Linear(d_model, d_model)
            self.K = nn.Linear(d_model, d_model)
            self.V = nn.Linear(d_model, d_model)
            self.fc_out = nn.Linear(d_model, d_model)
    
        def forward(self, Q, K, V, mask):
            # Q, K, V: (batch_size, seq_len, d_model)
            Q = self.Q(Q)
            K = self.K(K)
            V = self.V(V)
    
            Q = self.split_heads(Q)
            K = self.split_heads(K)
            V = self.split_heads(V)
    
            scores = torch.matmul(Q, K.transpose(-1, -2)) / torch.sqrt(torch.tensor(self.d_model, dtype=torch.float32))
            if mask is not None:
                scores = scores.masked_fill(mask == 0, float("-inf"))
    
            attention = F.softmax(scores, dim=-1)
            x = torch.matmul(attention, V)
            x = self.combine_heads(x)
            x = self.fc_out(x)
            return x
    
        def split_heads(self, x):
            return x.view(x.size(0), -1, self.num_heads, self.d_model // self.num_heads).transpose(1, 2)
    
        def combine_heads(self, x):
            return x.transpose(1, 2).contiguous().view(x.size(0), -1, self.num_heads * (self.d_model // self.num_heads))
    
    class PositionwiseFeedforward(nn.Module):
        def __init__(self, d_model, d_ff, dropout=0.1):
            super(PositionwiseFeedforward, self).__init__()
            self.linear1 = nn.Linear(d_model, d_ff)
            self.dropout = nn.Dropout(dropout)
            self.linear2 = nn.Linear(d_ff, d_model)
    
        def forward(self, x):
            x = F.relu(self.linear1(x))
            x = self.dropout(x)
            x = self.linear2(x)
            return x
    
    class PositionalEncoding(nn.Module):
        def __init__(self, d_model, max_len=512):
            super(PositionalEncoding, self).__init__()
            self.encoding = torch.zeros(max_len, d_model)
            position = torch.arange(0, max_len).unsqueeze(1).float()
            div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(math.log(10000.0) / d_model))
            self.encoding[:, 0::2] = torch.sin(position * div_term)
            self.encoding[:, 1::2] = torch.cos(position * div_term)
            self.encoding = self.encoding.unsqueeze(0)
    
        def forward(self, x):
            return x + self.encoding[:, :x.size(1)].detach()
    
    class TransformerEncoderLayer(nn.Module):
        def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
            super(TransformerEncoderLayer, self).__init__()
            self.self_attention = MultiHeadAttention(d_model, num_heads)
            self.feedforward = PositionwiseFeedforward(d_model, d_ff, dropout)
            self.layer_norm1 = nn.LayerNorm(d_model)
            self.layer_norm2 = nn.LayerNorm(d_model)
            self.dropout = nn.Dropout(dropout)
    
        def forward(self, x, mask):
            attention_output = self.self_attention(x, x, x, mask)
            x = x + self.dropout(attention_output)
            x = self.layer_norm1(x)
            
            feedforward_output = self.feedforward(x)
            x = x + self.dropout(feedforward_output)
            x = self.layer_norm2(x)
            return x
    
    class TransformerEncoder(nn.Module):
        def __init__(self, vocab_size, d_model, num_heads, d_ff, num_layers, max_len=512, dropout=0.1):
            super(TransformerEncoder, self).__init__()
            self.embedding = nn.Embedding(vocab_size, d_model)
            self.positional_encoding = PositionalEncoding(d_model, max_len)
            self.layers = nn.ModuleList([
                TransformerEncoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)
            ])
    
        def forward(self, x, mask):
            x = self.embedding(x)
            x = self.positional_encoding(x)
            for layer in self.layers:
                x = layer(x, mask)
            return x
    
    class TransformerDecoderLayer(nn.Module):
        def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
            super(TransformerDecoderLayer, self).__init__()
            self.self_attention = MultiHeadAttention(d_model, num_heads)
            self.encoder_decoder_attention = MultiHeadAttention(d_model, num_heads)
            self.feedforward = PositionwiseFeedforward(d_model, d_ff, dropout)
            self.layer_norm1 = nn.LayerNorm(d_model)
            self.layer_norm2 = nn.LayerNorm(d_model)
            self.layer_norm3 = nn.LayerNorm(d_model)
            self.dropout = nn.Dropout(dropout)
    
        def forward(self, x, encoder_output, src_mask, tgt_mask):
            attention_output = self.self_attention(x, x, x, tgt_mask)
            x = x + self.dropout(attention_output)
            x = self.layer_norm1(x)
    
            encoder_attention_output = self.encoder_decoder_attention(x, encoder_output, encoder_output, src_mask)
            x = x + self.dropout(encoder_attention_output)
            x = self.layer_norm2(x)
    
            feedforward_output = self.feedforward(x)
            x = x + self.dropout(feedforward_output)
            x = self.layer_norm3(x)
    
            return x
    
    class TransformerDecoder(nn.Module):
        def __init__(self, vocab_size, d_model, num_heads, d_ff, num_layers, max_len=512, dropout=0.1):
            super(TransformerDecoder, self).__init__()
            self.embedding = nn.Embedding(vocab_size, d_model)
            self.positional_encoding = PositionalEncoding(d_model, max_len)
            self.layers = nn.ModuleList([
                TransformerDecoderLayer(d_model, num_heads, d_ff, dropout) for _ in range(num_layers)
            ])
            self.fc_out = nn.Linear(d_model, vocab_size)
    
        def forward(self, x, encoder_output, src_mask, tgt_mask):
            x = self.embedding(x)
            x = self.positional_encoding(x)
            for layer in self.layers:
                x = layer(x, encoder_output, src_mask, tgt_mask)
            x = self.fc_out(x)
            return x
    
    class Transformer(nn.Module):
        def __init__(self, src_vocab_size, tgt_vocab_size, d_model, num_heads, d_ff, num_layers, max_len=512, dropout=0.1):
            super(Transformer, self).__init__()
            self.encoder = TransformerEncoder(src_vocab_size, d_model, num_heads, d_ff, num_layers, max_len, dropout)
            self.decoder = TransformerDecoder(tgt_vocab_size, d_model, num_heads, d_ff, num_layers, max_len, dropout)
    
        def forward(self, src_input, tgt_input, src_mask, tgt_mask):
            encoder_output = self.encoder(src_input, src_mask)
            decoder_output = self.decoder(tgt_input, encoder_output, src_mask, tgt_mask)
            return decoder_output
    
    # Example usage:
    src_vocab_size = 1000
    tgt_vocab_size = 1000
    d_model = 512
    num_heads = 8
    d_ff = 2048
    num_layers = 6
    max_len = 100
    dropout = 0.1
    
    model = Transformer(src_vocab_size, tgt_vocab_size, d_model, num_heads, d_ff, num_layers, max_len, dropout)
    

五:深入细节

  1. 超参数调整:

    • 学习调整Transformer模型的超参数,包括学习率、层数、隐藏单元数等。
  2. 更深入的理解多头注意力:

    • 了解多头注意力是如何工作的,以及它如何在不同的子空间上学到不同的表示。

    多头注意力机制的工作原理:

    考虑一个输入序列 X X X,通过线性变换分别生成 Q u e r y ( Q ) Query(Q) QueryQ K e y ( K ) Key(K) KeyK V a l u e ( V ) Value(V) ValueV的表示。对于每个注意力头 i i i,通过计算注意力分数并对Value进行加权,得到头 i i i 的输出 O i O_i Oi

    多头注意力的输出 O O O 是所有注意力头输出的拼接:
    O = C o n c a t ( O 1 , O 2 , . . . , O h ) O=Concat(O_1,O_2,...,O_h) O=Concat(O1,O2,...,Oh)
    其中, h ℎ h 是注意力头的数量。

    学习不同表示的子空间:

    每个注意力头都有独立的权重矩阵 ( W i Q , W i K , W i V ) (W_i^Q,W_i^K,W_i^V) (WiQ,WiK,WiV),这意味着每个头可以学到不同的表示。这种独立性使得每个头都能够关注输入序列的不同部分,捕捉不同方面的信息。

    举例来说,考虑一个翻译任务,输入是一个包含动词的句子。不同的注意力头可以分别关注主语、宾语、谓语等不同的语法成分,从而更好地捕捉句子的语法结构。

六:应用到实际任务

  1. 语言建模或翻译任务:

    • 尝试将学到的Transformer模型应用于语言建模或翻译任务。使用开源的语料库和模型预训练,然后微调模型以适应你的任务。
    import torch
    import torch.nn as nn
    import torch.optim as optim
    from torchtext.data import Field, BucketIterator
    from torchtext.datasets import Multi30k
    from torch.nn import Transformer
    
    # 设置随机种子以保证可重复性
    torch.manual_seed(42)
    
    # 定义Field对象
    SRC = Field(tokenize='spacy', tokenizer_language='en', init_token='<sos>', eos_token='<eos>', lower=True)
    TRG = Field(tokenize='spacy', tokenizer_language='fr', init_token='<sos>', eos_token='<eos>', lower=True)
    
    # 加载Multi30k数据集
    train_data, valid_data, test_data = Multi30k.splits(exts=('.en', '.fr'), fields=(SRC, TRG))
    
    # 构建词汇表
    SRC.build_vocab(train_data, min_freq=2)
    TRG.build_vocab(train_data, min_freq=2)
    
    # 定义Transformer模型
    class TransformerModel(nn.Module):
        def __init__(self, src_vocab_size, trg_vocab_size, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6):
            super(TransformerModel, self).__init__()
            self.embedding = nn.Embedding(src_vocab_size, d_model)
            self.transformer = Transformer(d_model, nhead, num_encoder_layers, num_decoder_layers)
            self.fc = nn.Linear(d_model, trg_vocab_size)
    
        def forward(self, src, trg):
            src = self.embedding(src)
            trg = self.embedding(trg)
            output = self.transformer(src, trg)
            output = self.fc(output)
            return output
    
    # 初始化模型和优化器
    model = TransformerModel(len(SRC.vocab), len(TRG.vocab))
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    criterion = nn.CrossEntropyLoss()
    
    # 定义训练函数
    def train(model, iterator, optimizer, criterion):
        model.train()
        for batch in iterator:
            src = batch.src
            trg = batch.trg
            optimizer.zero_grad()
            output = model(src, trg)
            output = output.view(-1, output.shape[-1])
            trg = trg.view(-1)
            loss = criterion(output, trg)
            loss.backward()
            optimizer.step()
    
    # 构建BucketIterator
    BATCH_SIZE = 32
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    train_iterator, valid_iterator, test_iterator = BucketIterator.splits(
        (train_data, valid_data, test_data), 
        batch_size=BATCH_SIZE, 
        device=device)
    
    # 训练模型
    for epoch in range(10):
        train(model, train_iterator, optimizer, criterion)
    
    # 在测试集上评估模型(简化,实际应用中需要更详细的评估过程)
    def evaluate(model, iterator, criterion):
        model.eval()
        total_loss = 0
        with torch.no_grad():
            for batch in iterator:
                src = batch.src
                trg = batch.trg
                output = model(src, trg)
                output = output.view(-1, output.shape[-1])
                trg = trg.view(-1)
                loss = criterion(output, trg)
                total_loss += loss.item()
        return total_loss / len(iterator)
    
    test_loss = evaluate(model, test_iterator, criterion)
    print(f'Test Loss: {test_loss:.3f}')
    

七:关注最新研究

  1. 阅读最新的Transformer相关论文:

    • 随着研究的不断进展,关注最新的Transformer相关的研究论文,了解模型的演进和新的应用。

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

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

相关文章

C++面试宝典第21题:字符串解码

题目 给定一个经过编码的字符串,返回其解码后的字符串。具体的编码规则为:k[encoded_string],表示方括号内部的encoded_string正好重复k次。注意:k保证为正整数;encoded_string只包含大小写字母,不包含空格和数字;方括号确定是匹配的,且可以嵌套。 示例: 编码字符串为…

tcpdump常用参数以及wireshark密文解密

tcpdump常用参数以及wireshark密文解密 文章目录 一、tcpdump命令和常用参数二、在wireshark中协议解析 tcpdump常用参数 一、tcpdump命令和常用参数 tcpdump常用命令&#xff1a;tcpdump -i eth0 src host 11.6.224.1 and udp port 161 -s 0 -w 161.pcap &#xff08;161为sn…

开发知识点-JAVA-springboot

springboot springbootConfiguration注解的底层核心原理Bean注解的底层核心原理 springboot Configuration注解的底层核心原理 https://www.bilibili.com/video/BV1rq4y1E7gK/?spm_id_from333.999.0.0&vd_sourcef21773b7086456ae21a58a6cc59023be spring.io 全家桶 24…

Mysql 数据库DDL 数据定义语言——数据库,数据表的创建

DDL&#xff1a;数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;—Database Definition Language 1、登录数据库&#xff0c;输入用户名和密码 mysql -ufdd -p990107Wjl2、查看数据库 show databases;3、创建一个…

主流人工智能AI工具测评

主流人工智能AI工具测评 主流的人工智能AI工具ChatGPT ——OpenAI研发CHAT_BISON——Google研发Qwen通义千问 ——阿里云研发文心一言——百度研发 根据10个问题分析人工智能的回答女朋友生气了怎么哄千元机性价比推荐小米13 和 vivo iQOO 11s哪个好计算机专业毕业论文护士年终…

node.js(expree.js )模拟手机验证码功能及登录功能

dbconfig.js const mysql require(mysql) module.exports {// 数据库配置config: {host: localhost, // 连接地址port: 3306, //端口号user: root, //用户名password: wei630229, //密码database: exapp2, //数据库名}, // 连接数据库&#xff0c;使用mysql的连接池连接方式…

transfomer的位置编码

什么是位置编码 在transformer的encoder和decoder的输入层中&#xff0c;使用了Positional Encoding&#xff0c;使得最终的输入满足&#xff1a; input_embeddingpositional_encoding 这里&#xff0c;input_embedding的shape为[n,b,embed_dim],positional_encoding和input_…

xilinxi mulitboot 启动

xilinix在线更新有两种方式&#xff0c;一种是使用ICAP原语&#xff0c;另一中是在xdc中约束&#xff0c;根据应用的场景不同&#xff0c;选用不同的启动方式&#xff0c;第二种更为简单。 可参考官方提供的手册和实例 XAPP1247 链接&#xff1a; XAPP1247 golden和updata.b…

VitePress-01-从零开始的项目创建(npm版)

说明 本文介绍一下 VitePress的项目创建的步骤。 主要用到的命令工具是 npm。 本文的操作步骤是从无到有的创建一个完整的基本的【VitePress】项目。 环境准备 根据官方文档的介绍&#xff0c;截止本文发稿时&#xff0c;需要使用node.js 18 的版本。 可以使用node -v 的命令查…

muduo网络库剖析——通道Channel类

muduo网络库剖析——通道Channel类 前情从muduo到my_muduo 概要事件种类channel 框架与细节成员函数细节实现使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否可以高效满足&#xff1b;而作为学习者&#x…

Asp .Net Core 系列:集成 Ocelot+Consul实现网关、服务注册、服务发现

什么是Ocelot? Ocelot是一个开源的ASP.NET Core微服务网关&#xff0c;它提供了API网关所需的所有功能&#xff0c;如路由、认证、限流、监控等。 Ocelot是一个简单、灵活且功能强大的API网关&#xff0c;它可以与现有的服务集成&#xff0c;并帮助您保护、监控和扩展您的微…

RTSP/Onvif安防视频监控平台EasyNVR漏洞扫描及解决方法

视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。安防视频监控平台可提供视频实时监控直播、云端录像、云存储、录像检索与回看、告警等视频…

【前端架构】前端通用架构

一个强大的前端通用架构应该具备多种能力&#xff0c;以支持现代化的应用程序开发和提高开发效率。以下是一些前端通用架构应该具备的关键能力&#xff1a; 模块化和组件化&#xff1a;支持模块化开发和组件化架构&#xff0c;能够将应用拆分为独立的模块和组件&#xff0c;以便…

外观模式(结构型)

目录 一、前言 二、外观模式 三、总结 一、前言 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它为系统中的一组复杂子系统提供一个简单的接口&#xff0c;从而隐藏了这些子系统的复杂性&#xff0c;并且使得代码更加易于使用和理解。 外…

XUbuntu22.04之免费开源DesktopNaotu脑图(二百零七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

5个99%的人可能不知道的实用程序库!

前言 作为一名前端开发者,这些 JavaScript 库极大地提高了我的工作效率,如格式化日期、处理 URL 参数和调试移动网页。朋友们,我想和你们分享这些库。 1. 使用 “Day.js” 来格式化日期和时间 链接 作为开发者,我已经厌倦了在 JavaScript 中操作日期和时间,因为它太麻烦了。…

MySQL的多表数据记录查询笔记

关系数据操作 合并查询数据记录 在MySQL中通过关键字UNION来实现并操作&#xff0c;即可以通过其将多个SELECT语句的查询结果合并在一起组成新的关系。 两张表&#xff0c;表1 和表2 带有关键字UNION的合并操作 关键字UNION会把查询结果集直接合并在一起&#xff0c;同时将…

vite和webpack的区别

1 构建原理 Webpack 是一个静态模块打包器&#xff0c;通过对项目中的 JavaScript、CSS、图片等文件进行分析&#xff0c;生成对应的静态资源&#xff0c;并且可以通过一些插件和加载器来实现各种功能。Webpack 的主要特点是支持各种复杂的构建场景&#xff0c;例如代码分割、…

vs2022配置OpenCV测试

1&#xff0c;下载Opencv安装包 OpenCV官网下载地址&#xff1a;Releases - OpenCV 大家可以按需选择版本进行下载&#xff0c;官网下载速度还是比较慢的&#xff0c;推荐大家使用迅雷进行下载 下载安装包到自定义文件夹下 双击安装 按以下图示进行安装 2、 添加环境变量 打…

【HarmonyOS】消息通知场景的实现

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…