《动手学深度学习 Pytorch版》 10.7 Transformer

news2025/4/11 16:01:22

自注意力同时具有并行计算和最短的最大路径长度这两个优势。Transformer 模型完全基于注意力机制,没有任何卷积层或循环神经网络层。尽管 Transformer 最初是应用于在文本数据上的序列到序列学习,但现在已经推广到各种现代的深度学习中,例如语言、视觉、语音和强化学习领域。

10.7.1 模型

Transformer 作为编码器-解码器架构的一个实例,其编码器和解码器是基于自注意力的模块叠加而成的,源(输入)序列和目标(输出)序列的嵌入(embedding)表示将加上位置编码(positional encoding),再分别输入到编码器和解码器中。

在这里插入图片描述

结构简介:

  • 编码器:

    • 由多个相同的层 叠加 而成,每个层有 两个子层(sublayer)

      • 第一个子层为 多头自注意力(multi-head self-attention)汇聚

      • 第二个子层为 基于位置的前馈网络(positionwise feed-forward network)

    • 在计算编码器的自注意力时,查询、键和值都来自前一个编码器层的输出

    • 每个子层都采用了残差连接(residual connection)

    • 残差连接的加法计算之后紧接着应用层规范化(layer normalization)

  • 解码器:

    • 由多个相同的层 叠加 而成,除了编码器中描述的两个子层之外,解码器还在这两个子层之间插入了第三个子层:

      • 编码器-解码器注意力(encoder-decoder attention)层
    • 在解码器自注意力中,查询、键和值都来自上一个解码器层的输出

    • 在第三个子层编码器-解码器注意力层中,查询来自前一个解码器层的输出,而键和值来自整个编码器的输出

import math
import pandas as pd
import torch
from torch import nn
from d2l import torch as d2l

10.7.2 基于位置的前馈网络

基于位置的前馈网络对序列中的所有位置的表示进行变换时使用的是同一个多层感知机(MLP),这就是称前馈网络是基于位置的(positionwise)的原因。

名字很帅,其实就是全连接,但隐藏层的 MLP : )

输入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.2199,  0.1357, -0.2216,  0.1659,  0.5388, -0.4541,  0.2121, -0.1025],
        [-0.2199,  0.1357, -0.2216,  0.1659,  0.5388, -0.4541,  0.2121, -0.1025],
        [-0.2199,  0.1357, -0.2216,  0.1659,  0.5388, -0.4541,  0.2121, -0.1025]],
       grad_fn=<SelectBackward0>)

10.7.3 残差连接和层规范化

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

层规范化和批量规范化的目标相同,但层规范化是基于特征维度进行规范化,即对每个样本里的元素进行归一化。

在这里插入图片描述

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 规范化的是每个样本(行)  batch norm 规范化的是每个特征(列)
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>)
#@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])

10.7.4 编码器

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)  # 第一个层规范化
        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  # Transformer编码器中的任何层都不会改变其输入的形状
torch.Size([2, 100, 24])

以下对 num_layers个EncoderBlock 类的实例进行了堆叠。

这里使用的是值范围在 -1 和 1 之间的固定位置编码,因此通过学习得到的输入的嵌入表示的值需要先乘以嵌入维度的平方根进行重新缩放,然后再与位置编码相加。

#@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之间,数值比较小,因此嵌入值乘以嵌入维度的平方根缩放到差不多大小,然后再与位置编码相加。
        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)  # 一层一层的丢进去进行 attention
            self.attention_weights[  # 记录注意力汇聚权重
                i] = blk.attention.attention.attention_weights
        return X
