快速学习一个算法,Transformer模型架构

news2024/11/23 18:51:39

今天给大家分享一个超强的算法模型,Transformer

Transformer 模型是目前自然语言处理(NLP)以及计算机视觉等领域中应用非常广泛的深度学习模型架构。

它由 Vaswani 等人在 2017 年的论文《Attention is All You Need》中提出,并迅速取代了传统的循环神经网络(RNN)和长短期记忆网络(LSTM)在许多任务中的主导地位。
在这里插入图片描述

Transformer 模型的关键创新点是其自注意力机制,它通过自注意力机制解决了长期依赖问题,极大地提高了并行计算的效率。

图片

Transformer 模型架构

Transformer 模型主要由编码器(Encoder)和解码器(Decoder)两部分组成。

图片

编码器

编码器主要负责将输入序列转化为一组上下文相关的高维表示。

它由多个相同的层组成,每个层都有两个主要子层:

  • 多头自注意力层
  • 前馈神经网络

此外,每个子层都包含残差连接和层归一化,以增强稳定性和性能。

解码器

解码器利用编码器的输出生成目标序列。

解码器的结构与编码器类似,包含多个相同的层,但解码器的每一层有三个子层

  • 掩蔽自注意力层
  • 编码器-解码器注意力层
  • 前馈神经网络

核心组件

下面,我们一起来看一下 transformer 中的核心组件。

*1.输入嵌入*

输入嵌入层的作用是将输入序列的每个词转换为高维空间中的向量表示。

在自然语言处理中,通常使用预训练的词向量如 Word2Vec 或 GloVe,或者在训练过程中与模型一起学习嵌入。

这些嵌入能够捕捉词汇的语义信息并将其转换为固定大小的向量,通常大小为 512。

图片

*2.位置编码*

由于 Transformer 完全基于注意力机制并且不使用任何循环结构,它需要一种方法来利用输入序列中词的顺序信息。

位置编码通过将一个相对或绝对位置的编码添加到输入嵌入中来实现。

位置编码常使用正弦和余弦函数,其公式如下所示:

这里,pos 表示词的位置, 表示维度索引, 是嵌入维度。

图片

*3.多头自注意力机制*

多头自注意力机制是 Transformer 的核心,它允许模型在不同的表示空间中关注输入序列的不同部分。

它的主要思想是在同一时间通过多个独立的注意力头(Attention Head)来关注序列中不同部分的信息,然后将这些信息综合起来,生成更丰富的表示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*自注意力机制*

在讨论多头自注意力机制之前,首先需要理解自注意力(Self-Attention)机制。

自注意力机制的目的是对输入序列中的每个元素计算一个输出,这个输出是其他所有元素的加权求和,权重由当前元素与序列中其他元素的相关度决定。

自注意力机制的计算过程包括以下几个步骤。

  • 计算查询(Query)、键(Key)和值(Value)

    对于序列中的每个元素 ,通过三个线性变换分别映射为查询向量 、键向量 和值向量 。

    其中,、 和 是可学习的权重矩阵。

  • 计算注意力分数

    通过计算其查询向量 与所有键向量 的点积,得到该元素与其他元素的相关性(即注意力得分)

    为了稳定训练过程,这些分数会除以 ,其中 是键向量的维度。

  • 计算注意力权重

    将注意力分数通过 Softmax 函数转换为注意力权重,使得它们和为1。

  • 计算注意力输出

    注意力输出是值向量的加权和。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多头注意力机制通过并行地计算多组查询、键和值,来捕捉输入序列中不同子空间的依赖关系。

具体来说,它将自注意力机制的计算过程复制 h 次,每次使用不同的查询、键、值的线性变换。

最后,将这些头的输出拼接起来,并通过一个线性变换得到最终的输出。

其中,每个 是自注意力机制的输出, 是一个可学习的线性变换矩阵。

*4.前馈神经网络*

每个编码器和解码器层中,除了自注意力机制,还有一个独立的前馈神经网络(FFN),它用于对自注意力输出的结果进行进一步的非线性变换。

FFN 由两层线性变换和一个激活函数(通常是 ReLU)组成

其中

  • 和 是线性变换的权重矩阵
  • 和 是偏置向量

FFN 的作用是通过非线性映射提升模型的表达能力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*5.层归一化和残差连接*

