저장까지 잘 되던 n8n 워크플로가 Publish에서 HTTPS URL 오류로 막혔나요? 서버든 로컬 PC든 환경에 맞는 방법을 골라 다시 활성화를 시도하는 순서를 정리했어요.

결론부터

오류 문구상 외부에서 닿는 HTTPS 주소가 없는 것이 원인으로 보여요. 서버나 EasyPanel이면 도메인에 SSL을 붙이고, 로컬 PC면 터널로 HTTPS 주소를 연 뒤 워크플로를 다시 Publish해 보세요. 정확한 설정 이름은 쓰는 버전의 공식 문서로 확인해주세요.

이 내용은 2026년 11월 기준이에요. 버전은 짚어 둘 게 있는데요, Publish 버튼 글에는 버전이 적혀 있지 않고 1.74.3은 Docker에서 실행이 안 된다는 다른 글에서 나온 버전이에요.

이 오류가 왜 나는지

글에 인용된 오류 문구는 이래요.

Telegram 트리거: 잘못된 요청: 잘못된 웹훅: 웹훅에 HTTPS URL을 제공해야 합니다

증상은 글마다 조금 달라요. Publish 글에서는 Docker나 EasyPanel로 자기 서버에 띄운 n8n에서 버튼이 "게시"만 보였고, 게시한 뒤에도 워크플로가 활성화되지 않았죠. Docker 글은 1.74.3 버전에서 워크플로가 실행되지 않은 사례예요. 위 오류 문구는 글에 인용된 것이라, 두 글이 같은 상황인지는 아직 확인이 안 됐고요.

작성자는 저장된 워크플로에서 눈에 띄는 오류를 찾지 못했는데도 활성 상태로 바꾸지 못했어요. 남는 단서는 오류 문구 하나뿐이에요. "웹훅에 HTTPS URL을 제공해야 합니다"라고 하니, 활성화 단계에서 웹훅 주소를 HTTPS로 받지 못해 거절된 것으로 읽혀요.

에디터는 브라우저로 접속만 되면 열리지만, Telegram 트리거가 쓰는 웹훅 주소는 별개거든요. 이 주소가 HTTPS여야 한다는 점이 문구에 드러나 있죠. 그래서 워크플로 내용보다 n8n이 쓰는 주소를 보는 게 빨라요. 다만 n8n이 이 주소를 만드는 내부 동작은 공식 문서로 확인해야 해요.

HTTPS 주소를 붙이고 다시 Publish하기

1. 내 n8n이 어디서 도는지 확인해요

같은 에디터 화면이어도 해결 경로는 달라져요. 회사 서버나 EasyPanel에 올렸다면 도메인과 SSL이 필요하고, 노트북에서 Docker로 띄웠다면 터널이 필요하죠. 에디터가 열리고 저장되는 것과 외부에서 HTTPS로 닿는 것은 다른 문제예요.

  • 서버, VM, EasyPanel: 도메인과 SSL을 붙여요 (2번)
  • 내 PC의 Docker: 터널로 외부 주소를 열어요 (3번)

여기서 Docker를 지우고 다시 설치해야 하나 고민될 수 있는데요. 커뮤니티에는 Docker로 돌리면 웹훅이 전부 실패한다는 댓글이 있지만, 이 글의 오류 문구가 요구하는 건 Docker가 아니라 HTTPS 주소예요. 1.74.3 사례도 Docker로 설치해 UI는 완벽하게 작동했는데 워크플로가 예상대로 실행되지 않은 경우라, Docker 때문이라고 단정할 근거는 없어요.

오류 문구상 원인은 Docker보다 HTTPS 주소 쪽으로 읽히거든요. 컨테이너를 바꾸거나 다시 설치하기 전에 외부에서 닿는 HTTPS 주소부터 갖추는 게 맞는 순서로 보여요.

2. 서버라면 도메인과 SSL을 붙여요

커뮤니티 댓글은 SSL이 있는 URL 뒤에 n8n을 설치하라고 권해요. 공식 문서로 확인한 내용은 아니라서, 방향을 잡는 참고로만 보세요.