encoder = TransformerEncoder(  # 创建一个两层的Transformer编码器
    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  # 输出的形状是(批量大小,时间步数目,num_hiddens)
torch.Size([2, 100, 24])

10.7.5 解码器

DecoderBlock 类中实现的每个层包含了三个子层:解码器自注意力、“编码器-解码器”注意力和基于位置的前馈网络。这些子层也都被残差连接和紧随的层规范化围绕。

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]
        if state[2][self.i] is None:  # 训练阶段,输出序列的所有词元都在同一时间处理,因此state[2][self.i]初始化为None。
            key_values = X
        else:  # 预测阶段,输出序列是通过词元一个接着一个解码的,因此需要把直到当前时间步第i个块解码的输出在state[2][self.i]里面存着
            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)
        Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)  # 编码器-解码器注意力。enc_outputs的开头:(batch_size,num_steps,num_hiddens)
        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

10.7.6 训练

依照 Transformer 架构来实例化编码器-解码器模型。

num_hiddens, num_layers, dropout, batch_size, num_steps = 32, 2, 0.1, 64, 10  # 指定编码器和解码器都是2层
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_input, ffn_num_hiddens, num_heads = 32, 64, 4  # 都使用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)  # 并行度应该挺高的 训起来不慢 1分9秒
loss 0.031, 6866.6 tokens/sec on cuda:0

在这里插入图片描述

训练结束后,使用 Transformer 模型将一些英语句子翻译成法语,并且计算它们的BLEU分数。

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}')
go . => va !,  bleu 1.000
i lost . => j'ai perdu .,  bleu 1.000
he's calm . => il est paresseux .,  bleu 0.658
i'm home . => je suis chez moi .,  bleu 1.000

可视化 Transformer 的注意力权重。

enc_attention_weights = torch.cat(net.encoder.attention_weights, 0).reshape((num_layers, num_heads,
    -1, num_steps))
enc_attention_weights.shape  # 编码器自注意力权重的形状为(编码器层数,注意力头数,num_steps或查询的数目,num_steps或“键-值”对的数目)
torch.Size([2, 4, 10, 10])
d2l.show_heatmaps(  # 逐行呈现编码器的两层多头注意力的权重
    enc_attention_weights.cpu(), xlabel='Key positions',
    ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
    figsize=(7, 3.5))  # 可以看到每个注意力头的注意力都不大一样。


在这里插入图片描述

用零填充被掩蔽住的注意力权重后,可视化解码器的自注意力权重和“编码器-解码器”的注意力权重。

解码器的自注意力权重和“编码器-解码器”的注意力权重都有相同的查询:即以序列开始词元(beginning-of-sequence,BOS)打头,再与后续输出的词元共同组成序列。

dec_attention_weights_2d = [head[0].tolist()
                            for step in dec_attention_weight_seq
                            for attn in step for blk in attn for head in blk]
dec_attention_weights_filled = torch.tensor(
    pd.DataFrame(dec_attention_weights_2d).fillna(0.0).values)  # 用零填充被掩蔽住的注意力权重
dec_attention_weights = dec_attention_weights_filled.reshape((-1, 2, num_layers, num_heads, num_steps))
dec_self_attention_weights, dec_inter_attention_weights = \
    dec_attention_weights.permute(1, 2, 3, 0, 4)
dec_self_attention_weights.shape, dec_inter_attention_weights.shape
(torch.Size([2, 4, 6, 10]), torch.Size([2, 4, 6, 10]))

由于解码器自注意力的自回归属性,查询不会对当前位置之后的“键-值”对进行注意力计算。

d2l.show_heatmaps(  # 逐行呈现解码器的多头自注意力的权重
    dec_self_attention_weights[:, :, :, :len(translation.split()) + 1],
    xlabel='Key positions', ylabel='Query positions',
    titles=['Head %d' % i for i in range(1, 5)], figsize=(7, 3.5))


在这里插入图片描述

与编码器的自注意力的情况类似,通过指定输入序列的有效长度,输出序列的查询不会与输入序列中填充位置的词元进行注意力计算。

d2l.show_heatmaps(  # 逐行呈现解码器的编-解多头自注意力的权重
    dec_inter_attention_weights, xlabel='Key positions',
    ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
    figsize=(7, 3.5))


在这里插入图片描述

练习

(1)在实验中训练更深的 Transformer 将如何影响训练速度和翻译效果?

更慢了,注意力越往后越浓重。

