第十一章 原理篇:transformer模型入门

news2024/11/16 7:41:15

说在前面的话:

找工作面试不是特别顺利。进了目标公司的二面,但是一面面试官问的一些“新技术”问题答得不太好,尤其是transformer相关的。这一点确实是自己的问题,在工作后总是面向业务学习,对很多算法都是处于“听说过但没用过”的状态。VIT/SWIN/Convnext之前明明都专门去学习过,但是因为没有深入了解,所以也没有留下比较深刻的记忆。
加油啊!

参考教程:

  • bilibili 迪哥的人工智能课
  • WORD EMBEDDINGS: ENCODING LEXICAL SEMANTICS
  • https://zhuanlan.zhihu.com/p/48731949

文章目录

  • 从Embedding开始
    • 词的向量化
    • 词向量模型
    • 过程介绍
      • 数据的获取
      • word2vec
    • 代码介绍
      • torch.nn.Embedding()
      • 代码实现
  • Transformer 与 self-attention
    • self-attention
    • multi-head attention
    • position-embedding
    • Transformer
      • Encoder
      • Decoder
    • 代码实现
      • 整体结构
      • input-part
      • attention and multi-head attention
      • LN + add
      • encoderlayer

从Embedding开始

词的向量化

transformer最初主要还是用于NLP任务中。在NLP任务中,对于一个文本的输入,通常会使用embedding对它进行一个向量化的表示。
embedding的目标,就是用一组向量来刻画现有的数据集。那么就对这个embedding有了一点要求。

  • 要有词的先后顺序。
  • 相似的词在向量空间的表达应该也是相似的。
  • 同一类的词在空间内应该是相近的。

第一个要求很好理解,比如你可以说“这个房子里有一条狗”但是你不能说“这条狗里有一个房子”。顺序的改变是会影响文本的含义的,因此我们希望对文本中单词进行编码时,能记录他们的位置信息。
第二个要求举个例子,“月亮”和“月球”两个词虽然组成它们的文字不一样,但是它们都是“月”的一个称呼,意思也都是“月”。因此我们希望它们的向量也比较相近。
第三个要求是限制同一类词的距离,比如说“篮球,足球,泰山”。那么篮球和足球作为一种体育用品,并且都是球类,它们两者在向量空间内肯定是比它们和泰山的距离要近的。泰山和这两个词本来就是八竿子打不着,如果它距离篮球比足球距离篮球还要近,这就很违背常人的逻辑印象。

同时呢,我们也希望得到的这个embedding的向量表示的维度也相对高一点,维度高意味着信息更多,那么在使用这个向量表示时,我们得到的结果也会相对更可靠一些。

词向量模型

我们的词向量,也是是文本中词汇的embedding化表示,是可以通过多种方法实现的。比如说简单的one-hot编码,又或者使用频率表示。但是这两种方法都是有缺陷的。

  • 使用one-hot编码,假如你的词库非常大,那么为了表示一个单词,就需要一个极其长的向量表示,造成极大的内存浪费。
  • 频率是通过用某单词在文章中出现次数除以文章的单词个数获得的,它的优点是简单快速,但是忽略了词的位置信息和相关性。

我们可以通过所谓的词向量模型来获得词向量。
词向量模型是一种需要训练的模型,使用这个模型,可以帮助我们获得对词库中的单词的向量化表达。这种模型的输入也是一个embedding,可以理解成对指定单词的一个标志,用于把单词区分开来。而它的输出结果是一个概率,表示在给定输入的情况下,最可能返回的单词是什么。

当然我们想要的并不是这个输出的结果,而是藏在模型中间的embedding。

整体的过程可以理解为,我们想要的embedding是这个模型的一个中间结果,它会随着模型的更新迭代而变化,我们判断这个embedding是否好用的根据是模型的输出结果是否准确。

而在这个任务中,模型的输入是给定的文本编码,模型的输出是目标单词出现的概率。所以它其实是一个多分类的问题,最终的概率是由softmax函数得到的。在你的分类目标很多的情况下,softmax计算会很麻烦,因此又有一个优化的方法:

输入A和B两个单词,判断这两个词是不是前后邻居。

这样这个任务就变成了二分类的任务。

过程介绍

数据的获取

