블로그로 돌아가기
Machine Learning

VESSL Cloud로 Vision-Language-Action(VLA) 모델 파인튜닝하기 (1)

Ian Lee
Ian Lee
||12분 소요

이 포스트는 실물 로봇 없이 오픈소스 Vision-Language-Action(VLA) 모델인 Isaac-GR00T의 파인튜닝을 시도한 기록이에요.

VESSL AI의 Tech Sales Engineer로서 최근에 physical AI 분야의 잠재 고객과 미팅을 할 일이 많아졌어요. 미팅에서 고객들에게 필요한 이야기를 잘 하기 위해서는 내가 실제로 physical AI 분야에 대한 경험이 있어야 한다고 생각했어요.

모델

Isaac-GR00T는 NVIDIA에서 공개한 오픈소스 Vision-Language-Action(VLA) 모델이에요. 카메라 이미지(Vision)와 자연어로 된 지시(Language)를 입력받아 로봇의 행동(Action)을 출력해요. 이 모델을 파인튜닝해 보면서 VLA 학습을 어떻게 하는지 감을 잡아 보기로 했어요.

데이터

VLA 모델을 파인튜닝할 때는 과업을 정의하는 자연어(e.g. "빨간색 큐브를 집어서 초록색 마커 위로 옮겨.")와 로봇이 실제 과업을 수행하는 비디오가 필요해요. 원래는 파인튜닝에 사용할 수 있는 공개 데이터셋이 있지만, 이미 존재하는 데이터셋을 그대로 가져다 쓰는 건 튜토리얼을 한 번 더 따라하는 것 이상의 의미가 없을 것 같았어요. 그래서 데이터 생성부터 직접 해 보기로 했어요.

실물 로봇 없이 데이터 만들기

VESSL AI가 로보틱스 회사는 아니기 때문에 내부에 실물 로봇을 가지고 있지는 않았어요. 그래서 Isaac Sim에 내장되어 있는 로봇을 사용해서 시뮬레이션을 통해 데이터를 만들기로 했어요.

Isaac Sim은 가상 환경에서 로봇 시뮬레이션을 할 수 있도록 지원하는 오픈소스 프레임워크예요. 내부적으로 다양한 종류의 사전 설치된 로봇을 지원하는데, 그 중에서 그리퍼(gripper)가 달린 로봇 팔인 Franka 로봇을 사용했어요.

Franka Panda 로봇
Franka Panda 로봇

과업은 pick-and-place로 정했어요. 세 가지 색깔의 큐브 중 하나를 집어 초록색 골 마커 위에 내려놓는 아주 기초적인 작업이에요.

Claude Code의 Opus 4.6 모델을 이용해서 데이터 생성을 위한 규칙 기반 코드를 작성했어요. 큐브와 골 마커의 좌표를 알고 있는 상태에서 그리퍼를 정해진 시퀀스로 움직이는 코드였어요. 이 코드를 Isaac Sim 환경에서 반복 실행해 얻은 자료를 학습 데이터로 쓸 계획이었어요.

1차 시도

시뮬레이션을 돌려 데이터를 모으고, 이걸로 Isaac-GR00T를 파인튜닝하고, 평가까지 진행했어요. 시뮬레이션의 성공률이 낮긴 했지만, 처음에는 파이프라인을 끝까지 돌려 보며 전체 프로세스에 대한 감을 잡는 것을 목표로 했기 때문에 크게 신경쓰지 않았어요. 데이터의 품질이 좋지 않으니 당연히 모델의 성능도 나오지 않았어요. 여기까지는 예상한 결과였어요.

이제 전체 파이프라인을 돌려 봤으니 모델의 성능을 개선해 보기로 했어요. 시뮬레이션의 성공률을 높이는 것이 가장 우선이었어요. Claude Code를 붙잡고 한참 씨름을 했지만 시뮬레이션의 성공률은 향상되지 않았어요. 한참 원인을 찾던 저는 시뮬레이션 영상을 직접 보고서야 그 원인을 알게 되었어요.

