大きなモデルのサイズはパフォーマンスを向上させ続けていますが、より効率的でコンパクトなモデルの需要も高まっています。ただし、コア関数を失うことなくモデルのサイズを縮小することは複雑なタスクです。
量子化や剪定などの手法は、モデルサイズを縮小するためによく使用されますが、知識の蒸留や転送学習などの方法は、削減プロセス中に失われた機能を保持または復元します。その中で、
剪定は、モデルのサイズを削減するための最も効果的な戦略の1つです。単純化された数値表現の量子化とは異なり、剪定には、ニューロンや層全体など、モデルの特定の部分を除去することが含まれます。しかし、この有効性にはコストがかかります。剪定を正しく適用することは困難です。剪定するモデルの部分を決定する必要があるだけでなく、モデルの機能への影響を最小限に抑えるために、削除する要素を慎重に選択する必要もあります。
この記事では、構造化された幅剪定(選択されたニューロンの除去)に焦点を当て、ゲート線形ユニット(GLU)構造を使用してMLP層に効果的に適用する方法を示します。概説された手順に従うことにより、剪定がモデルサイズを大幅に削減しながら、コヒーレント出力を生成し、重要なベンチマークでうまく機能する能力を保持できる方法を理解できます。
剪定とは何ですか?モデルにどのように影響しますか?
前述のように、プルーニングでは、モデルの最終出力に最小の寄与に寄与すると考えられる部分を削除することが含まれます。これらの重要性の低いコンポーネントを慎重に選択することにより、Pruningは、コア機能を犠牲にすることなく、より少ないパラメーターとより低い計算要件を備えたより効率的なモデルを作成することを目指しています。これを説明するために、この記事で使用されているモデルの構造を調べてみましょう:Llama 3.2–1b。
構造を調べると、剪定ターゲットとして使用できる3つの主要なモジュールを識別できます:埋め込み、自己関節メカニズム、およびMLP層。どの部分が剪定プロセスの焦点であるべきかを決定するために、潜在的な利点と可能な影響を理解する必要があります。
最初のステップは、潜在的な減少を理解するために、これらの部品がモデルで占有する空間のサイズを評価することです。
<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>
パラメーター分布分析
埋め込みおよび出力レイヤー(embed_tokens、lm_head):128256×2048≈262mパラメーター/レイヤー
2つのレイヤーには、合計524mパラメーター16レイヤー、各レイヤーには4つの投影サブレイヤーが含まれています レイヤーあたり
2048×(2048 512 512 2048)≈10.5mパラメーターインパクト分析
埋め込み層は、モデルが効果的に処理できる密なベクトル表現に入力を変換する責任があります。編成層を剪定すると、モデルが特定の単語を理解する能力を失うか、少なくとも入力セマンティクスの意味を正しくキャプチャするベクトルを作成する能力を低下させる可能性があります。たとえば、入力語彙の非常に特定の部分のみを使用する非常に特定のモデル(たとえば、財務または医療分析のモデル)を作成する場合、この層を剪定することがオプションになる場合があります。
注意メカニズムにより、各マーカーを処理するときに、モデルが入力シーケンスの最も関連する部分に焦点を合わせることができます。入力シーケンス内の各ペアのマーカー間の加重重要性スコアを計算し、モデルがコンテキストをキャプチャし、関連情報に集中できるようにします。このセクションを剪定すると、テキストの概要や翻訳など、入力コンテキストを幅広く理解する必要があるタスクを実行するモデルの能力が低下します。また、生成されたテキストの一貫性にも影響します。
MLPレイヤー注意メカニズムとともに、一連のデータ拡張と収縮を通じて複雑なパターンを理解するモデルの能力を高めます。このセクションを剪定すると、トレーニング中に見られない、またはカバーされていないタスクに対するモデルの応答が制限されます。言い換えれば、モデルの一般化能力と、なじみのない入力にコヒーレントな応答を提供する能力が低下します。
ターゲットをターゲットにするモデルのどの部分を決定したら、次のステップは、幅の剪定(単一のニューロンの除去)または深さ剪定(レイヤー全体の除去)を実行するかどうかを判断することです。ご覧のとおり、剪定モデルは多くの決定を含むかなり複雑なプロセスです。生成されたモデルの機能だけでなく、トレーニング機能も評価する必要があります。これらのモデルは微調整されるように設計されており、特定のタスクに使用されることが多いため、基礎となるモデルよりも作成する特定のタスクに対してより効率的です。
ゲートされた線形ユニットの特性
このメカニズムにより、モデルは効率を維持しながら、より複雑なパターンを処理できます。ただし、これは、GLU構造の層がしっかりと結合されており、これらの層を剪定するには慎重に検討する必要があることも意味します。
レイヤー上の操作(たとえば、ニューロンの除去)は、対応するペア層に反射する必要があります。たとえば、_gateproj からニューロンを除去する場合、up_projから同じニューロンを除去する必要があり、_downprojレイヤーをそれに応じて再変更する必要があります。最も重要なことは、どのニューロンが保持されるかを決定するためにニューロンの重要性を計算する場合、ニューロンペアを一緒に評価する必要があることです。
これらの層のバランスを破壊すると、少数のニューロンのみが除去されたとしても、モデルさえもパフォーマンスの低下につながる可能性があり、モデルは完全に無効です。プルーニングllama 3.2モデル
githubコードベースの完全なコードにアクセスできます。
メモリ内の元のモデルで最初に行ったステップは、小さなプロンプトを実行して結果を保存することでした。これにより、剪定プロセスを通じて生成されたモデルが一貫性があるか、それどころか、理解可能なテキストを生成する能力を失うかどうかを簡単に、直感的かつ迅速に確認できます。Github - Peremartra/Large-Language-Model-Notebooks-Course:大規模な言語に関する実用的なコース…
私の最初の試みで、結果のテキストは、モデルのGlu構造を遵守できないため、剪定プロセスの根本的な欠陥を間違いなく示していることを保証できます。
元のプロンプトは、「パリは...の首都です」です。元のモデルの応答を見て、最初の失敗した剪定の試みによって返された応答と比較しましょう。
基本モデル:
「パリはフランスの首都であり、世界で最も訪問された都市の1つです。芸術、文化、ファッション、食べ物の首都です。都市には豊かな歴史があり、…を含む多くの有名なランドマークがあります。 …」
誤ったモデルの20%のみを剪定します:
「パリはフランスの首都です。これが主な領域です。これは...フランスの都市です...」 明らかに、最初の試みでは何かがうまくいきませんでした。これは些細なように思えるかもしれませんが、このような経験チェックはあなたに多くの時間を節約することができます。
実装の詳細
この関数は、_gate
proj<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>
projレイヤーの重みを受け取り、説明したように、それらはペアで動作します。したがって、ニューロンの重要性は組み合わせて計算する必要があります。 計算は非常に単純です。各ニューロンの重量の絶対値を計算します。理論的には、最も極端な値を持つニューロンは、値を介して値を大幅に変更することにより、モデルの出力に大きな影響を与えるため、正と負の値の両方が考慮されます。
ここでは、マリウス・カーマンが計算に最小値を組み込むための貢献に感謝しなければなりません。この方法はそれらなしではうまく機能しますが、それらを含むことで結果を改善できます。各レイヤーの重要性は個別に計算されますが、関数は複合値を返します。
<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>
<code># 从 MLP 层提取权重 gate_weight = mlp.gate_proj.weight.data.float() up_weight = mlp.up_proj.weight.data.float()</code>
<code># 计算重要性分数 importance_scores = compute_neuron_pair_importance(gate_weight, up_weight) original_intermediate_size = gate_weight.size(0)</code>
<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>
projおよび_new_upproj では、出力寸法が縮小されている間に入力寸法が保存されます。代わりに、_new_downprojでは、出力寸法が同じままである間、入力寸法が調整されます。
<code># 选择要保留的神经元 _, indices_to_keep = torch.topk(importance_scores, k, largest=True, sorted=True) indices_to_keep = indices_to_keep.sort().values</code>
さて、すべてのレイヤーを反復し、修正されたモデルを構築する責任のある関数を見てみましょう。
この関数は、モデルの各レイヤーを反復し、剪定プロセスを適用し、モデルの構成を更新して新しいアーキテクチャを反映します。
<code># 创建并填充新层 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)</code>
構成ファイルが更新されていない場合、フェイスを抱きしめる場合でもローカルでも、保存後にモデルを使用できません。多くのライブラリ(Faceの変圧器を抱き締めるなど)は、
model.configに依存して、モデルのアーキテクチャを説明しています。構成が実際の構造と一致しない場合、これらのライブラリを介して実行される微調整または推論操作が失敗する可能性があります。 結果分析
このコードを使用して、Face Hubの抱きしめで使用できるいくつかのモデルを作成しました。llama-3.2–1bに由来する3つのモデル、MLP層のニューロンは、それぞれ20%、40%、および60%を剪定しました。
Gemma-2–2bに基づくモデル、40%剪定。
MLPブロックの中間層のサイズを除いて、モデルの構造は変化しません。ご覧のとおり、_gate
projおよび_up
proj<code># 将选定的权重复制到新层。 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]</code>
レイヤーは8192の機能から6554に削減されており、_downproj レイヤー。
この変更は、コードの関数とまったく同じです。モデルのパフォーマンスに重要なニューロンを保持しながら、これらのレイヤーを変更します。 8192の20%を除去すると、6553.6が得られます。これにより、ニューロンの正しい割合が剪定されていることが確認されます。
さあ、プルーニックモデルがテストプロンプトでどのように機能するかを見てみましょう:
パリはフランスの首都です。また、世界で最も美しい都市の1つです。パリには見たり経験する価値のあることはたくさんありますので、1日でそれらをすべてカバーすることは不可能です。しかし、いくつかのことがあります...
応答は、元のモデルの応答とまったく同じではありませんが、一貫性を維持します。これは、モデルがその能力のほとんどを保持していることを示唆しており、さらに重要なことに、知識の蒸留または微調整を通じて損失を回復できることを示唆しています。
eleutherai / lm-valuationBoolqテストでは、タスクが有意な減少を経験しなかったと評価し、MLP層でニューロンの40%を失ったモデルでは、わずか2%減少しました。
対照的に、ランバダテストへの影響は非常に重要であり、精度は50%以上であり、
これは、モデルがその理解のほとんどを保持しているが、より自由な世代を必要とするテストで対処することは困難であることを示唆しています。
Boolqは、モデルにYes/Noで回答する必要があるテキストと質問のみを提示します。これは、入力テキストの関係を理解するモデルの能力を測定することに焦点を当てたテストです。 一方、ランバダは、段落の最後の単語を推測するようにモデルに依頼します。これは、最後の単語が複雑な言語モデリングにおけるモデルの能力をテストする複雑なタスクです。
フェイスオープンLLMランキングをハグする
hugging hugging hugging face open LLMランキングのモデルの20%への剪定の結果は、基本モデルと広く使用されているTinyllama-1.1b-v1.1よりも優れているため、さらに驚くべきことです。
このチャートでは、2つのモデルの結果を見ることができます。よりも優れています。これは、剪定プロセスが冗長性を減らしながら、主要領域のパフォーマンスを効果的に保持または強化することを示唆しています。
研究結果を通じて、剪定モデルの利点と短所を特定できます。
利点:
短所:
エネルギー効率:剪定されたモデルは、エネルギー効率がわずかに高い(0.4 kg対0.42 kgのCO₂)、これは競争力のあるパフォーマンスを維持しながら計算オーバーヘッドを削減するという目標と一致しています。
さまざまなランキングでのモデルのパフォーマンスに関するより包括的な研究が必要ですが、これらの結果は、適切な知識の蒸留または微調整で大幅に改善できる有望なモデルがあることを示唆しています。最も重要なことは、これらの結果は、MLP層で実行される剪定プロセスと一致していることです。結論
テスト結果は、剪定モデルの前に任意の能力回復プロセス(例:
知識の蒸留または微調整)を実行することで得られることに注意することが重要です。 。 将来の仕事 探索する価値のある剪定技術はたくさんあります。おそらく最も直接的なことは、モデルのパフォーマンスに最も寄与するレイヤーを削除することを含む深度剪定です。 研究のもう1つの重要な分野は、これらの剪定されたモデルの知識蒸留プロセスであり、新しいタスクを学習する能力を保持するかどうかを評価することです。これにより、特に剪定されたモデルが最大の損失を示すベンチマークでは、パフォーマンスが基本モデルに近づく可能性があります。
特に、広範なインフラストラクチャ要件なしにLLM機能を展開しようとしている企業にとって、より軽量で効率的なモデルの開発は魅力的な領域のままです。この作業は、これらの強力なモデルを簡単にアクセスして展開できるようにする方法に関するさらなる研究のための基盤となります。この記事は、大規模な言語モデルに関する完全なコースの一部であり、GitHubで入手できます。新しい記事の更新について学ぶには、コードベースまたは主演に従うことを検討してください。この方法で、新しいコンテンツを追加するときに通知されます。 私は本の著者です。「グランドランゲージモデルプロジェクト:大規模な言語モデル戦略のアプリケーションと実装」Apress Publishing Houseが発行しています。
生成AI、深い学習、Tensorflowについて定期的に書きます。 新しい記事の最新情報については、Mediumのアカウントをフォローすることを検討してください。もちろん、LinkedInで私に連絡してください。
以上がllama3.2および同様の大手言語モデルを剪定する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。