NLP自然语言处理04 transformer架构模拟实现

总体架构

输入部分

代码实现:

导包

# -*-coding:utf-8-*-
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
# -*-coding:utf-8-*-
import copy
import torch.nn.functional as F
import math
位置编码器部分
词嵌入WordEmbedding

# todo 作用:输入数据进行词嵌入升维处理
class Embeddings(nn.Module):def __init__(self, vocab_size, embed_dim):super().__init__()# vocab_size:代表单词的总个数self.vocab_size = vocab_size# embed_dim:代表词嵌入维度self.embed_dim = embed_dim# 定义Embedding层self.embed = nn.Embedding(vocab_size, embed_dim)def forward(self, x):# x--》[batch_size, seq_len]return self.embed(x) * math.sqrt(self.embed_dim)
位置编码模型PositionEncoding

# todo 作用:生成位置编码矩阵,与输入数据x进行融合,并输出-->加入了位置编码信息的词嵌入张量
class PositionEncoding(nn.Module):def __init__(self, d_model, dropout_p, max_len=60):super().__init__()# d_model:代表词嵌入维度self.d_model = d_model# dropout_p:代表随机失活的系数self.dropout_p = dropout_p# max_len:代表最大句子长度self.max_len = max_len# 定义dropout层self.dropout = nn.Dropout(p=dropout_p)# 根据三角函数的公式实现位置的编码# 定义位置编码矩阵[max_len, d_model]-->[60, 512]pe = torch.zeros(max_len, d_model)# 定义位置列矩阵--》[max_len, 1]-->[60, 1]position = torch.arange(0, max_len).unsqueeze(dim=1)# 定义转换矩阵:根据三角函数的计算公式,是其中的除了pos之外的系数(频率)# temp_vec-->[256]temp_vec = torch.exp(torch.arange(0, d_model, 2) * -(math.log(10000)/d_model))# 根据三角函数的计算公式,计算角度:pos_vec-->[60, 256]pos_vec = position * temp_vec# 将奇数位用sin处理,偶数位用cos处理pe[:, 0::2] = torch.sin(pos_vec)pe[:, 1::2] = torch.cos(pos_vec)# 需要对上述的位置编码结果升维:pe-->[1, max_len, d_model]-->[1, 60, 512]#todo  pe就是位置编码矩阵 似乎每次结果一样pe = pe.unsqueeze(dim=0)# pe位置编码结果不随着模型的训练而更新,因此需要进行注册到缓存区self.register_buffer('pe', pe)def forward(self, x):# x--》来自于embedding之后的结果--》[batch_size, seq_len, embed_dim]-->[2, 4, 512]# 将x和位置编码的信息进行融合# todo x.size()[1]是指句子有多长pe就取出多长与x相加,pe的形状为[1, max_len, d_model]-->[1, 4, 512]x = x + self.pe[:, :x.size()[1]]return self.dropout(x)

编码器部分

掩码矩阵部分:生成掩码矩阵
# 生成一个下三角矩阵(sentence_mask)
def generate_triu(size):# a = np.triu(m=np.ones((1, size, size)), k=1).astype(int)# return torch.from_numpy(1-a)return 1-torch.triu(torch.ones(1, size, size, dtype=torch.int), 1)# 生成掩码矩阵(padding_mask)
def generate_padding_mask(tensor_x):# tensor_x-->注意力权重分数--》张量tensor_x[tensor_x == 0] = 0tensor_x[tensor_x != 0] = 1return tensor_x.to(dtype=torch.int)# 绘图:生成下三角矩阵
def show__triu():plt.figure(figsize=(5, 5))plt.imshow(generate_triu(20)[0])plt.show()
attention:基础注意力计算方式,muti_head_atten将调用次模组
def attention(query, key, value, mask=None, dropout=None):# query/key/value-->[batch_size, seq_len, embed_dim]# mask-->shape-->[batch_size, seq_len, seq_len]# dropout--》实例化的对象# 第一步:获得词嵌入表达的维度d_k = query.size(-1)# 第二步:计算query和key之间的相似性分数(注意力权重分数(未经过softmax归一化的结果))# query-->[2, 4, 512];key-->[2, 4, 512]-->转置--》[2, 512,4]. 相乘后--》scores-->[2, 4, 4]scores = torch.matmul(query, torch.transpose(key, -1, -2)) / math.sqrt(d_k)# 第三步:判断是否需要maskif mask is not None:scores = scores.masked_fill(mask==0, -1e9)# print(f'未归一化的scores--》{scores}')# 第四步:进行softmax归一化atten_weights = F.softmax(scores, dim=-1)# print(f'atten_weights--》{atten_weights}')# 第五步:如果有dropout 就进行随机失活防止过拟合if dropout is not None:atten_weights = dropout(atten_weights)return torch.matmul(atten_weights, value), atten_weights # todo 返回注意力输出,以及注意力权重
多头注意力类与clones

