熬了一晚上,我从零实现了 Transformer 模型,把代码讲给你听

news2024/11/15 21:57:24

自从彻底搞懂Self_Attention机制之后,笔者对Transformer模型的理解直接从地下一层上升到大气层,瞬间打通任督二脉。夜夜入睡之前,那句柔情百转的"Attention is all you need"时常在耳畔环绕,情到深处不禁拍床叫好。于是在肾上腺素的驱使下,笔者熬了一个晚上,终于实现了Transformer模型。

对于 Self_Attention 机制一知半解的读者,强烈推荐我的上一篇文章,没有繁复的公式,将 Self_Attention 的本质思想讲给你听。

关于 Transformer的理论讲解,请参考这篇文章。

1. 模型总览

代码讲解之前,首先放出这张经典的模型架构图。下面的内容中,我会将每个模块的实现思路以及笔者在Coding过程中的感悟知无不答。没有代码基础的读者不要慌张,笔者也是最近才入门的,所写Pytorch代码没有花里胡哨,所用变量名词尽量保持与论文一致,对新手十分友好。

图片

我们观察模型的结构图,Transformer模型包含哪些模块?笔者将其分为以下几个部分:

图片

接下来我们首先逐个讲解,最后将其拼接完成模型的复现。

2. config

下面是这个Demo所用的库文件以及一些超参的信息。单独实现一个Config类保存的原因是,方便日后复用。直接将模型部分复制,所用超参保存在新项目的Config类中即可。这里不过多赘述。

在这里插入图片描述

3. Embedding

Embedding部分接受原始的文本输入(batch_size*seq_len,例:[[1,3,10,5],[3,4,5],[5,3,1,1]]),叠加一个普通的Embedding层以及一个Positional Embedding层,输出最后结果。

图片

在这一层中,输入的是一个list: [batch_size * seq_len],输出的是一个tensor:[batch_size * seq_len * d_model]

普通的 Embedding 层想说两点:

  • 采用torch.nn.Embedding实现embedding操作。需要关注的一点是论文中提到的Mask机制,包括padding_mask以及sequence_mask(具体请见文章开头给出的理论讲解那篇文章)。在文本输入之前,我们需要进行padding统一长度,padding_mask的实现可以借助torch.nn.Embedding中的padding_idx参数。
  • 在padding过程中,短补长截
class Embedding(nn.Module):
    def __init__(self,vocab_size):    
        super(Embedding, self).__init__()        
        # 一个普通的 embedding层,我们可以通过设置padding_idx=config.PAD 来实现论文中的 padding_mask        
        self.embedding = nn.Embedding(vocab_size,config.d_model,padding_idx=config.PAD)    
        
    def forward(self,x):     
        # 根据每个句子的长度,进行padding,短补长截        
        for i in range(len(x)):         
            if len(x[i]) < config.padding_size:             
               x[i].extend([config.UNK] * (config.padding_size - len(x[i]))) # 注意 UNK是你词表中用来表示oov的token索引,这里进行了简化,直接假设为6            
            else:            
               x[i] = x[i][:config.padding_size]        
        x = self.embedding(torch.tensor(x)) # batch_size * seq_len * d_model        
        return x

关于Positional Embedding,我们需要参考论文给出的公式。说一句题外话,在作者的实验中对比了Positional Embedding与单独采用一个Embedding训练模型对位置的感知两种方式,模型效果相差无几。

图片

图片

class Positional_Encoding(nn.Module):

    def __init__(self,d_model):     
        super(Positional_Encoding,self).__init__()        
        self.d_model = d_model    
        
    def forward(self,seq_len,embedding_dim):      
        positional_encoding = np.zeros((seq_len,embedding_dim))        
        for pos in range(positional_encoding.shape[0]):        
            for i in range(positional_encoding.shape[1]):           
                positional_encoding[pos][i] = math.sin(pos/(10000**(2*i/self.d_model))) if i % 2 == 0 else math.cos(pos/(10000**(2*i/self.d_model)))        
        return torch.from_numpy(positional_encoding)

4. Encoder

图片

Muti_head_Attention

这一部分是模型的核心内容,理论部分就不过多讲解了,读者可以参考文章开头的第一个传送门,文中有基础的代码实现。

Encoder 中的 Muti_head_Attention 不需要Mask,因此与我们上一篇文章中的实现方式相同。

为了避免模型信息泄露的问题,Decoder 中的 Muti_head_Attention 需要Mask。这一节中我们重点讲解Muti_head_Attention中Mask机制的实现。

