MSProbe 디버깅 가이드

MSProbe 디버깅 가이드

MSProbe는 AI 모델의 정확도 이상과 수치 오류를 진단하는 디버깅 도구예요. 모델 학습·추론 중 중간 데이터(특성 맵, 가중치, 활성화, 레이어 출력)와 컨텍스트 메타데이터(프롬프트, 텐서 dtype, 하드웨어 구성)를 캡처·모니터링하고, 시각 분석을 지원해서 정확도 저하나 수치 오류(예: NaN/Inf, 출력 드리프트, 예측 불일치)의 근본 원인을 체계적으로 추적해요.

출처: MSProbe 디버깅 가이드

기본 정보

배경 개념: MSProbe 덤핑 레벨

MSProbe는 데이터 덤핑을 위한 정확도 레벨 세 가지를 지원하고, 각각 다른 디버깅 니즈용이에요.

  • L0: 모듈 레벨에서 텐서/통계를 덤프하고 construct.json을 생성해요(시각화에서 네트워크 구조 재구성용). 모델/서브모듈 핸들을 전달해야 해요.
  • L1: torch API 레벨에서 텐서/통계를 덤프해요. 세밀한 API 레벨 수치 검사에 적합해요.
  • mix: L0 + L1을 결합해요. 그래프 재구성수치 비교가 모두 필요한 시나리오에 이상적이에요.

사전 조건: MSProbe 설치

pip로 MSProbe를 설치하세요.

pip install mindstudio-probe --pre

핵심 구성 파라미터

MSProbe는 커스터마이즈된 데이터 덤핑을 위해 JSON 구성 파일을 사용해요. 모든 핵심 파라미터는 아래 표에 정리했고, 기본 JSON 구성도 참고용으로 제공해요.

구성 파라미터 표

필드 설명 필수
task 덤프 작업 유형. 일반적인 PyTorch 값은 "statistics""tensor"예요. statistics 작업은 텐서 통계(평균, 분산, 최대, 최소 등)를 수집하고, tensor 작업은 임의 텐서를 캡처해요.
dump_path 덤프 결과가 저장될 디렉토리. 생략하면 MSProbe가 기본 경로를 사용해요. 아니오
rank 샘플링할 rank. 빈 리스트는 모든 rank를 수집해요. 단일 카드 작업에서는 이 필드를 []로 설정해야 해요. 아니오
step 샘플링할 토큰 반복. 빈 리스트는 모든 반복을 의미해요. 아니오
level 덤프 레벨 문자열("L0", "L1" 또는 "mix"). L0nn.Module을, L1torch.api를 대상으로 하고, mix는 둘 다 수집해요.
async_dump 비동기 덤프 활성화 여부(PyTorch statistics/tensor 작업 지원). 기본값은 false예요. 아니오
scope 덤프 범위 커스터마이즈. 도구의 네이밍 규칙을 따르는 모듈·API 이름 두 개를 제공해 범위를 고정하면, 두 이름 사이의 데이터만 덤프돼요. 빈 리스트는 모든 모듈 또는 torch API를 덤프해요.

예:
"scope": ["Module.conv1.Conv2d.forward.0", "Module.fc2.Linear.forward.0"]
"scope": ["Tensor.add.0.forward", "Functional.square.2.forward"]

level 설정이 무엇을 제공할 수 있는지 결정해요 — level=L0면 모듈, level=L1이면 API, level=mix면 모듈 또는 API.
아니오
list 덤프 리스트 커스터마이즈, 리스트의 요소만 덤프해요. 빈 리스트는 모든 모듈 또는 torch API를 덤프해요. 옵션:

•PyTorch eager 모드에서 특정 API의 전체 이름을 제공해 그 API만 덤프. 예: "list": ["Tensor.permute.1.forward", "Tensor.transpose.2.forward", "Torch.relu.3.backward"].
level=mix일 때 모듈 이름을 제공하면 그 모듈이 실행되는 동안 생성된 모든 것으로 덤프가 확장돼요. 예: "list": ["Module.module.language_model.encoder.layers.0.mlp.ParallelMlp.forward.0"].
"list": ["relu"]처럼 일부 문자열을 제공하면 이름에 그 문자열을 포함하는 모든 API를 덤프해요. level=mix에서는 이름에 문자열을 포함하는 모듈도 확장돼요.
아니오

