Skip to content

Commit

Permalink
Merge pull request #24 from aisingapore/0.3.0-pytorch-abstract
Browse files Browse the repository at this point in the history
Abstracting Pytorch example, refactoring documentation (Check #24 for more details)
  • Loading branch information
Syakyr authored Mar 11, 2024
2 parents 0610e33 + 21ce563 commit 672ac8b
Show file tree
Hide file tree
Showing 91 changed files with 4,137 additions and 1,683 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,31 @@ $ cookiecutter --replay-file cookiecutter.json \

### Input Parameters

| Parameter | Detail | Default | Regex Reference |
|------------------------- |--------------------------------------------------------------------------------------------------------------------------------- |---------------------------------------------------------------------------- |-------------------------------------------------------------------------------------------------------------------- |
| Parameter | Detail | Default | Regex Reference |
|------------------------- |--------------------------------------------------------------------------------------------------------------------------------- |---------------------------------------------------------------------------- |-------------------------------------------------------------------------------------------- |
| `project_name` | Name of project that will be the header for the `README.md`. Input to start with alphabet. Only whitespace as separators. | My Project | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L8) |
| `description` | A short description of the project that will be populated in `README.md`. Max of 72 characters. | A short description of the project. | NIL |
| `description` | A short description of the project that will be populated in `README.md`. Max of 72 characters. | A short description of the project. | NIL |
| `repo_name` | Name of the repository folder. Input to start with alphabet characters. No whitespaces or underscores are allowed. | `project_name` where whitespaces and underscores are replaced with hyphens. | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L13) |
| `src_package_name` | Name of the source code's package under `src`. Input to start with alphabet characters. No whitespaces or hyphens are allowed. | `repo_name` where hyphens are replaced with underscores. | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L16) |
| `src_package_name_short` | The alias for the source code's package. Input to start with alphabet characters. No whitespaces or hyphens are allowed. | `src_package_name` | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L19) |
| `platform` | The platform the project is running on. (Choose between "on-premise" or "Google Cloud Platform") | `onprem` or `gcp` | NIL |
| `orchestrator` | The orchestrator the project is using. (Choose between "Run:AI", "Polyaxon" or "No orchestrator") | `runai` or `polyaxon` or `none` | NIL |
| `proj_name` | The project name used in by the repository. If you're using Run:AI, this will be the Run:AI project name used by the repository. | `sample-project` | NIL |
| `platform` | The platform the project is running on. (Choose between "on-premise" or "Google Cloud Platform") | `onprem` or `gcp` | NIL |
| `orchestrator` | The orchestrator the project is using. (Choose between "Run:AI", "Polyaxon" or "No orchestrator") | `runai`, `polyaxon` or `none` | NIL |
| `proj_name` | The project name used in by the repository. If you're using Run:AI, this will be the Run:AI project name used by the repository. | `sample-project` | NIL |
| `registry_project_path` | Path of the registry repository for your container images to be located under. Cannot end with a slash character. | `registry.domain.tld/sample-project/my-project` | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L22) |
| `problem_template` | Initialise the repository with a default problem statement | `base`, `cv`, `nlp` or `tabular` | NIL |
| `author_name` | Your alias or project team's name. Relatively arbitrary. No hyphens are allowed. | `AISG` | [Link](https://github.com/aisingapore/kapitan-hull/blob/main/hooks/pre_gen_project.py#L25) |

### Version Control

Following the creation of your repository, initialise it with Git, push
it to a remote, and follow its `README.md` document for a full guide on
its usage.

## Note on AMD GPUs

