入门pytorch-Transformer

前言

虽然Transformer是2017年由Google推出,如果按照读论文只读近两年的思路看,那它无疑是过时的,但可惜的是,目前很多论文的核心依然是Transformer,或者由其进行改进的,故本文使用pytorch来搭建一下Transformer这个模型

全局分析

首先,我们要从整个模型架构入手,从大的层面看这块内容,然后再开始编写代码。欧克,这里默认大家掌握了一些基础知识,Transformer是由Google于2017年的Attention Is All You Need论文上所提出来的。如下图,则是论文中所提出的整个框架,可以很清晰的看出具体所使用的组件

在这里插入图片描述
总的来看,两个模块,编码器、解码器,特别有自编码器的思想(换句话说,Transformer借鉴了seq2sqe,而seq2sqe天然就是自编码器的思想),下面引用了论文当中的话.

  1. Encoder: 编码器由 N = 6 N = 6 N=6 个相同层的堆叠组成。每一层有两个子层。第一个是多头自注意机制,第二个是一个简单的,位置全连接前馈网络。我们在两个子层中的每一个周围使用残差连接,随后进行层归一化。也就是说,每个子层的输出是 L a y e r N o r m ( x + S u b l a y e r ( x ) ) LayerNorm(x + Sublayer(x)) LayerNorm(x+Sublayer(x)),其中 S u b l a y e r ( x ) Sublayer(x) Sublayer(x) 是由子层本身实现的函数。为了促进这些剩余连接,模型中的所有子层以及嵌入层产生维度 d m o d e l = 512 d_{model} = 512 dmodel=512 的输出。

  2. Decoder: 解码器也由 N = 6 N = 6 N=6 个相同层的堆栈组成。除了每个编码器层中的两个子层之外,解码器还插入第三子层,该第三子层对编码器堆栈的输出执行多头注意。与编码器类似,我们在每个子层周围使用残差连接,然后进行层归一化。我们还修改了解码器堆栈中的自注意子层,以防止位置注意到后续位置。该掩蔽与输出嵌入偏移一个位置的事实相结合,确保了位置i的预测可以仅依赖于小于 i i i 的位置处的已知输出。

好了,我们了解了整个大的框架,开始接触小的组件,由上图,我们可以进行拆分,其整个框架是由嵌入层、位置编码层、(掩码)多头注意力层、前馈神经网络层、残差连接+归一化层;接下来我们分别实现这些神经网络块(层),并将其堆叠在一起,其实就是Transformer了。

好了,接下来我们就开始编写代码吧

位置编码层

下面的就是位置编码。在这里打个比方,就比如机器翻译问题上,我们需要将一句话翻译成另一句话,即A --> B,那么A和B必然是长度随机的编码,这里输入的就是(想要翻译几句话,几个单词,每个单词的特征编码)

P E ( p o s , 2 i ) = s i n ( p o s / 1000 0 2 i / d m o d e l ) PE_{(pos,2i)} = sin(pos/10000^{2i/d_{model}}) PE(pos,2i)=sin(pos/100002i/dmodel)
P E ( p o s , 2 i + 1 ) = c o s ( p o s / 1000 0 2 i / d m o d e l ) PE_{(pos,2i+1)} = cos(pos/10000^{2i/d_{model}}) PE(pos,2i+1)=cos(pos/100002i/dmodel)

# 位置编码
class PositionalEncoding(nn.Module):'''num_hiddens: 神经元数量(嵌入维度数量)dropout: 神经元的丢弃概率max_len: 序列最大长度'''def __init__(self, num_hiddens, dropout, max_len=1000):super(PositionalEncoding, self).__init__()self.dropout = nn.Dropout(dropout)self.P = torch.zeros((1, max_len, num_hiddens))# 等差数列X = (torch.arange(max_len, dtype=torch.float32).reshape(-1, 1)/ torch.pow(10000, torch.arange(0, num_hiddens, 2, dtype=torch.float32) / num_hiddens))self.P[:, :, 0::2] = torch.sin(X)self.P[:, :, 1::2] = torch.cos(X)def forward(self, X): # X = (批次,序列长度,特征)X = X + self.P[:, :X.shape[1], :].to(X.device)return self.dropout(X)