기본 구성

{
  "task": "statistics",
  "dump_path": "./dump_path",
  "rank": [],
  "step": [],
  "level": "L1",
  "async_dump": false,
  "statistics": {
    "scope": [],
    "list": [],
    "data_mode": [
      "all"
    ],
    "summary_mode": "statistics"
  },
  "tensor": {
    "scope": [],
    "list": [],
    "data_mode": [
      "all"
    ],
    "file_format": "npy"
  },
  "acc_check": {
    "white_list": [],
    "black_list": [],
    "error_data_path": "./"
  }
}

출력

덤프 파일은 정의한 dump_path에 작성돼요. 보통 다음을 포함해요.

  • dump.json: dtype, shape, min, max, mean, L2 norm, requires_grad 같은 메타데이터 기록.
  • construct.json: 계층적 구조 설명. levelL0 또는 mix일 때(시각화 필수) 내용이 비어 있지 않아요.
  • stack.json: API/Module의 호출 스택 정보 기록.
  • dump_tensor_data: tasktensor일 때 생성되며 수집된 텐서 데이터를 저장.

자세한 내용은 덤프 디렉토리 설명을 보세요.

참고: MSProbe가 활성화되면 cuda graph가 비활성화됩니다(disable_cuda_graph=True). MSProbe는 eager 모드에서만 덤프를 지원하기 때문이에요. warmup도 비활성화됩니다(skip_server_warmup=True). 이 단계의 데이터를 덤프할 필요가 없기 때문이에요.

End-to-end 예시

MSProbe의 전체 디버깅 워크플로는 **활성화(Enable) → 데이터 수집(Collect Data) → 시각화(Visualize) → 근본 원인 분석(Analyze Root Cause)**을 따라요. 아래는 SGLang 기반 모델 추론 디버깅의 흔한 E2E 예시예요.

예시: 커스텀 구성으로 고급 디버깅

타깃 디버깅(예: 특정 rank/step의 통계 데이터만 수집, 그래프 재구성 + 수치 비교를 위한 mix 레벨 활성화)과 문제 vs. 벤치마크 비교를 통한 근본 원인 분석에 적합해요.

Step 1: 활성화

커스텀 구성 JSON 준비

msprobe-config.json 생성(rank0/1, step0/1에 대한 통계 데이터 덤프, mix 레벨):

{
  "task": "statistics",
  "dump_path": "./problem_dump",
  "rank": [
    0,
    1
  ],
  "step": [
    0,
    1
  ],
  "level": "mix",
  "async_dump": false,
  "statistics": {
    "scope": [],
    "list": [],
    "data_mode": [
      "all"
    ],
    "summary_mode": "statistics"
  }
}
SGLang에서 커스텀 구성으로 MSProbe 활성화

SGLang 서버를 실행하고 --msprobe-dump-config로 구성 파일 경로를 지정하세요.

python3 -m sglang.launch_server \
 --model-path Qwen/Qwen2.5-0.5B-Instruct \
 --host 127.0.0.1 \
 --port 1027 \
 --msprobe-dump-config /home/msprobe-config.json

Step 2: 데이터 수집

문제 측과 벤치마크 측 덤프 데이터 수집

일반 추론 요청을 보내 모델 실행을 트리거해요(MSProbe는 요청 처리 중 자동으로 데이터를 수집해요).