Those who plan to use AMD GPUs and RoCM can check the `rocm` folder and
copy the contents into the `{{cookiecutter.repo_name}}` folder before
populating your template. This is experimental, so official support for
this should not be expected any time soon. This is also not added to
the main template to reduce the confusion of having multiple file
variants for the users.
5 changes: 3 additions & 2 deletions cookiecutter-gcp-runai.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"src_package_name_short": "khgr_test",
"platform": "gcp",
"orchestrator": "runai",
"proj_name": "mlops-test",
"registry_project_path": "registry.aisingapore.net/mlops/kapitan-hull-gcp-runai",
"proj_name": "test-proj",
"registry_project_path": "asia-southeast1-docker.pkg.dev/infratest-311303/mlops",
"problem_template": "cv",
"author_name": "mlops"
}
}
1 change: 1 addition & 0 deletions cookiecutter-onprem-runai.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"orchestrator": "runai",
"proj_name": "mlops-test",
"registry_project_path": "registry.aisingapore.net/mlops/kapitan-hull-onprem-runai",
"problem_template": "cv",
"author_name": "mlops"
}
}
12 changes: 10 additions & 2 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"orchestrator": ["runai", "polyaxon", "none"],
"proj_name": "sample-project",
"registry_project_path": "registry.domain.tld/sample-project/my-project",
"problem_template": ["base", "cv", "nlp", "tabular"],
"author_name": "AISG",
"__prompts__": {
"project_name": "project_name - Name of project that will be the header for the README.md. Input to start with alphabet. Only whitespace as separators.",
Expand All @@ -18,7 +19,7 @@
"platform": {
"__prompt__": "platform - The platform the project is running on.",
"onprem": "onprem - On premise",
"gcp": "gcp - Google Cloud Platform (not implemented yet)"
"gcp": "gcp - Google Cloud Platform"
},
"orchestrator": {
"__prompt__": "orchestrator - The orchestrator the project is using.",
Expand All @@ -28,6 +29,13 @@
},
"proj_name": "proj_name - The project name used in by the repository. If you're using Run:AI, this will be the Run:AI project name used by the repository.",
"registry_project_path": "registry_project_path - Path of the registry repository for your container images to be located under. Cannot end with a slash character.",
"problem_template": {
"__prompt__": "problem_template - Initialise the repository with a default problem statement.",
"base": "base - Base template with no model packages",
"cv": "cv - Computer Vision (MNIST with Pytorch)",
"nlp": "nlp - Natural Language Processing (not implemented yet)",
"tabular": "tabular - Tabular data (not implemented yet)"
},
"author_name": "author_name - Your alias or project team's name. Relatively arbitrary. No hyphens are allowed."
}
}
}
88 changes: 88 additions & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import shutil
import os


def populate_problem(problem_domain: str) -> None:

SUB_DIRS = ["src", "conf", "notebooks", "aisg-context"]

for subdir in SUB_DIRS:

working_dir = os.getcwd()
src_dir = os.path.join(working_dir, "problem-templates", problem_domain)
shutil.copytree(
os.path.join(src_dir, subdir),
os.path.join(working_dir, subdir),
dirs_exist_ok=True
)
shutil.copy2(
os.path.join(src_dir, "{{cookiecutter.repo_name}}-conda-env.yaml"),
os.path.join(working_dir, "{{cookiecutter.repo_name}}-conda-env.yaml")
)


def generate_template_scripts() -> None:

PROBLEM_TEMPLATE = "{{cookiecutter.problem_template}}"

match PROBLEM_TEMPLATE:
case "base":
pass
case "cv":
populate_problem("cv")
case "nlp":
populate_problem("nlp")
case "tabular":
populate_problem("tabular")
case _:
raise ValueError(f"{PROBLEM_TEMPLATE} is not a valid problem template.")
shutil.rmtree(os.path.join(os.getcwd(), "problem-templates"))


def remove_redundant_files() -> None:

PLATFORM = "{{cookiecutter.platform}}"
ORCH = "{{cookiecutter.orchestrator}}"

BANNER_PATH = os.path.join(
os.getcwd(), "aisg-context", "guide-site", "docs"
)
BANNERS = [
'kapitan-hull-eptg-gcp-runai-banner.png',
'kapitan-hull-eptg-onprem-runai-banner.png'
]
WORKFLOW_HTML_PATH = os.path.join(
BANNER_PATH, 'guide-for-user', 'assets'
)
WORKFLOWS_HTML = [
'aisg-e2e-mlops-gcp-runai-workflow-components_jul2023.html',
'aisg-e2e-mlops-onprem-runai-workflow-components_jul2023.html',
]
WORKFLOW_PNG_PATH = os.path.join(
WORKFLOW_HTML_PATH, 'images'
)
WORKFLOWS_PNG = [
'aisg-e2e-mlops-gcp-runai-workflow-components_jul2023.png',
'aisg-e2e-mlops-onprem-runai-workflow-components_jul2023.png'
]

# Remove all banners that doesn't have both PLATFORM and ORCH
for path, item in zip(
[BANNER_PATH, WORKFLOW_HTML_PATH, WORKFLOW_PNG_PATH],
[BANNERS, WORKFLOWS_HTML, WORKFLOWS_PNG]
):
[os.remove(
os.path.join(path, x)) for x in item
if not (PLATFORM in x and ORCH in x
)]