num_hiddens, num_layers_deeper, dropout, batch_size, num_steps = 32, 4, 0.1, 64, 10  # 加深到 4 层
lr, num_epochs, device = 0.005, 200, d2l.try_gpu()
ffn_num_input, ffn_num_hiddens, num_heads = 32, 64, 4  # 还是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_deeper = TransformerEncoder(
    len(src_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers_deeper, dropout)
decoder_deeper = TransformerDecoder(
    len(tgt_vocab), key_size, query_size, value_size, num_hiddens,
    norm_shape, ffn_num_input, ffn_num_hiddens, num_heads,
    num_layers_deeper, dropout)
net_deeper = d2l.EncoderDecoder(encoder_deeper, decoder_deeper)
d2l.train_seq2seq(net_deeper, train_iter, lr, num_epochs, tgt_vocab, device)  # 时间慢到2分了 怎么精度跌了
loss 0.063, 4044.0 tokens/sec on cuda:0

在这里插入图片描述

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_deeper, eng, src_vocab, tgt_vocab, num_steps, device, True)
    print(f'{eng} => {translation}, ',
          f'bleu {d2l.bleu(translation, fra, k=2):.3f}')
go . => va le chercher !,  bleu 0.000
i lost . => je me suis tombé .,  bleu 0.000
he's calm . => il est malade .,  bleu 0.658
i'm home . => je suis sûr .,  bleu 0.512
enc_attention_weights_deeper = torch.cat(net_deeper.encoder.attention_weights, 0).reshape((num_layers_deeper, num_heads,
    -1, num_steps))

d2l.show_heatmaps(
    enc_attention_weights_deeper.cpu(), xlabel='Key positions',
    ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
    figsize=(10, 8))


在这里插入图片描述

dec_attention_weights_2d_deeper = [head[0].tolist()
                            for step in dec_attention_weight_seq
                            for attn in step for blk in attn for head in blk]
dec_attention_weights_filled_deeper = torch.tensor(
    pd.DataFrame(dec_attention_weights_2d_deeper).fillna(0.0).values)  # 用零填充被掩蔽住的注意力权重
dec_attention_weights_deeper = dec_attention_weights_filled_deeper.reshape((-1, 2, num_layers_deeper, num_heads, num_steps))
dec_self_attention_weights_deeper, dec_inter_attention_weights_deeper = \
    dec_attention_weights_deeper.permute(1, 2, 3, 0, 4)

d2l.show_heatmaps(  # 逐行呈现解码器的多头自注意力的权重
    dec_self_attention_weights_deeper[:, :, :, :len(translation.split()) + 1],
    xlabel='Key positions', ylabel='Query positions',
    titles=['Head %d' % i for i in range(1, 5)], figsize=(10, 8))


在这里插入图片描述

d2l.show_heatmaps(
    dec_inter_attention_weights_deeper, xlabel='Key positions',
    ylabel='Query positions', titles=['Head %d' % i for i in range(1, 5)],
    figsize=(10, 8))


在这里插入图片描述


(2)在 Transformer 中使用加性注意力取代缩放点积注意力是不是个好办法?为什么?

用加性的会慢点。


(3)对于语言模型,应该使用 Transformer 的编码器还是解码器,或者两者都用?如何设计?

我不道哇,还有半用半不用的?


(4)如果输入序列很长,Transformer 会面临什么挑战?为什么?

越长越不好算,太长了需要注意的就太多了。


(5)如何提高 Transformer 的计算速度和内存使用效率?提示:可以参考论文 (Tay et al., 2020)。

略。


(6)如果不使用卷积神经网络,如何设计基于 Transformer 模型的图像分类任务?提示:可以参考Vision Transformer (Dosovitskiy et al., 2021)。

图像切块。

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

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

相关文章

QA新人入职任务

一、背景 分享记录一下入职新公司后&#xff0c;新人第一周接到的新手任务&#xff0c;回顾总结&#xff0c;方便自己成长和思考~ 二、新人任务说明 题目1&#xff1a;接口相关 题目2&#xff1a;UI相关 UI原型图 三、任务要求 1、根据题目2原型图&#xff0c;进行UI测试…

