MIT 연구실에서 나온 GenCAD는 손그림이나 사진을 입력하면 단순한 3D 모형이 아니라 "수정 가능한 CAD 프로그램"까지 만들어준다.
3D 모델을 만든다는 것 = 좌표 더미를 그리는 게 아니다.
CAD는 컴퓨터로 기계 부품·건물·제품을 설계할 때 쓰는 소프트웨어다. AutoCAD, SolidWorks, Fusion 360 같은 도구를 들어봤다면 그게 CAD다. 엔지니어가 마우스를 움직여 "여기에 원을 그리고, 그걸 10mm 돌출시키고, 모서리를 둥글게 깎고…" 하는 식으로 명령을 쌓아 하나의 부품을 만들어낸다.
그래서 진짜 CAD 파일은 "완성된 모형 사진"이 아니라 그 모형을 만든 명령들의 역사다. 이 역사가 살아 있어야 나중에 "원 지름을 20mm로 바꿔" 한 줄로 수정할 수 있다. 단순한 3D 메시(삼각형 덩어리)에는 이런 수정 능력이 없다.
CAD 명령 시퀀스는 김치찌개 레시피 같다. "돼지고기 200g 볶고, 김치 한 컵 넣고, 물 500ml 부어 10분 끓인다." 이 텍스트가 있으면 누구든 재현하고, 양을 두 배로 늘리고, 매운맛을 빼는 식으로 수정할 수 있다.
반면 완성된 김치찌개 사진(메시·복셀)은 보기엔 좋지만 "고기 양 두 배로" 같은 수정이 불가능하다. AI가 사진만 그리는 건 쉽지만, 레시피까지 만들어내려면 훨씬 어려운 일을 해야 한다.
GenCAD가 다른 3D 생성 AI와 결정적으로 다른 지점.
점·선·면이 아니라 "원을 그렸다 → 돌출했다 → 모서리를 깎았다" 같은 parametric 명령 시퀀스를 생성한다. 결과적으로 같은 이미지에서 출발해도 GenCAD가 만든 모델은 그대로 제조 현장이나 엔지니어 손으로 넘어갈 수 있다 — 수치 한 줄만 바꿔서.
이걸 가능하게 만든 비밀은 한 모델이 아니라 4개의 모듈을 사슬처럼 연결한 파이프라인이다. Transformer가 CAD 언어를 익히고, Contrastive Learning이 이미지와 CAD를 짝지어주고, Latent Diffusion이 그 사이 빈자리를 채워준다.
2025년 TMLR에 실린 논문이 코드 공개와 함께 뒤늦게 주목받는 이유.
3D 생성 AI는 그동안 두 갈래로 발전해 왔다. 한쪽은 NeRF처럼 시점별 사진을 모아 입체 장면을 복원하는 길, 다른 한쪽은 점구름·메시·복셀을 직접 생성하는 길이다. 둘 다 "어떻게 보이는가"는 잘 만들어내지만, "어떻게 만들어졌는가"는 알려주지 않는다. 제조업·엔지니어링에서 이걸 그대로 쓰기에는 부족하다.
GenCAD가 다른 점은 출력 형식이다. AI가 3D 메시 대신 CAD 명령을 토큰처럼 줄지어 뱉으면, 그 출력을 pythonocc-core(오픈소스 CAD 커널) 같은 도구로 다시 진짜 솔리드 모델로 변환할 수 있다. 이걸 진짜로 동작시킨 첫 공개 구현체라는 점이 트렌딩 이유다. 1.4k 별을 단 GitHub와 함께 가중치·데이터셋·도커 환경까지 같이 풀려서 학생·연구자가 곧바로 돌려볼 수 있다.
| 비교 항목 | 전통 3D 생성기 | GenCAD |
|---|---|---|
| 출력 형식 | 메시 / 점구름 / 복셀 | CAD 명령 시퀀스 |
| 수정 가능성 | 거의 불가 | 수치 한 줄 변경 |
| 제조 적용 | 리메시·청소 필요 | B-rep 솔리드 직접 사용 |
| 표현 정확도 | 곡면 근사 (삼각형) | 곡면 정확 표현 |
| 입력 | 이미지·텍스트 | 이미지·손그림 |
| 핵심 기술 | NeRF / Diffusion | Transformer + CLIP + Latent Diffusion |
파이썬 99.8% — 모델 코드부터 CAD 커널까지.
레포의 environment.yml과 requirements.txt를 보면 라이브러리 선택이 흥미롭다. 일반 딥러닝 코드에서 잘 안 보이는 CAD 도메인 전용 라이브러리가 섞여 있다.
rearrange(x, 'b h w c -> b c h w'))설치: conda install -c conda-forge pythonocc-core=7.9.0
실행 예: xvfb-run --server-args="-screen 0 2048x2048x24" python inference_gencad.py
한 모델이 아니라 네 모델이 협업하는 구조.
GenCAD는 다음 네 모듈이 줄지어 학습된다. 한 번에 통째로 학습하는 게 아니라, 앞 단계가 끝나야 다음 단계를 학습할 수 있는 의존 관계를 갖는다.
┌─────────────────────────────────────────────────────────┐
│ (1) CSR (CAD Sequence Representation) │
│ CAD 명령 시퀀스를 압축된 latent 벡터로 변환 │
│ Autoregressive Transformer Encoder + Decoder │
│ │
│ "원 그리기 → 돌출 → 깎기 …" ──► [768차원 벡터] │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ (2) CCIP (Contrastive CAD-Image Pretraining) │
│ CAD latent 와 이미지 latent 를 같은 공간에 정렬 │
│ CLIP 방식의 contrastive loss │
│ │
│ 이미지 ─────► [latent A] ┐ │
│ ├ "이건 같은 부품이다" 학습│
│ CAD 시퀀스 ──► [latent B] ┘ │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ (3) DP (Diffusion Prior) │
│ 이미지 latent 를 보고 → CAD latent 를 만들어내는 │
│ latent diffusion 모델 │
│ │
│ 이미지 latent ──► [noise] ──► ... ──► CAD latent │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ (4) Decoder (재사용된 CSR Decoder) │
│ CAD latent 를 다시 명령 시퀀스로 변환 │
│ │
│ [latent 벡터] ──► "원 그리기 → 돌출 → 깎기 …" │
│ │
│ ↓ pythonocc-core 가 받아 진짜 3D 솔리드로 변환 │
└─────────────────────────────────────────────────────────┘
여기서 흥미로운 설계 선택은 이미지와 CAD를 직접 잇지 않고 latent 공간에서 잇는다는 점이다. 이미지에서 곧바로 명령을 뱉게 만들 수도 있지만, 그러면 학습이 어렵다. CAD 명령은 길고 구조적이고, 이미지는 픽셀 매트릭스다 — 두 세계가 너무 다르다. 그 사이에 latent space(잠재 공간)이라는 중간 광장을 두고, 양쪽 모두를 그 광장 위에 떨어뜨려 짝짓는다.
이미지에서 CAD 명령을 곧바로 만드는 건 그림책을 보고 영어로 곧바로 받아쓰기하는 일이다. 가능은 하지만 어렵다.
GenCAD는 중간에 "공통 언어(latent)"를 만들어 둔다. 그림책도 한국어로 풀고, 영어 문장도 한국어로 풀어둔 뒤, 한국어끼리 짝짓는다. 이러면 양쪽이 각자의 모국어로만 학습돼도 짝짓기는 쉬워진다.
레포를 처음 열었을 때 어디부터 봐야 하나.
GenCAD/ ├── cadlib/ # CAD 명령 시퀀스 정의·파싱·B-rep 변환 ├── model/ # 모델 정의 (CSR Encoder/Decoder, CCIP, DP) │ └── ckpt/ # 다운받은 사전학습 가중치 자리 ├── trainer/ # 학습 루프 (CSR / CCIP / DP 각각) ├── config/ # 하이퍼파라미터 yaml ├── data/ # 데이터셋 + 임베딩 캐시 (h5) │ ├── images/ # CAD 렌더 이미지 │ └── embeddings/ # CCIP 학습 후 추출된 임베딩 ├── utils/ # 헬퍼 함수 ├── results/ # 추론 결과 저장 자리 ├── assets/ # README 이미지 │ ├── train_gencad.py # 학습 진입점 (3단계 모두) ├── inference_gencad.py # 이미지 입력 → CAD 출력 ├── stl2img.py # STL 파일 → PNG 변환 (시각화용) │ ├── Dockerfile # miniconda + xvfb 환경 ├── environment.yml # conda env 정의 └── requirements.txt # pip 의존성 (Docker용 conda env가 권장)
핵심 흐름은 단순하다. train_gencad.py가 csr, ccip, dp 세 가지 모드를 받아 각 단계를 순차로 학습한다. 그 다음 inference_gencad.py가 이미지를 받아 학습된 4개 모듈을 모두 통과시켜 CAD 시퀀스를 토해낸다. cadlib/는 그 시퀀스를 진짜 솔리드로 변환하는 책임을 진다.
model/ 디렉토리만 들여다보면 GenCAD가 안 보인다. 진짜 핵심은 "CAD 명령을 어떻게 토큰으로 표현했는가"에 있고, 그건 cadlib/ 안에 있다. 거기 정의된 vocabulary(원 그리기, 돌출, 깎기 …)를 먼저 봐야 모델이 무엇을 출력하는지 머릿속에 그려진다.
도메인(cadlib) → 학습 루프(trainer) → 모델 구조(model) 순서로 따라가면, 각 단계가 왜 그렇게 설계됐는지 보인다. 모델 구조부터 보면 "이 차원은 왜 768이지?" 같은 질문에 답하기가 어렵다.
한 번에 통째로가 아니라, 세 번 굽고 마지막에 조립.
GenCAD를 처음부터 학습한다고 가정하면 다음 세 명령을 순서대로 돌려야 한다. 각 단계는 앞 단계의 가중치를 입력으로 받는다.
모델이 CAD 명령 시퀀스를 받아 압축했다가 복원하는 법을 익힌다. 이미지는 아직 안 본다. 이 단계가 끝나면 모델은 "CAD 언어"를 말할 줄 알게 된다.
python train_gencad.py csr -name test -gpu 0
1단계에서 만들어진 CAD encoder를 가져와 고정하고, 이미지 encoder를 새로 학습한다. Contrastive loss로 "같은 부품의 이미지와 명령 시퀀스는 가깝게, 다른 부품끼리는 멀게" 표현을 정렬한다.
python train_gencad.py ccip -name test -gpu 0 \ -cad_ckpt "model/ckpt/ae_ckpt_epoch1000.pth"
2단계에서 추출된 임베딩(cad_embeddings.h5, sketch_embeddings.h5)을 입력으로 받아, 이미지 latent를 보고 CAD latent를 생성하는 diffusion prior를 학습한다. 추론 시 이 단계가 "그림에서 설계 의도"를 추정하는 다리 역할을 한다.
python train_gencad.py dp -name test -gpu 0 \ -cad_emb 'data/embeddings/cad_embeddings.h5' \ -img_emb 'data/embeddings/sketch_embeddings.h5'
이렇게 단계를 나누는 이유는 학습 효율성이다. 한 번에 통째로 학습하려 하면 어느 한 부분이 못 따라와서 전체가 흔들린다. 단계별로 "이번엔 이것만 잘하면 돼"를 강제하면 각 모듈이 안정적으로 수렴한다.
pythonocc-core 때문에 로컬에서 그냥 pip install 하면 거의 망한다.
레포는 두 가지 설치 방법을 안내하지만, Docker 방식이 압도적으로 추천이다. pythonocc-core는 conda-forge로만 깔리고, OpenGL/xvfb까지 세팅돼야 추론이 돌아가기 때문이다. Mac이나 Windows에서 로컬 설치를 시도하면 첫 한 시간이 의존성 지옥에 빠진다.
git clone https://github.com/ferdous-alam/GenCAD cd GenCAD docker build -t gencad:latest .
처음 빌드는 conda env 설치 때문에 10~20분 걸린다. 이후엔 캐시가 잡혀 빨라진다.
먼저 사전학습 가중치와 샘플 이미지를 Google Drive에서 받아 각각 model/ckpt/와 data/images/에 배치해야 한다. 그 다음 컨테이너에 GPU 권한을 주고 진입한다.
docker run --gpus all \ -v $(pwd)/data/images:/app/data/images \ -v $(pwd)/assets:/app/assets \ -v $(pwd)/results:/app/results \ -it gencad:latest /bin/bash # 컨테이너 안에서 xvfb-run --server-args="-screen 0 2048x2048x24" \ python inference_gencad.py -image_path data/images -export_img
-v $(pwd)/results:/app/results는 "현재 디렉토리의 results 폴더를 컨테이너의 /app/results와 같은 자리로 만들어줘"라는 뜻. 컨테이너 안에서 결과를 저장하면 호스트에서도 바로 볼 수 있다.GenCAD 코드를 따라가며 자연스럽게 익히게 되는 기술들.
이 레포는 단순한 "재밌는 데모" 이상이다. 한 도메인을 깊이 풀어내기 위해 여러 딥러닝 빌딩블록을 어떻게 조립하는지를 보여주는 좋은 예다. 분야가 다르더라도 응용할 수 있는 패턴이 많다.
cadlib/ 안에 있다train_gencad.py가 어떻게 한 진입점에서 세 모드를 분기하는지 따라가보면 큰 프로젝트의 학습 코드를 어떻게 구조화할지 감이 잡힌다Easy → Hard.
1) Docker 이미지 빌드 2) 사전학습 가중치·샘플 이미지 5장 다운 3) inference_gencad.py 실행 4) 출력된 CAD 명령 시퀀스를 텍스트로 직접 확인
배우는 것: Docker GPU 마운트, "AI가 명령을 만든다"의 의미를 체감
1) cadlib/의 명령 vocabulary 정의 추적 2) B-rep 변환 코드 따라가기 3) 같이 OpenCASCADE 기본 개념(Wire, Face, Solid) 검색해 익히기 4) 작은 부품(원기둥 등)의 명령 시퀀스를 손으로 작성해 변환
배우는 것: CAD 토큰화 설계, OpenCASCADE 기초
1) 전체 파이프라인은 무겁지만 CSR 단계는 GPU 한 대로 며칠이면 돌릴 수 있다 2) 사전학습 모델 없이 train_gencad.py csr 실행 3) loss 곡선, latent 차원 변화 모니터링 4) 학습된 encoder로 재구성(reconstruction) 정확도 측정
배우는 것: Transformer 학습 곡선, autoregressive 디코딩
1) 종이에 간단한 부품 스케치 2) 사진 찍어 data/images/에 넣기 3) 추론 실행 → 출력된 CAD 모델 확인 4) 어떤 종류의 스케치가 잘 되고 어떤 게 안 되는지 분석
배우는 것: 모델의 실제 한계, 도메인 갭, 데이터 분포
1) SVG 명령(M, L, C 등)을 토큰화하는 vocabulary 설계 2) 작은 SVG 데이터셋 수집 (수만 개) 3) CSR과 동일 구조로 SVG encoder/decoder 학습 4) 이미지 → SVG 생성기를 작은 스케일로 구현 5) GenCAD의 전체 구조를 그대로 가져와도 동작 원리가 비슷함을 확인
배우는 것: 도메인 이식, 구조적 출력 생성 모델의 일반화
주차별로 따라가면 GenCAD 전 영역 이해.
"Attention Is All You Need" 정독, BPE/SentencePiece 작동 원리 → 직접 작은 시퀀스 vocab 만들어보기
GPT 스타일 디코더 직접 구현, teacher forcing → CSR 코드와 비교
OpenAI CLIP 논문, InfoNCE loss 수식 이해 → x_clip 라이브러리 분석, CCIP 코드 읽기
DDPM, DDIM 논문 → Stable Diffusion의 latent diffusion 개념 → DP 모듈 분석
B-rep 표현, pythonocc-core 튜토리얼 → 작은 부품 직접 코드로 생성
DeepCAD, ABC dataset 구조 분석 → h5py로 임베딩 캐시 직접 구축
NVIDIA Container Toolkit 세팅, xvfb로 OpenGL 헤드리스 → 클라우드 GPU에서 GenCAD 처음부터 학습 시도
자주 마주칠 단어들.
공식 · 논문 · 관련 기술.
M, L, C)을 토큰화해 이미지 → SVG 생성기를 작은 스케일로. GenCAD의 전체 구조를 그대로 가져와도 동작 원리가 비슷하다.