残差连接+归一化层

欧克,接下来我们先来看一下简单理解的层。残差连接可以理解为防止梯度消失问题,以及加快收敛的一个有效的方法。而归一化就是防止梯度爆炸的问题

# 残差连接 + 归一化层
class AddNorm(nn.Module):'''normalized_shape: 形状大小dropout: 神经元的丢弃概率'''def __init__(self, normalized_shape, dropout):super(AddNorm, self).__init__()self.drop = nn.Dropout(dropout) # 丢弃层self.layer = nn.LayerNorm(normalized_shape) # 对输入的小批量应用层规范化def forward(self, X, Y):return self.layer(self.drop(Y) + X)

前馈神经网络层

这块也比较简单,就是两层的感知机而已。

F F N ( x ) = m a x ( 0 , x W 1 + b 1 ) W 2 + b 2 FFN(x) = max(0, xW_1 + b_1)W_2 + b_2 FFN(x)=max(0,xW1+b1)W2+b2

# 前馈神经网络
class PostionWiseFFN(nn.Module):'''num_input: 输入形状num_hiddens: 隐藏层形状num_ouput: 输出层形状'''def __init__(self, num_input, num_hiddens, num_ouput):super(PostionWiseFFN, self).__init__()self.liner1 = nn.Linear(num_input, num_hiddens) # 线性层1self.relu = nn.ReLU() # 激活函数self.liner2 = nn.Linear(num_hiddens, num_ouput) # 线性层2def forward(self, X):return self.liner2(self.relu(self.liner1(X)))

点积注意力机制

在这里插入图片描述

这里给出点积注意力机制的公式

A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d ) V Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d}})V Attention(Q,K,V)=softmax(d QKT)V

其中Q为查询矩阵,K键矩阵,V值矩阵,它们的维度是 ( n , d k ) (n,d_k) (n,dk) ( m , d k ) (m,d_k) (m,dk) ( m , d v ) (m,d_v) (m,dv),m是键值对的数量, d k d_k dk是键矩阵和查询矩阵的特征维度, d v d_v dv是值矩阵的特征维度。而除于 d \sqrt{d} d 是为了避免梯度消失问题

class DotProductAttention(nn.Module):'''dropout: 神经元的丢弃概率'''def __init__(self, dropout):super(DotProductAttention, self).__init__()self.dropout = nn.Dropout(dropout)def sequence_mask(self, X, valid_lens, value=0): # 根据valid_lens生成掩码'''X: (批量, 最大序列长度, 特征)valid_lens: 有效长度value: 填充数据'''maxlen = X.size(1)mask = torch.arange((maxlen), dtype=torch.float32,device=X.device)[None, :] < valid_lens[:, None]X[~mask] = valuereturn Xdef masked_softmax(self, X, valid_lens):if valid_lens is None:return nn.functional.softmax(X, dim=-1)else:shape = X.shapeif valid_lens.dim == 1:valid_lens = valid_lens.repeat_interleave(valid_lens, shape[1])else:valid_lens = valid_lens.reshape(-1)X = self.sequence_mask(X, valid_lens)return nn.functional.softmax(X.reshape(shape), dim=-1)def forward(self, Q, K, V, valid_lens=None):d = Q.shape[-1]scores = torch.bmm(Q, K.transpose(1, 2)) / math.sqrt(d) # 点积获取注意力分数self.attention_weights = self.masked_softmax(scores, valid_lens) # 获取注意力权重return torch.bmm(self.dropout(self.attention_weights), V) # 注意力权重 * 值V

到这里,我们由此便可以提出自注意力机制,而自注意力机制对上面做出了一个很简单的改变,就是 Q = K = V Q = K = V Q=K=V,换句话说Q、K、V同源

多头注意力

所谓多头注意力机制,就是有多个自注意力机制并行,然后将输出进行拼接送到线性层进一步整合