EasyPanel이라면 n8n 서비스에 도메인을 붙이고 SSL을 적용하면 돼요. 화면 이름과 절차는 EasyPanel 문서를 따라가세요. 직접 구성한 VM이라면 리버스 프록시에 도메인과 인증서를 붙이고, 그 프록시가 n8n 컨테이너로 요청을 넘기게 해요. 설정 문법은 쓰는 프록시 문서에 있어요.

3. 로컬 PC라면 터널을 열어요

커뮤니티 댓글에서는 로컬에서 호스팅한다면 시스템을 외부에서 접근할 수 있게 터널을 설정하라고 해요. 터널 도구 이름이나 명령은 근거 자료에 없어서 적지 않을게요. 도구를 고를 때는 https://로 시작하는 주소를 발급해주는지만 확인하세요. 발급된 주소는 다음 단계에서 써요.

4. n8n이 그 주소를 쓰는지 확인해요

주소를 열었다고 끝났다고 보면 안 돼요. 도메인이나 터널 주소를 여는 것만으로 바로 해결된다는 근거는 없거든요. n8n이 웹훅 URL을 만들 때 그 HTTPS 주소를 쓰도록 따로 알려줘야 하는지, 그렇다면 어떤 환경변수나 설정을 쓰는지는 여기서 확인이 안 된 부분이에요. 보고된 사례의 버전은 1.74.3인데, 이 버전에서 검증된 설정값도 없고요.

검증된 값을 바로 건네지 못하는 건 아쉽지만, 근거 없는 값을 넣어 보는 것보다 문서를 찾는 편이 나아 보여요. 쓰는 버전의 n8n 문서에서 웹훅 URL, 호스트, 프로토콜 항목을 찾아 확인하세요. 설정을 바꿨다면 컨테이너 재시작이나 EasyPanel 재배포로 반영하면 돼요.

터널을 쓴다면 하나 더 챙겨야 해요. 터널 주소가 바뀌면 n8n에 설정해 둔 주소와 어긋날 수 있죠. 주소가 바뀌는 터널이라면 바뀔 때마다 다시 봐야 하고, 터널을 다시 열었다면 n8n이 쓰는 주소가 새 HTTPS 주소와 같은지 쓰는 버전의 문서에서 확인한 뒤 워크플로를 다시 Publish하세요. 터널을 자주 다시 여는 분이라면 이 확인까지 한 세트로 묶어 두는 게 좋겠어요.

5. 워크플로를 다시 Publish해요

워크플로를 열고 Publish를 다시 눌러요. 버전에 따라 버튼 이름이 다르면 화면의 활성화 버튼을 누르면 돼요.

같은 HTTPS 오류가 사라지고 워크플로가 active 상태가 되면 성공이에요. 오류가 그대로라면 4번의 주소가 실제로 반영됐는지 먼저 보고, 공개 주소가 https://로 시작하는지도 확인하세요.

활성화된 뒤에는 Telegram에서 봇에게 메시지를 보내고, n8n의 실행 기록(Executions)에 이 워크플로 실행이 생기는지 봐요. 기록이 생기면 트리거가 잡힌 거예요.

그래도 안 되면

주소를 붙여도 같은 오류가 난다면 그때 배포 방식을 다시 점검하세요. 커뮤니티 댓글에는 URL과 SSL 없이 올린 구성은 배포가 잘못된 것이라는 주장도 있는데, 공식 문서로 확인된 내용은 아니에요.

다른 증상을 같은 원인으로 단정하지 않는 것도 중요해요. HTTP로 올린 VM에서 hosted chat이 동작하지 않았다는 사례가 하나 보고됐지만, 아직 조사 중이라 이 오류와 같은 원인인지는 확인되지 않았어요. HTTPS 적용으로 후보 하나를 정리한 뒤에도 안 되면 그 기능의 문서를 따로 확인하세요.