curl -H "Content-type: application/json" \
 -X POST \
 -d '{
     "model": "Qwen/Qwen2.5-0.5B-Instruct",
     "messages": [
         {
             "role": "user",
             "content": "Hello, my name is"
         }
     ],
     "max_tokens": 10
 }' \
 http://127.0.0.1:1027/v1/chat/completions
  • 문제 측: 위 SGLang 서버(정확도/수치 문제가 있는)를 실행하고 추론 요청을 보내면, 덤프 데이터가 ./problem_dump에 저장돼요.
  • 벤치마크 측: 같은 커스텀 구성으로 정상 SGLang 서버(문제 없는, 예: 안정 버전 프레임워크/연산자)를 실행하고 같은 추론 요청을 보내고, 덤프 디렉토리를 ./bench_dump로 이름을 바꿔요.

핵심 요구사항: 문제 측과 벤치마크 측 덤프는 유효한 비교를 위해 같은 입력과 샘플링 지점(rank/step)을 사용해야 해요.

생성된 덤프 파일 확인

덤프 파일은 정의한 ./problem_dump./bench_dump에 저장되고, 이후 분석에 필요한 핵심 파일을 포함해요.

  • dump.json: API와 모듈의 텐서 메타데이터(dtype, shape, min/max/mean, L2 norm, requires_grad 등) 기록.
  • stack.json: API와 모듈의 호출 스택 정보 기록.
  • construct.json: 시각화에 필요한 계층적 구조 설명, 내용이 비어 있지 않아요.

Step 3: 시각화

문제 vs. 벤치마크 비교 시각화(멀티 rank)

멀티 rank 비교 시각화 파일을 생성해요(mix 레벨은 그래프 재구성용 construct.json을 생성해요).

msprobe graph_visualize -tp ./problem_dump/step0 -gp ./bench_dump/step0 -o ./graph_output
  • -tp: 문제 측 덤프 데이터 경로
  • -gp: 벤치마크 측 덤프 데이터 경로
  • -o: 시각화 파일 출력 디렉토리

오버플로우 체크(NaN/Inf 감지)가 필요하면 -oc 파라미터를 지정하세요.

msprobe graph_visualize -tp ./problem_dump/step0 -gp ./bench_dump/step0 -o ./graph_output -oc

비교 또는 빌드 작업이 끝나면 graph_output 아래에 compare_{timestamp}.vis.db 파일이 생성돼요.

덤프된 데이터는 Matplotlib이나 Excel 같은 시각화 도구로 생성된 표나 차트를 이용해 차이를 시각화·분석하는 데 쓸 수 있어요.

TensorBoard 실행

TensorBoard를 시작하세요.

tensorboard --logdir ./graph_output --bind_all --port 6006

Step 4: 근본 원인 분석

근본 원인 찾기

TensorBoard에서의 근본 원인 분석:

  • 발산 노드(정확도/수치 차이가 있는)는 빨간색으로 강조돼요(진한 빨강일수록 차이가 큼).
  • 발산 노드를 클릭하면 상세 텐서 데이터(입력/출력, 파라미터)와 API/모듈 호출 스택을 볼 수 있어요.
  • 검색/필터 기능으로 핵심 레이어/API(예: "relu", "conv")를 빠르게 찾아요.
  • UI에서 rank/step을 전환해 크로스-rank/크로스-step 발산을 확인해요.
  • 특정 노드에서 NaN/Inf 값(수치 불안정의 직접 원인)이 있는지 오버플로우 체크 탭을 확인하세요.
근본 원인 검증

발산 노드(예: 비정상 텐서 값을 가진 특정 Conv 레이어나 torch API)를 찾은 뒤, 다음으로 검증해요.

  • 구성 파일의 scope/list로 덤프 범위를 이 노드로 좁혀 세밀한 데이터를 수집.
  • 문제가 있는 레이어/API를 수정(예: 연산자 교체, dtype 조정)하고 디버깅 워크플로를 다시 실행해 문제가 해결됐는지 확인.

문제 해결

덤프 파일이 생성되지 않음

  1. pip show mindstudio-probe로 MSProbe 설치 여부를 확인하세요. 설치돼 있으면 MSProbe 버전 정보가 출력돼요. 설치되지 않았다면 pip install mindstudio-probe --pre로 설치하세요.
  2. --msprobe-dump-config 파라미터가 올바른 JSON 파일 경로를 가리키는지 확인하세요.

