'같은 코드'를 먼저 구체적으로 말하기
어제는 잘 읽히던 로그가 오늘은 다르게 계산된다고 하자. “같은 프로그램을 썼다”는 말에는 프로그램의 어느 상태인지 빠져 있다. Git 커밋은 기록한 파일들의 특정 상태를 식별하는 데 도움이 된다. 하지만 커밋 뒤 작업 트리에서 소스를 고쳤거나 추적하지 않는 설정 파일을 바꾸었다면 같은 커밋 ID를 표시해도 실제 실행 내용은 달라질 수 있다.
작업 트리는 현재 파일을 편집하는 영역이고, 스테이징 영역은 다음 커밋에 넣을 상태를 준비하는 영역이다. 모든 수정이 자동으로 커밋되거나 서버에 올라가는 것은 아니다. Pro Git의 변경 기록 설명은 추적된 파일·미추적 파일과 상태를 구분한다. 읽기 확인에서는 git status --short로 상태를 보고, git diff와 git diff --cached로 스테이징 전후의 차이를 구분할 수 있다. 명령의 이름을 외우는 것보다 어느 두 상태를 비교하는지 설명하는 것이 중요하다.
미추적 파일의 내용이나 저장소 밖 데이터는 커밋 ID만으로 복원되지 않는다. ignore 규칙으로 제외한 대형 로그·지역 설정도 별도 관리해야 한다. 그렇다고 인증정보를 모두 저장소에 넣어 재현성을 높이는 방식은 적절하지 않다. 재현에 필요한 설정 항목의 이름·사용한 값의 비민감 부분·외부 의존성 식별자를 구분해 기록한다. 이 단위는 실제 저장소의 reset·삭제·이력 재작성 같은 변경을 요구하지 않는다.
소스와 패키지, 데이터는 다른 층이다
Python 코드가 같아도 실행 인터프리터나 패키지 버전, C++ 컴파일러 옵션과 연결 라이브러리가 다르면 결과나 실행 가능성이 달라질 수 있다. 의존성 목록과 잠금(lock) 기록은 사용한 버전·해결된 의존성을 재구성하는 데 도움이 된다. 그러나 잠금 파일 하나가 운영체제·장치 드라이버·모든 외부 데이터를 자동으로 고정하지는 않는다. 무엇을 고정하는 파일인지부터 읽어야 한다.
데이터도 파일명만으로 같다고 할 수 없다. run.csv라는 이름을 유지하며 내용을 바꿀 수 있기 때문이다. 내용 해시와 자료 수집 조건·전처리 방식을 기록하면 다른 입력을 같은 실험으로 오인할 위험을 줄일 수 있다. 해시가 같다는 것은 기록한 바이트의 동일성을 확인하는 근거이지 센서가 정확했다는 증명은 아니다. 단위·좌표·기간·누락 등 데이터 의미는 별도 계약이다.
venv는 프로젝트의 Python 패키지 환경을 분리하지만 다른 위치로 폴더만 옮기면 그대로 재현된다고 보장하지 않는다. Python 공식 venv 문서는 환경을 옮겨 다니는 코드 보관소보다 재생성할 수 있는 환경으로 다루도록 안내한다. 사용한 Python·의존성 명세·설치 경로 조건을 기록하고 새 환경에서 다시 구성·확인하는 접근이 필요하다.
AI 모델과 native 산출물의 재현 명세
AI 입력에는 모델 가중치뿐 아니라 축·채널 순서, 단위, 정규화 통계와 행동 표현이 연결된다. 같은 가중치 파일을 써도 전처리 통계가 바뀌면 모델에 들어가는 값이 달라질 수 있다. 가중치·통계·교정 설정 각각의 식별자와 이들을 묶는 설정 revision을 기록한다. 한 파일의 해시를 전체 실행 계약의 해시처럼 해석하지 않는다. 교정 파일이 같다는 사실도 현재 장치 위치와 교정 조건이 여전히 맞다는 증거는 아니다.
합성 예로 입력 위치가 250 mm이면 먼저 0.250 m로 바꾼다. 정규화 계약이 ‘입력에서 평균을 빼고 표준편차로 나눈다’이고 A의 평균이 0.100 m, 표준편차가 0.050 m라면 결과는 (0.250−0.100)/0.050=3이다. B의 통계가 평균 0.200 m, 표준편차 0.100 m라면 결과는 0.5다. 두 값은 무차원이며 같은 가중치라도 입력은 같지 않다. 이 예는 학습이나 추론을 실행한 결과가 아니라 전처리 계약의 계산이다.
native 확장은 소스·패키지 버전과 함께 실제 빌드 산출물·CPU 대상·인터프리터 ABI·컴파일/링크 옵션을 기록한다. pip의 반복 설치 안내는 버전 고정과 다운로드 파일 해시를 구분하고, 컴파일한 wheel 묶음이 운영체제·아키텍처에 종속될 수 있음을 설명한다. 같은 요구 목록을 다른 플랫폼에서 해결했다고 같은 바이너리가 만들어졌다는 뜻은 아니다. 빌드 도구와 실제 로드 경로를 M01.03~04의 기록과 연결한다.
컨테이너 태그와 고정된 내용 구분하기
컨테이너 이미지는 사용자 공간의 파일과 실행 구성을 묶어 환경 차이를 줄인다. 이미지 태그는 이름표이며 같은 태그가 나중에 다른 내용을 가리킬 수 있다. digest는 특정 이미지 내용의 식별에 사용된다. Docker의 공식 빌드 권고는 base image 버전과 digest를 다루면서 고정·갱신 관리의 필요성을 설명한다.
특정 이미지 digest를 적어도 호스트의 커널·장치·GPU 드라이버·바깥에서 연결한 데이터와 설정이 모두 이미지에 들어 있는 것은 아니다. 여러 플랫폼을 지원하는 이미지라면 실제 사용한 플랫폼도 확인해야 한다. 컨테이너 실행 때 외부 디렉터리를 연결하면 이미지 밖의 파일이 결과를 바꿀 수 있다. 따라서 “Docker를 썼으므로 어디서나 똑같다”보다 어떤 이미지·플랫폼·외부 입력·실행 옵션을 사용했는지 기록한다.
내용을 고정하는 것과 최신 수정 사항을 검토하는 일도 별개다. 영원히 오래된 이미지에 머무르는 것이 재현의 목적은 아니다. 변경 전후 식별자와 결과를 비교해 업데이트의 영향을 추적할 수 있어야 한다. 여기서는 실제 이미지를 내려받거나 드라이버를 바꾸지 않고 합성 기록의 불일치만 분석한다.
결과의 재현 범위도 미리 정의한다
어떤 실험에서는 출력 텍스트가 정확히 일치해야 하고, 어떤 수치 실험에서는 정한 오차 범위와 성공 조건을 비교해야 한다. 난수 시드 하나가 모든 병렬 계산·외부 시간·장치 차이까지 없애는 것은 아니다. 무엇을 동일하게 유지하고 어떤 차이를 허용할지 정하지 않으면 결과가 달랐을 때 결함인지 허용된 차이인지 판단할 수 없다. PyTorch 재현성 공식 안내도 release·플랫폼과 CPU/GPU 간 동일 결과를 시드만으로 보장하지 않는다. 실제 사용하는 연산·장치·라이브러리 버전과 결정성 설정의 적용 범위를 확인한다.
비교 기준은 결과를 보기 전에 정한다. 예를 들어 이 단위의 로그 변환을 같은 입력에서 확인할 때 위치 오차의 절댓값 0.000001 m 이하와 거부 행 번호의 정확한 일치를 요구할 수 있다. 기대값 0.250000 m에 결과 0.2500004 m이면 오차 0.0000004 m로 수치 조건을 통과하고, 0.250002 m이면 0.000002 m로 실패한다. 이 허용오차는 합성 계약이며 실물 작업의 허용 위치 오차로 쓰지 않는다. 허용오차를 만족해도 검사하지 않은 입력이나 장치 성능은 미확인이다.
최소 기록을 만들 때에는 소스 revision과 미커밋 변경, 의존성 명세의 식별자, 입력 데이터와 설정, 실행 환경·명령, 확인할 출력과 판정 기준을 서로 다른 항목에 적는다. 이후 “먼저 입력과 단위를 대조하고, 소스/설정 차이와 의존성 차이를 차례로 확인한다”처럼 비교 순서를 세운다. 모든 환경을 완벽하게 고정했다고 선언하기보다 미확인 영역을 분명히 남기는 편이 실제 원인 조사에 도움이 된다.