多头注意力机制原理(核心)

1. ​​输入与线性变换​

输入序列(如词向量)通过三个独立的线性变换层生成查询(Query, Q)、键(Key, K)和值(Value, V)矩阵:

  • ​自注意力机制​​:输入为同一矩阵 X,通过不同权重矩阵 WQh​,WKh​,WVh​ 生成Q、K、V。
  • ​交叉注意力机制​​:输入为两个不同矩阵(如 Xq​ 和 Xkv​),分别生成Q和K、V

 

2. ​​分头处理与并行计算​

  • ​分头​​:将Q、K、V按头的数量 h 拆分为多个子矩阵,每个子矩阵对应一个注意力头。例如,将 Q 拆分为 [Q1​,Q2​,...,Qh​],每个 Qi​ 的维度为 dk​ 。
  • ​并行计算​​:每个头独立计算缩放点积注意力(Scaled Dot-Product Attention):

3. ​​多头输出的拼接与融合​

  • ​拼接​​:将所有头的输出 head1​,head2​,...,headh​ 沿特征维度拼接,形成组合输出。
  • ​线性变换​​:通过权重矩阵 WO​ 将拼接后的结果映射回原始维度:

 

        原论文中是先把qkv从[2,6,512]变成[2,6,8,64]后各经过8个权重矩阵总共24个权重矩阵得到变换后的qkv再进行注意力计算再concat拼接起来

        这里的代码实现是qkv[[2,6,512]]各经过1个矩阵总共3个矩阵得到[2,6,512]再变成[2,6,8,64]进行注意力计算再concat拼接起来

编码器实例化一个多头注意力类且无masked


