Skip to content
This repository was archived by the owner on Jun 3, 2025. It is now read-only.

Commit 1392a35

Browse files
authored
Merge branch 'main' into prompt-mask
2 parents d819f8d + f784980 commit 1392a35

File tree

101 files changed

+5025
-326
lines changed

Some content is hidden

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

101 files changed

+5025
-326
lines changed

.github/workflows/build-container.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Build Docker Container
2+
on:
3+
pull_request:
4+
types: [opened, synchronize, reopened]
5+
branches:
6+
- main
7+
- 'release/[0-9]+.[0-9]+'
8+
push:
9+
branches:
10+
- 'main'
11+
release:
12+
types: [created, published]
13+
schedule:
14+
- cron: '0 2 * * *'
15+
16+
# TODO: docker containers created through a release cut vs PR to the release branch
17+
# will be pushed to different locations (i.e one will be sparseml the other will be test-sparseml).
18+
# These containers rely on the new internal pypi server being enabled. Once enabled,
19+
# this workflow can be expanded to make this distinction.
20+
env:
21+
RELEASE: ${{ github.event_name =='release' || (startsWith(github.base_ref, 'release/') && github.event_name == 'pull_request')}}
22+
DEV: ${{ github.base_ref == 'main' && github.event_name == 'pull_request'}}
23+
NAME: ${{ github.event.number }}
24+
25+
permissions:
26+
contents: read
27+
packages: write
28+
29+
jobs:
30+
build-container:
31+
name: Build sparseml container
32+
runs-on: ubuntu-20.04
33+
steps:
34+
- name: Checkout code
35+
uses: actions/checkout@v3
36+
with:
37+
fetch-depth: 1
38+
- name: Set up Docker Buildx
39+
id: buildx
40+
uses: docker/setup-buildx-action@v2
41+
with:
42+
buildkitd-flags: --debug
43+
- name: Get current date
44+
id: date
45+
run: echo "::set-output name=date::$(date +'%Y%m%d')"
46+
- name: Get the current version
47+
if: ${{ env.RELEASE == 'true' }}
48+
id: version
49+
run: echo "::set-output name=version::$(echo ${{ github.base_ref }} | cut -c 9-15)"
50+
- name: Login to Github Packages
51+
uses: docker/login-action@v2
52+
with:
53+
registry: ghcr.io
54+
username: ${{ github.actor }}
55+
password: ${{ secrets.GITHUB_TOKEN }}
56+
- name: Build Dev Docker Container
57+
if: ${{ env.DEV == 'true' }}
58+
uses: docker/build-push-action@v4
59+
with:
60+
context: ./docker/containers/docker_dev
61+
build-args: |
62+
BRANCH=${{github.head_ref}}
63+
push: true
64+
tags: ghcr.io/neuralmagic/sparseml-dev:${{ env.NAME }}
65+
- name: Build Release Docker Container
66+
if: ${{ env.RELEASE == 'true' }}
67+
uses: docker/build-push-action@v4
68+
with:
69+
context: ./docker/containers/docker_release
70+
build-args: |
71+
VERSION=${{ steps.version.outputs.version }}
72+
push: true
73+
tags: ghcr.io/neuralmagic/test-sparseml:latest, ghcr.io/neuralmagic/test-sparseml:${{ steps.version.outputs.version }}
74+
- name: Build Nightly Docker Container
75+
if: ${{ env.DEV == 'false' && env.RELEASE == 'false'}}
76+
uses: docker/build-push-action@v4
77+
with:
78+
context: ./docker/containers/docker_nightly
79+
push: true
80+
tags: ghcr.io/neuralmagic/test-sparseml-nightly:latest, ghcr.io/neuralmagic/test-sparseml-nightly:${{ steps.date.outputs.date }}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
ARG SOURCE=ghcr.io/neuralmagic/cuda-python3.10
2+
3+
ARG TORCH_VERSION=2.1.2
4+
ARG TORCHVISION_VERSION=0.16.2
5+
ARG CUDA=121
6+
ARG BRANCH
7+
8+
FROM $SOURCE
9+
10+
ARG BRANCH
11+
12+
RUN python3.10 -m pip install --upgrade pip \
13+
&& python3.10 -m pip install --upgrade setuptools
14+
15+
ARG CUDA
16+
ARG TORCH_VERSION
17+
ARG TORCHVISION_VERSION
18+
19+
RUN python3.10 -m pip install torch==${TORCH_VERSION}+cu${CUDA} torchvision==${TORCHVISION_VERSION}+cu${CUDA} -f https://download.pytorch.org/whl/torch_stable.html \
20+
&& git clone https://github.com/neuralmagic/sparseml.git --depth 1 --single-branch -b ${BRANCH} \
21+
&& python3.10 -m pip install -e "./sparseml[dev]"
22+
23+
HEALTHCHECK CMD python3.10 -c 'import sparseml'
24+
RUN python3.10 -m pip list | grep sparseml
25+
CMD bash
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ARG SOURCE=ghcr.io/neuralmagic/cuda-python3.10
2+
3+
ARG TORCH_VERSION=2.1.2
4+
ARG TORCHVISION_VERSION=0.16.2
5+
ARG CUDA=121
6+
7+
FROM $SOURCE
8+
9+
RUN python3.10 -m pip install --upgrade pip \
10+
&& python3.10 -m pip install --upgrade setuptools
11+
12+
ARG CUDA
13+
ARG TORCH_VERSION
14+
ARG TORCHVISION_VERSION
15+
16+
RUN python3.10 -m pip install torch==${TORCH_VERSION}+cu${CUDA} torchvision==${TORCHVISION_VERSION}+cu${CUDA} -f https://download.pytorch.org/whl/torch_stable.html \
17+
&& python3.10 -m pip install --no-cache-dir "sparseml-nightly[onnxruntime,torchvision,transformers,yolov5,ultralytics]"
18+
19+
HEALTHCHECK CMD python3.10 -c 'import sparseml'
20+
RUN python3.10 -m pip list | grep sparseml
21+
CMD bash
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
ARG SOURCE=ghcr.io/neuralmagic/cuda-python3.10
2+
3+
ARG TORCH_VERSION=2.1.2
4+
ARG TORCHVISION_VERSION=0.16.2
5+
ARG CUDA=121
6+
ARG VERSION
7+
8+
FROM $SOURCE
9+
10+
ARG VERSION
11+
12+
ARG CUDA
13+
ARG TORCH_VERSION
14+
ARG TORCHVISION_VERSION
15+
16+
RUN python3.10 -m pip install --upgrade pip \
17+
&& python3.10 -m pip install --upgrade setuptools
18+
19+
RUN python3.10 -m pip install torch==${TORCH_VERSION}+cu${CUDA} torchvision==${TORCHVISION_VERSION}+cu${CUDA} -f https://download.pytorch.org/whl/torch_stable.html \
20+
&& python3.10 -m pip install --no-cache-dir "sparseml[onnxruntime,torchvision,transformers,yolov5,ultralytics]==$VERSION"
21+
22+
HEALTHCHECK CMD python3.10 -c 'import sparseml'
23+
RUN python3.10 -m pip list | grep sparseml
24+
CMD bash

