5.5.transformer

news2024/11/13 11:52:43

Transformer

​ Transformer是由编码器和解码器组成的,基于自注意力的模块叠加而成的,源(输入)序列和目标(输出)序列的嵌入(embedding)表示将加上位置编码在分别输入到编码器和解码器中:

在这里插入图片描述

​ 从宏观角度来看,Transformer的编码器是由多个相同的层叠加而成的,每个层都有两个子层(子层表示为sublayer)。

​ 第一个子层是多头自注意力(multi-head self-attention)汇聚;第二个子层是基于位置的前馈网络(positionwise feed-forward network)。具体来说,在计算编码器的自注意力时,查询、键和值都来自前一个编码器层的输出。

​ 每个子层之间采用了残差连接。

1.多头注意力

​ 对同一key,value,query,希望抽取不同的信息,例如短距离关系和长距离关系,多头注意力使用 h h h个独立的注意力池化,合并哥哥头输出得到最终的输出:

在这里插入图片描述

1.1 模型

​ 用数学模型形式化描述:给定查询 q ∈ R d q q\in R ^{d_q} qRdq,键 k ∈ R d k k\in R^{d_k} kRdk和值 v ∈ R d v v\in R^{d_v} vRdv,每个注意力头 h i ( i = 1 , ⋯   , h ) h_i(i=1,\cdots,h) hi(i=1,,h)的计算方法为:
h i = f ( W i ( q ) q , W i ( k ) k , W i ( v ) v ) ∈ R p v h_i = f(W_i^{(q)}q,W_i^{(k)}k,W_i^{(v)}v)\in R^{p_v} hi=f(Wi(q)q,Wi(k)k,Wi(v)v)Rpv
​ 其中,可学习的参数包括 W i ( q ) ∈ R p q × d q , W i ( k ) ∈ R p k × d k , W i ( v ) ∈ R p v × d v W_i^{(q)}\in R^{p_q\times d_q},W_i^{(k)}\in R^{p_k\times d_k},W_i^{(v)}\in R^{p_v\times d_v} Wi(q)Rpq×dq,Wi(k)Rpk×dk,Wi(v)Rpv×dv以及代表注意力汇聚的函数 f f f。可以使加性注意力和缩放点积注意力。

​ 多头注意力的输出需要经过另一个线性转换,对应着 h h h个头连结后的结果,因此其可学习参数为 W o ∈ R p o × h p v W_o\in R^{p_o\times hp_v} WoRpo×hpv:
W o [ h 1 ⋮ h h ] ∈ R p o W_o\begin{equation} \begin{bmatrix} h_1\\ \vdots \\ h_h \end{bmatrix} \end{equation} \in R^{p_o} Wo h1hh Rpo
​ 基于这种设计,每个头都可能会关注输入的不同部分, 可以表示比简单加权平均值更复杂的函数。

1.2 有掩码的多头注意力

​ 解码器对序列中一个元素输出时,不应该考虑该元素之后的元素,可以通过掩码来实现,即计算 x i x_i xi输出时,记当前序列长度为 i i i

1.3 代码实现

​ 通常选择缩放点积注意力作为每一个注意力头,为了避免计算代价和参数代价的大幅增长,设定 p q = p k = p v = p o / h p_q = p_k = p_v =p_o /h pq=pk=pv=po/h,如果将查询、键和值的线性变换的输出数量设置为 p q h = p k h = p v h = p o p_q h = p_kh =p_vh = p_o pqh=pkh=pvh=po,则可以并行计算h个头,下面代码中 p o p_o po是通过参数num_hiddens指定的:

import math
import torch
from torch import nn
from d2l import torch as d2l