为了加速训练并解决梯度消失或爆炸的问题,Transformer 在每个子层(包括自注意力机制和前馈神经网络)后面引入了残差连接和层归一化。

  • 残差连接

    将输入直接加到子层的输出上,保持信息传递流畅

  • 层归一化

    对每个子层的输出进行归一化处理,提升模型的稳定性

    其中, 和 分别是输入的均值和标准差

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*6.Masked 多头自注意力子层*

Masked 多头自注意力子层用于解码器的生成任务。

在生成过程中,模型生成当前时间步的单词时,只能依赖之前生成的单词,而不能看到未来未生成的单词。

为了确保这一点,Masked 多头自注意力机制通过在注意力矩阵中对未来位置进行掩码,使其权重为负无穷,从而保证模型只能“关注”到之前的位置。

具体来说,在计算注意力得分时,对未来的位置进行屏蔽,将这些位置的得分设为负无穷大,使得 Softmax 归一化后的权重为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*7.编码器-解码器多头注意力子层*

编码器-解码器多头注意力子层在 Transformer 解码器中起到了关键作用,它使解码器能够有效地关注输入序列(编码器的输出),从而在生成序列时参考原始输入信息。

具体来说,编码器-解码器多头注意力的基本思想是通过对编码器输出(Key 和 Value)和解码器当前输入(Query)来生成新的表示。

这种机制使得解码器能够在生成序列时动态地选择性关注输入序列的不同部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例代码

下面,我们从头开始构建一个 Transformer 模型。

首先,我们来创建一个 MultiHeadAttention 类,它实现了 Transformer 中的多头自注意力机制。

import numpy as np
class MultiHeadAttention:
    def __init__(self, num_hiddens: int, num_heads: int, dropout: float = 0.0, bias: bool = False):
        self.num_heads = num_heads
        self.num_hiddens = num_hiddens
        self.d_k = self.d_v = num_hiddens // num_heads
        
        self.W_q = np.random.rand(num_hiddens, num_hiddens)
        self.W_k = np.random.rand(num_hiddens, num_hiddens)
        self.W_v = np.random.rand(num_hiddens, num_hiddens)
        self.W_o = np.random.rand(num_hiddens, num_hiddens)
        
        if bias:
            self.b_q = np.random.rand(num_hiddens)
            self.b_k = np.random.rand(num_hiddens)
            self.b_v = np.random.rand(num_hiddens)
            self.b_o = np.random.rand(num_hiddens)
        else:
            self.b_q = self.b_k = self.b_v = self.b_o = np.zeros(num_hiddens)

    def transpose_qkv(self, X: np.ndarray) -> np.ndarray:
        X = X.reshape(X.shape[0], X.shape[1], self.num_heads, -1)
        X = X.transpose(0, 2, 1, 3)
        return X.reshape(-1, X.shape[2], X.shape[3])
    
    def transpose_output(self, X: np.ndarray) -> np.ndarray:
        X = X.reshape(-1, self.num_heads, X.shape[1], X.shape[2])
        X = X.transpose(0, 2, 1, 3)
        return X.reshape(X.shape[0], X.shape[1], -1)
    
    def scaled_dot_product_attention(self, Q: np.ndarray, K: np.ndarray, V: np.ndarray, valid_lens: np.ndarray = None) -> np.ndarray:
        d_k = Q.shape[-1]
        scores = np.matmul(Q, K.transpose(0, 2, 1)) / np.sqrt(d_k)
        if valid_lens is not None:
            mask = np.arange(scores.shape[-1]) < valid_lens[:, None]
            scores = np.where(mask[:, None, :], scores, -np.inf)
        attention_weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
        attention_weights /= attention_weights.sum(axis=-1, keepdims=True)
        return np.matmul(attention_weights, V)
    
    def forward(self, queries: np.ndarray, keys: np.ndarray, values: np.ndarray, valid_lens: np.ndarray = None) -> np.ndarray:
        queries = self.transpose_qkv(np.dot(queries, self.W_q) + self.b_q)
        keys = self.transpose_qkv(np.dot(keys, self.W_k) + self.b_k)
        values = self.transpose_qkv(np.dot(values, self.W_v) + self.b_v)
        
        if valid_lens is not None:
            valid_lens = np.repeat(valid_lens, self.num_heads, axis=0)
        
        output = self.scaled_dot_product_attention(queries, keys, values, valid_lens)
        output_concat = self.transpose_output(output)
        return np.dot(output_concat, self.W_o) + self.b_o

在 forword 方法中,首先对 queries、keys 和 values 进行线性变换,分别生成查询、键和值向量。