# clones 的作用:将一个模块复制N次,并返回一个ModuleList,ModuleList是一个Module的子类,可以迭代,并且可以保存多个Module
def clones(module, N):return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])# todo:2. 定义多头注意力类,注意 编码器层的q=k=v=原句子 解码器层的mask_muti_head输入的q=k=v=预测的句子 解码层的第二个muti_head的q=k=编码器输出,v=mask_muti_head输出
class MutiHeadAttention(nn.Module):def __init__(self, head, embed_dim, dropout_p=0.1):super().__init__()# 第一步:确定embed_dim是否能被head整除assert embed_dim % head == 0# 第二步:确定每个head应该处理多少维度特征self.d_k = embed_dim // head# 第三步:定义head的属性self.head = head# 第四步:定义4个全连接层self.linears = clones(nn.Linear(embed_dim, embed_dim), 4)# 第五步:定义atten权重属性self.atten = None# 第六步:实例化dropout对象self.dropout = nn.Dropout(p=dropout_p)def forward(self, query, key, value, mask=None):# 需要对mask的形状进行升维度# mask-->输入的形状--》[head, seq_len, seq_len]-->[8, 4, 4],升维之后--》[1, 8, 4, 4]if mask is not None:mask = mask.unsqueeze(dim=0)# 获取当前输入的batch_sizebatch_size = query.size(0)# 开始处理query,key,value,都要经过线性变化并且切分为8个头# model(x)-->就是将数据经过linear层处理x-->[2, 4, 512]-->经过Linear-->[2, 4, 512]-->分割--》[2, 4, 8, 64]-->transpose-->[2, 8, 4, 64]# query,key,value--》shape-->[2, 8, 4, 64]query, key, value = [model(x).view(batch_size, -1, self.head, self.d_k).transpose(1, 2)for model, x in zip(self.linears, (query, key, value))]# 接下来将上述处理后的query,key,value--》shape-->[2, 8, 4, 64]送入attention方法进行注意力的计算:# query--》[2, 8, 4, 64]和key--》[2, 8, 4, 64]转置结果[2, 8, 64, 4]进行相乘--》shape--》[2,8, 4, 4](所以传的mask矩阵是4维的)# [2, 8, 4, 4]要和value-->[2, 8, 4, 64]-->相乘--》shape--》x-->[2, 8, 4, 64]x, self.atten = attention(query, key, value, mask=mask, dropout=self.dropout)# 需要将多头注意力的结果进行合并#  x.transpose(1, 2)-->【2,4, 8, 64】# y 合并后的结果-->[2, 4, 512]y = x.transpose(1, 2).contiguous().view(batch_size, -1, self.head*self.d_k)# 经过线性变化得到指定输出维度的结果return self.linears[-1](y)
前馈全连接层

两层线性层 作用:进行特征提取,进行非线性映射,简单来说就是经过两个线性层一个relu激活函数加入非线性,再dropout随机失活防止过拟合

class FeedForward(nn.Module):def __init__(self, d_model, d_ff, dropout_p=0.1):super().__init__()# d_model:第一个全连接层输入的特征维度;第二个全连接层输出的特征维度self.d_model = d_model# d_ff: 第一个全连接层输出的特征维度;第二个全连接层输入的特征维度self.d_ff = d_ff# 定义第一个全连接层self.linear1 = nn.Linear(d_model, d_ff)# 定义第二个全连接层self.linear2 = nn.Linear(d_ff, d_model)# 定义dropout层self.dropout = nn.Dropout(p=dropout_p)def forward(self, x):return self.linear2(self.dropout(F.relu(self.linear1(x))))
规范化层

让数据符合标准正态分布 作用机制:self.a * (x - x_mean) / (x_std + self.eps) + self.b, eps:防止分母为0

Add中是把原始经过embedding+position后得到的x与经过(多头注意力层或者前馈全连接层)再进行规范化之后的结果进行相加得到残差链接

残差链接的作用:通过跨层连接(如恒等映射),梯度可直接通过“捷径”回传,避免因多层非线性变换导致的信号衰减或放大


class LayerNorm(nn.Module):def __init__(self, features, eps=1e-6):super().__init__()# 定义属性self.features = features # 代表词嵌入维度# epsself.eps = eps# 定义一个模型的参数(系数)self.a = nn.Parameter(torch.ones(features))self.b = nn.Parameter(torch.zeros(features))def forward(self, x):# x--->[2, 4, 512]# 1.求出均值:x_mean-->[2, 4, 1]x_mean = torch.mean(x, dim=-1, keepdim=True)# 2.求出标准差x_std = torch.std(x, dim=-1, keepdim=True)return self.a * (x - x_mean) / (x_std + self.eps) + self.b
子层链接结构

定义子层连接结构 把norm&add这一层与feedforward层或者muti_head_atten层进行连接,取决与输入的sublayer是什么层

