x
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
# =============================================================================
|
||||
# MinerU on ROCm 7.2.1 Docker Image
|
||||
# 原生 Linux + Ubuntu 24.04 + ROCm 7.2.1 + PyTorch 2.11.0 + vllm + MinerU 3.2.0
|
||||
#
|
||||
# 构建前请根据你的 GPU 修改 ARCH 参数(默认 gfx1201 = RX 9070)
|
||||
# =============================================================================
|
||||
|
||||
FROM ubuntu:24.04
|
||||
|
||||
# -- 构建参数 ---------------------------------------------------------------
|
||||
# 改成你的 gfx 代号:gfx1201(RX 9070) gfx1200(RX 9060) gfx1100(RX 7900) gfx1101(RX 7800/7700) gfx1030(RX 6900/6800)
|
||||
ARG ARCH=gfx1201
|
||||
ARG PYTHON_VER=3.12
|
||||
ARG VENV=/opt/mineru_venv
|
||||
ARG TORCH_INDEX=https://download.pytorch.org/whl/rocm7.2
|
||||
|
||||
# -- 环境变量 ---------------------------------------------------------------
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
PATH=/opt/rocm/bin:/opt/rocm/llvm/bin:${VENV}/bin:${PATH} \
|
||||
PYTORCH_ROCM_ARCH=${ARCH} \
|
||||
FLASH_ATTENTION_TRITON_AMD_ENABLE=TRUE \
|
||||
MINERU_MODEL_SOURCE=huggingface \
|
||||
TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1 \
|
||||
HSA_ENABLE_SDMA=1 \
|
||||
VLLM_TARGET_DEVICE=rocm
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 1:安装 ROCm 7.2.1
|
||||
# ===========================================================================
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
wget curl ca-certificates gnupg software-properties-common && \
|
||||
# 添加 AMD ROCm 仓库
|
||||
wget -q https://repo.radeon.com/rocm/rocm.gpg.key -O - | \
|
||||
gpg --dearmor | tee /etc/apt/trusted.gpg.d/rocm.gpg > /dev/null && \
|
||||
echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/7.2.1 noble main' \
|
||||
> /etc/apt/sources.list.d/rocm.list && \
|
||||
apt-get update && \
|
||||
# 安装 ROCm 基础组件
|
||||
apt-get install -y --no-install-recommends \
|
||||
rocminfo hip-dev miopen-hip && \
|
||||
# 修复 rocminfo / rocm-device-libs 版本(替换 Ubuntu 自带旧版)
|
||||
apt-get install -y --allow-downgrades \
|
||||
rocminfo=1.0.0.70201-38~24.04 \
|
||||
rocm-device-libs=1.0.0.70201-38~24.04 && \
|
||||
# 清理
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 2:ROCm 头文件补丁(LLVM 22 兼容性修复)
|
||||
# 这些是 ROCm 7.2.1 在 24.04 上的已知问题,每次 apt 升级 ROCm 后需重新应用
|
||||
# ===========================================================================
|
||||
RUN set -ex && \
|
||||
# 补丁 1: hipcc/clang 符号链接(hipcc.pl 硬编码 clang-17,实际是 clang-22)
|
||||
ln -sf /usr/bin/hipvars.pm /usr/share/perl5/hipvars.pm && \
|
||||
ln -sf /usr/bin/hipcc.pl /opt/rocm/bin/hipcc && \
|
||||
ln -sf /opt/rocm/llvm/bin/clang-22 /opt/rocm/llvm/bin/clang-17 && \
|
||||
ln -sf /opt/rocm/llvm/bin/clang++ /opt/rocm/llvm/bin/clang++-17 && \
|
||||
# 补丁 2: __hip_internal::conditional → std::conditional
|
||||
find /opt/rocm/include/hip -name "*.h" \
|
||||
-exec sed -i 's/__hip_internal::conditional/std::conditional/g' {} + && \
|
||||
# 补丁 3: warpSize 常量(__AMDGCN_WAVEFRONT_SIZE 在 LLVM 22 未定义)
|
||||
find /opt/rocm/include/hip -name "amd_warp_functions.h" \
|
||||
-exec sed -i 's/static constexpr int warpSize = __AMDGCN_WAVEFRONT_SIZE;/constexpr int warpSize = 32;/g' {} + && \
|
||||
# 补丁 4: __activemask() → __builtin_amdgcn_read_exec()
|
||||
# 注意:只改 amd_warp_sync_functions.h,不要动 amd_warp_functions.h(那是定义本身)
|
||||
sed -i 's/__activemask()/__builtin_amdgcn_read_exec()/g' \
|
||||
/opt/rocm/include/hip/amd_detail/amd_warp_sync_functions.h && \
|
||||
echo "ROCm 7.2.1 header patches applied."
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 3:系统依赖 + Python 3.12
|
||||
# ===========================================================================
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential git ninja-build pkg-config \
|
||||
python${PYTHON_VER} python${PYTHON_VER}-venv python${PYTHON_VER}-dev \
|
||||
libnuma-dev libdrm2 libhwloc-dev libgl1 \
|
||||
# vllm 运行时依赖
|
||||
libgomp1 libopenblas0 && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 4:CMake 4.0(vllm 要求 ≥ 4.0,Ubuntu 24.04 自带 3.28 不够)
|
||||
# ===========================================================================
|
||||
RUN cd /tmp && \
|
||||
wget -q https://github.com/Kitware/CMake/releases/download/v4.0.0/cmake-4.0.0-linux-x86_64.tar.gz && \
|
||||
tar -xzf cmake-4.0.0-linux-x86_64.tar.gz && \
|
||||
cp -r cmake-4.0.0-linux-x86_64/bin/* /usr/local/bin/ && \
|
||||
cp -r cmake-4.0.0-linux-x86_64/share/* /usr/local/share/ && \
|
||||
rm -rf cmake-4.0.0-linux-x86_64* && \
|
||||
cmake --version
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 5:Python 虚拟环境 + PyTorch ROCm
|
||||
# ===========================================================================
|
||||
RUN python${PYTHON_VER} -m venv ${VENV} && \
|
||||
${VENV}/bin/pip install --no-cache-dir -U pip setuptools wheel && \
|
||||
# 安装 PyTorch ROCm 版(锁定 2.11,≥ 2.12 在部分环境下有 rocprofiler 问题)
|
||||
${VENV}/bin/pip install --no-cache-dir --pre \
|
||||
torch==2.11.0+rocm7.2 \
|
||||
torchvision \
|
||||
pytorch-triton-rocm \
|
||||
--index-url ${TORCH_INDEX} && \
|
||||
# 验证
|
||||
${VENV}/bin/python -c "import torch; print('PyTorch:', torch.__version__); print('ROCm:', torch.version.hip); assert torch.version.hip is not None"
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 6:ROCm 开发包(vllm 编译必需)
|
||||
# ===========================================================================
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
hipblas-dev hiprand-dev hipsparse-dev hipsparselt-dev \
|
||||
hipsolver-dev hipcub-dev rocprim-dev rocthrust-dev \
|
||||
rocblas-dev rocrand-dev hipfft-dev hipblaslt && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 7:amd-aiter + flash_attn
|
||||
# ===========================================================================
|
||||
RUN set -ex && \
|
||||
# aiter(AMD 优化的 attention 算子)
|
||||
cd /opt && git clone --recursive --depth 1 https://github.com/ROCm/aiter.git && \
|
||||
${VENV}/bin/pip install --no-cache-dir -e /opt/aiter && \
|
||||
# flash_attn(Triton AMD 后端,锁定已验证的 commit)
|
||||
cd /opt && git clone --recursive https://github.com/Dao-AILab/flash-attention.git && \
|
||||
cd flash-attention && git checkout bba578d43974c1d3ba157ab597124dd0fe2ccdb4 && \
|
||||
${VENV}/bin/pip install --no-cache-dir --no-build-isolation -e /opt/flash-attention && \
|
||||
# 验证 PyTorch 没被覆盖
|
||||
${VENV}/bin/python -c "import torch; v=torch.__version__; assert 'rocm' in v, f'PyTorch overwritten: {v}'; print('PyTorch OK:', v)"
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 8:编译 vllm
|
||||
# ===========================================================================
|
||||
RUN set -ex && \
|
||||
# setuptools 升级(PEP 639 兼容)
|
||||
${VENV}/bin/pip install --no-cache-dir -U \
|
||||
"setuptools>=77.0.3" setuptools_scm setuptools_rust wheel && \
|
||||
# 克隆 vllm main
|
||||
cd /opt && git clone --depth 1 https://github.com/vllm-project/vllm.git && \
|
||||
# 补丁 5:注释掉 vllm mamba 模块的 operator+ 定义(ROCm 7.2 头文件已自带)
|
||||
cd /opt/vllm && \
|
||||
sed -i '109,121s/^/\/\/ /' csrc/mamba/mamba_ssm/selective_scan.h && \
|
||||
echo "vllm mamba operator+ patch applied." && \
|
||||
# cmake 配置
|
||||
mkdir -p /opt/vllm_build && \
|
||||
cmake -S /opt/vllm -B /opt/vllm_build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DVLLM_TARGET_DEVICE=rocm \
|
||||
-DVLLM_PYTHON_EXECUTABLE=${VENV}/bin/python \
|
||||
-DHIP_ROOT_DIR=/opt/rocm \
|
||||
-DROCM_PATH=/opt/rocm \
|
||||
-DCMAKE_HIP_ARCHITECTURES=${ARCH} \
|
||||
-DCMAKE_PREFIX_PATH="${VENV}/lib/python${PYTHON_VER}/site-packages/torch/share/cmake" && \
|
||||
# ninja 编译(-j4 防 OOM,内存 > 32GB 可调高)
|
||||
cd /opt/vllm_build && ninja -j4 && \
|
||||
# 安装 .so 到 vllm 源码目录
|
||||
cp /opt/vllm_build/*.abi3.so /opt/vllm/vllm/ && \
|
||||
# pip install vllm(让 pip 解析运行时依赖:xgrammar, compressed_tensors 等)
|
||||
cd /opt/vllm && ${VENV}/bin/pip install --no-cache-dir -e . --no-build-isolation && \
|
||||
# 验证 PyTorch 没被 vllm 依赖覆盖
|
||||
${VENV}/bin/python -c "import torch; v=torch.__version__; assert 'rocm' in v, f'PyTorch overwritten by vllm deps: {v}'; print('PyTorch OK:', v)" && \
|
||||
# 清理可能的 CUDA triton 残余
|
||||
${VENV}/bin/pip uninstall -y triton triton-rocm 2>/dev/null; \
|
||||
${VENV}/bin/pip install --no-cache-dir --force-reinstall \
|
||||
torch==2.11.0+rocm7.2 torchvision pytorch-triton-rocm \
|
||||
--index-url ${TORCH_INDEX} && \
|
||||
# 最终验证 vllm 平台检测
|
||||
${VENV}/bin/python -c "
|
||||
from vllm.platforms import current_platform
|
||||
print('Platform:', type(current_platform).__name__)
|
||||
print('is_rocm:', current_platform.is_rocm())
|
||||
print('device_type:', current_platform.device_type)
|
||||
assert current_platform.is_rocm(), 'vllm ROCm detection failed!'
|
||||
print('vllm OK')
|
||||
" && \
|
||||
# 清理构建目录(减小镜像体积,约 3-5GB)
|
||||
rm -rf /opt/vllm_build
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 9:安装 MinerU + RDNA 适配补丁
|
||||
# ===========================================================================
|
||||
RUN set -ex && \
|
||||
${VENV}/bin/pip install --no-cache-dir 'mineru[core]' && \
|
||||
# 验证 PyTorch 没被覆盖
|
||||
${VENV}/bin/python -c "import torch; v=torch.__version__; assert 'rocm' in v, f'PyTorch overwritten: {v}'; print('PyTorch OK:', v)" && \
|
||||
# 定位 mineru infer 目录
|
||||
MINERU_INFER_DIR=$(${VENV}/bin/python -c "import mineru.model.utils.tools.infer; import os; print(os.path.dirname(mineru.model.utils.tools.infer.__file__))") && \
|
||||
echo "MinerU infer dir: ${MINERU_INFER_DIR}" && \
|
||||
# --- Patch A: predict_rec.py imgW 对齐到 32 ---
|
||||
${VENV}/bin/python -c "
|
||||
import re
|
||||
f = '${MINERU_INFER_DIR}/predict_rec.py'
|
||||
c = open(f).read()
|
||||
# 在 imgW = max(min(... 之后插入 imgW = math.ceil(imgW / 32) * 32
|
||||
old = '(imgW = max\(min\(imgW, self\.limited_max_width\), self\.limited_min_width\)\n)'
|
||||
new = r'\1 imgW = math.ceil(imgW / 32) * 32\n'
|
||||
c2 = re.sub(old, new, c)
|
||||
if c2 == c:
|
||||
# 尝试找已经插入过的情况
|
||||
if 'math.ceil(imgW / 32)' not in c:
|
||||
raise RuntimeError('Patch A: cannot find imgW line in predict_rec.py')
|
||||
else:
|
||||
print('Patch A: already applied')
|
||||
else:
|
||||
open(f, 'w').write(c2)
|
||||
print('Patch A: imgW 32-align inserted')
|
||||
" && \
|
||||
# --- Patch B: predict_rec.py 批次填充 ---
|
||||
${VENV}/bin/python -c "
|
||||
f = '${MINERU_INFER_DIR}/predict_rec.py'
|
||||
c = open(f).read()
|
||||
# 在 norm_img_batch = np.concatenate(norm_img_batch) 前插入 padding 逻辑
|
||||
old = '( {8}norm_img_batch = np\.concatenate\(norm_img_batch\))'
|
||||
new = ''' actual_batch_size = len(norm_img_batch)
|
||||
if actual_batch_size < batch_num:
|
||||
pad_size = batch_num - actual_batch_size
|
||||
pad_img = np.zeros_like(norm_img_batch[0])
|
||||
for _ in range(pad_size):
|
||||
norm_img_batch.append(pad_img)
|
||||
\\1'''
|
||||
import re
|
||||
c2 = re.sub(old, new, c)
|
||||
if c2 == c:
|
||||
if 'actual_batch_size' not in c:
|
||||
raise RuntimeError('Patch B: cannot find norm_img_batch concatenation')
|
||||
else:
|
||||
print('Patch B: already applied')
|
||||
else:
|
||||
open(f, 'w').write(c2)
|
||||
print('Patch B: batch padding inserted')
|
||||
# 修改 range(len(rec_result)) → range(actual_batch_size)
|
||||
c3 = open(f).read()
|
||||
c4 = re.sub(r'for rno in range\(len\(rec_result\)\):', ' for rno in range(actual_batch_size):', c3)
|
||||
open(f, 'w').write(c4)
|
||||
" && \
|
||||
# --- Patch C: predict_det.py contiguous 检查 ---
|
||||
${VENV}/bin/python -c "
|
||||
f = '${MINERU_INFER_DIR}/predict_det.py'
|
||||
c = open(f).read()
|
||||
old = '( {8}inp = inp\.to\(self\.device\)\n)'
|
||||
new = r'\1 if not inp.is_contiguous():\n inp = inp.contiguous()\n'
|
||||
import re
|
||||
c2 = re.sub(old, new, c)
|
||||
if c2 == c:
|
||||
if 'is_contiguous' not in c:
|
||||
raise RuntimeError('Patch C: cannot find inp.to(device) line')
|
||||
else:
|
||||
print('Patch C: already applied')
|
||||
else:
|
||||
open(f, 'w').write(c2)
|
||||
print('Patch C: contiguous check inserted')
|
||||
" && \
|
||||
echo "All MinerU RDNA patches applied."
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 10:MIOpen 预热脚本
|
||||
# ===========================================================================
|
||||
COPY scripts/cache_warmer.py /opt/cache_warmer.py
|
||||
|
||||
# ===========================================================================
|
||||
# 阶段 11:入口与最终设置
|
||||
# ===========================================================================
|
||||
RUN echo 'source /opt/mineru_venv/bin/activate' >> /etc/bash.bashrc && \
|
||||
echo "MinerU Docker image built successfully." && \
|
||||
${VENV}/bin/python -c "
|
||||
import torch, vllm, mineru
|
||||
print('='*50)
|
||||
print('MinerU ROCm Docker Image Ready')
|
||||
print(f' PyTorch : {torch.__version__}')
|
||||
print(f' ROCm : {torch.version.hip}')
|
||||
print(f' vllm : {vllm.__version__}')
|
||||
print(f' MinerU : {mineru.__version__}')
|
||||
print(f' Arch : ${ARCH}')
|
||||
print('='*50)
|
||||
"
|
||||
|
||||
# 容器入口:默认 bash,用户可 override
|
||||
ENTRYPOINT ["/bin/bash", "-c"]
|
||||
CMD ["bash"]
|
||||
@@ -0,0 +1,442 @@
|
||||
# MinerU AMD GPU Docker 部署指南
|
||||
|
||||
> **Ubuntu 24.04 + ROCm 7.2.1 + PyTorch 2.11.0+rocm7.2 + vllm main + MinerU 3.2.0**
|
||||
> 面向原生 Linux,通过 Docker 容器化一键部署
|
||||
> 实测通过:RX 9070 (gfx1201),其他 RDNA2/3/4 显卡按相同流程套用
|
||||
|
||||
---
|
||||
|
||||
## 0. 为什么用 Docker
|
||||
|
||||
| 方案 | 适用场景 |
|
||||
|------|---------|
|
||||
| **裸机部署**([MinerU本地部署教程.md](MinerU本地部署教程.md)) | 单机开发、追求极致性能 |
|
||||
| **Docker 部署**(本文) | 团队共享、CI/CD、环境隔离、快速迁移 |
|
||||
|
||||
Docker 方案的优势:
|
||||
- 宿主机只需安装 ROCm 内核驱动 + Docker,不需要污染系统 Python/库
|
||||
- 镜像一次构建,多机复用
|
||||
- 模型和 MIOpen 缓存通过卷挂载持久化,容器重建不丢失
|
||||
- 支持 CLI / WebUI / API 三种运行模式
|
||||
|
||||
**与 WSL2 教程的关键区别(原生 Linux 用户看这里)**:
|
||||
|
||||
| | WSL2 教程 | 本 Docker 文档 |
|
||||
|:--|:--|:--|
|
||||
| librocdxg 编译 | 必需 | **不需要**(原生 KFD 驱动) |
|
||||
| Windows SDK | 必需 | **不需要** |
|
||||
| vllm 平台检测补丁(9.10 节) | 必需 | **不需要**(amdsmi 原生可用) |
|
||||
| Hyper-V / 镜像网络 / DNS | 必需 | **不需要**(Docker 网络独立) |
|
||||
| ROCm 头文件补丁 | 5 个 | **5 个**(Dockerfile 自动应用) |
|
||||
| MinerU RDNA 补丁 | 3 个 | **3 个**(Dockerfile 自动应用) |
|
||||
|
||||
---
|
||||
|
||||
## 1. 宿主机要求
|
||||
|
||||
### 1.1 硬件
|
||||
|
||||
| 项目 | 最低要求 | 推荐 |
|
||||
|------|---------|------|
|
||||
| GPU | AMD RDNA2/3/4 独显 | RX 7900 / RX 9070 / RX 7800 等 |
|
||||
| 显存 | 8 GB | 16 GB+ |
|
||||
| 内存 | 16 GB | 32 GB+ |
|
||||
| 磁盘 | 50 GB | 100 GB+ (SSD) |
|
||||
|
||||
### 1.2 软件
|
||||
|
||||
| 组件 | 版本 | 说明 |
|
||||
|------|------|------|
|
||||
| 操作系统 | Ubuntu 24.04 (noble) | 也支持 22.04 (jammy),但需改用 ROCm 7.1.1 |
|
||||
| ROCm 内核驱动 | 7.2.x | `amdgpu-dkms` + `rocm-dkms`,容器**共享宿主机内核驱动** |
|
||||
| Docker | ≥ 24.0 | 需要 GPU 设备透传能力 |
|
||||
| Docker Compose | ≥ 2.0 | 可选,简化容器管理 |
|
||||
|
||||
### 1.3 显卡兼容性
|
||||
|
||||
查自己的 gfx 代号:
|
||||
|
||||
```bash
|
||||
rocminfo | grep gfx
|
||||
```
|
||||
|
||||
| 显卡 | gfx 代号 | 编译参数 | 状态 |
|
||||
|------|---------|---------|:--:|
|
||||
| RX 9070 XT / 9070 / 9070 GRE | gfx1201 | `ARCH=gfx1201` | 实测通过 |
|
||||
| RX 9060 XT / 9060 XT LP | gfx1200 | `ARCH=gfx1200` | ROCm 7.2 起正式支持 |
|
||||
| RX 7900 XTX / XT / GRE | gfx1100 | `ARCH=gfx1100` | 原生支持 |
|
||||
| RX 7800 XT / 7700 XT | gfx1101 | `ARCH=gfx1101` | ROCm 较新版原生支持 |
|
||||
| RX 7600 XT / 7600 | gfx1102 | `ARCH=gfx1102` | vllm 支持,可能需伪装 |
|
||||
| RX 6950 / 6900 / 6800 XT / 6800 | gfx1030 | `ARCH=gfx1030` | 预期可用 |
|
||||
| RX 6750 XT / 6700 XT | gfx1031 | `ARCH=gfx1030` | 伪装编译 |
|
||||
|
||||
不支持的:RDNA1 (gfx1010/gfx1012)、Navi 23 (gfx1032/gfx1034)、APU 核显。
|
||||
|
||||
---
|
||||
|
||||
## 2. 宿主机准备
|
||||
|
||||
### 2.1 安装 ROCm 内核驱动(仅内核部分)
|
||||
|
||||
容器里的 ROCm 用户空间库是自带的,但**内核驱动必须在宿主机上**。
|
||||
|
||||
```bash
|
||||
# 添加 AMD ROCm 仓库
|
||||
wget https://repo.radeon.com/rocm/rocm.gpg.key -O - | \
|
||||
sudo gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rocm.gpg > /dev/null
|
||||
echo 'deb [arch=amd64] https://repo.radeon.com/rocm/apt/7.2.1 noble main' | \
|
||||
sudo tee /etc/apt/sources.list.d/rocm.list
|
||||
sudo apt update
|
||||
|
||||
# 只装内核驱动部分(不装整个 ROCm 用户空间)
|
||||
sudo apt install -y amdgpu-dkms rocm-dkms
|
||||
|
||||
# 把自己加入 render/video 组
|
||||
sudo usermod -a -G render,video $USER
|
||||
|
||||
# 重启
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
验证驱动:
|
||||
|
||||
```bash
|
||||
ls /dev/kfd /dev/dri/render* # 三个设备节点都应该存在
|
||||
/opt/rocm/bin/rocminfo # 如果装了 rocminfo
|
||||
```
|
||||
|
||||
> **如果你已经完整安装过 ROCm 7.2.1**(包括用户空间),不需要重复装内核驱动,直接跳到 2.2。
|
||||
|
||||
### 2.2 安装 Docker
|
||||
|
||||
```bash
|
||||
# 官方脚本(推荐)
|
||||
curl -fsSL https://get.docker.com | sudo sh
|
||||
|
||||
# 把自己加入 docker 组,免 sudo
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
|
||||
# 验证
|
||||
docker run --rm hello-world
|
||||
```
|
||||
|
||||
### 2.3 创建数据目录
|
||||
|
||||
```bash
|
||||
mkdir -p ~/mineru-docker/data/{input,output,models,miopen}
|
||||
cd ~/mineru-docker
|
||||
```
|
||||
|
||||
将本仓库 `docker/` 目录下的所有文件复制到 `~/mineru-docker/`(或直接在仓库目录下操作)。
|
||||
|
||||
目录结构:
|
||||
|
||||
```
|
||||
~/mineru-docker/
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── env.example
|
||||
├── scripts/
|
||||
│ └── cache_warmer.py
|
||||
└── data/
|
||||
├── input/ # 放待处理的 PDF
|
||||
├── output/ # 处理结果输出
|
||||
├── models/ # HuggingFace / ModelScope 模型缓存
|
||||
└── miopen/ # MIOpen kernel 缓存
|
||||
```
|
||||
|
||||
复制 `env.example` 并根据你的 GPU 修改:
|
||||
|
||||
```bash
|
||||
cp env.example .env
|
||||
# 编辑 .env,将 ARCH=gfx1201 改为你的 gfx 代号
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 构建镜像
|
||||
|
||||
### 3.1 构建
|
||||
|
||||
```bash
|
||||
# 方式一:docker build(直接指定 ARCH)
|
||||
docker build \
|
||||
--build-arg ARCH=gfx1201 \
|
||||
-t mineru-rocm:7.2.1 \
|
||||
-f Dockerfile .
|
||||
|
||||
# 方式二:docker compose(使用 .env 中的 ARCH)
|
||||
docker compose build
|
||||
```
|
||||
|
||||
构建时间参考:
|
||||
- 下载 ROCm 包:~5 分钟
|
||||
- 编译 vllm:30-45 分钟(LLVM 22,`-j4`)
|
||||
- 安装 MinerU + 依赖:~3 分钟
|
||||
- **总计:约 40-60 分钟**(首次,后续利用 Docker 层缓存会快很多)
|
||||
|
||||
如果编译中途 OOM 被杀(exit 137),把 Dockerfile 第 155 行的 `ninja -j4` 改成 `ninja -j2`。
|
||||
|
||||
### 3.2 关键构建参数
|
||||
|
||||
| 参数 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `ARCH` | `gfx1201` | GPU 架构代号,见 1.3 节表格 |
|
||||
| `PYTHON_VER` | `3.12` | Python 版本 |
|
||||
| `VENV` | `/opt/mineru_venv` | 虚拟环境路径 |
|
||||
| `TORCH_INDEX` | `https://download.pytorch.org/whl/rocm7.2` | PyTorch wheel 源 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 运行容器
|
||||
|
||||
### 4.1 交互模式(调试 / 手动处理)
|
||||
|
||||
```bash
|
||||
# docker compose
|
||||
docker compose run --rm mineru
|
||||
|
||||
# 或 docker run
|
||||
docker run -it --rm \
|
||||
--device /dev/kfd --device /dev/dri \
|
||||
--security-opt seccomp=unconfined \
|
||||
--group-add video --group-add render \
|
||||
--ipc host \
|
||||
-v ./data/input:/data/input:ro \
|
||||
-v ./data/output:/data/output \
|
||||
-v ./data/models:/opt/models \
|
||||
-v ./data/miopen:/root/.cache/miopen \
|
||||
mineru-rocm:7.2.1
|
||||
```
|
||||
|
||||
进入容器后,虚拟环境已自动激活,可直接使用:
|
||||
|
||||
```bash
|
||||
# 验证 GPU
|
||||
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
|
||||
|
||||
# 处理 PDF
|
||||
mineru -p /data/input/example.pdf -o /data/output -b hybrid-auto-engine
|
||||
```
|
||||
|
||||
### 4.2 CLI 模式(一键处理)
|
||||
|
||||
```bash
|
||||
docker compose run --rm mineru \
|
||||
mineru -p /data/input/example.pdf -o /data/output -b hybrid-auto-engine
|
||||
```
|
||||
|
||||
或修改 `docker-compose.yml` 的 `command` 为:
|
||||
```yaml
|
||||
command: mineru -p /data/input/example.pdf -o /data/output -b hybrid-auto-engine
|
||||
```
|
||||
|
||||
### 4.3 WebUI 模式
|
||||
|
||||
修改 `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
command: mineru-gradio --server-name 0.0.0.0 --server-port 7860
|
||||
ports:
|
||||
- "7860:7860"
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
# 浏览器打开 http://<宿主机IP>:7860
|
||||
```
|
||||
|
||||
### 4.4 API 模式
|
||||
|
||||
```yaml
|
||||
command: mineru-api --host 0.0.0.0 --port 8000
|
||||
ports:
|
||||
- "8000:8000"
|
||||
```
|
||||
|
||||
API 用法参考 [MinerU 官方文档](https://github.com/opendatalab/MinerU)。
|
||||
|
||||
### 4.5 中国用户:使用 ModelScope 下载模型
|
||||
|
||||
设置环境变量即可切换下载源:
|
||||
|
||||
```bash
|
||||
docker compose run --rm -e MINERU_MODEL_SOURCE=modelscope mineru \
|
||||
mineru -p /data/input/example.pdf -o /data/output -b hybrid-auto-engine
|
||||
```
|
||||
|
||||
或修改 `.env`:`MINERU_MODEL_SOURCE=modelscope`
|
||||
|
||||
---
|
||||
|
||||
## 5. MIOpen 缓存预热
|
||||
|
||||
容器首次使用前,建议预热 MIOpen kernel 缓存(约 3-4 分钟)。缓存通过卷挂载持久化,只需执行一次。
|
||||
|
||||
```bash
|
||||
# 进入容器
|
||||
docker compose run --rm mineru
|
||||
|
||||
# 运行预热
|
||||
python /opt/cache_warmer.py --device cuda --max_side 960 --step 32
|
||||
```
|
||||
|
||||
| 输入尺寸 | 冷启动耗时 | 预热后 |
|
||||
|---------|----------|-------|
|
||||
| (1, 3, 544, 672) | ~1320 ms | ~30 ms |
|
||||
| (1, 3, 416, 704) | ~1133 ms | ~30 ms |
|
||||
|
||||
缓存存在 `./data/miopen/`,升级 ROCm 版本后需重新预热。
|
||||
|
||||
---
|
||||
|
||||
## 6. 验证
|
||||
|
||||
```bash
|
||||
docker compose run --rm mineru python -c "
|
||||
import torch
|
||||
from vllm.platforms import current_platform
|
||||
|
||||
print('=== Environment Check ===')
|
||||
print(f'PyTorch : {torch.__version__}')
|
||||
print(f'ROCm : {torch.version.hip}')
|
||||
print(f'GPU : {torch.cuda.get_device_name(0)}')
|
||||
print(f'GPU Avail: {torch.cuda.is_available()}')
|
||||
print(f'Platform : {type(current_platform).__name__}')
|
||||
print(f'is_rocm : {current_platform.is_rocm()}')
|
||||
|
||||
# 快速算力测试
|
||||
x = torch.randn(100, 100).cuda()
|
||||
print(f'Compute : {(x @ x).shape} PASS')
|
||||
"
|
||||
|
||||
# 期望输出:
|
||||
# PyTorch : 2.11.0+rocm7.2
|
||||
# GPU Avail: True
|
||||
# Platform : RocmPlatform
|
||||
# is_rocm : True
|
||||
# Compute : torch.Size([100, 100]) PASS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 性能参考(RX 9070,13 页 example.pdf)
|
||||
|
||||
| 阶段 | 耗时 / 速度 |
|
||||
|:-----|:-----------|
|
||||
| VLM 推理 (Two Step Extraction) | ~5 秒 (2+ it/s) |
|
||||
| Layout Predict | 1.2-1.5 秒 |
|
||||
| OCR-det | ~20 it/s |
|
||||
| Processing pages | **65-71 it/s** |
|
||||
| 13 页总耗时 | 5-7 秒 |
|
||||
|
||||
得益于 hipBLASLt 在线 GEMM 调优(ROCm 7.2 相比 7.1 提升约 106%)和 RX 9070 的 640 GB/s 显存带宽。
|
||||
|
||||
---
|
||||
|
||||
## 8. Dockerfile 补丁清单
|
||||
|
||||
Dockerfile 自动应用了以下所有补丁,了解即可(排查问题时有用):
|
||||
|
||||
| # | 补丁 | 目标文件 | 原因 |
|
||||
|---|------|---------|------|
|
||||
| 1 | hipcc/clang 符号链接 | 系统 | `hipcc.pl` 硬编码 `clang-17`,ROCm 7.2 实际带 `clang-22` |
|
||||
| 2 | `__hip_internal::conditional` | `/opt/rocm/include/hip/*.h` | LLVM 22 不接受此命名空间 |
|
||||
| 3 | `warpSize` 常量 | `amd_warp_functions.h` | `__AMDGCN_WAVEFRONT_SIZE` 在 LLVM 22 未定义 |
|
||||
| 4 | `__activemask()` | `amd_warp_sync_functions.h` | 替换为 `__builtin_amdgcn_read_exec()` |
|
||||
| 5 | mamba `operator+` 冲突 | `vllm csrc/mamba/.../selective_scan.h` | ROCm 7.2 头文件已自带定义 |
|
||||
| A | imgW 32 对齐 | MinerU `predict_rec.py` | RDNA MIOpen 最优尺寸 |
|
||||
| B | 批次填充 | MinerU `predict_rec.py` | 避免 MIOpen 冷启动 |
|
||||
| C | contiguous 检查 | MinerU `predict_det.py` | RDNA 内存布局兼容 |
|
||||
|
||||
---
|
||||
|
||||
## 9. 常见问题
|
||||
|
||||
**Q: `docker: Error response from daemon: could not select device driver`**
|
||||
|
||||
Docker 没有 GPU 支持。安装 `nvidia-container-toolkit` 的 AMD 等价物——实际上 ROCm 不需要额外的 container runtime,只要 `/dev/kfd` 和 `/dev/dri` 存在即可。检查宿主机驱动:
|
||||
|
||||
```bash
|
||||
ls /dev/kfd /dev/dri/render*
|
||||
```
|
||||
|
||||
**Q: 容器启动后 `torch.cuda.is_available()` 返回 `False`**
|
||||
|
||||
1. 确认容器有 `--device /dev/kfd --device /dev/dri`
|
||||
2. 确认 `--security-opt seccomp=unconfined`
|
||||
3. 确认当前用户在宿主机的 `render` 和 `video` 组
|
||||
4. 容器内运行 `rocminfo` 看能否检测到 GPU
|
||||
|
||||
**Q: 构建时 `ninja` 被 kill(exit 137)**
|
||||
|
||||
内存不足。将 Dockerfile 中 `ninja -j4` 改为 `ninja -j2` 或 `ninja -j1`,或给 Docker 分配更多内存。
|
||||
|
||||
**Q: 构建时 cmake 报 `Failed to find ROCm root directory`**
|
||||
|
||||
`/opt/rocm/bin` 不在 PATH 中。检查 Dockerfile 中 `ENV PATH` 是否正确设置。
|
||||
|
||||
**Q: 构建时 cmake 报 `roc::hipsparselt target not found`**
|
||||
|
||||
`hipsparselt-dev` 没装上。检查 Dockerfile 阶段 6 的 apt install 列表。
|
||||
|
||||
**Q: MinerU 运行时很慢(单页 > 10 秒)**
|
||||
|
||||
大概率 MIOpen 在冷启动。先跑一次 `cache_warmer.py`。
|
||||
|
||||
**Q: HuggingFace 连不上 / 模型下载失败**
|
||||
|
||||
切换下载源:`MINERU_MODEL_SOURCE=modelscope`。或设置代理:
|
||||
|
||||
```bash
|
||||
docker compose run --rm -e http_proxy=http://host:port -e https_proxy=http://host:port mineru
|
||||
```
|
||||
|
||||
**Q: WebUI/API 端口无法访问**
|
||||
|
||||
检查 `docker-compose.yml` 中 `ports` 是否取消注释。检查宿主机防火墙。
|
||||
|
||||
**Q: 显存不足 (Out of Memory)**
|
||||
|
||||
- 8GB 显卡设置 `MINERU_VIRTUAL_VRAM_SIZE=6` 触发保守策略
|
||||
- 或改用 pipeline 后端:`mineru -p input.pdf -o output -b pipeline`
|
||||
|
||||
---
|
||||
|
||||
## 10. 镜像体积优化(可选)
|
||||
|
||||
完整镜像约 25-30 GB(含 ROCm 库、vllm 编译产物、Python 包)。如需优化:
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile 构建完成后追加清理阶段:
|
||||
RUN rm -rf /opt/vllm_build /opt/vllm/.git /opt/aiter/.git /opt/flash-attention/.git && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
|
||||
${VENV}/bin/pip cache purge
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 升级指南
|
||||
|
||||
### 升级 MinerU
|
||||
|
||||
```bash
|
||||
docker compose run --rm mineru pip install --upgrade 'mineru[core]'
|
||||
# 然后重新应用 RDNA 补丁(参考 Dockerfile 阶段 9)
|
||||
```
|
||||
|
||||
### 升级 vllm / ROCm
|
||||
|
||||
重新构建镜像即可(补丁在 Dockerfile 中自动重应用):
|
||||
|
||||
```bash
|
||||
docker compose build --no-cache
|
||||
```
|
||||
|
||||
> ROCm 版本的补丁 1-4 需要 sudo 权限,构建时 Docker 容器内默认为 root,无需额外处理。
|
||||
|
||||
---
|
||||
|
||||
*文档最后更新: 2026-06-03*
|
||||
*实测环境:Ubuntu 24.04 + AMD RX 9070 (gfx1201) + ROCm 7.2.1 + PyTorch 2.11.0+rocm7.2 + vllm main + MinerU 3.2.0*
|
||||
@@ -0,0 +1,71 @@
|
||||
# =============================================================================
|
||||
# MinerU ROCm Docker Compose 配置
|
||||
# 原生 Linux + AMD GPU 环境
|
||||
# =============================================================================
|
||||
|
||||
services:
|
||||
mineru:
|
||||
image: mineru-rocm:7.2.1
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
# ---- 按你的 GPU 修改 ----
|
||||
# gfx1201 = RX 9070 / 9070 XT / 9070 GRE
|
||||
# gfx1200 = RX 9060 XT / 9060 XT LP
|
||||
# gfx1100 = RX 7900 XTX / XT / GRE
|
||||
# gfx1101 = RX 7800 XT / 7700 XT
|
||||
# gfx1030 = RX 6950 / 6900 / 6800 系列
|
||||
ARCH: gfx1201
|
||||
container_name: mineru-rocm
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ipc: host # vllm 共享内存需要
|
||||
|
||||
# ---- GPU 设备透传(必需)----
|
||||
devices:
|
||||
- /dev/kfd # ROCm KFD 内核驱动接口
|
||||
- /dev/dri # GPU 渲染节点 (renderD*)
|
||||
|
||||
# ---- 安全配置(ROCm 需要)----
|
||||
security_opt:
|
||||
- seccomp=unconfined # 允许 ROCm 系统调用
|
||||
|
||||
# ---- 用户权限(访问 GPU 设备)----
|
||||
group_add:
|
||||
- video # /dev/dri/render* 权限
|
||||
- render # /dev/kfd 权限
|
||||
# 如果要与宿主机用户对齐,取消下面注释并改为你的 UID/GID
|
||||
# user: "${UID:-1000}:${GID:-1000}"
|
||||
|
||||
# ---- 环境变量 ----
|
||||
environment:
|
||||
- MINERU_MODEL_SOURCE=${MINERU_MODEL_SOURCE:-huggingface}
|
||||
- HF_HUB_CACHE=${HF_HUB_CACHE:-/opt/models/huggingface}
|
||||
- MODELSCOPE_CACHE=${MODELSCOPE_CACHE:-/opt/models/modelscope}
|
||||
|
||||
# ---- 卷挂载 ----
|
||||
volumes:
|
||||
# 输入/输出目录(按需修改)
|
||||
- ${INPUT_DIR:-./data/input}:/data/input:ro
|
||||
- ${OUTPUT_DIR:-./data/output}:/data/output
|
||||
# 模型缓存(持久化,避免每次下载)
|
||||
- ${MODEL_DIR:-./data/models}:/opt/models
|
||||
# MIOpen kernel 缓存(持久化,避免每次预热)
|
||||
- ${MIOPEN_CACHE:-./data/miopen}:/root/.cache/miopen
|
||||
|
||||
# ---- 启动命令(默认 bash,可改)----
|
||||
# 例如直接处理 PDF:
|
||||
# command: mineru -p /data/input/example.pdf -o /data/output -b hybrid-auto-engine
|
||||
# 启动 WebUI:
|
||||
# command: mineru-gradio --server-name 0.0.0.0 --server-port 7860
|
||||
# 启动 API:
|
||||
# command: mineru-api --host 0.0.0.0 --port 8000
|
||||
command: bash
|
||||
|
||||
# ---- 端口(WebUI / API 模式时启用)----
|
||||
# ports:
|
||||
# - "7860:7860" # WebUI
|
||||
# - "8000:8000" # API
|
||||
|
||||
restart: "no"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Docker 环境变量(按需修改)
|
||||
# 复制为 .env 后使用: cp env.example .env
|
||||
|
||||
# ---- GPU 架构(按你的显卡修改)----
|
||||
# gfx1201 = RX 9070 / 9070 XT / 9070 GRE
|
||||
# gfx1200 = RX 9060 XT / 9060 XT LP
|
||||
# gfx1100 = RX 7900 XTX / XT / GRE
|
||||
# gfx1101 = RX 7800 XT / 7700 XT
|
||||
# gfx1030 = RX 6950 / 6900 / 6800 系列
|
||||
ARCH=gfx1201
|
||||
|
||||
# ---- 目录挂载 ----
|
||||
INPUT_DIR=./data/input
|
||||
OUTPUT_DIR=./data/output
|
||||
MODEL_DIR=./data/models
|
||||
MIOPEN_CACHE=./data/miopen
|
||||
|
||||
# ---- 模型下载源 ----
|
||||
# huggingface(默认,需科学上网)或 modelscope(国内可用)
|
||||
MINERU_MODEL_SOURCE=huggingface
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ROCm MIOpen Cache Warmer for MinerU
|
||||
|
||||
在 AMD RDNA 架构上,MIOpen 遇到新尺寸的卷积运算时需要搜索最优 kernel(冷启动)。
|
||||
预热脚本提前跑一遍常用尺寸,将 kernel 缓存到 ~/.cache/miopen/,避免运行时等待。
|
||||
|
||||
缓存持久化到磁盘,重启不丢失;只有升级 ROCm 后才需要重新跑。
|
||||
|
||||
用法:
|
||||
python cache_warmer.py --device cuda --max_side 960 --step 32
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def get_args():
|
||||
p = argparse.ArgumentParser(description="ROCm MIOpen Cache Warmer")
|
||||
p.add_argument("--device", type=str, default="cuda")
|
||||
p.add_argument("--max_side", type=int, default=960)
|
||||
p.add_argument("--step", type=int, default=32)
|
||||
return p.parse_args()
|
||||
|
||||
|
||||
class MockOCRModel(nn.Module):
|
||||
"""模拟 MinerU OCR 模型的卷积结构,覆盖 MIOpen 常用 kernel 尺寸。"""
|
||||
|
||||
def __init__(self, in_channels: int = 3):
|
||||
super().__init__()
|
||||
self.stem = nn.Conv2d(in_channels, 16, 3, stride=2, padding=1)
|
||||
self.dw_3x3 = nn.Conv2d(16, 16, 3, stride=1, padding=1, groups=16)
|
||||
self.pw_1 = nn.Conv2d(16, 64, 1)
|
||||
self.dw_5x5 = nn.Conv2d(64, 64, 5, stride=2, padding=2, groups=64)
|
||||
self.pw_2 = nn.Conv2d(64, 128, 1)
|
||||
self.dw_3x3_s2 = nn.Conv2d(128, 128, 3, stride=2, padding=1, groups=128)
|
||||
self.pw_3 = nn.Conv2d(128, 256, 1)
|
||||
self.out_conv = nn.Conv2d(256, 64, 1)
|
||||
self.binarize_conv = nn.Conv2d(64, 1, 3, stride=1, padding=1)
|
||||
self.act = nn.ReLU()
|
||||
|
||||
def forward(self, x):
|
||||
x = self.stem(x)
|
||||
x = self.act(x)
|
||||
x = self.dw_3x3(x)
|
||||
x = self.pw_1(x)
|
||||
x = self.dw_5x5(x)
|
||||
x = self.act(x)
|
||||
x = self.pw_2(x)
|
||||
x = self.dw_3x3_s2(x)
|
||||
x = self.pw_3(x)
|
||||
x = self.out_conv(x)
|
||||
x = F.interpolate(x, scale_factor=2, mode="bilinear", align_corners=True)
|
||||
x = self.binarize_conv(x)
|
||||
return x
|
||||
|
||||
|
||||
def main():
|
||||
args = get_args()
|
||||
assert torch.cuda.is_available(), "GPU not available"
|
||||
device = torch.device(args.device)
|
||||
|
||||
print("=" * 50)
|
||||
print("ROCm MIOpen Cache Warmer")
|
||||
print(f" GPU : {torch.cuda.get_device_name(0)}")
|
||||
print(f" ROCm : {torch.version.hip}")
|
||||
print(f" Cache : ~/.cache/miopen/")
|
||||
print("=" * 50)
|
||||
|
||||
model = MockOCRModel().to(device).eval()
|
||||
sizes = list(range(64, args.max_side + 1, args.step))
|
||||
combos = [(h, w) for h in sizes for w in sizes]
|
||||
|
||||
print(f"Warming {len(combos)} shapes ({len(sizes)}x{len(sizes)} grid, "
|
||||
f"step={args.step})...")
|
||||
|
||||
ok = 0
|
||||
with torch.no_grad():
|
||||
for h, w in tqdm(combos, desc="Warming"):
|
||||
try:
|
||||
model(torch.zeros((1, 3, h, w), device=device, dtype=torch.float32))
|
||||
ok += 1
|
||||
except RuntimeError as e:
|
||||
if "out of memory" in str(e):
|
||||
torch.cuda.empty_cache()
|
||||
# 其他错误跳过,不影响后续
|
||||
|
||||
print(f"\nDone! {ok}/{len(combos)} shapes cached (~3–4 min)")
|
||||
print("Kernels saved to ~/.cache/miopen/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user