常用的方法是:
使用滑动窗口构建输入数据。
在这里插入图片描述
从别的地方偷了一张图。这里就给出了一个很直观的例子。
在上面一节我们说词向量的模型输入是给定的文本编码,输出是目标单词的概率。而图中就是一个用前文预测下文的例子,它的前文单词数长度为2,也就说说用前两个词预测第三个词。
对于一段文本,从第一个单词开始,用连续的两个词预测下一个词,所以才会有inputs是“thou shalt”,output(target)是“not”。

具体input和output是如何获得的,还是由使用者自行决定的。除了用前n个词预测下一个词外,也可以使用前n个加后n个词,来预测中间的词。

word2vec

常用的两种模型介绍

  • CBOW:输入目标单词前面和后面的n个词,进行单词本身的预测。
  • skip-gram:输入一个单词,预测它前n个词和后n个词。

下方左边就是CBOW的例子,右边是Skip-gram的例子。
在这里插入图片描述
两个模型的原理是相似的。

对于CBOW,我们传入四个单词的编码(这里的编码不是我们想要的embedding,只是为了区分不同单词的标号),通过模型获得四个单词的embedding,对这个embedding进行处理,并用于预测四个单词正中间的单词,获得“目标单词为词库中的第n个词的概率”,最大概率对应的单词就是我们的输出。

对于Skip-gram,我们传入中间单词的编码,通过模型获得embedding,再用于预测它周围的四个单词。

代码介绍

torch.nn.Embedding()

torch.nn.Embedding(num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False, _weight=None, _freeze=False, device=None, dtype=None)

先来看一下这个class中的参数都有什么用。

  • 第一个参数 num_embeddings:代表你的embedding有多少个,也可以等价于你期望的词库大小。
  • 第二个参数embedding_dim是你希望得到的embedding向量的维度。
  • padding_idx:可以指定不用于梯度更新的index的位置。也就是在模型训练的过程中,别的位置会随着backward更新,而这个位置不会。
  • max-norm:用于对你的vector进行归一化
  • p-norm:归一化使用L2还是L1。
  • _weight: 你可以自己传入一个weights,这里的weight就是生成的embedding矩阵的值,如果这个weight和你预设的大小不一致,也会重新生成。

它的前向传播得到的结果是

def forward(self, input: Tensor) -> Tensor:
        return F.embedding(
            input, self.weight, self.padding_idx, self.max_norm,
            self.norm_type, self.scale_grad_by_freq, self.sparse)

对于在前向传播中使用的函数F.embedding,它的解释是This module is often used to retrieve word embeddings using indices. The input to the module is a list of indices, and the embedding matrix, and the output is the corresponding word embeddings. 也就是说我们传入的input是一组index,F.embedding会根据这个index去我们的weight中取vector。
比如你用的index是[1,3,5],返回结果就是self.weights的第二行,第四行和第六行,三行长度为embedding_dim的向量。

代码实现

代码实现可以直接参考pytorch tutorial中给出的例子,在这里会对代码进行解释。

下面是一个CBOW的例子

首先获取我们的数据。完成输入输出对的构建。

context_size = 2   # 代表我们会使用前面和后面各两个单词在预测中间的词。
raw_text = """...""".split()  # 原始文章的string,这里因为字数太多没有列出来,可以直接替换成自己的
vocab = set(raw_text) # 文章中都使用了那些单词,这个也就是我们的词库
vocab_size = len(vocab) # 我们的词库大小

word_to_ix = {word:i for i, word in enumerate(vocab)} # 这里构建了一个字典,该字典是key是单词,value是单词的index。

# 下一步来构建我们的dataset
for i in range(context_size, len(raw_text) - context_size):
# 遍历整个文章
	context = (
	[raw_text[i-j-1] for j in range(context_size)]  # 第i个单词的前context_size个词
	+ [raw_text[i+j+1] for j in range(context_size)] # 第i个单词的后context_size个词
	)
	target = raw_text[i] # 第i个单词
	data.append((context, target)) # 将我们的input和target按对放入data中

然后定义我们的模型。
在这里给出一个forward流程的更详细的介绍。
假如我们输入的是[2,1,3,4]。target是0。预设的vocab_size = 500, embedding_dim = 64。
那么我们期望得到的一个整个词库的embedding大小就是500*64,也就是nn.Embedding中weights的大小。
现在输入[2,1,3,4],经过第一步self.embeddings(inputs),得到的结果大小为4*64。然后求和并用view(1,-1)后,得到的结果为1*64,也就是四个embedding的融合。
经过两个线性层后,得到的out大小是1*500。

