Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions .github/workflows/BuildDocker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,37 @@ jobs:
name: Deploy Docker image
runs-on: ubuntu-22.04
steps:
# Actually build the Docker container
- uses: actions/checkout@v3
- uses: docker/setup-buildx-action@v1
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3

- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-

- name: GHCR Log-in
uses: docker/login-action@v1
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v2
uses: docker/build-push-action@v5
with:
context: .
file: Container/Dockerfile
push: true
# JUNGVI: If you operate from a fork and want to build a new docker make sure to replace 'pulp-platform' by your uname.
tags: ghcr.io/pulp-platform/deepquant:main
tags: ghcr.io/pulp-platform/deepquant:main
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

# Prevent cache from growing indefinitely
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
25 changes: 9 additions & 16 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build DeepQuant
- name: Install DeepQuant in existing venv
run: |
python -m pip install --upgrade pip
pip install -e .
uv pip install -e .

single-layer-tests:
runs-on: ubuntu-latest
Expand All @@ -35,31 +34,25 @@ jobs:
uses: actions/checkout@v4
with:
submodules: recursive
- name: Build DeepQuant
- name: Install DeepQuant in existing venv
run: |
python -m pip install --upgrade pip
pip install -e .
uv pip install -e .
- name: Run Tests
run: |
pytest -m SingleLayerTests

