1CNN(Convolutional Neural Network) 기초

CNN은 이미지 인식에 특화된 딥러닝 아키텍처로, 제조 비전 AI의 핵심 기술입니다. 이미지의 공간적 특징을 계층적으로 학습하여 복잡한 결함 패턴도 인식할 수 있습니다.

CNN Architecture
Input
224x224
x3 RGB
Conv
64ch
특징맵
Pool
112x112
다운샘플
Conv
128ch
특징맵
Pool
56x56
다운샘플
FC
4096
벡터
Output
[OK/NG]
확률
1. Convolution Layer (합성곱)
작은 필터(3x3, 5x5)로 이미지를 스캔하며 특징 추출. 에지, 텍스처, 패턴 등 시각적 특징 학습
2. Pooling Layer (풀링)
특징맵 크기 축소 (Max Pooling, Average Pooling). 위치 변화에 대한 강건성 확보
3. Fully Connected Layer
추출된 특징을 조합하여 최종 분류. 출력: 클래스별 확률 (Softmax)

제조 비전에서 CNN의 장점: (1) 결함 패턴을 자동으로 학습 - 규칙 프로그래밍 불필요, (2) 다양한 변형에 강건 - 조명, 각도, 위치 변화 대응, (3) 새로운 결함 유형 학습 가능 - 데이터만 추가하면 됨

주요 CNN 아키텍처:

모델연도파라미터특징제조 적용
ResNet-50201525MSkip Connection, 깊은 네트워크범용 결함 분류
EfficientNet20195-66M효율적 스케일링, 높은 정확도엣지 디바이스
ConvNeXt202228-350M최신 설계, ViT 수준 성능고성능 검사
MobileNetV320192-5M경량화, 모바일 최적화실시간 검사

2Transfer Learning (전이 학습)

Transfer Learning은 대규모 데이터셋(ImageNet)으로 사전 학습된 모델을 제조 결함 검출에 재활용하는 기법입니다. 소량의 데이터로도 높은 성능을 달성할 수 있어 제조 AI에서 필수적입니다.

Transfer Learning 전략
방법 1: Feature Extraction (특징 추출기)
Pre-trained CNN
Conv Layers
(동결, 학습X)
ImageNet 가중치
새로운 분류기
FC Layer
(학습O)
제조 결함용
적용: 데이터 1,000장 미만, 빠른 구축 필요
방법 2: Fine-tuning (미세 조정)
초기 레이어
동결/낮은LR
일반 특징
중간 레이어
낮은 LR
중간 특징
후반 레이어
높은 LR
도메인 특징
적용: 데이터 5,000장 이상, 높은 정확도 필요

PyTorch 구현 - 결함 분류 모델:

import torch import torch.nn as nn from torchvision import models, transforms class DefectClassifier(nn.Module): """제조 결함 분류기 (Transfer Learning)""" def __init__(self, num_classes: int, freeze_backbone: bool = True): super().__init__() # EfficientNet-B0 백본 (ImageNet 사전학습) self.backbone = models.efficientnet_b0(pretrained=True) # 백본 동결 (Feature Extraction 모드) if freeze_backbone: for param in self.backbone.parameters(): param.requires_grad = False # 분류기 교체 (제조 결함용) in_features = self.backbone.classifier[1].in_features self.backbone.classifier = nn.Sequential( nn.Dropout(p=0.3), nn.Linear(in_features, 256), nn.ReLU(), nn.Dropout(p=0.2), nn.Linear(256, num_classes) ) def forward(self, x): return self.backbone(x) # 데이터 전처리 파이프라인 train_transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 사용 예시 model = DefectClassifier(num_classes=5) # OK, Scratch, Dent, Stain, Crack device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device)

3성능 평가 지표

제조 품질 검사에서는 단순 정확도(Accuracy)보다 재현율(Recall)과 정밀도(Precision)가 더 중요합니다. 미검출(Escape)과 허위 불량(False Call)의 비즈니스 영향이 다르기 때문입니다.

제조 품질 검사 성능 지표 (Confusion Matrix)
실제
불량 정상
예측 불량 TP
정상 검출
FP
허위 불량
False Call
(과잉 검출)
정상 FN
미검출
TN
정상 통과
Escape
(치명적!)
1. Recall (재현율)
TP / (TP + FN)
실제 불량 중 검출한 비율
목표: 99% 이상
2. Precision (정밀도)
TP / (TP + FP)
불량 예측 중 실제 불량 비율
목표: 95% 이상
3. Escape Rate
FN / (TP + FN)
불량 중 미검출 비율
목표: 0.1% 이하