덤프 파일이 너무 큼(과도한 데이터)

  1. "tensor" 대신 task: "statistics"로 시작해 텐서 통계만 수집하세요(raw 텐서 덤프 방지).
  2. scope 필드로 덤프 범위를 좁히세요(시작/끝 모듈/API 지정).
  3. list 필드로 덤프 대상을 필터링하세요(특정 모듈/API 또는 하위 문자열만 덤프).
  4. 특정 rankstep을 샘플링하세요(모든 rank/반복 덤프 방지).

TensorBoard 시각화 실패

  1. construct.json이 비어 있지 않은지 확인하세요(level: L0 또는 mix 필요 — L1은 그래프 파일을 생성하지 않음).
  2. -tp(문제 덤프)와 -gp(벤치마크 덤프) 경로가 유효한 rank/step 서브디렉토리(예: step0/rank0)를 가리키는지 확인하세요.
  3. MSProbe 버전이 최신인지 확인하세요(pip install mindstudio-probe --pre --upgrade로 재설치).
  4. TensorBoard가 설치돼 있고 --logdir 파라미터가 .vis.db 파일이 들어 있는 디렉토리를 가리키는지 확인하세요(파일 자체가 아니라).

수치 비교에 발산이 없는데 모델 정확도가 낮음

  1. 덤프 step 범위를 넓히세요(후기 발산을 위해 더 많은 토큰 반복 확인).
  2. task: "tensor"로 전환하세요(통계는 raw 텐서 데이터의 미묘한 수치 차이를 가릴 수 있음).
  3. 문제 측과 벤치마크 측 덤프가 같은 입력 데이터/하드웨어 구성을 사용하는지 확인하세요(다른 입력은 무효 비교를 만듦).
  4. TensorBoard의 manual mapping 기능을 사용하세요(자동 매핑은 커스텀 모델의 일부 노드를 놓칠 수 있음).

부록

덤프 디렉토리 설명

├── problem_dump or bench_dump
│   ├── step0
│   │   ├── rank0
│   │   │   ├── dump_tensor_data
│   │   │   │    ├── Tensor.permute.1.forward.pt
│   │   │   │    ├── Functional.linear.5.backward.output.pt    # Format: {api_type}.{api_name}.{call_count}.{forward/backward}.{input/output}.{arg_index}.
│   │   │   │    │                                              # arg_index is the nth input or output of the API. If an input is a list, keep numbering with decimals (e.g., 1.1 is the first element of the first argument).
│   │   │   │    ├── Module.conv1.Conv2d.forward.0.input.0.pt          # Format: {Module}.{module_name}.{class_name}.{forward/backward}.{call_count}.{input/output}.{arg_index}.
│   │   │   │    ├── Module.conv1.Conv2d.forward.0.parameters.bias.pt  # Module parameter data: {Module}.{module_name}.{class_name}.forward.{call_count}.parameters.{parameter_name}.
│   │   │   │    └── Module.conv1.Conv2d.parameters_grad.weight.pt     # Module parameter gradients: {Module}.{module_name}.{class_name}.parameters_grad.{parameter_name}. Gradients do not include call_count because the same gradient updates all invocations.
│   │   │   │                                                          # When the `model` argument passed to dump is a List[torch.nn.Module] or Tuple[torch.nn.Module], module-level data names also include the index inside the list ({Module}.{index}.*), e.g., Module.0.conv1.Conv2d.forward.0.input.0.pt.
│   │   │   ├── dump.json
│   │   │   ├── stack.json
│   │   │   ├── dump_error_info.log
│   │   │   └── construct.json
│   │   ├── rank1
│   │   │   ├── dump_tensor_data
│   │   │   │   └── ...
│   │   │   ├── dump.json
│   │   │   ├── stack.json
│   │   │   ├── dump_error_info.log
│   │   │   └── construct.json
│   │   ├── ...
│   │   │
│   │   └── rank7
│   ├── step1
│   │   ├── ...
│   ├── step2
  • rank: 디바이스 ID. 각 카드는 해당 rank{ID} 디렉토리에 자신의 데이터를 작성해요. 분산이 아닌 시나리오에서는 디렉토리가 단순히 rank로 명명돼요.
  • dump_tensor_data: 수집된 텐서 데이터를 저장.
  • dump.json: 각 API 또는 모듈의 forward 데이터 통계. 이름, dtype, shape, max, min, mean, L2 norm(L2 분산의 제곱근), 그리고 summary_mode="md5"일 때 CRC-32를 포함해요. 자세한 내용은 dump.json 파일 설명을 보세요.
  • dump_error_info.log: 덤프 도구가 오류를 만났을 때만 존재하며 실패 로그를 기록.
  • stack.json: API/모듈의 호출 스택.
  • construct.json: 계층적 구조 설명. level=L1일 때 비어 있음.