【继承顺序和方式,子类构造方法,protected 关键字,final 关键字】

文章目录 子类构造方法super和this继承关系上的执行顺序protected 关键字继承方式final 关键字总结 子类构造方法 子类继承父类的成员变量&#xff0c;那父类的成员变量怎么初始化呢&#xff1f; 要先初始化父类成员&#xff0c;子类的构造方法才能完成 class Animal{public…

解决:idea中代码文件背景变成特殊颜色(浅褐色),且代码智能提示完全消失,即使在新项目中创建同类型文件也会出现同样情况

解决&#xff1a;idea开发中代码文件背景变成浅褐色&#xff0c;且代码智能提示完全消失&#xff0c;不同项目新建同类型文件也会出现同样情况 一问题描述&#xff1a;1.idea开发中&#xff0c;使用idea的快捷提示组合键进行提示&#xff0c;但是手快点击确认了什么提示选项&am…

DSP开发例程(3): sys_print_to_uart

目录 DSP开发例程: sys_print_to_uart创建工程源码编辑os.capp.cfgmain.c 调试原理分析 DSP开发例程: sys_print_to_uart 在DSP 应用的执行过程中, 我们经常需要调用 System_printf() 来显示当前的执行状态. 不只是 System_printf() 函数, SYS/BIOS 打印信息的函数还包括: Sys…

橙河网络:国外问卷调查赚钱是真的吗?

大家好&#xff0c;我是橙河网络&#xff0c;今天聊一聊国外问卷调查赚钱是真的吗&#xff1f; 有不少人在第一次了解这个项目的时候&#xff0c;看到“海外”“国外”这些字眼的时候&#xff0c;心里都会犯嘀咕&#xff1a;这海外问卷调查项目能做吗&#xff1f;国外问卷调查…

使用 Pyro 和 PyTorch 的贝叶斯神经网络

一、说明 构建图像分类器已成为新的“hello world”。还记得当你第一次接触 Python 时&#xff0c;你的打印“hello world”感觉很神奇吗&#xff1f;几个月前&#xff0c;当我按照PyTorch 官方教程并为自己构建了一个运行良好的简单分类器时&#xff0c;我也有同样的感觉。 我…

Spring应用启动很慢?试试这个工具

前段时间搞应用启动优化的时候参考了这篇文章的很多观点&#xff0c;并基于此开发了此工具——spring-startup-analyzer。这是一个分析Spring应用启动过程的工具&#xff0c;通过采集Spring应用启动过程数据&#xff0c;生成交互式分析报告&#xff0c;用于分析Spring应用启动卡…

Netty进阶-黏包半包

黏包半包 1. 粘包与半包1.1 粘包现象1.3 现象分析1.4 解决方案方法1&#xff0c;短链接方法2&#xff0c;固定长度方法3&#xff0c;固定分隔符方法4&#xff0c;预设长度LengthFieldBasedFrameDecoder 1. 粘包与半包 1.1 粘包现象 服务端代码 Slf4j public class HelloWorl…

智能矩阵,引领商业新纪元!拓世方案:打破线上线下界限,开启无限营销可能!

在科技赋能商业大潮中&#xff0c;一切行业都在经历巨大变革&#xff0c;传统的营销策略被彻底改变&#xff0c;催生着无数企业去打造横跨线上线下、多维度、全方位的矩阵营销帝国。无数的成功案例已经告诉我们&#xff0c;营销不再只是宣传&#xff0c;而是建立品牌与消费者之…

JAVA实现校园失物招领管理系统 开源

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系统公告模块2.4 感谢留言模块 三、界面展示3.1 登录注册3.2 招领模块3.3 寻物模块3.4 公告模块3.5 感谢留言模块3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 基于VueSpri…

甲骨文真的要开放Java EE?