그럼 n8n 버전을 올리면 이 오류가 사라질까요? 업그레이드로 해결된다는 근거는 찾지 못했어요. 1.74.3은 Docker에서 실행이 안 된다는 다른 글에서 나온 버전이고, Publish 오류 글에는 버전이 적혀 있지 않거든요. 오류 문구가 HTTPS 주소를 요구하니, 올리기 전에 도메인+SSL이나 터널부터 시도해 보세요.

정리

  • 서버나 EasyPanel이면 도메인과 SSL을, 로컬 PC면 HTTPS 주소를 주는 터널을 열어요.
  • 웹훅 URL 설정은 쓰는 버전의 공식 문서에서 확인해 적용한 뒤, 컨테이너나 EasyPanel을 재배포해요.
  • 워크플로를 다시 Publish하고, 같은 HTTPS 오류가 사라졌는지와 Executions에 실행 기록이 생기는지로 성공을 판단해요.

참고 자료

이 글은 공개 자료를 바탕으로 생성형 AI를 활용해 작성했어요.

Posted by 튼튼한노예
,

에러 없이 tsc를 TypeScript 7로 바꿨는데 CI 총 시간이 꿈쩍도 안 하나요? 시간이 그대로인 이유와 확인 순서를 바로 짚어 드릴게요.

결론부터

타입 체크가 전체 시간에서 차지하는 비중이 작으면 TypeScript 7로 바꿔도 총 시간은 안 줄어요. 그러니 새 브랜치에서 npx tsc --noEmit으로 타입 체크만 먼저 재 보세요. 병렬 옵션은 CI 사양에 맞춰 실험하면 되고요. TypeScript API를 내부에서 쓰는 typescript-eslint나 Vue 같은 도구가 있다면 TypeScript 6 병행이 필요할 수 있어요. 타입 인식 lint가 병목이면 tsgolint나 oxlint로 옮기되, 쓰는 규칙이 지원되는지 확인한 뒤 전후 시간을 재세요.

이 글은 2026년 11월 기준이에요. npm 최신 버전은 7월 10일 확인 시 7.0.2였고, 이후 바뀌었을 수 있어요.

npm install -D typescript && time npx tsc --noEmit

증상은 단순해요. TypeScript 7로 올렸는데 에러는 없고, 빌드·CI 전체 시간만 그대로죠. 에러 메시지가 따로 없어서 검색할 원문이 없다는 게 이 문제의 곤란한 점이에요. 아래는 한 블로그에서 보고된 실제 콜드 빌드 측정값이에요.

마이그레이션 전: 43.0s
마이그레이션 후: 43.4s

컴파일러만 바꿨는데 총 시간이 줄지 않고 로그에도 실패가 없다면 이 증상으로 보면 돼요. 타입 인식 lint 단계가 그대로 무거울 때도 총 시간은 안 줄어요.

확인 순서

1. 바꾸기 전 숫자부터 남겨요

비교 기준이 없으면 빨라졌는지 알 수 없어요. TypeScript 6 상태에서 npx tsc --noEmit을 먼저 돌려 숫자를 남겨 두세요. 이 명령은 번들링과 lint가 빠지고 컴파일러 시간만 남거든요.

처음 실행하는 콜드 상태와 변경 없이 다시 돌리는 웜 상태는 나눠서 기록하세요. 웜만 보면 효과가 작아 보일 수 있으니 콜드 숫자도 같이 남겨야 해요.

2. 새 브랜치에서 TypeScript 7을 올려요

main을 건드리지 않게 브랜치를 먼저 따고, 같은 명령으로 다시 재요.

git switch -c chore/ts7-trial
npm install -D typescript
time npx tsc --noEmit

설치가 끝나면 npx tsc --version으로 실제 버전을 확인하세요. 7월 10일 확인 시점에는 npm의 typescript 패키지 최신 버전이 7.0.2로 표시됐지만, 그 뒤에 올라간 버전이 있을 수 있거든요.

3. TypeScript 6이 필요한 도구를 바로 확인해요

