diff --git a/.gitignore b/.gitignore index 8010553..fc2333c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ agents/* *.pkl checkpoints/* *.npy -*.json logs/* logs LeNet5/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef17c0e --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +# Project: End-to-End CNN Design Exploration using FINN + +We provide instructions on how to run for model LeNet5. ResNets models are also supported. Models were tested on Alveo U250. + +## Step 1: Install Dependencies + +``` +git clone https://github.com/joannapng/finn.git +cd finn +git checkout dev +``` + +## Step 2: Download project and add to FINN folder +``` +git clone https://github.com/joannapng/Thesis.git +``` + +## Step 3: Run finn docker container +``` +bash run-docker.sh +cd Thesis +``` + +## Step 4: Pretrain LeNet5 on MNIST +``` +python pretrain.py --model-name LeNet5 --dataset MNIST --training-epochs 10 +``` + +## Step 5: Train agent on LeNet5 +``` +mkdir LeNet5 +python train.py --model-name LeNet5 --dataset MNIST --model-path --freq 300 --target-fps 6000 --board U250 --num-episodes 30 +``` +## Step 6: Test agent on LeNet5 +``` +python test.py --model-name LeNet5 --dataset MNIST --model-path --freq 300 --target-fps 6000 --output-dir LeNet5 --onnx-output LeNet5 --agent-path agents/agent_LeNet5 --board U250 +``` + +## Step 7: Export model to HW +``` +python export.py --model-name LeNet5 --onnx-model LeNet5/LeNet5_quant.onnx --output-dir LeNet5 --input-file LeNet5/input.npy --expected-output-file LeNet5/expected_output_file.npy --folding-config-file LeNet5/folding_config.json --board U250 +``` +## Step 8: +This project has not been tested with other models or platforms yet, but if you want to do so you have to add the model and the platform to the flow as so: + +# How to add your model to the flow +1. Add your PyTorch model definition inside folder `pretrain/models` and update `pretrain/models/__init__.py` +2. Change lines 14-21 of `pretrain/trainer/Trainer.py` to include your model. Do the same with lines 16-24 of `train/finetune/Finetuner.py` +3. You must also add your custom streamlining and convert to hw function. Add those in files `train/export/Exporter.py` and `exporter/Exporter.py` following the format of `streamline_resnet` and `convert_to_hw_resnet`. Include then in the dictionary `streamline_functions` and `convert_to_hw_functions` of `exporter.py` and `train/env/ModelEnv.py`. + +# How to add your platform to the flow +1. Add a `.json` file in the folder `platforms`. Make sure it follows the format of `platforms/U250.json` +2. Update the dictionary `platform_files` in `train/env/ModelEnv.py` and `train/exporter/Exporter.py` \ No newline at end of file diff --git a/Simple/auto_folding_config.json b/Simple/auto_folding_config.json new file mode 100644 index 0000000..6cff448 --- /dev/null +++ b/Simple/auto_folding_config.json @@ -0,0 +1,104 @@ +{ + "Defaults": {}, + "Thresholding_rtl_0": { + "PE": 1, + "runtime_writeable_weights": 0, + "depth_trigger_uram": 0, + "depth_trigger_bram": 0, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "FMPadding_rtl_0": { + "SIMD": 1, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "ConvolutionInputGenerator_rtl_0": { + "SIMD": 1, + "parallel_window": 0, + "ram_style": "distributed", + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "VVAU_hls_0": { + "PE": 1, + "SIMD": 1, + "ram_style": "auto", + "resType": "auto", + "mem_mode": "internal_decoupled", + "runtime_writeable_weights": 0, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "Thresholding_rtl_1": { + "PE": 1, + "runtime_writeable_weights": 0, + "depth_trigger_uram": 0, + "depth_trigger_bram": 0, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "ConvolutionInputGenerator_rtl_1": { + "SIMD": 1, + "parallel_window": 0, + "ram_style": "distributed", + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "Pool_hls_0": { + "PE": 1, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "MVAU_hls_0": { + "PE": 1, + "SIMD": 1, + "ram_style": "auto", + "resType": "auto", + "mem_mode": "internal_decoupled", + "runtime_writeable_weights": 0, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + }, + "LabelSelect_hls_0": { + "PE": 1, + "inFIFODepths": [ + 2 + ], + "outFIFODepths": [ + 2 + ] + } +} \ No newline at end of file diff --git a/export.py b/export.py index 945d688..3cd7180 100644 --- a/export.py +++ b/export.py @@ -24,7 +24,7 @@ parser.add_argument('--tidy-up-verification', action = argparse.BooleanOptionalAction, help = 'Perform verification after tidy-up transformation') parser.add_argument('--qonnx-to-finn-verification', action = argparse.BooleanOptionalAction, help = 'Perform verification after QONNXToFinn transformation') -parser.add_argument('--streamlined-python-verification', action = argsparse.BooleanOptionalAction, help = 'Perform verification after streamlining') +parser.add_argument('--streamlined-python-verification', action = argparse.BooleanOptionalAction, help = 'Perform verification after streamlining') parser.add_argument('--folded-hls-cppsim', action = argparse.BooleanOptionalAction, help = 'Perform cpp simulation after folding') parser.add_argument('--rtlsim-performance', action=argparse.BooleanOptionalAction, help = 'Generate rtlsim performance reports') parser.add_argument('--rtlsim-verification', action=argparse.BooleanOptionalAction, help = 'Perform rtlsim verification (not recommended for large networks)') @@ -66,15 +66,21 @@ def main(): if args.rtlsim_performance: generate_outputs.append(build_cfg.DataflowOutputType.RTLSIM_PERFORMANCE) - verify_steps = [ - #build_cfg.VerificationStepType.QONNX_TO_FINN_PYTHON, - #build_cfg.VerificationStepType.TIDY_UP_PYTHON, - #build_cfg.VerificationStepType.STREAMLINED_PYTHON, - #build_cfg.VerificationStepType.FOLDED_HLS_CPPSIM, - ] + verify_steps = [] + if args.tidy_up_verification: + verify_steps.append(build_cfg.VerificationStepType.TIDY_UP_PYTHON) + + if args.qonnx_to_finn_verification: + verify_steps.append(build_cfg.VerificationStepType.QONNX_TO_FINN_PYTHON) + + if args.streamlined_python_verification: + verify_steps.append(build_cfg.VerificationStepType.STREAMLINED_PYTHON) + + if args.folded_hls_cppsim: + verify-steps.append(build_cfg.VerificationStepType.FOLDED_HLS_CPPSIM) - #if args.rtlsim_verification: - #verify_steps.append(build_cfg.VerificationStepType.STITCHED_IP_RTLSIM) + if args.rtlsim_verification: + verify_steps.append(build_cfg.VerificationStepType.STITCHED_IP_RTLSIM) cfg_build = build.DataflowBuildConfig( output_dir = output_dir, diff --git a/pretrain.py b/pretrain.py index b34dfbf..f5abd11 100644 --- a/pretrain.py +++ b/pretrain.py @@ -4,13 +4,10 @@ from pretrain.trainer import Trainer from pretrain.utils import get_model_config -model_names = ['LeNet5', 'resnet18', 'resnet34', 'resnet50', 'resnet100', 'resnet152'] - parser = argparse.ArgumentParser(description = 'Pretraining model parameters') # Model Parameters -parser.add_argument('--model-name', default='resnet18', metavar='ARCH', choices=model_names, - help = 'model_architecture: ' + ' | '.join(model_names) + ' (default: resnet18)') +parser.add_argument('--model-name', default='resnet18', help = 'Target model name') parser.add_argument('--pretrained', action = 'store_true', default = False, help = 'Whether to use pretrained model (default: false)') parser.add_argument('--model-path', default = None, help = 'Path to pretrained model. Should be provided if pretrained is True (default: None)') parser.add_argument('--resume-from', default = None, help = 'If resume-from is not None, training resumes from specified checkpoint (default: None)') diff --git a/pretrain/models/Simple.py b/pretrain/models/Simple.py deleted file mode 100644 index c42d126..0000000 --- a/pretrain/models/Simple.py +++ /dev/null @@ -1,31 +0,0 @@ -import torch -import torch.nn as nn -from torch.nn import ModuleList - -class Simple(nn.Module): - def __init__(self, num_classes, in_channels): - super(Simple, self).__init__() - - self.conv_features = ModuleList() - self.linear_features = ModuleList() - - self.conv_features.append(nn.Conv2d(1, 1, 5, padding = 2)) - self.conv_features.append(nn.BatchNorm2d(1)) - self.conv_features.append(nn.ReLU()) - self.conv_features.append(nn.MaxPool2d(kernel_size = 4, stride = 4)) - - self.linear_features.append(nn.Linear(7 * 7 , num_classes)) - self.name = "Simple" - - def forward(self, x): - x = 2.0 * x - 1.0 - - for mod in self.conv_features: - x = mod(x) - - x = x.flatten(1) - - for mod in self.linear_features: - x = mod(x) - - return x \ No newline at end of file diff --git a/pretrain/models/__init__.py b/pretrain/models/__init__.py index 0a713c2..dac2659 100644 --- a/pretrain/models/__init__.py +++ b/pretrain/models/__init__.py @@ -3,5 +3,4 @@ __all__ = ["LeNet5", "resnet", "Simple"] from .LeNet5 import * -from .resnet import * -from .Simple import * \ No newline at end of file +from .resnet import * \ No newline at end of file diff --git a/pretrain/trainer/Trainer.py b/pretrain/trainer/Trainer.py index 23da174..04c15a3 100644 --- a/pretrain/trainer/Trainer.py +++ b/pretrain/trainer/Trainer.py @@ -11,15 +11,14 @@ from ..logger import Logger from ..utils import * -from ..models import LeNet5, ResNet18, ResNet34, ResNet50, ResNet101, ResNet152, Simple +from ..models import LeNet5, ResNet18, ResNet34, ResNet50, ResNet101, ResNet152 networks = {'LeNet5' : LeNet5, 'resnet18' : ResNet18, 'resnet34' : ResNet34, 'resnet50' : ResNet50, 'resnet101' : ResNet101, - 'resnet152' : ResNet152, - 'Simple' : Simple} + 'resnet152' : ResNet152} class Trainer(object): def __init__(self, args, model_config): diff --git a/test.py b/test.py index 0d42b6a..1f7b8eb 100644 --- a/test.py +++ b/test.py @@ -21,13 +21,11 @@ 'TD3': TD3 } -model_names = ['LeNet5', 'resnet18', 'resnet34', 'resnet50', 'resnet100', 'resnet152'] parser = argparse.ArgumentParser(description = 'Test RL Agent') # Model Parameters -parser.add_argument('--model-name', default='resnet18', metavar='ARCH', choices=model_names, - help = 'model_architecture: ' + ' | '.join(model_names) + ' (default: resnet18)') +parser.add_argument('--model-name', default='resnet18', help = 'Target model name') parser.add_argument('--model-path', default = None, help = 'Path to pretrained model') # Dataset Parameters diff --git a/train.py b/train.py index c014873..c64e11f 100644 --- a/train.py +++ b/train.py @@ -23,13 +23,10 @@ 'TD3': TD3 } -model_names = ['LeNet5', 'resnet18', 'resnet34', 'resnet50', 'resnet100', 'resnet152'] - parser = argparse.ArgumentParser(description = 'Train RL Agent') # Model Parameters -parser.add_argument('--model-name', default='resnet18', metavar='ARCH', choices=model_names, - help = 'model_architecture: ' + ' | '.join(model_names) + ' (default: resnet18)') +parser.add_argument('--model-name', default='resnet18', help = 'Target model name') parser.add_argument('--model-path', required = True, default = None, help = 'Path to pretrained model') # Dataset Parameters