class SublayerConnection(nn.Module):def __init__(self, size, dropout_p=0.1):super().__init__()# 定义size属性:词嵌入的维度大小self.size = size# 实例化规范化层self.layer_norm = LayerNorm(features=size)# 实例化dropout层self.dropout = nn.Dropout(p=dropout_p)def forward(self, x, sublayer):# x--》来自于输入部分:positionEncoding+WordEmbedding;[batch_size, seq_len, embed_dim]-->[2, 4, 512]# sublayer-->代表函数的对象:可以是处理多头自注意力机制函数的对象,也可以是前馈全连接层对象# post_normx1 = x + self.dropout(self.layer_norm(sublayer(x)))# pre_norm# x1 = x + self.dropout(sublayer(self.layer_norm(x)))return x1
编码器层

定义编码器层 #超级拼装:先试用子层链接拼成(norm&add+feedforward)层与(muti_head_atten+norm&add)层 把这两个子层拼起来就是编码器结构

输入的是输入部分:positionEncoding+WordEmbedding;[batch_size, seq_len, embed_dim]-->[2, 4, 512],输出的是编码器的结果-->送给解码器当k和v使用

class EncoderLayer(nn.Module):def __init__(self, size, self_atten, feed_forward, dropout_p):super().__init__()# size:代表词嵌入的维度self.size = size# self_atten:代表多头自注意力机制的对象self.self_atten = self_atten# feed_forward:代表前馈全连接层的对象self.feed_forward = feed_forward# 定义两层子层连接结构self.sub_layers = clones(SublayerConnection(size, dropout_p), 2)def forward(self, x, mask):# x-->来自输入部分--》[batch_size, seq_len, embed_dim]:[2, 4, 512]# mask-->[head, seq_len, seq_len]-=-->[8, 4, 4]# 经过第一个子层连接结构:先经过多头自注意力层--》然后经过norm-->最后残差连接x1 = self.sub_layers[0](x, lambda x: self.self_atten(x, x, x, mask))# 经过第二个子层连接结构:先经过前馈全连接层--》然后经过norm-->最后残差连接x2 = self.sub_layers[1](x1, self.feed_forward)return x2
编码器

定义编码器 超级拼装2.0 n个编码器层构成一个编码器,按照这里的代码,多个编码器层是串联执行,上一个编码器的输出作为下一个编码器的输入,最终输出编码器的结果给解码器当v使用

class Encoder(nn.Module):def __init__(self, layer, N):super().__init__()# layer:代表编码器层self.layer = layer# N:代表有几个编码器层# 定义N个编码器层self.layers = clones(layer, N)# 实例化规范化层self.norm = LayerNorm(features=layer.size)def forward(self, x, mask):# x-->来自输入部分--》[batch_size, seq_len, embed_dim]:[2, 4, 512]# mask-->[head, seq_len, seq_len]-=-->[8, 4, 4]# for循环迭代N个编码器层得到最终的结果for layer in self.layers:x = layer(x, mask)return self.norm(x)

解码器部分

解码器层

依旧超级拼装:先试用子层链接拼成(norm&add+feedforward)层,(muti_head_atten+norm&add)层,(mask_muti_head_atten+norm&add)层 ,然后把这三个子层拼起来就是解码器结构

