使用数据组git中的kfqself分支来处理,或者clcd分支(需要自己添加srtm3的支持)
一键脚本(推荐):把 3.1 → 3.6 串起来跑,断点续跑,失败可直接重跑:
bash scripts/hangzhou/run_full_hangzhou.sh自定义 workers:
WORKERS=4 bash scripts/hangzhou/run_full_hangzhou.shWindows 几何由 repo 根目录的
hangzhou_olmoearth_windows.json固定;如需自定义可
WINDOWS_JSON=/path/to/other.jsonoverride。如果想了解每一步具体在做什么,或想分步排查,继续看下面 3.1-3.6 手动版本。
3.1 转换原始 hgt + 写 file_list.json
python scripts/hangzhou/01_convert_hgt_to_geotiff.py \
--hgt_dir data/srtm_hangzhou \
--mirror_dir data/srtm_hangzhou/hf_mirror
预期:data/srtm_hangzhou/hf_mirror/ 下出现 N29/、N30/ 共 6 个 SRTM1*V2.tif,加一个 file_list.json。
3.2 生成杭州 res_10 windows
python scripts/hangzhou/02_create_windows.py \
--ds_path data/rslearn_hangzhou_srtm
Windows 来自 repo 根目录的 hangzhou_olmoearth_windows.json(无需手动指定)。
预期:输出末尾 Created 6272 windows under data/rslearn_hangzhou_srtm/windows/res_10/。
3.3 准备 rslearn dataset 配置
sed "s|LOCAL_ROOT_PLACEHOLDER|$(pwd)/data/srtm_hangzhou/hf_mirror|" \
data/rslearn_hangzhou_srtm/config.template.json \
> data/rslearn_hangzhou_srtm/config.json
3.4 rslearn 三连(用 8 workers 适配 16 GB)
export DS=./data/rslearn_hangzhou_srtm
export WORKERS=8
rslearn dataset prepare --root $DS --group res_10 --workers $WORKERS
rslearn dataset ingest --root $DS --group res_10 --workers $WORKERS --no-use-initial-job
rslearn dataset materialize --root $DS --group res_10 --workers $WORKERS --no-use-initial-job
中途内存压力大可
Ctrl-C,降WORKERS=4重跑。三连均可断点续跑(已完成的 window 自动跳过)。
与 JSON 清单对账
确认 materialize 后的 rslearn dataset 与 hangzhou_olmoearth_windows.json 完全一致(名字 + projection + bounds + 每个 window 都有非空 .tif):
uv run --extra all-no-flash python scripts/hangzhou/verify_windows_against_json.py \
--ds_path data/rslearn_hangzhou_srtm \
--require-materialized
预期:OK: 6272 windows match JSON exactly (names + projection + bounds), all materialized。该脚本三模态共用(SRTM / Landsat / CLCD 都跑同一份 JSON)。
3.5 跑 srtm.py 转 OlmoEarth 单文件格式
python -m olmoearth_pretrain.dataset_creation.rslearn_to_olmoearth.srtm \
--ds_path $DS \
--olmoearth_path ./data/olmoearth_hangzhou_srtm \
--workers $WORKERS
3.6 聚合模态级 metadata
python -m olmoearth_pretrain.dataset_creation.make_meta_summary \
--olmoearth_path ./data/olmoearth_hangzhou_srtm \
--modality srtm \
--time_span static
预期产物:data/olmoearth_hangzhou_srtm/10_srtm.csv,行数 = window 数 + 1(header)。
服务器端运行(Linux 生产环境)
如果本地 Mac 跑完 sample / 烟囱测试通过,要把全杭州流程挪到 Linux 服务器上跑(更多核 / 更多 RAM / 不用占本地机器),按下面流程。整体流水线和"第 3 步"完全一样,差异只在环境配置 + 后台运行 + 数据进出。
服 0. 服务器侧前置
- Linux x86_64(Ubuntu 22.04+ / Debian 12+ / RHEL 9+ 都可以)
- Python 3.12 +
uv(预装,或现装curl -LsSf https://astral.sh/uv/install.sh | sh) - 磁盘 ≥ 10 GB 空闲(原始 .hgt ~150 MB + rslearn 中间产物 ~3 GB + OlmoEarth 输出 ~600 MB + 缓冲)
- RAM ≥ 16 GB(8 workers);RAM 紧时降到 4 workers + 8 GB 也能跑
- 不需要 AWS / 网络(SRTM 是纯本地数据源)
服 2. 在服务器上跑一键脚本(后台 + 日志)
cd /path/to/olmoearth_pretrain
mkdir -p logs
# 用 nohup 后台跑,日志落到 logs/srtm_run.log
nohup bash scripts/hangzhou/run_full_hangzhou.sh > logs/srtm_run.log 2>&1 &
echo $! > logs/srtm_run.pid
# 看进度(日志末尾)
tail -f logs/srtm_run.log
或者用 tmux(可断开 SSH 重连):
tmux new -s srtm
bash scripts/hangzhou/run_full_hangzhou.sh 2>&1 | tee logs/srtm_run.log
# Ctrl-B D 断开;ssh 上来后 tmux attach -t srtm 重连
可调环境变量:
WORKERS=16 bash scripts/hangzhou/run_full_hangzhou.sh
服 3. 监控 + 中断恢复
跑到一半想看进度:
# 看当前进度
tail -50 logs/srtm_run.log
# 看已完成的 windows 数(主要瓶颈在 ingest + materialize)
find data/rslearn_hangzhou_srtm/windows/res_10 -name 'completed_*' | wc -l
# 看产出 .tif 数(srtm.py 转换阶段)
find data/olmoearth_hangzhou_srtm/10_srtm -name '*.tif' | wc -l
中断(Ctrl-C 或 kill $(cat logs/srtm_run.pid))后重跑同一脚本,所有子命令自动跳过已完成 window,安全续跑。
服 4. 拿回产物
如果训练也在服务器上,不用拿回——data/olmoearth_hangzhou_srtm/ 留在原地等下游消费。
如果要拷回本地:
# 本地
rsync -avP --exclude='10_srtm_meta' \
user@server:/path/to/olmoearth_pretrain/data/olmoearth_hangzhou_srtm/ \
./data/olmoearth_hangzhou_srtm/
-
10_srtm/:核心交付物(每 window 一个 GeoTIFF,~600 MB) -
10_srtm.csv:聚合 metadata(几十 KB,必拷) -
10_srtm_meta/:per-window 临时 CSV,不拷(已被聚合进.csv)
服 5. 服务器侧验证
uv run --extra all-no-flash python scripts/hangzhou/verify_hangzhou.py \
--srtm_dir data/olmoearth_hangzhou_srtm/10_srtm
跟本地"## 验证产物"完全一致(下面那段)。
资源预算(典型 Linux 服务器)
| 配置 | WORKERS | 全杭州耗时 | RAM 峰值 |
|---|---|---|---|
| 8 vCPU / 16 GB(够用) | 8 | 30-50 分钟 | ~12 GB |
| 16 vCPU / 32 GB(舒服) | 16 | 15-25 分钟 | ~20 GB |
| 32 vCPU / 64 GB(过剩) | 24 | 10-15 分钟 | ~30 GB |
主要时间在 rslearn dataset materialize(~70%)+ srtm.py 转换(~25%)。
跟 Landsat / CLCD 服务器流程的关系
| 模态 | 是否需要 AWS | 是否需要外网 | 跑全杭州时间 | 输出体积 |
|---|---|---|---|---|
| SRTM(本指南) | ❌ | ❌(纯本地) | ~30 分钟 | ~600 MB |
| Landsat | ✅ Requester Pays | ✅(s3.us-west-2) |
~4-8 小时 | ~50 GB |
| CLCD | ❌ | ❌(纯本地,从 Zenodo 下) | ~5-10 分钟 | ~50 MB |
SRTM 是三个里最简单的服务器侧场景(无 AWS、无网络、无代理需求)。如果 SRTM 跑通,Landsat 和 CLCD 的服务器流程见各自的运行指南。
验证产物
一键深度验证(推荐)
uv run --extra all-no-flash python scripts/hangzhou/verify_hangzhou.py \
--srtm_dir data/olmoearth_hangzhou_srtm/10_srtm
会做 4 项交叉检查:
- CRS / 尺寸 / 分辨率:每个 tile 必须是 256×256 @ 10 m,在 EPSG:32650 或 32651
- 反投影到 WGS84:每个 tile 中心反算成经纬度,必须落在杭州 bbox 内,平均中心应在 (120.20, 30.25) 附近
- 地标交叉验证:找离西湖、杭州湾、临安西部山地、千岛湖、萧山主城最近的 tile,验证高程值在合理范围(西湖 ~5–30m、杭州湾 ~0–5m、临安山地 200–1500m 等)
- 全局高程分布:全局 min 应 ≈ 0(海面),max 应 ≥ 500m(临安/淳安山地)
跑完应该看到大量 ✅。出现 ⚠️ 时阅读上下文判断是真错(数据不对)还是合理(比如 bbox 缩小了某个地标没覆盖)。
主城区范围的烟囱测试不会覆盖到临安山地,所以"临安"那项和"全局 max ≥ 500"在烟囱阶段会显示 ⚠️,这是正常的;全杭州 run 完之后这两项必须变 ✅。
手动快速检查
# 1. 检查 SRTM 切片数量
find data/olmoearth_hangzhou_srtm/10_srtm -name '*.tif' | wc -l
# 应该接近 window 数
# 2. 抽样一张 SRTM tif 看元数据
SAMPLE=$(find data/olmoearth_hangzhou_srtm/10_srtm -name '*.tif' | head -1)
uv run --extra all-no-flash python -c "
import rasterio, sys
with rasterio.open(sys.argv[1]) as src:
print('CRS:', src.crs) # 应是 EPSG:32650 或 32651
print('Size:', src.width, 'x', src.height) # 应是 256 x 256
print('Resolution:', src.res) # 应是 (10, 10)
arr = src.read(1)
print('Elevation range:', arr.min(), '-', arr.max(), 'meters')
" "$SAMPLE"
# 3. 检查聚合 CSV
head -3 data/olmoearth_hangzhou_srtm/10_srtm.csv
wc -l data/olmoearth_hangzhou_srtm/10_srtm.csv
可视化(肉眼看地形)
GeoTIFF 在 Finder/Preview 里显示成黑色是正常的(int16 高程值远超 8-bit 范围),看图必须做拉伸:
# 抽 4 张拼出彩色地形图
uv run --extra all-no-flash python -c "
import rasterio, matplotlib.pyplot as plt, glob
files = sorted(glob.glob('data/olmoearth_hangzhou_srtm/10_srtm/*.tif'))
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
for ax, f in zip(axes.flat, files[:4]):
with rasterio.open(f) as src:
arr = src.read(1)
im = ax.imshow(arr, cmap='terrain', vmin=0, vmax=max(arr.max(), 100))
ax.set_title(f.split('/')[-1] + f' max={arr.max()}m')
plt.colorbar(im, ax=ax)
plt.tight_layout()
plt.savefig('/tmp/hangzhou_srtm.png', dpi=120)
print('Saved /tmp/hangzhou_srtm.png')
" && open /tmp/hangzhou_srtm.png
或者直接把 data/olmoearth_hangzhou_srtm/10_srtm/*.tif 拖进 QGIS —— QGIS 会自动拉伸,能直接看到地形(主城区平原 + 西部山地)。
故障排查
| 现象 | 可能原因 | 处置 |
|---|---|---|
gdal_translate: command not found |
没装 GDAL,且我们的代码意外调用了它 | 我们用 rasterio,不该需要;若仍需要 brew install gdal
|
Failed to build flash-attn |
Mac 用 --all-extras 触发了 flash-attn 编译 |
改用 uv sync --locked --extra all-no-flash --python 3.12
|
encode_raster() got an unexpected keyword argument 'raster' |
rslearn 0.0.29 把 kwarg 从 raster 改成了 array,仓库里很多 rslearn_to_olmoearth/*.py 还用旧名 |
把 raster=image 改成 array=image(srtm.py 已修;若合作方跑其它模态时遇到,同样改) |
ModuleNotFoundError: rslearn |
没装 dataset-creation extra | uv sync --locked --extra all-no-flash |
requests.exceptions.InvalidSchema: file:// |
LocalSRTM override 没生效 | 检查 config.json 的 class_path 是 olmoearth_pretrain.dataset_creation.local_data_sources.local_srtm.LocalSRTM
|
| Step 3.4 卡住 / 内存满 | workers 太多 | 降到 4 重跑(已完成 window 会跳过) |
is_layer_completed=False 跳过所有 window |
materialize 没跑完或失败 | 检查 windows/res_10/<name>/layers/srtm/ 下有无 completed 文件,重跑 materialize |
| Step 3.6 报 "no temp metadata found" | Step 3.5 没真正写出文件 | 抽查任意 window 的 OlmoEarth 目录确认有 tif + temp CSV |
| Window count 远低于预期 | bbox 错了 / hf_mirror 路径配错 | 检查 config.json 里 local_root 是绝对路径,且指向真实存在的 mirror |
清理 / 回滚
整条流水线产物都在 data/ 下三个新目录,可整体删除重来,不影响仓库其它部分:
rm -rf data/srtm_hangzhou/hf_mirror
rm -rf data/rslearn_hangzhou_srtm
rm -rf data/olmoearth_hangzhou_srtm
rm -rf data/_smoke_hangzhou
新增源代码(olmoearth_pretrain/dataset_creation/{hangzhou,local_data_sources}/ 和 scripts/hangzhou/、tests/unit/dataset_creation/)未修改任何已有文件,删除即回到当前状态。
想做更多?
-
改 windows 几何:生产管线 windows 全部来自
hangzhou_olmoearth_windows.json(repo 根目录,6,272 个 256×256 @ 10 m tile)。要做嘉兴 / 宁波 / 整个长三角,先重新生成一份 windows JSON,再--windows-json <path>或WINDOWS_JSON=<path>传给脚本——同时要保证原始.hgt数据覆盖到目标区域。本地子区域开发可以用scripts/hangzhou/_filter_windows_json.py --bbox ... --output <subset>.json从主 JSON 切一个子集。 -
走完训练链路:补一个时变模态(ERA5_10 是最简单,纯本地不依赖外部凭证),重跑 Step 3.4-3.6 给该模态,然后跑
python -m olmoearth_pretrain.internal.run_h5_conversion(参见 plan 文件 Step 9 章节)。 - 真正训练模型:需要至少 Sentinel-2 + SRTM 这种多模态组合,且涉及大量 GPU 资源,远超本指南范围。
本任务的边界 — 你做到哪步就停
结论:第 3.6 步跑完(10_srtm.csv 写出),你的全部数据准备工作就完成了。
后续 H5 打包(convert_to_h5py)和模型训练不属于本任务,留给合作方:
- ❌ 不要单独打包 SRTM-only H5:OlmoEarth dataloader 的过滤器(
convert_to_h5py.py:617-633)要求每样本至少一个时变模态 ≥12 时间步,SRTM 是静态的,SRTM-only H5 全部样本都会被丢弃,毫无意义。 - ✅ 正确路径:把 SRTM 切片交付给合作方;他们补完 Sentinel-2/Sentinel-1/ERA5/OSM 等模态后,统一跑一次 H5 打包,所有模态的 sample 才能配齐。
完整 OlmoEarth 预训练数据集地图(供参考,看你卡在哪)
| 模态 | 类型 | 时间属性 | 数据源 | 凭证要求 | 谁负责 |
|---|---|---|---|---|---|
| SRTM | 高程 | static | 已本地 30m | 无 | ✅ 本任务 |
| Sentinel-2 L2A | 12 波段光学 | two_week | Microsoft PC | 无 | 合作方 |
| Sentinel-1 GRD VV+VH | 2 波段雷达 | two_week | Microsoft PC | 无 | 合作方 |
| Landsat 8/9 | 11 波段光学 | two_week | AWS | AWS 凭证 | 合作方(可选) |
| ERA5 | 月度气象 | two_week | Copernicus CDS | CDS API key | 合作方 |
| OpenStreetMap | 矢量栅格化 | static | OSM PBF | 无 | 合作方 |
| WorldCover | 土地覆盖 | static | ESA/HF | 无 | 合作方 |
| WorldCereal | 作物图 | static | ESA | 无 | 合作方 |
| WRI Canopy Height | 树冠高度 | static | Meta/HF | 无 | 合作方 |
| 美国农田 | annual | USDA | — | 跳过(美国境外无数据) | |
| H5 打包(终点) | 训练样本 | — | convert_to_h5py |
— | 合作方汇总后统一跑 |
交付清单(给合作方)
把下面 4 项打包给合作方,他们就能继续跑其它模态并最终做 H5 打包:
| # | 路径 | 说明 |
|---|---|---|
| 1 |
data/rslearn_hangzhou_srtm/windows/res_10/(打 tar,~50 MB,纯 metadata JSON) |
杭州 windows 集合,~6000 个。合作方处理其它模态时必须复用这同一份 windows,不能各自重新生成 |
| 2 |
data/olmoearth_hangzhou_srtm/10_srtm/(每 window 一个 GeoTIFF,~600 MB) |
OlmoEarth 标准格式的 SRTM 切片 |
| 3 | data/olmoearth_hangzhou_srtm/10_srtm.csv |
模态级聚合 metadata,行数 = window 数 + 1(header) |
| 4 | 本指南 docs/杭州SRTM-运行指南.md + 设计文档 ~/.claude/plans/a-async-valiant.md(可选) |
让合作方了解你做了什么、约定如何形成 |
协议要点(必须告知合作方)
| 项 | 约定值 |
|---|---|
| 杭州空间范围 | bbox (118.2889081, 29.1563713, 120.7598261, 30.5935326) WGS84(与 CLAUDE.md "杭州 (Hangzhou) 区域常量" 一致) |
| windows 来源 |
data/rslearn_hangzhou_srtm/windows/res_10/,~6000 个,禁止重新生成
|
| CRS 集合 | EPSG:32650(UTM 50N)+ EPSG:32651(UTM 51N)。杭州跨两个 UTM 区,合作方 ingest 必须保留 windows 原有 CRS |
| 每 window 尺寸 | 256×256 像素 @ 10 m,即 2.56 km × 2.56 km |
| 占位时间戳 | SRTM 用 2020-01-01 UTC 作占位。合作方处理时变模态时会用真实采集时间替换,这没问题 |
| OlmoEarth 输出根目录 |
data/olmoearth_hangzhou_srtm/。合作方的其它模态目录必须落在同一个根下,H5 打包阶段才能找齐所有模态 |
| H5 打包触发条件 | 等所有约定模态(至少 S2 L2A 必须有,因为它是 H5 的 required_modality)都跑完 + 各自 make_meta_summary 完毕,统一跑一次 python -m olmoearth_pretrain.internal.run_h5_conversion --tile_path ./data/olmoearth_hangzhou_srtm --supported_modality_names '[srtm,sentinel2_l2a,...]' --required_modality_names '[sentinel2_l2a]' --compression zstd --tile_size 128
|
给合作方的一句话提示
杭州 windows 用我这份(
data/rslearn_hangzhou_srtm/windows/res_10/),你们处理 S2/S1/ERA5/OSM 等模态时,把各自 rslearn dataset 的windows/res_10/替换成我这份再跑 ingest+materialize;olmoearth_path都指向data/olmoearth_hangzhou_srtm/。所有模态都灌完之后再统一跑convert_to_h5py,产物h5py_data_w_missing_timesteps/sample_*.h5即是 dataloader 直接读的训练数据集。
合作方接手后还需做哪些(供你了解,不要做)
- 复用杭州 windows + 每个时变 / 静态模态分别跑 6 步流程(prepare → ingest → materialize → 转 OlmoEarth → make_meta_summary)
- Sentinel-2 / Sentinel-1 数据量大(~30-100 GB),需准备 ~300 GB 磁盘 + 数小时下载
- ERA5 需注册 Copernicus CDS 账号(免费)拿 API key
- OpenStreetMap 处理最折腾(需 OSM PBF + Go 处理 + 类别配置),建议放最后
- 所有模态齐了之后,跑一次 H5 打包,产物即"可直接训练的数据集"
- 训练(GPU + 多模态多日训练)是再下一阶段的事,与数据准备无关