甲骨文表示&#xff0c;目前正在与可能的几个候选基金会&#xff0c;以及许可证持有者和社区在进行谈判。随着Java EE 8平台的确定&#xff0c;甲骨文在周四表示&#xff0c;目前正在考虑将Java Enterprise Edition技术转移到开源社区。 甲骨文在其博客中说道&#xff0c;这次的…

数据结构介绍与时间、空间复杂度

数据结构介绍 什么是数据结构&#xff1f;什么是算法&#xff1f;数据结构和算法的重要性 数据结构定义 数据结构是计算机科学中研究数据组织、存储和管理的一门学科。数据结构描述了数据对象之间的关系&#xff0c;以及对数据对象进行操作的方法和规则。 常见的数据结构 数…

解决node项目一个极度困难的捕获异常却无法读取异常信息的问题

这个项目是集成了第三方NeteaseCloudMusicApi项目的接口代码&#xff0c;我没有直接使用它的接口&#xff0c;因为需要再跑一个npm run开个端口&#xff0c;感觉很麻烦。 所以下定决心&#xff0c;使用拆分代码的方式&#xff0c;硬生生将这个api项目的部分api接口代码集成到了…

JavaScript变量和作用域简介

目录 变量 var let const 作用域 全局作用域 局部作用域 总结 JavaScript是一种广泛应用于网页开发的脚本语言。在JavaScript中&#xff0c;变量和作用域是非常重要的概念&#xff0c;它们决定了代码中数据的存储和访问方式。本文将介绍JavaScript中的变量和作用域&…

如何使用Abaqus进行摩擦生热仿真

Abaqus除了可以对结构进行强度分析&#xff0c;同样也有强大的固体传热分析功能&#xff0c;下面通过一个简单的实例演示Abaqus的双向热固耦合分析。 因为本案例涉及物体表面辐射&#xff0c;因此需要定义绝对零度和输入史蒂夫-波兹曼常数&#xff0c;如下&#xff1a; 本次分…

ESP8266,手机与电脑之间的TCP通讯

电脑端运行通讯猫调试助手,作为服务端: 电脑端 电脑的IP地址是: 192.168.2.232 手机与电脑之间的TCP通讯 手机端运行网络调试精灵,作为客户端: 手机端 如果从手机端点击"发送"按钮,则也会将"ghhh东方红广场"几个字发送到电脑上(服务端). ESP8266作为客户…

接口自动化测试框架搭建完整版

1、基本目录的搭建 report&#xff1a;静态输出目录(报告或者日志) data&#xff1a;静态输入目录(可以存放Excel数据&#xff0c;被读取的一些数据) utils&#xff1a;实用方法层(这里存放的是项目的公共方法&#xff0c;一般拿到别的项目可以直接使用&#xff0c;列如&…

【列存储学习总结】

在 OpenGauss 中&#xff0c;列存储是一种高效的数据存储方式&#xff0c;它在处理分析查询和数据仓库工作负载时具有很高的性能优势。列存储将表中的数据按列存储在磁盘上&#xff0c;而不是按行存储&#xff0c;这样可以极大地提高数据读取和分析操作的效率。当涉及大量数据的…

护眼灯买哪种好? 推荐五款儿童护眼台灯

台灯如何选择&#xff0c;随着人们生活水平的提高及科技的不断进步&#xff0c;台灯的品质也得到了极大的提高&#xff0c;在生活中很多时候都需要使用台灯&#xff0c;但是市面上的台灯那么多&#xff0c;台灯如何选择。本次小编为大家推荐五款好用的护眼灯。 1.书客护眼台灯L…

一款功能强大的视频编辑软件会声会影2024中文破解版安装教程教程含会声会影2024注册机下载

会声会影2023中文破解版是一款功能强大的视频编辑软件、大型视频制作软件、专业视频剪辑软件。会声会影专业视频编辑处理软件&#xff0c;可以用于剪辑合并视频&#xff0c;制作视频&#xff0c;屏幕录制&#xff0c;光盘制作&#xff0c;视频后期编辑、添加特效、字幕和配音等…