如果读者阅读了我们的上一篇文章,可以发现下面的代码有一点小小的不同,主要体现在 forward 函数的参数。

  • forward 函数的参数从 x 变为 x,y:请读者观察模型架构,Decoder需要接受Encoder的输入作为公式中的V,即我们参数中的y。在普通的自注意力机制中,我们在调用中设置y=x即可。
  • requires_mask:是否采用Mask机制,在Decoder中设置为True
class Mutihead_Attention(nn.Module):
    def __init__(self,d_model,dim_k,dim_v,n_heads):        
        super(Mutihead_Attention, self).__init__()        
        self.dim_v = dim_v        
        self.dim_k = dim_k        
        self.n_heads = n_heads     
       
        self.q = nn.Linear(d_model,dim_k)        
        self.k = nn.Linear(d_model,dim_k)        
        self.v = nn.Linear(d_model,dim_v)    
        
        self.o = nn.Linear(dim_v,d_model)        
        self.norm_fact = 1 / math.sqrt(d_model)   
     
    def generate_mask(self,dim):    
        # 此处是 sequence mask ,防止 decoder窥视后面时间步的信息。        
        # padding mask 在数据输入模型之前完成。        
        matirx = np.ones((dim,dim))        
        mask = torch.Tensor(np.tril(matirx))        
        
        return mask==1    
    def forward(self,x,y,requires_mask=False):     
        assert self.dim_k % self.n_heads == 0 and self.dim_v % self.n_heads == 0        
        # size of x : [batch_size * seq_len * batch_size]        
        # 对 x 进行自注意力        
        Q = self.q(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k        
        K = self.k(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k       
        V = self.v(y).reshape(-1,y.shape[0],y.shape[1],self.dim_v // self.n_heads) # n_heads * batch_size * seq_len * dim_v        
        # print("Attention V shape : {}".format(V.shape))        
        attention_score = torch.matmul(Q,K.permute(0,1,3,2)) * self.norm_fact        
        if requires_mask:          
           mask = self.generate_mask(x.shape[1])            
           attention_score.masked_fill(mask,value=float("-inf")) # 注意这里的小Trick,不需要将Q,K,V 分别MASK,只MASKSoftmax之前的结果就好了        
        output = torch.matmul(attention_score,V).reshape(y.shape[0],y.shape[1],-1)        
        # print("Attention output shape : {}".format(output.shape))    
            
        output = self.o(output)        
        return output

Feed Forward

图片

这一部分实现很简单,两个Linear中连接Relu即可,目的是为模型增添非线性信息,提高模型的拟合能力。

在这里插入图片描述

Add & LayerNorm

这一节我们实现论文中提出的残差连接以及LayerNorm。

论文中关于这部分给出公式:

图片

代码中的dropout,在论文中也有所解释,对输入layer_norm的tensor进行dropout,对模型的性能影响还是蛮大的。

代码中的参数sub_layer ,可以是Feed Forward,也可以是Muti_head_Attention。

在这里插入图片描述

OK,Encoder中所有模块我们已经讲解完毕,接下来我们将其拼接作为Encoder

class Encoder(nn.Module):
    def __init__(self):    
        super(Encoder, self).__init__()        
        self.positional_encoding = Positional_Encoding(config.d_model)        
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)        
        self.feed_forward = Feed_Forward(config.d_model)    
            
        self.add_norm = Add_Norm()    

    def forward(self,x): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list 
    
        x += self.positional_encoding(x.shape[1],config.d_model)        
        # print("After positional_encoding: {}".format(x.size()))        
        output = self.add_norm(x,self.muti_atten,y=x)        
        output = self.add_norm(output,self.feed_forward)        
        
        return output

5.Decoder

在 Encoder 部分的讲解中,我们已经实现了大部分Decoder的模块。Decoder的Muti_head_Attention引入了Mask机制,Decoder与Encoder 中模块的拼接方式不同。以上两点读者在Coding的时候需要注意。

class Decoder(nn.Module):
    def __init__(self):     
        super(Decoder, self).__init__()        
        self.positional_encoding = Positional_Encoding(config.d_model)        
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)        self.feed_forward = Feed_Forward(config.d_model)        
        self.add_norm = Add_Norm()    
        
    def forward(self,x,encoder_output): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list        
        # print(x.size())        
        x += self.positional_encoding(x.shape[1],config.d_model)        
        # print(x.size())        
        # 第一个 sub_layer        
        output = self.add_norm(x,self.muti_atten,y=x,requires_mask=True)        
        # 第二个 sub_layer        
        output = self.add_norm(output,self.muti_atten,y=encoder_output,requires_mask=True)        
        # 第三个 sub_layer        
        output = self.add_norm(output,self.feed_forward)        
        
        return output

6.Transformer

至此,所有内容已经铺垫完毕,我们开始组装Transformer模型。论文中提到,Transformer中堆叠了6个我们上文中实现的Encoder 和 Decoder。这里笔者采用nn.Sequential实现了堆叠操作。

Output模块的 Linear 和 Softmax 的实现也包含在下面的代码中

在这里插入图片描述

完整代码

# @Author:Yifx
# @Contact: Xxuyifan1999@163.com
# @Time:2021/9/16 20:02
# @Software: PyCharm

"""
文件说明:
"""

import torch
import torch.nn as nn
import numpy as np
import math

class Config(object):
    def __init__(self):     
        self.vocab_size = 6        
        
        self.d_model = 20        
        self.n_heads = 2        
        
        assert self.d_model % self.n_heads == 0        
        dim_k  = self.d_model // self.n_heads        
        dim_v = self.d_model // self.n_heads        
        
        self.padding_size = 30        
        self.UNK = 5        
        self.PAD = 4      
          
        self.N = 6        
        self.p = 0.1
config = Config()


class Embedding(nn.Module):
    def __init__(self,vocab_size):    
        super(Embedding, self).__init__()        
        # 一个普通的 embedding层,我们可以通过设置padding_idx=config.PAD 来实现论文中的 padding_mask        
        self.embedding = nn.Embedding(vocab_size,config.d_model,padding_idx=config.PAD)    
        
    def forward(self,x):
        # 根据每个句子的长度,进行padding,短补长截       
        for i in range(len(x)):       
            if len(x[i]) < config.padding_size:             
                x[i].extend([config.UNK] * (config.padding_size - len(x[i]))) # 注意 UNK是你词表中用来表示oov的token索引,这里进行了简化,直接假设为6            
            else:             
                x[i] = x[i][:config.padding_size]        
        x = self.embedding(torch.tensor(x)) # batch_size * seq_len * d_model        
        return x


class Positional_Encoding(nn.Module):

    def __init__(self,d_model):   
        super(Positional_Encoding,self).__init__()        
        self.d_model = d_model    
        
    def forward(self,seq_len,embedding_dim):        
    positional_encoding = np.zeros((seq_len,embedding_dim))        
    for pos in range(positional_encoding.shape[0]):       
        for i in range(positional_encoding.shape[1]):            
            positional_encoding[pos][i] = math.sin(pos/(10000**(2*i/self.d_model))) if i % 2 == 0 else math.cos(pos/(10000**(2*i/self.d_model)))        
        return torch.from_numpy(positional_encoding)

class Mutihead_Attention(nn.Module):
    def __init__(self,d_model,dim_k,dim_v,n_heads):    
        super(Mutihead_Attention, self).__init__()        
        self.dim_v = dim_v        
        self.dim_k = dim_k        
        self.n_heads = n_heads    
            
        self.q = nn.Linear(d_model,dim_k)        
        self.k = nn.Linear(d_model,dim_k)        
        self.v = nn.Linear(d_model,dim_v)     
           
        self.o = nn.Linear(dim_v,d_model)        
        self.norm_fact = 1 / math.sqrt(d_model)    
        
    def generate_mask(self,dim):   
        # 此处是 sequence mask ,防止 decoder窥视后面时间步的信息。        
        # padding mask 在数据输入模型之前完成。        
        matirx = np.ones((dim,dim))        
        mask = torch.Tensor(np.tril(matirx))        
        
        return mask==1    
        
    def forward(self,x,y,requires_mask=False):    
        assert self.dim_k % self.n_heads == 0 and self.dim_v % self.n_heads == 0        
        # size of x : [batch_size * seq_len * batch_size]        
        # 对 x 进行自注意力        
        Q = self.q(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k        
        K = self.k(x).reshape(-1,x.shape[0],x.shape[1],self.dim_k // self.n_heads) # n_heads * batch_size * seq_len * dim_k        
        V = self.v(y).reshape(-1,y.shape[0],y.shape[1],self.dim_v // self.n_heads) # n_heads * batch_size * seq_len * dim_v        
        # print("Attention V shape : {}".format(V.shape))        
        attention_score = torch.matmul(Q,K.permute(0,1,3,2)) * self.norm_fact        
        if requires_mask:         
           mask = self.generate_mask(x.shape[1])            
           # masked_fill 函数中,对Mask位置为True的部分进行Mask            
           attention_score.masked_fill(mask,value=float("-inf")) # 注意这里的小Trick,不需要将Q,K,V 分别MASK,只MASKSoftmax之前的结果就好了        
        output = torch.matmul(attention_score,V).reshape(y.shape[0],y.shape[1],-1)        
        # print("Attention output shape : {}".format(output.shape))        
        
        output = self.o(output)        
        return output


class Feed_Forward(nn.Module):
    def __init__(self,input_dim,hidden_dim=2048):     
        super(Feed_Forward, self).__init__()        
        self.L1 = nn.Linear(input_dim,hidden_dim)        
        self.L2 = nn.Linear(hidden_dim,input_dim)    
    def forward(self,x):     
       output = nn.ReLU()(self.L1(x))        
       output = self.L2(output)       
       return output

class Add_Norm(nn.Module):
    def __init__(self):    
        self.dropout = nn.Dropout(config.p)        
        super(Add_Norm, self).__init__()    

    def forward(self,x,sub_layer,**kwargs):    
        sub_output = sub_layer(x,**kwargs)        
        # print("{} output : {}".format(sub_layer,sub_output.size()))        
        x = self.dropout(x + sub_output)        
        
        layer_norm = nn.LayerNorm(x.size()[1:])        
        out = layer_norm(x)        
        return out

class Encoder(nn.Module):
    def __init__(self):    
        super(Encoder, self).__init__()        
        self.positional_encoding = Positional_Encoding(config.d_model)        
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)        
        self.feed_forward = Feed_Forward(config.d_model)     
           
        self.add_norm = Add_Norm()    
        
    def forward(self,x): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list    
        x += self.positional_encoding(x.shape[1],config.d_model)        
        # print("After positional_encoding: {}".format(x.size()))        
        output = self.add_norm(x,self.muti_atten,y=x)        
        output = self.add_norm(output,self.feed_forward)   
             
        return output

# 在 Decoder 中,Encoder的输出作为Query和KEy输出的那个东西。即 Decoder的Input作为V。此时是可行的
# 因为在输入过程中,我们有一个padding操作,将Inputs和Outputs的seq_len这个维度都拉成一样的了
# 我们知道,QK那个过程得到的结果是 batch_size * seq_len * seq_len .既然 seq_len 一样,那么我们可以这样操作
# 这样操作的意义是,Outputs 中的 token 分别对于 Inputs 中的每个token作注意力

class Decoder(nn.Module):
    def __init__(self):    
        super(Decoder, self).__init__()        
        self.positional_encoding = Positional_Encoding(config.d_model)        
        self.muti_atten = Mutihead_Attention(config.d_model,config.dim_k,config.dim_v,config.n_heads)        
        self.feed_forward = Feed_Forward(config.d_model)        
        self.add_norm = Add_Norm()   
        
    def forward(self,x,encoder_output): # batch_size * seq_len 并且 x 的类型不是tensor,是普通list        
        # print(x.size())        
        x += self.positional_encoding(x.shape[1],config.d_model)        
        # print(x.size())        
        # 第一个 sub_layer        
        output = self.add_norm(x,self.muti_atten,y=x,requires_mask=True)        
        # 第二个 sub_layer        
        output = self.add_norm(x,self.muti_atten,y=encoder_output,requires_mask=True)        
        # 第三个 sub_layer        
        output = self.add_norm(output,self.feed_forward)        
        return output

class Transformer_layer(nn.Module):
    def __init__(self):   
        super(Transformer_layer, self).__init__()        
        self.encoder = Encoder()        
        self.decoder = Decoder()    
        
    def forward(self,x):     
        x_input,x_output = x        
        encoder_output = self.encoder(x_input)        
        decoder_output = self.decoder(x_output,encoder_output)        
        return (encoder_output,decoder_output)

class Transformer(nn.Module):
    def __init__(self,N,vocab_size,output_dim):    
        super(Transformer, self).__init__()        
        self.embedding_input = Embedding(vocab_size=vocab_size)       
        self.embedding_output = Embedding(vocab_size=vocab_size)      
          
        self.output_dim = output_dim        
        self.linear = nn.Linear(config.d_model,output_dim)        
        self.softmax = nn.Softmax(dim=-1)        
        self.model = nn.Sequential(*[Transformer_layer() for _ in range(N)])    
        
    def forward(self,x):   
        x_input , x_output = x        
        x_input = self.embedding_input(x_input)        
        x_output = self.embedding_output(x_output)     
          
        _ , output = self.model((x_input,x_output))     
          
         output = self.linear(output)        
         output = self.softmax(output)        
         
         return output

觉得本文内容不错的读者记得点赞呀,感谢~~

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

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

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

相关文章

无线领夹麦克风怎么挑选,降噪麦克风能消除旁边人说话声吗?

在自媒体行业的蓬勃发展下&#xff0c;音频设备的地位愈发显著&#xff0c;尤其是麦克风这一关键组件。它见证了从传统新闻采访、电视节目制作到现代网络直播、个人视频日志&#xff08;Vlog&#xff09;的演变。随着技术的进步和应用场景的多样化&#xff0c;麦克风的种类也日…

软考分数线有3种,低于45分也能拿证!

软考合格分数标准是45分&#xff0c;这个是广泛为人所知的。然而&#xff0c;有些地区即使没有达到45分也可以获得证书&#xff0c;这一点许多考生并不清楚。总的来说&#xff0c;软考的合格标准有三种&#xff01; ● 全国分数线&#xff1a;通常是各科45分及格&#xff0c;证…

将WordPress的文章重新排序的3个方法

有效的调整文章显示顺序看开可以更好突出内容&#xff0c;还可以保持网站的新鲜感&#xff0c;今天我将带您了解三种方法&#xff0c;通过重新排序文章显著提升网站的吸引力。我们将逐步讲解从调整设置到使用插件以及“置顶”文章的每一种方法&#xff0c;确保WordPress 新手也…

学生护眼台灯十大排名有哪些?品牌台灯质量前十的排名曝光!

在孩子学习过程中&#xff0c;有一样物品的重要性不容忽视&#xff0c;那就是一盏提供舒适光源的台灯。面对不断增加的学业负担&#xff0c;孩子们经常需要在夜晚借助台灯的光亮进行学习&#xff0c;这已经成为了家庭生活中普遍的情景。然而&#xff0c;我们必须给予足够的关注…

股票分析系统设计方案大纲与细节

股票分析系统设计方案大纲与细节 一、引言 随着互联网和金融行业的迅猛发展,股票市场已成为重要的投资渠道。投资者在追求财富增值的过程中,对股票市场的分析和预测需求日益增加。因此,设计并实现一套高效、精准的股票分析系统显得尤为重要。本设计方案旨在提出一个基于大…

智启未来,共筑工业软件新梦 ——清华大学博士生天洑软件实习启航

2024年6月30日&#xff0c;清华大学工程物理系、深圳国际研究生院、航天航空学院、机械工程系、能源与动力工程系的10名博士研究生抵达南京天洑软件有限公司&#xff0c;正式开启为期6周的博士生必修环节社会实践。 “天洑软件清华基地”成立于2021年&#xff0c;旨在为清华理工…

智能锁赛博化,凯迪仕携全球顶尖科技亮相建博会!

7月8日&#xff0c;作为大家居建材行业全球规模第一大展&#xff0c;2024中国建博会&#xff08;广州&#xff09;在广交会展馆正式拉开序幕。据官方数据显示&#xff0c;本届展会展出规模展览总规模近40万平方米&#xff0c;建筑装饰领域各细分题材的一线品牌几乎全部参展。 其…

[图解]企业应用架构模式2024新译本讲解24-标识映射3

1 00:00:00,460 --> 00:00:02,580 超类定义了一个抽象方法 2 00:00:03,170 --> 00:00:03,450 3 00:00:06,410 --> 00:00:09,690 把reader内容 4 00:00:10,870 --> 00:00:12,350 把它变成一个领域对象 5 00:00:13,690 --> 00:00:15,800 但这里只是把它变成一个…

SpringBoot + MyBatisPlus 实现多租户分库

一、引言 在如今的软件开发中&#xff0c;多租户(Multi-Tenancy)应用已经变得越来越常见。多租户是一种软件架构技术&#xff0c;它允许一个应用程序实例为多个租户提供服务。每个租户都有自己的数据和配置&#xff0c;但应用程序实例是共享的。而在我们的Spring Boot MyBati…

【吊打面试官系列-MyBatis面试题】什么是 MyBatis 的接口绑定?有哪些实现方式?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是 MyBatis 的接口绑定&#xff1f;有哪些实现方式&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是 MyBatis 的接口绑定&#xff1f;有哪些实现方式&#xff1f; 接口绑定&#xff0c;就是在 MyBatis 中…

【windows OBS开启直播】Windows搭建RTMP视频流服务(Nginx服务器版)

如果您想在windows 电脑上设置RTMP服务器&#xff0c;并使用VLC播放器播放OBS的直播流&#xff0c;您可以使用一个本地的RTMP服务器软件&#xff0c;如nginx配合nginx-rtmp-module来搭建。下面 详细介绍下如何搭建此视频流服务。 1、安装和配置本地RTMP服务器 步骤1&#xff…

在Morelogin中使用IPXProxy海外代理IP的设置指南

Morelogin指纹浏览器是市场上较受欢迎的指纹浏览器&#xff0c;允许用户管理多个账号并进行自动化操作。它提供免费环境供用户进行体验&#xff0c;并且操作起来非常简单。大多数人都会将Morelogin指纹浏览器和海外代理IP进行使用&#xff0c;来应用于多种场景&#xff0c;如电…

拨开迷雾,寻找大模型应用落地的支点

自主可控大模型底座个性化刚需场景&#xff0c;这家大模型公司率先趟出一条个性化发展路径。 作者 | 辰纹 来源 | 洞见新研社 上海的温度很高&#xff0c;接近40度&#xff0c;比上海温度更高的是AI的热度。 7月4日&#xff0c;2024世界人工智能大会暨人工智能全球治理高…

uniapp内置组件uni.navigateTo跳转后页面空白问题解决

文章目录 导文空白问题 导文 在h5上跳转正常 但是在小程序里面跳转有问题 无任何报错 页面跳转地址显示正确&#xff0c;但页面内容为空 空白问题 控制台&#xff1a; 问题解决&#xff1a; 方法1&#xff1a; 可能是没有注册的问题&#xff0c;把没注册的页面 注册一下。 方…

如何理解数据模型?颗粒度、维度及指标?

问题1 什么是数据模型&#xff1f; 数据模型反映在数据库中就是一张表&#xff0c;该表把他分开来看有以下关注的点。 &#xff08;1&#xff09;主键&#xff1a;表明该表主要的分析对象&#xff0c;比如我们的分析对象是订单、是商品、是门店&#xff0c;那么主键就是订单id,…

烧烤炉发霉怎么处理 烧烤炉发霉的原因分析

仓库储存的烧烤炉表面布满了霉菌是什么原因&#xff1f;烧烤炉发霉不仅影响外观和卖点&#xff0c;若是出口给到客户手上还会导致面临客户的索赔的问题 &#xff0c;经ihaoer防霉人士介绍烧烤炉发霉处理方法如下&#xff1a; 烧烤炉发霉的原因分析 一、储存的环境潮湿&#xff…

动手学深度学习(Pytorch版)代码实践 -循环神经网络-55循环神经网络的从零开始实现和简洁实现

55循环神经网络的实现 1.从零开始实现 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l import matplotlib.pyplot as plt import liliPytorch as lp# 读取H.G.Wells的时光机器数据集 batch_size, num_ste…

若依 ruoyi-vue SpringBoot highlight-textarea 输入框敏感词关键词高亮标红(二)

参考文章&#xff0c;非常感谢大佬的分享 实现可高亮的输入框 — HighlightTextarea GitHub:highlight-textarea 可看作者上一篇文章 若依 ruoyi-vue SpringBoot聊天敏感词过滤sensitive-word&#xff08;一&#xff09; 效果图 审核时&#xff0c;输入框高亮敏感词&#xff…

由于找不到emp.dll无法运行游戏的多个有效解决方法分享

在玩游戏时候是否遇到过找不到emp.dll,无法继续执行代码问题无法打开游戏&#xff1f;那么这个emp.dll是什么呢&#xff1f;为什么会丢失&#xff0c;emp.dll丢失要怎么办&#xff1f;今天就给大家详细介绍一下emp.dll文件与emp.dll丢失的多个解决方法&#xff01; 一、emp.dll…

详解[USACO07OPEN] Cheapest Palindrome G(洛谷PP2890)(区间DP经典题)

题目 思路 考虑区间DP。 设dp[i][j]为从i到j这段区间被修正为回文串的最小花费 c[cc][1]为添加字符cc的花费 c[cc][2]为删去字符cc的花费 s为题目给出的字符串。 用[i 1,j]区间转移&#xff1a;这种转移相当于在[i1,j]区间的左边加入一个字符&#xff0c;让[i,j]变为回文的方…