diff --git a/hpvm/test/epoch_dnn/assets/miniera/calib.txt b/hpvm/test/epoch_dnn/assets/miniera/calib.txt new file mode 100644 index 0000000000000000000000000000000000000000..4fff404f7e9b7beb3ef3dfc19bf1648c68dbe7be --- /dev/null +++ b/hpvm/test/epoch_dnn/assets/miniera/calib.txt @@ -0,0 +1,21 @@ +input: 0.007874015748031496 +conv1: 0.041152522047244094 +add1: 0.041152522047244094 +relu1: 0.03673215196850394 +conv2: 0.18990387401574804 +add2: 0.18990387401574804 +relu2: 0.12851757480314963 +pool1: 0.12851757480314963 +conv3: 0.4379456692913386 +add3: 0.4379456692913386 +relu3: 0.13566622047244095 +conv4: 0.3754296456692913 +add4: 0.3754296456692913 +relu4: 0.15238466141732282 +pool2: 0.15238466141732282 +gemm1: 0.27306629921259845 +add5: 0.27306629921259845 +relu5: 0.20020370078740157 +gemm2: 0.6007037007874017 +add6: 0.6007037007874017 +softmax1: 0.007874015748031496 diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_b.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..39c3fbac7f94a6824736f8b21f184b71b3d45a7b --- /dev/null +++ b/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_b.bin @@ -0,0 +1,2 @@ +αÝ>aŸ¾Ì?N?„œQ¿JÙ%½t‰¼ªl©=™&¼½œ ¿¿^L8?د>r¾:õö¾ νóù¼š¶?B Y?–;Uì>ç—=€ëh?rXö½ï +‹=&ç½Ýˆ™½C#S>¥”½7ü¹>vÉ…>ɇ¿!? \ No newline at end of file diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_w.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..d01508286ed5fddf05790e261efa168847699efd Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/conv2d_1_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_b.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..39489675632774a46e0ea704d7d13807b2e4feb5 --- /dev/null +++ b/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_b.bin @@ -0,0 +1,2 @@ +„§Ÿ½æ.î=•·?¾Š¥¿vƒ¿ºS¿Óë†»Þ > +Qżøæ¼å—8¿ÂVä».I>Æp𼄃;dd=h䈾Ðé¾N½.¾ÓñÍ=/Ú¾ŒÖl¾×;¾ð4¾6ƒ>cTʾR¶ ¼ê¿¾ô2Í=c_¨¾¾ÚZ¾ \ No newline at end of file diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_w.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..381b72379b85614a79910c9560c6115310da538a Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/conv2d_2_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_b.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..43fe41a6edcae03a1a531123940e528a71807300 --- /dev/null +++ b/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_b.bin @@ -0,0 +1 @@ +˜Ò½Ê+¥½ò?Š$ù¾méÊ>(>¼½hŠ >qÂB½y²*½‚ì>IÒ¸>»Kˆ?@ ¨¼t\¢?æH¾ •=ùÔý>…œ;½_å—>Ÿfœ=;┿®Œû>›jÞ¾DâÓ»×Á‰¾šU>·`†? Éc>ÈŽ?Õª?Ÿ·/<#&?—?ôðš¾Dy<Hbf¾lò:?ÑwS¾M )>}¾«t ¾Ÿt'?ö¥ú¿´$¾<¥!æ½ œ?Ë¡½c’‹<Za¾>E5“>0Jê>p9J¾†žÁ>ÞWð>nèr>e–'?¹+R¿Gž>kæò;·bÆ¿²àR>˜{ÿ>ãš½ \ No newline at end of file diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_w.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..a82a9e397918217bf37873c59dd92bee713fa9df Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/conv2d_3_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_b.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..bd29ee60df1f0f0b3a199f1f65adb810a2649a3c Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_b.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_w.bin b/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..7f6211e76617ee18dac06c9b5449c18183a149de Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/conv2d_4_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/dense_1_b.bin b/hpvm/test/epoch_dnn/assets/miniera/dense_1_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..fae8736fa3da691229c66e73962cb4f0131c2961 Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/dense_1_b.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/dense_1_w.bin b/hpvm/test/epoch_dnn/assets/miniera/dense_1_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..034b84905ae34893f5b57be77033fd91b388a80b Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/dense_1_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/dense_2_b.bin b/hpvm/test/epoch_dnn/assets/miniera/dense_2_b.bin new file mode 100644 index 0000000000000000000000000000000000000000..2bb9d3e89f7d35a4e66063ec93baab23ef90b86e --- /dev/null +++ b/hpvm/test/epoch_dnn/assets/miniera/dense_2_b.bin @@ -0,0 +1 @@ +k"õ;(¿¾òë>™¿JÄ@? \ No newline at end of file diff --git a/hpvm/test/epoch_dnn/assets/miniera/dense_2_w.bin b/hpvm/test/epoch_dnn/assets/miniera/dense_2_w.bin new file mode 100644 index 0000000000000000000000000000000000000000..f3bce42e01c37928d0cd54493835bcda70529bf2 Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/dense_2_w.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/input.bin b/hpvm/test/epoch_dnn/assets/miniera/input.bin new file mode 100644 index 0000000000000000000000000000000000000000..0abae55bf84ff5dc8e2d1074c97853331fc5d879 Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/input.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/labels.bin b/hpvm/test/epoch_dnn/assets/miniera/labels.bin new file mode 100644 index 0000000000000000000000000000000000000000..effaef8583b30228039ff7f61d9c6be51c020b49 Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/labels.bin differ diff --git a/hpvm/test/epoch_dnn/assets/miniera/miniera.pth b/hpvm/test/epoch_dnn/assets/miniera/miniera.pth new file mode 100644 index 0000000000000000000000000000000000000000..f264a4ea67f88c361923e7c7f0208703001a622d Binary files /dev/null and b/hpvm/test/epoch_dnn/assets/miniera/miniera.pth differ diff --git a/hpvm/test/epoch_dnn/main.py b/hpvm/test/epoch_dnn/main.py new file mode 100644 index 0000000000000000000000000000000000000000..6f9e5795d323359a5a798abb0aa985bc8c91e224 --- /dev/null +++ b/hpvm/test/epoch_dnn/main.py @@ -0,0 +1,18 @@ +import site +from pathlib import Path + +import torch +from torch2hpvm import BinDataset, ModelExporter + +self_folder = Path(__file__).parent.absolute() +site.addsitedir(self_folder) + +from torch_dnn import MiniERA + +asset_dir = self_folder / "assets/miniera" +output_dir = Path("/tmp/miniera") +bin_dataset = BinDataset(asset_dir / "input.bin", asset_dir / "labels.bin", (5000, 3, 32, 32)) +model = MiniERA() +model.load_state_dict(torch.load(asset_dir / "miniera.pth")) +exporter = ModelExporter(model, bin_dataset, output_dir, asset_dir / "calib.txt") +exporter.generate().compile(output_dir / "miniera", output_dir) diff --git a/hpvm/test/epoch_dnn/make_ckpt.py b/hpvm/test/epoch_dnn/make_ckpt.py new file mode 100644 index 0000000000000000000000000000000000000000..eda4c2c02a63cd181e1a1a8552865ba7b72d26d2 --- /dev/null +++ b/hpvm/test/epoch_dnn/make_ckpt.py @@ -0,0 +1,33 @@ +"""Make PyTorch checkpoint of MiniERA model from legacy HPVM weights.""" +import site +from pathlib import Path + +import torch + +self_folder = Path(__file__).parent.absolute() +site.addsitedir(self_folder) + +from torch_dnn import CIFAR, MiniERA + + +@torch.no_grad() +def main(): + prefix = self_folder / "assets/miniera" + model = MiniERA().load_legacy_hpvm_weights(prefix) + # Test mini ERA + dataset = CIFAR.from_file(prefix / "input.bin", prefix / "labels.bin") + dataloader = torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False) + correct = 0 + total = 0 + for data in dataloader: + images, labels = data[0], data[1] + outputs = model(images) + _, predicted = torch.max(outputs.data, 1) + total += labels.size(0) + correct += (predicted == labels).sum().item() + print(f"Accuracy of the network on the test images: {100 * correct / total} %") + torch.save(model.state_dict(), prefix / "miniera.pth") + + +if __name__ == "__main__": + main() diff --git a/hpvm/test/epoch_dnn/torch_dnn/__init__.py b/hpvm/test/epoch_dnn/torch_dnn/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..62f29d0d7e715cceb1679f3dde21bc11e22dc131 --- /dev/null +++ b/hpvm/test/epoch_dnn/torch_dnn/__init__.py @@ -0,0 +1,2 @@ +from .datasets import CIFAR +from .miniera import MiniERA diff --git a/hpvm/test/epoch_dnn/torch_dnn/datasets.py b/hpvm/test/epoch_dnn/torch_dnn/datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..ac519bfe26ba48429cf40d6f16fdc3587e606b37 --- /dev/null +++ b/hpvm/test/epoch_dnn/torch_dnn/datasets.py @@ -0,0 +1,99 @@ +import logging +from pathlib import Path +from typing import Iterator, Tuple, Union + +import numpy as np +import torch +from torch.utils.data.dataset import Dataset + +RetT = Tuple[torch.Tensor, torch.Tensor] +msg_logger = logging.getLogger(__name__) + +PathLike = Union[Path, str] + + +class SingleFileDataset(Dataset): + def __init__(self, inputs: torch.Tensor, outputs: torch.Tensor): + self.inputs, self.outputs = inputs, outputs + + @classmethod + def from_file(cls, *args, **kwargs): + pass + + @property + def sample_input(self): + inputs, outputs = next(iter(self)) + return inputs + + def __len__(self) -> int: + return len(self.inputs) + + def __getitem__(self, idx) -> RetT: + return self.inputs[idx], self.outputs[idx] + + def __iter__(self) -> Iterator[RetT]: + for i in range(len(self)): + yield self[i] + + +class DNNDataset(SingleFileDataset): + image_shape = None + label_ty = np.int32 + + @classmethod + def from_file( + cls, + input_file: PathLike, + labels_file: PathLike, + count: int = -1, + offset: int = 0, + ): + # NOTE: assuming (N, *) ordering of inputs (such as NCHW, NHWC) + channel_size = np.prod(np.array(cls.image_shape)) + inputs_count_byte = -1 if count == -1 else count * channel_size + inputs = read_tensor_from_file( + input_file, + -1, + *cls.image_shape, + count=inputs_count_byte, + offset=offset * channel_size, + ) + labels = read_tensor_from_file( + labels_file, + -1, + read_ty=cls.label_ty, + cast_ty=np.long, + count=count, + offset=offset, + ) + if inputs.shape[0] != labels.shape[0]: + raise ValueError("Input and output have different number of data points") + msg_logger.info(f"%d entries loaded from dataset.", inputs.shape[0]) + return cls(inputs, labels) + + +class MNIST(DNNDataset): + image_shape = 1, 28, 28 + + +class CIFAR(DNNDataset): + image_shape = 3, 32, 32 + + +class ImageNet(DNNDataset): + image_shape = 3, 224, 224 + + +def read_tensor_from_file( + filename: Union[str, Path], + *shape: int, + read_ty=np.float32, + cast_ty=np.float32, + count: int = -1, + offset: int = 0, +) -> torch.Tensor: + offset = offset * read_ty().itemsize + mmap = np.memmap(filename, dtype=read_ty, mode="r", offset=offset) + n_entries = min(mmap.shape[0], count) if count != -1 else mmap.shape[0] + np_array = mmap[:n_entries].reshape(shape).astype(cast_ty) + return torch.from_numpy(np_array).clone() diff --git a/hpvm/test/epoch_dnn/torch_dnn/miniera.py b/hpvm/test/epoch_dnn/torch_dnn/miniera.py new file mode 100644 index 0000000000000000000000000000000000000000..84c88ac8f882b4b6d1ba98cdb3a88f92cf418075 --- /dev/null +++ b/hpvm/test/epoch_dnn/torch_dnn/miniera.py @@ -0,0 +1,55 @@ +from pathlib import Path +from typing import Union + +import numpy as np +import torch +from torch.nn import Conv2d, Linear, MaxPool2d, Module, ReLU, Sequential, Softmax + + +class MiniERA(Module): + def __init__(self): + super().__init__() + self.convs = Sequential( + Conv2d(3, 32, 3), + ReLU(), + Conv2d(32, 32, 3), + ReLU(), + MaxPool2d(2, 2), + Conv2d(32, 64, 3), + ReLU(), + Conv2d(64, 64, 3), + ReLU(), + MaxPool2d(2, 2), + ) + self.fcs = Sequential(Linear(1600, 256), ReLU(), Linear(256, 5)) + self.softmax = Softmax(1) + + def forward(self, input): + outputs = self.convs(input) + outputs = self.fcs(outputs.flatten(1, -1)) + return self.softmax(outputs) + + def load_legacy_hpvm_weights(self, prefix: Union[Path, str]): + prefix = Path(prefix) + # Load in model convolution weights + count = 0 + for conv in self.convs: + if not isinstance(conv, Conv2d): + continue + weight_np = np.fromfile(prefix / "conv2d_{count+1}_w.bin", dtype=np.float32) + bias_np = np.fromfile(prefix / "conv2d_{count+1}_b.bin", dtype=np.float32) + conv.weight.data = torch.tensor(weight_np).reshape(conv.weight.shape) + conv.bias.data = torch.tensor(bias_np).reshape(conv.bias.shape) + count += 1 + # Load in model fc weights + count = 0 + for linear in self.fcs: + if not isinstance(linear, Linear): + continue + weight_np = np.fromfile(prefix / "dense_{count+1}_w.bin", dtype=np.float32) + bias_np = np.fromfile(prefix / "dense_{count+1}_b.bin", dtype=np.float32) + cout, cin = linear.weight.shape + linear.weight.data = torch.tensor(weight_np).reshape(cin, cout).T + linear.bias.data = torch.tensor(bias_np).reshape(linear.bias.shape) + count += 1 + return self