integrations/huggingface-transformers/finetuning/example_fsdp_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fsdp_config:
1515
machine_rank: 0
1616
main_training_function: main
1717
num_machines: 1
18-
num_processes: 2
18+
num_processes: 4
1919
rdzv_backend: static
2020
same_network: true
2121
tpu_env: []

integrations/huggingface-transformers/tutorials/sparse-transfer-learning-bert-python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ With the models downloaded, we will set up the Hugging Face `tokenizer`, `config
7777
We instantiate these classes by passing the local path to the directory containing the `pytorch_model.bin`, `tokenizer.json`, and `config.json` files from the SparseZoo download.
7878

7979
```python
80-
from sparseml.transformers.utils import SparseAutoModel
80+
from sparseml.transformers import SparseAutoModel
8181
from transformers import AutoModelForSequenceClassification, AutoConfig, AutoTokenizer
8282

8383
NUM_LABELS = 2

pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ line-length = 88
33
target-version = ['py36']
44

55
[tool.pytest.ini_options]
6-
tmp_path_retention_policy = "none"
6+
tmp_path_retention_policy = "none"
7+
markers = [
8+
"integration: integration tests",
9+
"unit: unit tests",
10+
"custom: custom integration tests",
11+
"smoke: smoke tests"
12+
]

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
_deps = [
4040
"setuptools<=59.5.0",
4141
"pyyaml>=5.0.0",
42-
"numpy>=1.0.0",
42+
"numpy>=1.17.0",
4343
"matplotlib>=3.0.0",
4444
"merge-args>=0.1.0",
4545
"onnx>=1.5.0,<1.15.0",
@@ -79,8 +79,7 @@
7979
"opencv-python<=4.6.0.66",
8080
]
8181
_transformers_deps = _pytorch_deps + [
82-
f"{'nm-transformers' if is_release else 'nm-transformers-nightly'}"
83-
f"~={version_nm_deps}",
82+
"transformers<4.35.0",
8483
"datasets<=2.14.6",
8584
"dvc",
8685
"scikit-learn",
@@ -92,7 +91,7 @@
9291
]
9392
_llm_deps = _transformers_deps + ["sentencepiece"]
9493
_yolov5_deps = _pytorch_vision_deps + [
95-
f"{'nm-yolov5' if is_release else 'nm-yolov5-nightly'}~={version_nm_deps}"
94+
f"{'nm-yolov5' if is_release else 'nm-yolov5-nightly'}<={version_nm_deps}"
9695
]
9796
_notebook_deps = [
9897
"jupyter>=1.0.0",
@@ -120,6 +119,7 @@
120119
"tensorboard>=1.0,<2.9",
121120
"tensorboardX>=1.0",
122121
"evaluate>=0.4.1",
122+
"parameterized",
123123
]
124124