#@save
class MultiHeadAttention(nn.Module):
    """多头注意力"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 num_heads, dropout, bias=False, **kwargs):
        super(MultiHeadAttention, self).__init__(**kwargs)
        self.num_heads = num_heads
        self.attention = d2l.DotProductAttention(dropout) #缩放点积,不用学参数,稍微简单一点
        self.W_q = nn.Linear(query_size, num_hiddens, bias=bias)
        self.W_k = nn.Linear(key_size, num_hiddens, bias=bias)
        self.W_v = nn.Linear(value_size, num_hiddens, bias=bias)
        self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias)

    def forward(self, queries, keys, values, valid_lens):
        # queries,keys,values的形状:
        # (batch_size,查询或者“键-值”对的个数,num_hiddens)
        # valid_lens 的形状:
        # (batch_size,)或(batch_size,查询的个数)
        # 经过变换后,输出的queries,keys,values 的形状:
        # (batch_size*num_heads,查询或者“键-值”对的个数,
        # num_hiddens/num_heads)
        queries = transpose_qkv(self.W_q(queries), self.num_heads)
        keys = transpose_qkv(self.W_k(keys), self.num_heads)
        values = transpose_qkv(self.W_v(values), self.num_heads)

        if valid_lens is not None:
            # 在轴0,将第一项(标量或者矢量)复制num_heads次,
            # 然后如此复制第二项,然后诸如此类。
            valid_lens = torch.repeat_interleave(
                valid_lens, repeats=self.num_heads, dim=0)

        # output的形状:(batch_size*num_heads,查询的个数,
        # num_hiddens/num_heads)
        output = self.attention(queries, keys, values, valid_lens)

        # output_concat的形状:(batch_size,查询的个数,num_hiddens)
        output_concat = transpose_output(output, self.num_heads)
        return self.W_o(output_concat)

​ 多头输入需要对形状变换一下,使得一个头只需要做一次矩阵乘法:

#@save
def transpose_qkv(X, num_heads):
    """为了多注意力头的并行计算而变换形状"""
    # 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens)
    # 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads,
    # num_hiddens/num_heads)
    X = X.reshape(X.shape[0], X.shape[1], num_heads, -1)

    # 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数,
    # num_hiddens/num_heads)
    X = X.permute(0, 2, 1, 3)

    # 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数,
    # num_hiddens/num_heads) ,又将前两个维度合并在一起,变换成3维,可以直接使用attention
    # 这样可以让每一个头都只做一个矩阵乘法
    return X.reshape(-1, X.shape[2], X.shape[3])


#@save
def transpose_output(X, num_heads):
    """逆转transpose_qkv函数的操作"""
    X = X.reshape(-1, num_heads, X.shape[1], X.shape[2])
    X = X.permute(0, 2, 1, 3)
    return X.reshape(X.shape[0], X.shape[1], -1)
  
num_hiddens, num_heads = 100, 5
attention = MultiHeadAttention(num_hiddens, num_hiddens, num_hiddens,
                               num_hiddens, num_heads, 0.5)
attention.eval()
batch_size, num_queries = 2, 4
num_kvpairs, valid_lens =  6, torch.tensor([3, 2])
X = torch.ones((batch_size, num_queries, num_hiddens))
Y = torch.ones((batch_size, num_kvpairs, num_hiddens))
attention(X, Y, Y, valid_lens).shape

2.基于位置的前馈网络

​ 将输入形状由 ( b , n , d ) (b,n,d) (b,n,d)变换成 ( b n , d ) (bn,d) (bn,d),作用两个全连接层,输出形状由 ( b n , d ) (bn,d) (bn,d)变化回 ( b , n , d ) (b,n,d) (b,n,d),等价于两层核窗口为1的一维卷积层。

​ 基于位置的前馈网络对序列中的所有位置的表示进行变换时使用的是同一个多层感知机(MLP),这就是称前馈网络是基于位置的(positionwise)的原因。在下面的实现中,输入X的形状(批量大小,时间步数或序列长度,隐单元数或特征维度)将被一个两层的感知机转换成形状为(批量大小,时间步数,ffn_num_outputs)的输出张量。

#@save
class PositionWiseFFN(nn.Module):
    """基于位置的前馈网络,更多的输出通道,更多的特征"""
    def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs,
                 **kwargs):
        super(PositionWiseFFN, self).__init__(**kwargs)
        self.dense1 = nn.Linear(ffn_num_input, ffn_num_hiddens)
        self.relu = nn.ReLU()
        self.dense2 = nn.Linear(ffn_num_hiddens, ffn_num_outputs)

    def forward(self, X):
        return self.dense2(self.relu(self.dense1(X)))
      
ffn = PositionWiseFFN(4, 4, 8)
ffn.eval()
ffn(torch.ones((2, 3, 4)))[0]

'''
tensor([[-0.8290,  1.0067,  0.3619,  0.3594, -0.5328,  0.2712,  0.7394,  0.0747],
        [-0.8290,  1.0067,  0.3619,  0.3594, -0.5328,  0.2712,  0.7394,  0.0747],
        [-0.8290,  1.0067,  0.3619,  0.3594, -0.5328,  0.2712,  0.7394,  0.0747]],
       grad_fn=<SelectBackward0>)
'''# 输出的最后一个维度从4变成8

3.层归一化

​ (加&规范化)

​ 批量归一化对每个特征/通道里元素进行归一化,不适合序列长度会变的NLP应用,层归一化对每个样本里的元素进行归一化:

在这里插入图片描述

​ b句话,每句话长度为len,有d个特征通道

​ 批量归一化就是对所有句子所有词的某一特征做归一化;层归一化就是对某句话的所有词的所有特征做归一化。

ln = nn.LayerNorm(2)
bn = nn.BatchNorm1d(2)
X = torch.tensor([[1, 2], [2, 3]], dtype=torch.float32)
# 在训练模式下计算X的均值和方差
print('layer norm:', ln(X), '\nbatch norm:', bn(X))

'''
layer norm: tensor([[-1.0000,  1.0000],
        [-1.0000,  1.0000]], grad_fn=<NativeLayerNormBackward0>)
batch norm: tensor([[-1.0000, -1.0000],
        [ 1.0000,  1.0000]], grad_fn=<NativeBatchNormBackward0>)
'''

​ 使用残差连接和层规范化来实现AddNorm类,暂退法作为正则化方法使用

#@save
class AddNorm(nn.Module):
    """残差连接后进行层规范化"""
    def __init__(self, normalized_shape, dropout, **kwargs):
        super(AddNorm, self).__init__(**kwargs)
        self.dropout = nn.Dropout(dropout)
        self.ln = nn.LayerNorm(normalized_shape)

    def forward(self, X, Y):
        return self.ln(self.dropout(Y) + X)
      
add_norm = AddNorm([3, 4], 0.5)
add_norm.eval()
add_norm(torch.ones((2, 3, 4)), torch.ones((2, 3, 4))).shape

'''torch.Size([2, 3, 4])'''

4.信息传递

​ 编码器中的输出 y 1 , ⋯   , y n y_1,\cdots,y_n y1,,yn,将其作为解码中第 i i i个Transformer块中多头注意力的key和value,query来自目标序列。这意味着编码器和解码器中块的个数和输出维度都是一样的。

5.预测

​ 在预测第 t + 1 t+1 t+1个输出时,解码器中输入前 t t t个预测值,在自注意力中,前 t t t个预测值作为key和value,第t个预测值还作为query:

在这里插入图片描述

6.代码实现

6.1 编码器

​ EncoderBlock包含两个子层:多头注意力和基于位置的前馈网络,两个子层都是用了残差连接和紧随的层规范化。

#@save
class EncoderBlock(nn.Module):
    """Transformer编码器块"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, use_bias=False, **kwargs):
        super(EncoderBlock, self).__init__(**kwargs)
        self.attention = d2l.MultiHeadAttention(
            key_size, query_size, value_size, num_hiddens, num_heads, dropout,
            use_bias)
        self.addnorm1 = AddNorm(norm_shape, dropout)
        # 两个全连接层24->48->24 
        self.ffn = PositionWiseFFN(
            ffn_num_input, ffn_num_hiddens, num_hiddens)
        self.addnorm2 = AddNorm(norm_shape, dropout)

    def forward(self, X, valid_lens):
        Y = self.addnorm1(X, self.attention(X, X, X, valid_lens))
        return self.addnorm2(Y, self.ffn(Y))
      
