ChatGLM之modeling_glm.py代码理解

本文是对ChatGLM的model部分进行讲解,主要讲解时modeling_glm的代码,更接近ChaGLM计算的核心去了解,大模型chatGLM的运行逻辑!本文只是对代码内容进行个人理解的解释,如有问题欢迎批评改造。后续会逐步展开对chatGLM项目的讲解。让更多人了解chatGLM,从而更好理解大模型。

"""GPT-2 model."""
#导入了所需的Python库和自定义模块。
import torch
import torch.nn as nn
import torch.nn.functional as F

import mpu
from model.prompt import PromptSpell
from utils import print_rank_0


def init_method_normal(std=0.02):
    """基于正态分布的初始化方法。

    这仅用于嵌入层(embeddings)。Transformer模型有自己的初始化方法。
    """
    

    def init_(tensor):
        return torch.nn.init.normal_(tensor, mean=0.0, std=std) #初始化嵌入层(embeddings)

    return init_


#创建一个GLM(Generalized Language Model)语言模型
class GLMModel(torch.nn.Module):
    """一个继承自torch.nn.Module的类,代表了一个GLM语言模型。
    forward方法的输出是logits(即预测结果),这些logits可能是并行或串行的,
    这取决于parallel_output标志的设置。
    """

    def __init__(self,
                 num_layers,
                 vocab_size,
                 hidden_size,
                 num_attention_heads,
                 embedding_dropout_prob,
                 attention_dropout_prob,
                 output_dropout_prob,
                 max_sequence_length,
                 max_memory_length,
                 checkpoint_activations,
                 checkpoint_num_layers=1,
                 parallel_output=True,
                 relative_encoding=False,
                 block_position_encoding=False,
                 output_predict=True,
                 spell_length=None,
                 spell_func='lstm',
                 attention_scale=1.0,
                 ):

        super(GLMModel, self).__init__()

        self.parallel_output = parallel_output
        self.output_predict = output_predict
        self.hidden_size = hidden_size

        init_method = init_method_normal(std=0.02)
        
        '''
		创建了一个嵌入层(embeddings)对象,用于将输入的单词ID转换为对应的词向量。
	    mpu.VocabParallelEmbedding是一个特殊的并行嵌入层,它可以处理多个GPU并行工作。
	    它使用之前创建的init_method来初始化权重。
		'''
        # Word embeddings (parallel).
        self.word_embeddings = mpu.VocabParallelEmbedding(
            vocab_size, hidden_size, init_method=init_method)
        '''
		创建了一个Transformer模型对象,使用GPT-2的并行实现。
		这个Transformer模型是用来处理文本数据的核心部分。
		'''
        # Transformer
        self.transformer = mpu.GPT2ParallelTransformer(num_layers,
                                                       hidden_size,
                                                       num_attention_heads,
                                                       max_sequence_length,
                                                       max_memory_length,
                                                       embedding_dropout_prob,
                                                       attention_dropout_prob,
                                                       output_dropout_prob,
                                                       checkpoint_activations,
                                                       checkpoint_num_layers,
                                                       attention_scale=attention_scale,
                                                       relative_encoding=relative_encoding,
                                                       block_position_encoding=block_position_encoding)
        
        #                                               
        if spell_length is not None:
            self.prompt_spell = PromptSpell(spell_length, self.hidden_size, spell_func)

    #用于冻结Transformer模型的一部分或全部权重,以便在训练过程中固定它们
    #将词嵌入层(word_embeddings)的梯度计算设置为False,
    #这样在反向传播过程中,词嵌入层的权重不会更新,即冻结了这一层。
    def freeze_transformer(self, tune_prefix_layers=None):
        log_str = "Freeze transformer"
        self.word_embeddings.requires_grad_(False)
        self.transformer.requires_grad_(False)
        #这里检查是否传入了tune_prefix_layers参数。
        #如果tune_prefix_layers不为None,则会对Transformer的部分权重进行微调(解冻)。
        if tune_prefix_layers is not None:
            log_str += f" tune {tune_prefix_layers} prefix layers"
            
            #遍历从0到tune_prefix_layers-1的数字,根据tune_prefix_layers的值决定解冻哪些Transformer层。
            for i in range(tune_prefix_layers):
                #对于第i层Transformer,将其梯度计算设置为True,这样在反向传播时,第i层的权重会参与更新,
                #从而实现对前几层的微调
                self.transformer.layers[i].requires_grad_(True)
                
        #打印记录的日志信息,提示哪些部分的权重被冻结,哪些被解冻(微调)
        print_rank_0(log_str)
        
    #
    def forward(self, input_ids, position_ids, attention_mask, *mems, return_memory=False, detach_memory=True,
        prompt_pos=None):
        
        # Embeddings.
        batch_size = input_ids.size(0)
        #将输入的单词ID转换为对应的词向量(词嵌入),通过调用之前创建的self.word_embeddings对象。
        words_embeddings = self.word_embeddings(input_ids)
        embeddings = words_embeddings
        
		#检查是否传入了prompt_pos参数,用于指定插入Prompt的位置。
        if prompt_pos is not None:
            embeddings = embeddings.clone()
            
            #根据Prompt信息,生成与之相对应的嵌入向量(可能是通过调用self.prompt_spell对象实现的)。
            prompt_embeds = self.prompt_spell()
            #将生成的Prompt嵌入向量插入到对应位置的词嵌入中
            batch_index = torch.arange(batch_size, device=input_ids.device).unsqueeze(1)
            embeddings[batch_index, prompt_pos] = prompt_embeds
        
        #调用Transformer模型,传递嵌入向量和其他参数,得到Transformer的输出。    
        # Transformer.
        transformer_output = self.transformer(embeddings, position_ids, attention_mask, mems,
                                              return_memory=return_memory, detach_memory=detach_memory)
        #从Transformer输出中提取logits(预测结果)和隐藏层输出                                      
        logits, hidden_layers = transformer_output
        
        #将隐藏层输出保存到outputs变量中,以便在返回时使用。
        outputs = hidden_layers

        if self.output_predict:
            # Parallel logits.
            logits_parallel = mpu.copy_to_model_parallel_region(
                logits)
            logits_parallel = F.linear(logits_parallel, self.word_embeddings.weight)
             
            #根据是否采用多个GPU并行输出的结果
            if self.parallel_output:
                return (logits_parallel, *outputs)

            return (mpu.gather_from_model_parallel_region(logits_parallel), *outputs)
        else:
            return (logits, *outputs)