# Remove runai yaml files if ORCH != runai
RUNAI_YAML_PATH = os.path.join(
os.getcwd(), "aisg-context", "runai"
)
if ORCH != 'runai':
shutil.rmtree(RUNAI_YAML_PATH)

if __name__ == "__main__":
generate_template_scripts()
remove_redundant_files()
69 changes: 46 additions & 23 deletions hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@
"src_package_name_short": {
"user_input": "{{cookiecutter.src_package_name_short}}",
"regex": r"^[a-z](?:_?[a-z0-9]+)*$"},
"platform": {
"user_input": "{{cookiecutter.platform}}",
"avail": ["onprem", "gcp"]},
"orchestrator": {
"user_input": "{{cookiecutter.orchestrator}}",
"avail": ["runai"]},
"registry_project_path": {
"user_input": "{{cookiecutter.registry_project_path}}",
"regex": r"^(https?:\/\/)?([a-zA-Z0-9.-]+(:[a-zA-Z0-9.-]+)?@)?([a-zA-Z0-9.-]+)(:[0-9]+)?\/([a-zA-Z0-9._-]+\/)*([a-zA-Z0-9._-]+)(:[a-zA-Z0-9._-]+)?$"},
"problem_template": {
"user_input": "{{cookiecutter.problem_template}}",
"avail": ["base", "cv"]},
"author_name": {
"user_input": "{{cookiecutter.author_name}}",
"regex": r"^[a-zA-Z](?:_?[a-zA-Z0-9]+)*$"}
Expand All @@ -30,6 +39,8 @@

def check_input_length(cookie_input_key, cookie_input_val):

global ERROR_MSG_LIST

input_val = cookie_input_val["user_input"].strip()
if len(input_val) not in range(1, 73):
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not of valid length (1 to 72)."
Expand All @@ -38,41 +49,53 @@ def check_input_length(cookie_input_key, cookie_input_val):

def check_input_regex(cookie_input_key, cookie_input_val):

if not re.match(cookie_input_val["regex"], cookie_input_val["user_input"]):

if cookie_input_key == "project_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid project name. Please use only alphanumeric characters."
% (cookie_input_key, cookie_input_val["user_input"]))

if cookie_input_key == "repo_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid repository name. Only alphanumeric characters and hyphens are permitted."
% (cookie_input_key, cookie_input_val["user_input"]))

if cookie_input_key in ["src_package_name", "src_package_name_short"]:
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid Python package name."
% (cookie_input_key, cookie_input_val["user_input"]))

if cookie_input_key == "registry_project_path":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid container registry path."
% (cookie_input_key, cookie_input_val["user_input"]))
global ERROR_MSG_LIST

if cookie_input_key == "author_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid name."
% (cookie_input_key, cookie_input_val["user_input"]))
if not re.match(cookie_input_val["regex"], cookie_input_val["user_input"]):
match cookie_input_key:
case "project_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid project name. Please use only alphanumeric characters."
% (cookie_input_key, cookie_input_val["user_input"]))
case "repo_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid repository name. Only alphanumeric characters and hyphens are permitted."
% (cookie_input_key, cookie_input_val["user_input"]))
case "src_package_name" | "src_package_name_short":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid Python package name."
% (cookie_input_key, cookie_input_val["user_input"]))
case "registry_project_path":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid container registry path."
% (cookie_input_key, cookie_input_val["user_input"]))
case "author_name":
ERROR_MSG_LIST.append("ERROR: %s - '%s' is not a valid name."
% (cookie_input_key, cookie_input_val["user_input"]))


def check_not_implemented(cookie_input_key, cookie_input_val):

global ERROR_MSG_LIST

if cookie_input_val["user_input"] not in cookie_input_val["avail"]:
ERROR_MSG_LIST.append("ERROR: %s - '%s' has not been implemented."
% (cookie_input_key, cookie_input_val["user_input"]))


def check_cookiecutter_inputs():

for cookie_input_key, cookie_input_val in COOKIE_INPUTS.items():
global COOKIE_INPUTS
global ERROR_MSG_LIST

for cookie_input_key, cookie_input_val in COOKIE_INPUTS.items():
check_input_length(cookie_input_key, cookie_input_val)
if "regex" in cookie_input_val:
check_input_regex(cookie_input_key, cookie_input_val)
if "avail" in cookie_input_val:
check_not_implemented(cookie_input_key, cookie_input_val)