X = torch.ones((2, 100, 24))
valid_lens = torch.tensor([3, 2])
encoder_blk = EncoderBlock(24, 24, 24, 24, [100, 24], 24, 48, 8, 0.5)
encoder_blk.eval()
encoder_blk(X, valid_lens).shape

'''torch.Size([2, 100, 24])'''
#输出形状不变


#@save
class TransformerEncoder(d2l.Encoder):
    """Transformer编码器"""
    def __init__(self, vocab_size, key_size, query_size, value_size,
                 num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
                 num_heads, num_layers, dropout, use_bias=False, **kwargs):
        super(TransformerEncoder, self).__init__(**kwargs)
        self.num_hiddens = num_hiddens
        self.embedding = nn.Embedding(vocab_size, num_hiddens)
        self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
        self.blks = nn.Sequential()
        for i in range(num_layers):
            self.blks.add_module("block"+str(i),
                EncoderBlock(key_size, query_size, value_size, num_hiddens,
                             norm_shape, ffn_num_input, ffn_num_hiddens,
                             num_heads, dropout, use_bias))

    def forward(self, X, valid_lens, *args):
        # 因为位置编码值在-1和1之间,
        # 因此嵌入值乘以嵌入维度的平方根进行缩放,避免embedding值小了
        # 然后再与位置编码相加。
        X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
        self.attention_weights = [None] * len(self.blks)
        for i, blk in enumerate(self.blks):
            X = blk(X, valid_lens)
            self.attention_weights[
                i] = blk.attention.attention.attention_weights
        return X