class CBOW(nn.Module):
	# 定义CBOW类,继承nn.Module
    def __init__(self, vocab_size, embedding_dim):
        super(CBOW,self).__init__()
		# vocab_size: 我们的词库的大小,因为输出结果是在每个词上的概率,所以如果直接用softmax求概率,我们的输出维度应该=词库大小。
		# embedding_dim:你自己决定的embedding向量的长度。
		self.embeddings = nn.Embedding(vocab_size, embedding_dim) # 我们想要的embedding结果在这里,它可以看作是一个高为vocab_size, 高为embedding_dim的矩阵。
		self.proj = nn.Linear(embedding_dim, 128) # 中间层
		self.output = nn.Linear(128, vocab_size) # 用于输出的全连接层,对vocab_size个类别进行预测。
    def forward(self, inputs):
        embeds = sum(self.embeddings(inputs)).view(1,-1) # 找到inputs的embedding并求和处理
        out = F.relu(self.proj(embeds)) # 中间结果
        out = self.output(out) # 预测的pred
        prob = F.log_softmax(out, dim=-1) # 这里直接进行了softmax计算。
        return prob

在上面的部分中解释过这里的nn.Embedding()在进行前向传播时,传入的input是词的index。而我们刚刚构建的dataset中datat[i][0]是输入的词,data[i][1]是对应的target。并不是index。所以我们要对输入再转换一下。

def make_context_vector(context, word_to_ix):
    idxs = [word_to_ix[w] for w in context]  # 从我们之前创建的word_to_index的dict中根据word取index
    return torch.tensor(idxs, dtype=torch.long) # 要注意输入是tensor


make_context_vector(data[0][0], word_to_ix)  # example

可以看一下转换的结果
在这里插入图片描述
下方就是一个比较常规的训练代码。

EMBEDDING_DIM = 64

loss_function = nn.NLLLoss()
model = CBOW(len(vocab), EMBEDDING_DIM)
optimizer = optim.SGD(model.parameters(), lr=0.001)

for epoch in range(10):
    total_loss = 0
    for context, target in data:
        context_idxs = make_context_vector(context, word_to_ix)

        optimizer.zero_grad()
        log_probs = model(context_idxs)

        loss = loss_function(log_probs, torch.tensor([word_to_ix[target]], dtype=torch.long))

        loss.backward()
        optimizer.step()

        total_loss += loss.item()

这里给出了一个可视化的例子,因为训练的轮数比较少,得到的embedding可能不是很准确,但是也勉强可以看到单词‘we’和‘We’比‘computer’要更接近。
在这里插入图片描述

Transformer 与 self-attention

self-attention

传统的向量会遇到一个问题,就是在生成了embedding后,这个embedding不会发生变化了。那么在不同的语境情况下,固定的embedding肯定是不合适的,它代表的语义不会随着情境改变,就会产生错误。

注意力的加入可以帮助解决这个问题,这样在不同的情境下,语句内部的元素的关注度是不一样的。

传统的注意力机制是发生在input和output之间的,而self-attention则是发生在输入的内部,或者输出的内部。相当于你输入一长串文字时,每个文字对上下文元素的关注度是不一样的,你可以通过self-attention来得到元素间的关注度。

在这里插入图片描述
在self-attention中有三个很重要的矩阵,分别是queries, keys, values。也就是上图中的q, k, v。

  • queries: 你要去查询的向量。
  • keys:要被查询的向量。
  • values: 实际的特征信息。

这个解释可能比较难理解。具体来讲,qi是你的目标,你想知道q对别的元素的关注度attention,那么你就用qi和别的所有的k进行比较,当然也包括了ki。

它们的获得方法也很简单,就是与权重矩阵相乘。下图为一个比较完整的步骤。

第一步,对于输入的向量I,分别与Wq, Wk和Wv相乘,得到备用的Q, K, V。
具体来说,假如你现在有N个1*C的输入,W系列的权重大小均是C*V,那么你分别会得到N个大小为1*V的向量。也就是你的{q1…qn},{k1…kn},{v1…vn}。
第二步,用Q与K进行内积,算出匹配度。上图给了更具体的细节,对于输入向量a1,除了用它的q1和k1做内积外,还要用它的q1和别的输入向量的k做内积。
因为一共有n个k,所以这一步完成后,你得到的是一个大小为1*n的向量。因为你除了a1外还有n-1个输出,所以整体的结果应该是一个n*n的矩阵,反应的是n个输入之间彼此的相关性。这个结果会用softmax处理,得到一个概率分布,代表每个元素相对当前目标的重要程度。
s o f t m a x ( Q × K T d k ) softmax(\frac{Q\times K^T}{\sqrt{dk}}) softmax(dk Q×KT)
补充一下为什么要除以根号dk,因为长度很大的时候,点积的结果也会很大,那么在大数和小数差距很大的情况下,softmax容易被推到梯度很小的区域,所以这里除以dk来平衡一下。

