Skip to content

Commit 0f1e8a6

Browse files
authored
feat(token_compressor): introduce universal pruning framework and ali… (#238)
1 parent c5c8186 commit 0f1e8a6

87 files changed

Lines changed: 6838 additions & 4 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,18 @@ For more details, please refer to the [Quick Start Documentation](https://angels
290290
```
291291
For more quantization inference methods, please refer to [the Diffusion Model Quantization Documentation](https://angelslim.readthedocs.io/zh-cn/latest/features/diffusion/quantization.html).
292292

293+
#### 2.4 Token Compression (VLM)
294+
295+
AngelSlim provides a universal metadata-driven framework for vision token pruning and merging. You can quickly verify a compression strategy (e.g., **VisionZip**) with a smoke test:
296+
297+
```shell
298+
python tools/test_universal_pruning.py \
299+
--model_path "Qwen/Qwen2.5-VL-3B-Instruct" \
300+
--config "configs/qwen2_5_vl/pruning/visionzip_r0.9.yaml"
301+
```
302+
303+
For more details on implementing new strategies, please refer to the [Token Compressor Documentation](https://angelslim.readthedocs.io/zh-cn/latest/features/token_compressor/index.html).
304+
293305
### 3. Deployment and Testing
294306

295307
#### 3.1 Offline Inference
@@ -1025,6 +1037,84 @@ Other models such as GLM-4.6, Qwen2.5, and Seed-OSS have been evaluated on bench
10251037
10261038
</details>
10271039
1040+
### 3. Token Compression (VLM)
1041+
1042+
We evaluated various vision token compression strategies on the **Qwen2.5-VL-3B-Instruct** model across multiple multimodal benchmarks. You can replicate these results using the following command:
1043+
1044+
```shell
1045+
python tools/run_pruning_eval.py \
1046+
--model_path "Qwen/Qwen2.5-VL-3B-Instruct" \
1047+
--configs "configs/qwen2_5_vl/pruning/visionzip_r0.9.yaml" \
1048+
--tasks "textvqa" \
1049+
--output_dir "./results/visionzip_test"
1050+
```
1051+
1052+
<details>
1053+
<summary><b>Detailed Benchmark Results (Qwen2.5-VL-3B-Instruct)</b></summary>
1054+
1055+
<table style="text-align:center; vertical-align:middle;">
1056+
<thead>
1057+
<tr>
1058+
<th>Method</th>
1059+
<th>AI2D</th>
1060+
<th>ChartQA</th>
1061+
<th>DocVQA</th>
1062+
<th>MMB<sup>CN</sup></th>
1063+
<th>MMB</th>
1064+
<th>MME</th>
1065+
<th>MMStar</th>
1066+
<th>OCRBench</th>
1067+
<th>POPE</th>
1068+
<th>SQA</th>
1069+
<th>VQA<sup>Text</sup></th>
1070+
<th>Avg</th>
1071+
</tr>
1072+
</thead>
1073+
<tbody>
1074+
<tr>
1075+
<td><b>Baseline</b></td>
1076+
<td>79.11</td>
1077+
<td>83.56</td>
1078+
<td>92.48</td>
1079+
<td>73.28</td>
1080+
<td>77.32</td>
1081+
<td>1517</td>
1082+
<td>56.05</td>
1083+
<td>80.10</td>
1084+
<td>87.41</td>
1085+
<td>80.81</td>
1086+
<td>78.79</td>
1087+
<td>100.0%</td>
1088+
</tr>
1089+
<tr style="background-color: #808080;">
1090+
<th colspan="13">Retain 25% Tokens (75% Compression Ratio)</th>
1091+
</tr>
1092+
<tr><td>FastV</td><td>72.70</td><td>70.04</td><td>75.98</td><td>63.40</td><td>66.92</td><td>1437</td><td>47.39</td><td>36.60</td><td>86.42</td><td>79.33</td><td>73.51</td><td>86.02%</td></tr>
1093+
<tr><td>VisionZip</td><td>74.19</td><td>71.32</td><td>70.11</td><td>67.35</td><td>71.22</td><td>1452</td><td>49.37</td><td>42.50</td><td>85.51</td><td><u>81.36</u></td><td>68.12</td><td>87.34%</td></tr>
1094+
<tr><td>HiPrune</td><td>73.83</td><td>72.76</td><td>72.10</td><td>67.27</td><td>72.34</td><td>1449</td><td>48.93</td><td>41.30</td><td>85.86</td><td>80.91</td><td>69.27</td><td>87.67%</td></tr>
1095+
<tr><td>VisionSelector</td><td>75.19</td><td>73.72</td><td><b>90.24</b></td><td><u>68.81</u></td><td>72.59</td><td><b>1521</b></td><td><u>49.97</u></td><td><u>61.80</u></td><td>85.36</td><td>80.37</td><td><u>76.86</u></td><td><u>93.62%</u></td></tr>
1096+
<tr><td>DivPrune</td><td>73.06</td><td>62.96</td><td>78.46</td><td>67.10</td><td>71.82</td><td>1459</td><td>48.38</td><td>51.40</td><td><b>86.81</b></td><td>80.22</td><td>68.91</td><td>88.15%</td></tr>
1097+
<tr><td>DART</td><td>71.08</td><td>65.20</td><td>79.72</td><td>65.38</td><td>71.05</td><td>1428</td><td>48.78</td><td>41.80</td><td>80.97</td><td>80.91</td><td>68.25</td><td>86.17%</td></tr>
1098+
<tr><td>VisPruner</td><td>74.29</td><td>68.20</td><td>72.52</td><td>67.35</td><td>70.88</td><td>1458</td><td>49.74</td><td>44.80</td><td>86.59</td><td><b>81.46</b></td><td>69.62</td><td>87.87%</td></tr>
1099+
<tr><td>SCOPE</td><td><u>75.84</u></td><td><u>74.00</u></td><td>82.40</td><td><u>68.81</u></td><td><u>72.94</u></td><td>1471</td><td><b>50.35</b></td><td>56.00</td><td><u>86.62</u></td><td>80.96</td><td>74.04</td><td>91.98%</td></tr>
1100+
<tr><td><b>IDPruner</b></td><td><b>75.94</b></td><td><b>75.84</b></td><td><u>90.00</u></td><td><b>69.42</b></td><td><b>73.80</b></td><td><u>1505</u></td><td>49.49</td><td><b>64.90</b></td><td>86.26</td><td>80.42</td><td><b>76.90</b></td><td><b>94.42%</b></td></tr>
1101+
<tr style="background-color: #808080;">
1102+
<th colspan="13">Retain 10% Tokens (90% Compression Ratio)</th>
1103+
</tr>
1104+
<tr><td>FastV</td><td>65.87</td><td>29.72</td><td>36.89</td><td>48.37</td><td>51.98</td><td>1257</td><td>37.28</td><td>13.90</td><td>79.50</td><td>77.05</td><td>57.75</td><td>65.30%</td></tr>
1105+
<tr><td>VisionZip</td><td>67.65</td><td>51.60</td><td>37.88</td><td>59.62</td><td>63.06</td><td>1338</td><td>42.82</td><td>21.40</td><td>81.14</td><td>80.47</td><td>51.56</td><td>72.75%</td></tr>
1106+
<tr><td>HiPrune</td><td>67.75</td><td>53.20</td><td>41.15</td><td>59.45</td><td>63.14</td><td>1326</td><td>41.08</td><td>20.30</td><td>80.90</td><td><b>80.96</b></td><td>53.31</td><td>73.00%</td></tr>
1107+
<tr><td>VisionSelector</td><td><u>70.50</u></td><td><b>65.92</b></td><td><b>79.94</b></td><td>59.97</td><td>64.69</td><td>1374</td><td>42.86</td><td><u>45.20</u></td><td>82.66</td><td><u>80.61</u></td><td><b>71.57</b></td><td><u>84.42%</u></td></tr>
1108+
<tr><td>DivPrune</td><td>67.71</td><td>43.12</td><td>58.03</td><td>61.25</td><td>65.12</td><td>1389</td><td>40.43</td><td>27.90</td><td>82.24</td><td>79.18</td><td>56.87</td><td>75.50%</td></tr>
1109+
<tr><td>DART</td><td>67.49</td><td>47.56</td><td>60.23</td><td>57.99</td><td>63.83</td><td>1299</td><td>42.18</td><td>23.40</td><td>74.20</td><td>78.63</td><td>58.02</td><td>74.09%</td></tr>
1110+
<tr><td>VisPruner</td><td>67.75</td><td>47.92</td><td>48.65</td><td>59.28</td><td>63.32</td><td>1305</td><td>41.51</td><td>22.50</td><td>78.74</td><td>79.77</td><td>54.95</td><td>73.19%</td></tr>
1111+
<tr><td>SCOPE</td><td>69.75</td><td>56.24</td><td>55.01</td><td><b>64.26</b></td><td><u>67.18</u></td><td><u>1390</u></td><td><b>44.35</b></td><td>30.80</td><td><u>83.34</u></td><td>80.47</td><td>62.58</td><td>79.37%</td></tr>
1112+
<tr><td><b>IDPruner</b></td><td><b>71.79</b></td><td><u>63.32</u></td><td><u>79.38</u></td><td><u>63.57</u></td><td><b>68.21</b></td><td><b>1438</b></td><td><u>44.05</u></td><td><b>45.50</b></td><td><b>84.51</b></td><td>80.57</td><td><u>70.02</u></td><td><b>85.71%</b></td></tr>
1113+
</tbody>
1114+
</table>
1115+
1116+
</details>
1117+
10281118
## 📝 License
10291119

10301120
The code for this project is open-sourced under the [License for AngelSlim](LICENSE).

README_cn.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,18 @@ python scripts/diffusion/run_diffusion.py \
290290

291291
更多量化推理方式请参考[Diffusion模型量化文档](https://angelslim.readthedocs.io/zh-cn/latest/features/diffusion/quantization.html)
292292

293+
#### 2.4 Token 压缩 (多模态/VLM)
294+
295+
AngelSlim 提供了一套基于元数据驱动的通用视觉 Token 剪枝与合并框架。你可以通过以下 Smoke Test 快速验证压缩策略(如 **VisionZip**):
296+
297+
```shell
298+
python tools/test_universal_pruning.py \
299+
--model_path "Qwen/Qwen2.5-VL-3B-Instruct" \
300+
--config "configs/qwen2_5_vl/pruning/visionzip_r0.9.yaml"
301+
```
302+
303+
关于如何新增自定义剪枝策略及算法实现细节,请参考 [Token 压缩说明文档](https://angelslim.readthedocs.io/zh-cn/latest/features/token_compressor/index.html)
304+
293305
### 3、部署与测试
294306

295307
#### 3.1 离线推理
@@ -1026,6 +1038,84 @@ Qwen3-Omni系列模型的`BF16`、`FP8-Static`、`FP8-Dynamic`在`aime25`、`gpq
10261038
10271039
</details>
10281040
1041+
### 3. Token 压缩 (多模态/VLM)
1042+
1043+
我们在 **Qwen2.5-VL-3B-Instruct** 模型上评估了多种视觉 Token 压缩策略(剪枝与合并)。你可以使用以下指令复现评测结果:
1044+
1045+
```shell
1046+
python tools/run_pruning_eval.py \
1047+
--model_path "Qwen/Qwen2.5-VL-3B-Instruct" \
1048+
--configs "configs/qwen2_5_vl/pruning/visionzip_r0.9.yaml" \
1049+
--tasks "textvqa" \
1050+
--output_dir "./results/visionzip_test"
1051+
```
1052+
1053+
<details>
1054+
<summary><b>Token 压缩详细评测结果 (Qwen2.5-VL-3B-Instruct)</b></summary>
1055+
1056+
<table style="text-align:center; vertical-align:middle;">
1057+
<thead>
1058+
<tr>
1059+
<th>方法</th>
1060+
<th>AI2D</th>
1061+
<th>ChartQA</th>
1062+
<th>DocVQA</th>
1063+
<th>MMB<sup>CN</sup></th>
1064+
<th>MMB</th>
1065+
<th>MME</th>
1066+
<th>MMStar</th>
1067+
<th>OCRBench</th>
1068+
<th>POPE</th>
1069+
<th>SQA</th>
1070+
<th>VQA<sup>Text</sup></th>
1071+
<th>平均值</th>
1072+
</tr>
1073+
</thead>
1074+
<tbody>
1075+
<tr>
1076+
<td><b>Baseline</b></td>
1077+
<td>79.11</td>
1078+
<td>83.56</td>
1079+
<td>92.48</td>
1080+
<td>73.28</td>
1081+
<td>77.32</td>
1082+
<td>1517</td>
1083+
<td>56.05</td>
1084+
<td>80.10</td>
1085+
<td>87.41</td>
1086+
<td>80.81</td>
1087+
<td>78.79</td>
1088+
<td>100.0%</td>
1089+
</tr>
1090+
<tr style="background-color: #808080;">
1091+
<th colspan="13">保留 25% Tokens (75% 压缩比)</th>
1092+
</tr>
1093+
<tr><td>FastV</td><td>72.70</td><td>70.04</td><td>75.98</td><td>63.40</td><td>66.92</td><td>1437</td><td>47.39</td><td>36.60</td><td>86.42</td><td>79.33</td><td>73.51</td><td>86.02%</td></tr>
1094+
<tr><td>VisionZip</td><td>74.19</td><td>71.32</td><td>70.11</td><td>67.35</td><td>71.22</td><td>1452</td><td>49.37</td><td>42.50</td><td>85.51</td><td><u>81.36</u></td><td>68.12</td><td>87.34%</td></tr>
1095+
<tr><td>HiPrune</td><td>73.83</td><td>72.76</td><td>72.10</td><td>67.27</td><td>72.34</td><td>1449</td><td>48.93</td><td>41.30</td><td>85.86</td><td>80.91</td><td>69.27</td><td>87.67%</td></tr>
1096+
<tr><td>VisionSelector</td><td>75.19</td><td>73.72</td><td><b>90.24</b></td><td><u>68.81</u></td><td>72.59</td><td><b>1521</b></td><td><u>49.97</u></td><td><u>61.80</u></td><td>85.36</td><td>80.37</td><td><u>76.86</u></td><td><u>93.62%</u></td></tr>
1097+
<tr><td>DivPrune</td><td>73.06</td><td>62.96</td><td>78.46</td><td>67.10</td><td>71.82</td><td>1459</td><td>48.38</td><td>51.40</td><td><b>86.81</b></td><td>80.22</td><td>68.91</td><td>88.15%</td></tr>
1098+
<tr><td>DART</td><td>71.08</td><td>65.20</td><td>79.72</td><td>65.38</td><td>71.05</td><td>1428</td><td>48.78</td><td>41.80</td><td>80.97</td><td>80.91</td><td>68.25</td><td>86.17%</td></tr>
1099+
<tr><td>VisPruner</td><td>74.29</td><td>68.20</td><td>72.52</td><td>67.35</td><td>70.88</td><td>1458</td><td>49.74</td><td>44.80</td><td>86.59</td><td><b>81.46</b></td><td>69.62</td><td>87.87%</td></tr>
1100+
<tr><td>SCOPE</td><td><u>75.84</u></td><td><u>74.00</u></td><td>82.40</td><td><u>68.81</u></td><td><u>72.94</u></td><td>1471</td><td><b>50.35</b></td><td>56.00</td><td><u>86.62</u></td><td>80.96</td><td>74.04</td><td>91.98%</td></tr>
1101+
<tr><td><b>IDPruner</b></td><td><b>75.94</b></td><td><b>75.84</b></td><td><u>90.00</u></td><td><b>69.42</b></td><td><b>73.80</b></td><td><u>1505</u></td><td>49.49</td><td><b>64.90</b></td><td>86.26</td><td>80.42</td><td><b>76.90</b></td><td><b>94.42%</b></td></tr>
1102+
<tr style="background-color: #808080;">
1103+
<th colspan="13">保留 10% Tokens (90% 压缩比)</th>
1104+
</tr>
1105+
<tr><td>FastV</td><td>65.87</td><td>29.72</td><td>36.89</td><td>48.37</td><td>51.98</td><td>1257</td><td>37.28</td><td>13.90</td><td>79.50</td><td>77.05</td><td>57.75</td><td>65.30%</td></tr>
1106+
<tr><td>VisionZip</td><td>67.65</td><td>51.60</td><td>37.88</td><td>59.62</td><td>63.06</td><td>1338</td><td>42.82</td><td>21.40</td><td>81.14</td><td>80.47</td><td>51.56</td><td>72.75%</td></tr>
1107+
<tr><td>HiPrune</td><td>67.75</td><td>53.20</td><td>41.15</td><td>59.45</td><td>63.14</td><td>1326</td><td>41.08</td><td>20.30</td><td>80.90</td><td><b>80.96</b></td><td>53.31</td><td>73.00%</td></tr>
1108+
<tr><td>VisionSelector</td><td><u>70.50</u></td><td><b>65.92</b></td><td><b>79.94</b></td><td>59.97</td><td>64.69</td><td>1374</td><td>42.86</td><td><u>45.20</u></td><td>82.66</td><td><u>80.61</u></td><td><b>71.57</b></td><td>84.42%</td></tr>
1109+
<tr><td>DivPrune</td><td>67.71</td><td>43.12</td><td>58.03</td><td>61.25</td><td>65.12</td><td>1389</td><td>40.43</td><td>27.90</td><td>82.24</td><td>79.18</td><td>56.87</td><td>75.50%</td></tr>
1110+
<tr><td>DART</td><td>67.49</td><td>47.56</td><td>60.23</td><td>57.99</td><td>63.83</td><td>1299</td><td>42.18</td><td>23.40</td><td>74.20</td><td>78.63</td><td>58.02</td><td>74.09%</td></tr>
1111+
<tr><td>VisPruner</td><td>67.75</td><td>47.92</td><td>48.65</td><td>59.28</td><td>63.32</td><td>1305</td><td>41.51</td><td>22.50</td><td>78.74</td><td>79.77</td><td>54.95</td><td>73.19%</td></tr>
1112+
<tr><td>SCOPE</td><td>69.75</td><td>56.24</td><td>55.01</td><td><b>64.26</b></td><td><u>67.18</u></td><td><u>1390</u></td><td><b>44.35</b></td><td>30.80</td><td><u>83.34</u></td><td>80.47</td><td>62.58</td><td>79.37%</td></tr>
1113+
<tr><td><b>IDPruner</b></td><td><b>71.79</b></td><td><u>63.32</u></td><td><u>79.38</u></td><td><u>63.57</u></td><td><b>68.21</b></td><td><b>1438</b></td><td><u>44.05</u></td><td><b>45.50</b></td><td><b>84.51</b></td><td>80.57</td><td><u>70.02</u></td><td><b>85.71%</b></td></tr>
1114+
</tbody>
1115+
</table>
1116+
1117+
</details>
1118+
10291119
## 📝许可协议
10301120

10311121
本项目的代码依照 [License for AngelSlim](LICENSE) 协议开源。
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Copyright 2026 Tencent Inc. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import importlib
16+
from typing import Any, Dict, List, Tuple
17+
18+
import torch.nn as nn
19+
20+
from .base.config import TokenCompressorConfig
21+
from .utils.config_utils import plan_pruning_execution
22+
23+
24+
class UniversalPruningAdapter:
25+
"""
26+
A metadata-driven adapter that transforms standard models into prunable models.
27+
The transformation sequence and targets are determined at initialization.
28+
"""
29+
30+
def __init__(
31+
self,
32+
model: nn.Module,
33+
strategy_config: TokenCompressorConfig,
34+
raw_map_data: List[Dict[str, Any]],
35+
):
36+
"""
37+
Args:
38+
model: The base HuggingFace model.
39+
strategy_config: User-defined compression and data requirements.
40+
raw_map_data: The ordered list of component mappings from YAML.
41+
"""
42+
self.model = model
43+
44+
# 1. Generate the immutable execution plan at initialization
45+
self.strategy_config, self.execution_plan = plan_pruning_execution(
46+
strategy_config=strategy_config,
47+
raw_map_data=raw_map_data,
48+
model_config=getattr(model, "config", None),
49+
)
50+
51+
# 2. Initialize backup storage for original module pointers
52+
if not hasattr(self.model, "old_model"):
53+
self.model.old_model = {}
54+
55+
def _get_parent_and_attr(self, path: str) -> Tuple[Any, str]:
56+
"""Resolves a dot-separated string path to (parent_object, attribute_name)."""
57+
parts = path.split(".")
58+
current = self.model
59+
for part in parts[:-1]:
60+
current = getattr(current, part)
61+
return current, parts[-1]
62+
63+
def _get_wrapper_class(self, module_path: str, class_name: str) -> Any:
64+
"""
65+
Dynamically imports the specified wrapper class.
66+
"""
67+
# Relative import logic: assumes we are inside the 'token_compressor'
68+
# package
69+
module = importlib.import_module(
70+
module_path,
71+
package="angelslim.compressor.token_compressor",
72+
)
73+
return getattr(module, class_name)
74+
75+
def _expand_execution_step(self, step: Dict[str, Any]) -> List[Tuple[str, int]]:
76+
"""
77+
Expands a plan step into physical module paths.
78+
Handles '[n]' by referencing the 'indices' field determined during planning.
79+
"""
80+
path_template = step["path"]
81+
if "[n]" not in path_template:
82+
return [(path_template, -1)]
83+
84+
prefix, suffix = path_template.split("[n]")
85+
suffix = suffix.lstrip(".")
86+
container_path = prefix.rstrip(".")
87+
88+
parent, attr = self._get_parent_and_attr(container_path)
89+
container = getattr(parent, attr)
90+
91+
# Use planned indices; if None, default to the entire range of the
92+
# container
93+
target_indices = step.get("indices")
94+
if target_indices is None:
95+
target_indices = range(len(container))
96+
97+
expanded = []
98+
for i in target_indices:
99+
full_path = f"{container_path}.{i}"
100+
if suffix:
101+
full_path += f".{suffix}"
102+
expanded.append((full_path, i))
103+
return expanded
104+
105+
def wrap_model(self) -> nn.Module:
106+
"""
107+
Sequentially wraps model components according to the execution_plan.
108+
"""
109+
for step in self.execution_plan:
110+
name = step["name"]
111+
wrapper_mod = step["wrapper_module"]
112+
wrapper_cls = step["wrapper_class"]
113+
114+
WrapperClass = self._get_wrapper_class(wrapper_mod, wrapper_cls)
115+
targets = self._expand_execution_step(step)
116+
117+
if name not in self.model.old_model:
118+
self.model.old_model[name] = {}
119+
120+
print(f"targets: {targets}")
121+
122+
for path, idx in targets:
123+
parent, attr_name = self._get_parent_and_attr(path)
124+
original_module = getattr(parent, attr_name)
125+
126+
# Prevent double-wrapping
127+
if not isinstance(original_module, WrapperClass):
128+
# Store original module for safe recovery
129+
backup_key = idx if idx != -1 else "single"
130+
self.model.old_model[name][backup_key] = original_module
131+
132+
# Instantiate and replace with the prunable wrapper
133+
new_module = WrapperClass(original_module, self.strategy_config)
134+
135+
# Explicitly inject the layer index for collection
136+
# components
137+
if idx != -1:
138+
new_module.layer_idx = idx
139+
140+
setattr(parent, attr_name, new_module)
141+
142+
print(f"[UniversalAdapter] '{name}' wrapped successfully")
143+
144+
return self.model
145+
146+
def unwrap_model(self) -> nn.Module:
147+
"""
148+
Restores the model to its original state by iterating the plan in REVERSE order.
149+
"""
150+
if not hasattr(self.model, "old_model") or not self.model.old_model:
151+
return self.model
152+
153+
# Order is reversed to restore nested modules from inside-out
154+
for step in reversed(self.execution_plan):
155+
name = step["name"]
156+
if name not in self.model.old_model:
157+
continue
158+
159+
targets = self._expand_execution_step(step)
160+
backups = self.model.old_model[name]
161+
162+
for path, idx in targets:
163+
backup_key = idx if idx != -1 else "single"
164+
if backup_key in backups:
165+
parent, attr_name = self._get_parent_and_attr(path)
166+
setattr(parent, attr_name, backups[backup_key])
167+
168+
print(f"[UniversalAdapter] '{name}' successfully restored.")
169+
170+
# Cleanup metadata
171+
self.model.old_model = {}
172+
if hasattr(self.model, "_pruning_adapter"):
173+
del self.model._pruning_adapter
174+
175+
print("[UniversalAdapter] Model fully reverted to standard architecture.")
176+
return self.model

0 commit comments

Comments
 (0)