#两层的Transformer编码器,输出形状是(批量大小,时间步数目,num_hiddens)
encoder = TransformerEncoder(
    200, 24, 24, 24, 24, [100, 24], 24, 48, 8, 2, 0.5)
encoder.eval()
encoder(torch.ones((2, 100), dtype=torch.long), valid_lens).shape

'''torch.Size([2, 100, 24])'''

6.2 解码器

​ 解码器也是由多个相同的层组成,在DecoderBlock类中实现的每个层包含了3个子层:解码器自注意力、"编码器-解码器"注意力和基于位置的前馈网络。这些子层也都被残差连接和紧随的层规范化围绕。

​ 在第一个子层掩蔽多头自注意力层中,查询、键和值都来自上一个解码器层的输出。对于seq2seq模型,训练阶段,所有词元都是已知的,但在预测阶段,输出序列的词元是逐个生成的,因此,在任何解码器的时间步中,只有生成的词元才能用于解码器的自注意力计算中,所以需要设定参数dec_valid_lens,以便任何查询都只会与解码器中所有已经生成词元的位置进行注意力计算。

class DecoderBlock(nn.Module):
    """解码器中第i个块"""
    def __init__(self, key_size, query_size, value_size, num_hiddens,
                 norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
                 dropout, i, **kwargs):
        super(DecoderBlock, self).__init__(**kwargs)
        self.i = i
        self.attention1 = d2l.MultiHeadAttention(
            key_size, query_size, value_size, num_hiddens, num_heads, dropout)
        self.addnorm1 = AddNorm(norm_shape, dropout)
        self.attention2 = d2l.MultiHeadAttention(
            key_size, query_size, value_size, num_hiddens, num_heads, dropout)
        self.addnorm2 = AddNorm(norm_shape, dropout)
        self.ffn = PositionWiseFFN(ffn_num_input, ffn_num_hiddens,
                                   num_hiddens)
        self.addnorm3 = AddNorm(norm_shape, dropout)

    def forward(self, X, state):
        enc_outputs, enc_valid_lens = state[0], state[1]
        # 训练阶段,输出序列的所有词元都在同一时间处理,
        # 因此state[2][self.i]初始化为None。
        # 预测阶段,输出序列是通过词元一个接着一个解码的,
        # 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表示
        if state[2][self.i] is None:
            key_values = X # 没有就是在训练,是本身
        else:
           #预测时需要将之前的信息(输出)存起来,然后连接一下
            key_values = torch.cat((state[2][self.i], X), axis=1)
        state[2][self.i] = key_values
        if self.training:
            batch_size, num_steps, _ = X.shape
            # dec_valid_lens的开头:(batch_size,num_steps),
            # 其中每一行是[1,2,...,num_steps]
            # 训练时需要将后面的遮掉
            dec_valid_lens = torch.arange(
                1, num_steps + 1, device=X.device).repeat(batch_size, 1)
        else:
           #预测的话,反正也没后面,就不需要遮了
            dec_valid_lens = None

        # 自注意力
        X2 = self.attention1(X, key_values, key_values, dec_valid_lens)
        Y = self.addnorm1(X, X2)
        # 编码器-解码器注意力。
        # enc_outputs的开头:(batch_size,num_steps,num_hiddens)
        Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)
        Z = self.addnorm2(Y, Y2)
        return self.addnorm3(Z, self.ffn(Z)), state
      
      