TypeScript 7에는 안정적인 programmatic API가 아직 없다고 알려져 있어요. 그래서 TypeScript API를 내부에서 쓰는 도구는 TypeScript 7과 함께 쓸 때 TypeScript 6을 같이 둬야 할 수 있죠. typescript-eslint, Vue, Astro, Svelte, Angular, MDX가 해당돼요. 설치 직후 아래 목록으로 확인하세요.

  • 위 도구를 쓰는지 package.json에서 찾아봤나요?
  • 쓴다면 TypeScript 6 병행이 필요한지 각 도구 문서로 확인해요
  • tsc 타입 체크 단계가 TypeScript 7로 도는지도 봐 두세요

병행 방법과 패키지 구성은 도구마다 달라서 여기서는 단정하지 않을게요. 이후 버전에서 API가 제공되는 시점도 확인된 자료가 없어요.

4. 10배가 안 나와도 실패로 단정하지 마세요

전후 숫자가 나왔는데 10배보다 낮다고 해서 설치 실패로 단정하면 안 돼요. 롤백부터 할 일도 아니고요. 공식 10배는 순수 타입 체크 시간이 지배하는 큰 코드베이스에서 나온 수치예요.

커뮤니티 보고는 출처마다 코드베이스가 달라서 값이 갈리는데요. 약 5배가 나왔다는 보고가 있고, tsc -b && vite build 풀 빌드 파이프라인에서는 약 2.5배였다고 해요. 콜드 빌드가 43.0초에서 43.4초로 그대로였던 사례도 있죠. 이 사례도 타입 체크 단독은 4.2초에서 1.1초로 약 4배 줄었어요.

서로 직접 비교할 수 있는 숫자가 아니에요. 내 숫자는 내 코드베이스에서 같은 명령의 전후 비교로만 판단하세요.

5. 파이프라인 단계별로 시간을 나눠요

여기가 이 글의 핵심이에요. tsc가 빨라져도 번들링이나 lint가 느리면 총 시간은 그대로거든요. 컴파일러 교체로 줄어드는 건 타입 체크 구간뿐이에요. 이 구간이 전체에서 수 초에 불과하면 아무리 빨라져도 총 시간은 거의 달라지지 않고, 번들링 같은 나머지 단계는 TypeScript 7과 상관없이 같은 시간이 걸려요.

병렬 옵션을 만지기 전에 이 측정부터 해 두는 게 나아 보여요. 단계마다 따로 재세요.

time npx tsc --noEmit
time npm run build
time npm run lint

스크립트 이름은 package.json에 맞게 바꿔 주세요. 전체에서 tsc 비중이 작으면 컴파일러를 바꿔도 줄어드는 시간이 작아요.

6. 병렬 옵션은 CI 사양에 맞춰 실험해요

--checkers의 기본값은 4예요. Apple M4 10코어 한 대에서는 이 기본값이 콜드 시간 최선이었고, 8로 올려도 눈에 띄는 추가 개선이 없었어요. 다만 CI 러너에서 나온 측정은 아니에요. 코어 수만큼 올리면 빨라진다고 가정하면 안 되는 이유죠.

값은 하나씩만 바꿔서 CI 러너에서 같은 명령으로 비교해요.

time npx tsc --noEmit --checkers 2
time npx tsc --noEmit --checkers 4
time npx tsc --noEmit --checkers 8
time npx tsc --noEmit --singleThreaded

--builders와 --singleThreaded는 효과를 뒷받침할 측정 자료를 찾지 못했어요. 참고할 숫자가 없다는 점은 조금 아쉽네요. 근거 없이 고정하지 말고, 러너의 CPU와 메모리에서 직접 재서 정하세요. 시간과 함께 메모리 사용량도 기록해 두면 좋아요.

--extendedDiagnostics의 타입 수는 판단 기준으로 쓰지 않는 게 좋아요. 한 측정에서 tsgo는 타입을 205,998개 인스턴스화해 94,539개보다 많았는데도, 메모리는 388MB로 431MB보다 덜 쓰고 시간도 짧았거든요. 타입 수가 늘었다고 퇴보는 아니에요.

