블록체인 그래프 분류를 위한 AMD MI300X 기반 Qwen2-VL 파인튜닝(Fine-Tuning): 문서에 없는 내용들
요약
AMD MI300X 하드웨어를 활용하여 Qwen2-VL 모델을 블록체인 그래프 분류용으로 파인튜닝하는 기술적 과정을 다룹니다. GNN 대신 비전 모델을 사용하여 그래프의 구조적 패턴을 시각적으로 추론하고 자연어로 근거를 제시하는 실용적인 파이프라인 구축 방법을 설명합니다.
핵심 포인트
- 블록체인 트랜잭션의 구조적 신호를 포착하기 위해 시각적 추론 활용
- GNN 대비 설명 가능한 AI(XAI)로서의 Qwen2-VL 장점 강조
- AMD MI300X의 대용량 HBM3 메모리를 활용한 효율적인 파인튜닝
- LoRA 기법과 vLLM을 이용한 모델 학습 및 서빙 워크플로우
요약(TL;DR): 블록체인 트랜잭션의 그래프 시각화에는 임베딩(embedding) 기반 분류기에는 본질적으로 보이지 않는 구조적 신호가 포함되어 있습니다. Peel-chain 믹서는 하나의 입력과 거의 동일한 금액의 수많은 출력을 갖는 독특한 팬아웃(fan-out) 패턴을 생성하며, 이는 즉각적으로 시각적 모티프로 나타납니다.
📖 읽기 시간: 약 23분
이 글의 내용
- 문제점: 블록체인 보안에는 임베딩(embeddings)뿐만 아니라 시각적 추론(visual reasoning)이 필요함
- 하드웨어 및 소프트웨어 스택: 시작하기 전에 실제로 필요한 것들
- 비전 파인튜닝(Vision Fine-Tuning)을 위한 블록체인 그래프 데이터셋 준비
- MI300X에서 LoRA를 이용한 파인튜닝(Fine-Tuning): 설정, 명령어 및 실패 모드
- 추론(Inference): ROCm 상의 vLLM을 사용하여 파인튜닝된 체크포인트 서빙하기
- 실제 결과 및 솔직한 트레이드오프(trade-offs)
- 파이프라인 운영화: 지속적인 실행 유지
문제점: 블록체인 보안에는 임베딩(embeddings)뿐만 아니라 시각적 추론(visual reasoning)이 필요함
블록체인 트랜잭션의 그래프 시각화에는 임베딩(embedding) 기반 분류기에는 본질적으로 보이지 않는 구조적 신호가 포함되어 있습니다. Peel-chain 믹서는 하나의 입력과 거의 동일한 금액의 수많은 출력을 갖는 독특한 팬아웃(fan-out) 패턴을 생성하며, 이는 즉각적으로 시각적 모티프로 나타나지만 그래프를 특징 벡터(feature vector)로 평탄화(flatten)하면 희석되어 버립니다. 레이어링 공격(layering attacks)을 나타내는 루프 패턴, 조정된 지갑 활동을 시사하는 밀집된 클러스터(clusters), 특정 거래소 입금 흐름의 비대칭적인 별 모양: 이것들은 기하학(geometry)으로 읽힙니다. 텍스트 모델은 토큰(token) 시퀀스를 처리하며, 공간적 인접성(spatial adjacency)에 대한 고유한 개념이 없습니다. 만약 렌더링된 트랜잭션 그래프가 인간 분석가가 2초 만에 식별할 수 있는 방식으로 서로 다르게 보인다면, 비전 모델(vision model)은 정확히 그 차이점을 활용할 가능성이 높습니다.
전용 그래프 신경망 (Graph Neural Network, GNN) 대신 Qwen2-VL을 선택하는 이유는 운영상의 실용성 (operational pragmatism) 때문입니다. GNN은 별도의 그래프 구축 파이프라인과 자체적인 학습 루프 (training loop)가 필요하며, 분류 레이블 (classification label) 외에는 아무것도 생성하지 못합니다. 특정 트랜잭션 클러스터가 왜 알려진 믹싱 패턴 (mixing pattern)과 일치하는지 그 이유를 설명하라고 요구할 수 없습니다. 반면 Qwen2-VL은 단일 체크포인트 (checkpoint)로부터 분류 결과와 자연어 기반의 근거 (rationale)를 동시에 제공하며, 이는 감사 추적 (audit trails)이 필요하거나 분석가가 경계선에 있는 사례 (borderline cases)를 검토해야 할 때 매우 중요합니다. 또 다른 요인은 이미 많은 블록체인 분석 파이프라인에 렌더링된 이미지들이 존재한다는 점입니다. Gephi 내보내기 파일, 커스텀 NetworkX 렌더링, 심지어 블록 탐색기 (block explorer)의 스크린샷까지 포함됩니다. 이러한 결과물들을 재사용한다는 것은 그래프 직렬화 (graph serialization) 과정을 완전히 건너뛰고, 이미 보유하고 있는 데이터를 사용하여 즉시 파인튜닝 (fine-tuning) 단계로 넘어갈 수 있음을 의미합니다.
MI300X는 제약 조건 계산 (constraint calculus)을 크게 변화시킵니다. 192 GB HBM3 통합 메모리 풀 (unified memory pool) 덕분에 Qwen2-VL-7B (BF16 기준 약 16~18 GB)는 물론, 72B 변체 (BF16 기준 약 145 GB)조차 시스템 RAM이나 NVMe로의 텐서 오프로딩 (tensor offloading) 없이 로드할 수 있습니다. 이는 매우 중요한데, 오프로딩은 조정하기 까다로운 방식으로 추론 지연 시간 (inference latency)을 급격히 증가시키기 때문입니다. 이를 통해 VRAM 예산과 싸우는 대신 실제 처리량 (throughput)에 집중할 수 있게 됩니다. 다만 주의할 점은 ROCm입니다. AMD의 ROCm 스택은 상당히 개선되었지만, 이 아키텍처를 실제 운영 환경에 도입하기 전에 사용 중인 특정 ROCm 버전(2025년 중반 기준 6.x)이 Qwen2-VL이 의존하는 플래시 어텐션 커널 (flash-attention kernels) 및 비전 인코더 연산 (vision encoder ops)에 대해 안정적인 지원을 제공하는지 감사해야 합니다. transformers 통합은 작동하지만, ROCm 환경에서의 flash-attention-2는 CUDA 빌드가 아닌 flash-attn ROCm 포크 (fork)가 필요하며, 이러한 차이점은 Qwen2-VL 모델 카드에 명확하게 문서화되어 있지 않습니다.
# 무엇보다 먼저 ROCm이 MI300X를 인식하는지 확인하십시오
rocm-smi --showproductname
# 예상 결과: VRAM 항목 아래 MI300X 192 GB가 표시됨
...
여기서 흔히 발생하는 실패 사례는 MI300X 호스트에서 pip를 통해 표준 torch CUDA wheel을 설치하고, ROCm의 CUDA 호환성 심(shim)을 통해 조용히 "작동"하게 두는 것입니다. 이는 심(shim)이 지원하지 않는 연산(op)을 만날 때까지 지속되다가, 학습 도중 모호한 커널 실행(kernel launch) 오류를 발생시키며 중단됩니다. ROCm 전용 wheel을 명시적으로 설치하십시오: AMD 인덱스인 https://download.pytorch.org/whl/rocm6.0에서 torch==2.3.0+rocm6.0을 설치하세요. 모델 코드를 다루기 전에 HIP가 null이 아닌지 확인하십시오. 시각-언어 파인튜닝(vision-language fine-tuning)이 더 넓은 로컬 AI 스택에서 어디에 위치하는지 평가하려는 독자라면, AI Coding Tools in 2026: Cloud Copilots vs Local Models 가이드를 참조하십시오. 특히 추론 하드웨어(inference hardware) 섹션은 자체 호스팅된 파인튜닝된 체크포인트(fine-tuned checkpoints)가 설정 비용을 들일 가치가 있게 만드는 로컬 대 클라우드 API 의존성 트레이드오프(tradeoff)를 다룹니다.
하드웨어 및 소프트웨어 스택: 시작 전 실제로 필요한 것들
ROCm 버전 확인은 Python 환경을 생성하기 전, 모델 가중치(model weights)를 가져오기 전, 그 무엇을 하기 전에도 이루어져야 합니다. Qwen2-VL은 flash-attention-2에 대한 강력한 의존성(hard dependency)을 가지고 있으며, ROCm 호환 flash-attn wheel은 ROCm 6.1 버전부터 MI300X에서 안정적으로 사용 가능해졌습니다. 이전 버전들은 컴파일에 실패하거나, 즉각적으로 감지할 수 있는 충돌(crash) 대신 하이퍼파라미터(hyperparameter) 문제처럼 보이는 성능 저하된 어텐션(attention) 출력을 생성했습니다. 가장 먼저 다음을 실행하십시오:
# Python 환경을 건드리기 전에 확인
rocm-smi --showdriverversion
...
PyTorch 설치는 단순히 플래그만 다른 표준 CUDA 경로가 아닙니다. ROCm ABI가 중요합니다. Nightly ROCm 6.1 빌드는 MI300X 메모리 컨트롤러에서 작동하는 torch.bfloat16과 flash-attn을 위한 올바른 HIP 커널 디스패치(kernel dispatch)를 제공합니다. Qwen2-VL 파인튜닝을 위해 실제로 결합되어 작동하는 전체 스택은 다음과 같습니다:
# ROCm 6.1 ABI와 함께 PyTorch 설치 — 기본 인덱스를 사용하지 마십시오
pip install torch torchvision --index-url https://download.pytorch.org/whl/rocm6.1
...
VRAM 상에서: bf16 형식의 Qwen2-VL-7B 가중치는 약 15 GB를 차지합니다. 이는 MI300X의 192 GB HBM3 환경에서는 여유롭게 느껴지지만, 실제 학습 시 점유하는 메모리(training footprint)는 완전히 다른 숫자입니다. AdamW 옵티마이저(Optimizer) 상태는 파라미터 점유량의 약 2배를 추가하며, 448×448 해상도의 그래프 이미지 배치 — 특히 Qwen2-VL이 사용하는 동적 해상도 전처리(dynamic resolution preprocessing)를 포함한 멀티 프레임 입력 — 를 사용하면 프로세스당 총 할당량이 쉽게 40–60 GB까지 치솟습니다. 이 범위는 MI300X가 문제없이 구동되는 수준이지만, 32 GB급 소비자용 그래픽 카드를 사용한다면 공격적인 그래디언트 체크포인팅(gradient checkpointing), 즉 accelerate 설정에서 activation_checkpointing=True를 적용하고, 배치 크기(batch size)를 1로 설정한 뒤 그래디언트 누적(gradient accumulation)을 사용해야 합니다. 불가능한 것은 아니지만, 압축되지 않은 상태로 실행할 때보다 처리량(throughput)이 40–50% 감소할 것을 예상해야 합니다.
호스트의 Python 환경을 오염시키지 않도록 ROCm을 격리하는 Docker 베이스라인은 공유 환경이나 반복적인 설정 작업 시 필수적입니다. AMD의 공식 이미지는 검증된 시작점을 제공합니다:
# 재현 가능한 MI300X 파인튜닝(Fine-Tuning) 환경을 위한 Dockerfile 발췌
FROM rocm/pytorch:rocm6.1_ubuntu22.04_py3.10_pytorch_2.3.0
...
# requirements.txt — 버전을 고정하거나, 아니면 오류를 감수해야 합니다
transformers==4.45.2
accelerate==0.34.2
...
AMD 문서에서 간과하는 한 가지는 --device=/dev/kfd와 --device=/dev/dri 플래그가 컨테이너 내부에서 GPU에 접근하기 위해 둘 다 반드시 필요하다는 점입니다. 이 중 하나라도 누락되면 마치 ROCm 설치 문제처럼 보이는, 도움이 되지 않는 "no GPU found" 에러가 발생합니다. 또한, 그래프 이미지의 멀티 워커(multi-worker) DataLoader 프리페칭(prefetching)을 수행하는 경우 /dev/shm을 넉넉한 크기로 마운트하십시오. 기본값인 64 MB는 더 큰 배치 처리 시 조용히 시스템이 멈추는(silent hangs) 현상을 유발할 수 있습니다. 시작점으로 --shm-size=16g를 사용하십시오.
비전 파인튜닝(Vision Fine-Tuning)을 위한 블록체인 그래프 데이터셋 준비
렌더링 파이프라인(rendering pipeline)은 대부분의 파인튜닝 시도가 조용히 실패하는 지점입니다
단 하나의 그래디언트 (gradient)가 가중치 (weights)를 업데이트하기 전에, 여기서 내리는 시각적 인코딩 (visual encoding) 결정이 모델이 블록체인 토폴로지 (topology)를 학습할지, 아니면 Matplotlib의 기본 설정값을 학습할지를 결정하게 됩니다. 핵심 통찰은 다음과 같습니다: Qwen2-VL은 이미지를 448×448 타일 패치 (tile patches)로 처리하므로, 정확히 해당 해상도로 렌더링하면 그렇지 않을 경우 가짜 훈련 신호 (spurious training signal)가 될 수 있는 보간 아티팩트 (interpolation artifact)를 제거할 수 있습니다. 모든 서브그래프 (subgraph)는 448×448 해상도의 PNG로 렌더링됩니다. 업스케일링 (upscaling), 패딩 (padding), 또는 "대충 비슷하게" 처리하는 방식은 허용되지 않습니다. 모델은 훈련 이미지 전체에서 일관되게 나타나는 것을 학습하며, 만약 렌더링 파이프라인 (rendering pipeline)에 비결정론적 요소 (non-determinism, 예: 무작위 레이아웃 시드, 자동 축 스케일링)가 있다면, 모델은 당신이 중요하게 생각하는 그래프 구조 대신 그 요소를 학습하게 될 것입니다.
노드 색상 (node color)과 엣지 가중치 (edge weight)는 두 가지 의미론적 채널 (semantic channels)이며, 이를 엄격하게 고정해야 합니다. 제가 베이스라인으로 사용하는 렌더링 함수는 다음과 같습니다:
import networkx as nx
import matplotlib
matplotlib.use('Agg') # 디스플레이가 필요 없으며, 헤드리스 서버 (headless servers)에서의 스레딩 문제를 방지합니다
...
엣지 가중치에 적용하는 log1p 정규화 (normalization)는 보기보다 훨씬 중요합니다. 원시 BTC 트랜잭션 볼륨 (transaction volumes)은 약 6자릿수(six orders of magnitude)에 걸쳐 분포합니다. 예를 들어 0.000001 BTC를 보내는 더스트 공격 (dust attack)과 거래소가 500 BTC를 이동시키는 경우를 비교해 보십시오. 로그 스케일링 (log scaling)이 없다면 1 BTC와 2 BTC 엣지 사이의 시각적 차이는 보이지 않겠지만, 0.001 BTC 엣지와 500 BTC 엣지는 극단적으로 다른 선 굵기를 생성하여 레이아웃을 시각적으로 지배하게 될 것입니다. 로그 정규화를 수행한 다음, 가시적인 픽셀 범위 내로 선형 스케일링 (linearly scale)하고, 전체 데이터셋에 걸쳐 해당 경계값 (bounds)을 고정하십시오.
레이블 스키마 (Label schema): 이를 뒷받침할 데이터를 확보하기 전까지는 다중 클래스 (multiclass) 분류를 지양하십시오
이진 분류 (Binary classification) — 의심스러운 것(suspicious) vs. 정상적인 것(benign) — 은 단순히 더 간단하기 때문만이 아니라, 올바른 시작점입니다. 더 중요한 이유는 데이터 예산 (data budgeting) 때문입니다. 4개의 카테고리(mixer cluster, exchange cluster, normal transfer, dusting attack)를 가진 다중 클래스 (multiclass) 분류는 모델이 단지 몇 개의 예시를 암기하는 것이 아니라, 클래스 내 분산 (intra-class variance)을 의미 있게 학습할 수 있을 만큼 클래스당 충분한 샘플이 필요함을 의미합니다. 손실 곡선 (loss curves)이 불규칙하게 진동하는 것을 멈추기 위한 실질적인 하한선은 클래스당 대략 500개의 레이블링된 이미지입니다. 이보다 적으면 검증 손실 (validation loss)이 조기에 평탄해졌다가 이후 발산하는 것을 보게 될 것이며, 이는 모델이 위상 (topology)이 아닌 클래스별 렌더링 특이점 (rendering quirks)에 맞춰 패턴 매칭을 하고 있다는 신호입니다. 공개 블록체인 분석 데이터셋에서 레이블링된 데이터를 가져오거나 알려진 주소로부터 직접 레이블링을 하는 경우, 클래스당 500개의 하한선을 맞추는 데 비용이 많이 들 것으로 예상해야 합니다. 이진 분류로 시작하여 파이프라인이 작동하는지 검증한 다음 확장하십시오.
Qwen2-VL의 채팅 템플릿을 위한 포맷팅 샘플 (Formatting samples for Qwen2-VL's chat template)
Qwen2-VL은 특정 메시지 구조 (messages structure)를 기대하며, 프로세서 (processor)가 핵심적인 작업을 수행하지만, 이는 사용자가 올바른 형태를 전달했을 때만 가능합니다. 각 학습 예시는 JSON 객체이며, 사용자 턴 (user turn)은 <|image_pad|> 플레이스홀더를 통해 이미지를 임베딩하고, 어시스턴트 턴 (assistant turn)은 레이블과 한 문장의 근거 (rationale)를 포함합니다. 근거는 단순히 레이블 평활화 (label smoothing)를 위한 형식적인 절차가 아닙니다. 이는 모델이 분류를 특정 시각적 증거에 고정시키는 중간 토큰 시퀀스 (intermediate token sequence)를 생성하도록 강제하며, 이는 분포 외 (out-of-distribution) 그래프에 대해 확신에 찬 오답 (confident-wrong predictions)을 내놓는 것을 측정 가능한 수준으로 감소시킵니다.
{
"messages": [
{
...
transformers 라이브러리의 AutoProcessor는 processor(messages, images=..., return_tensors='pt')를 호출할 때 토큰화 (Tokenization) 및 이미지 패치 임베딩 (Image Patch Embedding)을 처리합니다. 주의할 점이 하나 있습니다. 만약 위에서와 같이 image 필드에 파일 URI 형식으로 이미지 경로를 전달하면, 프로세서는 호출 시점에 해당 경로를 해석(Resolve)합니다. 따라서 학습 스크립트가 해당 경로가 유효한 컨텍스트에서 실행되도록 하거나, 경로 해석 과정을 완전히 피하기 위해 PIL Image 객체를 직접 전달하십시오. 후자의 방식은 작업 디렉토리가 변경되는 Docker 환경에서 더 안전합니다.
Train/val 분할 및 증강 (Augmentation) 전략
80/20 분할은 괜찮지만, 주소 (Address) 레벨이 아닌 서브그래프 (Subgraph) 레벨에서 분할해야 합니다. 만약 두 개의 서브그래프가 주요 거래소 허브 노드 (Exchange Hub Node)를 공유하고 있다면, 하나는 학습 (Train) 세트에 있고 다른 하나는 검증 (Val) 세트에 있을 경우 분할 간에 구조적 정보가 누출 (Leak)됩니다. 서브그래프들을 가장 차수가 높은 노드 (Highest-degree node)의 주소를 기준으로 그룹화한 뒤, 그 그룹들을 분할하십시오. 증강 (Augmentation)의 경우: 수평 뒤집기 (Horizontal flip)와 작은 회전 (±15°)은 안전합니다. 분류 신호 (Classification signal)가 위상학적 (Topological)이며 방향에 독립적이기 때문입니다. 팬인 (Fan-in) 패턴을 가진 믹서 허브 (Mixer hub)는 뒤집혀 있어도 똑같이 의심스럽게 보입니다. 절대 해서는 안 될 행동은 컬러 지터 (Color jitter)를 적용하는 것입니다. 노드 색상은 엔티티 유형 인코딩 (Entity-type encoding)입니다. 증강 중에 색조 (Hue)를 무작위로 변경하면 모델은 색상을 노이즈 (Noise)로 학습하게 되는데, 이는 의도와 정반대의 결과입니다. 증강은 구조적으로만 수행하고 가볍게 유지하십시오. 그래프 이미지는 이미 정보 밀도가 높습니다. 과적합 (Overfitting)을 방지하기 위해 공격적인 증강을 하기보다는 클래스당 충분한 레이블링된 샘플을 확보하는 것이 더 중요합니다.
MI300X에서 LoRA를 이용한 파인튜닝 (Fine-Tuning): 설정, 명령어 및 실패 모드
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기