397 lines
13 KiB
Plaintext
397 lines
13 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# 간단한 이미지 분류기\n",
|
|
"\n",
|
|
"이 노트북은 사전 학습된 신경망을 사용하여 이미지를 분류하는 방법을 보여줍니다.\n",
|
|
"\n",
|
|
"**배울 내용:**\n",
|
|
"- 사전 학습된 모델을 로드하고 사용하는 방법\n",
|
|
"- 이미지 전처리\n",
|
|
"- 이미지에 대한 예측 수행\n",
|
|
"- 신뢰도 점수 이해하기\n",
|
|
"\n",
|
|
"**사용 사례:** 이미지에서 객체 식별 (예: \"고양이\", \"강아지\", \"자동차\" 등)\n",
|
|
"\n",
|
|
"---\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 1단계: 필요한 라이브러리 가져오기\n",
|
|
"\n",
|
|
"필요한 도구를 가져와 봅시다. 아직 모든 것을 이해하지 못해도 걱정하지 마세요!\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Core libraries\n",
|
|
"import numpy as np\n",
|
|
"from PIL import Image\n",
|
|
"import requests\n",
|
|
"from io import BytesIO\n",
|
|
"\n",
|
|
"# TensorFlow for deep learning\n",
|
|
"try:\n",
|
|
" import tensorflow as tf\n",
|
|
" from tensorflow.keras.applications import MobileNetV2\n",
|
|
" from tensorflow.keras.applications.mobilenet_v2 import preprocess_input, decode_predictions\n",
|
|
" print(\"✅ TensorFlow loaded successfully!\")\n",
|
|
" print(f\" Version: {tf.__version__}\")\n",
|
|
"except ImportError:\n",
|
|
" print(\"❌ Please install TensorFlow: pip install tensorflow\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 2단계: 사전 학습된 모델 로드하기\n",
|
|
"\n",
|
|
"우리는 **MobileNetV2**를 사용할 것입니다. 이 신경망은 이미 수백만 개의 이미지로 학습되었습니다.\n",
|
|
"\n",
|
|
"이것을 **전이 학습**이라고 합니다 - 다른 사람이 학습시킨 모델을 사용하는 것이죠!\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"print(\"📦 Loading pre-trained MobileNetV2 model...\")\n",
|
|
"print(\" This may take a minute on first run (downloading weights)...\")\n",
|
|
"\n",
|
|
"# Load the model\n",
|
|
"# include_top=True means we use the classification layer\n",
|
|
"# weights='imagenet' means it was trained on ImageNet dataset\n",
|
|
"model = MobileNetV2(weights='imagenet', include_top=True)\n",
|
|
"\n",
|
|
"print(\"✅ Model loaded!\")\n",
|
|
"print(f\" The model can recognize 1000 different object categories\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 3단계: 헬퍼 함수\n",
|
|
"\n",
|
|
"모델에 사용할 이미지를 로드하고 준비하는 함수를 만들어봅시다.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def load_image_from_url(url):\n",
|
|
" \"\"\"\n",
|
|
" Load an image from a URL.\n",
|
|
" \n",
|
|
" Args:\n",
|
|
" url: Web address of the image\n",
|
|
" \n",
|
|
" Returns:\n",
|
|
" PIL Image object\n",
|
|
" \"\"\"\n",
|
|
" response = requests.get(url)\n",
|
|
" img = Image.open(BytesIO(response.content))\n",
|
|
" return img\n",
|
|
"\n",
|
|
"\n",
|
|
"def prepare_image(img):\n",
|
|
" \"\"\"\n",
|
|
" Prepare an image for the model.\n",
|
|
" \n",
|
|
" Steps:\n",
|
|
" 1. Resize to 224x224 (model's expected size)\n",
|
|
" 2. Convert to array\n",
|
|
" 3. Add batch dimension\n",
|
|
" 4. Preprocess for MobileNetV2\n",
|
|
" \n",
|
|
" Args:\n",
|
|
" img: PIL Image\n",
|
|
" \n",
|
|
" Returns:\n",
|
|
" Preprocessed image array\n",
|
|
" \"\"\"\n",
|
|
" # Resize to 224x224 pixels\n",
|
|
" img = img.resize((224, 224))\n",
|
|
" \n",
|
|
" # Convert to numpy array\n",
|
|
" img_array = np.array(img)\n",
|
|
" \n",
|
|
" # Add batch dimension (model expects multiple images)\n",
|
|
" img_array = np.expand_dims(img_array, axis=0)\n",
|
|
" \n",
|
|
" # Preprocess for MobileNetV2\n",
|
|
" img_array = preprocess_input(img_array)\n",
|
|
" \n",
|
|
" return img_array\n",
|
|
"\n",
|
|
"\n",
|
|
"def classify_image(img):\n",
|
|
" \"\"\"\n",
|
|
" Classify an image and return top predictions.\n",
|
|
" \n",
|
|
" Args:\n",
|
|
" img: PIL Image\n",
|
|
" \n",
|
|
" Returns:\n",
|
|
" List of (class_name, confidence) tuples\n",
|
|
" \"\"\"\n",
|
|
" # Prepare the image\n",
|
|
" img_array = prepare_image(img)\n",
|
|
" \n",
|
|
" # Make prediction\n",
|
|
" predictions = model.predict(img_array, verbose=0)\n",
|
|
" \n",
|
|
" # Decode predictions to human-readable labels\n",
|
|
" # top=5 means we get the top 5 most likely classes\n",
|
|
" decoded = decode_predictions(predictions, top=5)[0]\n",
|
|
" \n",
|
|
" # Convert to simpler format\n",
|
|
" results = [(label, float(confidence)) for (_, label, confidence) in decoded]\n",
|
|
" \n",
|
|
" return results\n",
|
|
"\n",
|
|
"\n",
|
|
"print(\"✅ Helper functions ready!\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 4단계: 샘플 이미지 테스트\n",
|
|
"\n",
|
|
"인터넷에서 가져온 몇 가지 이미지를 분류해 봅시다!\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Sample images to classify\n",
|
|
"# These are from Unsplash (free stock photos)\n",
|
|
"test_images = [\n",
|
|
" {\n",
|
|
" \"url\": \"https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=400\",\n",
|
|
" \"description\": \"A cat\"\n",
|
|
" },\n",
|
|
" {\n",
|
|
" \"url\": \"https://images.unsplash.com/photo-1552053831-71594a27632d?w=400\",\n",
|
|
" \"description\": \"A dog\"\n",
|
|
" },\n",
|
|
" {\n",
|
|
" \"url\": \"https://images.unsplash.com/photo-1511919884226-fd3cad34687c?w=400\",\n",
|
|
" \"description\": \"A car\"\n",
|
|
" },\n",
|
|
"]\n",
|
|
"\n",
|
|
"print(f\"🧪 Testing on {len(test_images)} images...\")\n",
|
|
"print(\"=\" * 70)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### 각 이미지를 분류하세요\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"for i, img_data in enumerate(test_images, 1):\n",
|
|
" print(f\"\\n📸 Image {i}: {img_data['description']}\")\n",
|
|
" print(\"-\" * 70)\n",
|
|
" \n",
|
|
" try:\n",
|
|
" # Load image\n",
|
|
" img = load_image_from_url(img_data['url'])\n",
|
|
" \n",
|
|
" # Display image\n",
|
|
" display(img.resize((200, 200))) # Show smaller version\n",
|
|
" \n",
|
|
" # Classify\n",
|
|
" results = classify_image(img)\n",
|
|
" \n",
|
|
" # Show predictions\n",
|
|
" print(\"\\n🎯 Top 5 Predictions:\")\n",
|
|
" for rank, (label, confidence) in enumerate(results, 1):\n",
|
|
" # Create a visual bar\n",
|
|
" bar_length = int(confidence * 50)\n",
|
|
" bar = \"█\" * bar_length\n",
|
|
" \n",
|
|
" print(f\" {rank}. {label:20s} {confidence*100:5.2f}% {bar}\")\n",
|
|
" \n",
|
|
" except Exception as e:\n",
|
|
" print(f\"❌ Error: {e}\")\n",
|
|
"\n",
|
|
"print(\"\\n\" + \"=\" * 70)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 5단계: 직접 이미지를 시도해보세요!\n",
|
|
"\n",
|
|
"아래 URL을 원하는 이미지 URL로 교체하여 분류를 시도해보세요.\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Try your own image!\n",
|
|
"# Replace this URL with any image URL\n",
|
|
"custom_image_url = \"https://images.unsplash.com/photo-1472491235688-bdc81a63246e?w=400\" # A flower\n",
|
|
"\n",
|
|
"print(\"🖼️ Classifying your custom image...\")\n",
|
|
"print(\"=\" * 70)\n",
|
|
"\n",
|
|
"try:\n",
|
|
" # Load and show image\n",
|
|
" img = load_image_from_url(custom_image_url)\n",
|
|
" display(img.resize((300, 300)))\n",
|
|
" \n",
|
|
" # Classify\n",
|
|
" results = classify_image(img)\n",
|
|
" \n",
|
|
" # Show results\n",
|
|
" print(\"\\n🎯 Top 5 Predictions:\")\n",
|
|
" print(\"-\" * 70)\n",
|
|
" for rank, (label, confidence) in enumerate(results, 1):\n",
|
|
" bar_length = int(confidence * 50)\n",
|
|
" bar = \"█\" * bar_length\n",
|
|
" print(f\" {rank}. {label:20s} {confidence*100:5.2f}% {bar}\")\n",
|
|
" \n",
|
|
" # Highlight top prediction\n",
|
|
" top_label, top_confidence = results[0]\n",
|
|
" print(\"\\n\" + \"=\" * 70)\n",
|
|
" print(f\"\\n🏆 Best guess: {top_label} ({top_confidence*100:.2f}% confident)\")\n",
|
|
" \n",
|
|
"except Exception as e:\n",
|
|
" print(f\"❌ Error: {e}\")\n",
|
|
" print(\" Make sure the URL points to a valid image!\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 💡 방금 무슨 일이 있었나요?\n",
|
|
"\n",
|
|
"1. **사전 학습된 모델을 로드했습니다** - MobileNetV2는 수백만 개의 이미지로 학습되었습니다.\n",
|
|
"2. **이미지를 전처리했습니다** - 모델에 맞게 크기를 조정하고 형식을 변환했습니다.\n",
|
|
"3. **모델이 예측을 수행했습니다** - 1000개의 객체 클래스에 대한 확률을 출력했습니다.\n",
|
|
"4. **결과를 디코딩했습니다** - 숫자를 사람이 읽을 수 있는 레이블로 변환했습니다.\n",
|
|
"\n",
|
|
"### 신뢰 점수 이해하기\n",
|
|
"\n",
|
|
"- **90-100%**: 매우 높은 신뢰도 (거의 확실히 정확함)\n",
|
|
"- **70-90%**: 높은 신뢰도 (아마도 정확함)\n",
|
|
"- **50-70%**: 어느 정도 신뢰도 있음 (정확할 가능성 있음)\n",
|
|
"- **50% 미만**: 신뢰도 낮음 (불확실함)\n",
|
|
"\n",
|
|
"### 왜 예측이 틀릴 수 있을까요?\n",
|
|
"\n",
|
|
"- **특이한 각도나 조명** - 모델은 일반적인 사진으로 학습되었습니다.\n",
|
|
"- **여러 객체가 있는 경우** - 모델은 주요 객체 하나를 예상합니다.\n",
|
|
"- **드문 객체** - 모델은 1000개의 카테고리만 알고 있습니다.\n",
|
|
"- **저화질 이미지** - 흐릿하거나 픽셀화된 이미지는 더 어렵습니다.\n",
|
|
"\n",
|
|
"---\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 🚀 다음 단계\n",
|
|
"\n",
|
|
"1. **다양한 이미지 시도하기:**\n",
|
|
" - [Unsplash](https://unsplash.com)에서 이미지를 찾아보세요.\n",
|
|
" - 마우스 오른쪽 클릭 → \"이미지 주소 복사\"로 URL 가져오기\n",
|
|
"\n",
|
|
"2. **실험해보기:**\n",
|
|
" - 추상 예술 작품에서는 어떤 결과가 나올까요?\n",
|
|
" - 다양한 각도에서 객체를 인식할 수 있을까요?\n",
|
|
" - 여러 객체를 처리하는 방식은 어떨까요?\n",
|
|
"\n",
|
|
"3. **더 알아보기:**\n",
|
|
" - [컴퓨터 비전 강의](../lessons/4-ComputerVision/README.md)를 탐색해보세요.\n",
|
|
" - 직접 이미지 분류기를 학습시키는 방법을 배워보세요.\n",
|
|
" - CNN(합성곱 신경망)이 어떻게 작동하는지 이해해보세요.\n",
|
|
"\n",
|
|
"---\n",
|
|
"\n",
|
|
"## 🎉 축하합니다!\n",
|
|
"\n",
|
|
"최첨단 신경망을 사용하여 이미지 분류기를 직접 구축하셨습니다!\n",
|
|
"\n",
|
|
"이 기술은 다음과 같은 분야에서 활용됩니다:\n",
|
|
"- Google Photos (사진 정리)\n",
|
|
"- 자율주행 자동차 (객체 인식)\n",
|
|
"- 의료 진단 (X-ray 분석)\n",
|
|
"- 품질 관리 (결함 감지)\n",
|
|
"\n",
|
|
"계속 탐구하고 배우세요! 🚀\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"\n---\n\n**면책 조항**: \n이 문서는 AI 번역 서비스 [Co-op Translator](https://github.com/Azure/co-op-translator)를 사용하여 번역되었습니다. 정확성을 위해 최선을 다하고 있으나, 자동 번역에는 오류나 부정확성이 포함될 수 있습니다. 원본 문서의 원어 버전이 권위 있는 출처로 간주되어야 합니다. 중요한 정보의 경우, 전문적인 인간 번역을 권장합니다. 이 번역 사용으로 인해 발생하는 오해나 잘못된 해석에 대해 당사는 책임을 지지 않습니다.\n"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.8.0"
|
|
},
|
|
"coopTranslator": {
|
|
"original_hash": "1d472141d9df46b751542b3c29f88677",
|
|
"translation_date": "2025-10-03T11:41:28+00:00",
|
|
"source_file": "examples/03-image-classifier.ipynb",
|
|
"language_code": "ko"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
} |