화면 속 Franka 로봇은 큐브를 집고 있지 않았어요. 그리퍼를 좁히지 않고, 그냥 큐브를 바닥에 끌고 다닐 뿐이었어요. 시뮬레이션이 성공하는 경우는 둘 중 하나였어요. 무작위로 환경이 생성될 때 우연히 큐브와 골 마커가 인접하게 만들어지거나, 끌려가던 큐브가 우연히 골 마커 주변을 지나가거나. 어느 쪽도 로봇이 보고 배우기를 원하는 형태는 아니었어요.

Isaac Sim Franka 로봇 pick-and-place 1차 시도 시뮬레이션 — gripper가 큐브를 집지 못하고 바닥에 끌고 다니는 모습
1차 시도 시뮬레이션: gripper가 큐브를 집지 않고 바닥에 끌고 다니고 있어요.

어떻게 해도 시뮬레이션을 정상화시킬 수 없어서 일단 포기하기로 했어요. 파인튜닝이 어떻게 굴러가는지에 대한 감은 잡았으니, 어떻게 실패했는지와 지금까지 배운 점 등을 글로 남기는 정도로 마무리하자고 생각했어요. 그렇게 글을 쓰고 있을 때 두 가지 일이 일어났어요.

Opus 4.7과 VESSL Job

마침 Anthropic에서 Opus 4.7 모델을 공개했고, 같은 시기에 VESSL Job과 vesslctl CLI도 출시되었어요. 어차피 실패담을 쓸 거라면, 한 번 더 시도해 보고 쓰기로 했어요.

이번에는 실험 방식을 바꿨어요. vesslctl Skill을 Claude Code에 추가해서, Claude Code가 직접 코드를 수정하고 수정한 코드를 기반으로 VESSL Job을 띄우도록 구성했어요.

1차 시도 때는 Workspace를 띄우고 그 안에 Claude Code를 설치해서 사용했는데, 이 방식의 문제는 Workspace를 띄워 놓은 동안 계속 비용이 발생한다는 점이었어요. 비용에 신경을 쓰느라 효율적으로 실험을 진행하기가 어려웠어요. 하지만 Job 단위로 작업을 하니 실제 GPU 자원을 띄우고 작업이 돌아가는 동안에만 비용이 발생해서 효율적으로 실험을 진행할 수 있었고, 여러 작업을 동시에 띄우거나 하는 것도 가능했어요.

2차 시도

1차 시도의 가장 큰 실수는 큐브 3개를 랜덤 위치에 놓고 그 중 하나를 골 마커로 이동시키는 과업을 처음부터 한번에 구현하려고 한 것이었다고 생각했어요. 그래서 이번에는 가장 단순한 케이스에서 시작해서, 하나가 확실히 동작하는 것을 확인한 뒤에 다음 단계로 넘어가기로 했어요.

첫 단계는 고정된 위치에 한 개의 큐브와 골 마커를 두고 pick-and-place를 수행하는 것이었어요. 이번에는 처음부터 영상을 통해 확실하게 그리퍼가 큐브를 집어올려서 골 마커에 내려놓는 것을 확인하고 다음 단계로 넘어갔어요. 두 번째 단계는 고정된 위치의 큐브 세 개 중 하나를 골 마커로 옮기는 과업이었어요. 멀티 타겟 환경에서도 동일한 정책이 잘 동작하는지 검증하는 단계였죠.

마지막 단계가 원래 목표로 했던 환경이었어요. 큐브 세 개를 랜덤한 위치에 생성하고, 골 마커는 고정 위치에 두고, 그 중 하나의 큐브를 골 마커로 옮기는 과업을 진행했어요. 다만 이번에는 1차 시도에서 발견한 우연한 성공을 피하기 위해, 큐브가 골 마커와 인접한 위치에 생성되지 않도록 제약을 걸었어요.

세 단계를 모두 거친 후 모은 데이터는 1차 시도 때와는 질적으로 달랐어요. 그리퍼가 실제로 큐브를 잡고 들어올려서 골 마커 위에 내려놓는, 과업의 정의에 부합하는 에피소드들이었어요.

Isaac Sim Franka 로봇 pick-and-place 2차 시도 시뮬레이션 — gripper가 큐브를 집어올려 골 마커에 내려놓는 모습
2차 시도 시뮬레이션: gripper가 큐브를 실제로 집어올려 골 마커 위에 내려놓아요. (2배속)