7. 타입 인식 lint는 tsgolint나 oxlint로 옮겨서 측정해요

단계별로 재 봤더니 lint가 무겁다면 원인은 따로 있어요. typescript-eslint는 타입 정보를 TypeScript 7 이전 버전인 TS 5 컴파일러로 만들어요. tsgo로 컴파일이 빨라져도 이 경로는 거치지 않아서, tsc를 바꿔도 lint 단계는 그대로 무겁죠.

typed linting은 여기에 워크스페이스마다 TypeScript 프로그램을 새로 빌드하는 비용까지 더해져요. CI에서 가장 무거운 단계가 lint로 남는 이유예요.

차이가 얼마나 날까요? 한 모노레포에서는 typescript-eslint가 약 752초, TypeScript 7 위에서 도는 tsgolint가 약 1.9초였어요. 다른 저장소에서는 pnpm lint가 12분 32초에서 0.4초로 줄었고요.

이 두 번째 사례를 TypeScript 7 효과로 읽으면 안 돼요. oxlint로 도구를 바꾼 결과라서 TypeScript 7만의 효과가 아니거든요. tsc와 빌드, lint를 따로 재야 하는 이유이기도 해요.

두 사례 모두 규칙 커버리지가 같다고 확인된 비교는 아니에요. 그래서 내가 쓰는 규칙이 tsgolint나 oxlint에 있는지부터 확인하고, 같은 코드베이스에서 전후 시간을 직접 재야 하죠. lint가 병목이라면 컴파일러 옵션보다 이쪽을 먼저 보는 게 맞을 것 같아요.

정리

  • TypeScript 6 상태에서 콜드·웜을 각각 재고, 새 브랜치에서 npm install -D typescript로 TypeScript 7을 올린 뒤 같은 명령으로 다시 재요.
  • time으로 단계를 나눠 재서 병목이 어디인지 먼저 확인해요.
  • TypeScript 6이 필요한 도구를 분리하고, 타입 인식 lint는 tsgolint나 oxlint로 옮겨 전후를 측정해요.

참고 자료

이 글은 공개 자료를 바탕으로 생성형 AI를 활용해 작성했어요.

Posted by 튼튼한노예
,

LLM 학습이나 파인튜닝 중에 device-side assert가 뜨면 어디부터 봐야 할지 막막하죠. 실패 위치를 확정하는 데서 시작해 수정까지, 실제로 손대는 순서대로 풀어 드릴게요.

이런 에러라면, 결론부터

메시지는 보통 이렇게 생겼어요.

torch.AcceleratorError: CUDA error: device-side assert triggered

CUDA GPU에서 학습, 파인튜닝, 추론을 돌릴 때 떠요. 아래는 재구성한 예시 트레이스인데요, 임베딩 조회가 있는 LLM 학습 코드에서 이런 모양이 자주 나와요.

File "train.py", in estimate_loss
    logits, loss = model(X, Y)
File "train.py", in forward
    tok_emb = self.token_embedding_table(idx)
    pos_emb = self.position_embedding_table(...)
torch.AcceleratorError: CUDA error: device-side assert triggered

그럼 트레이스가 가리킨 줄을 그대로 믿어도 될까요? 아니에요. CUDA 연산은 비동기라서 스택 트레이스가 엉뚱한 줄을 가리킬 수 있거든요. HuggingFace 추론에서는 probs.cpu().numpy()나 .to(device) 줄을 가리키기도 하는데, 그 줄은 실제 원인과 무관할 수 있어요.

그래서 순서는 이래요. 터미널에서 CUDA_LAUNCH_BLOCKING=1을 켜고 다시 실행해 진짜 실패한 줄을 찾고, 그 줄의 인덱스 값이 범위를 벗어나지 않았는지 확인하는 거죠.

CUDA_LAUNCH_BLOCKING=1 python script.py args

