首页 > 其他分享 >LLM大模型: 生成式模型的数学原理和prompt融入image

LLM大模型: 生成式模型的数学原理和prompt融入image

时间:2024-09-25 17:00:55浏览次数:10  
标签:prompt nn 模型 生成式 batch lora self channel size

   1、(1)上文介绍了DDPM生成图片的原理和代码测试结果,训练时给样本图片加上gaussian noise,预测时也是预测gaussian noise;

  • 这里为啥要用gaussian distribution?为啥不用其他的分布?
    • 高斯分布相对比较简单,只有两个参数:均值和方差,容易控制
  • 为啥一张随机生成的gaussion noise经过很多次裁剪后能得到想要的图片?数学上的依据是什么?
    • 理论上讲: 任意K个高斯分布按照特定的权重组合,能得到任意曲线,也就是拟合任意的分布

      

    • 假设有K个高斯分布,这K个高斯分布称作混合模型的隐变量则复杂分布的概率分布是:

      

   通过这种Gaussian分布拟合任意分布,这下知道为啥diffusion模型会使用上千个Gaussian noise来生成 image了吧?本质就是利用Gaussian分布的组合生成所需image!两个乘数一个是DDPM中的alpha,另一个是epsilon(预测noise的网络).一张样本图片,比如是3 channel * 28 width * 28 height = 2352个像素点。理论上讲,只要有一个像素点不同,图片就不同。再说直白一点:每个像素点的值刚开始都是随机生成的,生成的值符合Gaussian~(0,1)分布;后续迭代很多次(因为是Gaussian~(0,1)分布,每次生成的数值都较小,99.7%的数值会在(-3,3)之间。为了满足channel的数值范围(一般是0~255),需要多次迭代。比如channel的数值是240,随机生成的noise值是3,那么至少迭代80次才能满足要求),每次迭代都会用新生成的值(也符合Gaussian分布)加减初始值,直到迭代结束(这个思路和"小步快跑"的梯度下降没任何区别)。2352个像素点拼接起来就生成了预测的noise图片!

  (2)怎么求seta?以VAE为例,整个网络结构如下:

  

   Z是隐变量,经过seta网络生成X;如果这个生成的X和原来数据集一样,说明seta是正确的。那么既然x已经发生了,最合理的思路就是让X的概率最大化了,也就是max likelyhood!也就是seta网络要让X生成的概率最大,以此来得到最合适的seta网络参数!

  

  2、实战时,肯定是要加入用户输入的prompt的!怎么严格按照用户的prompt生成image了?transformer架构最初是用来做翻译的,encoder把一种语言的输入转成embedding后,通过cross attention的机制把输入信息转移到decoder用于生成输出的token,这里也需要把输入的prompt信息传递到图片的decoder部分,是不是也能借鉴一下这个思路了?

  用户输入的prompt经过矩阵的线性转换后生成了embedding,在每个resnetblock后都加上一个transformer block(down 和 up 都要加),通过这种方式把用户的prompt信息融入整个unet网络!

        0

   具体代码实现,参考如下:像素点是query,prompt是key和value,做cross attention!注意:为啥要用像素点做attention?生成最终的image,需要每个像素点都参与,只有每个像素点的值对了,最终的image才能正确!为了确保每个像素点都正确,需要把prompt的值融入!

import torch 
from torch import nn 
from config import * 
import math 

class CrossAttention(nn.Module):
    def __init__(self,channel,qsize,vsize,fsize,cls_emb_size):
        super().__init__()
        self.w_q=nn.Linear(channel,qsize)
        self.w_k=nn.Linear(cls_emb_size,qsize)
        self.w_v=nn.Linear(cls_emb_size,vsize)
        self.softmax=nn.Softmax(dim=-1)
        self.z_linear=nn.Linear(vsize,channel)
        self.norm1=nn.LayerNorm(channel)
        # feed-forward结构
        self.feedforward=nn.Sequential(
            nn.Linear(channel,fsize),
            nn.ReLU(),
            nn.Linear(fsize,channel)
        )
        self.norm2=nn.LayerNorm(channel)
    
    def forward(self,x,cls_emb): # x:(batch_size,channel,width,height), cls_emb:(batch_size,cls_emb_size)
        x=x.permute(0,2,3,1) # x:(batch_size,width,height,channel)
        
        # 像素是Query
        Q=self.w_q(x)   # Q: (batch_size,width,height,qsize)
        Q=Q.view(Q.size(0),Q.size(1)*Q.size(2),Q.size(3))   # Q: (batch_size,width*height,qsize) 每个像素点都要参与attention的计算

        # prompt是Key和Value
        K=self.w_k(cls_emb) # K: (batch_size,qsize)
        K=K.view(K.size(0),K.size(1),1) # K: (batch_size,qsize,1)
        V=self.w_v(cls_emb) # V: (batch_size,vsize)
        V=V.view(V.size(0),1,V.size(1))  # v: (batch_size,1,vsize)

        # attention打分矩阵Q*K
        attn=torch.matmul(Q,K)/math.sqrt(Q.size(2)) # attn: (batch_size,width*height,1)
        attn=self.softmax(attn) # attn: (batch_size,width*height,1)
        # print(attn) # 就一个Key&value,所以Query对其注意力打分总是1分满分

        # attention输出
        Z=torch.matmul(attn,V)    # Z: (batch_size,width*height,vsize)
        Z=self.z_linear(Z)  # Z: (batch_size,width*height,channel)
        Z=Z.view(x.size(0),x.size(1),x.size(2),x.size(3))   # Z: (batch_size,width,height,channel)

        # 残差&layerNorm
        Z=self.norm1(Z+x)# Z: (batch_size,width,height,channel)

        # FeedForward
        out=self.feedforward(Z)# Z: (batch_size,width,height,channel)
        # 残差&layerNorm
        out=self.norm2(out+Z)
        return out.permute(0,3,1,2)