2차 시도의 결과

깨끗한 데이터를 모았으니 이번엔 결과도 다를 거라고 기대했어요. 같은 방식으로 Isaac-GR00T를 파인튜닝하고 평가를 진행했어요.

결과는 여전히 실패였어요. 그리퍼는 큐브의 위치와 무관하게 고정된 방향으로 움직였어요. 데이터가 깨끗해진 것과는 별개로 모델이 과업을 학습하는 데 필요한 또 다른 무언가가 부족한 것으로 보였어요.

Isaac-GR00T VLA 파인튜닝 2차 시도 evaluation — gripper가 큐브 위치와 무관하게 고정된 방향으로 움직이는 모습
2차 시도 evaluation: gripper가 큐브 위치와 무관하게 고정된 방향으로 움직여요.

추가로 테스트해볼 만한 가설은 몇 가지 있었어요. 데이터 양이 부족했을 수도 있고, 액션의 표현 방식이 모델이 사전학습한 분포와 잘 맞지 않았을 수도 있고, 카메라 시점이나 관측 데이터 포맷이 GR00T가 기대하는 것과 미묘하게 달랐을 수도 있겠죠. 하지만 이제 시간이 다 되었고, 이런 아이디어들은 다음 시도의 출발점으로 남겨두기로 했어요.

실패에서 건진 것들

두 번의 시도 모두 성공하진 못했지만, 그 과정에서 얻은 것은 분명 있었어요.

가장 큰 수확은 VLA 파인튜닝 과정 전체를 직접 돌려 본 경험이에요. 데이터를 어떻게 만들고 검증하는지, 모델을 어떻게 학습시키고 결과를 어떻게 평가하는지, 각 단계에서 어떤 문제가 생길 수 있는지를 직접 부딪혀 보며 배울 수 있었어요. 단순히 포맷이 맞고 결측치가 없는 데이터를 확보하는 것이 중요한 게 아니라, 데이터가 우리가 학습시키고자 하는 행동을 실제로 보여주고 있는지가 중요하다는 것을 배웠어요.

또 한 가지 배운 것은 단계적 검증의 가치였어요. 한번에 완성된 환경을 만들려고 했을 때는 실패했던 시뮬레이션을, 가장 단순한 케이스부터 점진적으로 단계를 밟아 나가니 성공적으로 만들 수 있었어요. 이것은 비단 ML 실험뿐만 아니라 거의 모든 시스템 개발에 통하는 원리일 거예요.

도구가 만든 차이

돌이켜보면 두 번의 시도에서 가장 큰 차이는 Claude의 모델 버전이 아니라 실험을 돌리는 방식이었어요.

ML 실험은 결국 사이클당 비용의 싸움이에요. 가설을 세우고, 검증하고, 다음 가설로 넘어가는 루프를 얼마나 자주, 싸게 돌릴 수 있는지가 결과를 결정해요. 1차 시도에서 포기했던 이유는 시뮬레이션이 어려웠기 때문이라기보다는, 가설 하나를 검증하는 데 시간이 너무 오래 걸렸기 때문이었어요.

2차 시도에서 Claude Code를 통해 곧바로 Job을 띄울 수 있게 되자 사이클 횟수는 늘고 대기 시간은 줄었어요. 그 결과 파인튜닝 자체는 실패했지만 적어도 깨끗한 시뮬레이션을 만들 수는 있었고, 다음 시도의 출발점도 훨씬 명확해졌어요. 도구가 좋은 결과를 직접 만들어 주는 건 아니지만, 더 많은 시도를 가능하게 함으로써 좋은 결과에 더 가까워질 수 있게 해 준다는 걸 이번에 다시 느꼈어요.

다음 시도에서는 모델 측면을 더 연구해 보려고 해요. 진전이 있으면 또 글로 정리해서 공유할게요.

VESSL Cloud 시작하기

Ian Lee

Ian Lee

뉴스레터 구독

AI 인프라 구축 노하우와 최신 GPU 소식을 매달 보내드려요.

구독하면 개인정보처리방침에 동의하는 것으로 간주돼요.

VLA 모델 파인튜닝 실전기 — Isaac-GR00T와 Isaac Sim | VESSL Cloud