继续。下面进入 第 6 讲:评估、推理、LoRA 合并与复现闭环。
这一讲解决一个实际问题:
训练完成后,怎么把模型真正跑起来?
怎么合并 LoRA?
怎么测试 LaSeRS?
怎么得到 mask?
怎么判断复现是否成功?
第 6 讲总目标
前面我们已经吃透了训练链路:
train.sh
↓
train.py
↓
dataset.py
↓
llava_phi.py forward
↓
loss 训练
现在进入推理链路:
训练 checkpoint
↓
merge_lora_weights.sh
↓
合并后的 HF 模型
↓
test.sh / eval.py
↓
model.inference()
↓
eval_seg()
↓
pred mask
↓
IoU / gIoU
官方测试脚本使用 deepspeed --include localhost:0 --master_port=29500 segearth_r2/eval/eval.py 启动,并需要传入 base_data_path、model_path、vision_tower_mask、mask_config 和 output_dir。(GitHub)
一、先理解训练后为什么要合并 LoRA
你训练时不是全量微调整个大模型,而是:
基础模型 Mipha-3B
+
LoRA adapter
+
训练后的分割模块参数
训练完后,checkpoint 里通常不是一个完整可直接部署的 HuggingFace 模型,而是包含 LoRA、DeepSpeed 分片、部分可训练模块权重。
所以需要:
merge_lora_weights.sh
它的作用是:
读取训练 checkpoint
↓
重新构建 SegEarthR2
↓
加载 LoRA 配置
↓
从 DeepSpeed ZeRO checkpoint 恢复 fp32 权重
↓
merge_and_unload()
↓
保存成完整 HuggingFace 模型目录
官方 merge_lora_weights.sh 调用的是 segearth_r2/train/merge_lora_weights_and_save_hf_model.py,参数包括 model_path、vision_tower、vision_tower_mask、mask_config、save_path 和 lora_r。(GitHub)
二、merge_lora_weights.sh 逐项解释
官方脚本核心形式是:
CUDA_VISIBLE_DEVICES=0 python segearth_r2/train/merge_lora_weights_and_save_hf_model.py \
--model_path=your_model_path \
--vision_tower=pretrained_model/CLIP/siglip-so400m-patch14-384 \
--vision_tower_mask=pretrained_model/mask2former/model_final_54b88a.pkl \
--mask_config=segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml \
--save_path=your_save_path \
--lora_r=4
1. CUDA_VISIBLE_DEVICES=0
只使用第 0 张 GPU。
RTX 5090 单卡复现时保留即可。
2. --model_path
这是你训练输出的 checkpoint 目录,例如:
--model_path=outputs/debug_5090_lora_r4
或者如果你训练到了某个 checkpoint:
--model_path=outputs/debug_5090_lora_r4/checkpoint-5000
这个路径必须包含 DeepSpeed / LoRA 训练保存出来的权重。
3. --vision_tower
这是 SigLIP 路径:
--vision_tower=pretrained_model/CLIP/siglip-so400m-patch14-384
注意,它虽然叫 vision tower,但这是给多模态语言模型使用的视觉塔,不是 Mask2Former 分割视觉骨干。
4. --vision_tower_mask
这是 Mask2Former / Swin 分割分支权重:
--vision_tower_mask=pretrained_model/mask2former/model_final_54b88a.pkl
如果这个路径错了,合并时初始化分割模块就可能失败。
5. --mask_config
这是分割头配置:
--mask_config=segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml
它必须和训练时一致。
如果训练时用的是 Swin-B 配置,合并时不能突然换成 Swin-L 配置,否则 predictor、pixel decoder、hidden dim 都可能不匹配。
6. --save_path
合并后的完整模型保存目录,例如:
--save_path=outputs/merged_segearth_r2_lasers
后续 eval.py 的:
--model_path
就应该指向这个目录。
7. --lora_r
必须和训练时一致。
如果训练时:
--lora_r 4
合并时也要:
--lora_r=4
如果不一致,LoRA 层 shape 可能不匹配。
三、合并脚本内部做了什么
merge_lora_weights_and_save_hf_model.py 里有几个关键步骤。
1. 重新构建模型
脚本会:
model = SegEarthR2.from_pretrained(...)
然后:
model.initial_mask_module(mask2former_ckpt, model_args)
model.get_model().initialize_vision_modules(model_args)
也就是说,它不是简单读取权重,而是先按模型结构把 SegEarthR2 重新搭起来。源码里 load_pretrained_model() 会读取 mask config,构建 SegEarthR2,初始化 mask module,再初始化视觉模块。(GitHub)
2. 重新注入 LoRA
脚本里也会调用:
find_linear_layers()
LoraConfig()
get_peft_model()
这一步必须和训练时的 LoRA 目标层一致。
它默认还是找:
q_proj
v_proj
并排除:
vision_tower
vision_tower_mask
lm_head
pixel_decoder
predictor
SEG_token_projector
这和训练时逻辑一致。
3. 从 DeepSpeed ZeRO checkpoint 恢复
核心代码是:
from deepspeed.utils.zero_to_fp32 import load_state_dict_from_zero_checkpoint
model = load_state_dict_from_zero_checkpoint(model, model_path)
意思是:
DeepSpeed ZeRO 保存的分片权重
↓
恢复成完整 fp32 state_dict
这是使用 ZeRO-2 / ZeRO-3 后必须理解的一步。源码中合并脚本明确调用 load_state_dict_from_zero_checkpoint(),然后执行 model.merge_and_unload()。(GitHub)
4. 合并 LoRA
核心代码:
model = model.merge_and_unload()
含义:
基础权重 W
+
LoRA 增量 ΔW
↓
合并成新的 W'
合并后就不再需要 LoRA adapter 单独存在。
5. 保存完整模型
最后:
model.save_pretrained(args.save_path, state_dict=state_dict)
tokenizer.save_pretrained(args.save_path)
这一步会保存成 HuggingFace 格式模型目录。
最终你应该得到类似:
outputs/merged_segearth_r2_lasers/
├── config.json
├── generation_config.json
├── model-00001-of-000xx.safetensors 或 pytorch_model.bin
├── tokenizer_config.json
├── tokenizer.model / tokenizer.json
├── special_tokens_map.json
└── ...
四、合并阶段最容易错的地方
错误 1:lora_r 不一致
表现:
size mismatch
shape mismatch
解决:
训练时 lora_r 是多少,合并时就是多少。
错误 2:model_path 指错
如果你传的是空目录,或者不是 DeepSpeed checkpoint 目录,会报:
checkpoint not found
latest file not found
zero checkpoint not found
解决:检查目录里是否有类似:
global_step*
zero_pp_rank*
mp_rank*
latest
checkpoint-*
错误 3:mask_config 和训练时不一致
表现:
pixel_decoder key mismatch
predictor key mismatch
hidden dim mismatch
解决:
合并、训练、评估三阶段必须使用同一个 mask_config。
错误 4:vision_tower_mask 权重版本不匹配
表现:
unexpected key
missing key
load_state_dict error
官方代码初始化分割模块时使用 strict=False 加载部分模块,但如果权重结构差异太大,仍会影响效果。合并脚本会传入 vision_tower_mask 并调用 initial_mask_module()。(GitHub)
五、合并后的测试命令
官方 test.sh 是:
deepspeed --include localhost:0 --master_port=29500 segearth_r2/eval/eval.py \
--base_data_path data_path \
--model_path model_path \
--vision_tower_mask pretrained_model/mask2former/model_final_54b88a.pkl \
--mask_config segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml \
--output_dir output/res
其中 model_path 应该指向合并后的模型目录,而不是原始 Mipha-3B,也不是未合并的 LoRA adapter。官方评估文档同样要求运行 segearth_r2/eval/eval.py,并修改 base_data_path、mask_config、model_path、output_dir 等路径。(GitHub)
你可以改成:
deepspeed --include localhost:0 --master_port=29500 segearth_r2/eval/eval.py \
--base_data_path /your/path/LaSeRS \
--model_path outputs/merged_segearth_r2_lasers \
--vision_tower pretrained_model/CLIP/siglip-so400m-patch14-384 \
--vision_tower_mask pretrained_model/mask2former/model_final_54b88a.pkl \
--mask_config segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml \
--output_dir outputs/eval_res
注意:官方 test.sh 没有显式传 --vision_tower,但 eval.py 的 Arguments 里有默认 vision_tower="pretrained_model/CLIP"。如果你的 SigLIP 不在这个默认路径,一定要自己传 --vision_tower。(GitHub)
六、eval.py 总体流程
eval.py 可以分成 5 段:
1. 解析 Arguments
2. load_pretrained_model() 加载模型
3. 构造 SigLIP image processor 和 DataCollator
4. 遍历 LaSeRS test annotations
5. do_eval() 逐样本推理并计算 IoU
源码中 main() 会调用 load_pretrained_model() 加载 tokenizer、model、image processor 和 context length,然后设置 conversation template,构造 SiglipImageProcessor、DataCollatorForCOCODatasetV2,再遍历 base_data_path/rs_reason_seg/LaSeRS/test/annotations 下的各个 split。(GitHub)
七、Arguments 参数类解释
eval.py 中定义了:
@dataclass
class Arguments:
local_rank: int = 0
vision_tower: str = "pretrained_model/CLIP"
vision_tower_mask: str = "pretrained_model/mask2former/model_final_54b88a.pkl"
base_data_path: Optional[str] = field(default='your_data_path')
model_path: Optional[str] = field(default="SegEarthR2_LaSeRS/hfweights-50000")
mask_config: Optional[str] = field(default="...")
model_map_name: str = 'segearth_r2'
version: str = 'llava_phi'
temperature: float = 0.2
num_beams: int = 1
max_new_tokens: int = 128
do_sample: bool = True
output_dir: str = 'save_folder'
dataloader_num_workers: int = 8
重点参数:
| 参数 | 含义 |
|---|---|
vision_tower | SigLIP 路径 |
vision_tower_mask | Mask2Former / Swin 权重路径 |
base_data_path | 数据集根目录 |
model_path | 合并后的 SegEarth-R2 模型路径 |
mask_config | Mask2Former 配置 |
temperature | 生成随机性 |
num_beams | beam search 数量 |
max_new_tokens | 最大生成 token 数 |
do_sample | 是否采样生成 |
output_dir | 输出目录 |
这里有个小细节:官方 docs/Evaluation.md 命令里写了 --eval_batch_size 1,但当前 eval.py 的 Arguments 片段中没有看到 eval_batch_size 字段,而代码里 dataloader 的 batch size 是固定写成 1。因此如果你运行时发现 --eval_batch_size 被识别为未知参数,就把这个参数删掉,按源码当前逻辑使用 batch size 1。(GitHub)
八、preprocess_input() 推理输入构造
eval.py 推理时并不是直接使用 dataloader 里的 input_ids,而是重新构造了一次输入。
核心函数:
preprocess_input(text, image_path, tokenizer, clip_image_processor)
它返回:
input_ids
images
images_clip
也就是和训练时一样,仍然有两路图像:
images → Swin / Mask2Former 分割分支
images_clip → SigLIP / LLM 视觉塔
源码中 preprocess_input() 会先调用 preprocess_image(image_path) 构造主分割图像,并用 ImageNet mean/std 归一化;然后调用 preprocess_instruction() 构造文本 input_ids;最后调用 preprocess_image_clip() 构造 SigLIP 输入图像。(GitHub)
九、preprocess_instruction() 做了什么
推理时的 prompt 结构是:
sources = [[
{'from': 'human', 'value': prefix_inst + '\n' + text},
{'from': 'gpt', 'value': ''}
]]
其中 prefix_inst 是:
This is an image
, please doing Reasoning Segmentation according to the following instruction:
然后把用户指令 text 拼进去。
这说明推理时模型输入是:
Human:
This is an image, please doing Reasoning Segmentation according to the following instruction:
{遥感分割指令}
GPT:
模型需要继续生成 answer,并在生成过程中产生 [SEG]。源码中 preprocess_instruction() 使用 conversation template 拼接 prompt,并调用 tokenizer_special_tokens() 保留图像和 refer 特殊 token。(GitHub)
十、do_eval() 推理主循环
do_eval() 是评估核心。
主流程是:
model.eval()
torch.no_grad()
↓
获取 SEG_token_id
↓
遍历 eval_dataloader
↓
读取 text 和 image_path
↓
preprocess_input()
↓
model.inference()
↓
得到 output_ids 和 masks_pred
↓
读取 gt mask
↓
对齐 pred / gt 数量
↓
计算 IoU
↓
输出 split gIoU
源码中 do_eval() 会从 inputs['seg_info'][0]['instruction'] 取文本,从 inputs['seg_info'][0]['image_path'] 取图像路径,然后调用 model.inference(...) 得到 output_ids 和 masks_pred。(GitHub)
十一、model.inference() 关键理解
虽然我们这次重点看 eval.py,但你要把它和 llava_phi.py 接上。
推理阶段大致是:
input_ids + images_clip
↓
LLM generate
↓
生成 output_ids
↓
找到 output_ids 中的 [SEG]
↓
重新或同步取 [SEG] hidden state
↓
eval_seg()
↓
输出 masks_pred
训练时 [SEG] 来自 ground truth answer。
推理时 [SEG] 来自模型自己生成的 answer。
这就是训练和推理最大的区别:
训练:
answer 已知,里面本来就有 [SEG]
推理:
answer 未知,模型必须自己生成 [SEG]
如果推理时模型没有生成 [SEG],那就没有 mask query,最终 masks_pred 可能为空。
十二、为什么评估里要处理 masks_pred is None
源码中有这样的逻辑:
if masks_pred is None:
H, W = images.shape[-2], images.shape[-1]
masks_pred = np.zeros((n_gt, 1, H, W), dtype=np.uint8)
含义是:
如果模型没有预测出 mask,
就用全 0 mask 作为预测结果,
避免评估程序崩溃。
这也说明一个重要问题:
模型如果没有生成 [SEG],评估不一定报错,但 IoU 会很差。
所以复现时不能只看程序能跑完,还要看模型输出是否真的包含 [SEG]。
源码中原本有打印模型输入和输出文本的代码,但被注释掉了。它会把生成文本中的 [SEG] 用颜色标出来。(GitHub)
你调试时建议把这段打开:
input_token_len = input_ids.shape[1]
generated_ids = output_ids[0][input_token_len:]
output_text = tokenizer.decode(generated_ids, skip_special_tokens=True)
print("Model Input:", text)
print("Model Output:", output_text)
你要确认输出里有:
[SEG]
十三、IoU 是怎么计算的
源码中每个预测 mask 和 GT mask 做:
inter = np.logical_and(pred_bin, gt_bin).sum()
union = np.logical_or(pred_bin, gt_bin).sum()
IoU += (inter / union) if union > 0 else 1.0
最后:
print(f"{split} gIoU: {IoU / overall_mask_num}")
也就是说这里的 gIoU 不是严格意义上目标检测里的 generalized IoU,而是所有 mask 的平均 IoU。源码中 do_eval() 会累计 overall_mask_num、I、U 和 IoU,最后打印 split gIoU。(GitHub)
你可以理解为:
每个 mask 计算一个 IoU
↓
所有 mask 求平均
↓
输出 split gIoU
十四、评估时 pred 和 gt 数量如何对齐
源码中有一段很实用:
n_gt = len(gt_masks)
if masks_pred is None:
masks_pred = zeros
n_pred = masks_pred.shape[0]
if n_pred < n_gt:
masks_pred = np.concatenate([...], axis=0)
elif n_pred > n_gt:
masks_pred = masks_pred[:n_gt]
含义:
如果预测 mask 少于 GT,就复制最后一个预测补齐;
如果预测 mask 多于 GT,就截断。
这让评估程序更加稳健,但从研究严谨性看,你要注意:
预测 mask 数量不等于 GT mask 数量,本身就说明模型生成 [SEG] 数量不稳定。
所以你复现时除了看 IoU,还应该统计:
生成 [SEG] 数量
预测 mask 数量
GT mask 数量
三者是否一致
十五、复现评估前的检查清单
在正式跑 eval.py 前,按下面顺序检查。
1. 合并模型目录是否完整
ls outputs/merged_segearth_r2_lasers
至少应该有:
config.json
tokenizer_config.json
special_tokens_map.json
模型权重文件
2. [SEG] token 是否存在
运行:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("outputs/merged_segearth_r2_lasers")
print(tokenizer.encode("[SEG]", add_special_tokens=False))
print(tokenizer.convert_tokens_to_ids("[SEG]"))
你希望看到一个有效 id,而不是 unknown token。
3. 数据路径是否符合代码预期
eval.py 当前写死查找:
json_folders = os.path.join(
data_args.base_data_path,
'rs_reason_seg/LaSeRS/test/annotations'
)
也就是说你的实际目录应该类似:
base_data_path/
└── rs_reason_seg/
└── LaSeRS/
└── test/
├── annotations/
└── images/
这一点和有些文档里直接写 train/images、test/images 的理解可能不同。以当前 eval.py 源码为准。源码中 main() 明确使用 base_data_path/rs_reason_seg/LaSeRS/test/annotations 获取测试 split。(GitHub)
4. vision_tower 路径是否正确
如果你的 SigLIP 放在:
pretrained_model/CLIP/siglip-so400m-patch14-384
评估命令必须传:
--vision_tower pretrained_model/CLIP/siglip-so400m-patch14-384
否则 SiglipImageProcessor.from_pretrained(data_args.vision_tower) 可能找不到正确配置。源码中 main() 会用 data_args.vision_tower 初始化 SiglipImageProcessor。(GitHub)
5. batch size 先保持 1
评估代码中 dataloader batch size 固定为 1。不要一开始改 batch size,因为当前推理代码大量使用:
inputs['seg_info'][0]
inputs['mask_num'][0]
说明作者默认逐样本评估。源码中 dataloader_params 里 batch_size 固定为 1。(GitHub)
十六、建议你修改 eval.py 的几个调试点
为了真正吃透项目,我建议你临时加这些打印。
1. 打印输入文本
在 do_eval() 里:
print("instruction:", text)
print("image_path:", image_path)
确认模型看到的是正确指令。
2. 打印生成文本
取消官方注释,或者加:
input_token_len = input_ids.shape[1]
generated_ids = output_ids[0][input_token_len:]
output_text = tokenizer.decode(generated_ids, skip_special_tokens=False)
print("output_text:", output_text)
print("[SEG] count:", output_text.count("[SEG]"))
重点看:
output_text 是否包含 [SEG]
[SEG] 数量是否等于 gt mask 数量
3. 打印 mask 数量
print("n_gt:", n_gt)
print("n_pred:", None if masks_pred is None else masks_pred.shape[0])
如果长期 masks_pred is None,说明模型没有生成 [SEG] 或 model.inference() 中 [SEG] 解析失败。
4. 保存预测 mask 可视化
官方评估当前主要计算 IoU,没有明显保存彩色可视化结果。你可以在 do_eval() 中加:
save_dir = os.path.join(data_args.output_dir, split)
os.makedirs(save_dir, exist_ok=True)
pred_vis = (pred_bin * 255).astype(np.uint8)
cv2.imwrite(os.path.join(save_dir, f"{idx}_pred.png"), pred_vis)
gt_vis = (gt_bin * 255).astype(np.uint8)
cv2.imwrite(os.path.join(save_dir, f"{idx}_gt.png"), gt_vis)
这样你能直观看到:
模型到底分割了哪里
是完全空白
还是偏移
还是边界粗糙
还是目标类别错了
十七、完整复现闭环建议
你不要直接从全量训练开始,而是按下面做。
第一步:只验证模型能加载
python - <<'PY'
from segearth_r2.utils.builder import load_pretrained_model
model_path = "outputs/merged_segearth_r2_lasers"
mask_config = "segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml"
print("start load")
# 这里按项目 builder 的实际参数补齐
print("load ok")
PY
先不跑数据,确认模型权重没问题。
第二步:只取一张图推理
临时改 eval.py:
if idx >= 1:
break
确认:
能读图
能读文本
能生成 answer
能生成 [SEG]
能得到 masks_pred
第三步:跑一个 split
例如只跑一个测试 annotation 文件。
如果 test/annotations 下有多个 split,不要一开始全部跑。
第四步:跑完整测试集
完整跑:
deepspeed --include localhost:0 --master_port=29500 segearth_r2/eval/eval.py \
--base_data_path /your/path \
--model_path outputs/merged_segearth_r2_lasers \
--vision_tower pretrained_model/CLIP/siglip-so400m-patch14-384 \
--vision_tower_mask pretrained_model/mask2former/model_final_54b88a.pkl \
--mask_config segearth_r2/model/mask_decoder/mask_config/maskformer2_swin_base_384_bs16_50ep.yaml \
--output_dir outputs/eval_res \
--dataloader_num_workers 2
十八、评估常见报错与解决
报错 1:unrecognized arguments: --eval_batch_size
原因:官方文档命令提到 --eval_batch_size 1,但当前 eval.py 的 dataclass 里没有该字段。(GitHub)
解决:删掉这个参数。
报错 2:FileNotFoundError: rs_reason_seg/LaSeRS/test/annotations
原因:base_data_path 指错。
解决:让路径满足:
base_data_path/rs_reason_seg/LaSeRS/test/annotations
或者改 eval.py 里的 json_folders 路径。
报错 3:SiglipImageProcessor 找不到配置
原因:--vision_tower 默认是 pretrained_model/CLIP,但你的 SigLIP 可能在更深目录。
解决:
--vision_tower pretrained_model/CLIP/siglip-so400m-patch14-384
报错 4:masks_pred is None 很多
可能原因:
模型没有生成 [SEG]
[SEG] token 没有正确加入 tokenizer
LoRA 没有合并好
推理 prompt 和训练 prompt 不一致
训练步数太少
优先检查:
print(output_text)
print(output_text.count("[SEG]"))
报错 5:IoU 很低但程序正常
可能原因:
mask 和 [SEG] 顺序错位
GT mask resize 有问题
模型只学会生成 [SEG] 但 mask decoder 没学好
Mask2Former 权重没正确加载
训练步数太少
检查:
pred mask 可视化
gt mask 可视化
[SEG] 数量
pred mask 数量
gt mask 数量
十九、训练、合并、评估三阶段参数必须一致
这是复现的铁律。
| 参数 | 训练 | 合并 | 评估 |
|---|---|---|---|
model_name_or_path / model_path | Mipha-3B | 训练 checkpoint | 合并后模型 |
vision_tower | SigLIP | SigLIP | SigLIP |
vision_tower_mask | Mask2Former 权重 | Mask2Former 权重 | Mask2Former 权重 |
mask_config | 同一个 yaml | 同一个 yaml | 同一个 yaml |
lora_r | 例如 4 | 必须 4 | 合并后无需传 |
[SEG] tokenizer | 添加 | 保存 | 必须存在 |
如果你只记一句话:
训练、合并、评估的 mask_config、vision_tower、vision_tower_mask 必须一致。
二十、到目前为止的完整复现路线
现在我们已经把项目主线串起来了。
1. 安装环境
↓
2. 准备 Mipha-3B / SigLIP / Mask2Former / LaSeRS
↓
3. 修改 train.sh
↓
4. 跑 debug 20 steps
↓
5. 确认 loss_llm / loss_mask / loss_dice / loss_attention 正常
↓
6. 跑完整训练
↓
7. merge_lora_weights.sh 合并 LoRA
↓
8. 检查 tokenizer 中 [SEG]
↓
9. 修改 test.sh / eval.py 路径
↓
10. 单样本推理
↓
11. 可视化 pred mask
↓
12. 跑完整 test split
↓
13. 得到 IoU / gIoU
二十一、你现在应该掌握的核心结论
到这一讲为止,你应该能讲清楚:
1. 为什么训练后不能直接 eval,要先合并 LoRA。
2. merge_lora_weights.sh 如何恢复 ZeRO checkpoint。
3. 合并时 lora_r 必须和训练一致。
4. eval.py 重新构造 input_ids、images、images_clip。
5. 推理时 [SEG] 是模型自己生成的。
6. 如果没有生成 [SEG],masks_pred 可能是 None。
7. eval.py 当前默认 batch size 是 1。
8. IoU 是逐 mask 计算后平均。
9. 评估时必须检查生成文本里的 [SEG] 数量。
10. 训练、合并、评估的 vision_tower、vision_tower_mask、mask_config 必须一致。
下一讲:进入 RTX 5090 复现环境与编译问题
下一讲建议专门解决你最可能遇到的问题:
第 7 讲:RTX 5090 上复现 SegEarth-R2
重点:
1. CUDA 12.8 / PyTorch cu128 选择
2. Detectron2 编译
3. MSDeformAttn 编译
4. flash-attn 是否能装
5. DeepSpeed 安装
6. BF16 / TF32 设置
7. 常见 nvcc、sm_120、gcc、CUDA_HOME 报错
8. 单卡 RTX 5090 的推荐 train.sh
这部分非常关键,因为 SegEarth-R2 不是纯 Python 项目,真正卡人的地方往往不是模型逻辑,而是 Detectron2 + Mask2Former + MSDeformAttn + RTX 5090 编译环境。

4608

被折叠的 条评论
为什么被折叠?



