공부

chatglm LoRA 튜닝하기 (LoRA 안되는 LLM 모델 학습하는 방법)

4n3mone 2024. 7. 9. 10:38

THUDM/glm-4-9b-chatLogiKor 리더보드에서 공개 모델 중 상당한 순위를 자랑하는 중국산 모델인데요, 

 

문제는 이 모델은  huggingface transformer 라이브러리에 등록되어있지 않아 인터넷에 공개되어있는 여러 예제 코드가 작동하지 않을 때가 많습니다.

 

peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    inference_mode=False,
    r=8,
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, peft_config)

 

위의 코드는 많이들 사용하는 LoRAConfig 세팅인데요, chatglm 모델에 다음과 같은 코드를 적용하면 이런 에러를 볼 수 있습니다.

 

ValueError: Target modules ["q_proj", "v_proj"] not found in the base model. Please check the target modules and try again.

 

 

이는 chatglm의 모델링 코드 구조에서 "q_proj", "v_proj" 가 없어서 존재하는 오류로, 실제 내부 코드 "modeling_glm.py" 를 들여다보면 다음과 같습니다.

 

class SelfAttention(torch.nn.Module):
    """Parallel self-attention layer abstract class.
    Self-attention layer takes input with size [s, b, h]
    and returns output of the same size.
    """

    def __init__(self, config: ChatGLMConfig, layer_number, device=None):
        super(SelfAttention, self).__init__()
        self.layer_number = max(1, layer_number)

        self.projection_size = config.kv_channels * config.num_attention_heads

        # Per attention head and per partition values.
        self.hidden_size_per_attention_head = self.projection_size // config.num_attention_heads
        self.num_attention_heads_per_partition = config.num_attention_heads

        self.multi_query_attention = config.multi_query_attention
        self.qkv_hidden_size = 3 * self.projection_size
        if self.multi_query_attention:
            self.num_multi_query_groups_per_partition = config.multi_query_group_num
            self.qkv_hidden_size = (
                    self.projection_size + 2 * self.hidden_size_per_attention_head * config.multi_query_group_num
            )
        self.query_key_value = nn.Linear(config.hidden_size, self.qkv_hidden_size,
                                         bias=config.add_bias_linear or config.add_qkv_bias,
                                         device=device, **_config_to_kwargs(config)
                                         )

        self.core_attention = CORE_ATTENTION_CLASSES[config._attn_implementation](config, self.layer_number)

        # Output.
        self.dense = nn.Linear(self.projection_size, config.hidden_size, bias=config.add_bias_linear,
                               device=device, **_config_to_kwargs(config)
                               )

 

 

이상한점을 잘 모르시겠다면, transformers 라이브러리의 modeling_llama.py 를 한번 볼까요?

 

class LlamaAttention(nn.Module):
    """Multi-headed attention from 'Attention Is All You Need' paper"""

    def __init__(self, config: LlamaConfig, layer_idx: Optional[int] = None):
        super().__init__()
        self.config = config
        self.layer_idx = layer_idx
        if layer_idx is None:
            logger.warning_once(
                f"Instantiating {self.__class__.__name__} without passing a `layer_idx` is not recommended and will "
                "lead to errors during the forward call if caching is used. Please make sure to provide a `layer_idx` "
                "when creating this class."
            )

        self.attention_dropout = config.attention_dropout
        self.hidden_size = config.hidden_size
        self.num_heads = config.num_attention_heads
        self.head_dim = self.hidden_size // self.num_heads
        self.num_key_value_heads = config.num_key_value_heads
        self.num_key_value_groups = self.num_heads // self.num_key_value_heads
        self.max_position_embeddings = config.max_position_embeddings
        self.rope_theta = config.rope_theta
        self.is_causal = True

        if (self.head_dim * self.num_heads) != self.hidden_size:
            raise ValueError(
                f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
                f" and `num_heads`: {self.num_heads})."
            )

        self.q_proj = nn.Linear(self.hidden_size, self.num_heads * self.head_dim, bias=config.attention_bias)
        self.k_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias)
        self.v_proj = nn.Linear(self.hidden_size, self.num_key_value_heads * self.head_dim, bias=config.attention_bias)
        self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=config.attention_bias)
        self._init_rope()

 

 

맞습니다. transformers 내부 코드의 modeling 코드에는 "q_proj", "v_proj"가 존재하지만, chatglm의 커스텀 코드에는 존재하지 않는 다는 것을 확인할 수 있죠.

 

따라서 LoRA 학습을 하기 위해서는 모델링 코드를 수정하거나(어려운 방법), 모델링 코드에서 지정해놓은 매개변수 이름으로 LoraConfig를 수정하면 됩니다.

 

chatglm의 모델링 코드에서 ["q_proj", "v_proj"] 와 유사한 기능을 하는 매개변수는 "query_key_value" 이므로, 다음과 같이 LoraConfig를 바꿔주면 학습을 할 수 있습니다.

 

peft_config = LoraConfig(
    task_type=TaskType.CAUSAL_LM,
    inference_mode=False,
    r=8,
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=["query_key_value"],
)
model = get_peft_model(model, peft_config)

 

 

이와 유사한 방법으로 사용하던 LoraConfig가 작동하지 않는 커스텀 코드를 사용하는 모델에 대해 Lora 학습을 해볼 수 있겠습니다.

 

혹시 틀린 점이 있다면 댓글로 달아주세요!