if __name__=='__main__':
    batch_size=2
    channel=1
    qsize=256
    cls_emb_size=32
    
    cross_atn=CrossAttention(channel=1,qsize=256,vsize=128,fsize=512,cls_emb_size=32)
    
    x=torch.randn((batch_size,channel,IMG_SIZE,IMG_SIZE))
    cls_emb=torch.randn((batch_size,cls_emb_size)) # cls_emb_size=32

    Z=cross_atn(x,cls_emb)
    print(Z.size())     # Z: (2,1,48,48)

  把attention机制打包到convblock中:

from torch import nn 
from cross_attn import CrossAttention

class ConvBlock(nn.Module):
    def __init__(self,in_channel,out_channel,time_emb_size,qsize,vsize,fsize,cls_emb_size):
        super().__init__()

        self.seq1 = nn.Sequential(
            nn.Conv2d(in_channel,out_channel,kernel_size=3,stride=1,padding=1), # 改通道数,不改大小
            nn.BatchNorm2d(out_channel),
            nn.ReLU(),
        )

        self.time_emb_linear=nn.Linear(time_emb_size,out_channel)    # Time时刻emb转成channel宽,加到每个像素点上
        self.relu=nn.ReLU()

        self.seq2=nn.Sequential(
            nn.Conv2d(out_channel,out_channel,kernel_size=3,stride=1,padding=1), # 不改通道数,不改大小
            nn.BatchNorm2d(out_channel),
            nn.ReLU(),
        )

        # 像素做Query,计算对token的attention,实现分类信息融入图像,不改变图像形状和通道数
        self.crossattn=CrossAttention(channel=out_channel,qsize=qsize,vsize=vsize,fsize=fsize,cls_emb_size=cls_emb_size)

    def forward(self,x,t_emb,cls_emb): # t_emb: (batch_size,time_emb_size)
        x=self.seq1(x)  # 改通道数,不改大小
        t_emb=self.relu(self.time_emb_linear(t_emb)).view(x.size(0),x.size(1),1,1)   # t_emb: (batch_size,out_channel,1,1) 
        output=self.seq2(x+t_emb)        # 不改通道数,不改大小
        return self.crossattn(output,cls_emb)   # 图像和prompt embedding做attention

  3、模型微调:市面上可能有已经训练好的模型,但模型的训练数据大概率是通用的数据,并不是某些垂直细分领域的数据,怎么才能加上自己所需垂直领域的数据了?最合适的当然是微调了!微调的方式也有很多:全量参数微调、冻结部分参数微调、lora微调。如果训练数据有限、算力也有限,那么最合适的就是lora微调了!理论上讲,任何线性变换(直白一点就是矩阵乘法啦)都可以旁挂两个m*r和r*n的小矩阵来完成lora微调!但是:这种任务的核心是根据prompt生成image(所以微调的样本肯定也有配对的prompt和image),重点就是融合prompt和image的cross attention了,所以这里直接在cross attention的矩阵乘法旁边外挂新矩阵来达到融合新样本信息的目的

  先找到需要旁挂小矩阵的层:

from unet import UNet
from dataset import train_dataset
from diffusion import forward_diffusion
from config import * 
import torch 
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
import os 
from lora import inject_lora

EPOCH=200
BATCH_SIZE=400

