Files
nvidia-nemo--speech/nemo/utils/cloud.py
T
wehub-resource-sync ba4be087d5
Create PR to main with cherry-pick from release / cherry-pick (push) Failing after 0s
CICD NeMo / pre-flight (push) Failing after 0s
CICD NeMo / configure (push) Has been skipped
Build, validate, and release Neural Modules / pre-flight (push) Failing after 1s
CICD NeMo / code-linting (push) Has been skipped
Build, validate, and release Neural Modules / release (push) Has been skipped
Build, validate, and release Neural Modules / release-summary (push) Has been cancelled
CICD NeMo / cicd-test-container-build (push) Has been cancelled
CICD NeMo / cicd-import-tests (push) Has been cancelled
CICD NeMo / L0_Setup_Test_Data_And_Models (push) Has been cancelled
CICD NeMo / cicd-main-unit-tests (push) Has been cancelled
CICD NeMo / cicd-main-speech (push) Has been cancelled
CICD NeMo / Nemo_CICD_Test (push) Has been cancelled
CICD NeMo / Coverage (e2e) (push) Has been cancelled
CICD NeMo / Coverage (unit-test) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
CICD NeMo / cicd-wait-in-queue (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:58 +08:00

179 lines
6.6 KiB
Python

# Copyright (c) 2020, NVIDIA CORPORATION. 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.
import os
import urllib.request
from pathlib import Path
from time import sleep
from lightning.pytorch.plugins.environments import LightningEnvironment
from lightning.pytorch.strategies import DDPStrategy, StrategyRegistry
from nemo.utils import logging
def maybe_download_from_cloud(url, filename, subfolder=None, cache_dir=None, refresh_cache=False) -> str:
"""
Helper function to download pre-trained weights from the cloud
Args:
url: (str) URL of storage
filename: (str) what to download. The request will be issued to url/filename
subfolder: (str) subfolder within cache_dir. The file will be stored in cache_dir/subfolder. Subfolder can
be empty
cache_dir: (str) a cache directory where to download. If not present, this function will attempt to create it.
If None (default), then it will be $HOME/.cache/torch/NeMo
refresh_cache: (bool) if True and cached file is present, it will delete it and re-fetch
Returns:
If successful - absolute local path to the downloaded file
else - empty string
"""
# try:
if cache_dir is None:
cache_location = Path.joinpath(Path.home(), ".cache/torch/NeMo")
else:
cache_location = cache_dir
if subfolder is not None:
destination = Path.joinpath(cache_location, subfolder)
else:
destination = cache_location
if not os.path.exists(destination):
os.makedirs(destination, exist_ok=True)
destination_file = Path.joinpath(destination, filename)
if os.path.exists(destination_file):
logging.info(f"Found existing object {destination_file}.")
if refresh_cache:
logging.info("Asked to refresh the cache.")
logging.info(f"Deleting file: {destination_file}")
os.remove(destination_file)
else:
logging.info(f"Re-using file from: {destination_file}")
return str(destination_file)
# download file
wget_uri = url + filename
logging.info(f"Downloading from: {wget_uri} to {str(destination_file)}")
# NGC links do not work everytime so we try and wait
i = 0
max_attempts = 3
while i < max_attempts:
i += 1
try:
urllib.request.urlretrieve(wget_uri, str(destination_file))
if os.path.exists(destination_file):
return destination_file
else:
return ""
except:
logging.info(f"Download from cloud failed. Attempt {i} of {max_attempts}")
sleep(0.05)
continue
raise ValueError("Not able to download url right now, please try again.")
class SageMakerDDPStrategy(DDPStrategy):
"""DDP strategy configured for AWS SageMaker distributed training."""
@property
def cluster_environment(self):
"""Return a LightningEnvironment configured from SageMaker environment variables."""
env = LightningEnvironment()
env.world_size = lambda: int(os.environ["WORLD_SIZE"])
env.global_rank = lambda: int(os.environ["RANK"])
return env
@cluster_environment.setter
def cluster_environment(self, env):
"""No-op setter to prevent Lightning from overriding the SageMaker environment."""
pass
def initialize_sagemaker() -> None:
"""
Helper function to initiate sagemaker with NeMo.
This function installs libraries that NeMo requires for the ASR toolkit + initializes sagemaker ddp.
"""
logging.info("Registering SageMaker DDP strategy 'smddp'.")
StrategyRegistry.register(
name='smddp',
strategy=SageMakerDDPStrategy,
process_group_backend="smddp",
find_unused_parameters=False,
)
def _install_system_libraries() -> None:
import subprocess
logging.info("Installing system libraries: libsndfile1, ffmpeg")
try:
logging.info("Running apt-get update")
subprocess.run(
["apt-get", "update"],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
logging.info("Running apt-get install for libsndfile1 and ffmpeg")
subprocess.run(
["apt-get", "install", "-y", "libsndfile1", "ffmpeg"],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError as e:
logging.error(
"Failed to install system libraries via apt-get (command=%s, returncode=%s): %s",
getattr(e, "cmd", None),
getattr(e, "returncode", None),
e,
)
logging.info("System libraries installed successfully.")
except Exception as e:
logging.error(f"Failed to install system libraries: {e}")
def _patch_torch_metrics() -> None:
"""
Patches torchmetrics to not rely on internal state.
This is because sagemaker DDP overrides the `__init__` function of the modules to do automatic-partitioning.
"""
from torchmetrics import Metric
def __new_hash__(self):
hash_vals = [self.__class__.__name__, id(self)]
return hash(tuple(hash_vals))
Metric.__hash__ = __new_hash__
logging.info("Patching torchmetrics hash function for SageMaker compatibility.")
_patch_torch_metrics()
if os.environ.get("RANK") and os.environ.get("WORLD_SIZE"):
import smdistributed.dataparallel.torch.distributed as dist
# has to be imported, as it overrides torch modules and such when DDP is enabled.
import smdistributed.dataparallel.torch.torch_smddp # noqa: F401
logging.info("Initializing SageMaker distributed process group.")
dist.init_process_group()
if dist.get_local_rank():
_install_system_libraries()
logging.info("Waiting at barrier for all processes.")
return dist.barrier() # wait for main process
_install_system_libraries()
return