model-tests:
runs-on: ubuntu-latest
# container:
# image: ghcr.io/pulp-platform/deepquant:main
container:
image: ghcr.io/pulp-platform/deepquant:main
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build DeepQuant
- name: Install DeepQuant in existing venv
run: |
python -m pip install --upgrade pip
pip install -e .
uv pip install -e .
- name: Run Tests
run: |
pytest -m ModelTests
pytest -m ModelTests -s
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.11
18 changes: 12 additions & 6 deletions Container/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
&& rm -rf /var/lib/apt/lists/* \
&& pip install --no-cache-dir uv

WORKDIR /app

COPY pyproject.toml .
COPY pyproject.toml uv.lock ./

RUN pip install --upgrade pip setuptools wheel && \
pip install toml-to-requirements && \
toml-to-req --toml-file pyproject.toml && \
pip install -r requirements.txt
ENV UV_CACHE_DIR=/opt/uv-cache

RUN uv venv /opt/venv \
&& uv sync --frozen --no-dev --no-install-project

ENV VIRTUAL_ENV=/opt/venv
ENV PATH="/opt/venv/bin:$PATH"

WORKDIR /workspace
55 changes: 28 additions & 27 deletions DeepQuant/ExportBrevitas.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@


def exportBrevitas(
model: nn.Module, exampleInput: torch.Tensor, debug: bool = False
model: nn.Module, exampleInput: torch.Tensor, stricTesting: bool = True, debug: bool = False
) -> nn.Module:
"""
Export a Brevitas model to an FX GraphModule with unrolled quantization operations.
Expand Down Expand Up @@ -234,22 +234,6 @@ def exportBrevitas(
printer.print_tabular(fxModelUnified)
print()

# # Verify numerical consistency after dequant modification
# if torch.allclose(
# output_model, output_fx_model_dequant_modified, atol=1e-5
# ): # Verify numerical consistency
# if debug:
# print(f"{BLUE} ✓ Modification of Dequant Nodes: output is consistent{ENDC}")
# else:
# raise RuntimeError( # Raise error if inconsistent
# f"{RED} ✗ Modification of Dequant Nodes changed the output significantly{ENDC}"
# )

# if debug:
# print("\n=== 4. Network after the Modification of Dequant Nodes ===\n")
# printer.print_tabular(fx_model_unified)
# print()

onnxFile: str = EXPORT_FOLDER / "4_model_dequant_moved.onnx"
torch.onnx.export(
fxModelUnified,
Expand All @@ -263,16 +247,33 @@ def exportBrevitas(
output_names=["output"],
)

# Verify numerical consistency after dequant modification
if torch.allclose(
outputModel, outputFxModelDequantModified, atol=1e-5
): # Verify numerical consistency
if debug:
print(f"{BLUE} ✓ Modification of Dequant Nodes: output is consistent{ENDC}")
else:
raise RuntimeError( # Raise error if inconsistent
f"{RED} ✗ Modification of Dequant Nodes changed the output significantly{ENDC}"
)
if stricTesting:

if torch.allclose(
outputModel, outputFxModelDequantModified, atol=1e-5
): # Verify numerical consistency
if debug:
print(f"{BLUE} ✓ Modification of Dequant Nodes: output is consistent{ENDC}")
else:
# Calculate error metrics
abs_error = torch.abs(outputModel - outputFxModelDequantModified)
max_abs_error = torch.max(abs_error).item()
mean_abs_error = torch.mean(abs_error).item()

# Calculate relative error (avoid division by zero)
rel_error = abs_error / (torch.abs(outputModel) + 1e-10)
max_rel_error = torch.max(rel_error).item()
mean_rel_error = torch.mean(rel_error).item()

print(f"{RED} ✗ Modification of Dequant Nodes changed the output significantly{ENDC}")
print(f" Max Absolute Error: {max_abs_error:.6e}")
print(f" Mean Absolute Error: {mean_abs_error:.6e}")
print(f" Max Relative Error: {max_rel_error:.6e}")
print(f" Mean Relative Error: {mean_rel_error:.6e}")

raise RuntimeError( # Raise error if inconsistent
f"{RED} ✗ Modification of Dequant Nodes changed the output significantly{ENDC}"
)

import numpy as np
import onnxruntime as ort
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@

A library for true-quantization and optimization of neural networks.

Deeploy is developed as part of the PULP project, a joint effort between ETH Zurich and the University of Bologna.
DeepQuant is developed as part of the PULP project, a joint effort between ETH Zurich and the University of Bologna.

## License

Unless specified otherwise in the respective file headers, all code checked into this repository is made available under a permissive license. All software sources and tool scripts are licensed under Apache 2.0, except for files contained in the `scripts` directory, which are licensed under the MIT license, and files contained in the `DeeployTest/Tests`directory, which are licensed under the [Creative Commons Attribution-NoDerivates 4.0 International](https://creativecommons.org/licenses/by-nd/4.0) license (CC BY-ND 4.0).

## Installation

Start by creating a new env with `Python 3.11` or higher. Then clone the repo and install the library as an editable package with:
Start by creating a new env with `Python 3.11`. Then clone the repo and install the library as an editable package with:
```
pip install -e .
```

If you use `uv`, a lock file is provided, and you can simply run `uv sync` to create the appropriate `venv`. Then use `uv run <your-command>` to run anything in the created `venv`. For instance, to run all tests on the `venv` created by `uv` you can run:
```
uv run pytest
```

## Running Tests

We provide comprehensive tests with pytest, to execute all tests, simply run `pytest`. We mark our tests in two categories, `SingleLayerTests` and `ModelTests`, to execute the tests of the specific category, you can run `pytest -m <category>`. For instance, to execute only the single layer tests, you can run `pytest -m SingleLayerTests`.
Expand Down
15 changes: 13 additions & 2 deletions Tests/TestResNet18.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Federico Brancasi <fbrancasi@ethz.ch>


from email.mime import base
import pytest
import torch
import torch.nn as nn
Expand Down Expand Up @@ -120,6 +121,16 @@ def deepQuantTestResnet18() -> None:
torch.manual_seed(42)

quantizedModel = prepareResnet18Model()
sampleInput = torch.randn(1, 3, 224, 224)

exportBrevitas(quantizedModel, sampleInput, debug=True)
# sampleInput = torch.randn(1, 3, 224, 224)
sampleInput = torch.randint(low=-4, high=4, size=(1, 3, 224, 224))

exportedModel = exportBrevitas(quantizedModel, sampleInput, stricTesting=False, debug=True)

quantizedModel = prepareResnet18Model()

trueQuantizedPrediction = torch.argmax(exportedModel(sampleInput)).item()
fakeQuantizedPrediction = torch.argmax(quantizedModel(sampleInput)).item()

assert fakeQuantizedPrediction == trueQuantizedPrediction, "Error: Network predicted different labels"

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,9 @@ target-version = ["py311"]

[tool.isort]
profile = "black"
multi_line_output = 3
multi_line_output = 3

[dependency-groups]
dev = [
"ipython>=9.9.0",
]
Loading