dump.json 파일 설명

L0 레벨

L0 dump.json은 모듈의 forward/backward I/O를 파라미터·파라미터 그래디언트와 함께 포함해요. PyTorch의 Conv2d를 예로 들면, 네트워크 코드는 이렇게 생겼어요.

output = self.conv2(input) # self.conv2 = torch.nn.Conv2d(64, 128, 5, padding=2, bias=True)

dump.json은 다음 엔트리를 포함해요.

  • Module.conv2.Conv2d.forward.0: 모듈의 forward 데이터. input_args는 위치 입력, input_kwargs는 키워드 입력, output은 forward 출력, parameters는 가중치/바이어스를 저장해요.
  • Module.conv2.Conv2d.parameters_grad: 파라미터 그래디언트(weight와 bias).
  • Module.conv2.Conv2d.backward.0: 모듈의 backward 데이터. input은 모듈로 흘러 들어오는 그래디언트(forward 출력의 그래디언트), output은 흘러 나가는 그래디언트(모듈 입력의 그래디언트)예요.

참고: 덤프 API에 전달된 model 파라미터가 List[torch.nn.Module] 또는 Tuple[torch.nn.Module]이면, 모듈 레벨 이름에 리스트 내 인덱스가 포함돼요({Module}.{index}.*). 예: Module.0.conv1.Conv2d.forward.0.

다음은 L0 dump.json의 전체 예시예요.

{
  "task": "tensor",
  "level": "L0",
  "framework": "pytorch",
  "dump_data_dir": "/dump/path",
  "data": {
    "Module.conv2.Conv2d.forward.0": {
      "input_args": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            8,
            16,
            14,
            14
          ],
          "Max": 1.638758659362793,
          "Min": 0.0,
          "Mean": 0.2544615864753723,
          "Norm": 70.50277709960938,
          "requires_grad": true,
          "data_name": "Module.conv2.Conv2d.forward.0.input.0.pt"
        }
      ],
      "input_kwargs": {},
      "output": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            8,
            32,
            10,
            10
          ],
          "Max": 1.6815717220306396,
          "Min": -1.5120246410369873,
          "Mean": -0.025344856083393097,
          "Norm": 149.65576171875,
          "requires_grad": true,
          "data_name": "Module.conv2.Conv2d.forward.0.output.0.pt"
        }
      ],
      "parameters": {
        "weight": {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            5,
            5
          ],
          "Max": 0.05992485210299492,
          "Min": -0.05999220535159111,
          "Mean": -0.0006165213999338448,
          "Norm": 3.421217441558838,
          "requires_grad": true,
          "data_name": "Module.conv2.Conv2d.forward.0.parameters.weight.pt"
        },
        "bias": {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32
          ],
          "Max": 0.05744686722755432,
          "Min": -0.04894155263900757,
          "Mean": 0.006410328671336174,
          "Norm": 0.17263513803482056,
          "requires_grad": true,
          "data_name": "Module.conv2.Conv2d.forward.0.parameters.bias.pt"
        }
      }
    },
    "Module.conv2.Conv2d.parameters_grad": {
      "weight": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            5,
            5
          ],
          "Max": 0.018550323322415352,
          "Min": -0.008627401664853096,
          "Mean": 0.0006675920449197292,
          "Norm": 0.26084786653518677,
          "requires_grad": false,
          "data_name": "Module.conv2.Conv2d.parameters_grad.weight.pt"
        }
      ],
      "bias": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32
          ],
          "Max": 0.014914230443537235,
          "Min": -0.006656786892563105,
          "Mean": 0.002657240955159068,
          "Norm": 0.029451673850417137,
          "requires_grad": false,
          "data_name": "Module.conv2.Conv2d.parameters_grad.bias.pt"
        }
      ]
    },
    "Module.conv2.Conv2d.backward.0": {
      "input": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            8,
            32,
            10,
            10
          ],
          "Max": 0.0015069986693561077,
          "Min": -0.001139344065450132,
          "Mean": 3.3215508210560074e-06,
          "Norm": 0.020567523315548897,
          "requires_grad": false,
          "data_name": "Module.conv2.Conv2d.backward.0.input.0.pt"
        }
      ],
      "output": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            8,
            16,
            14,
            14
          ],
          "Max": 0.0007466732058674097,
          "Min": -0.00044813455315306783,
          "Mean": 6.814070275140693e-06,
          "Norm": 0.01474067009985447,
          "requires_grad": false,
          "data_name": "Module.conv2.Conv2d.backward.0.output.0.pt"
        }
      ]
    }
  }
}