class DecoderLayer(nn.Module):def __init__(self, size, self_atten, src_atten, feed_forward, dropout_p):super().__init__()# size:代表词嵌入维度的大小self.size = size# self_atten:自注意力机制的对象:Q=K=Vself.self_atten = self_atten# src_atten:一般注意力机制的对象:Q!=K=Vself.src_atten = src_atten# feed_forward:前馈全连接层对象self.feed_forward = feed_forward# 定义三个子层连接结构self.sub_layers = clones(SublayerConnection(size, dropout_p), 3)def forward(self, y, encoder_output, source_mask, target_mask):# y:代表解码器的输入--》[batch_size, seq_len, embed_dim]# encoder_output:代表编码器的输出结果--》[batch_size, seq_len, emebed_dim]# target_mask防止未来信息被提前看到/target_mask-->[head, y_seq_len, y_seq_len]# source_mask消除padding的影响# source_mask--shape-->[head, y_seq_len, x_seq_len]# 经过第一个子层连接结构 todo 看图写作:第一个子层连接结构是带mask掩码滴,输入是q=k=v==预测值y的positionEncoding+WordEmbedding输出,y1 = self.sub_layers[0](y, lambda x: self.self_atten(x, x, x, target_mask))# 经过第二个子层连接结构 todo 第二个子层链接是不带mask掩码,输入的k=v==(源文本嵌入+位置编码)再经过编码器的输出,v是第一个子层结构的输出# query--》[2,6,512]-->[2, 8, 6, 64],key/value-->[2, 4, 512]-->[2, 8, 4, 64]# [2, 8, 6, 64]--和[2, 8, 4, 64]转置[2,8, 64, 4]-->[2, 8, 6, 4]y2 = self.sub_layers[1](y1, lambda x: self.src_atten(x, encoder_output, encoder_output, source_mask))# 经过第三个子层连接结构 todo 这一层就是feed+norm&add 输入什么维度输出就是什么维度y3 = self.sub_layers[2](y2, self.feed_forward)return y3
解码器

拼拼拼:n个解码器层构成一个解码器,这里的n==6 按照这里的代码,多个解码器层是串联执行,上一个解码器的输出作为下一个解码器的输入,最终输出解码器的结果给输出层

class Decoder(nn.Module):def __init__(self, layer, N):super().__init__()# layer:代表解码器层self.layer = layer# N:代表有几个解码器层# 定义N个解码层self.layers = clones(layer, N)# 实例化规范化层self.norm = LayerNorm(features=layer.size)def forward(self, y, encoder_output, source_mask, target_mask):# y:代表解码器的输入--》[batch_size, seq_len, embed_dim]# encoder_output:代表编码器的输出结果--》[batch_size, seq_len, emebed_dim]# target_mask防止未来信息被提前看到/target_mask-->[head, y_seq_len, y_seq_len]# source_mask消除padding的影响# source_mask--shape-->[head, y_seq_len, x_seq_len]# for循环迭代N个编码器层得到最终的结果for layer in self.layers:y = layer(y, encoder_output, source_mask, target_mask)return self.norm(y)

输出部分

生成器generator

输出部分:将解码器输出经过一个线性层,再经过softmax,得到当前预测的结果

输出[batch_size,seq_len,vocab_size] vocab_size是词表词个数,概率最大的为预测结果

class Generator(nn.Module):def __init__(self, d_model, vocab_size):# 参数d_model 线性层输入特征尺寸大小# 参数vocab_size 线层输出尺寸大小super(Generator, self).__init__()# 定义线性层self.project = nn.Linear(d_model, vocab_size)def forward(self, x):# 数据经过线性层 最后一个维度归一化 log方式x = F.log_softmax(self.project(x), dim=-1)return x

使用部分

模拟使用,仅预测一个单词
源文本输入:也就是要翻译的文本,两个句子4个单词
x = torch.tensor([[1, 40, 28, 100], [45, 89, 39, 10]])
上一步预测值输入:如果为头单词,则为sos_token的词向量表达
y0 = torch.tensor([[2, 4, 10, 29, 67, 89],[34, 56, 78, 20, 19, 6]])
词表大小:要翻译的语言总共有多少个单词
vocab_size = 1000
词向量维度
embed_dim = 512

总体流程概述:

编码器部分:源文本输入x经过embedding后与positionnal_encoding相加结果输入encoder()

在每一个编码器层经过两个子层:

1.多头自注意力子层+(残差链接+规范化)

在编码器的多头注意力层中q=k=v

  1. 前馈全连接层+(残差链接+规范化)

前馈全连接层的作用: 通过增加两层网络来增强模型的能力.

输出x1

经过n个编码器层后输出xn给解码器

解码器部分:前一步的实际值或预测值 y0 = torch.tensor([[2, 4, 10, 29, 67, 89],[34, 56, 78, 20, 19, 6]])经过embedding后与positionnal_encoding相加输入结果decoder()