decoder_blk = DecoderBlock(24, 24, 24, 24, [100, 24], 24, 48, 8, 0.5, 0)
decoder_blk.eval()
X = torch.ones((2, 100, 24))
state = [encoder_blk(X, valid_lens), valid_lens, [None]]
decoder_blk(X, state)[0].shape

#形状不发生变化,torch.Size([2, 100, 24])


class TransformerDecoder(d2l.AttentionDecoder):
    def __init__(self, vocab_size, key_size, query_size, value_size,
                 num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens,
                 num_heads, num_layers, dropout, **kwargs):
        super(TransformerDecoder, self).__init__(**kwargs)
        self.num_hiddens = num_hiddens
        self.num_layers = num_layers
        self.embedding = nn.Embedding(vocab_size, num_hiddens)
        self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout)
        self.blks = nn.Sequential()
        for i in range(num_layers):
            self.blks.add_module("block"+str(i),
                DecoderBlock(key_size, query_size, value_size, num_hiddens,
                             norm_shape, ffn_num_input, ffn_num_hiddens,
                             num_heads, dropout, i))
        self.dense = nn.Linear(num_hiddens, vocab_size)

    def init_state(self, enc_outputs, enc_valid_lens, *args):
        return [enc_outputs, enc_valid_lens, [None] * self.num_layers]

    def forward(self, X, state):
        X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens))
        self._attention_weights = [[None] * len(self.blks) for _ in range (2)]
        for i, blk in enumerate(self.blks):
            X, state = blk(X, state)
            # 解码器自注意力权重
            self._attention_weights[0][
                i] = blk.attention1.attention.attention_weights
            # “编码器-解码器”自注意力权重
            self._attention_weights[1][
                i] = blk.attention2.attention.attention_weights
        return self.dense(X), state

    @property
    def attention_weights(self):
        return self._attention_weights

6.3 训练

​ 两层,4头注意力

num_hiddens, num_layers, dropout, batch_size, num_steps = 32, 2, 0.1, 64, 10
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_input, ffn_num_hiddens, num_heads = 32, 64, 4
key_size, query_size, value_size = 32, 32, 32
norm_shape = [32]

train_iter, src_vocab, tgt_vocab = d2l.load_data_nmt(batch_size, num_steps)