if len(ERROR_MSG_LIST) > 0:

for error_msg in ERROR_MSG_LIST:
print(error_msg)
sys.exit(1)

check_cookiecutter_inputs()

if __name__ == "__main__":
check_cookiecutter_inputs()
55 changes: 55 additions & 0 deletions rocm/docker/{{cookiecutter.repo_name}}-gpu-rocm.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
FROM rocm/dev-ubuntu-20.04:5.7.1

ARG DEBIAN_FRONTEND="noninteractive"

ARG NON_ROOT_USER="aisg"
ARG NON_ROOT_UID="2222"
ARG NON_ROOT_GID="2222"
ARG HOME_DIR="/home/${NON_ROOT_USER}"

ARG REPO_DIR="."
ARG CONDA_ENV_FILE="{{cookiecutter.repo_name}}-rocm-conda-env.yaml"
ARG CONDA_ENV_NAME="{{cookiecutter.repo_name}}"

# Miniconda arguments
ARG CONDA_HOME="/miniconda3"
ARG CONDA_BIN="${CONDA_HOME}/bin/conda"
ARG CONDA_VER="py310_23.5.2-0"
ARG CONDA_ARCH="Linux-x86_64"
ARG MINICONDA_SH="Miniconda3-${CONDA_VER}-${CONDA_ARCH}.sh"

RUN useradd -l -m -s /bin/bash -u ${NON_ROOT_UID} ${NON_ROOT_USER} && \
mkdir -p ${CONDA_HOME} && \
chown -R ${NON_ROOT_USER}:${NON_ROOT_GID} ${CONDA_HOME}

RUN apt-get update && \
apt-get -y install bzip2 curl wget gcc rsync git vim locales && \
sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
locale-gen && \
dpkg-reconfigure --frontend=noninteractive locales && \
update-locale LANG=en_US.UTF-8 && \
apt-get clean

ENV PYTHONIOENCODING utf8
ENV LANG "C.UTF-8"
ENV LC_ALL "C.UTF-8"
ENV HIP_VISIBLE_DEVICES 0
ENV LD_LIBRARY_PATH /opt/rocm/lib

USER ${NON_ROOT_USER}
WORKDIR ${HOME_DIR}

COPY --chown=${NON_ROOT_USER}:${NON_ROOT_GID} ${REPO_DIR} {{cookiecutter.repo_name}}

# Install Miniconda
RUN curl -O https://repo.anaconda.com/miniconda/${MINICONDA_SH} && \
chmod +x ${MINICONDA_SH} && \
./${MINICONDA_SH} -u -b -p ${CONDA_HOME} && \
rm ${MINICONDA_SH}
ENV PATH ${CONDA_HOME}/bin:${HOME_DIR}/.local/bin:$PATH

# Install conda environment
RUN ${CONDA_BIN} env create -f {{cookiecutter.repo_name}}/${CONDA_ENV_FILE} && \
${CONDA_BIN} init bash && \
${CONDA_BIN} clean -a -y && \
echo "source activate ${CONDA_ENV_NAME}" >> "${HOME_DIR}/.bashrc"
25 changes: 25 additions & 0 deletions rocm/{{cookiecutter.repo_name}}-rocm-conda-env.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: {{cookiecutter.repo_name}}-rocm
channels:
- defaults
- pytorch
- conda-forge
dependencies:
- python=3.11.7
- pip=23.3.2
- pip:
- mlflow-skinny==2.9.2
- hydra-core==1.3.2
- hydra-optuna-sweeper==1.2.0
- python-json-logger==2.0.7
- fastapi==0.109.0
- uvicorn[standard]==0.25.0
- python-multipart==0.0.6
- jsonlines==4.0.0
- pandas==2.1.4
- gunicorn==21.2.0
- pydantic==2.5.3
- pydantic-settings==2.1.0
- ipykernel==6.25.0
- https://download.pytorch.org/whl/rocm5.7/torch-2.2.1%2Brocm5.7-cp311-cp311-linux_x86_64.whl
- https://download.pytorch.org/whl/rocm5.7/torchvision-0.17.1%2Brocm5.7-cp311-cp311-linux_x86_64.whl
- https://download.pytorch.org/whl/pytorch_triton_rocm-2.2.0-cp311-cp311-linux_x86_64.whl
Loading

0 comments on commit 672ac8b

Please sign in to comment.