在每一个解码器层经过三个子层:

1.带掩码的多头自注意力子层+(残差链接+规范化)

在此层q=k=v mask的作用是防止未来信息被提前看见

2.多头注意力子层+(残差链接+规范化)

在此层k=v = 编码器的输出xn , q=上一个子层的输入

  1. 前馈全连接子层+(残差链接+规范化)

前馈全连接层的作用: 通过增加两层网络来增强模型的能力.

经过n个解码器层后输出yn给输出部分

输出部分:经过一个线性层和一个softmax层

线性层:通过对上一步的线性变化得到指定维度的输出, 也就是转换维度的作用.

softmax层:使最后一维的向量中的数字缩放到0-1的概率值域内, 并满足他们的和为1.

输出预测值result[batch_size,seq_len,vocab_size],batch_size句seq_len个单词,其中概率最大的值为预测结果

def usb_position(): #todo 生成位置编码vocab_size = 1000 # 定义词汇大小embed_dim = 512 # 词嵌入维度my_embed = Embeddings(vocab_size, embed_dim)x = torch.tensor([[1, 40, 28, 100], [45, 89, 39, 10]])embed_result = my_embed(x)my_position = PositionEncoding(d_model=512, dropout_p=0.1)position_result = my_position(embed_result)print(f'position_result-->{position_result.shape}')# print(f'position_result-->{position_result}')return position_result
def use_encoder():  # todo 输入编码器输入(待翻译的句子)以及位置编码,得到编码器输出# 获取编码器输入部分:[2, 4, 512]position_result = usb_position()# 实例化多头注意力机制对象mutiHead_atten = MutiHeadAttention(head=8, embed_dim=512)# 实例化前馈全连接层对象ff = FeedForward(d_model=512, d_ff=1024)mask = torch.zeros(8, 4, 4)#  实例化编码器层对象encoder_layer = EncoderLayer(size=512, self_atten=mutiHead_atten, feed_forward=ff, dropout_p=0.1)#  实例化编码器对象encoder = Encoder(layer=encoder_layer, N=6)# 将数据送入编码器 todo position_result先encoder_output = encoder(position_result,  mask)print(f'encoder_output编码器得到的结果--》{encoder_output}')print(f'encoder_output编码器得到的结果--》{encoder_output.shape}')# todo 编码器输出return encoder_output
def use_decoder():  #todo 输入编码器输出,得到解码器输出# 定义解码器端的输入 todo 编码器的输入是对应原始输入序列(如待翻译的源语言句子),解码器输入的是解码器的输入是右移(shifted right)的目标序列(如已生成的部分目标语言句子)。# todo 解码器的输出是解码器逐步生成的目标序列(如翻译结果),每次预测一个词。y0 = torch.tensor([[2, 4, 10, 29, 67, 89],[34, 56, 78, 20, 19, 6]])vocab_size = 1000embed_dim = 512# 实例化Embedding层embed = Embeddings(vocab_size, embed_dim)# embed_y-->[2, 6, 512]embed_y = embed(y0)# # 实例化PositionEncoding层position_encode = PositionEncoding(d_model=512, dropout_p=0.1)# todo position_y 是预测的目标序列,位置编码对预测结果进行编码,从而提高预测效果。若为首字母则对应sos_tokenposition_y = position_encode(embed_y)# 实例化多头注意力机制的对象muti_head_atten = MutiHeadAttention(head=8, embed_dim=512)self_atten = copy.deepcopy(muti_head_atten)src_atten = copy.deepcopy(muti_head_atten)# 实例化前馈全连接的对象ff = FeedForward(d_model=512, d_ff=1024)#  实例化解码器层的对象decoder_layer = DecoderLayer(size=512, self_atten=self_atten, src_atten=src_atten, feed_forward=ff, dropout_p=0.1)# 准备数据# todo encoder_output是编码器输出结果,对应源语言句子encoder_output = use_encoder()source_mask = torch.zeros(8, 6, 4)target_mask = torch.zeros(8, 6, 6)#  实例化解码器的对象decoder = Decoder(layer=decoder_layer, N=6)result = decoder(position_y, encoder_output, source_mask, target_mask)# print(f'解码器得到的结果--》{result}')# print(f'解码器得到的结果--》{result.shape}')return resultdef use_generator():x=use_decoder()my_generator  = Generatorresult = my_generator(512, 1000)(x)print(f'生成器得到结果--》{result}')print(f'生成器得到结果--》{result.shape}')return result
if __name__ == '__main__':# use_decoder()use_generator()

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

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