L1 레벨

L1 dump.json은 API의 forward/backward I/O를 기록해요. PyTorch의 relu 함수를 예로 들면(output = torch.nn.functional.relu(input)), 파일은 다음을 포함해요.

  • Functional.relu.0.forward: API의 forward 데이터. input_args는 위치 입력, input_kwargs는 키워드 입력, output은 forward 출력을 저장해요.
  • Functional.relu.0.backward: API의 backward 데이터. input은 forward 출력의 그래디언트, output은 forward 입력으로 흘러 돌아가는 그래디언트예요.

다음은 L1 dump.json의 전체 예시예요.

{
  "task": "tensor",
  "level": "L1",
  "framework": "pytorch",
  "dump_data_dir": "/dump/path",
  "data": {
    "Functional.relu.0.forward": {
      "input_args": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            28,
            28
          ],
          "Max": 1.3864083290100098,
          "Min": -1.3364859819412231,
          "Mean": 0.03711778670549393,
          "Norm": 236.20692443847656,
          "requires_grad": true,
          "data_name": "Functional.relu.0.forward.input.0.pt"
        }
      ],
      "input_kwargs": {},
      "output": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            28,
            28
          ],
          "Max": 1.3864083290100098,
          "Min": 0.0,
          "Mean": 0.16849493980407715,
          "Norm": 175.23345947265625,
          "requires_grad": true,
          "data_name": "Functional.relu.0.forward.output.0.pt"
        }
      ]
    },
    "Functional.relu.0.backward": {
      "input": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            28,
            28
          ],
          "Max": 0.0001815402356442064,
          "Min": -0.00013352684618439525,
          "Mean": 0.00011915402356442064,
          "Norm": 0.007598237134516239,
          "requires_grad": false,
          "data_name": "Functional.relu.0.backward.input.0.pt"
        }
      ],
      "output": [
        {
          "type": "torch.Tensor",
          "dtype": "torch.float32",
          "shape": [
            32,
            16,
            28,
            28
          ],
          "Max": 0.0001815402356442064,
          "Min": -0.00012117840378778055,
          "Mean": 2.0098118724831693e-08,
          "Norm": 0.006532244384288788,
          "requires_grad": false,
          "data_name": "Functional.relu.0.backward.output.0.pt"
        }
      ]
    }
  }
}

mix 레벨

mix dump.json은 L0와 L1 레벨 데이터를 모두 포함해요. 파일 포맷은 위 예시와 같아요.

더 알아보기 (Learn more)