2026년 10월 기준으로 정리했어요. 특정 버전에 한정되지 않는 일반 절차이고, 참고한 사례는 PyTorch cu118 빌드(pip 설치)예요.

예외 이름이 RuntimeError로 떠도 마찬가지예요. 메시지에 CUDA error: device-side assert triggered가 들어 있다면 같은 방식으로 접근하면 돼요. 예외 클래스 이름보다 메시지 본문을 기준으로 보세요.

실패한 줄부터 확정하기

CUDA_LAUNCH_BLOCKING=1은 반드시 터미널에서

가장 많이 틀리는 곳이 여기예요. 환경 변수를 스크립트 안이나 노트북 셀에서 설정하면 안 돼요. 이 변수는 CUDA가 시작되기 전에 적용돼야 하는데, 스크립트가 돌기 시작하면 CUDA가 이미 초기화된 뒤라 너무 늦거든요. 터미널에서 먼저 설정하고, 같은 터미널에서 새 프로세스로 스크립트를 재실행하세요.

주피터 노트북이라면 커널을 재시작한 뒤, 변수를 설정한 터미널에서 다시 띄워야 해요. 커널 재시작은 어차피 피할 수 없기도 해요. NVIDIA 문서 기준으로 device-side assert가 한 번 터지면 그 프로세스에서는 GPU를 다시 쓸 수 없어서 프로세스를 종료해야 하거든요.

macOS/Linux(bash 계열 셸)에서는 아래 한 줄이면 돼요.

CUDA_LAUNCH_BLOCKING=1 python script.py args

Windows는 사정이 달라요. VAR=1 python 형태는 bash 계열 문법이라 쓸 수 없고, PowerShell에서는 변수를 먼저 설정한 다음 실행해야 해요. 참고로 이 PowerShell 문법은 사례 자료가 아닌 일반 지식이에요.

$env:CUDA_LAUNCH_BLOCKING=1
python script.py args

트레이스가 가리키는 줄 확인하기

다시 실행해서 나온 스택 트레이스에서 실패한 연산의 줄을 찾으세요. 이후 점검은 전부 이 줄이 기준이에요. 로그에 아래와 비슷한 assertion 문구가 찍혀 있다면 인덱스 범위 문제일 가능성이 크니, 인덱싱 쪽을 먼저 의심하면 되고요.

Indexing.cu:1289: ... Assertion `srcIndex < srcSelectDimSize` failed.

에러가 학습 몇 번 반복 뒤에야 터지고, 그 횟수가 실행마다 달라지는 경우도 있어요. 이러면 첫 반복부터 문제라고 단정하기 쉬워요. 반복 횟수로 원인을 짐작하지 말고, 이때도 CUDA_LAUNCH_BLOCKING=1로 재실행해 트레이스가 가리키는 줄부터 보면 돼요.

그래도 줄이 안 잡히면 TORCH_USE_CUDA_DSA

CUDA_LAUNCH_BLOCKING=1만으로 실패 지점이 분명하지 않을 때 시도해 볼 수 있는 설정이에요. TORCH_USE_CUDA_DSA는 device-side assertion 정보를 더 얻으려고 제안되는 변수인데, 이것도 터미널에서 먼저 설정해야 해요.

TORCH_USE_CUDA_DSA=1 CUDA_LAUNCH_BLOCKING=1 python script.py args
$env:TORCH_USE_CUDA_DSA=1
$env:CUDA_LAUNCH_BLOCKING=1
python script.py args

이 단계는 기대를 좀 낮추는 게 나아 보여요. 두 변수를 1로 설정했는데도 소용없었다는 보고가 있거든요. 자료로 확인되는 건 이 이름이 "compiling with"라는 문구와 함께 언급된다는 점까지예요.

빌드 단계 옵션일 수 있어서, 설치한 빌드에 따라 효과가 없을 수 있고 설치본에서 동작하는지는 확인되지 않았어요. 사용법이 분명하게 정리돼 있지 않은 건 좀 아쉽네요.

