여기, 나는 최소 가치를 계산에 통합하는 데 기여한 Mariusz Kurman에게 감사해야합니다. 이 방법은 그들 없이는 잘 작동하지만 결과를 향상시킬 수 있습니다. 각 레이어의 중요성은 별도로 계산되지만 함수는 결합 된 값을 반환합니다.
이 기능은 가장 중요한 뉴런을 유지하면서 새롭고 작은 층을 생성합니다. 이 과정은 다음과 같습니다
<code>LlamaForCausalLM(
(model): LlamaModel(
(embed_tokens): Embedding(128256, 2048)
(layers): ModuleList(
(0-15): 16 x LlamaDecoderLayer(
(self_attn): LlamaSdpaAttention(
(q_proj): Linear(in_features=2048, out_features=2048, bias=False)
(k_proj): Linear(in_features=2048, out_features=512, bias=False)
(v_proj): Linear(in_features=2048, out_features=512, bias=False)
(o_proj): Linear(in_features=2048, out_features=2048, bias=False)
(rotary_emb): LlamaRotaryEmbedding()
)
(mlp): LlamaMLP(
(gate_proj): Linear(in_features=2048, out_features=8192, bias=False)
(up_proj): Linear(in_features=2048, out_features=8192, bias=False)
(down_proj): Linear(in_features=8192, out_features=2048, bias=False)
(act_fn): SiLU()
)
(input_layernorm): LlamaRMSNorm((2048,), eps=1e-05)
(post_attention_layernorm): LlamaRMSNorm((2048,), eps=1e-05)
)
)
(norm): LlamaRMSNorm((2048,), eps=1e-05)
(rotary_emb): LlamaRotaryEmbedding()
)
(lm_head): Linear(in_features=2048, out_features=128256, bias=False)
)</code>
현재 무게를 추출하십시오 :
뉴런 쌍의 중요 점수를 계산합니다 :
-
각 뉴런에 대해 계산 된 중요 점수를 포함하는 텐서를 얻습니다. 이 점수는 최종 출력에 대한 각 뉴런의 기여를 반영하여 어떤 뉴런을 유지 해야하는지를 나타냅니다.
<code>def compute_neuron_pair_importance(gate_weight, up_weight):
"""
计算神经元对重要性分数(最大绝对权重)
参数:
- gate_weight:来自 gate_proj 层的权重矩阵。
- up_weight:来自 up_weight 层的权重矩阵。
返回:
- importance_scores:每个神经元对的重要性分数。
"""
gate_max_abs = torch.max(gate_weight, dim=1).values + torch.abs(torch.min(gate_weight, dim=1).values)
up_max_abs = torch.max(up_weight, dim=1).values + torch.abs(torch.min(up_weight, dim=1).values)
importance_scores = gate_max_abs + up_max_abs
return importance_scores</code>
보유 할 뉴런의 수를 결정하십시오
-
보존 될 총 뉴런 수를 계산하기 위해 보존 할 층의 원래 크기로 제공된 가지 치기 백분율을 사용하십시오.
가장 중요한 뉴런을 선택하십시오 :
<code>def prune_neuron_pairs(mlp, prune_percent):
"""
减少**gate_proj**、**up_proj**、**down_proj**层的维度,移除不太重要的神经元。
参数:
- mlp:要剪枝的层。
- prune_percent:要剪枝的神经元的百分比。
返回:
- new_gate_proj, new_up_proj, new_down_proj:新的剪枝层。
- k:新的中间大小。
"""
# 从 MLP 层提取权重
gate_weight = mlp.gate_proj.weight.data.float()
up_weight = mlp.up_proj.weight.data.float()
# 计算重要性分数
importance_scores = compute_neuron_pair_importance(gate_weight, up_weight)
original_intermediate_size = gate_weight.size(0)
# 计算要保留的神经元
num_neuron_pairs_to_prune = min(int(prune_percent * original_intermediate_size),
original_intermediate_size - 1)
k = original_intermediate_size - num_neuron_pairs_to_prune
# 验证检查
if k < 1:
raise ValueError("k must be greater than 0")
# 选择要保留的神经元
_, indices_to_keep = torch.topk(importance_scores, k, largest=True, sorted=True)
indices_to_keep = indices_to_keep.sort().values
# 创建并填充新层
new_gate_proj = nn.Linear(mlp.gate_proj.in_features, k, bias=False).to(device)
new_up_proj = nn.Linear(mlp.up_proj.in_features, k, bias=False).to(device)
new_down_proj = nn.Linear(k, mlp.down_proj.out_features, bias=False).to(device)
# 将选定的权重复制到新层。
new_gate_proj.weight.data = mlp.gate_proj.weight.data[indices_to_keep, :]
new_up_proj.weight.data = mlp.up_proj.weight.data[indices_to_keep, :]
new_down_proj.weight.data = mlp.down_proj.weight.data[:, indices_to_keep]
return new_gate_proj, new_up_proj, new_down_proj, k</code>
토치는 가장 중요한 점수를 가진 뉴런을 검색하는 데 사용되며 가장 중요한 순서에서 가장 중요한 순서까지 뉴런을 검색하는 데 사용됩니다. Torch는 내림차순으로 데이터를 반환하기 때문에 정렬 방법을 사용하여 오름차순으로 재 배열됩니다.
새롭고 작은 레이어를 만듭니다 :
-
선택한 인덱스에 따라 치수가 조정되는 3 개의 새로운 레이어를 만듭니다. _new_gate
proj <code># 从 MLP 层提取权重
gate_weight = mlp.gate_proj.weight.data.float()
up_weight = mlp.up_proj.weight.data.float()</code>
및 _new_up proj
에서 입력 차원이 보존되고 출력 차원이 줄어 듭니다. 대신, _new_down proj - 에서 입력 차원이 조정되고 출력 차원은 동일하게 유지됩니다.
선택한 가중치를 새 레이어로 복사하십시오 :
<code># 计算重要性分数
importance_scores = compute_neuron_pair_importance(gate_weight, up_weight)
original_intermediate_size = gate_weight.size(0)</code>
관련 가중치는 원래 층에서 새 층으로 전송되어 선택된 뉴런에 해당하는 가중치 만 유지되도록합니다.
이제, 모든 레이어를 반복하고 수정 된 모델을 구축하는 기능을 살펴 보겠습니다.
-
이 기능은 모델의 각 계층을 반복하고 가지 치기 프로세스를 적용하고 새로운 아키텍처를 반영하도록 모델의 구성을 업데이트합니다.
구성 파일이 업데이트되지 않으면 포옹 또는 로컬에 관계없이 저장 후 모델을 사용할 수 없습니다. 많은 라이브러리 (예 : Hugging Face의 Transformers)는 모델의 아키텍처를 설명하기 위해 model.config
에 의존합니다. 구성이 실제 구조와 일치하지 않으면 이러한 라이브러리를 통해 수행되는 미세 조정 또는 추론 작업이 실패 할 수 있습니다. <code># 计算要保留的神经元
num_neuron_pairs_to_prune = min(int(prune_percent * original_intermediate_size),
original_intermediate_size - 1)
k = original_intermediate_size - num_neuron_pairs_to_prune</code>
결과 분석
이 코드를 사용하여 Hugging Face Hub에서 사용할 수있는 여러 모델을 만들었습니다.
여기에는 다음이 포함됩니다
Gemma-2–2B를 기반으로 한 모델, 40%가지.
당신은이 모델을 다운로드 할 수 있으며,이 모델을 사용하는 것 외에도 아키텍처와 원래 모델에 비해 어떤 변화가 발생했는지 연구 할 수도 있습니다.
LLAMA3.2–1B 모델을 20% 가지 치기에 적용한 후 아키텍처 변경을 분석하겠습니다.
MLP 블록에서 중간 층의 크기를 제외하고 모델의 구조는 변경되지 않은 상태로 유지됩니다. 보시다시피, _gate - proj
및 _up
proj <code># 选择要保留的神经元
_, indices_to_keep = torch.topk(importance_scores, k, largest=True, sorted=True)
indices_to_keep = indices_to_keep.sort().values</code>
proj> proj 레이어는 8192 기능에서 6554로 감소되었으며 _down proj 레이어는 다양하지만 입력 기능에 다양합니다.
이 변경은 코드의 기능과 정확히 동일합니다. 모델 성능에 중요한 뉴런을 유지하면서 이러한 층을 수정하십시오. 우리가 8192의 20%를 제거하면 6553.6을 얻게되며, 이는 뉴런의 올바른 비율이 정리되었음을 확인합니다.
경험 팁 테스트
이제 테스트 프롬프트에서 가지 치기 모델이 어떻게 수행되는지 보자.
파리는 프랑스의 수도입니다. 또한 세계에서 가장 아름다운 도시 중 하나입니다. 파리에서보고 경험할 가치가있는 것들이 너무 많아서 하루 만에 그들 모두를 덮는 것이 불가능합니다. 그러나 몇 가지가 있습니다 ...
응답은 원래 모델의 응답과 정확히 동일하지 않지만 일관성을 유지합니다. 이는 모델이 대부분의 기능을 유지하며, 더 중요한 것은
지식 증류 또는 미세 조정을 통해 손실을 복구 할 수 있음을 시사한다.
eleutherai / lm-evaluation
이 경험적 점검 외에도 가장 일반적인 벤치 마크를 사용하여 모델을 평가했습니다. 다른 가지 치기 정도가 모델의 성능에 어떤 영향을 미치는지 분석 해 봅시다.
우리가 본 바와 같이, 가지 치기의 효과는 다소 비대칭입니다. Boolq 테스트는이 과제를 평가하지 않았으며 MLP 층에서 뉴런의 40%를 잃은 모델의 경우 약 2% 만 감소했습니다.
대조적으로, Lambada 테스트에 대한 영향은 매우 중요했으며 정확도는 50%이상입니다.
이것은 모델이 대부분의 이해력을 유지하지만 더 개방적인 생성이 필요한 테스트에서는 다루기가 어렵다는 것을 시사합니다.
boolq는 모델에 예/아니오로 답변 해야하는 텍스트와 질문만을 제시합니다. 이것은 입력 텍스트에서 관계를 이해하는 모델의 능력을 측정하는 데 중점을 둔 테스트입니다.
반면에 람다는 모델에 마지막 단어가 복잡한 언어 모델링에서 모델의 능력을 테스트하는 복잡한 작업 인 단락의 마지막 단어를 추측하도록 모델에 요청합니다.
포그 페이스 페이스 오픈 LLM 순위
Hugging Face Open LLM 순위에서 모델의 20%에 대한 가지 치기 결과는 기본 모델과 널리 사용되는 Tinyllama-1.1B-V1.1보다 성능이 우수하기 때문에 훨씬 더 놀라운 일입니다.
이 차트에서는 두 모델의 결과를 볼 수 있습니다.
이 차트를 연구함으로써 다음과 같은 결론을 도출 할 수 있습니다. 가지 치기 후 모델의 평균 성능은 기본 모델 (4.86 vs. 4.03)보다 낫습니다. 이는 가지 치기 프로세스가 주요 영역의 성능을 효과적으로 유지하거나 향상시키는 동시에 중복성을 줄입니다.
연구 결과를 통해 가지 치기 모델의 장점과 단점을 식별 할 수 있습니다.
장점 :
ifeval - : 상당한 개선 (19.94 vs. 14.78)은 가지 치기가 과적색을 줄이거 나 모델의 정보 추출 능력을 효율적으로 추출 할 수 있음을 나타냅니다.
musr : 더 나은 성능 (4.39 vs. 2.56)은 가지 치기 모델이 긴 맥락이나 이야기 이해에 대한 추론이 필요한 작업을 더 잘 처리한다는 것을 보여줍니다.
-
단점 :
BBH : 불확실성의 추론 능력 감소 (3.19 vs. 4.37)는 가지 치기가 모호하거나 다중 인터뷰 시나리오를 처리하는 모델의 능력을 감소시킬 수 있습니다.
mmlu-pro : 특수 지역의 감소 작업 (1.36 vs. 2.26)은 특정 영역에서 세부 사항을 유지하는 주요 중량의 제거로 인한 것일 수 있습니다.
에너지 효율 : 가지 치기 모델은 경쟁력있는 성능을 유지하면서 계산 간접비를 줄이기위한 목표와 일치하는 에너지 효율 (0.4kg vs. 0.42kg Co₂)을 가지고 있습니다.
다른 순위에서 모델의 성능에 대한보다 포괄적 인 연구가 필요하지만, 이러한 결과는 적절한 지식 증류 또는 미세 조정으로 크게 개선 될 수있는 유망한 모델을 가지고 있음을 시사합니다. 가장 중요한 것은 이러한 결과는 MLP 층에서 수행 된 가지 치기 프로세스와 일치한다.
결론
모델의 가지 치기 과정이 성공적이었습니다. GLU 계층을 처리하는이 접근법을 통해 모델의 기능의 대부분을 유지하면서 크기와 자원 소비를 크게 줄이는 동안 가지 치기를 수행 할 수 있습니다. -
테스트 결과는 가지 치기 모델 전에 기능 복구 프로세스 (예 : 지식 증류 또는 미세 조정 )를 수행함으로써 얻어진다는 점에 유의해야합니다. .
미래의 작업
탐색 할 가치가있는 많은 가지 치기 기술이 있습니다. 아마도 가장 직접적인 것은 깊이 가지 치기 일 것입니다. 여기에는 모델 성능에 가장 적게 기여하는 레이어를 제거하는 것이 포함됩니다.
연구의 또 다른 중요한 영역은 이러한 가지 치기 모델의 지식 증류 과정이며 새로운 작업을 배울 수있는 능력을 유지하는지 평가하는 것입니다. 이로 인해 성능이 기본 모델에 더 가깝게 만들 수 있습니다. 특히 치열한 모델이 최대 손실을 나타내는 벤치 마크에서.
더 가볍고 효율적인 모델을 개발하는 것은 특히 광범위한 인프라 요구 사항없이 LLM 기능을 배포하려는 회사의 매력적인 영역으로 남아 있습니다. 이 작업은 이러한 강력한 모델에 쉽게 액세스하고 배포 할 수 있도록하는 방법에 대한 추가 연구를위한 토대를 마련합니다.
이 기사는 대형 언어 모델에 대한 완전한 과정의 일부이며 Github에서 제공됩니다. 새 기사 업데이트에 대해 알아 보려면 코드 기반 또는 주연을 고려하십시오. 이 방법으로 새 컨텐츠를 추가 할 때 알림을받습니다.
저는 Apress Publishing House에서 출판 한 "Grand Lang 나는 생성 AI, 딥 러닝 및 텐서 플로에 대해 정기적으로 씁니다. 새 기사에 대한 업데이트를 위해 내 계정을 매체에 팔로우하십시오. 물론, 당신은 LinkedIn에서 저에게 연락 할 수 있습니다.