encoder = TransformerEncoder(
    len(src_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers, dropout)
decoder = TransformerDecoder(
    len(tgt_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers, dropout)
net = d2l.EncoderDecoder(encoder, decoder)
d2l.train_seq2seq(net, train_iter, lr, num_epochs, tgt_vocab, device)

engs = ['go .', "i lost .", 'he\'s calm .', 'i\'m home .']
fras = ['va !', 'j\'ai perdu .', 'il est calme .', 'je suis chez moi .']
for eng, fra in zip(engs, fras):
    translation, dec_attention_weight_seq = d2l.predict_seq2seq(
        net, eng, src_vocab, tgt_vocab, num_steps, device, True)
    print(f'{eng} => {translation}, ',
          f'bleu {d2l.bleu(translation, fra, k=2):.3f}')

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

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

相关文章

Elastic 8.15:更好的语义搜索、新的 OTel 分布、SIEM 数据导入

作者&#xff1a;来自 Elastic Brian Bergholm 今天&#xff0c;我们很高兴地宣布 Elastic 8.15 正式发布。 有什么新功能&#xff1f; 8.15 版本包含大量新功能&#xff0c;包括更多工具来优化相关性、增强模型的灵活性和改进向量搜索&#xff0c;以及在 AI 驱动的安全分析方…

学习大数据DAY35 利用 echarts 的开源图表和 python 异常处理优化网站

目录 根据分数统计电影数量来生成图表 上机练习 14 添加异常 添加电影类型判断是整数及正整数异常 部署项目到 Nginx 上机练习 15 根据分数统计电影数量来生成图表 Echarts 官网&#xff1a; https://echarts.apache.org/examples/zh/index.html 下载柱状图和饼图 可以…

访问网站显示不安全如何处理

当访问网站时浏览器提示“不安全”&#xff0c;这通常是由于多种原因造成的。下面是一些常见的原因及其解决办法&#xff1a; 未启用HTTPS协议 如果网站仅使用HTTP协议&#xff0c;数据传输没有加密&#xff0c;会被浏览器标记为“不安全”。解决办法是启用HTTPS协议&#xff…

C++(4):基类-派生类

基类—>派生类 先构造基类再构造派生类 匿名对象形式向基类传参&#xff0c;基类先构造先传参 复制构造函数&#xff0c;采用类型兼容性规则即用派生类代替基类 使用protect派生类可访问其它地方不能访问 private只能自己访问 p访问的是自己类里的showTime 类型兼容性规则&…

HCIP | 实验二

概述 要求&#xff1a; 1.如图连接&#xff0c;合理规划IP地址&#xff0c;所有路由器各自创建一个loopback接口 2.R1再创建三个接口IP地址为201.1.1.1/24、201.1.2.1/24、201.1.3.1/24 R5再创建三个接口IP地址为202.1.1.1/24、202.1.2.1/24、202.1.3.1/24 R7再创建三个接口…

Mac平台M1PRO芯片MiniCPM-V-2.6网页部署跑通

Mac平台M1PRO芯片MiniCPM-V-2.6网页部署跑通 契机 ⚙ 2.6的小钢炮可以输入视频了&#xff0c;我必须拉到本地跑跑。主要解决2.6版本默认绑定flash_atten问题&#xff0c;pip install flash_attn也无法安装&#xff0c;因为强制依赖cuda。主要解决的就是这个问题&#xff0c;还…

批发行业进销存-入库单表格识别 源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构

一、进销存入库进货单单识别意义 对个人、商品、公示内容等纸质信息登记表进行识别&#xff0c;用于登记信息的结构化整理和统计&#xff0c;大幅度降低人力录入成本&#xff0c;提升信息管理的便捷性 1. 提高效率&#xff1a;自动转换节省了手动录入的时间和精力&#xff0c;…

实景视频可视化的结构化脚本,脚本分为三类:文字脚本,分镜头脚本和动态脚本

在视频创作的世界中&#xff0c;脚本是创作的基础和核心。无论是简短的广告视频&#xff0c;还是复杂的电影制作&#xff0c;脚本都扮演着不可或缺的角色。随着视频内容需求的多样化&#xff0c;结构化脚本逐渐成为确保创作效率和质量的重要工具。结构化脚本不仅帮助创作者清晰…

PythonStudio 控件使用常用方式(十八)TCategoryButtons

PythonStudio是一个极强的开发Python的IDE工具&#xff0c;它使用的是Delphi的控件&#xff0c;常用的内容是与Delphi一致的。但是相关文档并一定完整。现在我试试能否逐步把它的控件常用用法写一点点&#xff0c;也作为PythonStudio的参考。 从1.2.1版开始&#xff0c;Python…

Elastic Search 8.15:通过语义文本和重新排序实现可访问的语义搜索

作者&#xff1a;来自 Elastic Nick Chow, Sunayana Vatassery 在 8.15 中&#xff0c;我们的客户可以更轻松地获得出色的搜索结果。我们的最新版本带来了语义重新排名&#xff08;semantic reranking&#xff09;、额外的向量搜索工具和更多第三方模型提供商&#xff0c;并将我…

告别焦虑:使用 acme 实现 ssl 免费证书到期自动更新

文章目录 前言什么是 ACME 协议&#xff1f;ACME 使用指南安装下载使用 gitee 下载设置别名&#xff08;非必要&#xff09;注册账号更改证书生成方式生成证书重新生成证书并认证安装 SSL 证书 使用 SSL 证书验证 配置证书自动续期证书续期命令自动续期查看添加的定时任务 cron…

haproxy基础

目录 1 HAProxy介绍 1.1 版本对比 1.2 HAProxy功能 2 参数介绍与实践 2.1 global参数说明 2.2 真实代码格式实例 2.3 常用全局参数 2.3.1 nbproc -- 开启几个进程 2.3.2 cpu-map(CUP绑定) 2.3.3 nbthread 2 --开启2个线程 3 Proxies配置 3.1 Proxies配置-defaults 3.2 Proxi…

dolphinscheduler版本差异的配置造成的故障处理

dolphinscheduler1.3.4的common.properties的配置 [root@dbos-bigdata-test003 conf]# vim /opt/dolphinscheduler/conf/common.properties 下面的这个配置中8088直接在配置成端口即可 yarn.application.status.address=http://yarnIp1:8088/ws/v1/cluster/apps/%s dolphin…

守护历史文化瑰宝,RFID藏品管理系统助力文物保护

在中国悠久的历史长河中&#xff0c;有一座蕴藏着千年文化的古老建筑。这座建筑曾经是伟大文人杜甫的居所&#xff0c;承载着他卓越的文学成就和丰富的人生经历。然而&#xff0c;这样一座历史文化瑰宝的保护和管理一直面临着诸多挑战。 为了解决这一难题&#xff0c;我…

运维工具的衍化对运维工作的新挑战

运维工具的衍化对运维工作产生了深远的影响&#xff0c;这些影响体现在多个方面&#xff0c;包括提升运维效率、优化资源配置、增强故障应对能力、促进团队协作与沟通&#xff0c;以及面临新的挑战如数据安全和隐私保护等。运维工具的衍化对运维工作带来了多方面的新挑战&#…

用户体验至上:9款软件界面设计工具分享

你知道如何选择正确的UI设计软件吗&#xff1f;您知道哪些界面设计软件需要设计美观的用户界面&#xff0c;以及带来良好用户体验的APP吗&#xff1f;根据APP界面的不同功能&#xff0c;制作软件界面的选择也会有所不同。但是&#xff0c;并非要非常精通所有的制作软件界面&…

k8s集群管理 Pod管理命令

k8s集群管理命令 信息查询命令 子命令说明help用于查看命令及子命令的帮助信息cluster-info显示集群的相关配置信息api-resources查看当前服务器上所有的资源对象api-versions查看当前服务器上所有资源对象的版本config管理当前节点上的认证信息 资源对象概述 Pod概述 Pod 管…

vscode 快速生成vue 格式

1.用快捷Ctrl Shift P唤出控制台 输入“Snippets”并选择 Snippets: Configure User Snippets 2.输入vue&#xff0c;选中vue.json vs code自动生成vue.json文件 3.在 vue.json 中添加模板 {"Print to console": {"prefix": "vue2","b…

MATLAB计算心理声学烦恼度例子

本例中&#xff0c;通过检测发动机噪音&#xff0c;并结合心理声学参数&#xff0c;评估了其响度、尖锐度、波动强度、粗糙度及整体心理声学烦恼度。接着&#xff0c;模拟了隔音材料的添加&#xff0c;并对噪音水平进行了重新评估。比较分析后&#xff0c;展示了隔音材料对降低…

【学习笔记】Matlab和python双语言的学习(动态规划)

文章目录 前言一、动态规划动态规划的基本步骤示例1示例2 三、代码实现----Matlab1.示例12.示例2 四、代码实现----python1.示例12.示例2 总结 前言 通过模型算法&#xff0c;熟练对Matlab和python的应用。 学习视频链接&#xff1a; https://www.bilibili.com/video/BV1EK411…