M u l t i H e a d ( Q , K , V ) = C o n c a t ( h e a d 1 , . . . , h e a d h ) W O w h e r e h e a d i = A t t e n t i o n ( Q W i Q , K W i K , V W i V ) MultiHead(Q,K,V) = Concat(head_1,...,head_h)W^O\\ where \ head_i = Attention(QW_i^Q,KW_i^K,VW_i^V) MultiHead(Q,K,V)=Concat(head1,...,headh)WOwhere headi=Attention(QWiQ,KWiK,VWiV)

# 多头注意力
class MultiHeadAttention(nn.Module):'''key_size: K值形状大小query_size: Q值形状大小value_size: V值形状大小num_hiddens: 隐藏层神经元数量num_heads: 头数dropout: 神经元的丢弃概率bias: 是否学习加性偏差,默认不学习'''def __init__(self, key_size, query_size, value_size, num_hiddens,num_heads, dropout, bias=False):super(MultiHeadAttention, self).__init__()assert num_hiddens % num_heads == 0self.num_heads = num_headsself.attention = DotProductAttention(dropout) # 这里使用到了点积注意力机制self.W_q = nn.Linear(query_size, num_hiddens, bias=bias) # Q矩阵权重self.W_k = nn.Linear(key_size, num_hiddens, bias=bias) # K矩阵权重self.W_v = nn.Linear(value_size, num_hiddens, bias=bias) # V矩阵权重self.W_o = nn.Linear(num_heads, num_hiddens, bias=bias) # 输出线性层def forward(self, Q, K, V, valid_lens): # valid_lens 有效长度Q = transpose_qkv(self.W_q(Q), self.num_heads)K = transpose_qkv(self.W_k(K), self.num_heads)V = transpose_qkv(self.W_v(V), self.num_heads)if valid_lens is not None:valid_lens = torch.repeat_interleave(valid_lens, repeats=self.num_heads, dim=0)output = self.attention(Q, K, V, valid_lens)output_concat = transpose_output(output, self.num_heads)return self.W_o(output_concat)

这里我们通过transpose_qkvtranspose_output函数来将数据进行改造,以此来进行并行操作

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 = X.permute(0, 2, 1, 3)return X.reshape(-1, X.shape[2], X.shape[3])
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)

编码器

如下则是编码器的代码

class EncoderBlock(nn.Module):'''K_size, Q_size, V_size: 键值对和查询的大小num_hiddens: 中间隐藏层神经元num_heads: 头数norm_shape: 残差连接+归一化层的输入形状num_input: FFN的输入形状ffn_num_hiddens: FFN隐藏层神经元dropout: 神经元的丢弃概率'''def __init__(self, K_size, Q_size, V_size, num_hiddens, num_heads,norm_shape, num_input, ffn_num_hiddens, dropout, bias=False):super(EncoderBlock, self).__init__()self.attention = MultiHeadAttention(K_size, Q_size, V_size, num_hiddens, num_heads, dropout, bias) # 多头注意力机制 self.addnorm1 = AddNorm(norm_shape, dropout) # 归一化+残差连接self.ffn = PostionWiseFFN(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))

接下来我们需要将位置编码层、嵌入层、残差连接+归一化层、前馈神经网络、编码器等封装起来

class TransformerEncoder(nn.Module):'''vocab_size: 词典大小K_size, Q_size, V_size: 键值对和查询的大小num_hiddens: 中间隐藏层神经元num_heads: 头数norm_shape: 残差连接+归一化层的输入形状num_input: FFN的输入形状ffn_num_hiddens: FFN隐藏层神经元num_layers: 编码器的层数dropout: 神经元的丢弃概率'''def __init__(self, vocab_size, K_size, Q_size, V_size, num_hiddens, num_heads,norm_shape, num_input, ffn_num_hiddens, num_layers, dropout, bias=False):super(TransformerEncoder, self).__init__()self.num_hiddens = num_hiddens#  num_embeddings: 嵌入词典的大小  embedding_dim: 每个嵌入向量的尺寸self.embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=num_hiddens) # 嵌入层self.pos_encoding = PositionalEncoding(num_hiddens, dropout) # 位置编码层self.blks = nn.Sequential()for i in range(num_layers):self.blks.add_module("block" + str(i),EncoderBlock(K_size, Q_size, V_size, num_hiddens, num_heads, norm_shape, num_input, ffn_num_hiddens, dropout, bias))def forward(self, X, valid_lens):# 输入 X (句子个数,单词个数)# 输出 ret (句子个数,单词个数,单词特征)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_weightsreturn X