if __name__=='__main__':
    # 加载模型
    model=torch.load('model.pt')

    # 向nn.Linear层注入Lora
    for name,layer in model.named_modules():
        name_cols=name.split('.')
        # 找到cross attention中的线性变换,也就是矩阵乘法
        filter_names=['w_q','w_k','w_v']
        if any(n in name_cols for n in filter_names) and isinstance(layer,nn.Linear):
            inject_lora(model,name,layer)
    
    # lora权重的加载
    try:
        restore_lora_state=torch.load('lora.pt')
        model.load_state_dict(restore_lora_state,strict=False)
    except:
        pass 

    model=model.to(DEVICE)

    # 冻结非Lora参数
    for name,param in model.named_parameters():
        if name.split('.')[-1] not in ['lora_a','lora_b']:  # 非lora部分不计算梯度
            param.requires_grad=False
        else:
            param.requires_grad=True

    dataloader=DataLoader(train_dataset,batch_size=BATCH_SIZE,num_workers=4,persistent_workers=True,shuffle=True)   # 数据加载器

    optimizer=torch.optim.Adam(filter(lambda x: x.requires_grad==True,model.parameters()),lr=0.001) # 优化器只更新Lorac参数
    loss_fn=nn.L1Loss() # 损失函数(绝对值误差均值)

    print(model)

    writer = SummaryWriter()
    model.train()
    n_iter=0
    for epoch in range(EPOCH):
        last_loss=0
        for batch_x,batch_cls in dataloader:
            # 图像的像素范围转换到[-1,1],和高斯分布对应
            batch_x=batch_x.to(DEVICE)*2-1
            # 引导分类ID
            batch_cls=batch_cls.to(DEVICE)
            # 为每张图片生成随机t时刻
            batch_t=torch.randint(0,T,(batch_x.size(0),)).to(DEVICE)
            # 生成t时刻的加噪图片和对应噪音
            batch_x_t,batch_noise_t=forward_diffusion(batch_x,batch_t)
            # 模型预测t时刻的噪音
            batch_predict_t=model(batch_x_t,batch_t,batch_cls)
            # 求损失
            loss=loss_fn(batch_predict_t,batch_noise_t)
            # 优化参数
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            last_loss=loss.item()
            writer.add_scalar('Loss/train', last_loss, n_iter)
            n_iter+=1
        print('epoch:{} loss={}'.format(epoch,last_loss))

        # 保存训练好的Lora权重
        lora_state={}
        for name,param in model.named_parameters():
            name_cols=name.split('.')
            filter_names=['lora_a','lora_b']
            if any(n==name_cols[-1] for n in filter_names):
                lora_state[name]=param
        torch.save(lora_state,'lora.pt.tmp')
        os.replace('lora.pt.tmp','lora.pt')

   旁挂两个小矩阵的实现:

from config import * 
import torch 
from torch import nn
import math 

# Lora实现,封装linear,替换到父module里
class LoraLayer(nn.Module):
    def __init__(self,raw_linear,in_features,out_features,r,alpha):
        super().__init__()
        self.r=r 
        self.alpha=alpha
        self.lora_a=nn.Parameter(torch.empty((in_features,r)))
        self.lora_b=nn.Parameter(torch.zeros((r,out_features)))
    
        nn.init.kaiming_uniform_(self.lora_a,a=math.sqrt(5))

        self.raw_linear=raw_linear
    
    def forward(self,x):    # x:(batch_size,in_features)
        raw_output=self.raw_linear(x)   
        lora_output=x@(([email protected]_b)*self.alpha/self.r)    # matmul(x,matmul(lora_a,lora_b)*alpha/r)
        return raw_output+lora_output

def inject_lora(model,name,layer):
    name_cols=name.split('.')

    # 逐层下探到linear归属的module
    children=name_cols[:-1]
    cur_layer=model 
    for child in children:
        cur_layer=getattr(cur_layer,child)
    
    #print(layer==getattr(cur_layer,name_cols[-1]))
    lora_layer=LoraLayer(layer,layer.in_features,layer.out_features,LORA_R,LORA_ALPHA)
    setattr(cur_layer,name_cols[-1],lora_layer)

 

 

总结:

1、 机器学习核心是根据输入数据得到所需的输出数据,肯定要对输入数据做各种转换,常见的做法就是matrix multi、active、attention等:

  • matrix multi:旧的向量转移到新的空间
    • 通过更改matrix的参数让新向量的数值适配下游任务
    • 向量长度做调整适配
  • active:特征多维组合后生成新特征,用于下游任务
  • attention:相似度的计算,用于不同网络之间的信息传递与融合

 

参考:

1、https://www.bilibili.com/video/BV19H4y1G73r/?spm_id_from=333.337.search-card.all.click&vd_source=241a5bcb1c13e6828e519dd1f78f35b2