相关文章

记录一本书: Python机器学习:基于PyTorch和Scikit-Learn

记录一本书: Python机器学习:基于PyTorch和Scikit-Learn 作者:(美)塞巴斯蒂安拉施卡(Sebastian Raschka)(美)刘玉溪(海登)(Yuxi(Hayden)Liu) (美)…

Datomic数据库简介(TBC)

Datomic 数据库详细介绍 Datomic 是一个由 Rich Hickey(Clojure 语言创始人)设计的 不可变、时间感知、分布式数据库,专为现代应用程序设计,强调 数据不变性(immutability)、查询灵活性和可审计性。它结合…

xformers 完整安装教程【pip conda】(解决 conda 版本 pytorch 自适应安装 xformers)

我个人喜欢用 mamba(conda)创建环境,然后用 mamba 安装 pytorch CUDA(如果需要使用 CUDA 编译),还有一些比如 gcc/g 等与 python 无关的一些工具。 但是最近我在扩充环境的时候,发现需要额外安…

VM虚拟机全版本网盘+免费本地网络穿透端口映射实时同步动态家庭IP教程

VM虚拟机全版本秘钥,文章末尾。 首先网络穿透的意义是让公网可以直接访问家庭电脑,这样本地电脑的硬件性能得以完全发挥,特别是在云服务器贵性能又没家庭电脑好,专线宽带又贵,第三方网络穿透贵的场景下。一般第三方网…

C++ - 仿 RabbitMQ 实现消息队列--项目介绍与环境搭建

目录 项目介绍 开发环境 技术选型 环境搭建 安装 wget(一般情况下默认会自带) 更换国内软件源 安装 lrzsz 传输工具 安装编译器 安装项目构建工具 make 安装调试器 安装 git 安装 cmake 安装 Protobuf 安装 Muduo 安装 SQLite3 安装 Gtest 项目介绍 首先说一下…

《目标检测模块实践手册:从原理到落地的尝试与分享》第一期

大家好,欢迎来到《目标检测模块实践手册》系列的第一篇。从今天开始,我想以一种 “实践记录者” 的身份,和大家聊聊在目标检测任务中那些形形色色的模块。这些内容没有权威结论,更多的是我在实际操作中的一些尝试、发现和踩过的坑…

C++11笑传之引用

C11前言列表初始化{}进行初始化initializer_list右值引用和移动语义左值与右值左值引用与右值引用引用延长生命周期右值引用和移动语义的使用场景左值引用移动构造和移动赋值右值引用在容器插入的提效引用折叠万能折叠完美转发前言 C11是C继98后的更新,其更新了许多…

瀚高数据库提交数据后,是否需要COMMIT(APP)

文章目录环境症状问题原因解决方案报错编码环境 系统平台: 版本:5.6.5,4.5 症状 瀚高数据库提交数据后,是否需要commit,瀚高数据库是否有配置项。 问题原因 瀚高数据库默认自动COMMIT(提交数据)&#…

深大计算机游戏开发实验三

主要步骤主角飞船的创建和移动边界设置以及护盾设置创建敌机自动生成敌机图层设置弹丸设置武器创建不同发射模式管理竞态条件击败敌机掉落升级道具不同敌机的生成分值显示实现退出游戏界面之后进入游戏的最高记录重置游戏界面失败后重新加载最记录不会重置任何时候在游戏界面按…