第三步,将v按照上一步得到的结果进行加权求和后得到一个新的输出。
在这里插入图片描述

multi-head attention

一组q,k,v能得到一个当前特征的表达。假如有多个q, k, v 就能得到多个表达。每一组的计算过程仍然是一样的。
对于多组的输出,将结果直接concat在一起。
具体如下图所示。
我们的bi,1是通过[qi1ki1,qi1kj1]与vi1,vj1结果加权求和得到的。
我们的bi,2是通过[qi2ki2, qi2kj2]与vi2,vj2结果加权求和得到的。
在这里插入图片描述
两个结果concat起来,再使用全连接降维,就得到最终的输出。

position-embedding

上面讲到的输入a1, a2, … an,我们是可以知道顺序的,但是在模型的计算中没有反应出这种顺序,甚至也利用不到这个信息。因此为了体现出前后顺序的区别,可以在输入ai中加入位置的编码信息。
在这里插入图片描述

Transformer

可以直接参考:https://zhuanlan.zhihu.com/p/460588687
我感觉解释得比较详细。
一个完整的transformer结构,包括了encoder和decoder两部分。
这两部分都是由堆叠的 multi-head attention + BN组成的。当然还有不可忽略的残差结构。

Encoder

编码器在原始输入序列中进行了两个预处理:

  • 词嵌入:将词转为成向量。
  • 位置编码:将词在序列中的位置信息加入考量。

预处理的结果会被传入到block里,一个block里面有两个子层。

  1. 多头注意力+残差和+层归一化
  2. 全连接+残差和+层归一化

写一个公式化的流程

# 输入ai 并生成position embedding pi
ai  = ai + pi # 把ai和pi合成一个新的ai
while n: # 假设有n个block

	x = multi_head_attention(ai)
	x = layer_norm(x + ai)
	
	x2 = feed_forward(x)
	x2 = layer_norm(x2 + x)
	ai = x
	n-=1

在这里插入图片描述

Decoder

解码器虽然和编码器看起来很相似,但是还是有一点区别。

  1. Masked Multi-Head Attention
    Decoder中第一个注意力机制使用的Masked Multi-Head Attention,Masked Multi-Head attention的计算顺序其实是和Decoder的串行计算顺序相对应。
    因为在进行预测的时候,我们并不是能直接看见所有的文本的,文本的结果是一个一个出现的,因此masked multi-head attention就是为了模拟这个过程【我是这么理解的,也可能不对】。
    你只能使用已知的信息做预测,所以masked multi-head attention遮住了理论上应该未知的信息。

  2. Cross Attention
    在这个地方,queries, keys, values来源不同。

    • keys: encoder输出中计算
    • values:encoder输出中计算
    • queries:decoder输出中计算。
      在这里插入图片描述

代码实现

参考教程:https://zhuanlan.zhihu.com/p/339207092

跟着教程写一遍,加深理解。

整体结构

首先要思考一下在这个过程中都需要完成哪些组件。

  • embedding,我们encoder和decoder的输入都是词向量,而非词本身,所以我们最开始要使用embeddinglayer进行单词的转换。
  • positionembedding,我们的输入要带有位置信息,因此还需要和位置进行编码。
  • 网络由多个block组成,每个block都是一样的结构。
    • multi-head attention和attention。
    • layernorm 用于对输出做归一化
    • 残差结构,输出要和输入相加。
    • feedforward部分,有的网络中也会使用更高级的forward,我还没有学到那里。
  • 对于decoder,它使用的是带mask的attention。

在这里就不多关注decoder了。

input-part

在数据传入网络前,我们要对它进行编码,并加上位置信息。因此这里需要一个embedding和一个positionembedding。