125125
_docs_deps = [

src/sparseml/evaluation/integrations/perplexity.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414

1515
from typing import List, Optional, Union
1616

17-
from sparseml.transformers.utils.sparse_model import SparseAutoModelForCausalLM
18-
from sparseml.transformers.utils.sparse_tokenizer import SparseAutoTokenizer
17+
from sparseml.transformers import SparseAutoModelForCausalLM, SparseAutoTokenizer
1918

2019

2120
try:

src/sparseml/export/validators.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
from collections import OrderedDict
1919
from pathlib import Path
2020
from typing import Callable, List, Optional, Union
21+
from typing import OrderedDict as OrderedDictType
2122

2223
import numpy
24+
import onnx
2325

2426
from sparseml.export.export_data import InputsNames, LabelNames, OutputsNames
2527
from sparseml.export.helpers import ONNX_MODEL_NAME, onnx_data_files
@@ -164,8 +166,11 @@ def validate_correctness(
164166

165167
sample_inputs_files = sorted(glob.glob(os.path.join(sample_inputs_path, "*")))
166168
sample_outputs_files = sorted(glob.glob(os.path.join(sample_outputs_path, "*")))
167-
168-
session = ort.InferenceSession(os.path.join(directory, onnx_model_name))
169+
model_path = os.path.join(directory, onnx_model_name)
170+
expected_input_names = [
171+
inp.name for inp in onnx.load(model_path, load_external_data=False).graph.input
172+
]
173+
session = ort.InferenceSession(model_path)
169174

170175
validations = (
171176
[]
@@ -180,6 +185,11 @@ def validate_correctness(
180185
sample_input_with_batch_dim = OrderedDict(
181186
(key, numpy.expand_dims(value, 0)) for key, value in sample_input.items()
182187
)
188+
189+
sample_input_with_batch_dim = _potentially_rename_input(
190+
sample_input_with_batch_dim, expected_input_names
191+
)
192+
183193
outputs = session.run(None, sample_input_with_batch_dim)
184194
if isinstance(outputs, list):
185195
validations_sample = []
@@ -205,3 +215,17 @@ def validate_correctness(
205215
f"Successfully validated the exported model on all {len(validations)} samples."
206216
)
207217
return True
218+
219+
220+
def _potentially_rename_input(
221+
sample_input_with_batch_dim: OrderedDictType[str, numpy.ndarray],
222+
expected_input_names: List[str],
223+
) -> OrderedDictType[str, numpy.ndarray]:
224+
# if required, rename the input names of the sample to match
225+
# the input names of the model
226+
input_names = list(sample_input_with_batch_dim.keys())
227+
if set(input_names) != set(expected_input_names):
228+
return OrderedDict(
229+
zip(expected_input_names, sample_input_with_batch_dim.values())
230+
)
231+
return sample_input_with_batch_dim

src/sparseml/modifiers/distillation/utils/pytorch/model_wrapper.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ def _clear_missing_keys(module, incompatible_keys):
4949
self.register_load_state_dict_post_hook(_clear_missing_keys)
5050

5151
def forward(self, *args, **kwargs):
52-
self.teacher_model.eval()
5352
if not self.kd_enabled:
5453
return self.student_model(*args, **kwargs)
5554

@@ -118,6 +117,13 @@ def named_modules(
118117
memo=memo, prefix=prefix, remove_duplicate=remove_duplicate
119118
)
120119

120+
def named_children(self):
121+
return self.student_model.named_children()
122+
123+
def train(self, mode: bool = True):
124+
self.student_model.train(mode)
125+
return self
126+
121127
def __getattr__(self, name: str) -> Any:
122128
try:
123129
return super().__getattr__(name)

src/sparseml/pytorch/model_load/helpers.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from torch.nn import Module
2323

2424
import sparseml.core.session as session_manager
25+
from safetensors import safe_open
2526
from sparseml.core.framework import Framework
2627
from sparseml.pytorch.sparsification.quantization.helpers import (
2728
initialize_channel_wise_scale_zp,
@@ -143,7 +144,8 @@ def reload_model_state(
143144
weight_files = [
144145
os.path.join(load_path, os.path.basename(f))
145146
for f in files
146-
if f.startswith("pytorch_model") and f.endswith("bin")
147+
if (f.startswith("pytorch_model") and f.endswith("bin"))
148+
or (f.endswith("safetensors"))
147149
]
148150
if not weight_files:
149151
_LOGGER.warning(
@@ -168,7 +170,10 @@ def reload_model_state(
168170
# change in keys due to architecture changes, reload statedict
169171
loaded_state_dict = {}
170172
for f in weight_files:
171-
dd = torch.load(f, map_location="cpu")
173+
if f.endswith("safetensors"):
174+
dd = load_safetensors_state_dict(file_path=f)
175+
else:
176+
dd = torch.load(f, map_location="cpu")
172177
loaded_state_dict.update(dd)
173178

174179
_, missing, unexpected, mismatched, _, _ = model._load_pretrained_model(
@@ -229,17 +234,25 @@ def reload_model_from_checkpoint(model: Module, checkpoint: Optional[str] = None
229234

230235

231236
def save_model_and_recipe(
232-
model: Module, save_path: str, tokenizer: Optional[Any] = None
237+
model: Module,
238+
save_path: str,
239+
tokenizer: Optional[Any] = None,
240+
save_safetensors: bool = False,
241+
save_compressed: bool = False,
233242
):
234243
"""
235244
Save a model, tokenizer and the currently loaded recipe to file
236245
237246
:param model: pytorch model to save
238247
:param save_path: path to save output to
239248
:param tokenizer: model tokenizer to save
249+
:param save_safetensors: whether to save as safetensors or pickle (bin)
250+
:param save_compressed: whether to compress sparse weights on disk
240251
"""
241252

242-
model.save_pretrained(save_path)
253+
model.save_pretrained(
254+
save_path, save_compressed=save_compressed, safe_serialization=save_safetensors
255+
)
243256

244257
if tokenizer is not None:
245258
tokenizer.save_pretrained(save_path)
@@ -326,3 +339,14 @@ def save_completed_stages(checkpoint_dir: str, completed_stages: List[str]):
326339
stage_path = os.path.join(checkpoint_dir, COMPLETED_STAGES_FILENAME)
327340
with open(stage_path, "w") as out_file:
328341
json.dump({"completed": completed_stages}, out_file)
342+
343+
344+
def load_safetensors_state_dict(file_path: str) -> Dict[str, torch.Tensor]:
345+
"""
346+
Load a safetensors file from disk
347+
348+
:param file_path: path to the safetensors file
349+
:return: dictionary of safetensors data
350+
"""
351+
with safe_open(file_path, framework="pt", device="cpu") as f:
352+
return {key: f.get_tensor(key) for key in f.keys()}

0 commit comments

Comments
 (0)