변화가 없다면 이 단계는 넘어가 다음 점검으로 가세요. 더 파고들고 싶으면 PyTorch 공식 문서나 이슈 트래커를 확인하고, 그동안은 CUDA_LAUNCH_BLOCKING=1 트레이스와 assertion 문구를 기준으로 보면 돼요.

그 줄의 인덱스 범위 점검하기

줄을 찾았으면 이제 원인을 볼 차례예요. device-side assert는 GPU 커널이 실행 중에 스스로 값을 검사하다가 조건이 깨지면 멈추는 방식이에요. 그래서 에러가 호스트 코드가 아니라 GPU 쪽에서 터지죠. 같은 코드가 CPU에서는 도는데 GPU에서만 터졌다는 보고도 있어요.

이 에러는 잘못된 인덱싱 연산 때문에 생기는 경우가 많고, 흔한 원인은 범위를 벗어난 인덱스예요. 임베딩 조회나 CrossEntropyLoss처럼 텐서 값을 인덱스로 쓰는 연산에서 값이 허용 범위를 넘으면 커널 안의 검사가 실패하거든요. 트레이스가 가리킨 줄에서 텐서 값을 인덱스로 쓰는 부분을 찾아, 그 값이 대상 텐서의 크기 범위 안에 있는지 확인하세요.

print(idx.min().item(), idx.max().item())

점검용 예시라서 idx는 문제 줄에서 인덱스로 쓰이는 텐서로 바꿔 써야 해요. 최솟값이 0보다 작거나 최댓값이 대상 크기 이상이면 범위를 벗어난 거예요. 그럴 땐 인덱스를 만드는 쪽이나 대상 크기를 맞추면 되고요.

여기서 흔한 실수가 하나 있어요. 입력값이 0 ~ vocab_size-1 범위 안이니까 인덱스는 문제없다고 넘기는 거죠. 그런데 입력 값이 범위 안인데도 같은 에러가 난 사례가 있어요.

입력 텐서만 믿지 말고, 트레이스가 가리킨 줄에서 인덱스로 쓰이는 다른 텐서도 idx.min()과 idx.max()를 함께 찍어 확인하세요. 한 줄에 인덱스로 쓰이는 텐서가 여럿이라면 하나만 보고 끝내지 않는 편이 안전해 보여요.

문제 배치만 떼어 돌리는 것도 조심해야 해요. GPU와 CPU 양쪽에서 통과했다고 문제없다고 결론 내리기 쉬운데, 단일 배치로는 재현되지 않는 사례가 있거든요. 배치 하나로 판단하지 말고, 원래 학습 루프를 그대로 둔 채 환경 변수를 켜고 재실행하는 게 맞아요.

다중 클래스 분류라면 클래스 수부터

CrossEntropyLoss에서 터졌다면 GPU 문제가 아니라 shape 문제인 사례가 많아요. 다중 클래스 분류에서는 특히 클래스 수 처리가 어긋나기 쉬운데요, 모델 출력 차원과 타깃 클래스 수가 안 맞으면 CrossEntropyLoss가 범위 밖 클래스를 가리키게 돼요.

볼 곳은 두 군데예요. 하나는 모델 출력층으로, 마지막 레이어의 출력 크기가 실제 클래스 수와 같은지 봐요. 다른 하나는 손실 함수 입력이고, 타깃 값이 0부터 클래스 수 - 1 사이에 있는지 확인하면 돼요. 모델 출력과 loss 함수 입력, 이 둘부터 보세요.

정리

  • 터미널에서 CUDA_LAUNCH_BLOCKING=1을 설정하고 스크립트를 재실행해요.
  • 정확해진 스택 트레이스에서 실패한 코드 줄을 찾고요.
  • 그 줄에서 인덱스 값이 텐서 크기 범위를 벗어나지 않는지 점검하면 돼요.

참고 자료

이 글은 공개 자료를 바탕으로 생성형 AI를 활용해 작성했어요.

Posted by 튼튼한노예
,