class Embedding(nn.Module):
    def __init__(self, embed_dim, vocab):
        super(Embedding, self).__init__()
        self.embed = nn.Embedding(vocal, d_model)
        self.embed_dim = embed_dim
        
    def forward(self, x):
        return self.embed(x)*math.sqrt(self.embed_dim)
        # 这里用权重乘以向量长度开根,其实不是很理解为啥。
def PositionalEncoding(nn.Module):
    def __init__(self, embed_dim, dropout, max_len = 5000):
        super(PositionalEncoding, self).__init__()
        self.dropout = nn.Dropout(p=dropout)
        
        pe = torch.zeros(max_len, embed_dim)
        # pe是我们的位置编码,它是max_len个长度为embed_dim的vector。
        # 因为pe的位置编码是要和词向量直接相加的,所以他们的长度也要保持一致。
        position = torch.arange(0, max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, embed_dim, 2)*
                            (-math.log(10000.0)/embed_dim))
        pe[:,0::2] = torch.sin(position * div_term)
        pe[:,1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        
        
    def forward(self, x):
        # 这里的上限x.size(1)应该是怕出现维度过长的情况。
        # 可以看到这里是直接相加的,那么x应该是词向量。
        x = x + Variable(self.pe[:, :x.size(1)], requires_grad = False)
        return self.dropout(x)
        

attention and multi-head attention

attention的计算是比较好理解的,对于得到的q,k,v。q和k做内积和softmax求概率值,然后和v加权求和。

def attention(query, key, value, mask = None, dropout = None):
    
    # 向量长度
    d_k = query.size(-1)
    # 内积
    scores = torch.matmul(query, key.transpose(-2,-1))/math.sqrt(d_k)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, -1e9)
    
    p_attn = F.softmax(scores, dim = -1) # 求得不同元素相对目标的重要性
    
    if dropout is not None:
        p_attn = dropout(p_attn)
    
    # 特征加权求和。得到输出
    return torch.matmul(p_attn, value), p_attn

多头attention会把每个attention的结果concat在一起然后使用全连接层。多头attention计算的时候也是直接使用的矩阵,它的效率更高。并不需要我们用什么遍历的方法一个一个计算。

在这里的concat,也只是把结果reshape了一下就可以得到了。

class MultiHeadAttention(nn.Module):
    def __init__(self, h, embed_dim, dropout = 0.1):
        super(MultiHeadAttention, self).__init__()
        self.d_k = embed_dim//h # 这里是每个head关注embedding的一部分
        self.h = h # head的个数
        
        self.q = nn.Linear(embed_dim, embed_dim)
        self.k = nn.Linear(embed_dim, embed_dim)
        self.v = nn.Linear(embed_dim, embed_dim) 
        
        self.linear = nn.Linear(embed_dim, embed_dim) # 全连接的部分
        # 用于计算Q,K,V
        self.attn = None
        self.dropout = nn.Dropout(p=dropout)
        
    def forward(self, q, k, v, mask = None):
        
        if mask is not None:
            mask = mask.unsqueeze(1)
            
        nbatches = query.size(0)
        
        query = self.q(q).view(nbatches, -1, self.h, self.d_k).transpose(1,2)
        key= self.k(k).view(nbatches, -1, self.h, self.d_k).transpose(1,2)
        value = self.v(v).view(nbatches, -1, self.h, self.d_k).transpose(1,2)
        
        x, self.attn = attention(query, key, value, mask = mask, dropout = self.dropout)
        
        x = x.transpose(1,2).contiguout().view(nbatches, -1, self.h * self.d_k)
        
        return self.linear(x)
        
        

LN + add

LN的作用是对同一物体的不同维度进行归一化。

class LayerNorm(nn.Module):
    def __init__(self, features, eps = 1e-6):
        super(LayerNrom,self).__init__()
        self.gamma = nn.Parameter(torch.ones(features))
        self.beta = nn.Parameter(torch.zeros(features))
        
        self.eps = eps
        
    def forward(self, x):
        mean = x.mean(-1, keepdim = True)
        std = x.std(-1, keepdim = True)
        return self.gamma * (x - mean) / (std+self.eps) + self.beta

add是一个残差结构,将输入和输出直接相加在一起。

class SublayerConnection(nn.Module):
    
    def __init__(self, size, dropout):
        super(SublayerConnection, self).__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, residual,  x):
        
        return self.norm(x + residual)
