레이어별 구성
레이어별 구성 (Per-layer configurations)
일부 체크포인트는 이질적(heterogeneous, 레이어가 균일하지 않음)이에요. 한 레이어에는 더 작은 MLP가, 다른 레이어에는 더 적은 key-value 헤드가, 선택된 레이어에는 다른 배치가 있는 경우, 단일 전역 구성으로는 스택을 정확히 설명할 수 없어요.
config를 작성하거나 검사할 때, 그리고 모델 코드가 이를 소비할 때 PreTrainedConfig의 per_layer_config를 사용해서 그 차이를 기록해요.각 항목은 전역 구성과 다른 부분만 저장하며, 나머지는 상속돼요.
[!NOTE] 이질적 구성은 고급(파워) 기능입니다. 이질적 레이아웃이 일반적이거나 두드러진 아키텍처가 되면,
per_layer_config에 의존하기보다 아키텍처 구현에서 이를 명시적으로 모델링하도록 노력할 것입니다. 존재한다면 명시적 아키텍처를 선호하세요.
출처: 문서
본문
아래 모델들은 이질적 체크포인트예요. 레이어가 스택 전체에서 균일하지 않아요. 표준 아키텍처의 per_layer_config 대신 block_configs와 자체 model_type을 가진 전용 아키텍처를 사용해요.
레이어별 오버라이드 정의
레이어 인덱스에서 속성 오버라이드로의 매핑으로 per_layer_config를 LlamaConfig에 전달해요. 레이어 인덱스는 0부터 시작해요. 전역 구성과 다른 속성만 지정하면 돼요.
per_layer_config는 구성 값을 기록하고 해석(resolve)해요. 그 자체로 모델이 만드는 모듈이나 모듈이 실행되는 방식을 바꾸지는 않아요. 크기 오버라이드 또는 skip을 적용하려면 각 레이어를 구성하거나 실행할 때 해석된 레이어별 구성을 읽는 모델 코드가 필요해요. 현재 LlamaModel은 모든 LlamaDecoderLayer를 전역 구성으로 구성하므로, 아래 skip 항목들은 from_pretrained 후에 모듈을 제거하는 게 아니라 구성 값으로 남아요.
다음 예시는 네 개 레이어에 대한 오버라이드를 기록해요: 레이어 5는 더 작은 MLP를, 레이어 11은 더 적은 key-value 헤드를, 레이어 23과 27은 이를 지원하는 아키텍처를 위한 skip 값을 기록해요.
from transformers import LlamaConfig
config = LlamaConfig(
hidden_size=4096,
intermediate_size=14336,
num_hidden_layers=32,
num_attention_heads=32,
num_key_value_heads=8,
per_layer_config={
# Use a smaller MLP in one layer.
5: {"intermediate_size": 8192},
# Use fewer key-value heads in another layer.
11: {"num_key_value_heads": 4},
# Record a request to skip the MLP in architectures that support it.
23: {"skip": ["mlp"]},
# Record a request to skip attention in architectures that support it.
27: {"skip": ["attention"]},
},
)
아키텍처가 생략할 수 있는 서브모듈(예: "mlp", "attention")은 아키텍처별로 정의돼요.skip은 리스트를 받으므로, 한 레이어에 서브모듈 오버라이드 여러 개를 기록할 수 있어요.
config.per_layer_config[layer_idx]에 접근하면 해석된 레이어 구성을 반환해요. 해석된 구성은 전역 구성에 해당 레이어의 오버라이드를 합친 것이에요.
# Layer 0 does not define overrides, so it inherits the global values.
config.per_layer_config[0].intermediate_size
# 14336
config.per_layer_config[0].num_key_value_heads
# 8
# Layer 5 overrides the MLP intermediate size.
config.per_layer_config[5].intermediate_size
# 8192
# Layer 11 overrides the number of key-value heads.
config.per_layer_config[11].num_key_value_heads
# 4
# Layer 23 records an MLP skip.
config.per_layer_config[23].skip
# ["mlp"]
# Layer 27 records an attention skip.
config.per_layer_config[27].skip
# ["attention"]
per_layer_config를 사용하는 구성은 다른 구성과 동일한 save_pretrained() 및 from_pretrained() 라운드 트립을 지원해요.
각 아키텍처는 코드에서 레이어 수준에 어떤 속성을 소비할지 정의해요. per_layer_config는 그러한 레이어 수준 차이를 기록하고 전역 config에 대해 해석하는 메커니즘을 제공해요.
전역 속성 접근
레이어별 오버라이드가 있는 속성은 모델 전역 단일 값을 갖지 않아요. num_key_value_heads가 대부분의 레이어에서는 8이고 선택된 레이어에서는 4일 수 있어요. 레이어 컨텍스트 밖에서 config.num_key_value_heads를 읽는 것은 모호해요.
기본적으로 그 접근은 AmbiguousGlobalPerLayerAttributeError를 발생시키고 config.per_layer_config[layer_idx]를 가리켜요. 속성은 전역 config에 여전히 존재하므로 이는 AttributeError가 아니에요. 레이어 인덱스 없이 전역 값을 읽는 것은 여전히 잘못됐어요. 전역 num_key_value_heads로 key-value 캐시를 만드는 코드는 잘못된 레이어를 잘못된 크기로 만들게 돼요.
전역 폴백이 의도적으로 필요하고 이질적 구성을 처리할 수 있을 때만 allow_global_per_layer_attribute_access=True를 설정해요. 그러면 전역 접근이 허용돼요. 경고가 한 번 방출돼요.
config = LlamaConfig(
hidden_size=4096,
intermediate_size=14336,
num_hidden_layers=32,
num_attention_heads=32,
num_key_value_heads=8,
allow_global_per_layer_attribute_access=True,
per_layer_config={
11: {"num_key_value_heads": 4},
},
)
config.num_key_value_heads
# 8
# Emits a one-time warning because num_key_value_heads has a per-layer override.
직렬화
per_layer_config는 기본적으로 희소(sparse)하게 직렬화되며, 오버라이드가 없는 레이어는 생략돼요. 전역 값과 일치하는 오버라이드된 속성도 생략돼요.
from transformers import LlamaConfig
config = LlamaConfig(
hidden_size=4096,
intermediate_size=14336,
num_hidden_layers=4,
num_attention_heads=32,
num_key_value_heads=8,
per_layer_config={
0: {"num_key_value_heads": 8},
2: {"num_key_value_heads": 4},
},
)
config.to_dict()["per_layer_config"]
# {"2": {"num_key_value_heads": 4}}
serialize_explicit_per_layer_config=True를 설정하면 per_layer_config에 표현된 속성에 대해 모든 레이어를 포함해요. 일부 값이 여전히 전역 구성과 일치할 때 레이어 레이아웃을 더 쉽게 검사할 수 있게 해줘요.
explicit_config = LlamaConfig(
hidden_size=4096,
intermediate_size=14336,
num_hidden_layers=4,
num_attention_heads=32,
num_key_value_heads=8,
serialize_explicit_per_layer_config=True,
per_layer_config={
0: {"num_key_value_heads": 8},
2: {"num_key_value_heads": 4},
},
)
serialized_per_layer_config = explicit_config.to_dict()["per_layer_config"]
serialized_per_layer_config
# {
# "0": {"num_key_value_heads": 8},
# "1": {"num_key_value_heads": 8},
# "2": {"num_key_value_heads": 4},
# "3": {"num_key_value_heads": 8},
# }
간결한 구성을 위해서는 희소 직렬화를, 읽기 쉽거나 툴링을 위해 전체 레이어별 레이아웃이 필요하면 명시적 직렬화를 사용해요.
더 알아보기 (Learn more)
- PreTrainedConfig 문서를 참고해 주세요.