然后通过 scaled_dot_product_attention 方法计算每个注意力头的输出。

接下来,调用 transpose_output 方法将多个注意力头的输出拼接起来。

最后,拼接后的输出通过 矩阵进行线性变换,并加上偏置 ,得到最终的输出。

下面,我们来看一下 位置编码的实现。

def positional_encoding(seq_len: int, d_model: int) -> np.ndarray:
    pos = np.arange(seq_len)[:, np.newaxis]
    i = np.arange(d_model)[np.newaxis, :]
    angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
    pos_encoding = pos * angle_rates
    
    pos_encoding[:, 0::2] = np.sin(pos_encoding[:, 0::2])
    pos_encoding[:, 1::2] = np.cos(pos_encoding[:, 1::2])
    
    return pos_encoding

然后,我们创建一个前馈网络。

class FeedForward:
    def __init__(self, d_model: int, d_ff: int):
        self.W1 = np.random.randn(d_model, d_ff) * np.sqrt(2.0 / (d_model + d_ff))
        self.b1 = np.zeros(d_ff)
        self.W2 = np.random.randn(d_ff, d_model) * np.sqrt(2.0 / (d_ff + d_model))
        self.b2 = np.zeros(d_model)

    def __call__(self, x: np.ndarray) -> np.ndarray:
        return self.forward(x)

    def forward(self, x: np.ndarray) -> np.ndarray:
        return np.dot(np.maximum(0, np.dot(x, self.W1) + self.b1), self.W2) + self.b2

接下来,我们构建一个编码器层,将多头注意力机制与前馈神经网络相结合,构成了 Transformer 模型的核心构建块之一。

