■ 판별 프로그램 만들기
cats_vs_dogs_improved.py('개와 고양이 분류' 실습 #3 코드 실행)를 실행하면 CNN 알고리즘이 학습되어 가중치(패턴 지식)가 저장된 모델 파일이 만들어집니다

■ 이미지 판별 프로그램 사용을 위한 두 파일 연결 방법 (순서)
① 먼저 실행 — cats_vs_dogs_improved.py → 학습 완료 후 cats_vs_dogs_model.h5 파일이 생성됨
② 그 다음 실행 — predict_cat_dog.py (이미지 판별 프로그램) → .h5 파일을 불러와 사진을 판별함
두 파일은 .h5 모델 파일 하나로 연결됩니다 (다이어그램의 주황색 부분).
■ 배포 시 필요한 파일
| 파일 | 설명 |
| predict_cat_dog.py | 판별 프로그램 본체 |
| cats_vs_dogs_model.h5 | 학습된 모델 (가중치) |
단독 실행 파일(.exe)로 배포하고 싶다면
상대방이 Python 설치 없이 바로 실행할 수 있게 만들 수 있습니다.
pip install pyinstaller
pyinstaller --onefile --windowed --add-data "cats_vs_dogs_model.h5;." predict_cat_dog.py
실행하면 dist/ 폴더 안에 predict_cat_dog.exe 하나만 생성되고, 모델 파일이 exe 안에 포함됩니다. 단, .exe 파일 크기가 수백 MB 정도로 커집니다 (TensorFlow가 통째로 포함되기 때문).
TensorFlow가 필요한 이유
predict_cat_dog.py 안의 model.predict() 한 줄이 호출되는 순간, TensorFlow가 내부에서 다음을 처리합니다.
사진 한 장(150×150×3 = 67,500개 숫자)이 들어오면, Conv2D·MaxPooling·Dense 레이어를 통과하면서 수백만 번의 행렬 곱셈과 덧셈이 실행됩니다. 이 계산을 Python 코드로 직접 짜면 몇 분이 걸리지만, TensorFlow가 GPU/CPU에 최적화된 방식으로 처리하기 때문에 1초도 안 걸리는 겁니다.
즉, .h5 파일은 "계산에 쓸 숫자(가중치)"고, TensorFlow는 "그 숫자로 계산을 실제로 돌리는 엔진"입니다. 둘 중 하나라도 없으면 판별이 불가능합니다.

Predict cat dog.py
"""
개와 고양이 판별 프로그램
- 학습된 모델(cats_vs_dogs_model.h5)을 불러와서
- tkinter GUI로 사진을 드래그 앤 드롭하여 판별
- 사용법: python predict_cat_dog.py
"""
import tkinter as tk
from tkinter import filedialog, ttk
import threading
import os
# tkinter DnD는 별도 라이브러리 필요 → tkinterdnd2 사용
try:
from tkinterdnd2 import TkinterDnD, DND_FILES
DND_AVAILABLE = True
except ImportError:
DND_AVAILABLE = False
import numpy as np
from PIL import Image, ImageTk
import tensorflow as tf
# ──────────────────────────────────────────
# 설정값
# ──────────────────────────────────────────
MODEL_PATH = "cats_vs_dogs_model.h5" # 저장된 모델 경로
IMG_SIZE = (150, 150)
# ──────────────────────────────────────────
# 모델 로드
# ──────────────────────────────────────────
def load_model():
if not os.path.exists(MODEL_PATH):
raise FileNotFoundError(
f"모델 파일을 찾을 수 없습니다: {MODEL_PATH}\n"
"먼저 cats_vs_dogs_improved.py 를 실행해 모델을 저장하세요."
)
return tf.keras.models.load_model(MODEL_PATH)
# ──────────────────────────────────────────
# 이미지 전처리 & 예측
# ──────────────────────────────────────────
def preprocess(image_path):
img = Image.open(image_path).convert("RGB")
img = img.resize(IMG_SIZE)
arr = np.array(img, dtype=np.float32) / 255.0
return np.expand_dims(arr, axis=0), img # (1,150,150,3), PIL Image
def predict(model, image_path):
arr, pil_img = preprocess(image_path)
prob = float(model.predict(arr, verbose=0)[0][0]) # 0=고양이, 1=개
label = "🐶 개 (Dog)" if prob >= 0.5 else "🐱 고양이 (Cat)"
confidence = prob if prob >= 0.5 else 1 - prob
return label, confidence, pil_img
# ──────────────────────────────────────────
# GUI
# ──────────────────────────────────────────
class App:
def __init__(self, root, model):
self.root = root
self.model = model
root.title("🐾 개 vs 고양이 판별기")
root.geometry("520x680")
root.configure(bg="#0f1117")
root.resizable(False, False)
self._build_ui()
# ── UI 구성 ──────────────────────────
def _build_ui(self):
BG = "#0f1117"
CARD = "#1a1d27"
ACC = "#7c6aff"
TXT = "#e8e6f0"
SUB = "#6b6880"
# 제목
tk.Label(self.root, text="🐾 Cat vs Dog Classifier",
font=("Helvetica", 18, "bold"),
bg=BG, fg=TXT).pack(pady=(28, 4))
tk.Label(self.root, text="이미지를 드래그하거나 클릭하여 업로드하세요",
font=("Helvetica", 10),
bg=BG, fg=SUB).pack(pady=(0, 18))
# 드롭존 프레임
self.drop_frame = tk.Frame(self.root, bg=CARD,
width=420, height=300,
highlightthickness=2,
highlightbackground=ACC)
self.drop_frame.pack(padx=50)
self.drop_frame.pack_propagate(False)
self.preview_label = tk.Label(self.drop_frame,
text="📂\n\n사진을 여기에 드래그하거나\n클릭하여 선택",
font=("Helvetica", 12),
bg=CARD, fg=SUB,
cursor="hand2")
self.preview_label.pack(expand=True, fill="both")
self.preview_label.bind("<Button-1>", lambda e: self._open_file())
# 드래그 앤 드롭 활성화
if DND_AVAILABLE:
self.drop_frame.drop_target_register(DND_FILES)
self.drop_frame.dnd_bind("<<Drop>>", self._on_drop)
self.preview_label.drop_target_register(DND_FILES)
self.preview_label.dnd_bind("<<Drop>>", self._on_drop)
# 버튼
btn_frame = tk.Frame(self.root, bg=BG)
btn_frame.pack(pady=20)
self.btn = tk.Button(btn_frame, text=" 📂 파일 선택 ",
font=("Helvetica", 11, "bold"),
bg=ACC, fg="white",
activebackground="#5c4de0",
activeforeground="white",
bd=0, padx=20, pady=10,
cursor="hand2",
command=self._open_file)
self.btn.pack()
# 결과 카드
result_bg = tk.Frame(self.root, bg=CARD,
width=420, height=130,
highlightthickness=1,
highlightbackground="#2e2d3a")
result_bg.pack(padx=50, pady=(0, 10))
result_bg.pack_propagate(False)
self.result_label = tk.Label(result_bg,
text="판별 결과가 여기에 표시됩니다",
font=("Helvetica", 14, "bold"),
bg=CARD, fg=SUB)
self.result_label.pack(expand=True)
self.conf_label = tk.Label(result_bg, text="",
font=("Helvetica", 10),
bg=CARD, fg=SUB)
self.conf_label.pack(pady=(0, 12))
# 프로그레스바
style = ttk.Style()
style.theme_use("default")
style.configure("custom.Horizontal.TProgressbar",
troughcolor=CARD,
background=ACC,
thickness=10)
self.progress = ttk.Progressbar(self.root, length=420,
style="custom.Horizontal.TProgressbar",
mode="determinate")
self.progress.pack(padx=50)
# 상태 메시지
self.status_var = tk.StringVar(value="모델 준비 완료 ✅")
tk.Label(self.root, textvariable=self.status_var,
font=("Helvetica", 9),
bg=BG, fg=SUB).pack(pady=8)
# ── 이벤트 핸들러 ────────────────────
def _on_drop(self, event):
path = event.data.strip().strip("{}")
if path.lower().endswith((".png", ".jpg", ".jpeg", ".bmp", ".webp")):
self._run_predict(path)
else:
self.status_var.set("⚠️ 지원 형식: PNG, JPG, JPEG, BMP, WEBP")
def _open_file(self):
path = filedialog.askopenfilename(
filetypes=[("이미지 파일", "*.png *.jpg *.jpeg *.bmp *.webp"),
("모든 파일", "*.*")]
)
if path:
self._run_predict(path)
def _run_predict(self, path):
self.status_var.set("🔍 분석 중...")
self.result_label.config(text="분석 중...", fg="#7c6aff")
self.conf_label.config(text="")
self.progress["value"] = 0
threading.Thread(target=self._predict_thread,
args=(path,), daemon=True).start()
def _predict_thread(self, path):
try:
label, conf, pil_img = predict(self.model, path)
self.root.after(0, self._update_ui, label, conf, pil_img)
except Exception as e:
self.root.after(0, self.status_var.set, f"❌ 오류: {e}")
def _update_ui(self, label, conf, pil_img):
# 미리보기 이미지
pil_img.thumbnail((380, 270))
tk_img = ImageTk.PhotoImage(pil_img)
self.preview_label.config(image=tk_img, text="")
self.preview_label.image = tk_img # 참조 유지
# 결과 표시
color = "#7c6aff" if "개" in label else "#ff6a8e"
self.result_label.config(text=label, fg=color,
font=("Helvetica", 18, "bold"))
self.conf_label.config(text=f"신뢰도: {conf*100:.1f}%", fg="#aaa8c0")
self.progress["value"] = conf * 100
self.status_var.set("✅ 분석 완료")
# ──────────────────────────────────────────
# 실행
# ──────────────────────────────────────────
if __name__ == "__main__":
# 모델 로드
print("모델 로딩 중...")
model = load_model()
print("모델 로드 완료!")
# GUI 실행
if DND_AVAILABLE:
root = TkinterDnD.Tk()
else:
root = tk.Tk()
print("⚠️ tkinterdnd2 미설치 → 드래그 비활성, 클릭으로 파일 선택 가능")
print(" pip install tkinterdnd2 으로 설치하면 드래그 기능 활성화됩니다.")
app = App(root, model)
root.mainloop()
by korealionkk@gmail.com
'업무 자동화 > AI' 카테고리의 다른 글
| 성공적이고 신뢰할 수 있는 분석 모형(Model)을 구축 (0) | 2026.06.01 |
|---|---|
| '개와 고양이 분류' 실습 #3 (0) | 2026.05.15 |
| '개와 고양이 분류' 실습 #2 (0) | 2026.05.14 |
| '개와 고양이 분류' 실습 #1 (0) | 2026.05.14 |
| '개와 고양이 분류' 프로젝트 #3 - 이미지 데이터 전처리 (0) | 2026.05.13 |