LabHub
배우기 러닝패스 코스

AI 다이어트 실패 사건 · 숫자는 같은데 단위가 달랐다 · 이론

숫자는 같은데 단위가 달랐다

LabHub 에서 이어서 보기

한 줄 요약

입력 단위와 표본의 역할을 고정해야 모델 비교가 실험이 됩니다.

왜 이게 필요했나

택배 분류 로봇의 숫자 판독기를 작은 장치로 옮긴다고 상상해 보세요. 개발 PC에서는 잘 읽던 모델이 배포 후에는 숫자를 틀립니다. 모델 파일은 정상적으로 열리고 오류 로그도 없습니다. 이런 고장은 프로그램이 실행되는지만 확인해서는 발견할 수 없습니다. 입력 단위와 데이터 분할부터 고정해야 나중에 바뀐 결과의 원인을 찾을 수 있습니다.

이번 과정에서는 이미 학습한 작은 손글씨 분류기를 받습니다. 새 모델 학습 경쟁이 아니라 모델 변환과 검증이 과제입니다. Python 함수·리스트·JSON·NumPy 배열 기초가 필요합니다. GPU나 실물 로봇 없이 Linux CPU에서 실행하며, 실제 MCU·NPU의 속도나 전력 소비는 이 실습으로 증명할 수 없습니다.

어떻게 동작하나

제공 digits 자료는 8×8 이미지를 특징 64개로 펼친 배열입니다. 픽셀 값은 0–16입니다. 흔히 보는 이미지라는 이유만으로 255로 나누면 학습 때와 입력 범위가 달라집니다. normalize(pixels)는 float32 배열로 바꾸고 16으로 나누며 입력 모양을 유지해야 합니다. 원본 배열 자체를 수정할 필요는 없습니다.

이미지의 픽셀 값과 표본 ID는 서로 다릅니다. ID는 원본 자료의 행을 식별하는 정수입니다. digits.npz의 x_train·x_calibration·x_test에는 이미지가, y_test에는 평가 정답이, ids_train·ids_calibration·ids_test에는 각 분할의 ID가 있습니다. 제공 분할은 학습 1,077개, 보정 360개, 평가 360개입니다. 분할을 다시 뽑지 않고 제공 ID를 그대로 사용합니다.

보정 자료는 변환기의 숫자 범위를 정하는 재료이고, 평가 자료는 변환 후 품질을 판단하는 재료입니다. 평가 정답을 보며 보정 표본을 골라 성적을 올리면 둘의 역할이 섞입니다. 보정 파일에 평가 ID가 하나 들어간 경우도 누출입니다. ID의 개수만 세지 말고 집합과 중복을 확인하세요. 단, 이 작은 공개 평가 집합을 반복 사용한 성적은 독립적인 현장 일반화 성능이 아닙니다.

현장에서 만나는 모습

전처리 함수는 데이터 팀과 응용 프로그램 팀 사이의 작은 API입니다. 한쪽은 0–1 실수를 기대하는데 다른 쪽은 0–16 정수를 넣어도 배열 크기가 같으면 호출은 성공할 수 있습니다. 그래서 계약에는 특징 수뿐 아니라 자료형과 값의 범위가 필요합니다. 카메라·센서 입력을 다루는 실제 시스템에서는 여기에 채널 순서·시간 단위·결측값 처리도 붙습니다. 이번 입력에는 그 복잡성을 일부러 넣지 않았습니다. 먼저 범위 하나를 틀렸을 때 오류가 어떻게 조용히 퍼지는지 확인합니다.

작은 계산을 종이에 먼저 해보세요. 픽셀 세 개가 0, 8, 16이면 올바른 정규화 결과는 0.0, 0.5, 1.0입니다. 255로 나누면 마지막 값조차 약 0.0627입니다. 프로그램 입장에서는 둘 다 유한한 실수이므로 예외가 생기지 않습니다. 모델은 평소와 다른 어두운 입력을 받은 셈입니다. 오류가 없다는 사실과 입력이 올바르다는 사실을 분리해야 하는 이유입니다.

배열 전체를 수정하는 함수와 새 배열을 돌려주는 함수도 구분해야 합니다. 원본 x_test를 바꿔 버리면 같은 프로세스에서 다음 모델을 평가할 때 이미 정규화된 값을 한 번 더 나눌 수 있습니다. 각 모델의 평가 입력이 달라지므로 성능 차이를 양자화 탓으로 잘못 설명하게 됩니다. 이번 normalize 계약은 입력 배열을 보존합니다. 별도 함수 시험은 실제 표본 외에 전부 0인 행과 전부 16인 행을 넣어 양 끝값도 확인합니다.

ID 선택은 “개수·중복·집합” 세 질문으로 나누면 편합니다. 360개를 담았는가, 같은 ID를 두 번 넣지 않았는가, 제공 보정 집합과 같은가를 각각 답해 보세요. 마지막 질문은 순서가 달라도 통과해야 합니다. 순서를 고정하면 같은 자료를 다른 순서로 읽는 정상 구현을 거절하기 때문입니다. 단순히 0부터 359까지 채우면 ID가 원본의 어느 행을 뜻하는지 놓칩니다. 보정 표본을 읽을 때는 원본 ID에서 보정 배열 안의 행 위치를 찾는 대응표를 만들면 됩니다.

이미 학습한 FP32 모델과 데이터가 고정돼 있으므로 같은 바이트에서 출발합니다. 그래도 이 자료는 손글씨의 모든 스타일을 대표하지 않습니다. 공개 자료의 재현성과 실제 고객 입력의 대표성은 다른 문제입니다. 현장 배포 전에는 별도 필체·촬영 조건·오염 상태의 자료를 모아 평가해야 하며, 이 과정의 높은 성적으로 그 일을 생략할 수는 없습니다. 바로 뒤 퀴즈에서 입력과 분할을 확인한 뒤 마지막 모듈의 통합 실습으로 이어갑니다.

다음 실습에서 할 것

1–3단계에서 contract.json에 입력 계약을 기록하고, preprocess.py의 정규화 함수를 구현하며, calibration.json에 보정용 ID만 저장합니다. 단위를 먼저 틀려 보고 어느 검사가 이를 잡는지 확인해 보세요. 첫 단계는 제공 배열의 모양과 길이를 읽는 작은 성공으로 시작합니다.

자료 출처: [scikit-learn load_digits](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html), [UCI 원자료](https://archive.ics.uci.edu/dataset/80/optical+recognition+of+handwritten+digits). 원자료의 저자·CC BY 4.0 표기는 제공 DATA-LICENSE.json에도 있습니다.