class EncoderLayer:
    def __init__(self, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.0, bias: bool = False):
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_ff = d_ff
        
        self.multi_head_attention = MultiHeadAttention(d_model, num_heads, dropout, bias)
        self.feed_forward = FeedForward(d_model, d_ff)

    def __call__(self, x: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        return self.forward(x, mask)

    def forward(self, x: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
    
        attn_output = self.multi_head_attention.forward(x, x, x, mask)
        output = self.feed_forward(attn_output)
        return output

然后,我们来构建一个解码器层,它包括两个多头注意力机制和一个前馈网络。

class DecoderLayer:
    def __init__(self, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.0, bias: bool = False):
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_ff = d_ff
        
        self.multi_head_attention_1 = MultiHeadAttention(d_model, num_heads, dropout, bias)
        self.multi_head_attention_2 = MultiHeadAttention(d_model, num_heads, dropout, bias)
        self.feed_forward = FeedForward(d_model, d_ff)
    def __call__(self, x: np.ndarray, enc_output: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        return self.forward(x, enc_output, mask)
    def forward(self, x: np.ndarray, enc_output: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        attn_output1 = self.multi_head_attention_1.forward(x, x, x, mask)
        attn_output2 = self.multi_head_attention_2.forward(attn_output1, enc_output, enc_output, mask)
        output = self.feed_forward(attn_output2)
        return output

最后,我们创建一个 Transformer 类 ,它集成了编码器和解码器层。

class Transformer:
    def __init__(self, d_model: int, num_heads: int, d_ff: int, num_layers: int, input_vocab_size: int, target_vocab_size: int, max_seq_len: int):
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_ff = d_ff
        self.num_layers = num_layers
        self.input_vocab_size = input_vocab_size
        self.target_vocab_size = target_vocab_size
        self.max_seq_len = max_seq_len
        
        self.encoder_layers = [EncoderLayer(d_model, num_heads, d_ff) for _ in range(num_layers)]
        self.decoder_layers = [DecoderLayer(d_model, num_heads, d_ff) for _ in range(num_layers)]
        
        self.embedding = np.random.randn(input_vocab_size, d_model) * np.sqrt(2.0 / (input_vocab_size + d_model))
        self.pos_encoding = positional_encoding(max_seq_len, d_model)
        self.output_layer = np.random.randn(d_model, target_vocab_size) * np.sqrt(2.0 / (d_model + target_vocab_size))

    def __call__(self, input_seq: np.ndarray, target_seq: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        
        return self.forward(input_seq, target_seq, mask)

    def forward(self, input_seq: np.ndarray, target_seq: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        
        enc_output = self.encode(input_seq, mask)
        dec_output = self.decode(target_seq, enc_output, mask)
        output = np.dot(dec_output, self.output_layer)
        return output

    def encode(self, input_seq: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        
        seq_len = input_seq.shape[1]
        x = self.embedding[input_seq] + self.pos_encoding[:seq_len, :]
        
        for layer in self.encoder_layers:
            x = layer(x, mask)
        
        return x

    def decode(self, target_seq: np.ndarray, enc_output: np.ndarray, mask: np.ndarray = None) -> np.ndarray:
        
        seq_len = target_seq.shape[1]
        x = self.embedding[target_seq] + self.pos_encoding[:seq_len, :]
        
        for layer in self.decoder_layers:
            x = layer(x, enc_output, mask)
        
        return x
        
# Example
d_model = 512
num_heads = 8
d_ff = 2048
num_layers = 6
input_vocab_size = 10000
target_vocab_size = 10000
max_seq_len = 100

# Create a transformer model
transformer = Transformer(d_model, num_heads, d_ff, num_layers, input_vocab_size, target_vocab_size, max_seq_len)

# Dummy input and target sequences
input_seq = np.random.randint(0, input_vocab_size, (32, 50))
target_seq = np.random.randint(0, target_vocab_size, (32, 50))

# Forward pass through the model
output = transformer(input_seq, target_seq)
print(output.shape)  # Should be (batch_size, target_seq_len, target_vocab_size)
#(32, 50, 10000)

最后

最后如果您也对AI大模型感兴趣想学习却苦于没有方向👀
小编给自己收藏整理好的学习资料分享出来给大家💖
👉获取方式:

😝有需要的小伙伴,可以保存图片到wx扫描二v码关注免费领取【保证100%免费】🆓

在这里插入图片描述

👉AI大模型学习路线汇总👈

大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)
在这里插入图片描述

第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;

第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;

第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;

第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;

第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;

第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;

第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。

👉如何学习AI大模型?👈

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
在这里插入图片描述

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!
在这里插入图片描述

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。
在这里插入图片描述

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。
在这里插入图片描述

四、AI大模型商业化落地方案

在这里插入图片描述

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

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

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

相关文章

【智能大数据分析 | 实验三】Storm实验:实时WordCountTopology

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…

并查集的实现(朴素版)

这是C算法基础-数据结构专栏的第二十九篇文章&#xff0c;专栏详情请见此处。 由于作者即将参加CSP&#xff0c;所以到比赛结束前将不再发表文章&#xff01; 引入 并查集是一种可以快速合并查找集合的一种数据结构&#xff0c;这次我们将通过三道题来详细讲解并查集&#xff…

迈普pnsr2900x DOWNLOAD_FILE 任意文件读取漏洞

0x01 产品描述&#xff1a; ‌ 迈普NSR2900X系列是一款专为军队、政府、金融、中小型企业分支机构和中小型企业总部设计的信创接入路由器。‌ 该路由器采用国产核心元器件&#xff0c;基于国产操作系统运行迈普自主研发的网络操作系统及应用软件。它全面支持IPv4、IPv6、OS…

insert into values 语句优化

insert into values插入单行数据 SQL语句&#xff0c;insert into values插入单行数据&#xff0c;执行10万次&#xff0c;执行时间1279秒&#xff0c;优化总体执行耗时。 SQL文本&#xff0c;单行insert values&#xff0c;没有select部分。需要进一步分析执行过程消耗。 ins…

软考《信息系统运行管理员》- 5.1 信息系统数据资源维护体系

5.1 信息系统数据资源维护体系 文章目录 5.1 信息系统数据资源维护体系数据资源维护的管理对象数据资源维护的管理类型运行监控故障响应数据备份归档检索数据优化 数据资源维护的管理内容维护方案例行管理应急响应数据资源的开发与利用 数据是信息系统管理的对象与结果&#xf…

7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡

一、板卡概述 本板卡系我公司自主研发&#xff0c;基于6U CPCI的通用高性能信号处理平台。板卡采用一片国产8核DSP FT-C6678和一片国产FPGA JFM7K325T-2FFG900作为主处理器。为您提供了丰富的运算资源。如下图所示&#xff1a; 二、设计参考标准 ● PCIMG 2.0 R3.0 CompactP…

Python酷库之旅-第三方库Pandas(147)

目录 一、用法精讲 666、pandas.Timestamp.astimezone方法 666-1、语法 666-2、参数 666-3、功能 666-4、返回值 666-5、说明 666-6、用法 666-6-1、数据准备 666-6-2、代码示例 666-6-3、结果输出 667、pandas.Timestamp.ceil方法 667-1、语法 667-2、参数 667…

基础篇:带你打开Vue的大门(一)

学习目标&#xff1a; 理解Vue的基本概念&#xff1a;掌握Vue.js是什么&#xff0c;它的设计理念&#xff0c;以及它在现代Web开发中的应用。掌握Vue的基本语法&#xff1a;学习Vue的基础指令和语法&#xff0c;能够使用Vue构建简单的交互式界面。熟悉Vue组件化开发&#xff1…

DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中?

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 原文链接&#xff1a;DBA | 如何将 .bak 的数据库备份文件导入到SQL Server 数据库中? 如何将&#xff08;.bak&#xff09;的SQL Server 数据库备份文件导入到当前数据库中? Step 1.登录到 Sql…

Centos7安装RocketMQ[图文教程]

文章目录 RocketMQ介绍基于Linux服务部署RocketMQ&#xff08;单机&#xff09;配置JDK环境下载RocketMQ部署RocketMQ1、解压2、修改VM参数3、配置环境变量4、编写Service文件5、启动服务 基于Docker方式部署RocketMQ安装Docker编写docker-compose文件启动RocketMQ服务 部署Roc…

前端学习-css的背景(十六)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 背景颜色 语法格式 背景图片 语法格式 背景平铺 语法格式 背景图片位置 语法格式 参数代表的意思 参数是方位名词 参数是精确单位 参数是混合单位 背…

架构设计笔记-11-未来信息综合技术

知识要点 云原生架构原则包括&#xff1a;服务化原则、弹性原则、可观测原则、韧性原则、所有过程自动化原则、零信任原则和架构持续演进原则。 区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构&#xff0c;并以密码学方式保证的不可篡改和不可…

【项目案例】-音乐播放器-Android前端实现-Java后端实现

精品专题&#xff1a; 01.C语言从不挂科到高绩点 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. SpringBoot详细教程 https://blog.csdn.ne…

项目管理系统如何助力新药研发?药物研发企业康诺亚上线瑞杰项目管理系统

在新药研发过程中&#xff0c;其特点是&#xff1a;周期长、风险高、投入大&#xff0c;同时还要与其他科学相结合&#xff0c;相互渗透、更加需要多部门的共同参与&#xff0c;因此面临的问题相对复杂&#xff0c;而且要求也比较高。所以在这一过程中&#xff0c;必须对新药研…

软考系统分析师知识点十一:系统规划

前言 今年报考了11月份的软考高级&#xff1a;系统分析师。 考试时间为&#xff1a;11月9日。 倒计时&#xff1a;26天。 目标&#xff1a;优先应试&#xff0c;其次学习&#xff0c;再次实践。 复习计划第一阶段&#xff1a;扫平基础知识点&#xff0c;仅抽取有用信息&am…

49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?

上一篇文章我们学习了第一种结构型模式&#xff1a;代理模式。它在不改变原始类&#xff08;或者叫被代理类&#xff09;代码的情况下&#xff0c;通过引入代理类来给原始类附加功能。代理模式在平时的开发经常被用到&#xff0c;常用在业务系统中开发一些非功能性需求&#xf…

嵌入式~CAN-专辑2

我自己的原文哦~ 只发CAN相关2 随时更新~~ 一、CAN总线错误分析与解决 从实际工作中碰到的具体问题来分析一些常见的CAN总线错误和解决办法。 CAN节点数据收发过程 我们知道&#xff0c;CAN总线上的每个节点往总线上发送数据的同时&#xff0c;会读取总线上的数据&#x…

stm32单片机个人学习笔记10(TIM编码器接口)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

简单实现手机投屏到电脑代码

1、从手机截图到sdcard 2、将图片导出到PC 3、从PC加载图片 4、开启定时器 1、 private static void takeScreenshot(String path) {long t1 System.currentTimeMillis();String command "adb devices"; // 替换为你需要执行的shell命令String command1 "…

氧化锆ZrO2纳米颗粒50nm|L-ZrO2@mSiO2|Ir1-N-C/ZrO2|AuPd/HB-ZrO2

氧化锆ZrO2纳米颗粒50nm|L-ZrO2mSiO2|Ir1-N-C/ZrO2|AuPd/HB-ZrO2 氧化锆&#xff08;ZrO₂&#xff09;纳米颗粒&#xff0c;特别是直径为50纳米&#xff08;nm&#xff09;的颗粒&#xff0c;是一种具有多种应用前景的功能材料。这种材料因其独特的物理和化学性质&#xff0c…