构建沙箱环境
部署前准备
确保你的系统:
- 已经部署好了 Docker 环境。
- 已经部署好了 NodeJS 20.x+ 环境。
- 可选:代码片段与练习题使用 CPU 即可运行,但模型训练的内容需使用 GPU,需具备 NVIDIA GPU 且已经安装了 NVIDIA 驱动、满足 CUDA 12.8 GA 的驱动版本要求、磁盘空间等条件,具体为:
- 驱动版本要求:
- Linux >= 570.28.03
- Windows: >= 570.76
- 存储与内存:
- CPU 镜像约为 680 MB;GPU 镜像约 7.83 GB(其中 CUDA 官方镜像 >4GB,PyTorch GPU 版本 ≈3GB)。
- CPU 镜像内存上限为 4GB;GPU 镜像无内存上限,但模型训练通常至少需要 16GB 内存和 8GB 显存(具体评估见训练章节)。
- 宿主机还应预留20GB 以上空间,用于存放数据集、预处理缓存及模型 Checkpoint 等内容。
- 其他工具要求:
- Git LFS:模型训练/评估的数据集需使用 Git LFS 下载。
- 驱动版本要求:
- 其余依赖(如 Jupyter Notebook Kernel、Python、NumPy、PyTorch、CUDA 等)均通过 Docker 镜像自动提供,无需单独安装。
快速开始
本文档内包含大量的代码用于演示机器学习算法以及进行模型训练,因此部署一套沙箱环境用于练习是有必要的。
如果你使用的是互联网上部署的文档(https://ai.icyfenix.cn),可以在本地运行如下命令,使用
DMLA-CLI部署沙箱环境,让网站上的代码能够在你本地执行:npx @icyfenix-dmla/install@latest部署后,使用如下命令启动沙箱服务:
# 启动服务 dmla start # 默认端口 3001,自动选择镜像,CPU 优先 dmla start --port 3002 # 自定义端口 dmla start --gpu # GPU 模式 # 停止服务 dmla stop # 查看状态 dmla status # 安装镜像 dmla images # 环境诊断 dmla doctor # 数据管理 dmla data # 进入数据管理 TUI如果你使用的是源码部署(https://github.com/fenixsoft/dmla),除
DMLA-CLI外,也可以直接拉取或者编译 Docker 镜像:# 启动沙箱(需先在仓库根目录执行 npm install 安装依赖) npm run server # 启动文档服务和沙箱 npm run local # 拉取镜像 # 从 Docker Hub 拉取(全球用户),拉取后需重命名为本地镜像名 docker pull icyfenix/dmla-sandbox:gpu docker tag icyfenix/dmla-sandbox:gpu dmla-sandbox:gpu # 或从阿里云 ACR 拉取(国内加速) docker pull crpi-aani1ibpows293b8.cn-hangzhou.personal.cr.aliyuncs.com/fenixsoft/dmla-sandbox:gpu docker tag crpi-aani1ibpows293b8.cn-hangzhou.personal.cr.aliyuncs.com/fenixsoft/dmla-sandbox:gpu dmla-sandbox:gpu # 本地编译镜像 npm run build:sandbox:[cpu|gpu|all]
环境建议
对于文章内容的代码片段和课后练习题算法,只需纯 CPU 环境即可运行。在进入深度学习部分后,会出现专门的模型工程训练章节,它们需要有 GPU 异构计算环境的支持,当前 Docker 镜像使用的是 PyTorch with CUDA 12.8,支持 20/30/40/50 系列显卡,A100/A800/H100/H800 专业计算卡。如果你的硬件不在此范畴,需要自行下载代码,调整 PyTorch 版本后重新编译镜像(譬如 AMD 显卡要自己处理 PyTorch + ROCm)。
基于以下硬性限制,笔者建议在 Linux 宿主环境下使用 GPU 进行模型训练
- NVIDIA NVML 约束:本项目的模型训练虽不会直接调用 NVML 去调整 GPU 硬件参数,但会用到 DALI 等数据处理库,它们依赖 NVML 支持。DALI 本身仅支持 Linux 平台,Windows 上无法运行。即使在 WSL 2 中通过 NVIDIA Container Toolkit 获得 GPU 支持,NVML API 的可用性和功能完整性也有限,部分 API 仍不受支持。
- Docker SHM 约束:PyTorch 的
DataLoader在 Windows 上使用多进程数据加载(num_workers > 0)时会遇到共享内存问题。Linux Docker 容器通过/dev/shm提供共享内存,而 Windows Docker 容器不具备此机制。即使通过--shm-size手动设置,在 WSL 2 环境下运行 Linux 容器时,/dev/shm的行为也可能与原生 Linux 存在差异,导致 DataLoader 多进程模式不稳定。 - WSL 2 的跨宿主机 I/O 约束:数据管理中提到,训练数据和模型是存放在宿主机,通过 Volume Mount 到容器的,由于宿主机是 NTFS 磁盘格式,要通过 9P 协议翻译,会带来大量 I/O 损耗。
尽管如此,本文档仍确保代码可在 Windows / Linux 环境下正常运行(功能完整,性能有差距)。macOS 或非 NVIDIA 硬件环境(如昇腾)可能需要额外适配。
如本地硬件不满足要求,可考虑通过云服务商的 GPU 异构计算服务,以按用量付费方式部署沙箱来完成练习(按 GeForce RTX 3090 GPU 约 2 元 / 小时计费(价格随云服务商和时段浮动),完成所有模型训练预计花费在十元以内)。沙箱环境默认为
http://localhost:3001,如果你选择了其他端口或者非本机的沙箱(譬如云服务),请点击文档右上角设置图标 手动填入沙箱地址。
安全提示
由于沙箱的功能是从外部接收并执行 Python 代码,安全防护主要依赖 Docker 容器的 Linux 命名空间隔离(PID、网络、文件系统等)、cgroups 资源限制以及默认的 seccomp 系统调用过滤,将沙箱服务直接暴露在公网环境可能会带来安全风险。建议你优先考虑将沙箱运行于本地或者无敏感数据的云服务中。
数据管理
为便于管理实验数据、复用训练记录,本项目提供数据持久化功能,支持自动/手动数据集下载、模型保存等。数据目录可通过 dmla data 自定义,如未设置默认为宿主机的 ~/dmla-data 目录。以下为该目录的完整数据结构(目录均会按需自动创建,无需手动处理):
~/dmla-data/
├── datasets/ # 数据集目录
│ ├── tiny-imagenet-200/ # Tiny ImageNet-200
│ ├── cifar-10/ # CIFAR-10
│ ├── cifar-100/ # CIFAR-100
│ ├── mnist/ # MNIST
│ └── custom/ # 用户自定义数据集
│ │ …
│
├── models/ # 模型目录
│ ├── alexnet/ # AlexNet 相关模型
│ │ ├── checkpoints/ # 训练中间 checkpoint
│ │ └── final/ # 最终模型
│ ├── vgg/ # VGG 系列模型
│ ├── resnet/ # ResNet 系列模型
│ ├── gan/ # GAN 模型
│ ├── llm/ # 大语言模型
│ └── pretrained/ # 预训练模型下载
│ │ …
│
├── outputs/ # 输出目录
│ ├── training_logs/ # 训练日志
│ ├── visualizations/ # 可视化结果
│ └── exports/ # 导出文件 (ONNX等)
│
└── cache/ # 缓存目录
├── downloads/ # 数据集下载临时文件
├── preprocessing/ # 预处理缓存
└── torch_hub/ # torch hub 缓存
检查环境
使用以下示例代码检查沙箱环境是否可用。代码可编辑,点击 Run 或 Run on GPU 按钮即可运行:
import importlib
# 检查沙箱环境中的 Python 包
required_packages = {
# 基础库
'numpy': 'NumPy',
'pandas': 'Pandas',
'matplotlib': 'Matplotlib',
'scipy': 'SciPy',
'sklearn': 'scikit-learn',
'requests': 'Requests',
'PIL': 'Pillow',
'cv2': 'OpenCV',
'lmdb': 'LMDB',
# PyTorch
'torch': 'PyTorch',
'torchvision': 'TorchVision',
'torchaudio': 'TorchAudio',
# HuggingFace
'transformers': 'HuggingFace Transformers',
'tokenizers': 'HuggingFace Tokenizers',
'datasets': 'HuggingFace Datasets',
# Jupyter
'ipykernel': 'IPyKernel',
'jupyter_client': 'Jupyter Client',
'ipywidgets': 'IPyWidgets',
}
print("=== Python 包检查 ===")
for pkg, desc in required_packages.items():
try:
mod = importlib.import_module(pkg)
version = getattr(mod, '__version__', '内置')
print(f" ✅ {pkg:20s} {desc:30s} {version}")
except ModuleNotFoundError:
print(f" ❌ {pkg:20s} {desc:30s} 未安装")
# 检查 Python 版本和运行模式
import sys
import os
print(f"\nPython: {sys.version}")
print(f"运行模式: {'Docker' if os.path.exists('/.dockerenv') else 'Native'}")
print(f"DATA_DIR: {DATA_DIR}")
# 检查 shared 包位置和内容
print(f"\n=== Shared 包检查 ===")
is_docker = os.path.exists('/.dockerenv')
if is_docker:
# Docker 模式:shared 包位于镜像内置路径或 Volume Mount 路径
shared_candidates = [
('/workspace/shared', 'Volume Mount(开发模式挂载)'),
('/usr/local/lib/python{sys.version_info.major}.{sys.version_info.minor}/site-packages/shared', '镜像内置'),
]
else:
# Native 模式:从 PYTHONPATH 中查找
python_paths = os.environ.get('PYTHONPATH', '').split(os.pathsep)
shared_candidates = []
for p in python_paths:
candidate = os.path.join(p, 'shared')
shared_candidates.append((candidate, f'PYTHONPATH: {p}'))
shared_found = False
for shared_path, source in shared_candidates:
if os.path.isdir(shared_path):
print(f" ✅ Shared 包路径: {shared_path}")
print(f" 来源: {source}")
shared_found = True
# 列出 shared 包中的子模块
submodules = sorted([
d for d in os.listdir(shared_path)
if os.path.isdir(os.path.join(shared_path, d))
and not d.startswith('_')
and os.path.exists(os.path.join(shared_path, d, '__init__.py'))
])
if submodules:
print(f" 子模块: {', '.join(submodules)}")
# 列出每个子模块中的类
for mod in submodules:
mod_path = os.path.join(shared_path, mod)
classes = sorted([
f[:-3] for f in os.listdir(mod_path)
if f.endswith('.py') and f != '__init__.py'
])
if classes:
print(f" {mod}: {', '.join(classes)}")
break
if not shared_found:
print(f" ⚠️ 未找到 shared 包(部分章节的代码将无法复用类定义)")
# 检查硬件信息
import multiprocessing
import torch
print("\n=== 硬件信息 ===")
print(f"CPU 核心数: {multiprocessing.cpu_count()}")
try:
with open('/proc/meminfo') as f:
for line in f:
if line.startswith('MemTotal:'):
mem_gb = int(line.split()[1]) / 1024 / 1024
print(f"内存: {mem_gb:.1f} GB")
break
except Exception:
pass
if torch.cuda.is_available():
print(f"\n=== GPU 信息 ===")
print(f"CUDA 版本: {torch.version.cuda}")
for i in range(torch.cuda.device_count()):
props = torch.cuda.get_device_properties(i)
print(f"GPU {i}: {props.name}")
print(f" 显存: {props.total_mem / 1024**3:.1f} GB")
print(f" 计算能力: {props.major}.{props.minor}")
else:
print("GPU: 不可用(当前为 CPU 模式)")