2、https://nn.labml.ai/diffusion/stable_diffusion/model/unet.html  

3、https://deepsense.ai/diffusion-models-in-practice-part-1-the-tools-of-the-trade/  

4、https://aitechtogether.com/python/77485.html

标签:prompt,nn,模型,生成式,batch,lora,self,channel,size
From: https://www.cnblogs.com/theseventhson/p/18427192

相关文章

  • 【线程】POSIX信号量---基于环形队列的生产消费者模型
    信号量概念这篇文章是以前写的,里面讲了 SystemV的信号量的概念,POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步。信号量的概念POSIX信号量的接口初始化信号量参数:pshared:0表示线程间共享,非0表示进程......
  • 大模型项目部署时Gradio Web页面打不开或者打开用不了及pydantic.errors.PydanticSche
    问题描述 在复现大模型demo时连接器和模型加载都没问题,但是gradio界面打不开或者打开后用不了原因分析:感觉应该是gradio的版本问题导致该文件缺少相关文件解决方案:可以首先按照上面要求下载文件https://cdn-media.huggingface.co/frpc-gradio-0.2/frpc_linux_a......
  • 【基础岛·第2关】8G 显存玩转书生大模型 Demo
    目录创建开发机环境配置CliDemo部署InternLM2-Chat-1.8B模型创建开发机我们选择10%的开发机,镜像选择为Cuda-12.2。在输入开发机名称后,点击创建开发机环境配置在/root/share/pre_envs中配置好了预置环境icamp3_demo可以通过如下指令进行激活:condaactivate/root......
  • AI美女占领小红书,卷翻真人女网红,最新Flux模型火爆全网!
    文章开始之前,先考考各位小伙伴的眼力:下面的图是真人还是AI,你能分辨出来吗?答案是:AI,AI,全是AI!这些图片都源于科小编在某AI群中发现的一份群聊记录。据群聊记录里的一位网友所说,他用文生图模型生成AI美女图片,然后“用账号矩阵管理软件,养了1327个小红书账号,直接把小......
  • 【基础岛·第1关】书生大模型全链路开源体系
    目录过往一年历程1.核心技术2.大海捞针实验3、人机解决方式的差别全链路开源生态1数据2开源数据处理工具箱3预训练4微调xtuner5评测6部署7智能体8项目过往一年历程7b可以应对中小企业业务,但20b才有涌现现象1.核心技术2.大海捞针实验超长文本内容中的问答正确度3......
  • 大模型训练:K8s 环境中数千节点存储最佳实践
    今天这篇博客来自全栈工程师朱唯唯,她在前不久举办的KubeCon中国大会上进行了该主题分享。Kubernetes已经成为事实的应用编排标准,越来越多的应用在不断的向云原生靠拢。与此同时,人工智能技术的迅速发展,尤其是大型语言模型(LLM)的推进,导致企业需要处理的数据量急剧增加,例如,Llama......
  • YOLOv8实战和matlab建模:检测监控站视频的车流量、速度、车辆时间占用率以及预估拥堵模
            我们得到了某监控点的检测视频数据,需要从数据当中得到车流量、速度、车辆时间占用率等基本数据然后用于车道推测拥堵。以某一路段内检测点为例利用YOLOv8实战检验。假设:车辆只有轿车与卡车两种类型并分别设置车长;某路段只检测双车道并且应急车道不开放;YOLOv8......
  • 基于 o1 草莓模型开发的交易策略,轻松超越 GPT-4o
    作者:老余捞鱼原创不易,转载请标明出处及原作者。写在前面的话:    本文记录了我用OpenAI的o1模型(草莓模型)开发算法交易策略的过程。我虽然在首次尝试时遇到了模型响应慢的问题,但最终还是创建了出色的交易策略。本文展示了如何结合大型语言模型与算法交易,包括......
  • 前端如何接收SSE流式数据传输(大模型网站常用)
    使用fetchEventSource参考:https://blog.csdn.net/qq_43750656/article/details/131591198https://zhuanlan.zhihu.com/p/686618062首先安装:npminstall--save@microsoft/fetch-event-source我参考各个资料写的函数://流式传输处理函数exportfunctionsseRequest(url:s......
  • 阿里云主力模型直降97%,AI行业起飞
    阿里云主力模型直降97%,AI行业起飞大模型降价潮对AI应用的爆发有哪些意义对AI行业的影响大模型降价潮关于大模型降价潮,阿里云对外宣布通义千问GPT-4级主力模型Qwen-Long,API输入价格从0.02元/千tokens降至0.0005元/千tokens,直降97%,那么为了证实这一点,特意查询了通义千问的......