#     def forward(self, x, sublayer):
#         return x + self.dropout(sublayer(self.norm(x)))
# 在教程提供的代码中,x可以理解成上一层的未经过norm的输出。所以在这里先做了norm。
# 然后这里的sublayer是我们的的多头注意力,所以这里返回的是未作norm的残差和
    

encoderlayer

class EncoderLayer(nn.Module):
    
    def __init__(self, size, self_attn, feed_dorward, dropout):
        super(EncoderLayer, self).__init__()
        
        self.self_attn = self_attn  # 这个self_attn就是我们的多头attention
        self.feed_forward = feed_forward
        self.sublayer1 = SublayerConnection(size, dropout) # 我们的sublayer1是多头+add+norm
        self.sublayer2 = SublayerConnection(size, dropout) # 我们的sublayer2是feedforward+add+norm
        self.size = size
        
    def forward(self, x, mask):
        
        x1 = self.self_attn(x, x, x, mask)
        # 得到新的value
        x1 = self.sublayer1(x, x1)
        
        x2 = self.feed_forward(x1)
        x2 = self.sublayer2(x1, x2)
        
        return x2

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

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

相关文章

AD从原理图到PCB超详细教程

AD超详细教程 前言一、建立一个工程模板二、原理图1.设计原理图。2.使用AD自带库和网上开源原理图库3.画原理图库4.编译原理图 三、PCB1.确定元器件尺寸大小2.绘制PCB Library①使用元器件向导绘制元件库②原理图与PCB的映射 3.绘制PCB①更新PCB②调整元件位置③布线④漏线检查…

库操作和表操作(数据库系列2)

目录 前言: 1.数据库的操作 1.1显示当前的数据库 1.2创建数据库 1.3使用数据库 1.4删除数据库 2.常用数据类型 2.1数值类型 2.2字符串类型 2.3日期类型 3.表的操作 3.1查看表结构 3.2创建表 3.3查看表 3.4删除表 结束语: 前言&#xff1…

【硬件自动化测试--测试软件的设计及实现】如何设计并实现!

今天来聊聊关于硬件方向的自动化软件设计及实现,后面我会用实例来让我们更加深入的了解硬件自动化,首先开发工具选择的是python语言,为啥选择python语言呢,因为他的语法比较简洁,外置库非常多,反正就是对于做自动化方面很实用就对了。 1.硬件自动化测试大致分为三个阶段实…

拓展:IDEA如何使用不同版本的JDK?(改了还报错很可能因为没改全,以mac为例)

以下面的案例为例 Enhanced ‘switch’ blocks are not supported at language level ‘8’ 后面知道是因为Spring的版本和JDK的版本不对应,结果网上找到的解决方案都很简单。下载了一个新版本的JDK,然后IDEA里面Project Structure的Project标签里把SDK给…

ubuntu的aarch64版本上安装anaconda

ubuntu的aarch64版本上安装anaconda 问题背景:今天在基于docker安装的ubuntu18-04的版本上想要安装anaconda,但是出现了问题,发现ubuntu的版本18-04对应的是aarch64,因此记录安装方法。 首先下载安装包没问题但是,在具…

机器学习复习7

机器学习复习7 1 - 根据下图中绘制的决策树,如果一个动物的耳朵是软的,脸型是圆的,并且有胡须,那么这个模型会预测它是猫还是不是猫? A. 不是猫 B. 是猫 **答案:B ** 2 - 以一棵决策树学习来对垃圾邮件和非…

spring boot启动原理分析

springboot启动类中有两个关键的地方 1.SpringBootApplication注解 2.SpringApplication.run方法 SpringBootApplication注解分析 SpringBootApplication注解由三大注解构成, SpringBootConfiguration、EnableAutoConfiguration、ComponentScan。 SpringBootCon…

Java-八股文-基础本部分<一>

Java基础部分 基础篇<一> Java基础部分 基础篇<二> Java基础部分 基础篇<三> Java基础部分 异常篇 Java基础部分 集合篇 Java基础部分 线程篇 ❤️ &#x1f9e1; &#x1f49b; &#x1f49a; &#x1f499; &#x1f49c;&#x1f5a4; &#x1f90d;…

什么是网络货运平台?

一、什么是网络货运平台&#xff1f; 网络货运平台是依托互联网平台整合配置运输资源&#xff0c;以承运人身份与托运人签订运输服务合同、承担承运人责任&#xff0c;委托实际承运人完成运输服务的物流平台。它通过互联网形式实现运输过程真实、公平、公正、合法&#xff0c…