详解缓存淘汰策略:LRU

文章目录缓存淘汰策略LRU核心结构核心操作流程局限性源码走读AddGet缓存淘汰策略 缓存淘汰策略的存在是为了解决 缓存容量有限性 和 高缓存命中率 之间的矛盾。其核心目标是在有限的缓存空间内,尽可能提高缓存命中率 缓存容量有限性:缓存(例…

什么是 Bootloader?怎么把它移植到 STM32 上?

一、Bootloader 是啥?它都干了些啥?想象一下你的 MCU(比如 STM32)是一个小机器人,上电之后第一件事,它不会立马开始“干正事”(运行你的主程序),而是先去运行一个“开场引…

无人机避障——感知篇(Ego_Planner_v2中的滚动窗口实现动态实时感知建图grid_map ROS节点理解与参数调整影响)

处理器:Orin nx 双目视觉传感器:ZED2 实时感知建图方法:Vins Fusion Raycast (VIO与射线投影法感知定位加建图方法) 项目地址:https://github.com/ZJU-FAST-Lab/EGO-Planner-v2 【注意】:建…

26-计组-寻址方式

指令寻址与PC自增一、指令寻址方式定义:寻找下一条将要执行的指令地址的过程。 核心部件:程序计数器(PC),用于指示待执行指令的地址。 执行流程:CPU根据PC值从主存取指令。取指后,PC自动自增&am…

生成式对抗网络(GAN)模型原理概述

生成对抗网络(Generative Adversarial Network, GAN)是一种通过对抗训练生成数据的深度学习模型,由生成器(Generator)和判别器(Discriminator)两部分组成,其核心思想源于博弈论中的零…

Vue和Element的使用

文章目录1.vue 脚手架创建步骤2.vue项目开发流程3.vue路由4.Element1.vue 脚手架创建步骤 创建一个文件夹 vue双击进入文件夹,在路径上输入cmd输入vue ui, 目的:调出图形化用户界面点击创建 9. 10.在vscode中打开 主要目录介绍 src目录介绍 vue项目启动 图形化界面中没有npm…

如何设置直播间的观看门槛,让直播间安全有效地运行?

文章目录前言一、直播间观看门槛有哪几种形式?二、设置直播间的观看门槛,对直播的好处是什么三、如何一站式实现上述功能?总结前言 打造一个安全、高效、互动良好的直播间并非易事。面对海量涌入的观众,如何有效识别并阻挡潜在的…

【SkyWalking】配置告警规则并通过 Webhook 推送钉钉通知

🧭 本文为 【SkyWalking 系列】第 3 篇 👉 系列导航:点击跳转 【SkyWalking】配置告警规则并通过 Webhook 推送钉钉通知 简介 介绍 SkyWalking 告警机制、告警规则格式以及如何通过 webhook 方式将告警信息发送到钉钉。 引入 服务响应超时…

关于 验证码系统 详解

验证码系统的目的是:阻止自动化脚本访问网页资源,验证访问者是否为真实人类用户。它通过各种测试(图像、行为、计算等)判断请求是否来自机器人。一、验证码系统的整体架构验证码系统通常由 客户端 服务端 风控模型 数据采集 四…

微服务集成snail-job分布式定时任务系统实践

前言 从事开发工作的同学,应该对定时任务的概念并不陌生,就是我们的系统在运行过程中能够自动执行的一些任务、工作流程,无需人工干预。常见的使用场景包括:数据库的定时备份、文件系统的定时上传云端服务、每天早上的业务报表数…

依赖注入的逻辑基于Java语言

对于一个厨师,要做一道菜。传统的做法是:你需要什么食材,就自己去菜市场买什么。这意味着你必须知道去哪个菜市场、怎么挑选食材、怎么讨价还价等等。你不仅要会做菜,还要会买菜,职责变得复杂了。 而依赖注入就像是有一…