Learning
레슨 6 / 8·20분

이미지 분류

이미지 분류는 머신러닝에서 가장 대표적인 작업 중 하나입니다. CNN(Convolutional Neural Network)을 사용하면 이미지의 특징을 자동으로 학습하여 높은 정확도의 분류를 수행할 수 있습니다.

CNN 모델 구축

javascript
import * as tf from '@tensorflow/tfjs';

// CNN 모델: 이미지 패턴 인식에 특화
const model = tf.sequential();

// 합성곱 레이어 1
model.add(tf.layers.conv2d({
  inputShape: [28, 28, 1],  // 28x28 그레이스케일 이미지
  filters: 32,              // 32개의 필터
  kernelSize: 3,            // 3x3 필터 크기
  activation: 'relu',
}));
model.add(tf.layers.maxPooling2d({ poolSize: 2 }));

// 합성곱 레이어 2
model.add(tf.layers.conv2d({
  filters: 64,
  kernelSize: 3,
  activation: 'relu',
}));
model.add(tf.layers.maxPooling2d({ poolSize: 2 }));

// Flatten: 2D -> 1D
model.add(tf.layers.flatten());

// 완전 연결 레이어
model.add(tf.layers.dropout({ rate: 0.25 }));
model.add(tf.layers.dense({ units: 128, activation: 'relu' }));
model.add(tf.layers.dropout({ rate: 0.5 }));
model.add(tf.layers.dense({ units: 10, activation: 'softmax' }));

model.summary();

이미지 데이터 전처리

javascript
// 캔버스에서 이미지 데이터 가져오기
function getImageTensor(canvas) {
  return tf.tidy(() => {
    // 캔버스를 텐서로 변환
    const imageTensor = tf.browser.fromPixels(canvas, 1);
    // 그레이스케일, 1채널

    // 28x28로 리사이즈
    const resized = tf.image.resizeBilinear(imageTensor, [28, 28]);

    // 0~1 범위로 정규화
    const normalized = resized.div(255.0);

    // 배치 차원 추가: [28, 28, 1] -> [1, 28, 28, 1]
    return normalized.expandDims(0);
  });
}

// 파일 입력에서 이미지 로드
async function loadImage(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  });
}

모델 학습과 평가

javascript
model.compile({
  optimizer: 'adam',
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy'],
});

// 학습 실행
const history = await model.fit(trainImages, trainLabels, {
  epochs: 10,
  batchSize: 64,
  validationSplit: 0.2,  // 20%를 검증 데이터로 사용
  callbacks: {
    onEpochEnd: (epoch, logs) => {
      console.log(
        "에포크 " + (epoch + 1) +
        " - accuracy: " + logs.acc.toFixed(4) +
        " - val_accuracy: " + logs.val_acc.toFixed(4)
      );
    },
  },
});

// 테스트 데이터로 평가
const evalResult = model.evaluate(testImages, testLabels);
console.log("테스트 정확도:", evalResult[1].dataSync()[0].toFixed(4));

실시간 분류 예측

javascript
// 단일 이미지 분류
async function classifyImage(model, canvas) {
  const tensor = getImageTensor(canvas);
  const predictions = model.predict(tensor);

  // 확률 배열 가져오기
  const probabilities = await predictions.data();
  tensor.dispose();
  predictions.dispose();

  // 클래스별 확률 정리
  const classNames = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
  const results = classNames.map((name, i) => ({
    className: name,
    probability: probabilities[i],
  }));

  // 확률 높은 순으로 정렬
  results.sort((a, b) => b.probability - a.probability);

  return results;
}

// 사용 예시
const results = await classifyImage(model, canvas);
console.log("예측:", results[0].className);
console.log("확률:", (results[0].probability * 100).toFixed(1) + "%");
💡

CNN에서 Conv2D 레이어는 이미지의 지역적 패턴(엣지, 텍스처 등)을 학습하고, MaxPooling은 공간 크기를 줄여 계산 효율을 높입니다. Dropout은 학습 시 무작위로 뉴런을 비활성화하여 과적합을 방지합니다.