#构建一个Seq2Seq Transformer模型。
#它包含了一个编码器(Encoder)和一个解码器(Decoder)部分,用于将源文本(source)转换为目标文本(target)。
class EncoderDecoder(torch.nn.Module):
    """Seq2Seq Transformer Model
    The output of the forward method are the logits (parallel or serial depending on the `parallel_output` flag).
    """

    def __init__(self,
                 num_layers,
                 vocab_size,
                 hidden_size,
                 num_attention_heads,
                 embedding_dropout_prob,
                 attention_dropout_prob,
                 output_dropout_prob,
                 max_sequence_length,
                 max_memory_length,
                 checkpoint_activations,
                 checkpoint_num_layers=1,
                 parallel_output=True,
                 output_predict=True
                 ):
        super(EncoderDecoder, self).__init__()

        self.parallel_output = parallel_output
        self.output_predict = output_predict

        init_method = init_method_normal(std=0.02)

        # Word embeddings (parallel).词嵌入层(Word embeddings)
        #mpu.VocabParallelEmbedding是一个用于处理词汇表并行嵌入的特殊层。
        #它接受词汇表大小(vocab_size)和隐藏层大小(hidden_size)作为参数,并使用init_method来初始化词嵌入的权重。词嵌入层用于将输入的单词ID转换为对应的词向量,用于后续的模型处理。
        self.word_embeddings = mpu.VocabParallelEmbedding(
            vocab_size, hidden_size, init_method=init_method)

        # Transformer
        '''
        编码器(self.encoder)和解码器(self.decoder)。它们都是使用GPT-2的并行实现的Transformer模型。
        这两个Transformer模型共享相同的参数设置,包括层数(num_layers)、隐藏层大小(hidden_size)、
        注意力头数(num_attention_heads)、最大序列长度(max_sequence_length)、最大记忆长度(max_memory_length)以及各种丢弃率参数等。
        解码器还使用了一个名为use_decoder_layer的参数,用于区分编码器和解码器的处理方式。'''
        self.encoder = mpu.GPT2ParallelTransformer(num_layers,
                                                   hidden_size,
                                                   num_attention_heads,
                                                   max_sequence_length,
                                                   max_memory_length,
                                                   embedding_dropout_prob,
                                                   attention_dropout_prob,
                                                   output_dropout_prob,
                                                   checkpoint_activations,
                                                   checkpoint_num_layers)
        self.decoder = mpu.GPT2ParallelTransformer(num_layers,
                                                   hidden_size,
                                                   num_attention_heads,
                                                   max_sequence_length,
                                                   max_memory_length,
                                                   embedding_dropout_prob,
                                                   attention_dropout_prob,
                                                   output_dropout_prob,
                                                   checkpoint_activations,
                                                   checkpoint_num_layers,
                                                   use_decoder_layer=True)
    '''
    前向传播过程,将输入数据(source和target)通过编码器(encoder)和解码器(decoder)进行处理,并生成模型的输出。
   '''
    def forward(self, source_ids, target_ids, source_position_ids, target_position_ids, source_mask, target_mask):
        # Embeddings. 通过词嵌入层(self.word_embeddings)将输入的源文本(source)和目标文本(target)的单词ID转换为对应的词向量。
        #源文本和目标文本都分别对应着嵌入后的向量source_embeddings和target_embeddings。
        source_embeddings = self.word_embeddings(source_ids)
        target_embeddings = self.word_embeddings(target_ids)

        # Transformer.
        '''
        将源文本的嵌入向量source_embeddings输入到编码器(self.encoder)中进行处理。
        调用self.encoder并传入嵌入向量、位置ID(source_position_ids)和掩码(source_mask),得到编码器的输出encoder_output。
        '''
        encoder_output, _ = self.encoder(source_embeddings, source_position_ids, source_mask)
        #调用self.decoder并传入嵌入向量、位置ID(target_position_ids)和掩码(target_mask),得到解码器的输出decoder_output。
        decoder_output, _ = self.decoder(target_embeddings, target_position_ids, target_mask)
        
        #根据模型的配置,这里判断是否进行输出预测(self.output_predict为True或False)
        if self.output_predict:
            # Parallel logits.
            '''将解码器的输出decoder_output复制到模型的并行区域(mpu.copy_to_model_parallel_region)。
            然后,通过线性变换F.linear将复制的输出与词嵌入的权重(self.word_embeddings.weight)相乘,
            得到并行的logits(预测结果),即logits_parallel。'''
            output_parallel = mpu.copy_to_model_parallel_region(decoder_output)
            logits_parallel = F.linear(output_parallel, self.word_embeddings.weight)
            
            #self.parallel_output为True,表示模型要返回并行的logits,则直接返回(logits_parallel,),其中逗号表示返回一个元组。
            if self.parallel_output:
                return (logits_parallel,)
            '''self.parallel_output为False,表示模型要返回非并行的logits,因此需要将并行的logits从多个GPU上收集和合并成单个输出。
            通过mpu.gather_from_model_parallel_region函数实现,最终返回一个包含非并行logits的元组(logits_parallel,)
            '''
            return (mpu.gather_from_model_parallel_region(logits_parallel),)
        else:
            #self.output_predict为False,表示模型不进行输出预测,而是直接返回解码器的输出(decoder_output),因此返回(decoder_output,)。
            return (decoder_output,)

def glm_get_params_for_weight_decay_optimization(module):
    weight_decay_params = {'params': []}
    no_weight_decay_params = {'params': [], 'weight_decay': 0.0}
    for module_ in module.modules():
        if isinstance(module_, (mpu.LayerNorm, torch.nn.LayerNorm)):
            no_weight_decay_params['params'].extend(
                [p for p in list(module_._parameters.values())
                 if p is not None and p.requires_grad])
        else:
            weight_decay_params['params'].extend(
                [p for n, p in list(module_._parameters.items())
                 if p is not None and p.requires_grad and n != 'bias'])
            no_weight_decay_params['params'].extend(
                [p for n, p in list(module_._parameters.items())
                 if p is not None and p.requires_grad and n == 'bias'])

    return weight_decay_params, no_weight_decay_params

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

果粒橙_LGC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值