【LeetCode】239. 滑动窗口最大值

239. 滑动窗口最大值 思路 当我们遍历数组时&#xff0c;我们需要维护一个双端队列&#xff0c;用于存储滑动窗口中的元素的索引。 队列中的元素按照降序排列&#xff0c;即「队头元素是当前滑动窗口中的最大值」。 具体的步骤如下&#xff1a; 创建一个双端队列 window&#…

Spring Boot中的Elasticsearch自动配置:原理与使用

Spring Boot中的Elasticsearch自动配置&#xff1a;原理与使用 简介 在Spring Boot中&#xff0c;Elasticsearch是非常流行的搜索引擎。为了方便开发人员使用Elasticsearch&#xff0c;Spring Boot提供了Elasticsearch自动配置功能。本文将介绍Elasticsearch自动配置的原理与…

burpsuite踩坑(一)

今天在使用burpsuite的时候&#xff0c;能抓到https或者http的包。 但是repeater模块无法使用&#xff0c;而且放行包之后&#xff0c;会出现提示。 搞了半天&#xff0c;以为是证书的问题&#xff0c;或者是burp汉化版的原因&#xff0c;还把汉化版的burp给删除了。 发现都…

【Docker】基于jib插件,实现Docker部署springboot项目

文章目录 创建springboot项目jib插件介绍使用打tar包 Docker部署springboot项目 在工作中&#xff0c;作为一名后端开发人员&#xff0c;项目部署运维的事我们可能都要同时干&#xff0c;今天想跟大家聊聊关于springboot项目使用docker部署相关操作。后期还会跟大家分享docker-…

笔记-方向导数和梯度

笔记-方向导数和梯度 目录方向导数梯度方向导数和梯度的关系 目录 方向导数 方向导数和偏导数的区别就是&#xff1a;方向不同。仅此而已。 我们常说的偏导数无非就是对x轴求偏导&#xff0c;对y求偏导。而方向导数则是对x轴与y轴之间的某一新方向求导数。 还是用一下上次的…

大模型基础之注意力机制和Transformer

【注意力机制】 核心思想&#xff1a;在decoder的每一步&#xff0c;把encoder端所有的向量提供给decoder&#xff0c;这样decoder根据当前自身状态&#xff0c;来自动选择需要使用的向量和信息. 【注意力带来的可解释性】 decoder在每次生成时可以关注到encoder端所有位置的…

Llama大模型运行的消费级硬件要求【CPU|GPU|RAM|SSD】

大型语言模型 (LLM) 是强大的工具&#xff0c;可以为各种任务和领域生成自然语言文本。 最先进的LLM之一是 LLaMA&#xff08;大型语言模型 Meta AI&#xff09;&#xff0c;这是由 Facebook 的研究部门 Meta AI 开发的一个包含 650 亿个参数的模型 要在家运行 LLaMA 模型&…

详解LeafLet中如何展示GeoServer发布的图层组

目录 前言 一、关于图层组 1、使用图层图组的好处 2、创建图层组 二、在Leaflet中展示图层组 1、新建Html模板框架 2、绑定地图map和底图设置 3、绑定图层组 总结 前言 在之前的博文中&#xff0c;曾经重点介绍如何使用LeafLet叠加Geoserver wms图层到已有底图的方法 ,…

机器学习复习6

机器学习复习 1 - 在机器学习的背景下&#xff0c;什么是诊断(diagnostic)&#xff1f; A. 这指的是衡量一个学习算法在测试集(算法没有被训练过的数据)上表现如何的过程 B. 迅速尝试尽可能多的不同方法来改进算法&#xff0c;从而看看什么方法有效 C. 机器学习在医疗领域的应用…

上手vue2的学习笔记1之了解前端三剑客

纯小白学习前端开发&#xff0c;找学习资料也花费了一些时间&#xff0c;后续配置环境&#xff0c;也走了很多弯路&#xff0c;这里梳理一下这几天的学习资料&#xff0c;做一个简单的总结。 1.初步了解vue Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&a…

CountDownLatch基本使用及原理

CountDownLatch基本使用及原理 一、CountDownLatch简介二、CountDownLatch类的继承关系1. AbstractQueuedSynchronizer: 用于构建锁和同步器的框架。2. Sync: CountDownLatch的内部类&#xff0c;提供了锁的具体实现。 三、Semaphore的基本使用1. 使用场景&#xff1a;2. 代码实…