解码器

class DecodeBlock(nn.Module):'''K_size, Q_size, V_size: 键值对和查询的大小num_hiddens: 中间隐藏层神经元num_heads: 头数norm_shape: 残差连接+归一化层的输入形状num_input: FFN的输入形状ffn_num_hiddens: FFN隐藏层神经元dropout: 神经元的丢弃概率i:'''def __init__(self, K_size, Q_size, V_size, num_hiddens, num_heads,norm_shape, num_input, ffn_num_hiddens, dropout, i):super(DecodeBlock, self).__init__()self.i = iself.attention1 = MultiHeadAttention(K_size, Q_size, V_size, num_hiddens, num_heads, dropout) # 多头注意力机制self.addnorm1 = AddNorm(norm_shape, dropout)self.attention2 = MultiHeadAttention(K_size, Q_size, V_size, num_hiddens, num_heads, dropout) # 多头注意力机制self.addnorm2 = AddNorm(norm_shape, dropout)self.ffn = PostionWiseFFN(num_input, ffn_num_hiddens, num_hiddens)self.addnorm3 = AddNorm(norm_shape, dropout)def forward(self, X, state):# state [编码器输入,编码器有效长度,中间状态用于记录]enc_outputs, enc_valid_lens = state[0], state[1]if state[2][self.i] is None:K = Xelse:K = torch.cat((state[2][self.i], X), axis=1)state[2][self.i] = Kif self.training:batch_size, num_steps, _ = X.shapedec_valid_lens = torch.arange(1, batch_size + 1, device=X.device)else:dec_valid_lens = NoneX2 = self.attention1(X, K, K, dec_valid_lens)Y = self.addnorm1(X, X2)Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens)Z = self.addnorm2(Y, Y2)return self.addnorm3(Z, self.ffn(Z)), state

封装

class TransformerDecoder(nn.Module):'''vocab_size: 词典大小K_size, Q_size, V_size: 键值对和查询的大小num_hiddens: 中间隐藏层神经元num_heads: 头数norm_shape: 残差连接+归一化层的输入形状num_input: FFN的输入形状ffn_num_hiddens: FFN隐藏层神经元num_layers: 编码器的层数dropout: 神经元的丢弃概率'''def __init__(self, vocab_size, K_size, Q_size, V_size, num_hiddens, num_heads,norm_shape, num_input, ffn_num_hiddens, num_layers, dropout):super(TransformerDecoder, self).__init__()self.num_hiddens = num_hiddensself.num_layers = num_layersself.embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=num_hiddens)self.pos_encoding = PositionalEncoding(num_hiddens, dropout)self.blks = nn.Sequential()for i in range(num_layers):self.blks.add_module("block" + str(i),DecodeBlock(K_size, Q_size, V_size, num_hiddens, num_heads, norm_shape, num_input, ffn_num_hiddens, dropout, i))self.linear = nn.Linear(num_hiddens, vocab_size)def init_state(self, enc_outputs, enc_valid_lens):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_weightsself._attention_weights[1][i] = blk.attention2.attention.attention_weightsreturn self.linear(X), self._attention_weights@propertydef attention_weights(self):return self._attention_weights

Transformer

最后整合一下

class EncoderDecoder(nn.Module):"""编码器-解码器架构的基类"""def __init__(self, encoder, decoder, **kwargs):super(EncoderDecoder, self).__init__(**kwargs)self.encoder = encoderself.decoder = decoderdef forward(self, enc_X, dec_X, *args):enc_outputs = self.encoder(enc_X, *args)dec_state = self.decoder.init_state(enc_outputs, *args)return self.decoder(dec_X, dec_state)

