From 0b6f994099372d2f74c646d917a85a770542c6a2 Mon Sep 17 00:00:00 2001 From: Miguel Pineda <110496466+ma-pineda@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:43:28 -0600 Subject: [PATCH 01/56] Adding classical-ml and data-analytics presets (#255) Signed-off-by: Tyler Titsworth Signed-off-by: tylertitsworth Co-authored-by: Tyler Titsworth Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: jafraustro --- .github/workflows/container-ci.yaml | 9 +- .github/workflows/integration-test.yaml | 2 +- preset/classical-ml/Dockerfile | 128 ++--- preset/classical-ml/docker-compose.yaml | 82 ++- preset/classical-ml/requirements.txt | 2 +- preset/classical-ml/tests/scikit/kmeans.py | 1 + .../classical-ml/tests/scikit/test_scikit.sh | 4 +- preset/data-analytics/Dockerfile | 115 ++-- preset/data-analytics/docker-compose.yaml | 66 +-- preset/data-analytics/requirements.txt | 2 +- preset/deep-learning/Dockerfile | 527 ++++++++---------- preset/deep-learning/docker-compose.yaml | 201 +++---- preset/deep-learning/requirements.txt | 15 +- preset/deep-learning/tests.yaml | 33 +- preset/inference-optimization/Dockerfile | 39 +- .../docker-compose.yaml | 200 +++---- .../inference-optimization/requirements.txt | 19 +- preset/inference-optimization/tests.yaml | 45 +- 18 files changed, 709 insertions(+), 781 deletions(-) mode change 100644 => 100755 preset/deep-learning/tests.yaml diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 08669ffa..6dbf8532 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -63,7 +63,7 @@ jobs: setup-build: outputs: matrix: ${{ steps.build-matrix.outputs.matrix }} - runs-on: ubuntu-latest # ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} + runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 @@ -79,13 +79,14 @@ jobs: build-containers: needs: [setup-build] env: ${{ matrix }} - runs-on: ubuntu-latest # ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} + runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} strategy: matrix: ${{ fromJson(needs.setup-build.outputs.matrix) }} fail-fast: false outputs: group: ${{ steps.build-group.outputs.container-group }} steps: + - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 if: ${{ !inputs.no_build }} - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 @@ -111,7 +112,7 @@ jobs: setup-scan: needs: [build-containers] if: ${{ github.event_name == 'pull_request' }} - runs-on: ubuntu-latest # ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} + runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} outputs: matrix: ${{ steps.scan-matrix.outputs.matrix }} steps: @@ -164,7 +165,7 @@ jobs: #################################################################################################### setup-test: needs: [build-containers] - runs-on: ubuntu-latest # ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} + runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} outputs: matrix: ${{ steps.test-matrix.outputs.matrix }} steps: diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index af6f4cc2..2a102efd 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -113,7 +113,7 @@ jobs: path: output.txt recreate: true status-check: - needs: [group-diff, pipeline-ci] + needs: [group-diff, pipeline-ci, merge-logs] runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} if: always() steps: diff --git a/preset/classical-ml/Dockerfile b/preset/classical-ml/Dockerfile index a9666e3a..bd6cebde 100644 --- a/preset/classical-ml/Dockerfile +++ b/preset/classical-ml/Dockerfile @@ -12,40 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. + ARG BASE_IMAGE="ubuntu" ARG BASE_TAG="22.04" -FROM ${BASE_IMAGE}:${BASE_TAG} as classical-ml-base +FROM ${BASE_IMAGE}:${BASE_TAG} as classical-ml ENV DEBIAN_FRONTEND=noninteractive -# See http://bugs.python.org/issue19846 - ENV LANG=C.UTF-8 SHELL ["/bin/bash", "-c"] RUN apt-get update -y && \ apt-get install -y --no-install-recommends --fix-missing \ - bzip2 \ - ca-certificates \ - diffutils \ - gcc \ - git \ - gzip \ - make \ - patch \ - rsync \ - unzip \ - wget \ - xz-utils && \ + bzip2 \ + ca-certificates \ + diffutils \ + gcc \ + git \ + gzip \ + make \ + patch \ + rsync \ + unzip \ + wget \ + xz-utils && \ rm -rf /var/lib/apt/lists/* -FROM classical-ml-base as classical-ml-python - -# Setting up non-root directories RUN useradd --uid 1000 -d /home/dev -s /bin/bash -m dev -# Set a password for the user (Optional) RUN echo 'dev:password' | chpasswd USER dev WORKDIR /home/dev @@ -56,68 +51,69 @@ ARG PYTHON_VERSION ARG IDP_VERSION ARG INTEL_CHANNEL -RUN wget --progress=dot:giga --no-check-certificate https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-${MINIFORGE_VERSION}.sh -O miniforge.sh && \ +RUN wget --progress=dot:giga --no-check-certificate https://github.com/conda-forge/miniforge/releases/latest/download/${MINIFORGE_VERSION}.sh -O miniforge.sh && \ chmod +x miniforge.sh && \ ./miniforge.sh -b -p "${CONDA_ROOT}" && \ rm ./miniforge.sh && \ - ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge3" && \ + ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge" && \ export PATH="${CONDA_ROOT}/bin/:${PATH}" && \ - conda update -y conda && \ - conda config --add channels conda-forge && \ - conda config --add channels https://software.repos.intel.com/python/conda/ && \ conda init --all && \ conda install -y \ - 'jupyterlab>=4.1.8' \ - 'notebook>=7.1.3' \ - 'jupyterhub>=4.1.5' \ - 'jupyter-server-proxy>=4.1.2' \ - 'mako>=1.2.2' \ - 'pyjwt>=2.4.0' \ - 'cryptography>=42.0.5' \ - 'nodejs>=20.12.2' \ - 'aiohttp>=3.9.4' \ + 'colorama==0.4.6' \ + 'conda==24.5.0' \ + 'jupyterhub==5.1.0' \ + 'jupyter-server-proxy==4.3.0' \ + 'mamba==1.5.8' \ + 'networkx==3.3' \ + 'notebook==7.2.1' \ + 'pip==24.0' \ + 'python==3.10.14' \ 'idna>=3.7' \ - 'oauthlib>=3.2.2' \ - && \ - jupyter labextension disable "@jupyterlab/apputils-extension:announcements" && \ - conda clean -y --all + 'requests>=2.32.0' \ + 'setuptools>=70.0.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + 'nodejs==22.5.1' \ + && \ + jupyter labextension disable "@jupyterlab/apputils-extension:announcements" \ + && \ + conda clean -y --all \ + && \ + conda config --add channels ${INTEL_CHANNEL} ENV PATH ${CONDA_ROOT}/condabin:${CONDA_ROOT}/bin/:${PATH} +RUN conda config --set pip_interop_enabled True ARG IDP_VERSION +ARG DAAL4PY_VERSION ARG DPNP_VERSION ARG XGBOOST_VERSION ARG MODIN_VERSION ARG NUMPY_VERSION ARG SKLEARNEX_VERSION -# Conda packages -RUN conda create -yn classical-ml -c ${INTEL_CHANNEL} -c conda-forge \ - dpnp=${DPNP_VERSION} \ - numpy=${NUMPY_VERSION} \ - python=${PYTHON_VERSION} \ - scikit-learn-intelex==${SKLEARNEX_VERSION} \ - xgboost=${XGBOOST_VERSION} \ - modin-ray=${MODIN_VERSION} \ - 'python-dotenv>=1.0.1' \ - 'tqdm>=4.66.2' \ - 'matplotlib-base>=3.4.3' \ - 'threadpoolctl>=3.3.0' \ - 'ipython>=8.18.1' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'protobuf>=4.24' \ - 'pillow>=10.2.0' \ - 'tornado>=6.3.3' && \ +RUN conda create -yn classical-ml \ + "python=${PYTHON_VERSION}" \ + "daal4py=${DAAL4PY_VERSION}" \ + "dpnp=${DPNP_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base==3.8.4' \ + "modin-ray=${MODIN_VERSION}" \ + 'python-dotenv==1.0.1' \ + "scikit-learn-intelex=${SKLEARNEX_VERSION}" \ + 'tqdm==4.66.4' \ + "xgboost=${XGBOOST_VERSION}" \ + 'idna>=3.7' \ + 'requests>=2.32.0' \ + 'setuptools>=70.0.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + && \ conda clean -y --all - - -# PyPI packages RUN conda run -n classical-ml python -m pip install --no-deps --no-cache-dir \ - 'dataset-librarian==1.0.4' \ - 'cloud-data-connector==1.0.3' - + 'dataset-librarian==1.0.4' ENV PYTHONSTARTUP=~/.patch_sklearn.py COPY base/.patch_sklearn.py ~/.patch_sklearn.py @@ -125,8 +121,6 @@ COPY base/.patch_sklearn.py ~/.patch_sklearn.py ENV PYTHONSTARTUP=/home/dev/.patch_sklearn.py COPY base/.patch_sklearn.py /home/dev/.patch_sklearn.py -FROM classical-ml-python as classical-ml-jupyter - EXPOSE 8888 RUN mkdir -p ~/jupyter/ && chmod -R a+rwx ~/jupyter/ && \ @@ -136,10 +130,10 @@ WORKDIR /home/dev COPY --chown=dev notebooks /home/dev/jupyter COPY --chown=dev tests /home/dev/sample-tests -RUN "${CONDA_ROOT}/envs/classical-ml/bin/python" -m ipykernel install --user --name classical-ml --display-name "Classical ML" && \ - "${CONDA_ROOT}/envs/classical-ml/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo classical-ml | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - "${CONDA_ROOT}/envs/classical-ml/bin/python" -m ipykernel.kernelspec --user && \ - conda clean -y --all +RUN KERNEL_DIR="${CONDA_ROOT}/share/jupyter/kernels/classical-ml" && \ + conda run -n classical-ml python -m ipykernel install --prefix "$CONDA_ROOT" --name classical-ml --display-name "Classical ML" && \ + conda run -n classical-ml kernda -o -y "$KERNEL_DIR/kernel.json" && \ + conda run -n base jupyter kernelspec list CMD ["bash", "-c", "source activate classical-ml && jupyter lab --notebook-dir=~/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/preset/classical-ml/docker-compose.yaml b/preset/classical-ml/docker-compose.yaml index a6e06fbd..c2dc9c1a 100644 --- a/preset/classical-ml/docker-compose.yaml +++ b/preset/classical-ml/docker-compose.yaml @@ -15,6 +15,7 @@ # -*- coding: utf-8 -*- # + version: '3' services: classical-ml: @@ -22,28 +23,30 @@ services: args: BASE_IMAGE: ${BASE_IMAGE:-ubuntu} BASE_TAG: ${BASE_TAG:-22.04} - DPNP_VERSION: ${NUMBA_DPEX_VERSION:-0.14.0} - IDP_VERSION: ${IDP_VERSION:-2024.1.0} + DAAL4PY_VERSION: ${DAAL4PY_VERSION:-2024.5.0} + DPNP_VERSION: ${DPNP_VERSION:-0.15.0} + IDP_VERSION: ${IDP_VERSION:-2024.2} INTEL_CHANNEL: ${INTEL_CHANNEL:-https://software.repos.intel.com/python/conda/} - MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} - MODIN_VERSION: ${MODIN_VERSION:-0.26.1} - MPI_VERSION: ${MPI_VERSION:-2021.12.0} - NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.22.1} + MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Miniforge3-Linux-x86_64} + MODIN_VERSION: ${MODIN_VERSION:-0.30.0} + MPI_VERSION: ${MPI_VERSION:-2021.13} + NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.23.0} NUMPY_VERSION: ${NUMPY_VERSION:-1.26.4} - PYTHON_VERSION: ${PYTHON_VERSION:-3.10} - SKLEARNEX_VERSION: ${SKLEARNEX_VERSION:-2024.2.0} + PYTHON_VERSION: ${PYTHON_VERSION:-3.9} + SKLEARNEX_VERSION: ${SKLEARNEX_VERSION:-2024.5.0} XGBOOST_VERSION: ${XGBOOST_VERSION:-2.0.3} http_proxy: ${http_proxy} https_proxy: ${https_proxy} no_proxy: '' context: . + target: classical-ml labels: docs: classical_ml org.opencontainers.image.title: "Intel® AI Tools Selector Preset Containers - Classical ML" org.opencontainers.base.name: "ubuntu:22.04" org.opencontainers.image.name: "intel/classical-ml" - org.opencontainers.image.version: 2024.1.0-py${PYTHON_VERSION:-3.10} - dependency.python: ${PYTHON_VERSION:-3.10} + org.opencontainers.image.version: 2024.2.0-py${PYTHON_VERSION:-3.9} + dependency.python: ${PYTHON_VERSION:-3.9} dependency.python.pip: requirements.txt dependency.apt.bzip2: true dependency.apt.ca-certificates: true @@ -57,39 +60,26 @@ services: dependency.apt.unzip: true dependency.apt.wget: true dependency.apt.xz-utils: true - dependency.conda.jupyterlab: '>=4.1.8' - dependency.conda.notebook: '>=7.1.3' - dependency.conda.jupyterhub: '>=4.1.5' - dependency.conda.jupyter-server-proxy: '>=4.1.2' - dependency.conda.mako: '>=1.2.2' - dependency.conda.pyjwt: '>=2.4.0' - dependency.conda.cryptography: '>=42.0.5' - dependency.conda.nodejs: '>=20.12.2' - dependency.conda.aiohttp: '>=3.9.4' - dependency.conda.idna: '>=3.7' - dependency.conda.oauthlib: '>=3.2.2' - dependency.conda.dpnp: '>=0.14.0' - dependency.conda.numpy: '>=1.26.4' - dependency.conda.python: "=${PYTHON_VERSION:-3.10}" - dependency.conda.scikit-learn-intelex: '>=2024.2.0' - dependency.conda.xgboost: '>=2.0.3' - dependency.conda.modin-ray: '>=0.26.1' - dependency.conda.python-dotenv: '>=1.0.1' - dependency.conda.tqdm: '>=4.66.2' - dependency.conda.matplotlib-base: '>=3.4.3' - dependency.conda.dataset_librarian: '>=1.0.4' - dependency.conda.threadpoolctl: '>=3.3.0' - dependency.conda.ipython: '>=8.18.1' - dependency.conda.ipykernel: '>=6.29.3' - dependency.conda.kernda: '>=0.3.0' - dependency.conda.protobuf: '>=4.24' - dependency.conda.pillow: '>=10.2.0' - dependency.conda.tornado: '>=6.3.3' - target: classical-ml-jupyter - command: | - bash -c "conda run -n classical-ml python -c 'import sklearn; import xgboost; print(\"SciKit:\", sklearn.__version__, \" XGBoost:\",xgboost.__version__)' && \ - conda run -n classical-ml python -c 'import modin.pandas as pd, modin.config as cfg; cfg.Engine.put(\"Ray\"); df = pd.DataFrame([1]);print(df+1)'" - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-2024.1.0-py${PYTHON_VERSION:-3.10} + dependency.conda.colorama: '==0.4.6' + dependency.conda.conda: '==24.5.0' + dependency.conda.daal4py: '=2024.5.0' + dependency.conda.dpnp: '=0.15.0' + dependency.conda.ipykernel: '==6.29.5' + dependency.conda.jupyterhub: '==5.1.0' + dependency.conda.jupyter-server-proxy: '==4.3.0' + dependency.conda.kernda: '==0.3.0' + dependency.conda.mamba: '==1.5.8' + dependency.conda.matplotlib-base: '==3.8.4' + dependency.conda.modin-ray: '=0.30.0' + dependency.conda.networkx: '==3.3' + dependency.conda.notebook: '==7.2.1' + dependency.conda.pip: '==24.0' + dependency.conda.python: '==3.10.14' + dependency.conda.python-dotenv: '==1.0.1' + dependency.conda.scikit-learn-intelex: '=2024.5.0' + dependency.conda.tqdm: '==4.66.4' + dependency.conda.xgboost: '=2.0.3' + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} environment: http_proxy: ${http_proxy} https_proxy: ${https_proxy} @@ -97,3 +87,9 @@ services: shm_size: 12GB volumes: - /dev/dri/by-path:/dev/dri/by-path + command: > + bash -c " conda run -n classical-ml python -c 'import sklearn;import xgboost;print(\"SciKit:\", + sklearn.__version__, \" XGBoost:\", xgboost.__version__)' && + + conda run -n classical-ml python -c 'import modin.pandas as pd;import modin.config + as cfg;cfg.Engine.put(\"Ray\");df = pd.DataFrame([1]);print(df+1)' " diff --git a/preset/classical-ml/requirements.txt b/preset/classical-ml/requirements.txt index d231202d..8fe3dfff 100644 --- a/preset/classical-ml/requirements.txt +++ b/preset/classical-ml/requirements.txt @@ -1 +1 @@ -cloud-data-connector==1.0.3 +dataset-librarian==1.0.4 diff --git a/preset/classical-ml/tests/scikit/kmeans.py b/preset/classical-ml/tests/scikit/kmeans.py index 9120b7d0..c78acba7 100644 --- a/preset/classical-ml/tests/scikit/kmeans.py +++ b/preset/classical-ml/tests/scikit/kmeans.py @@ -62,6 +62,7 @@ data, labels = load_digits(return_X_y=True) (n_samples, n_features), n_digits = data.shape, np.unique(labels).size +data = np.array(data, dtype=np.float64) print(f"# digits: {n_digits}; # samples: {n_samples}; # features {n_features}") diff --git a/preset/classical-ml/tests/scikit/test_scikit.sh b/preset/classical-ml/tests/scikit/test_scikit.sh index a6b2f24e..9d16e938 100755 --- a/preset/classical-ml/tests/scikit/test_scikit.sh +++ b/preset/classical-ml/tests/scikit/test_scikit.sh @@ -14,8 +14,8 @@ # limitations under the License. set -xe + SCRIPT_DIR=$(dirname "$0") python "${SCRIPT_DIR}/kmeans.py" - -python "${SCRIPT_DIR}/kmeans.py" true +python "${SCRIPT_DIR}/kmeans.py" true # Enable intel opt diff --git a/preset/data-analytics/Dockerfile b/preset/data-analytics/Dockerfile index 37954c83..ffb56ceb 100644 --- a/preset/data-analytics/Dockerfile +++ b/preset/data-analytics/Dockerfile @@ -12,107 +12,100 @@ # See the License for the specific language governing permissions and # limitations under the License. + ARG BASE_IMAGE="ubuntu" ARG BASE_TAG="22.04" -FROM ${BASE_IMAGE}:${BASE_TAG} as data-analytics-base +FROM ${BASE_IMAGE}:${BASE_TAG} as data-analytics ENV DEBIAN_FRONTEND=noninteractive -# See http://bugs.python.org/issue19846 - ENV LANG=C.UTF-8 SHELL ["/bin/bash", "-c"] RUN apt-get update -y && \ apt-get install -y --no-install-recommends --fix-missing \ - bzip2 \ - ca-certificates \ - diffutils \ - gcc \ - git \ - gzip \ - make \ - patch \ - rsync \ - unzip \ - wget \ - xz-utils && \ + bzip2 \ + ca-certificates \ + diffutils \ + gcc \ + git \ + gzip \ + make \ + patch \ + rsync \ + unzip \ + wget \ + xz-utils && \ rm -rf /var/lib/apt/lists/* -FROM data-analytics-base as data-analytics-python - -# Setting up non-root directories RUN useradd --uid 1000 -d /home/dev -s /bin/bash -m dev -# Set a password for the user (Optional) RUN echo 'dev:password' | chpasswd USER dev WORKDIR /home/dev ENV CONDA_ROOT=/home/dev/conda - ARG MINIFORGE_VERSION ARG PYTHON_VERSION ARG IDP_VERSION ARG INTEL_CHANNEL -RUN wget --progress=dot:giga --no-check-certificate "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-${MINIFORGE_VERSION}.sh" -O miniforge.sh && \ +RUN wget --progress=dot:giga --no-check-certificate "https://github.com/conda-forge/miniforge/releases/latest/download/${MINIFORGE_VERSION}.sh" -O miniforge.sh && \ chmod +x miniforge.sh && \ ./miniforge.sh -b -p "${CONDA_ROOT}" && \ rm ./miniforge.sh && \ - ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge3" && \ + ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge" && \ export PATH="${CONDA_ROOT}/bin/:${PATH}" && \ - conda update -y conda && \ - conda config --add channels conda-forge && \ - conda config --add channels https://software.repos.intel.com/python/conda/ && \ conda init --all && \ conda install -y \ - 'jupyterlab>=4.1.8' \ - 'notebook>=7.1.3' \ - 'jupyterhub>=4.1.5' \ - 'jupyter-server-proxy>=4.1.2' \ - 'mako>=1.2.2' \ - 'pyjwt>=2.4.0' \ - 'cryptography>=42.0.5' \ - 'nodejs>=20.12.2' \ + 'colorama==0.4.6' \ + 'conda==24.5.0' \ + 'jupyterhub==5.1.0' \ + 'jupyter-server-proxy==4.3.0' \ + 'mamba==1.5.8' \ + 'networkx==3.3' \ + 'notebook==7.2.1' \ + 'python==3.10.14' \ 'idna>=3.7' \ - 'tqdm>=4.66.2' \ - && \ - jupyter labextension disable "@jupyterlab/apputils-extension:announcements" && \ - conda clean -y --all + 'requests>=2.32.0' \ + 'setuptools>=70.0.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + 'nodejs==22.5.1' \ + && \ + jupyter labextension disable "@jupyterlab/apputils-extension:announcements" \ + && \ + conda clean -y --all \ + && \ + conda config --add channels ${INTEL_CHANNEL} ENV PATH ${CONDA_ROOT}/condabin:${CONDA_ROOT}/bin/:${PATH} +RUN conda config --set pip_interop_enabled True ARG IDP_VERSION ARG DPNP_VERSION ARG MODIN_VERSION ARG NUMPY_VERSION -# data-analytics Env - conda packages -RUN conda create -yn data-analytics -c "${INTEL_CHANNEL}" -c conda-forge \ - dpnp="${DPNP_VERSION}" \ - numpy="${NUMPY_VERSION}" \ - python="${PYTHON_VERSION}" \ - modin-ray="${MODIN_VERSION}" \ - 'python-dotenv>=1.0.1' \ - 'tqdm>=4.66.2' \ - 'matplotlib-base>=3.4.3' \ - 'threadpoolctl>=3.3.0' \ - 'ipython>=8.18.1' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'protobuf>=4.24.4' \ - 'pillow>=10.2.0' \ +RUN conda create -yn data-analytics \ + "python=${PYTHON_VERSION}" \ + "dpnp=${DPNP_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base==3.8.4' \ + "modin-ray=${MODIN_VERSION}" \ + 'python-dotenv==1.0.1' \ 'idna>=3.7' \ - 'tornado>=6.3.3' && \ + 'requests>=2.32.0' \ + 'setuptools>=70.0.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + && \ conda clean -y --all RUN conda run -n data-analytics python -m pip install --no-deps --no-cache-dir \ - 'dataset-librarian==1.0.4' \ - 'cloud-data-connector==1.0.3' - -FROM data-analytics-python as data-analytics-jupyter + 'dataset-librarian==1.0.4' EXPOSE 8888 @@ -122,10 +115,10 @@ RUN mkdir -p ~/jupyter/ && chmod -R a+rwx ~/jupyter/ && \ COPY --chown=dev notebooks /home/dev/jupyter COPY --chown=dev tests /home/dev/sample-tests -RUN "${CONDA_ROOT}/envs/data-analytics/bin/python" -m ipykernel install --user --name data-analytics --display-name "Data Analytics" && \ - "${CONDA_ROOT}/envs/data-analytics/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo data-analytics | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - "${CONDA_ROOT}/envs/data-analytics/bin/python" -m ipykernel.kernelspec --user && \ - conda clean -y --all +RUN KERNEL_DIR="${CONDA_ROOT}/share/jupyter/kernels/data-analytics" && \ + conda run -n data-analytics python -m ipykernel install --prefix "$CONDA_ROOT" --name data-analytics --display-name "Data Analytics" && \ + conda run -n data-analytics kernda -o -y "$KERNEL_DIR/kernel.json" && \ + conda run -n base jupyter kernelspec list CMD ["bash", "-c", "source activate data-analytics && jupyter lab --notebook-dir=~/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root"] diff --git a/preset/data-analytics/docker-compose.yaml b/preset/data-analytics/docker-compose.yaml index 99b37f6d..9c00331e 100644 --- a/preset/data-analytics/docker-compose.yaml +++ b/preset/data-analytics/docker-compose.yaml @@ -15,6 +15,7 @@ # -*- coding: utf-8 -*- # + version: '3' services: data-analytics: @@ -22,26 +23,26 @@ services: args: BASE_IMAGE: ${BASE_IMAGE:-ubuntu} BASE_TAG: ${BASE_TAG:-22.04} - DPNP_VERSION: ${NUMBA_DPEX_VERSION:-0.14.0} - IDP_VERSION: ${IDP_VERSION:-2024.1.0} + DPNP_VERSION: ${DPNP_VERSION:-0.15.0} + IDP_VERSION: ${IDP_VERSION:-2024.2} INTEL_CHANNEL: ${INTEL_CHANNEL:-https://software.repos.intel.com/python/conda/} - MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} - MODIN_VERSION: ${MODIN_VERSION:-0.26.1} - MPI_VERSION: ${MPI_VERSION:-2021.12.0} - NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.22.1} + MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Miniforge3-Linux-x86_64} + MODIN_VERSION: ${MODIN_VERSION:-0.30.0} NUMPY_VERSION: ${NUMPY_VERSION:-1.26.4} - PYTHON_VERSION: ${PYTHON_VERSION:-3.10} + PYTHON_VERSION: ${PYTHON_VERSION:-3.9} + XGBOOST_VERSION: ${XGBOOST_VERSION:-2.0.3} http_proxy: ${http_proxy} https_proxy: ${https_proxy} no_proxy: '' context: . + target: data-analytics labels: docs: data_analytics org.opencontainers.image.title: "Intel® AI Tools Selector Preset Containers - Data Analytics" org.opencontainers.base.name: "ubuntu:22.04" org.opencontainers.image.name: "intel/data-analytics" - org.opencontainers.image.version: 2024.1.0-py${PYTHON_VERSION:-3.10} - dependency.python: ${PYTHON_VERSION:-3.10} + org.opencontainers.image.version: 2024.2.0-py${PYTHON_VERSION:-3.9} + dependency.python: ${PYTHON_VERSION:-3.9} dependency.python.pip: requirements.txt dependency.apt.bzip2: true dependency.apt.ca-certificates: true @@ -55,34 +56,21 @@ services: dependency.apt.unzip: true dependency.apt.wget: true dependency.apt.xz-utils: true - dependency.conda.jupyterlab: '>=4.1.8' - dependency.conda.notebook: '>=7.1.3' - dependency.conda.jupyterhub: '>=4.1.5' - dependency.conda.jupyter-server-proxy: '>=4.1.2' - dependency.conda.mako: '>=1.2.2' - dependency.conda.pyjwt: '>=2.4.0' - dependency.conda.cryptography: '>=42.0.5' - dependency.conda.nodejs: '>=20.12.2' - dependency.conda.idna: '>=3.7' - dependency.conda.tqdm: '>=4.66.2' - dependency.conda.dpnp: '>=0.14.0' - dependency.conda.numpy: '>=1.26.4' - dependency.conda.python: "=${PYTHON_VERSION:-3.10}" - dependency.conda.modin-ray: '>=0.26.1' - dependency.conda.python-dotenv: '>=1.0.1' - dependency.conda.matplotlib-base: '>=3.4.3' - dependency.conda.dataset_librarian: '>=1.0.4' - dependency.conda.threadpoolctl: '>=3.3.0' - dependency.conda.ipython: '>=8.18.1' - dependency.conda.ipykernel: '>=6.29.3' - dependency.conda.kernda: '>=0.3.0' - dependency.conda.protobuf: '>=4.24.4' - dependency.conda.pillow: '>=10.2.0' - dependency.conda.tornado: '>=6.3.3' - target: data-analytics-jupyter - command: > - bash -c "conda run -n data-analytics python -c 'import modin.pandas as pd, modin.config as cfg; cfg.Engine.put(\"Ray\"); df = pd.DataFrame([1]);print(df+1)'" - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-2024.1.0-py${PYTHON_VERSION:-3.10} + dependency.conda.colorama: '==0.4.6' + dependency.conda.conda: '==24.5.0' + dependency.conda.dpnp: '=0.15.0' + dependency.conda.ipykernel: '==6.29.5' + dependency.conda.jupyterhub: '==5.1.0' + dependency.conda.jupyter-server-proxy: '==4.3.0' + dependency.conda.kernda: '==0.3.0' + dependency.conda.mamba: '==1.5.8' + dependency.conda.matplotlib-base: '==3.8.4' + dependency.conda.modin-ray: '=0.30.0' + dependency.conda.networkx: '==3.3' + dependency.conda.notebook: '==7.2.1' + dependency.conda.python: '==3.10.14' + dependency.conda.python-dotenv: '==1.0.1' + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} environment: http_proxy: ${http_proxy} https_proxy: ${https_proxy} @@ -90,3 +78,7 @@ services: shm_size: 12GB volumes: - /dev/dri/by-path:/dev/dri/by-path + command: > + bash -c " conda run -n data-analytics python -c 'import modin.pandas as pd;import + modin.config as cfg;cfg.Engine.put(\"Ray\");df = pd.DataFrame([1]);print(df+1)' + " diff --git a/preset/data-analytics/requirements.txt b/preset/data-analytics/requirements.txt index d231202d..8fe3dfff 100644 --- a/preset/data-analytics/requirements.txt +++ b/preset/data-analytics/requirements.txt @@ -1 +1 @@ -cloud-data-connector==1.0.3 +dataset-librarian==1.0.4 diff --git a/preset/deep-learning/Dockerfile b/preset/deep-learning/Dockerfile index 05721e11..213606b8 100644 --- a/preset/deep-learning/Dockerfile +++ b/preset/deep-learning/Dockerfile @@ -12,158 +12,148 @@ # See the License for the specific language governing permissions and # limitations under the License. + ARG BASE_IMAGE=ubuntu ARG BASE_TAG=22.04 -FROM ${BASE_IMAGE}:${BASE_TAG} AS dgpu-base +FROM ${BASE_IMAGE}:${BASE_TAG} AS deep-learning-base -ENV DEBIAN_FRONTEND=noninteractive +SHELL ["/bin/bash", "-c"] -# See http://bugs.python.org/issue19846 +ENV DEBIAN_FRONTEND=noninteractive ENV LANG C.UTF-8 ARG PYTHON_VERSION EXPOSE 8080 -ENV LANG=C.UTF-8 - -SHELL ["/bin/bash", "-c"] - RUN apt-get update -y && \ apt-get install -y --no-install-recommends --fix-missing \ - apt-utils \ - build-essential \ - bzip2 \ - ca-certificates \ - clinfo \ - cmake \ - diffutils \ - g++ \ - gcc \ - git \ - gnupg2 \ - gpg-agent \ - gzip \ - make \ - numactl \ - patch \ - rsync \ - unzip \ - wget \ - sudo \ - xz-utils && \ + apt-utils \ + build-essential \ + bzip2 \ + ca-certificates \ + clinfo \ + cmake \ + diffutils \ + g++ \ + gcc \ + git \ + gnupg2 \ + gpg-agent \ + gzip \ + make \ + numactl \ + patch \ + rsync \ + unzip \ + wget \ + sudo \ + xz-utils \ + && \ rm -rf /var/lib/apt/lists/* -# GPU Drivers setup ARG DEVICE ARG ICD_VER ARG LEVEL_ZERO_GPU_VER ARG LEVEL_ZERO_VER ARG LEVEL_ZERO_DEV_VER - -# Public Drivers link RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \ - gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg -RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy/lts/2350 unified" | \ + gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg && \ + echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy/lts/2350 unified" | \ tee /etc/apt/sources.list.d/intel-gpu-jammy.list RUN apt-get update && \ apt-get install -y --no-install-recommends --fix-missing \ - intel-opencl-icd="${ICD_VER}" \ - intel-level-zero-gpu="${LEVEL_ZERO_GPU_VER}" \ - level-zero="${LEVEL_ZERO_VER}" + intel-level-zero-gpu="${LEVEL_ZERO_GPU_VER}" \ + intel-opencl-icd="${ICD_VER}" \ + level-zero="${LEVEL_ZERO_VER}" RUN apt-get update && \ apt-get install -y --no-install-recommends --fix-missing \ - intel-media-va-driver-non-free \ - libmfx1 \ - libmfxgen1 \ - libvpl2 \ - libegl-mesa0 \ - libegl1-mesa \ - libegl1-mesa-dev \ - libgbm1 \ - libgl1-mesa-dev \ - libgl1-mesa-dri \ - libglapi-mesa \ - libgles2-mesa-dev \ - libglx-mesa0 \ - libigdgmm12 \ - libxatracker2 \ - mesa-va-drivers \ - mesa-vdpau-drivers \ - mesa-vulkan-drivers \ - va-driver-all \ - vainfo \ - hwinfo \ - clinfo + clinfo \ + hwinfo \ + intel-media-va-driver-non-free \ + libegl-mesa0 \ + libegl1-mesa \ + libegl1-mesa-dev \ + libgbm1 \ + libgl1-mesa-dev \ + libgl1-mesa-dri \ + libglapi-mesa \ + libgles2-mesa-dev \ + libglx-mesa0 \ + libigdgmm12 \ + libmfx1 \ + libmfxgen1 \ + libvpl2 \ + mesa-va-drivers \ + mesa-vdpau-drivers \ + mesa-vulkan-drivers \ + va-driver-all \ + vainfo RUN apt-get install -y --no-install-recommends --fix-missing \ - libigc-dev \ - intel-igc-cm \ - libigdfcl-dev \ - libigfxcmrt-dev \ - level-zero-dev="${LEVEL_ZERO_DEV_VER}" && \ - rm -rf /var/lib/apt/lists/* - -RUN rm /etc/apt/sources.list.d/*list + intel-igc-cm \ + libigc-dev \ + libigdfcl-dev \ + libigfxcmrt-dev \ + level-zero-dev="${LEVEL_ZERO_DEV_VER}" \ + && \ + rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/*list -FROM dgpu-base as deep-learning-python - -# Setting up non-root directories RUN useradd --uid 1000 -d /home/dev -s /bin/bash dev RUN groupadd -g 109 render -## Add the user to the required groups RUN usermod -aG root,sudo,video,render dev -# Set a password for the user (Optional) RUN echo 'dev:password' | chpasswd USER dev WORKDIR /home/dev ENV CONDA_ROOT=/home/dev/conda - -# Miniforge Python Installation ARG MINIFORGE_VERSION ARG PYTHON_VERSION ARG IDP_VERSION ARG INTEL_CHANNEL -RUN wget --progress=dot:giga --no-check-certificate "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-${MINIFORGE_VERSION}.sh" -O miniforge.sh && \ +RUN wget --progress=dot:giga --no-check-certificate "https://github.com/conda-forge/miniforge/releases/latest/download/${MINIFORGE_VERSION}.sh" -O miniforge.sh && \ chmod +x miniforge.sh && \ ./miniforge.sh -b -p "${CONDA_ROOT}" && \ rm ./miniforge.sh && \ - ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge3" && \ + ln -s "${CONDA_ROOT}" "${CONDA_ROOT}/../miniforge" && \ export PATH="${CONDA_ROOT}/bin/:${PATH}" && \ - conda update -y conda && \ - conda config --add channels conda-forge && \ - conda config --add channels https://software.repos.intel.com/python/conda/ && \ conda init --all && \ - conda install -c conda-forge \ - 'jupyterlab>=4.1.8' \ - 'notebook>=7.1.3' \ - 'jupyterhub>=4.1.5' \ - 'jupyter-server-proxy>=4.1.2' \ - 'mako>=1.2.2' \ - 'pyjwt>=2.4.0' \ - 'cryptography>=42.0.5' \ - 'nodejs>=20.12.2' \ + conda install -y \ + 'colorama==0.4.6' \ + 'conda==24.5.0' \ + 'jupyter-server-proxy==4.3.0' \ + 'jupyterhub==5.1.0' \ + 'ld_impl_linux-64==2.40' \ + 'mamba==1.5.8' \ + 'networkx==3.3' \ + 'notebook==7.2.1' \ + 'python==3.10.14' \ + 'aiohttp>=3.9.4' \ + 'certifi>=2024.07.04' \ 'idna>=3.7' \ - 'tqdm>=4.66.2' \ - && \ - jupyter labextension disable "@jupyterlab/apputils-extension:announcements" && \ - conda clean -y --all + 'jinja2>=3.1.4' \ + 'requests>=2.32.0' \ + 'setuptools>=70.0.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + 'zipp>=3.19.1' \ + 'nodejs==22.5.1' \ + && \ + jupyter labextension disable "@jupyterlab/apputils-extension:announcements" \ + && \ + conda clean -y --all \ + && \ + conda config --add channels ${INTEL_CHANNEL} ENV PATH ${CONDA_ROOT}/condabin:${CONDA_ROOT}/bin/:${PATH} +RUN conda config --set pip_interop_enabled True -RUN conda config --set pip_interop_enabled True # Improve interoperabilty among conda an pypi packages - - -# PyTorch Installation -ARG IDP_VERSION ARG DPNP_VERSION ARG NUMPY_VERSION - ARG TORCH_CPU_VERSION ARG ONECCL_CPU_VERSION ARG IPEX_CPU_VERSION @@ -171,120 +161,94 @@ ARG TORCHVISION_CPU_VERSION ARG TORCHAUDIO_CPU_VERSION ARG DEEPSPEED_VERSION -# PyTorch CPU Env - conda packages -RUN conda create -yn pytorch-cpu -c "${INTEL_CHANNEL}" -c conda-forge \ - dpnp="${DPNP_VERSION}" \ - numpy="${NUMPY_VERSION}" \ - python="${PYTHON_VERSION}" \ - intel-openmp="${IDP_VERSION}" \ - pytorch="${TORCH_CPU_VERSION}" \ - oneccl_bind_pt="${ONECCL_CPU_VERSION}" \ - intel-extension-for-pytorch="${IPEX_CPU_VERSION}" \ - torchvision="${TORCHVISION_CPU_VERSION}" \ - torchaudio="${TORCHAUDIO_CPU_VERSION}" \ - 'matplotlib-base>=3.4.3' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'pillow>=10.2.0' \ - 'aiohttp>=3.9.0' \ - 'tornado>=6.3.3' \ - 'jinja2>=3.1.3' \ +RUN conda create -yn 'pytorch-cpu' \ + -c huggingface \ + "python=${PYTHON_VERSION}" \ + 'accelerate==0.32.1' \ + "dpnp=${DPNP_VERSION}" \ + "intel-extension-for-pytorch=${IPEX_CPU_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base>=3.8.4' \ + "oneccl_bind_pt=${ONECCL_CPU_VERSION}" \ + "pytorch=${TORCH_CPU_VERSION}" \ + 'tensorboardx==2.6.2.2' \ + "torchaudio=${TORCHAUDIO_CPU_VERSION}" \ + "torchvision=${TORCHVISION_CPU_VERSION}" \ + 'python-dotenv==1.0.1' \ + 'aiohttp>=3.9.4' \ + 'certifi>=2024.07.04' \ 'idna>=3.7' \ - 'onnx>=1.15.0' \ + 'jinja2>=3.1.4' \ + 'onnx>=1.16.0' \ + 'requests>=2.32.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + 'zipp>=3.19.1' \ && \ conda clean -y --all -# PyPI packages -RUN conda run -n pytorch-cpu pip install --no-deps --no-cache-dir --ignore-installed \ - 'ninja>=1.11.1.1' \ - 'python-dotenv>=1.0.1' \ - 'tqdm>=4.66.2' \ - 'cloud-data-connector==1.0.3' \ - 'dataset-librarian==1.0.4' && \ - conda run -n pytorch-cpu pip install --no-cache-dir --ignore-installed \ - 'transformers>=4.40.2' \ - 'datasets>=2.19.1' \ - 'evaluate>=0.4.2' && \ - conda run -n pytorch-cpu pip install --no-cache-dir -U 'accelerate>=0.30.0' && \ - conda run -n pytorch-cpu pip install --no-cache-dir "git+https://github.com/huggingface/optimum-intel.git" && \ +RUN conda run -n 'pytorch-cpu' pip install --no-deps --no-cache-dir \ + 'dataset-librarian==1.0.4' \ + && \ + conda run -n 'pytorch-cpu' pip install --no-cache-dir \ + 'evaluate==0.4.2' \ + "git+https://github.com/huggingface/optimum-intel.git" \ + && \ conda clean -y --all - - -RUN conda run -n pytorch-cpu conda install 'protobuf=4.24' -c conda-forge --override --force-reinstall -y - -# PyTorch Installation ARG IDP_VERSION ARG DPNP_VERSION ARG NUMPY_VERSION - -ARG TORCH_GPU_VERSION -ARG ONECCL_GPU_VERSION -ARG IPEX_GPU_VERSION -ARG TORCHVISION_GPU_VERSION -ARG TORCHAUDIO_GPU_VERSION +ARG TORCH_XPU_VERSION +ARG ONECCL_XPU_VERSION +ARG IPEX_XPU_VERSION +ARG TORCHVISION_XPU_VERSION +ARG TORCHAUDIO_XPU_VERSION ARG IDEX_VERSION -ARG DEEPSPEED_VERSION -# PyTorch GPU Env - conda packages -RUN conda create -yn pytorch-gpu -c "${INTEL_CHANNEL}" -c conda-forge \ - dpnp="${DPNP_VERSION}" \ - dpcpp-cpp-rt="${IDP_VERSION}" \ - mkl-dpcpp="${IDP_VERSION}" \ - dpcpp_impl_linux-64="${IDP_VERSION}" \ - numpy="${NUMPY_VERSION}" \ - python="${PYTHON_VERSION}" \ - intel-openmp="${IDP_VERSION}" \ - python="${PYTHON_VERSION}" \ - pytorch="${TORCH_GPU_VERSION}" \ - oneccl_bind_pt="${ONECCL_GPU_VERSION}" \ - intel-extension-for-pytorch="${IPEX_GPU_VERSION}" \ - torchvision="${TORCHVISION_GPU_VERSION}" \ - torchaudio="${TORCHAUDIO_GPU_VERSION}" \ - 'tensorboardx>=2.6.2.2' \ - 'matplotlib-base>=3.4.3' \ - 'pandas>=2.2.2' \ - 'ipython>=8.18.1' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'pillow>=10.2.0' \ - 'aiohttp>=3.9.0' \ - 'tornado>=6.3.3' \ - 'jinja2>=3.1.3' \ +RUN conda create -yn 'pytorch-gpu' \ + -c huggingface \ + "python=${PYTHON_VERSION}" \ + 'accelerate==0.32.1' \ + "dpnp=${DPNP_VERSION}" \ + "intel-extension-for-pytorch=${IPEX_XPU_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base>=3.8.4' \ + "oneccl_bind_pt=${ONECCL_XPU_VERSION}" \ + "pytorch=${TORCH_XPU_VERSION}" \ + 'tensorboardx==2.6.2.2' \ + "torchaudio=${TORCHAUDIO_XPU_VERSION}" \ + "torchvision=${TORCHVISION_XPU_VERSION}" \ + 'python-dotenv==1.0.1' \ + 'aiohttp>=3.9.4' \ + 'certifi>=2024.07.04' \ 'idna>=3.7' \ - 'onnx>=1.15.0' \ - 'packaging=23.2' \ - 'setuptools=69.1.0' \ + 'jinja2>=3.1.4' \ + 'onnx>=1.16.0' \ + 'requests>=2.32.0' \ + 'tqdm>=4.66.3' \ + 'urllib3>=2.2.2' \ + 'zipp>=3.19.1' \ && \ conda clean -y --all -# PyPI packages -RUN conda run -n pytorch-gpu pip install --no-deps --no-cache-dir --ignore-installed \ - 'ninja>=1.11.1.1' \ - 'python-dotenv>=1.0.1' \ - 'tqdm>=4.66.2' \ - 'cloud-data-connector==1.0.3' \ - 'dataset-librarian==1.0.4' && \ - conda run -n pytorch-gpu pip install --no-cache-dir --ignore-installed \ - 'transformers>=4.40.2' \ - 'datasets>=2.19.1' \ - 'evaluate>=0.4.2' && \ - conda run -n pytorch-gpu pip install --no-cache-dir -U 'accelerate>=0.30.0' && \ - conda run -n pytorch-gpu pip install --no-cache-dir "git+https://github.com/huggingface/optimum-intel.git" && \ +RUN conda run -n 'pytorch-gpu' pip install --no-deps --no-cache-dir \ + 'dataset-librarian==1.0.4' \ + && \ + conda run -n 'pytorch-gpu' pip install --no-cache-dir \ + 'evaluate==0.4.2' \ + "git+https://github.com/huggingface/optimum-intel.git" \ + && \ conda clean -y --all - - -RUN conda run -n pytorch-gpu conda install 'protobuf=4.24' -c conda-forge --override --force-reinstall -y - - -# TensorFlow Installation ARG IDP_VERSION ARG DPNP_VERSION ARG NUMPY_VERSION - ARG TF_VERSION -ARG ITEX_VERSION +ARG ITEX_CPU_VERSION +ARG ITEX_XPU_VERSION ARG HOROVOD_VERSION ARG IMPI_VERSION @@ -293,149 +257,122 @@ ARG HOROVOD_WITHOUT_MXNET=1 ARG HOROVOD_WITHOUT_GLOO=1 ARG HOROVOD_WITH_MPI=1 - -# Tensorflow Env - conda packages -RUN conda create -yn tensorflow-cpu -c "${INTEL_CHANNEL}" -c conda-forge \ - dpnp="${DPNP_VERSION}" \ - dpcpp-cpp-rt="${IDP_VERSION}" \ - mkl-dpcpp="${IDP_VERSION}" \ - numpy="${NUMPY_VERSION}" \ - python="${PYTHON_VERSION}" \ - intel-extension-for-tensorflow="${ITEX_VERSION}=*cpu*" \ - intel-optimization-for-horovod="${INTEL_HOROVOD}" \ - tensorflow="${TF_VERSION}" \ - impi-devel="${IMPI_VERSION}" \ - 'matplotlib-base>=3.4.3' \ - 'ipython>=8.18.1' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'pillow>=10.2.0' \ - 'cryptography>=42.0.4' \ - 'werkzeug>=2.2.3' \ - 'aiohttp>=3.9.0' \ - 'tornado>=6.3.3' \ - 'pyjwt>=2.8.0' \ - 'oauthlib>=3.2.2' \ - 'idna>=3.7' \ +RUN conda create -yn 'tensorflow-cpu' \ + "python=${PYTHON_VERSION}" \ + "dpnp=${DPNP_VERSION}" \ + "intel-extension-for-tensorflow=${ITEX_CPU_VERSION}=*cpu*" \ + "intel-optimization-for-horovod=${HOROVOD_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base>=3.8.4' \ 'onnx>=1.14.1' \ + 'py-cpuinfo==9.0.0' \ + "tensorflow=${TF_VERSION}" \ + 'tensorflow-hub==0.16.1' \ + 'tqdm==4.66.4' \ + 'python-dotenv==1.0.1' \ + 'aiohttp>=3.9.4' \ + 'certifi>=2024.07.04' \ + 'idna>=3.7' \ + 'requests>=2.32.0' \ + 'urllib3>=2.2.2' \ + 'werkzeug>=3.0.3' \ + 'zipp>=3.19.1' \ && \ conda clean -y --all -# PyPI packages -RUN conda run -n tensorflow-cpu pip install --no-cache-dir --ignore-installed \ - 'py-cpuinfo>=9.0.0' \ - 'requests>=2.31.0' \ - 'cryptography>=42.0.7' -RUN conda run -n tensorflow-cpu pip install --no-deps --no-cache-dir --ignore-installed \ - 'tensorflow-hub>=0.16.1' \ - 'tqdm>=4.66.2' \ +RUN conda run -n 'tensorflow-cpu' pip install --no-deps --no-cache-dir \ 'dataset-librarian==1.0.4' \ - 'cloud-data-connector>=1.0.3' && \ + && \ conda clean -y --all -# Tensorflow Env - conda packages -RUN conda create -yn tensorflow-gpu -c "${INTEL_CHANNEL}" -c conda-forge \ - dpnp="${DPNP_VERSION}" \ - dpcpp-cpp-rt="${IDP_VERSION}" \ - mkl-dpcpp="${IDP_VERSION}" \ - numpy="${NUMPY_VERSION}" \ - python="${PYTHON_VERSION}" \ - intel-extension-for-tensorflow="${ITEX_VERSION}=*xpu*" \ - intel-optimization-for-horovod="${INTEL_HOROVOD}" \ - tensorflow="${TF_VERSION}" \ - impi-devel="${IMPI_VERSION}" \ - 'matplotlib-base>=3.4.3' \ - 'ipython>=8.18.1' \ - 'ipykernel>=6.29.3' \ - 'kernda>=0.3.0' \ - 'pillow>=10.2.0' \ - 'cryptography>=42.0.4' \ - 'werkzeug>=2.2.3' \ - 'aiohttp>=3.9.0' \ - 'tornado>=6.3.3' \ - 'pyjwt>=2.8.0' \ - 'oauthlib>=3.2.2' \ - 'idna>=3.7' \ +RUN conda create -yn 'tensorflow-gpu' \ + "python=${PYTHON_VERSION}" \ + "dpnp=${DPNP_VERSION}" \ + "intel-extension-for-tensorflow=${ITEX_XPU_VERSION}=*xpu*" \ + "intel-optimization-for-horovod=${HOROVOD_VERSION}" \ + 'ipykernel==6.29.5' \ + 'kernda==0.3.0' \ + 'matplotlib-base>=3.8.4' \ 'onnx>=1.14.1' \ - 'packaging=23.2' \ - 'setuptools=69.1.0' \ + 'py-cpuinfo==9.0.0' \ + "tensorflow=${TF_VERSION}" \ + 'tensorflow-hub==0.16.1' \ + 'tqdm==4.66.4' \ + 'python-dotenv==1.0.1' \ + 'aiohttp>=3.9.4' \ + 'certifi>=2024.07.04' \ + 'idna>=3.7' \ + 'requests>=2.32.0' \ + 'urllib3>=2.2.2' \ + 'zipp>=3.19.1' \ && \ conda clean -y --all -# PyPI packages -RUN conda run -n tensorflow-gpu pip install --no-cache-dir --ignore-installed \ - 'py-cpuinfo>=9.0.0' \ - 'requests>=2.31.0' \ - 'cryptography>=42.0.7' -RUN conda run -n tensorflow-gpu pip install --no-deps --no-cache-dir --ignore-installed \ - 'tensorflow-hub>=0.16.1' \ - 'tqdm>=4.66.2' \ +RUN conda run -n 'tensorflow-gpu' pip install --no-deps --no-cache-dir \ 'dataset-librarian==1.0.4' \ - 'cloud-data-connector==1.0.3' && \ + && \ conda clean -y --all -FROM deep-learning-python as deep-learning-jupyter - -ARG KERNEL_NAME_TF_CPU="Intel TensorFlow cpu" -ARG KERNEL_NAME_TF_GPU="Intel TensorFlow gpu" -ARG KERNEL_NAME_PT_CPU="Intel PyTorch cpu" -ARG KERNEL_NAME_PT_GPU="Intel PyTorch gpu" - EXPOSE 8888 RUN mkdir -p ~/jupyter/ && chmod -R a+rwx ~/jupyter/ && \ mkdir ~/.local && chmod a+rwx ~/.local RUN \ - "${CONDA_ROOT}/envs/pytorch-cpu/bin/python" -m ipykernel install --user --name pytorch-cpu --display-name "${KERNEL_NAME_PT_CPU}" && \ - "${CONDA_ROOT}/envs/pytorch-cpu/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo pytorch-cpu | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - "${CONDA_ROOT}/envs/pytorch-gpu/bin/python" -m ipykernel install --user --name pytorch-gpu --display-name "${KERNEL_NAME_PT_GPU}" && \ - "${CONDA_ROOT}/envs/pytorch-gpu/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo pytorch-gpu | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - "${CONDA_ROOT}/envs/tensorflow-cpu/bin/python" -m ipykernel install --user --name tensorflow-cpu --display-name "${KERNEL_NAME_TF_CPU}" && \ - "${CONDA_ROOT}/envs/tensorflow-cpu/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo tensorflow-cpu | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - "${CONDA_ROOT}/envs/tensorflow-gpu/bin/python" -m ipykernel install --user --name tensorflow-gpu --display-name "${KERNEL_NAME_TF_GPU}" && \ - "${CONDA_ROOT}/envs/tensorflow-gpu/bin/kernda" -o -y "$HOME/.local/share/jupyter/kernels/$(echo tensorflow-gpu | sed -e 's/\(.*\)/\L\1/')/kernel.json" && \ - python -m ipykernel.kernelspec --user + ENVS_LIST=('pytorch-cpu' 'pytorch-gpu' 'tensorflow-cpu' 'tensorflow-gpu') && \ + KERNEL_NAMES=('Intel PyTorch CPU' 'Intel PyTorch GPU' 'Intel TensorFlow CPU' 'Intel TensorFlow GPU') && \ + for i in "${!ENVS_LIST[@]}"; do \ + CONDA_ENV="${ENVS_LIST[i]}" && \ + KERNEL_NAME="${KERNEL_NAMES[i]}" && \ + KERNEL_DIR="${CONDA_ROOT}/share/jupyter/kernels/$CONDA_ENV" && \ + conda run -n "$CONDA_ENV" python -m ipykernel install --prefix "$CONDA_ROOT" --name "$CONDA_ENV" --display-name "$KERNEL_NAME" && \ + conda run -n "$CONDA_ENV" kernda -o -y "$KERNEL_DIR/kernel.json" && \ + conda run -n base jupyter kernelspec list \ + ; done CMD ["bash", "-c", "jupyter lab --notebook-dir=~/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root"] -FROM deep-learning-jupyter as distributed-deep-learning +FROM deep-learning-base as deep-learning +SHELL ["/bin/bash", "-c"] USER root -# Install OpenMPI -RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \ - libopenmpi-dev \ - openmpi-bin \ - openmpi-common +RUN apt-get update -y && \ + apt-get install -y --no-install-recommends --fix-missing \ + libopenmpi-dev \ + openmpi-bin \ + openmpi-common ENV OMPI_ALLOW_RUN_AS_ROOT=1 ENV OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 ENV OMPI_MCA_tl_tcp_if_exclude="lo,docker0" -# Install OpenSSH RUN apt-get install -y --no-install-recommends --fix-missing \ - openssh-client \ - openssh-server && \ - rm /etc/ssh/ssh_host_*_key \ - /etc/ssh/ssh_host_*_key.pub && \ - rm -rf /var/lib/apt/lists/* - -RUN mkdir -p /var/run/sshd && \ + openssh-client \ + openssh-server \ + && \ + rm -rf \ + /etc/ssh/ssh_host_*_key \ + /etc/ssh/ssh_host_*_key.pub \ + /var/lib/apt/lists/* \ + && \ + mkdir -p /var/run/sshd \ + && \ echo 'LoginGraceTime 0' >> /etc/ssh/sshd_config -# https://github.com/openucx/ucx/issues/4742#issuecomment-584059909 ENV UCX_TLS=ud,sm,self USER dev -RUN conda install -n pytorch-cpu -c "${INTEL_CHANNEL}" -c conda-forge \ - deepspeed="${DEEPSPEED_VERSION}" \ - 'tensorboardx>=2.6.2.2' - -RUN conda install -n pytorch-gpu -c "${INTEL_CHANNEL}" -c conda-forge \ - deepspeed="${DEEPSPEED_VERSION}" \ - 'tensorboardx>=2.6.2.2' +RUN ENVS_LIST=('pytorch-cpu' 'pytorch-gpu') && \ + for i in "${!ENVS_LIST[@]}"; do \ + CONDA_ENV="${ENVS_LIST[i]}" && \ + conda install -yn "$CONDA_ENV" \ + "deepspeed=${DEEPSPEED_VERSION}" \ + 'tensorboardx==2.6.2.2' \ + ; done && \ + conda clean -y --all COPY --chown=dev notebooks /home/dev/jupyter COPY --chown=dev tests /home/dev/sample-tests diff --git a/preset/deep-learning/docker-compose.yaml b/preset/deep-learning/docker-compose.yaml index 663e064c..023b6f82 100644 --- a/preset/deep-learning/docker-compose.yaml +++ b/preset/deep-learning/docker-compose.yaml @@ -15,6 +15,7 @@ # -*- coding: utf-8 -*- # + version: '3' services: dl-base: @@ -22,44 +23,42 @@ services: args: BASE_IMAGE: ${BASE_IMAGE:-ubuntu} BASE_TAG: ${BASE_TAG:-22.04} - DEEPSPEED_VERSION: ${DEEPSPEED_VERSION:-0.14.0} + DEEPSPEED_VERSION: ${DEEPSPEED_VERSION:-0.14.2} DEVICE: ${DEVICE:-flex} - DPNP_VERSION: ${NUMBA_DPEX_VERSION:-0.14.0} - HOROVOD_VERSION: ${HOROVOD_VERSION:-0.28.1.4} - ICD_VER: 23.43.27642.40-803~22.04 - IDP_VERSION: ${IDP_VERSION:-2024.1.0} - IMPI_VERSION: ${IMPI_VERSION:-2021.12} + DPNP_VERSION: ${DPNP_VERSION:-0.15.0} + HOROVOD_VERSION: ${HOROVOD_VERSION:-0.28.1.5} + ICD_VER: 23.43.27642.52-803~22.04 + IDP_VERSION: ${IDP_VERSION:-2024.2} + IMPI_VERSION: ${IMPI_VERSION:-2021.13} INTEL_CHANNEL: ${INTEL_CHANNEL:-https://software.repos.intel.com/python/conda/} - IPEX_CPU_VERSION: ${IPEX_CPU_VERSION:-2.2.0=*cpu*} - IPEX_GPU_VERSION: ${IPEX_GPU_VERSION:-2.1.20=*xpu*} - ITEX_VERSION: ${ITEX_VERSION:-2.15} + IPEX_CPU_VERSION: ${IPEX_CPU_VERSION:-2.3.100} + IPEX_XPU_VERSION: ${IPEX_XPU_VERSION:-2.1.40} + ITEX_CPU_VERSION: ${ITEX_CPU_VERSION:-2.15.0} + ITEX_XPU_VERSION: ${ITEX_XPU_VERSION:-2.15.0.1} LEVEL_ZERO_DEV_VER: 1.14.0-744~22.04 - LEVEL_ZERO_GPU_VER: 1.3.27642.40-803~22.04 + LEVEL_ZERO_GPU_VER: 1.3.27642.52-803~22.04 LEVEL_ZERO_VER: 1.14.0-744~22.04 - MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} - MPI_VERSION: ${MPI_VERSION:-2021.12.0} - NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.22.1} + MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Miniforge3-Linux-x86_64} + MPI_VERSION: ${MPI_VERSION:-2021.13} + NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.23.0} NUMPY_VERSION: ${NUMPY_VERSION:-1.26.4} - ONECCL_CPU_VERSION: ${ONECCL_CPU_VERSION:-2.2.0=*cpu*} - ONECCL_GPU_VERSION: ${ONECCL_GPU_VERSION:-2.1.200=*xpu*} - PYTHON_VERSION: ${PYTHON_VERSION:-3.10} - TF_VERSION: ${TF_VERSION:-2.15} - TORCHAUDIO_CPU_VERSION: ${TORCHAUDIO_CPU_VERSION:-2.2.0=*cpu*} - TORCHAUDIO_GPU_VERSION: ${TORCHAUDIO_GPU_VERSION:-2.1.0=*xpu*} - TORCHVISION_CPU_VERSION: ${TORCHVISION_CPU_VERSION:-0.17=*cpu*} - TORCHVISION_GPU_VERSION: ${TORCHVISION_GPU_VERSION:-0.16.0=*xpu*} - TORCH_CPU_VERSION: ${TORCH_CPU_VERSION:-2.2.0=*cpu*} - TORCH_GPU_VERSION: ${TORCH_GPU_VERSION:-2.1.0=*xpu*} + ONECCL_CPU_VERSION: ${ONECCL_CPU_VERSION:-2.3.0} + ONECCL_XPU_VERSION: ${ONECCL_XPU_VERSION:-2.1.400} + PYTHON_VERSION: ${PYTHON_VERSION:-3.9} + TF_VERSION: ${TF_VERSION:-2.15.1} + TORCHAUDIO_CPU_VERSION: ${TORCHAUDIO_CPU_VERSION:-2.3.1} + TORCHAUDIO_XPU_VERSION: ${TORCHAUDIO_XPU_VERSION:-2.1.0} + TORCHVISION_CPU_VERSION: ${TORCHVISION_CPU_VERSION:-0.18.1} + TORCHVISION_XPU_VERSION: ${TORCHVISION_XPU_VERSION:-0.16.0} + TORCH_CPU_VERSION: ${TORCH_CPU_VERSION:-2.3.1} + TORCH_XPU_VERSION: ${TORCH_XPU_VERSION:-2.1.0} http_proxy: ${http_proxy} https_proxy: ${https_proxy} no_proxy: '' context: . labels: docs: false - target: deep-learning-jupyter - command: | - bash -c "conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import intel_extension_for_pytorch as ipex;print(ipex.__version__);' && \ - conda run -n tensorflow-cpu python -c 'import tensorflow as tf; print(tf.__version__)'" + target: deep-learning-base environment: http_proxy: ${http_proxy} https_proxy: ${https_proxy} @@ -67,15 +66,24 @@ services: shm_size: 12GB volumes: - /dev/dri/by-path:/dev/dri/by-path + command: > + bash -c " conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import + intel_extension_for_pytorch as ipex;print(ipex.__version__);' && + + conda run -n tensorflow-cpu python -c 'import tensorflow as tf;print(tf.__version__)' + " + + deep-learning: build: + target: deep-learning labels: docs: deep_learning org.opencontainers.image.title: "Intel® AI Tools Selector Preset Containers - Deep Learning" org.opencontainers.base.name: "ubuntu:22.04" org.opencontainers.image.name: "intel/deep-learning" - org.opencontainers.image.version: 2024.1.0-py${PYTHON_VERSION:-3.10} - dependency.python: ${PYTHON_VERSION:-3.10} + org.opencontainers.image.version: 2024.2.0-py${PYTHON_VERSION:-3.9} + dependency.python: ${PYTHON_VERSION:-3.9} dependency.python.pip: requirements.txt dependency.apt.apt-utils: true dependency.apt.build-essential: true @@ -92,11 +100,11 @@ services: dependency.apt.gzip: true dependency.apt.hwinfo: true dependency.apt.intel-igc-cm: true - dependency.apt.intel-level-zero-gpu: '=1.3.27642.40-803~22.04' + dependency.apt.intel-level-zero-gpu: true dependency.apt.intel-media-va-driver-non-free: true - dependency.apt.intel-opencl-icd: '=23.43.27642.40-803~22.04' - dependency.apt.level-zero: '=1.14.0-744~22.04' - dependency.apt.level-zero-dev: '=1.14.0-744~22.04' + dependency.apt.intel-opencl-icd: true + dependency.apt.level-zero: true + dependency.apt.level-zero-dev: true dependency.apt.libegl1-mesa: true dependency.apt.libegl1-mesa-dev: true dependency.apt.libegl-mesa0: true @@ -114,7 +122,6 @@ services: dependency.apt.libmfxgen1: true dependency.apt.libopenmpi-dev: true dependency.apt.libvpl2: true - dependency.apt.libxatracker2: true dependency.apt.make: true dependency.apt.mesa-va-drivers: true dependency.apt.mesa-vdpau-drivers: true @@ -132,69 +139,71 @@ services: dependency.apt.vainfo: true dependency.apt.wget: true dependency.apt.xz-utils: true - dependency.conda.jupyterlab: '>=4.1.8' - dependency.conda.aiohttp: '>=3.9.0' - dependency.conda.cryptography: '>=42.0.4' - dependency.conda.dataset_librarian: '>=1.0.4' - dependency.conda.deepspeed: '=0.14.0' - dependency.conda.dpcpp_impl_linux-64: '=2024.1.0' - dependency.conda.dpcpp-cpp-rt: '=2024.1.0' - dependency.conda.dpnp: '=0.14.0' - dependency.conda.idna: '>=3.7' - dependency.conda.impi-devel: '=2021.12' - dependency.conda.intel-extension-for-pytorch_cpu: '=2.2.0=*cpu*' - dependency.conda.intel-extension-for-pytorch_gpu: '=2.1.20=*xpu*' - dependency.conda.intel-extension-for-tensorflow_cpu: '=2.15=*cpu*' - dependency.conda.intel-extension-for-tensorflow_gpu: '=2.15=*xpu*' - dependency.conda.intel-openmp: '=2024.1.0' - dependency.conda.intel-optimization-for-horovod: '=0.28.1.4' - dependency.conda.ipykernel: '>=6.29.3' - dependency.conda.ipython: '>=8.18.1' - dependency.conda.jinja2: '>=3.1.3' - dependency.conda.jupyterhub: '>=4.1.5' - dependency.conda.jupyter-server-proxy: '>=4.1.2' - dependency.conda.kernda: '>=0.3.0' - dependency.conda.mako: '>=1.2.2' - dependency.conda.matplotlib-base: '>=3.4.3' - dependency.conda.mkl-dpcpp: '2024.1.0' - dependency.conda.nodejs: '>=20.12.2' - dependency.conda.notebook: '>=7.1.3' - dependency.conda.numpy: '=1.26.4' - dependency.conda.oauthlib: '>=3.2.2' - dependency.conda.oneccl_bind_pt_cpu: '=2.2.0=*cpu*' - dependency.conda.oneccl_bind_pt_gpu: '=2.1.200=*xpu*' + dependency.conda.accelerate: '==0.32.1' + dependency.conda.colorama: '==0.4.6' + dependency.conda.conda: '==24.5.0' + dependency.conda.dpnp: '=0.15.0' + dependency.conda.intel-extension-for-pytorch_cpu: '=2.3.100' + dependency.conda.intel-extension-for-pytorch_xpu: '=2.1.40' + dependency.conda.intel-extension-for-tensorflow_cpu: '=2.15.0=*cpu*' + dependency.conda.intel-extension-for-tensorflow_xpu: '=2.15.0.1=*xpu*' + dependency.conda.intel-optimization-for-horovod: '=0.28.1.5' + dependency.conda.ipykernel: '==6.29.5' + dependency.conda.jupyterhub: '==5.1.0' + dependency.conda.jupyter-server-proxy: '==4.3.0' + dependency.conda.kernda: '==0.3.0' + dependency.conda.ld_impl_linux-64: '==2.40' + dependency.conda.mamba: '==1.5.8' + dependency.conda.matplotlib-base: '>=3.8.4' + dependency.conda.mpi: '==1.0' + dependency.conda.mpich: '==4.2.2' + dependency.conda.networkx: '==3.3' + dependency.conda.notebook: '==7.2.1' + dependency.conda.oneccl_bind_pt_cpu: '=2.3.0' + dependency.conda.oneccl_bind_pt_xpu: '=2.1.400' dependency.conda.onnx: '>=1.14.1' - dependency.conda.packaging: '=23.2' - dependency.conda.pandas: '>=2.2.2' - dependency.conda.pillow: '>=10.2.0' - dependency.conda.protobuf: '=4.24' - dependency.conda.pyjwt: '>=2.4.0' - dependency.conda.python: "=${PYTHON_VERSION:-3.10}" - dependency.conda.pytorch_cpu: '=2.2.0=*cpu*' - dependency.conda.pytorch_gpu: '=2.1.0=*xpu*' - dependency.conda.setuptools: '=69.1.0' - dependency.conda.tensorboardx: '>=2.6.2.2' - dependency.conda.tensorflow: '=2.15' - dependency.conda.torchaudio_cpu: '=2.2.0=*cpu*' - dependency.conda.torchaudio_gpu: '=2.1.0=*xpu*' - dependency.conda.torchvision_cpu: '=0.17=*cpu*' - dependency.conda.torchvision_gpu: '=0.16.0=*xpu*' - dependency.conda.tornado: '>=6.3.3' - dependency.conda.tqdm: '>=4.66.2' - dependency.conda.werkzeug: '>=2.2.3' - target: distributed-deep-learning + dependency.conda.py-cpuinfo: '==9.0.0' + dependency.conda.python: '==3.10.14' + dependency.conda.pytorch_cpu: '=2.3.1' + dependency.conda.pytorch_xpu: '=2.1.0' + dependency.conda.tensorboardx: '==2.6.2.2' + dependency.conda.tensorflow: '=2.15.1' + dependency.conda.tensorflow-hub: '==0.16.1' + dependency.conda.torchaudio_cpu: '=2.3.1' + dependency.conda.torchaudio_xpu: '=2.1.0' + dependency.conda.torchvision_cpu: '=0.18.1' + dependency.conda.torchvision_xpu: '=0.16.0' + dependency.conda.tqdm: '==4.66.4' depends_on: - dl-base extends: dl-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-2024.1.0-py${PYTHON_VERSION:-3.10} - command: | - bash -c "conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import intel_extension_for_pytorch as ipex;print(ipex.__version__);' && \ - conda run -n pytorch-cpu bash -c 'mpirun --version' && \ - conda run -n pytorch-cpu python -c 'import oneccl_bindings_for_pytorch as oneccl;print(\"\\nOneCCL:\", oneccl.__version__)' && \ - conda run -n pytorch-gpu python -c 'import torch;print(torch.device(\"xpu\"));import intel_extension_for_pytorch as ipex;print(ipex.xpu.is_available());print(ipex.xpu.has_onemkl())' && \ - conda run -n pytorch-gpu bash -c 'mpirun --version' && \ - conda run -n pytorch-gpu python -c 'import oneccl_bindings_for_pytorch as oneccl;print(\"\\nOneCCL:\", oneccl.__version__)' && \ - conda run -n tensorflow-cpu python -c 'import tensorflow;print(tensorflow.__version__);import intel_extension_for_tensorflow as itex;print(itex.__version__)' && \ - conda run -n tensorflow-gpu python -c 'from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())' && \ - conda run -n tensorflow-gpu bash -c 'horovodrun --check-build && mpirun --version' && \ - conda run -n tensorflow-gpu python -c 'import horovod.tensorflow as hvd;hvd.init();import horovod.tensorflow'" + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + command: > + bash -c " conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import + intel_extension_for_pytorch as ipex;print(ipex.__version__);' && + + conda run -n pytorch-cpu bash -c 'mpirun --version' && + + conda run -n pytorch-cpu python -c 'import oneccl_bindings_for_pytorch as oneccl;print(\"\\nOneCCL:\", + oneccl.__version__)' && + + conda run -n pytorch-gpu python -c 'import torch;print(torch.device(\"xpu\"));import + intel_extension_for_pytorch as ipex;print(ipex.xpu.is_available());print(ipex.xpu.has_onemkl())' + && + + conda run -n pytorch-gpu bash -c 'mpirun --version' && + + conda run -n pytorch-gpu python -c 'import oneccl_bindings_for_pytorch as oneccl;print(\"\\nOneCCL:\", + oneccl.__version__)' && + + conda run -n tensorflow-cpu python -c 'import tensorflow;print(tensorflow.__version__);import + intel_extension_for_tensorflow as itex;print(itex.__version__)' && + + conda run -n tensorflow-gpu python -c 'from tensorflow.python.client import + device_lib;print(device_lib.list_local_devices())' && + + conda run -n tensorflow-gpu bash -c 'horovodrun --check-build && mpirun --version' + && + + conda run -n tensorflow-gpu python -c 'import horovod.tensorflow as hvd;hvd.init();import + horovod.tensorflow' " diff --git a/preset/deep-learning/requirements.txt b/preset/deep-learning/requirements.txt index 4122126b..db93ef0d 100644 --- a/preset/deep-learning/requirements.txt +++ b/preset/deep-learning/requirements.txt @@ -1,14 +1,3 @@ -accelerate>=0.30.0 -cloud-data-connector>=1.0.3 -cryptography>=42.0.7 -dataset-librarian>=1.0.4 -datasets>=2.19.1 -evaluate>=0.4.2 +dataset-librarian==1.0.4 +evaluate==0.4.2 git+https://github.com/huggingface/optimum-intel.git -ninja>=1.11.1.1 -py-cpuinfo>=9.0.0 -python-dotenv>=1.0.1 -requests>=2.31.0 -tensorflow-hub>=0.16.1 -tqdm>=4.66.2 -transformers>=4.40.2 diff --git a/preset/deep-learning/tests.yaml b/preset/deep-learning/tests.yaml old mode 100644 new mode 100755 index 0b0cdcae..399d4291 --- a/preset/deep-learning/tests.yaml +++ b/preset/deep-learning/tests.yaml @@ -12,39 +12,50 @@ # See the License for the specific language governing permissions and # limitations under the License. +--- deep-learning-ipex-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n pytorch-cpu python -W ignore sample-tests/intel_extension_for_pytorch/test_ipex.py --device cpu --ipex - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} deep-learning-ipex-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n pytorch-gpu python -W ignore sample-tests/intel_extension_for_pytorch/test_ipex.py --device xpu --ipex - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] + deep-learning-ipex-notebook-${PYTHON_VERSION:-3.9}-cpu: cmd: papermill --log-output jupyter/ipex/ResNet50_Inference.ipynb -k pytorch-cpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True deep-learning-ipex-notebook-${PYTHON_VERSION:-3.9}-gpu: cmd: papermill --log-output jupyter/ipex/ResNet50_Inference.ipynb -k pytorch-gpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True + device: ["/dev/dri"] + deep-learning-ipex-quantization-notebook-${PYTHON_VERSION:-3.9}-cpu: cmd: papermill --log-output jupyter/ipex-quantization/IntelPytorch_Quantization.ipynb -k pytorch-cpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True + deep-learning-itex-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu python -W ignore sample-tests/intel_extension_for_tensorflow/test_itex.py - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} deep-learning-itex-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu python -W ignore sample-tests/intel_extension_for_tensorflow/test_itex.py - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] + deep-learning-tensorflow-dataset-librarian-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} deep-learning-tensorflow-dataset-librarian-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] + deep-learning-torch-dataset-librarian-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n pytorch-cpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} deep-learning-torch-dataset-librarian-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n pytorch-gpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-deep-learning-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] diff --git a/preset/inference-optimization/Dockerfile b/preset/inference-optimization/Dockerfile index 6689b437..a38e8266 100644 --- a/preset/inference-optimization/Dockerfile +++ b/preset/inference-optimization/Dockerfile @@ -12,35 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. + ARG COMPOSE_PROJECT_NAME FROM ${COMPOSE_PROJECT_NAME}-dl-base as inference-optimization -ENV SIGOPT_PROJECT=. +SHELL ["/bin/bash", "-c"] +ENV SIGOPT_PROJECT=. ARG NEURAL_COMPRESSOR_VERSION ARG INTEL_CHANNEL - -RUN conda install -yn pytorch-cpu -c "${INTEL_CHANNEL}" -c conda-forge \ - neural-compressor="${NEURAL_COMPRESSOR_VERSION}" - -RUN conda install -yn pytorch-gpu -c "${INTEL_CHANNEL}" -c conda-forge \ - neural-compressor="${NEURAL_COMPRESSOR_VERSION}" - -RUN conda install -yn tensorflow-cpu -c "${INTEL_CHANNEL}" -c conda-forge \ - neural-compressor="${NEURAL_COMPRESSOR_VERSION}" - -RUN conda install -yn tensorflow-gpu -c "${INTEL_CHANNEL}" -c conda-forge \ - neural-compressor="${NEURAL_COMPRESSOR_VERSION}" - -RUN conda run -n tensorflow-cpu python -m pip install --no-deps --no-cache-dir \ - 'tf2onnx>=1.16.1' \ - 'onnxruntime>=1.17.3' && \ +RUN ENVS_LIST=('pytorch-cpu' 'pytorch-gpu' 'tensorflow-cpu' 'tensorflow-gpu') && \ + for i in "${!ENVS_LIST[@]}"; do \ + CONDA_ENV="${ENVS_LIST[i]}" && \ + conda install -yn "$CONDA_ENV" \ + "neural-compressor=${NEURAL_COMPRESSOR_VERSION}" \ + 'scikit-learn>=1.5.0' \ + ; \ + done && \ conda clean -y --all -RUN conda run -n tensorflow-gpu python -m pip install --no-deps --no-cache-dir \ - 'tf2onnx>=1.16.1' \ - 'onnxruntime>=1.17.3' && \ +RUN ENVS_LIST=('tensorflow-cpu' 'tensorflow-gpu') && \ + for i in "${!ENVS_LIST[@]}"; do \ + CONDA_ENV="${ENVS_LIST[i]}" && \ + conda run -n "$CONDA_ENV" python -m pip install --no-deps --no-cache-dir \ + 'tf2onnx==1.16.1' \ + 'onnxruntime==1.18.1' \ + ; \ + done && \ conda clean -y --all COPY --chown=dev notebooks /home/dev/jupyter diff --git a/preset/inference-optimization/docker-compose.yaml b/preset/inference-optimization/docker-compose.yaml index ac8ebc07..cf543bff 100644 --- a/preset/inference-optimization/docker-compose.yaml +++ b/preset/inference-optimization/docker-compose.yaml @@ -15,6 +15,7 @@ # -*- coding: utf-8 -*- # + version: '3' services: dl-base: @@ -22,42 +23,42 @@ services: args: BASE_IMAGE: ${BASE_IMAGE:-ubuntu} BASE_TAG: ${BASE_TAG:-22.04} - DEEPSPEED_VERSION: ${DEEPSPEED_VERSION:-0.14.0} DEVICE: ${DEVICE:-flex} - DPNP_VERSION: ${NUMBA_DPEX_VERSION:-0.14.0} - HOROVOD_VERSION: ${HOROVOD_VERSION:-0.28.1.4} - ICD_VER: 23.43.27642.40-803~22.04 - IDP_VERSION: ${IDP_VERSION:-2024.1.0} - IMPI_VERSION: ${IMPI_VERSION:-2021.12} + DPNP_VERSION: ${DPNP_VERSION:-0.15.0} + HOROVOD_VERSION: ${HOROVOD_VERSION:-0.28.1.5} + ICD_VER: 23.43.27642.52-803~22.04 + IDP_VERSION: ${IDP_VERSION:-2024.2} + IMPI_VERSION: ${IMPI_VERSION:-2021.13} INTEL_CHANNEL: ${INTEL_CHANNEL:-https://software.repos.intel.com/python/conda/} - IPEX_CPU_VERSION: ${IPEX_CPU_VERSION:-2.2.0=*cpu*} - IPEX_GPU_VERSION: ${IPEX_GPU_VERSION:-2.1.20=*xpu*} - ITEX_VERSION: ${ITEX_VERSION:-2.15} + IPEX_CPU_VERSION: ${IPEX_CPU_VERSION:-2.3.100} + IPEX_XPU_VERSION: ${IPEX_XPU_VERSION:-2.1.40} + ITEX_CPU_VERSION: ${ITEX_CPU_VERSION:-2.15.0} + ITEX_XPU_VERSION: ${ITEX_XPU_VERSION:-2.15.0.1} LEVEL_ZERO_DEV_VER: 1.14.0-744~22.04 - LEVEL_ZERO_GPU_VER: 1.3.27642.40-803~22.04 + LEVEL_ZERO_GPU_VER: 1.3.27642.52-803~22.04 LEVEL_ZERO_VER: 1.14.0-744~22.04 - MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} - MPI_VERSION: ${MPI_VERSION:-2021.12.0} - NEURAL_COMPRESSOR_VERSION: ${NEURAL_COMPRESSOR_VERSION:-2.4.1} - NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.22.1} + MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Miniforge3-Linux-x86_64} + MPI_VERSION: ${MPI_VERSION:-2021.13} + NEURAL_COMPRESSOR_VERSION: ${NEURAL_COMPRESSOR_VERSION:-2.5.1} + NUMBA_DPEX_VERSION: ${NUMBA_DPEX_VERSION:-0.23.0} NUMPY_VERSION: ${NUMPY_VERSION:-1.26.4} - ONECCL_CPU_VERSION: ${ONECCL_CPU_VERSION:-2.2.0=*cpu*} - ONECCL_GPU_VERSION: ${ONECCL_GPU_VERSION:-2.1.200=*xpu*} - PYTHON_VERSION: ${PYTHON_VERSION:-3.10} - TF_VERSION: ${TF_VERSION:-2.15} - TORCHAUDIO_CPU_VERSION: ${TORCHAUDIO_CPU_VERSION:-2.2.0=*cpu*} - TORCHAUDIO_GPU_VERSION: ${TORCHAUDIO_GPU_VERSION:-2.1.0=*xpu*} - TORCHVISION_CPU_VERSION: ${TORCHVISION_CPU_VERSION:-0.17=*cpu*} - TORCHVISION_GPU_VERSION: ${TORCHVISION_GPU_VERSION:-0.16.0=*xpu*} - TORCH_CPU_VERSION: ${TORCH_CPU_VERSION:-2.2.0=*cpu*} - TORCH_GPU_VERSION: ${TORCH_GPU_VERSION:-2.1.0=*xpu*} + ONECCL_CPU_VERSION: ${ONECCL_CPU_VERSION:-2.3.0} + ONECCL_XPU_VERSION: ${ONECCL_XPU_VERSION:-2.1.400} + PYTHON_VERSION: ${PYTHON_VERSION:-3.9} + TF_VERSION: ${TF_VERSION:-2.15.1} + TORCHAUDIO_CPU_VERSION: ${TORCHAUDIO_CPU_VERSION:-2.3.1} + TORCHAUDIO_XPU_VERSION: ${TORCHAUDIO_XPU_VERSION:-2.1.0} + TORCHVISION_CPU_VERSION: ${TORCHVISION_CPU_VERSION:-0.18.1} + TORCHVISION_XPU_VERSION: ${TORCHVISION_XPU_VERSION:-0.16.0} + TORCH_CPU_VERSION: ${TORCH_CPU_VERSION:-2.3.1} + TORCH_XPU_VERSION: ${TORCH_XPU_VERSION:-2.1.0} http_proxy: ${http_proxy} https_proxy: ${https_proxy} no_proxy: '' context: ../deep-learning labels: docs: false - target: deep-learning-jupyter + target: deep-learning-base environment: http_proxy: ${http_proxy} https_proxy: ${https_proxy} @@ -65,9 +66,12 @@ services: shm_size: 12GB volumes: - /dev/dri/by-path:/dev/dri/by-path - command: | - bash -c "conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import intel_extension_for_pytorch as ipex;print(ipex.__version__);' && \ - conda run -n tensorflow-cpu python -c 'import tensorflow as tf; print(tf.__version__)'" + command: > + bash -c " conda run -n pytorch-cpu python -c 'import torch;print(torch.__version__);import + intel_extension_for_pytorch as ipex;print(ipex.__version__)' && + + conda run -n tensorflow-cpu python -c 'import tensorflow as tf;print(tf.__version__)' + " inference-optimization: @@ -75,13 +79,14 @@ services: args: COMPOSE_PROJECT_NAME: ${COMPOSE_PROJECT_NAME:-preset} context: . + target: inference-optimization labels: docs: inference_optimization org.opencontainers.image.title: "Intel® AI Tools Selector Preset Containers - Inference Optimization" org.opencontainers.base.name: "intel/deep-learning" org.opencontainers.image.name: "intel/inference-optimization" - org.opencontainers.image.version: 2024.1.0-py${PYTHON_VERSION:-3.10} - dependency.python: ${PYTHON_VERSION:-3.10} + org.opencontainers.image.version: 2024.2.0-py${PYTHON_VERSION:-3.9} + dependency.python: ${PYTHON_VERSION:-3.9} dependency.python.pip: requirements.txt dependency.apt.apt-utils: true dependency.apt.build-essential: true @@ -98,11 +103,11 @@ services: dependency.apt.gzip: true dependency.apt.hwinfo: true dependency.apt.intel-igc-cm: true - dependency.apt.intel-level-zero-gpu: '1.3.27642.40-803~22.04' + dependency.apt.intel-level-zero-gpu: true dependency.apt.intel-media-va-driver-non-free: true - dependency.apt.intel-opencl-icd: '23.43.27642.40-803~22.04' - dependency.apt.level-zero: '1.14.0-744~22.04' - dependency.apt.level-zero-dev: '1.14.0-744~22.04' + dependency.apt.intel-opencl-icd: true + dependency.apt.level-zero: true + dependency.apt.level-zero-dev: true dependency.apt.libegl1-mesa: true dependency.apt.libegl1-mesa-dev: true dependency.apt.libegl-mesa0: true @@ -120,7 +125,6 @@ services: dependency.apt.libmfxgen1: true dependency.apt.libopenmpi-dev: true dependency.apt.libvpl2: true - dependency.apt.libxatracker2: true dependency.apt.make: true dependency.apt.mesa-va-drivers: true dependency.apt.mesa-vdpau-drivers: true @@ -138,68 +142,72 @@ services: dependency.apt.vainfo: true dependency.apt.wget: true dependency.apt.xz-utils: true - dependency.conda.jupyterlab: '>=4.1.8' - dependency.conda.aiohttp: '>=3.9.0' - dependency.conda.cryptography: '>=42.0.4' - dependency.conda.dataset_librarian: '>=1.0.4' - dependency.conda.deepspeed: '>=0.14.0' - dependency.conda.dpcpp_impl_linux-64: '>=2024.1.' - dependency.conda.dpcpp-cpp-rt: '>=2024.1.' - dependency.conda.dpnp: '>=0.14.0' - dependency.conda.idna: '>=3.7' - dependency.conda.impi-devel: '>=2021.12' - dependency.conda.intel-extension-for-pytorch_cpu: '>=2.2.0=*cpu*' - dependency.conda.intel-extension-for-pytorch_gpu: '>=2.1.20=*xpu*' - dependency.conda.intel-extension-for-tensorflow_cpu: '>=2.15=*cpu*' - dependency.conda.intel-extension-for-tensorflow_gpu: '>=2.15=*xpu*' - dependency.conda.intel-openmp: '>=2024.1.0' - dependency.conda.intel-optimization-for-horovod: '>=0.28.1.4' - dependency.conda.ipykernel: '>=6.29.3' - dependency.conda.ipython: '>=8.18.1' - dependency.conda.jinja2: '>=3.1.3' - dependency.conda.jupyterhub: '>=4.1.5' - dependency.conda.jupyter-server-proxy: '>=4.1.2' - dependency.conda.kernda: '>=0.3.0' - dependency.conda.mako: '>=1.2.2' - dependency.conda.matplotlib-base: '>=3.4.3' - dependency.conda.mkl-dpcpp: '>=2024.1.0' - dependency.conda.neural-compressor: '>=2.4.1' - dependency.conda.nodejs: '>=20.12.2' - dependency.conda.notebook: '>=7.1.3' - dependency.conda.numpy: '>=1.26.4' - dependency.conda.oauthlib: '>=3.2.2' - dependency.conda.oneccl_bind_pt_cpu: '>=2.2.0=*cpu*' - dependency.conda.oneccl_bind_pt_gpu: '>=2.1.200=*xpu*' + dependency.conda.accelerate: '==0.32.1' + dependency.conda.colorama: '==0.4.6' + dependency.conda.conda: '==24.5.0' + dependency.conda.dpnp: '=0.15.0' + dependency.conda.intel-extension-for-pytorch_cpu: '=2.3.100' + dependency.conda.intel-extension-for-pytorch_xpu: '=2.1.40' + dependency.conda.intel-extension-for-tensorflow_cpu: '=2.15.0=*cpu*' + dependency.conda.intel-extension-for-tensorflow_xpu: '=2.15.0.1=*xpu*' + dependency.conda.intel-optimization-for-horovod: '=0.28.1.5' + dependency.conda.ipykernel: '==6.29.5' + dependency.conda.jupyterhub: '==5.1.0' + dependency.conda.jupyter-server-proxy: '==4.3.0' + dependency.conda.kernda: '==0.3.0' + dependency.conda.ld_impl_linux-64: '==2.40' + dependency.conda.mamba: '==1.5.8' + dependency.conda.matplotlib-base: '>=3.8.4' + dependency.conda.mpi: '==1.0' + dependency.conda.mpich: '==4.2.2' + dependency.conda.networkx: '==3.3' + dependency.conda.neural-compressor: '=2.5.1' + dependency.conda.notebook: '==7.2.1' + dependency.conda.oneccl_bind_pt_cpu: '=2.3.0' + dependency.conda.oneccl_bind_pt_xpu: '=2.1.400' dependency.conda.onnx: '>=1.14.1' - dependency.conda.packaging: '>=23.2' - dependency.conda.pandas: '>=2.2.2' - dependency.conda.pillow: '>=10.2.0' - dependency.conda.protobuf: '>=4.24' - dependency.conda.pyjwt: '>=2.4.0' - dependency.conda.python: "=${PYTHON_VERSION:-3.10}" - dependency.conda.pytorch_cpu: '>=2.2.0=*cpu*' - dependency.conda.pytorch_gpu: '>=2.1.0=*xpu*' - dependency.conda.setuptools: '>=69.1.0' - dependency.conda.tensorboardx: '>=2.6.2.2' - dependency.conda.tensorflow: '>=2.15' - dependency.conda.torchaudio_cpu: '>=2.2.0=*cpu*' - dependency.conda.torchaudio_gpu: '>=2.1.0=*xpu*' - dependency.conda.torchvision_cpu: '>=0.17=*cpu*' - dependency.conda.torchvision_gpu: '>=0.16.0=*xpu*' - dependency.conda.tornado: '>=6.3.3' - dependency.conda.tqdm: '>=4.66.2' - dependency.conda.werkzeug: '>=2.2.3' - target: inference-optimization + dependency.conda.onnxruntime: '==1.18.1' + dependency.conda.py-cpuinfo: '==9.0.0' + dependency.conda.python: '==3.10.14' + dependency.conda.pytorch_cpu: '=2.3.1' + dependency.conda.pytorch_xpu: '=2.1.0' + dependency.conda.scikit-learn: '>=1.5.0' + dependency.conda.tensorboardx: '==2.6.2.2' + dependency.conda.tensorflow: '=2.15.1' + dependency.conda.tensorflow-hub: '==0.16.1' + dependency.conda.tf2onnx: '==1.16.1' + dependency.conda.torchaudio_cpu: '=2.3.1' + dependency.conda.torchaudio_xpu: '=2.1.0' + dependency.conda.torchvision_cpu: '=0.18.1' + dependency.conda.torchvision_xpu: '=0.16.0' + dependency.conda.tqdm: '==4.66.4' depends_on: - dl-base extends: dl-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-2024.1.0-py${PYTHON_VERSION:-3.10} - command: | - bash -c "conda run -n pytorch-cpu python -c 'import intel_extension_for_pytorch as ipex;print(ipex.__version__);' && \ - conda run -n pytorch-cpu python -c 'import neural_compressor;print(\"Neural Compressor Version:\", neural_compressor.__version__)' && \ - conda run -n pytorch-gpu python -c 'import torch;print(torch.device(\"xpu\"));import intel_extension_for_pytorch as ipex;print(ipex.xpu.is_available());' && \ - conda run -n pytorch-gpu python -c 'import neural_compressor;print(\"Neural Compressor Version:\", neural_compressor.__version__)' && \ - conda run -n tensorflow-cpu python -c 'import intel_extension_for_tensorflow as itex;print(itex.__version__);' && \ - conda run -n tensorflow-cpu python -c 'import neural_compressor, tf2onnx; print(\"\\nNeural Compressor Version:\", neural_compressor.__version__, \"\\\nTensorFlow2ONNX Version:\", tf2onnx.__version__)' && \ - conda run -n tensorflow-gpu python -c 'from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())' && \ - conda run -n tensorflow-gpu python -c 'import neural_compressor, tf2onnx; print(\"\\nNeural Compressor Version:\", neural_compressor.__version__, \"\\\nTensorFlow2ONNX Version:\", tf2onnx.__version__)'" + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + command: > + bash -c "conda run -n pytorch-cpu python -c 'import intel_extension_for_pytorch + as ipex;print(ipex.__version__)' && + + conda run -n pytorch-cpu python -c 'import neural_compressor;print(\"Neural + Compressor Version:\", neural_compressor.__version__)' && + + conda run -n pytorch-gpu python -c 'import torch;print(torch.device(\"xpu\"));import + intel_extension_for_pytorch as ipex;print(ipex.xpu.is_available())' && + + conda run -n pytorch-gpu python -c 'import neural_compressor;print(\"Neural + Compressor Version:\", neural_compressor.__version__)' && + + conda run -n tensorflow-cpu python -c 'import intel_extension_for_tensorflow + as itex;print(itex.__version__)' && + + conda run -n tensorflow-cpu python -c 'import neural_compressor, tf2onnx;print(\"\nNeural + Compressor Version:\", neural_compressor.__version__)';print(\"\nTensorFlow2ONNX + Version:\", tf2onnx.__version__)' && + + conda run -n tensorflow-gpu python -c 'from tensorflow.python.client import + device_lib;print(device_lib.list_local_devices())' && + + conda run -n tensorflow-gpu python -c 'import neural_compressor, tf2onnx;print(\"\\nNeural + Compressor Version:\", neural_compressor.__version__)';print(\"\\TensorFlow2ONNX + Version:\", tf2onnx.__version__)' " diff --git a/preset/inference-optimization/requirements.txt b/preset/inference-optimization/requirements.txt index 15dad774..8f0091ac 100644 --- a/preset/inference-optimization/requirements.txt +++ b/preset/inference-optimization/requirements.txt @@ -1,16 +1,5 @@ -accelerate>=0.30.0 -cloud-data-connector>=1.0.3 -cryptography>=42.0.7 -dataset-librarian>=1.0.4 -datasets>=2.19.1 -evaluate>=0.4.2 +dataset-librarian==1.0.4 +evaluate==0.4.2 git+https://github.com/huggingface/optimum-intel.git -ninja>=1.11.1.1 -onnxruntime>=1.17.3 -py-cpuinfo>=9.0.0 -python-dotenv>=1.0.1 -requests>=2.31.0 -tensorflow-hub>=0.16.1 -tf2onnx>==1.16.1 -tqdm>=4.66.2 -transformers>=4.40.2 +tf2onnx==1.16.1 +onnxruntime==1.18.1 diff --git a/preset/inference-optimization/tests.yaml b/preset/inference-optimization/tests.yaml index 98731067..a906ecde 100644 --- a/preset/inference-optimization/tests.yaml +++ b/preset/inference-optimization/tests.yaml @@ -12,78 +12,87 @@ # See the License for the specific language governing permissions and # limitations under the License. +--- inference-optimization-inc-ipex-quantization-notebook-${PYTHON_VERSION:-3.9}-cpu: cmd: papermill --log-output jupyter/inc-ipex-quantization/quantize_with_inc.ipynb result.ipynb -k pytorch-cpu --cwd jupyter/inc-ipex-quantization - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True inference-optimization-inc-ipex-quantization-notebook-${PYTHON_VERSION:-3.9}-gpu: cmd: papermill --log-output jupyter/inc-ipex-quantization/quantize_with_inc.ipynb result.ipynb -k pytorch-gpu --cwd jupyter/inc-ipex-quantization - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True + device: ["/dev/dri"] inference-optimization-inc-itex-notebook-${PYTHON_VERSION:-3.9}-cpu: cmd: papermill --log-output jupyter/inc-itex/inc_sample_tensorflow.ipynb result.ipynb -k tensorflow-cpu --cwd jupyter/inc-itex - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True # Status: Commented due to out of resources error # inference-optimization-inc-itex-notebook-${PYTHON_VERSION:-3.9}-gpu: # cmd: papermill --log-output jupyter/inc-itex/inc_sample_tensorflow.ipynb result.ipynb -k tensorflow-gpu --cwd jupyter/inc-itex -# img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} +# img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} # notebook: True inference-optimization-inc-tensorflow-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu sample-tests/neural_compressor/tensorflow/run.sh cpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-inc-tensorflow-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu sample-tests/neural_compressor/tensorflow/run.sh gpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] inference-optimization-inc-torch-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n pytorch-cpu sample-tests/neural_compressor/torch/run.sh cpu - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-ipex-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n pytorch-cpu python -W ignore sample-tests/intel_extension_for_pytorch/test_ipex.py --device cpu --ipex - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-ipex-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n pytorch-gpu python -W ignore sample-tests/intel_extension_for_pytorch/test_ipex.py --device xpu --ipex - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] inference-optimization-itex-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu python -W ignore sample-tests/intel_extension_for_tensorflow/test_itex.py - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-itex-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu python -W ignore sample-tests/intel_extension_for_tensorflow/test_itex.py - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] inference-optimization-itex-inference-notebook-${PYTHON_VERSION:-3.9}-cpu: cmd: papermill --log-output jupyter/itex-inference/tutorial_optimize_TensorFlow_pretrained_model.ipynb result.ipynb -k tensorflow-cpu --cwd jupyter/itex-inference - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True # Need update from TensorFlow v1 to V2 # inference-optimization-itex-inference-notebook-${PYTHON_VERSION:-3.9}-gpu: # cmd: papermill --log-output jupyter/itex-inference/tutorial_optimize_TensorFlow_pretrained_model.ipynb result.ipynb -k tensorflow-gpu --cwd jupyter/itex-inference -# img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} +# img: ${REGISTRY}/aiops/mlops-ci:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} # notebook: True inference-optimization-onnx-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu sample-tests/onnx/run.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-onnx-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu sample-tests/onnx/run.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] inference-optimization-tensorflow-dataset-librarian-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n tensorflow-cpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-tensorflow-dataset-librarian-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n tensorflow-gpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] inference-optimization-torch-dataset-librarian-${PYTHON_VERSION:-3.9}-cpu: cmd: conda run -n pytorch-cpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} inference-optimization-torch-dataset-librarian-${PYTHON_VERSION:-3.9}-gpu: cmd: conda run -n pytorch-gpu bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: amr-registry.caas.intel.com/aiops/aikit-products-dev:b-${GITHUB_RUN_NUMBER:-0}-inference-optimization-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} + device: ["/dev/dri"] From 58cc2a39570246d05a2fe03d77a5232b98194768 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 15:57:45 +0000 Subject: [PATCH 02/56] Bump step-security/harden-runner from 2.9.0 to 2.9.1 (#302) --- .github/workflows/chart-ci.yaml | 2 +- .github/workflows/container-ci.yaml | 10 +++++----- .github/workflows/dependency-review.yaml | 2 +- .github/workflows/dockerhub-description.yml | 4 ++-- .github/workflows/docs.yaml | 2 +- .github/workflows/integration-test.yaml | 4 ++-- .github/workflows/lint.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- .github/workflows/security-report.yaml | 2 +- .github/workflows/test-runner-ci.yaml | 6 +++--- .github/workflows/weekly-test.yaml | 6 +++--- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/chart-ci.yaml b/.github/workflows/chart-ci.yaml index 24b5404e..916423b2 100644 --- a/.github/workflows/chart-ci.yaml +++ b/.github/workflows/chart-ci.yaml @@ -26,7 +26,7 @@ jobs: runs-on: kubectl steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 6dbf8532..3fb4f8a6 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -66,7 +66,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -117,7 +117,7 @@ jobs: matrix: ${{ steps.scan-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -136,7 +136,7 @@ jobs: fail-fast: false steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -170,7 +170,7 @@ jobs: matrix: ${{ steps.test-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -187,7 +187,7 @@ jobs: experimental: [true] fail-fast: false steps: - - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml index 9feca423..cce8357f 100644 --- a/.github/workflows/dependency-review.yaml +++ b/.github/workflows/dependency-review.yaml @@ -34,7 +34,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml index f3bbd9bf..201e8888 100644 --- a/.github/workflows/dockerhub-description.yml +++ b/.github/workflows/dockerhub-description.yml @@ -24,7 +24,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -39,7 +39,7 @@ jobs: fail-fast: false steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 900afb5f..0b8742c6 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -32,7 +32,7 @@ jobs: pages: write steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index 2a102efd..2a112c5b 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -26,7 +26,7 @@ jobs: groups: ${{ steps.group-list.outputs.FOLDERS }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -118,7 +118,7 @@ jobs: if: always() steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - run: exit 1 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0d170a62..2e550689 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,7 +31,7 @@ jobs: statuses: write steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 72abc0f8..d74f88a2 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -36,7 +36,7 @@ jobs: actions: read steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/security-report.yaml b/.github/workflows/security-report.yaml index f1ccde65..444262e8 100644 --- a/.github/workflows/security-report.yaml +++ b/.github/workflows/security-report.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: rsdmike/github-security-report-action@a149b24539044c92786ec39af8ba38c93496495d # v3.0.4 diff --git a/.github/workflows/test-runner-ci.yaml b/.github/workflows/test-runner-ci.yaml index 6ef0e617..448e0970 100644 --- a/.github/workflows/test-runner-ci.yaml +++ b/.github/workflows/test-runner-ci.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: true steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -66,7 +66,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0 @@ -76,7 +76,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/weekly-test.yaml b/.github/workflows/weekly-test.yaml index 41c8a1df..41189eed 100644 --- a/.github/workflows/weekly-test.yaml +++ b/.github/workflows/weekly-test.yaml @@ -25,7 +25,7 @@ jobs: groups: ${{ steps.group-list.outputs.FOLDERS }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -56,7 +56,7 @@ jobs: runs-on: kubectl steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -72,7 +72,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 From 45d23db760230e34ab14536656de6af62985925a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:05:37 -0700 Subject: [PATCH 03/56] Bump the python group in /python with 3 updates (#297) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- python/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/requirements.txt b/python/requirements.txt index 0418b164..fa2002ba 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,6 +1,6 @@ numpy==1.26.4 setuptools>=70.0.0 psutil==6.0.0 -mkl==2024.2.0 -mkl-include==2024.2.0 -intel-openmp==2024.2.0 +mkl==2024.2.1 +mkl-include==2024.2.1 +intel-openmp==2024.2.1 From 325d79ceaca189e55a7f279e065d287925ab3b1d Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Mon, 12 Aug 2024 09:16:59 -0700 Subject: [PATCH 04/56] Remove keyless sources from pytorch xpu (#293) Signed-off-by: Tyler Titsworth Co-authored-by: Srikanth Ramakrishna --- pytorch/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytorch/Dockerfile b/pytorch/Dockerfile index 809da9c8..0a68ac58 100644 --- a/pytorch/Dockerfile +++ b/pytorch/Dockerfile @@ -194,7 +194,8 @@ RUN apt-get update && \ WORKDIR / COPY xpu-requirements.txt . -RUN python -m pip install --no-cache-dir -r xpu-requirements.txt +RUN python -m pip install --no-cache-dir -r xpu-requirements.txt && \ + rm -rf xpu-requirements.txt /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list ENV LD_LIBRARY_PATH=/opt/intel/oneapi/redist/lib:$LD_LIBRARY_PATH From 31f296aa870fbc58c658e21d1e034f7cf18b297f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:27:49 +0000 Subject: [PATCH 05/56] Bump github/codeql-action from 3.25.15 to 3.26.0 (#303) --- .github/workflows/container-ci.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 3fb4f8a6..20213f5b 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Cleanup if: always() run: docker rmi -f ${{ secrets.REGISTRY }}/${{ secrets.REPO }}:${{ matrix.container }} - - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: sarif_file: '${{ matrix.container }}-scan.sarif' category: '${{ matrix.container }}' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index d74f88a2..fd31f769 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -53,6 +53,6 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 with: sarif_file: results.sarif From 88325dfdb4d19586339b768b1feb78d54fb51e11 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:38:55 +0000 Subject: [PATCH 06/56] Bump actions/upload-artifact from 4.3.5 to 4.3.6 (#304) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yaml | 2 +- .github/workflows/security-report.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index fd31f769..086103d8 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -48,7 +48,7 @@ jobs: results_format: sarif repo_token: ${{ secrets.GITHUB_TOKEN }} publish_results: true - - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/security-report.yaml b/.github/workflows/security-report.yaml index 444262e8..a9d3b98b 100644 --- a/.github/workflows/security-report.yaml +++ b/.github/workflows/security-report.yaml @@ -35,7 +35,7 @@ jobs: sarifReportDir: ${{ github.workspace }} template: report token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 with: name: Security Report Summary path: ./*.pdf From 9803461b3b2da4b3d302dc64680b5d45e61657a8 Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Tue, 13 Aug 2024 11:57:33 -0700 Subject: [PATCH 07/56] Token Authentication enabled and tested for Torchserve workflow (#306) Signed-off-by: sharvil10 Signed-off-by: Tyler Titsworth Signed-off-by: dependabot[bot] Co-authored-by: Tyler Titsworth Co-authored-by: Srikanth Ramakrishna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pytorch/serving/config.properties | 1 + workflows/charts/torchserve/README.md | 3 ++- workflows/charts/torchserve/templates/NOTES.txt | 5 +++++ workflows/charts/torchserve/templates/deploy.yaml | 3 +++ workflows/charts/torchserve/values.yaml | 2 ++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pytorch/serving/config.properties b/pytorch/serving/config.properties index 8f17094d..ecaec9e0 100644 --- a/pytorch/serving/config.properties +++ b/pytorch/serving/config.properties @@ -12,3 +12,4 @@ cpu_launcher_enable=true cpu_launcher_args=--use_logical_core disable_token_authorization=true enable_model_api=true +enable_envvars_config=true diff --git a/workflows/charts/torchserve/README.md b/workflows/charts/torchserve/README.md index b84a964c..b35cc7d4 100644 --- a/workflows/charts/torchserve/README.md +++ b/workflows/charts/torchserve/README.md @@ -18,6 +18,7 @@ For more information about how to use Intel Optimized TorchServe, check out the | deploy.resources.limits | object | `{"cpu":"4000m","memory":"1Gi"}` | Maximum resources per pod | | deploy.resources.requests | object | `{"cpu":"1000m","memory":"512Mi"}` | Minimum resources per pod | | deploy.storage.nfs | object | `{"enabled":false,"path":"nil","readOnly":true,"server":"nil","subPath":"nil"}` | Network File System (NFS) storage for models | +| deploy.tokens_disabled | bool | `false` | Set token authentication on or off. Checkout the latest [torchserve docs](https://github.com/pytorch/serve/blob/master/docs/token_authorization_api.md) for more details. | | fullnameOverride | string | `""` | Full qualified Domain Name | | nameOverride | string | `""` | Name of the serving service | | pvc.size | string | `"1Gi"` | Size of the storage | @@ -37,4 +38,4 @@ There are some additional steps that can be taken to prepare your service for yo - Integrate an [SSL Certificate](https://pytorch.org/serve/configuration.html#enable-ssl) in your model config file to serve models securely. ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/workflows/charts/torchserve/templates/NOTES.txt b/workflows/charts/torchserve/templates/NOTES.txt index 8796b205..7cf61fc4 100644 --- a/workflows/charts/torchserve/templates/NOTES.txt +++ b/workflows/charts/torchserve/templates/NOTES.txt @@ -14,3 +14,8 @@ echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT {{- end }} +{{- if eq false .Values.deploy.tokens_disabled }} +2. Display the tokens for accessing the APIs. For more details about token authentication checkout: https://github.com/pytorch/serve/blob/master/docs/token_authorization_api.md + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "torchserve.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + kubectl exec --namespace {{ .Release.Namespace }} $POD_NAME -- cat /home/model-server/key_file.json +{{- end }} diff --git a/workflows/charts/torchserve/templates/deploy.yaml b/workflows/charts/torchserve/templates/deploy.yaml index 544a2fb1..85f03142 100644 --- a/workflows/charts/torchserve/templates/deploy.yaml +++ b/workflows/charts/torchserve/templates/deploy.yaml @@ -47,6 +47,9 @@ spec: - configMapRef: name: {{ .Values.deploy.env.configMapName }} {{- end }} + env: + - name: TS_DISABLE_TOKEN_AUTHORIZATION + value: "{{ .Values.deploy.tokens_disabled }}" ports: - name: rest-1 containerPort: 8080 diff --git a/workflows/charts/torchserve/values.yaml b/workflows/charts/torchserve/values.yaml index e95efb15..f59e1c40 100644 --- a/workflows/charts/torchserve/values.yaml +++ b/workflows/charts/torchserve/values.yaml @@ -23,6 +23,8 @@ deploy: env: configMapName: intel-proxy-config enabled: true + # -- Set token authentication on or off. Checkout the latest [torchserve docs](https://github.com/pytorch/serve/blob/master/docs/token_authorization_api.md) for more details. + tokens_disabled: true # -- Models to be loaded models: all # -- Model Server Configuration file location From 4dded501f37391f91397f4c4a589977e5bedb1a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 11:34:05 -0700 Subject: [PATCH 08/56] Bump the pytorch group across 1 directory with 14 updates (#309) Signed-off-by: dependabot[bot] Signed-off-by: tylertitsworth Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: tylertitsworth --- pytorch/README.md | 31 +++++++++++++++++-------- pytorch/docker-compose.yaml | 36 ++++++++++++++--------------- pytorch/hf-genai-requirements.txt | 10 ++++---- pytorch/jupyter-requirements.txt | 2 +- pytorch/multinode/requirements.txt | 6 ++--- pytorch/requirements.txt | 10 ++++---- pytorch/serving/README.md | 6 ++--- pytorch/tests/tests.yaml | 20 ++++++++-------- pytorch/torchserve-requirements.txt | 6 ++--- pytorch/xpu-requirements.txt | 2 +- 10 files changed, 70 insertions(+), 59 deletions(-) diff --git a/pytorch/README.md b/pytorch/README.md index c2302f78..de9a45b3 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -66,7 +66,8 @@ The images below are built only with CPU optimizations (GPU acceleration support | Tag(s) | Pytorch | IPEX | Dockerfile | | -------------------------- | -------- | ------------ | --------------- | -| `2.3.0-pip-base`, `latest` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | +| `2.4.0-pip-base`, `latest` | [v2.4.0] | [v2.4.0+cpu] | [v0.4.0-Beta] | +| `2.3.0-pip-base` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | | `2.2.0-pip-base` | [v2.2.0] | [v2.2.0+cpu] | [v0.3.4] | | `2.1.0-pip-base` | [v2.1.0] | [v2.1.0+cpu] | [v0.2.3] | | `2.0.0-pip-base` | [v2.0.0] | [v2.0.0+cpu] | [v0.1.0] | @@ -83,6 +84,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Dockerfile | | ------------------- | -------- | ------------ | --------------- | +| `2.4.0-pip-jupyter` | [v2.4.0] | [v2.4.0+cpu] | [v0.4.0-Beta] | | `2.3.0-pip-jupyter` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | | `2.2.0-pip-jupyter` | [v2.2.0] | [v2.2.0+cpu] | [v0.3.4] | | `2.1.0-pip-jupyter` | [v2.1.0] | [v2.1.0+cpu] | [v0.2.3] | @@ -93,7 +95,7 @@ docker run -it --rm \ -p 8888:8888 \ -v $PWD/workspace:/workspace \ -w /workspace \ - intel/intel-extension-for-pytorch:2.3.0-pip-jupyter + intel/intel-extension-for-pytorch:2.4.0-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -104,6 +106,7 @@ The images below additionally include [Intel® oneAPI Collective Communications | Tag(s) | Pytorch | IPEX | oneCCL | INC | Dockerfile | | --------------------- | -------- | ------------ | -------------------- | --------- | -------------- | +| `2.4.0-pip-multinode` | [v2.4.0] | [v2.4.0+cpu] | [v2.4.0][ccl-v2.4.0] | [v3.0] | [v0.4.0-Beta] | | `2.3.0-pip-multinode` | [v2.3.0] | [v2.3.0+cpu] | [v2.3.0][ccl-v2.3.0] | [v2.6] | [v0.4.0-Beta] | | `2.2.0-pip-multinode` | [v2.2.2] | [v2.2.0+cpu] | [v2.2.0][ccl-v2.2.0] | [v2.6] | [v0.4.0-Beta] | | `2.1.100-pip-mulitnode` | [v2.1.2] | [v2.1.100+cpu] | [v2.1.0][ccl-v2.1.0] | [v2.6] | [v0.4.0-Beta] | @@ -186,7 +189,7 @@ To add these files correctly please follow the steps described below. -v $PWD/authorized_keys:/etc/ssh/authorized_keys \ -v $PWD/tests:/workspace/tests \ -w /workspace \ - intel/intel-extension-for-pytorch:2.3.0-pip-multinode \ + intel/intel-extension-for-pytorch:2.4.0-pip-multinode \ bash -c '/usr/sbin/sshd -D' ``` @@ -199,7 +202,7 @@ To add these files correctly please follow the steps described below. -v $PWD/tests:/workspace/tests \ -v $PWD/hostfile:/workspace/hostfile \ -w /workspace \ - intel/intel-extension-for-pytorch:2.3.0-pip-multinode \ + intel/intel-extension-for-pytorch:2.4.0-pip-multinode \ bash -c 'ipexrun cpu --nnodes 2 --nprocs-per-node 1 --master-addr 127.0.0.1 --master-port 3022 /workspace/tests/ipex-resnet50.py --ipex --device cpu --backend ccl' ``` @@ -227,7 +230,7 @@ Additionally, if you have a [DeepSpeed* configuration](https://www.deepspeed.ai/ -v $PWD/hostfile:/workspace/hostfile \ -v $PWD/ds_config.json:/workspace/ds_config.json \ -w /workspace \ - intel/intel-extension-for-pytorch:2.3.0-pip-multinode \ + intel/intel-extension-for-pytorch:2.4.0-pip-multinode \ bash -c 'deepspeed --launcher IMPI \ --master_addr 127.0.0.1 --master_port 3022 \ --deepspeed_config ds_config.json --hostfile /workspace/hostfile \ @@ -240,9 +243,9 @@ Additionally, if you have a [DeepSpeed* configuration](https://www.deepspeed.ai/ The image below is an extension of the IPEX Multi-Node Container designed to run Hugging Face Generative AI scripts. The container has the typical installations needed to run and fine tune PyTorch generative text models from Hugging Face. It can be used to run multinode jobs using the same instructions from the [IPEX Multi-Node container](#setup-and-run-ipex-multi-node-container). -| Tag(s) | Pytorch | IPEX | oneCCL | transformers | Dockerfile | -| --------------------- | -------- | ------------ | -------------------- | --------- | --------------- | -| `2.3.0-pip-multinode-hf-4.41.2-genai` | [v2.3.1](https://github.com/pytorch/pytorch/releases/tag/v2.3.1) | [v2.3.0+cpu] | [v2.3.0][ccl-v2.3.0] | [v4.41.2] | [v0.4.0-Beta] | +| Tag(s) | Pytorch | IPEX | oneCCL | HF Transformers | Dockerfile | +| ------------------------------------- | -------- | ------------ | -------------------- | --------------- | --------------- | +| `2.4.0-pip-multinode-hf-4.44.0-genai` | [v2.4.0] | [v2.4.0+cpu] | [v2.4.0][ccl-v2.4.0] | [v4.44.0] | [v0.4.0-Beta] | Below is an example that shows single node job with the existing [`finetune.py`](../workflows/charts/huggingface-llm/scripts/finetune.py) script. @@ -251,7 +254,7 @@ Below is an example that shows single node job with the existing [`finetune.py`] docker run -it \ -v $PWD/workflows/charts/huggingface-llm/scripts:/workspace/scripts \ -w /workspace/scripts \ - intel/intel-extension-for-pytorch:2.3.0-pip-multinode-hf-4.41.2-genai \ + intel/intel-extension-for-pytorch:2.4.0-pip-multinode-hf-4.44.0-genai \ bash -c 'python finetune.py ' ``` @@ -261,6 +264,7 @@ The images below are [TorchServe*] with CPU Optimizations: | Tag(s) | Pytorch | IPEX | Dockerfile | | ------------------- | -------- | ------------ | --------------- | +| `2.4.0-serving-cpu` | [v2.4.0] | [v2.4.0+cpu] | [v0.4.0-Beta] | | `2.3.0-serving-cpu` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | | `2.2.0-serving-cpu` | [v2.2.0] | [v2.2.0+cpu] | [v0.3.4] | @@ -272,6 +276,7 @@ The images below are built only with CPU optimizations (GPU acceleration support | Tag(s) | Pytorch | IPEX | Dockerfile | | ---------------- | -------- | ------------ | --------------- | +| `2.4.0-idp-base` | [v2.4.0] | [v2.4.0+cpu] | [v0.4.0-Beta] | | `2.3.0-idp-base` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | | `2.2.0-idp-base` | [v2.2.0] | [v2.2.0+cpu] | [v0.3.4] | | `2.1.0-idp-base` | [v2.1.0] | [v2.1.0+cpu] | [v0.2.3] | @@ -281,6 +286,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Dockerfile | | ------------------- | -------- | ------------ | --------------- | +| `2.4.0-idp-jupyter` | [v2.4.0] | [v2.4.0+cpu] | [v0.4.0-Beta] | | `2.3.0-idp-jupyter` | [v2.3.0] | [v2.3.0+cpu] | [v0.4.0-Beta] | | `2.2.0-idp-jupyter` | [v2.2.0] | [v2.2.0+cpu] | [v0.3.4] | | `2.1.0-idp-jupyter` | [v2.1.0] | [v2.1.0+cpu] | [v0.2.3] | @@ -290,6 +296,7 @@ The images below additionally include [Intel® oneAPI Collective Communications | Tag(s) | Pytorch | IPEX | oneCCL | INC | Dockerfile | | --------------------- | -------- | ------------ | -------------------- | --------- | --------------- | +| `2.4.0-idp-multinode` | [v2.4.0] | [v2.4.0+cpu] | [v2.4.0][ccl-v2.3.0] | [v3.0] | [v0.4.0-Beta] | | `2.3.0-idp-multinode` | [v2.3.0] | [v2.3.0+cpu] | [v2.3.0][ccl-v2.3.0] | [v2.6] | [v0.4.0-Beta] | | `2.2.0-idp-multinode` | [v2.2.0] | [v2.2.0+cpu] | [v2.2.0][ccl-v2.2.0] | [v2.4.1] | [v0.3.4] | | `2.1.0-idp-mulitnode` | [v2.1.0] | [v2.1.0+cpu] | [v2.1.0][ccl-v2.1.0] | [v2.3.1] | [v0.2.3] | @@ -380,6 +387,7 @@ It is the image user's responsibility to ensure that any use of The images below [v2.1.10+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.10%2Bxpu [v2.0.110+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.0.110%2Bxpu +[v2.4.0]: https://github.com/pytorch/pytorch/releases/tag/v2.4.0 [v2.3.0]: https://github.com/pytorch/pytorch/releases/tag/v2.3.0 [v2.2.2]: https://github.com/pytorch/pytorch/releases/tag/v2.2.2 [v2.2.0]: https://github.com/pytorch/pytorch/releases/tag/v2.2.0 @@ -388,11 +396,13 @@ It is the image user's responsibility to ensure that any use of The images below [v2.0.1]: https://github.com/pytorch/pytorch/releases/tag/v2.0.1 [v2.0.0]: https://github.com/pytorch/pytorch/releases/tag/v2.0.0 +[v3.0]: https://github.com/intel/neural-compressor/releases/tag/v3.0 [v2.6]: https://github.com/intel/neural-compressor/releases/tag/v2.6 [v2.4.1]: https://github.com/intel/neural-compressor/releases/tag/v2.4.1 [v2.3.1]: https://github.com/intel/neural-compressor/releases/tag/v2.3.1 [v2.1.1]: https://github.com/intel/neural-compressor/releases/tag/v2.1.1 +[v2.4.0+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.4.0%2Bcpu [v2.3.0+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.3.0%2Bcpu [v2.2.0+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.2.0%2Bcpu [v2.1.100+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.0%2Bcpu @@ -400,13 +410,14 @@ It is the image user's responsibility to ensure that any use of The images below [v2.0.100+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.0.0%2Bcpu [v2.0.0+cpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.0.0%2Bcpu +[ccl-v2.4.0]: https://github.com/intel/torch-ccl/releases/tag/v2.4.0%2Bcpu%2Brc0 [ccl-v2.3.0]: https://github.com/intel/torch-ccl/releases/tag/v2.3.0%2Bcpu [ccl-v2.2.0]: https://github.com/intel/torch-ccl/releases/tag/v2.2.0%2Bcpu [ccl-v2.1.0]: https://github.com/intel/torch-ccl/releases/tag/v2.1.0%2Bcpu [ccl-v2.0.0]: https://github.com/intel/torch-ccl/releases/tag/v2.1.0%2Bcpu -[v4.41.2]: https://github.com/huggingface/transformers/releases/tag/v4.41.2 +[v4.44.0]: https://github.com/huggingface/transformers/releases/tag/v4.44.0 [803]: https://dgpu-docs.intel.com/releases/LTS_803.29_20240131.html [736]: https://dgpu-docs.intel.com/releases/stable_736_25_20231031.html diff --git a/pytorch/docker-compose.yaml b/pytorch/docker-compose.yaml index 03f51ab4..75d2e8d2 100644 --- a/pytorch/docker-compose.yaml +++ b/pytorch/docker-compose.yaml @@ -25,16 +25,16 @@ services: BASE_IMAGE_NAME: ${BASE_IMAGE_NAME:-ubuntu} BASE_IMAGE_TAG: ${BASE_IMAGE_TAG:-22.04} GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-0} - IPEX_VERSION: ${IPEX_VERSION:-2.3.0} + IPEX_VERSION: ${IPEX_VERSION:-2.4.0} MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} NO_PROXY: '' PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} PYTHON_VERSION: ${PYTHON_VERSION:-3.10} - PYTORCH_VERSION: ${PYTORCH_VERSION:-2.3.0+cpu} + PYTORCH_VERSION: ${PYTORCH_VERSION:-2.4.0+cpu} REGISTRY: ${REGISTRY} REPO: ${REPO} - TORCHAUDIO_VERSION: ${TORCHAUDIO_VERSION:-2.3.0+cpu} - TORCHVISION_VERSION: ${TORCHVISION_VERSION:-0.18.0+cpu} + TORCHAUDIO_VERSION: ${TORCHAUDIO_VERSION:-2.4.0} + TORCHVISION_VERSION: ${TORCHVISION_VERSION:-0.19.0} context: . labels: dependency.python: ${PYTHON_VERSION:-3.10} @@ -43,21 +43,21 @@ services: org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.name: "intel/intel-optimized-pytorch" org.opencontainers.image.title: "Intel® Extension for PyTorch Base Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.2.0}-${PACKAGE_OPTION:-pip}-base + org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-base target: ipex-base-${PACKAGE_OPTION:-pip} command: > sh -c "python -c 'import torch; import intel_extension_for_pytorch as ipex; print(\"torch:\", torch.__version__, \" ipex:\",ipex.__version__)'" depends_on: - ${PACKAGE_OPTION:-pip} - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-base pull_policy: always jupyter: build: labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.2.0}-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for PyTorch Jupyter Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.2.0}-${PACKAGE_OPTION:-pip}-jupyter + org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-jupyter target: jupyter command: > bash -c "python -m jupyter --version" @@ -65,7 +65,7 @@ services: http_proxy: ${http_proxy} https_proxy: ${https_proxy} extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-jupyter network_mode: host ports: - 8888:8888 @@ -79,9 +79,9 @@ services: dependency.pip.apt.virtualenv: true dependency.pip.deepspeed: 0.14.4 dependency.python.pip: multinode/requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.2.0}-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for PyTorch MultiNode Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.2.0}-${PACKAGE_OPTION:-pip}-multinode + org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-multinode target: multinode command: > bash -c "python -c 'import neural_compressor;import oneccl_bindings_for_pytorch as oneccl;import deepspeed; @@ -89,7 +89,7 @@ services: \"\\nOneCCL:\", oneccl.__version__, \"\\nDeepspeed:\", deepspeed.__version__)'" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} shm_size: 2gb xpu: build: @@ -177,7 +177,7 @@ services: docs: serving org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch Serving Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.2.0}-serving-cpu + org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-serving-cpu target: torchserve command: torchserve --version entrypoint: "" @@ -192,14 +192,14 @@ services: hf-genai: build: args: - HF_VERSION: ${HF_VERSION:-4.41.2} + HF_VERSION: ${HF_VERSION:-4.44.0} labels: dependency.python.pip: hf-genai-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.3.0}-${PACKAGE_OPTION:-pip}-multinode" + org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-multinode" org.opencontainers.image.title: "Intel® Extension for PyTorch MultiNode Huggingface Generative AI Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.3.0}-${PACKAGE_OPTION:-pip}-multinode-hf-${HF_VERSION:-4.41.2}-genai" + org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-${PACKAGE_OPTION:-pip}-multinode-hf-${HF_VERSION:-4.44.0}-genai" target: hf-genai extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-hf-${HF_VERSION:-4.41.2} + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-hf-${HF_VERSION:-4.44.0} command: > - bash -c "python-c 'import transformers; print(transformers.__version__)'" + bash -c "python -c 'import transformers; print(transformers.__version__)'" diff --git a/pytorch/hf-genai-requirements.txt b/pytorch/hf-genai-requirements.txt index df77695f..6671cbaf 100644 --- a/pytorch/hf-genai-requirements.txt +++ b/pytorch/hf-genai-requirements.txt @@ -1,13 +1,13 @@ -accelerate==0.32.1 -datasets==2.20.0 +accelerate==0.33.0 +datasets==2.21.0 einops==0.8.0 evaluate==0.4.2 onnxruntime-extensions==0.11.0 onnxruntime==1.18.1 -peft==0.11.1 -protobuf==5.27.2 +peft==0.12.0 +protobuf==5.27.3 py-cpuinfo==9.0.0 scikit-learn==1.5.1 SentencePiece==0.2.0 tokenizers==0.19.1 -transformers==4.42.4 +transformers==4.44.0 diff --git a/pytorch/jupyter-requirements.txt b/pytorch/jupyter-requirements.txt index b5ab6652..e95ad6e8 100644 --- a/pytorch/jupyter-requirements.txt +++ b/pytorch/jupyter-requirements.txt @@ -1,4 +1,4 @@ -jupyterlab==4.3.0a2 +jupyterlab==4.3.0b0 jupyterhub==5.1.0 notebook==7.3.0a1 jupyter-server-proxy>=4.1.2 diff --git a/pytorch/multinode/requirements.txt b/pytorch/multinode/requirements.txt index 53f579ca..c941708a 100644 --- a/pytorch/multinode/requirements.txt +++ b/pytorch/multinode/requirements.txt @@ -1,5 +1,5 @@ -neural-compressor==2.6 -oneccl_bind_pt==2.3.0+cpu ---extra-index-url https://developer.intel.com/ipex-whl-stable-cpu +neural-compressor==3.0 +oneccl_bind_pt==2.4.0+cpu +--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ oneccl-devel>=2021.13.0 # required to build deepspeed ops mpi4py>=3.1.0 # required to build deepspeed ops diff --git a/pytorch/requirements.txt b/pytorch/requirements.txt index 6e20f9ea..33202d78 100644 --- a/pytorch/requirements.txt +++ b/pytorch/requirements.txt @@ -1,6 +1,6 @@ -torch==2.3.1 -torchvision==0.18.1 -torchaudio==2.3.1 +torch==2.4.0 +torchvision==0.19.0 +torchaudio==2.4.0 -f https://download.pytorch.org/whl/cpu/torch_stable.html -intel_extension_for_pytorch==2.3.100+cpu ---extra-index-url https://developer.intel.com/ipex-whl-stable-cpu +intel_extension_for_pytorch==2.4.0+cpu +--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ diff --git a/pytorch/serving/README.md b/pytorch/serving/README.md index 133c48f4..5e48251f 100644 --- a/pytorch/serving/README.md +++ b/pytorch/serving/README.md @@ -16,7 +16,7 @@ Follow the instructions found in the link above depending on whether you are int curl -O https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth docker run --rm -it \ -v $PWD:/home/model-server \ - intel/intel-optimized-pytorch:2.2.0-serving-cpu \ + intel/intel-optimized-pytorch:2.4.0-serving-cpu \ torch-model-archiver --model-name squeezenet \ --version 1.0 \ --model-file model-archive/model.py \ @@ -34,7 +34,7 @@ Test Torchserve with the new archived model. The example below is for the squeez docker run -d --rm --name server \ -v $PWD:/home/model-server/model-store \ --net=host \ - intel/intel-optimized-pytorch:2.2.0-serving-cpu + intel/intel-optimized-pytorch:2.4.0-serving-cpu # Verify that the container has launched successfully docker logs server # Attempt to register the model and make an inference request @@ -87,7 +87,7 @@ As demonstrated in the above example, models must be registered before they can -v $PWD:/home/model-server/model-store \ -v $PWD/config.properties:/home/model-server/config.properties \ --net=host \ - intel/intel-optimized-pytorch:2.2.0-serving-cpu + intel/intel-optimized-pytorch:2.4.0-serving-cpu # Verify that the container has launched successfully docker logs server # Check the models list diff --git a/pytorch/tests/tests.yaml b/pytorch/tests/tests.yaml index 1011c7a0..d903cece 100644 --- a/pytorch/tests/tests.yaml +++ b/pytorch/tests/tests.yaml @@ -13,34 +13,34 @@ # limitations under the License. import-ipex-cpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-base cmd: python -c "import torch;import intel_extension_for_pytorch as ipex;print(f'torch {torch.__version__} ipex {ipex.__version__}')" import-ipex-xpu-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-base cmd: python -c "import torch; import intel_extension_for_pytorch as ipex;[print(f'[{i}] {torch.xpu.get_device_properties(i)}') for i in range(torch.xpu.device_count())];" device: ["/dev/dri"] import-cpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-jupyter cmd: python -m jupyter --version import-xpu-jupyter-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-jupyter cmd: python -m jupyter --version device: ["/dev/dri"] import-cpu-oneccl-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: python -c "'import oneccl_bindings_for_pytorch as oneccl;print(oneccl.__version__)'" import-cpu-transformers-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-hf-${HF_VERSION:-4.41.2} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-hf-${HF_VERSION:-4.44.0} cmd: python -c "import transformers;print(f'transformers {transformers.__version__}');assert transformers.utils.import_utils.is_ipex_available()" import-cpu-inc-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: python -c "'import neural_compressor as inc;print(inc.__version__)'" import-cpu-deepspeed-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: ds_report shm_size: 2gb ipex-cpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-base cmd: python /tests/ipex-resnet50.py --ipex --device cpu --backend gloo volumes: - dst: /tests @@ -58,21 +58,21 @@ ipex-xpu-jupyter-${PACKAGE_OPTION:-pip}: device: ["/dev/dri"] notebook: True oneccl-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: ipexrun cpu /tests/ipex-resnet50.py --ipex --device cpu --backend ccl privileged: true volumes: - dst: /tests src: $PWD/pytorch/tests oneccl-ds-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: ipexrun cpu /tests/ipex-resnet50.py --ipex --device cpu --backend ccl --deepspeed privileged: true volumes: - dst: /tests src: $PWD/pytorch/tests inc-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.0}-oneccl-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-oneccl-inc-${INC_VERSION:-3.0} cmd: python /tests/inc-quant.py volumes: - dst: /tests diff --git a/pytorch/torchserve-requirements.txt b/pytorch/torchserve-requirements.txt index 0dbb45d5..f495a686 100644 --- a/pytorch/torchserve-requirements.txt +++ b/pytorch/torchserve-requirements.txt @@ -7,6 +7,6 @@ torch-model-archiver==0.11.1 torch-workflow-archiver==0.2.14 torchserve==0.11.1 torchtext==0.18.0 -torchvision==0.18.1 --f https://developer.intel.com/ipex-whl-stable-cpu -intel_extension_for_pytorch==2.3.100+cpu +torchvision==0.19.0 +-f https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ +intel_extension_for_pytorch==2.4.0+cpu diff --git a/pytorch/xpu-requirements.txt b/pytorch/xpu-requirements.txt index b64b92a4..73129f9d 100644 --- a/pytorch/xpu-requirements.txt +++ b/pytorch/xpu-requirements.txt @@ -3,5 +3,5 @@ torchvision==0.16.0.post2+cxx11.abi torchaudio==2.1.0.post2+cxx11.abi intel_extension_for_pytorch==2.1.30+xpu oneccl_bind_pt==2.1.300+xpu ---extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us +--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/ setuptools==71.1.0 From 4a7f238c7c625dca543644d8633042b968bd290d Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Wed, 14 Aug 2024 13:18:47 -0700 Subject: [PATCH 09/56] update ipex layers to 2.1.40-xpu (#305) Signed-off-by: Srikanth Ramakrishna Signed-off-by: sharvil10 Signed-off-by: Tyler Titsworth Signed-off-by: dependabot[bot] Signed-off-by: tylertitsworth Signed-off-by: Srikanth Ramakrishna Co-authored-by: Sharvil Shah Co-authored-by: Tyler Titsworth Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pytorch/Dockerfile | 18 +++-------- pytorch/README.md | 18 +++++++---- pytorch/docker-compose.yaml | 58 ++++++++++++++++-------------------- pytorch/tests/tests.yaml | 8 ++--- pytorch/xpu-requirements.txt | 15 +++++----- 5 files changed, 53 insertions(+), 64 deletions(-) diff --git a/pytorch/Dockerfile b/pytorch/Dockerfile index 0a68ac58..1a5b497d 100644 --- a/pytorch/Dockerfile +++ b/pytorch/Dockerfile @@ -36,11 +36,6 @@ ARG PYTHON_VERSION ARG PYTHON_BASE=${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER}-${BASE_IMAGE_NAME}-${BASE_IMAGE_TAG}-${PACKAGE_OPTION}-py${PYTHON_VERSION}-base FROM ${PYTHON_BASE} AS ipex-base-pip -ARG IPEX_VERSION -ARG PYTORCH_VERSION -ARG TORCHAUDIO_VERSION -ARG TORCHVISION_VERSION - WORKDIR / COPY requirements.txt . @@ -49,11 +44,6 @@ RUN python -m pip install --no-cache-dir -r requirements.txt && \ FROM ${PYTHON_BASE} AS ipex-base-idp -ARG IPEX_VERSION -ARG PYTORCH_VERSION -ARG TORCHAUDIO_VERSION -ARG TORCHVISION_VERSION - WORKDIR / COPY requirements.txt . @@ -158,8 +148,8 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \ - gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg -RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy/lts/2350 unified" | \ + gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy unified" | \ tee /etc/apt/sources.list.d/intel-gpu-jammy.list ARG ICD_VER @@ -171,8 +161,8 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends --fix-missing \ intel-opencl-icd=${ICD_VER} \ intel-level-zero-gpu=${LEVEL_ZERO_GPU_VER} \ - level-zero=${LEVEL_ZERO_VER} \ - level-zero-dev=${LEVEL_ZERO_DEV_VER} && \ + libze1=${LEVEL_ZERO_VER} \ + libze-dev=${LEVEL_ZERO_DEV_VER} && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* diff --git a/pytorch/README.md b/pytorch/README.md index de9a45b3..53adcb1d 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -24,6 +24,7 @@ The images below include support for both CPU and GPU optimizations: | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------------- | -------- | -------------- | ------ | --------------- | +| `2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.20-xpu` | [v2.1.0] | [v2.1.20+xpu] | [803] | [v0.3.4] | | `2.1.10-xpu` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | @@ -36,7 +37,7 @@ docker run -it --rm \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ --ipc=host \ - intel/intel-extension-for-pytorch:2.1.30-xpu + intel/intel-extension-for-pytorch:2.1.40-xpu ``` --- @@ -45,8 +46,9 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | -| `2.1.20-xpu-pip-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | -| `2.1.10-xpu-pip-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | +| `2.1.40-xpu-pip-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | +| `2.1.20-xpu-pip-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | +| `2.1.10-xpu-pip-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | ### Run the XPU Jupyter Container @@ -55,7 +57,7 @@ docker run -it --rm \ -p 8888:8888 \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ - intel/intel-extension-for-pytorch:2.1.20-xpu-pip-jupyter + intel/intel-extension-for-pytorch:2.1.40-xpu-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -308,6 +310,7 @@ The images below are built only with CPU and GPU optimizations and include [Inte | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------- | -------- | ------------ | -------- | ------ | +| `2.1.40-xpu-idp-base` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu-idp-base` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.10-xpu-idp-base` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | @@ -315,8 +318,9 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | -| `2.1.20-xpu-idp-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | -| `2.1.10-xpu-idp-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | +| `2.1.40-xpu-idp-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | +| `2.1.20-xpu-idp-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | +| `2.1.10-xpu-idp-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | ## Build from Source @@ -382,6 +386,7 @@ It is the image user's responsibility to ensure that any use of The images below [v0.2.3]: https://github.com/intel/ai-containers/blob/v0.2.3/pytorch/Dockerfile [v0.1.0]: https://github.com/intel/ai-containers/blob/v0.1.0/pytorch/Dockerfile +[v2.1.40+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.40%2Bxpu [v2.1.30+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.30%2Bxpu [v2.1.20+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.20%2Bxpu [v2.1.10+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.10%2Bxpu @@ -419,6 +424,7 @@ It is the image user's responsibility to ensure that any use of The images below [v4.44.0]: https://github.com/huggingface/transformers/releases/tag/v4.44.0 +[914]: https://dgpu-docs.intel.com/releases/stable_914_33_20240730.html [803]: https://dgpu-docs.intel.com/releases/LTS_803.29_20240131.html [736]: https://dgpu-docs.intel.com/releases/stable_736_25_20231031.html [647]: https://dgpu-docs.intel.com/releases/stable_647_21_20230714.html diff --git a/pytorch/docker-compose.yaml b/pytorch/docker-compose.yaml index 75d2e8d2..838ee5cb 100644 --- a/pytorch/docker-compose.yaml +++ b/pytorch/docker-compose.yaml @@ -94,38 +94,34 @@ services: xpu: build: args: - CCL_VER: ${CCL_VER:-2021.12.0-309} - DPCPP_VER: ${DPCPP_VER:-2024.1.0-963} - ICD_VER: ${ICD_VER:-23.43.27642.40-803~22.04} - IPEX_XPU_VERSION: ${IPEX_VERSION:-2.1.20} - LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} - LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} - LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} - MKL_VER: ${MKL_VER:-2024.1.0-691} + CCL_VER: ${CCL_VER:-2021.13.1-31} + DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} + ICD_VER: ${ICD_VER:-24.22.29735.27-914~22.04} + LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} + LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} + LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} + MKL_VER: ${MKL_VER:-2024.2.1-103} NO_PROXY: '' - ONECCL_VERSION: ${ONECCL_VERSION:-2.1.300} PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} - PYTORCH_XPU_VERSION: ${PYTORCH_VERSION:-2.1.0} - TORCHVISION_XPU_VERSION: ${TORCHVISION_VERSION:-0.16.0} labels: dependency.apt.build-essential: true dependency.apt.clinfo: true dependency.apt.git: true dependency.apt.gnupg2: true dependency.apt.gpg-agent: true - dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} - dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.12.0-309} - dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.1.0-963} - dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.1.0-691} + dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} + dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.13.1-31} + dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.2.1-1079} + dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.2.1-103} dependency.apt.intel-opencl-icd: ${ICD_VER:-23.43.27642.40-803~22.04} - dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} - dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} + dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} + dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} dependency.apt.rsync: true dependency.apt.unzip: true dependency.idp.pip: false org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Base Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.1.20}-xpu-${PACKAGE_OPTION:-pip}-base + org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-base target: ipex-xpu-base command: > python -c "import torch;print(torch.device('xpu'));import @@ -135,33 +131,29 @@ services: {ipex.xpu.get_device_properties(i)}') for i in range(ipex.xpu.device_count())];" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base xpu-jupyter: build: args: - CCL_VER: ${CCL_VER:-2021.12.0-309} - DPCPP_VER: ${DPCPP_VER:-2024.1.0-963} - ICD_VER: ${ICD_VER:-23.43.27642.40-803~22.04} - IPEX_XPU_VERSION: ${IPEX_VERSION:-2.1.20} - LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} - LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} - LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} - MKL_VER: ${MKL_VER:-2024.1.0-691} + CCL_VER: ${CCL_VER:-2021.13.1-31} + DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} + ICD_VER: ${ICD_VER:-24.22.29735.27-914~22.04} + LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} + LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} + LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} + MKL_VER: ${MKL_VER:-2024.2.1-103} NO_PROXY: '' - ONECCL_VERSION: ${ONECCL_VERSION:-2.1.200} PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} - PYTORCH_XPU_VERSION: ${PYTORCH_VERSION:-2.1.0} - TORCHVISION_XPU_VERSION: ${TORCHVISION_VERSION:-0.16.0} labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.1.20}-xpu-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Jupyter Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.1.20}-xpu-${PACKAGE_OPTION:-pip}-jupyter + org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-jupyter target: ipex-xpu-jupyter command: > bash -c "python -m jupyter --version" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter ports: - 8888:8888 torchserve: diff --git a/pytorch/tests/tests.yaml b/pytorch/tests/tests.yaml index d903cece..21aeeadc 100644 --- a/pytorch/tests/tests.yaml +++ b/pytorch/tests/tests.yaml @@ -16,14 +16,14 @@ import-ipex-cpu-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-base cmd: python -c "import torch;import intel_extension_for_pytorch as ipex;print(f'torch {torch.__version__} ipex {ipex.__version__}')" import-ipex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base cmd: python -c "import torch; import intel_extension_for_pytorch as ipex;[print(f'[{i}] {torch.xpu.get_device_properties(i)}') for i in range(torch.xpu.device_count())];" device: ["/dev/dri"] import-cpu-jupyter-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-jupyter cmd: python -m jupyter --version import-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter cmd: python -m jupyter --version device: ["/dev/dri"] import-cpu-oneccl-${PACKAGE_OPTION:-pip}: @@ -46,14 +46,14 @@ ipex-cpu-${PACKAGE_OPTION:-pip}: - dst: /tests src: $PWD/pytorch/tests ipex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base cmd: python /tests/ipex-resnet50.py --ipex --device xpu device: ["/dev/dri"] volumes: - dst: /tests src: $PWD/pytorch/tests ipex-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.30xpu}-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter cmd: papermill --log-output /jupyter/xpu.ipynb -k python3 device: ["/dev/dri"] notebook: True diff --git a/pytorch/xpu-requirements.txt b/pytorch/xpu-requirements.txt index 73129f9d..5d7d2e8a 100644 --- a/pytorch/xpu-requirements.txt +++ b/pytorch/xpu-requirements.txt @@ -1,7 +1,8 @@ -torch==2.1.0.post2+cxx11.abi -torchvision==0.16.0.post2+cxx11.abi -torchaudio==2.1.0.post2+cxx11.abi -intel_extension_for_pytorch==2.1.30+xpu -oneccl_bind_pt==2.1.300+xpu ---extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us/ -setuptools==71.1.0 +torch==2.1.0.post3+cxx11.abi +torchvision==0.16.0.post3+cxx11.abi +torchaudio==2.1.0.post3+cxx11.abi +intel_extension_for_pytorch==2.1.40+xpu +oneccl_bind_pt==2.1.400+xpu +--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us +setuptools==69.5.1 +numpy==1.26.4 From 14fa971ea5c82384f72fbd7cd01841622b3d0f4c Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Wed, 14 Aug 2024 14:33:28 -0700 Subject: [PATCH 10/56] update pytorch cpu whl index (#312) Signed-off-by: tylertitsworth --- pytorch/requirements.txt | 8 ++++---- pytorch/serving/build-kfs.sh | 2 +- pytorch/serving/patch.yaml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pytorch/requirements.txt b/pytorch/requirements.txt index 33202d78..664b5ad8 100644 --- a/pytorch/requirements.txt +++ b/pytorch/requirements.txt @@ -1,6 +1,6 @@ -torch==2.4.0 -torchvision==0.19.0 -torchaudio==2.4.0 --f https://download.pytorch.org/whl/cpu/torch_stable.html +torch==2.4.0+cpu +torchvision==0.19.0+cpu +torchaudio==2.4.0+cpu +--extra-index-url https://download.pytorch.org/whl/cpu intel_extension_for_pytorch==2.4.0+cpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ diff --git a/pytorch/serving/build-kfs.sh b/pytorch/serving/build-kfs.sh index 7cdedc93..89e30823 100755 --- a/pytorch/serving/build-kfs.sh +++ b/pytorch/serving/build-kfs.sh @@ -16,7 +16,7 @@ cd .. || exit docker compose pull torchserve -docker tag "$(docker images -q | head -n1)" intel/torchserve:latest +docker tag "${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-ubuntu-22.04-py3.10-torchserve" intel/torchserve:latest git clone https://github.com/pytorch/serve cd serve/kubernetes/kserve || exit git apply ../../../serving/kfs.patch diff --git a/pytorch/serving/patch.yaml b/pytorch/serving/patch.yaml index 487eab34..cd18e847 100644 --- a/pytorch/serving/patch.yaml +++ b/pytorch/serving/patch.yaml @@ -242,7 +242,7 @@ spec: - grpc-v1 containers: - name: kserve-container - image: "intel/intel-extension-for-pytorch:2.2.0-serving-cpu-kserve" + image: "intel/intel-extension-for-pytorch:2.4.0-serving-cpu-kserve" args: - torchserve - --start From 7b8b19b19a36d3ac281ab7b87eb393f63bf5bb2c Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Fri, 16 Aug 2024 09:24:00 -0700 Subject: [PATCH 11/56] Update Intel Optimized ML Docs (#313) Signed-off-by: tylertitsworth --- classical-ml/README.md | 15 ++++++++++++--- classical-ml/docker-compose.yaml | 10 +++++----- classical-ml/tests/tests.yaml | 6 +++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/classical-ml/README.md b/classical-ml/README.md index 06cfa613..9d63355c 100644 --- a/classical-ml/README.md +++ b/classical-ml/README.md @@ -10,7 +10,8 @@ The images below include [Intel® Extension for Scikit-learn*] and [XGBoost*]. | Tag(s) | Intel SKLearn | Scikit-learn | XGBoost | Dockerfile | | ------------------------------------------------- | -------------- | ------------ | -------- | --------------- | -| `2024.5.0-pip-base`, `latest` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.6.0-pip-base`, `latest` | [v2024.6.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.5.0-pip-base` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | | `2024.3.0-pip-base` | [v2024.3.0] | [v1.4.2] | [v2.0.3] | [v0.4.0-Beta] | | `2024.2.0-xgboost-2.0.3-pip-base` | [v2024.2.0] | [v1.4.1] | [v2.0.3] | [v0.4.0-Beta] | | `scikit-learning-2024.0.0-xgboost-2.0.2-pip-base` | [v2024.0.0] | [v1.3.2] | [v2.0.2] | [v0.3.4] | @@ -19,6 +20,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Intel SKLearn | Scikit-learn | XGBoost | Dockerfile | | ---------------------------------------------------- | -------------- | ------------ | -------- | --------------- | +| `2024.6.0-pip-jupyter` | [v2024.6.0] | [v1.5.1] | [v2.1.1] | [v0.4.0] | | `2024.5.0-pip-jupyter` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | | `2024.3.0-pip-jupyter` | [v2024.3.0] | [v1.4.2] | [v2.0.3] | [v0.4.0-Beta] | | `2024.2.0-xgboost-2.0.3-pip-jupyter` | [v2024.2.0] | [v1.4.1] | [v2.0.3] | [v0.4.0-Beta] | @@ -43,7 +45,9 @@ The images below include [Intel® Distribution for Python*]: | Tag(s) | Intel SKLearn | Scikit-learn | XGBoost | Dockerfile | | ------------------------------------------------- | -------------- | ------------ | -------- | --------------- | -| `2024.3.0-idp-base` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.6.0-idp-base` | [v2024.6.0] | [v1.5.1] | [v2.1.1] | [v0.4.0] | +| `2024.5.0-idp-base` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.3.0-idp-base` | [v2024.3.0] | [v1.4.1] | [v2.1.0] | [v0.4.0] | | `2024.2.0-xgboost-2.0.3-idp-base` | [v2024.2.0] | [v1.4.1] | [v2.0.3] | [v0.4.0-Beta] | | `scikit-learning-2024.0.0-xgboost-2.0.2-idp-base` | [v2024.0.0] | [v1.3.2] | [v2.0.2] | [v0.3.4] | @@ -51,7 +55,9 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Intel SKLearn | Scikit-learn | XGBoost | Dockerfile | | ---------------------------------------------------- | -------------- | ------------ | -------- | --------------- | -| `2024.3.0-idp-jupyter` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.6.0-idp-jupyter` | [v2024.6.0] | [v1.5.1] | [v2.1.1] | [v0.4.0] | +| `2024.5.0-idp-jupyter` | [v2024.5.0] | [v1.5.0] | [v2.1.0] | [v0.4.0] | +| `2024.3.0-idp-jupyter` | [v2024.3.0] | [v1.4.0] | [v2.1.0] | [v0.4.0] | | `2024.2.0-xgboost-2.0.3-idp-jupyter` | [v2024.2.0] | [v1.4.1] | [v2.0.3] | [v0.4.0-Beta] | | `scikit-learning-2024.0.0-xgboost-2.0.2-idp-jupyter` | [v2024.0.0] | [v1.3.2] | [v2.0.2] | [v0.3.4] | @@ -89,16 +95,19 @@ It is the image user's responsibility to ensure that any use of The images below [Scikit-learn*]: https://scikit-learn.org/stable/ [XGBoost*]: https://github.com/dmlc/xgboost +[v2024.6.0]: https://github.com/intel/scikit-learn-intelex/releases/tag/2024.6.0 [v2024.5.0]: https://github.com/intel/scikit-learn-intelex/releases/tag/2024.5.0 [v2024.3.0]: https://github.com/intel/scikit-learn-intelex/releases/tag/2024.3.0 [v2024.2.0]: https://github.com/intel/scikit-learn-intelex/releases/tag/2024.2.0 [v2024.0.0]: https://github.com/intel/scikit-learn-intelex/releases/tag/2024.0.0 +[v1.5.1]: https://github.com/scikit-learn/scikit-learn/releases/tag/1.5.1 [v1.5.0]: https://github.com/scikit-learn/scikit-learn/releases/tag/1.5.0 [v1.4.2]: https://github.com/scikit-learn/scikit-learn/releases/tag/1.4.2 [v1.4.1]: https://github.com/scikit-learn/scikit-learn/releases/tag/1.4.1 [v1.3.2]: https://github.com/scikit-learn/scikit-learn/releases/tag/1.3.2 +[v2.1.1]: https://github.com/dmlc/xgboost/releases/tag/v2.1.1 [v2.1.0]: https://github.com/dmlc/xgboost/releases/tag/v2.1.0 [v2.0.3]: https://github.com/dmlc/xgboost/releases/tag/v2.0.3 [v2.0.2]: https://github.com/dmlc/xgboost/releases/tag/v2.0.2 diff --git a/classical-ml/docker-compose.yaml b/classical-ml/docker-compose.yaml index 0a775bdc..491005de 100644 --- a/classical-ml/docker-compose.yaml +++ b/classical-ml/docker-compose.yaml @@ -40,21 +40,21 @@ services: org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.name: "intel/intel-optimized-ml" org.opencontainers.image.title: "Intel® Optimized ML Base Image" - org.opencontainers.image.version: ${SKLEARN_VERSION:-2024.4.0}-${PACKAGE_OPTION:-pip}-base + org.opencontainers.image.version: ${SKLEARN_VERSION:-2024.6.0}-${PACKAGE_OPTION:-pip}-base target: ml-base-${PACKAGE_OPTION:-pip} command: > bash -c "python -c 'import sklearnex, sklearn; import xgboost as xgb; print(\"Scikit version:\", sklearn.__version__, \"\\nXGBoost version:\", xgb.__version__)'" depends_on: - ${PACKAGE_OPTION:-pip} - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SCIKIT_VERSION:-2024.5.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SKLEARN_VERSION:-2024.6.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base pull_policy: always jupyter: build: labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-ml:${SKLEARN_VERSION:-2024.4.0}-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-ml:${SKLEARN_VERSION:-2024.6.0}-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Optimized ML Jupyter Base Image" - org.opencontainers.image.version: ${SKLEARN_VERSION:-2024.4.0}-${PACKAGE_OPTION:-pip}-jupyter + org.opencontainers.image.version: ${SKLEARN_VERSION:-2024.6.0}-${PACKAGE_OPTION:-pip}-jupyter target: jupyter command: > bash -c "python -m jupyter --version" @@ -62,5 +62,5 @@ services: http_proxy: ${http_proxy} https_proxy: ${https_proxy} extends: ml-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SCIKIT_VERSION:-2024.5.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SKLEARN_VERSION:-2024.6.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-jupyter network_mode: host diff --git a/classical-ml/tests/tests.yaml b/classical-ml/tests/tests.yaml index 0016987b..197dd285 100644 --- a/classical-ml/tests/tests.yaml +++ b/classical-ml/tests/tests.yaml @@ -14,13 +14,13 @@ --- classical-ml-import-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SCIKIT_VERSION:-2024.5.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SKLEARN_VERSION:-2024.6.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base cmd: python -c "from sklearnex import patch_sklearn; patch_sklearn();import xgboost as xgb; print(xgb.__version__)" classical-ml-import-${PACKAGE_OPTION:-pip}-jupyter: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SCIKIT_VERSION:-2024.5.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SKLEARN_VERSION:-2024.6.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-jupyter cmd: sh -c "python -m jupyter --version" classical-ml-performance-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SCIKIT_VERSION:-2024.5.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-scikit-learn-${SKLEARN_VERSION:-2024.6.0}-xgboost-${XGBOOST_VERSION:-2.1.0}-base cmd: python /tests/performance.py volumes: - src: $PWD/classical-ml/tests From d952100ba4bbf44826a25a92b33074bc16578347 Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Fri, 16 Aug 2024 14:13:21 -0700 Subject: [PATCH 12/56] rework PR 310 for review (#314) Signed-off-by: Srikanth Ramakrishna --- tensorflow/Dockerfile | 7 +++---- tensorflow/README.md | 21 +++++++++++++++++++-- tensorflow/docker-compose.yaml | 26 +++++++++++++------------- tensorflow/tests/tests.yaml | 12 ++++++++---- tensorflow/xpu-requirements.txt | 4 ++-- 5 files changed, 45 insertions(+), 25 deletions(-) diff --git a/tensorflow/Dockerfile b/tensorflow/Dockerfile index 47dc728f..a54b5466 100644 --- a/tensorflow/Dockerfile +++ b/tensorflow/Dockerfile @@ -254,11 +254,10 @@ RUN no_proxy="" NO_PROXY="" apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* -ARG TF_VER="2.15.0" +ARG ITEX_VER="2.15.0.1" -RUN conda install intel-extension-for-tensorflow=${TF_VER}=*xpu* \ - -c https://software.repos.intel.com/python/conda \ - -c conda-forge +RUN conda install -n idp -y intel-extension-for-tensorflow=${ITEX_VER}=*xpu* \ + -c https://software.repos.intel.com/python/conda ENV LD_LIBRARY_PATH=/opt/conda/envs/idp/lib:$LD_LIBRARY_PATH diff --git a/tensorflow/README.md b/tensorflow/README.md index c92533ef..195cebdf 100644 --- a/tensorflow/README.md +++ b/tensorflow/README.md @@ -37,6 +37,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | TensorFlow | IPEX | Driver | Dockerfile | | ------------- | ----------- | ------------- | ------ | --------------- | +| `2.15.0.1-xpu-jupyter` | [v2.15.1] | [v2.15.0.1] | [803.63]| [v0.4.0-Beta] | | `xpu-jupyter` | [v2.14.1] | [v2.14.0.1] | [736] | [v0.3.4] | ### Run the XPU Jupyter Container @@ -48,7 +49,7 @@ docker run -it --rm \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ --ipc=host \ - intel/intel-extension-for-tensorflow:xpu-jupyter + intel/intel-extension-for-tensorflow:2.15.0.1-xpu-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -104,7 +105,7 @@ docker run -it --rm \ --net=host \ -v $PWD/workspace:/workspace \ -w /workspace \ - intel/intel-extension-for-tensorflow:xpu-jupyter + intel/intel-extension-for-tensorflow:2.15.0-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -170,6 +171,22 @@ The images below additionally include [Horovod]: | `2.14.0-idp-openmpi-multinode` | [v2.14.1] | [v2.14.0.1] | [v0.28.1] | [v0.3.4] | | `2.13-idp-openmpi-mulitnode` | [v2.13.0] | [v2.13.0.0] | [v0.28.0] | [v0.2.3] | +## XPU images with Intel® Distribution for Python* + +The images below are built only with CPU and GPU optimizations and include [Intel® Distribution for Python*]: + +| Tag(s) | Pytorch | ITEX | Driver | Dockerfile | +| ---------------- | -------- | ------------ | -------- | ------ | +| `2.15.0.1-xpu-idp-base` | [v2.15.1] | [v2.15.0.1] | [803] | [v0.4.0-Beta] | +| `2.15.0-xpu-idp-base` | [v2.15.0] | [v2.15.0.0] | [803] | [v0.4.0-Beta] | + +The images below additionally include [Jupyter Notebook](https://jupyter.org/) server: + +| Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | +| --------------------- | -------- | ------------- | ------ | ------------ | --------------- | +| `2.15.0.1-xpu-idp-jupyter` | [v2.15.1] | [v2.15.0.1] | [803] | `8888` | [v0.4.0-Beta] | +| `2.15.0-xpu-idp-jupyter` | [v2.1.0] | [v2.15.0.0] | [803] | `8888` | [v0.4.0-Beta] | + ## Build from Source To build the images from source, clone the [Intel® AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: diff --git a/tensorflow/docker-compose.yaml b/tensorflow/docker-compose.yaml index 2d7e84a0..9583b296 100644 --- a/tensorflow/docker-compose.yaml +++ b/tensorflow/docker-compose.yaml @@ -95,10 +95,10 @@ services: LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} - DPCPP_VER: ${DPCPP_VER:-2024.1.0-963} - MKL_VER: ${MKL_VER:-2024.1.0-691} - CCL_VER: ${CCL_VER:-2021.12.0-309} - TF_VER: ${TF_VER:-2.15.0} + DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} + MKL_VER: ${MKL_VER:-2024.2.1-103} + CCL_VER: ${CCL_VER:-2021.13.1-31} + TF_VER: ${TF_VER:-2.15.1} no_proxy: '' NO_PROXY: '' labels: @@ -108,9 +108,9 @@ services: dependency.apt.gnupg2: true dependency.apt.gpg-agent: true dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} - dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.12.0-309} - dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.1.0-963} - dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.1.0-691} + dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.13.1-31} + dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.2.1-1079} + dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.2.1-103} dependency.apt.intel-opencl-icd: ${ICD_VER:-23.43.27642.40-803~22.04} dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} @@ -124,7 +124,7 @@ services: command: > sh -c "python -c 'import tensorflow as tf;print(tf.__version__);from tensorflow.python.client import device_lib;print(device_lib.list_local_devices())'" extends: tf-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-base xpu-jupyter: build: args: @@ -132,10 +132,10 @@ services: LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.27642.40-803~22.04} LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.14.0-744~22.04} LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.14.0-744~22.04} - DPCPP_VER: ${DPCPP_VER:-2024.1.0-963} - MKL_VER: ${MKL_VER:-2024.1.0-691} - CCL_VER: ${CCL_VER:-2021.12.0-309} - TF_VER: ${TF_VER:-2.15.0} + DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} + MKL_VER: ${MKL_VER:-2024.2.1-103} + CCL_VER: ${CCL_VER:-2021.13.1-31} + ITEX_VER: ${ITEX_VER:-2.15.0.1} no_proxy: '' NO_PROXY: '' labels: @@ -147,4 +147,4 @@ services: extends: tf-base command: > bash -c "python -m jupyter --version" - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-jupyter diff --git a/tensorflow/tests/tests.yaml b/tensorflow/tests/tests.yaml index 0d45d9e8..0fa5b2b3 100644 --- a/tensorflow/tests/tests.yaml +++ b/tensorflow/tests/tests.yaml @@ -17,8 +17,9 @@ import-itex-cpu-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-base cmd: python -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())" import-itex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-base cmd: python /tests/xpu_import_test.py + device: ["/dev/dri"] volumes: - src: ${PWD}/tensorflow/tests dst: /tests @@ -26,8 +27,9 @@ import-cpu-jupyter-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-jupyter cmd: python -m jupyter --version import-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-jupyter cmd: python -m jupyter --version + device: ["/dev/dri"] import-multinode-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} cmd: horovodrun --check-build && mpirun --version && python -c 'import horovod.tensorflow as hvd;hvd.init();import horovod.tensorflow' @@ -41,15 +43,17 @@ itex-cpu-${PACKAGE_OPTION:-pip}: - src: ${PWD}/tensorflow/tests dst: /tests itex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-base cmd: python /tests/tf_base_test.py + device: ["/dev/dri"] volumes: - dst: /tests src: $PWD/tensorflow/tests itex-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.0}-itex-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-jupyter cmd: papermill --log-output /jupyter/xpu.ipynb -k python3 - notebook: True + device: ["/dev/dri"] multinode-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} cmd: horovodrun -np 2 -H localhost:2 --binding-args="-bind-to socket -map-by socket" python /tests/tf_base_test.py diff --git a/tensorflow/xpu-requirements.txt b/tensorflow/xpu-requirements.txt index c7099048..0280ef9d 100644 --- a/tensorflow/xpu-requirements.txt +++ b/tensorflow/xpu-requirements.txt @@ -1,2 +1,2 @@ -tensorflow==2.15.0 -intel-extension-for-tensorflow[xpu]==2.15.0.0 +tensorflow==2.15.1 +intel-extension-for-tensorflow[xpu]==2.15.0.1 From 47d5dfb067cce9ee5a60322671234979eb919cec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 19:23:50 -0700 Subject: [PATCH 13/56] Bump the tensorflow group across 1 directory with 8 updates (#315) Signed-off-by: dependabot[bot] Signed-off-by: tylertitsworth Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: tylertitsworth --- tensorflow/Dockerfile | 150 +++++++-------------- tensorflow/README.md | 103 +++++++++++++- tensorflow/docker-compose.yaml | 39 +++--- tensorflow/hvd-requirements.txt | 1 - tensorflow/jupyter-requirements.txt | 4 +- tensorflow/multinode-requirements.txt | 3 - tensorflow/multinode/dockerd-entrypoint.sh | 21 +++ tensorflow/multinode/generate_ssh_keys.sh | 28 ++++ tensorflow/multinode/requirements.txt | 5 + tensorflow/multinode/ssh_config | 4 + tensorflow/multinode/sshd_config | 12 ++ tensorflow/ompi-requirements.txt | 1 - tensorflow/requirements.txt | 6 +- tensorflow/serving/requirements.txt | 8 +- tensorflow/tests/tests.yaml | 14 +- tensorflow/xpu-requirements.txt | 2 +- 16 files changed, 258 insertions(+), 143 deletions(-) delete mode 100644 tensorflow/hvd-requirements.txt delete mode 100644 tensorflow/multinode-requirements.txt create mode 100755 tensorflow/multinode/dockerd-entrypoint.sh create mode 100755 tensorflow/multinode/generate_ssh_keys.sh create mode 100644 tensorflow/multinode/requirements.txt create mode 100644 tensorflow/multinode/ssh_config create mode 100644 tensorflow/multinode/sshd_config delete mode 100644 tensorflow/ompi-requirements.txt diff --git a/tensorflow/Dockerfile b/tensorflow/Dockerfile index a54b5466..48fb7332 100644 --- a/tensorflow/Dockerfile +++ b/tensorflow/Dockerfile @@ -33,12 +33,11 @@ ENV KMP_AFFINITY='granularity=fine,verbose,compact,1,0' \ KMP_BLOCKTIME=1 \ KMP_SETTINGS=1 -ARG TF_VERSION - WORKDIR / COPY requirements.txt . -RUN python -m pip install --no-cache-dir -r requirements.txt +RUN python -m pip install --no-cache-dir -r requirements.txt && \ + rm -rf requirements.txt ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/THIRD-PARTY-PROGRAMS.txt /licenses/ ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/third-party-program-of-intel-extension-for-tensorflow.txt /licenses/ @@ -53,12 +52,13 @@ ENV KMP_AFFINITY='granularity=fine,verbose,compact,1,0' \ ENV PATH /usr/bin:/root/conda/envs/idp/bin:/root/conda/condabin:~/conda/bin/:${PATH} ENV TF_ENABLE_ONEDNN_OPTS=1 -ARG TF_VERSION WORKDIR / COPY requirements.txt . -RUN python -m pip install --no-cache-dir -r requirements.txt +RUN conda run -n idp python -m pip install --no-cache-dir -r requirements.txt && \ + rm -rf requirements.txt && \ + conda clean -y --all ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/THIRD-PARTY-PROGRAMS.txt /licenses/ ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/third-party-program-of-intel-extension-for-tensorflow.txt /licenses/ @@ -77,37 +77,43 @@ EXPOSE 8888 CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root --ServerApp.token= --ServerApp.password= --ServerApp.allow_origin=* --ServerApp.base_url=$NB_PREFIX"] -FROM tf-base-${PACKAGE_OPTION} AS openmpi +FROM tf-base-${PACKAGE_OPTION} AS multinode RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \ + build-essential \ + cmake \ + g++ \ + gcc \ + git \ + libgl1-mesa-glx \ + libglib2.0-0 \ libopenmpi-dev \ + numactl \ openmpi-bin \ - openmpi-common + openmpi-common \ + python3-dev \ + unzip \ + virtualenv -WORKDIR / -COPY ompi-requirements.txt . +ENV SIGOPT_PROJECT=. -RUN python -m pip install --no-cache-dir -r ompi-requirements.txt +WORKDIR / +COPY multinode/requirements.txt requirements.txt -FROM openmpi AS horovod +RUN python -m pip install --no-cache-dir -r requirements.txt && \ + rm -rf requirements.txt -ENV LD_LIBRARY_PATH /lib64/:/usr/lib64/:/usr/local/lib64 +ENV LD_LIBRARY_PATH="/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH}:/usr/local/lib/python${PYTHON_VERSION}/dist-packages/oneccl_bindings_for_pytorch/opt/mpi/libfabric/lib:/usr/local/lib/python${PYTHON_VERSION}/dist-packages/oneccl_bindings_for_pytorch/lib" RUN apt-get install -y --no-install-recommends --fix-missing \ - unzip \ openssh-client \ openssh-server && \ - rm /etc/ssh/ssh_host_*_key \ - /etc/ssh/ssh_host_*_key.pub - -ENV OMPI_ALLOW_RUN_AS_ROOT=1 -ENV OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 - -ENV OMPI_MCA_tl_tcp_if_exclude="lo,docker0" + rm /etc/ssh/ssh_host_*_key \ + /etc/ssh/ssh_host_*_key.pub && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* -# Install OpenSSH for MPI to communicate between containers -RUN mkdir -p /var/run/sshd && \ - echo 'LoginGraceTime 0' >> /etc/ssh/sshd_config +RUN mkdir -p /var/run/sshd # Install Horovod ARG HOROVOD_WITH_TENSORFLOW=1 @@ -116,43 +122,32 @@ ARG HOROVOD_WITHOUT_PYTORCH=1 ARG HOROVOD_WITHOUT_GLOO=1 ARG HOROVOD_WITH_MPI=1 -RUN apt-get install -y --no-install-recommends --fix-missing \ - build-essential \ - cmake \ - g++ \ - gcc \ - git \ - libgl1-mesa-glx \ - libglib2.0-0 \ - python3-dev && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -WORKDIR / -COPY hvd-requirements.txt . - -RUN python -m pip install --no-cache-dir -r hvd-requirements.txt - -ENV SIGOPT_PROJECT=. - -RUN wget --progress=dot:giga --no-check-certificate https://github.com/intel/neural-compressor/raw/master/docker/third-party-programs-tensorflow.txt -O /licenses/inc-third-party-programs-tensorflow.txt && \ - wget --progress=dot:giga --no-check-certificate https://raw.githubusercontent.com/intel/neural-compressor/master/LICENSE -O /licenses/INC_LICENSE +ENV LD_LIBRARY_PATH /lib64/:/usr/lib64/:/usr/local/lib64 -FROM horovod AS multinode-pip +RUN python -m pip install --no-cache-dir horovod==0.28.1 -WORKDIR / -COPY multinode-requirements.txt . +ARG PYTHON_VERSION -RUN python -m pip install --no-cache-dir -r multinode-requirements.txt +COPY multinode/generate_ssh_keys.sh /generate_ssh_keys.sh -FROM horovod AS multinode-idp +# modify generate_ssh_keys to be a helper script +# print how to use helper script on bash startup +# Avoids loop for further execution of the startup file +ARG PACKAGE_OPTION=pip +ARG PYPATH="/usr/local/lib/python${PYTHON_VERSION}/dist-packages" +RUN if [ "${PACKAGE_OPTION}" = "idp" ]; then PYPATH="/opt/conda/envs/idp/lib/python${PYTHON_VERSION}/site-packages"; fi && \ + echo "source ${PYPATH}/oneccl_bindings_for_pytorch/env/setvars.sh" >> ~/.startup && \ + cat '/generate_ssh_keys.sh' >> ~/.startup && \ + rm -rf /generate_ssh_keys.sh -WORKDIR / -COPY multinode-requirements.txt . +COPY multinode/dockerd-entrypoint.sh /usr/local/bin/dockerd-entrypoint.sh +COPY multinode/sshd_config /etc/ssh/sshd_config +COPY multinode/ssh_config /etc/ssh/ssh_config -RUN python -m pip install --no-cache-dir -r multinode-requirements.txt +RUN wget --progress=dot:giga --no-check-certificate https://github.com/intel/neural-compressor/raw/master/docker/third-party-programs-tensorflow.txt -O /licenses/inc-third-party-programs-tensorflow.txt && \ + wget --progress=dot:giga --no-check-certificate https://raw.githubusercontent.com/intel/neural-compressor/master/LICENSE -O /licenses/INC_LICENSE -FROM ${PYTHON_BASE} AS itex-xpu-base-pip +FROM ${PYTHON_BASE} AS itex-xpu-base RUN apt-get update && \ apt-get install -y --no-install-recommends --fix-missing \ @@ -219,54 +214,7 @@ ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/maste ENV LD_LIBRARY_PATH=/opt/intel/oneapi/redist/lib:$LD_LIBRARY_PATH -FROM ${PYTHON_BASE} AS itex-xpu-base-idp - -RUN apt-get update && \ - apt-get install -y --no-install-recommends --fix-missing \ - apt-utils \ - build-essential \ - clinfo \ - git \ - gnupg2 \ - gpg-agent \ - rsync \ - unzip \ - wget && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -ARG ICD_VER -ARG LEVEL_ZERO_GPU_VER -ARG LEVEL_ZERO_VER -ARG LEVEL_ZERO_DEV_VER - -RUN no_proxy="" NO_PROXY="" wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \ - gpg --dearmor --output /usr/share/keyrings/intel-graphics.gpg -RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy/lts/2350 unified" | \ - tee /etc/apt/sources.list.d/intel-gpu-jammy.list - -RUN no_proxy="" NO_PROXY="" apt-get update && \ - apt-get install -y --no-install-recommends --fix-missing \ - intel-opencl-icd=${ICD_VER} \ - intel-level-zero-gpu=${LEVEL_ZERO_GPU_VER} \ - level-zero=${LEVEL_ZERO_VER} \ - level-zero-dev=${LEVEL_ZERO_DEV_VER} && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -ARG ITEX_VER="2.15.0.1" - -RUN conda install -n idp -y intel-extension-for-tensorflow=${ITEX_VER}=*xpu* \ - -c https://software.repos.intel.com/python/conda - -ENV LD_LIBRARY_PATH=/opt/conda/envs/idp/lib:$LD_LIBRARY_PATH - -ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/THIRD-PARTY-PROGRAMS.txt /licenses/ -ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/third-party-program-of-intel-extension-for-tensorflow.txt /licenses/ -ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/third-party-programs-of-intel-tensorflow.txt /licenses/ -ADD https://raw.githubusercontent.com/intel/intel-extension-for-tensorflow/master/third-party-programs/dockerlayer/third-party-programs-of-intel-optimization-for-horovod.txt /licenses/ - -FROM itex-xpu-base-${PACKAGE_OPTION} AS itex-xpu-jupyter +FROM itex-xpu-base AS itex-xpu-jupyter WORKDIR /jupyter COPY jupyter-requirements.txt . diff --git a/tensorflow/README.md b/tensorflow/README.md index 195cebdf..ac2c8b7c 100644 --- a/tensorflow/README.md +++ b/tensorflow/README.md @@ -85,7 +85,8 @@ The images below are built only with CPU optimizations (GPU acceleration support | Tag(s) | TensorFlow | ITEX | Dockerfile | | --------------------------- | ----------- | ------------ | --------------- | -| `2.15.0-pip-base`, `latest` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | +| `2.15.1-pip-base`, `latest` | [v2.15.1] | [v2.15.0.1] | [v0.4.0-Beta] | +| `2.15.0-pip-base` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | | `2.14.0-pip-base` | [v2.14.1] | [v2.14.0.1] | [v0.3.4] | | `2.13-pip-base` | [v2.13.0] | [v2.13.0.0] | [v0.2.3] | @@ -93,6 +94,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | TensorFlow | ITEX | Dockerfile | | -------------------- | ----------- | ------------- | --------------- | +| `2.15.1-pip-jupyter` | [v2.15.1] | [v2.15.0.1] | [v0.4.0-Beta] | | `2.15.0-pip-jupyter` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | | `2.14.0-pip-jupyter` | [v2.14.1] | [v2.14.0.1] | [v0.3.4] | | `2.13-pip-jupyter` | [v2.13.0] | [v2.13.0.0] | [v0.2.3] | @@ -105,7 +107,7 @@ docker run -it --rm \ --net=host \ -v $PWD/workspace:/workspace \ -w /workspace \ - intel/intel-extension-for-tensorflow:2.15.0-pip-jupyter + intel/intel-extension-for-tensorflow:2.15.1-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -116,10 +118,102 @@ The images below additionally include [Horovod]: | Tag(s) | Tensorflow | ITEX | Horovod | Dockerfile | | ------------------------------ | --------- | ------------ | --------- | --------------- | +| `2.15.1-pip-multinode` | [v2.15.1] | [v2.15.0.1] | [v0.28.1] | [v0.4.0-Beta] | | `2.15.0-pip-multinode` | [v2.15.0] | [v2.15.0.0] | [v0.28.1] | [v0.4.0-Beta] | | `2.14.0-pip-openmpi-multinode` | [v2.14.1] | [v2.14.0.1] | [v0.28.1] | [v0.3.4] | | `2.13-pip-openmpi-mulitnode` | [v2.13.0] | [v2.13.0.0] | [v0.28.0] | [v0.2.3] | +> [!NOTE] +> Passwordless SSH connection is also enabled in the image, but the container does not contain any SSH ID keys. The user needs to mount those keys at `/root/.ssh/id_rsa` and `/etc/ssh/authorized_keys`. + +> [!TIP] +> Before mounting any keys, modify the permissions of those files with `chmod 600 authorized_keys; chmod 600 id_rsa` to grant read access for the default user account. + +#### Setup and Run ITEX Multi-Node Container + +Some additional assembly is required to utilize this container with OpenSSH. To perform any kind of DDP (Distributed Data Parallel) execution, containers are assigned the roles of launcher and worker respectively: + +SSH Server (Worker) + +1. *Authorized Keys* : `/etc/ssh/authorized_keys` + +SSH Client (Launcher) + +1. *Private User Key* : `/root/.ssh/id_rsa` + +To add these files correctly please follow the steps described below. + +1. Setup ID Keys + + You can use the commands provided below to [generate the identity keys](https://www.ssh.com/academy/ssh/keygen#creating-an-ssh-key-pair-for-user-authentication) for OpenSSH. + + ```bash + ssh-keygen -q -N "" -t rsa -b 4096 -f ./id_rsa + touch authorized_keys + cat id_rsa.pub >> authorized_keys + ``` + +2. Configure the permissions and ownership for all of the files you have created so far + + ```bash + chmod 600 id_rsa config authorized_keys + chown root:root id_rsa.pub id_rsa config authorized_keys + ``` + +3. Create a hostfile for horovod. (Optional) + + ```txt + Host host1 + HostName + IdentitiesOnly yes + IdentityFile ~/.root/id_rsa + Port + Host host2 + HostName + IdentitiesOnly yes + IdentityFile ~/.root/id_rsa + Port + ... + ``` + +4. Configure [Horovod] in your python script + + ```python + import horovod.torch as hvd + + hvd.init() + ``` + +5. Now start the workers and execute DDP on the launcher + + 1. Worker run command: + + ```bash + docker run -it --rm \ + --net=host \ + -v $PWD/authorized_keys:/etc/ssh/authorized_keys \ + -v $PWD/tests:/workspace/tests \ + -w /workspace \ + intel/intel-optimized-tensorflow:2.15.1-pip-multinode \ + bash -c '/usr/sbin/sshd -D' + ``` + + 2. Launcher run command: + + ```bash + docker run -it --rm \ + --net=host \ + -v $PWD/id_rsa:/root/.ssh/id_rsa \ + -v $PWD/tests:/workspace/tests \ + -v $PWD/hostfile:/root/ssh/config \ + -w /workspace \ + intel/intel-optimized-tensorflow:2.15.1-pip-multinode \ + bash -c 'horovodrun --verbose -np 2 -H host1:1,host2:1 /workspace/tests/tf_base_test.py' + ``` + +> [!NOTE] +> [Intel® MPI] can be configured based on your machine settings. If the above commands do not work for you, see the documentation for how to configure based on your network. + --- The images below are [TensorFlow* Serving] with CPU Optimizations: @@ -151,7 +245,8 @@ The images below are built only with CPU optimizations (GPU acceleration support | Tag(s) | TensorFlow | ITEX | Dockerfile | | --------------------------- | ----------- | ------------ | --------------- | -| `2.15.0-idp-base`, `latest` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | +| `2.15.1-idp-base` | [v2.15.1] | [v2.15.0.1] | [v0.4.0-Beta] | +| `2.15.0-idp-base` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | | `2.14.0-idp-base` | [v2.14.1] | [v2.14.0.1] | [v0.3.4] | | `2.13-idp-base` | [v2.13.0] | [v2.13.0.0] | [v0.2.3] | @@ -159,6 +254,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | TensorFlow | ITEX | Dockerfile | | -------------------- | ----------- | ------------- | --------------- | +| `2.15.1-idp-jupyter` | [v2.15.1] | [v2.15.0.1] | [v0.4.0-Beta] | | `2.15.0-idp-jupyter` | [v2.15.0] | [v2.15.0.0] | [v0.4.0-Beta] | | `2.14.0-idp-jupyter` | [v2.14.1] | [v2.14.0.1] | [v0.3.4] | | `2.13-idp-jupyter` | [v2.13.0] | [v2.13.0.0] | [v0.2.3] | @@ -167,6 +263,7 @@ The images below additionally include [Horovod]: | Tag(s) | Tensorflow | ITEX | Horovod | Dockerfile | | ------------------------------ | --------- | ------------ | --------- | --------------- | +| `2.15.1-idp-multinode` | [v2.15.1] | [v2.15.0.1] | [v0.28.1] | [v0.4.0-Beta] | | `2.15.0-idp-multinode` | [v2.15.0] | [v2.15.0.0] | [v0.28.1] | [v0.4.0-Beta] | | `2.14.0-idp-openmpi-multinode` | [v2.14.1] | [v2.14.0.1] | [v0.28.1] | [v0.3.4] | | `2.13-idp-openmpi-mulitnode` | [v2.13.0] | [v2.13.0.0] | [v0.28.0] | [v0.2.3] | diff --git a/tensorflow/docker-compose.yaml b/tensorflow/docker-compose.yaml index 9583b296..18aec65a 100644 --- a/tensorflow/docker-compose.yaml +++ b/tensorflow/docker-compose.yaml @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -version: '3' include: - path: - ../python/docker-compose.yaml @@ -31,7 +30,7 @@ services: PYTHON_VERSION: ${PYTHON_VERSION:-3.10} REGISTRY: ${REGISTRY} REPO: ${REPO} - TF_VERSION: ${TF_VERSION:-2.15.0} + TF_VERSION: ${TF_VERSION:-2.15.1} target: tf-base-${PACKAGE_OPTION:-pip} context: . labels: @@ -41,20 +40,20 @@ services: org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.name: "intel/intel-optimized-tensorflow" org.opencontainers.image.title: "Intel® Extension for TensorFlow Base Image" - org.opencontainers.image.version: ${TF_VERSION:-2.15.0}-${PACKAGE_OPTION:-pip}-base + org.opencontainers.image.version: ${TF_VERSION:-2.15.1}-${PACKAGE_OPTION:-pip}-base depends_on: - ${PACKAGE_OPTION:-pip} command: > python -c 'import tensorflow as tf; print("Tensorflow Version:", tf.__version__)' - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-base pull_policy: always jupyter: build: labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.0}-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.1}-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for TensorFlow Jupyter Image" - org.opencontainers.image.version: ${TF_VERSION:-2.15.0}-${PACKAGE_OPTION:-pip}-jupyter + org.opencontainers.image.version: ${TF_VERSION:-2.15.1}-${PACKAGE_OPTION:-pip}-jupyter target: jupyter command: > bash -c "python -m jupyter --version" @@ -62,32 +61,38 @@ services: http_proxy: ${http_proxy} https_proxy: ${https_proxy} extends: tf-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-jupyter network_mode: host volumes: - /$PWD:/jupyter multinode: build: labels: + dependency.apt.build-essential: true + dependency.apt.cmake: true dependency.apt.gcc: true + dependency.apt.g++: true + dependency.apt.git: true dependency.apt.libgl1-mesa-glx: true dependency.apt.libglib2: true - dependency.apt.python3-dev: true - dependency.pip.apt.virtualenv: true dependency.apt.libopenmpi-dev: true + dependency.apt.numactl: true dependency.apt.openmpi-bin: true - dependency.apt.unzip: true dependency.apt.openssh-client: true dependency.apt.openssh-server: true - dependency.python.pip: multinode-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.0}-${PACKAGE_OPTION:-pip}-base" + dependency.apt.python3-dev: true + dependency.apt.unzip: true + dependency.pip.apt.virtualenv: true + dependency.pip.horovod: 0.28.1 + dependency.python.pip: multinode/requirements.txt + org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.1}-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for TensorFlow MultiNode Image" - org.opencontainers.image.version: ${TF_VERSION:-2.15.0}-${PACKAGE_OPTION:-pip}-multinode - target: multinode-${PACKAGE_OPTION:-pip} + org.opencontainers.image.version: ${TF_VERSION:-2.15.1}-${PACKAGE_OPTION:-pip}-multinode + target: multinode command: > bash -c "horovodrun --check-build && mpirun --version && python -c 'import horovod.tensorflow as hvd;hvd.init();import horovod.tensorflow;import neural_compressor, tf2onnx; print(\"\\nNeural Compressor Version:\", neural_compressor.__version__, \"\\\nTensorFlow2ONNX Version:\", tf2onnx.__version__)'" extends: tf-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-3.0} xpu: build: args: @@ -120,7 +125,7 @@ services: org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for TensorFlow XPU Base Image" org.opencontainers.image.version: ${TF_VER:-2.15.0}-xpu-${PACKAGE_OPTION:-pip}-base - target: itex-xpu-base-${PACKAGE_OPTION:-pip} + target: itex-xpu-base command: > sh -c "python -c 'import tensorflow as tf;print(tf.__version__);from tensorflow.python.client import device_lib;print(device_lib.list_local_devices())'" extends: tf-base @@ -140,7 +145,7 @@ services: NO_PROXY: '' labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.0}-xpu-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-tensorflow:${TF_VERSION:-2.15.1}-xpu-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for TensorFlow XPU Jupyter Image" org.opencontainers.image.version: ${TF_VER:-2.15.0}-xpu-${PACKAGE_OPTION:-pip}-jupyter target: itex-xpu-jupyter diff --git a/tensorflow/hvd-requirements.txt b/tensorflow/hvd-requirements.txt deleted file mode 100644 index f2eadcce..00000000 --- a/tensorflow/hvd-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -horovod==0.28.1 diff --git a/tensorflow/jupyter-requirements.txt b/tensorflow/jupyter-requirements.txt index 23a73885..9bdbed92 100644 --- a/tensorflow/jupyter-requirements.txt +++ b/tensorflow/jupyter-requirements.txt @@ -1,4 +1,4 @@ -jupyterlab==4.3.0a0 +jupyterlab>=4.2.4 jupyterhub==5.1.0 -notebook==7.3.0a0 +notebook>=7.1.3 jupyter-server-proxy>=4.1.2 diff --git a/tensorflow/multinode-requirements.txt b/tensorflow/multinode-requirements.txt deleted file mode 100644 index d9cff369..00000000 --- a/tensorflow/multinode-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -cython -tf2onnx -neural-compressor==2.6 diff --git a/tensorflow/multinode/dockerd-entrypoint.sh b/tensorflow/multinode/dockerd-entrypoint.sh new file mode 100755 index 00000000..ba13c0f9 --- /dev/null +++ b/tensorflow/multinode/dockerd-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -a +# shellcheck disable=SC1091 +source "$HOME/.startup" +set +a +"$@" diff --git a/tensorflow/multinode/generate_ssh_keys.sh b/tensorflow/multinode/generate_ssh_keys.sh new file mode 100755 index 00000000..0ee61398 --- /dev/null +++ b/tensorflow/multinode/generate_ssh_keys.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Copyright (c) 2023 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +function gen_single_key() { + ALG_NAME=$1 + if [[ ! -f /etc/ssh/ssh_host_${ALG_NAME}_key ]]; then + ssh-keygen -q -N "" -t "${ALG_NAME}" -f "/etc/ssh/ssh_host_${ALG_NAME}_key" + fi +} + +gen_single_key dsa +gen_single_key rsa +gen_single_key ecdsa +gen_single_key ed25519 diff --git a/tensorflow/multinode/requirements.txt b/tensorflow/multinode/requirements.txt new file mode 100644 index 00000000..80747740 --- /dev/null +++ b/tensorflow/multinode/requirements.txt @@ -0,0 +1,5 @@ +cython>=3.0.11 +impi-rt>=2021.12.0 +mpi4py>=3.1.0 +neural-compressor==3.0 +tf2onnx>=1.16.1 diff --git a/tensorflow/multinode/ssh_config b/tensorflow/multinode/ssh_config new file mode 100644 index 00000000..9ac73017 --- /dev/null +++ b/tensorflow/multinode/ssh_config @@ -0,0 +1,4 @@ +Host * + Port 3022 + IdentityFile ~/.ssh/id_rsa + StrictHostKeyChecking no diff --git a/tensorflow/multinode/sshd_config b/tensorflow/multinode/sshd_config new file mode 100644 index 00000000..4796a48a --- /dev/null +++ b/tensorflow/multinode/sshd_config @@ -0,0 +1,12 @@ +HostKey /etc/ssh/ssh_host_dsa_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key +AuthorizedKeysFile /etc/ssh/authorized_keys +## Enable DEBUG log. You can ignore this but this may help you debug any issue while enabling SSHD for the first time +LogLevel DEBUG3 +Port 3022 +UsePAM yes +Subsystem sftp /usr/lib/openssh/sftp-server +# https://ubuntu.com/security/CVE-2024-6387 +LoginGraceTime 0 diff --git a/tensorflow/ompi-requirements.txt b/tensorflow/ompi-requirements.txt deleted file mode 100644 index 7b64c166..00000000 --- a/tensorflow/ompi-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -impi-rt>=2021.12.0 diff --git a/tensorflow/requirements.txt b/tensorflow/requirements.txt index 9b50ec78..92fd1059 100644 --- a/tensorflow/requirements.txt +++ b/tensorflow/requirements.txt @@ -1,4 +1,4 @@ -tensorflow==2.15.0 -intel-extension-for-tensorflow[cpu]==2.15.0.0 +tensorflow==2.15.1 +intel-extension-for-tensorflow[cpu]>=2.15,<2.16 tensorflow-hub==0.16.1 -pillow==10.3.0 +pillow==10.4.0 diff --git a/tensorflow/serving/requirements.txt b/tensorflow/serving/requirements.txt index cf28053c..cd80fbcd 100644 --- a/tensorflow/serving/requirements.txt +++ b/tensorflow/serving/requirements.txt @@ -1,5 +1,5 @@ -numpy==2.0.0 -pillow==10.3.0 +numpy==2.0.1 +pillow==10.4.0 requests==2.32.3 -tensorflow==2.16.1 -tensorflow-serving-api==2.16.1 +tensorflow==2.17.0 +tensorflow-serving-api==2.17.0 diff --git a/tensorflow/tests/tests.yaml b/tensorflow/tests/tests.yaml index 0fa5b2b3..43af2239 100644 --- a/tensorflow/tests/tests.yaml +++ b/tensorflow/tests/tests.yaml @@ -14,7 +14,7 @@ --- import-itex-cpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-base cmd: python -c "from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())" import-itex-xpu-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-base @@ -24,20 +24,20 @@ import-itex-xpu-${PACKAGE_OPTION:-pip}: - src: ${PWD}/tensorflow/tests dst: /tests import-cpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-jupyter cmd: python -m jupyter --version import-xpu-jupyter-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-itex-${TF_VERSION:-2.15.1}-itex-xpu-jupyter cmd: python -m jupyter --version device: ["/dev/dri"] import-multinode-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-3.0} cmd: horovodrun --check-build && mpirun --version && python -c 'import horovod.tensorflow as hvd;hvd.init();import horovod.tensorflow' import-inc-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-3.0} cmd: python -c "import neural_compressor as inc;print(inc.__version__)" itex-cpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-base cmd: python /tests/tf_base_test.py volumes: - src: ${PWD}/tensorflow/tests @@ -55,13 +55,13 @@ itex-xpu-jupyter-${PACKAGE_OPTION:-pip}: notebook: True device: ["/dev/dri"] multinode-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-3.0} cmd: horovodrun -np 2 -H localhost:2 --binding-args="-bind-to socket -map-by socket" python /tests/tf_base_test.py volumes: - dst: /tests src: $PWD/tensorflow/tests inc-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.0}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-2.6} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-${TF_VERSION:-2.15.1}-horovod-${HOROVOD_VERSION:-0.28.1}-inc-${INC_VERSION:-3.0} cmd: bash /tests/inc_test.sh volumes: - dst: /tests diff --git a/tensorflow/xpu-requirements.txt b/tensorflow/xpu-requirements.txt index 0280ef9d..9e4bb523 100644 --- a/tensorflow/xpu-requirements.txt +++ b/tensorflow/xpu-requirements.txt @@ -1,2 +1,2 @@ -tensorflow==2.15.1 +tensorflow==2.15.0 intel-extension-for-tensorflow[xpu]==2.15.0.1 From 908fd4a56aacfea0fbc571d5cb9ad692a253d00d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:58:28 -0700 Subject: [PATCH 14/56] Bump mkdocs-material from 9.5.31 to 9.5.32 in /docs in the docs group (#320) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index bbcf99a8..54a9b029 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs-callouts>=1.13.2 mkdocs-git-authors-plugin>=0.8.0 mkdocs-git-revision-date-localized-plugin>=1.2.5 -mkdocs-material==9.5.31 +mkdocs-material==9.5.32 mkdocs-table-reader-plugin>=2.1.0 mkdocs==1.6.0 pandas>=2.0.3 From 6197b787ed3fa71581f42bd60bc7104b4be20220 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:08:35 -0700 Subject: [PATCH 15/56] Bump onnxruntime from 1.18.1 to 1.19.0 in /preset (#319) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- preset/inference-optimization/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preset/inference-optimization/requirements.txt b/preset/inference-optimization/requirements.txt index 8f0091ac..3a3f0f13 100644 --- a/preset/inference-optimization/requirements.txt +++ b/preset/inference-optimization/requirements.txt @@ -2,4 +2,4 @@ dataset-librarian==1.0.4 evaluate==0.4.2 git+https://github.com/huggingface/optimum-intel.git tf2onnx==1.16.1 -onnxruntime==1.18.1 +onnxruntime==1.19.0 From f5024ac2be58254da6638e739580e2d4c3870a55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:14:31 -0700 Subject: [PATCH 16/56] Bump matplotlib from 3.9.1.post1 to 3.9.2 in /classical-ml in the classical-ml group (#323) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- classical-ml/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classical-ml/requirements.txt b/classical-ml/requirements.txt index 484856df..b7ff293b 100644 --- a/classical-ml/requirements.txt +++ b/classical-ml/requirements.txt @@ -1,5 +1,5 @@ daal4py==2024.6.0 -matplotlib==3.9.1.post1 +matplotlib==3.9.2 numpy==1.26.4 scikit-learn-intelex==2024.6.0 threadpoolctl==3.5.0 From e938f411a9f9ab369532774a44e55eae5625b057 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 09:16:28 -0700 Subject: [PATCH 17/56] Bump github/codeql-action from 3.26.0 to 3.26.2 (#322) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/container-ci.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 20213f5b..48fc214d 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Cleanup if: always() run: docker rmi -f ${{ secrets.REGISTRY }}/${{ secrets.REPO }}:${{ matrix.container }} - - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: sarif_file: '${{ matrix.container }}-scan.sarif' category: '${{ matrix.container }}' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 086103d8..2c387c4a 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -53,6 +53,6 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@eb055d739abdc2e8de2e5f4ba1a8b246daa779aa # v3.26.0 + - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 with: sarif_file: results.sarif From cdef1e491a1f1d8f67edf1d57ac67c0e177eb3ab Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Tue, 20 Aug 2024 09:25:13 -0700 Subject: [PATCH 18/56] update xpu requirements and image tags (#325) Signed-off-by: Srikanth Ramakrishna --- pytorch/README.md | 2 +- pytorch/xpu-requirements.txt | 4 ++++ tensorflow/README.md | 6 +++--- tensorflow/xpu-requirements.txt | 4 ++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pytorch/README.md b/pytorch/README.md index 53adcb1d..f036502f 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -24,7 +24,7 @@ The images below include support for both CPU and GPU optimizations: | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------------- | -------- | -------------- | ------ | --------------- | -| `2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | +| `2.1.40-xpu-pip-base`,`2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.20-xpu` | [v2.1.0] | [v2.1.20+xpu] | [803] | [v0.3.4] | | `2.1.10-xpu` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | diff --git a/pytorch/xpu-requirements.txt b/pytorch/xpu-requirements.txt index 5d7d2e8a..09badb28 100644 --- a/pytorch/xpu-requirements.txt +++ b/pytorch/xpu-requirements.txt @@ -6,3 +6,7 @@ oneccl_bind_pt==2.1.400+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us setuptools==69.5.1 numpy==1.26.4 +idna==3.7 +requests==2.32.0 +tqdm==4.66.3 +urllib3==2.2.2 diff --git a/tensorflow/README.md b/tensorflow/README.md index ac2c8b7c..d71dc349 100644 --- a/tensorflow/README.md +++ b/tensorflow/README.md @@ -16,7 +16,7 @@ The images below include support for both CPU and GPU optimizations: | Tag(s) | TensorFlow | ITEX | Driver | Dockerfile | | ---------------------- | ----------- | -------------- | ------- | --------------- | -| `2.15.0.1-xpu`, `xpu` | [v2.15.1] | [v2.15.0.1] | [803.63]| [v0.4.0-Beta] | +| `2.15.0.1-xpu-pip-base`, `xpu` | [v2.15.1] | [v2.15.0.1] | [803.63]| [v0.4.0-Beta] | | `2.15.0.0-xpu` | [v2.15.0] | [v2.15.0.0] | [803] | [v0.4.0-Beta] | | `2.14.0.1-xpu` | [v2.14.1] | [v2.14.0.1] | [736] | [v0.3.4] | | `2.13.0.0-xpu` | [v2.13.0] | [v2.13.0.0] | [647] | [v0.2.3] | @@ -37,7 +37,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | TensorFlow | IPEX | Driver | Dockerfile | | ------------- | ----------- | ------------- | ------ | --------------- | -| `2.15.0.1-xpu-jupyter` | [v2.15.1] | [v2.15.0.1] | [803.63]| [v0.4.0-Beta] | +| `2.15.0.1-xpu-pip-jupyter` | [v2.15.1] | [v2.15.0.1] | [803.63]| [v0.4.0-Beta] | | `xpu-jupyter` | [v2.14.1] | [v2.14.0.1] | [736] | [v0.3.4] | ### Run the XPU Jupyter Container @@ -49,7 +49,7 @@ docker run -it --rm \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ --ipc=host \ - intel/intel-extension-for-tensorflow:2.15.0.1-xpu-jupyter + intel/intel-extension-for-tensorflow:2.15.0.1-xpu-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. diff --git a/tensorflow/xpu-requirements.txt b/tensorflow/xpu-requirements.txt index 9e4bb523..cbb01fc4 100644 --- a/tensorflow/xpu-requirements.txt +++ b/tensorflow/xpu-requirements.txt @@ -1,2 +1,6 @@ tensorflow==2.15.0 intel-extension-for-tensorflow[xpu]==2.15.0.1 +idna==3.7 +requests==2.32.0 +tqdm==4.66.3 +urllib3==2.2.2 From 4139104eeea434bbd7f84ca797d7c5b93148190c Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Fri, 23 Aug 2024 10:50:32 -0700 Subject: [PATCH 19/56] Add TGI on Intel Chart (#283) Signed-off-by: tylertitsworth --- workflows/charts/tgi/.helmignore | 23 ++++++ workflows/charts/tgi/Chart.yaml | 42 ++++++++++ workflows/charts/tgi/README.md | 30 +++++++ workflows/charts/tgi/README.md.gotmpl | 16 ++++ workflows/charts/tgi/templates/NOTES.txt | 22 +++++ workflows/charts/tgi/templates/_helpers.tpl | 76 +++++++++++++++++ workflows/charts/tgi/templates/deploy.yaml | 81 +++++++++++++++++++ workflows/charts/tgi/templates/ingress.yaml | 76 +++++++++++++++++ workflows/charts/tgi/templates/secret.yaml | 22 +++++ workflows/charts/tgi/templates/service.yaml | 29 +++++++ .../tgi/templates/tests/test-connection.yaml | 29 +++++++ workflows/charts/tgi/values.yaml | 64 +++++++++++++++ 12 files changed, 510 insertions(+) create mode 100644 workflows/charts/tgi/.helmignore create mode 100644 workflows/charts/tgi/Chart.yaml create mode 100644 workflows/charts/tgi/README.md create mode 100644 workflows/charts/tgi/README.md.gotmpl create mode 100644 workflows/charts/tgi/templates/NOTES.txt create mode 100644 workflows/charts/tgi/templates/_helpers.tpl create mode 100644 workflows/charts/tgi/templates/deploy.yaml create mode 100644 workflows/charts/tgi/templates/ingress.yaml create mode 100644 workflows/charts/tgi/templates/secret.yaml create mode 100644 workflows/charts/tgi/templates/service.yaml create mode 100644 workflows/charts/tgi/templates/tests/test-connection.yaml create mode 100644 workflows/charts/tgi/values.yaml diff --git a/workflows/charts/tgi/.helmignore b/workflows/charts/tgi/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/workflows/charts/tgi/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/workflows/charts/tgi/Chart.yaml b/workflows/charts/tgi/Chart.yaml new file mode 100644 index 00000000..761d8b0c --- /dev/null +++ b/workflows/charts/tgi/Chart.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v2 +name: tgi-on-intel +description: A Rust, Python and gRPC server for text generation inference by huggingface on Intel GPUs. + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +maintainers: + - name: tylertitsworth + email: tyler.titsworth@intel.com + url: https://github.com/tylertitsworth +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/workflows/charts/tgi/README.md b/workflows/charts/tgi/README.md new file mode 100644 index 00000000..7c020fe1 --- /dev/null +++ b/workflows/charts/tgi/README.md @@ -0,0 +1,30 @@ +# Text Generation Inference on Intel GPU + +A Rust, Python and gRPC server for text generation inference by huggingface on Intel GPUs. + +For more information about how to use Huggingface text-generation-inference with Intel optimizations, check out [huggingface's documentation](https://huggingface.co/docs/text-generation-inference/installation_intel). + +> [!TIP] +> For Gaudi-related documentation, check out [tgi-gaudi](https://github.com/huggingface/tgi-gaudi). + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| deploy.configMapName | string | `"intel-proxy-config"` | ConfigMap of Environment Variables | +| deploy.image | string | `"ghcr.io/huggingface/text-generation-inference:latest-intel"` | Intel TGI Image | +| deploy.model | string | `"HuggingFaceTB/SmolLM-135M"` | Model to be loaded | +| deploy.quantize | string | `""` | Enable Quantization (ex: bitsandbytes-nf4) | +| deploy.replicaCount | int | `1` | Number of pods | +| deploy.resources | object | `{"limits":{"cpu":"4000m","gpu.intel.com/i915":1},"requests":{"cpu":"1000m","memory":"1Gi"}}` | Resource configuration | +| deploy.resources.limits."gpu.intel.com/i915" | int | `1` | Intel GPU Device Configuration | +| fullnameOverride | string | `""` | Full qualified Domain Name | +| ingress | object | `{"annotations":{},"className":"","enabled":false,"hosts":[{"host":"chart-example.local","paths":[{"path":"/","pathType":"ImplementationSpecific"}]}],"tls":[]}` | Ingress configuration | +| nameOverride | string | `""` | Name of the serving service | +| secret.encodedToken | string | `""` | Base64 Encoded Huggingface Hub API Token | +| service | object | `{"port":80,"type":"NodePort"}` | Service configuration | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/workflows/charts/tgi/README.md.gotmpl b/workflows/charts/tgi/README.md.gotmpl new file mode 100644 index 00000000..0d773d1a --- /dev/null +++ b/workflows/charts/tgi/README.md.gotmpl @@ -0,0 +1,16 @@ +# Text Generation Inference on Intel GPU + +{{ template "chart.description" . }} + +For more information about how to use Huggingface text-generation-inference with Intel optimizations, check out [huggingface's documentation](https://huggingface.co/docs/text-generation-inference/installation_intel). + +> [!TIP] +> For Gaudi-related documentation, check out [tgi-gaudi](https://github.com/huggingface/tgi-gaudi). + +{{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +{{ template "helm-docs.versionFooter" . }} diff --git a/workflows/charts/tgi/templates/NOTES.txt b/workflows/charts/tgi/templates/NOTES.txt new file mode 100644 index 00000000..fc906eb6 --- /dev/null +++ b/workflows/charts/tgi/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tgi.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tgi.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tgi.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tgi.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/workflows/charts/tgi/templates/_helpers.tpl b/workflows/charts/tgi/templates/_helpers.tpl new file mode 100644 index 00000000..b98dd8cb --- /dev/null +++ b/workflows/charts/tgi/templates/_helpers.tpl @@ -0,0 +1,76 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{{/* +Expand the name of the chart. +*/}} +{{- define "tgi.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "tgi.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "tgi.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "tgi.labels" -}} +helm.sh/chart: {{ include "tgi.chart" . }} +{{ include "tgi.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "tgi.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tgi.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "tgi.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "tgi.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/workflows/charts/tgi/templates/deploy.yaml b/workflows/charts/tgi/templates/deploy.yaml new file mode 100644 index 00000000..6c5a5bd5 --- /dev/null +++ b/workflows/charts/tgi/templates/deploy.yaml @@ -0,0 +1,81 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tgi.fullname" . }} + labels: + {{- include "tgi.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.deploy.replicaCount }} + selector: + matchLabels: + {{- include "tgi.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "tgi.selectorLabels" . | nindent 8 }} + spec: + securityContext: + fsGroup: 1000 + runAsUser: 1000 + containers: + - name: {{ .Chart.Name }} + args: + - '--model-id' + - {{ .Values.deploy.model | quote }} + {{- if index .Values.deploy.resources.limits "gpu.intel.com/i915" }} + - '--num-shard' + - {{ index .Values.deploy.resources.limits "gpu.intel.com/i915" | quote }} + {{- end }} + - '-p' + - {{ .Values.service.port | quote }} + {{- if .Values.quantize }} + - '--quantize' + - {{ .Values.deploy.quantize | quote }} + {{- end }} + - '--cuda-graphs=0' + envFrom: + - configMapRef: + name: {{ .Values.deploy.configMapName }} + - secretRef: + name: {{ .Release.Name }}-hf-token + env: + - name: NUMBA_CACHE_DIR # https://github.com/huggingface/text-generation-inference/pull/2443 + value: /data/numba_cache + image: {{ .Values.deploy.image }} + livenessProbe: + httpGet: + path: /health + port: {{ .Values.service.port }} + initialDelaySeconds: 5 + periodSeconds: 5 + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + resources: + {{- toYaml .Values.deploy.resources | nindent 12 }} + volumeMounts: + - mountPath: /dev/shm + name: dshm + - mountPath: /data + name: hf-data + volumes: + - name: dshm + emptyDir: + medium: Memory + - name: hf-data + emptyDir: {} diff --git a/workflows/charts/tgi/templates/ingress.yaml b/workflows/charts/tgi/templates/ingress.yaml new file mode 100644 index 00000000..f87f6cb0 --- /dev/null +++ b/workflows/charts/tgi/templates/ingress.yaml @@ -0,0 +1,76 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "tgi.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "tgi.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + kubernetes.io/ingress.allow-http: "false" + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/workflows/charts/tgi/templates/secret.yaml b/workflows/charts/tgi/templates/secret.yaml new file mode 100644 index 00000000..0507543e --- /dev/null +++ b/workflows/charts/tgi/templates/secret.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +{{- $name := .Values.secret.encodedToken | required ".Values.secret.encodedToken is required in Base64 Format." -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-hf-token +type: Opaque +data: + HF_TOKEN: {{ .Values.secret.encodedToken }} diff --git a/workflows/charts/tgi/templates/service.yaml b/workflows/charts/tgi/templates/service.yaml new file mode 100644 index 00000000..7aff68e5 --- /dev/null +++ b/workflows/charts/tgi/templates/service.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tgi.fullname" . }} + labels: + {{- include "tgi.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "tgi.selectorLabels" . | nindent 4 }} diff --git a/workflows/charts/tgi/templates/tests/test-connection.yaml b/workflows/charts/tgi/templates/tests/test-connection.yaml new file mode 100644 index 00000000..113d8acf --- /dev/null +++ b/workflows/charts/tgi/templates/tests/test-connection.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "tgi.fullname" . }}-test-connection" + labels: + {{- include "tgi.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: info + image: curlimages/curl + command: ['sh', '-c'] + args: ['curl --noproxy "*" -f {{ include "tgi.fullname" . }}:{{ .Values.service.port }}/info'] + restartPolicy: OnFailure diff --git a/workflows/charts/tgi/values.yaml b/workflows/charts/tgi/values.yaml new file mode 100644 index 00000000..7d2434cc --- /dev/null +++ b/workflows/charts/tgi/values.yaml @@ -0,0 +1,64 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# -- Name of the serving service +nameOverride: "" +# -- Full qualified Domain Name +fullnameOverride: "" +deploy: + # -- ConfigMap of Environment Variables + configMapName: intel-proxy-config + # -- Intel TGI Image + image: ghcr.io/huggingface/text-generation-inference:latest-intel + # -- Model to be loaded + model: HuggingFaceTB/SmolLM-135M + # -- Enable Quantization (ex: bitsandbytes-nf4) + quantize: "" + # -- Number of pods + replicaCount: 1 + # -- Resource configuration + resources: + limits: + cpu: 4000m + # -- Intel GPU Device Configuration + gpu.intel.com/i915: 1 + # habana.ai/gaudi: 1 + # memory: 409Gi + # hugepages-2Mi: 95000Mi + requests: + cpu: 1000m + memory: "1Gi" +secret: + # -- Base64 Encoded Huggingface Hub API Token + encodedToken: "" +# -- Service configuration +service: + port: 80 + type: NodePort +# -- Ingress configuration +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local From 13a241680d9c6869d9afd7b156dcbd953db4f463 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:37:21 -0700 Subject: [PATCH 20/56] Bump github/codeql-action from 3.26.2 to 3.26.5 (#333) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/container-ci.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 48fc214d..27ccabad 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Cleanup if: always() run: docker rmi -f ${{ secrets.REGISTRY }}/${{ secrets.REPO }}:${{ matrix.container }} - - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 + - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: '${{ matrix.container }}-scan.sarif' category: '${{ matrix.container }}' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 2c387c4a..1c08423e 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -53,6 +53,6 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@429e1977040da7a23b6822b13c129cd1ba93dbb2 # v3.26.2 + - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 with: sarif_file: results.sarif From 262a89c27763c9af7b27bdf37c80bf52cdd9ede5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 15:48:26 +0000 Subject: [PATCH 21/56] Bump mkdocs-material from 9.5.32 to 9.5.33 in /docs in the docs group (#331) --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 54a9b029..1058a38f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ mkdocs-callouts>=1.13.2 mkdocs-git-authors-plugin>=0.8.0 mkdocs-git-revision-date-localized-plugin>=1.2.5 -mkdocs-material==9.5.32 +mkdocs-material==9.5.33 mkdocs-table-reader-plugin>=2.1.0 mkdocs==1.6.0 pandas>=2.0.3 From 658e61e414029596e99a7342855e7762862155c7 Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Mon, 26 Aug 2024 10:53:24 -0700 Subject: [PATCH 22/56] IPEX XPU Torchserve support (#336) Signed-off-by: Srikanth Ramakrishna --- pytorch/Dockerfile | 104 +++++++++++++++--- pytorch/docker-compose.yaml | 31 +++++- pytorch/serving/README.md | 56 +++++++++- pytorch/serving/config-xpu.properties | 15 +++ .../serving/model-archive/ipex_squeezenet.py | 57 ++++++++++ pytorch/serving/model-archive/mar-test.sh | 16 ++- pytorch/serving/tests.yaml | 47 ++++++-- .../{ => serving}/torchserve-requirements.txt | 0 .../serving/torchserve-xpu-requirements.txt | 15 +++ 9 files changed, 302 insertions(+), 39 deletions(-) create mode 100644 pytorch/serving/config-xpu.properties create mode 100644 pytorch/serving/model-archive/ipex_squeezenet.py rename pytorch/{ => serving}/torchserve-requirements.txt (100%) create mode 100644 pytorch/serving/torchserve-xpu-requirements.txt diff --git a/pytorch/Dockerfile b/pytorch/Dockerfile index 1a5b497d..2f7903d1 100644 --- a/pytorch/Dockerfile +++ b/pytorch/Dockerfile @@ -34,6 +34,7 @@ ARG BASE_IMAGE_TAG ARG PACKAGE_OPTION=pip ARG PYTHON_VERSION ARG PYTHON_BASE=${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER}-${BASE_IMAGE_NAME}-${BASE_IMAGE_TAG}-${PACKAGE_OPTION}-py${PYTHON_VERSION}-base +ARG TORCHSERVE_BASE=${PYTHON_BASE} FROM ${PYTHON_BASE} AS ipex-base-pip WORKDIR / @@ -181,13 +182,17 @@ RUN apt-get update && \ intel-oneapi-runtime-mkl=${MKL_VER} \ intel-oneapi-runtime-ccl=${CCL_VER}; +RUN rm -rf /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list + +ENV LD_LIBRARY_PATH=/opt/intel/oneapi/redist/lib:$LD_LIBRARY_PATH + +FROM ipex-xpu-base AS ipex-xpu-base-wheels + WORKDIR / COPY xpu-requirements.txt . RUN python -m pip install --no-cache-dir -r xpu-requirements.txt && \ - rm -rf xpu-requirements.txt /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list - -ENV LD_LIBRARY_PATH=/opt/intel/oneapi/redist/lib:$LD_LIBRARY_PATH + rm -rf xpu-requirements.txt FROM ipex-xpu-base AS ipex-xpu-jupyter @@ -205,7 +210,8 @@ EXPOSE 8888 CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root --ServerApp.token= --ServerApp.password= --ServerApp.allow_origin=* --ServerApp.base_url=$NB_PREFIX"] -FROM ${PYTHON_BASE} as torchserve-base + +FROM ${TORCHSERVE_BASE} as torchserve-base ENV PYTHONUNBUFFERED=TRUE @@ -221,8 +227,6 @@ RUN useradd -m -s /bin/bash model-server && \ mkdir -p /home/model-server/model-store && \ chown -R model-server /home/model-server/ -FROM torchserve-base AS compile - RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missing \ g++ \ git \ @@ -237,11 +241,6 @@ RUN python3 -m venv /home/venv ENV PATH="/home/venv/bin:$PATH" WORKDIR /home/model-server -COPY torchserve-requirements.txt . -COPY requirements.txt . - -RUN python -m pip install --no-cache-dir -r requirements.txt && \ - python -m pip install --no-cache-dir -r torchserve-requirements.txt RUN echo -e "#!/bin/bash \n\ set -e \n\ @@ -253,13 +252,29 @@ else \n\ fi \n\ tail -f /dev/null" >> /usr/local/bin/dockerd-entrypoint.sh -FROM torchserve-base AS torchserve +FROM torchserve-base AS compile-cpu + +COPY serving/torchserve-requirements.txt . +COPY requirements.txt . + +RUN python -m pip install --no-cache-dir -r requirements.txt && \ + python -m pip install --no-cache-dir -r torchserve-requirements.txt && \ + rm -rf requirements.txt torchserve-requirements.txt + +FROM torchserve-base AS compile-xpu + +COPY serving/torchserve-xpu-requirements.txt . + +RUN python -m pip install --no-cache-dir -r torchserve-xpu-requirements.txt && \ + rm -rf torchserve-xpu-requirements.txt + +FROM torchserve-base AS torchserve-cpu USER model-server WORKDIR /home/model-server -COPY --chown=model-server --from=compile /home/venv /home/venv -COPY --chown=model-server --chmod=755 --from=compile /usr/local/bin/dockerd-entrypoint.sh /usr/local/bin/dockerd-entrypoint.sh +COPY --chown=model-server --from=compile-cpu /home/venv /home/venv +COPY --chown=model-server --chmod=755 --from=compile-cpu /usr/local/bin/dockerd-entrypoint.sh /usr/local/bin/dockerd-entrypoint.sh COPY --chown=model-server serving/config.properties /home/model-server/config.properties ENV PATH="/home/venv/bin:$PATH" @@ -270,3 +285,64 @@ EXPOSE 8080 8081 8082 7070 7071 ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh"] CMD ["serve"] + +FROM torchserve-base AS torchserve-xpu + +RUN apt-get update && \ + apt-get install -y --no-install-recommends --fix-missing \ + gnupg2 \ + gpg-agent \ + rsync && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \ + gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy unified" | \ + tee /etc/apt/sources.list.d/intel-gpu-jammy.list + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + jq \ + curl \ + libnl-genl-3-200 \ + intel-gsc \ + libdrm2 \ + intel-metrics-discovery \ + intel-metrics-library && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* + +ARG XPU_SMI_VERSION + +ARG API_URL=https://api.github.com/repos/intel/xpumanager/releases/tags/V${XPU_SMI_VERSION} + +RUN wget -q --header="Accept: application/vnd.github.v3+json" --header="User-Agent: MyClient/1.0.0" -O - "$API_URL" | tee /tmp/asset_data.txt && \ + wget -q --no-check-certificate "$(jq -r '.assets[] | select(.name | test("^xpu-smi.*u22\\.04_amd64\\.deb$")) | .browser_download_url' < /tmp/asset_data.txt)" && \ + ldconfig && dpkg -i --force-all -- *.deb && \ + rm -rf -- *.deb /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list /tmp/asset_data.txt + +ARG GID=109 + +RUN groupadd -g ${GID} render &&\ + usermod -aG video,render model-server + +USER model-server + +WORKDIR /home/model-server + +RUN wget --progress=dot:giga https://raw.githubusercontent.com/pytorch/serve/master/examples/intel_extension_for_pytorch/intel_gpu_metric_collector.py && \ + wget --progress=dot:giga https://raw.githubusercontent.com/pytorch/serve/master/examples/intel_extension_for_pytorch/intel_gpu.py + +COPY --chown=model-server --from=compile-xpu /home/venv /home/venv +COPY --chown=model-server --chmod=755 --from=compile-xpu /usr/local/bin/dockerd-entrypoint.sh /usr/local/bin/dockerd-entrypoint.sh +COPY --chown=model-server serving/config-xpu.properties /home/model-server/config.properties + +ENV PATH="/home/venv/bin:$PATH" +ENV TEMP=/home/model-server/tmp + +# 8080/8081/8082 REST and 7070/7071 gRPC +EXPOSE 8080 8081 8082 7070 7071 + +ENTRYPOINT ["/usr/local/bin/dockerd-entrypoint.sh"] +CMD ["serve"] diff --git a/pytorch/docker-compose.yaml b/pytorch/docker-compose.yaml index 838ee5cb..6aeeefc9 100644 --- a/pytorch/docker-compose.yaml +++ b/pytorch/docker-compose.yaml @@ -122,7 +122,7 @@ services: org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Base Image" org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-base - target: ipex-xpu-base + target: ipex-xpu-base-wheels command: > python -c "import torch;print(torch.device('xpu'));import intel_extension_for_pytorch as @@ -156,7 +156,7 @@ services: image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter ports: - 8888:8888 - torchserve: + torchserve-cpu: build: args: PACKAGE_OPTION: pip @@ -165,22 +165,43 @@ services: dependency.apt.openjdk-17-jdk: true dependency.idp: false dependency.python.ipex: requirements.txt - dependency.python.pip: torchserve-requirements.txt + dependency.python.pip: serving/torchserve-requirements.txt docs: serving org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch Serving Image" org.opencontainers.image.version: ${IPEX_VERSION:-2.4.0}-serving-cpu - target: torchserve + target: torchserve-cpu command: torchserve --version entrypoint: "" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu ports: - 8080:8080 - 8081:8081 - 8082:8082 - 7070:7070 - 7071:7071 + torchserve-xpu: + build: + args: + PACKAGE_OPTION: pip + XPU_SMI_VERSION: ${XPU_SMI_VERSION:-1.2.38} + TORCHSERVE_BASE: ipex-xpu-base + labels: + dependency.apt.numactl: true + dependency.apt.openjdk-17-jdk: true + dependency.apt.xpu-smi: ${XPU_SMI_VERSION:-1.2.38} + dependency.idp: false + dependency.python.pip: serving/torchserve-xpu-requirements.txt + docs: serving + org.opencontainers.base.name: "intel/python:3.10-core" + org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Serving Image" + org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-serving-xpu + target: torchserve-xpu + command: torchserve --version + entrypoint: "" + extends: xpu + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-xpu hf-genai: build: args: diff --git a/pytorch/serving/README.md b/pytorch/serving/README.md index 5e48251f..6ca33ef1 100644 --- a/pytorch/serving/README.md +++ b/pytorch/serving/README.md @@ -12,29 +12,73 @@ The [Torchserve Model Archiver](https://github.com/pytorch/serve/blob/master/mod Follow the instructions found in the link above depending on whether you are intending to archive a model or a workflow. Use the provided container rather than installing the archiver with the example command below: +#### Create a Model Archive for CPU device + ```bash curl -O https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth docker run --rm -it \ + --entrypoint='' \ + -u root \ -v $PWD:/home/model-server \ intel/intel-optimized-pytorch:2.4.0-serving-cpu \ - torch-model-archiver --model-name squeezenet \ - --version 1.0 \ - --model-file model-archive/model.py \ - --serialized-file squeezenet1_1-b8a52dc0.pth \ - --handler image_classifier \ - --export-path /home/model-server + torch-model-archiver --model-name squeezenet1_1 \ + --version 1.1 \ + --model-file model-archive/model.py \ + --serialized-file squeezenet1_1-b8a52dc0.pth \ + --handler image_classifier \ + --export-path /home/model-server +``` + +### Create a Model Archive for XPU device + +Use a squeezenet model [optimized](./model-store/ipex_squeezenet.py) for XPU using Intel® Extension for PyTorch*. + +```bash +docker run --rm -it \ + --entrypoint='' \ + -u root \ + -v $PWD:/home/model-server \ + --device /dev/dri \ + intel/intel-optimized-pytorch:2.1.40-serving-xpu \ + sh -c 'python model-archive/ipex_squeezenet.py && \ + torch-model-archiver --model-name squeezenet1_1 \ + --version 1.1 \ + --serialized-file squeezenet1_1-jit.pt \ + --handler image_classifier \ + --export-path /home/model-server' ``` ### Test Model Test Torchserve with the new archived model. The example below is for the squeezenet model. +#### Run Torchserve for CPU device + ```bash # Assuming that the above pre-archived model is in the current working directory docker run -d --rm --name server \ -v $PWD:/home/model-server/model-store \ + -v $PWD/wf-store:/home/model-server/wf-store \ --net=host \ intel/intel-optimized-pytorch:2.4.0-serving-cpu +``` + +#### Run Torchserve for XPU device + +```bash +# Assuming that the above pre-archived model is in the current working directory +docker run -d --rm --name server \ + -v $PWD:/home/model-server/model-store \ + -v $PWD/wf-store:/home/model-server/wf-store \ + -v $PWD/config-xpu.properties:/home/model-server/config.properties \ + --net=host \ + --device /dev/dri \ + intel/intel-optimized-pytorch:2.1.40-serving-xpu +``` + +After lauching the container, follow the steps below: + +```bash # Verify that the container has launched successfully docker logs server # Attempt to register the model and make an inference request diff --git a/pytorch/serving/config-xpu.properties b/pytorch/serving/config-xpu.properties new file mode 100644 index 00000000..170a1485 --- /dev/null +++ b/pytorch/serving/config-xpu.properties @@ -0,0 +1,15 @@ +inference_address=http://0.0.0.0:8080 +management_address=http://0.0.0.0:8081 +metrics_address=http://0.0.0.0:8082 +number_of_netty_threads=32 +install_py_dep_per_model=true +job_queue_size=1000 +model_store=/home/model-server/model-store +workflow_store=/home/model-server/wf-store +allowed_urls=https://s3.amazonaws.com/.*,https://torchserve.pytorch.org/.* +ipex_enable=true +ipex_gpu_enable=true +system_metrics_cmd=/home/model-server/intel_gpu_metric_collector.py --gpu 1 +disable_token_authorization=true +enable_model_api=true +enable_envvars_config=true diff --git a/pytorch/serving/model-archive/ipex_squeezenet.py b/pytorch/serving/model-archive/ipex_squeezenet.py new file mode 100644 index 00000000..14c0dcb4 --- /dev/null +++ b/pytorch/serving/model-archive/ipex_squeezenet.py @@ -0,0 +1,57 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +# +# This file was assembled from multiple pieces, whose use is documented +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. +# based on https://github.com/pytorch/pytorch/blob/master/Dockerfile +# +# NOTE: To build this you will need a docker version >= 19.03 and DOCKER_BUILDKIT=1 +# +# If you do not use buildkit you are not going to have a good time +# +# For reference: +# https://docs.docker.com/develop/develop-images/build_enhancements/ + +# pylint: skip-file + +import intel_extension_for_pytorch as ipex +import torch +import torchvision.models as models + +# load the model +model = models.squeezenet1_1(pretrained=True) +model = model.eval() + +# define dummy input tensor to use for the model's forward call to record operations in the model for tracing +N, C, H, W = 1, 3, 224, 224 +data = torch.randn(N, C, H, W) + +model.eval() +data = torch.rand(1, 3, 224, 224) + +#################### code changes ################# +model = model.to("xpu") +data = data.to("xpu") +model = ipex.optimize(model, dtype=torch.bfloat16) +#################### code changes ################# + +with torch.no_grad(): + with torch.xpu.amp.autocast(enabled=True, dtype=torch.bfloat16): + ############################# code changes ##################### + model = torch.jit.trace(model, data) + model = torch.jit.freeze(model) + model(data) +torch.jit.save(model, "squeezenet1_1-jit.pt") diff --git a/pytorch/serving/model-archive/mar-test.sh b/pytorch/serving/model-archive/mar-test.sh index f07b83ad..aabee71f 100644 --- a/pytorch/serving/model-archive/mar-test.sh +++ b/pytorch/serving/model-archive/mar-test.sh @@ -26,8 +26,18 @@ # For reference: # https://docs.docker.com/develop/develop-images/build_enhancements/ -wget https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth -torch-model-archiver --model-name squeezenet1_1 --version 1.1 --model-file /home/model-server/model-archive/model.py --serialized-file squeezenet1_1-b8a52dc0.pth --handler image_classifier --export-path /home/model-server/model-store +if [[ "$1" == "cpu" ]]; then + wget https://download.pytorch.org/models/squeezenet1_1-b8a52dc0.pth + torch-model-archiver --model-name squeezenet1_1 --version 1.1 --model-file /home/model-server/model-archive/model.py --serialized-file squeezenet1_1-b8a52dc0.pth --handler image_classifier --export-path /home/model-server/model-store + rm -rf squeezenet1_1-b8a52dc0.pth +elif [[ "$1" == "xpu" ]]; then + python /home/model-server/model-archive/ipex_squeezenet.py + torch-model-archiver --model-name squeezenet1_1 --version 1.1 --serialized-file squeezenet1_1-jit.pt --handler image_classifier --export-path /home/model-server/model-store + rm -rf squeezenet1_1-jit.pt +else + echo "Only cpu and xpu devices supported" + exit 1 +fi + [ -f "/home/model-server/model-store/squeezenet1_1.mar" ] && echo "squeezenet1_1.pth Archived Succesfully at /home/model-server/model-store/squeezenet1_1.mar" -rm -rf squeezenet1_1-b8a52dc0.pth find . | grep -E "(/__pycache__$|\.pyc$|\.pyo$)" | xargs rm -rf diff --git a/pytorch/serving/tests.yaml b/pytorch/serving/tests.yaml index 3c91eced..986e220a 100644 --- a/pytorch/serving/tests.yaml +++ b/pytorch/serving/tests.yaml @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -ipex-serving-model-archive: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve - cmd: /home/model-server/model-archive/mar-test.sh +ipex-serving-cpu-model-archive: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu + cmd: /home/model-server/model-archive/mar-test.sh cpu entrypoint: /bin/bash volumes: - src: $PWD/pytorch/serving/model-archive @@ -23,8 +23,20 @@ ipex-serving-model-archive: dst: /home/model-server/model-store user: root workdir: /home/model-server/model-archive -ipex-serving-workflow-archive: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve +ipex-serving-xpu-model-archive: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-xpu + cmd: /home/model-server/model-archive/mar-test.sh xpu + entrypoint: /bin/bash + device: ["/dev/dri"] + volumes: + - src: $PWD/pytorch/serving/model-archive + dst: /home/model-server/model-archive + - src: $PWD/pytorch/serving/model-store + dst: /home/model-server/model-store + user: root + workdir: /home/model-server/model-archive +ipex-serving-cpu-workflow-archive: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu cmd: /home/model-server/model-archive/war-test.sh entrypoint: /bin/bash volumes: @@ -34,10 +46,23 @@ ipex-serving-workflow-archive: dst: /home/model-server/wf-store user: root workdir: /home/model-server/model-archive -ipex-serving-rest-workflow: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve +ipex-serving-cpu-rest-workflow: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu + cmd: bash /home/model-server/wf-store/rest-test.sh + serving: True + volumes: + - src: $PWD/pytorch/serving/model-store + dst: /home/model-server/model-store + - src: $PWD/pytorch/serving/wf-store + dst: /home/model-server/wf-store + env: + ENABLE_TORCH_PROFILER: 'true' + shm_size: 1g +ipex-serving-xpu-rest-workflow: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-xpu cmd: bash /home/model-server/wf-store/rest-test.sh serving: True + device: ["/dev/dri"] volumes: - src: $PWD/pytorch/serving/model-store dst: /home/model-server/model-store @@ -47,8 +72,8 @@ ipex-serving-rest-workflow: ENABLE_TORCH_PROFILER: 'true' shm_size: 1g workdir: /home/model-server/wf-store -ipex-serving-rest-inference: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve +ipex-serving-cpu-rest-inference: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu cmd: bash /home/model-server/model-store/rest-test.sh serving: True volumes: @@ -60,8 +85,8 @@ ipex-serving-rest-inference: ENABLE_TORCH_PROFILER: 'true' shm_size: 1g workdir: /home/model-server/model-store -ipex-serving-grpc-inference: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve +ipex-serving-cpu-grpc-inference: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-py${PYTHON_VERSION:-3.10}-torchserve-cpu cmd: bash /home/model-server/model-store/grpc-test.sh serving: True volumes: diff --git a/pytorch/torchserve-requirements.txt b/pytorch/serving/torchserve-requirements.txt similarity index 100% rename from pytorch/torchserve-requirements.txt rename to pytorch/serving/torchserve-requirements.txt diff --git a/pytorch/serving/torchserve-xpu-requirements.txt b/pytorch/serving/torchserve-xpu-requirements.txt new file mode 100644 index 00000000..534f6514 --- /dev/null +++ b/pytorch/serving/torchserve-xpu-requirements.txt @@ -0,0 +1,15 @@ +torch==2.1.0.post3+cxx11.abi +torchvision==0.16.0.post3+cxx11.abi +torchaudio==2.1.0.post3+cxx11.abi +intel_extension_for_pytorch==2.1.40+xpu +--extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us +setuptools==69.5.1 +numpy==1.26.4 +captum>=0.7.0 +cython>=3.0.10 +pynvml>=11.5.0 +pyyaml>=6.0.1 +-f https://download.pytorch.org/whl/torch_stable.html +torch-model-archiver==0.11.1 +torch-workflow-archiver==0.2.14 +torchserve==0.11.1 From 18f2760ba0d74df7835240f2f5efaa634743a82a Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Mon, 26 Aug 2024 15:47:02 -0700 Subject: [PATCH 23/56] Correct TM&B (#329) Signed-off-by: tylertitsworth --- CONTRIBUTING.md | 4 ++-- README.md | 4 ++-- classical-ml/README.md | 2 +- mkdocs.yml | 2 +- python/README.md | 2 +- pytorch/README.md | 4 +--- pytorch/serving/README.md | 2 +- tensorflow/README.md | 2 +- workflows/README.md | 8 ++++---- workflows/charts/huggingface-llm/README.md | 2 +- workflows/charts/torchserve/Chart.yaml | 4 ++-- workflows/charts/torchserve/README.md | 8 ++++---- workflows/charts/torchserve/README.md.gotmpl | 4 ++-- 13 files changed, 23 insertions(+), 25 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f431ef44..9d61a8ac 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -Thank you for considering contributing to Intel® AI Containers! We welcome your help to make this project better. Contributing to an open source project can be a daunting task, but the Intel AI Containers team is here to help you through the process. If at any point in this process you feel out of your depth or confused by our processes, please don't hesitate to reach out to a maintainer or file an [issue](https://github.com/intel/ai-containers/issues). +Thank you for considering contributing to AI Containers! We welcome your help to make this project better. Contributing to an open source project can be a daunting task, but the Intel AI Containers team is here to help you through the process. If at any point in this process you feel out of your depth or confused by our processes, please don't hesitate to reach out to a maintainer or file an [issue](https://github.com/intel/ai-containers/issues). ## Getting Started @@ -138,4 +138,4 @@ commit automatically with `git commit -s`. ## License -Intel® AI Containers is licensed under the terms in [LICENSE](./LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. +AI Containers is licensed under the terms in [LICENSE](./LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. diff --git a/README.md b/README.md index 23705123..a152105c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Intel® AI Containers +# AI Containers [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8270/badge)](https://www.bestpractices.dev/projects/8270) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/intel/ai-containers/badge)](https://securityscorecards.dev/viewer/?uri=github.com/intel/ai-containers) @@ -28,7 +28,7 @@ docker login $REGISTRY docker pull $REGISTRY/$REPO:latest ``` -The maintainers of Intel® AI Containers use Azure to store containers, but an open source container registry like [harbor](https://github.com/goharbor/harbor) is preferred. +The maintainers of AI Containers use Azure to store containers, but an open source container registry like [harbor](https://github.com/goharbor/harbor) is preferred. > [!WARNING] > You can optionally skip this step and use some placeholder values, however some container groups depend on other images and will pull from a registry that you have not defined and result in an error. diff --git a/classical-ml/README.md b/classical-ml/README.md index 9d63355c..97cc50c2 100644 --- a/classical-ml/README.md +++ b/classical-ml/README.md @@ -63,7 +63,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s ## Build from Source -To build the images from source, clone the [Intel® AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: +To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: ```bash cd classical-ml diff --git a/mkdocs.yml b/mkdocs.yml index 20c73487..94322e7d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -51,7 +51,7 @@ plugins: - read_csv repo_name: intel/ai-containers repo_url: https://github.com/intel/ai-containers -site_name: Intel® AI Containers +site_name: AI Containers #TODO: Get previous container versions in an easy way # https://squidfunk.github.io/mkdocs-material/setup/setting-up-versioning/ theme: diff --git a/python/README.md b/python/README.md index 0b9f95dc..25d3d230 100644 --- a/python/README.md +++ b/python/README.md @@ -15,7 +15,7 @@ The images below include variations for only the core packages in the [Intel® D ## Build from Source -To build the images from source, clone the [Intel® AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: +To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: ```bash cd python diff --git a/pytorch/README.md b/pytorch/README.md index f036502f..f3fcda4e 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -241,8 +241,6 @@ Additionally, if you have a [DeepSpeed* configuration](https://www.deepspeed.ai/ --- -#### Hugging Face Generative AI Container - The image below is an extension of the IPEX Multi-Node Container designed to run Hugging Face Generative AI scripts. The container has the typical installations needed to run and fine tune PyTorch generative text models from Hugging Face. It can be used to run multinode jobs using the same instructions from the [IPEX Multi-Node container](#setup-and-run-ipex-multi-node-container). | Tag(s) | Pytorch | IPEX | oneCCL | HF Transformers | Dockerfile | @@ -324,7 +322,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s ## Build from Source -To build the images from source, clone the [Intel® AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: +To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: ```bash cd pytorch diff --git a/pytorch/serving/README.md b/pytorch/serving/README.md index 6ca33ef1..08114bba 100644 --- a/pytorch/serving/README.md +++ b/pytorch/serving/README.md @@ -155,7 +155,7 @@ As demonstrated in the above example, models must be registered before they can ### KServe -Apply Intel Optimizations to KServe by patching the serving runtimes to use Intel Optimized Serving Containers with `kubectl apply -f patch.yaml` +Apply Intel Optimizations to KServe by patching the serving runtimes to use Serving Containers with Intel Optimizations via `kubectl apply -f patch.yaml` > [!NOTE] > You can modify this `patch.yaml` file to change the serving runtime pod configuration. diff --git a/tensorflow/README.md b/tensorflow/README.md index d71dc349..990ecf71 100644 --- a/tensorflow/README.md +++ b/tensorflow/README.md @@ -286,7 +286,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s ## Build from Source -To build the images from source, clone the [Intel® AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: +To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: ```bash cd pytorch diff --git a/workflows/README.md b/workflows/README.md index 21269eb7..1f6c9ea6 100644 --- a/workflows/README.md +++ b/workflows/README.md @@ -1,6 +1,6 @@ # Intel® AI Workflows -Demonstrating showing how the [Intel® AI Containers] can be used for different use cases: +Demonstrating showing how the [AI Containers] can be used for different use cases: ## PyTorch Workflows @@ -11,7 +11,7 @@ Demonstrating showing how the [Intel® AI Containers] can be used for different ## Build from Source -To build the images from source, clone the [Intel® AI Containers] repository, follow the main `README.md` file to setup your environment, and run the following command: +To build the images from source, clone the [AI Containers] repository, follow the main `README.md` file to setup your environment, and run the following command: ```bash cd workflows/charts/huggingface-llm @@ -21,7 +21,7 @@ docker compose run huggingface-llm sh -c "python /workspace/scripts/finetune.py ## License -View the [License](https://github.com/intel/ai-containers/blob/main/LICENSE) for the [Intel® AI Containers]. +View the [License](https://github.com/intel/ai-containers/blob/main/LICENSE) for the [AI Containers]. The images below also contain other software which may be under other licenses (such as Pytorch*, Jupyter*, Bash, etc. from the base). @@ -31,6 +31,6 @@ It is the image user's responsibility to ensure that any use of The images below -[Intel® AI Containers]: https://github.com/intel/ai-containers +[AI Containers]: https://github.com/intel/ai-containers [Distributed LLM Fine Tuning with Kubernetes]: https://github.com/intel/ai-containers/tree/main/workflows/charts/huggingface-llm [TorchServe* with Kubernetes]: https://github.com/intel/ai-containers/tree/main/workflows/charts/torchserve diff --git a/workflows/charts/huggingface-llm/README.md b/workflows/charts/huggingface-llm/README.md index e2439830..47755eef 100644 --- a/workflows/charts/huggingface-llm/README.md +++ b/workflows/charts/huggingface-llm/README.md @@ -347,4 +347,4 @@ fine tune the model. ``` ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.13.1](https://github.com/norwoodj/helm-docs/releases/v1.13.1) +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/workflows/charts/torchserve/Chart.yaml b/workflows/charts/torchserve/Chart.yaml index f3472530..99db0496 100644 --- a/workflows/charts/torchserve/Chart.yaml +++ b/workflows/charts/torchserve/Chart.yaml @@ -13,8 +13,8 @@ # limitations under the License. apiVersion: v2 -name: intel-torchserve -description: Intel TorchServe is a performant, flexible and easy to use tool for serving PyTorch models in production. +name: torchserve-on-intel +description: TorchServe on Intel is a performant, flexible and easy to use tool for serving PyTorch models in production. # A chart can be either an 'application' or a 'library' chart. # diff --git a/workflows/charts/torchserve/README.md b/workflows/charts/torchserve/README.md index b35cc7d4..c1a717f5 100644 --- a/workflows/charts/torchserve/README.md +++ b/workflows/charts/torchserve/README.md @@ -1,8 +1,8 @@ -# Intel TorchServe +# TorchServe with Intel Optimizations -Intel TorchServe is a performant, flexible and easy to use tool for serving PyTorch models in production. +TorchServe on Intel is a performant, flexible and easy to use tool for serving PyTorch models in production. -For more information about how to use Intel Optimized TorchServe, check out the [container documentation](../../../pytorch/serving/README.md). +For more information about how to use TorchServe with Intel Optimizations, check out the [container documentation](../../../pytorch/serving/README.md). ![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) @@ -18,7 +18,7 @@ For more information about how to use Intel Optimized TorchServe, check out the | deploy.resources.limits | object | `{"cpu":"4000m","memory":"1Gi"}` | Maximum resources per pod | | deploy.resources.requests | object | `{"cpu":"1000m","memory":"512Mi"}` | Minimum resources per pod | | deploy.storage.nfs | object | `{"enabled":false,"path":"nil","readOnly":true,"server":"nil","subPath":"nil"}` | Network File System (NFS) storage for models | -| deploy.tokens_disabled | bool | `false` | Set token authentication on or off. Checkout the latest [torchserve docs](https://github.com/pytorch/serve/blob/master/docs/token_authorization_api.md) for more details. | +| deploy.tokens_disabled | bool | `true` | Set token authentication on or off. Checkout the latest [torchserve docs](https://github.com/pytorch/serve/blob/master/docs/token_authorization_api.md) for more details. | | fullnameOverride | string | `""` | Full qualified Domain Name | | nameOverride | string | `""` | Name of the serving service | | pvc.size | string | `"1Gi"` | Size of the storage | diff --git a/workflows/charts/torchserve/README.md.gotmpl b/workflows/charts/torchserve/README.md.gotmpl index 1ddf329d..465c03ae 100644 --- a/workflows/charts/torchserve/README.md.gotmpl +++ b/workflows/charts/torchserve/README.md.gotmpl @@ -1,8 +1,8 @@ -# Intel TorchServe +# TorchServe with Intel Optimizations {{ template "chart.description" . }} -For more information about how to use Intel Optimized TorchServe, check out the [container documentation](../../../pytorch/serving/README.md). +For more information about how to use TorchServe with Intel Optimizations, check out the [container documentation](../../../pytorch/serving/README.md). {{ template "chart.versionBadge" . }}{{ template "chart.typeBadge" . }}{{ template "chart.appVersionBadge" . }} From cd824cf70f6f8a28e31643891328f6d5f59c4c44 Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Tue, 27 Aug 2024 09:27:07 -0700 Subject: [PATCH 24/56] Open Up XPU Conda Reqs (#330) Signed-off-by: tylertitsworth Signed-off-by: Tyler Titsworth --- pytorch/xpu-requirements.txt | 10 +++++----- tensorflow/xpu-requirements.txt | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pytorch/xpu-requirements.txt b/pytorch/xpu-requirements.txt index 09badb28..e7771aa0 100644 --- a/pytorch/xpu-requirements.txt +++ b/pytorch/xpu-requirements.txt @@ -5,8 +5,8 @@ intel_extension_for_pytorch==2.1.40+xpu oneccl_bind_pt==2.1.400+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us setuptools==69.5.1 -numpy==1.26.4 -idna==3.7 -requests==2.32.0 -tqdm==4.66.3 -urllib3==2.2.2 +numpy>=1.26.4 +idna>=3.7 +requests>=2.32.0 +tqdm>=4.66.3 +urllib3>=2.2.2 diff --git a/tensorflow/xpu-requirements.txt b/tensorflow/xpu-requirements.txt index cbb01fc4..a338e80f 100644 --- a/tensorflow/xpu-requirements.txt +++ b/tensorflow/xpu-requirements.txt @@ -1,6 +1,6 @@ tensorflow==2.15.0 intel-extension-for-tensorflow[xpu]==2.15.0.1 -idna==3.7 -requests==2.32.0 -tqdm==4.66.3 -urllib3==2.2.2 +idna>=3.7 +requests>=2.32.0 +tqdm>=4.66.3 +urllib3>=2.2.2 From 7ad40200d702be4ddeef9d2e9e176ff63b3c689f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:01:02 -0700 Subject: [PATCH 25/56] Bump numpy from 2.0.1 to 2.1.0 in /tensorflow in the tensorflow group across 1 directory (#341) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tensorflow/serving/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tensorflow/serving/requirements.txt b/tensorflow/serving/requirements.txt index cd80fbcd..1f2e56e4 100644 --- a/tensorflow/serving/requirements.txt +++ b/tensorflow/serving/requirements.txt @@ -1,4 +1,4 @@ -numpy==2.0.1 +numpy==2.1.0 pillow==10.4.0 requests==2.32.3 tensorflow==2.17.0 From ecdeddb2b2ed5c22804f69fc67324e1bff19e872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 09:07:53 -0700 Subject: [PATCH 26/56] Bump the apptainer group across 1 directory with 5 updates (#335) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Srikanth Ramakrishna --- apptainer/python/requirements.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apptainer/python/requirements.txt b/apptainer/python/requirements.txt index 9dede726..b259254d 100644 --- a/apptainer/python/requirements.txt +++ b/apptainer/python/requirements.txt @@ -1,6 +1,6 @@ -numpy==2.0.1 -setuptools==72.1.0 +numpy==2.1.0 +setuptools==73.0.1 psutil==6.0.0 -mkl==2024.2.0 -mkl-include==2024.2.0 -intel-openmp==2024.2.0 +mkl==2024.2.1 +mkl-include==2024.2.1 +intel-openmp==2024.2.1 From 1d643a672eab8f99f6dc0c0df82c74b384e50823 Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Thu, 29 Aug 2024 11:23:20 -0700 Subject: [PATCH 27/56] Add Perf Sample Example Section (#307) Signed-off-by: Tyler Titsworth --- python/README.md | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/python/README.md b/python/README.md index 25d3d230..07d30dc0 100644 --- a/python/README.md +++ b/python/README.md @@ -1,19 +1,44 @@ -# Intel® Distribution for Python +# Intel® Distribution for Python* -[Intel® Distribution for Python] enhances performance and can improve your program speed from 10 to 100 times faster. It is a Python distribution that includes the [Intel® Math Kernel Library] (oneMKL) and other Intel performance libraries to enable near-native performance through acceleration of core numerical and machine learning packages. - -[Intel® Distribution for Python] is available as part of the [Intel® oneAPI Base Toolkit](https://software.intel.com/content/www/us/en/develop/tools/oneapi/base-toolkit.html). +[Intel® Distribution for Python*] enhances performance and can improve your program speed from 10 to 100 times faster. It is a Python* distribution that includes the [Intel® Math Kernel Library] (oneMKL) and other Intel performance libraries to enable near-native performance through acceleration of core numerical and machine learning packages. ## Images -The images below include variations for only the core packages in the [Intel® Distribution for Python] installation, or all of the packages. +The images below include variations for only the core packages in the [Intel® Distribution for Python*] installation, or all of the packages. | Tag(s) | IDP | | ---------------------- | ---------- | | `3.10-full`, `latest` | `2024.2.0` | | `3.10-core` | `2024.2.0` | -## Build from Source +## Run a Performance Sample + +To run a performance sample run the following commands: + +```bash +git clone https://github.com/intel/ai-containers +cd ai-containers/python +docker run --rm -it \ + -v $PWD/tests:/tests \ + intel/python:latest \ + python /tests/perf_sample.py +``` + +### Compare the results against stock python + +In the previous command, you should see a result at the bottom like: `Time Consuming: 0.03897857666015625`. We can compare this against `python:3.11-slim-bullseye` + +```bash +# Use the working directory from the above command +docker run --rm -it \ + -v $PWD/tests:/tests \ + python:3.11-slim-bullseye \ + bash +pip install numpy +python /tests/perf_sample.py +``` + +## Build from Source (Advanced) To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: @@ -27,8 +52,8 @@ You can find the list of services below for each container in the group: | Service Name | Description | | ------------ | ------------------------------------------------------------------- | -| `idp` | Base image with [Intel® Distribution for Python] | -| `pip` | Equivalent python image without [Intel® Distribution for Python] | +| `idp` | Base image with [Intel® Distribution for Python*] | +| `pip` | Equivalent python image without [Intel® Distribution for Python*] | ## License @@ -40,5 +65,5 @@ It is the image user's responsibility to ensure that any use of The images below -[Intel® Distribution for Python]: https://www.intel.com/content/www/us/en/developer/tools/oneapi/distribution-for-python.html#gs.9bos9m +[Intel® Distribution for Python*]: https://www.intel.com/content/www/us/en/developer/tools/oneapi/distribution-for-python.html#gs.9bos9m [Intel® Math Kernel Library]: https://www.intel.com/content/www/us/en/developer/tools/oneapi/onemkl.html From 57291b3e3ddab4ada5b1b57ea01bd930bcf77c39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:33:48 -0700 Subject: [PATCH 28/56] Bump notebook from 7.2.1 to 7.2.2 in /classical-ml in the pip group (#347) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- classical-ml/jupyter-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classical-ml/jupyter-requirements.txt b/classical-ml/jupyter-requirements.txt index 2cae0f91..2be0be31 100644 --- a/classical-ml/jupyter-requirements.txt +++ b/classical-ml/jupyter-requirements.txt @@ -1,4 +1,4 @@ jupyterlab==4.2.4 jupyterhub==5.1.0 -notebook==7.2.1 +notebook==7.2.2 jupyter-server-proxy>=4.1.2 From 3f6e3a5bc457cc82f4557134d36111d62209270e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:41:03 -0700 Subject: [PATCH 29/56] Bump jupyterlab from 4.2.4 to 4.2.5 in /classical-ml in the pip group (#348) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- classical-ml/jupyter-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classical-ml/jupyter-requirements.txt b/classical-ml/jupyter-requirements.txt index 2be0be31..d98ce88b 100644 --- a/classical-ml/jupyter-requirements.txt +++ b/classical-ml/jupyter-requirements.txt @@ -1,4 +1,4 @@ -jupyterlab==4.2.4 +jupyterlab==4.2.5 jupyterhub==5.1.0 notebook==7.2.2 jupyter-server-proxy>=4.1.2 From 69b24d3d5e2df5da32fdff7f3b3be256edee9bc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:46:44 -0700 Subject: [PATCH 30/56] Bump the docs group in /docs with 2 updates (#351) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 1058a38f..33207ff8 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,9 +1,9 @@ mkdocs-callouts>=1.13.2 mkdocs-git-authors-plugin>=0.8.0 mkdocs-git-revision-date-localized-plugin>=1.2.5 -mkdocs-material==9.5.33 +mkdocs-material==9.5.34 mkdocs-table-reader-plugin>=2.1.0 -mkdocs==1.6.0 +mkdocs==1.6.1 pandas>=2.0.3 pymdown-extensions>=10.8.1 python_on_whales>=0.71.0 From b95845e7e676321799a0b695623f81171a73b098 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:00:24 +0000 Subject: [PATCH 31/56] Bump actions/upload-artifact from 4.3.6 to 4.4.0 (#354) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yaml | 2 +- .github/workflows/security-report.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 1c08423e..5c5ab9e5 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -48,7 +48,7 @@ jobs: results_format: sarif repo_token: ${{ secrets.GITHUB_TOKEN }} publish_results: true - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/security-report.yaml b/.github/workflows/security-report.yaml index a9d3b98b..07290f08 100644 --- a/.github/workflows/security-report.yaml +++ b/.github/workflows/security-report.yaml @@ -35,7 +35,7 @@ jobs: sarifReportDir: ${{ github.workspace }} template: report token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: Security Report Summary path: ./*.pdf From f7e8c19499d13f7999cc3396996e54301b23ae02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 11:13:29 -0700 Subject: [PATCH 32/56] Bump github/codeql-action from 3.26.5 to 3.26.6 (#355) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/container-ci.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 27ccabad..a64e2307 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Cleanup if: always() run: docker rmi -f ${{ secrets.REGISTRY }}/${{ secrets.REPO }}:${{ matrix.container }} - - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: '${{ matrix.container }}-scan.sarif' category: '${{ matrix.container }}' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 5c5ab9e5..ecdde523 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -53,6 +53,6 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@2c779ab0d087cd7fe7b826087247c2c81f27bfa6 # v3.26.5 + - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: results.sarif From 536e096785a2c6905decd050a8e95513c614a784 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:27:02 +0000 Subject: [PATCH 33/56] Bump actions/setup-python from 5.1.1 to 5.2.0 (#357) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docs.yaml | 2 +- .github/workflows/test-runner-ci.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 0b8742c6..997f3e6a 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -36,7 +36,7 @@ jobs: with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: 3.8 cache: pip diff --git a/.github/workflows/test-runner-ci.yaml b/.github/workflows/test-runner-ci.yaml index 448e0970..75aa8c06 100644 --- a/.github/workflows/test-runner-ci.yaml +++ b/.github/workflows/test-runner-ci.yaml @@ -45,7 +45,7 @@ jobs: registry: ${{ secrets.REGISTRY }} username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_TOKEN }} - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ matrix.python }} - name: Install requirements @@ -88,7 +88,7 @@ jobs: registry: ${{ secrets.REGISTRY }} username: ${{ secrets.REGISTRY_USER }} password: ${{ secrets.REGISTRY_TOKEN }} - - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: "3.8" - name: Test Container Group From bde3ae0131b032a5562097bf91a2d62dd7c7cd44 Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Wed, 4 Sep 2024 08:20:15 -0700 Subject: [PATCH 34/56] Compare against same Python Version (#358) Signed-off-by: Tyler Titsworth --- python/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/README.md b/python/README.md index 07d30dc0..16b1d591 100644 --- a/python/README.md +++ b/python/README.md @@ -32,7 +32,7 @@ In the previous command, you should see a result at the bottom like: `Time Consu # Use the working directory from the above command docker run --rm -it \ -v $PWD/tests:/tests \ - python:3.11-slim-bullseye \ + python:3.10-slim-bullseye \ bash pip install numpy python /tests/perf_sample.py From 31730a917ebe8528313cdeb61de566ba8528f3df Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Thu, 5 Sep 2024 09:27:46 -0700 Subject: [PATCH 35/56] Create Container Map (#360) Signed-off-by: tylertitsworth --- .github/release/v0.4.0.json | 230 ++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 .github/release/v0.4.0.json diff --git a/.github/release/v0.4.0.json b/.github/release/v0.4.0.json new file mode 100644 index 00000000..2fc6b506 --- /dev/null +++ b/.github/release/v0.4.0.json @@ -0,0 +1,230 @@ +[ + { + "base": "ubuntu:22.04", + "dockerfile": "python/Dockerfile", + "repo": "intel/python", + "tag": "3.10-core" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "python/Dockerfile", + "repo": "intel/python", + "tag": "3.10-full" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "classical-ml/Dockerfile", + "repo": "intel/intel-optimized-ml", + "tag": "2024.6.0-pip-base" + }, + { + "base": "intel/intel-optimized-ml:2024.6.0-pip-base", + "dockerfile": "classical-ml/Dockerfile", + "repo": "intel/intel-optimized-ml", + "tag": "2024.6.0-pip-jupyter" + }, + { + "base": "intel/python:3.10-core", + "dockerfile": "classical-ml/Dockerfile", + "repo": "intel/intel-optimized-ml", + "tag": "2024.6.0-idp-base" + }, + { + "base": "intel/intel-optimized-ml:2024.6.0-idp-base", + "dockerfile": "classical-ml/Dockerfile", + "repo": "intel/intel-optimized-ml", + "tag": "2024.6.0-idp-jupyter" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.0.1-xpu-pip-base" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.0.1-xpu-pip-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.0.1-xpu-pip-jupyter" + }, + { + "base": "intel/python:3.10-core", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.0.1-xpu-idp-base" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.0.1-xpu-idp-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.0.1-xpu-idp-jupyter" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-pip-base" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.1-pip-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-pip-jupyter" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.1-pip-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-pip-multinode" + }, + { + "base": "intel/python:3.10-core", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-idp-base" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.1-idp-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-idp-jupyter" + }, + { + "base": "intel/intel-optimized-tensorflow:2.15.1-idp-base", + "dockerfile": "tensorflow/Dockerfile", + "repo": "intel/intel-optimized-tensorflow", + "tag": "2.15.1-idp-multinode" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.1.40-xpu-pip-base" + }, + { + "base": "intel/intel-optimized-pytorch:2.1.40-xpu-pip-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.1.40-xpu-pip-jupyter" + }, + { + "base": "intel/python:3.10-core", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.1.40-xpu-idp-base" + }, + { + "base": "intel/intel-optimized-pytorch:2.1.40-xpu-idp-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.1.40-xpu-idp-jupyter" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-pip-base" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-pip-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-pip-jupyter" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-pip-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-pip-multinode" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-pip-multinode", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-pip-multinode-hf-4.44.0-genai" + }, + { + "base": "intel/python:3.10-core", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-idp-base" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-idp-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-idp-jupyter" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-idp-base", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-idp-multinode" + }, + { + "base": "intel/intel-optimized-pytorch:2.4.0-idp-multinode", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-idp-multinode-hf-4.44.0-genai" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-serving-cpu" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "pytorch/Dockerfile", + "repo": "intel/intel-optimized-pytorch", + "tag": "2.4.0-serving-xpu" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/classical-ml", + "repo": "intel/classical-ml", + "tag": "latest-py3.9" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/classical-ml", + "repo": "intel/classical-ml", + "tag": "latest-py3.10" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/data-analytics", + "repo": "intel/data-analytics", + "tag": "latest-py3.9" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/data-analytics", + "repo": "intel/data-analytics", + "tag": "latest-py3.10" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/deep-learning", + "repo": "intel/deep-learning", + "tag": "latest-py3.9" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/deep-learning", + "repo": "intel/deep-learning", + "tag": "latest-py3.10" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/inference-optimization", + "repo": "intel/inference-optimization", + "tag": "latest-py3.9" + }, + { + "base": "ubuntu:22.04", + "dockerfile": "preset/inference-optimization", + "repo": "intel/inference-optimization", + "tag": "latest-py3.10" + } +] From 0af8f7ff5574664f2687f5467b6dc6a0ee153493 Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Fri, 6 Sep 2024 08:19:56 -0700 Subject: [PATCH 36/56] Patch Container Mapping (#361) Signed-off-by: Tyler Titsworth --- .github/release/v0.4.0.json | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/release/v0.4.0.json b/.github/release/v0.4.0.json index 2fc6b506..f9cd6ef2 100644 --- a/.github/release/v0.4.0.json +++ b/.github/release/v0.4.0.json @@ -81,19 +81,19 @@ "base": "intel/python:3.10-core", "dockerfile": "tensorflow/Dockerfile", "repo": "intel/intel-optimized-tensorflow", - "tag": "2.15.1-idp-base" + "tag": "2.15.0-idp-base" }, { - "base": "intel/intel-optimized-tensorflow:2.15.1-idp-base", + "base": "intel/intel-optimized-tensorflow:2.15.0-idp-base", "dockerfile": "tensorflow/Dockerfile", "repo": "intel/intel-optimized-tensorflow", - "tag": "2.15.1-idp-jupyter" + "tag": "2.15.0-idp-jupyter" }, { - "base": "intel/intel-optimized-tensorflow:2.15.1-idp-base", + "base": "intel/intel-optimized-tensorflow:2.15.0-idp-base", "dockerfile": "tensorflow/Dockerfile", "repo": "intel/intel-optimized-tensorflow", - "tag": "2.15.1-idp-multinode" + "tag": "2.15.0-idp-multinode" }, { "base": "ubuntu:22.04", @@ -181,49 +181,49 @@ }, { "base": "ubuntu:22.04", - "dockerfile": "preset/classical-ml", + "dockerfile": "preset/classical-ml/Dockerfile", "repo": "intel/classical-ml", "tag": "latest-py3.9" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/classical-ml", + "dockerfile": "preset/classical-ml/Dockerfile", "repo": "intel/classical-ml", "tag": "latest-py3.10" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/data-analytics", + "dockerfile": "preset/data-analytics/Dockerfile", "repo": "intel/data-analytics", "tag": "latest-py3.9" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/data-analytics", + "dockerfile": "preset/data-analytics/Dockerfile", "repo": "intel/data-analytics", "tag": "latest-py3.10" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/deep-learning", + "dockerfile": "preset/deep-learning/Dockerfile", "repo": "intel/deep-learning", "tag": "latest-py3.9" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/deep-learning", + "dockerfile": "preset/deep-learning/Dockerfile", "repo": "intel/deep-learning", "tag": "latest-py3.10" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/inference-optimization", + "dockerfile": "preset/inference-optimization/Dockerfile", "repo": "intel/inference-optimization", "tag": "latest-py3.9" }, { "base": "ubuntu:22.04", - "dockerfile": "preset/inference-optimization", + "dockerfile": "preset/inference-optimization/Dockerfile", "repo": "intel/inference-optimization", "tag": "latest-py3.10" } From 2e9bee7260fc0f9b2d15a2f993fb0517bdbe561e Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Fri, 6 Sep 2024 09:51:51 -0700 Subject: [PATCH 37/56] Patch container mapping (2) (#363) Signed-off-by: Tyler Titsworth --- .github/release/v0.4.0.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/release/v0.4.0.json b/.github/release/v0.4.0.json index f9cd6ef2..671769e9 100644 --- a/.github/release/v0.4.0.json +++ b/.github/release/v0.4.0.json @@ -141,7 +141,7 @@ "base": "intel/intel-optimized-pytorch:2.4.0-pip-multinode", "dockerfile": "pytorch/Dockerfile", "repo": "intel/intel-optimized-pytorch", - "tag": "2.4.0-pip-multinode-hf-4.44.0-genai" + "tag": "2.4.0-pip-hf-4.44.0-genai" }, { "base": "intel/python:3.10-core", @@ -165,7 +165,7 @@ "base": "intel/intel-optimized-pytorch:2.4.0-idp-multinode", "dockerfile": "pytorch/Dockerfile", "repo": "intel/intel-optimized-pytorch", - "tag": "2.4.0-idp-multinode-hf-4.44.0-genai" + "tag": "2.4.0-idp-hf-4.44.0-genai" }, { "base": "ubuntu:22.04", From 02e20979861586db7e849654e793be96849c996d Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Fri, 6 Sep 2024 17:04:14 -0700 Subject: [PATCH 38/56] Test CPU Validation Runners (#362) Signed-off-by: Tyler Titsworth Signed-off-by: tylertitsworth --- classical-ml/.actions.json | 2 +- preset/classical-ml/.actions.json | 2 +- preset/classical-ml/tests.yaml | 12 ++++++------ preset/data-analytics/.actions.json | 2 +- preset/data-analytics/tests.yaml | 6 +++--- python/.actions.json | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/classical-ml/.actions.json b/classical-ml/.actions.json index 36e21ad8..e7e793d3 100644 --- a/classical-ml/.actions.json +++ b/classical-ml/.actions.json @@ -1,5 +1,5 @@ { "PACKAGE_OPTION": ["idp", "pip"], "experimental": [true], - "runner_label": ["PVC"] + "runner_label": ["clx"] } diff --git a/preset/classical-ml/.actions.json b/preset/classical-ml/.actions.json index 639f025c..bc955304 100644 --- a/preset/classical-ml/.actions.json +++ b/preset/classical-ml/.actions.json @@ -1,5 +1,5 @@ { "PYTHON_VERSION": ["3.9", "3.10"], "experimental": [true], - "runner_label": ["PVC"] + "runner_label": ["clx"] } diff --git a/preset/classical-ml/tests.yaml b/preset/classical-ml/tests.yaml index 14529526..919eb4e9 100644 --- a/preset/classical-ml/tests.yaml +++ b/preset/classical-ml/tests.yaml @@ -21,23 +21,23 @@ # img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py3.9 modin-${PYTHON_VERSION:-3.9}: cmd: conda run -n classical-ml sample-tests/modin/test_modin.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} shm_size: 10.24G modin-notebook-${PYTHON_VERSION:-3.9}: cmd: papermill --log-output jupyter/modin/IntelModin_Vs_Pandas.ipynb -k classical-ml - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True scikit-${PYTHON_VERSION:-3.9}: cmd: conda run -n classical-ml sample-tests/scikit/test_scikit.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} scikit-notebook-${PYTHON_VERSION:-3.9}: cmd: papermill --log-output jupyter/sklearn/Intel_Extension_For_SKLearn_GettingStarted.ipynb -k classical-ml - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True xgboost-${PYTHON_VERSION:-3.9}: cmd: conda run -n classical-ml sample-tests/xgboost/test_xgboost.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} xgboost-notebook-${PYTHON_VERSION:-3.9}: cmd: papermill --log-output jupyter/xgboost/IntelPython_XGBoost_Performance.ipynb -k classical-ml - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-classical-ml-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True diff --git a/preset/data-analytics/.actions.json b/preset/data-analytics/.actions.json index 639f025c..bc955304 100644 --- a/preset/data-analytics/.actions.json +++ b/preset/data-analytics/.actions.json @@ -1,5 +1,5 @@ { "PYTHON_VERSION": ["3.9", "3.10"], "experimental": [true], - "runner_label": ["PVC"] + "runner_label": ["clx"] } diff --git a/preset/data-analytics/tests.yaml b/preset/data-analytics/tests.yaml index 5aff8c81..846bf0b9 100644 --- a/preset/data-analytics/tests.yaml +++ b/preset/data-analytics/tests.yaml @@ -14,12 +14,12 @@ dataset-librarian-${PYTHON_VERSION:-3.9}: cmd: conda run -n data-analytics bash -c 'yes | python -m dataset_librarian.dataset -n msmarco --download -d ~/msmarco' - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} modin-${PYTHON_VERSION:-3.9}: cmd: conda run -n data-analytics sample-tests/modin/test_modin.sh - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} shm_size: 10G modin-notebook-${PYTHON_VERSION:-3.9}: cmd: papermill --log-output jupyter/modin/IntelModin_Vs_Pandas.ipynb -k data-analytics - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.1.0}-py${PYTHON_VERSION:-3.9} + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-data-analytics-${RELEASE:-2024.2.0}-py${PYTHON_VERSION:-3.9} notebook: True diff --git a/python/.actions.json b/python/.actions.json index db103a36..1112d076 100644 --- a/python/.actions.json +++ b/python/.actions.json @@ -1,5 +1,5 @@ { "IDP_VERSION": ["full", "core"], "experimental": [true], - "runner_label": ["PVC"] + "runner_label": ["clx"] } From d1cc89f0c568c1a8c0ce6a23076cc9d33544ba53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 08:34:15 -0700 Subject: [PATCH 39/56] Bump pydantic from 2.8.2 to 2.9.1 in /test-runner in the test-runner group (#371) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test-runner/dev-requirements.txt | 2 +- test-runner/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test-runner/dev-requirements.txt b/test-runner/dev-requirements.txt index 2f4f8fbf..e409fb0f 100644 --- a/test-runner/dev-requirements.txt +++ b/test-runner/dev-requirements.txt @@ -3,7 +3,7 @@ coverage>=7.5.0 coveralls>=4.0.1 expandvars>=0.12.0 hypothesis>=6.100.1 -pydantic==2.8.2 +pydantic==2.9.1 pylint>=3.1.0 pytest>=8.1.1 python_on_whales>=0.70.1 diff --git a/test-runner/requirements.txt b/test-runner/requirements.txt index 79752623..ee95cc0a 100644 --- a/test-runner/requirements.txt +++ b/test-runner/requirements.txt @@ -1,5 +1,5 @@ expandvars>=0.12.0 -pydantic==2.8.2 +pydantic==2.9.1 python_on_whales>=0.70.1 pyyaml>=6.0.1 tabulate>=0.9.0 From 70bfa5655e2ed6e7023eaa2e195a5f0eae61e58f Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Tue, 10 Sep 2024 10:03:50 -0700 Subject: [PATCH 40/56] Gaudi OpenShift notebook container (#365) Signed-off-by: sharvil10 --- .github/dependabot.yml | 8 + .../gaudi/demo/oneapi-sample.ipynb | 315 ++++++++++++++++++ .../gaudi/docker/Dockerfile.rhel9.2 | 236 +++++++++++++ .../gaudi/docker/Dockerfile.rhel9.4 | 249 ++++++++++++++ .../openshift-ai/gaudi/docker/builder/run | 39 +++ .../gaudi/docker/docker-compose.yaml | 64 ++++ .../gaudi/docker/install-python310.sh | 103 ++++++ .../openshift-ai/gaudi/docker/install_efa.sh | 40 +++ .../gaudi/docker/install_packages.sh | 46 +++ .../gaudi/docker/requirements.txt | 43 +++ .../gaudi/docker/start-notebook.sh | 59 ++++ .../gaudi/docker/utils/process.sh | 34 ++ .../openshift-ai/{ => oneapi}/README.md | 0 .../{ => oneapi}/assets/step-1.png | Bin .../{ => oneapi}/assets/step-2.png | Bin .../{ => oneapi}/assets/step-3.png | Bin .../{ => oneapi}/assets/step-4.png | Bin .../manifests/intel-optimized-ml.yaml | 0 .../manifests/intel-optimized-pytorch.yaml | 0 .../manifests/intel-optimized-tensorflow.yaml | 0 20 files changed, 1236 insertions(+) create mode 100644 enterprise/redhat/openshift-ai/gaudi/demo/oneapi-sample.ipynb create mode 100644 enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.2 create mode 100644 enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.4 create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/builder/run create mode 100644 enterprise/redhat/openshift-ai/gaudi/docker/docker-compose.yaml create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/install-python310.sh create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/install_efa.sh create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/install_packages.sh create mode 100644 enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/start-notebook.sh create mode 100755 enterprise/redhat/openshift-ai/gaudi/docker/utils/process.sh rename enterprise/redhat/openshift-ai/{ => oneapi}/README.md (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/assets/step-1.png (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/assets/step-2.png (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/assets/step-3.png (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/assets/step-4.png (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/manifests/intel-optimized-ml.yaml (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/manifests/intel-optimized-pytorch.yaml (100%) rename enterprise/redhat/openshift-ai/{ => oneapi}/manifests/intel-optimized-tensorflow.yaml (100%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1fe07741..4289ee44 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -95,3 +95,11 @@ updates: package-ecosystem: pip schedule: interval: weekly + - directory: enterprise/redhat/openshift-ai/gaudi/docker + groups: + gaudi-openshift: + patterns: + - "requirements.txt" + package-ecosystem: pip + schedule: + interval: weekly diff --git a/enterprise/redhat/openshift-ai/gaudi/demo/oneapi-sample.ipynb b/enterprise/redhat/openshift-ai/gaudi/demo/oneapi-sample.ipynb new file mode 100644 index 00000000..9bd94af3 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/demo/oneapi-sample.ipynb @@ -0,0 +1,315 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1e973d1b-c6d0-48a5-a774-0f114101e81e", + "metadata": {}, + "source": [ + "# Getting started with PyTorch on Intel® Gaudi.\n", + "\n", + "This notebook is to help you get started quickly using the Intel® Gaudi accelerator in this container. A simple MNIST model is trained on the Gaudi acclerator. You can tune some of the parameters below to change configuration of the training. For more information and reference please refer to the official documentation of [Intel® Gaudi acclerator](https://docs.habana.ai/en/latest/index.html)." + ] + }, + { + "cell_type": "markdown", + "id": "7eaacf55-bea2-43be-bb48-163848db1a30", + "metadata": { + "tags": [] + }, + "source": [ + "### Setup modes for training\n", + "\n", + "1. lazy_mode: Set to True(False) to enable(disable) lazy mode.\n", + "2. enable_amp: Set to True(False) to enable Automatic Mixed Precision.\n", + "3. epochs: Number of epochs for training\n", + "4. lr: Learning rate for training\n", + "5. batch_size: Number of samples in a batch\n", + "6. milestones: Milestone epochs for the stepLR scheduler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e7cf831-6fe6-46ed-a6fd-f2651cc226af", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "lazy_mode = False\n", + "enable_amp = False\n", + "epochs = 20\n", + "batch_size = 128\n", + "lr = 0.01\n", + "milestones = [10,15]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cee8ad90-c52d-4a50-876f-ce0762cb1b62", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import os\n", + "os.environ['HABANA_LOGS']='/opt/app-root/logs'\n", + "if lazy_mode:\n", + " os.environ['PT_HPU_LAZY_MODE'] = '1'\n", + "else:\n", + " os.environ['PT_HPU_LAZY_MODE'] = '0'" + ] + }, + { + "cell_type": "markdown", + "id": "6eac33d0-2e64-4233-8b3f-40bb7217fef8", + "metadata": { + "tags": [] + }, + "source": [ + "### Import packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06ad44ff-9744-4d6f-af90-375e64717b59", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "import torch.nn.functional as F\n", + "import torchvision\n", + "import torchvision.transforms as transforms\n", + "import os\n", + "\n", + "# Import Habana Torch Library\n", + "import habana_frameworks.torch.core as htcore" + ] + }, + { + "cell_type": "markdown", + "id": "062de7f3-4561-4af3-a9ed-2c4cfc918f2f", + "metadata": {}, + "source": [ + "### Define Model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9df57abb-0b63-4e1c-9d9b-87e74964300e", + "metadata": {}, + "outputs": [], + "source": [ + "class SimpleModel(nn.Module):\n", + " def __init__(self):\n", + " super(SimpleModel, self).__init__()\n", + "\n", + " self.fc1 = nn.Linear(784, 256)\n", + " self.fc2 = nn.Linear(256, 64)\n", + " self.fc3 = nn.Linear(64, 10)\n", + "\n", + " def forward(self, x):\n", + "\n", + " out = x.view(-1,28*28)\n", + " out = F.relu(self.fc1(out))\n", + " out = F.relu(self.fc2(out))\n", + " out = self.fc3(out)\n", + "\n", + " return out" + ] + }, + { + "cell_type": "markdown", + "id": "d899885b-5b4d-4557-a90c-9d507875c2ee", + "metadata": {}, + "source": [ + "### Define training routine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b17e9aa-fa11-4870-a7d4-183b803177ab", + "metadata": {}, + "outputs": [], + "source": [ + "def train(net,criterion,optimizer,trainloader,device):\n", + "\n", + " net.train()\n", + " if not lazy_mode:\n", + " net = torch.compile(net,backend=\"hpu_backend\")\n", + " train_loss = 0.0\n", + " correct = 0\n", + " total = 0\n", + "\n", + " for batch_idx, (data, targets) in enumerate(trainloader):\n", + "\n", + " data, targets = data.to(device), targets.to(device)\n", + "\n", + " optimizer.zero_grad()\n", + " if enable_amp:\n", + " with torch.autocast(device_type=\"hpu\", dtype=torch.bfloat16):\n", + " outputs = net(data)\n", + " loss = criterion(outputs, targets)\n", + " else:\n", + " outputs = net(data)\n", + " loss = criterion(outputs, targets)\n", + "\n", + " loss.backward()\n", + " \n", + " # API call to trigger execution\n", + " if lazy_mode:\n", + " htcore.mark_step()\n", + " \n", + " optimizer.step()\n", + "\n", + " # API call to trigger execution\n", + " if lazy_mode:\n", + " htcore.mark_step()\n", + "\n", + " train_loss += loss.item()\n", + " _, predicted = outputs.max(1)\n", + " total += targets.size(0)\n", + " correct += predicted.eq(targets).sum().item()\n", + "\n", + " train_loss = train_loss/(batch_idx+1)\n", + " train_acc = 100.0*(correct/total)\n", + " print(\"Training loss is {} and training accuracy is {}\".format(train_loss,train_acc))" + ] + }, + { + "cell_type": "markdown", + "id": "b7a22d69-a91f-48e1-8fac-e1cfe68590b7", + "metadata": {}, + "source": [ + "### Define testing routine" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9aa379b-b376-4623-9b5c-f778c3d90ce7", + "metadata": {}, + "outputs": [], + "source": [ + "def test(net,criterion,testloader,device):\n", + "\n", + " net.eval()\n", + " test_loss = 0\n", + " correct = 0\n", + " total = 0\n", + "\n", + " with torch.no_grad():\n", + "\n", + " for batch_idx, (data, targets) in enumerate(testloader):\n", + "\n", + " data, targets = data.to(device), targets.to(device)\n", + " \n", + " if enable_amp:\n", + " with torch.autocast(device_type=\"hpu\", dtype=torch.bfloat16):\n", + " outputs = net(data)\n", + " loss = criterion(outputs, targets)\n", + " else:\n", + " outputs = net(data)\n", + " loss = criterion(outputs, targets)\n", + "\n", + "\n", + " # API call to trigger execution\n", + " if lazy_mode:\n", + " htcore.mark_step()\n", + "\n", + " test_loss += loss.item()\n", + " _, predicted = outputs.max(1)\n", + " total += targets.size(0)\n", + " correct += predicted.eq(targets).sum().item()\n", + "\n", + " test_loss = test_loss/(batch_idx+1)\n", + " test_acc = 100.0*(correct/total)\n", + " print(\"Testing loss is {} and testing accuracy is {}\".format(test_loss,test_acc))" + ] + }, + { + "cell_type": "markdown", + "id": "22e76af9-e355-4299-b84d-f34c9a25e76d", + "metadata": {}, + "source": [ + "### Run the main routine to train and test the model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c8ddfb1-d4f7-44b2-aff0-f86f1db8c971", + "metadata": {}, + "outputs": [], + "source": [ + "load_path = './data'\n", + "save_path = './checkpoints'\n", + "\n", + "if(not os.path.exists(save_path)):\n", + " os.makedirs(save_path)\n", + "\n", + "# Target the Gaudi HPU device\n", + "device = torch.device(\"hpu\")\n", + "\n", + "# Data\n", + "transform = transforms.Compose([\n", + " transforms.ToTensor(),\n", + "])\n", + "\n", + "trainset = torchvision.datasets.MNIST(root=load_path, train=True,\n", + " download=True, transform=transform)\n", + "trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,\n", + " shuffle=True, num_workers=2)\n", + "testset = torchvision.datasets.MNIST(root=load_path, train=False,\n", + " download=True, transform=transform)\n", + "testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,\n", + " shuffle=False, num_workers=2)\n", + "\n", + "net = SimpleModel()\n", + "net.to(device)\n", + "\n", + "criterion = nn.CrossEntropyLoss()\n", + "optimizer = optim.SGD(net.parameters(), lr=lr,\n", + " momentum=0.9, weight_decay=5e-4)\n", + "scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.1)\n", + "\n", + "for epoch in range(1, epochs+1):\n", + " print(\"=====================================================================\")\n", + " print(\"Epoch : {}\".format(epoch))\n", + " train(net,criterion,optimizer,trainloader,device)\n", + " test(net,criterion,testloader,device)\n", + "\n", + " torch.save(net.state_dict(), os.path.join(save_path,'epoch_{}.pth'.format(epoch)))\n", + "\n", + " scheduler.step()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.2 b/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.2 new file mode 100644 index 00000000..d1449fd8 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.2 @@ -0,0 +1,236 @@ +ARG BASE_IMAGE +ARG BASE_TAG +FROM ${BASE_IMAGE}:${BASE_TAG} AS gaudi-base +ARG ARTIFACTORY_URL +ARG VERSION +ARG REVISION + +LABEL vendor="Intel Corporation" +LABEL release="${VERSION}-${REVISION}" + +ENV HOME="/opt/app-root/src" +WORKDIR /opt/app-root/src + +RUN dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \ + dnf clean all && rm -rf /var/cache/yum + +RUN echo "[BaseOS]" > /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "name=CentOS Linux 9 - BaseOS" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo + +RUN echo "[centos9]" > /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "name=CentOS Linux 9 - AppStream" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo + +RUN dnf install -y \ + clang \ + cmake3 \ + cpp \ + gcc \ + gcc-c++ \ + glibc \ + glibc-headers \ + glibc-devel \ + jemalloc \ + libarchive \ + libksba \ + unzip \ + llvm \ + lsof \ + python3-devel \ + openssh-clients \ + openssl \ + openssl-devel \ + libjpeg-devel \ + openssh-server \ + lsb_release \ + wget \ + git \ + libffi-devel \ + bzip2-devel \ + zlib-devel \ + mesa-libGL \ + iproute \ + python3-dnf-plugin-versionlock && \ + # update pkgs (except OS version) for resolving potentials CVEs + dnf versionlock add redhat-release* && \ + dnf update -y && \ + dnf clean all && rm -rf /var/cache/yum + +RUN mkdir -p /licenses && \ + wget -O /licenses/LICENSE https://raw.githubusercontent.com/intel/ai-containers/main/LICENSE + +ENV PYTHON_VERSION=3.10 +COPY install-python310.sh . +RUN ./install-python310.sh rhel9.2 && rm install-python310.sh +ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + +COPY install_efa.sh . +RUN ./install_efa.sh && rm install_efa.sh && rm -rf /etc/ld.so.conf.d/efa.conf /etc/profile.d/efa.sh + +ENV LIBFABRIC_VERSION="1.20.0" +ENV LIBFABRIC_ROOT="/opt/habanalabs/libfabric-${LIBFABRIC_VERSION}" +ENV MPI_ROOT=/opt/amazon/openmpi +ENV LD_LIBRARY_PATH=$LIBFABRIC_ROOT/lib:${MPI_ROOT}/lib:/usr/lib/habanalabs:$LD_LIBRARY_PATH +ENV PATH=${LIBFABRIC_ROOT}/bin:${MPI_ROOT}/bin:$PATH +ENV OPAL_PREFIX=${MPI_ROOT} +ENV MPICC=${MPI_ROOT}/bin/mpicc +ENV RDMAV_FORK_SAFE=1 +ENV FI_EFA_USE_DEVICE_RDMA=1 + +RUN echo "[habanalabs]" > /etc/yum.repos.d/habanalabs.repo && \ + echo "name=Habana RH9 Linux repo" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "baseurl=https://${ARTIFACTORY_URL}/artifactory/rhel/9/9.2" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "gpgkey=https://${ARTIFACTORY_URL}/artifactory/rhel/9/9.2/repodata/repomd.xml.key" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/habanalabs.repo + +# for Habana GPG key with SHA-1 signature +RUN update-crypto-policies --set DEFAULT:SHA1 + +RUN dnf install -y habanalabs-rdma-core-"$VERSION"-"$REVISION".el9 \ + habanalabs-thunk-"$VERSION"-"$REVISION".el9 \ + habanalabs-firmware-tools-"$VERSION"-"$REVISION".el9 \ + habanalabs-graph-"$VERSION"-"$REVISION".el9 && \ + rm -f /etc/yum.repos.d/habanalabs.repo && rm -f /etc/yum.repos.d/habana.repo && rm -rf /tmp/* && \ + dnf clean all && rm -rf /var/cache/yum + +RUN rpm -V habanalabs-rdma-core && rpm -V habanalabs-thunk && rpm -V habanalabs-firmware-tools && rpm -V habanalabs-graph + +# There is no need to store pip installation files inside docker image +ENV PIP_NO_CACHE_DIR=on +ENV PIP_DISABLE_PIP_VERSION_CHECK=1 +ENV RDMA_CORE_ROOT=/opt/habanalabs/rdma-core/src +ENV RDMA_CORE_LIB=${RDMA_CORE_ROOT}/build/lib + +RUN wget -nv -O /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 https://github.com/ofiwg/libfabric/releases/download/v${LIBFABRIC_VERSION}/libfabric-${LIBFABRIC_VERSION}.tar.bz2 && \ + cd /tmp/ && tar xf /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 && \ + cd /tmp/libfabric-${LIBFABRIC_VERSION} && \ + ./configure --prefix=$LIBFABRIC_ROOT --enable-psm3-verbs --enable-verbs=yes --with-synapseai=/usr && \ + make && make install && cd / && rm -rf /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 /tmp/libfabric-${LIBFABRIC_VERSION} + +RUN wget -nv -O /tmp/main.zip https://github.com/HabanaAI/hccl_ofi_wrapper/archive/refs/heads/main.zip && \ + unzip /tmp/main.zip -d /tmp && \ + cd /tmp/hccl_ofi_wrapper-main && \ + make && cp -f libhccl_ofi_wrapper.so /usr/lib/habanalabs/libhccl_ofi_wrapper.so && \ + cd / && \ + rm -rf /tmp/main.zip /tmp/hccl_ofi_wrapper-main + +ENV GC_KERNEL_PATH=/usr/lib/habanalabs/libtpc_kernels.so +ENV HABANA_LOGS=/opt/app-root/log/habana_logs/ +ENV HABANA_SCAL_BIN_PATH=/opt/habanalabs/engines_fw +ENV HABANA_PLUGINS_LIB_PATH=/opt/habanalabs/habana_plugins + +ENV APP_ROOT="/opt/app-root" + +RUN python3.10 -m pip install "pip>=23.3" "setuptools>=70.0.0" "wheel==0.38.4" + +WORKDIR ${APP_ROOT} + +RUN python3.10 -m venv ${APP_ROOT} && \ + wget -O ${APP_ROOT}/bin/fix-permissions \ + https://raw.githubusercontent.com/sclorg/s2i-python-container/master/3.9-minimal/root/usr/bin/fix-permissions && \ + chown -R 1001:0 ${APP_ROOT} && \ + chmod +x ${APP_ROOT}/bin/fix-permissions && \ + ${APP_ROOT}/bin/fix-permissions ${APP_ROOT} -P && \ + echo "unset BASH_ENV PROMPT_COMMAND ENV" >> ${APP_ROOT}/bin/activate + +USER 1001 + +ENV BASH_ENV="${APP_ROOT}/bin/activate" +ENV ENV="${APP_ROOT}/bin/activate" +ENV PROMPT_COMMAND=". ${APP_ROOT}/bin/activate" + +SHELL ["/bin/bash", "-c"] + +RUN python -m pip install habana_media_loader=="${VERSION}"."${REVISION}" + + +FROM gaudi-base AS gaudi-pytorch + +ARG PT_VERSION +ARG VERSION +ARG REVISION +ARG ARTIFACTORY_URL +ENV BASE_NAME=rhel9.2 + +LABEL name="PyTorch Installer" +LABEL summary="Habanalabs PyTorch installer layer for RHEL9.2" +LABEL description="Image with pre installed Habanalabs packages for PyTorch" + +RUN echo "/usr/lib/habanalabs" > $(python -c "import sysconfig; print(sysconfig.get_path('platlib'))")/habanalabs-graph.pth + +USER root + +RUN echo "[CRB]" > /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "name=CentOS Linux 9 - CRB" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/CRB/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo + +RUN dnf install --allowerasing -y \ + curl \ + cairo-devel \ + numactl-devel \ + iproute \ + which \ + zlib-devel \ + lapack-devel \ + openblas-devel \ + numactl \ + gperftools-devel && \ + dnf clean all && rm -rf /var/cache/yum + +RUN dnf config-manager --add-repo https://yum.repos.intel.com/mkl/setup/intel-mkl.repo -y && \ + dnf install --allowerasing -y intel-mkl-64bit-2020.4-912 && \ + dnf clean all && rm -rf /var/cache/yum + +# Set LD_PRELOAD after all required installations to +# avoid warnings during docker creation +ENV LD_PRELOAD=/lib64/libtcmalloc.so.4 +ENV TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLD=7516192768 + +RUN rm -rf /tmp/* + +USER 1001 + +COPY --chown=1001:0 install_packages.sh . +RUN ./install_packages.sh && rm -f install_packages.sh + +USER root + +RUN /sbin/ldconfig && echo "source /etc/profile.d/habanalabs.sh" >> ~/.bashrc && \ + chown 1001:0 ~/.bashrc + +USER 1001 + +FROM gaudi-pytorch AS gaudi-notebooks + +WORKDIR ${APP_ROOT}/src + +COPY --chown=1001:0 requirements.txt requirements.txt +COPY --chown=1001:0 start-notebook.sh /opt/app-root/bin +COPY --chown=1001:0 builder /opt/app-root/builder +COPY --chown=1001:0 utils /opt/app-root/bin/utils + +USER 1001 + +RUN python -m pip install -r requirements.txt && \ + chmod -R g+w ${APP_ROOT}/lib/python3.10/site-packages && \ + fix-permissions ${APP_ROOT} -P && \ + chmod -R g+w /opt/app-root/src && \ + sed -i -e "s/Python.*/$(python --version | cut -d '.' -f-2)\",/" /opt/app-root/share/jupyter/kernels/python3/kernel.json && \ + jupyter labextension disable "@jupyterlab/apputils-extension:announcements" + +RUN cd ${APP_ROOT}/ && \ + git clone https://github.com/HabanaAI/vllm-fork.git && \ + cd vllm-fork && \ + VLLM_TARGET_DEVICE=hpu pip install -e . + +WORKDIR ${APP_ROOT}/src +ENV NOTEBOOK_SAMPLE_LINK="https://raw.githubusercontent.com/sharvil10/ai-containers/main/enterprise/redhat/openshift-ai/gaudi/demo/Getting-started.ipynb" + +ENTRYPOINT ["bash", "-c", "/opt/app-root/builder/run"] diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.4 b/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.4 new file mode 100644 index 00000000..18eeef28 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/Dockerfile.rhel9.4 @@ -0,0 +1,249 @@ +ARG BASE_IMAGE +ARG BASE_TAG +FROM ${BASE_IMAGE}:${BASE_TAG} AS gaudi-base +ARG ARTIFACTORY_URL +ARG VERSION +ARG REVISION + +LABEL vendor="Intel Corporation" +LABEL release="${VERSION}-${REVISION}" + +ENV HOME="/opt/app-root/src" +WORKDIR /opt/app-root/src + +RUN echo "[BaseOS]" > /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "name=CentOS Linux 9 - BaseOS" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/BaseOS/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-BaseOS.repo + +RUN echo "[centos9]" > /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "name=CentOS Linux 9 - AppStream" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-AppStream.repo + +RUN echo "[CRB]" > /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "name=CentOS Linux 9 - CRB" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/CRB/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo + +RUN dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \ + dnf clean all && rm -rf /var/cache/yum + +RUN dnf install -y \ + clang \ + cmake3 \ + cpp \ + gcc \ + gcc-c++ \ + glibc \ + glibc-headers \ + glibc-devel \ + jemalloc \ + libarchive \ + libksba \ + unzip \ + llvm \ + lsof \ + python3-devel \ + openssh-clients \ + openssl-1:3.0.7-27.el9 \ + openssl-devel-1:3.0.7-27.el9 \ + libjpeg-devel \ + openssh-server \ + lsb_release \ + wget \ + git \ + libffi-devel \ + bzip2-devel \ + zlib-devel \ + mesa-libGL \ + iproute \ + python3.11 \ + python3.11-pip \ + python3.11-devel \ + ffmpeg-free \ + perl-Net-SSLeay-1.92-2.el9 \ + python3-dnf-plugin-versionlock && \ + # update pkgs (except OS version) for resolving potentials CVEs + dnf versionlock add redhat-release* openssl* perl-Net-SSLeay && \ + dnf update -y && \ + dnf clean all && rm -rf /var/cache/yum + +RUN mkdir -p /licenses && \ + wget -O /licenses/LICENSE https://raw.githubusercontent.com/intel/ai-containers/main/LICENSE + +RUN alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 2 && \ + alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 && \ + alternatives --set python3 /usr/bin/python3.11 && \ + alternatives --install /usr/bin/pip3 pip3 /usr/bin/pip3.11 2 && \ + alternatives --install /usr/bin/pip3 pip3 /usr/bin/pip3.9 1 && \ + alternatives --set pip3 /usr/bin/pip3.11 + +COPY install_efa.sh . +RUN ./install_efa.sh && rm install_efa.sh && rm -rf /etc/ld.so.conf.d/efa.conf /etc/profile.d/efa.sh + +ENV LIBFABRIC_VERSION="1.20.0" +ENV LIBFABRIC_ROOT="/opt/habanalabs/libfabric-${LIBFABRIC_VERSION}" +ENV MPI_ROOT=/opt/amazon/openmpi +ENV LD_LIBRARY_PATH=$LIBFABRIC_ROOT/lib:${MPI_ROOT}/lib:/usr/lib/habanalabs:$LD_LIBRARY_PATH +ENV PATH=${LIBFABRIC_ROOT}/bin:${MPI_ROOT}/bin:$PATH +ENV OPAL_PREFIX=${MPI_ROOT} +ENV MPICC=${MPI_ROOT}/bin/mpicc +ENV RDMAV_FORK_SAFE=1 +ENV FI_EFA_USE_DEVICE_RDMA=1 + +RUN echo "[habanalabs]" > /etc/yum.repos.d/habanalabs.repo && \ + echo "name=Habana RH9 Linux repo" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "baseurl=https://${ARTIFACTORY_URL}/artifactory/rhel/9/9.4" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "gpgkey=https://${ARTIFACTORY_URL}/artifactory/rhel/9/9.4/repodata/repomd.xml.key" >> /etc/yum.repos.d/habanalabs.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/habanalabs.repo + +# for Habana GPG key with SHA-1 signature +RUN update-crypto-policies --set DEFAULT:SHA1 + +RUN dnf install -y habanalabs-rdma-core-"$VERSION"-"$REVISION".el9 \ + habanalabs-thunk-"$VERSION"-"$REVISION".el9 \ + habanalabs-firmware-tools-"$VERSION"-"$REVISION".el9 \ + habanalabs-graph-"$VERSION"-"$REVISION".el9 && \ + rm -f /etc/yum.repos.d/habanalabs.repo && rm -f /etc/yum.repos.d/habana.repo && rm -rf /tmp/* && \ + dnf clean all && rm -rf /var/cache/yum + +RUN rpm -V habanalabs-rdma-core && rpm -V habanalabs-thunk && rpm -V habanalabs-firmware-tools && rpm -V habanalabs-graph + +# There is no need to store pip installation files inside docker image +ENV PIP_NO_CACHE_DIR=on +ENV PIP_DISABLE_PIP_VERSION_CHECK=1 +ENV RDMA_CORE_ROOT=/opt/habanalabs/rdma-core/src +ENV RDMA_CORE_LIB=${RDMA_CORE_ROOT}/build/lib + +RUN wget -nv -O /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 https://github.com/ofiwg/libfabric/releases/download/v${LIBFABRIC_VERSION}/libfabric-${LIBFABRIC_VERSION}.tar.bz2 && \ + cd /tmp/ && tar xf /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 && \ + cd /tmp/libfabric-${LIBFABRIC_VERSION} && \ + ./configure --prefix=$LIBFABRIC_ROOT --enable-psm3-verbs --enable-verbs=yes --with-synapseai=/usr && \ + make && make install && cd / && rm -rf /tmp/libfabric-${LIBFABRIC_VERSION}.tar.bz2 /tmp/libfabric-${LIBFABRIC_VERSION} + +RUN wget -nv -O /tmp/main.zip https://github.com/HabanaAI/hccl_ofi_wrapper/archive/refs/heads/main.zip && \ + unzip /tmp/main.zip -d /tmp && \ + cd /tmp/hccl_ofi_wrapper-main && \ + make && cp -f libhccl_ofi_wrapper.so /usr/lib/habanalabs/libhccl_ofi_wrapper.so && \ + cd / && \ + rm -rf /tmp/main.zip /tmp/hccl_ofi_wrapper-main + +ENV APP_ROOT="/opt/app-root" + +RUN python3.11 -m pip install pip==23.3.1 setuptools==67.3.3 wheel==0.38.4 + +WORKDIR ${APP_ROOT} + +RUN python3.11 -m venv ${APP_ROOT} && \ + wget -O ${APP_ROOT}/bin/fix-permissions \ + https://raw.githubusercontent.com/sclorg/s2i-python-container/master/3.9-minimal/root/usr/bin/fix-permissions && \ + chown -R 1001:0 ${APP_ROOT} && \ + chmod +x ${APP_ROOT}/bin/fix-permissions && \ + ${APP_ROOT}/bin/fix-permissions ${APP_ROOT} -P && \ + echo "unset BASH_ENV PROMPT_COMMAND ENV" >> ${APP_ROOT}/bin/activate + +USER 1001 + +ENV BASH_ENV="${APP_ROOT}/bin/activate" +ENV ENV="${APP_ROOT}/bin/activate" +ENV PROMPT_COMMAND=". ${APP_ROOT}/bin/activate" + +SHELL ["/bin/bash", "-c"] + +RUN python -m pip install habana_media_loader=="${VERSION}"."${REVISION}" + +ENV GC_KERNEL_PATH=/usr/lib/habanalabs/libtpc_kernels.so +ENV HABANA_LOGS=/opt/app-root/log/habana_logs/ +ENV HABANA_SCAL_BIN_PATH=/opt/habanalabs/engines_fw +ENV HABANA_PLUGINS_LIB_PATH=/opt/habanalabs/habana_plugins + +FROM gaudi-base AS gaudi-pytorch + +ARG PT_VERSION +ARG VERSION +ARG REVISION +ARG ARTIFACTORY_URL +ENV BASE_NAME=rhel9.4 + +LABEL name="PyTorch Installer" +LABEL summary="Habanalabs PyTorch installer layer for RHEL9.2" +LABEL description="Image with pre installed Habanalabs packages for PyTorch" + +RUN echo "/usr/lib/habanalabs" > $(python -c "import sysconfig; print(sysconfig.get_path('platlib'))")/habanalabs-graph.pt + +USER root + +RUN echo "[CRB]" > /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "name=CentOS Linux 9 - CRB" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "baseurl=https://mirror.stream.centos.org/9-stream/CRB/x86_64/os" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgkey=https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official-SHA256" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo && \ + echo "gpgcheck=1" >> /etc/yum.repos.d/CentOS-Linux-CRB.repo + +RUN dnf install --allowerasing -y \ + curl \ + cairo-devel \ + numactl-devel \ + iproute \ + which \ + zlib-devel \ + lapack-devel \ + openblas-devel \ + numactl \ + gperftools-devel && \ + dnf clean all && rm -rf /var/cache/yum + +RUN dnf config-manager --add-repo https://yum.repos.intel.com/mkl/setup/intel-mkl.repo -y && \ + dnf install --allowerasing -y intel-mkl-64bit-2020.4-912 && \ + dnf clean all && rm -rf /var/cache/yum + +RUN rm -rf /tmp/* + +USER 1001 + +COPY --chown=1001:0 install_packages.sh . + +# Set LD_PRELOAD after all required installations to +# avoid warnings during docker creation +ENV LD_PRELOAD=/lib64/libtcmalloc.so.4 +ENV TCMALLOC_LARGE_ALLOC_REPORT_THRESHOLD=7516192768 + +RUN ./install_packages.sh && rm -f install_packages.sh + +USER root + +RUN /sbin/ldconfig && echo "source /etc/profile.d/habanalabs.sh" >> ~/.bashrc && \ + chown 1001:0 ~/.bashrc + +USER 1001 + +FROM gaudi-pytorch AS gaudi-notebooks + +WORKDIR ${APP_ROOT}/src + +COPY --chown=1001:0 requirements.txt requirements.txt +COPY --chown=1001:0 start-notebook.sh /opt/app-root/bin +COPY --chown=1001:0 builder /opt/app-root/builder +COPY --chown=1001:0 utils /opt/app-root/bin/utils + +USER 1001 + +RUN python -m pip install -r requirements.txt && \ + chmod -R g+w ${APP_ROOT}/lib/python3.11/site-packages && \ + fix-permissions ${APP_ROOT} -P && \ + chmod -R g+w /opt/app-root/src && \ + sed -i -e "s/Python.*/$(python --version | cut -d '.' -f-2)\",/" /opt/app-root/share/jupyter/kernels/python3/kernel.json && \ + jupyter labextension disable "@jupyterlab/apputils-extension:announcements" + +RUN cd ${APP_ROOT}/ && \ + git clone https://github.com/HabanaAI/vllm-fork.git && \ + cd vllm-fork && \ + VLLM_TARGET_DEVICE=hpu pip install -e . + +WORKDIR ${APP_ROOT}/src +ENV JUPYTER_PRELOAD_REPOS="https://github.com/IntelAI/oneAPI-samples" +ENV REPO_BRANCH="main" +ENTRYPOINT ["bash", "-c", "/opt/app-root/builder/run"] diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/builder/run b/enterprise/redhat/openshift-ai/gaudi/docker/builder/run new file mode 100755 index 00000000..f91d869e --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/builder/run @@ -0,0 +1,39 @@ +#!/bin/bash + +set -eo pipefail + +set -x + +APP_ROOT=${APP_ROOT:-/opt/app-root} + +# Pre-clone repositories defined in JUPYTER_PRELOAD_REPOS +if [ -n "${JUPYTER_PRELOAD_REPOS}" ]; then + for repo in $(echo "${JUPYTER_PRELOAD_REPOS}" | tr ',' ' '); do + # Check for the presence of "@branch" in the repo string + REPO_BRANCH=$(echo "${repo}" | cut -s -d'@' -f2) + if [[ -n ${REPO_BRANCH} ]]; then + # Remove the branch from the repo string and convert REPO_BRANCH to git clone arg + repo=$(echo "${repo}" | cut -d'@' -f1) + REPO_BRANCH="-b ${REPO_BRANCH}" + fi + echo "Checking if repository $repo exists locally" + REPO_DIR=$(basename "${repo}") + if [ -d "${REPO_DIR}" ]; then + pushd "${REPO_DIR}" + # Do nothing if the repo already exists + echo "The ${repo} has already been cloned" + : + popd + else + GIT_SSL_NO_VERIFY=true git clone "${repo}" "${REPO_DIR}" "${REPO_BRANCH}" + fi + done +fi + +if [ -n "${NOTEBOOK_SAMPLES_LINK}" ]; then + for link in $(echo "${NOTEBOOK_SAMPLES_LINK}" | tr ',' ' '); do + wget "${link}" + done +fi + +"${APP_ROOT}"/bin/start-notebook.sh "$@" diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/docker-compose.yaml b/enterprise/redhat/openshift-ai/gaudi/docker/docker-compose.yaml new file mode 100644 index 00000000..d2901e32 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/docker-compose.yaml @@ -0,0 +1,64 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +services: + gaudi-base: + build: + args: + BASE_IMAGE: ${BASE_IMAGE:-registry.access.redhat.com/ubi9/ubi} + BASE_TAG: ${RHEL_OS:-9.2} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: "" + ARTIFACTORY_URL: ${ARTIFACTORY_URL:-vault.habana.ai} + VERSION: ${VERSION:-1.17.0} + REVISION: ${REVISION:-495} + context: . + target: gaudi-base + dockerfile: Dockerfile.rhel${RHEL_OS:-9.2} + image: gaudi-base:${RHEL_OS:-9.2}-${VERSION:-1.17.0}-${REVISION:-495} + gaudi-pytorch: + build: + args: + BASE_IMAGE: ${BASE_IMAGE:-registry.access.redhat.com/ubi9/ubi} + BASE_TAG: ${RHEL_OS:-9.2} + BASE_NAME: rhel${RHEL_OS:-rhel9.2} + PT_VERSION: ${PT_VERSION:-2.3.1} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: "" + ARTIFACTORY_URL: ${ARTIFACTORY_URL:-vault.habana.ai} + VERSION: ${VERSION:-1.17.0} + REVISION: ${REVISION:-495} + context: . + target: gaudi-pytorch + dockerfile: Dockerfile.rhel${RHEL_OS:-9.2} + image: gaudi-pytorch:${RHEL_OS:-9.2}-${VERSION:-1.17.0}-${REVISION:-495} + gaudi-notebooks: + build: + args: + BASE_IMAGE: ${BASE_IMAGE:-registry.access.redhat.com/ubi9/ubi} + BASE_TAG: ${RHEL_OS:-9.2} + BASE_NAME: ${BASE_NAME:-rhel9.2} + PT_VERSION: ${PT_VERSION:-2.3.1} + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: "" + ARTIFACTORY_URL: ${ARTIFACTORY_URL:-vault.habana.ai} + VERSION: ${VERSION:-1.17.0} + REVISION: ${REVISION:-495} + context: . + target: gaudi-notebooks + dockerfile: Dockerfile.rhel${RHEL_OS:-9.2} + image: gaudi-notebooks:${RHEL_OS:-9.2}-${VERSION:-1.17.0}-${REVISION:-495} diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/install-python310.sh b/enterprise/redhat/openshift-ai/gaudi/docker/install-python310.sh new file mode 100755 index 00000000..a9d25005 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/install-python310.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +_BASE_NAME=${1:-"ubuntu22.04"} +_SSL_LIB="" + +# preinstall dependencies and define variables +case "${_BASE_NAME}" in +*ubuntu22.04*) + echo "Skip install Python3.10 from source on Ubuntu22.04" + exit 0 + ;; +*debian* | *ubuntu*) + apt update + apt install -y libsqlite3-dev libreadline-dev + ;; +*rhel*) + yum install -y sqlite-devel readline-devel xz-devel + ;; +*tencentos3.1*) + dnf install -y sqlite-devel readline-devel zlib-devel xz-devel bzip2-devel libffi-devel + wget -nv -O /opt/openssl-1.1.1w.tar.gz https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz && + cd /opt/ && + tar xzf openssl-1.1.1w.tar.gz && + rm -rf openssl-1.1.1w.tar.gz && + cd openssl-1.1.1w && + ./config --prefix=/usr/local/openssl-1.1.1w shared zlib && + make && make install + ln -s /etc/pki/tls/cert.pem /usr/local/openssl-1.1.1w/ssl/cert.pem + + PATH=$PATH:/usr/local/protoc/bin:/usr/local/openssl-1.1.1w/bin + LD_LIBRARY_PATH=/usr/local/openssl-1.1.1w/lib:$LD_LIBRARY_PATH + _SSL_LIB="--with-openssl=/usr/local/openssl-1.1.1w" + ;; +*amzn2*) + yum install -y sqlite-devel readline-devel + wget -nv -O /opt/openssl-1.1.1w.tar.gz https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz && + cd /opt/ && + tar xzf openssl-1.1.1w.tar.gz && + rm -rf openssl-1.1.1w.tar.gz && + cd openssl-1.1.1w && + ./config --prefix=/usr/local/openssl-1.1.1w shared zlib && + make && make install + ln -s /etc/pki/tls/cert.pem /usr/local/openssl-1.1.1w/ssl/cert.pem + + PATH=$PATH:/usr/local/protoc/bin:/usr/local/openssl-1.1.1w/bin + LD_LIBRARY_PATH=/usr/local/openssl-1.1.1w/lib:$LD_LIBRARY_PATH + _SSL_LIB="--with-openssl=/usr/local/openssl-1.1.1w" + ;; +esac + +# install Python +wget -nv -O /opt/Python-3.10.14.tgz https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz +cd /opt/ +tar xzf Python-3.10.14.tgz +rm -f Python-3.10.14.tgz +cd Python-3.10.14 +./configure --enable-optimizations --enable-loadable-sqlite-extensions --enable-shared $_SSL_LIB +make -j && make altinstall + +# post install +case "${_BASE_NAME}" in +*rhel9*) + alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.10 2 && + alternatives --install /usr/bin/python3 python3 /usr/bin/python3.9 1 && + alternatives --set python3 /usr/local/bin/python3.10 + export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + ;; +*tencentos3.1*) + alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.10 4 && + alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 3 && + alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 2 && + alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1 && + alternatives --set python3 /usr/local/bin/python3.10 + export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + ;; +*amzn2*) + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.10 3 && + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 2 && + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 + ;; +*debian*) + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.10 3 + update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.8 2 + update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1 + ;; +esac + +python3 -m pip install --upgrade pip setuptools diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/install_efa.sh b/enterprise/redhat/openshift-ai/gaudi/docker/install_efa.sh new file mode 100755 index 00000000..4175e8f8 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/install_efa.sh @@ -0,0 +1,40 @@ +#!/bin/bash -ex + +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DEFAULT_EFA_INSTALLER_VER=1.29.0 +efa_installer_version=${1:-$DEFAULT_EFA_INSTALLER_VER} + +tmp_dir=$(mktemp -d) +wget -nv https://efa-installer.amazonaws.com/aws-efa-installer-"$efa_installer_version".tar.gz -P "$tmp_dir" +tar -xf "$tmp_dir"/aws-efa-installer-"$efa_installer_version".tar.gz -C "$tmp_dir" +pushd "$tmp_dir"/aws-efa-installer +# shellcheck disable=SC1091 +case $( + . /etc/os-release + echo -n "$ID" +) in +rhel) + # we cannot install dkms packages on RHEL images due to OCP rules + rm -f RPMS/RHEL8/x86_64/dkms*.rpm + ;; +tencentos) + dnf install -y RPMS/ROCKYLINUX8/x86_64/rdma-core/libibverbs-46.0-1.el8.x86_64.rpm RPMS/ROCKYLINUX8/x86_64/rdma-core/libibverbs-utils-46.0-1.el8.x86_64.rpm + patch -f -p1 -i /tmp/tencentos_efa_patch.txt --reject-file=tencentos_efa_patch.rej --no-backup-if-mismatch + ;; +esac +./efa_installer.sh -y --skip-kmod --skip-limit-conf --no-verify +popd +rm -rf "$tmp_dir" diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/install_packages.sh b/enterprise/redhat/openshift-ai/gaudi/docker/install_packages.sh new file mode 100755 index 00000000..d67bb4f3 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/install_packages.sh @@ -0,0 +1,46 @@ +#!/bin/bash +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -ex + +pt_package_name="pytorch_modules-v${PT_VERSION}_${VERSION}_${REVISION}.tgz" +os_string="ubuntu${OS_NUMBER}" +case "${BASE_NAME}" in +*rhel9.2*) + os_string="rhel92" + ;; +*rhel9.4*) + os_string="rhel94" + ;; +*rhel8*) + os_string="rhel86" + ;; +*amzn2*) + os_string="amzn2" + ;; +*tencentos*) + os_string="tencentos31" + ;; +esac +pt_artifact_path="https://${ARTIFACTORY_URL}/artifactory/gaudi-pt-modules/${VERSION}/${REVISION}/pytorch/${os_string}" + +tmp_path=$(mktemp --directory) +wget --no-verbose "${pt_artifact_path}/${pt_package_name}" +tar -xf "${pt_package_name}" -C "${tmp_path}"/. +pushd "${tmp_path}" +./install.sh "$VERSION" "$REVISION" +popd +# cleanup +rm -rf "${tmp_path}" "${pt_package_name}" diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt b/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt new file mode 100644 index 00000000..9d3a3ca7 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt @@ -0,0 +1,43 @@ +# LLM Packages +deepspeed @ git+https://github.com/HabanaAI/DeepSpeed.git@1.17.0 + +# Datascience and useful extensions +kafka-python~=2.0.2 +matplotlib~=3.8.3 +pandas~=2.2.0 +plotly~=5.20.0 +scikit-learn +scipy~=1.12.0 +skl2onnx~=1.16.0 +codeflare-sdk~=0.18.0 + +# DB connectors +pymongo~=4.6.2 +psycopg~=3.1.18 +pyodbc~=5.1.0 +mysql-connector-python~=8.3.0 + +# JupyterLab packages +odh-elyra~=3.16.7 +jupyterlab~=3.6.7 # Wait on upgrade till plugins are ready +jupyter-bokeh~=3.0.7 # Upgrade would bring in jupyterlab 4 +jupyter-server~=2.14.1 +jupyter-server-proxy~=4.2.0 # Upgrade would bring in jupyterlab 4 +jupyter-server-terminals~=0.5.3 +jupyterlab-git~=0.44.0 +jupyterlab-lsp~=4.2.0 +jupyterlab-widgets~=3.0.10 +jupyter-resource-usage~=0.7.2 +nbdime~=3.2.1 +nbgitpuller~=1.2.0 + +# pycodestyle is dependency of below packages +# and to achieve compatible of pycodestyle with python-lsp-server[all] +# pinned the below packages +autopep8~=2.0.4 +flake8~=7.0.0 +# Base packages +wheel~=0.43.0 +setuptools>=70.0.0 +pip>=23.3 +aiohttp==3.10.2 diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/start-notebook.sh b/enterprise/redhat/openshift-ai/gaudi/docker/start-notebook.sh new file mode 100755 index 00000000..f13aa7d8 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/start-notebook.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Load bash libraries +SCRIPT_DIR=${APP_ROOT}/bin +# shellcheck disable=SC1091 +source "${SCRIPT_DIR}"/utils/process.sh + +if [ -f "${SCRIPT_DIR}/utils/setup-elyra.sh" ]; then + # shellcheck disable=SC1091 + source "${SCRIPT_DIR}"/utils/setup-elyra.sh +fi + +# Initialize notebooks arguments variable +NOTEBOOK_PROGRAM_ARGS="" + +# Set default ServerApp.port value if NOTEBOOK_PORT variable is defined +if [ -n "${NOTEBOOK_PORT}" ]; then + NOTEBOOK_PROGRAM_ARGS+="--ServerApp.port=${NOTEBOOK_PORT} " +fi + +# Set default ServerApp.base_url value if NOTEBOOK_BASE_URL variable is defined +if [ -n "${NOTEBOOK_BASE_URL}" ]; then + NOTEBOOK_PROGRAM_ARGS+="--ServerApp.base_url=${NOTEBOOK_BASE_URL} " +fi + +# Set default ServerApp.root_dir value if NOTEBOOK_ROOT_DIR variable is defined +if [ -n "${NOTEBOOK_ROOT_DIR}" ]; then + NOTEBOOK_PROGRAM_ARGS+="--ServerApp.root_dir=${NOTEBOOK_ROOT_DIR} " +else + NOTEBOOK_PROGRAM_ARGS+="--ServerApp.root_dir=${HOME} " +fi + +# Add additional arguments if NOTEBOOK_ARGS variable is defined +if [ -n "${NOTEBOOK_ARGS}" ]; then + NOTEBOOK_PROGRAM_ARGS+=${NOTEBOOK_ARGS} +fi + +echo "${NOTEBOOK_PROGRAM_ARGS}" + +# Start the JupyterLab notebook +# shellcheck disable=SC2086 +start_process jupyter lab ${NOTEBOOK_PROGRAM_ARGS} \ + --ServerApp.ip=0.0.0.0 \ + --ServerApp.allow_origin="*" \ + --ServerApp.open_browser=False diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/utils/process.sh b/enterprise/redhat/openshift-ai/gaudi/docker/utils/process.sh new file mode 100755 index 00000000..95028188 --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/docker/utils/process.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function start_process() { + trap stop_process TERM INT + + echo "Running command:" "$@" + echo -e "$@" + "$@" & + + PID=$! + wait $PID + trap - TERM INT + wait $PID + STATUS=$? + exit $STATUS +} + +function stop_process() { + kill -TERM "$PID" +} diff --git a/enterprise/redhat/openshift-ai/README.md b/enterprise/redhat/openshift-ai/oneapi/README.md similarity index 100% rename from enterprise/redhat/openshift-ai/README.md rename to enterprise/redhat/openshift-ai/oneapi/README.md diff --git a/enterprise/redhat/openshift-ai/assets/step-1.png b/enterprise/redhat/openshift-ai/oneapi/assets/step-1.png similarity index 100% rename from enterprise/redhat/openshift-ai/assets/step-1.png rename to enterprise/redhat/openshift-ai/oneapi/assets/step-1.png diff --git a/enterprise/redhat/openshift-ai/assets/step-2.png b/enterprise/redhat/openshift-ai/oneapi/assets/step-2.png similarity index 100% rename from enterprise/redhat/openshift-ai/assets/step-2.png rename to enterprise/redhat/openshift-ai/oneapi/assets/step-2.png diff --git a/enterprise/redhat/openshift-ai/assets/step-3.png b/enterprise/redhat/openshift-ai/oneapi/assets/step-3.png similarity index 100% rename from enterprise/redhat/openshift-ai/assets/step-3.png rename to enterprise/redhat/openshift-ai/oneapi/assets/step-3.png diff --git a/enterprise/redhat/openshift-ai/assets/step-4.png b/enterprise/redhat/openshift-ai/oneapi/assets/step-4.png similarity index 100% rename from enterprise/redhat/openshift-ai/assets/step-4.png rename to enterprise/redhat/openshift-ai/oneapi/assets/step-4.png diff --git a/enterprise/redhat/openshift-ai/manifests/intel-optimized-ml.yaml b/enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-ml.yaml similarity index 100% rename from enterprise/redhat/openshift-ai/manifests/intel-optimized-ml.yaml rename to enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-ml.yaml diff --git a/enterprise/redhat/openshift-ai/manifests/intel-optimized-pytorch.yaml b/enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-pytorch.yaml similarity index 100% rename from enterprise/redhat/openshift-ai/manifests/intel-optimized-pytorch.yaml rename to enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-pytorch.yaml diff --git a/enterprise/redhat/openshift-ai/manifests/intel-optimized-tensorflow.yaml b/enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-tensorflow.yaml similarity index 100% rename from enterprise/redhat/openshift-ai/manifests/intel-optimized-tensorflow.yaml rename to enterprise/redhat/openshift-ai/oneapi/manifests/intel-optimized-tensorflow.yaml From e4a00731ccb3a3b68df2a46150385e4f167f5e39 Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Wed, 11 Sep 2024 14:42:08 -0700 Subject: [PATCH 41/56] IPEX XPU 2.3.110 support (#378) Signed-off-by: Srikanth Ramakrishna Signed-off-by: Srikanth Ramakrishna Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tyler Titsworth --- python/docker-compose.yaml | 1 + pytorch/Dockerfile | 27 ++++------ pytorch/README.md | 20 +++++-- pytorch/docker-compose.yaml | 53 ++++++++----------- pytorch/notebooks/ipex-xpu.ipynb | 8 +-- pytorch/serving/README.md | 4 +- .../serving/torchserve-xpu-requirements.txt | 9 ++-- pytorch/serving/wf-store/rest-test.sh | 4 +- pytorch/tests/tests.yaml | 8 +-- pytorch/xpu-requirements.txt | 11 ++-- 10 files changed, 71 insertions(+), 74 deletions(-) diff --git a/python/docker-compose.yaml b/python/docker-compose.yaml index a8039de4..2d674ae3 100644 --- a/python/docker-compose.yaml +++ b/python/docker-compose.yaml @@ -17,6 +17,7 @@ services: build: args: MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} + no_proxy: "" context: . labels: dependency.apt.wget: true diff --git a/pytorch/Dockerfile b/pytorch/Dockerfile index 2f7903d1..4015c2a5 100644 --- a/pytorch/Dockerfile +++ b/pytorch/Dockerfile @@ -167,34 +167,27 @@ RUN apt-get update && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* -RUN no_proxy=$no_proxy wget -q -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \ - | gpg --dearmor | tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null && \ - echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" \ - | tee /etc/apt/sources.list.d/oneAPI.list +RUN rm -rf /etc/apt/sources.list.d/intel-gpu-jammy.list -ARG DPCPP_VER -ARG MKL_VER -ARG CCL_VER +ENV OCL_ICD_VENDORS=/etc/OpenCL/vendors -RUN apt-get update && \ - apt-get install -y --no-install-recommends --fix-missing \ - intel-oneapi-runtime-dpcpp-cpp=${DPCPP_VER} \ - intel-oneapi-runtime-mkl=${MKL_VER} \ - intel-oneapi-runtime-ccl=${CCL_VER}; +FROM ipex-xpu-base AS ipex-xpu-base-wheels-pip -RUN rm -rf /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list +WORKDIR / +COPY xpu-requirements.txt . -ENV LD_LIBRARY_PATH=/opt/intel/oneapi/redist/lib:$LD_LIBRARY_PATH +RUN python -m pip install --no-cache-dir -r xpu-requirements.txt && \ + rm -rf xpu-requirements.txt -FROM ipex-xpu-base AS ipex-xpu-base-wheels +FROM ipex-xpu-base AS ipex-xpu-base-wheels-idp WORKDIR / COPY xpu-requirements.txt . -RUN python -m pip install --no-cache-dir -r xpu-requirements.txt && \ +RUN conda run -n idp python -m pip install --no-cache-dir -r xpu-requirements.txt && \ rm -rf xpu-requirements.txt -FROM ipex-xpu-base AS ipex-xpu-jupyter +FROM ipex-xpu-base-wheels-${PACKAGE_OPTION} AS ipex-xpu-jupyter WORKDIR /jupyter COPY jupyter-requirements.txt . diff --git a/pytorch/README.md b/pytorch/README.md index f3fcda4e..a96170fb 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -24,7 +24,8 @@ The images below include support for both CPU and GPU optimizations: | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------------- | -------- | -------------- | ------ | --------------- | -| `2.1.40-xpu-pip-base`,`2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | +| `2.3.110-xpu-pip-base`,`2.3.110-xpu` | [torch-2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0] | +| `2.1.40-xpu-pip-base`,`2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.20-xpu` | [v2.1.0] | [v2.1.20+xpu] | [803] | [v0.3.4] | | `2.1.10-xpu` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | @@ -37,7 +38,7 @@ docker run -it --rm \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ --ipc=host \ - intel/intel-extension-for-pytorch:2.1.40-xpu + intel/intel-extension-for-pytorch:2.3.110-xpu ``` --- @@ -46,6 +47,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | +| `2.3.110-xpu-pip-jupyter` | [torch-2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | | `2.1.40-xpu-pip-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | | `2.1.20-xpu-pip-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | | `2.1.10-xpu-pip-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | @@ -57,7 +59,7 @@ docker run -it --rm \ -p 8888:8888 \ --device /dev/dri \ -v /dev/dri/by-path:/dev/dri/by-path \ - intel/intel-extension-for-pytorch:2.1.40-xpu-pip-jupyter + intel/intel-extension-for-pytorch:2.3.110-xpu-pip-jupyter ``` After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. @@ -270,6 +272,12 @@ The images below are [TorchServe*] with CPU Optimizations: For more details, follow the procedure in the [TorchServe](https://github.com/pytorch/serve/blob/master/examples/intel_extension_for_pytorch/README.md) instructions. +The images below are [TorchServe*] with XPU Optimizations: + +| Tag(s) | Pytorch | IPEX | Dockerfile | +| ------------------- | -------- | ------------ | --------------- | +| `2.3.110-serving-xpu` | [torch-2.3.1] | [v2.3.110+xpu] | [v0.4.0-Beta] | + ## CPU only images with Intel® Distribution for Python* The images below are built only with CPU optimizations (GPU acceleration support was deliberately excluded) and include [Intel® Distribution for Python*]: @@ -308,6 +316,7 @@ The images below are built only with CPU and GPU optimizations and include [Inte | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------- | -------- | ------------ | -------- | ------ | +| `2.3.110-xpu-idp-base` | [torch-v2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0-Beta] | | `2.1.40-xpu-idp-base` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu-idp-base` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.10-xpu-idp-base` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | @@ -316,6 +325,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | +| `2.3.110-xpu-idp-jupyter` | [torch-v2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | | `2.1.40-xpu-idp-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | | `2.1.20-xpu-idp-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | | `2.1.10-xpu-idp-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | @@ -384,6 +394,7 @@ It is the image user's responsibility to ensure that any use of The images below [v0.2.3]: https://github.com/intel/ai-containers/blob/v0.2.3/pytorch/Dockerfile [v0.1.0]: https://github.com/intel/ai-containers/blob/v0.1.0/pytorch/Dockerfile +[v2.3.110+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.3.110%2Bxpu [v2.1.40+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.40%2Bxpu [v2.1.30+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.30%2Bxpu [v2.1.20+xpu]: https://github.com/intel/intel-extension-for-pytorch/releases/tag/v2.1.20%2Bxpu @@ -399,6 +410,8 @@ It is the image user's responsibility to ensure that any use of The images below [v2.0.1]: https://github.com/pytorch/pytorch/releases/tag/v2.0.1 [v2.0.0]: https://github.com/pytorch/pytorch/releases/tag/v2.0.0 +[torch-v2.3.1]: https://github.com/pytorch/pytorch/tree/v2.3.1 + [v3.0]: https://github.com/intel/neural-compressor/releases/tag/v3.0 [v2.6]: https://github.com/intel/neural-compressor/releases/tag/v2.6 [v2.4.1]: https://github.com/intel/neural-compressor/releases/tag/v2.4.1 @@ -422,6 +435,7 @@ It is the image user's responsibility to ensure that any use of The images below [v4.44.0]: https://github.com/huggingface/transformers/releases/tag/v4.44.0 +[950]: https://dgpu-docs.intel.com/releases/stable_950_13_20240814.html [914]: https://dgpu-docs.intel.com/releases/stable_914_33_20240730.html [803]: https://dgpu-docs.intel.com/releases/LTS_803.29_20240131.html [736]: https://dgpu-docs.intel.com/releases/stable_736_25_20231031.html diff --git a/pytorch/docker-compose.yaml b/pytorch/docker-compose.yaml index 6aeeefc9..efacdea1 100644 --- a/pytorch/docker-compose.yaml +++ b/pytorch/docker-compose.yaml @@ -94,13 +94,10 @@ services: xpu: build: args: - CCL_VER: ${CCL_VER:-2021.13.1-31} - DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} - ICD_VER: ${ICD_VER:-24.22.29735.27-914~22.04} - LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} - LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} - LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} - MKL_VER: ${MKL_VER:-2024.2.1-103} + ICD_VER: ${ICD_VER:-24.26.30049.10-950~22.04} + LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-950~22.04} + LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.30049.10-950~22.04} + LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-950~22.04} NO_PROXY: '' PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} labels: @@ -109,51 +106,45 @@ services: dependency.apt.git: true dependency.apt.gnupg2: true dependency.apt.gpg-agent: true - dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} - dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.13.1-31} - dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.2.1-1079} - dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.2.1-103} - dependency.apt.intel-opencl-icd: ${ICD_VER:-23.43.27642.40-803~22.04} - dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} - dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} + dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.30049.10-950~22.04} + dependency.apt.intel-opencl-icd: ${ICD_VER:-24.26.30049.10-950~22.04} + dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.17.6-950~22.04} + dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.17.6-950~22.04} dependency.apt.rsync: true dependency.apt.unzip: true dependency.idp.pip: false org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Base Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-base - target: ipex-xpu-base-wheels + org.opencontainers.image.version: ${IPEX_VERSION:-2.3.110}-xpu-${PACKAGE_OPTION:-pip}-base + target: ipex-xpu-base-wheels-${PACKAGE_OPTION:-pip} command: > python -c "import torch;print(torch.device('xpu'));import intel_extension_for_pytorch as - ipex;print(ipex.xpu.is_available());print(torch.__version__); + ipex;print(torch.xpu.has_xpu());print(torch.__version__); print(ipex.__version__); [print(f'[{i}]: - {ipex.xpu.get_device_properties(i)}') for i in - range(ipex.xpu.device_count())];" + {torch.xpu.get_device_properties(i)}') for i in + range(torch.xpu.device_count())];" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-base xpu-jupyter: build: args: - CCL_VER: ${CCL_VER:-2021.13.1-31} - DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} - ICD_VER: ${ICD_VER:-24.22.29735.27-914~22.04} - LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} - LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} - LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} - MKL_VER: ${MKL_VER:-2024.2.1-103} + ICD_VER: ${ICD_VER:-24.26.30049.10-950~22.04} + LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-950~22.04} + LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.30049.10-950~22.04} + LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-950~22.04} NO_PROXY: '' PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} labels: dependency.python.pip: jupyter-requirements.txt - org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-base" + org.opencontainers.base.name: "intel/intel-optimized-pytorch:${IPEX_VERSION:-2.3.110}-xpu-${PACKAGE_OPTION:-pip}-base" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Jupyter Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-xpu-${PACKAGE_OPTION:-pip}-jupyter + org.opencontainers.image.version: ${IPEX_VERSION:-2.3.110}-xpu-${PACKAGE_OPTION:-pip}-jupyter target: ipex-xpu-jupyter command: > bash -c "python -m jupyter --version" extends: ipex-base - image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-jupyter ports: - 8888:8888 torchserve-cpu: @@ -196,7 +187,7 @@ services: docs: serving org.opencontainers.base.name: "intel/python:3.10-core" org.opencontainers.image.title: "Intel® Extension for PyTorch XPU Serving Image" - org.opencontainers.image.version: ${IPEX_VERSION:-2.1.40}-serving-xpu + org.opencontainers.image.version: ${IPEX_VERSION:-2.3.110}-serving-xpu target: torchserve-xpu command: torchserve --version entrypoint: "" diff --git a/pytorch/notebooks/ipex-xpu.ipynb b/pytorch/notebooks/ipex-xpu.ipynb index 662dd634..45df4c35 100644 --- a/pytorch/notebooks/ipex-xpu.ipynb +++ b/pytorch/notebooks/ipex-xpu.ipynb @@ -25,13 +25,13 @@ "outputs": [], "source": [ "import intel_extension_for_pytorch as ipex\n", - "print(ipex.xpu.is_available())\n", - "if (not ipex.xpu.is_available()):\n", + "print(torch.xpu.has_xpu())\n", + "if (not torch.xpu.is_available()):\n", " print('Intel GPU not detected. Please install GPU with compatible drivers')\n", " sys.exit(1)\n", - "print(ipex.xpu.has_onemkl())\n", + "print(torch.xpu.has_onemkl())\n", "print(torch.__version__); print(ipex.__version__)\n", - "[print(f'[{i}]: {ipex.xpu.get_device_properties(i)}') for i in range(ipex.xpu.device_count())]\n" + "[print(f'[{i}]: {torch.xpu.get_device_properties(i)}') for i in range(torch.xpu.device_count())]\n" ] } ], diff --git a/pytorch/serving/README.md b/pytorch/serving/README.md index 08114bba..c0a5413c 100644 --- a/pytorch/serving/README.md +++ b/pytorch/serving/README.md @@ -39,7 +39,7 @@ docker run --rm -it \ -u root \ -v $PWD:/home/model-server \ --device /dev/dri \ - intel/intel-optimized-pytorch:2.1.40-serving-xpu \ + intel/intel-optimized-pytorch:2.3.110-serving-xpu \ sh -c 'python model-archive/ipex_squeezenet.py && \ torch-model-archiver --model-name squeezenet1_1 \ --version 1.1 \ @@ -73,7 +73,7 @@ docker run -d --rm --name server \ -v $PWD/config-xpu.properties:/home/model-server/config.properties \ --net=host \ --device /dev/dri \ - intel/intel-optimized-pytorch:2.1.40-serving-xpu + intel/intel-optimized-pytorch:2.3.110-serving-xpu ``` After lauching the container, follow the steps below: diff --git a/pytorch/serving/torchserve-xpu-requirements.txt b/pytorch/serving/torchserve-xpu-requirements.txt index 534f6514..693402fe 100644 --- a/pytorch/serving/torchserve-xpu-requirements.txt +++ b/pytorch/serving/torchserve-xpu-requirements.txt @@ -1,9 +1,8 @@ -torch==2.1.0.post3+cxx11.abi -torchvision==0.16.0.post3+cxx11.abi -torchaudio==2.1.0.post3+cxx11.abi -intel_extension_for_pytorch==2.1.40+xpu +torch==2.3.1+cxx11.abi +torchvision==0.18.1+cxx11.abi +torchaudio==2.3.1+cxx11.abi +intel_extension_for_pytorch==2.3.110+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us -setuptools==69.5.1 numpy==1.26.4 captum>=0.7.0 cython>=3.0.10 diff --git a/pytorch/serving/wf-store/rest-test.sh b/pytorch/serving/wf-store/rest-test.sh index 2e5850aa..2c37871f 100644 --- a/pytorch/serving/wf-store/rest-test.sh +++ b/pytorch/serving/wf-store/rest-test.sh @@ -33,13 +33,13 @@ apt-get -y install curl curl --fail -X GET http://localhost:8080/ping -cd ../model-store || exit +cd /home/model-server/model-store || exit curl --fail -O https://torchserve.pytorch.org/mar_files/cat_dog_classification.mar curl --fail -O https://torchserve.pytorch.org/mar_files/dog_breed_classification.mar curl --fail -X POST "http://127.0.0.1:8081/models?url=cat_dog_classification.mar" curl --fail -X POST "http://127.0.0.1:8081/models?url=dog_breed_classification.mar" -cd ../wf-store || exit +cd /home/model-server/wf-store || exit curl --fail -X POST "http://127.0.0.1:8081/workflows?url=dog_breed_wf.war" curl --fail -O https://raw.githubusercontent.com/pytorch/serve/master/examples/Workflows/dog_breed_classification/model_input/Cat.jpg diff --git a/pytorch/tests/tests.yaml b/pytorch/tests/tests.yaml index 21aeeadc..ceb3df74 100644 --- a/pytorch/tests/tests.yaml +++ b/pytorch/tests/tests.yaml @@ -16,14 +16,14 @@ import-ipex-cpu-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-base cmd: python -c "import torch;import intel_extension_for_pytorch as ipex;print(f'torch {torch.__version__} ipex {ipex.__version__}')" import-ipex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-base cmd: python -c "import torch; import intel_extension_for_pytorch as ipex;[print(f'[{i}] {torch.xpu.get_device_properties(i)}') for i in range(torch.xpu.device_count())];" device: ["/dev/dri"] import-cpu-jupyter-${PACKAGE_OPTION:-pip}: img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.4.0}-jupyter cmd: python -m jupyter --version import-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-jupyter cmd: python -m jupyter --version device: ["/dev/dri"] import-cpu-oneccl-${PACKAGE_OPTION:-pip}: @@ -46,14 +46,14 @@ ipex-cpu-${PACKAGE_OPTION:-pip}: - dst: /tests src: $PWD/pytorch/tests ipex-xpu-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-base + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-base cmd: python /tests/ipex-resnet50.py --ipex --device xpu device: ["/dev/dri"] volumes: - dst: /tests src: $PWD/pytorch/tests ipex-xpu-jupyter-${PACKAGE_OPTION:-pip}: - img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.1.40xpu}-xpu-jupyter + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-ipex-${IPEX_VERSION:-2.3.110xpu}-xpu-jupyter cmd: papermill --log-output /jupyter/xpu.ipynb -k python3 device: ["/dev/dri"] notebook: True diff --git a/pytorch/xpu-requirements.txt b/pytorch/xpu-requirements.txt index e7771aa0..217ecdf7 100644 --- a/pytorch/xpu-requirements.txt +++ b/pytorch/xpu-requirements.txt @@ -1,10 +1,9 @@ -torch==2.1.0.post3+cxx11.abi -torchvision==0.16.0.post3+cxx11.abi -torchaudio==2.1.0.post3+cxx11.abi -intel_extension_for_pytorch==2.1.40+xpu -oneccl_bind_pt==2.1.400+xpu +torch==2.3.1+cxx11.abi +torchvision==0.18.1+cxx11.abi +torchaudio==2.3.1+cxx11.abi +intel_extension_for_pytorch==2.3.110+xpu +oneccl_bind_pt==2.3.100+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us -setuptools==69.5.1 numpy>=1.26.4 idna>=3.7 requests>=2.32.0 From 5ca74f7e12029ed622964d16f2d3cd5b658ce90f Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Thu, 12 Sep 2024 11:39:59 -0700 Subject: [PATCH 42/56] Update Dependabot Patterns (#377) Signed-off-by: Tyler Titsworth --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4289ee44..79f0dfb8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -83,7 +83,7 @@ updates: groups: preset: patterns: - - "*requirements.txt" + - "*" package-ecosystem: pip schedule: interval: weekly @@ -99,7 +99,7 @@ updates: groups: gaudi-openshift: patterns: - - "requirements.txt" + - "*" package-ecosystem: pip schedule: interval: weekly From fd50d3f79c0b17c5c7edca8b8e84592f7bd4b646 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:03:53 -0700 Subject: [PATCH 43/56] Bump step-security/harden-runner from 2.9.1 to 2.10.1 (#381) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/chart-ci.yaml | 2 +- .github/workflows/container-ci.yaml | 10 +++++----- .github/workflows/dependency-review.yaml | 2 +- .github/workflows/dockerhub-description.yml | 4 ++-- .github/workflows/docs.yaml | 2 +- .github/workflows/integration-test.yaml | 4 ++-- .github/workflows/lint.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- .github/workflows/security-report.yaml | 2 +- .github/workflows/test-runner-ci.yaml | 6 +++--- .github/workflows/weekly-test.yaml | 6 +++--- 11 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/chart-ci.yaml b/.github/workflows/chart-ci.yaml index 916423b2..6f698aa6 100644 --- a/.github/workflows/chart-ci.yaml +++ b/.github/workflows/chart-ci.yaml @@ -26,7 +26,7 @@ jobs: runs-on: kubectl steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index a64e2307..6d8a22f7 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -66,7 +66,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -117,7 +117,7 @@ jobs: matrix: ${{ steps.scan-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 @@ -136,7 +136,7 @@ jobs: fail-fast: false steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -170,7 +170,7 @@ jobs: matrix: ${{ steps.test-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -187,7 +187,7 @@ jobs: experimental: [true] fail-fast: false steps: - - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml index cce8357f..635a8176 100644 --- a/.github/workflows/dependency-review.yaml +++ b/.github/workflows/dependency-review.yaml @@ -34,7 +34,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml index 201e8888..1dbd23b9 100644 --- a/.github/workflows/dockerhub-description.yml +++ b/.github/workflows/dockerhub-description.yml @@ -24,7 +24,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -39,7 +39,7 @@ jobs: fail-fast: false steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 997f3e6a..e51dddbb 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -32,7 +32,7 @@ jobs: pages: write steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/integration-test.yaml b/.github/workflows/integration-test.yaml index 2a112c5b..10bc3879 100644 --- a/.github/workflows/integration-test.yaml +++ b/.github/workflows/integration-test.yaml @@ -26,7 +26,7 @@ jobs: groups: ${{ steps.group-list.outputs.FOLDERS }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -118,7 +118,7 @@ jobs: if: always() steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - run: exit 1 diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2e550689..057aab1e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -31,7 +31,7 @@ jobs: statuses: write steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index ecdde523..45c83ef4 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -36,7 +36,7 @@ jobs: actions: read steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/security-report.yaml b/.github/workflows/security-report.yaml index 07290f08..2aaa7655 100644 --- a/.github/workflows/security-report.yaml +++ b/.github/workflows/security-report.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: rsdmike/github-security-report-action@a149b24539044c92786ec39af8ba38c93496495d # v3.0.4 diff --git a/.github/workflows/test-runner-ci.yaml b/.github/workflows/test-runner-ci.yaml index 75aa8c06..e9f4bb88 100644 --- a/.github/workflows/test-runner-ci.yaml +++ b/.github/workflows/test-runner-ci.yaml @@ -33,7 +33,7 @@ jobs: fail-fast: true steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -66,7 +66,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: coverallsapp/github-action@643bc377ffa44ace6394b2b5d0d3950076de9f63 # v2.3.0 @@ -76,7 +76,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 diff --git a/.github/workflows/weekly-test.yaml b/.github/workflows/weekly-test.yaml index 41189eed..388c021f 100644 --- a/.github/workflows/weekly-test.yaml +++ b/.github/workflows/weekly-test.yaml @@ -25,7 +25,7 @@ jobs: groups: ${{ steps.group-list.outputs.FOLDERS }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -56,7 +56,7 @@ jobs: runs-on: kubectl steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 @@ -72,7 +72,7 @@ jobs: runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} steps: - name: Harden Runner - uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 + uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 with: egress-policy: audit - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 From 5b55cc1246643f38c5f54229ae6ad4ca045bd1e8 Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Thu, 12 Sep 2024 15:24:00 -0700 Subject: [PATCH 44/56] update setuptools and pip to resolve trivy vulnerabilities (#380) Signed-off-by: Srikanth Ramakrishna --- pytorch/Dockerfile | 8 +++++++- pytorch/serving/torchserve-xpu-requirements.txt | 2 +- pytorch/venv-requirements.txt | 5 +++++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 pytorch/venv-requirements.txt diff --git a/pytorch/Dockerfile b/pytorch/Dockerfile index 4015c2a5..0ad9c07f 100644 --- a/pytorch/Dockerfile +++ b/pytorch/Dockerfile @@ -229,7 +229,13 @@ RUN apt-get update -y && apt-get install -y --no-install-recommends --fix-missin python3-venv && \ rm -rf /var/lib/apt/lists/* -RUN python3 -m venv /home/venv +WORKDIR / +COPY venv-requirements.txt . + +RUN python3 -m venv /home/venv && \ + /home/venv/bin/python -m pip install --no-cache-dir --upgrade pip && \ + /home/venv/bin/python -m pip install --no-cache-dir -r venv-requirements.txt && \ + rm -rf venv-requirements.txt ENV PATH="/home/venv/bin:$PATH" diff --git a/pytorch/serving/torchserve-xpu-requirements.txt b/pytorch/serving/torchserve-xpu-requirements.txt index 693402fe..6cd3ff99 100644 --- a/pytorch/serving/torchserve-xpu-requirements.txt +++ b/pytorch/serving/torchserve-xpu-requirements.txt @@ -3,7 +3,7 @@ torchvision==0.18.1+cxx11.abi torchaudio==2.3.1+cxx11.abi intel_extension_for_pytorch==2.3.110+xpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/xpu/us -numpy==1.26.4 +numpy==2.1.1 captum>=0.7.0 cython>=3.0.10 pynvml>=11.5.0 diff --git a/pytorch/venv-requirements.txt b/pytorch/venv-requirements.txt new file mode 100644 index 00000000..4d686efe --- /dev/null +++ b/pytorch/venv-requirements.txt @@ -0,0 +1,5 @@ +setuptools>=70.0.0 +psutil==6.0.0 +mkl==2024.2.1 +mkl-include==2024.2.1 +intel-openmp==2024.2.1 From 1b1c1d4002c2ca45f970c280e0163344df44383a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Sep 2024 15:25:13 -0700 Subject: [PATCH 45/56] Bump the pytorch group across 1 directory with 13 updates (#379) Signed-off-by: dependabot[bot] Signed-off-by: Srikanth Ramakrishna Signed-off-by: Srikanth Ramakrishna Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Srikanth Ramakrishna Co-authored-by: Tyler Titsworth --- pytorch/hf-genai-requirements.txt | 16 ++++++++-------- pytorch/jupyter-requirements.txt | 2 +- pytorch/multinode/requirements.txt | 2 +- pytorch/serving/torchserve-requirements.txt | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pytorch/hf-genai-requirements.txt b/pytorch/hf-genai-requirements.txt index 6671cbaf..8eb7fb3a 100644 --- a/pytorch/hf-genai-requirements.txt +++ b/pytorch/hf-genai-requirements.txt @@ -1,13 +1,13 @@ -accelerate==0.33.0 -datasets==2.21.0 +accelerate==0.34.2 +datasets==3.0.0 einops==0.8.0 -evaluate==0.4.2 -onnxruntime-extensions==0.11.0 -onnxruntime==1.18.1 +evaluate==0.4.3 +onnxruntime-extensions==0.12.0 +onnxruntime==1.19.2 peft==0.12.0 -protobuf==5.27.3 +protobuf==5.28.1 py-cpuinfo==9.0.0 -scikit-learn==1.5.1 +scikit-learn==1.5.2 SentencePiece==0.2.0 tokenizers==0.19.1 -transformers==4.44.0 +transformers==4.44.2 diff --git a/pytorch/jupyter-requirements.txt b/pytorch/jupyter-requirements.txt index e95ad6e8..4313b738 100644 --- a/pytorch/jupyter-requirements.txt +++ b/pytorch/jupyter-requirements.txt @@ -1,4 +1,4 @@ -jupyterlab==4.3.0b0 +jupyterlab==4.3.0b1 jupyterhub==5.1.0 notebook==7.3.0a1 jupyter-server-proxy>=4.1.2 diff --git a/pytorch/multinode/requirements.txt b/pytorch/multinode/requirements.txt index c941708a..a303e658 100644 --- a/pytorch/multinode/requirements.txt +++ b/pytorch/multinode/requirements.txt @@ -1,4 +1,4 @@ -neural-compressor==3.0 +neural-compressor==3.0.2 oneccl_bind_pt==2.4.0+cpu --extra-index-url https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ oneccl-devel>=2021.13.0 # required to build deepspeed ops diff --git a/pytorch/serving/torchserve-requirements.txt b/pytorch/serving/torchserve-requirements.txt index f495a686..41d78b17 100644 --- a/pytorch/serving/torchserve-requirements.txt +++ b/pytorch/serving/torchserve-requirements.txt @@ -6,7 +6,7 @@ pyyaml>=6.0.1 torch-model-archiver==0.11.1 torch-workflow-archiver==0.2.14 torchserve==0.11.1 -torchtext==0.18.0 +torchtext==0.18.0+cpu torchvision==0.19.0 -f https://pytorch-extension.intel.com/release-whl/stable/cpu/us/ intel_extension_for_pytorch==2.4.0+cpu From fb3a573293bd4d287bba9817bd313d1c11ab90fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:00:29 -0700 Subject: [PATCH 46/56] Bump github/codeql-action from 3.26.6 to 3.26.7 (#386) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/container-ci.yaml | 2 +- .github/workflows/scorecard.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-ci.yaml b/.github/workflows/container-ci.yaml index 6d8a22f7..0c8c8f94 100644 --- a/.github/workflows/container-ci.yaml +++ b/.github/workflows/container-ci.yaml @@ -155,7 +155,7 @@ jobs: - name: Cleanup if: always() run: docker rmi -f ${{ secrets.REGISTRY }}/${{ secrets.REPO }}:${{ matrix.container }} - - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 with: sarif_file: '${{ matrix.container }}-scan.sarif' category: '${{ matrix.container }}' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 45c83ef4..99ada495 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -53,6 +53,6 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - - uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 + - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 with: sarif_file: results.sarif From f912d3193cc8c885a3819faa9e36a545343cfb25 Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Mon, 16 Sep 2024 13:37:34 -0700 Subject: [PATCH 47/56] fix broken links (#390) Signed-off-by: Srikanth Ramakrishna Signed-off-by: Srikanth Ramakrishna Signed-off-by: dependabot[bot] Co-authored-by: Tyler Titsworth Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pytorch/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pytorch/README.md b/pytorch/README.md index a96170fb..6b7a4012 100644 --- a/pytorch/README.md +++ b/pytorch/README.md @@ -24,7 +24,7 @@ The images below include support for both CPU and GPU optimizations: | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------------- | -------- | -------------- | ------ | --------------- | -| `2.3.110-xpu-pip-base`,`2.3.110-xpu` | [torch-2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0] | +| `2.3.110-xpu-pip-base`,`2.3.110-xpu` | [v2.3.1][torch-v2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0-Beta] | | `2.1.40-xpu-pip-base`,`2.1.40-xpu` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.20-xpu` | [v2.1.0] | [v2.1.20+xpu] | [803] | [v0.3.4] | @@ -47,7 +47,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | -| `2.3.110-xpu-pip-jupyter` | [torch-2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | +| `2.3.110-xpu-pip-jupyter` | [v2.3.1][torch-v2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | | `2.1.40-xpu-pip-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | | `2.1.20-xpu-pip-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | | `2.1.10-xpu-pip-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | @@ -276,7 +276,7 @@ The images below are [TorchServe*] with XPU Optimizations: | Tag(s) | Pytorch | IPEX | Dockerfile | | ------------------- | -------- | ------------ | --------------- | -| `2.3.110-serving-xpu` | [torch-2.3.1] | [v2.3.110+xpu] | [v0.4.0-Beta] | +| `2.3.110-serving-xpu` | [v2.3.1][torch-v2.3.1] | [v2.3.110+xpu] | [v0.4.0-Beta] | ## CPU only images with Intel® Distribution for Python* @@ -316,7 +316,7 @@ The images below are built only with CPU and GPU optimizations and include [Inte | Tag(s) | Pytorch | IPEX | Driver | Dockerfile | | ---------------- | -------- | ------------ | -------- | ------ | -| `2.3.110-xpu-idp-base` | [torch-v2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0-Beta] | +| `2.3.110-xpu-idp-base` | [v2.3.1][torch-v2.3.1] | [v2.3.110+xpu] | [950] | [v0.4.0-Beta] | | `2.1.40-xpu-idp-base` | [v2.1.0] | [v2.1.40+xpu] | [914] | [v0.4.0-Beta] | | `2.1.30-xpu-idp-base` | [v2.1.0] | [v2.1.30+xpu] | [803] | [v0.4.0-Beta] | | `2.1.10-xpu-idp-base` | [v2.1.0] | [v2.1.10+xpu] | [736] | [v0.2.3] | @@ -325,7 +325,7 @@ The images below additionally include [Jupyter Notebook](https://jupyter.org/) s | Tag(s) | Pytorch | IPEX | Driver | Jupyter Port | Dockerfile | | --------------------- | -------- | ------------- | ------ | ------------ | --------------- | -| `2.3.110-xpu-idp-jupyter` | [torch-v2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | +| `2.3.110-xpu-idp-jupyter` | [v2.3.1][torch-v2.3.1] | [v2.3.110+xpu] | [950] | `8888` | [v0.4.0-Beta] | | `2.1.40-xpu-idp-jupyter` | [v2.1.0] | [v2.1.40+xpu] | [914] | `8888` | [v0.4.0-Beta] | | `2.1.20-xpu-idp-jupyter` | [v2.1.0] | [v2.1.20+xpu] | [803] | `8888` | [v0.3.4] | | `2.1.10-xpu-idp-jupyter` | [v2.1.0] | [v2.1.10+xpu] | [736] | `8888` | [v0.2.3] | From 43c88502b7d4a4543fef2e8ee1808d0f9c48b592 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:05:55 -0700 Subject: [PATCH 48/56] [pre-commit.ci] pre-commit autoupdate (#391) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b972cfa..fc2fc4a1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: - hooks: - id: gitleaks repo: https://github.com/gitleaks/gitleaks - rev: v8.18.4 + rev: v8.19.2 - hooks: - args: [--license-filepath=.github/license_template.txt, --use-current-year, --detect-license-in-X-top-lines=40, --skip-license-insertion-comment=Copyright] files: | From 4f78e1506a4d838dc47f13334fc6e99c0dcad04b Mon Sep 17 00:00:00 2001 From: Srikanth Ramakrishna Date: Wed, 18 Sep 2024 12:57:20 -0700 Subject: [PATCH 49/56] Resolve apptainer build error by bumping the apptainer version to 1.3.4 (#392) Signed-off-by: Srikanth Ramakrishna --- .github/workflows/apptainer-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/apptainer-ci.yaml b/.github/workflows/apptainer-ci.yaml index 62083391..330dcc0c 100644 --- a/.github/workflows/apptainer-ci.yaml +++ b/.github/workflows/apptainer-ci.yaml @@ -72,7 +72,7 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: eWaterCycle/setup-apptainer@4bb22c52d4f63406c49e94c804632975787312b3 # v2.0.0 with: - apptainer-version: 1.3.3 + apptainer-version: 1.3.4 - name: Apptainer login to registry env: APPTAINER_DOCKER_USERNAME: ${{ secrets.REGISTRY_USER }} From edd19a8373549db1a4221da5cb9f85c74d9a4232 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:10:51 -0700 Subject: [PATCH 50/56] Bump the apptainer group across 1 directory with 2 updates (#388) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Srikanth Ramakrishna --- apptainer/python/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apptainer/python/requirements.txt b/apptainer/python/requirements.txt index b259254d..a18c568e 100644 --- a/apptainer/python/requirements.txt +++ b/apptainer/python/requirements.txt @@ -1,5 +1,5 @@ -numpy==2.1.0 -setuptools==73.0.1 +numpy==2.1.1 +setuptools==75.1.0 psutil==6.0.0 mkl==2024.2.1 mkl-include==2024.2.1 From 99dcba2d475f82a75fd6b88a6167e2bce0b6b404 Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Wed, 18 Sep 2024 13:41:47 -0700 Subject: [PATCH 51/56] TensorFlow Serving on XPU Chart (#359) Signed-off-by: tylertitsworth --- .../charts/tensorflow-serving/.helmignore | 23 +++++ .../charts/tensorflow-serving/Chart.yaml | 42 ++++++++++ workflows/charts/tensorflow-serving/README.md | 31 +++++++ .../tensorflow-serving/templates/NOTES.txt | 19 +++++ .../tensorflow-serving/templates/_helpers.tpl | 51 +++++++++++ .../templates/deployment.yaml | 84 +++++++++++++++++++ .../tensorflow-serving/templates/pvc.yaml | 29 +++++++ .../tensorflow-serving/templates/service.yaml | 31 +++++++ .../templates/tests/test-connection.yaml | 29 +++++++ .../charts/tensorflow-serving/values.yaml | 53 ++++++++++++ .../tgi/templates/tests/test-connection.yaml | 2 +- 11 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 workflows/charts/tensorflow-serving/.helmignore create mode 100644 workflows/charts/tensorflow-serving/Chart.yaml create mode 100644 workflows/charts/tensorflow-serving/README.md create mode 100644 workflows/charts/tensorflow-serving/templates/NOTES.txt create mode 100644 workflows/charts/tensorflow-serving/templates/_helpers.tpl create mode 100644 workflows/charts/tensorflow-serving/templates/deployment.yaml create mode 100644 workflows/charts/tensorflow-serving/templates/pvc.yaml create mode 100644 workflows/charts/tensorflow-serving/templates/service.yaml create mode 100644 workflows/charts/tensorflow-serving/templates/tests/test-connection.yaml create mode 100644 workflows/charts/tensorflow-serving/values.yaml diff --git a/workflows/charts/tensorflow-serving/.helmignore b/workflows/charts/tensorflow-serving/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/workflows/charts/tensorflow-serving/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/workflows/charts/tensorflow-serving/Chart.yaml b/workflows/charts/tensorflow-serving/Chart.yaml new file mode 100644 index 00000000..e6a61952 --- /dev/null +++ b/workflows/charts/tensorflow-serving/Chart.yaml @@ -0,0 +1,42 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v2 +name: tensorflow-serving-on-intel +description: TensorFlow Serving is a flexible, high-performance serving system for machine learning models, designed for production environments. TensorFlow Serving makes it easy to deploy new algorithms and experiments, while keeping the same server architecture and APIs. TensorFlow Serving provides out-of-the-box integration with TensorFlow models, but can be easily extended to serve other types of models and data. + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +maintainers: + - name: tylertitsworth + email: tyler.titsworth@intel.com + url: https://github.com/tylertitsworth +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/workflows/charts/tensorflow-serving/README.md b/workflows/charts/tensorflow-serving/README.md new file mode 100644 index 00000000..bfbb2900 --- /dev/null +++ b/workflows/charts/tensorflow-serving/README.md @@ -0,0 +1,31 @@ +# tensorflow-serving-on-intel + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.16.0](https://img.shields.io/badge/AppVersion-1.16.0-informational?style=flat-square) + +TensorFlow Serving is a flexible, high-performance serving system for machine learning models, designed for production environments. TensorFlow Serving makes it easy to deploy new algorithms and experiments, while keeping the same server architecture and APIs. TensorFlow Serving provides out-of-the-box integration with TensorFlow models, but can be easily extended to serve other types of models and data. + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| tylertitsworth | | | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| deploy.env | object | `{"configMapName":"intel-proxy-config","enabled":true}` | Add Environment mapping | +| deploy.image | string | `"intel/intel-extension-for-tensorflow:serving-gpu"` | Intel Extension for Tensorflow Serving image | +| deploy.modelName | string | `""` | Model Name | +| deploy.replicas | int | `1` | Number of pods | +| deploy.resources.limits | object | `{"cpu":"4000m","gpu.intel.com/i915":1,"memory":"1Gi"}` | Maximum resources per pod | +| deploy.resources.limits."gpu.intel.com/i915" | int | `1` | Intel GPU Device Configuration | +| deploy.resources.requests | object | `{"cpu":"1000m","memory":"512Mi"}` | Minimum resources per pod | +| deploy.storage.nfs | object | `{"enabled":false,"path":"nil","readOnly":true,"server":"nil"}` | Network File System (NFS) storage for models | +| fullnameOverride | string | `""` | Full qualified Domain Name | +| nameOverride | string | `""` | Name of the serving service | +| pvc.size | string | `"5Gi"` | Size of the storage | +| service.type | string | `"NodePort"` | Type of service | + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/workflows/charts/tensorflow-serving/templates/NOTES.txt b/workflows/charts/tensorflow-serving/templates/NOTES.txt new file mode 100644 index 00000000..fb69969c --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tensorflow-serving.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tensorflow-serving.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tensorflow-serving.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tensorflow-serving.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} +2. Make a prediction + curl http://$NODE_IP:$NODE_PORT/v1/models/{{ .Values.deploy.modelName }} + curl -X POST http://$NODE_IP:$NODE_PORT/v1/models/{{ .Values.deploy.modelName }}:predict -d '{"data": []}' diff --git a/workflows/charts/tensorflow-serving/templates/_helpers.tpl b/workflows/charts/tensorflow-serving/templates/_helpers.tpl new file mode 100644 index 00000000..2afbfd70 --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/_helpers.tpl @@ -0,0 +1,51 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "tensorflow-serving.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "tensorflow-serving.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "tensorflow-serving.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "tensorflow-serving.labels" -}} +helm.sh/chart: {{ include "tensorflow-serving.chart" . }} +{{ include "tensorflow-serving.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "tensorflow-serving.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tensorflow-serving.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/workflows/charts/tensorflow-serving/templates/deployment.yaml b/workflows/charts/tensorflow-serving/templates/deployment.yaml new file mode 100644 index 00000000..e6a1fcf6 --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/deployment.yaml @@ -0,0 +1,84 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +{{- $name := .Values.deploy.modelName | required ".Values.deploy.modelName is required." -}} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tensorflow-serving.fullname" . }} + labels: + {{- include "tensorflow-serving.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.deploy.replicas }} + selector: + matchLabels: + {{- include "tensorflow-serving.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "tensorflow-serving.labels" . | nindent 8 }} + spec: + securityContext: + fsGroup: 1000 + runAsUser: 1000 + containers: + - name: tensorflow-serving + image: {{ .Values.deploy.image }} + {{- if eq .Values.deploy.env.enabled true }} + envFrom: + - configMapRef: + name: {{ .Values.deploy.env.configMapName }} + {{- end }} + env: + - name: MODEL_NAME + value: {{ .Values.deploy.modelName }} + ports: + - name: rest + containerPort: 8500 + protocol: TCP + - name: grpc + containerPort: 8501 + protocol: TCP + readinessProbe: + tcpSocket: + port: rest + initialDelay: 15 + timeoutSeconds: 1 + volumeMounts: + - mountPath: /dev/shm + name: dshm + {{- if .Values.deploy.storage.nfs.enabled }} + - name: model + mountPath: /models/{{ .Values.deploy.modelName }} + {{- else }} + - name: model + mountPath: /models/{{ .Values.deploy.modelName }} + {{- end }} + resources: + {{- toYaml .Values.deploy.resources | nindent 12 }} + volumes: + - name: dshm + emptyDir: + medium: Memory + {{- if .Values.deploy.storage.nfs.enabled }} + - name: model + nfs: + server: {{ .Values.deploy.storage.nfs.server }} + path: {{ .Values.deploy.storage.nfs.path }} + readOnly: {{ .Values.deploy.storage.nfs.readOnly }} + {{- else }} + - name: model + persistentVolumeClaim: + claimName: {{ include "tensorflow-serving.fullname" . }}-model-dir + {{- end }} diff --git a/workflows/charts/tensorflow-serving/templates/pvc.yaml b/workflows/charts/tensorflow-serving/templates/pvc.yaml new file mode 100644 index 00000000..2cf9040d --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/pvc.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +{{- if not .Values.deploy.storage.nfs.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "tensorflow-serving.fullname" . }}-model-dir + labels: + {{- include "tensorflow-serving.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: {{ .Values.pvc.size }} +{{- end }} diff --git a/workflows/charts/tensorflow-serving/templates/service.yaml b/workflows/charts/tensorflow-serving/templates/service.yaml new file mode 100644 index 00000000..2eab7890 --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/service.yaml @@ -0,0 +1,31 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tensorflow-serving.fullname" . }} + labels: + {{- include "tensorflow-serving.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - name: rest + port: 8500 + targetPort: rest + - name: grpc + port: 8501 + targetPort: grpc + selector: + {{- include "tensorflow-serving.selectorLabels" . | nindent 4 }} diff --git a/workflows/charts/tensorflow-serving/templates/tests/test-connection.yaml b/workflows/charts/tensorflow-serving/templates/tests/test-connection.yaml new file mode 100644 index 00000000..0fe61c9a --- /dev/null +++ b/workflows/charts/tensorflow-serving/templates/tests/test-connection.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "tensorflow-serving.fullname" . }}-test-connection" + labels: + {{- include "tensorflow-serving.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: info + image: curlimages/curl + command: ['sh', '-c'] + args: ['curl -f {{ include "tensorflow-serving.fullname" . }}:8501/v1/models/{{ .Values.deploy.modelName}}'] + restartPolicy: OnFailure diff --git a/workflows/charts/tensorflow-serving/values.yaml b/workflows/charts/tensorflow-serving/values.yaml new file mode 100644 index 00000000..39ed23af --- /dev/null +++ b/workflows/charts/tensorflow-serving/values.yaml @@ -0,0 +1,53 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# -- Name of the serving service +nameOverride: "" +# -- Full qualified Domain Name +fullnameOverride: "" +deploy: + # -- Intel Extension for Tensorflow Serving image + image: intel/intel-extension-for-tensorflow:serving-gpu + # -- Add Environment mapping + env: + configMapName: intel-proxy-config + enabled: true + # -- Model Name + modelName: "" + # -- Number of pods + replicas: 1 + resources: + # -- Maximum resources per pod + limits: + cpu: 4000m + memory: 1Gi + # -- Intel GPU Device Configuration + gpu.intel.com/i915: 1 + # -- Minimum resources per pod + requests: + cpu: 1000m + memory: 512Mi + storage: + # -- Network File System (NFS) storage for models + nfs: + enabled: false + server: nil + path: nil + readOnly: true +service: + # -- Type of service + type: NodePort +pvc: + # -- Size of the storage + size: 5Gi diff --git a/workflows/charts/tgi/templates/tests/test-connection.yaml b/workflows/charts/tgi/templates/tests/test-connection.yaml index 113d8acf..007086c4 100644 --- a/workflows/charts/tgi/templates/tests/test-connection.yaml +++ b/workflows/charts/tgi/templates/tests/test-connection.yaml @@ -25,5 +25,5 @@ spec: - name: info image: curlimages/curl command: ['sh', '-c'] - args: ['curl --noproxy "*" -f {{ include "tgi.fullname" . }}:{{ .Values.service.port }}/info'] + args: ['curl -f {{ include "tgi.fullname" . }}:{{ .Values.service.port }}/info'] restartPolicy: OnFailure From 6860ada288e6354991e1967dc20cd8c27722d2e8 Mon Sep 17 00:00:00 2001 From: Sharvil Shah Date: Thu, 19 Sep 2024 09:33:52 -0700 Subject: [PATCH 52/56] Docs for Intel Gaudi notebook container added (#393) Signed-off-by: sharvil10 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../redhat/openshift-ai/gaudi/README.md | 68 ++++++++++++++++++ .../openshift-ai/gaudi/assets/step-3.png | Bin 0 -> 157783 bytes .../openshift-ai/gaudi/assets/step-4.png | Bin 0 -> 126062 bytes .../redhat/openshift-ai/gaudi/crd-sample.yaml | 28 ++++++++ 4 files changed, 96 insertions(+) create mode 100644 enterprise/redhat/openshift-ai/gaudi/README.md create mode 100644 enterprise/redhat/openshift-ai/gaudi/assets/step-3.png create mode 100644 enterprise/redhat/openshift-ai/gaudi/assets/step-4.png create mode 100644 enterprise/redhat/openshift-ai/gaudi/crd-sample.yaml diff --git a/enterprise/redhat/openshift-ai/gaudi/README.md b/enterprise/redhat/openshift-ai/gaudi/README.md new file mode 100644 index 00000000..a5682adf --- /dev/null +++ b/enterprise/redhat/openshift-ai/gaudi/README.md @@ -0,0 +1,68 @@ +# Intel® Gaudi AI Software Tools Containers on OpenShift AI + +Intel® Gaudi AI Software Tools for OpenShift AI(RedHat OpenShift Data Science/RHODS) is a suite of containers that enables the AI practitioners to utilize Intel® Gaudi accelerator for AI workflows on OpenShift platforms. You can access these containers using the RHODS Jupyter dashboard. More details about each container is described in the table below. + +## Gaudi Notebook Containers + +| Notebook Container Name | Tools | Image Name | +| -----------------------------| ------------- | ------------- | +| Intel Gaudi Notebook Container | [Intel® Gaudi Software Stack*](https://docs.habana.ai/en/latest/Installation_Guide/Bare_Metal_Fresh_OS.html), [Intel® Gaudi PyTorch](https://docs.habana.ai/en/latest/PyTorch/index.html), [Intel® Gaudi vLLM](https://github.com/HabanaAI/vllm-fork.git), [Intel® Gaudi DeepSpeed](https://github.com/HabanaAI/DeepSpeed) | [`registry.connect.redhat.com/intel/gaudi-notebooks:1.17.0-495-rhel-9.2`](registry.connect.redhat.com/intel/gaudi-notebooks@sha256:a62baf968caa7dd23b7f4cdcddc26e109d894f1436e247b4ea1e2fb4a5c94d54) | + +## Run Gaudi Notebook Containers + +You can access the Intel® Gaudi AI SW Tools containers from OpenShift* AI dashboard. + +### Prerequisite + +1. Make sure you have access to [OpenShift* Container Platform](https://docs.openshift.com/container-platform/4.14/installing/index.html) and [OpenShift* AI operator](https://docs.redhat.com/en/documentation/red_hat_openshift_ai_cloud_service/1/html/installing_and_uninstalling_openshift_ai_cloud_service/installing-and-deploying-openshift-ai_install#installing-and-deploying-openshift-ai_install) is installed if you want to access the containers from OpenShift* AI dashboard. + +2. To utilize the Intel® Gaudi accelerator with the notebook please, install the Intel® Gaudi Base Operator for OpenShift([instructions](https://catalog.redhat.com/software/container-stacks/detail/6683b2cce45daa25e36bddcb)) and the accelerate profile using the following command on your machine connected to the OCP cluster. You need to be logged into the OCP cluster for this command to work. + + ```bash + oc apply -f https://raw.githubusercontent.com/intel/intel-technology-enabling-for-openshift/main/e2e/inference/accelerator_profile_gaudi.yaml + ``` + +3. Install the [Intel® Gaudi AI SW Tool Operator](https://catalog.redhat.com/software/container-stacks/detail/66d7aa630eb66a02febc8103). + +4. Create the CRD to install Gaudi notebook into OpenShift AI Jupyter dashboard using following command. + + ```bash + oc apply -f https://raw.githubusercontent.com/intel/ai-containers/main/enterprise/redhat/openshift-ai/gaudi/crd-sample.yaml + ``` + +### Start the jupyter notebook from RHODS dashboard + +To access the jupyter notebook from the jupyter server running inside the Gaudi notebook container in OpenShift AI follow the steps below. + +1. Once the OpenShift AI operator is installed correctly, you can access the dashboard by following the instructions below. + + 1. Go to the *Routes* menu in your OCP console in the menu left side in the *Networking* tab. + + 2. Select the project as `redhat-ods-applications` from the menu at the top. + + 3. You will see list of all the routes available to you in the project. You will also see the corresponding link to access the routes. Please select the link in the row named *rhods-dashboard* as shown in the picture. This will take you to the OpenShift AI dashboard. Please refer to the screenshot below for more details. + + ![Step-1](../oneapi/assets/step-1.png) + +2. Once on the OpenShift AI dashboard you can select the link to `Launch Application` inside the *Jupyter* tile in the *Enabled* applications from the left menu. Please refer to the screenshot below for more details. + + ![Step-2](../oneapi/assets/step-2.png) + +3. If you've followed step 3 in [prerequisites](#prerequisite) to import images you should be able to see the Intel® Gaudi AI Software Tools images in the dashboard as shown in the screenshot below. + + ![Step-3](./assets/step-3.png) + +4. Select the size of the resources you want to request from the dropdown menu *Container Size*. The options available are `Small`, `Medium`, `Large`, `X-Large`. The sizes describes the request of resources like CPU, RAM and Disk Space for the jupyter server container. + +5. (*Optional*) To utilize the Intel® Gaudi accelerators select the accelerator profile from the dropdown menu as shown in the screenshot below. + + ![Step-4](./assets/step-4.png) + +6. Once all options are selected click on the *Start Server* button to start the jupyter server. + +## Troubleshooting and Support + +If you need more help feel free to submit an [issue](https://github.com/intel/ai-containers/issues). + +--- +\* Other names and brands may be claimed as the property of others. Trademarks diff --git a/enterprise/redhat/openshift-ai/gaudi/assets/step-3.png b/enterprise/redhat/openshift-ai/gaudi/assets/step-3.png new file mode 100644 index 0000000000000000000000000000000000000000..a561bbb21495f2f5328531e3da7928de9eb375bd GIT binary patch literal 157783 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 z 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 Date: Thu, 19 Sep 2024 09:40:58 -0700 Subject: [PATCH 53/56] Intel Extension for OpenXLA Containers (#385) Signed-off-by: tylertitsworth Signed-off-by: Tyler Titsworth Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .gitignore | 1 + docs/roadmap.md | 2 +- docs/scripts/hook.py | 1 + docs/scripts/readmes.py | 1 + jax/.actions.json | 5 ++ jax/Dockerfile | 104 +++++++++++++++++++++++++++++++++++ jax/README.md | 86 +++++++++++++++++++++++++++++ jax/docker-compose.yaml | 87 +++++++++++++++++++++++++++++ jax/jupyter-requirements.txt | 4 ++ jax/requirements.txt | 5 ++ jax/tests/example.py | 37 +++++++++++++ jax/tests/tests.yaml | 29 ++++++++++ 12 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 jax/.actions.json create mode 100644 jax/Dockerfile create mode 100644 jax/README.md create mode 100644 jax/docker-compose.yaml create mode 100644 jax/jupyter-requirements.txt create mode 100644 jax/requirements.txt create mode 100644 jax/tests/example.py create mode 100644 jax/tests/tests.yaml diff --git a/.gitignore b/.gitignore index f3e1d08f..d6f27a92 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ logs/ models-perf/ output/ site +test-runner-summary-output.json venv/ diff --git a/docs/roadmap.md b/docs/roadmap.md index 8e22e7c8..018808b6 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -10,7 +10,7 @@ - Granite Rapids Support - CLS Support -- Intel Developer Cloud Support +- Intel Tiber Developer Cloud Support - AI Tools 2024.3/2025.0 Support ## Q4'24 diff --git a/docs/scripts/hook.py b/docs/scripts/hook.py index 3b862bdf..2f0c96ec 100644 --- a/docs/scripts/hook.py +++ b/docs/scripts/hook.py @@ -34,6 +34,7 @@ def create_support_matrix(): compose_to_csv("pytorch", "serving") compose_to_csv("tensorflow", None) compose_to_csv("classical-ml", None) + compose_to_csv("jax", None) # get_repo(models) compose_to_csv("preset/data-analytics", "data_analytics") diff --git a/docs/scripts/readmes.py b/docs/scripts/readmes.py index 3e7d5e09..8eb2553b 100644 --- a/docs/scripts/readmes.py +++ b/docs/scripts/readmes.py @@ -17,6 +17,7 @@ readmes = [ "classical-ml/README.md", + "jax/README.md", "preset/README.md", "python/README.md", "pytorch/README.md", diff --git a/jax/.actions.json b/jax/.actions.json new file mode 100644 index 00000000..36e21ad8 --- /dev/null +++ b/jax/.actions.json @@ -0,0 +1,5 @@ +{ + "PACKAGE_OPTION": ["idp", "pip"], + "experimental": [true], + "runner_label": ["PVC"] +} diff --git a/jax/Dockerfile b/jax/Dockerfile new file mode 100644 index 00000000..0d0118fa --- /dev/null +++ b/jax/Dockerfile @@ -0,0 +1,104 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +# +# +# This file was assembled from multiple pieces, whose use is documented +# throughout. Please refer to the TensorFlow dockerfiles documentation +# for more information. + +ARG REGISTRY +ARG REPO +ARG GITHUB_RUN_NUMBER +ARG BASE_IMAGE_NAME +ARG BASE_IMAGE_TAG +ARG PACKAGE_OPTION=pip +ARG PYTHON_VERSION +ARG PYTHON_BASE=${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER}-${BASE_IMAGE_NAME}-${BASE_IMAGE_TAG}-${PACKAGE_OPTION}-py${PYTHON_VERSION}-base +ARG TORCHSERVE_BASE=${PYTHON_BASE} +FROM ${PYTHON_BASE} AS xpu-base + +RUN apt-get update && \ + apt-get install -y --no-install-recommends --fix-missing \ + apt-utils \ + build-essential \ + clinfo \ + git \ + gnupg2 \ + gpg-agent \ + rsync \ + unzip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN wget -qO - https://repositories.intel.com/gpu/intel-graphics.key | \ + gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu jammy unified" | \ + tee /etc/apt/sources.list.d/intel-gpu-jammy.list + +ARG ICD_VER +ARG LEVEL_ZERO_GPU_VER +ARG LEVEL_ZERO_VER +ARG LEVEL_ZERO_DEV_VER + +RUN apt-get update && \ + apt-get install -y --no-install-recommends --fix-missing \ + intel-opencl-icd=${ICD_VER} \ + intel-level-zero-gpu=${LEVEL_ZERO_GPU_VER} \ + libze1=${LEVEL_ZERO_VER} \ + libze-dev=${LEVEL_ZERO_DEV_VER} && \ + rm -rf /var/lib/apt/lists/* + +RUN no_proxy="" NO_PROXY="" wget --progress=dot:giga -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \ + | gpg --dearmor | tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null && \ + echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" \ + | tee /etc/apt/sources.list.d/oneAPI.list + +ARG DPCPP_VER +ARG MKL_VER +ARG CCL_VER + +RUN apt-get update && \ + apt-get install -y --no-install-recommends --fix-missing \ + intel-oneapi-runtime-dpcpp-cpp=${DPCPP_VER} \ + intel-oneapi-runtime-mkl=${MKL_VER} \ + intel-oneapi-runtime-ccl=${CCL_VER} && \ + rm -rf /var/lib/apt/lists/* + +RUN rm -rf /etc/apt/sources.list.d/intel-gpu-jammy.list /etc/apt/sources.list.d/oneAPI.list + +ENV OCL_ICD_VENDORS=/etc/OpenCL/vendors + +FROM xpu-base AS jax-base + +WORKDIR / +COPY requirements.txt . + +RUN python -m pip install --no-cache-dir -r requirements.txt && \ + rm -rf requirements.txt + +FROM jax-base AS jupyter + +WORKDIR /jupyter +COPY jupyter-requirements.txt . + +RUN python -m pip install --no-cache-dir -r jupyter-requirements.txt && \ + rm -rf jupyter-requirements.txt + +RUN mkdir -p /jupyter/ && chmod -R a+rwx /jupyter/ +RUN mkdir /.local && chmod a+rwx /.local + +EXPOSE 8888 + +CMD ["bash", "-c", "source /etc/bash.bashrc && jupyter notebook --notebook-dir=/jupyter --port 8888 --ip 0.0.0.0 --no-browser --allow-root --ServerApp.token= --ServerApp.password= --ServerApp.allow_origin=* --ServerApp.base_url=$NB_PREFIX"] diff --git a/jax/README.md b/jax/README.md new file mode 100644 index 00000000..67ea81ed --- /dev/null +++ b/jax/README.md @@ -0,0 +1,86 @@ +# Intel® Optimized OpenXLA\* + +Transformable numerical computing at scale combined with [Intel® Extension for OpenXLA\*], which includes a PJRT plugin implementation to seamlessly runs [JAX\*] models on Intel GPUs. + +## Images + +The images below include [JAX\*] and [Intel® Extension for OpenXLA\*]. + +| Tag(s) | [JAX\*] | [Intel® Extension for OpenXLA\*] | [Flax] | Dockerfile | +| -------------------------- | --------- | ----------------- | -------- | --------------- | +| `0.4.0-pip-base`, `latest` | [v0.4.32] | [v0.4.0-jax] | [v0.9.0] | [v0.4.0] | + +The images below additionally include [Jupyter Notebook](https://jupyter.org/) server: + +| Tag(s) | [JAX\*] | [Intel® Extension for OpenXLA\*] | [Flax] | Dockerfile | +| ------------------- | --------- | ----------------- | -------- | --------------- | +| `0.4.0-pip-jupyter` | [v0.4.32] | [v0.4.0-jax] | [v0.9.0] | [v0.4.0] | + +### Run the Jupyter Container + +```bash +docker run -it --rm \ + -p 8888:8888 \ + --net=host \ + -v $PWD/workspace:/workspace \ + -w /workspace \ + intel/intel-optimized-xla:0.4.0-pip-jupyter +``` + +After running the command above, copy the URL (something like `http://127.0.0.1:$PORT/?token=***`) into your browser to access the notebook server. + +## Images with Intel® Distribution for Python* + +The images below include [Intel® Distribution for Python*]: + +| Tag(s) | [JAX\*] | [Intel® Extension for OpenXLA\*] | [Flax] | Dockerfile | +| ---------------- | --------- | ----------------- | -------- | --------------- | +| `0.4.0-idp-base` | [v0.4.32] | [v0.4.0-jax] | [v0.9.0] | [v0.4.0] | + +The images below additionally include [Jupyter Notebook](https://jupyter.org/) server: + +| Tag(s) | [JAX\*] | [Intel® Extension for OpenXLA\*] | [Flax] | Dockerfile | +| ------------------- | --------- | ----------------- | -------- | --------------- | +| `0.4.0-idp-jupyter` | [v0.4.32] | [v0.4.0-jax] | [v0.9.0] | [v0.4.0] | + +## Build from Source + +To build the images from source, clone the [AI Containers](https://github.com/intel/ai-containers) repository, follow the main `README.md` file to setup your environment, and run the following command: + +```bash +cd jax +docker compose build jax-base +docker compose run -it jax-base +``` + +You can find the list of services below for each container in the group: + +| Service Name | Description | +| ------------ | ----------------------------------------------- | +| `jax-base` | Base image with [Intel® Extension for OpenXLA\*] | +| `jupyter` | Adds Jupyter Notebook server | + +## License + +View the [License](https://github.com/intel/ai-containers/blob/main/LICENSE) for the [Intel® Distribution for Python]. + +The images below also contain other software which may be under other licenses (such as Pytorch*, Jupyter*, Bash, etc. from the base). + +It is the image user's responsibility to ensure that any use of The images below comply with any relevant licenses for all software contained within. + +\* Other names and brands may be claimed as the property of others. + + + +[Intel® Distribution for Python*]: https://www.intel.com/content/www/us/en/developer/tools/oneapi/distribution-for-python.html#gs.9bos9m +[Intel® Extension for OpenXLA\*]: https://github.com/intel/intel-extension-for-openxla +[JAX\*]: https://github.com/google/jax +[Flax]: https://github.com/google/flax + +[v0.4.32]: https://github.com/google/jax/releases/tag/jax-v0.4.32 + +[v0.4.0-jax]: https://github.com/intel/intel-extension-for-openxla/releases/tag/0.4.0 + +[v0.9.0]: https://github.com/google/Flax/releases/tag/v0.9.0 + +[v0.4.0]: https://github.com/intel/ai-containers/blob/v0.4.0/jax/Dockerfile diff --git a/jax/docker-compose.yaml b/jax/docker-compose.yaml new file mode 100644 index 00000000..e2c47d63 --- /dev/null +++ b/jax/docker-compose.yaml @@ -0,0 +1,87 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include: + - path: + - ../python/docker-compose.yaml +services: + jax-base: + build: + args: + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + no_proxy: "" + BASE_IMAGE_NAME: ${BASE_IMAGE_NAME:-ubuntu} + BASE_IMAGE_TAG: ${BASE_IMAGE_TAG:-22.04} + CCL_VER: ${CCL_VER:-2021.13.1-31} + DPCPP_VER: ${DPCPP_VER:-2024.2.1-1079} + GITHUB_RUN_NUMBER: ${GITHUB_RUN_NUMBER:-0} + ICD_VER: ${ICD_VER:-24.22.29735.27-914~22.04} + LEVEL_ZERO_DEV_VER: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} + LEVEL_ZERO_GPU_VER: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} + LEVEL_ZERO_VER: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} + MINIFORGE_VERSION: ${MINIFORGE_VERSION:-Linux-x86_64} + MKL_VER: ${MKL_VER:-2024.2.1-103} + NO_PROXY: '' + PACKAGE_OPTION: ${PACKAGE_OPTION:-pip} + PYTHON_VERSION: ${PYTHON_VERSION:-3.10} + REGISTRY: ${REGISTRY} + REPO: ${REPO} + context: . + labels: + dependency.python: ${PYTHON_VERSION:-3.10} + dependency.apt.build-essential: true + dependency.apt.clinfo: true + dependency.apt.git: true + dependency.apt.gnupg2: true + dependency.apt.gpg-agent: true + dependency.apt.intel-level-zero-gpu: ${LEVEL_ZERO_GPU_VER:-1.3.29735.27-914~22.04} + dependency.apt.intel-oneapi-runtime-ccl: ${CCL_VER:-2021.13.1-31} + dependency.apt.intel-oneapi-runtime-dpcpp-cpp: ${DPCPP_VER:-2024.2.1-1079} + dependency.apt.intel-oneapi-runtime-mkl: ${MKL_VER:-2024.2.1-103} + dependency.apt.intel-opencl-icd: ${ICD_VER:-23.43.27642.40-803~22.04} + dependency.apt.level-zero: ${LEVEL_ZERO_VER:-1.17.6-914~22.04} + dependency.apt.level-zero-dev: ${LEVEL_ZERO_DEV_VER:-1.17.6-914~22.04} + dependency.apt.rsync: true + dependency.apt.unzip: true + dependency.idp.pip: false + dependency.python.pip: requirements.txt + docs: jax + org.opencontainers.base.name: "intel/python:3.10-core" + org.opencontainers.image.name: "intel/intel-optimized-xla" + org.opencontainers.image.title: "Intel® Optimized XLA Base Image" + org.opencontainers.image.version: ${INTEL_XLA_VERSION:-v0.4.0}-${PACKAGE_OPTION:-pip}-base + target: jax-base + command: > + bash -c "python -c 'import jax; print(\"Jax Version:\", jax.__version__)'" + depends_on: + - ${PACKAGE_OPTION:-pip} + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-xla-${INTEL_XLA_VERSION:-v0.4.0}-base + pull_policy: always + jupyter: + build: + labels: + dependency.python.pip: jupyter-requirements.txt + org.opencontainers.base.name: "intel/intel-optimized-xla:${INTEL_XLA_VERSION:-v0.4.0}-base" + org.opencontainers.image.title: "Intel® Optimized XLA Jupyter Base Image" + org.opencontainers.image.version: ${INTEL_XLA_VERSION:-v0.4.0}-jupyter + target: jupyter + command: > + bash -c "python -m jupyter --version" + environment: + http_proxy: ${http_proxy} + https_proxy: ${https_proxy} + extends: jax-base + image: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-xla-${INTEL_XLA_VERSION:-v0.4.0}-jupyter + network_mode: host diff --git a/jax/jupyter-requirements.txt b/jax/jupyter-requirements.txt new file mode 100644 index 00000000..2cae0f91 --- /dev/null +++ b/jax/jupyter-requirements.txt @@ -0,0 +1,4 @@ +jupyterlab==4.2.4 +jupyterhub==5.1.0 +notebook==7.2.1 +jupyter-server-proxy>=4.1.2 diff --git a/jax/requirements.txt b/jax/requirements.txt new file mode 100644 index 00000000..09d7cb7f --- /dev/null +++ b/jax/requirements.txt @@ -0,0 +1,5 @@ +flax==0.8.2 +intel-extension-for-openxla==0.4.0 +jax==0.4.26 +jaxlib==0.4.26 +cython==3.0.11 diff --git a/jax/tests/example.py b/jax/tests/example.py new file mode 100644 index 00000000..9227d066 --- /dev/null +++ b/jax/tests/example.py @@ -0,0 +1,37 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: skip-file + +import jax +import jax.numpy as jnp + +print("jax.local_devices(): ", jax.local_devices()) + + +@jax.jit +def lax_conv(): + key = jax.random.PRNGKey(0) + lhs = jax.random.uniform(key, (2, 1, 9, 9), jnp.float32) + rhs = jax.random.uniform(key, (1, 1, 4, 4), jnp.float32) + side = jax.random.uniform(key, (1, 1, 1, 1), jnp.float32) + out = jax.lax.conv_with_general_padding( + lhs, rhs, (1, 1), ((0, 0), (0, 0)), (1, 1), (1, 1) + ) + out = jax.nn.relu(out) + out = jnp.multiply(out, side) + return out + + +print(lax_conv()) diff --git a/jax/tests/tests.yaml b/jax/tests/tests.yaml new file mode 100644 index 00000000..419dbf3f --- /dev/null +++ b/jax/tests/tests.yaml @@ -0,0 +1,29 @@ +# Copyright (c) 2024 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +jax-import-${PACKAGE_OPTION:-pip}: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-xla-${INTEL_XLA_VERSION:-v0.4.0}-base + cmd: python -c 'import jax; print("Jax Version:", jax.__version__); print(jax.devices())' + device: ["/dev/dri"] +jax-import-jupyter-${PACKAGE_OPTION:-pip}: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-xla-${INTEL_XLA_VERSION:-v0.4.0}-jupyter + cmd: sh -c "python -m jupyter --version" +jax-xpu-example-${PACKAGE_OPTION:-pip}: + img: ${REGISTRY}/${REPO}:b-${GITHUB_RUN_NUMBER:-0}-${BASE_IMAGE_NAME:-ubuntu}-${BASE_IMAGE_TAG:-22.04}-${PACKAGE_OPTION:-pip}-py${PYTHON_VERSION:-3.10}-xla-${INTEL_XLA_VERSION:-v0.4.0}-base + cmd: python /tests/example.py + device: ["/dev/dri"] + volumes: + - src: $PWD/jax/tests + dst: /tests From 536d75ad4d4efdf2b09cce45db84ab183ce5bc7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:29:40 +0000 Subject: [PATCH 54/56] Bump the pip group across 1 directory with 2 updates (#395) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- jax/jupyter-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jax/jupyter-requirements.txt b/jax/jupyter-requirements.txt index 2cae0f91..d98ce88b 100644 --- a/jax/jupyter-requirements.txt +++ b/jax/jupyter-requirements.txt @@ -1,4 +1,4 @@ -jupyterlab==4.2.4 +jupyterlab==4.2.5 jupyterhub==5.1.0 -notebook==7.2.1 +notebook==7.2.2 jupyter-server-proxy>=4.1.2 From 1424193882da821485017ef42398069d88ed576a Mon Sep 17 00:00:00 2001 From: Tyler Titsworth Date: Thu, 19 Sep 2024 13:05:32 -0700 Subject: [PATCH 55/56] Re-Enable Weekly Test CI (#394) Signed-off-by: tylertitsworth --- .github/workflows/weekly-test.yaml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/weekly-test.yaml b/.github/workflows/weekly-test.yaml index 388c021f..edbfb7a8 100644 --- a/.github/workflows/weekly-test.yaml +++ b/.github/workflows/weekly-test.yaml @@ -52,21 +52,21 @@ jobs: group_dir: ${{ matrix.group }} ref: main secrets: inherit - helm-ci: - runs-on: kubectl - steps: - - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 - with: - egress-policy: audit - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - fetch-depth: 0 - - uses: intel/ai-containers/workflows/charts@main - with: - config: '--all --namespace helm-ci' - list_changed: false - kubeconfig_path: ${{ secrets.KUBECONFIG_PATH }} + # helm-ci: + # runs-on: kubectl + # steps: + # - name: Harden Runner + # uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1 + # with: + # egress-policy: audit + # - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + # with: + # fetch-depth: 0 + # - uses: intel/ai-containers/workflows/charts@main + # with: + # config: '--all --namespace helm-ci' + # list_changed: false + # kubeconfig_path: ${{ secrets.KUBECONFIG_PATH }} scan: name: gitleaks runs-on: ${{ github.repository_owner == 'intel' && 'intel-ubuntu-latest' || 'ubuntu-latest' }} From 8cd96046a51efed3fb1135418ab27257b0eca3ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:48:27 +0000 Subject: [PATCH 56/56] Bump the gaudi-openshift group across 1 directory with 23 updates Updates the requirements on [matplotlib](https://github.com/matplotlib/matplotlib), [pandas](https://github.com/pandas-dev/pandas), [plotly](https://github.com/plotly/plotly.py), [scipy](https://github.com/scipy/scipy), [skl2onnx](https://github.com/onnx/sklearn-onnx), [codeflare-sdk](https://github.com/project-codeflare/codeflare-sdk), [pymongo](https://github.com/mongodb/mongo-python-driver), [psycopg](https://github.com/psycopg/psycopg), [mysql-connector-python](http://dev.mysql.com/doc/connector-python/en/index.html), [jupyterlab](https://github.com/jupyterlab/jupyterlab), [jupyter-bokeh](https://github.com/bokeh/jupyter_bokeh), [jupyter-server](https://github.com/jupyter-server/jupyter_server), [jupyter-server-proxy](https://github.com/jupyterhub/jupyter-server-proxy), [jupyterlab-git](https://github.com/jupyterlab/jupyterlab-git), [jupyterlab-lsp](https://github.com/jupyter-lsp/jupyterlab-lsp), [jupyterlab-widgets](https://github.com/jupyter-widgets/ipywidgets), [jupyter-resource-usage](https://github.com/jupyter-server/jupyter-resource-usage), [nbdime](https://github.com/jupyter/nbdime), [nbgitpuller](https://github.com/jupyterhub/nbgitpuller), [autopep8](https://github.com/hhatto/autopep8), [flake8](https://github.com/pycqa/flake8), [wheel](https://github.com/pypa/wheel) and [aiohttp](https://github.com/aio-libs/aiohttp) to permit the latest version. Updates `matplotlib` to 3.9.2 - [Release notes](https://github.com/matplotlib/matplotlib/releases) - [Commits](https://github.com/matplotlib/matplotlib/compare/v3.8.3...v3.9.2) Updates `pandas` to 2.2.3 - [Release notes](https://github.com/pandas-dev/pandas/releases) - [Commits](https://github.com/pandas-dev/pandas/compare/v2.2.0...v2.2.3) Updates `plotly` to 5.24.1 - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.20.0...v5.24.1) Updates `scipy` to 1.14.1 - [Release notes](https://github.com/scipy/scipy/releases) - [Commits](https://github.com/scipy/scipy/compare/v1.12.0...v1.14.1) Updates `skl2onnx` to 1.17.0 - [Release notes](https://github.com/onnx/sklearn-onnx/releases) - [Changelog](https://github.com/onnx/sklearn-onnx/blob/main/CHANGELOGS.md) - [Commits](https://github.com/onnx/sklearn-onnx/compare/1.16.0...1.17.0) Updates `codeflare-sdk` to 0.20.2 - [Release notes](https://github.com/project-codeflare/codeflare-sdk/releases) - [Commits](https://github.com/project-codeflare/codeflare-sdk/compare/v0.18.0...v0.20.2) Updates `pymongo` to 4.9.1 - [Release notes](https://github.com/mongodb/mongo-python-driver/releases) - [Changelog](https://github.com/mongodb/mongo-python-driver/blob/master/doc/changelog.rst) - [Commits](https://github.com/mongodb/mongo-python-driver/compare/4.6.2...4.9.1) Updates `psycopg` to 3.2.2 - [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst) - [Commits](https://github.com/psycopg/psycopg/compare/3.1.18...3.2.2) Updates `mysql-connector-python` to 9.0.0 Updates `jupyterlab` to 4.2.5 - [Release notes](https://github.com/jupyterlab/jupyterlab/releases) - [Changelog](https://github.com/jupyterlab/jupyterlab/blob/@jupyterlab/lsp@4.2.5/CHANGELOG.md) - [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/vdom@3.6.7...@jupyterlab/lsp@4.2.5) Updates `jupyter-bokeh` to 4.0.5 - [Commits](https://github.com/bokeh/jupyter_bokeh/compare/3.0.7...4.0.5) Updates `jupyter-server` to 2.14.2 - [Release notes](https://github.com/jupyter-server/jupyter_server/releases) - [Changelog](https://github.com/jupyter-server/jupyter_server/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyter-server/jupyter_server/compare/v2.14.1...v2.14.2) Updates `jupyter-server-proxy` to 4.4.0 - [Release notes](https://github.com/jupyterhub/jupyter-server-proxy/releases) - [Changelog](https://github.com/jupyterhub/jupyter-server-proxy/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/jupyter-server-proxy/compare/v4.2.0...v4.4.0) Updates `jupyterlab-git` to 0.50.1 - [Release notes](https://github.com/jupyterlab/jupyterlab-git/releases) - [Changelog](https://github.com/jupyterlab/jupyterlab-git/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterlab/jupyterlab-git/compare/v0.44.0...v0.50.1) Updates `jupyterlab-lsp` to 5.1.0 - [Release notes](https://github.com/jupyter-lsp/jupyterlab-lsp/releases) - [Changelog](https://github.com/jupyter-lsp/jupyterlab-lsp/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyter-lsp/jupyterlab-lsp/compare/v4.2.0...v5.1.0) Updates `jupyterlab-widgets` to 3.0.13 - [Release notes](https://github.com/jupyter-widgets/ipywidgets/releases) - [Commits](https://github.com/jupyter-widgets/ipywidgets/commits) Updates `jupyter-resource-usage` to 1.1.0 - [Release notes](https://github.com/jupyter-server/jupyter-resource-usage/releases) - [Changelog](https://github.com/jupyter-server/jupyter-resource-usage/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyter-server/jupyter-resource-usage/compare/v0.7.2...v1.1.0) Updates `nbdime` to 4.0.2 - [Release notes](https://github.com/jupyter/nbdime/releases) - [Changelog](https://github.com/jupyter/nbdime/blob/master/CHANGELOG.md) - [Commits](https://github.com/jupyter/nbdime/compare/3.2.1...nbdime@4.0.2) Updates `nbgitpuller` to 1.2.1 - [Changelog](https://github.com/jupyterhub/nbgitpuller/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nbgitpuller/compare/1.2.0...1.2.1) Updates `autopep8` to 2.3.1 - [Release notes](https://github.com/hhatto/autopep8/releases) - [Commits](https://github.com/hhatto/autopep8/compare/v2.0.4...v2.3.1) Updates `flake8` to 7.1.1 - [Commits](https://github.com/pycqa/flake8/compare/7.0.0...7.1.1) Updates `wheel` to 0.44.0 - [Release notes](https://github.com/pypa/wheel/releases) - [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst) - [Commits](https://github.com/pypa/wheel/compare/0.43.0...0.44.0) Updates `aiohttp` from 3.10.2 to 3.10.5 - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.2...v3.10.5) --- updated-dependencies: - dependency-name: matplotlib dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: pandas dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: plotly dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: scipy dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: skl2onnx dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: codeflare-sdk dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: pymongo dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: psycopg dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: mysql-connector-python dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyterlab dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyter-bokeh dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyter-server dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyter-server-proxy dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyterlab-git dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyterlab-lsp dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyterlab-widgets dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: jupyter-resource-usage dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: nbdime dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: nbgitpuller dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: autopep8 dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: flake8 dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: wheel dependency-type: direct:production dependency-group: gaudi-openshift - dependency-name: aiohttp dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gaudi-openshift ... Signed-off-by: dependabot[bot] --- .../gaudi/docker/requirements.txt | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt b/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt index 9d3a3ca7..e3140984 100644 --- a/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt +++ b/enterprise/redhat/openshift-ai/gaudi/docker/requirements.txt @@ -3,41 +3,41 @@ deepspeed @ git+https://github.com/HabanaAI/DeepSpeed.git@1.17.0 # Datascience and useful extensions kafka-python~=2.0.2 -matplotlib~=3.8.3 -pandas~=2.2.0 -plotly~=5.20.0 +matplotlib~=3.9.2 +pandas~=2.2.3 +plotly~=5.24.1 scikit-learn -scipy~=1.12.0 -skl2onnx~=1.16.0 -codeflare-sdk~=0.18.0 +scipy~=1.14.1 +skl2onnx~=1.17.0 +codeflare-sdk~=0.20.2 # DB connectors -pymongo~=4.6.2 -psycopg~=3.1.18 +pymongo~=4.9.1 +psycopg~=3.2.2 pyodbc~=5.1.0 -mysql-connector-python~=8.3.0 +mysql-connector-python~=9.0.0 # JupyterLab packages odh-elyra~=3.16.7 -jupyterlab~=3.6.7 # Wait on upgrade till plugins are ready -jupyter-bokeh~=3.0.7 # Upgrade would bring in jupyterlab 4 -jupyter-server~=2.14.1 -jupyter-server-proxy~=4.2.0 # Upgrade would bring in jupyterlab 4 +jupyterlab~=4.2.5 # Wait on upgrade till plugins are ready +jupyter-bokeh~=4.0.5 # Upgrade would bring in jupyterlab 4 +jupyter-server~=2.14.2 +jupyter-server-proxy~=4.4.0 # Upgrade would bring in jupyterlab 4 jupyter-server-terminals~=0.5.3 -jupyterlab-git~=0.44.0 -jupyterlab-lsp~=4.2.0 -jupyterlab-widgets~=3.0.10 -jupyter-resource-usage~=0.7.2 -nbdime~=3.2.1 -nbgitpuller~=1.2.0 +jupyterlab-git~=0.50.1 +jupyterlab-lsp~=5.1.0 +jupyterlab-widgets~=3.0.13 +jupyter-resource-usage~=1.1.0 +nbdime~=4.0.2 +nbgitpuller~=1.2.1 # pycodestyle is dependency of below packages # and to achieve compatible of pycodestyle with python-lsp-server[all] # pinned the below packages -autopep8~=2.0.4 -flake8~=7.0.0 +autopep8~=2.3.1 +flake8~=7.1.1 # Base packages -wheel~=0.43.0 +wheel~=0.44.0 setuptools>=70.0.0 pip>=23.3 -aiohttp==3.10.2 +aiohttp==3.10.5