실무 주의: 정확도(Accuracy)가 99%여도 불량 검출률이 낮을 수 있습니다. 클래스 불균형이 심한 제조 데이터에서는 Recall과 Precision을 함께 확인해야 합니다.

4클래스 불균형 처리

제조 데이터에서 불량 샘플은 전체의 0.1~5%에 불과합니다. 이러한 극심한 클래스 불균형 문제를 해결하는 방법을 알아봅니다.

1 가중 손실함수

희소 클래스에 높은 가중치 부여. CrossEntropyLoss의 weight 파라미터 활용

2 오버샘플링

불량 샘플 복제 또는 합성(SMOTE). WeightedRandomSampler 활용

3 Focal Loss

쉬운 샘플 가중치 감소, 어려운 샘플에 집중. 객체 탐지에 효과적

4 데이터 증강

불량 샘플에 더 많은 증강 적용. 회전, 플립, 색상 변환 등

import torch.nn as nn # 가중 CrossEntropy def get_weighted_loss(class_counts): """클래스별 빈도 역수로 가중치 계산""" total = sum(class_counts) weights = [total / c for c in class_counts] weights = torch.FloatTensor(weights) weights = weights / weights.sum() * len(weights) return nn.CrossEntropyLoss(weight=weights) # 예: OK 9000장, Scratch 500장, Dent 300장 class_counts = [9000, 500, 300, 150, 50] criterion = get_weighted_loss(class_counts)

5모델 학습 파이프라인

결함 분류 모델의 학습 파이프라인을 구현합니다. 데이터 로딩, 학습 루프, 검증, 체크포인트 저장까지 포함합니다.

from torch.utils.data import DataLoader from tqdm import tqdm def train_epoch(model, dataloader, criterion, optimizer, device): """1 에포크 학습""" model.train() running_loss = 0.0 correct = 0 total = 0 for images, labels in tqdm(dataloader, desc="Training"): images, labels = images.to(device), labels.to(device) optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() _, predicted = outputs.max(1) total += labels.size(0) correct += predicted.eq(labels).sum().item() return running_loss / len(dataloader), correct / total def validate(model, dataloader, criterion, device): """검증""" model.eval() running_loss = 0.0 all_preds, all_labels = [], [] with torch.no_grad(): for images, labels in dataloader: images, labels = images.to(device), labels.to(device) outputs = model(images) loss = criterion(outputs, labels) running_loss += loss.item() _, predicted = outputs.max(1) all_preds.extend(predicted.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) accuracy = np.mean(np.array(all_preds) == np.array(all_labels)) return running_loss / len(dataloader), accuracy

학습 팁: (1) 불량 클래스 불균형 → 가중 손실함수 적용, (2) 과적합 방지 → 데이터 증강, 드롭아웃, Early Stopping, (3) 학습률 → Cosine Annealing 사용 권장

6추론 및 배포

학습된 모델을 실제 검사 라인에 적용하기 위한 추론 코드입니다.

class DefectInferenceEngine: """결함 분류 추론 엔진""" def __init__(self, model_path: str, class_names: list, device='cuda'): self.device = torch.device(device if torch.cuda.is_available() else 'cpu') self.class_names = class_names # 모델 로드 self.model = DefectClassifier(num_classes=len(class_names)) checkpoint = torch.load(model_path, map_location=self.device) self.model.load_state_dict(checkpoint['model_state_dict']) self.model.to(self.device) self.model.eval() def predict(self, image_path: str): """단일 이미지 추론""" image = Image.open(image_path).convert('RGB') input_tensor = self.transform(image).unsqueeze(0).to(self.device) with torch.no_grad(): outputs = self.model(input_tensor) probabilities = torch.softmax(outputs, dim=1)[0] predicted_idx = probabilities.argmax().item() return { 'predicted_class': self.class_names[predicted_idx], 'confidence': probabilities[predicted_idx].item(), 'is_defect': predicted_idx != 0 # OK가 0번 클래스 } # 사용 예시 engine = DefectInferenceEngine( model_path='best_model.pth', class_names=['OK', 'Scratch', 'Dent', 'Stain', 'Crack'] ) result = engine.predict('test_image.jpg') print(f"결과: {result['predicted_class']} ({result['confidence']*100:.1f}%)")