Transformer以三种不同的方式使用多头注意力:(引自论文)

  • 在 “encoder-decoder attention” 层中,查询 Q Q Q来自先前的解码器层,并且存储器键 K K K和值 V V V来自编码器的输出。这使得解码器中的每个位置都能处理输入序列中的所有位置。这模拟了序列到序列模型中的典型编码器-解码器注意机制。
  • 编码器包含自我注意层。在自关注层中,所有的键、值和查询都来自同一个地方,在这种情况下,是编码器中前一层的输出。编码器中的每个位置都可以处理编码器的前一层中的所有位置。
  • 类似地,解码器中的自关注层允许解码器中的每个位置关注解码器中的直到并且包括该位置的所有位置。我们需要防止解码器中的冗余信息流,以保持自回归特性。我们通过屏蔽(设置为 − ∞ -\infty s o f t m a x softmax softmax 输入中对应于非法连接的所有值来实现这一点。

最后,大家再来回顾一下Transformer模型整体架构
在这里插入图片描述

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

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

相关文章

利用代理IP爬取Zillow房产数据用于数据分析

引言 最近数据分析的热度在编程社区不断攀升&#xff0c;有很多小伙伴都开始学习或从事数据采集相关的工作。然而&#xff0c;网站数据已经成为网站的核心资产&#xff0c;许多网站都会设置一系列很复杂的防范措施&#xff0c;阻止外部人员随意采集其数据。为了解决这个问题&a…

精品基于Python实现的微信小程序校园导航系统-微信小程序

[含文档PPT源码等] [包运行成功永久免费答疑辅导] 《django微信小程序校园导航系统》该项目采用技术Python的django框架、mysql数据库 &#xff0c;项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、核心代码介绍视频等 软件开发环境及开发工具&#xf…

动手学深度学习-线性神经网络-7softmax回归的简洁实现

目录 初始化模型参数 重新审视Softmax的实现 优化算法 训练 小结 在 线性回归的实现中&#xff0c; 我们发现通过深度学习框架的高级API能够使实现 线性回归变得更加容易。 同样&#xff0c;通过深度学习框架的高级API也能更方便地实现softmax回归模型。 本节如在上一节…

09篇--图片的水印添加(掩膜的运用)

如何添加水印&#xff1f; 添加水印其实可以理解为将一张图片中的某个物体或者图案提取出来&#xff0c;然后叠加到另一张图片上。具体的操作思想是通过将原始图片转换成灰度图&#xff0c;并进行二值化处理&#xff0c;去除背景部分&#xff0c;得到一个类似掩膜的图像。然后…

封装数组去重的方法

前言 之前在工作中我一直在用lodash这个方法库&#xff0c;前段时间又接触了更现代化的方法库radash&#xff0c;这两个方法库可以说是各有优劣&#xff0c;lodash中有很实用的cloneDeep&#xff0c;radash中则有tryit、all等异步方法&#xff0c;它们都无法做到完全代替对方。…

前端成长之路:CSS复合选择器

复合选择器 在CSS中&#xff0c;可以根据选择器的类型将选择器分为基础选择器和复合选择器。 基础选择器就是前面提到过的类选择器、id选择器、标签选择器等&#xff1b;而复合选择器就是在基础选择器的基础之上&#xff0c;将基本选择器进行组合形成的。 复合选择器是由两个及…

在数字孪生开发领域threejs现在的最新版本已经更新到多少了?

在数字孪生开发领域three.js现在的最新版本已经更新到多少了&#xff1f; 在数字孪生开发领域&#xff0c;three.js作为一款强大的JavaScript 3D库&#xff0c;广泛应用于Web3D可视化、智慧城市、智慧园区、数字孪生等多个领域。随着技术的不断进步和需求的日益增长&#xff0…

HTML入门级学习笔记1【超详细】

目录 一、计算机基础知识 2.1 文件和文件夹管理 2.2 特殊按键和快捷键 2.3 打字速度 二、互联网的原理 3.1 上网就是请求数据 3.2 服务器 3.3 浏览器 3.4 HTTP 三、HTML初步认识 4.1 认识什么是纯文本文件txt 4.2 HTML是负责描述文档语义的语言 四、Sublime 五、HTML骨架和基本…

【网络】传输层协议UDP/TCP网络层IP数据链路层MACNAT详解

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;计算机网络原理_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.传输层协议 UDP 1.1 传输层 1.2 端口号 1.3 UDP 协议 1.3.1 UDP 协议端格式 1.3.2 UDP 的特点 1.3.3 面向数据报 1…

计算机网络技术基础:3.计算机网络的拓扑结构

网络拓扑结构是指用传输媒体互连各种设备的物理布局&#xff0c;即用什么方式把网络中的计算机等设备连接起来。将工作站、服务站等网络设备抽象为点&#xff0c;称为“节点”&#xff1b;将通信线路抽象为线&#xff0c;称为“链路”。由节点和链路构成的抽象结构就是网络拓扑…

SecureCRT/FX使用[无限试用SecureCRT][新版本SecureFX双击站点总是自动跳到SecureCRT]

无限试用SecureCRT 本文摘录于&#xff1a;https://blog.csdn.net/qq_52162404/article/details/139703993#:~:textSecureCRT只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 我这里修改BAT如下,同时删除CRT和FX的license: echo off re…

Nacos系列:Nacos 控制台手册

引言 Nacos是阿里巴巴中间件部门开源的一款用于服务发现和配置管理的产品&#xff0c;Nacos 控制台主要旨在于增强对于服务列表、健康状态管理、服务治理、分布式配置管理等方面的管控能力&#xff0c;以便进一步帮助用户降低管理微服务应用架构的成本。 一、访问 Nacos 控制台…

frida打印日志字体加颜色

平时写frida hook 脚本&#xff0c;有时打印出来的日志太多都是一个颜色不美观。 于是找AI 问了一下frida打印日志字体加颜色的方法 // ANSI 转义码 const RESET “\x1b[0m”; const RED “\x1b[31m”; const GREEN “\x1b[32m”; const YELLOW “\x1b[33m”; const BLUE …

Thunderfury, Blessed Blade of the Windseeker

Thunderfury, Blessed Blade of the Windseeker 雷霆之怒&#xff0c;逐风者的祝福之剑 她应该是wow里面造型最帅&#xff0c;最酷的模型超大武器&#xff0c;而且特效触发频率很高&#xff0c;群拉效果非常好。 雷霆之怒&#xff0c;逐风者的祝福之剑 - 物品 - NFU魔兽世界1…

ubuntu+ros新手笔记(三)

系统ubuntu22.04 ros2 humble 1. 设置ubuntu终端字体大小 点击Terminal右上角的三条横线—>Preferences—>Unnamed—>Text—>勾选Custom font—>点击右侧的字号&#xff08;我的显示的是12&#xff09;—>最下方Size处设置字号大小—>Select—>设置完…

Deveco报错Only files in a module can be previewed

创建工程后需要看看预览的效果&#xff0c;却出现了这个错误 到Index.ets文件下重新构建工程 详细可以查询 鸿蒙报错&#xff08;Only files in a module can be previewed&#xff09;

第六届地博会开幕,世界酒中国菜美食文化节同期启幕推动地标发展

第六届知交会暨地博会开幕&#xff0c;辽黔欧三地馆亮点纷呈&#xff0c;世界酒中国菜助力地理标志产品发展 第六届知交会暨地博会盛大开幕&#xff0c;多地展馆亮点频出&#xff0c;美食文化节同期启幕推动地标产业发展 12月9日&#xff0c;第六届粤港澳大湾区知识产权交易博…

计算机毕业设计PySpark+PyFlink+Hive地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Hadoop 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

C# 探险之旅:第二十七节 - 类型class(属性) —— 给你的类穿上“属性”的外衣

嘿&#xff0c;探险家们&#xff01;欢迎再次踏上我们的C#奇幻之旅。今天&#xff0c;我们要聊聊一个超级有趣的话题——类的“属性”。想象一下&#xff0c;如果我们要给类穿上一件酷炫的外衣&#xff0c;那属性就是这件外衣上的各种口袋和装饰&#xff0c;让类变得既实用又拉…

【学习记录】Docker初探-容器创建与拉取(2)

参考资料 Window下玩转Docker Desktop哔哩哔哩bilibili Docker镜像推送至Docker Hub的完整教程-百度开发者中心 (baidu.com) 为什么需要Docker&#xff1f; Docker可以在不同服务器之间转移打包好的程序和环境&#xff0c;从而方便测试。打包好的程序和环境可以被称之为容器…