<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>s_s.log</title>
        <link>https://velog.io/</link>
        <description>인공지능을 공부하고 가르치는 김민수 강사입니다. 공부한 내용 및 수업 자료가 업로드 됩니다.</description>
        <lastBuildDate>Sun, 02 Mar 2025 15:39:34 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>s_s.log</title>
            <url>https://velog.velcdn.com/images/s_s/profile/3b6b163a-6e35-402a-a7e1-adadba52f0e4/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. s_s.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/s_s" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[영상처리 기초 - 6. OCR 프로젝트-2(학습 및 평가)]]></title>
            <link>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-6.-OCR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%ED%95%99%EC%8A%B5-%EB%B0%8F-%ED%8F%89%EA%B0%80</link>
            <guid>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-6.-OCR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-2%ED%95%99%EC%8A%B5-%EB%B0%8F-%ED%8F%89%EA%B0%80</guid>
            <pubDate>Sun, 02 Mar 2025 15:39:34 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-검증의-중요성">데이터 검증의 중요성</h2>
<p><strong>부정확하거나 일관되지 않은 라벨:</strong></p>
<ul>
<li>라벨의 오류는 모델의 성능을 저하시킬 수 있습니다. 모델이 잘못된 라벨을 학습하면, 정확성, 정밀도, 재현율과 같은 성능 지표가 모두 감소하게 됩니다.</li>
</ul>
<p><strong>왜곡된 라벨링:</strong>  </p>
<ul>
<li>라벨이 실제 시나리오를 제대로 대표하지 못할 경우, 모델은 새로운 데이터에 대해 일반화하지 못할 위험이 있습니다. 이는 모델이 실제 환경에서 예측 성능이 떨어지게 만드는 주요 원인이 됩니다.</li>
</ul>
<p><strong>비용 문제:</strong></p>
<ul>
<li>모델 학습 후에 잘못된 라벨을 수정하는 것은 많은 시간과 계산 리소스를 필요로 합니다. 사전에 정확한 라벨링을 보장하는 것이 훨씬 더 비용 효율적입니다.</li>
</ul>
<p><strong>편향 문제:</strong>  </p>
<ul>
<li>일관되지 않거나 부정확한 라벨링은 모델에 편향을 초래할 수 있습니다. 이는 특정 그룹이나 시나리오에 대해 모델이 불공평하거나 비효율적인 결정을 내리는 결과를 초래할 수 있습니다.</li>
</ul>
<p><strong>디버깅 어려움:</strong>  </p>
<ul>
<li>라벨링 오류는 모델 디버깅을 더 어렵게 만듭니다. 성능 저하가 모델 아키텍처, 하이퍼파라미터, 혹은 데이터 자체의 문제로 인한 것인지 구별하기가 힘들어질 수 있습니다.</li>
</ul>
<p><strong>신뢰성:</strong></p>
<ul>
<li>기계 학습 모델의 신뢰성과 효율성은 사용자 신뢰에 매우 중요합니다. 잘못된 라벨이 포함된 데이터는 특히 의료, 금융, 또는 안전이 중요한 시스템에서 모델의 신뢰성을 크게 약화시킬 수 있습니다.</li>
</ul>
<h2 id="라이브러리-import">라이브러리 import</h2>
<pre><code class="language-python">import pandas as pd
import numpy as np
from tqdm import tqdm
from glob import glob
from typing import *
from IPython.display import Image as IPImage
from sklearn.model_selection import train_test_split
import random
import os
import shutil
from PIL import Image as Image
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, models, transforms
from torch.utils.data import DataLoader, Dataset
from torch.utils.data import ConcatDataset
from sklearn.metrics import confusion_matrix, accuracy_score, f1_score
import seaborn as sns
import matplotlib.pyplot as plt
from torchvision.transforms import functional as F</code></pre>
<pre><code class="language-python"># 코랩에서 GPU 사용을 위해서는 별도의 설정이 필요합니다.
# 상단 런타임 -&gt; 런타임 유형 변경의 항목을 통해 CPU대신 사용 가능한 GPU로 하드웨어 가속기를 변경해줍니다.
# 아래 코드는 GPU 사용이 가능한 상태인지 확인하는 코드로, 올바르게 GPU 설정이 되어있다면 TURE, 1이 출력됩니다.
print(torch.cuda.is_available())
print(torch.cuda.device_count())</code></pre>
<pre><code>True
1</code></pre><h2 id="gpu-환경-설정">GPU 환경 설정</h2>
<pre><code class="language-python"># nivida-smi 명령어를 통해 할당된 GPU의 정보에 대해 확인할 수 있습니다.
!nvidia-smi</code></pre>
<pre><code>Wed Nov 29 07:59:40 2023       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 450.80.02    Driver Version: 450.80.02    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla V100-DGXS...  On   | 00000000:0F:00.0 Off |                    0 |
| N/A   39C    P0    37W / 300W |     14MiB / 32508MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+</code></pre><h2 id="랜덤시드-고정">랜덤시드 고정</h2>
<pre><code class="language-python">def seed_everything(seed):
    random.seed(seed)  # Python
    np.random.seed(seed)  # NumPy
    torch.manual_seed(seed)  # PyTorch
    torch.cuda.manual_seed(seed)  # PyTorch cuda
    torch.backends.cudnn.deterministic = True  # CuDNN
    torch.backends.cudnn.benchmark = False  # CuDNN benchmark</code></pre>
<pre><code class="language-python">seed_everything(seed=32)</code></pre>
<h2 id="google-colab에서-데이터-관리-및-최적-활용-전략">Google Colab에서 데이터 관리 및 최적 활용 전략</h2>
<h3 id="드라이브-설정">드라이브 설정</h3>
<ul>
<li><p>코랩에서 데이터에 접근하는 방법에는 크게 두 가지가 있습니다.</p>
<ul>
<li><ol>
<li>구글 드라이브 연동 : 자신의 Google Drive를 코랩에 연결하여 드라이브 안의 데이터에 접근합니다.   </li>
</ol>
</li>
<li><ol start="2">
<li>코랩의 세션 드라이브 업로드 : 코랩의 임시 저장 공간에 데이터를 업로드하여 사용합니다.</li>
</ol>
</li>
</ul>
</li>
<li><p>각각의 방법은 장단점을 가지고 있습니다.</p>
</li>
<li><p>구글 드라이브 연동의 장점은 드라이브에 데이터가 미리 준비되어 있으면 즉시 사용할 수 있다는 것이며, 데이터가 지속적으로 보존됩니다. 단점은 이미지 데이터와 같이 용량이 큰 데이터를 다룰 때, 로딩 속도가 매우 느리다는 단점이 있습니다.  </p>
</li>
<li><p>코랩의 세션 드라이브에 업로드하는 방법의 장점은 드라이브 연동에 비해 로딩 속도를 매우 빠르게 사용할 수 있다는 것입니다. 하지만 이 방법의 단점은 코랩 런타임이 초기화될 때마다 데이터가 사라지고, 다른 ipynb에서 접근이 안된다는 단점이 존재합니다.</p>
</li>
<li><p>따라서 이미지 데이터와 같이 대용량의 학습 데이터는 학습 직전에 임시 경로에 업로드하여 사용하고, 그 외 필요한 파일들은 구글 드라이브에서 직접 불러오는 것이 좋습니다. 또한 학습된 모델 파일처럼 저장이 필요한 자료는 보존을 위해 반드시 구글 드라이브에 저장합니다. 이렇게 하면 데이터 사용의 효율성과 접근성을 극대화할 수 있습니다.</p>
</li>
</ul>
<h2 id="데이터셋-로드">데이터셋 로드</h2>
<pre><code class="language-python"># 작업 환경의 현재 경로를 확인합니다.
os.getcwd()</code></pre>
<pre><code>&#39;/content&#39;</code></pre><h2 id="코랩의-세션-드라이브에-데이터-업로드">코랩의 세션 드라이브에 데이터 업로드</h2>
<pre><code class="language-python">import gdown

# Google Drive 파일 ID와 URL 설정
file_id = &quot;1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw&quot;
url = f&quot;https://drive.google.com/uc?id={file_id}&quot;

# 다운로드할 파일 이름 설정
output = &quot;OCR_ResNet18_dataset.zip&quot;

# gdown을 사용하여 파일 다운로드
gdown.download(url, output, quiet=False)

print(f&quot;파일 다운로드가 완료되었습니다: {output}&quot;)</code></pre>
<pre><code>Downloading...
From (original): https://drive.google.com/uc?id=1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw
From (redirected): https://drive.google.com/uc?id=1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw&amp;confirm=t&amp;uuid=213ebd5e-b908-4884-a0fc-fcc4cf26a37d
To: /content/OCR_ResNet18_dataset.zip
100%|██████████| 82.9M/82.9M [00:00&lt;00:00, 171MB/s]


파일 다운로드가 완료되었습니다: OCR_ResNet18_dataset.zip</code></pre><pre><code class="language-python"># unzip 명령어를 통해 압축 파일을 해제해 줍니다.
!unzip -qq OCR_ResNet18_dataset.zip</code></pre>
<pre><code class="language-python"># 코랩 드라이브에서 대량의 이미지 파일을 로드할 경우 매우 시간이 오래 걸립니다.
# 따라서 사용할 이미지는 drive 경로(/content/drive)가 아닌 코랩 세션 드라이브(/content/drive 이외의 경로)의 경로에 데이터를 업로드해줍니다.
image_path = &quot;./dataset/digit_data&quot;
asset_path = &quot;./assets&quot;</code></pre>
<pre><code class="language-python"># 학습 데이터셋
origin_train_df = pd.read_csv(f&quot;{image_path}/train_data.txt&quot;, names=[&quot;path&quot;])
origin_train_df[&quot;label&quot;] = origin_train_df[&quot;path&quot;].str[0].astype(int)
origin_train_df[&quot;path&quot;] = image_path + &quot;/&quot; + origin_train_df[&quot;path&quot;]
origin_train_df</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/fe009eba-473f-43a2-bdd2-97d7a913fcc9/image.png" alt=""></p>
<pre><code class="language-python"># 테스트 데이터셋 (valid_data.txt지만 미리 나눠진 test set으로 사용하도록 하겠음)
test_df = pd.read_csv(f&quot;{image_path}/valid_data.txt&quot;, names=[&quot;path&quot;])
test_df[&quot;label&quot;] = test_df[&quot;path&quot;].str[0].astype(int)
test_df[&quot;path&quot;] = image_path + &quot;/&quot; + test_df[&quot;path&quot;]
test_df</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8c2b7739-1549-41fe-8321-a7153f033c5d/image.png" alt=""></p>
<h2 id="trainvalidtest-데이터셋-분리">Train/Valid/Test 데이터셋 분리</h2>
<p>kamp데이터는 train/valid 밖에 구분되어 있지 않지만</p>
<p>저희는 보다 명확한 검토를 위해</p>
<pre><code>train =&gt; train/valid

valid = &gt; test</code></pre><p>로 사용하도록 하겠습니다</p>
<pre><code class="language-python"># train 데이터셋을 8:2로 나눠서 valid를 하도록 하겠습니다
train_df, valid_df = train_test_split(
    origin_train_df,
    test_size=0.2,
    random_state=0,
    shuffle=True,
    stratify=origin_train_df[&quot;label&quot;],
)</code></pre>
<h2 id="dataset-dataloader-생성">DataSet, DataLoader 생성</h2>
<p>PyTorch에서 torch.utils.data.Dataset 클래스를 사용하는 이유:</p>
<ol>
<li><p>데이터 관리의 일관성
Dataset 클래스를 사용하면 데이터를 일관된 방식으로 관리할 수 있습니다. 데이터 로드, 전처리, 샘플링을 위한 구조를 명확히 정의할 수 있어 코드 유지보수가 용이합니다. raw 데이터를 직접 사용하면 데이터를 불러오는 로직이 코드 곳곳에 흩어질 수 있습니다.
Dataset 클래스는 데이터 관련 로직을 캡슐화하여 재사용성을 높이고 코드 가독성을 향상시킵니다.</p>
</li>
<li><p>효율적인 데이터 로드
Dataset 클래스는 DataLoader와 함께 사용되어 배치 처리 및 병렬 데이터 로드를 지원합니다.raw 데이터를 직접 사용하면 배치 처리를 수동으로 구현해야 하며, 효율적이지 않을 수 있습니다.
DataLoader는 Dataset에서 배치를 자동으로 생성하고, 멀티스레딩을 통해 데이터 로드 속도를 높입니다.</p>
</li>
<li><p>동적인 데이터 전처리
Dataset 클래스의 핵심은 <strong>getitem</strong>과 <strong>len</strong> 메서드를 커스터마이징할 수 있다는 점입니다. 데이터를 로드하는 동시에 필요한 전처리(예: 텍스트 토크나이징, 이미지 변환 등)를 수행할 수 있습니다.
raw 데이터를 직접 사용하면 데이터 전처리를 별도의 단계에서 처리해야 하므로 유연성이 떨어집니다.</p>
</li>
<li><p>메모리 관리
Dataset은 필요할 때 데이터를 로드하는 방식을 지원합니다(예: Lazy Loading).
raw 데이터를 한꺼번에 메모리에 올리면 큰 데이터셋의 경우 메모리 부족 문제가 발생할 수 있습니다.
Dataset 클래스는 데이터를 필요할 때만 로드하거나 스트리밍 방식으로 처리하여 메모리 사용량을 최소화할 수 있습니다.</p>
</li>
<li><p>확장성
사용자가 직접 데이터 구조를 정의하고 커스터마이징할 수 있습니다.
raw 데이터는 구조적으로 고정되어 있어 추가적인 기능(예: 샘플링, 데이터 증강 등)을 구현하기 어렵습니다.
Dataset을 상속받아 자신만의 데이터셋 클래스를 만들어 다양한 데이터 소스와 처리 방법을 통합할 수 있습니다.</p>
</li>
</ol>
<pre><code class="language-python"># 사용자 정의 데이터셋 클래스 생성 (CustomDataset)
class CustomDataset(Dataset):
    def __init__(self, dataframe, transform=None):
        # 데이터셋 초기화
        # dataframe: 이미지 경로와 레이블이 포함된 데이터프레임
        # transform: 이미지에 적용할 전처리(transform) 함수
        self.dataframe = dataframe
        self.transform = transform

    def __len__(self):
        # 데이터셋의 총 샘플 수 반환
        return len(self.dataframe)

    def __getitem__(self, idx):
        # 주어진 인덱스(idx)에 해당하는 샘플을 반환
        # 이미지 경로를 데이터프레임에서 가져옴
        img_name = self.dataframe.iloc[idx, 0]
        # 이미지 파일을 열고 RGB 모드로 변환
        img = Image.open(img_name).convert(&#39;RGB&#39;)
        # 레이블 정보를 정수형으로 가져옴
        label = int(self.dataframe.iloc[idx, 1])

        # transform이 지정되어 있다면 이미지에 전처리를 적용
        if self.transform:
            img = self.transform(img)

        # 이미지와 레이블을 반환
        return img, label</code></pre>
<pre><code class="language-python">&#39;&#39;&#39;
아래는 이미지 데이터셋의 각 RGB 채널에 대해 전체적인 평균(mean)과 표준 편차(standard deviation, std)를 계산하는 코드입니다.
이 작업은 데이터 전처리 과정에서 매우 중요한데, 다음과 같은 목적이 있습니다.

데이터 정규화 (Normalization): 딥러닝 모델에서 이미지를 입력으로 사용할 때, 입력 데이터의 스케일링을 동일하게 하는 것이 중요합니다.
각 픽셀 값에서 채널별 평균을 빼고, 표준 편차로 나누어서 픽셀 값의 분포를 정규화할 수 있습니다.
이렇게 함으로써 모델은 더 빠르게 수렴할 수 있고, 다른 데이터셋에 대한 모델의 일반화 능력을 향상시킬 수 있습니다.

데이터 표준화 (Standardization):
데이터셋에 있는 이미지들이 서로 다른 출처에서 오거나 다양한 조명 환경에서 촬영된 경우, 픽셀 값의 분포가 다를 수 있습니다.
표준화 과정을 거치면 이러한 차이를 줄여 모델이 이미지의 구조적인 특성에 더 집중할 수 있게 해줍니다.

모델 입력 요구사항 충족:
많은 딥러닝 모델들, 특히 사전 훈련된 네트워크는 특정 범위의 입력 값을 요구합니다.
예를 들어, 일부 모델은 입력 값으로 0에서 1 사이, 또는 -1에서 1 사이의 값을 요구할 수 있는데,
평균과 표준 편차를 계산하면 이러한 요구사항을 충족시키는 정규화 과정을 쉽게 적용할 수 있습니다.

하지만 해당 코드를 직접 실행하면, 매우 많은 연산량으로 인해 코랩 환경이 다운될 가능성이 높습니다.
보다 좋은 환경에서 직접 실행해 보고, 본 과정에서는 미리 계산된 값으로 이를 대체하겠습니다.
&#39;&#39;&#39;

def compute_overall_mean_std(dfs):
    all_pixels = {0: [], 1: [], 2: []}

    for df in dfs:
        for index, row in df.iterrows():
            img_path = row[&#39;path&#39;]  # &#39;path&#39; 대신에 실제 경로가 있는 컬럼명을 사용해주세요.
            img = Image.open(img_path)
            img_np = np.array(img)

            for i in range(3): # RGB 채널
                channel_pixels = img_np[:, :, i].ravel().tolist()  # 각 채널의 모든 픽셀 값을 수집합니다.
                all_pixels[i].extend(channel_pixels)

    means = [np.mean(all_pixels[i]) for i in range(3)]
    stds = [np.std(all_pixels[i]) for i in range(3)]

    return means, stds


# train_df와 valid_df에 대해서 계산
# 컴퓨팅 자원이 풍부한 환경에서 실행할 경우 직접 실행해볼 수 있습니다.
# channel_means, channel_stds = compute_overall_mean_std([train_df, valid_df])

# 연산량 이슈로 미리 계산해둔 값 사용
channel_means = [193.67807472479592, 111.60517909723688, 118.44033090803525]
channel_stds = [63.00208387441839, 87.1299016633704, 71.00312754293988]

print(&quot;Overall Data: Mean -&quot;, channel_means, &quot;Std -&quot;, channel_stds)</code></pre>
<pre><code>Overall Data: Mean - [193.67807472479592, 111.60517909723688, 118.44033090803525] Std - [63.00208387441839, 87.1299016633704, 71.00312754293988]</code></pre><pre><code class="language-python"># 채널 평균 및 표준편차를 0~1 사이의 값으로 정규화
normalized_channel_means = [x / 255 for x in channel_means]
normalized_channel_stds = [x / 255 for x in channel_stds]

# 이미지 전처리를 위한 변환 작업 정의.
# transforms.Normalize 이전에 이미 픽셀값은 0~1 사이로 정규화 되어 있습니다.
# 따라서 기존의 0~255 픽셀값 기준에서 도출된 channel_means, channel_stds 도 0~1 사이 값으로 정규화 된 normalized_channel_means, normalized_channel_stds 로 변환하여 사용합니다
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=normalized_channel_means, std=normalized_channel_stds),
])

# 코랩의 GPU 환경은 실행시마다 바뀔 수 있습니다.
# 혹시 메모리 에러가 난다면 batch size를 줄여서 학습을 시도해 볼 수 있습니다.
batch_size = 256

# 데이터셋 로딩
train_dataset = CustomDataset(dataframe=train_df, transform=transform)
valid_dataset = CustomDataset(dataframe=valid_df, transform=transform)
test_dataset = CustomDataset(dataframe=test_df, transform=transform)

# 데이터 로더 설정
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)
valid_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4)</code></pre>
<pre><code>/usr/local/lib/python3.10/dist-packages/torch/utils/data/dataloader.py:557: UserWarning: This DataLoader will create 4 worker processes in total. Our suggested max number of worker in current system is 2, which is smaller than what this DataLoader is going to create. Please be aware that excessive worker creation might get DataLoader running slow or even freeze, lower the worker number to avoid potential slowness/freeze if necessary.
  warnings.warn(_create_warning_msg(</code></pre><h2 id="resnet-모델-학습">ResNet 모델 학습</h2>
<pre><code class="language-python"># 사전 학습된 ResNet18 모델 불러오기
model = models.resnet18(pretrained=True)

# 모델의 마지막 완전 연결 계층(fc)을 사용자 정의 계층으로 대체
# 이 계층은 in_features에서 10개의 출력으로 매핑합니다 (10개 클래스 분류를 위함)
model.fc = nn.Sequential(
    nn.Linear(model.fc.in_features, 10),
)

# 교차 엔트로피 손실 함수 초기화
criterion = nn.CrossEntropyLoss()
# 최적화 알고리즘으로 Adam 사용
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5, weight_decay=1e-5)

device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
model = model.to(device)</code></pre>
<pre><code>/usr/local/lib/python3.10/dist-packages/torchvision/models/_utils.py:208: UserWarning: The parameter &#39;pretrained&#39; is deprecated since 0.13 and may be removed in the future, please use &#39;weights&#39; instead.
  warnings.warn(
/usr/local/lib/python3.10/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for &#39;weights&#39; are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet18_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet18_Weights.DEFAULT` to get the most up-to-date weights.
  warnings.warn(msg)
Downloading: &quot;https://download.pytorch.org/models/resnet18-f37072fd.pth&quot; to /root/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth
100%|██████████| 44.7M/44.7M [00:00&lt;00:00, 137MB/s]</code></pre><pre><code class="language-python"># 학습 파라미터 설정
num_epochs = 50  # 학습 반복 횟수
best_val_acc = 0.0  # 최상의 검증 정확도를 저장하는 변수
patience = 5  # 얼리 스탑핑을 위한 patience 설정 (개선되지 않은 횟수)
no_improve = 0  # 개선되지 않은 에포크 수를 카운트하는 변수

for epoch in range(num_epochs):

    # 모델을 학습 모드로 전환
    model.train()
    running_loss = 0.0  # 에포크 동안의 총 손실을 저장하는 변수
    correct_train = 0  # 정확하게 예측한 학습 데이터의 개수를 저장하는 변수
    total_train = 0  # 총 학습 데이터 개수를 저장하는 변수

    # 학습 데이터를 반복하여 모델 업데이트
    for inputs, labels in tqdm(train_loader, desc=f&quot;Epoch {epoch+1}/{num_epochs}&quot;, dynamic_ncols=True):
        # 데이터를 장치로 이동 (GPU 또는 CPU)
        inputs, labels = inputs.to(device), labels.to(device)

        # 옵티마이저의 기울기 초기화
        optimizer.zero_grad()
        # 모델에 입력을 전달하여 예측값 출력
        outputs = model(inputs)
        # 손실 함수 계산
        loss = criterion(outputs, labels)
        # 손실에 대한 역전파 수행 (기울기 계산)
        loss.backward()
        # 옵티마이저를 통해 모델 파라미터 업데이
        optimizer.step()
        # 현재 배치의 손실을 누적
        running_loss += loss.item()

        # 정확도 계산을 위한 예측값 처리
        _, predicted = torch.max(outputs.data, 1) # 최대값을 가지는 클래스 예측
        total_train += labels.size(0) # 총 학습 데이터 수 누적
        correct_train += (predicted == labels).sum().item() # 맞춘 예측의 개수 누적

    # 에포크별 학습 정확도와 손실 계산
    train_acc = correct_train / total_train
    train_loss = running_loss / len(train_loader)

    # Validate
    model.eval()  # 모델을 평가 모드로 전환 (드롭아웃, 배치 정규화 등 비활성화)
    running_val_loss = 0.0  # 검증 손실을 저장하는 변수
    correct_val = 0  # 정확하게 예측한 검증 데이터의 개수를 저장하는 변수
    total_val = 0  # 총 검증 데이터 개수를 저장하는 변수
    with torch.no_grad(): # 검증 시에는 기울기를 계산하지 않음 (메모리 및 계산량 절약)
        for inputs, labels in valid_loader:
            # 데이터를 장치로 이동
            inputs, labels = inputs.to(device), labels.to(device)
            # 모델에 입력을 전달하여 예측값 출력
            outputs = model(inputs)
            # 손실 함수 계산
            loss = criterion(outputs, labels)
            # 현재 배치의 손실을 누적
            running_val_loss += loss.item()

            # 정확도 계산을 위한 예측값 처리
            _, predicted = torch.max(outputs.data, 1)  # 최대값을 가지는 클래스 예측
            total_val += labels.size(0)  # 총 검증 데이터 수 누적
            correct_val += (predicted == labels).sum().item()  # 맞춘 예측의 개수 누적

    # 에포크별 검증 정확도와 손실 계산
    val_acc = correct_val / total_val  # 검증 정확도 계산
    val_loss = running_val_loss / len(valid_loader)  # 검증 손실 평균 계산

    # 학습 및 검증 결과 출력
    print(f&#39;Train Loss: {train_loss:.4f}, Train Accuracy: {train_acc:.4f}, &#39;
          f&#39;Validation Loss: {val_loss:.4f}, Validation Accuracy: {val_acc:.4f}&#39;)

     # 최상의 검증 정확도를 기록하고 모델 저장
    if val_acc &gt; best_val_acc:
        best_val_acc = val_acc
        torch.save(model.state_dict(), &#39;best_model_before.pth&#39;)
        no_improve = 0
    else:
        no_improve += 1
        if no_improve &gt;= patience:  # 설정한 얼리 스타핑 patience에 도달하면 학습을 중단합니다.
            print(&quot;Early stopping&quot;)
            break

print(&#39;Finished Training&#39;)</code></pre>
<h2 id="best-model-load">best model load</h2>
<pre><code class="language-python">model.load_state_dict(torch.load(&#39;best_model_before.pth&#39;))
model.eval()</code></pre>
<h2 id="오차-데이터-분석---mis-label-확인">오차 데이터 분석 - mis label 확인</h2>
<ul>
<li>높은 confidence로 예측 했으나, class가 다를 시 해당 데이터의 라벨링이 잘못 되었는지 확인이 필요합니다.</li>
</ul>
<pre><code class="language-python">all_df = pd.concat([origin_train_df,test_df])

# 잘못 예측된 이미지와 라벨을 저장할 리스트
mismatched_images = []

# Softmax 함수 초기화 (확률 값으로 변환하기 위함)
softmax = nn.Softmax(dim=1)

# 모델을 GPU로 이동
model = model.to(&#39;cuda&#39;)  # 만약 사용 가능한 GPU가 있다면.

for index,(img_path,true_label) in tqdm(all_df.iterrows()):

    # Load and preprocess the image
    image = Image.open(img_path).convert(&#39;RGB&#39;)
    input_tensor = transform(image).unsqueeze(0)

    # 입력 텐서를 GPU로 이동
    input_tensor = input_tensor.to(&#39;cuda&#39;)  # 만약 사용 가능한 GPU가 있다면.

    # Get model outputs
    with torch.no_grad():
        logits = model(input_tensor)

    # Softmax를 사용하여 확률로 변환
    probabilities = softmax(logits)

    # 가장 높은 확률을 가진 라벨과 그 확률을 추출
    max_prob, predicted_label = torch.max(probabilities, 1)

     # 예측된 라벨이 실제 라벨과 다르고, 확률이 0.90보다 높은 경우 리스트에 추가
    if predicted_label.cpu().item() != true_label and max_prob.item() &gt; 0.90:
        mismatched_images.append((img_path, true_label))</code></pre>
<pre><code>27237it [04:09, 108.98it/s]</code></pre><h2 id="label-mistakenness-확인">label mistakenness 확인</h2>
<ul>
<li>높은 confidence로 예측 했으나, class가 다를 시, 해당 데이터의 라벨링이 잘못 되었는지 확인이 필요합니다.</li>
</ul>
<pre><code class="language-python">for path,label in mismatched_images:
    print(path, label)
    img = Image.open(path)
    img.show()</code></pre>
<pre><code>./dataset/digit_data/0/00247_0.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/488f9f3a-3b7d-4928-99c6-15af42e4b488/image.png" alt=""></p>
<pre><code>./dataset/digit_data/0/00411_4.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/77d208fe-2628-4137-af77-d15aa76b9497/image.png" alt=""></p>
<pre><code>./dataset/digit_data/0/00548_0.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/87729521-b85e-4979-ae05-6bc3f47196b5/image.png" alt=""></p>
<pre><code>./dataset/digit_data/0/00642_4.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/8f86ecde-d371-46d6-870a-98035ee844b7/image.png" alt=""></p>
<pre><code>./dataset/digit_data/0/00832_4.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/b8699658-2c63-4fcc-ba0b-fbfb67724557/image.png" alt=""></p>
<pre><code>./dataset/digit_data/0/02819_2.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/0737be2b-2de1-435c-b460-e5b7654989a9/image.png" alt=""></p>
<pre><code>./dataset/digit_data/1/02701_3.jpg 1</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/9dd48e08-6d96-4db9-abe9-8856082165c9/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00140_4.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/20d5a07d-9a9c-45cb-8c57-db576cd0a789/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00251_6.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/fd11340e-1ea3-40be-8995-2dd37ad5079a/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00673_1.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/a2261d34-0253-4740-a1e3-f611e583c6eb/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00767_5.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/9bec88fa-8e71-43d5-94d8-0d19b26a82a8/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00978_4.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/97b3d373-2c10-4e3c-9a92-1861638f3f1b/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/02489_3.jpg 2</code></pre><p>   <img src="https://velog.velcdn.com/images/s_s/post/d846e4dc-0ab8-4ca0-be70-69f8f7807648/image.png" alt=""></p>
<pre><code>./dataset/digit_data/3/02282_0.jpg 3</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/aa36bd44-7cb9-4059-b90c-62e1f3069b0b/image.png" alt=""></p>
<pre><code>./dataset/digit_data/4/00626_1.jpg 4</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/69f838ce-b5eb-4232-a7b8-b03169c795cc/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/00150_2.jpg 5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/9c6a9034-f4a8-4eae-9865-e7e52aebbf2a/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/00268_0.jpg 5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/8cd146a6-1d22-4784-a87a-2fe9e6c5ef37/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/01557_5.jpg 5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/04bbc920-0df9-4017-a0c1-f6675f4462c7/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/01868_6.jpg 5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/abe4e00b-fa52-4047-9026-4946f36cbad8/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/02526_5.jpg 5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/a6815a72-a483-4990-a20b-ad6e384194c3/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/03525_3.jpg 5



![](https://velog.velcdn.com/images/s_s/post/fb359701-5463-446b-8785-ac44a46ff828/image.png)




./dataset/digit_data/6/00064_5.jpg 6




![](https://velog.velcdn.com/images/s_s/post/477e8aff-4bad-4e9c-bc53-95145359d114/image.png)



./dataset/digit_data/6/00617_6.jpg 6



![](https://velog.velcdn.com/images/s_s/post/e26c4162-37ac-42b4-b2b4-9cf541291377/image.png)




./dataset/digit_data/6/02063_5.jpg 6</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/8bf84ebd-ec3c-4a2a-9170-d9763e74d916/image.png" alt=""></p>
<pre><code>./dataset/digit_data/7/01186_6.jpg 7</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/f227f41a-1134-4f5d-92fe-91add2b4a8c3/image.png" alt=""></p>
<pre><code>./dataset/digit_data/8/00164_2.jpg 8



![](https://velog.velcdn.com/images/s_s/post/384cff07-df78-4522-b2ab-56d112da101e/image.png)




./dataset/digit_data/8/00239_5.jpg 8



![](https://velog.velcdn.com/images/s_s/post/d6e1da18-74e4-4630-b6bb-d1c7ab5f53dc/image.png)




./dataset/digit_data/8/00826_6.jpg 8



![](https://velog.velcdn.com/images/s_s/post/0fcc20e8-e569-405c-8779-197e1b962703/image.png)




./dataset/digit_data/8/01567_7.jpg 8



![](https://velog.velcdn.com/images/s_s/post/39662e37-418c-421f-bba4-587e17b43c24/image.png)




./dataset/digit_data/0/00527_0.jpg 0



![](https://velog.velcdn.com/images/s_s/post/c92b17f8-300f-41f0-82e9-4b2985602401/image.png)




./dataset/digit_data/0/00744_4.jpg 0</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/8293e3ec-e9de-4b3b-ad6e-9050561ead35/image.png" alt=""></p>
<pre><code>./dataset/digit_data/1/01141_6.jpg 1</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/0c67fd9a-18e2-4137-8d19-e81ff35e21e7/image.png" alt=""></p>
<pre><code>./dataset/digit_data/1/02206_6.jpg 1</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/b7272297-a057-420b-bd27-5d9171389031/image.png" alt=""></p>
<pre><code>./dataset/digit_data/2/00025_3.jpg 2</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/5abb0344-c537-46be-b61f-3d4aa971bda7/image.png" alt=""></p>
<pre><code>./dataset/digit_data/3/02534_5.jpg 3</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/0ffdaa47-fd6a-4e6f-b885-80e64125fa2e/image.png" alt=""></p>
<pre><code>./dataset/digit_data/5/00170_2.jpg 5



![](https://velog.velcdn.com/images/s_s/post/1912890b-0b36-48c8-87ef-657116523eec/image.png)




./dataset/digit_data/5/03605_4.jpg 5



![](https://velog.velcdn.com/images/s_s/post/c029c709-0d13-43ad-84c1-c35e5312f4fd/image.png)




./dataset/digit_data/6/01340_6.jpg 6



![](https://velog.velcdn.com/images/s_s/post/04260f5b-a805-4756-9a66-a1d478077cc4/image.png)




./dataset/digit_data/7/00895_3.jpg 7



![](https://velog.velcdn.com/images/s_s/post/7625b753-e31f-434b-a9a8-005aea3d720d/image.png)




./dataset/digit_data/8/02076_3.jpg 8



![](https://velog.velcdn.com/images/s_s/post/dee48ea0-5092-4d63-8a82-b9cbca400598/image.png)




./dataset/digit_data/9/01117_4.jpg 9</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/3bc4b71a-324c-4d10-925b-57fb31bebebf/image.png" alt=""></p>
<h2 id="잘못된-라벨-수정">잘못된 라벨 수정</h2>
<ul>
<li>시각화를 해보니 잘못 라벨링이 된 항목들이 몇 개 보입니다.</li>
<li>confidence가 높은 오답을 시각화 했을 때, 육안으로 구분 가능한 잘못된 라벨들이 포함되어 있음을 알 수 있습니다</li>
<li>모델의 성능 향상을 위해 라벨을 수정하여 줍니다 (가능한 만큼)</li>
</ul>
<pre><code class="language-python"># label_index : 라벨을 뜻하는 폴더명의 위치 (split 할 경우)
def move_data(filename: str, label: int, label_index=3):
    moved_paths = []
    for path1 in glob(f&quot;{image_path}/*/{filename}&quot;):
        divided = path1.split(&quot;/&quot;)
        divided[label_index] = str(label)
        path2 = &quot;/&quot;.join(divided)
        if path1 != path2:
            shutil.move(path1, path2)
            print(f&quot;{path1} =&gt; {path2}&quot;)
            moved_paths.append((path1, path2))
    return moved_paths</code></pre>
<pre><code class="language-python">image_changes = [
    (&quot;00247_0.jpg&quot;, 1),
    (&quot;00642_4.jpg&quot;, 3),
    (&quot;02819_2.jpg&quot;, 7),
    (&quot;02701_3.jpg&quot;, 0),
    (&quot;00140_4.jpg&quot;, 3),
    (&quot;02489_3.jpg&quot;, 0),
    (&quot;02574_7.jpg&quot;, 0),
    (&quot;02282_0.jpg&quot;, 2),
    (&quot;00626_1.jpg&quot;, 1),
    (&quot;01561_0.jpg&quot;, 5),
    (&quot;00150_2.jpg&quot;, 1),
    (&quot;00268_0.jpg&quot;, 4),
    (&quot;03525_3.jpg&quot;, 0),
    (&quot;00617_6.jpg&quot;, 1),
    (&quot;00164_2.jpg&quot;, 1),
    (&quot;00527_0.jpg&quot;, 1),
    (&quot;00025_3.jpg&quot;, 0),
    (&quot;02534_5.jpg&quot;, 8),
    (&quot;00895_3.jpg&quot;, 0),
    (&quot;02334_0.jpg&quot;, 2),
    (&quot;00411_4.jpg&quot;, 3),
    (&quot;01186_6.jpg&quot;, 2),
]</code></pre>
<pre><code class="language-python">moved_paths = []

# image_changes 리스트를 기반으로 이미지 데이터 이동
for img_file, new_label in image_changes:
    moved_paths.extend(move_data(img_file, new_label))</code></pre>
<pre><code>./dataset/digit_data/0/00247_0.jpg =&gt; ./dataset/digit_data/1/00247_0.jpg
./dataset/digit_data/0/00642_4.jpg =&gt; ./dataset/digit_data/3/00642_4.jpg
./dataset/digit_data/0/02819_2.jpg =&gt; ./dataset/digit_data/7/02819_2.jpg
./dataset/digit_data/1/02701_3.jpg =&gt; ./dataset/digit_data/0/02701_3.jpg
./dataset/digit_data/2/00140_4.jpg =&gt; ./dataset/digit_data/3/00140_4.jpg
./dataset/digit_data/2/02489_3.jpg =&gt; ./dataset/digit_data/0/02489_3.jpg
./dataset/digit_data/2/02574_7.jpg =&gt; ./dataset/digit_data/0/02574_7.jpg
./dataset/digit_data/3/02282_0.jpg =&gt; ./dataset/digit_data/2/02282_0.jpg
./dataset/digit_data/4/00626_1.jpg =&gt; ./dataset/digit_data/1/00626_1.jpg
./dataset/digit_data/4/01561_0.jpg =&gt; ./dataset/digit_data/5/01561_0.jpg
./dataset/digit_data/5/00150_2.jpg =&gt; ./dataset/digit_data/1/00150_2.jpg
./dataset/digit_data/5/00268_0.jpg =&gt; ./dataset/digit_data/4/00268_0.jpg
./dataset/digit_data/5/03525_3.jpg =&gt; ./dataset/digit_data/0/03525_3.jpg
./dataset/digit_data/6/00617_6.jpg =&gt; ./dataset/digit_data/1/00617_6.jpg
./dataset/digit_data/8/00164_2.jpg =&gt; ./dataset/digit_data/1/00164_2.jpg
./dataset/digit_data/0/00527_0.jpg =&gt; ./dataset/digit_data/1/00527_0.jpg
./dataset/digit_data/2/00025_3.jpg =&gt; ./dataset/digit_data/0/00025_3.jpg
./dataset/digit_data/3/02534_5.jpg =&gt; ./dataset/digit_data/8/02534_5.jpg
./dataset/digit_data/7/00895_3.jpg =&gt; ./dataset/digit_data/0/00895_3.jpg
./dataset/digit_data/3/02334_0.jpg =&gt; ./dataset/digit_data/2/02334_0.jpg
./dataset/digit_data/0/00411_4.jpg =&gt; ./dataset/digit_data/3/00411_4.jpg
./dataset/digit_data/7/01186_6.jpg =&gt; ./dataset/digit_data/2/01186_6.jpg</code></pre><pre><code class="language-python">changes_dict = {img_name: new_label for img_name, new_label in image_changes}</code></pre>
<pre><code class="language-python">def update_labels_txt(txt_path, changes_dict):
    # 함수 설명: 주어진 텍스트 파일(txt_path)의 레이블을 changes_dict에 따라 업데이트합니다.
    # txt_path: 레이블 정보가 저장된 텍스트 파일 경로
    # changes_dict: 이미지 이름을 키로 하고, 변경할 레이블을 값으로 가지는 딕셔너리

    try:
        # 텍스트 파일을 읽기 모드로 열기
        with open(txt_path, &#39;r&#39;) as file:
            data = file.readlines()  # 파일의 모든 라인을 읽어와 리스트로 저장
    except FileNotFoundError:
        # 파일이 존재하지 않을 경우 예외 처리
        print(f&quot;{txt_path} not found.&quot;)  # 파일이 없음을 알리는 메시지 출력
        return  # 함수 종료

    # 업데이트된 데이터를 저장할 리스트
    updated_data = []

    # 파일의 각 라인을 반복하면서 레이블을 변경합니다.
    for line in data:
        # 라인에서 이미지 이름 추출 (&quot;/&quot;로 나누어 마지막 부분이 이미지 이름)
        img_name = line.strip().split(&quot;/&quot;)[-1]

        # changes_dict에 이미지 이름이 있는지 확인
        if img_name in changes_dict:
            # 만약 변경해야 하는 이미지 이름이 있다면, 기존 레이블을 새로운 레이블로 업데이트
            print(f&quot;Updating {img_name}: label {line.strip().split(&#39;/&#39;)[0]} =&gt; {changes_dict[img_name]}&quot;)
            # 변경된 레이블과 이미지 이름을 새로운 형식으로 저장
            updated_data.append(f&quot;{changes_dict[img_name]}/{img_name}\n&quot;)
        else:
            # 변경할 필요가 없는 경우, 원래의 라인을 그대로 저장
            updated_data.append(line)

    # 업데이트된 데이터를 원래 파일에 덮어쓰기
    try:
        with open(txt_path, &#39;w&#39;) as file:
            file.writelines(updated_data)  # 모든 업데이트된 라인을 파일에 기록
    except IOError:
        # 파일 쓰기 중 에러가 발생할 경우 예외 처리
        print(f&quot;Error writing to {txt_path}.&quot;)  # 쓰기 실패 메시지 출력</code></pre>
<pre><code class="language-python"># txt 파일 업데이트
train_txt_path = f&quot;{image_path}/train_data.txt&quot;
update_labels_txt(train_txt_path, changes_dict)</code></pre>
<pre><code>Updating 00247_0.jpg: label 0 =&gt; 1
Updating 00411_4.jpg: label 0 =&gt; 3
Updating 00642_4.jpg: label 0 =&gt; 3
Updating 02819_2.jpg: label 0 =&gt; 7
Updating 02701_3.jpg: label 1 =&gt; 0
Updating 00140_4.jpg: label 2 =&gt; 3
Updating 02489_3.jpg: label 2 =&gt; 0
Updating 02574_7.jpg: label 2 =&gt; 0
Updating 02282_0.jpg: label 3 =&gt; 2
Updating 02334_0.jpg: label 3 =&gt; 2
Updating 00626_1.jpg: label 4 =&gt; 1
Updating 01561_0.jpg: label 4 =&gt; 5
Updating 00150_2.jpg: label 5 =&gt; 1
Updating 00268_0.jpg: label 5 =&gt; 4
Updating 03525_3.jpg: label 5 =&gt; 0
Updating 00617_6.jpg: label 6 =&gt; 1
Updating 01186_6.jpg: label 7 =&gt; 2
Updating 00164_2.jpg: label 8 =&gt; 1</code></pre><pre><code class="language-python">valid_txt_path = f&quot;{image_path}/valid_data.txt&quot;
update_labels_txt(valid_txt_path, changes_dict)</code></pre>
<pre><code>Updating 00527_0.jpg: label 0 =&gt; 1
Updating 00025_3.jpg: label 2 =&gt; 0
Updating 02534_5.jpg: label 3 =&gt; 8
Updating 00895_3.jpg: label 7 =&gt; 0</code></pre><h2 id="수정된-데이터셋-로드">수정된 데이터셋 로드</h2>
<pre><code class="language-python"># 학습 데이터셋
origin_train_df = pd.read_csv(f&quot;{image_path}/train_data.txt&quot;, names=[&quot;path&quot;])
origin_train_df[&quot;label&quot;] = origin_train_df[&quot;path&quot;].str[0].astype(int)
origin_train_df[&quot;path&quot;] = image_path + &quot;/&quot; + origin_train_df[&quot;path&quot;]
origin_train_df</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/92eeb4a3-da4d-4ed2-85c6-61376273b62a/image.png" alt=""></p>
<pre><code class="language-python"># 테스트 데이터셋 (valid_data.txt지만 미리 나눠진 test set으로 사용하도록 하겠음)
test_df = pd.read_csv(f&quot;{image_path}/valid_data.txt&quot;, names=[&quot;path&quot;])
test_df[&quot;label&quot;] = test_df[&quot;path&quot;].str[0].astype(int)
test_df[&quot;path&quot;] = image_path + &quot;/&quot; + test_df[&quot;path&quot;]
test_df</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/1dd69bcb-bf2b-4d6e-bdc9-062e453cb12c/image.png" alt=""></p>
<h2 id="trainvalidtest-데이터셋-분리-1">Train/Valid/Test 데이터셋 분리</h2>
<pre><code class="language-python"># train 데이터셋을 8:2로 나눠서 valid를 하도록 하겠습니다
train_df, valid_df = train_test_split(
    origin_train_df,
    test_size=0.2,
    random_state=0,
    shuffle=True,
    stratify=origin_train_df[&quot;label&quot;],
)</code></pre>
<h2 id="data-augmentation--dataloader-생성">Data Augmentation &amp; DataLoader 생성</h2>
<p>이미지 데이터의 수가 불균형하므로 학습 시 모델의 성능 저하가 발생하고 예측의 편향이 발생할 수 있고, Overfitting이 발생할 수 있습니다.</p>
<p>데이터의 개수가 적은 4, 5, 7, 8, 9 이미지를 현재 이미지 개수의 평균만큼만 데이터 증강(Data Augmentation)을 수행합니다.</p>
<pre><code class="language-python"># 사용자 정의 데이터셋 클래스 생성 (CustomDataset)
class CustomDataset(Dataset):
    def __init__(self, dataframe, transform=None):
        # 데이터셋 초기화
        # dataframe: 이미지 경로와 레이블이 포함된 데이터프레임
        # transform: 이미지에 적용할 전처리(transform) 함수
        self.dataframe = dataframe
        self.transform = transform

    def __len__(self):
        # 데이터셋의 총 샘플 수 반환
        return len(self.dataframe)

    def __getitem__(self, idx):
        # 주어진 인덱스(idx)에 해당하는 샘플을 반환
        # 이미지 경로를 데이터프레임에서 가져옴
        img_name = self.dataframe.iloc[idx, 0]
        # 이미지 파일을 열고 RGB 모드로 변환
        img = Image.open(img_name).convert(&#39;RGB&#39;)
        # 레이블 정보를 정수형으로 가져옴
        label = int(self.dataframe.iloc[idx, 1])

        # transform이 지정되어 있다면 이미지에 전처리를 적용
        if self.transform:
            img = self.transform(img)

        # 이미지와 레이블을 반환
        return img, label</code></pre>
<pre><code class="language-python"># 특정 레이블을 가진 데이터만 포함하는 AugmentedDataset 클래스 생성
class AugmentedDataset(Dataset):
    def __init__(self, dataframe, transform=None):
        # 데이터프레임에서 특정 레이블(4, 5, 7, 8, 9)만 필터링하여 데이터셋 생성
        self.dataframe = dataframe[dataframe[&#39;label&#39;].isin([4, 5, 7, 8, 9])]
        self.transform = transform

    def __len__(self):
        # 데이터셋의 총 샘플 수 반환
        return len(self.dataframe)

    def __getitem__(self, idx):
        # 주어진 인덱스(idx)에 해당하는 샘플을 반환
        # 이미지 경로를 데이터프레임에서 가져옴
        img_name = self.dataframe.iloc[idx, 0]
        # 이미지 파일을 열고 RGB 모드로 변환
        img = Image.open(img_name).convert(&#39;RGB&#39;)
        # 레이블 정보를 정수형으로 가져옴
        label = int(self.dataframe.iloc[idx, 1])

        # transform이 지정되어 있다면 이미지에 전처리를 적용
        if self.transform:
            img = self.transform(img)

        # 이미지와 레이블을 반환
        return img, label</code></pre>
<pre><code class="language-python"># 데이터 전처리(transform) 정의
transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 이미지를 224x224로 리사이즈
    transforms.ToTensor(),  # 이미지를 텐서로 변환 (0-255 값을 0-1 범위로 변환)
    transforms.Normalize(mean=normalized_channel_means, std=normalized_channel_stds),  # 채널별 평균과 표준편차로 정규화
])

# 데이터 증강을 위한 transform 정의
augment_transform = transforms.Compose([
    transforms.Resize((224, 224)),  # 이미지를 224x224로 리사이즈
    transforms.RandomRotation(10),  # 이미지를 -10도에서 10도 사이로 랜덤 회전
    transforms.ToTensor(),  # 이미지를 텐서로 변환
    transforms.Normalize(mean=normalized_channel_means, std=normalized_channel_stds),  # 채널별 평균과 표준편차로 정규화
])

# 배치 사이즈 설정
batch_size = 256  # 한 번에 처리할 데이터의 개수</code></pre>
<pre><code class="language-python"># CustomDataset 인스턴스 생성 (기본 학습 데이터셋)
train_dataset = CustomDataset(train_df, transform=transform)
# AugmentedDataset 인스턴스 생성 (증강된 학습 데이터셋)
augmented_dataset = AugmentedDataset(train_df, transform=augment_transform)

# 기본 학습 데이터셋과 증강된 데이터셋을 결합하여 하나의 데이터셋으로 생성
combined_train_dataset = ConcatDataset([train_dataset, augmented_dataset])</code></pre>
<pre><code class="language-python"># DataLoader 생성 (학습, 검증, 테스트 데이터셋)
train_loader = DataLoader(combined_train_dataset, batch_size=batch_size, shuffle=True, num_workers=4)  # 학습 데이터 로더 (데이터 섞기 활성화)
valid_loader = DataLoader(CustomDataset(valid_df, transform=transform), batch_size=batch_size, shuffle=True, num_workers=4)  # 검증 데이터 로더
test_loader = DataLoader(CustomDataset(test_df, transform=transform), batch_size=batch_size, shuffle=False, num_workers=4)  # 테스트 데이터 로더 (데이터 섞기 비활성화)</code></pre>
<pre><code class="language-python">## ResNet 모델 학습</code></pre>
<pre><code class="language-python"># 사전 학습된 ResNet18 모델 불러오기
model = models.resnet18(pretrained=True)

# 모델의 마지막 완전 연결 계층(fc)을 사용자 정의 계층으로 대체
# 이 계층은 in_features에서 10개의 출력으로 매핑합니다 (10개 클래스 분류를 위함)
model.fc = nn.Sequential(
    nn.Linear(model.fc.in_features, 10),
)

class_counts = train_df[&#39;label&#39;].value_counts().sort_index().values

augmented_labels = [4, 5, 7, 8, 9]
class_counts[augmented_labels] = class_counts[augmented_labels] * 2

weights = [1 / class_count for class_count in class_counts]
class_weights = torch.FloatTensor(weights).cuda()

criterion = nn.CrossEntropyLoss(weight=class_weights)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5, weight_decay=1e-5)

device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
model = model.to(device)</code></pre>
<pre><code>/usr/local/lib/python3.7/dist-packages/torchvision/models/_utils.py:209: UserWarning: The parameter &#39;pretrained&#39; is deprecated since 0.13 and may be removed in the future, please use &#39;weights&#39; instead.
  f&quot;The parameter &#39;{pretrained_param}&#39; is deprecated since 0.13 and may be removed in the future, &quot;
/usr/local/lib/python3.7/dist-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for &#39;weights&#39; are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet18_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet18_Weights.DEFAULT` to get the most up-to-date weights.
  warnings.warn(msg)</code></pre><pre><code class="language-python"># 전체 훈련 횟수 설정
num_epochs = 50
# 최고 검증 정확도 초기화
best_val_acc = 0.0
# 얼리 스타핑을 위한 조건 설정 (성능 향상이 없을 때 몇 에포크까지 기다릴지)
patience = 5
# 연속적으로 성능 향상이 없는 에포크 수를 추적
no_improve = 0

# 훈련 및 검증 손실을 추적하기 위한 리스트
train_losses = []
valid_losses = []

# 정해진 훈련 횟수만큼 반복
for epoch in range(num_epochs):

    model.train() # 모델을 훈련 모드로 설정
    running_loss = 0.0
    correct_train = 0
    total_train = 0

     # 훈련 데이터 로더를 통해 배치를 반복
    for inputs, labels in tqdm(train_loader, desc=f&quot;Epoch {epoch+1}/{num_epochs}&quot;, dynamic_ncols=True):
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad() # 이전 반복에서 계산된 그래디언트를 초기화
        outputs = model(inputs) # 모델에 입력을 전달하여 출력을 계산
        loss = criterion(outputs, labels) # 손실 함수를 사용하여 손실 계산
        loss.backward() # 손실에 대한 그래디언트를 계산
        optimizer.step() # 옵티마이저를 사용하여 모델의 가중치를 업데이트

        running_loss += loss.item()  # 총 손실을 누적

        _, predicted = torch.max(outputs.data, 1) # 예측 결과 계산
        total_train += labels.size(0) # 전체 레이블 수 업데이트
        correct_train += (predicted == labels).sum().item() # 정확한 예측 수 업데이트

    # 에포크별 훈련 정확도 및 손실 계산
    train_acc = correct_train / total_train
    train_loss = running_loss / len(train_loader)

    # Validate
    model.eval()
    running_val_loss = 0.0
    correct_val = 0
    total_val = 0
    with torch.no_grad():
        for inputs, labels in valid_loader:
            inputs, labels = inputs.to(device), labels.to(device)
            outputs = model(inputs)
            loss = criterion(outputs, labels)

            running_val_loss += loss.item()

            _, predicted = torch.max(outputs.data, 1)
            total_val += labels.size(0)
            correct_val += (predicted == labels).sum().item()

    val_acc = correct_val / total_val
    val_loss = running_val_loss / len(valid_loader)

    # 손실 기록
    train_losses.append(train_loss)
    valid_losses.append(val_loss)

    print(f&#39;Train Loss: {train_loss:.4f}, Train Accuracy: {train_acc:.4f}, &#39;
          f&#39;Validation Loss: {val_loss:.4f}, Validation Accuracy: {val_acc:.4f}&#39;)

    # 최고 검증 정확도를 갱신하고 모델 저장
    if val_acc &gt; best_val_acc:
        best_val_acc = val_acc
        torch.save(model.state_dict(), &#39;best_model_after.pth&#39;)
        no_improve = 0
    else:
        no_improve += 1 # 성능 향상이 없으면 no_improve 카운터 증가
        if no_improve &gt;= patience:  # 설정한 얼리 스타핑 patience에 도달하면 학습을 중단합니다.
            print(&quot;Early stopping&quot;)
            break

print(&#39;Finished Training&#39;)</code></pre>
<pre><code>Epoch 1/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 2.1961, Train Accuracy: 0.1828, Validation Loss: 2.0223, Validation Accuracy: 0.2999


Epoch 2/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 1.7645, Train Accuracy: 0.4246, Validation Loss: 1.6268, Validation Accuracy: 0.5202


Epoch 3/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 1.4034, Train Accuracy: 0.6086, Validation Loss: 1.3011, Validation Accuracy: 0.6583


Epoch 4/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 1.1005, Train Accuracy: 0.7387, Validation Loss: 1.0421, Validation Accuracy: 0.7632


Epoch 5/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.8541, Train Accuracy: 0.8256, Validation Loss: 0.8000, Validation Accuracy: 0.8398


Epoch 6/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.6497, Train Accuracy: 0.8779, Validation Loss: 0.6262, Validation Accuracy: 0.8850


Epoch 7/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.4918, Train Accuracy: 0.9118, Validation Loss: 0.4855, Validation Accuracy: 0.9119


Epoch 8/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.3787, Train Accuracy: 0.9327, Validation Loss: 0.3751, Validation Accuracy: 0.9302


Epoch 9/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.2984, Train Accuracy: 0.9465, Validation Loss: 0.3162, Validation Accuracy: 0.9436


Epoch 10/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.2402, Train Accuracy: 0.9577, Validation Loss: 0.2613, Validation Accuracy: 0.9525


Epoch 11/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.1980, Train Accuracy: 0.9661, Validation Loss: 0.2280, Validation Accuracy: 0.9580


Epoch 12/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.1658, Train Accuracy: 0.9712, Validation Loss: 0.1984, Validation Accuracy: 0.9598


Epoch 13/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.1412, Train Accuracy: 0.9759, Validation Loss: 0.1740, Validation Accuracy: 0.9633


Epoch 14/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.1230, Train Accuracy: 0.9797, Validation Loss: 0.1623, Validation Accuracy: 0.9672


Epoch 15/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.1075, Train Accuracy: 0.9820, Validation Loss: 0.1424, Validation Accuracy: 0.9683


Epoch 16/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0960, Train Accuracy: 0.9841, Validation Loss: 0.1376, Validation Accuracy: 0.9709


Epoch 17/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0848, Train Accuracy: 0.9865, Validation Loss: 0.1215, Validation Accuracy: 0.9722


Epoch 18/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0761, Train Accuracy: 0.9878, Validation Loss: 0.1209, Validation Accuracy: 0.9736


Epoch 19/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0682, Train Accuracy: 0.9892, Validation Loss: 0.1140, Validation Accuracy: 0.9757


Epoch 20/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0616, Train Accuracy: 0.9911, Validation Loss: 0.1121, Validation Accuracy: 0.9754


Epoch 21/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0563, Train Accuracy: 0.9921, Validation Loss: 0.1030, Validation Accuracy: 0.9768


Epoch 22/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0505, Train Accuracy: 0.9922, Validation Loss: 0.0935, Validation Accuracy: 0.9771


Epoch 23/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0466, Train Accuracy: 0.9935, Validation Loss: 0.0960, Validation Accuracy: 0.9782


Epoch 24/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0426, Train Accuracy: 0.9942, Validation Loss: 0.0910, Validation Accuracy: 0.9798


Epoch 25/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0397, Train Accuracy: 0.9943, Validation Loss: 0.0840, Validation Accuracy: 0.9803


Epoch 26/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0363, Train Accuracy: 0.9952, Validation Loss: 0.0862, Validation Accuracy: 0.9800


Epoch 27/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.52s/it]


Train Loss: 0.0340, Train Accuracy: 0.9959, Validation Loss: 0.0840, Validation Accuracy: 0.9812


Epoch 28/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0313, Train Accuracy: 0.9962, Validation Loss: 0.0853, Validation Accuracy: 0.9805


Epoch 29/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0286, Train Accuracy: 0.9970, Validation Loss: 0.0859, Validation Accuracy: 0.9821


Epoch 30/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0263, Train Accuracy: 0.9969, Validation Loss: 0.0850, Validation Accuracy: 0.9807


Epoch 31/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0251, Train Accuracy: 0.9973, Validation Loss: 0.0739, Validation Accuracy: 0.9823


Epoch 32/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0228, Train Accuracy: 0.9978, Validation Loss: 0.0747, Validation Accuracy: 0.9819


Epoch 33/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0218, Train Accuracy: 0.9982, Validation Loss: 0.0733, Validation Accuracy: 0.9830


Epoch 34/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0198, Train Accuracy: 0.9983, Validation Loss: 0.0804, Validation Accuracy: 0.9830


Epoch 35/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0190, Train Accuracy: 0.9983, Validation Loss: 0.0749, Validation Accuracy: 0.9832


Epoch 36/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0179, Train Accuracy: 0.9985, Validation Loss: 0.0749, Validation Accuracy: 0.9835


Epoch 37/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0166, Train Accuracy: 0.9989, Validation Loss: 0.0773, Validation Accuracy: 0.9835


Epoch 38/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0157, Train Accuracy: 0.9986, Validation Loss: 0.0699, Validation Accuracy: 0.9832


Epoch 39/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0149, Train Accuracy: 0.9990, Validation Loss: 0.0653, Validation Accuracy: 0.9837


Epoch 40/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0141, Train Accuracy: 0.9990, Validation Loss: 0.0716, Validation Accuracy: 0.9837


Epoch 41/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.54s/it]


Train Loss: 0.0132, Train Accuracy: 0.9992, Validation Loss: 0.0715, Validation Accuracy: 0.9835


Epoch 42/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0127, Train Accuracy: 0.9992, Validation Loss: 0.0703, Validation Accuracy: 0.9837


Epoch 43/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0118, Train Accuracy: 0.9992, Validation Loss: 0.0736, Validation Accuracy: 0.9837


Epoch 44/50: 100%|██████████| 21/21 [00:32&lt;00:00,  1.53s/it]


Train Loss: 0.0110, Train Accuracy: 0.9993, Validation Loss: 0.0761, Validation Accuracy: 0.9837
Early stopping
Finished Training</code></pre><pre><code class="language-python">model.load_state_dict(torch.load(&#39;best_model_after.pth&#39;))</code></pre>
<pre><code>&lt;All keys matched successfully&gt;</code></pre><pre><code class="language-python"># Validate
all_labels = []
all_predictions = []
with torch.no_grad():
    for inputs, labels in valid_loader:
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = model(inputs)
        _, predicted = torch.max(outputs.data, 1)

        all_labels.append(labels.cpu().numpy())
        all_predictions.append(predicted.cpu().numpy())

all_labels = np.concatenate(all_labels)
all_predictions = np.concatenate(all_predictions)

conf_mat = confusion_matrix(all_labels, all_predictions)
conf_mat_normalized = conf_mat.astype(&#39;float&#39;) / conf_mat.sum(axis=1)[:, np.newaxis]</code></pre>
<h3 id="혼동행렬로-시각화">혼동행렬로 시각화</h3>
<ul>
<li>이 혼동행렬 히트맵은 모델이 0부터 9까지의 숫자를 얼마나 잘 분류하는지를 보여줍니다.</li>
<li>각 행은 실제 라벨을, 각 열은 모델이 예측한 라벨을 나타냅니다.</li>
<li>대각선 상의 값이 높으면 높을수록 모델의 예측 정확도가 높음을 의미합니다.</li>
<li>이 히트맵에서는 모든 클래스(0~9)가 95% 이상의 높은 정확도로 잘 예측되었음을 확인할 수 있습니다.</li>
<li>이는 모델이 각 클래스를 정확하게 분류하는 데 효과적임을 나타냅니다.</li>
</ul>
<pre><code class="language-python">plt.figure(figsize=(8, 8))
sns.heatmap(conf_mat_normalized, annot=True, cmap=&#39;Blues&#39;)
plt.xlabel(&#39;Predicted&#39;)
plt.ylabel(&#39;True&#39;)
plt.title(&#39;Normalized Confusion Matrix&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0697a15e-4006-43cd-a965-9964c6088a3c/image.png" alt=""></p>
<h3 id="loss-시각화">Loss 시각화</h3>
<ul>
<li>이 그래프는 학습 과정에서의 train loss와 validation loss의 변화를 보여줍니다.</li>
<li>x축은 에포크를, y축은 손실 값을 나타냅니다.</li>
<li>train loss는 모델이 훈련 데이터에 얼마나 잘 맞는지를 나타내며, validation loss는 모델이 보지 못한 데이터에 대한 성능을 나타냅니다.</li>
<li>두 손실 모두 시간이 지남에 따라 안정적으로 감소하는 추세를 보이면, 이는 모델이 제대로 학습되고 있음을 의미합니다.</li>
<li>급격한 변동이나 높은 손실 값이 지속되지 않는 것은 모델이 과적합 없이 일반화 능력을 갖추고 있음을 시사합니다.</li>
</ul>
<pre><code class="language-python">actual_epochs = len(train_losses)

fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# Train Loss 그래프
axes[0].plot(range(1, actual_epochs + 1), train_losses, label=&#39;Train&#39;, color=&#39;blue&#39;)
axes[0].set_xlabel(&#39;Epoch&#39;)
axes[0].set_ylabel(&#39;Loss&#39;)
axes[0].set_title(&#39;Training Loss&#39;)
axes[0].legend()

# Validation Loss 그래프
axes[1].plot(range(1, actual_epochs + 1), valid_losses, label=&#39;Validation&#39;, color=&#39;orange&#39;)
axes[1].set_xlabel(&#39;Epoch&#39;)
axes[1].set_ylabel(&#39;Loss&#39;)
axes[1].set_title(&#39;Validation Loss&#39;)
axes[1].legend()

# 그래프 출력
plt.tight_layout()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/c1167974-70ea-4ffb-a797-282c62b0af7c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[영상처리 기초 - 4. OCR 프로젝트-1(데이터분석)]]></title>
            <link>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-4.-OCR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9D</link>
            <guid>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-4.-OCR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-1%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B6%84%EC%84%9D</guid>
            <pubDate>Sun, 02 Mar 2025 15:29:15 GMT</pubDate>
            <description><![CDATA[<h1 id="전이학습을-활용한-이미지-분류">전이학습을 활용한 이미지 분류</h1>
<ol>
<li><p>전이학습(Transfer Learning)이란?</p>
<ul>
<li><p>전이학습은 <strong>기존에 학습된 모델의 가중치</strong>를 새로운 문제에 활용하는 방법입니다.</p>
</li>
<li><p>대규모 데이터셋에서 사전 학습된 모델을 통해 기본적인 특성(feature)을 이해하게 하고, 이를 새로운 데이터셋에 적용하여 모델 학습 시간을 줄이고 성능을 향상시킬 수 있습니다.</p>
</li>
<li><p>전이학습은 특히 이미지 분류와 같이 대량의 데이터가 필요하고 복잡한 모델 구조가 요구되는 작업에서 유용하게 사용됩니다. </p>
</li>
<li><p>전이학습은 주로 두 단계로 이루어집니다:</p>
<ol>
<li>사전학습(Pre-training): 대규모 데이터셋으로 모델을 학습시켜 기본적인 특징 추출 능력을 갖춥니다</li>
<li>미세조정(Fine-tuning): 사전학습된 모델을 특정 작업에 맞게 조정합니다. 이 과정에서는 적은 양의 데이터로도 효과적인 학습이 가능합니다.</li>
</ol>
<img src="https://i0.wp.com/neptune.ai/wp-content/uploads/2022/10/Transfer-learning-vs-fine-tuning.png?ssl=1" width="600">
</li>
</ul>
</li>
<li><p>전이학습의 주요 개념</p>
<ul>
<li><strong>기존 모델 활용</strong>: ImageNet과 같은 대규모 데이터셋으로 학습된 모델은 다양한 시각적 특성을 이미 학습하였으므로, 이를 재사용하여 새로운 데이터셋에 쉽게 적용할 수 있습니다.</li>
<li><strong>가중치 고정(freeze)</strong>: 사전 학습된 모델의 가중치를 고정하고 새로운 데이터셋에서 상위 특성(고수준 특징)을 학습합니다. 이렇게 하면 기존의 특성 추출 기능을 보존하면서 학습의 범위를 좁힐 수 있습니다.</li>
<li><strong>미세 조정(Fine-tuning)</strong>: 모델의 일부 가중치를 학습 가능하게 두고 새로운 데이터셋에 맞춰 추가 학습을 진행합니다. 이를 통해 기존 모델이 학습하지 못한 고유한 데이터셋의 특성을 잘 반영할 수 있습니다.</li>
</ul>
</li>
<li><p>전이학습의 장점</p>
<ul>
<li><strong>효율성</strong>: 적은 양의 데이터로도 높은 성능을 얻을 수 있습니다.</li>
<li><strong>시간 단축</strong>: 사전 학습된 모델의 특성을 재사용함으로써 모델 훈련에 필요한 시간을 줄입니다.</li>
<li><strong>높은 성능</strong>: 복잡한 데이터셋에서도 더 나은 결과를 기대할 수 있습니다.</li>
</ul>
</li>
</ol>
<h2 id="task-소개--ocr">Task 소개 : OCR</h2>
<p>Optical Character Recognition(OCR)은 기술이 발전하며 전통적인 문서를 디지털 형태로 변환하는 데 사용되는 중요한 기술로 자리 잡았습니다. OCR은 사람이 쓰거나 기계로 인쇄한 문자들을 스캔하고, 이를 텍스트 데이터로 변환합니다. 이를 통해 수많은 문서들이 검색 가능하고 편집 가능한 형태로 재사용될 수 있게 됩니다.</p>
<p>이 기술은 글자와 단어를 정확하게 인식하여 디지털 데이터로 변환하는데 중점을 둡니다. 한글, 영어, 숫자 등 다양한 문자를 인식할 수 있으며, 다양한 언어와 폰트에서도 확장하여 적용이 가능합니다.</p>
<img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*8i5nZ5lr5CgvVrH8rCSn7w.png">

<h2 id="라이브러리-import">라이브러리 import</h2>
<pre><code class="language-python"># 데이터 처리 및 분석을 위한 라이브러리
import pandas as pd  # 데이터 처리 및 분석을 위한 라이브러리
from sklearn.model_selection import train_test_split  # 데이터 세트를 훈련 및 테스트 세트로 분할하는 데 사용

# 파일 및 경로 처리를 위한 라이브러리
import os  # 운영 체제와 상호작용, 파일 및 디렉토리 경로 처리에 사용
import glob  # 파일 시스템 내의 파일 경로명을 찾는 데 사용

# 이미지 처리를 위한 라이브러리
from PIL import Image  # 이미지 파일을 열고 조작하는 데 사용

# 데이터 시각화를 위한 라이브러리
import matplotlib.pyplot as plt  # 데이터 시각화를 위한 플로팅 라이브러리

# 기타 유틸리티
import random  # 난수 생성 및 무작위 선택을 위한 유틸리티 함수 제공
from typing import *  # 타입 힌트 및 제네릭 타입을 위한 라이브러리</code></pre>
<h2 id="google-colab에서-데이터-관리-및-최적-활용-전략">Google Colab에서 데이터 관리 및 최적 활용 전략</h2>
<h3 id="드라이브-설정">드라이브 설정</h3>
<ul>
<li><p>코랩에서 데이터에 접근하는 방법에는 크게 두 가지가 있습니다.</p>
<ul>
<li><ol>
<li>구글 드라이브 연동 : 자신의 Google Drive를 코랩에 연결하여 드라이브 안의 데이터에 접근합니다.   </li>
</ol>
</li>
<li><ol start="2">
<li>코랩의 세션 드라이브 업로드 : 코랩의 임시 저장 공간에 데이터를 업로드하여 사용합니다.</li>
</ol>
</li>
</ul>
</li>
<li><p>각각의 방법은 장단점을 가지고 있습니다.</p>
</li>
<li><p>구글 드라이브 연동의 장점은 드라이브에 데이터가 미리 준비되어 있으면 즉시 사용할 수 있다는 것이며, 데이터가 지속적으로 보존됩니다. 단점은 이미지 데이터와 같이 용량이 큰 데이터를 다룰 때, 로딩 속도가 매우 느리다는 단점이 있습니다.  </p>
</li>
<li><p>코랩의 세션 드라이브에 업로드하는 방법의 장점은 드라이브 연동에 비해 로딩 속도를 매우 빠르게 사용할 수 있다는 것입니다. 하지만 이 방법의 단점은 코랩 런타임이 초기화될 때마다 데이터가 사라지고, 다른 ipynb에서 접근이 안된다는 단점이 존재합니다.</p>
</li>
<li><p>따라서 이미지 데이터와 같이 대용량의 학습 데이터는 학습 직전에 임시 경로에 업로드하여 사용하고, 그 외 필요한 파일들은 구글 드라이브에서 직접 불러오는 것이 좋습니다. 이렇게 하면 데이터 사용의 효율성과 접근성을 극대화할 수 있습니다.</p>
</li>
</ul>
<h2 id="코랩의-세션-드라이브에-데이터-업로드">코랩의 세션 드라이브에 데이터 업로드</h2>
<pre><code class="language-python">import gdown

# Google Drive 파일 ID와 URL 설정
file_id = &quot;1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw&quot;
url = f&quot;https://drive.google.com/uc?id={file_id}&quot;

# 다운로드할 파일 이름 설정
output = &quot;OCR_ResNet18_dataset.zip&quot;

# gdown을 사용하여 파일 다운로드
gdown.download(url, output, quiet=False)

print(f&quot;파일 다운로드가 완료되었습니다: {output}&quot;)</code></pre>
<pre><code>Downloading...
From (original): https://drive.google.com/uc?id=1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw
From (redirected): https://drive.google.com/uc?id=1U7vCDZ4X9ElceQvBllL8ccGHXHjc1qTw&amp;confirm=t&amp;uuid=2e7bc296-418a-47e6-bb69-f5cb9d5d22f5
To: /content/OCR_ResNet18_dataset.zip
100%|██████████| 82.9M/82.9M [00:03&lt;00:00, 22.2MB/s]

파일 다운로드가 완료되었습니다: OCR_ResNet18_dataset.zip</code></pre><pre><code class="language-python"># unzip 명령어를 통해 압축 파일을 해제해 줍니다.
!unzip -qq OCR_ResNet18_dataset.zip</code></pre>
<pre><code class="language-python"># 코랩 환경에서 오른쪽 폴더 모양의 아이콘을 누르면 세션 드라이브 및 연동된 구글 드라이브를 확인할 수 있습니다.
root_path = os.getcwd() # 현재 경로를 root_path로 설정합니다.
print(root_path)</code></pre>
<pre><code>/content</code></pre><pre><code class="language-python"># 코랩 드라이브에서 대량의 파일을 로드할 경우 매우 시간이 오래 걸립니다.
# 따라서 추후 사용할 이미지는 drive가 아닌 코랩 세션 드라이브의 경로로 설정해주고, 추후 해당 경로에 데이터를 업로드해줍니다.
image_path = os.path.join(root_path,&quot;dataset&quot;,&quot;digit_data&quot;)
asset_path = &quot;./assets&quot;</code></pre>
<pre><code class="language-python"># 학습 데이터셋
origin_train_df = pd.read_csv(os.path.join(image_path, &quot;train_data.txt&quot;), names=[&quot;path&quot;])
origin_train_df[&quot;label&quot;] = origin_train_df[&quot;path&quot;].str[0].astype(int)
origin_train_df[&quot;path&quot;] = origin_train_df[&quot;path&quot;].apply(lambda x: os.path.join(image_path,x))
origin_train_df</code></pre>
<h2 id="trainvalidtest-데이터셋-분리">Train/Valid/Test 데이터셋 분리</h2>
<p>kamp데이터는 train/valid 밖에 구분되어 있지 않지만</p>
<p>저희는 보다 명확한 검토를 위해</p>
<pre><code>train =&gt; train/valid

valid = &gt; test</code></pre><p>로 사용하도록 하겠습니다</p>
<pre><code class="language-python"># train 데이터셋을 8:2로 나눠서 valid를 하도록 하겠습니다
train_df, valid_df = train_test_split(
    origin_train_df,
    test_size=0.2,
    random_state=0,
    shuffle=True,
    stratify=origin_train_df[&quot;label&quot;],
)</code></pre>
<h2 id="eda">EDA</h2>
<ul>
<li><p>제조현장용 OCR(Optical Character Recognition) 학습 AI 데이터셋</p>
</li>
<li><p>데이터의 품질은 모델 성능을 결정하는 핵심 요소입니다. 모델의 최적 성능을 위해서는 고품질의 데이터가 필수적입니다.</p>
</li>
<li><p>하지만, 대다수의 데이터셋은 예상치 못한 오류나 결점을 포함할 수 있습니다. 그러므로, 단순히 데이터에 의존하기보다는 철저한 검증과정이 필요합니다.탐색적 데이터 분석(EDA)는 이 검증 과정의 핵심입니다. EDA는 데이터의 본질과 구조를 깊게 파악하는 데 중점을 둡니다. 주요 목적은 아래와 같습니다.</p>
<ul>
<li>데이터 구조 파악</li>
<li>이상치의 탐지</li>
<li>데이터 전처리 방향성 설정</li>
<li>데이터의 시각화</li>
</ul>
</li>
</ul>
<p>이를 통해 데이터의 품질 향상을 지향합니다.</p>
<ul>
<li><p>데이터셋 형태 : image파일과 그에 연계된 txt 및 folder name기반 tagging</p>
</li>
<li><p>데이터 수집 방법 : 클라우드 기반의 RPMS를 통해, 생산현장에서 업로드된 이미지를 확보. 실제 기업 현장에서 발생하는 계측 이미지를 직접 촬영하여 수집</p>
</li>
<li><p>데이터셋 파일 확장자 : 이미지 (jpg), 레이블 (folder name)</p>
</li>
<li><p>데이터 개수 :  27,237개(76.1MB)</p>
</li>
</ul>
<hr>
<ul>
<li><p>이미지 당 하나의 숫자만 있는 것을 알 수 있습니다</p>
</li>
<li><p>7 Segment 이미지는 전반적으로 붉은 빛을 띄고 있습니다 데이터를 normalize할 때 이를 고려하면 더 좋은 성능이 나올 수 있습니다</p>
</li>
<li><p>약간 회전되거나 치우쳐져 있어도 맞출 수 있게 rotate, traslate 증강을 적용</p>
</li>
<li><p>이미지의 밝기 및 채도가 변화하여도 맞출 수 있게 brightnss, contrast 증강을 적용</p>
</li>
</ul>
<pre><code class="language-python">image_path</code></pre>
<pre><code>&#39;/content/dataset/digit_data&#39;</code></pre><pre><code class="language-python"># 전체 이미지 리스트 중 임의로 30개 추출하여 시각화합니다.
all_image_list = glob.glob(f&#39;{image_path}/*/*&#39;)
image_files = random.sample(all_image_list,30)

# 이미지를 로드하고 저장
images = [Image.open(img) for img in image_files]
labels = list(map(lambda x: x.split(&#39;/&#39;)[-2], image_files))

# 플롯 설정
fig, axes = plt.subplots(3, 10, figsize=(30, 10))  # 5x4 격자
axes = axes.flatten()

# 각 격자에 이미지 표시
for label,img, ax in zip(labels,images, axes):
    ax.imshow(img)
    ax.axis(&#39;off&#39;)
    ax.set_title(label, fontsize=8)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/52437b47-079a-4b81-9625-1e76f160fa56/image.png" alt=""></p>
<ul>
<li><p>2와 5는 좌우 반전 시 동일한 형태라는 것을 알 수 있습니다</p>
</li>
<li><p>데이터 학습 및 추론 시 좌우 반전을 하게되면 안됩니다</p>
</li>
</ul>
<hr>
<pre><code class="language-python"># 전체 이미지 리스트 중 숫자 6과 9를 임의로 20개 추출하여 시각화합니다.
image_6_or_9_list = glob.glob(f&#39;{image_path}/6/*&#39;) + glob.glob(f&#39;{image_path}/9/*&#39;)
image_files = random.sample(image_6_or_9_list,20)

# 이미지를 로드하고 저장
images = [Image.open(img) for img in image_files]
labels = list(map(lambda x: x.split(&#39;/&#39;)[-2], image_files))

# 플롯 설정
fig, axes = plt.subplots(2, 5, figsize=(10, 5))  # 2*5 격자
axes = axes.flatten()

# 각 격자에 이미지 표시
for label,img, ax in zip(labels,images, axes):
    ax.imshow(img)
    ax.axis(&#39;off&#39;)
    ax.set_title(label, fontsize=8)

plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/05f46558-ec1a-4f21-b00d-710ffc45e577/image.png" alt=""></p>
<p>6과 9는 좌우반전 + 상하반전을 같이해야 같은 모양됩니다 (또는 180도 회전)</p>
<p>그러나 숫자가 거꾸로 출력될 일은 현재 문제에는 없을 것으로 예상되어, 좌우상하 반전은 증강에서 제외하도록 합니다</p>
<hr>
<h3 id="메타-데이터-분석">메타 데이터 분석</h3>
<pre><code class="language-python"># 숫자 폴더 이름 리스트 생성
number_folders = [str(i) for i in range(10)]

# 각 숫자 폴더에서 이미지 개수 카운트
image_counts = []
for folder in number_folders:
    folder_path = os.path.join(image_path, folder)
    count = len([f for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f))])
    image_counts.append(count)

# 막대 그래프 생성
plt.bar(number_folders, image_counts, color=&#39;blue&#39;)
plt.xlabel(&#39;Number&#39;)
plt.ylabel(&#39;Image Count&#39;)
plt.title(&#39;Image Counts for Each Number&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/67f21b5b-69a4-44ca-9839-9e8af1dfad7a/image.png" alt=""></p>
<ul>
<li><p>데이터 클래스가 불균형함을 알 수 있습니다</p>
</li>
<li><p>모델을 학습 시 이를 고려하여 학습하여야 합니다</p>
</li>
<li><p>그렇지 않으면 특정 클래스에 편향된 모델이 개발될 수 있습니다</p>
</li>
<li><p>데이터 불균형을 해소하는 방법은 여러가지가 있습니다</p>
</li>
</ul>
<hr>
<p>언더 샘플링</p>
<ol>
<li>Random Sampling</li>
<li>Tomek Links</li>
<li>CNN Rule</li>
<li>One Sided Selection</li>
</ol>
<p>언더 샘플링의 장단점</p>
<p>장점</p>
<ul>
<li>다수 범주 데이터의 제거로 계산시간이 감소합니다.</li>
</ul>
<p>단점</p>
<ul>
<li>데이터 제거로 인한 정보 손실이 발생할 수 있습니다.</li>
</ul>
<hr>
<p>오버 샘플링</p>
<ol>
<li>Resampling</li>
<li>SMOTE</li>
<li>Borderline SMOTE</li>
<li>ADASYN</li>
<li>GAN</li>
</ol>
<p>오버 샘플링의 장단점</p>
<p>장점</p>
<ul>
<li>데이터를 증가시키기 때문에 정보 손실이 없습니다.</li>
<li>대부분의 경우 언더 샘플링에 비해 높은 분류 정확도를 보입니다.</li>
</ul>
<p>단점</p>
<ul>
<li>데이터 증가로 인해 계산 시간이 증가할 수 있으며 과적합 가능성이 존재합니다.</li>
<li>노이즈 또는 이상치에 민감합니다.</li>
</ul>
<hr>
<p>손실 함수</p>
<ol>
<li>Focal Loss</li>
<li>Weighted Cross Entropy</li>
</ol>
<hr>
<pre><code class="language-python"># 모든 이미지의 가로 길이를 저장할 리스트
widths = []

# 각 숫자 폴더를 순회하며 이미지의 가로 길이 수집
for folder in number_folders:
    folder_path = os.path.join(image_path, folder)
    # 해당 폴더의 모든 파일에 대해 반복
    for file in os.listdir(folder_path):
        file_path = os.path.join(folder_path, file)
        # 파일이 이미지인 경우만 처리
        if os.path.isfile(file_path) and file.lower().endswith((&#39;.png&#39;, &#39;.jpg&#39;, &#39;.jpeg&#39;)):
            with Image.open(file_path) as img:
                width, height = img.size
                widths.append(width)  # 가로 길이 저장

# 이미지 가로 길이의 분포 시각화
plt.hist(widths, bins=20, color=&#39;skyblue&#39;, edgecolor=&#39;black&#39;)
plt.xlabel(&#39;Width&#39;)
plt.ylabel(&#39;Frequency&#39;)
plt.title(&#39;Distribution of Image Widths&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/08a4ba5c-990e-4091-90ca-489ccd1d0d7f/image.png" alt=""></p>
<pre><code class="language-python"># 모든 이미지의 세로 길이를 저장할 리스트
heights = []

# 각 숫자 폴더를 순회하며 이미지의 세로 길이 수집
for folder in number_folders:
    folder_path = os.path.join(image_path, folder)
    # 해당 폴더의 모든 파일에 대해 반복
    for file in os.listdir(folder_path):
        file_path = os.path.join(folder_path, file)
        # 파일이 이미지인 경우만 처리
        if os.path.isfile(file_path) and file.lower().endswith((&#39;.png&#39;, &#39;.jpg&#39;, &#39;.jpeg&#39;)):
            with Image.open(file_path) as img:
                width, height = img.size
                heights.append(height)  # 세로 길이 저장

# 이미지 세로 길이의 분포 시각화
plt.hist(heights, bins=20, color=&#39;skyblue&#39;, edgecolor=&#39;black&#39;)
plt.xlabel(&#39;height&#39;)
plt.ylabel(&#39;Frequency&#39;)
plt.title(&#39;Distribution of Image heights&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/51223ecc-edf5-4361-b41a-cd222a194685/image.png" alt=""></p>
<p>이미지의 사이즈는 매우 작은 사이즈 ~ 중간 사이즈의 분포임을 알 수 있습니다</p>
<p>모델의 input으로 넣을 이미지 사이즈는 보통의 사이즈로 해도 문제 없을 것으로 보입니다 (224x224)</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[영상처리 기초 - 3. 컨볼루션 신경망]]></title>
            <link>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-3.-%EC%BB%A8%EB%B3%BC%EB%A3%A8%EC%85%98-%EC%8B%A0%EA%B2%BD%EB%A7%9D</link>
            <guid>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-3.-%EC%BB%A8%EB%B3%BC%EB%A3%A8%EC%85%98-%EC%8B%A0%EA%B2%BD%EB%A7%9D</guid>
            <pubDate>Sun, 02 Mar 2025 15:12:35 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-python"># 본 자료는 이수안 교수님(https://suanlab.com/)의 자료를 기반으로 수정 및 보완하여 제작되었습니다.
# 제작자 : 김민수(rlaalstn1504@naver.com)</code></pre>
<h1 id="컨볼루션-신경망convolution-neural-networks-cnn">컨볼루션 신경망(Convolution Neural Networks, CNN)</h1>
<ul>
<li><p>완전 연결 네트워크의 문제점으로부터 시작</p>
<ul>
<li>매개변수의 폭발적인 증가</li>
<li>공간 추론의 부족: 픽셀 사이의 근접성 개념이 완전 연결 계층(Fully-Connected Layer)에서는 손실됨</li>
</ul>
<img src ="https://img1.daumcdn.net/thumb/R720x0.q80/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99B957465E1EF41210&scode=mtistory2">
</li>
<li><p>동물의 시각피질의 구조에서 영감을 받아 만들어진 딥러닝 신경망 모델</p>
</li>
<li><p>시각 자극이 1차 시각피질을 통해서 처리된 다음, 2차 시각피질을 경유하여, 3차 시각피질 등 여러 영역을 통과하여 계층적인 정보처리</p>
</li>
<li><p>정보가 계층적으로 처리되어 가면서 점차 추상적인 특징이 추출되어 시각 인식</p>
</li>
<li><p>동물의 계층적 특징 추출과 시각인식 체계를 참조하여 만들어진 모델</p>
<ul>
<li>전반부 : 컨볼루션 연산을 수행하여 특징 추출</li>
<li>후반부 : 특징을 이용하여 분류</li>
</ul>
</li>
<li><p>영상분류, 문자 인식 등 인식문제에 높은 성능</p>
<img src="https://velog.velcdn.com/images/nayeo2/post/c02b66a7-ede2-4998-9624-c20d9c56cb75/image.png">

</li>
</ul>
<h2 id="컨볼루션-연산-convolution-operation">컨볼루션 연산 (Convolution Operation)</h2>
<ul>
<li><p>필터(filter) 연산</p>
<ul>
<li>입력 데이터에 필터를 통한 어떠한 연산을 진행</li>
<li>필터에 대응하는 원소끼리 곱하고, 그 합을 구함</li>
<li>연산이 완료된 결과 데이터를 <strong>특징 맵(feature map)</strong>이라 부름</li>
</ul>
</li>
<li><p>필터(filter)</p>
<ul>
<li>커널(kernel)이라고도 함</li>
<li>이미지 처리에서 사용하는 &#39;이미지 필터&#39;와 비슷한 개념</li>
<li>필터의 사이즈는 거의 항상 홀수<ul>
<li>SAME 패딩을 사용하여 입력과 출력의 크기를 동일하게 유지하기 쉬움</li>
<li>중심위치가 존재, 즉 구별된 하나의 픽셀(중심 픽셀)이 존재</li>
</ul>
</li>
<li>필터의 학습 파라미터 개수는 입력 데이터의 크기와 상관없이 일정  </li>
<li>과적합을 방지할 수 있음</li>
</ul>
<img src="https://www.tomasbeuzen.com/deep-learning-with-pytorch/_images/conv-2.gif">
</li>
<li><p>연산 시각화</p>
<img src="https://www.researchgate.net/profile/Ihab_S_Mohamed/publication/324165524/figure/fig3/AS:611103423860736@1522709818959/An-example-of-convolution-operation-in-2D-2.png" width="500">
</li>
<li><p>일반적으로, 합성곱 연산을 한 후의 데이터 사이즈</p>
<p>$\quad (n-f+1) \times (n-f+1)$</p>
<p>$n$: 입력 데이터의 크기<br>$f$: 필터(커널)의 크기</p>
<img src="https://miro.medium.com/max/1400/1*Fw-ehcNBR9byHtho-Rxbtw.gif" width="400">
</li>
<li><p>위 예에서 입력 데이터 크기($n$)는 5, 필터의 크기($k$)는 3이므로 출력 데이터의 크기는 $(5 - 3 + 1) = 3$</p>
</li>
</ul>
<h2 id="필터가-이미지의-특징을-추출하는-과정-sobel-필터를-예시로">필터가 이미지의 특징을 추출하는 과정 (sobel 필터를 예시로)</h2>
<p>Sobel 필터란?</p>
<ul>
<li><p>Sobel 필터는 수학적으로 <strong>이미지의 밝기 변화(Gradient)</strong>를 계산하는 필터임</p>
<ul>
<li>두 가지 종류가 있음:</li>
<li>수평선 검출 필터: 이미지에서 수평 방향의 변화를 감지.</li>
<li>수직선 검출 필터: 이미지에서 수직 방향의 변화를 감지.</li>
</ul>
</li>
<li><p>Sobel 필터를 활용하면 CNN에서 필터가 특징을 추출하는 과정을 직관적으로 이해할 수 있음.</p>
</li>
<li><p>Sobel 필터는 가장 간단한 에지 검출 필터 중 하나로, 이미지에서 선(에지)을 강조함.</p>
<img src="https://www.researchgate.net/publication/375105942/figure/fig5/AS:11431281202227380@1698765640908/Sobel-filter-using-two-33-kernels-Gx-Gy.png">

<p>이미지출처: <a href="https://www.researchgate.net/figure/Sobel-filter-using-two-33-kernels-Gx-Gy_fig5_375105942">https://www.researchgate.net/figure/Sobel-filter-using-two-33-kernels-Gx-Gy_fig5_375105942</a></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/5071078c-5140-49aa-bf8d-fd8a15c395b5/image.png" alt=""></p>
<p> 이미지출처: <a href="https://learnopencv.com/edge-detection-using-opencv/">https://learnopencv.com/edge-detection-using-opencv/</a></p>
<pre><code class="language-python">import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_sample_image

from tensorflow.keras.layers import Conv2D</code></pre>
<pre><code class="language-python">flower = load_sample_image(&#39;flower.jpg&#39;) / 255
print(flower.dtype)
print(flower.shape)
plt.imshow(flower)
plt.show()</code></pre>
<pre><code>float64
(427, 640, 3)</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/76ec8355-5df8-4dbe-87ca-654757e6c62a/image.png" alt=""></p>
<pre><code class="language-python">china = load_sample_image(&#39;china.jpg&#39;) / 255
print(china.dtype)
print(china.shape)
plt.imshow(china)
plt.show()</code></pre>
<pre><code>float64
(427, 640, 3)</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/62cd09eb-957e-499d-8045-0c2e5f99935a/image.png" alt=""></p>
<pre><code class="language-python">images = np.array([china, flower])
batch_size, height, width, channels = images.shape
print(images.shape)</code></pre>
<pre><code>(2, 427, 640, 3)</code></pre><pre><code class="language-python"># 컨볼루션 연산을 수행할 필터를 직접 생성
filters = np.zeros(shape=(7, 7, channels, 2), dtype=np.float32)
filters[:, 3, :, 0] = 1 # 첫 번째 필터 세로줄 형성
filters[3, :, :, 0] = 1 # 첫 번째 필터 가로줄 형성
filters[3, 3, :, 1] = 1 # 두 번째 필터 중심점 형성

print(filters.shape)</code></pre>
<pre><code>(7, 7, 3, 2)</code></pre><pre><code class="language-python"># 필터가 학습을 통해 자동으로 생성되는 CNN 레이어 생성
convolve = Conv2D(filters=16, kernel_size=7, activation=&#39;relu&#39;)</code></pre>
<h2 id="패딩padding과-스트라이드stride">패딩(Padding)과 스트라이드(Stride)</h2>
<ul>
<li>필터(커널) 사이즈과 함께 입력 이미지와 출력 이미지의 사이즈를 결정하기 위해 사용</li>
<li>사용자가 결정할 수 있음</li>
</ul>
<h2 id="패딩padding">패딩(Padding)</h2>
<ul>
<li><p>입력 데이터의 주변을 특정 값으로 채우는 기법</p>
<ul>
<li>주로 0으로 많이 채움</li>
</ul>
<img src="https://miro.medium.com/max/395/1*1okwhewf5KCtIPaFib4XaA.gif" width="300">
</li>
<li><p>출력 데이터의 크기</p>
<p>$\quad (n+2p-f+1) \times (n+2p-f+1)$</p>
<ul>
<li>위 그림에서, 입력 데이터의 크기($n$)는 5, 필터의 크기($f$)는 3, 패딩값($p$)은 1이므로 출력 데이터의 크기는 ($5 + 2\times 1 - 3 + 1) = 5$</li>
</ul>
</li>
<li><p><code>valid</code></p>
<ul>
<li>패딩을 주지 않음</li>
<li><code>padding=0</code>은 0으로 채워진 테두리가 아니라 패딩을 주지 않는다는 의미</li>
</ul>
</li>
<li><p><code>same</code></p>
<ul>
<li>패딩을 주어 입력 이미지의 크기와 연산 후의 이미지 크기를 같도록 유지</li>
<li>만약, 필터(커널)의 크기가 $k$ 이면, 패딩의 크기는 $p = \frac{k-1}{2}$ (단, stride=1)</li>
</ul>
</li>
</ul>
<h2 id="스트라이드stride">스트라이드(Stride)</h2>
<ul>
<li><p>필터를 적용하는 간격을 의미</p>
</li>
<li><p>아래 예제 그림은 간격이 2</p>
<img src="https://miro.medium.com/max/294/1*BMngs93_rm2_BpJFH2mS0Q.gif">

</li>
</ul>
<h2 id="출력-데이터의-크기">출력 데이터의 크기</h2>
<p>  $\qquad OH = \frac{H + 2P - FH}{S} + 1 $</p>
<p>  $\qquad OW = \frac{W + 2P - FW}{S} + 1 $</p>
<ul>
<li>입력 크기 : $(H, W)$</li>
<li>필터 크기 : $(FH, FW)$</li>
<li>출력 크기 : $(OH, OW)$</li>
<li>패딩, 스트라이드 : $P, S$</li>
</ul>
<ul>
<li>위 식의 값에서 $\frac{H + 2P - FH}{S}$ 또는 $\frac{W + 2P - FW}{S}$가 정수로 나누어 떨어지는 값이어야 함</li>
<li>정수로 나누어 떨어지지 않으면, 패딩, 스트라이드 값을 조정하여 정수로 나누어 떨어지게 해야함</li>
</ul>
<pre><code class="language-python">conv = Conv2D(filters=16, kernel_size=3,padding=&#39;same&#39;,strides=1, activation=&#39;relu&#39;)</code></pre>
<h2 id="풀링pooling">풀링(Pooling)</h2>
<ul>
<li>필터(커널) 사이즈 내에서 특정 값을 추출하는 과정</li>
</ul>
<h2 id="맥스-풀링max-pooling">맥스 풀링(Max Pooling)</h2>
<ul>
<li><p>가장 많이 사용되는 방법</p>
</li>
<li><p>출력 데이터의 사이즈 계산은 컨볼루션 연산과 동일</p>
<p>$\quad OH = \frac{H + 2P - FH}{S} + 1 $</p>
<p>$\quad OW = \frac{W + 2P - FW}{S} + 1 $</p>
</li>
<li><p>일반적으로 stride=2, kernel_size=2 를 통해 특징맵의 크기를 절반으로 줄이는 역할</p>
</li>
<li><p>모델이 물체의 주요한 특징을 학습할 수 있도록 해주며, 컨볼루션 신경망이 이동 불변성 특성을 가지게 해줌</p>
</li>
<li><p>예를 들어, 아래의 그림에서 초록색 사각형 안에 있는 2와 8의 위치를 바꾼다해도 맥스 풀링 연산은 8을 추출</p>
</li>
<li><p>모델의 파라미터 개수를 줄여주고, 연산 속도를 빠르게 함</p>
<img src="https://cs231n.github.io/assets/cnn/maxpool.jpeg" width="600">


</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.layers import MaxPooling2D</code></pre>
<pre><code class="language-python">print(flower.shape)
flower = np.expand_dims(flower, axis=0)
print(flower.shape)

output = Conv2D(filters=32, kernel_size=3, strides=1, padding=&#39;same&#39;,activation=&#39;relu&#39;)(flower)
print(output.shape) # 아직 학습되지 않아 랜덤한 숫자로 초기화된 필터를 거친 연산 결과

output = MaxPooling2D(pool_size=2)(output)
print(output.shape)</code></pre>
<pre><code>(427, 640, 3)
(1, 427, 640, 3)
(1, 427, 640, 32)
(1, 213, 320, 32)</code></pre><pre><code class="language-python">plt.imshow(output[0,:,:,8],cmap=&#39;gray&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/ca4c1ce1-c15c-4519-ac25-42b10f928acd/image.png" alt=""></p>
<h2 id="평균-풀링avg-pooling">평균 풀링(Avg Pooling)</h2>
<ul>
<li><p>필터 내의 있는 픽셀값의 평균을 구하는 과정</p>
</li>
<li><p>과거에 많이 사용, 요즘은 잘 사용되지 않음</p>
</li>
<li><p>맥스풀링과 마찬가지로 stride=2, kernel_size=2 를 통해 특징 맵의 사이즈를 줄이는 역할</p>
<img src="https://www.researchgate.net/profile/Juan_Pedro_Dominguez-Morales/publication/329885401/figure/fig21/AS:707709083062277@1545742402308/Average-pooling-example.png" width="600">



</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.layers import AvgPool2D</code></pre>
<pre><code class="language-python">print(flower.shape)
output = Conv2D(filters=32, kernel_size=3, strides=1, padding=&#39;same&#39;,activation=&#39;relu&#39;)(flower)
output = AvgPool2D(pool_size=2)(output)
print(output.shape)</code></pre>
<pre><code>(1, 427, 640, 3)
(1, 213, 320, 32)</code></pre><pre><code class="language-python">plt.imshow(output[0,:,:,2], cmap=&#39;gray&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d2e88387-6db5-489e-a8b1-5684b0cbf180/image.png" alt=""></p>
<h2 id="전역-평균-풀링global-avg-pooling">전역 평균 풀링(Global Avg Pooling)</h2>
<ul>
<li>특징 맵 각각의 평균값을 출력하는 것이므로, 특성맵에 있는 대부분의 정보를 잃음</li>
<li>출력층에는 유용할 수 있음</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.layers import GlobalAvgPool2D</code></pre>
<pre><code class="language-python">print(flower.shape)
output = Conv2D(filters=32, kernel_size=3, strides=1, padding=&#39;same&#39;,activation=&#39;relu&#39;)(flower)
print(output.shape)
output = GlobalAvgPool2D()(output)
print(output.shape)</code></pre>
<pre><code>(1, 427, 640, 3)
(1, 427, 640, 32)
(1, 32)</code></pre><h1 id="완전-연결-계층fully-connected-layer">완전 연결 계층(Fully-Connected Layer)</h1>
<ul>
<li>입력으로 받은 텐서를 1차원으로 평면화(flatten) 함</li>
<li>밀집 계층(Dense Layer)라고도 함</li>
<li>일반적으로 분류기로서 네트워크의 마지막 계층에서 사용</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.layers import Dense</code></pre>
<pre><code class="language-python">output_size = 1000</code></pre>
<pre><code class="language-python">fc = Dense(units=output_size, activation=&#39;softmax&#39;)</code></pre>
<h1 id="유효-수용-영역erf-effective-receptive-field">유효 수용 영역(ERF, Effective Receptive Field)</h1>
<ul>
<li><p>입력 이미지에서 특정 뉴런의 활성화(결과)에 영향을 미치는 이미지의 영역</p>
</li>
<li><p>네트워크가 깊어지면 더 많은 필터와 연산이 겹쳐져, 뉴런이 이미지를 &quot;망원경&quot;으로 보는 것처럼 더 넓은 영역을 고려하게 됨</p>
</li>
<li><p>따라서 깊은 계층에서는 입력 이미지의 &quot;멀리 떨어진&quot; 부분까지 고려해 특징을 학습할 수 있음</p>
</li>
<li><p>유효 수용 영역이 크면 더 복잡하고 넓은 관계(예: 얼굴 전체 구조)를 학습할 수 있으며, 너무 작으면, 작은 패턴(예: 한 부분의 점, 선)만 학습하여 전반적인 구조를 이해하지 못할 수 있음</p>
<img src="https://wiki.math.uwaterloo.ca/statwiki/images/8/8c/understanding_ERF_fig0.png">
</li>
<li><p>RF의 중앙에 위치한 픽셀은 주변에 있는 픽셀보다 더 높은 가중치를 가짐</p>
<ul>
<li>중앙부에 위치한 픽셀은 여러 개의 계층을 전파한 값</li>
<li>중앙부에 있는 픽셀은 주변에 위치한 픽셀보다 더 많은 정보를 가짐</li>
</ul>
</li>
<li><p>가우시안 분포를 따름</p>
<img src="https://www.researchgate.net/publication/316950618/figure/fig4/AS:495826810007552@1495225731123/The-receptive-field-of-each-convolution-layer-with-a-3-3-kernel-The-green-area-marks.png">

</li>
</ul>
<h1 id="cnn-모델-학습">CNN 모델 학습</h1>
<h2 id="mnist-lenet">MNIST (LeNet)</h2>
<ul>
<li><p>Yann LeCun 등의 제안(1998)</p>
</li>
<li><p>5 계층 구조: Conv-Pool-Conv- Pool-Conv-FC-FC(SM)</p>
</li>
<li><p>입력 : 32x32 필기체 숫자 영상 (MNIST 데이터)</p>
</li>
<li><p>풀링 : 가중치x(2x2블록의 합) + 편차항  </p>
</li>
<li><p>시그모이드 활성화 함수 사용</p>
</li>
<li><p>성능: 오차율 0.95%(정확도: 99.05%)</p>
<img src="https://miro.medium.com/max/4308/1*1TI1aGBZ4dybR6__DI9dzA.png">
<center>[LeNet-5 구조]</center>

</li>
</ul>
<h3 id="모듈-임포트">모듈 임포트</h3>
<pre><code class="language-python">from tensorflow.keras import Model
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.callbacks import EarlyStopping, TensorBoard
from tensorflow.keras.datasets import mnist

import matplotlib.pyplot as plt
import numpy as np</code></pre>
<h3 id="데이터-로드-및-전처리">데이터 로드 및 전처리</h3>
<pre><code class="language-python">(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train[:,:,:,np.newaxis] / 255.0, x_test[:,:,:,np.newaxis] / 255.0 # 채널 정보가 누락되어 있으므로 축 추가
print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)</code></pre>
<pre><code>(60000, 28, 28, 1)
(60000,)
(10000, 28, 28, 1)
(10000,)</code></pre><pre><code class="language-python">plt.imshow(x_train[0,:,:,0], cmap=&#39;gray&#39;)
print(y_train[0])</code></pre>
<pre><code>5</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/785dd691-f153-4813-8d41-1d4e0b377839/image.png" alt=""></p>
<h3 id="모델-구성-및-컴파일">모델 구성 및 컴파일</h3>
<pre><code class="language-python">num_classes = 10
epochs = 30
batch_size = 32</code></pre>
<pre><code class="language-python">class LeNet5(Model):
    def __init__(self, num_classes=10):
        # 부모 클래스의 기능을 상속받기 위해 부모 클래스의 초기화 메서드(__init__)를 호출
        super(LeNet5, self).__init__()
        # 첫 번째 합성곱 층: 6개의 필터를 사용하고, 각 필터의 크기는 5x5입니다. 
        # &#39;same&#39; 패딩을 사용하여 입력 이미지와 출력 이미지의 크기를 동일하게 유지합니다.
        # 활성화 함수로는 ReLU를 사용하여 비선형 변환을 제공합니다.
        self.conv1 = Conv2D(filters=6, kernel_size=5, padding=&#39;same&#39;, activation=&#39;relu&#39;)

        # 첫 번째 풀링 층: 최대 풀링을 사용하고, 풀링의 크기는 2x2입니다.
        # 이 층은 데이터의 공간 크기를 줄이고, 주요 특징을 유지합니다.
        self.pool1 = MaxPooling2D(pool_size=2)

        # 두 번째 합성곱 층: 16개의 필터를 사용하고, 각 필터의 크기는 5x5입니다.
        # &#39;same&#39; 패딩을 사용하여 입력 이미지와 출력 이미지의 크기를 동일하게 유지합니다.
        # 활성화 함수로는 ReLU를 사용합니다.
        self.conv2 = Conv2D(filters=16, kernel_size=5, padding=&#39;same&#39;, activation=&#39;relu&#39;)

        # 데이터를 일렬로 펼치는 층입니다. 이 층은 합성곱 층과 풀링 층을 거쳐 얻어진
        # 2차원 특징 맵을 1차원 벡터로 변환하여 완전 연결 층에 전달할 수 있도록 합니다.
        self.flatten = Flatten()

        # 첫 번째 완전 연결 층: 120개의 유닛을 가지며, 활성화 함수로 ReLU를 사용합니다.
        # 이 층은 1차원 벡터의 형태로 펼쳐진 데이터를 받아 처리합니다.
        self.fc1 = Dense(units=120, activation=&#39;relu&#39;)

        # 두 번째 완전 연결 층: 84개의 유닛을 가지며, 활성화 함수로 ReLU를 사용합니다.
        self.fc2 = Dense(units=84, activation=&#39;relu&#39;)

        # 출력 층: num_classes 개수의 유닛을 가지며, 각 클래스에 대한 확률을 출력하기 위해
        # 활성화 함수로 softmax를 사용합니다.
        self.fc3 = Dense(units=num_classes, activation=&#39;softmax&#39;)

    def call(self, input_data):
        # 각 층을 차례대로 호출하여 입력 데이터를 처리합니다.
        x = self.conv1(input_data)  # 첫 번째 합성곱 층을 통과
        x = self.pool1(x)           # 첫 번째 풀링 층을 통과
        x = self.conv2(x)           # 두 번째 합성곱 층을 통과
        x = self.pool1(x)           # 두 번째 풀링 층을 통과 (동일한 풀링 층 재사용)
        x = self.flatten(x)         # 플래튼 층을 통과
        x = self.fc1(x)             # 첫 번째 완전 연결 층을 통과
        x = self.fc2(x)             # 두 번째 완전 연결 층을 통과
        x = self.fc3(x)             # 출력 층을 통과
        return x

model = LeNet5(num_classes=10)</code></pre>
<pre><code class="language-python">model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;accuracy&#39;])</code></pre>
<pre><code class="language-python">callbacks = [EarlyStopping(patience=3, monitor=&#39;val_loss&#39;), TensorBoard(log_dir=&#39;./logs&#39;, histogram_freq=1)] # histogram_freq=1 : epoch마다 히스토그램 기록</code></pre>
<h3 id="모델-학습-및-평가">모델 학습 및 평가</h3>
<pre><code class="language-python">model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2,
          callbacks=callbacks)</code></pre>
<pre><code>Epoch 1/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m52s[0m 33ms/step - accuracy: 0.8790 - loss: 0.4138 - val_accuracy: 0.9700 - val_loss: 0.0958
Epoch 2/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m79s[0m 32ms/step - accuracy: 0.9761 - loss: 0.0724 - val_accuracy: 0.9787 - val_loss: 0.0724
Epoch 3/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m79s[0m 29ms/step - accuracy: 0.9857 - loss: 0.0485 - val_accuracy: 0.9855 - val_loss: 0.0482
Epoch 4/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m84s[0m 31ms/step - accuracy: 0.9892 - loss: 0.0336 - val_accuracy: 0.9880 - val_loss: 0.0427
Epoch 5/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m80s[0m 29ms/step - accuracy: 0.9919 - loss: 0.0254 - val_accuracy: 0.9852 - val_loss: 0.0508
Epoch 6/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m82s[0m 30ms/step - accuracy: 0.9939 - loss: 0.0202 - val_accuracy: 0.9874 - val_loss: 0.0451
Epoch 7/30
[1m1500/1500[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m83s[0m 31ms/step - accuracy: 0.9940 - loss: 0.0183 - val_accuracy: 0.9864 - val_loss: 0.0504





&lt;keras.src.callbacks.history.History at 0x7b6eb6a15600&gt;</code></pre><pre><code class="language-python">%load_ext tensorboard</code></pre>
<pre><code>The tensorboard extension is already loaded. To reload it, use:
  %reload_ext tensorboard</code></pre><pre><code class="language-python">%tensorboard --logdir logs</code></pre>
<pre><code>&lt;IPython.core.display.Javascript object&gt;</code></pre><h2 id="fashion-mnist">Fashion MNIST</h2>
<img src="https://www.tensorflow.org/tutorials/keras/classification_files/output_oZTImqg_CaW1_0.png?hl=ko" width="500">

<h3 id="모듈-임포트-1">모듈 임포트</h3>
<pre><code class="language-python">import datetime
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from tensorflow.keras import Model
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout, Input
from tensorflow.keras.callbacks import EarlyStopping, TensorBoard</code></pre>
<h3 id="데이터-로드-및-전처리-1">데이터 로드 및 전처리</h3>
<pre><code class="language-python">(x_train, y_train), (x_test, y_test) = load_data()
x_train, x_test = x_train[:,:,:,np.newaxis] / 255.0, x_test[:,:,:,np.newaxis] / 255

print(x_train.shape)
print(y_train.shape)
print(x_test.shape)
print(y_test.shape)</code></pre>
<pre><code>(60000, 28, 28, 1)
(60000,)
(10000, 28, 28, 1)
(10000,)</code></pre><pre><code class="language-python">class_names = [&#39;T-shirt/top&#39;, &#39;Trouser&#39;, &#39;Pullover&#39;, &#39;Dress&#39;, &#39;Coat&#39;,
               &#39;Sandal&#39;, &#39;Shirt&#39;, &#39;Sneaker&#39;, &#39;Bag&#39;, &#39;Ankle boot&#39;]</code></pre>
<h3 id="모델-구성-및-학습">모델 구성 및 학습</h3>
<ul>
<li>임의의 모델</li>
</ul>
<pre><code class="language-python">def build_model():
    # 입력 레이어 정의
    input = Input(shape=(28,28,1))
    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(input)
    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(output)
    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(output)

    # 평탄화와 완전 연결 레이어
    output = Flatten()(output)
    output = Dense(128, activation=&#39;relu&#39;)(output)
    output = Dense(64, activation=&#39;relu&#39;)(output)

    # 출력 레이어
    output = Dense(10, activation=&#39;softmax&#39;)(output)

    model = Model(inputs=[input], outputs=output)

    model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;acc&#39;])

    return model</code></pre>
<pre><code class="language-python">model_1 = build_model()
model_1.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)             │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">320</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">9,248</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">9,248</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">25088</span>)               │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                        │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)                 │       <span style="color: #00af00; text-decoration-color: #00af00">3,211,392</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)                  │           <span style="color: #00af00; text-decoration-color: #00af00">8,256</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)                  │             <span style="color: #00af00; text-decoration-color: #00af00">650</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">3,239,114</span> (12.36 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">3,239,114</span> (12.36 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">hist_1 = model_1.fit(x_train, y_train,
                     epochs=20,
                     validation_split=0.3,
                     batch_size=512)</code></pre>
<pre><code>Epoch 1/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m13s[0m 68ms/step - acc: 0.6512 - loss: 0.9781 - val_acc: 0.8304 - val_loss: 0.4678
Epoch 2/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 30ms/step - acc: 0.8564 - loss: 0.3977 - val_acc: 0.8658 - val_loss: 0.3770
Epoch 3/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 31ms/step - acc: 0.8847 - loss: 0.3199 - val_acc: 0.8898 - val_loss: 0.3059
Epoch 4/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 30ms/step - acc: 0.9049 - loss: 0.2574 - val_acc: 0.8946 - val_loss: 0.2976
Epoch 5/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 33ms/step - acc: 0.9172 - loss: 0.2230 - val_acc: 0.8961 - val_loss: 0.2930
Epoch 6/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 40ms/step - acc: 0.9273 - loss: 0.1986 - val_acc: 0.9093 - val_loss: 0.2612
Epoch 7/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 34ms/step - acc: 0.9399 - loss: 0.1639 - val_acc: 0.8875 - val_loss: 0.3520
Epoch 8/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 29ms/step - acc: 0.9346 - loss: 0.1766 - val_acc: 0.8737 - val_loss: 0.3914
Epoch 9/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9398 - loss: 0.1657 - val_acc: 0.9060 - val_loss: 0.2923
Epoch 10/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 28ms/step - acc: 0.9608 - loss: 0.1113 - val_acc: 0.9093 - val_loss: 0.2991
Epoch 11/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 29ms/step - acc: 0.9657 - loss: 0.0964 - val_acc: 0.9070 - val_loss: 0.3465
Epoch 12/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 29ms/step - acc: 0.9699 - loss: 0.0851 - val_acc: 0.9096 - val_loss: 0.3286
Epoch 13/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9814 - loss: 0.0536 - val_acc: 0.9056 - val_loss: 0.3904
Epoch 14/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9776 - loss: 0.0685 - val_acc: 0.9146 - val_loss: 0.3694
Epoch 15/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9920 - loss: 0.0262 - val_acc: 0.9105 - val_loss: 0.4255
Epoch 16/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 29ms/step - acc: 0.9878 - loss: 0.0361 - val_acc: 0.9142 - val_loss: 0.4280
Epoch 17/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 29ms/step - acc: 0.9955 - loss: 0.0153 - val_acc: 0.9119 - val_loss: 0.4758
Epoch 18/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9964 - loss: 0.0120 - val_acc: 0.9134 - val_loss: 0.4914
Epoch 19/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 28ms/step - acc: 0.9976 - loss: 0.0086 - val_acc: 0.9142 - val_loss: 0.5214
Epoch 20/20
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 28ms/step - acc: 0.9989 - loss: 0.0054 - val_acc: 0.9144 - val_loss: 0.5490</code></pre><pre><code class="language-python">hist_1.history.keys()

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(hist_1.history[&#39;loss&#39;], &#39;b--&#39;, label=&#39;loss&#39;)
plt.plot(hist_1.history[&#39;val_loss&#39;],&#39;r:&#39;,label=&#39;val_loss&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Loss&#39;)
plt.grid()
plt.legend()

plt.subplot(1,2,2)
plt.plot(hist_1.history[&#39;acc&#39;], &#39;b--&#39;, label=&#39;acc&#39;)
plt.plot(hist_1.history[&#39;val_acc&#39;],&#39;r:&#39;,label=&#39;val_acc&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/4e4bcd72-d34b-4f1c-a3f7-386b6bcd8186/image.png" alt=""></p>
<pre><code class="language-python">model_1.evaluate(x_test, y_test)</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 3ms/step - acc: 0.9092 - loss: 0.6197





[0.602406919002533, 0.9063000082969666]</code></pre><h3 id="모델-구성-및-학습-과대적합-방지">모델 구성 및 학습: 과대적합 방지</h3>
<ul>
<li>학습 파라미터의 수 비교</li>
</ul>
<pre><code class="language-python">def build_model2():
    # 입력 레이어 정의
    input = Input(shape=(28,28,1))

    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(input)
    output = MaxPooling2D(strides=2)(output)

    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(output)
    output = MaxPooling2D(strides=2)(output)
    output = Dropout(0.4)(output)

    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(output)
    output = Dropout(0.4)(output)

    # 평탄화와 완전 연결 레이어
    output = Flatten()(output)
    output = Dense(128, activation=&#39;relu&#39;)(output)
    output = Dense(64, activation=&#39;relu&#39;)(output)

    # 출력 레이어
    output = Dense(10, activation=&#39;softmax&#39;)(output)

    model = Model(inputs=[input], outputs=output)

    model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;acc&#39;])

    return model</code></pre>
<pre><code class="language-python">model_2 = build_model2()
model_2.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional_3"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">320</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">9,248</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)            │           <span style="color: #00af00; text-decoration-color: #00af00">9,248</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1568</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)                 │         <span style="color: #00af00; text-decoration-color: #00af00">200,832</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                     │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)                  │           <span style="color: #00af00; text-decoration-color: #00af00">8,256</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                     │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)                  │             <span style="color: #00af00; text-decoration-color: #00af00">650</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">228,554</span> (892.79 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">228,554</span> (892.79 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">hist_2 = model_2.fit(x_train, y_train,
                     epochs=40,
                     validation_split=0.3,
                     batch_size=512)</code></pre>
<pre><code>Epoch 1/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m8s[0m 59ms/step - acc: 0.5021 - loss: 1.4116 - val_acc: 0.7751 - val_loss: 0.5945
Epoch 2/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m4s[0m 11ms/step - acc: 0.7724 - loss: 0.5961 - val_acc: 0.8313 - val_loss: 0.4664
Epoch 3/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8223 - loss: 0.4867 - val_acc: 0.8351 - val_loss: 0.4346
Epoch 4/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8314 - loss: 0.4567 - val_acc: 0.8628 - val_loss: 0.3815
Epoch 5/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8552 - loss: 0.4041 - val_acc: 0.8757 - val_loss: 0.3460
Epoch 6/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.8554 - loss: 0.3948 - val_acc: 0.8814 - val_loss: 0.3294
Epoch 7/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.8701 - loss: 0.3554 - val_acc: 0.8718 - val_loss: 0.3508
Epoch 8/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8619 - loss: 0.3682 - val_acc: 0.8886 - val_loss: 0.3064
Epoch 9/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8746 - loss: 0.3418 - val_acc: 0.8887 - val_loss: 0.3051
Epoch 10/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8773 - loss: 0.3354 - val_acc: 0.8958 - val_loss: 0.2841
Epoch 11/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8832 - loss: 0.3175 - val_acc: 0.8904 - val_loss: 0.2967
Epoch 12/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8809 - loss: 0.3196 - val_acc: 0.8974 - val_loss: 0.2780
Epoch 13/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8830 - loss: 0.3095 - val_acc: 0.9017 - val_loss: 0.2681
Epoch 14/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.8900 - loss: 0.2956 - val_acc: 0.9008 - val_loss: 0.2671
Epoch 15/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8896 - loss: 0.2998 - val_acc: 0.8984 - val_loss: 0.2735
Epoch 16/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8942 - loss: 0.2825 - val_acc: 0.9066 - val_loss: 0.2590
Epoch 17/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8918 - loss: 0.2820 - val_acc: 0.9088 - val_loss: 0.2516
Epoch 18/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.8960 - loss: 0.2823 - val_acc: 0.8995 - val_loss: 0.2668
Epoch 19/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.8983 - loss: 0.2699 - val_acc: 0.9087 - val_loss: 0.2475
Epoch 20/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9016 - loss: 0.2681 - val_acc: 0.9141 - val_loss: 0.2390
Epoch 21/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9043 - loss: 0.2582 - val_acc: 0.9126 - val_loss: 0.2389
Epoch 22/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.8995 - loss: 0.2648 - val_acc: 0.9127 - val_loss: 0.2425
Epoch 23/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9019 - loss: 0.2676 - val_acc: 0.9166 - val_loss: 0.2280
Epoch 24/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9075 - loss: 0.2469 - val_acc: 0.9136 - val_loss: 0.2336
Epoch 25/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9107 - loss: 0.2395 - val_acc: 0.9155 - val_loss: 0.2313
Epoch 26/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9138 - loss: 0.2304 - val_acc: 0.9148 - val_loss: 0.2368
Epoch 27/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9100 - loss: 0.2364 - val_acc: 0.9172 - val_loss: 0.2285
Epoch 28/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.9080 - loss: 0.2471 - val_acc: 0.9198 - val_loss: 0.2229
Epoch 29/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.9121 - loss: 0.2334 - val_acc: 0.9174 - val_loss: 0.2269
Epoch 30/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 11ms/step - acc: 0.9122 - loss: 0.2322 - val_acc: 0.9189 - val_loss: 0.2228
Epoch 31/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9086 - loss: 0.2378 - val_acc: 0.9204 - val_loss: 0.2224
Epoch 32/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9131 - loss: 0.2292 - val_acc: 0.9209 - val_loss: 0.2198
Epoch 33/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9154 - loss: 0.2256 - val_acc: 0.9201 - val_loss: 0.2189
Epoch 34/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9153 - loss: 0.2237 - val_acc: 0.9161 - val_loss: 0.2290
Epoch 35/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.9155 - loss: 0.2244 - val_acc: 0.9221 - val_loss: 0.2133
Epoch 36/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9209 - loss: 0.2112 - val_acc: 0.9229 - val_loss: 0.2155
Epoch 37/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9238 - loss: 0.2039 - val_acc: 0.9199 - val_loss: 0.2152
Epoch 38/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9188 - loss: 0.2157 - val_acc: 0.9222 - val_loss: 0.2143
Epoch 39/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 10ms/step - acc: 0.9239 - loss: 0.2039 - val_acc: 0.9150 - val_loss: 0.2285
Epoch 40/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - acc: 0.9230 - loss: 0.2062 - val_acc: 0.9222 - val_loss: 0.2114</code></pre><pre><code class="language-python">hist_2.history.keys()

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(hist_2.history[&#39;loss&#39;], &#39;b--&#39;, label=&#39;loss&#39;)
plt.plot(hist_2.history[&#39;val_loss&#39;],&#39;r:&#39;,label=&#39;val_loss&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Loss&#39;)
plt.grid()
plt.legend()

plt.subplot(1,2,2)
plt.plot(hist_2.history[&#39;acc&#39;], &#39;b--&#39;, label=&#39;acc&#39;)
plt.plot(hist_2.history[&#39;val_acc&#39;],&#39;r:&#39;,label=&#39;val_acc&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8e344017-2b67-4308-abdd-38c60c13e4d7/image.png" alt=""></p>
<pre><code class="language-python">model_2.evaluate(x_test, y_test) # 학습시간 감소 및 과적합 방지</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 4ms/step - acc: 0.9150 - loss: 0.2314





[0.22845305502414703, 0.9151999950408936]</code></pre><h3 id="모델-성능-높이기-레이어-추가">모델 성능 높이기: 레이어 추가</h3>
<pre><code class="language-python"># 배치마다 층의 출력을 정규화하여 학습을 안정화하고 속도를 높이며, 과적합을 줄이는 효과
# 평균이 0, 표준편차가 1이 되도록 변형
from tensorflow.keras.layers import BatchNormalization</code></pre>
<pre><code class="language-python">def build_model3():
    # 입력 레이어 정의
    input = Input(shape=(28,28,1))

    output = Conv2D(filters=32, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(input)
    output = BatchNormalization()(output)
    output = MaxPooling2D(strides=2)(output)

    output = Conv2D(filters=64, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;valid&#39;)(output)
    output = BatchNormalization()(output)
    output = MaxPooling2D(strides=2)(output)
    output = Dropout(0.4)(output)

    output = Conv2D(filters=128, kernel_size=3, activation=&#39;relu&#39;, padding=&#39;same&#39;)(output)
    output = Dropout(0.4)(output)

    # 평탄화와 완전 연결 레이어
    output = Flatten()(output)
    output = Dense(512, activation=&#39;relu&#39;)(output)
    output = BatchNormalization()(output)
    output = Dense(256, activation=&#39;relu&#39;)(output)
    output = BatchNormalization()(output)

    # 출력 레이어
    output = Dense(10, activation=&#39;softmax&#39;)(output)

    model = Model(inputs=[input], outputs=output)

    model.compile(optimizer=&#39;adam&#39;, loss=&#39;sparse_categorical_crossentropy&#39;, metrics=[&#39;acc&#39;])

    return model</code></pre>
<pre><code class="language-python">model_3 = build_model3()
model_3.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional_5"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_15 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">320</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_4                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">128</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_16 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │          <span style="color: #00af00; text-decoration-color: #00af00">18,496</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_5                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">256</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_17 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │          <span style="color: #00af00; text-decoration-color: #00af00">73,856</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4608</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_15 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                     │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)                 │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_6                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)                 │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_16 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                     │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)                 │         <span style="color: #00af00; text-decoration-color: #00af00">131,328</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_7                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)                 │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_17 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                     │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)                  │           <span style="color: #00af00; text-decoration-color: #00af00">2,570</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">2,589,834</span> (9.88 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">2,588,106</span> (9.87 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">1,728</span> (6.75 KB)
</pre>




<pre><code class="language-python">hist_3 = model_3.fit(x_train, y_train,
                     epochs=40,
                     validation_split=0.3,
                     batch_size=512)</code></pre>
<pre><code>Epoch 1/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m21s[0m 160ms/step - acc: 0.7350 - loss: 0.7634 - val_acc: 0.1299 - val_loss: 2.7767
Epoch 2/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.8582 - loss: 0.3842 - val_acc: 0.3571 - val_loss: 2.5578
Epoch 3/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.8845 - loss: 0.3142 - val_acc: 0.4129 - val_loss: 1.9564
Epoch 4/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.8887 - loss: 0.2980 - val_acc: 0.6051 - val_loss: 1.2800
Epoch 5/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.8991 - loss: 0.2685 - val_acc: 0.7246 - val_loss: 0.7777
Epoch 6/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 20ms/step - acc: 0.9092 - loss: 0.2384 - val_acc: 0.7784 - val_loss: 0.6222
Epoch 7/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9110 - loss: 0.2406 - val_acc: 0.8777 - val_loss: 0.3326
Epoch 8/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9180 - loss: 0.2145 - val_acc: 0.9142 - val_loss: 0.2361
Epoch 9/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9120 - loss: 0.2314 - val_acc: 0.9153 - val_loss: 0.2320
Epoch 10/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9286 - loss: 0.1951 - val_acc: 0.9119 - val_loss: 0.2439
Epoch 11/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.9304 - loss: 0.1859 - val_acc: 0.9118 - val_loss: 0.2508
Epoch 12/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 23ms/step - acc: 0.9236 - loss: 0.2017 - val_acc: 0.8492 - val_loss: 0.4852
Epoch 13/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9318 - loss: 0.1800 - val_acc: 0.9248 - val_loss: 0.2186
Epoch 14/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9301 - loss: 0.1841 - val_acc: 0.9146 - val_loss: 0.2461
Epoch 15/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9372 - loss: 0.1692 - val_acc: 0.9279 - val_loss: 0.2054
Epoch 16/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 18ms/step - acc: 0.9417 - loss: 0.1566 - val_acc: 0.9127 - val_loss: 0.2526
Epoch 17/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.9340 - loss: 0.1773 - val_acc: 0.8458 - val_loss: 0.5771
Epoch 18/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9166 - loss: 0.2161 - val_acc: 0.8932 - val_loss: 0.3014
Epoch 19/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9365 - loss: 0.1630 - val_acc: 0.9253 - val_loss: 0.2123
Epoch 20/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9461 - loss: 0.1421 - val_acc: 0.9290 - val_loss: 0.2070
Epoch 21/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 19ms/step - acc: 0.9450 - loss: 0.1443 - val_acc: 0.9127 - val_loss: 0.2592
Epoch 22/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 19ms/step - acc: 0.9522 - loss: 0.1256 - val_acc: 0.9237 - val_loss: 0.2341
Epoch 23/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9516 - loss: 0.1296 - val_acc: 0.9283 - val_loss: 0.2085
Epoch 24/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.9526 - loss: 0.1224 - val_acc: 0.9123 - val_loss: 0.2683
Epoch 25/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 20ms/step - acc: 0.9501 - loss: 0.1317 - val_acc: 0.9276 - val_loss: 0.2233
Epoch 26/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9536 - loss: 0.1240 - val_acc: 0.9330 - val_loss: 0.2050
Epoch 27/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9594 - loss: 0.1083 - val_acc: 0.9297 - val_loss: 0.2211
Epoch 28/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9623 - loss: 0.1014 - val_acc: 0.9193 - val_loss: 0.2532
Epoch 29/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9594 - loss: 0.1092 - val_acc: 0.9223 - val_loss: 0.2487
Epoch 30/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.9532 - loss: 0.1246 - val_acc: 0.9099 - val_loss: 0.2844
Epoch 31/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 20ms/step - acc: 0.9551 - loss: 0.1161 - val_acc: 0.9290 - val_loss: 0.2255
Epoch 32/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9599 - loss: 0.1057 - val_acc: 0.9151 - val_loss: 0.2691
Epoch 33/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 19ms/step - acc: 0.9633 - loss: 0.0979 - val_acc: 0.9321 - val_loss: 0.2323
Epoch 34/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9697 - loss: 0.0833 - val_acc: 0.9201 - val_loss: 0.2688
Epoch 35/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9483 - loss: 0.1393 - val_acc: 0.9292 - val_loss: 0.2192
Epoch 36/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 19ms/step - acc: 0.9514 - loss: 0.1292 - val_acc: 0.9185 - val_loss: 0.2540
Epoch 37/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 18ms/step - acc: 0.9528 - loss: 0.1289 - val_acc: 0.9311 - val_loss: 0.2254
Epoch 38/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9658 - loss: 0.0902 - val_acc: 0.9335 - val_loss: 0.2218
Epoch 39/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 19ms/step - acc: 0.9708 - loss: 0.0772 - val_acc: 0.9358 - val_loss: 0.2119
Epoch 40/40
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9703 - loss: 0.0761 - val_acc: 0.9302 - val_loss: 0.2325</code></pre><pre><code class="language-python">hist_3.history.keys()

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(hist_3.history[&#39;loss&#39;], &#39;b--&#39;, label=&#39;loss&#39;)
plt.plot(hist_3.history[&#39;val_loss&#39;],&#39;r:&#39;,label=&#39;val_loss&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Loss&#39;)
plt.grid()
plt.legend()

plt.subplot(1,2,2)
plt.plot(hist_3.history[&#39;acc&#39;], &#39;b--&#39;, label=&#39;acc&#39;)
plt.plot(hist_3.history[&#39;val_acc&#39;],&#39;r:&#39;,label=&#39;val_acc&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/bf4b7d09-0220-4eb3-ae41-c49bbb557252/image.png" alt=""></p>
<pre><code class="language-python">model_3.evaluate(x_test, y_test) # 학습시간 감소 및 과적합 방지</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 3ms/step - acc: 0.9212 - loss: 0.2699





[0.2635417580604553, 0.9228000044822693]</code></pre><ul>
<li>과대적합은 되지 않았고, 층을 늘려도 좋은 성능을 낼 수 있음</li>
</ul>
<h3 id="모델-성능-높이기-이미지-보강image-augmentation">모델 성능 높이기: 이미지 보강(Image Augmentation)</h3>
<ul>
<li>주요 인자 참고: <a href="https://keras.io/ko/preprocessing/image/">https://keras.io/ko/preprocessing/image/</a></li>
</ul>
<pre><code class="language-python">from tensorflow.keras.preprocessing.image import ImageDataGenerator</code></pre>
<pre><code class="language-python">image_generator = ImageDataGenerator(
    rotation_range=10,         # 이미지를 무작위로 최대 10도까지 회전
    zoom_range=0.2,            # 이미지를 무작위로 최대 20%까지 확대 또는 축소
    shear_range=0.7,           # 이미지를 무작위로 최대 0.7 라디안만큼 시프트
    width_shift_range=0.1,     # 이미지를 좌우로 최대 10%까지 이동
    height_shift_range=0.1,    # 이미지를 상하로 최대 10%까지 이동
    horizontal_flip=True,      # 이미지를 좌우로 무작위로 뒤집음
    vertical_flip=False        # 이미지를 상하로 뒤집지 않음
)

augment_size = 200
print(x_train.shape)
print(x_train[0].shape)</code></pre>
<pre><code>(60000, 28, 28, 1)
(28, 28, 1)</code></pre><pre><code class="language-python">x_augment = image_generator.flow(
    x_train[:augment_size],
    batch_size=augment_size, # 한 번에 200개 이미지를 반환
    shuffle=False # 데이터를 무작위로 섞지 않고 원래 순서를 유지
    )</code></pre>
<pre><code class="language-python"># 1개 이미지에 대한 증강 예시
# 여러 이미지로 할거면 x_augment[0][i]로 수정
plt.figure(figsize=(10,10))
for i in range(1,11):
    plt.subplot(1,10,i)
    plt.imshow(x_augment[0][0], cmap=&#39;gray&#39;)
    plt.axis(&#39;off&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d1d90387-2974-4f62-96da-157bccde3fcc/image.png" alt=""></p>
<p>데이터 추가</p>
<pre><code class="language-python">image_generator = ImageDataGenerator(
    rotation_range=15,         # 이미지를 무작위로 최대 10도까지 회전
    zoom_range=0.1,            # 이미지를 무작위로 최대 20%까지 확대 또는 축소
    shear_range=0.6,           # 이미지를 무작위로 최대 0.7 라디안만큼 시프트
    width_shift_range=0.1,     # 이미지를 좌우로 최대 10%까지 이동
    height_shift_range=0.1,    # 이미지를 상하로 최대 10%까지 이동
    horizontal_flip=True,      # 이미지를 좌우로 무작위로 뒤집음
    vertical_flip=False        # 이미지를 상하로 뒤집지 않음
)

augment_size = 20000

# x_train에서 10000개 샘플을 무작위로 선택
indices = np.random.permutation(len(x_train))[:20000]
x_sample = x_train[indices]

x_augmented = image_generator.flow(
    x_sample,           # 증강할 데이터 샘플
    batch_size=augment_size,  # 배치 크기: 한 번에 20000개 이미지 반환
    shuffle=False       # 데이터를 섞지 않고 원래 순서대로 반환
)

x_augmented = next(x_augmented)
y_augmented = y_train[indices]</code></pre>
<pre><code class="language-python">x_train = np.concatenate((x_train, x_augmented))
y_train = np.concatenate((y_train, y_augmented))

print(x_train.shape)
print(y_train.shape)</code></pre>
<pre><code>(90000, 28, 28, 1)
(90000,)</code></pre><pre><code class="language-python">x_augmented = image_generator.flow(
    x_sample[:1000],           # 증강할 데이터 샘플
    batch_size=10,  # 배치 크기: 한 번에 20000개 이미지 반환
    shuffle=False       # 데이터를 섞지 않고 원래 순서대로 반환
)
x_augmented = next(x_augmented)
x_augmented.shape</code></pre>
<pre><code>(10, 28, 28, 1)</code></pre><pre><code class="language-python">model_4 = build_model3()
model_4.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">320</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">128</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)         │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │          <span style="color: #00af00; text-decoration-color: #00af00">18,496</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_1                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">12</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">256</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │          <span style="color: #00af00; text-decoration-color: #00af00">73,856</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">6</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4608</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                        │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)                 │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_2                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)                 │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)                 │         <span style="color: #00af00; text-decoration-color: #00af00">131,328</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_3                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)                 │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)                  │           <span style="color: #00af00; text-decoration-color: #00af00">2,570</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">2,589,834</span> (9.88 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">2,588,106</span> (9.87 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">1,728</span> (6.75 KB)
</pre>




<pre><code class="language-python">hist_4 = model_4.fit(x_train, y_train,
                     epochs=40,
                     validation_split=0.3,
                     batch_size=512)</code></pre>
<pre><code>Epoch 1/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m34s[0m 184ms/step - acc: 0.7523 - loss: 0.7219 - val_acc: 0.0992 - val_loss: 3.1442
Epoch 2/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m13s[0m 18ms/step - acc: 0.8723 - loss: 0.3486 - val_acc: 0.2008 - val_loss: 2.4828
Epoch 3/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.8899 - loss: 0.2960 - val_acc: 0.3189 - val_loss: 1.9961
Epoch 4/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.8967 - loss: 0.2757 - val_acc: 0.6415 - val_loss: 0.9980
Epoch 5/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9052 - loss: 0.2499 - val_acc: 0.7106 - val_loss: 0.8654
Epoch 6/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9090 - loss: 0.2404 - val_acc: 0.7913 - val_loss: 0.5495
Epoch 7/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9149 - loss: 0.2268 - val_acc: 0.7949 - val_loss: 0.5521
Epoch 8/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9221 - loss: 0.2066 - val_acc: 0.8007 - val_loss: 0.5301
Epoch 9/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 16ms/step - acc: 0.9266 - loss: 0.1929 - val_acc: 0.8075 - val_loss: 0.5180
Epoch 10/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9203 - loss: 0.2103 - val_acc: 0.7842 - val_loss: 0.6006
Epoch 11/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 27ms/step - acc: 0.9272 - loss: 0.1967 - val_acc: 0.7857 - val_loss: 0.5904
Epoch 12/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m4s[0m 18ms/step - acc: 0.9290 - loss: 0.1888 - val_acc: 0.7979 - val_loss: 0.5497
Epoch 13/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9324 - loss: 0.1761 - val_acc: 0.8160 - val_loss: 0.5017
Epoch 14/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9391 - loss: 0.1584 - val_acc: 0.8068 - val_loss: 0.5432
Epoch 15/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9387 - loss: 0.1629 - val_acc: 0.7942 - val_loss: 0.5781
Epoch 16/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9389 - loss: 0.1612 - val_acc: 0.7939 - val_loss: 0.5850
Epoch 17/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9422 - loss: 0.1505 - val_acc: 0.7552 - val_loss: 0.7547
Epoch 18/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9452 - loss: 0.1445 - val_acc: 0.7646 - val_loss: 0.6880
Epoch 19/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9440 - loss: 0.1467 - val_acc: 0.7863 - val_loss: 0.6695
Epoch 20/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9511 - loss: 0.1321 - val_acc: 0.8042 - val_loss: 0.5792
Epoch 21/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9557 - loss: 0.1169 - val_acc: 0.8122 - val_loss: 0.5416
Epoch 22/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9530 - loss: 0.1261 - val_acc: 0.8137 - val_loss: 0.5474
Epoch 23/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9564 - loss: 0.1176 - val_acc: 0.8146 - val_loss: 0.5623
Epoch 24/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 16ms/step - acc: 0.9588 - loss: 0.1094 - val_acc: 0.8151 - val_loss: 0.5554
Epoch 25/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 18ms/step - acc: 0.9576 - loss: 0.1109 - val_acc: 0.7866 - val_loss: 0.7532
Epoch 26/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9547 - loss: 0.1175 - val_acc: 0.7893 - val_loss: 0.7020
Epoch 27/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9541 - loss: 0.1183 - val_acc: 0.8107 - val_loss: 0.5805
Epoch 28/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9624 - loss: 0.0984 - val_acc: 0.8139 - val_loss: 0.5872
Epoch 29/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9615 - loss: 0.0991 - val_acc: 0.8057 - val_loss: 0.6517
Epoch 30/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9671 - loss: 0.0884 - val_acc: 0.8136 - val_loss: 0.6044
Epoch 31/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 18ms/step - acc: 0.9662 - loss: 0.0898 - val_acc: 0.7320 - val_loss: 1.0154
Epoch 32/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9406 - loss: 0.1630 - val_acc: 0.8024 - val_loss: 0.6433
Epoch 33/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9539 - loss: 0.1209 - val_acc: 0.7805 - val_loss: 0.7357
Epoch 34/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9554 - loss: 0.1195 - val_acc: 0.8030 - val_loss: 0.6776
Epoch 35/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9591 - loss: 0.1081 - val_acc: 0.8242 - val_loss: 0.5672
Epoch 36/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 17ms/step - acc: 0.9648 - loss: 0.0918 - val_acc: 0.8075 - val_loss: 0.6650
Epoch 37/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9717 - loss: 0.0742 - val_acc: 0.8067 - val_loss: 0.6721
Epoch 38/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 16ms/step - acc: 0.9686 - loss: 0.0815 - val_acc: 0.8218 - val_loss: 0.5832
Epoch 39/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 17ms/step - acc: 0.9724 - loss: 0.0747 - val_acc: 0.8110 - val_loss: 0.7081
Epoch 40/40
[1m124/124[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 16ms/step - acc: 0.9747 - loss: 0.0675 - val_acc: 0.7956 - val_loss: 0.7368</code></pre><ul>
<li>학습 인자를 이전과 다르게 주면서 학습하면 더 잘 나올것으로 판단</li>
</ul>
<pre><code class="language-python">hist_4.history.keys()

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(hist_4.history[&#39;loss&#39;], &#39;b--&#39;, label=&#39;loss&#39;)
plt.plot(hist_4.history[&#39;val_loss&#39;],&#39;r:&#39;,label=&#39;val_loss&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Loss&#39;)
plt.grid()
plt.legend()

plt.subplot(1,2,2)
plt.plot(hist_4.history[&#39;acc&#39;], &#39;b--&#39;, label=&#39;acc&#39;)
plt.plot(hist_4.history[&#39;val_acc&#39;],&#39;r:&#39;,label=&#39;val_acc&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/981f21f9-0bba-4c13-93b5-f1f9a81bd448/image.png" alt=""></p>
<pre><code class="language-python">model_4.evaluate(x_test, y_test) # 학습시간 감소 및 과적합 방지</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 5ms/step - acc: 0.9187 - loss: 0.2915





[0.28360623121261597, 0.9200999736785889]</code></pre><h2 id="cifar-10">CIFAR-10</h2>
<ul>
<li>CIFAR-10<ul>
<li>10개의 클래스로 구분된 32 x 32 사물 사진을 모은 데이터셋</li>
<li>50,000개의 학습데이터, 10,000개의 테스트 데이터로 구성</li>
<li>데이터 복잡도가 MNIST보다 훨씬 높은 특징이 있음<ul>
<li>단순한 신경망으로 특징을 검출하기 어려움</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/2bb62028-5297-4cbd-9142-b3ede65e7e2d/image.png" alt=""></p>
<h3 id="모듈-임포트-2">모듈 임포트</h3>
<pre><code class="language-python">from tensorflow.keras.datasets import cifar10
from tensorflow.keras.layers import Conv2D, MaxPool2D, Dense, Input, Dropout, BatchNormalization
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.optimizers import Adam, SGD
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.utils import to_categorical

from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt</code></pre>
<h3 id="데이터-로드-및-전처리-2">데이터 로드 및 전처리</h3>
<pre><code class="language-python">(x_train_full, y_train_full), (x_test, y_test) = cifar10.load_data()

print(x_train_full.shape)
print(y_train_full.shape)
print(x_test.shape)
print(y_test.shape)</code></pre>
<pre><code>(50000, 32, 32, 3)
(50000, 1)
(10000, 32, 32, 3)
(10000, 1)</code></pre><pre><code class="language-python">class_names = [&#39;airplane&#39;, &#39;automobile&#39;, &#39;bird&#39;, &#39;cat&#39;, &#39;deer&#39;, &#39;dog&#39;, &#39;frog&#39;, &#39;horse&#39;, &#39;ship&#39;, &#39;truck&#39;]</code></pre>
<pre><code class="language-python">import random
plt.figure(figsize=(10, 5))
for i in range(10):
    plt.subplot(2, 5, i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    idx = random.randint(1,50000)
    plt.imshow(x_train_full[idx])
    plt.xlabel(class_names[y_train_full[idx][0]])</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/b210e84a-5b36-4eab-b369-337015585ba2/image.png" alt=""></p>
<pre><code class="language-python"># x_train_full 데이터셋의 각 채널에 대한 평균을 계산
x_mean = np.mean(x_train_full, axis=(0,1,2)) # array([125.30691805, 122.95039414, 113.86538318])
# x_train_full 데이터셋의 각 채널에 대한 표준편차를 계산
x_std = np.std(x_train_full, axis=(0,1,2))

# 훈련 데이터를 정규화: 각 픽셀의 값에서 채널별 평균을 빼고, 채널별 표준편차로 나누어줍니다.
# 이 과정은 데이터의 평균을 0, 표준편차를 1로 맞추어줌으로써, 데이터를 정규화합니다. 이렇게 하면 모델이 더 빠르고 효율적으로 학습할 수 있습니다.
x_train_full = (x_train_full - x_mean) / x_std
x_test = (x_test - x_mean) / x_std

x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full,test_size = 0.2)

print(x_train.shape)
print(y_train.shape)

print(x_val.shape)
print(y_val.shape)

print(x_test.shape)
print(y_test.shape)</code></pre>
<pre><code>(40000, 32, 32, 3)
(40000, 1)
(10000, 32, 32, 3)
(10000, 1)
(10000, 32, 32, 3)
(10000, 1)</code></pre><h3 id="모델-구성-및-학습-1">모델 구성 및 학습</h3>
<pre><code class="language-python">def model_build():
    input = Input(shape=(32,32,3))

    output = Conv2D(filters=32, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(input)
    output = BatchNormalization()(output)
    output = MaxPool2D(pool_size=2, strides=2, padding=&#39;same&#39;)(output)
    output = Dropout(0.3)(output)  # Dropout 추가

    output = Conv2D(filters=64, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(output)
    output = BatchNormalization()(output)
    output = MaxPool2D(pool_size=2, strides=2, padding=&#39;same&#39;)(output)
    output = Dropout(0.3)(output)  # Dropout 추가

    output = Conv2D(filters=128, kernel_size=3, padding=&#39;same&#39;, activation=&#39;relu&#39;)(output)
    output = BatchNormalization()(output)
    output = MaxPool2D(pool_size=2, strides=2, padding=&#39;same&#39;)(output)
    output = Dropout(0.3)(output)  # Dropout 추가

    output = Flatten()(output)
    output = Dense(256, activation=&#39;relu&#39;)(output)
    output = Dense(128, activation=&#39;relu&#39;)(output)
    output = Dense(10, activation=&#39;softmax&#39;)(output)

    model = Model(inputs=input, outputs=output)

    model.compile(optimizer=Adam(learning_rate=1e-4),
                  loss=&#39;sparse_categorical_crossentropy&#39;,
                  metrics=[&#39;accuracy&#39;])
    return model</code></pre>
<pre><code class="language-python">model = model_build()
model.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional_2"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">3</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">896</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_4                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">128</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │          <span style="color: #00af00; text-decoration-color: #00af00">18,496</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_5                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">256</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)            │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv2d_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │          <span style="color: #00af00; text-decoration-color: #00af00">73,856</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ batch_normalization_6                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">8</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │             <span style="color: #00af00; text-decoration-color: #00af00">512</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)                 │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ max_pooling2d_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4</span>, <span style="color: #00af00; text-decoration-color: #00af00">4</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4</span>, <span style="color: #00af00; text-decoration-color: #00af00">4</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">2048</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)                 │         <span style="color: #00af00; text-decoration-color: #00af00">524,544</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)                 │          <span style="color: #00af00; text-decoration-color: #00af00">32,896</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dense_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                      │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)                  │           <span style="color: #00af00; text-decoration-color: #00af00">1,290</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">652,874</span> (2.49 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">652,426</span> (2.49 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">448</span> (1.75 KB)
</pre>




<pre><code class="language-python">history = model.fit(x_train, y_train,
                    epochs=30,
                    batch_size=256,
                    validation_data=(x_val, y_val),
                    verbose=1)</code></pre>
<pre><code>Epoch 1/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m14s[0m 45ms/step - accuracy: 0.2141 - loss: 2.3805 - val_accuracy: 0.1080 - val_loss: 2.6461
Epoch 2/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.3799 - loss: 1.7169 - val_accuracy: 0.2475 - val_loss: 2.1843
Epoch 3/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 13ms/step - accuracy: 0.4375 - loss: 1.5589 - val_accuracy: 0.3796 - val_loss: 1.7988
Epoch 4/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 15ms/step - accuracy: 0.4722 - loss: 1.4586 - val_accuracy: 0.4345 - val_loss: 1.6436
Epoch 5/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.5085 - loss: 1.3641 - val_accuracy: 0.4518 - val_loss: 1.6275
Epoch 6/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 14ms/step - accuracy: 0.5273 - loss: 1.3022 - val_accuracy: 0.4682 - val_loss: 1.5677
Epoch 7/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.5494 - loss: 1.2620 - val_accuracy: 0.4836 - val_loss: 1.5374
Epoch 8/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.5694 - loss: 1.2036 - val_accuracy: 0.4937 - val_loss: 1.5278
Epoch 9/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 14ms/step - accuracy: 0.5831 - loss: 1.1606 - val_accuracy: 0.5038 - val_loss: 1.5092
Epoch 10/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.5991 - loss: 1.1246 - val_accuracy: 0.5139 - val_loss: 1.4902
Epoch 11/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.6068 - loss: 1.0984 - val_accuracy: 0.5207 - val_loss: 1.4868
Epoch 12/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 12ms/step - accuracy: 0.6163 - loss: 1.0636 - val_accuracy: 0.5354 - val_loss: 1.4020
Epoch 13/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 13ms/step - accuracy: 0.6294 - loss: 1.0313 - val_accuracy: 0.5522 - val_loss: 1.3588
Epoch 14/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 14ms/step - accuracy: 0.6372 - loss: 1.0137 - val_accuracy: 0.5594 - val_loss: 1.3492
Epoch 15/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.6482 - loss: 0.9896 - val_accuracy: 0.5594 - val_loss: 1.3671
Epoch 16/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.6594 - loss: 0.9630 - val_accuracy: 0.5738 - val_loss: 1.2931
Epoch 17/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 12ms/step - accuracy: 0.6688 - loss: 0.9328 - val_accuracy: 0.5773 - val_loss: 1.2941
Epoch 18/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 12ms/step - accuracy: 0.6717 - loss: 0.9184 - val_accuracy: 0.5790 - val_loss: 1.2898
Epoch 19/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 13ms/step - accuracy: 0.6817 - loss: 0.8892 - val_accuracy: 0.5928 - val_loss: 1.2357
Epoch 20/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 14ms/step - accuracy: 0.6863 - loss: 0.8761 - val_accuracy: 0.6040 - val_loss: 1.2148
Epoch 21/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 14ms/step - accuracy: 0.6956 - loss: 0.8556 - val_accuracy: 0.6048 - val_loss: 1.1849
Epoch 22/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7016 - loss: 0.8328 - val_accuracy: 0.6123 - val_loss: 1.1645
Epoch 23/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7058 - loss: 0.8243 - val_accuracy: 0.6244 - val_loss: 1.1196
Epoch 24/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7149 - loss: 0.8032 - val_accuracy: 0.6439 - val_loss: 1.0559
Epoch 25/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7195 - loss: 0.7888 - val_accuracy: 0.6500 - val_loss: 1.0487
Epoch 26/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 14ms/step - accuracy: 0.7284 - loss: 0.7690 - val_accuracy: 0.6487 - val_loss: 1.0579
Epoch 27/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7336 - loss: 0.7528 - val_accuracy: 0.6521 - val_loss: 1.0606
Epoch 28/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 12ms/step - accuracy: 0.7393 - loss: 0.7347 - val_accuracy: 0.6640 - val_loss: 1.0194
Epoch 29/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.7464 - loss: 0.7170 - val_accuracy: 0.6682 - val_loss: 1.0068
Epoch 30/30
[1m157/157[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 12ms/step - accuracy: 0.7474 - loss: 0.7082 - val_accuracy: 0.6692 - val_loss: 1.0084</code></pre><pre><code class="language-python">plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(history.history[&#39;loss&#39;], &#39;b--&#39;, label=&#39;loss&#39;)
plt.plot(history.history[&#39;val_loss&#39;],&#39;r:&#39;,label=&#39;val_loss&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Loss&#39;)
plt.grid()
plt.legend()

plt.subplot(1,2,2)
plt.plot(history.history[&#39;accuracy&#39;], &#39;b--&#39;, label=&#39;acc&#39;)
plt.plot(history.history[&#39;val_accuracy&#39;],&#39;r:&#39;,label=&#39;val_acc&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;accuracy&#39;)
plt.grid()
plt.legend()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8ec7a7ff-a707-45ca-817c-ae2bfcfe8160/image.png" alt=""></p>
<h1 id="cnn-모델의-발전">CNN 모델의 발전</h1>
<ul>
<li>1998: LeNet – Gradient-based Learning Applied to Document Recognition</li>
<li>2012: AlexNet – ImageNet Classification with Deep Convolutional Neural Network</li>
<li><strong>2014: VggNet – Very Deep Convolutional Networks for Large-Scale Image Recognition</strong></li>
<li><strong>2014: GooLeNet – Going Deeper with Convolutions</strong></li>
<li>2014: SppNet – Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition</li>
<li><strong>2015: ResNet – Deep Residual Learning for Image Recognition</strong></li>
<li><strong>2016: Xception – Xception: Deep Learning with Depthwise Separable Convolutions</strong></li>
<li><strong>2017: MobileNet – MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Application</strong></li>
<li><strong>2017: DenseNet – Densely Connected Convolutional Networks</strong></li>
<li>2017: SeNet – Squeeze and Excitation Networks</li>
<li>2017: ShuffleNet – ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices</li>
<li><strong>2018: NasNet – Learning Transferable Architectures for Scalable Image Recognition</strong></li>
<li>2018: Bag of Tricks – Bag of Tricks for Image Classification with Convolutional Neural Networks</li>
<li><strong>2019: EfficientNet – EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks</strong></li>
</ul>
<h2 id="vggnetvisual-geometry-group-net">VGGNet(Visual Geometry Group Net)</h2>
<ul>
<li>2014년 ILSVRC에서 2등 차지 (상위-5 오류율: 7.32%), 이 후의 수많은 연구에 영향을 미침</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/791b40cc-d17f-4744-bc57-a09b5f0410d9/image.png" alt=""></p>
<ul>
<li>ImageNet에서 훈련이 끝난 후 얻게된 매개변수 값 로딩</li>
<li>네트워크를 다시 처음부터 학습하고자 한다면 <code>weights=None</code>으로 설정, 케라스에서 무작위로 가중치를 설정함</li>
<li><code>include_top=False</code>: VGG의 밀집 계층을 제외한다는 뜻</li>
<li>해당 네트워크의 출력은 합성곱/최대-풀링 블록의 특징맵이 됨</li>
<li><code>pooling</code>: 특징맵을 반환하기 전에 적용할 선택적인 연산을 지정</li>
</ul>
<pre><code class="language-python">from keras.preprocessing import image
from keras.applications.vgg19 import VGG19, preprocess_input, decode_predictions

vggnet = VGG19(
    include_top=True,         # 최상위(fully connected) 분류 레이어를 포함할지 여부, True면 ImageNet 클래스 분류가 가능
    weights=&#39;imagenet&#39;,       # 사전 학습된 ImageNet 가중치 사용
    input_tensor=None,        # 입력 텐서를 지정할 경우, 기본값 None
    input_shape=None,         # 입력 이미지의 크기를 지정하지 않으면 기본값 사용 (224x224x3)
    pooling=None,             # 완전 연결 레이어 대신 특정 pooling을 할지 설정 가능 (None이면 기본 사용)
    classes=1000              # ImageNet의 클래스 개수 (1,000개)
    )
vggnet.summary()</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5
[1m574710816/574710816[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m12s[0m 0us/step</code></pre><pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "vgg19"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">3</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │           <span style="color: #00af00; text-decoration-color: #00af00">1,792</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_conv2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │          <span style="color: #00af00; text-decoration-color: #00af00">36,928</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block1_pool (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)       │          <span style="color: #00af00; text-decoration-color: #00af00">73,856</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_conv2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)       │         <span style="color: #00af00; text-decoration-color: #00af00">147,584</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block2_pool (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">295,168</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">590,080</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">590,080</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_conv4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">590,080</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block3_pool (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">1,180,160</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_conv4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block4_pool (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_conv4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │       <span style="color: #00af00; text-decoration-color: #00af00">2,359,808</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ block5_pool (<span style="color: #0087ff; text-decoration-color: #0087ff">MaxPooling2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ flatten (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">25088</span>)               │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ fc1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4096</span>)                │     <span style="color: #00af00; text-decoration-color: #00af00">102,764,544</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ fc2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">4096</span>)                │      <span style="color: #00af00; text-decoration-color: #00af00">16,781,312</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ predictions (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1000</span>)                │       <span style="color: #00af00; text-decoration-color: #00af00">4,097,000</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">143,667,240</span> (548.05 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">143,667,240</span> (548.05 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>



<ul>
<li>dog.jpg (<a href="https://www.publicdomainpictures.net/pictures/250000/nahled/dog-beagle-portrait.jpg">https://www.publicdomainpictures.net/pictures/250000/nahled/dog-beagle-portrait.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O dog.jpg https://www.publicdomainpictures.net/pictures/250000/nahled/dog-beagle-portrait.jpg</code></pre>
<pre><code>--2024-11-07 08:18:31--  https://www.publicdomainpictures.net/pictures/250000/nahled/dog-beagle-portrait.jpg
Resolving www.publicdomainpictures.net (www.publicdomainpictures.net)... 104.20.122.60, 104.20.123.60, 172.67.1.236, ...
Connecting to www.publicdomainpictures.net (www.publicdomainpictures.net)|104.20.122.60|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 88498 (86K) [image/jpeg]
Saving to: ‘dog.jpg’

dog.jpg             100%[===================&gt;]  86.42K  --.-KB/s    in 0.02s   

2024-11-07 08:18:31 (5.28 MB/s) - ‘dog.jpg’ saved [88498/88498]</code></pre><pre><code class="language-python">img = image.load_img(&#39;dog.jpg&#39;, target_size=(224, 224))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = vggnet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 3s/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/imagenet_class_index.json
[1m35363/35363[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step
[[(&#39;n02088364&#39;, &#39;beagle&#39;, 0.83840275), (&#39;n02089973&#39;, &#39;English_foxhound&#39;, 0.08918254), (&#39;n02089867&#39;, &#39;Walker_hound&#39;, 0.0624161), (&#39;n02088238&#39;, &#39;basset&#39;, 0.0046146614), (&#39;n02088632&#39;, &#39;bluetick&#39;, 0.003374971)]]</code></pre><p> <img src="https://velog.velcdn.com/images/s_s/post/24e41245-4bd7-4053-92e7-333a74a7307e/image.png" alt=""></p>
<h2 id="googlenet-inception">GoogLeNet, Inception</h2>
<ul>
<li><p>VGGNet을 제치고 같은 해 분류 과제에서 1등을 차지</p>
</li>
<li><p>인셉션 블록이라는 개념을 도입하여, <strong>인셉션 네트워크(Inception Network)</strong>라고도 불림</p>
</li>
<li><p>깊고 넓은 네트워크 구조를 가지면서도 효율적인 연산 구조를 가지고 있음 </p>
<img src="https://miro.medium.com/max/2800/0*rbWRzjKvoGt9W3Mf.png">
</li>
<li><p>특징</p>
<ul>
<li>GoogLeNet은 CNN 모델의 계산량을 최적화하는 데 중점을 둠</li>
<li>초기에는 전형적인 합성곱 계층과 풀링 계층으로 시작하며, 이후에 이 정보는 9개의 인셉션 모듈(Inception Module) 스택을 통과</li>
<li>인셉션 모듈에서는 입력 특징 맵이 서로 다른 기능을 수행하는 4개의 병렬 하위 블록으로 전달. 이 하위 블록들은 서로 다른 커널 크기와 풀링 방식을 사용하여 다양한 특징을 추출하고, 다시 연결하여 다음 계층으로 전달</li>
<li>모든 합성곱 및 풀링 계층에는 &#39;SAME&#39; 패딩과 stride=1 설정이 적용. 활성화 함수로는 ReLU가 사용되어 모델의 비선형성을 부여</li>
</ul>
</li>
<li><p>기여</p>
<ul>
<li>블록과 병목 구조를 통해 연산량을 효율적으로 관리하고, 네트워크가 더 깊어지면서도 학습에 안정성을 부여할 수 있게 함</li>
<li>병목 계층으로 1x1 합성곱 계층 사용(채널 수를 줄여 연산량 감소)</li>
<li>병목 계층은 중요한 정보만을 추출하고 압축하여 다음 계층으로 전달할 수 있음. 이를 통해 중요한 특징을 유지하면서도 불필요한 정보를 제거하는 효과가 있음</li>
<li>중간 손실(Intermediate Loss)로 네트워크 중간에서 손실을 발생시키고 이를 역전파함으로써 경사 소실 문제 해결</li>
</ul>
<img src="https://norman3.github.io/papers/images/google_inception/f01.png"></li>
<li><p>병목(bottleneck)**은 신경망에서 연산량과 메모리 사용을 줄이기 위해 특정 계층에서 데이터의 차원을 축소하는 기법</p>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.applications.inception_v3 import InceptionV3, preprocess_input, decode_predictions

inception = InceptionV3(include_top=True, weights=&#39;imagenet&#39;,
                        input_tensor=None, input_shape=None,
                        pooling=None, classes=1000)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/inception_v3/inception_v3_weights_tf_dim_ordering_tf_kernels.h5
[1m96112376/96112376[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step</code></pre><ul>
<li>fish.jpg (<a href="https://upload.wikimedia.org/wikipedia/commons/7/7a/Goldfish_1.jpg">https://upload.wikimedia.org/wikipedia/commons/7/7a/Goldfish_1.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O fish.jpg https://upload.wikimedia.org/wikipedia/commons/7/7a/Goldfish_1.jpg</code></pre>
<pre><code>--2024-11-07 08:56:00--  https://upload.wikimedia.org/wikipedia/commons/7/7a/Goldfish_1.jpg
Resolving upload.wikimedia.org (upload.wikimedia.org)... 198.35.26.112, 2620:0:863:ed1a::2:b
Connecting to upload.wikimedia.org (upload.wikimedia.org)|198.35.26.112|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 4648040 (4.4M) [image/jpeg]
Saving to: ‘fish.jpg’

fish.jpg            100%[===================&gt;]   4.43M  20.1MB/s    in 0.2s    

2024-11-07 08:56:00 (20.1 MB/s) - ‘fish.jpg’ saved [4648040/4648040]</code></pre><pre><code class="language-python">img = image.load_img(&#39;fish.jpg&#39;, target_size=(299, 299))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = inception.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m9s[0m 9s/step
[[(&#39;n01443537&#39;, &#39;goldfish&#39;, 0.9748253), (&#39;n02701002&#39;, &#39;ambulance&#39;, 0.0023263083), (&#39;n02606052&#39;, &#39;rock_beauty&#39;, 0.0019094164), (&#39;n02607072&#39;, &#39;anemone_fish&#39;, 0.00066422834), (&#39;n09256479&#39;, &#39;coral_reef&#39;, 0.00043224308)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/9c9fa38e-d311-4e17-b399-302e684e69c5/image.png" alt=""></p>
<h2 id="모델-깊이와-그라디언트-소실gradient-vanishing">모델 깊이와 그라디언트 소실(Gradient Vanishing)</h2>
<img src="https://wikidocs.net/images/page/230057/ResNet_Figure_1.png" width="500">

<p>[이미지출처] resnet</p>
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FokTU1%2FbtrCH4Fx7Ro%2Fm5f9UkRkl7CP3dUEgXTtk1%2Fimg.png" width="500">

<h2 id="resnetresidual-net">ResNet(Residual Net)</h2>
<ul>
<li><p>네트워크의 깊이가 깊어질수록 경사가 소실되거나 폭발하는 문제를 해결하고자 함</p>
</li>
<li><p>병목 합성곱 계층을 추가하거나 크기가 작은 커널을 사용</p>
</li>
<li><p>152개의 훈련가능한 계층을 수직으로 연결하여 구성</p>
</li>
<li><p>모든 합성곱과 풀링 계층에서 패딩옵션으로 <code>&#39;SAME&#39;</code>, <code>stride=1</code> 사용</p>
</li>
<li><p>3x3 합성곱 계층 다음마다 배치 정규화 적용, 1x1 합성곱 계층에는 활성화 함수가 존재하지 않음</p>
<img src="https://miro.medium.com/max/1200/1*6hF97Upuqg_LdsqWY6n_wg.png">

</li>
</ul>
<h3 id="잔차-학습residual-learning">잔차 학습(Residual Learning)</h3>
<ul>
<li><p>잔차 연결은 일부 레이어를 건너뛰어(Skip Connection) 데이터가 신경망 구조의 후반부에 도달하는 또 다른 경로를 제공함으로써 정보를 보존하고 gradient가 계속 커지거나 작아지는 문제를 해결할 수 있음</p>
</li>
<li><p>잔차 학습을 통해 입력과 출력 사이의 변화를 학습하도록 유도하여, 더 깊은 네트워크에서도 효율적인 학습이 가능하게 함.</p>
</li>
<li><p>잔차 블록은 지름길 연결을 추가하여 경사를 효과적으로 전달하므로, 기울기 소실 문제를 완화하고 깊은 네트워크에서도 안정적인 학습을 도움.</p>
</li>
<li><p>ResNet의 이러한 구조 덕분에 초깊은 네트워크에서도 학습이 가능해졌고, 일반화 성능이 뛰어난 모델로 평가됨</p>
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLujPF%2Fbtsk1au4T3D%2FaOzJxo1KAhj6fk5b99PYYK%2Fimg.png">


</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.applications.resnet50 import ResNet50, preprocess_input, decode_predictions

resnet = ResNet50(include_top=True, weights=&#39;imagenet&#39;,
                  input_tensor=None, input_shape=None,
                  pooling=None, classes=1000)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/resnet/resnet50_weights_tf_dim_ordering_tf_kernels.h5
[1m102967424/102967424[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 0us/step</code></pre><ul>
<li>bee.jpg (<a href="https://upload.wikimedia.org/wikipedia/commons/4/4d/Apis_mellifera_Western_honey_bee.jpg">https://upload.wikimedia.org/wikipedia/commons/4/4d/Apis_mellifera_Western_honey_bee.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O bee.jpg https://upload.wikimedia.org/wikipedia/commons/4/4d/Apis_mellifera_Western_honey_bee.jpg</code></pre>
<pre><code>--2024-11-07 09:26:11--  https://upload.wikimedia.org/wikipedia/commons/4/4d/Apis_mellifera_Western_honey_bee.jpg
Resolving upload.wikimedia.org (upload.wikimedia.org)... 198.35.26.112, 2620:0:863:ed1a::2:b
Connecting to upload.wikimedia.org (upload.wikimedia.org)|198.35.26.112|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2421052 (2.3M) [image/jpeg]
Saving to: ‘bee.jpg’

bee.jpg             100%[===================&gt;]   2.31M  --.-KB/s    in 0.1s    

2024-11-07 09:26:11 (16.0 MB/s) - ‘bee.jpg’ saved [2421052/2421052]</code></pre><pre><code class="language-python">img = image.load_img(&#39;bee.jpg&#39;, target_size=(224, 224))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = resnet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m4s[0m 4s/step
[[(&#39;n02206856&#39;, &#39;bee&#39;, 0.9990979), (&#39;n03530642&#39;, &#39;honeycomb&#39;, 0.00056267227), (&#39;n02190166&#39;, &#39;fly&#39;, 0.00014306529), (&#39;n02727426&#39;, &#39;apiary&#39;, 0.00010191327), (&#39;n02219486&#39;, &#39;ant&#39;, 5.741178e-05)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/c7116534-877c-4bd6-8581-6de8a39ff197/image.png" alt=""></p>
<h2 id="xception">Xception</h2>
<ul>
<li>Inception module을 이용하여 depthwise convolution 적용</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/a35796e8-5849-41e5-a266-d599d99a2c73/image.png" alt=""></p>
<h2 id="핵심-내용">핵심 내용</h2>
<ul>
<li>inception과 ResNet의 장점을 결합한 모델</li>
<li>일반적인 CNN 모델은 RGB 이미지를 입력으로 사용하는 경우, R, G, B 채널 전체에 대해 필터를 적용하여 각 필터가 모든 채널을 종합적으로 고려해 특징을 추출함</li>
<li>Xception은 각 채널에 대해 개별적으로 합성곱 연산을 수행하는 <strong>깊이별 합성곱 (depthwise convolution)</strong>과, 그 결과를 다시 결합하는 <strong>점별 합성곱 (pointwise convolution)</strong>을 사용하여 계산량을 크게 줄일 수 있으며, 일반적인 합성곱보다 더 복잡한 패턴을 학습하는 데 유리한 장점을 가짐</li>
<li>ResNet의 아이디어를 확장해서 <strong>skip connection</strong>을 도입해 학습을 안정화하고 정보 손실을 줄임</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/c4c1daf7-61b8-44cc-ab74-7011fee1b2ac/image.png" alt=""></p>
<pre><code class="language-python">from tensorflow.keras.applications.mobilenet import MobileNet, preprocess_input, decode_predictions

mobilenet = MobileNet(include_top=True, weights=&#39;imagenet&#39;,
                      input_tensor=None, input_shape=None,
                      pooling=None, classes=1000)
mobilenet.summary()</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet/mobilenet_1_0_224_tf.h5
[1m17225924/17225924[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step</code></pre><pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "mobilenet_1.00_224"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                         </span>┃<span style="font-weight: bold"> Output Shape                </span>┃<span style="font-weight: bold">         Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ input_layer_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">224</span>, <span style="color: #00af00; text-decoration-color: #00af00">3</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                       │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │             <span style="color: #00af00; text-decoration-color: #00af00">864</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv1_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)        │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │             <span style="color: #00af00; text-decoration-color: #00af00">128</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv1_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │             <span style="color: #00af00; text-decoration-color: #00af00">288</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_1_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │             <span style="color: #00af00; text-decoration-color: #00af00">128</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_1_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)        │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_1_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │             <span style="color: #00af00; text-decoration-color: #00af00">256</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_1_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">112</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pad_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">ZeroPadding2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">113</span>, <span style="color: #00af00; text-decoration-color: #00af00">113</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)        │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">576</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_2_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │             <span style="color: #00af00; text-decoration-color: #00af00">256</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_2_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">8,192</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_2_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │             <span style="color: #00af00; text-decoration-color: #00af00">512</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_2_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,152</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_3_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │             <span style="color: #00af00; text-decoration-color: #00af00">512</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_3_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │          <span style="color: #00af00; text-decoration-color: #00af00">16,384</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_3_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │             <span style="color: #00af00; text-decoration-color: #00af00">512</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_3_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">56</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pad_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">ZeroPadding2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">57</span>, <span style="color: #00af00; text-decoration-color: #00af00">57</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,152</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_4_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │             <span style="color: #00af00; text-decoration-color: #00af00">512</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_4_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │          <span style="color: #00af00; text-decoration-color: #00af00">32,768</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_4_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_4_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,304</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_5_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_5_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │          <span style="color: #00af00; text-decoration-color: #00af00">65,536</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_5_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_5_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">28</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pad_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">ZeroPadding2D</span>)           │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">29</span>, <span style="color: #00af00; text-decoration-color: #00af00">29</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,304</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_6_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">1,024</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_6_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_6 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">131,072</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_6_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_6_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_7_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_7_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_7 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">262,144</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_7_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_7_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_8_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_8_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">262,144</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_8_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_8_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_9_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_9_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">262,144</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_9_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_9_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)         │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_10_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_10_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">262,144</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_10_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_10_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)         │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_11_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_11_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │         <span style="color: #00af00; text-decoration-color: #00af00">262,144</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_11_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_11_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">14</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pad_12 (<span style="color: #0087ff; text-decoration-color: #0087ff">ZeroPadding2D</span>)          │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">15</span>, <span style="color: #00af00; text-decoration-color: #00af00">15</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)         │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_12 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)         │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)           │           <span style="color: #00af00; text-decoration-color: #00af00">4,608</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_12_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)           │           <span style="color: #00af00; text-decoration-color: #00af00">2,048</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_12_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)           │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_12 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │         <span style="color: #00af00; text-decoration-color: #00af00">524,288</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_12_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">4,096</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_12_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_13 (<span style="color: #0087ff; text-decoration-color: #0087ff">DepthwiseConv2D</span>)         │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">9,216</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_13_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">4,096</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_dw_13_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_13 (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │       <span style="color: #00af00; text-decoration-color: #00af00">1,048,576</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_13_bn (<span style="color: #0087ff; text-decoration-color: #0087ff">BatchNormalization</span>)   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │           <span style="color: #00af00; text-decoration-color: #00af00">4,096</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_pw_13_relu (<span style="color: #0087ff; text-decoration-color: #0087ff">ReLU</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ global_average_pooling2d             │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
│ (<span style="color: #0087ff; text-decoration-color: #0087ff">GlobalAveragePooling2D</span>)             │                             │                 │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ dropout (<span style="color: #0087ff; text-decoration-color: #0087ff">Dropout</span>)                    │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1024</span>)          │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ conv_preds (<span style="color: #0087ff; text-decoration-color: #0087ff">Conv2D</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>, <span style="color: #00af00; text-decoration-color: #00af00">1000</span>)          │       <span style="color: #00af00; text-decoration-color: #00af00">1,025,000</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ reshape_2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Reshape</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1000</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤
│ predictions (<span style="color: #0087ff; text-decoration-color: #0087ff">Activation</span>)             │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1000</span>)                │               <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">4,253,864</span> (16.23 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">4,231,976</span> (16.14 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">21,888</span> (85.50 KB)
</pre>



<ul>
<li>crane.jpg (<a href="https://p1.pxfuel.com/preview/42/50/534/europe-channel-crane-harbour-crane-harbour-cranes-cranes-transport.jpg">https://p1.pxfuel.com/preview/42/50/534/europe-channel-crane-harbour-crane-harbour-cranes-cranes-transport.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O crane.jpg https://p1.pxfuel.com/preview/42/50/534/europe-channel-crane-harbour-crane-harbour-cranes-cranes-transport.jpg</code></pre>
<pre><code>--2024-11-07 09:44:54--  https://p1.pxfuel.com/preview/42/50/534/europe-channel-crane-harbour-crane-harbour-cranes-cranes-transport.jpg
Resolving p1.pxfuel.com (p1.pxfuel.com)... 104.21.12.22, 172.67.151.78, 2606:4700:3037::ac43:974e, ...
Connecting to p1.pxfuel.com (p1.pxfuel.com)|104.21.12.22|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 86911 (85K) [image/jpeg]
Saving to: ‘crane.jpg’

crane.jpg           100%[===================&gt;]  84.87K  --.-KB/s    in 0.01s   

2024-11-07 09:44:54 (6.00 MB/s) - ‘crane.jpg’ saved [86911/86911]</code></pre><pre><code class="language-python">img = image.load_img(&#39;crane.jpg&#39;, target_size=(224, 224))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = mobilenet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>WARNING:tensorflow:5 out of the last 6 calls to &lt;function TensorFlowTrainer.make_predict_function.&lt;locals&gt;.one_step_on_data_distributed at 0x7d034f6fdcf0&gt; triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.


[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 3s/step
[[(&#39;n03126707&#39;, &#39;crane&#39;, 0.96338284), (&#39;n03216828&#39;, &#39;dock&#39;, 0.027054528), (&#39;n03240683&#39;, &#39;drilling_platform&#39;, 0.0047102487), (&#39;n03344393&#39;, &#39;fireboat&#39;, 0.0022202074), (&#39;n03532672&#39;, &#39;hook&#39;, 0.0005047548)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/c506dd05-4901-40a8-8fd0-962fdf4456be/image.png" alt=""></p>
<h2 id="densenet">DenseNet</h2>
<ul>
<li>각 층은 모든 이전 층으로 부터 출력을 입력으로 받음(밀집 블록(dense block))</li>
<li>이러한 조밀 연결은 특징 맵을 재활용하며 네트워크 깊이가 깊어지더라도 이전 층에서 추출된 정보를 유지해 효율적인 학습을 가능하게 함</li>
<li>특징지도의 크기를 줄이기 위해 풀링 연산 적용 필요</li>
<li>각 층이 이전 모든 층의 출력을 활용할 수 있어 불필요한 특성 학습을 줄여 고유한 필터를 학습하지 않아도 되기 때문에 네트워크의 파라미터 수가 줄어듬.</li>
<li>위 특징으로 인해 메모리와 계산량을 줄이고, 경량 모델을 만들 수 있음.</li>
<li>밀집 블록(dense block)과 전이층(transition layer)으로 구성</li>
<li>전이층 : 1x1 컨볼루션과 평균값 풀링(APool)으로 구성 -&gt; 특징 맵의 크기와 채널 수를 줄임</li>
</ul>
<img src="https://oi.readthedocs.io/en/latest/_images/cnn_vs_resnet_vs_densenet.png" width="700">


<pre><code class="language-python">from tensorflow.keras.applications.densenet import DenseNet201, preprocess_input, decode_predictions

densenet = DenseNet201(include_top=True, weights=&#39;imagenet&#39;,
                  input_tensor=None, input_shape=None,
                  pooling=None, classes=1000)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/densenet/densenet201_weights_tf_dim_ordering_tf_kernels.h5
[1m82524592/82524592[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 0us/step</code></pre><ul>
<li>zebra.jpg (<a href="https://upload.wikimedia.org/wikipedia/commons/f/f0/Zebra_standing_alone_crop.jpg">https://upload.wikimedia.org/wikipedia/commons/f/f0/Zebra_standing_alone_crop.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O zebra.jpg https://upload.wikimedia.org/wikipedia/commons/f/f0/Zebra_standing_alone_crop.jpg</code></pre>
<pre><code>--2024-11-07 10:49:10--  https://upload.wikimedia.org/wikipedia/commons/f/f0/Zebra_standing_alone_crop.jpg
Resolving upload.wikimedia.org (upload.wikimedia.org)... 198.35.26.112, 2620:0:863:ed1a::2:b
Connecting to upload.wikimedia.org (upload.wikimedia.org)|198.35.26.112|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 188036 (184K) [image/jpeg]
Saving to: ‘zebra.jpg’


zebra.jpg             0%[                    ]       0  --.-KB/s               
zebra.jpg           100%[===================&gt;] 183.63K  --.-KB/s    in 0.05s   

2024-11-07 10:49:11 (3.35 MB/s) - ‘zebra.jpg’ saved [188036/188036]</code></pre><pre><code class="language-python">img = image.load_img(&#39;zebra.jpg&#39;, target_size=(224, 224))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = densenet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>WARNING:tensorflow:6 out of the last 7 calls to &lt;function TensorFlowTrainer.make_predict_function.&lt;locals&gt;.one_step_on_data_distributed at 0x7d034faa2c20&gt; triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.


[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m18s[0m 18s/step
[[(&#39;n02391049&#39;, &#39;zebra&#39;, 0.9299189), (&#39;n01518878&#39;, &#39;ostrich&#39;, 0.020074163), (&#39;n02423022&#39;, &#39;gazelle&#39;, 0.012079946), (&#39;n02397096&#39;, &#39;warthog&#39;, 0.00449013), (&#39;n02422106&#39;, &#39;hartebeest&#39;, 0.003128522)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/2d4f303d-7a14-4512-87fc-b524fb2efd79/image.png" alt=""></p>
<h2 id="nasnet">NasNet</h2>
<ul>
<li>강화학습을 활용한 자동화된 아키텍처 탐색(Neural Architecture Search (NAS))으로 최적의 신경망 구조를 효율적으로 설계</li>
<li>NASNet에서 찾아낸 최적의 구조는 사람의 설계로는 발견하기 어려운 방식으로 조합되어 있어, 기존의 수작업 모델보다 효율적이면서도 성능이 뛰어난 경우가 많음</li>
<li>높은 성능과 효율성을 동시에 만족하는 최적의 아키텍처 발견</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.applications.nasnet import NASNetLarge, preprocess_input, decode_predictions

nasnet = NASNetLarge(include_top=True, weights=&#39;imagenet&#39;,
                  input_tensor=None, input_shape=None,
                  pooling=None, classes=1000)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/nasnet/NASNet-large.h5
[1m359748576/359748576[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 0us/step</code></pre><ul>
<li>notebook.jpg (<a href="https://cdn.pixabay.com/photo/2016/07/11/03/35/macbook-1508998_1280.jpg">https://cdn.pixabay.com/photo/2016/07/11/03/35/macbook-1508998_1280.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O notebook.jpg https://cdn.pixabay.com/photo/2016/07/11/03/35/macbook-1508998_1280.jpg</code></pre>
<pre><code>--2024-11-07 11:01:16--  https://cdn.pixabay.com/photo/2016/07/11/03/35/macbook-1508998_1280.jpg
Resolving cdn.pixabay.com (cdn.pixabay.com)... 172.64.147.160, 104.18.40.96, 2606:4700:4400::ac40:93a0, ...
Connecting to cdn.pixabay.com (cdn.pixabay.com)|172.64.147.160|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 270631 (264K) [binary/octet-stream]
Saving to: ‘notebook.jpg’

notebook.jpg        100%[===================&gt;] 264.29K  --.-KB/s    in 0.07s   

2024-11-07 11:01:16 (3.55 MB/s) - ‘notebook.jpg’ saved [270631/270631]</code></pre><pre><code class="language-python">img = image.load_img(&#39;notebook.jpg&#39;, target_size=(331, 331))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = nasnet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m21s[0m 21s/step
[[(&#39;n03832673&#39;, &#39;notebook&#39;, 0.79398525), (&#39;n03642806&#39;, &#39;laptop&#39;, 0.048131146), (&#39;n04264628&#39;, &#39;space_bar&#39;, 0.0341737), (&#39;n03085013&#39;, &#39;computer_keyboard&#39;, 0.020013861), (&#39;n03777754&#39;, &#39;modem&#39;, 0.011280097)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/a5d60eea-4ad5-4947-9a0d-3575bc5bf8c7/image.png" alt=""></p>
<h2 id="efficientnet">EfficientNet</h2>
<ul>
<li>EfficientNet은 EfficientNet-B0라는 기준 모델을 시작점으로 함. 이 모델은 신경망 아키텍처 검색(NAS)을 통해 찾은 효율적인 구조임</li>
<li>NAS를 통해 발견된 B0 아키텍처는 이미 효율성과 성능을 극대화한 구조로, 이를 바탕으로 다른 버전(B1~B7)들이 파생됨</li>
<li>NASNet처럼 각 모델의 모든 세부 구조를 탐색하지 않고, B0 모델을 시작으로 Compound Scaling 공식을 적용해 네트워크의 깊이, 너비, 해상도를 균형 있게 확장함</li>
<li>Compound Scaling은 깊(레이어수), 너비(채널수/필터수), 해상도(입력이미지크기)를 여러 시도와 탐색을 통해 찾은 비율로 함께 확장해 모델의 성능과 자원 효율성을 동시에 최적화하는 기법임</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/a5cc62f7-0b44-4302-aeeb-f722e47925c8/image.png" alt=""></p>
<pre><code class="language-python">from tensorflow.keras.applications.efficientnet import EfficientNetB1, preprocess_input, decode_predictions

efficientnet = EfficientNetB1(include_top=True, weights=&#39;imagenet&#39;,
                  input_tensor=None, input_shape=None,
                  pooling=None, classes=1000)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/keras-applications/efficientnetb1.h5
[1m32148312/32148312[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step</code></pre><ul>
<li>plane.jpg (<a href="https://upload.wikimedia.org/wikipedia/commons/1/12/Plane-in-flight.jpg">https://upload.wikimedia.org/wikipedia/commons/1/12/Plane-in-flight.jpg</a>)</li>
</ul>
<pre><code class="language-python">!wget -O plane.jpg https://upload.wikimedia.org/wikipedia/commons/1/12/Plane-in-flight.jpg</code></pre>
<pre><code>--2024-11-07 11:17:00--  https://upload.wikimedia.org/wikipedia/commons/1/12/Plane-in-flight.jpg
Resolving upload.wikimedia.org (upload.wikimedia.org)... 198.35.26.112, 2620:0:863:ed1a::2:b
Connecting to upload.wikimedia.org (upload.wikimedia.org)|198.35.26.112|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 487351 (476K) [image/jpeg]
Saving to: ‘plane.jpg’

plane.jpg           100%[===================&gt;] 475.93K  --.-KB/s    in 0.1s    

2024-11-07 11:17:00 (3.75 MB/s) - ‘plane.jpg’ saved [487351/487351]</code></pre><pre><code class="language-python">img = image.load_img(&#39;plane.jpg&#39;, target_size=(240, 240))
plt.imshow(img)

x = image.img_to_array(img)
x = preprocess_input(np.expand_dims(x,0))

preds = efficientnet.predict(x)
print(decode_predictions(preds))</code></pre>
<pre><code>[1m1/1[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m11s[0m 11s/step
[[(&#39;n02690373&#39;, &#39;airliner&#39;, 0.7460463), (&#39;n04592741&#39;, &#39;wing&#39;, 0.079652555), (&#39;n04552348&#39;, &#39;warplane&#39;, 0.05106398), (&#39;n04266014&#39;, &#39;space_shuttle&#39;, 0.016655603), (&#39;n01494475&#39;, &#39;hammerhead&#39;, 0.00594431)]]</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/b0fa716e-593b-4fbe-8e5c-1bd58a219c6e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[영상처리 기초 - 2. OPENCV 이미지처리]]></title>
            <link>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-2.-OPENCV-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-2.-OPENCV-%EC%9D%B4%EB%AF%B8%EC%A7%80%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 02 Mar 2025 14:52:40 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-python"># 본 자료는 이수안 교수님(https://suanlab.com/)의 자료를 기반으로 수정 및 보완하여 제작되었습니다.
# 제작자 : 김민수(rlaalstn1504@naver.com)</code></pre>
<h1 id="opencv-이미지처리">OPENCV 이미지처리</h1>
<pre><code class="language-python">import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
from google.colab.patches import cv2_imshow</code></pre>
<h2 id="도형-그리기">도형 그리기</h2>
<ul>
<li><p>다양한 도형을 그릴 수 있음</p>
</li>
<li><p>도형을 그리는 좌표가 해당 범위를 넘어가면 이미지에 표현되지 않음</p>
</li>
</ul>
<pre><code class="language-python">img = np.zeros((512,512,3), np.uint8)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f8652aaf-343d-49aa-9b68-a916f84aa144/image.png" alt=""></p>
<h2 id="line-그리기">Line 그리기</h2>
<ul>
<li><p><code>cv.line()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : 그림을 그릴 이미지 파일</p>
</li>
<li><p><code>start</code> : 시작 좌표</p>
</li>
<li><p><code>end</code> : 종료 좌표</p>
</li>
<li><p><code>color</code> : BGR형태의 Color (ex; (255, 0, 0) -&gt; Blue)</p>
</li>
<li><p><code>thickness</code> (int) : 선의 두께. pixel</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">img = cv2.line(img, (0,0), (511,511), (255,0,0), 5) # 시작, 끝, 컬러(BGR), 두께</code></pre>
<pre><code class="language-python">plt.imshow(img) # 왜 붉은 색 선일까? -&gt; B에 그렸으나 R로 인식
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/fc4351a9-b340-4dad-a150-f675f69a7f7b/image.png" alt=""></p>
<h2 id="rectangle-그리기">rectangle 그리기</h2>
<ul>
<li><p><code>cv2.rectangle()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : 그림을 그릴 이미지</p>
</li>
<li><p><code>start</code> : 시작 좌표</p>
</li>
<li><p><code>end</code> : 종료 좌표</p>
</li>
<li><p><code>color</code> : BGR형태의 Color(ex; (255, 0, 0) -&gt; Blue)</p>
</li>
<li><p><code>thickness</code> (int) : 선의 두께. pixel</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">img = cv2.rectangle(img, (0,0), (250,350), (0,255,0), 3)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/5992c480-4f76-4acb-840a-e283f2afb620/image.png" alt=""></p>
<h2 id="circle-그리기">Circle 그리기</h2>
<ul>
<li><p><code>cv2.circle()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : 그림을 그릴 이미지</p>
</li>
<li><p><code>center</code> : 원의 중심 좌표(x, y)</p>
</li>
<li><p><code>radian</code> : 반지름</p>
</li>
<li><p><code>color</code> : BGR형태의 Color</p>
</li>
<li><p><code>thickness</code> : 선의 두께, -1 이면 원 안쪽을 채움</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">img = cv2.circle(img, (447, 63), 63, (0,0,255), -1)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p> <img src="https://velog.velcdn.com/images/s_s/post/8dca00e0-7372-4469-80ed-5a56ad9caa58/image.png" alt=""></p>
<pre><code class="language-python">img = cv2.circle(img, (63,447), 63,  (0,255,255), 2)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/24aab999-29d5-4643-a7a0-f460450a6868/image.png" alt=""></p>
<h2 id="ellipse타원-그리기">ellipse(타원) 그리기</h2>
<ul>
<li><p><code>cv2.ellipse()</code></p>
<ul>
<li><p><code>Parameters</code>  </p>
<ul>
<li><p><code>img</code> : image</p>
</li>
<li><p><code>center</code> : 타원의 중심</p>
</li>
<li><p><code>axes</code> : 중심에서 가장 큰 거리와 작은 거리</p>
</li>
<li><p><code>angle</code> : 타원의 기울기 각</p>
</li>
<li><p><code>startAngle</code> : 타원의 시작 각도</p>
</li>
<li><p><code>endAngle</code> : 타원이 끝나는 각도</p>
</li>
<li><p><code>color</code> : 타원의 색</p>
</li>
<li><p><code>thickness</code> : 선 두께. -1이면 안쪽을 채움</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 타원을 이미지에 그리기 (회색 타원)
# 중심 좌표: (255, 255), 축 길이: (100, 50), 회전 각도: 0, 호의 시작각도: 10도, 끝각도: 255도, 채우기: -1
img = cv2.ellipse(img,  (255,255), (100,50), 0,  10, 255,  -1)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d9244e12-f4aa-416b-a46f-0a64fd8b2436/image.png" alt=""></p>
<pre><code class="language-python"># 타원을 이미지에 그리기 (흰색 타원)
# 중심 좌표: (255, 255), 축 길이: (150, 50), 회전 각도: 45, 호의 시작각도: 0도, 끝각도: 360도, 두께: 2
img = cv2.ellipse(img,  (255,255), (150,50), 45, 0, 360, (255,255,255), 2)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f3b65393-9722-4211-a46b-acf554dbc9d2/image.png" alt=""></p>
<pre><code class="language-python"># 타원을 이미지에 그리기 (파란색 부분 타원)
# 중심 좌표: (255, 255), 축 길이: (150, 10), 회전 각도: 135, 호의 시작각도: 0도, 끝각도: 270도, 두께: 2
img  = cv2.ellipse(img,  (255,255), (150,10), 135, 0, 270, (0,0,255), 2)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/aa858ae0-a55d-414f-a828-e8a765e21a99/image.png" alt=""></p>
<h2 id="polygon-그리기">Polygon 그리기</h2>
<ul>
<li><p><code>cv2.polylines()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : image</p>
</li>
<li><p><code>pts</code> (array) : 연결할 꼭지점 좌표</p>
</li>
<li><p><code>isClosed</code> : 닫힌 도형 여부</p>
</li>
<li><p><code>color</code> : Color</p>
</li>
<li><p><code>thickness</code> : 선 두께</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>이미지에 표현하기 위해 점 좌표를 3차원 행렬로 변환.</p>
<ul>
<li>변환이전과 이후의 행렬 갯수는 동일해야함.</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 폴리라인(다각형)을 그리기
# 점들의 좌표 정의 (정수형 배열로 변환)
pts = np.array([[10,5], [20,30], [70,20], [50,10]], np.int32)
print(pts.shape)</code></pre>
<pre><code>(4, 2)</code></pre><pre><code class="language-python"># 점들의 형태를 (n, 2, 1)에서 (n, 1, 2)로 변환
pts = pts.reshape((-1,2,1)) # 3차원 행렬로 변환하기 위해
print(pts.shape)
# 폴리라인 그리기, 닫힌 형태(True), 색상: 오렌지, 두께: 5
img = cv2.polylines(img, [pts], True, (0, 155, 255), 5)</code></pre>
<pre><code>(4, 2, 1)</code></pre><pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/fac5c387-61e7-46f1-898a-44638aa8ec64/image.png" alt=""></p>
<pre><code class="language-python"># 또 다른 다각형 정의 및 폴리라인 그리기
pts2 = np.array([[150,5], [200,30], [100,70], [50,20]], np.int32)
print(pts2.shape)</code></pre>
<pre><code>(4, 2)</code></pre><pre><code class="language-python"># (n, 2, 1) 형태에서 (n, 1, 2)로 변환
pts2 = pts2.reshape((-1,1,2))
print(pts2.shape)
# 폴리라인 그리기, 닫힌 형태(True), 색상: 연한 보라색, 두께: 4
img = cv2.polylines(img, [pts2], True, (172, 200, 255), 4)</code></pre>
<pre><code>(4, 1, 2)</code></pre><pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/43ab59f4-6ff2-4cca-9175-0a6d8a0e07c6/image.png" alt=""></p>
<h2 id="text-추가하기">Text 추가하기</h2>
<ul>
<li><p><code>cv2.putText()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : image</p>
</li>
<li><p><code>text</code> : 표시할 문자열</p>
</li>
<li><p><code>org</code> : 문자열이 표시될 위치. 문자열의 <strong>bottom-left corner 점</strong></p>
</li>
<li><p><code>font</code> : font type. CV2.FONT_XXX</p>
</li>
<li><p><code>fontSacle</code> : Font Size</p>
</li>
<li><p><code>color</code> : fond color</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 텍스트 추가
# 텍스트: &#39;OpenCV&#39;, 좌표: (10, 500), 폰트: SIMPLEX, 크기: 4, 색상: 흰색, 두께: 3
img = cv2.putText(img, &#39;OpenCV&#39;, (10, 500), cv2.FONT_HERSHEY_SIMPLEX, 4, (255,255,255), 3)</code></pre>
<pre><code class="language-python">plt.imshow(img)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/17a618e0-feb6-410b-b7ca-f4e881245535/image.png" alt=""></p>
<h1 id="문제--별-그리기">문제 : 별 그리기</h1>
<pre><code class="language-python">img = np.zeros((540,540), np.uint8)
plt.imshow(img, cmap=&#39;gray&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/090c9814-860a-4a75-a58b-89f1f38c8fcf/image.png" alt=""></p>
<p>정답 :</p>
<h1 id="문제-정답-별-그리기-8개의-교차선으로-구성">문제 정답: 별 그리기 (8개의 교차선으로 구성)</h1>
<p>img = cv2.line(img, (0, 0), (270, 540), 255, 4)  # 왼쪽 위 → 중앙 아래
img = cv2.line(img, (0, 0), (540, 270), 255, 4)  # 왼쪽 위 → 중앙 오른쪽
img = cv2.line(img, (540, 0), (0, 270), 255, 4)  # 오른쪽 위 → 중앙 왼쪽
img = cv2.line(img, (540, 0), (270, 540), 255, 4)  # 오른쪽 위 → 중앙 아래
img = cv2.line(img, (0, 540), (270, 0), 255, 4)  # 왼쪽 아래 → 중앙 위
img = cv2.line(img, (0, 540), (540, 270), 255, 4)  # 왼쪽 아래 → 중앙 오른쪽
img = cv2.line(img, (540, 540), (0, 270), 255, 4)  # 오른쪽 아래 → 중앙 왼쪽
img = cv2.line(img, (540, 540), (270, 0), 255, 4)  # 오른쪽 아래 → 중앙 위</p>
<h2 id="이미지-처리-image-processing">이미지 처리 (Image Processing)</h2>
<ul>
<li><p>필요에 따라 적절한 처리</p>
</li>
<li><p><code>resize()</code>, <code>flip()</code>, <code>getAffineTransform()</code>, <code>warpAffine()</code> 등 다양한 메서드 존재</p>
</li>
<li><p>코드 출처 : <a href="https://opencv-python.readthedocs.io/en/latest/index.html">https://opencv-python.readthedocs.io/en/latest/index.html</a></p>
</li>
</ul>
<pre><code class="language-python"># 이미지 다운로드 주소 : https://upload.wikimedia.org/wikipedia/ko/2/24/Lenna.png
files.upload()
image = cv2.imread(&#39;Lenna.png&#39;)
print(image.shape)
cv2_imshow(image)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/69eb3491-32f9-4353-a5cd-b80ec78dc4bf/image.jpg" alt=""></p>
<h2 id="resize">Resize</h2>
<ul>
<li><p><code>cv2.resize()</code></p>
<ul>
<li><p>사이즈가 변하면 pixel사이의 값을 결정을 해야함</p>
</li>
<li><p>보간법(Interpolation method)</p>
<ul>
<li><p>사이즈를 줄일 때 : <code>cv2.INTER_AREA</code></p>
</li>
<li><p>사이즈를 크게 할 때 : <code>cv2.INTER_CUBIC</code> , <code>cv2.INTER_LINEAR</code></p>
</li>
</ul>
</li>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>img</code> : Image</p>
</li>
<li><p><code>dsize</code> : Manual Size. 가로, 세로 형태의 tuple(ex; (100,200))</p>
</li>
<li><p><code>fx</code> : 가로 사이즈의 배수. 2배로 크게하려면 2. 반으로 줄이려면 0.5</p>
</li>
<li><p><code>fy</code> : 세로 사이즈의 배수</p>
</li>
<li><p><code>interpolation</code> : 보간법</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">cv2_imshow(image)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8dceb008-dd80-41de-aa6f-ae3fc93159ff/image.jpg" alt=""></p>
<pre><code class="language-python">&#39;&#39;&#39;
cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]])
여기서 각 인자는 다음을 의미합니다:

src: 크기를 조절하려는 원본 이미지입니다.
dsize: 새로운 이미지의 크기입니다. (width, height) 형식의 튜플로 지정할 수 있습니다.
dst: 선택적으로, 크기를 조절한 이미지를 저장할 곳을 지정합니다. 이 인자를 생략하면 함수가 새로운 이미지를 반환합니다.
fx: 선택적으로 가로 방향 크기의 배율 요인입니다.
fy: 선택적으로 세로 방향 크기의 배율 요인입니다.
interpolation: 선택적으로 크기 조절에 사용할 보간법을 지정합니다. 기본값은 cv2.INTER_LINEAR로, 선형 보간법을 사용합니다.
다른 옵션으로는 cv2.INTER_NEAREST, cv2.INTER_AREA, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4 등이 있습니다.
&#39;&#39;&#39;
height, width = image.shape[:2]
print(height, width)

shrink = cv2.resize(image, (0,0), fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA) # 새 이미지의 크기를 직접 지정하지 않은 것
print(shrink.shape)
expand1 = cv2.resize(image, (width*2, height*2), interpolation=cv2.INTER_CUBIC)  # 크기 2배 확대
print(expand1.shape)
expand2 = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)  # 크기 2배 확대 (명시적 크기 미지정)
print(expand2.shape)</code></pre>
<pre><code>220 220
(110, 110, 3)
(440, 440, 3)
(440, 440, 3)</code></pre><pre><code class="language-python">cv2_imshow(shrink)  # 축소된 이미지 표시
cv2_imshow(expand1)  # 확대된 이미지 표시 (방식 1)
cv2_imshow(expand2)  # 확대된 이미지 표시 (방식 2)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/53c6ad92-0429-4eb2-87a8-18cf4624c79d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/f6e02a07-ae65-4950-abb7-aceb6e6b4fcd/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/2c129f1e-b808-4f6a-aac7-67254049ceb8/image.jpg" alt=""></p>
<h2 id="translation">Translation</h2>
<ul>
<li><p>이미지의 위치를 변경</p>
</li>
<li><p><code>cv2.warpAffine()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>src</code> : Image</p>
</li>
<li><p><code>M</code> : 변환 행렬</p>
</li>
<li><p><code>dsize</code> (tuple) : output image size(ex; (width=columns, height=rows)</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">&#39;&#39;&#39;
[1, 0, 10], [0, 1, 20]

1: 축 방향의 스케일링 비율을 나타내며, 이 경우 1이므로 x축 방향의 크기 변화가 없음을 의미합니다.
0: y축으로부터 x축으로의 기울기(회전)를 나타냅니다. 0이므로 이 방향으로의 회전이 없음을 의미합니다.
10: x축 방향으로의 이동(변환)을 나타내며, 이 값은 이미지를 오른쪽으로 10 픽셀 이동시킵니다.

0: x축으로부터 y축으로의 기울기(회전)를 나타냅니다. 0이므로 이 방향으로의 회전이 없음을 의미합니다.
1: y축 방향의 스케일링 비율을 나타내며, 이 경우 1이므로 y축 방향의 크기 변화가 없음을 의미합니다.
20: y축 방향으로의 이동(변환)을 나타내며, 이 값은 이미지를 아래로 20 픽셀 이동시킵니다.
&#39;&#39;&#39;
# 이미지 이동 (Translation)
rows, cols = image.shape[:2]
M = np.float32([[1, 0, 10], [0, 1, 20]])  # 이동 행렬: x축 10 픽셀, y축 20 픽셀 이동
dst = cv2.warpAffine(image, M, (cols, rows))  # 이동 적용
cv2_imshow(dst)  # 이동된 이미지 표시</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/ce993f55-3869-4ed5-bf2f-ad725b35a3fb/image.jpg" alt=""></p>
<h2 id="rotate">Rotate</h2>
<ul>
<li><p>물체를 평면상의 한 점을 중심으로 𝜃 만큼 회전하는 변환</p>
</li>
<li><p>양의 각도는 시계반대방향으로 회전</p>
</li>
<li><p><code>cv2.getRotationMatrix2D()</code></p>
<ul>
<li><p><code>Parameters</code></p>
<ul>
<li><p><code>center</code> : 이미지의 중심 좌표</p>
</li>
<li><p><code>angle</code> : 회전 각도</p>
</li>
<li><p><code>scale</code> : scale factor</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 이미지 회전
rows, cols, _ = image.shape
M = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 1)  # 중심에서 45도 회전
rotated_img = cv2.warpAffine(image, M, (cols, rows))  # 회전 적용
cv2_imshow(rotated_img) # 회전된 이미지 표시</code></pre>
<p> <img src="https://velog.velcdn.com/images/s_s/post/3a32b737-8c00-43d7-8f61-be7fbba6fd4b/image.jpg" alt=""></p>
<h2 id="flip">Flip</h2>
<ul>
<li><p>대칭 변환</p>
<ul>
<li><p>좌우 대칭 (좌우 반전)</p>
</li>
<li><p>상하 대칭 (상하 반전)</p>
</li>
</ul>
</li>
<li><p>입력 영상과 출력 영상의 픽셀이 1:1 매칭이므로 보간법이 필요 없음</p>
</li>
<li><p><code>cv2.flip()</code></p>
<ul>
<li><p>Parameters</p>
<ul>
<li><p><code>src</code> : 입력 영상</p>
</li>
<li><p><code>flipCode</code> : 대칭 방법을 결정하는 flag 인자</p>
<ul>
<li><p>양수이면 좌우 대칭</p>
</li>
<li><p>0이면 상하 대칭</p>
</li>
<li><p>음수이면 상하, 좌우 대칭을 모두 실행</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">result2 = cv2.flip(image, 0)  # 수직 뒤집기
result3 = cv2.flip(image, 1)  # 수평 뒤집기
cv2_imshow(result2)  # 수직 뒤집힌 이미지 표시
cv2_imshow(result3)  # 수평 뒤집힌 이미지 표시</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/3d532ab0-7011-4497-b8fc-2af84361e4fa/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/9370c647-5e31-433b-9420-9d41b169f430/image.png" alt=""></p>
<h2 id="affine-transformation">Affine Transformation</h2>
<ul>
<li><p>기하학적 도형의 크기와 각도를 변경할 수 있지만, 원점 간의 상대적인 위치(선의 평행성과 점들 간의 비율)는 보존</p>
</li>
<li><p>이동, 확대, Scale, 반전까지 포함된 변환</p>
</li>
<li><p><code>cv2.getAffineTransform()</code></p>
<ul>
<li>Affine 변환을 위해서는 3개의 Match가 되는 점이 있으면 변환행렬을 구할 수 있음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/2ac67c87-aed6-453c-a159-c684eef7fc82/image.png" alt=""></p>
<pre><code class="language-python"># 원본 이미지의 크기와 채널 정보 가져오기
# rows: 이미지의 높이(픽셀 수), cols: 너비(픽셀 수), ch: 채널 수 (컬러 이미지라서 3)
rows, cols, ch = image.shape

# Affine 변환을 위한 원본 좌표와 이동 좌표 설정
# 원본 좌표: pts1 (Affine 변환 전의 3개 점)
pts1 = np.float32([[100, 50], [200, 50], [100, 100]])
# 이동 좌표: pts2 (Affine 변환 후의 3개 점)
pts2 = np.float32([[100, 150], [200, 100], [100, 200]])

# Affine 변환에서 사용하는 3개의 점 시각화를 위해 원본 이미지에 원 그리기
# 첫 번째 점 (200, 100) 빨간색 점
cv2.circle(image, (200, 100), 10, (255, 0, 0), -1)  # 색상: 빨강 (BGR 형식), 두께: -1 (채우기)
# 두 번째 점 (400, 100) 초록색 점
cv2.circle(image, (400, 100), 10, (0, 255, 0), -1)  # 색상: 초록 (BGR 형식), 두께: -1 (채우기)
# 세 번째 점 (200, 200) 파란색 점
cv2.circle(image, (200, 200), 10, (0, 0, 255), -1)  # 색상: 파랑 (BGR 형식), 두께: -1 (채우기)

# 원본 좌표(pts1)와 이동 좌표(pts2)를 기반으로 Affine 변환 행렬 계산
# M: 2x3 변환 행렬, 이 행렬은 점들의 위치를 기반으로 전체 이미지를 변환하는 데 사용됨
M = cv2.getAffineTransform(pts1, pts2)

# Affine 변환 적용
# cv2.warpAffine(): 이미지를 변환 행렬(M)을 기반으로 변환
# cols, rows: 결과 이미지의 크기 (원본 이미지와 동일하게 설정)
dst = cv2.warpAffine(image, M, (cols, rows))

# 변환된 이미지(dst) 출력
cv2_imshow(dst)  # Google Colab에서 이미지를 표시하는 함수
# 변환된 이미지의 크기 확인
print(dst.shape)  # 변환 후에도 원본 이미지 크기(높이, 너비, 채널)가 유지됨</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/bdd19fce-309d-48ed-b1d1-859ab3358e2e/image.png" alt=""></p>
<pre><code>(220, 220, 3)</code></pre><pre><code class="language-python"># 문제 1: 이미지 크기 조절
# 목표: 주어진 이미지의 크기를 사용자가 지정한 크기로 조절하는 기능을 구현합니다.
# 설명: 이미지를 200x200 크기로 조절해보세요. 다양한 보간법을 적용하며 결과를 비교합니다.

# 이미지 불러오기
img = cv2.imread(&#39;Lenna.png&#39;)
# 크기 조절

# 이미지 표시
plt.imshow(cv2.cvtColor(resized_img, cv2.COLOR_BGR2RGB))
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/314dc978-0b02-4d89-9f1e-56e9b4790de4/image.png" alt=""></p>
<pre><code class="language-python"># 문제 2: 이미지 뒤집기
# 목표: 주어진 이미지를 수평, 수직으로 뒤집는 기능을 구현합니다.
# 설명: 이미지를 수평, 수직으로 뒤집고 결과를 표시합니다.
# # 수평 뒤집기

# 수직 뒤집기

# 결과 표시
plt.imshow(cv2.cvtColor(flipped_img_hor, cv2.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv2.cvtColor(flipped_img_ver, cv2.COLOR_BGR2RGB))
plt.show()
</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/bf1c8ea1-c660-4693-825c-dc72c373b8af/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/4c954e82-67df-407a-b67e-18e92a6fd615/image.png" alt=""></p>
<pre><code class="language-python"># 문제 3: 이미지 회전
# 목표: 주어진 이미지를 특정 각도로 회전시키는 기능을 구현합니다.
# 설명: 이미지를 45도 회전시키고 결과를 표시합니다.
# 회전을 위한 변환 행렬 생성
rows, cols, _ = img.shape
# 이미지 회전

# 결과 표시
plt.imshow(cv2.cvtColor(rotated_img, cv2.COLOR_BGR2RGB))
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/a9593371-8bfb-4a1b-a951-20891cf7ecb3/image.png" alt=""></p>
<pre><code class="language-python"># 정답
# 문제 1
resized_img = cv2.resize(img, (200, 200), interpolation=cv2.INTER_LINEAR)
# 이미지 표시
plt.imshow(cv2.cvtColor(resized_img, cv2.COLOR_BGR2RGB))
plt.show()
# 문제 2
# # 수평 뒤집기
flipped_img_hor = cv2.flip(img, 1)
# 수직 뒤집기
flipped_img_ver = cv2.flip(img, 0)
# 결과 표시
plt.imshow(cv2.cvtColor(flipped_img_hor, cv2.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv2.cvtColor(flipped_img_ver, cv2.COLOR_BGR2RGB))
plt.show()

# 문제 3
rows, cols, _ = img.shape
M = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 1)
# 이미지 회전
rotated_img = cv2.warpAffine(img, M, (cols, rows))
# 결과 표시
plt.imshow(cv2.cvtColor(rotated_img, cv2.COLOR_BGR2RGB))
plt.show()</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[영상처리 기초 - 1.컴퓨터비전 기초]]></title>
            <link>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-%EC%BB%B4%ED%93%A8%ED%84%B0%EB%B9%84%EC%A0%84-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@s_s/%EC%98%81%EC%83%81%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%B4%88-%EC%BB%B4%ED%93%A8%ED%84%B0%EB%B9%84%EC%A0%84-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Sun, 02 Mar 2025 14:13:11 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-python"># 본 자료는 이수안 교수님(https://suanlab.com/)의 자료를 기반으로 수정 및 보완하여 제작되었습니다.
# 제작자 : 김민수(rlaalstn1504@naver.com)</code></pre>
<h1 id="컴퓨터-비전-computer-vision-이란">컴퓨터 비전 (Computer Vision) 이란?</h1>
<h2 id="1-개요">1. 개요</h2>
<ul>
<li>컴퓨터 비전은 컴퓨터가 디지털 이미지를 이해하고 분석하여 의미 있는 정보를 추출하도록 하는 학문 및 기술 분야입니다.</li>
<li>인간의 시각 시스템처럼 이미지나 비디오를 처리하고 그 안의 객체, 패턴, 또는 행동을 이해하려는 목표를 가지고 있습니다.</li>
<li>컴퓨터 비전은 인공지능(AI)의 하위 분야로, 주로 기계 학습과 딥러닝 기술에 의존합니다.</li>
</ul>
<h2 id="2-배경">2. 배경</h2>
<ol>
<li>배경</li>
</ol>
<ul>
<li>컴퓨터 비전은 초기에는 간단한 영상 처리 기술(예: 에지 검출, 히스토그램 분석)을 중심으로 발전했습니다.</li>
<li>2000년대 이후 딥러닝 기술의 발전과 함께 컴퓨터 비전은 비약적으로 발전했습니다.</li>
<li>특히, 합성곱 신경망(CNN) 구조가 컴퓨터 비전 분야에서 큰 성과를 거두면서 기존 방법론을 대체하거나 보완하고 있습니다.</li>
</ul>
<h2 id="3-주요-사용-사례">3. 주요 사용 사례</h2>
<ol>
<li><p>이미지 분류(Image Classification):</p>
<ul>
<li>이미지를 입력받아 해당 클래스(예: 고양이, 강아지)를 분류.</li>
<li>예: 구글 포토, 이미지 검색.</li>
</ul>
</li>
<li><p>객체 탐지(Object Detection)</p>
<ul>
<li>이미지 내 특정 객체의 위치와 종류를 식별.</li>
<li>예: 자율 주행 차량의 보행자 탐지.</li>
</ul>
</li>
<li><p>이미지 분할(Image Segmentation)</p>
<ul>
<li>이미지를 픽셀 단위로 분석하여 영역별로 구분.</li>
<li>예: 의료 영상 분석, 위성 이미지 처리.</li>
</ul>
</li>
<li><p>얼굴 인식(Face Recognition)</p>
<ul>
<li>얼굴 특징을 기반으로 개인을 식별.</li>
<li>예: 스마트폰의 얼굴 잠금 해제.</li>
</ul>
</li>
<li><p>동작 인식(Action Recognition)</p>
<ul>
<li>비디오에서 사람의 동작을 인식.</li>
<li>예: 스포츠 비디오 분석, 감시 시스템.</li>
</ul>
</li>
<li><p>이미지 생성(Image Generation)</p>
<ul>
<li>GAN(생성적 적대 신경망) 또는 VAE를 사용하여 새로운 이미지를 생성.</li>
<li>예: 딥페이크, 스타일 변환.</li>
</ul>
</li>
</ol>
<h1 id="컴퓨터-이미지에-대한-이해">컴퓨터 이미지에 대한 이해</h1>
<h2 id="1-컴퓨터-이미지-이해와-컴퓨터-비전의-연계">1. 컴퓨터 이미지 이해와 컴퓨터 비전의 연계</h2>
<ul>
<li><p>컴퓨터 이미지는 컴퓨터 비전의 핵심 기초 개념으로, 이미지 데이터를 어떻게 표현하고 처리하는지 이해하는 것이 중요합니다.</p>
</li>
<li><p>컴퓨터에서 이미지는 기본적으로 <strong>RGB 값</strong>으로 표현됩니다. RGB는 빨강(R), 녹색(G), 파랑(B) 세 가지 색의 조합으로 구성되며, 각 색의 강도는 0부터 255 사이의 정수 값으로 표현됩니다.</p>
</li>
<li><p>예시:</p>
<ul>
<li><code>(0, 0, 0)</code>: 검정색</li>
<li><code>(255, 255, 255)</code>: 흰색</li>
<li><code>(255, 0, 0)</code>: 빨간색</li>
<li><code>(0, 255, 0)</code>: 녹색</li>
<li><code>(0, 0, 255)</code>: 파란색</li>
<li><code>(255, 0, 255)</code>: 핑크색</li>
</ul>
</li>
<li><p>이러한 값들이 조합되어 이미지를 구성하며, 이미지의 크기에 따라 데이터의 양이 결정됩니다. 예를 들어, 1000 x 1000 크기의 이미지는 픽셀당 RGB(3개의 값)를 저장하므로 <strong>1000 x 1000 x 3 = 3,000,000개의 값</strong>이 필요합니다.</p>
</li>
<li><p>color: 3차원 (true color 라고도 불림)</p>
<img src="https://upload.wikimedia.org/wikipedia/commons/8/83/RGB_Cube_Show_lowgamma_cutout_b.png" width="500">
<img src="https://upload.wikimedia.org/wikipedia/commons/b/b0/RGB_color_space_animated_view.gif">

<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Beyoglu_4671_tricolor.png/400px-Beyoglu_4671_tricolor.png" width="600">

<p>[이미지 출처] <a href="https://en.wikipedia.org/wiki/RGB_color_space">https://en.wikipedia.org/wiki/RGB_color_space</a><br><a href="https://commons.wikimedia.org/wiki/File:RGB_color_space_animated_view.gif">https://commons.wikimedia.org/wiki/File:RGB_color_space_animated_view.gif</a></p>
</li>
<li><p>gray scale: 2차원</p>
<ul>
<li>0~255의 값을 통해 밝기를 표현</li>
<li>0으로 갈수록 어두워지고, 255로 갈수록 밝아짐</li>
</ul>
<img src="https://miro.medium.com/max/1400/1*euc4RxnNo78LFEGrb-QZ7w.jpeg">

<p>[이미지 출처] <a href="https://medium.com/@himnickson/converting-rgb-image-to-the-grayscale-image-in-java-9e1edc5bd6e7">https://medium.com/@himnickson/converting-rgb-image-to-the-grayscale-image-in-java-9e1edc5bd6e7</a></p>
<img src="https://ai.stanford.edu/~syyeung/cvweb/Pictures1/imagematrix.png">

<p>[이미지 출처] <a href="https://ai.stanford.edu/~syyeung/cvweb/tutorial1.html">https://ai.stanford.edu/~syyeung/cvweb/tutorial1.html</a></p>
</li>
</ul>
<h2 id="2-이미지-파일-포맷과-압축">2. 이미지 파일 포맷과 압축</h2>
<ul>
<li><p>이미지 데이터는 효율적인 저장과 전송을 위해 압축되어 사용됩니다. JPEG, PNG, GIF와 같은 파일 포맷이 대표적입니다.</p>
</li>
<li><p>주요 포맷:</p>
<ol>
<li><strong>JPEG (Joint Photographic Experts Group)</strong>:</li>
</ol>
<ul>
<li>손실 압축 방식으로 파일 크기를 줄이는 데 효과적입니다.</li>
<li>세부 정보를 일부 손실하지만 인간의 시각으로 인지하기 어려운 수준으로 처리됩니다.</li>
</ul>
<ol start="2">
<li><strong>PNG (Portable Network Graphics)</strong>:</li>
</ol>
<ul>
<li>무손실 압축 방식으로, 이미지 품질이 그대로 유지됩니다.</li>
<li>투명도(알파 채널)를 지원하여 그래픽 처리에 유용합니다.</li>
</ul>
<ol start="3">
<li><strong>GIF (Graphics Interchange Format)</strong>:</li>
</ol>
<ul>
<li><p>애니메이션을 지원하며, 256가지 색상으로 제한되는 간단한 압축 방식입니다.</p>
</li>
<li><p>이러한 압축 방식은 원래의 RGB 데이터를 효율적으로 압축하지만, 손실 압축 방식(JPEG 등)은 원본 데이터와 일부 차이가 발생할 수 있습니다. 압축된 데이터는 화면에 출력되기 전에 해당 포맷의 압축 해제 방식을 통해 RGB 값으로 복구됩니다. 하지만 손실된 데이터는 복구되지 않습니다.</p>
</li>
</ul>
</li>
</ul>
<h2 id="3-이미지에서-특징feature을-학습하는-방법">3. 이미지에서 특징(feature)을 학습하는 방법</h2>
<ul>
<li><p>컴퓨터 비전에서는 이미지 데이터를 입력으로 받아 다양한 작업을 수행합니다. 이를 위해, 이미지의 <strong>RGB 값</strong>이 컴퓨터가 이해할 수 있는 형태로 변환됩니다.</p>
</li>
<li><p>이미지 데이터를 처리하는 과정:</p>
<ol>
<li><p><strong>데이터 전처리</strong>:</p>
<ul>
<li>이미지를 <strong>픽셀 값(RGB)</strong>로 변환.</li>
<li>정규화(Normalization): RGB 값을 0 ~ 255 범위에서 0 ~ 1 사이의 값으로 변환하여 딥러닝 모델에 적합하게 만듦.</li>
<li>이미지 크기 조정(Resizing): 네트워크에 맞는 입력 크기로 변환.</li>
</ul>
</li>
<li><p><strong>특징 추출</strong>:</p>
<ul>
<li>딥러닝 모델(특히 CNN)에서 이미지를 처리하며 특징(feature)을 학습.</li>
<li>예: 에지(Edge), 텍스처(Texture), 패턴(Pattern) 등.</li>
<li>기존 방법(딥러닝 이전의 방법): 수작업으로 특징을 추출하거나, 필터와 머신러닝 알고리즘을 활용.</li>
<li>에지 검출 필터: 예를 들어, Sobel 필터, Canny Edge Detector 등을 사용하여 이미지의 윤곽선을 추출.</li>
<li>추출된 특징을 사용하여 SVM(Support Vector Machine)이나 KNN(K-Nearest Neighbors) 같은 머신러닝 모델로 분류 작업 수행.</li>
</ul>
</li>
<li><p><strong>모델 적용</strong>:</p>
</li>
</ol>
<ul>
<li>이미지 분류(Classification), 객체 탐지(Object Detection), 이미지 분할(Segmentation) 등 다양한 컴퓨터 비전 작업 수행.</li>
</ul>
</li>
<li><p>딥러닝 모델(특히 CNN)은 입력 이미지를 픽셀의 RGB 값으로 받아서 <strong>공간적 정보(Spatial Information)</strong>를 학습합니다. 예를 들어, <strong>이미지 분류 모델</strong>은 각 픽셀의 RGB 조합과 이들 간의 관계를 학습하여 이미지를 특정 클래스(예: 고양이, 강아지 등)로 분류합니다.</p>
</li>
</ul>
<h2 id="4-결론">4. 결론</h2>
<ul>
<li><p>컴퓨터 이미지는 기본적으로 RGB 데이터의 조합으로 구성되며, 다양한 포맷으로 저장 및 전송됩니다. 컴퓨터 비전에서는 이러한 데이터를 처리하여 이미지의 패턴과 의미를 추출하고, 이를 기반으로 분류, 탐지, 생성 등 복잡한 작업을 수행합니다.</p>
</li>
<li><p>기존의 필터나 머신러닝 기반 접근법은 컴퓨터 비전 작업에서 효율적인 방법을 제공했지만, 딥러닝 기반 접근법은 데이터를 기반으로 직접 특징을 학습할 수 있어 더 복잡하고 정교한 작업을 수행할 수 있습니다.</p>
</li>
<li><p>따라서 컴퓨터 이미지의 기초 개념과 함께 전통적인 특징 추출 기법과 딥러닝 방법을 모두 이해하는 것이 컴퓨터 비전의 성공적인 활용에 필수적입니다.</p>
</li>
</ul>
<h1 id="파이썬에서-이미지-처리하기">파이썬에서 이미지 처리하기</h1>
<h2 id="주요-라이브러리-소개">주요 라이브러리 소개</h2>
<h2 id="opencv">OpenCV</h2>
<ul>
<li><p>소개:</p>
<ol>
<li>OpenCV(Open Source Computer Vision)는 실시간 컴퓨터 비전 및 이미지 처리에 중점을 둔 오픈소스 라이브러리입니다.</li>
<li>인텔(Intel)에서 개발하였으며, 현재는 전 세계적으로 가장 널리 사용되는 이미지 처리 도구 중 하나입니다.</li>
<li>C++, Python, Java 등 여러 언어를 지원하며, 다양한 운영 체제에서 사용할 수 있습니다.</li>
<li>TensorFlow, PyTorch, Caffe 등의 딥러닝 프레임워크와도 통합이 가능합니다.</li>
<li>주요 기능</li>
</ol>
<ul>
<li><p>이미지 처리: 필터링, 히스토그램 계산, 에지 검출, 기하학적 변환 등</p>
</li>
<li><p>비디오 분석: 객체 추적, 모션 추정</p>
</li>
<li><p>머신러닝: 사전 정의된 알고리즘(SVM, KNN 등) 제공</p>
</li>
<li><p>딥러닝 지원: 사전 학습된 모델과 ONNX 포맷을 활용한 딥러닝 작업 가능</p>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/OpenCV_Logo_with_text.png/440px-OpenCV_Logo_with_text.png" width="300">

<p>[이미지 출처] <a href="https://ko.wikipedia.org/wiki/OpenCV">https://ko.wikipedia.org/wiki/OpenCV</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="pillow">Pillow</h2>
<ul>
<li><p>소개:</p>
<ol>
<li>Pillow는 <strong>PIL(Python Imaging Library)</strong>의 공식적인 확장판으로, Python에서 이미지 작업을 간단하고 직관적으로 수행할 수 있도록 설계된 라이브러리입니다.</li>
<li>다양한 이미지 파일 포맷(JPEG, PNG, BMP, GIF 등)을 지원하며, 이미지 열기, 저장, 변환, 편집 등이 가능합니다.</li>
<li>주요 기능</li>
</ol>
<ul>
<li><p>이미지 읽기 및 저장: 다양한 포맷의 이미지를 읽고 저장할 수 있습니다.</p>
</li>
<li><p>파일 형식 변환(예: PNG → JPEG 등)이 가능합니다.</p>
</li>
<li><p>이미지 변환: 크기 조정(Resizing), 회전(Rotation), 색상 변경(Grayscale 변환 등)과 같은 작업을 쉽게 수행할 수 있습니다.</p>
</li>
<li><p>이미지 필터링:블러(Blur), 샤프닝(Sharpening) 등 기본적인 필터링 기능 제공.</p>
</li>
<li><p>텍스트 추가: 이미지를 배경으로 텍스트를 추가하는 기능 지원.</p>
<img src="https://media.licdn.com/dms/image/v2/C4E12AQG7yNwFhR49lQ/article-cover_image-shrink_720_1280/article-cover_image-shrink_720_1280/0/1615721904733?e=2147483647&v=beta&t=nsuBrJ9hHok5huBhRjBkdCcjTOYxP046VwX19RWqdYI" width="300">

<p>[이미지 출처] <a href="https://www.linkedin.com/pulse/pil-library-functions-rahul-saxena">https://www.linkedin.com/pulse/pil-library-functions-rahul-saxena</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="이미지와-넘파이-numpy">이미지와 넘파이 (NumPy)</h2>
<ul>
<li>이미지는 컴퓨터에서 픽셀 값의 배열로 표현되며, 이러한 배열은 Python의 NumPy를 사용하여 쉽게 처리할 수 있습니다.</li>
<li>컬러 이미지: 3차원 배열(높이, 너비, 채널)로 표현됩니다. 각 픽셀은 [R, G, B] 값을 가지며, 일반적으로 0~255 범위의 정수로 표현됩니다.</li>
<li>흑백 이미지: 2차원 배열로 표현되며, 각 픽셀은 0~255의 밝기 값으로 나타냅니다.</li>
<li>NumPy는 이미지 데이터를 효율적으로 처리하기 위한 강력한 도구를 제공하며, OpenCV와 함께 사용하면 효과적입니다.</li>
</ul>
<ul>
<li>코드 참조 : <a href="https://opencv-python.readthedocs.io/en/latest/index.html">https://opencv-python.readthedocs.io/en/latest/index.html</a></li>
</ul>
<h2 id="이미지-읽기와-출력">이미지 읽기와 출력</h2>
<ul>
<li>이미지 파일을 읽고 화면에 출력하기 위해 다양한 라이브러리를 사용할 수 있습니다. 각각의 특징과 적합한 환경을 살펴봅니다.</li>
</ul>
<ol>
<li><strong>Pillow (PIL)</strong>와 Matplotlib<ul>
<li><strong>구글 코랩(Google Colab)</strong>이나 Jupyter Notebook 같은 노트북 환경에서 적합합니다.</li>
<li>간단한 이미지 읽기와 시각화에 적합하며, 사용법이 직관적입니다.</li>
<li>Matplotlib의 imshow()를 통해 이미지를 출력할 수 있습니다.</li>
</ul>
</li>
<li>OpenCV<ul>
<li>주로 Python 스크립트 기반 환경에서 사용됩니다.</li>
<li>OpenCV의 cv2.imread() 함수로 이미지를 읽고, cv2.imshow()로 이미지를 출력할 수 있습니다.</li>
<li>다만, Google Colab에서는 OpenCV의 기본 이미지 출력 함수인 cv2.imshow()가 지원되지 않기 때문에, Colab에서는 from google.colab.patches import cv2_imshow를 사용해야 합니다.</li>
<li>OpenCV는 이미지뿐 아니라 동영상 처리도 지원하지만, Colab에서는 동영상 관련 작업은 제약이 있습니다.</li>
</ul>
</li>
</ol>
<pre><code class="language-python">import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from PIL import Image
import requests
from io import BytesIO</code></pre>
<pre><code class="language-python"># url과 Image객체를 활용하여 이미지 읽기
url = &#39;https://cdn.pixabay.com/photo/2018/10/01/09/21/pets-3715733_960_720.jpg&#39;

response = requests.get(url)
pic = Image.open(BytesIO(response.content))</code></pre>
<h2 id="이미지-출력-1">이미지 출력 1</h2>
<ul>
<li>이미지 확인 (<code>pillow</code>)<ul>
<li>Image 객체</li>
</ul>
</li>
</ul>
<pre><code class="language-python">pic</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/5dc1436a-3921-494a-98c0-c8d921cc5f8b/image.png" alt=""></p>
<ul>
<li>타입(type) 확인</li>
</ul>
<pre><code class="language-python">print(type(pic))</code></pre>
<pre><code>&lt;class &#39;PIL.JpegImagePlugin.JpegImageFile&#39;&gt;</code></pre><ul>
<li>PIL 이미지를 array형으로 형변환<ul>
<li><code>np.asarray()</code></li>
</ul>
</li>
</ul>
<pre><code class="language-python">pic_arr = np.asarray(pic)
print(type(pic_arr)) # 타입(type) 확인
print(pic_arr.shape) # HWC
print(pic_arr)</code></pre>
<pre><code class="language-python">plt.imshow(pic_arr)</code></pre>
<pre><code>&lt;matplotlib.image.AxesImage at 0x7fa57d35f2e0&gt;</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/856fe202-6e0c-4273-a7d9-7fb62e14fb37/image.png" alt=""></p>
<h2 id="이미지-출력-2">이미지 출력 2</h2>
<ul>
<li>R, G, B 에 따라 이미지 확인</li>
</ul>
<pre><code class="language-python">pic_copy = pic_arr.copy()
print(pic_copy[:,:,0])
print(pic_copy[:,:,0].shape)</code></pre>
<pre><code>[[214 214 215 ... 235 234 233]
 [214 215 215 ... 235 233 233]
 [215 215 215 ... 235 233 233]
 ...
 [211 211 211 ... 229 229 229]
 [211 211 211 ... 229 229 230]
 [211 211 211 ... 231 231 232]]
(640, 960)</code></pre><ul>
<li>채널 순서 (R G B : 0 1 2)<ul>
<li><code>R channel</code></li>
</ul>
</li>
</ul>
<pre><code class="language-python"># cmap(컬러맵)은 1차원 데이터(단일 채널 이미지) 또는 2차원 배열에 색상을 매핑하기 위해 사용
plt.figure(figsize=(15,5))
plt.subplot(1,3,1)
plt.imshow(pic_copy[:,:,0], cmap=&#39;gray&#39;)
plt.subplot(1,3,2)
plt.imshow(pic_copy[:,:,1], cmap=&#39;gray&#39;)
plt.subplot(1,3,3)
plt.imshow(pic_copy[:,:,2], cmap=&#39;gray&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0473ad9d-14ef-445e-97f8-35963e95cf9a/image.png" alt=""></p>
<p>채널별 분포 확인</p>
<pre><code class="language-python"># R channel 분포 확인
pic_red = pic_arr.copy()
pic_red[:,:,1] = 0
pic_red[:,:,2] = 0

# G channel 분포 확인
pic_green = pic_arr.copy()
pic_green[:, :, 0] = 0
pic_green[:, :, 2] = 0

# B channel 분포 확인
pic_blue = pic_arr.copy()
pic_blue[:, :, 0] = 0
pic_blue[:, :, 1] = 0</code></pre>
<pre><code class="language-python">plt.figure(figsize=(20,5))
plt.subplot(1,4,1)
plt.imshow(pic_arr)
plt.subplot(1,4,2)
plt.imshow(pic_red)
plt.subplot(1,4,3)
plt.imshow(pic_green)
plt.subplot(1,4,4)
plt.imshow(pic_blue)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/baa38478-3d0e-4ce1-a966-6342ea55ec4d/image.png" alt=""></p>
<h2 id="이미지-출력-3">이미지 출력 3</h2>
<ul>
<li><p><code>OpenCV</code></p>
</li>
<li><p><code>from google.colab.patches import cv2_imshow</code></p>
<ul>
<li>원래는 <code>cv2.imshow</code></li>
</ul>
</li>
</ul>
<ul>
<li>아래 이미지는 어색하게 보이는데 이는 OpenCV와 matplotlib의 색공간 순서가 다르기 때문에 생기는 문제임</li>
<li><code>matploblib</code> : R G B</li>
<li><code>OpenCV</code> : B G R</li>
</ul>
<pre><code class="language-python">from google.colab.patches import cv2_imshow
cv2_imshow(pic_arr)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d7caa47a-805e-45aa-aa00-8a5924e51cc2/image.png" alt=""></p>
<h2 id="opencv-의-채널-순서">OpenCV 의 채널 순서</h2>
<ul>
<li><p>OpenCV를 통해 영상(이미지)을 다룰 때의 채널 순서는 B G R</p>
</li>
<li><p>matplotlib은 R G B 순서</p>
</li>
<li><p><code>cv2.cvtColor(image arrary, 변경할 색공간)</code> 함수를 이용하여 색공간을 변경해주면 정상적으로 시각화가 가능함</p>
</li>
<li><p>변경할 색공간은 여러 가지가 있음</p>
<ul>
<li><p><code>cv2.COLOR_BGR2RGB</code></p>
</li>
<li><p><code>cv2.COLOR_RGB2GRAY</code></p>
</li>
<li><p><code>cv2.COLOR_GRAY2RGB</code></p>
</li>
<li><p>...</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">image = cv2.cvtColor(pic_arr, cv2.COLOR_RGB2BGR)
cv2_imshow(image)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d616f8ed-1a73-4c11-b2b4-6466b7bea154/image.png" alt=""></p>
<ul>
<li><p>image, pic_arr 비교</p>
<ul>
<li>0 1 2 인덱스 중 0, 2가 바뀐 것을 알 수 있음</li>
</ul>
</li>
</ul>
<pre><code class="language-python">print(image[0][0])</code></pre>
<pre><code>[140 123 214]</code></pre><pre><code class="language-python">print(pic_arr[0][0])</code></pre>
<pre><code>[214 123 140]</code></pre><ul>
<li>(참고) array[ : ,  : ,  : : -1] 을 통해서도 인덱스 순서를 바꿀 수 있음</li>
</ul>
<pre><code class="language-python">temp_arr = pic_arr[:,:, ::-1]</code></pre>
<pre><code class="language-python">print(pic_arr[0][0])</code></pre>
<pre><code>[214 123 140]</code></pre><pre><code class="language-python">print(temp_arr[0][0])</code></pre>
<pre><code>[140 123 214]</code></pre><ul>
<li>전체 배열 확인</li>
</ul>
<pre><code class="language-python">print(temp_arr)</code></pre>
<pre><code class="language-python">print(pic_arr)</code></pre>
<h2 id="이미지-읽기-2">이미지 읽기 2</h2>
<ul>
<li><p><code>cv2.imread()</code></p>
<ul>
<li><p>path, 이미지 파일의 flag값을 인자로 넣어줌</p>
<ul>
<li><p><code>cv2.IMREAD_COLOR</code><br>이미지 파일을 Color로 읽어들이고, 투명한 부분은 무시되며, Default 값</p>
</li>
<li><p><code>cv2.IMREAD_GRAYSCALE</code><br>이미지를 Grayscale로 읽음. 실제 이미지 처리시 중간단계로 많이 사용</p>
</li>
<li><p><code>cv2.IMREAD_UNCHANGED</code><br>이미지 파일을 alpha channel (투명도)까지 포함하여 읽어 들임</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>(주의)</strong></p>
<ul>
<li><code>cv2.imread()</code>는 잘못된 경로로 읽어도 <code>NoneType</code>으로 들어갈 뿐, <u>오류를 발생하지 않음</u></li>
</ul>
</li>
<li><p>이미지 다운로드 후 colab에 업로드</p>
<ul>
<li><a href="https://upload.wikimedia.org/wikipedia/ko/thumb/2/24/Lenna.png/220px-Lenna.png">https://upload.wikimedia.org/wikipedia/ko/thumb/2/24/Lenna.png/220px-Lenna.png</a></li>
</ul>
<br>

<img src="https://upload.wikimedia.org/wikipedia/ko/thumb/2/24/Lenna.png/440px-Lenna.png" width="300">



</li>
</ul>
<pre><code class="language-python">from google.colab import files
files.upload()</code></pre>
<pre><code class="language-python">image = cv2.imread(&#39;Lenna.png&#39;, cv2.IMREAD_UNCHANGED)</code></pre>
<pre><code class="language-python">print(type(image))</code></pre>
<pre><code>&lt;class &#39;numpy.ndarray&#39;&gt;</code></pre><pre><code class="language-python">cv2_imshow(image)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/2c25abb3-2344-421d-9d78-f16faf9edb09/image.png" alt=""></p>
<pre><code class="language-python">plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(image)

plt.subplot(1,2,2)
image_temp = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
plt.imshow(image_temp)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0e4a8a16-dcf5-4598-9bdc-c02270505f57/image.png" alt=""></p>
<ul>
<li><code>grayscale</code>로 읽기</li>
</ul>
<pre><code class="language-python">img_gray = cv2.imread(&#39;Lenna.png&#39;,cv2.IMREAD_GRAYSCALE)</code></pre>
<pre><code class="language-python">print(img_gray.shape)</code></pre>
<pre><code>(220, 220)</code></pre><pre><code class="language-python">cv2_imshow(img_gray)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/1b643e46-4d21-4694-8a78-c86eac24f833/image.png" alt=""></p>
<pre><code class="language-python">plt.figure(figsize=(20,5))
plt.subplot(1,4,1)
plt.imshow(img_gray)
plt.subplot(1,4,2)
plt.imshow(img_gray, cmap=&#39;gray&#39;)
plt.subplot(1,4,3)
plt.imshow(img_gray, cmap=&#39;magma&#39;)
plt.subplot(1,4,4)
plt.imshow(img_gray, cmap=&#39;coolwarm&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/fb05d00f-31f1-4c8e-80da-75714e14a276/image.png" alt=""></p>
<h2 id="이미지-저장">이미지 저장</h2>
<ul>
<li><code>cv2.imwrite()</code><ul>
<li>경로, 이미지 배열을 인자로 받음</li>
</ul>
</li>
</ul>
<pre><code class="language-python">random_image = np.random.randint(0,256, size=(200,200,3))
print(random_image.shape )</code></pre>
<pre><code>(200, 200, 3)</code></pre><ul>
<li>이미지 저장을 하면 True반환</li>
</ul>
<pre><code class="language-python">cv2.imwrite(&#39;./random_image.png&#39;, random_image)</code></pre>
<pre><code>True</code></pre><ul>
<li>없는 이미지를 읽어도 type이 NoneType으로 들어갈 뿐 에러를 발생하지 않음</li>
</ul>
<pre><code class="language-python">no_image = cv2.imread(&#39;no_image.png&#39;)
```![업로드중..](blob:https://velog.io/e83a72b1-5ec4-4d98-9d17-eadf54c1c5ca)



```python
type(no_image)</code></pre>
<pre><code>NoneType</code></pre><pre><code class="language-python">my_image = cv2.imread(&#39;random_image.png&#39;)</code></pre>
<pre><code class="language-python">print(type(my_image))
print(my_image.shape)</code></pre>
<pre><code>&lt;class &#39;numpy.ndarray&#39;&gt;
(200, 200, 3)</code></pre><pre><code class="language-python">cv2_imshow(my_image)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/08349092-8c72-4df0-8cdc-53c30e2651c5/image.png" alt=""></p>
<h2 id="색-공간의-종류-참고">색 공간의 종류 (참고)</h2>
<ul>
<li><p>RGB</p>
<ul>
<li>컬러 표현을 빛의 3원색인 (Red, Green, Blue)으로 서로 다른 비율을 통해 색 표현</li>
</ul>
</li>
<li><p>CMYK</p>
<ul>
<li><p>Cyan, Magenta, Yellow, Black를 기본으로 하여 주로 컬러 프린터나 인쇄시에 사용</p>
</li>
<li><p>각 성분들의 감산에 의해 컬러 구현</p>
</li>
</ul>
</li>
<li><p>YUV</p>
<ul>
<li><p>Y축은 밝기 성분을 U,V 두축을 이용하여 색상을 표현</p>
</li>
<li><p>U축은 파란색에서 밝기 성분을 뺀 값, V축은 빨간색에서 밝기 성분을 뺀 값</p>
</li>
<li><p>아날로그 컬러신호 변환에 주로 사용.  (U = B - Y) , (V = R - Y)</p>
</li>
</ul>
</li>
<li><p>YCbCr</p>
<ul>
<li><p>Digital TV에서 사용하는 색공간</p>
</li>
<li><p>YPbPr이라는 아날로그 신호의 색공간을 디지털화한 것</p>
</li>
<li><p>YPbPr은 아날로그 컴포넌트 비디오에서 사용</p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>이미지  </p>
<ul>
<li>이미지 사용 (다른 이미지 사용 가능)</li>
</ul>
<img src="https://cdn.pixabay.com/photo/2017/09/25/13/12/dog-2785074_960_720.jpg" width="300">

<p><sub>[이미지 출처] <a href="https://pixabay.com/ko/photos/%EA%B0%95%EC%95%84%EC%A7%80-%EA%B0%9C-%EC%95%A0%EC%99%84-%EB%8F%99%EB%AC%BC-%EB%8F%99%EB%AC%BC-1903313/">https://pixabay.com/ko/photos/%EA%B0%95%EC%95%84%EC%A7%80-%EA%B0%9C-%EC%95%A0%EC%99%84-%EB%8F%99%EB%AC%BC-%EB%8F%99%EB%AC%BC-1903313/</a></sub></p>
</li>
</ul>
<pre><code class="language-python">files.upload()</code></pre>
<pre><code class="language-python">origin_image = cv2.imread(&#39;dog.jpg&#39;)</code></pre>
<ul>
<li><p>OpenCV와 matplotlib의 색공간 순서가 다르기 때문에 생기는 문제</p>
<ul>
<li><p><code>matploblib</code> : R G B</p>
</li>
<li><p><code>OpenCV</code> : B G R</p>
</li>
</ul>
</li>
</ul>
<pre><code class="language-python">plt.imshow(origin_image)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0424c162-0ea7-4198-b92b-ffdd49a665d2/image.png" alt=""></p>
<pre><code class="language-python">img_rgb = cv2.cvtColor(origin_image, cv2.COLOR_BGR2RGB)</code></pre>
<pre><code class="language-python">plt.imshow(img_rgb)</code></pre>
<pre><code>&lt;matplotlib.image.AxesImage at 0x7fa56f3ddc60&gt;</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/ee26767c-f668-4079-b0bc-5834c7cb6269/image.png" alt=""></p>
<h2 id="hsl-color-space">HSL Color Space</h2>
<ul>
<li><p>색상(Hue), 채도(Saturation), 밝기(Lightness)로 색을 표현하는 방식</p>
</li>
<li><p>HSV와 동일하지만 <strong>밝기</strong> 요소의 차이</p>
</li>
<li><p>HSV와 더불어 사람이 실제로 color를 인지하는 방식과 유사</p>
<br>

<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQkTOZcWYRd82Z8HCBRhA2zWiOH3LVQ7AhLXA&s">

<p><sub>[이미지 출처] <a href="https://rgbtohex.page/hsl">https://rgbtohex.page/hsl</a></sub></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 6 - 최신 강화학습 사례]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-6-%EC%B5%9C%EC%8B%A0-%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EC%82%AC%EB%A1%80</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-6-%EC%B5%9C%EC%8B%A0-%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EC%82%AC%EB%A1%80</guid>
            <pubDate>Sun, 02 Mar 2025 13:44:32 GMT</pubDate>
            <description><![CDATA[<h1 id="딥러닝에서-강화학습rl이-사용되는-최신-사례">딥러닝에서 강화학습(RL)이 사용되는 최신 사례</h1>
<h2 id="1대규모-언어-모델과-rlhf">1.대규모 언어 모델과 RLHF</h2>
<ul>
<li><strong>RLHF (Reinforcement Learning with Human Feedback)</strong>:<ul>
<li><strong>사용 사례</strong>:<ul>
<li>ChatGPT (OpenAI): 인간 피드백을 활용해 언어 모델의 응답 품질 향상.</li>
<li>InstructGPT: 사용자의 지침에 맞는 응답 생성.</li>
</ul>
</li>
<li><strong>작동 원리</strong>:<ul>
<li>인간 평가자가 제공하는 피드백(보상 모델)을 기반으로, 언어 모델이 보상을 극대화하는 방향으로 미세 조정.</li>
<li>예: 사용자 선호도가 높은 응답을 더 많이 생성하도록 학습.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="chatgpt에-적용된-rhlf-reinforcement-learning-with-human-feedback">ChatGPT에 적용된 RHLF (Reinforcement Learning with Human Feedback)</h2>
<p>ChatGPT는 인간의 피드백을 활용해 보다 자연스럽고 유용한 답변을 생성하기 위해 RHLF 과정을 거칩니다. 이 과정은 크게 3단계로 나뉩니다.</p>
<h3 id="1단계-supervised-fine-tuning-sft"><strong>1단계: Supervised Fine-Tuning (SFT)</strong></h3>
<ul>
<li><strong>목적</strong>: 사전 학습된 언어 모델(Pre-trained LM)이 인간이 의도한 정책(policy)을 따르도록 Fine-Tuning합니다.</li>
<li><strong>방법</strong>:  <ul>
<li>인간 Labeler(또는 Trainer)가 고품질의 소량 데이터셋을 선별합니다.</li>
<li>이 데이터를 사용하여 사전 학습된 모델을 Fine-Tuning합니다.</li>
</ul>
</li>
<li><strong>결과</strong>: 인간의 의도를 반영한 초기 모델(SFT 모델)이 생성됩니다.</li>
</ul>
<h3 id="2단계-reward-model-학습-mimic-human-preferences"><strong>2단계: Reward Model 학습 (Mimic Human Preferences)</strong></h3>
<ul>
<li><strong>목적</strong>: 모델의 응답 품질을 평가하는 보상 모델(Reward Model)을 학습합니다.</li>
<li><strong>방법</strong>:  <ul>
<li>SFT 모델이 생성한 여러 답변 후보를 수집합니다.</li>
<li>인간 Labeler가 각 답변에 대해 랭킹을 매기고, 어떤 답변이 더 나은지 점수화한 데이터를 만듭니다.</li>
<li>이 랭킹 데이터를 사용해 Reward Model을 학습시킵니다.</li>
</ul>
</li>
<li><strong>결과</strong>: 인간 선호도를 학습한 보상 모델이 생성됩니다.</li>
</ul>
<h3 id="3단계-강화학습-ppo를-이용한-fine-tuning"><strong>3단계: 강화학습 (PPO를 이용한 Fine-Tuning)</strong></h3>
<ul>
<li><strong>목적</strong>: SFT 모델을 사용자 피드백과 Reward Model을 활용해 강화합니다.</li>
<li><strong>방법</strong>:  <ul>
<li>사용자 입력 데이터를 SFT 모델에 제공합니다.</li>
<li>SFT 모델이 생성한 답변을 Reward Model과 상호작용하여 평가합니다.</li>
<li>Proximal Policy Optimization(PPO, 정책을 직접 최적화하는 방식) 알고리즘을 사용해 SFT 모델을 업데이트합니다.</li>
</ul>
</li>
<li><strong>결과</strong>: 사용자 피드백을 반영한 최적화된 ChatGPT 모델이 완성됩니다.</li>
</ul>
<h3 id="요약"><strong>요약</strong></h3>
<p>ChatGPT는 아래의 단계를 통해 인간 피드백을 최대한 반영한 모델로 발전합니다:</p>
<ol>
<li><strong>SFT</strong>: 인간의 의도를 반영한 초기 모델 학습.</li>
<li><strong>Reward Model</strong>: 인간 선호도를 반영한 평가 모델 학습.</li>
<li><strong>PPO 강화학습</strong>: 사용자 입력과 상호작용하며 최적화.</li>
</ol>
<p>이를 통해 ChatGPT는 더 높은 품질의 응답을 생성할 수 있습니다.</p>
<h2 id="2게임-분야에서의-강화학습-활용-사례와-기술">2.게임 분야에서의 강화학습 활용 사례와 기술</h2>
<ul>
<li>강화학습은 게임 분야에서 인공지능(AI) 에이전트의 성능을 혁신적으로 향상시키며, 다양한 방식으로 활용되고 있습니다. 아래는 주요 사례와 적용된 기술들을 정리한 내용입니다.</li>
</ul>
<h3 id="1-alphago와-alphago-zero"><strong>1. AlphaGo와 AlphaGo Zero</strong></h3>
<ul>
<li><strong>AlphaGo</strong>: DeepMind에서 개발한 AI로, 강화학습을 통해 바둑 게임을 마스터했습니다.  </li>
<li><strong>AlphaGo Zero</strong>: 인간의 기보 데이터를 사용하지 않고, <strong>자가 학습(self-play)</strong>만으로 최고 수준의 바둑 실력을 달성했습니다.</li>
</ul>
<h4 id="주요-기술">주요 기술:</h4>
<ul>
<li>심층 신경망을 사용하여 <strong>게임 상태를 평가</strong>하고 <strong>다음 수를 예측</strong>.</li>
<li>자가 대국(self-play)을 통한 강화학습.</li>
<li><strong>몬테카를로 트리 탐색(MCTS)</strong> 없이 단일 신경망과 간단한 트리 탐색만으로 구현.</li>
</ul>
<img src="https://res.cloudinary.com/lesswrong-2-0/image/upload/v1677275363/mirroredImages/FF8i6SLfKb4g7C4EL/ppa1lup4f8ydkxsgumxy.gif" width="300">

<h3 id="2-atari-게임-마스터"><strong>2. Atari 게임 마스터</strong></h3>
<ul>
<li>Google DeepMind의 <strong>Deep Q-Network (DQN)</strong>은 다양한 <strong>Atari 2600 게임</strong>을 플레이하며, 인간 수준 이상의 성과를 보여주었습니다.</li>
</ul>
<h4 id="주요-기술-1">주요 기술:</h4>
<ul>
<li><strong>컨볼루션 신경망(CNN)</strong>을 사용해 <strong>raw 픽셀 입력</strong>으로부터 Q-함수를 학습.</li>
<li><strong>경험 리플레이(Experience Replay)</strong>를 통한 안정적인 학습.</li>
<li>동일한 아키텍처와 하이퍼파라미터를 여러 게임에 적용하여 일반화된 성능을 달성.</li>
</ul>
<h3 id="강화학습의-게임-분야-적용-의의"><strong>강화학습의 게임 분야 적용 의의</strong></h3>
<p>이와 같은 사례들은 강화학습이 게임 AI 개발에 혁신을 가져오고 있음을 보여줍니다. 특히, 다음과 같은 측면에서 강화학습의 잠재력이 입증되고 있습니다:</p>
<ul>
<li><strong>복잡한 전략 수립</strong>: 바둑이나 MOBA 게임과 같은 복잡한 환경에서의 의사결정.</li>
<li><strong>실시간 의사결정</strong>: 플레이어와의 실시간 상호작용.</li>
<li><strong>적응형 게임플레이</strong>: 플레이어 행동에 맞춘 게임 난이도 조절.</li>
</ul>
<p>강화학습은 게임 AI의 성능과 플레이 경험을 향상시키는 데 중요한 역할을 하고 있습니다.</p>
<h2 id="3로봇">3.로봇</h2>
<ul>
<li><p>로봇들은 수동으로 프로그래밍되는 대신 자율적으로 움직임을 학습했을 때 성능이 크게 향상되었음</p>
</li>
<li><p>또한 걷기와 축구뿐만 아니라 더 복잡한 운동 기술도 습득가능했음</p>
</li>
<li><p>로봇의 정교한 움직임을 위해 강화학습이 앞으로도 많이 활용될 것으로 미래 전망됨</p>
<img src="https://i0.wp.com/www.sciencenews.org/wp-content/uploads/2024/05/052124_mh_ai-movement_feat.jpg?resize=1030%2C580&ssl=1" width="500">

<p><a href="https://www.sciencenews.org/article/reinforcement-learn-ai-humanoid-robots">https://www.sciencenews.org/article/reinforcement-learn-ai-humanoid-robots</a></p>
</li>
</ul>
<h2 id="4강화학습의-단점-및-한계점">4.강화학습의 단점 및 한계점</h2>
<ul>
<li>보상이 드물게 주어지는 환경에서의 학습이 어려움</li>
<li>먼 미래에 주어지는 불확실한 보상과 가까운 미래의 큰 보상 사이 딜레마</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 5 - DQN]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-5-DQN</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-5-DQN</guid>
            <pubDate>Sun, 02 Mar 2025 13:42:06 GMT</pubDate>
            <description><![CDATA[<h3 id="딥러닝과-강화학습의-융합">딥러닝과 강화학습의 융합</h3>
<ol>
<li><p><strong>강화학습(RL)</strong>: 에이전트가 환경과 상호작용하며 최적의 행동을 학습하는 과정.</p>
<ul>
<li>핵심 요소:<ul>
<li><strong>상태(State)</strong>: 현재 환경의 상태.</li>
<li><strong>행동(Action)</strong>: 에이전트가 취할 수 있는 행동.</li>
<li><strong>보상(Reward)</strong>: 행동의 결과로 환경이 에이전트에 제공하는 피드백.</li>
<li><strong>정책(Policy)</strong>: 상태에 따라 행동을 결정하는 함수.</li>
</ul>
</li>
<li>목표: 보상을 최대로 만드는 정책 학습.</li>
</ul>
</li>
<li><p><strong>딥러닝(DL)</strong>: 심층 신경망을 사용하여 복잡한 패턴이나 함수를 모델링.</p>
<ul>
<li>강화학습의 비선형 함수 근사를 위해 딥러닝이 사용됨.</li>
</ul>
</li>
<li><p><strong>심층 강화학습(DRL)</strong>: 딥러닝과 강화학습의 융합.</p>
<ul>
<li>Q-값, 정책 함수 등을 심층 신경망으로 근사.</li>
<li>복잡한 환경에서도 학습 가능.</li>
</ul>
</li>
</ol>
<h3 id="dqn-deep-q-network-소개">DQN (Deep Q-Network) 소개</h3>
<ul>
<li>전통적인 Q-Learning은 상태 공간이 클 경우 Q-테이블을 저장하기 어렵고, 일반화가 어려움.</li>
<li><strong>DQN</strong>은 심층 신경망(Deep Neural Network)을 사용하여 Q-값을 근사:<ul>
<li>입력: 상태(state).</li>
<li>출력: 행동(action)에 대한 Q-값.</li>
</ul>
</li>
</ul>
<h4 id="주요-특징">주요 특징</h4>
<ol>
<li><p><strong>경험 재생(Experience Replay)</strong>:</p>
<ul>
<li>에이전트가 경험한 데이터를 저장하여 랜덤 샘플링으로 학습.</li>
<li>데이터 간 상관성을 줄이고 학습 효율 향상.</li>
</ul>
</li>
<li><p><strong>타겟 네트워크(Target Network)</strong>:</p>
<ul>
<li>Q-값 업데이트 안정성을 위해 메인 네트워크와 별도로 고정된 타겟 네트워크 사용.</li>
<li>일정 간격으로 타겟 네트워크를 메인 네트워크의 가중치로 갱신.</li>
</ul>
</li>
</ol>
<h2 id="1-q-learning-업데이트-수식"><strong>1. Q-Learning 업데이트 수식</strong></h2>
<p>기존 Q-Learning에서는 다음과 같은 수식을 사용합니다:</p>
<p>$$
Q(s, a) \leftarrow Q(s, a) + \alpha \Big(r + \gamma max_{a&#39;} Q(s&#39;, a&#39;) - Q(s, a) $\Big)
$$</p>
<ul>
<li><strong>$Q(s, a)$</strong>: 상태 $( s $)에서 행동 $( a $)를 했을 때의 Q-값 (예상 보상)</li>
<li><strong>$r $</strong>: 현재 행동 $( a $)를 통해 받은 보상</li>
<li><strong>$\gamma$</strong>: 할인율 (미래 보상을 현재 가치로 반영할 비율)</li>
<li><strong>$\alpha$</strong>: 학습률 (새로운 값과 기존 값의 반영 비율)</li>
<li><strong>$\max_{a&#39;} Q(s&#39;, a&#39;) $</strong>: 다음 상태 $( s&#39; $)에서 가능한 행동 중 가장 큰 Q-값</li>
</ul>
<h2 id="2-dqn의-업데이트-수식"><strong>2. DQN의 업데이트 수식</strong></h2>
<p>DQN의 손실 함수는 &quot;현재 시점에서의 Q값&quot;과 &quot;미래의 기대 보상으로 계산된 목표 Q값&quot; 간의 차이를 줄이는 것을 목표로 합니다. 이를 통해 Q함수가 점점 더 정확하게 미래의 보상을 반영하게 되고, 에이전트가 최적의 행동을 학습할 수 있습니다.</p>
<p>DQN은 심층 신경망(Deep Neural Network)을 사용하여 $Q$값을 근사합니다. 업데이트를 위해 다음 <strong>손실 함수</strong>(Loss Function)를 사용합니다:</p>
<p>$$
\text{Loss} = \Big( r + \gamma \max_{a&#39;} Q_{\text{target}}(s&#39;, a&#39;) - Q_{\text{main}}(s, a) \Big)^2
$$</p>
<ul>
<li><strong>$Q_{\text{main}}(s, a)$</strong>: 메인 네트워크에서 예측한 Q-값</li>
<li><strong>$Q_{\text{target}}(s&#39;, a&#39;)$</strong>: 타겟 네트워크에서 계산한 Q-값 (고정된 값 사용)</li>
<li><strong>$r$</strong>: 현재 보상</li>
<li><strong>$\gamma \max_{a&#39;} Q_{\text{target}}(s&#39;, a&#39;)$</strong>: 미래 보상의 예상치</li>
</ul>
<p>이 손실 함수를 최소화하도록 메인 네트워크가 학습됩니다.</p>
<h2 id="3-경험-재생-experience-replay"><strong>3. 경험 재생 (Experience Replay)</strong></h2>
<p>경험 재생은 DQN의 중요한 구성 요소 중 하나입니다. 이를 통해 신경망 학습의 안정성을 높입니다.</p>
<h3 id="개념-설명"><strong>개념 설명</strong>:</h3>
<ul>
<li><p>에이전트는 환경과 상호작용하면서 경험을 만듭니다. 각 경험은 다음과 같은 형태로 저장됩니다:<br>$(s, a, r, s&#39;, \text{done})$</p>
<ul>
<li><strong>$ s $</strong>: 현재 상태</li>
<li><strong>$ a $</strong>: 행동</li>
<li><strong>$ r $</strong>: 보상</li>
<li><strong>$ s&#39; $</strong>: 다음 상태</li>
<li><strong>$\text{done}$</strong>: 에피소드 종료 여부 (True/False)</li>
</ul>
</li>
<li><p>이러한 경험을 모두 <strong>메모리 버퍼(Replay Buffer)</strong>순차적으로 저장됩니다. 학습 시, 이 버퍼에서 <strong>랜덤 샘플링</strong> 을 통해 데이터를 추출해 신경망을 학습시킵니다.</p>
</li>
</ul>
<h3 id="왜-경험-재생이-필요한가"><strong>왜 경험 재생이 필요한가?</strong></h3>
<ol>
<li><strong>데이터 상관성 제거</strong>:  
에이전트가 연속된 데이터를 사용하면 매우 비슷한 상태-행동 쌍이 반복적으로 등장하여, 모델이 특정 패턴에 편향될 가능성을 높입니다. 랜덤 샘플링을 통해 이를 방지합니다.  </li>
<li><strong>데이터 재사용</strong>:  <ul>
<li>경험 재생은 한 번의 경험을 여러 번 학습에 사용합니다.
이로 인해 새로운 데이터를 계속 생성하지 않아도 효율적으로 학습할 수 있습니다.</li>
<li>이를 통해 더 적은 데이터로도 신경망을 효과적으로 학습시킬 수 있습니다.</li>
</ul>
</li>
</ol>
<h3 id="쉽게-비유하면"><strong>쉽게 비유하면</strong>:</h3>
<ul>
<li>경험 재생은 과거의 학습 기록(노트)을 모아두고 복습하는 것과 같습니다.</li>
<li>즉, 에이전트가 과거 경험을 &quot;기록&quot;해두었다가 중요한 순간에 다시 꺼내 학습하는 방식입니다.</li>
<li>이를 통해 학습 과정에서 데이터가 부족하거나 연속된 데이터의 영향을 최소화할 수 있습니다.</li>
</ul>
<h2 id="4-타겟-네트워크-target-network"><strong>4. 타겟 네트워크 (Target Network)</strong></h2>
<p>타겟 네트워크는 DQN 학습의 <strong>안정성</strong>을 높이는 기술입니다.</p>
<h3 id="동작-원리"><strong>동작 원리</strong>:</h3>
<ul>
<li><p><strong>메인 네트워크</strong>:</p>
<ol>
<li>에이전트가 현재 상태에서 최적의 행동을 선택하도록 학습합니다.</li>
<li>Q-값을 계산해 행동의 가치를 예측합니다.</li>
</ol>
</li>
<li><p><strong>타겟 네트워크</strong>:</p>
<ol>
<li>메인 네트워크의 가중치를 일정 주기마다 복사해 고정된 상태로 유지합니다.</li>
<li>학습 중 목표값(Target Q-value)을 계산하는 데 사용됩니다.</li>
</ol>
</li>
</ul>
<h3 id="왜-타겟-네트워크가-필요한가"><strong>왜 타겟 네트워크가 필요한가?</strong></h3>
<ul>
<li>DQN에서는 목표 Q-값(Target Q-value)을 예측하기 위해 메인 네트워크의 Q-값을 사용합니다. 하지만 이 값이 학습 중 계속 변한다면 목표값 자체가 흔들리며 학습이 불안정해질 수 있습니다.</li>
<li><strong>타겟 네트워크</strong>는 일정 기간 동안 고정된 값을 제공하여 목표 Q-값을 안정적으로 유지하도록 돕습니다.</li>
<li>일정 주기마다 타겟 네트워크를 메인 네트워크로 업데이트하면서 최신 정보를 반영합니다.</li>
</ul>
<h3 id="쉽게-비유하면-1"><strong>쉽게 비유하면</strong>:</h3>
<p>타겟 네트워크는 <strong>참고서</strong>와 같습니다. 참고서는 일정 시간 동안 바뀌지 않으므로 학습 목표가 흔들리지 않습니다. 대신 시간이 지나면 최신 정보를 반영해 갱신됩니다.</p>
<h2 id="5-전체-구조-정리"><strong>5. 전체 구조 정리</strong></h2>
<p>DQN의 학습 과정은 다음과 같습니다:</p>
<ol>
<li><strong>환경과 상호작용</strong>하며 경험$( (s, a, r, s&#39;,\text{done})$)를 저장.</li>
<li>메모리 버퍼에서 <strong>랜덤 샘플링</strong>을 통해 학습 데이터를 추출.</li>
<li>메인 네트워크에서 현재 상태의 Q-값$( Q_{\text{main}}(s, a)$)를 예측.</li>
<li>타겟 네트워크에서 목표 Q-값$(r +\gamma \max_{a&#39;} Q_{\text{target}}(s&#39;, a&#39;))$를 계산.</li>
<li>손실 함수(Loss)를 계산하고 메인 네트워크를 <strong>업데이트</strong>.</li>
<li>일정 주기마다 타겟 네트워크를 메인 네트워크로 <strong>동기화</strong>.</li>
</ol>
<img src="https://blog.kakaocdn.net/dn/cw2t8w/btrDU3Srd1K/Ds7UlI3vY9qEhYqNtXSqi0/img.jpg" width="500">

<h2 id="dqn-실습">DQN 실습</h2>
<p>간단한 OpenAI Gym 환경에서 DQN 모델을 구현하여 학습하는 과정을 실습합니다.</p>
<ul>
<li>환경: <code>CartPole-v1</code>.</li>
<li>목표: 막대가 쓰러지지 않고 균형을 유지하도록 에이전트를 학습.</li>
<li>주요 구성 요소:<ol>
<li>경험 재생(Experience Replay)을 위한 메모리 버퍼.</li>
<li>메인 네트워크와 타겟 네트워크.</li>
<li>DQN 학습 루프.</li>
</ol>
</li>
<li>종료 조건 :<ol>
<li>막대의 각도가 일정 한계를 벗어나는 경우</li>
<li>정해진 일정 시간을 초과하는 경우</li>
</ol>
</li>
</ul>
<pre><code class="language-python"> # 필요한 라이브러리 임포트
import numpy as np  # 수학적 계산 및 배열 처리
import tensorflow as tf  # 딥러닝 프레임워크
from tensorflow.keras import Sequential  # 순차 모델
from tensorflow.keras.layers import Dense  # 신경망 층
from collections import deque  # 경험 재생 버퍼 구현을 위한 큐
import gym  # 강화학습 환경 제공 라이브러리

# CartPole-v1 환경을 생성하고 초기화
# render_mode=&quot;human&quot;을 통해 환경 시각화
env = gym.make(&quot;CartPole-v1&quot;, render_mode=&quot;human&quot;)
state = env.reset()  # 초기 상태 가져오기

# 상태 공간의 크기와 행동 공간의 크기 정의
state_size = env.observation_space.shape[0]  # 상태의 차원 (카트의 위치, 속도, 막대의 각도, 각속도)
action_size = env.action_space.n  # 가능한 행동의 수 (왼쪽, 오른쪽)

# 경험 재생(Experience Replay)을 위한 버퍼 클래스
class ReplayBuffer:
    def __init__(self, max_size=50000):
        # 버퍼를 deque로 생성 (최대 크기 50000)
        self.buffer = deque(maxlen=max_size)

    def add(self, experience):
        # 새로운 경험 (state, action, reward, next_state, done)을 버퍼에 추가
        self.buffer.append(experience)

    def sample(self, batch_size):
        # 버퍼에서 무작위로 batch_size개의 샘플을 추출
        indices = np.random.choice(len(self.buffer), batch_size, replace=False)
        return [self.buffer[idx] for idx in indices]

    def size(self):
        # 현재 버퍼의 크기를 반환
        return len(self.buffer)

# Q값을 예측할 신경망 모델을 생성하는 함수
def build_model():
    # 순차 모델 생성
    model = Sequential([
        Dense(24, input_dim=state_size, activation=&#39;relu&#39;),  # 첫 번째 은닉층 (입력: 상태 크기)
        Dense(24, activation=&#39;relu&#39;),  # 두 번째 은닉층
        Dense(action_size, activation=&#39;linear&#39;)  # 출력층 (출력: 각 행동의 Q값)
    ])
    # 모델 컴파일 (Adam 옵티마이저, 손실 함수: MSE)
    model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss=&#39;mse&#39;)
    return model

# DQN 에이전트 클래스
class DQNAgent:
    def __init__(self):
        # 주 신경망 (Main Network) 생성
        self.main_model = build_model()
        # 타겟 신경망 (Target Network) 생성
        self.target_model = build_model()
        # 타겟 신경망의 가중치를 주 신경망과 동일하게 초기화
        self.target_model.set_weights(self.main_model.get_weights())
        # 경험 재생 버퍼 초기화
        self.replay_buffer = ReplayBuffer()
        # 하이퍼파라미터 설정
        self.gamma = 0.99  # 할인 계수 (미래 보상의 중요도)
        self.epsilon = 1.0  # 탐험률 초기값
        self.epsilon_decay = 0.995  # 탐험률 감소 비율
        self.epsilon_min = 0.01  # 최소 탐험률
        self.batch_size = 64  # 학습 배치 크기

    def update_target_network(self):
        # 타겟 신경망의 가중치를 주 신경망의 가중치로 업데이트
        self.target_model.set_weights(self.main_model.get_weights())

    def select_action(self, state):
        # 입실론-그리디 정책에 따라 행동 선택
        if np.random.rand() &lt;= self.epsilon:
            # 무작위 행동 선택 (탐험)
            return env.action_space.sample()
        # 주 신경망을 통해 Q값 예측
        q_values = self.main_model.predict(state)
        # 가장 큰 Q값을 가진 행동 선택 (활용)
        return np.argmax(q_values[0])

    def train(self):
        # 경험 재생 버퍼에서 배치를 샘플링하여 학습
        if self.replay_buffer.size() &lt; self.batch_size:
            return  # 버퍼 크기가 배치 크기보다 작으면 학습하지 않음

        # 배치 샘플링
        batch = self.replay_buffer.sample(self.batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)
        states = np.array(states).squeeze(axis=1)  # 현재 상태
        next_states = np.array(next_states).squeeze(axis=1)  # 다음 상태

        # 현재 상태에 대한 Q값 예측
        target_qs = self.main_model.predict(states)
        # 다음 상태에 대한 Q값 예측 (타겟 네트워크 사용)
        next_qs = self.target_model.predict(next_states)

        # Q-Learning 업데이트 규칙 적용
        for i in range(self.batch_size):
            if dones[i]:  # 종료 상태에서는 보상만 반영
                target_qs[i][actions[i]] = rewards[i]
            else:  # 비종료 상태에서는 보상 + 할인된 미래 보상 반영
                target_qs[i][actions[i]] = rewards[i] + self.gamma * np.max(next_qs[i])

        # 주 신경망 학습
        self.main_model.fit(states, target_qs, epochs=1, verbose=0, batch_size=32)

# DQN 에이전트 생성
agent = DQNAgent()
episodes = 500  # 학습할 에피소드 수

# 학습 루프
for episode in range(episodes):
    state = env.reset()  # 환경 초기화 및 상태 가져오기
    state = state[0] if isinstance(state, tuple) else state  # 상태가 튜플이면 첫 번째 요소 사용
    state = np.reshape(state, [1, state_size])  # 상태를 2D 배열로 변환
    total_reward = 0
    done = False

    while not done:
        # 행동 선택
        action = agent.select_action(state)

        # 환경에서 한 스텝 진행
        step_result = env.step(action)
        if len(step_result) == 4:  # Gym 반환값 처리
            next_state, reward, done, info = step_result
        elif len(step_result) == 5:  # 일부 버전에서 반환값 추가 처리
            next_state, reward, done, truncated, info = step_result
            done = done or truncated  # truncated를 종료 조건으로 처리
        else:
            raise ValueError(f&quot;Unexpected step result length: {len(step_result)}&quot;)

        next_state = next_state[0] if isinstance(next_state, tuple) else next_state
        next_state = np.reshape(next_state, [1, state_size])  # 다음 상태 변환

        # 경험을 리플레이 버퍼에 저장
        agent.replay_buffer.add((state, action, reward, next_state, done))

        state = next_state  # 상태 업데이트
        total_reward += reward  # 총 보상 업데이트

        # 에이전트 학습
        agent.train()

    # 10 에피소드마다 타겟 네트워크 업데이트
    if episode % 10 == 0:
        agent.update_target_network()

    # 탐험률 감소
    if agent.epsilon &gt; agent.epsilon_min:
        agent.epsilon *= agent.epsilon_decay

    # 에피소드 정보 출력
    if episode % 10 == 0:
        print(f&quot;Episode: {episode}, Total Reward: {total_reward}, Epsilon: {agent.epsilon:.2f}&quot;)

# 환경 종료
env.close()</code></pre>
<h3 id="학습-결과를-비디오로-저장">학습 결과를 비디오로 저장</h3>
<pre><code class="language-python">
import os
from gym.wrappers import RecordVideo

# 비디오 저장 경로 설정
video_save_path = &quot;./cartpole_videos&quot;
os.makedirs(video_save_path, exist_ok=True)

# CartPole 환경을 생성하고 비디오 저장 설정
env = gym.make(&quot;CartPole-v1&quot;, render_mode=&quot;rgb_array&quot;)  # 비디오 저장용 환경 생성
env = RecordVideo(env, video_save_path, episode_trigger=lambda x: True)  # 모든 에피소드 비디오 저장

def play_and_record(agent, env, episodes=5):
    &quot;&quot;&quot;
    학습된 에이전트가 환경을 플레이하며 비디오를 저장하는 함수.

    Args:
        agent: 학습된 DQN 에이전트.
        env: 비디오를 기록할 Gym 환경.
        episodes: 에이전트가 플레이할 에피소드 수.
    &quot;&quot;&quot;
    for episode in range(episodes):
        state = env.reset()
        state = state[0] if isinstance(state, tuple) else state  # 상태가 튜플이면 첫 번째 요소 사용
        state = np.reshape(state, [1, state_size])  # 상태를 2D 배열로 변환
        total_reward = 0
        done = False

        while not done:
            # 학습된 네트워크로 행동 선택 (탐험 없이 활용만 수행)
            q_values = agent.main_model.predict(state)
            action = np.argmax(q_values[0])  # 가장 높은 Q값을 가진 행동 선택

            # 환경에서 한 스텝 진행
            step_result = env.step(action)
            if len(step_result) == 4:  # Gym 반환값 처리
                next_state, reward, done, info = step_result
            elif len(step_result) == 5:  # 일부 버전에서 반환값 추가 처리
                next_state, reward, done, truncated, info = step_result
                done = done or truncated  # truncated를 종료 조건으로 처리
            else:
                raise ValueError(f&quot;Unexpected step result length: {len(step_result)}&quot;)

            next_state = next_state[0] if isinstance(next_state, tuple) else next_state
            next_state = np.reshape(next_state, [1, state_size])  # 다음 상태 변환

            state = next_state  # 상태 업데이트
            total_reward += reward  # 총 보상 업데이트

        print(f&quot;Episode: {episode + 1}, Total Reward: {total_reward}&quot;)

# 학습된 에이전트로 플레이하며 비디오 저장
play_and_record(agent, env, episodes=5)

# 비디오 저장 완료 후 환경 종료
env.close()

print(f&quot;비디오가 &#39;{video_save_path}&#39;에 저장되었습니다.&quot;)</code></pre>
<h3 id="생성된-비디오-재생">생성된 비디오 재생</h3>
<pre><code class="language-python">from IPython.display import HTML
import base64

def display_video(video_path):
    &quot;&quot;&quot;저장된 비디오를 Jupyter Notebook에서 재생&quot;&quot;&quot;
    with open(video_path, &quot;rb&quot;) as video_file:
        video_data = video_file.read()
    encoded_video = base64.b64encode(video_data).decode(&quot;utf-8&quot;)
    return HTML(f&quot;&quot;&quot;
        &lt;video width=&quot;640&quot; height=&quot;480&quot; controls&gt;
            &lt;source src=&quot;data:video/mp4;base64,{encoded_video}&quot; type=&quot;video/mp4&quot;&gt;
        &lt;/video&gt;
    &quot;&quot;&quot;)

# 비디오 파일 경로
video_file_path = f&quot;{video_save_path}/rl-video-episode-4.mp4&quot;
display_video(video_file_path)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/d1047057-64ab-4fdf-b1da-35c47747c2fa/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가비아 DNS 설정 및 Nginx를 활용한 FastAPI 배포]]></title>
            <link>https://velog.io/@s_s/%EA%B0%80%EB%B9%84%EC%95%84-DNS-%EC%84%A4%EC%A0%95-%EB%B0%8F-Nginx%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-FastAPI-%EB%B0%B0%ED%8F%AC</link>
            <guid>https://velog.io/@s_s/%EA%B0%80%EB%B9%84%EC%95%84-DNS-%EC%84%A4%EC%A0%95-%EB%B0%8F-Nginx%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-FastAPI-%EB%B0%B0%ED%8F%AC</guid>
            <pubDate>Fri, 14 Feb 2025 17:53:45 GMT</pubDate>
            <description><![CDATA[<h1 id="1-가비아에서-dns-설정하기">1. 가비아에서 DNS 설정하기</h1>
<p>가비아에 로그인한 후, DNS 관리 페이지(예: 가비아 DNS 관리)로 이동합니다.
여기서 도메인에 대해 다음과 같이 DNS 레코드를 설정합니다.
링크 : <a href="https://dns.gabia.com/dns/internals/total_set">https://dns.gabia.com/dns/internals/total_set</a></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/c399cca7-01fb-40e4-9b18-13cd5753c445/image.png" alt=""></p>
<p>DNS 레코드 예시</p>
<ul>
<li>타입: A</li>
<li>호스트: @</li>
<li>값: 서버의 공인 IP 주소</li>
<li>TTL(Time To Live): 600</li>
</ul>
<p>TTL(Time To Live)란?</p>
<ul>
<li>TTL은 DNS 레코드가 DNS 서버나 클라이언트에 캐시되어 있는 시간을 초 단위로 나타낸 값입니다.</li>
<li>예를 들어, TTL이 600초로 설정되어 있으면, 변경 사항이 최대 600초(10분) 후에 반영될 수 있다는 의미입니다.</li>
<li>TTL 값을 낮추면 DNS 변경사항이 더 빠르게 반영되지만, 너무 낮게 설정하면 잦은 DNS 조회로 인해 부하가 증가할 수 있으므로 적당한 값을 선택하는 것이 좋습니다.</li>
<li>따라서, TTL 600은 일반적인 설정으로 적당합니다.</li>
</ul>
<p>이렇게 설정하면, 도메인(example.com)서버의 IP 주소를 가리키게 되어 웹 브라우저에서 접속할 수 있습니다.</p>
<p>이후 보안을 포함한 부하 분산, SSL 인증서 적용 등을 편리하게 하기 위해 Nginx를 적용하겠습니다.</p>
<p>그렇게되면 외부에서 서버로 바로 요청이 들어오는게 아니라, Nginx가 역방향 프록시 역할을 하여 클라이언트의 요청을 받아 내부 서버로 전달하게 됩니다. </p>
<p>예를 들어 fastapi를 이용해 웹서버를 구축했다면,
클라이언트 -&gt; fastapi 서버 방식이 아니라 
클라이언트 -&gt; Nginx -&gt; fastapi 단계로 이루어져 중간에서 SSL 통신 처리, 부하분산 등의 역할을 손쉽게 도와줍니다. </p>
<h1 id="2-nginx를-이용한-역방향-프록시-구성">2. Nginx를 이용한 역방향 프록시 구성</h1>
<p>보안을 강화하고 부하 분산, SSL 인증서 적용 등을 용이하게 하기 위해 Nginx를 사용합니다.
Nginx는 외부에서 들어오는 요청을 받아 내부의 FastAPI 서버(예: uvicorn)로 전달하는 역방향 프록시(reverse proxy) 역할을 합니다.</p>
<p>예를 들어 FastAPI 애플리케이션을 uvicorn으로 실행하면,
직접 외부에서 접속하는 방식(클라이언트 → FastAPI)보다는
클라이언트 → Nginx → FastAPI 방식으로 요청이 전달되어, Nginx에서 SSL 종료, 요청 헤더 처리, 부하 분산 등의 추가 기능을 활용할 수 있습니다</p>
<h2 id="nginx-적용-방법-우분투-기준">Nginx 적용 방법 (우분투 기준)</h2>
<ol>
<li><p>Nginx 설치</p>
<pre><code class="language-bash">sudo apt update
sudo apt install nginx</code></pre>
</li>
<li><p>Nginx 설정 파일 작성
/etc/nginx/sites-available/ 디렉토리에 새로운 설정 파일(예: fastapi.conf)을 생성합니다.</p>
<pre><code class="language-bash">sudo vi /etc/nginx/sites-available/fastapi.conf</code></pre>
<p>아래와 같이 내용을 입력합니다.
(이 설정은 외부의 80 포트로 들어온 요청을 내부의 127.0.0.1:8000 포트로 전달합니다.)</p>
<pre><code class="language-nginx">server {
 listen 80;
 server_name example.com www.example.com;  # 구매한 도메인 입력

 location / {
     proxy_pass http://127.0.0.1:8000;
     proxy_set_header Host $host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Proto $scheme;
 }
}</code></pre>
</li>
<li><p>방화벽(UFW) 설정</p>
</li>
</ol>
<ul>
<li>외부에서 80 포트(HTTP)와 443 포트(HTTPS)에 접근할 수 있도록 UFW 설정을 업데이트합니다.<pre><code>sudo ufw allow 80/tcp
sudo ufw allow 443/tcp # HTTPS를 사용</code></pre></li>
</ul>
<ol start="4">
<li>nginx 설정 활성화 및 재시작</li>
</ol>
<ul>
<li>sites-available에 작성한 설정 파일을 sites-enabled에 심볼릭 링크로 등록합니다.</li>
</ul>
<pre><code class="language-bash">sudo ln -s /etc/nginx/sites-available/fastapi.conf /etc/nginx/sites-enabled/</code></pre>
<ul>
<li>설정 문법을 확인한 후 Nginx를 재시작합니다<pre><code class="language-bash">sudo nginx -t  # 설정 파일 (문법)테스트
sudo systemctl restart nginx # 재시작</code></pre>
</li>
</ul>
<p>5.SSL 인증서 적용 (Certbot 사용) SSL을 적용하여 HTTPS 통신을 가능하게 하려면 Certbot을 사용합니다.</p>
<pre><code>sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com</code></pre><p>참고 : Nginx 명령어</p>
<pre><code>Nginx 상태 조회 명령어
sudo systemctl status nginx 

Nginx 시작 명령어
sudo systemctl start nginx

Nginx 중지 명령어
sudo systemctl stop nginx
재시작
sudo systemctl restart nginx

Nginx 자동 시작 설정: 서버 부팅 시 Nginx가 자동으로 시작되도록 설정하려면 다음 명령어를 사용합니다.
sudo systemctl enable nginx</code></pre><p>위 설정을 완료하면, 서버의 IP 주소 대신 도메인으로 웹사이트에 접속할 수 있으며, Nginx가 역방향 프록시로 작동하여 FastAPI 애플리케이션으로 요청을 전달합니다. 또한, Certbot을 통한 SSL 인증서 적용으로 HTTPS 연결도 사용할 수 있습니다.</p>
<p>이제 여러분의 도메인을 통해 안전하고 효율적인 웹 서비스 운영이 가능합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[암호화폐 재정거래 프로젝트]]></title>
            <link>https://velog.io/@s_s/%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EC%9E%AC%EC%A0%95%EA%B1%B0%EB%9E%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</link>
            <guid>https://velog.io/@s_s/%EC%95%94%ED%98%B8%ED%99%94%ED%8F%90-%EC%9E%AC%EC%A0%95%EA%B1%B0%EB%9E%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8</guid>
            <pubDate>Wed, 05 Feb 2025 17:20:55 GMT</pubDate>
            <description><![CDATA[<p>암호화폐는 탈중앙화 화폐이기 때문에 거래소마다 가격의 차이가 있다. </p>
<p>따라서 거래소마다 가격이 다른 것을 이용해 가격이 싼 곳에서 구매한 뒤 비싼곳에서 사면 적은 리스크로 돈을 벌 수 있고 이것을 무위험 차익거래 혹은 재정거래라고 부른다. </p>
<p>돈을 입금하면 재정거래를 자동으로 수행해주는 일부 서비스가 있지만, 수수료가 매우 크고 서비스 자체가 폐업해버리면 자산을 잃을 수 있기에 파이썬을 활용하여 재정거래 시스템을 직접 구축해보기로 했다.</p>
<p>제대로 돈을 벌기 위해서는 시중에 존재하는 대부분의 거래소를 탐색하여 가장 가격차이가 큰 두곳을 타겟으로 해야겠지만, 사람이 거래를 하는것과 달리 API를 이용한 프로그래밍 방식은 보안 절차가 상당히 까다로우며 시간이 많이 걸린다. 따라서 이번에는 업비트와 바이낸스 두 거래소를 타겟으로 하여 간단히 진행해본다. </p>
<h1 id="1-사전-준비-과정">1. 사전 준비 과정</h1>
<p>우선 두 거래소 모두 아래 절차를 진행한다.</p>
<ol>
<li>거래에서 사용할 API 주소 발급 과정</li>
<li>출금이 가능한 지갑 주소(Whitelist)를 등록</li>
</ol>
<p>1번은 자료가 많기에 생략한다. 2번의 경우 API를 이용해 제 3자에게 암호화폐가 출금되는 상황은 위험하기에, 본인의 지갑 주소로 검증이 된 주소로만 화폐 전송을 허가하기 위함이다. 이는 휴대폰 인증을 통해서 등록하는 절차를 각 거래소마다 필수적으로 진행해주어야 한다. </p>
<h3 id="11-바이낸스에서-업비트-출금-주소-등록하기">1.1 바이낸스에서 업비트 출금 주소 등록하기</h3>
<ul>
<li>링크 : <a href="https://www.binance.com/en/my/security/address-management">https://www.binance.com/en/my/security/address-management</a></li>
<li>아래 사진에 나와 있는 것처럼 whitelist에 암호 화폐별 지갑 주소를 별도 등록 해야한다.
<img src="https://velog.velcdn.com/images/s_s/post/cfcda4f3-b5f0-4409-bf4c-5c6f2b015add/image.png" alt=""></li>
</ul>
<h3 id="12-업비트에서-바이낸스-출금-주소-등록하기">1.2 업비트에서 바이낸스 출금 주소 등록하기</h3>
<ul>
<li>마이페이지 -&gt; Open API 관리 -&gt; 디지털 자산 출금주소 관리</li>
<li>링크 : <a href="https://upbit.com/mypage/open_api_management?tab=fund_source">https://upbit.com/mypage/open_api_management?tab=fund_source</a>
<img src="https://velog.velcdn.com/images/s_s/post/e8411935-c21c-418e-8483-d2c6dc37fa84/image.png" alt=""></li>
</ul>
<p>바이낸스와 업비트에서 출금이 가능한 주소를 직접 등록하게 되면, API를 통한 거래시 해당 주소로 출금이 가능해진다. 주소를 등록할때 휴대폰 인증을 통한 절차가 두 플랫폼 모두에 존재했기 때문에 꽤 안전하다고 느껴진다.  </p>
<h1 id="2-파이썬-재정거래-시스템-구축">2. 파이썬 재정거래 시스템 구축</h1>
<h3 id="21-환경-설정">2.1 환경 설정</h3>
<p>API를 활용한 수월한 거래를 위해 각 거래소에서 제공하는 API기능을 적극 활용한다. 이때, 주요 기능이 잘 구현된 라이브러리를 활용하면 수월하게 작업이 가능하다. 설치 명령어는 다음과 같다. </p>
<pre><code>pip3 install python-binance
pip3 install pyupbit
pip3 install upbit-client</code></pre><p>이후 본인의 API키를 .env파일에 잘 저장해준 뒤, 작업 파일에서 각 라이브러리를 임포트하고, API키를 불러와서 거래소 클라이언트 객체를 생성하는 과정은 다음과 같다. </p>
<pre><code class="language-python">import time
import csv
import os
import requests                     # 환율 조회용
import pyupbit                      # 가격 데이터 조회용
from binance.client import Client   # 바이낸스 API 사용
from binance.exceptions import BinanceAPIException
from upbit.client import Upbit as UpbitClient  # Upbit 공식 클라이언트
from dotenv import load_dotenv

# API KEY 정보로드
load_dotenv()

# ===== API Key/Secret =====
BINANCE_API_KEY    = os.environ.get(&#39;BINANCE_API_KEY&#39;)
BINANCE_API_SECRET = os.environ.get(&#39;BINANCE_API_SECRET&#39;)
UPBIT_ACCESS_KEY   = os.environ.get(&#39;UPBIT_ACCESS_KEY&#39;)
UPBIT_SECRET_KEY   = os.environ.get(&#39;UPBIT_SECRET_KEY&#39;)

# ===== 거래소 클라이언트 객체 생성 =====
binance_client = Client(BINANCE_API_KEY, BINANCE_API_SECRET)
upbit_client = UpbitClient(UPBIT_ACCESS_KEY, UPBIT_SECRET_KEY)</code></pre>
<p>여기까지 큰 문제 없이 진행되었다면, 아래 코드를 이용하여 본인 거래소 계좌에 있는 암호화폐 정보를 출력할 수 있을 것이다. </p>
<pre><code class="language-python"># -------------------------------
# 1. 각 거래소 잔고 출력
# -------------------------------

def print_upbit_balance():
    try:
        # Upbit 공식 클라이언트의 Account_info() 메서드를 사용하여 잔고 정보 조회
        resp = upbit_client.Account.Account_info()
        print(&quot;==== Upbit 잔고 정보 ====&quot;)
        for asset in resp[&#39;result&#39;]:
            balance = float(asset.get(&quot;balance&quot;, 0))
            if balance &gt; 0:
                print(f&quot;{asset[&#39;currency&#39;]}: {balance}&quot;)
    except Exception as e:
        print(&quot;Upbit 잔고 출력 오류:&quot;, e)

def print_binance_balance():
    try:
        # Binance의 계좌 정보를 조회하여 각 자산별 잔고 출력
        account_info = binance_client.get_account()
        print(&quot;==== Binance 잔고 정보 ====&quot;)
        for asset in account_info[&#39;balances&#39;]:
            free = float(asset.get(&#39;free&#39;, 0))
            locked = float(asset.get(&#39;locked&#39;, 0))
            total = free + locked
            if total &gt; 0:
                print(f&quot;{asset[&#39;asset&#39;]}: {total} (free: {free}, locked: {locked})&quot;)
    except Exception as e:
        print(&quot;Binance 잔고 출력 오류:&quot;, e)

# -------------------------------
# 2. 보유 코인 목록 및 양 출력
# -------------------------------

def print_upbit_coin_holdings():
    try:
        resp = upbit_client.Account.Account_info()
        print(&quot;==== Upbit 보유 코인 목록 ====&quot;)
        for asset in resp[&#39;result&#39;]:
            currency = asset.get(&quot;currency&quot;)
            # KRW는 기본 자산이므로 코인 목록에서는 제외할 수 있음 (필요 시 출력)
            if currency != &quot;KRW&quot;:
                balance = float(asset.get(&quot;balance&quot;, 0))
                if balance &gt; 0:
                    print(f&quot;{currency}: {balance}&quot;)
    except Exception as e:
        print(&quot;Upbit 코인 보유량 출력 오류:&quot;, e)

def print_binance_coin_holdings():
    try:
        account_info = binance_client.get_account()
        print(&quot;==== Binance 보유 코인 목록 ====&quot;)
        for asset in account_info[&#39;balances&#39;]:
            asset_name = asset.get(&quot;asset&quot;)
            # USDT와 같이 기본 자산은 제외할 수 있음 (필요 시 출력)
            if asset_name != &quot;USDT&quot;:
                free = float(asset.get(&quot;free&quot;, 0))
                locked = float(asset.get(&quot;locked&quot;, 0))
                total = free + locked
                if total &gt; 0:
                    print(f&quot;{asset_name}: {total} (free: {free}, locked: {locked})&quot;)
    except Exception as e:
        print(&quot;Binance 코인 보유량 출력 오류:&quot;, e)



print_upbit_balance()
print_binance_balance()
print_upbit_coin_holdings()
print_binance_coin_holdings()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0db48505-6b5e-4683-a1b3-b145b6aaac45/image.png" alt=""></p>
<p>다음은 각 거래소 클라이언트 객체를 이용해 원하는 코인을 매수하는 코드 예시다.</p>
<h3 id="업비트-시장가-매수">업비트 시장가 매수</h3>
<pre><code class="language-python">resp = upbit_client.Order.Order_new(
    market=symbol,
    side=&#39;bid&#39;, # bid : 매수 , ask : 매도
    #volume=&#39;10&#39;, # 주문량
    price=&#39;30&#39;, # 주문 가격
    ord_type=&#39;price&#39; #  limit: 지정가 주문, price: 시장가 주문 (매수), market: 시장가 주문 (매도)
)
print(resp[&#39;result&#39;])</code></pre>
<p>위 코드를 실행하면 현재 시장가격대로 30개를 매수하게 된다. 하지만 위 방법을 사용하는것은 비추천한다. 왜냐하면 업비트의 가격 산출 방식에 심각한 문제가 있기 때문이다. </p>
<p>아래 사진을 보자. 화면에 나와있는 대로 API를 통해 시장가 조회를 하거나 업비트 홈페이지에서 현재 가격을 확인하면 <strong>0.4269 USDT</strong>인 것을 확인할 수 있다. 하지만 우측 하단을 보면 <strong>꽤 오래전부터 가격이 0.3 USDT 이하로 유지되고 있었음을 알 수 있다.</strong> </p>
<p><img src="https://velog.velcdn.com/images/s_s/post/ec2458b6-d285-446b-aad6-fd53c7b3814f/image.png" alt=""></p>
<p>이로 인해 당시 가치보다 지나치게 비싸게 코인이 매수되어 손실을 보게 되었다.
암호화폐를 매수할 때는 지정가로 구매하도록 하자. 지정가를 이용해서 구매하는 코드는 아래와 같다. </p>
<pre><code class="language-python">resp = upbit_client.Order.Order_new(
    market=symbol,
    side=&#39;bid&#39;, # bid : 매수 , ask : 매도
    volume=&#39;10&#39;, # 주문량
    price=&#39;0.26&#39;, # 주문 가격
    ord_type=&#39;price&#39; #  limit: 지정가 주문, price: 시장가 주문 (매수), market: 시장가 주문 (매도)
)
print(resp[&#39;result&#39;])</code></pre>
<p>이후 API를 통한 거래가 가능하도록 출금 주소가 Whitelist에 잘 등록되었는지 확인하는 절차를 진행한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tensorflow를 활용한 인공신경망 구현2]]></title>
            <link>https://velog.io/@s_s/Tensorflow%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EA%B5%AC%ED%98%842</link>
            <guid>https://velog.io/@s_s/Tensorflow%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EA%B5%AC%ED%98%842</guid>
            <pubDate>Wed, 05 Feb 2025 10:28:15 GMT</pubDate>
            <description><![CDATA[<h3 id="mnist-딥러닝-모델-예제">MNIST 딥러닝 모델 예제</h3>
<ul>
<li>손으로 쓴 숫자들로 이루어진 이미지 데이터셋</li>
<li>기계 학습 분야의 트레이닝 및 테스트에 널리 사용되는 데이터</li>
<li>keras.datasets에 기본으로 포함되어 있는 데이터셋</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/be25e2ed-bee0-444a-b2f2-061dc3fefac3/image.png" alt=""></p>
<h4 id="모듈-임포트">모듈 임포트</h4>
<pre><code class="language-python">import tensorflow as tf
from tensorflow.keras.datasets.mnist import load_data
from tensorflow.keras.models import Sequential
from tensorflow.keras import models
from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.utils import to_categorical

from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt</code></pre>
<h4 id="데이터-로드-및-전처리">데이터 로드 및 전처리</h4>
<ul>
<li>MNIST 데이터셋을 로드</li>
<li>Train Data 중, 30%를 검증 데이터(validation data)로 사용</li>
</ul>
<pre><code class="language-python">(x_train_full, y_train_full), (x_test, y_test) = load_data(path=&#39;mnist.npz&#39;)
x_train, x_val, y_train, y_val = train_test_split(x_train_full, y_train_full, test_size=0.3, random_state=111)

# random_state 검증, random_state=111 여부에 따른 값 변화 확인
# train_test_split(x_train_full, y_train_full, test_size=0.3)[0][33].sum()</code></pre>
<pre><code class="language-python">num_x_train = x_train.shape[0]
num_x_val = x_val.shape[0]
num_x_test = x_test.shape[0]

print(f&quot;기존학습데이터 : {x_train_full.shape} 레이블 : {y_train_full.shape}&quot;)
print(f&quot;학습데이터 : {x_train.shape} 레이블 : {y_train.shape}&quot;)
print(f&quot;검증데이터 : {x_val.shape} 레이블 : {y_val.shape}&quot;)
print(f&quot;테스트데이터 : {x_test.shape} 레이블 : {y_test.shape}&quot;)</code></pre>
<pre><code>기존학습데이터 : (60000, 28, 28) 레이블 : (60000,)
학습데이터 : (42000, 28, 28) 레이블 : (42000,)
검증데이터 : (18000, 28, 28) 레이블 : (18000,)
테스트데이터 : (10000, 28, 28) 레이블 : (10000,)</code></pre><pre><code class="language-python">num_sample = 5
random_idx = np.random.randint(60000, size=num_sample)

plt.figure(figsize=(15,3))
for idx, i in enumerate(random_idx):
    img = x_train_full[i, :]
    label = y_train_full[i]

    plt.subplot(1,len(random_idx), idx+1) # 행 / 열 / 위치
    plt.imshow(img)
    plt.title(f&#39;index: {i}, label: {label}&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/af949725-96f8-4590-a7e5-fa8c857db62e/image.png" alt=""></p>
<pre><code class="language-python"># 0 ~ 1 사이 값으로 nomalization (정규화)
# 0 ~ 1 값으로 바꿔주면 학습이 잘됨
x_train = x_train / 255.
x_val = x_val / 255.
x_test = x_test / 255.

y_train = to_categorical(y_train)
y_val = to_categorical(y_val)
y_test = to_categorical(y_test)</code></pre>
<h4 id="모델-구성sequential">모델 구성(Sequential)</h4>
<pre><code class="language-python">
model = Sequential([
    Input(shape=(28,28)),
    Flatten(input_shape = [28,28], name = &#39;flatten&#39;),
    Dense(100, activation=&#39;relu&#39;),
    Dense(64, activation=&#39;relu&#39;),
    Dense(32, activation=&#39;relu&#39;),
    Dense(10, activation=&#39;softmax&#39;)])

model.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_2"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ flatten (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">784</span>)            │             <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">100</span>)            │        <span style="color: #00af00; text-decoration-color: #00af00">78,500</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">6,464</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">2,080</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">330</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">87,374</span> (341.30 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">87,374</span> (341.30 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>



<h4 id="모델-컴파일-및-학습">모델 컴파일 및 학습</h4>
<pre><code class="language-python">model.compile(loss=&#39;categorical_crossentropy&#39;, 
              optimizer=&#39;adam&#39;,
            metrics=[&#39;accuracy&#39;])</code></pre>
<pre><code class="language-python">history = model.fit(x_train, y_train,
                    epochs = 50,
                    batch_size = 128,
                    validation_data=(x_val, y_val))</code></pre>
<pre><code>Epoch 1/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.7467 - loss: 0.8140 - val_accuracy: 0.9292 - val_loss: 0.2333
Epoch 2/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9432 - loss: 0.1954 - val_accuracy: 0.9538 - val_loss: 0.1530
Epoch 3/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9610 - loss: 0.1314 - val_accuracy: 0.9559 - val_loss: 0.1394
Epoch 4/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9688 - loss: 0.1043 - val_accuracy: 0.9636 - val_loss: 0.1167
Epoch 5/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9756 - loss: 0.0790 - val_accuracy: 0.9656 - val_loss: 0.1105
Epoch 6/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9798 - loss: 0.0632 - val_accuracy: 0.9645 - val_loss: 0.1141
Epoch 7/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9826 - loss: 0.0567 - val_accuracy: 0.9679 - val_loss: 0.1069
Epoch 8/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9866 - loss: 0.0447 - val_accuracy: 0.9707 - val_loss: 0.1013
Epoch 9/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9901 - loss: 0.0346 - val_accuracy: 0.9693 - val_loss: 0.1078
Epoch 10/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9900 - loss: 0.0335 - val_accuracy: 0.9686 - val_loss: 0.1153
Epoch 11/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9897 - loss: 0.0301 - val_accuracy: 0.9716 - val_loss: 0.1054
Epoch 12/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9926 - loss: 0.0229 - val_accuracy: 0.9673 - val_loss: 0.1208
Epoch 13/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9939 - loss: 0.0189 - val_accuracy: 0.9714 - val_loss: 0.1072
Epoch 14/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9946 - loss: 0.0170 - val_accuracy: 0.9697 - val_loss: 0.1180
Epoch 15/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9931 - loss: 0.0235 - val_accuracy: 0.9726 - val_loss: 0.1171
Epoch 16/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9972 - loss: 0.0112 - val_accuracy: 0.9689 - val_loss: 0.1356
Epoch 17/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9949 - loss: 0.0152 - val_accuracy: 0.9700 - val_loss: 0.1323
Epoch 18/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9973 - loss: 0.0089 - val_accuracy: 0.9711 - val_loss: 0.1271
Epoch 19/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9967 - loss: 0.0104 - val_accuracy: 0.9692 - val_loss: 0.1376
Epoch 20/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9962 - loss: 0.0126 - val_accuracy: 0.9721 - val_loss: 0.1335
Epoch 21/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9949 - loss: 0.0151 - val_accuracy: 0.9703 - val_loss: 0.1429
Epoch 22/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9946 - loss: 0.0186 - val_accuracy: 0.9715 - val_loss: 0.1336
Epoch 23/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9970 - loss: 0.0097 - val_accuracy: 0.9744 - val_loss: 0.1306
Epoch 24/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9984 - loss: 0.0059 - val_accuracy: 0.9736 - val_loss: 0.1418
Epoch 25/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9987 - loss: 0.0048 - val_accuracy: 0.9707 - val_loss: 0.1535
Epoch 26/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9950 - loss: 0.0134 - val_accuracy: 0.9722 - val_loss: 0.1517
Epoch 27/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9989 - loss: 0.0040 - val_accuracy: 0.9727 - val_loss: 0.1534
Epoch 28/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9969 - loss: 0.0097 - val_accuracy: 0.9694 - val_loss: 0.1648
Epoch 29/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9986 - loss: 0.0048 - val_accuracy: 0.9734 - val_loss: 0.1489
Epoch 30/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9989 - loss: 0.0035 - val_accuracy: 0.9739 - val_loss: 0.1541
Epoch 31/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9956 - loss: 0.0155 - val_accuracy: 0.9703 - val_loss: 0.1637
Epoch 32/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9976 - loss: 0.0070 - val_accuracy: 0.9736 - val_loss: 0.1508
Epoch 33/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9987 - loss: 0.0032 - val_accuracy: 0.9743 - val_loss: 0.1453
Epoch 34/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9998 - loss: 9.3090e-04 - val_accuracy: 0.9761 - val_loss: 0.1458
Epoch 35/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 0.9999 - loss: 6.2426e-04 - val_accuracy: 0.9739 - val_loss: 0.1541
Epoch 36/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 4.3768e-04 - val_accuracy: 0.9760 - val_loss: 0.1477
Epoch 37/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 1.0332e-04 - val_accuracy: 0.9761 - val_loss: 0.1504
Epoch 38/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 7.5064e-05 - val_accuracy: 0.9758 - val_loss: 0.1514
Epoch 39/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 6.7781e-05 - val_accuracy: 0.9762 - val_loss: 0.1528
Epoch 40/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 5.3892e-05 - val_accuracy: 0.9764 - val_loss: 0.1545
Epoch 41/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 4.8135e-05 - val_accuracy: 0.9763 - val_loss: 0.1564
Epoch 42/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 3.6767e-05 - val_accuracy: 0.9762 - val_loss: 0.1588
Epoch 43/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 3.5120e-05 - val_accuracy: 0.9762 - val_loss: 0.1588
Epoch 44/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 2.9870e-05 - val_accuracy: 0.9764 - val_loss: 0.1614
Epoch 45/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 2.4976e-05 - val_accuracy: 0.9764 - val_loss: 0.1634
Epoch 46/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 2.2570e-05 - val_accuracy: 0.9764 - val_loss: 0.1647
Epoch 47/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 2.0138e-05 - val_accuracy: 0.9764 - val_loss: 0.1668
Epoch 48/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 1.7230e-05 - val_accuracy: 0.9761 - val_loss: 0.1682
Epoch 49/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 1.5470e-05 - val_accuracy: 0.9765 - val_loss: 0.1708
Epoch 50/50
[1m329/329[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - accuracy: 1.0000 - loss: 1.3635e-05 - val_accuracy: 0.9765 - val_loss: 0.1719</code></pre><pre><code class="language-python">history.history.keys()</code></pre>
<pre><code>dict_keys([&#39;accuracy&#39;, &#39;loss&#39;, &#39;val_accuracy&#39;, &#39;val_loss&#39;])</code></pre><pre><code class="language-python"># 기록을 눈으로 보기 편하게 시각화
# history 객체에서 학습 기록을 딕셔너리 형태로 가져옴
# history.history에는 loss, accuracy 등의 지표가 epoch별로 저장되어 있음
history_dict = history.history

# 학습 손실(loss)과 검증 손실(validation loss) 데이터 추출
loss = history_dict[&#39;loss&#39;]
val_loss = history_dict[&#39;val_loss&#39;]

# 시각화를 위한 x축 데이터(epochs) 생성. 1부터 시작하여 loss 데이터 길이만큼의 범위 생성
epochs = range(1, len(loss)+1)

# 그래프를 그리기 위한 figure 생성
# figsize로 그래프 크기 지정 (가로 12, 세로 5)
fig = plt.figure(figsize=(12,5))

ax1 = fig.add_subplot(1, 2, 1) # 행 / 열 / 위치
ax1.plot(epochs, loss, color=&#39;blue&#39;, label=&#39;train_loss&#39;)
ax1.plot(epochs, val_loss, color=&#39;red&#39;, label=&#39;val_loss&#39;)

ax1.set_title(&#39;Train and Validation Loss&#39;)
ax1.set_xlabel(&#39;Epochs&#39;)
ax1.set_ylabel(&#39;Loss&#39;)
ax1.grid()
ax1.legend() 

# 학습 정확도(accuracy)와 검증 정확도(validation accuracy) 데이터 추출
accuracy = history_dict[&#39;accuracy&#39;]
val_accuracy = history_dict[&#39;val_accuracy&#39;]

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, accuracy, color=&#39;blue&#39;, label=&#39;train_accuracy&#39;)
ax2.plot(epochs, val_accuracy, color=&#39;red&#39;, label=&#39;val_accuracy&#39;)

ax2.set_title(&#39;Train and Validation accuracy&#39;)
ax2.set_xlabel(&#39;Epochs&#39;)
ax2.set_ylabel(&#39;Loss&#39;)
ax2.grid()
ax2.legend()</code></pre>
<pre><code>&lt;matplotlib.legend.Legend at 0x2367e49c430&gt;</code></pre><p><img src="Tensorflow%EB%A5%BC%20%ED%99%9C%EC%9A%A9%ED%95%9C%20%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D%20%EA%B5%AC%ED%98%842_files/Tensorflow%EB%A5%BC%20%ED%99%9C%EC%9A%A9%ED%95%9C%20%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D%20%EA%B5%AC%ED%98%842_15_1.png" alt="png"></p>
<h4 id="모델-평가-및-예측">모델 평가 및 예측</h4>
<pre><code class="language-python">model.evaluate(x_test, y_test)</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 783us/step - accuracy: 0.9744 - loss: 0.1925





[0.16237415373325348, 0.9783999919891357]</code></pre><pre><code class="language-python">y_pred = model.predict(x_test)
print(y_pred.shape)

print(y_pred[0])
print(np.round(y_pred[0],3))</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 654us/step
(10000, 10)
[1.148e-19 1.542e-18 1.598e-15 2.186e-12 3.355e-22 1.069e-22 2.931e-35
 1.000e+00 1.177e-22 1.007e-15]
[0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]</code></pre><pre><code class="language-python">arg_pred_y = np.argmax(y_pred[0])

plt.imshow(x_test[0], cmap=&#39;gray&#39;)
plt.title(f&#39;predicted label : {arg_pred_y}&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8a22a4c0-3636-4e41-9b29-0eb4015c9f64/image.png" alt=""></p>
<ul>
<li>혼동 행렬 (Confusion Matrix)</li>
</ul>
<pre><code class="language-python">from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
sns.set(style=&#39;white&#39;)</code></pre>
<pre><code class="language-python">np.argmax(y_pred, axis=1)</code></pre>
<pre><code>array([7, 2, 1, ..., 4, 5, 6], dtype=int64)</code></pre><pre><code class="language-python">&#39;&#39;&#39;
cm: 이것은 히트맵으로 표시할 데이터입니다.
cm은 일반적으로 혼동 행렬을 나타내며, 2차원 배열 형태로 데이터를 포함합니다.

annot (annotation): True로 설정될 경우, 각 셀에 데이터 값이 표시됩니다.
즉, 매트릭스의 각 요소가 그래프에 숫자 형태로 주석으로 추가되어 보다 읽기 쉬운 히트맵을 생성합니다.

fmt (&#39;d&#39;): 주석으로 추가된 데이터의 포맷을 지정합니다.
여기서 &#39;d&#39;는 정수 형식을 의미합니다. 이 옵션은 annot=True일 때 유효하며, 각 셀의 숫자를 정수 형태로 표시하도록 설정합니다.

cmap (&#39;Blues&#39;): 이것은 히트맵에 사용할 컬러 맵을 지정합니다.
&#39;Blues&#39;는 파란색 계열의 컬러 맵을 의미하며, 값이 낮은 영역은 연한 파란색, 값이 높은 영역은 진한 파란색으로 표시됩니다.
Seaborn과 Matplotlib에서 다양한 컬러 맵을 제공하며, 이를 통해 시각적으로 표현의 차이를 뚜렷하게 할 수 있습니다.
&#39;&#39;&#39;</code></pre>
<pre><code class="language-python">plt.figure(figsize=(8,8))
cm = confusion_matrix(np.argmax(y_test, axis=1), np.argmax(y_pred, axis=1))
sns.heatmap(cm, annot=True, fmt=&#39;d&#39;, cmap=&#39;Blues&#39;)
plt.show(&#39;True Label&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/e2aebacb-4423-43cd-8b9c-fdbeb5ada4b1/image.png" alt=""></p>
<ul>
<li>분류 보고서</li>
</ul>
<pre><code class="language-python">print(classification_report(np.argmax(y_test, axis=1), np.argmax(y_pred, axis=1)))</code></pre>
<pre><code>              precision    recall  f1-score   support

           0       0.98      0.99      0.99       980
           1       0.98      0.99      0.99      1135
           2       0.97      0.98      0.98      1032
           3       0.97      0.98      0.98      1010
           4       0.97      0.98      0.97       982
           5       0.98      0.97      0.98       892
           6       0.99      0.97      0.98       958
           7       0.98      0.97      0.98      1028
           8       0.97      0.97      0.97       974
           9       0.98      0.97      0.98      1009

    accuracy                           0.98     10000
   macro avg       0.98      0.98      0.98     10000
weighted avg       0.98      0.98      0.98     10000</code></pre><h3 id="모델-저장과-복원">모델 저장과 복원</h3>
<ul>
<li><p><code>save()</code></p>
</li>
<li><p><code>load_model()</code></p>
</li>
<li><p>Sequencial API, 함수형 API에서는 모델의 저장 및 로드가 가능하지만 서브클래싱 방식으로는 할 수 없음</p>
</li>
<li><p>서브클래싱 방식은 <code>save_weights()</code>와 <code>load_weights()</code>를 이용해 모델의 파라미터만 저장 및 로드</p>
</li>
<li><p>custom_objects 옵션에 클래스 정의하여 가능 -&gt; models.load_model(&#39;mnist_model.h5&#39;, custom_objects={&#39;MyMode&#39;: MyModel})</p>
</li>
<li><p>JSON 형식</p>
<ul>
<li><code>model.to_json()</code> (저장)</li>
<li><code>tf.keras.models.model_from_json(file_path)</code> (복원)</li>
</ul>
</li>
<li><p>YAML로 직렬화</p>
<ul>
<li><code>model.to_yaml()</code> (저장)</li>
<li><code>tf.keras.models.model_from_yaml(file_path)</code> (복원)</li>
</ul>
</li>
</ul>
<pre><code class="language-python">model.save(&#39;mnist_model.h5&#39;)</code></pre>
<pre><code>WARNING:absl:You are saving your model as an HDF5 file via `model.save()` or `keras.saving.save_model(model)`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save(&#39;my_model.keras&#39;)` or `keras.saving.save_model(model, &#39;my_model.keras&#39;)`. </code></pre><pre><code class="language-python">loaded_model = models.load_model(&#39;mnist_model.h5&#39;)
loaded_model.summary()</code></pre>
<pre><code>WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.</code></pre><pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_2"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ flatten (<span style="color: #0087ff; text-decoration-color: #0087ff">Flatten</span>)               │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">784</span>)            │             <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_8 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">100</span>)            │        <span style="color: #00af00; text-decoration-color: #00af00">78,500</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">6,464</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">2,080</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">330</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">87,376</span> (341.32 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">87,374</span> (341.30 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Optimizer params: </span><span style="color: #00af00; text-decoration-color: #00af00">2</span> (12.00 B)
</pre>




<pre><code class="language-python">pred_y2 = loaded_model.predict(x_test)
arg_pred_y = np.argmax(pred_y2[0])

plt.imshow(x_test[0], cmap=&#39;gray&#39;)
plt.title(f&#39;predicted label : {arg_pred_y}&#39;)
plt.show()</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 782us/step</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/29134a90-63e0-4309-baea-f1d05f05b3e6/image.png" alt=""></p>
<h3 id="콜백-callbacks">콜백 (Callbacks)</h3>
<ul>
<li>모델의 학습 과정에서 특정 지점에서 자동으로 호출되는 함수 또는 작업의 집합</li>
<li><code>fit()</code> 함수의 callbacks 매개변수를 사용하여 케라스가 훈련의 시작이나 끝에 호출할 객체 리스트를 지정할 수 있음</li>
<li>여러 개 사용 가능</li>
<li>ModelCheckpoint<ul>
<li><code>tf.keras.callbacks.ModelCheckpoint</code></li>
<li>정기적으로 모델의 체크포인트를 저장하고, 문제가 발생할 때 복구하는데 사용</li>
</ul>
</li>
<li>EarlyStopping<ul>
<li><code>tf.keras.callbacks.EarlyStopping</code></li>
<li>검증 성능이 한동안 개선되지 않을 경우 학습을 중단할 때 사용</li>
</ul>
</li>
<li>LearningRateSchduler<ul>
<li><code>tf.keras.callbacks.LearningRateSchduler</code></li>
<li>최적화를 하는 동안 학습률(learning_rate)를 동적으로 변경할 때 사용</li>
</ul>
</li>
<li>TensorBoard<ul>
<li><code>tf.keras.callbacks.TensorBoard</code></li>
<li>모델의 경과를 모니터링할 때 사용</li>
</ul>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, LearningRateScheduler,TensorBoard</code></pre>
<h4 id="modelcheckpoint">ModelCheckpoint</h4>
<pre><code class="language-python">check_point_cb = ModelCheckpoint(&#39;keras_mnist_model.keras&#39;) # 저장할 model의 path 설정, 확장자h5 확인 필요
history = model.fit(x_train, y_train, epochs=10, callbacks=[check_point_cb])</code></pre>
<pre><code>Epoch 1/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9882 - loss: 0.0485
Epoch 2/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 907us/step - accuracy: 0.9900 - loss: 0.0307
Epoch 3/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 1ms/step - accuracy: 0.9936 - loss: 0.0195  
Epoch 4/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 1ms/step - accuracy: 0.9922 - loss: 0.0239
Epoch 5/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 923us/step - accuracy: 0.9935 - loss: 0.0188
Epoch 6/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 977us/step - accuracy: 0.9951 - loss: 0.0148
Epoch 7/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 913us/step - accuracy: 0.9949 - loss: 0.0162
Epoch 8/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 916us/step - accuracy: 0.9959 - loss: 0.0139
Epoch 9/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 927us/step - accuracy: 0.9953 - loss: 0.0141
Epoch 10/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 948us/step - accuracy: 0.9941 - loss: 0.0180</code></pre><p>최상의 모델만을 저장: <code>save_best_only=True</code></p>
<pre><code class="language-python">check_point_cb = ModelCheckpoint(&#39;keras_mnist_model.keras&#39;, save_best_only=True) # 저장할 model의 path 설정, 확장자h5 확인 필요
history = model.fit(x_train, y_train, epochs=10,
                    validation_data=(x_val, y_val),
                    callbacks=[check_point_cb])</code></pre>
<pre><code>Epoch 1/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9973 - loss: 0.0087 - val_accuracy: 0.9727 - val_loss: 0.1711
Epoch 2/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9963 - loss: 0.0102 - val_accuracy: 0.9758 - val_loss: 0.1526
Epoch 3/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9987 - loss: 0.0056 - val_accuracy: 0.9706 - val_loss: 0.1943
Epoch 4/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9968 - loss: 0.0089 - val_accuracy: 0.9717 - val_loss: 0.2081
Epoch 5/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9974 - loss: 0.0082 - val_accuracy: 0.9734 - val_loss: 0.1849
Epoch 6/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9968 - loss: 0.0107 - val_accuracy: 0.9706 - val_loss: 0.2134
Epoch 7/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9960 - loss: 0.0132 - val_accuracy: 0.9694 - val_loss: 0.2081
Epoch 8/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9970 - loss: 0.0087 - val_accuracy: 0.9751 - val_loss: 0.1717
Epoch 9/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9976 - loss: 0.0085 - val_accuracy: 0.9724 - val_loss: 0.1924
Epoch 10/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9979 - loss: 0.0067 - val_accuracy: 0.9726 - val_loss: 0.1963</code></pre><h4 id="earlystopping">EarlyStopping</h4>
<ul>
<li>일정 <code>patience</code> 동안 검증 세트에 대한 점수가 오르지 않으면 학습을 멈춤</li>
<li>모델이 향상되지 않으면 학습이 자동으로 중지되므로, <code>epochs</code> 숫자를 크게 해도 무방</li>
<li>학습이 끝난 후의 최상의 가중치를 복원하기 때문에 모델을 따로 복원할 필요없음</li>
</ul>
<pre><code class="language-python">check_point_cb = ModelCheckpoint(&#39;keras_mnist_model.keras&#39;, save_best_only=True)
early_stopping_cb = EarlyStopping(patience=3, monitor=&#39;val_loss&#39;, restore_best_weights=True)
history = model.fit(x_train, y_train, epochs=10,
                    validation_data=(x_val, y_val),
                    callbacks=[check_point_cb])

&#39;&#39;&#39; 
restore_best_weights=True 의 의미:
True 설정: 훈련이 조기 종료될 때, 가장 좋은 성능을 보였던 시점의 모델 가중치를 자동으로 복원합니다.
즉, monitor 파라미터로 지정된 메트릭 (예: val_loss)을 기준으로 최소/최대 값을 달성했을 때의 가중치로 모델을 복원합니다.
이는 훈련이 더 이상 성능 개선을 이루지 못하고 종료되더라도, 가장 좋은 상태의 모델을 사용할 수 있도록 보장합니다.

False 설정: 훈련이 종료될 때 현재의 가중치를 그대로 유지합니다.
이 경우, 성능이 가장 좋았던 시점의 가중치로 복원되지 않고,
훈련 중단 시점의 가중치가 모델에 남게 됩니다.
&#39;&#39;&#39;</code></pre>
<pre><code>Epoch 1/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9967 - loss: 0.0125 - val_accuracy: 0.9726 - val_loss: 0.1946
Epoch 2/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9975 - loss: 0.0070 - val_accuracy: 0.9699 - val_loss: 0.2254
Epoch 3/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9965 - loss: 0.0126 - val_accuracy: 0.9729 - val_loss: 0.1826
Epoch 4/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9989 - loss: 0.0045 - val_accuracy: 0.9737 - val_loss: 0.1836
Epoch 5/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9985 - loss: 0.0056 - val_accuracy: 0.9680 - val_loss: 0.2198
Epoch 6/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9964 - loss: 0.0115 - val_accuracy: 0.9739 - val_loss: 0.1783
Epoch 7/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9982 - loss: 0.0071 - val_accuracy: 0.9745 - val_loss: 0.1892
Epoch 8/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9989 - loss: 0.0047 - val_accuracy: 0.9722 - val_loss: 0.1979
Epoch 9/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9969 - loss: 0.0110 - val_accuracy: 0.9741 - val_loss: 0.1942
Epoch 10/10
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 1ms/step - accuracy: 0.9971 - loss: 0.0087 - val_accuracy: 0.9751 - val_loss: 0.2046





&#39; \nrestore_best_weights=True 의 의미:\nTrue 설정: 훈련이 조기 종료될 때, 가장 좋은 성능을 보였던 시점의 모델 가중치를 자동으로 복원합니다.\n즉, monitor 파라미터로 지정된 메트릭 (예: val_loss)을 기준으로 최소/최대 값을 달성했을 때의 가중치로 모델을 복원합니다.\n이는 훈련이 더 이상 성능 개선을 이루지 못하고 종료되더라도, 가장 좋은 상태의 모델을 사용할 수 있도록 보장합니다.\n\nFalse 설정: 훈련이 종료될 때 현재의 가중치를 그대로 유지합니다.\n이 경우, 성능이 가장 좋았던 시점의 가중치로 복원되지 않고,\n훈련 중단 시점의 가중치가 모델에 남게 됩니다.\n&#39;</code></pre><h4 id="learningratescheduler">LearningRateScheduler</h4>
<ul>
<li>학습 중에 학습률(learning rate)을 변경시키기 위해 사용</li>
</ul>
<pre><code class="language-python">def scheduler(epoch, learning_rate):
    if epoch &lt; 10:
        return learning_rate
    else:
        print(type(learning_rate * tf.math.exp(-0.1)))
        return learning_rate * 0.95</code></pre>
<pre><code class="language-python">round(model.optimizer.learning_rate.numpy(),5)</code></pre>
<pre><code>0.001</code></pre><pre><code class="language-python">lr_scheduler_cb = LearningRateScheduler(scheduler)

history = model.fit(x_train, y_train, epochs=15,
                    callbacks=[lr_scheduler_cb])</code></pre>
<pre><code>Epoch 1/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 924us/step - accuracy: 0.9987 - loss: 0.0049 - learning_rate: 0.0010
Epoch 2/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 890us/step - accuracy: 0.9992 - loss: 0.0034 - learning_rate: 0.0010
Epoch 3/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 950us/step - accuracy: 0.9993 - loss: 0.0031 - learning_rate: 0.0010
Epoch 4/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 913us/step - accuracy: 0.9980 - loss: 0.0096 - learning_rate: 0.0010
Epoch 5/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 917us/step - accuracy: 0.9990 - loss: 0.0036 - learning_rate: 0.0010
Epoch 6/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 910us/step - accuracy: 0.9987 - loss: 0.0046 - learning_rate: 0.0010
Epoch 7/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 904us/step - accuracy: 0.9986 - loss: 0.0063 - learning_rate: 0.0010
Epoch 8/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 935us/step - accuracy: 0.9989 - loss: 0.0037 - learning_rate: 0.0010
Epoch 9/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 958us/step - accuracy: 0.9986 - loss: 0.0059 - learning_rate: 0.0010
Epoch 10/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 904us/step - accuracy: 0.9988 - loss: 0.0040 - learning_rate: 0.0010
&lt;class &#39;tensorflow.python.framework.ops.EagerTensor&#39;&gt;
Epoch 11/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 905us/step - accuracy: 0.9984 - loss: 0.0053 - learning_rate: 9.5000e-04
&lt;class &#39;tensorflow.python.framework.ops.EagerTensor&#39;&gt;
Epoch 12/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 908us/step - accuracy: 0.9988 - loss: 0.0041 - learning_rate: 9.0250e-04
&lt;class &#39;tensorflow.python.framework.ops.EagerTensor&#39;&gt;
Epoch 13/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 907us/step - accuracy: 0.9998 - loss: 0.0010 - learning_rate: 8.5737e-04
&lt;class &#39;tensorflow.python.framework.ops.EagerTensor&#39;&gt;
Epoch 14/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 905us/step - accuracy: 0.9989 - loss: 0.0049 - learning_rate: 8.1451e-04
&lt;class &#39;tensorflow.python.framework.ops.EagerTensor&#39;&gt;
Epoch 15/15
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 930us/step - accuracy: 0.9994 - loss: 0.0016 - learning_rate: 7.7378e-04</code></pre><pre><code class="language-python">round(model.optimizer.learning_rate.numpy(),5)</code></pre>
<pre><code>0.00077</code></pre><h4 id="tensorboard">Tensorboard</h4>
<ul>
<li>TensorFlow와 Keras에서 사용하는 모델의 훈련 과정을 모니터링하고 디버깅하는 데 유용한 시각화 도구</li>
<li>텐서보드를 이용하여 학습과정 모니터링</li>
<li>텐서보드를 사용하기 위해 logs 폴더를 만들고, 학습이 진행되는 동안 로그 파일을 생성</li>
</ul>
<p>----- 텐서보드 옵션-----</p>
<ul>
<li>histogram_freq: 이 옵션은 몇 번째 에포크마다 히스토그램을 기록할지 설정</li>
<li>write_graph: 이 설정은 True로 설정되면, 훈련 중에 모델 구조 그래프를 기록하여 TensorBoard에서 볼 수 있습니다. </li>
<li>그래프는 모델의 구조를 시각화해줍니다. 이는 모델의 구조를 이해하거나 문제를 진단하는 데 도움을 줄 수 있습니다.</li>
<li>write_images: True일 경우 에포크마다 가중치를 이미지로 기록. 가중치가 어떻게 변화하는지 시각적으로 분석할 수 있는 기능</li>
</ul>
<pre><code class="language-python">log_dir = &#39;\\logs&#39; # 파일 경로 한글 없게
tensor_board_cb = [TensorBoard(log_dir=log_dir, histogram_freq=1, write_graph=True, write_images=True)]
history = model.fit(x_train, y_train, epochs=30,
                    validation_data=(x_val, y_val),
                    callbacks=tensor_board_cb)</code></pre>
<pre><code>Epoch 1/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9997 - loss: 0.0014 - val_accuracy: 0.9769 - val_loss: 0.2594
Epoch 2/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9994 - loss: 0.0021 - val_accuracy: 0.9754 - val_loss: 0.2795
Epoch 3/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9997 - loss: 9.3488e-04 - val_accuracy: 0.9766 - val_loss: 0.3055
Epoch 4/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0011 - val_accuracy: 0.9751 - val_loss: 0.3416
Epoch 5/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9986 - loss: 0.0056 - val_accuracy: 0.9769 - val_loss: 0.2949
Epoch 6/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 2ms/step - accuracy: 0.9994 - loss: 0.0019 - val_accuracy: 0.9743 - val_loss: 0.3046
Epoch 7/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 2ms/step - accuracy: 0.9990 - loss: 0.0041 - val_accuracy: 0.9749 - val_loss: 0.2963
Epoch 8/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 2ms/step - accuracy: 0.9994 - loss: 0.0030 - val_accuracy: 0.9767 - val_loss: 0.2759
Epoch 9/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 2ms/step - accuracy: 0.9995 - loss: 0.0023 - val_accuracy: 0.9747 - val_loss: 0.3031
Epoch 10/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 2ms/step - accuracy: 0.9991 - loss: 0.0025 - val_accuracy: 0.9769 - val_loss: 0.2728
Epoch 11/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0023 - val_accuracy: 0.9772 - val_loss: 0.2714
Epoch 12/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0013 - val_accuracy: 0.9753 - val_loss: 0.2902
Epoch 13/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0019 - val_accuracy: 0.9763 - val_loss: 0.2811
Epoch 14/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9994 - loss: 0.0030 - val_accuracy: 0.9759 - val_loss: 0.2879
Epoch 15/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9995 - loss: 0.0021 - val_accuracy: 0.9729 - val_loss: 0.3165
Epoch 16/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0010 - val_accuracy: 0.9766 - val_loss: 0.3044
Epoch 17/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0015 - val_accuracy: 0.9745 - val_loss: 0.3361
Epoch 18/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9993 - loss: 0.0042 - val_accuracy: 0.9748 - val_loss: 0.3142
Epoch 19/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0011 - val_accuracy: 0.9752 - val_loss: 0.3107
Epoch 20/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9995 - loss: 0.0017 - val_accuracy: 0.9757 - val_loss: 0.3031
Epoch 21/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9994 - loss: 0.0019 - val_accuracy: 0.9763 - val_loss: 0.3059
Epoch 22/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9999 - loss: 5.5803e-04 - val_accuracy: 0.9746 - val_loss: 0.3209
Epoch 23/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9991 - loss: 0.0041 - val_accuracy: 0.9753 - val_loss: 0.3280
Epoch 24/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9998 - loss: 8.1612e-04 - val_accuracy: 0.9769 - val_loss: 0.3084
Epoch 25/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9992 - loss: 0.0033 - val_accuracy: 0.9754 - val_loss: 0.3182
Epoch 26/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0017 - val_accuracy: 0.9760 - val_loss: 0.3251
Epoch 27/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 0.0015 - val_accuracy: 0.9763 - val_loss: 0.3228
Epoch 28/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9993 - loss: 0.0030 - val_accuracy: 0.9762 - val_loss: 0.3092
Epoch 29/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9996 - loss: 8.6534e-04 - val_accuracy: 0.9768 - val_loss: 0.3188
Epoch 30/30
[1m1313/1313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 2ms/step - accuracy: 0.9995 - loss: 0.0015 - val_accuracy: 0.9757 - val_loss: 0.3248</code></pre><pre><code class="language-python">%load_ext tensorboard # 코랩의 경우
%tensorboard --logdir {log_dir}</code></pre>
<ul>
<li>텐서보드 load가 안된다면 port 번호를 바꿔서 실행
<code>%tensorboard --logdir {log_dir} port 8000</code></li>
</ul>
<pre><code class="language-python"># 로컬
!tensorboard --logdir=&quot;c:\\Users\\rlaal\\OneDrive\\logs&quot; --port=8000</code></pre>
<pre><code>2024-10-21 02:31:21.254627: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2024-10-21 02:31:22.096791: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
usage: tensorboard [-h] [--helpfull] [--logdir PATH] [--logdir_spec PATH_SPEC]
                   [--host ADDR] [--bind_all] [--port PORT]
                   [--reuse_port BOOL] [--load_fast {false,auto,true}]
                   [--extra_data_server_flags EXTRA_DATA_SERVER_FLAGS]
                   [--grpc_creds_type {local,ssl,ssl_dev}]
                   [--grpc_data_provider PORT] [--purge_orphaned_data BOOL]
                   [--db URI] [--db_import] [--inspect] [--version_tb]
                   [--tag TAG] [--event_file PATH] [--path_prefix PATH]
                   [--window_title TEXT] [--max_reload_threads COUNT]
                   [--reload_interval SECONDS] [--reload_task TYPE]
                   [--reload_multifile BOOL]
                   [--reload_multifile_inactive_secs SECONDS]
                   [--generic_data TYPE]
                   [--samples_per_plugin SAMPLES_PER_PLUGIN]
                   [--detect_file_replacement BOOL]
                   {serve} ...
tensorboard: error: argument {serve}: invalid choice: &#39;c:\\\\Users\\\\rlaal\\\\OneDrive\\\\logs&#39; (choose from &#39;serve&#39;)</code></pre><h1 id="딥러닝-학습-기술">딥러닝 학습 기술</h1>
<h2 id="imdb-딥러닝-모델-예제">IMDB 딥러닝 모델 예제</h2>
<ul>
<li>영화 사이트 IMDB의 리뷰 데이터</li>
<li>텍스트 분류, 감성 분류를 위해 자주 사용하는 데이터</li>
<li>리뷰 텍스트와 리뷰가 긍정인 경우 1을 부정인 경우 0으로 표시한 레이블</li>
<li>케라스에서는 IMDB 영화 리뷰 데이터를 <code>imdb.load_data()</code> 함수를 통해 다운로드 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/6e9f1ef7-c9df-4a2f-bfa1-bb278850fbed/image.png" alt=""></p>
<pre><code class="language-python">from tensorflow.keras.datasets import imdb
import numpy as np</code></pre>
<pre><code class="language-python"># num_words=10000 데이터셋에서 가장 빈번하게 등장하는 상위 10,000개의 단어만을 포함하도록 제한
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000) </code></pre>
<pre><code class="language-python"># train_data의 숫자 자리를 1, 아닌곳 0

def vectorize_seq(seqs, dim=10000):
    &#39;&#39;&#39;
    각 리뷰(단어의 시퀀스)를 원-핫 인코딩 방식으로 벡터화합니다.
    원-핫 인코딩은 단어의 인덱스에 해당하는 위치의 값을 1로 설정하고,
    나머지는 0으로 설정하는 벡터 표현 방법입니다.
    &#39;&#39;&#39;

    results = np.zeros((len(seqs), dim))
    for i, seq in enumerate(seqs): 
        results[i,seq] = 1
    return results</code></pre>
<pre><code class="language-python">x_train = vectorize_seq(train_data)
x_test= vectorize_seq(test_data)

y_train = np.asarray(train_labels).astype(&#39;float32&#39;)
y_test = np.asarray(test_labels).astype(&#39;float32&#39;)</code></pre>
<pre><code class="language-python">import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense</code></pre>
<pre><code class="language-python">
model = Sequential([
    Dense(16, input_shape = (10000,), name = &#39;input&#39;),
    Dense(16, activation=&#39;relu&#39;, name = &#39;hidden&#39;),
    Dense(1, activation=&#39;sigmoid&#39;,name = &#39;output&#39;)])

model.compile(optimizer=&#39;adam&#39;,
            loss = &#39;binary_crossentropy&#39;,
            metrics=[&#39;acc&#39;])
model.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_1"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │       <span style="color: #00af00; text-decoration-color: #00af00">160,016</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">272</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ output (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │            <span style="color: #00af00; text-decoration-color: #00af00">17</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">model.optimizer.learning_rate = 0.0001
model_hist = model.fit(x_train, y_train,
                       epochs=20,
                       batch_size = 64,
                       validation_data=(x_test, y_test) )</code></pre>
<pre><code>Epoch 1/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m8s[0m 17ms/step - acc: 0.6973 - loss: 0.6106 - val_acc: 0.8600 - val_loss: 0.3992
Epoch 2/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 4ms/step - acc: 0.8852 - loss: 0.3409 - val_acc: 0.8816 - val_loss: 0.3086
Epoch 3/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 4ms/step - acc: 0.9185 - loss: 0.2442 - val_acc: 0.8879 - val_loss: 0.2833
Epoch 4/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 4ms/step - acc: 0.9336 - loss: 0.2010 - val_acc: 0.8892 - val_loss: 0.2772
Epoch 5/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9405 - loss: 0.1782 - val_acc: 0.8861 - val_loss: 0.2810
Epoch 6/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9466 - loss: 0.1590 - val_acc: 0.8853 - val_loss: 0.2882
Epoch 7/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9571 - loss: 0.1382 - val_acc: 0.8826 - val_loss: 0.2999
Epoch 8/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9631 - loss: 0.1204 - val_acc: 0.8775 - val_loss: 0.3185
Epoch 9/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9664 - loss: 0.1108 - val_acc: 0.8765 - val_loss: 0.3298
Epoch 10/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9718 - loss: 0.0977 - val_acc: 0.8732 - val_loss: 0.3488
Epoch 11/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9734 - loss: 0.0899 - val_acc: 0.8700 - val_loss: 0.3711
Epoch 12/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9774 - loss: 0.0808 - val_acc: 0.8696 - val_loss: 0.3875
Epoch 13/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9797 - loss: 0.0764 - val_acc: 0.8674 - val_loss: 0.4102
Epoch 14/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9828 - loss: 0.0676 - val_acc: 0.8636 - val_loss: 0.4400
Epoch 15/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9866 - loss: 0.0575 - val_acc: 0.8632 - val_loss: 0.4614
Epoch 16/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9878 - loss: 0.0554 - val_acc: 0.8611 - val_loss: 0.4892
Epoch 17/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9905 - loss: 0.0469 - val_acc: 0.8594 - val_loss: 0.5170
Epoch 18/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9923 - loss: 0.0411 - val_acc: 0.8571 - val_loss: 0.5487
Epoch 19/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9936 - loss: 0.0364 - val_acc: 0.8562 - val_loss: 0.5806
Epoch 20/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9945 - loss: 0.0331 - val_acc: 0.8543 - val_loss: 0.6128</code></pre><pre><code class="language-python">import matplotlib.pyplot as plt

epochs = range(1,21)
model_val_loss = model_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/e4219f8c-3f0b-4137-9850-12bd9a21c8f8/image.png" alt=""></p>
<h2 id="과소적합underfitting--과대적합overfitting">과소적합(Underfitting) / 과대적합(Overfitting)</h2>
<img src = https://media.geeksforgeeks.org/wp-content/cdn-uploads/20190523171258/overfitting_2.png>

<h3 id="과소적합-underfitting">과소적합 (Underfitting)</h3>
<ul>
<li>학습 데이터를 충분히 학습하지 않아 성능이 매우 안 좋은 경우</li>
<li>모델이 지나치게 단순한 경우</li>
<li>해결 방안<ul>
<li>충분한 학습 데이터 수집</li>
<li>보다 더 복잡한 모델 사용</li>
<li>에폭수(epochs)를 늘려 충분히 학습</li>
</ul>
</li>
</ul>
<h3 id="과대적합-overfitting">과대적합 (Overfitting)</h3>
<ul>
<li>모델이 학습 데이터에 지나치게 맞추어진 상태</li>
<li>새로운 데이터에서는 성능 저하</li>
<li>데이터에는 잡음이나 오류가 포함</li>
<li>학습 데이터가 매우 적을 경우</li>
<li>모델이 지나치게 복잡한 경우</li>
<li>학습 횟수가 매우 많을 경우</li>
<li>해결방안<ul>
<li>다양한 학습 데이터 수집 및 학습</li>
<li>모델 단순화: 파라미터가 적은 모델을 선택하거나, 학습 데이터의 특성 수를 줄임</li>
<li>정규화(Regularization)을 통한 규칙 단순화</li>
<li>적정한 하이퍼 파라미터 찾기</li>
</ul>
</li>
</ul>
<h3 id="과대적합overfitting과-과소적합underfitting-방지-방법">과대적합(overfitting)과 과소적합(underfitting) 방지 방법</h3>
<ul>
<li>모델의 크기 축소</li>
<li>가중치 초기화(Weight Initializer)</li>
<li>옵티마이저(Optimizer)</li>
<li>배치 정규화(Batch Normalization)</li>
<li>규제화(Regularization)</li>
<li>드롭아웃(Dropout)</li>
</ul>
<h2 id="모델-크기-조절">모델 크기 조절</h2>
<ul>
<li>가장 단순한 방법</li>
<li>모델의 크기를 줄인다는 것은 학습 파라미터의 수를 줄이는 것</li>
</ul>
<h3 id="모델-크기-감소">모델 크기 감소</h3>
<pre><code class="language-python">
model_s = Sequential([
    Dense(7, input_shape = (10000,), name = &#39;input&#39;),
    Dense(7, activation=&#39;relu&#39;, name = &#39;hidden&#39;),
    Dense(1, activation=&#39;sigmoid&#39;,name = &#39;output&#39;)])

model_s.compile(optimizer=&#39;adam&#39;,
            loss = &#39;binary_crossentropy&#39;,
            metrics=[&#39;acc&#39;])
model_s.summary()</code></pre>
<pre><code>c:\Users\rlaal\anaconda3\envs\py39\lib\site-packages\keras\src\layers\core\dense.py:87: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(activity_regularizer=activity_regularizer, **kwargs)</code></pre><pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_2"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>)              │        <span style="color: #00af00; text-decoration-color: #00af00">70,007</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">7</span>)              │            <span style="color: #00af00; text-decoration-color: #00af00">56</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ output (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │             <span style="color: #00af00; text-decoration-color: #00af00">8</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">70,071</span> (273.71 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">70,071</span> (273.71 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">model_s.optimizer.learning_rate = 0.0001
model_s_hist = model_s.fit(x_train, y_train,
                       epochs=20,
                       batch_size = 64,
                       validation_data=(x_test, y_test) )</code></pre>
<pre><code>Epoch 1/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m3s[0m 6ms/step - acc: 0.6636 - loss: 0.6506 - val_acc: 0.8339 - val_loss: 0.5109
Epoch 2/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.8666 - loss: 0.4570 - val_acc: 0.8669 - val_loss: 0.3952
Epoch 3/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.8919 - loss: 0.3462 - val_acc: 0.8784 - val_loss: 0.3369
Epoch 4/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9083 - loss: 0.2832 - val_acc: 0.8838 - val_loss: 0.3073
Epoch 5/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9148 - loss: 0.2512 - val_acc: 0.8876 - val_loss: 0.2901
Epoch 6/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9226 - loss: 0.2241 - val_acc: 0.8885 - val_loss: 0.2828
Epoch 7/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9316 - loss: 0.1982 - val_acc: 0.8888 - val_loss: 0.2789
Epoch 8/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9339 - loss: 0.1896 - val_acc: 0.8885 - val_loss: 0.2776
Epoch 9/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9437 - loss: 0.1713 - val_acc: 0.8870 - val_loss: 0.2807
Epoch 10/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9480 - loss: 0.1582 - val_acc: 0.8874 - val_loss: 0.2831
Epoch 11/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9535 - loss: 0.1460 - val_acc: 0.8854 - val_loss: 0.2877
Epoch 12/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9565 - loss: 0.1368 - val_acc: 0.8852 - val_loss: 0.2941
Epoch 13/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9613 - loss: 0.1268 - val_acc: 0.8825 - val_loss: 0.3006
Epoch 14/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9605 - loss: 0.1250 - val_acc: 0.8816 - val_loss: 0.3085
Epoch 15/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 3ms/step - acc: 0.9638 - loss: 0.1143 - val_acc: 0.8802 - val_loss: 0.3166
Epoch 16/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9685 - loss: 0.1068 - val_acc: 0.8795 - val_loss: 0.3252
Epoch 17/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9701 - loss: 0.1016 - val_acc: 0.8770 - val_loss: 0.3356
Epoch 18/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9719 - loss: 0.0960 - val_acc: 0.8753 - val_loss: 0.3463
Epoch 19/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9731 - loss: 0.0914 - val_acc: 0.8739 - val_loss: 0.3575
Epoch 20/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 2ms/step - acc: 0.9760 - loss: 0.0839 - val_acc: 0.8713 - val_loss: 0.3718</code></pre><pre><code class="language-python">import matplotlib.pyplot as plt

epochs = range(1,21)
model_s_val_loss = model_s_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, model_s_val_loss, &#39;bo&#39;, label=&#39;Model(small)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/6135c6a6-45ff-466f-b53c-562f96d6fec9/image.png" alt=""></p>
<h3 id="모델-크기-증가">모델 크기 증가</h3>
<pre><code class="language-python">
model_b = Sequential([
    Dense(128, input_shape = (10000,), name = &#39;input&#39;),
    Dense(128, activation=&#39;relu&#39;, name = &#39;hidden&#39;),
    Dense(1, activation=&#39;sigmoid&#39;,name = &#39;output&#39;)])

model_b.compile(optimizer=&#39;adam&#39;,
            loss = &#39;binary_crossentropy&#39;,
            metrics=[&#39;acc&#39;])
model_b.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_3"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                   │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)            │     <span style="color: #00af00; text-decoration-color: #00af00">1,280,128</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)            │        <span style="color: #00af00; text-decoration-color: #00af00">16,512</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ output (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │           <span style="color: #00af00; text-decoration-color: #00af00">129</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">1,296,769</span> (4.95 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">1,296,769</span> (4.95 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">model_b.optimizer.learning_rate = 0.0001
model_b_hist = model_b.fit(x_train, y_train,
                       epochs=20,
                       batch_size = 64,
                       validation_data=(x_test, y_test) )</code></pre>
<pre><code>Epoch 1/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m7s[0m 15ms/step - acc: 0.7663 - loss: 0.5186 - val_acc: 0.8875 - val_loss: 0.2858
Epoch 2/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9253 - loss: 0.2100 - val_acc: 0.8857 - val_loss: 0.2887
Epoch 3/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9443 - loss: 0.1578 - val_acc: 0.8791 - val_loss: 0.3134
Epoch 4/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9605 - loss: 0.1205 - val_acc: 0.8696 - val_loss: 0.3626
Epoch 5/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9664 - loss: 0.1010 - val_acc: 0.8686 - val_loss: 0.3954
Epoch 6/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9749 - loss: 0.0801 - val_acc: 0.8652 - val_loss: 0.4453
Epoch 7/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9808 - loss: 0.0698 - val_acc: 0.8593 - val_loss: 0.5088
Epoch 8/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9854 - loss: 0.0536 - val_acc: 0.8549 - val_loss: 0.5712
Epoch 9/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9890 - loss: 0.0431 - val_acc: 0.8505 - val_loss: 0.6445
Epoch 10/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9900 - loss: 0.0380 - val_acc: 0.8500 - val_loss: 0.7081
Epoch 11/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9927 - loss: 0.0300 - val_acc: 0.8483 - val_loss: 0.7733
Epoch 12/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9953 - loss: 0.0227 - val_acc: 0.8440 - val_loss: 0.8642
Epoch 13/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9950 - loss: 0.0201 - val_acc: 0.8456 - val_loss: 0.9303
Epoch 14/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9964 - loss: 0.0167 - val_acc: 0.8416 - val_loss: 1.0244
Epoch 15/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9971 - loss: 0.0140 - val_acc: 0.8430 - val_loss: 1.0881
Epoch 16/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9954 - loss: 0.0176 - val_acc: 0.8412 - val_loss: 1.1498
Epoch 17/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 13ms/step - acc: 0.9922 - loss: 0.0227 - val_acc: 0.8419 - val_loss: 1.1970
Epoch 18/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9954 - loss: 0.0151 - val_acc: 0.8419 - val_loss: 1.2428
Epoch 19/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9986 - loss: 0.0084 - val_acc: 0.8411 - val_loss: 1.3243
Epoch 20/20
[1m391/391[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m5s[0m 12ms/step - acc: 0.9996 - loss: 0.0045 - val_acc: 0.8406 - val_loss: 1.3812</code></pre><pre><code class="language-python">import matplotlib.pyplot as plt

epochs = range(1,21)
model_b_val_loss = model_b_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, model_s_val_loss, &#39;bo&#39;, label=&#39;Model(small)&#39;)
plt.plot(epochs, model_b_val_loss, &#39;g--&#39;, label=&#39;Model(big)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/59b24943-b58e-4edf-9917-3be4154b590a/image.png" alt=""></p>
<ul>
<li>볼륨이 큰 신경망일수록 빠르게 훈련데이터 모델링 가능(학습 손실이 낮아짐)</li>
<li>과대 적합에는 더욱 민감해짐</li>
<li>이는 학습-검증 데이터 손실을 통해 확인 가능</li>
</ul>
<pre><code class="language-python">model_train_loss = model_hist.history[&#39;loss&#39;]
model_s_train_loss = model_s_hist.history[&#39;loss&#39;]
model_b_train_loss = model_b_hist.history[&#39;loss&#39;]

import matplotlib.pyplot as plt

epochs = range(1,21)
model_b_val_loss = model_b_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_train_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, model_s_train_loss, &#39;bo&#39;, label=&#39;Model(small)&#39;)
plt.plot(epochs, model_b_train_loss, &#39;g--&#39;, label=&#39;Model(big)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/61319363-03a2-4b90-a263-1bc326cc8e52/image.png" alt=""></p>
<h2 id="옵티마이저optimizer">옵티마이저(Optimizer)</h2>
<h3 id="확률적-경사하강법stochastic-gradient-descent-sgd">확률적 경사하강법(Stochastic Gradient Descent, SGD)</h3>
<ul>
<li><p>전체를 한번에 계산하지 않고, <strong>확률적</strong>으로 일부 샘플을 뽑아 조금씩 나누어 학습을 시키는 과정  </p>
</li>
<li><p>반복할 때마다 다루는 데이터의 수가 적기때문에 한 번 처리하는 속도는 빠름  </p>
</li>
<li><p>한 번 학습할 때 필요한 메모리만 있으면 되므로 매우 큰 데이터셋에 대해서도 학습이 가능</p>
</li>
<li><p>확률적이기 때문에, 배치 경사하강법보다 불안정</p>
</li>
<li><p>손실함수의 최솟값에 이를 때까지 다소 위아래로 요동치면서 이동</p>
</li>
<li><p>따라서, 위와 같은 문제 때문에 <strong>미니 배치 경사하강법</strong>(mini-batch gradient descent)로 학습을 진행</p>
</li>
<li><p>요즘에는 보통 SGD라고하면 미니 배치 경사하강법을 의미하기도 함</p>
</li>
<li><p>SGD의 단점: 단순하지만 문제에 따라서 시간이 매우 오래걸림</p>
</li>
<li><p>SGD 수식</p>
<p>$\quad W \leftarrow W - \gamma \frac{\partial L}{\partial W}$  </p>
<ul>
<li>$\gamma$ : 학습률</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 배치 경사하강법: 전체 데이터를 사용하여 1 에포크에 1번 업데이트.
# 미니배치 경사하강법: 지정된 배치 크기만큼의 데이터를 사용하여 1 에포크에 여러 번 업데이트.
# SGD: 각 업데이트마다 1개의 데이터 샘플을 사용. 1 에포크에 데이터 수만큼 업데이트.</code></pre>
<h3 id="momentum">Momentum</h3>
<ul>
<li><p>운동량을 의미, 관성과 관련</p>
</li>
<li><p>이전 단계에서의 업데이트 방향을 참고하여 현재 단계의 파라미터 업데이트에 일종의 관성을 부여하는 방법</p>
</li>
<li><p>공이 그릇의 경사면을 따라서 내려가는 듯한 모습 (가속도를 받아 점점 빠르게 이동하는 것처럼 파라미터 업데이트도 가속)</p>
</li>
<li><p>이전의 속도를 유지하려는 성향</p>
</li>
<li><p>경사하강을 좀 더 유지하려는 성격을 지님</p>
</li>
<li><p>단순히 SGD만 사용하는 것보다 적게 방향이 변함</p>
</li>
<li><p>Momentum 수식</p>
<p>$
\qquad v \ \leftarrow \ \alpha \ v - \gamma \ \frac{\partial L}{\partial W} \
\qquad W \ \leftarrow \ W \ + \ v
$</p>
<ul>
<li>$\alpha$ : 관성계수</li>
<li>$v$ :  속도</li>
<li>$\gamma$ : 학습률</li>
<li>$
\frac{\partial L}{\partial W}\ 
$ : 손실함수에 대한 미분</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/efd50014-493e-44c7-8060-912e49fdf40b/image.png" alt=""></p>
<pre><code class="language-python">import tensorflow as tf
from tensorflow.keras.optimizers import SGD

optimizer = SGD(learning_rate=0.001, momentum=0.9)</code></pre>
<h3 id="nesterov">Nesterov</h3>
<ul>
<li><p>모멘텀의 방향으로 조금 앞선 곳에서 손실함수의 그라디언트를 구함</p>
</li>
<li><p>시간이 지날수록 조금 더 빨리 최솟값에 도달</p>
</li>
<li><p>Nesterov 수식</p>
<p>$
\qquad m \ \leftarrow \  \beta m - \eta \bigtriangledown_\theta J(\theta + \beta m) \
\qquad \theta \ \leftarrow \ \theta \ + m
$</p>
<ul>
<li>$h$ : 기존 기울기를 제곱하여 더한 값</li>
<li>$\eta$ : 학습률</li>
<li>$
\bigtriangledown_\theta J(\theta) \ 
$ : $\theta$에 대한 미분(그라디언트)</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/104834b2-56be-405d-987c-79c6ac347b33/image.png" alt=""></p>
<pre><code class="language-python">
import tensorflow as tf
from tensorflow.keras.optimizers import SGD

optimizer = SGD(learning_rate=0.001, momentum=0.9, nesterov=True)</code></pre>
<h3 id="adagradadaptive-gradient">AdaGrad(Adaptive Gradient)</h3>
<ul>
<li><p>가장 가파른 경사를 따라 빠르게 하강하는 방법</p>
</li>
<li><p>학습률을 변화시키며 진행하며 적응적 학습률이라고도 부름</p>
</li>
<li><p>경사가 급할 때는 빠르게 변화, 완만할 때는 느리게 변화</p>
</li>
<li><p>간단한 문제에서는 좋을 수는 있지만 딥러닝(Deep Learning)에서는 자주 쓰이지 않음</p>
</li>
<li><p>학습률이 너무 감소되어 전역최소값(global minimum)에 도달하기 전에 학습이 빨리 종료될 수 있기 때문</p>
</li>
<li><p>AdaGrad 수식</p>
<p>$
\qquad h \ \leftarrow \  h + \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \
\qquad W \ \leftarrow \ W \ + \gamma \frac{1}{\sqrt h} \ \frac{\partial L}{\partial W}
$</p>
<ul>
<li>$h$ : 기존 기울기를 제곱하여 더한 값</li>
<li>$gamma$ : 학습률</li>
<li>$
\frac{\partial L}{\partial W}\ 
$ : $W$에 대한 미분</li>
<li>과거의 기울기를 제곱하여 계속 더해하기 때문에 학습을 진행할수록 갱신 강도가 약해짐 ($\because \frac{1}{\sqrt h}$)    </li>
</ul>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.optimizers import Adagrad
optimizer = Adagrad(learning_rate = 0.001)</code></pre>
<h3 id="rmsprop-root-mean-square-propagation">RMSProp (Root Mean Square Propagation)</h3>
<ul>
<li><p>AdaGrad를 보완하기 위한 방법으로 등장</p>
</li>
<li><p>합 대신 지수의 평균값을 활용</p>
</li>
<li><p>학습이 안되기 시작하면 학습률이 커져서 잘 되게하고, 학습률이 너무 크면 학습률을 다시 줄임</p>
</li>
<li><p>RMSProp 수식</p>
<p>$
\qquad h \ \leftarrow \  \rho \ h + (1 - \rho)\ \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \
\qquad W \ \leftarrow \ W \ + \gamma \frac{\partial L}{\partial W} / \ \sqrt{h + \epsilon}
$</p>
<ul>
<li>$h\ $ : 기존 기울기를 제곱하여 업데이트 계수를 곱한 값과 업데이트 계수를 곱한 값을 더해줌</li>
<li>$\rho\ $ : 지수 평균의 업데이트 계수</li>
<li>$\gamma\ $ : 학습률</li>
<li>$
\frac{\partial L}{\partial W}\ 
$ : $W$에 대한 미분</li>
</ul>
</li>
</ul>
<pre><code class="language-python"># RMSProp 최적화 알고리즘의 하이퍼파라미터 설명
# learning_rate: 학습률 (기본값 = 0.001)
# rho: 이전 기울기의 이동 평균을 계산하는 데 사용되는 감쇠율 (기본값 = 0.9)
# epsilon: 분모가 0이 되는 것을 방지하기 위한 작은 상수 (기본값 = 1e-7)
# momentum: 모멘텀 계수 (기본값 = 0.0)
# centered: True일 경우 RMSProp의 centered 버전 사용 (기본값 = False)

from tensorflow.keras.optimizers import RMSprop
optimizer = RMSprop(learning_rate = 0.001, rho=0.9) # rho : 지수 평균의 업데이트 계수</code></pre>
<h3 id="adam-adaptive-moment-estimation">Adam (Adaptive Moment Estimation)</h3>
<ul>
<li><p>모멘텀 최적화와 RMSProp의 아이디어를 합친 것</p>
</li>
<li><p>지난 그래디언트의 지수 감소 평균을 따르고(Momentum), 지난 그레디언트 제곱의 지수 감소된 평균(RMSProp)을 따름</p>
</li>
<li><p>가장 많이 사용되는 최적화 방법</p>
</li>
<li><p>Adam 수식</p>
<p>$
\qquad t \ \leftarrow \ t + 1 \
\qquad m_t \ \leftarrow \  \beta_1 \ m_{t-1} - (1 - \beta_1)\ \frac{\partial L}{\partial W}  \
\qquad v_t \ \leftarrow \ \beta_2 \ v_{t-1} + (1 - \beta_2) \frac{\partial L}{\partial W} \odot \frac{\partial L}{\partial W} \
\qquad \hat{m_t} \ \leftarrow \frac{m_t}{1 - \beta_1^t} \
\qquad \hat{v_t} \ \leftarrow \frac{v_t}{1 - \beta_2^t} \
\qquad W_t \ \leftarrow \ W_{t-1} \ + \gamma \ \hat{m_t}\  / \sqrt{\hat{v_t} + \epsilon}
$</p>
<ul>
<li>$\beta$ : 지수 평균의 업데이트 계수</li>
<li>$\gamma$ : 학습률</li>
<li>$\beta_1 \approx 0.9\ ,\ \ \beta_2 \approx 0.999$</li>
<li>$
\frac{\partial L}{\partial W}\ 
$ : $W$에 대한 미분</li>
</ul>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.optimizers import Adam
optimizer = Adam(learning_rate = 0.001, beta_1=0.9, beta_2=0.999) # rho : 지수 평균의 업데이트 계수</code></pre>
<h3 id="최적화-optimizer-비교">최적화 Optimizer 비교</h3>
<img src="https://user-images.githubusercontent.com/11681225/50016682-39742a80-000d-11e9-81da-ab0406610b9c.gif" width="700">

<img src="https://img1.daumcdn.net/thumb/R720x0.q80/?scode=mtistory2&fname=http%3A%2F%2Fcfile25.uf.tistory.com%2Fimage%2F222B4F4F562BD0330EA41C">

<h2 id="가중치-초기화weights-initialization">가중치 초기화(Weights Initialization)</h2>
<ul>
<li>가중치 초기화 시각화: <a href="https://www.deeplearning.ai/ai-notes/initialization/">https://www.deeplearning.ai/ai-notes/initialization/</a></li>
</ul>
<h3 id="가중치-소실gradient-vanishing">가중치 소실(Gradient Vanishing)</h3>
<ul>
<li><p>활성화함수가 Sigmoid 함수 일 때, 은닉층의 갯수가 늘어 날수록 가중치가 역전파되면서 가중치 소실문제 발생</p>
<ul>
<li>시그모이드 함수의 미분값(0~1)사이 값이 거듭 곱해지면서 기울기가 출력층과 멀어질수록 미분값이 작아짐</li>
<li>이는 <u>미분값이 점점 0에 가까워짐</u>을 의미하기도 함</li>
<li><strong>ReLU 함수 등장(비선형 함수)</strong></li>
</ul>
</li>
<li><p>가중치 초기화 문제(은닉층의 활성화값 분포)</p>
<ul>
<li>가중치의 값이 일부 값으로 치우치게 되면, 활성화 함수를 통과한 값이 치우치게 되고, 표현할 수 있는 신경망의 수가 적어짐</li>
<li>따라서, 활성화값이 골고루 분포되는 것이 중요</li>
</ul>
</li>
</ul>
<img src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyJnMy%2FbtrCKh4ojvw%2FLtlaMwrtEZHC3KtGNWvuLK%2Fimg.png'>

<p><img src="https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F278186%2Fd158ec3585bc1551d9f3a03ae13a3a73%2Fvanishing%20gradient%20problem.png?generation=1574233763365617&alt=media" alt=""></p>
<h3 id="선형-함수에서-가중치-초기화">선형 함수에서 가중치 초기화</h3>
<h1 id="가중치-초기화의-중요성">가중치 초기화의 중요성</h1>
<ul>
<li>가중치 초기화는 딥러닝 모델의 학습 성능에 큰 영향을 미치는 중요한 요소입니다</li>
<li>잘못된 가중치 초기화는 다음과 같은 문제를 일으킬 수 있습니다:<ul>
<li>기울기 소실(Gradient Vanishing) 또는 폭주(Exploding) 문제 발생</li>
<li>학습 속도 저하</li>
<li>모델이 제대로 수렴하지 못함</li>
</ul>
</li>
</ul>
<p>주요 가중치 초기화 방법:</p>
<ol>
<li><p>0으로 초기화</p>
<ul>
<li>모든 뉴런이 동일한 출력을 내어 학습이 제대로 이루어지지 않음</li>
<li>실제로는 거의 사용되지 않음</li>
</ul>
</li>
<li><p>랜덤 초기화</p>
<ul>
<li>무작위로 작은 값을 할당</li>
<li>간단하지만 깊은 신경망에서는 문제가 발생할 수 있음</li>
</ul>
</li>
<li><p>Xavier/Glorot 초기화</p>
<ul>
<li>sigmoid, tanh 활성화 함수에 적합</li>
<li>입력과 출력 노드 수를 고려하여 초기화</li>
</ul>
</li>
<li><p>He 초기화</p>
<ul>
<li>ReLU 계열 활성화 함수에 적합</li>
<li>입력 노드 수만 고려하여 초기화</li>
<li>현대 딥러닝에서 가장 많이 사용되는 방식</li>
</ul>
</li>
</ol>
<p>적절한 가중치 초기화는 모델의 빠른 수렴과 좋은 성능을 위해 매우 중요합니다.</p>
<h2 id="배치-정규화-batch-normalization">배치 정규화 (Batch Normalization)</h2>
<ul>
<li>모델에 주입되는 샘플들을 균일하게 만드는 방법</li>
<li>가중치의 활성화값이 적당히 퍼지게끔 &#39;강제&#39;로 적용시키는 것</li>
<li>미니배치 단위로 데이터의 평균이 0, 표준편차가 1로 정규화</li>
<li>학습을 빨리 진행할 수 있음</li>
<li>학습 후 새로운 데이터에 잘 일반화 할 수 있도록 도와줌</li>
<li>초기값에 크게 의존하지 않아도 됨</li>
<li>과대적합 방지</li>
<li>데이터 전처리 단계에서 진행해도 되지만 정규화가 되어서 layer에 들어갔다는 보장이 없음</li>
<li>주로 <strong>Dense</strong> 또는 <strong>Conv2D</strong> Layer 후, <strong>활성화 함수</strong>이전에 놓임</li>
</ul>
<p><img src="https://www.jeremyjordan.me/content/images/2018/01/Screen-Shot-2018-01-23-at-2.27.20-PM.png" alt=""></p>
<h2 id="규제화regularization---가중치-감소">규제화(Regularization) - 가중치 감소</h2>
<ul>
<li><p>과대적합(Overfitting, 오버피팅)을 방지하는 방법 중 하나</p>
</li>
<li><p>과대적합은 가중치의 매개변수 값이 커서 발생하는 경우가 많음<br>이를 방지하기 위해 <strong>큰 가중치 값에 큰 규제를 가하는 것</strong></p>
</li>
<li><p>규제란 가중치의 절댓값을 가능한 작게 만드는 것으로, 가중치의 모든 원소를 0에 가깝게 하여 모든 특성이 출력에 주는 영향을 최소한으로 만드는 것(기울기를 작게 만드는 것)을 의미</p>
</li>
<li><p>가중치의 분포가 더 균일하게 됨</p>
</li>
<li><p>복잡한 네트워크 일수록 네트워크의 복잡도에 제한을 두어 가중치가 작은 값을 가지도록 함</p>
</li>
<li><p>규제란 과대적합이 되지 않도록 모델을 강제로 제한한다는 의미</p>
</li>
<li><p>적절한 규제값을 찾는 것이 중요</p>
</li>
<li><p>네트워크 손실함수에 큰 가중치와 연관된 비용을 추가</p>
<ul>
<li>L1 규제: 가중치의 절댓값에 비례하는 비용이 추가</li>
<li>L2 규제: 가중치의 제곱에 비례하는 비용이 추가(흔히 가중치 감쇠라고도 불림)</li>
<li>위 두 규제가 합쳐진 경우도 존재</li>
</ul>
</li>
</ul>
<h3 id="l2-규제">L2 규제</h3>
<ul>
<li><p>가중치의 제곱합</p>
</li>
<li><p>손실 함수일정 값을 더함으로써 과적합을 방지</p>
</li>
<li><p>$\lambda$ 값이 크면 가중치 감소가 커지고, 작으면 가하는 규제가 적어진다.</p>
</li>
<li><p>더 Robust한 모델을 생성하므로 L1보다 많이 사용됨</p>
<p>$\qquad Cost = \frac{1}{n} \sum{^n}_{i=1} {L(y_i, \hat{y_i}) + \frac{\lambda}{2}w^2}$</p>
<p>$\quad L(y_i, \hat{y_i})$ : 기존 Cost Function</p>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.regularizers import l1, l2, l1_l2

l2_model =  Sequential([Dense(16, kernel_regularizer=l2(0.001), activation=&#39;relu&#39;, input_shape=(10000,)),
                        Dense(16, kernel_regularizer=l2(0.001), activation=&#39;relu&#39;),
                        Dense(1, activation=&#39;relu&#39;)])
l2_model.compile(optimizer=&#39;rmsprop&#39;,
                loss=&#39;binary_crossentropy&#39;,
                metrics=[&#39;acc&#39;])
l2_model.summary()

plot_model(l2_model, show_shapes= True)</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_5"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense_3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │       <span style="color: #00af00; text-decoration-color: #00af00">160,016</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">272</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_5 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │            <span style="color: #00af00; text-decoration-color: #00af00">17</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">l2_model_hist = l2_model.fit(x_train, y_train, epochs=20, batch_size=512, validation_data=(x_test, y_test))</code></pre>
<pre><code>Epoch 1/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m4s[0m 60ms/step - acc: 0.6195 - loss: 1.1389 - val_acc: 0.8515 - val_loss: 0.4708
Epoch 2/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.8906 - loss: 0.3756 - val_acc: 0.8719 - val_loss: 0.4776
Epoch 3/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9220 - loss: 0.3026 - val_acc: 0.8650 - val_loss: 0.6070
Epoch 4/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9390 - loss: 0.2672 - val_acc: 0.8688 - val_loss: 0.7094
Epoch 5/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9469 - loss: 0.2527 - val_acc: 0.8731 - val_loss: 0.6746
Epoch 6/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9605 - loss: 0.2319 - val_acc: 0.8571 - val_loss: 0.8224
Epoch 7/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9652 - loss: 0.2242 - val_acc: 0.8745 - val_loss: 0.7368
Epoch 8/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9697 - loss: 0.2041 - val_acc: 0.8601 - val_loss: 0.9735
Epoch 9/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9666 - loss: 0.2243 - val_acc: 0.8762 - val_loss: 0.8641
Epoch 10/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9781 - loss: 0.2031 - val_acc: 0.8730 - val_loss: 0.9077
Epoch 11/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9798 - loss: 0.1919 - val_acc: 0.8635 - val_loss: 1.0065
Epoch 12/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9791 - loss: 0.1866 - val_acc: 0.8603 - val_loss: 1.0797
Epoch 13/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9816 - loss: 0.1798 - val_acc: 0.8593 - val_loss: 1.0720
Epoch 14/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 12ms/step - acc: 0.9860 - loss: 0.1669 - val_acc: 0.8703 - val_loss: 1.0340
Epoch 15/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9851 - loss: 0.1734 - val_acc: 0.8697 - val_loss: 0.9297
Epoch 16/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9899 - loss: 0.1516 - val_acc: 0.8712 - val_loss: 0.9510
Epoch 17/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.9927 - loss: 0.1462 - val_acc: 0.8692 - val_loss: 1.0476
Epoch 18/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9868 - loss: 0.1620 - val_acc: 0.8667 - val_loss: 1.1164
Epoch 19/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9875 - loss: 0.1554 - val_acc: 0.8704 - val_loss: 1.0520
Epoch 20/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9857 - loss: 0.1759 - val_acc: 0.8661 - val_loss: 1.0929</code></pre><pre><code class="language-python">import matplotlib.pyplot as plt
epochs = range(1,21)
model_val_loss = model_hist.history[&#39;val_loss&#39;]
l2_model_val_loss = l2_model_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, l2_model_val_loss, &#39;bo&#39;, label=&#39;Model(l2_regularize)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/ea9ad7e1-37e4-4b53-be1c-fa8631a41ee9/image.png" alt=""></p>
<h3 id="l1-규제">L1 규제</h3>
<ul>
<li><p>가중치의 절대값합</p>
</li>
<li><p>L2 규제와 달리 어떤 가중치는 0이 되는데 이는 모델이 가벼워짐을 의미</p>
<p>$\qquad Cost = \frac{1}{n} \sum{^n}_{i=1} {L(y_i, \hat{y_i}) + \frac{\lambda}{2}|w|}$</p>
<p>$\quad L(y_i, \hat{y_i})$ : 기존 Cost Function</p>
</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.regularizers import l1, l2, l1_l2

l1_model =  Sequential([Dense(16, kernel_regularizer=l1(0.0001), activation=&#39;relu&#39;, input_shape=(10000,)),
                        Dense(16, kernel_regularizer=l1(0.0001), activation=&#39;relu&#39;),
                        Dense(1, activation=&#39;relu&#39;)])
l1_model.compile(optimizer=&#39;rmsprop&#39;,
                loss=&#39;binary_crossentropy&#39;,
                metrics=[&#39;acc&#39;])
l1_model.summary()

plot_model(l1_model, show_shapes= True)</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_7"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense_9 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │       <span style="color: #00af00; text-decoration-color: #00af00">160,016</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_10 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">272</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_11 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │            <span style="color: #00af00; text-decoration-color: #00af00">17</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">l1_model_hist = l1_model.fit(x_train, y_train, epochs=20, batch_size=512, validation_data=(x_test, y_test))</code></pre>
<pre><code>Epoch 1/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m4s[0m 38ms/step - acc: 0.6668 - loss: 0.9749 - val_acc: 0.8598 - val_loss: 0.5532
Epoch 2/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.8981 - loss: 0.4217 - val_acc: 0.8712 - val_loss: 0.4843
Epoch 3/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9207 - loss: 0.3406 - val_acc: 0.8674 - val_loss: 0.5072
Epoch 4/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9268 - loss: 0.3240 - val_acc: 0.8785 - val_loss: 0.5719
Epoch 5/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9398 - loss: 0.2952 - val_acc: 0.8329 - val_loss: 0.7560
Epoch 6/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9373 - loss: 0.2952 - val_acc: 0.8701 - val_loss: 0.6519
Epoch 7/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9491 - loss: 0.2649 - val_acc: 0.8672 - val_loss: 0.8465
Epoch 8/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9519 - loss: 0.2617 - val_acc: 0.8752 - val_loss: 0.7189
Epoch 9/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9608 - loss: 0.2440 - val_acc: 0.8656 - val_loss: 0.7736
Epoch 10/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9607 - loss: 0.2518 - val_acc: 0.8726 - val_loss: 0.7548
Epoch 11/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9654 - loss: 0.2299 - val_acc: 0.8699 - val_loss: 0.8351
Epoch 12/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9686 - loss: 0.2313 - val_acc: 0.8734 - val_loss: 0.8613
Epoch 13/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9705 - loss: 0.2281 - val_acc: 0.8725 - val_loss: 0.9518
Epoch 14/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9745 - loss: 0.2085 - val_acc: 0.8719 - val_loss: 0.9590
Epoch 15/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9778 - loss: 0.2045 - val_acc: 0.8430 - val_loss: 1.1934
Epoch 16/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9732 - loss: 0.2112 - val_acc: 0.8647 - val_loss: 1.1163
Epoch 17/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9785 - loss: 0.2000 - val_acc: 0.8713 - val_loss: 1.0503
Epoch 18/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.9834 - loss: 0.1952 - val_acc: 0.8688 - val_loss: 1.0961
Epoch 19/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9847 - loss: 0.1890 - val_acc: 0.8704 - val_loss: 1.0849
Epoch 20/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 17ms/step - acc: 0.9861 - loss: 0.1807 - val_acc: 0.8697 - val_loss: 1.1318</code></pre><pre><code class="language-python">epochs = range(1,21)
model_val_loss = model_hist.history[&#39;val_loss&#39;]
l1_model_val_loss = l1_model_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, l1_model_val_loss, &#39;bo&#39;, label=&#39;Model(l1_regularize)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/c682e333-820e-4e5a-bf56-b63dfe57dfb8/image.png" alt=""></p>
<h3 id="l1-l2-규제">L1 L2 규제</h3>
<pre><code class="language-python">from tensorflow.keras.regularizers import l1, l2, l1_l2

l1_l2_model =  Sequential([Dense(16, kernel_regularizer=l1_l2(l1=0.0001, l2=0.0001), activation=&#39;relu&#39;, input_shape=(10000,)),
                           Dense(16, kernel_regularizer=l1_l2(l1=0.0001, l2=0.0001), activation=&#39;relu&#39;),
                           Dense(1, activation=&#39;sigmoid&#39;)])

l1_l2_model.compile(optimizer=&#39;rmsprop&#39;,
                loss=&#39;binary_crossentropy&#39;,
                metrics=[&#39;acc&#39;])
l1_l2_model.summary()

plot_model(l1_l2_model, show_shapes= True)</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "sequential_8"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense_12 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │       <span style="color: #00af00; text-decoration-color: #00af00">160,016</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_13 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">16</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">272</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_14 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">1</span>)              │            <span style="color: #00af00; text-decoration-color: #00af00">17</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">160,305</span> (626.19 KB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">l1_l2_model_hist = l1_l2_model.fit(x_train, y_train, epochs=20, batch_size=512, validation_data=(x_test, y_test))</code></pre>
<pre><code>Epoch 1/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 31ms/step - acc: 0.7163 - loss: 0.7340 - val_acc: 0.8803 - val_loss: 0.4520
Epoch 2/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.8981 - loss: 0.4003 - val_acc: 0.8698 - val_loss: 0.4016
Epoch 3/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9076 - loss: 0.3374 - val_acc: 0.8869 - val_loss: 0.3623
Epoch 4/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 16ms/step - acc: 0.9084 - loss: 0.3187 - val_acc: 0.8828 - val_loss: 0.3671
Epoch 5/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 18ms/step - acc: 0.9136 - loss: 0.3091 - val_acc: 0.8850 - val_loss: 0.3630
Epoch 6/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9169 - loss: 0.2990 - val_acc: 0.8856 - val_loss: 0.3611
Epoch 7/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9212 - loss: 0.2912 - val_acc: 0.8716 - val_loss: 0.3936
Epoch 8/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9246 - loss: 0.2853 - val_acc: 0.8750 - val_loss: 0.3913
Epoch 9/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9265 - loss: 0.2830 - val_acc: 0.8809 - val_loss: 0.3786
Epoch 10/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9304 - loss: 0.2692 - val_acc: 0.8778 - val_loss: 0.3820
Epoch 11/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9329 - loss: 0.2680 - val_acc: 0.8766 - val_loss: 0.3869
Epoch 12/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9372 - loss: 0.2570 - val_acc: 0.8793 - val_loss: 0.3808
Epoch 13/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9373 - loss: 0.2601 - val_acc: 0.8820 - val_loss: 0.3767
Epoch 14/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9354 - loss: 0.2566 - val_acc: 0.8665 - val_loss: 0.4184
Epoch 15/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 15ms/step - acc: 0.9390 - loss: 0.2523 - val_acc: 0.8725 - val_loss: 0.4026
Epoch 16/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9418 - loss: 0.2418 - val_acc: 0.8782 - val_loss: 0.3865
Epoch 17/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9497 - loss: 0.2311 - val_acc: 0.8783 - val_loss: 0.3876
Epoch 18/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9451 - loss: 0.2355 - val_acc: 0.8767 - val_loss: 0.3940
Epoch 19/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 13ms/step - acc: 0.9439 - loss: 0.2330 - val_acc: 0.8772 - val_loss: 0.3932
Epoch 20/20
[1m49/49[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 14ms/step - acc: 0.9499 - loss: 0.2245 - val_acc: 0.8680 - val_loss: 0.4272</code></pre><pre><code class="language-python">epochs = range(1,21)
model_val_loss = model_hist.history[&#39;val_loss&#39;]
l1_l2_model_val_loss = l1_l2_model_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, l1_l2_model_val_loss, &#39;bo&#39;, label=&#39;Model(l1_l2_regularize)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/26affbfd-9237-4dfe-9bc7-127a11e9c03f/image.png" alt=""></p>
<ul>
<li>규제 방법 비교</li>
</ul>
<pre><code class="language-python">epochs = range(1,21)
model_val_loss = model_hist.history[&#39;val_loss&#39;]
l1_model_val_loss = l1_model_hist.history[&#39;val_loss&#39;]
l2_model_val_loss = l2_model_hist.history[&#39;val_loss&#39;]
l1_l2_model_val_loss = l1_l2_model_hist.history[&#39;val_loss&#39;]

plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, l1_model_val_loss, &#39;bo&#39;, label=&#39;Model(l1_regularize)&#39;)
plt.plot(epochs, l2_model_val_loss, &#39;g--&#39;, label=&#39;Model(l2_regularize)&#39;)
plt.plot(epochs, l1_l2_model_val_loss, &#39;ko&#39;, label=&#39;Model(l1_l2_regularize)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f90df803-f63a-40cd-ae46-eefa6b6c5c2a/image.png" alt=""></p>
<h2 id="드롭아웃dropout">드롭아웃(Dropout)</h2>
<ul>
<li><p>신경망을 위해 사용되는 규제 기법 중 가장 효과적이고 널리 사용되는 방법</p>
</li>
<li><p>과적합을 방지하기 위한 방법</p>
</li>
<li><p>학습할 때 사용하는 노드의 수를 전체 노드 중에서 일부만을 사용</p>
</li>
<li><p>신경망의 레이어에 드롭아웃을 적용하면 훈련하는 동안 무작위로 층의 일부 특성(노드)를 제외</p>
<ul>
<li>예를 들어, [1.0, 3.2, 0.6, 0.8, 1.1] 라는 벡터에 대해 드롭아웃을 적용하면 <strong>무작위로 0으로 바뀜</strong> $\rightarrow$ [0, 3.2, 0.6, 0.8, 0]</li>
<li>보통 0.2 ~ 0.5 사이의 비율로 지정됨</li>
</ul>
</li>
<li><p>테스트 단계에서는 그 어떤 노드도 드롭아웃 되지 않고, 대신 해당 레이어의 출력 노드를 드롭아웃 비율에 맞게 줄여줌(ex 0.2 -&gt; *1.25)</p>
<img src="https://miro.medium.com/max/981/1*EinUlWw1n8vbcLyT0zx4gw.png" width="600">


</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.regularizers import l1, l2, l1_l2
from tensorflow.keras.layers import Dropout, Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import plot_model

dropout_model =  Sequential([Dense(16, activation=&#39;relu&#39;, input_shape=(10000,)),
                             Dropout(0.5),
                             Dense(16, activation=&#39;relu&#39;),
                             Dropout(0.5),
                             Dense(1, activation=&#39;sigmoid&#39;)])

dropout_model.compile(optimizer=&#39;rmsprop&#39;,
                loss=&#39;binary_crossentropy&#39;,
                metrics=[&#39;acc&#39;])
dropout_model.summary()

plot_model(dropout_model, show_shapes= True)</code></pre>
<pre><code class="language-python">dropout_model_hist = dropout_model.fit(x_train, y_train, epochs=20, batch_size=512, validation_data=(x_test, y_test))</code></pre>
<pre><code class="language-python">dropout_model_hist = dropout_model_hist.history[&#39;val_loss&#39;]

epochs = range(1,21)
plt.plot(epochs, model_val_loss, &#39;r+&#39;, label=&#39;Model&#39;)
plt.plot(epochs, dropout_model_val_loss, &#39;bo&#39;, label=&#39;Model(Dropout)&#39;)
plt.xlabel(&#39;Epochs&#39;)
plt.ylabel(&#39;Validation_Loss&#39;)
plt.legend()
plt.grid()
plt.show()</code></pre>
<h2 id="하이퍼-파라미터hyper-parameter">하이퍼 파라미터(Hyper Parameter)</h2>
<ul>
<li>사람이 직접 설정해야하는 매개변수</li>
<li>학습이 되기전 미리 설정되어 상수취급</li>
</ul>
<h3 id="학습률learning-rate">학습률(Learning Rate)</h3>
<ul>
<li>학습률에 따라 학습정도가 달라짐</li>
<li>적절한 학습률을 찾는 것이 핵심</li>
</ul>
<h3 id="학습-횟수epochs">학습 횟수(Epochs)</h3>
<ul>
<li>학습 횟수를 너무 작게, 또는 너무 크게 지정하면 과소적합 또는 과대적합 발생</li>
<li>여러번 진행하면서 최적의 학습 횟수(epochs)값을 찾아야함</li>
</ul>
<h3 id="미니배치-크기mini-batch-size">미니배치 크기(Mini Batch Size)</h3>
<ul>
<li>미니 배치 학습<ul>
<li>한번 학습할 때 메모리의 부족현상을 막기 위해 전체 데이터의 일부를 여러번 학습하는 방식</li>
</ul>
</li>
<li>한번 학습할 때마다 얼마만큼의 미니배치 크기를 사용할지 결정</li>
<li>배치 크기가 작을수록 학습 시간이 많이 소요되고, 클수록 학습 시간이 학습 시간은 적게 소요된다.  </li>
</ul>
<h3 id="검증데이터validation-data">검증데이터(Validation Data)</h3>
<ul>
<li><p>주어진 데이터를 학습 + 검증 + 테스트 데이터로 구분하여 과적합을 방지</p>
</li>
<li><p>일반적으로 전체 데이터의 2~30%를 테스트 데이터, 나머지에서 20%정도를 검증용 데이터, 남은 부분을 학습용 데이터로 사용</p>
<img src="https://miro.medium.com/max/1400/1*4G__SV580CxFj78o9yUXuQ.png" width="600">


</li>
</ul>
<h2 id="fashion-mnist-모델">Fashion MNIST 모델</h2>
<img src="https://www.tensorflow.org/tutorials/keras/classification_files/output_oZTImqg_CaW1_0.png?hl=ko" width="500">


<h3 id="모듈-임포트-1">모듈 임포트</h3>
<pre><code class="language-python">import tensorflow as tf
from tensorflow.keras.datasets.fashion_mnist import load_data
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras import models, Input
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.utils import plot_model
from tensorflow.keras.optimizers import Adam

from sklearn.model_selection import train_test_split

import numpy as np
import matplotlib.pyplot as plt
plt.style.use(&#39;seaborn-v0_8-white&#39;)</code></pre>
<h3 id="데이터-로드">데이터 로드</h3>
<pre><code class="language-python">tf.random.set_seed(42)
np.random.seed(42)

(x_train_full,y_train_full), (x_test,y_test) = load_data()

x_train, x_valid, y_train, y_valid = train_test_split(x_train_full, y_train_full, test_size=0.3, random_state=42)</code></pre>
<pre><code>Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
[1m29515/29515[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
[1m26421880/26421880[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
[1m5148/5148[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
[1m4422102/4422102[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 0us/step</code></pre><pre><code class="language-python">print(f&quot;학습 데이터 : {x_train.shape}&quot;)
print(f&quot;검증 데이터 : {x_valid.shape}&quot;)
print(f&quot;테스트 데이터 : {x_test.shape}&quot;)</code></pre>
<pre><code>학습 데이터 : (42000, 28, 28)
검증 데이터 : (18000, 28, 28)
테스트 데이터 : (10000, 28, 28)</code></pre><pre><code class="language-python">class_names = [&#39;T-shirt/Top&#39;, &#39;Trouser&#39;, &#39;Pullover&#39;, &#39;Dress&#39;, &#39;Coat&#39;,
               &#39;Sandal&#39;, &#39;Shirt&#39;, &#39;Sneaker&#39;, &#39;Bag&#39;, &#39;Ankle boot&#39;]</code></pre>
<pre><code class="language-python">class_names[y_train[0]]</code></pre>
<pre><code>&#39;Trouser&#39;</code></pre><pre><code class="language-python">plt.figure()
plt.imshow(x_train[0], cmap=&#39;gray&#39;)
plt.colorbar()
plt.grid(False)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/c292528d-fd87-4763-a205-798961a7c8ff/image.png" alt=""></p>
<pre><code class="language-python">num_sample = 4
random_idxs = np.random.randint(60000, size=num_sample)

plt.figure(figsize=(10,10))
for i, idx in enumerate(random_idxs):
    image = x_train_full[idx]
    label = y_train_full[idx]
    plt.subplot(1,len(random_idxs),i+1)
    plt.imshow(image, cmap=&#39;gray&#39;)
    plt.title(f&#39;index: {idx} label : {class_names[label]}&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/301eced6-52fc-46ef-a0e0-6cd6c5b4e67b/image.png" alt=""></p>
<h3 id="데이터-전처리">데이터 전처리</h3>
<ul>
<li>Normalization</li>
<li>Flatten</li>
<li>loss=&#39;sparse_categorical_crossentropy&#39;</li>
</ul>
<pre><code class="language-python"># 데이터 전처리
# 1. 이미지 데이터를 1차원 배열로 변환 (Flatten)
# 2. 픽셀값을 0~1 사이로 정규화 (Normalization)
# - 원본 이미지의 픽셀값(0~255)을 255로 나누어 0~1 사이의 값으로 변환
# - 이는 모델의 학습을 안정화하고 성능을 향상시키는데 도움이 됨

x_train = (x_train.reshape(-1,28*28))/255.0
x_valid = (x_valid.reshape(-1,28*28))/255.0
x_test = (x_test.reshape(-1,28*28))/255.0</code></pre>
<h3 id="모델-구성-함수형-api">모델 구성 (함수형 API)</h3>
<pre><code class="language-python">input = Input(shape=(784, ), name=&#39;input&#39;)
hidden1 = Dense(512, activation=&#39;relu&#39;, name=&#39;hidden1&#39;)(input)
hidden2 = Dense(256, activation=&#39;relu&#39;, name=&#39;hidden2&#39;)(hidden1)
hidden3 = Dense(128, activation=&#39;relu&#39;, name=&#39;hidden3&#39;)(hidden2)
hidden4 = Dense(64, activation=&#39;relu&#39;, name=&#39;hidden4&#39;)(hidden3)
hidden5 = Dense(32, activation=&#39;relu&#39;, name=&#39;hidden5&#39;)(hidden4)
output = Dense(10, activation=&#39;softmax&#39;, name=&#39;output&#39;)(hidden5)

model = Model(inputs=[input], outputs=[output])
model.summary()</code></pre>
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold">Model: "functional_9"</span>
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃<span style="font-weight: bold"> Layer (type)                    </span>┃<span style="font-weight: bold"> Output Shape           </span>┃<span style="font-weight: bold">       Param # </span>┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ input (<span style="color: #0087ff; text-decoration-color: #0087ff">InputLayer</span>)              │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">784</span>)            │             <span style="color: #00af00; text-decoration-color: #00af00">0</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden1 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">512</span>)            │       <span style="color: #00af00; text-decoration-color: #00af00">401,920</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden2 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">256</span>)            │       <span style="color: #00af00; text-decoration-color: #00af00">131,328</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden3 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">128</span>)            │        <span style="color: #00af00; text-decoration-color: #00af00">32,896</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden4 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">64</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">8,256</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ hidden5 (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                 │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">32</span>)             │         <span style="color: #00af00; text-decoration-color: #00af00">2,080</span> │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ output (<span style="color: #0087ff; text-decoration-color: #0087ff">Dense</span>)                  │ (<span style="color: #00d7ff; text-decoration-color: #00d7ff">None</span>, <span style="color: #00af00; text-decoration-color: #00af00">10</span>)             │           <span style="color: #00af00; text-decoration-color: #00af00">330</span> │
└─────────────────────────────────┴────────────────────────┴───────────────┘
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Total params: </span><span style="color: #00af00; text-decoration-color: #00af00">576,810</span> (2.20 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">576,810</span> (2.20 MB)
</pre>




<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="font-weight: bold"> Non-trainable params: </span><span style="color: #00af00; text-decoration-color: #00af00">0</span> (0.00 B)
</pre>




<pre><code class="language-python">plot_model(model, show_shapes=True)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/1e5f5ee5-0d76-47b9-87a9-850d4ca3c315/image.png" alt=""></p>
<h3 id="모델-컴파일-및-학습-1">모델 컴파일 및 학습</h3>
<pre><code class="language-python">model.compile(optimizer=Adam(learning_rate=0.01), #optimizer=&#39;adam&#39;
              loss=&#39;sparse_categorical_crossentropy&#39;,
              metrics=[&#39;accuracy&#39;])</code></pre>
<pre><code class="language-python">history = model.fit(x_train, y_train, epochs=30, batch_size=512, validation_data=(x_valid, y_valid))</code></pre>
<pre><code>Epoch 1/30


c:\Users\rlaal\anaconda3\envs\py39\lib\site-packages\keras\src\models\functional.py:225: UserWarning: The structure of `inputs` doesn&#39;t match the expected structure: [&#39;input&#39;]. Received: the structure of inputs=*
  warnings.warn(


[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m2s[0m 12ms/step - accuracy: 0.3034 - loss: 1.9492 - val_accuracy: 0.7751 - val_loss: 0.6158
Epoch 2/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 11ms/step - accuracy: 0.7996 - loss: 0.5476 - val_accuracy: 0.8348 - val_loss: 0.4601
Epoch 3/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8393 - loss: 0.4550 - val_accuracy: 0.8389 - val_loss: 0.4614
Epoch 4/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8516 - loss: 0.4150 - val_accuracy: 0.8584 - val_loss: 0.4023
Epoch 5/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8665 - loss: 0.3737 - val_accuracy: 0.8308 - val_loss: 0.4939
Epoch 6/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8676 - loss: 0.3632 - val_accuracy: 0.8564 - val_loss: 0.4082
Epoch 7/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8743 - loss: 0.3482 - val_accuracy: 0.8453 - val_loss: 0.4332
Epoch 8/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.8768 - loss: 0.3420 - val_accuracy: 0.8468 - val_loss: 0.4441
Epoch 9/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.8767 - loss: 0.3417 - val_accuracy: 0.8527 - val_loss: 0.4351
Epoch 10/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8837 - loss: 0.3291 - val_accuracy: 0.8513 - val_loss: 0.4447
Epoch 11/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8858 - loss: 0.3190 - val_accuracy: 0.8626 - val_loss: 0.3964
Epoch 12/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8885 - loss: 0.3064 - val_accuracy: 0.8612 - val_loss: 0.4014
Epoch 13/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.8947 - loss: 0.2927 - val_accuracy: 0.8724 - val_loss: 0.3677
Epoch 14/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8997 - loss: 0.2777 - val_accuracy: 0.8612 - val_loss: 0.4183
Epoch 15/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.8975 - loss: 0.2814 - val_accuracy: 0.8723 - val_loss: 0.3795
Epoch 16/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.9029 - loss: 0.2719 - val_accuracy: 0.8677 - val_loss: 0.3961
Epoch 17/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.8981 - loss: 0.2778 - val_accuracy: 0.8722 - val_loss: 0.3951
Epoch 18/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9055 - loss: 0.2661 - val_accuracy: 0.8683 - val_loss: 0.4019
Epoch 19/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9061 - loss: 0.2619 - val_accuracy: 0.8608 - val_loss: 0.4385
Epoch 20/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.9018 - loss: 0.2661 - val_accuracy: 0.8720 - val_loss: 0.4100
Epoch 21/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9072 - loss: 0.2547 - val_accuracy: 0.8704 - val_loss: 0.4092
Epoch 22/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9081 - loss: 0.2513 - val_accuracy: 0.8589 - val_loss: 0.4590
Epoch 23/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9085 - loss: 0.2563 - val_accuracy: 0.8642 - val_loss: 0.4246
Epoch 24/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9080 - loss: 0.2517 - val_accuracy: 0.8643 - val_loss: 0.4217
Epoch 25/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9070 - loss: 0.2497 - val_accuracy: 0.8701 - val_loss: 0.4414
Epoch 26/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9123 - loss: 0.2424 - val_accuracy: 0.8581 - val_loss: 0.4511
Epoch 27/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9082 - loss: 0.2515 - val_accuracy: 0.8727 - val_loss: 0.3929
Epoch 28/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9161 - loss: 0.2290 - val_accuracy: 0.8709 - val_loss: 0.4317
Epoch 29/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 8ms/step - accuracy: 0.9166 - loss: 0.2292 - val_accuracy: 0.8663 - val_loss: 0.4316
Epoch 30/30
[1m83/83[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m1s[0m 9ms/step - accuracy: 0.9153 - loss: 0.2345 - val_accuracy: 0.8696 - val_loss: 0.4381</code></pre><pre><code class="language-python"># 기록을 눈으로 보기 편하게 시각화

history_dict = history.history

loss = history_dict[&#39;loss&#39;]
val_loss = history_dict[&#39;val_loss&#39;]

epochs = range(1, len(loss)+1)
fig = plt.figure(figsize=(12,5))

ax1 = fig.add_subplot(1, 2, 1) # 행 / 열 / 위치
ax1.plot(epochs, loss, color=&#39;blue&#39;, label=&#39;train_loss&#39;)
ax1.plot(epochs, val_loss, color=&#39;red&#39;, label=&#39;val_loss&#39;)

ax1.set_title(&#39;Train and Validation Loss&#39;)
ax1.set_xlabel(&#39;Epochs&#39;)
ax1.set_ylabel(&#39;Loss&#39;)
ax1.grid()
ax1.legend()


accuracy = history_dict[&#39;accuracy&#39;]
val_accuracy = history_dict[&#39;val_accuracy&#39;]

ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(epochs, accuracy, color=&#39;blue&#39;, label=&#39;train_accuracy&#39;)
ax2.plot(epochs, val_accuracy, color=&#39;red&#39;, label=&#39;val_accuracy&#39;)

ax2.set_title(&#39;Train and Validation accuracy&#39;)
ax2.set_xlabel(&#39;Epochs&#39;)
ax2.set_ylabel(&#39;Loss&#39;)
ax2.grid()
ax2.legend()</code></pre>
<pre><code>&lt;matplotlib.legend.Legend at 0x1d90e56d760&gt;</code></pre><p><img src="https://velog.velcdn.com/images/s_s/post/03e0c9af-4283-49ac-aab1-c48ad338b546/image.png" alt=""></p>
<h3 id="모델-평가-및-예측-1">모델 평가 및 예측</h3>
<pre><code class="language-python">model.evaluate(x_test, y_test)</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step - accuracy: 0.8624 - loss: 0.4657





[0.47670942544937134, 0.8607000112533569]</code></pre><pre><code class="language-python">pred_y2 = model.predict(x_test)

print(pred_y2[0])
print(np.argmax(pred_y2[0]))</code></pre>
<pre><code>[1m313/313[0m [32m━━━━━━━━━━━━━━━━━━━━[0m[37m[0m [1m0s[0m 1ms/step
[7.7288782e-08 1.6886565e-08 5.5713816e-09 7.5809456e-09 1.1439542e-08
 3.1133941e-03 8.8125163e-10 3.7738454e-02 1.0476792e-08 9.5914805e-01]
9</code></pre><pre><code class="language-python">arg_pred_y = np.argmax(pred_y2, axis=1)
plt.imshow(x_test[0].reshape(-1,28), cmap=&#39;gray&#39;)
plt.title(f&#39;Predicted class : {class_names[arg_pred_y[0]]}&#39;)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/b58be512-1b4a-47cc-875b-9301b1d9792b/image.png" alt=""></p>
<pre><code class="language-python">def plot_image(i, predictions_array, true_label, img):
    predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
    plt.grid(False)
    plt.xticks([])
    plt.yticks([])

    plt.imshow(img.reshape(-1,28), cmap=plt.cm.binary)
    predicted_label = np.argmax(predictions_array)
    if predicted_label == true_label:
        color = &#39;blue&#39;
    else:
        color = &#39;red&#39;

    plt.xlabel(f&#39;{class_names[predicted_label]} / {class_names[true_label]}&#39;, color=color)

def plot_value_array(i, predictions_array, true_label):
    predictions_array, true_label = predictions_array[i], true_label[i]
    plt.grid(False)
    plt.yticks([])
    plt.xticks([])
    thisplot = plt.bar(range(10), predictions_array, color=&quot;#777777&quot;)
    #thisplot = plt.bar(class_names, predictions_array, color=&quot;#777777&quot;, label=class_names)
    #plt.xticks(rotation=90)
    plt.ylim([0, 1])
    predicted_label = np.argmax(predictions_array)
    thisplot[predicted_label].set_color(&#39;red&#39;)
    thisplot[true_label].set_color(&#39;blue&#39;)</code></pre>
<pre><code class="language-python">i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, pred_y2, y_test, x_test)
plt.subplot(1,2,2)
plot_value_array(i, pred_y2, y_test)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/c505aa22-c169-4c20-8488-275fbdf3424c/image.png" alt=""></p>
<pre><code class="language-python">i = 40
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, pred_y2, y_test, x_test)
plt.subplot(1,2,2)
plot_value_array(i, pred_y2, y_test)
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/e181cd09-a7b2-4ee5-af8f-68d35dd7f92f/image.png" alt=""></p>
<pre><code class="language-python">num_rows = 10
num_cols = 3
num_images = num_rows * num_cols

random_num = np.random.randint(10000, size=num_images)
plt.figure(figsize=(2*num_cols, num_rows))
for idx, num in enumerate(random_num):
    plt.subplot(num_rows, 2*num_cols, 2*idx+1)
    plot_image(num, pred_y2, y_test, x_test)
    plt.subplot(num_rows, 2*num_cols, 2*idx+2)
    plot_value_array(num, pred_y2, y_test)
plt.tight_layout()
plt.show()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f594f202-1616-4417-908d-6e0dffe3cbce/image.png" alt=""></p>
<ul>
<li>혼동 행렬 (Confusion Matrix)</li>
</ul>
<pre><code class="language-python">from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
plt.figure(figsize=(8,8))
cm = confusion_matrix(y_test, np.argmax(pred_y2, axis=1))
sns.heatmap(cm, annot=True, fmt=&#39;d&#39;, cmap=&#39;Blues&#39;)
plt.show(&#39;True Label&#39;)
plt.show()</code></pre>
<p><img src="blob:https://velog.io/cb823bfa-487d-4e1d-ae7e-70ef2160cd0f" alt="업로드중.."></p>
<ul>
<li>분류 보고서</li>
</ul>
<pre><code class="language-python">print(classification_report(y_test, np.argmax(pred_y2, axis=1)))</code></pre>
<pre><code>              precision    recall  f1-score   support

           0       0.88      0.73      0.80      1000
           1       0.92      0.98      0.95      1000
           2       0.80      0.74      0.77      1000
           3       0.86      0.85      0.85      1000
           4       0.69      0.89      0.78      1000
           5       0.93      0.97      0.95      1000
           6       0.69      0.62      0.65      1000
           7       0.91      0.95      0.93      1000
           8       0.97      0.96      0.97      1000
           9       0.98      0.91      0.94      1000

    accuracy                           0.86     10000
   macro avg       0.86      0.86      0.86     10000
weighted avg       0.86      0.86      0.86     10000</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Tensorflow를 활용한 인공신경망 구현1]]></title>
            <link>https://velog.io/@s_s/Tensorflow%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EA%B5%AC%ED%98%841</link>
            <guid>https://velog.io/@s_s/Tensorflow%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9D%B8%EA%B3%B5%EC%8B%A0%EA%B2%BD%EB%A7%9D-%EA%B5%AC%ED%98%841</guid>
            <pubDate>Tue, 04 Feb 2025 06:27:13 GMT</pubDate>
            <description><![CDATA[<p>본 자료는 이수안 교수님(<a href="https://suanlab.com/)%EC%9D%98">https://suanlab.com/)의</a> 자료를 일부 수정 후 업데이트 한 자료입니다. </p>
<p>ipynb 나 pdf 자료가 필요하신분은 연락주세요.</p>
<h1 id="인공신경망artificial-neural-network">인공신경망(Artificial Neural Network)</h1>
<ul>
<li>인간 두뇌에 대한 계산적 모델을 통해 인공지능을 구현하려는 분야</li>
<li>인간의 뇌 구조를 모방: 뉴런과 뉴런 사이에는 전기신호를 통해 정보를 전달  </li>
</ul>
<h2 id="생물학적-신경세포와-인공신경망-비교">생물학적 신경세포와 인공신경망 비교</h2>
<p><img src="https://velog.velcdn.com/images/s_s/post/41b514f0-0ec9-4b91-899d-4420d8c91e13/image.png" alt=""></p>
<ul>
<li><p>신경세포(Neuron)</p>
<ul>
<li>수상돌기(樹狀突起, Dendrite) : 다른 신경세포의 축색돌기와 연결되어 전기화학적 신호를 받아들이는 부위</li>
<li>축색돌기(軸索突起, Axon) : 수신한 전기화학적 신호의 합성결과 값이 특정 임계값이 이상이면 신호를 내보는 부위</li>
<li>신경연접(神經連接, Synapse) : 수상돌기와 축색돌기 연결 부위, 전달되는 신호의 증폭 또는 감쇄</li>
</ul>
</li>
<li><p>인공 뉴런(Artificial Neuron)</p>
<ul>
<li>신경세포 구조를 단순화하여 모델링한 구조</li>
<li>노드(Node)와 엣지(Edge)로 표현</li>
<li>하나의 노드안에서 입력(Inputs)와 가중치(Weights)를 곱하고 더하는 선형구조(linear)</li>
<li>활성화 함수(activation function)를 통한 비선형 구조(non-linear) 표현 가능</li>
</ul>
</li>
<li><p>인공 신경망(Artificial Neural Network)</p>
<ul>
<li>여러 개의 인공뉴런들이 모여 연결된 형태</li>
<li>뉴런들이 모인 하나의 단위를 층(layer)이라고 하고, 여러 층(multi layer)으로 이루어질 수 있음</li>
<li>ex) 입력층(input layer), 은닉층(hidden layer), 출력층(output layer)</li>
</ul>
<h1 id="딥러닝-프레임워크deep-learning-framework">딥러닝 프레임워크(Deep Learning Framework)</h1>
<h2 id="텐서플로우tensorflow">텐서플로우(Tensorflow)</h2>
</li>
</ul>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/TensorFlowLogo.svg/1200px-TensorFlowLogo.svg.png" width="300">

<ul>
<li><p>가장 널리 쓰이는 딥러닝 프레임워크 중 하나</p>
</li>
<li><p>구글이 주도적으로 개발하는 플랫폼</p>
</li>
<li><p>파이썬, C++ API를 기본적으로 제공하고,<br>자바스크립트(JavaScript), 자바(Java), 고(Go), 스위프트(Swift) 등 다양한 프로그래밍 언어를 지원</p>
</li>
<li><p>tf.keras를 중심으로 고수준 API 통합 (2.x 버전)</p>
</li>
<li><p>TPU(Tensor Processing Unit) 지원</p>
<ul>
<li>TPU는 GPU보다 전력을 적게 소모, 경제적</li>
<li>일반적으로 32비트(float32)로 수행되는 곱셈 연산을 16비트(float16)로 낮춤</li>
</ul>
<h2 id="케라스keras">케라스(Keras)</h2>
</li>
</ul>
<img src="https://s3.amazonaws.com/keras.io/img/keras-logo-2018-large-1200.png" width="320">

<ul>
<li>파이썬으로 작성된 고수준 신경망 API로 TensorFlow, CNTK, 혹은 Theano와 함께 사용 가능</li>
<li>사용자 친화성, 모듈성, 확장성을 통해 빠르고 간편한 프로토타이핑 가능</li>
<li>컨볼루션 신경망, 순환 신경망, 그리고 둘의 조합까지 모두 지원</li>
<li>CPU와 GPU에서 매끄럽게 실행</li>
</ul>
<h1 id="딥러닝-데이터-표현과-연산">딥러닝 데이터 표현과 연산</h1>
<ul>
<li>데이터 표현을 위한 기본 구조로 텐서(tensor)를 사용</li>
<li>텐서는 데이터를 담기위한 컨테이너(container)로서 일반적으로 수치형 데이터를 저장</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/60e3b8b6-ae7f-43fa-9d21-93ab86d8506c/image.png" alt=""></p>
<h2 id="텐서tensor">텐서(Tensor)</h2>
<ul>
<li>Rank: 축의 개수</li>
<li>Shape: 형상(각 축에 따른 차원 개수)</li>
<li>Type: 데이터 타입</li>
</ul>
<pre><code class="language-python">import tensorflow as tf
import numpy as np</code></pre>
<h3 id="0d-tensorscalar">0D Tensor(Scalar)</h3>
<ul>
<li>하나의 숫자를 담고 있는 텐서(tensor)</li>
<li>축과 형상이 없음</li>
</ul>
<pre><code class="language-python">t0 = tf.constant(1) 
print(t0)
print(tf.rank(t0)) # 축이 없는(0) 상태</code></pre>
<h3 id="1d-tensorvector">1D Tensor(Vector)</h3>
<ul>
<li>값들을 저장한 리스트와 유사한 텐서</li>
<li>하나의 축이 존재</li>
</ul>
<pre><code class="language-python">t1 = tf.constant([1,2,3]) 
print(t1)
print(tf.rank(t1))</code></pre>
<h3 id="2d-tensormatrix">2D Tensor(Matrix)</h3>
<ul>
<li>행렬과 같은 모양으로 두개의 축이 존재</li>
<li>일반적인 수치, 통계 데이터셋이 해당</li>
<li>주로 샘플(samples)과 특성(features)을 가진 구조로 사용</li>
</ul>
<img src="https://velog.velcdn.com/images/s_s/post/c3952a93-1ebc-485b-ad1c-de5328cc3368/image.png" width = 300 height=300 >



<pre><code class="language-python">t2 = tf.constant([[1,2,3],[4,5,6],[7,8,9]]) 
print(t2)
print(tf.rank(t2))
#tf.Tensor(
#[[1 2 3]
# [4 5 6]
# [7 8 9]], shape=(3, 3), dtype=int32)
#tf.Tensor(2, shape=(), dtype=int32)</code></pre>
<h3 id="3d-tensor">3D Tensor</h3>
<ul>
<li>큐브(cube)와 같은 모양으로 세개의 축이 존재</li>
<li>데이터가 연속된 시퀀스 데이터나 시간 축이 포함된 시계열 데이터에 해당</li>
<li>주식 가격 데이터셋, 시간에 따른 질병 발병 데이터 등이 존재</li>
<li>주로 샘플(samples), 타임스텝(timesteps), 특성(features)을 가진 구조로 사용</li>
</ul>
<img src="https://velog.velcdn.com/images/s_s/post/60fd1049-bf5f-4cdd-b545-3e2711348b97/image.png" width = 300 height=300>


<pre><code class="language-python">t3 = tf.constant([[[1,2,3],
                   [4,5,6],
                   [7,8,9]],
                  [[1,2,3],
                   [4,5,6],
                   [7,8,9]],
                  [[1,2,3],
                   [4,5,6],
                   [7,8,9]]]) 
print(t3)
print(tf.rank(t3)) # 축이 없는(0) 상태
</code></pre>
<p>output : 
tf.Tensor(
[[[1 2 3]
  [4 5 6]
  [7 8 9]]</p>
<p> [[1 2 3]
  [4 5 6]
  [7 8 9]]</p>
<p> [[1 2 3]
  [4 5 6]
  [7 8 9]]], shape=(3, 3, 3), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)</p>
<h3 id="4d-tensor">4D Tensor</h3>
<ul>
<li>4개의 축</li>
<li>컬러 이미지 데이터가 대표적인 사례 (흑백 이미지 데이터는 3D Tensor로 가능)</li>
<li>주로 샘플(samples), 높이(height), 너비(width), 컬러 채널(channel)을 가진 구조로 사용</li>
</ul>
<img src="https://velog.velcdn.com/images/s_s/post/8d722d94-9db1-4f63-ad24-bf15e7df28d3/image.png"  width = 300 height=300>

<h3 id="5d-tensor">5D Tensor</h3>
<ul>
<li>5개의 축</li>
<li>비디오 데이터가 대표적인 사례</li>
<li>주로 샘플(samples), 프레임(frames), 높이(height), 너비(width), 컬러 채널(channel)을 가진 구조로 사용</li>
</ul>
<h2 id="텐서-데이터-타입">텐서 데이터 타입</h2>
<ul>
<li>텐서의 기본 dtype<ul>
<li>정수형 텐서: <code>int32</code></li>
<li>실수형 텐서: <code>float32</code></li>
<li>문자열 텐서: <code>string</code></li>
</ul>
</li>
<li><code>int32</code>, <code>float32</code>,  <code>string</code> 타입 외에도 <code>float16</code>, <code>int8</code> 타입 등이 존재</li>
<li>연산시 텐서의 타입 일치 필요</li>
<li>타입변환에는 <code>tf.cast()</code> 사용</li>
</ul>
<pre><code class="language-pyhon">i = tf.constant(2) 
print(i)</code></pre>
<p>output : tf.Tensor(2, shape=(), dtype=int32)</p>
<pre><code class="language-python"># tf.constant(2.)는 실수형 텐서를 생성
# 기본 dtype은 float32tf.Tensor(2.0, shape=(), dtype=float32)
i = tf.constant(2.) 
print(i)</code></pre>
<p>output : tf.Tensor(2.0, shape=(), dtype=float32)</p>
<pre><code class="language-python"># tf.constant(&#39;ms&#39;)는 문자열 텐서를 생성
# 기본 dtype은 string
s = tf.constant(&#39;ms&#39;)
print(s) # b : 해당 문자열이 바이트(byte) 형식</code></pre>
<p>output : tf.Tensor(b&#39;ms&#39;, shape=(), dtype=string)</p>
<pre><code class="language-python">f16 = tf.constant(2., dtype=tf.float16)
print(f16)</code></pre>
<p>output : tf.Tensor(2.0, shape=(), dtype=float16)</p>
<pre><code class="language-python">i8 = tf.constant(2, dtype=tf.int8) 
print(i)</code></pre>
<p>output : tf.Tensor(2.0, shape=(), dtype=float32)</p>
<pre><code class="language-python">f32 = tf.cast(f16, tf.float32)
print(f32)</code></pre>
<p>output : tf.Tensor(2.0, shape=(), dtype=float32)</p>
<pre><code class="language-python">i32 = tf.cast(i8, tf.int32)
print(i32)</code></pre>
<p>output : tf.Tensor(2, shape=(), dtype=int32)</p>
<h2 id="텐서-연산">텐서 연산</h2>
<pre><code class="language-python"># 텐서 연산 예제
# tf.constant()를 사용하여 상수 텐서를 생성하고 기본적인 사칙연산을 수행
# 덧셈과 뺄셈은 + 와 - 연산자 또는 tf.add()와 tf.subtract() 함수를 사용할 수 있음
print(tf.constant(2) + tf.constant(2))
print(tf.constant(2) - tf.constant(2))
print(tf.add(tf.constant(2) , tf.constant(2)))
print(tf.subtract(tf.constant(2) , tf.constant(2)))</code></pre>
<p>output : 
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(0, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(0, shape=(), dtype=int32)</p>
<pre><code class="language-python">print(tf.constant(2) * tf.constant(2))
print(tf.constant(2) / tf.constant(2))
print(tf.multiply(tf.constant(2) , tf.constant(2)))
print(tf.divide(tf.constant(2) , tf.constant(2)))</code></pre>
<p>output : 
print(tf.constant(2) * tf.constant(2))
print(tf.constant(2) / tf.constant(2))
print(tf.multiply(tf.constant(2) , tf.constant(2)))
print(tf.divide(tf.constant(2) , tf.constant(2)))</p>
<pre><code class="language-python"># print(tf.constant(2) + tf.constant(2.2)) # 에러남
print(tf.cast(tf.constant(2),tf.float32) + tf.constant(2.2))</code></pre>
<p>output : tf.Tensor(4.2, shape=(), dtype=float32)</p>
<h1 id="딥러닝-구조-및-학습">딥러닝 구조 및 학습</h1>
<ul>
<li>딥러닝 구조와 학습에 필요한 요소<ul>
<li><strong>모델(네트워크)</strong>를 구성하는 <strong>레이어(layer)</strong></li>
<li><strong>입력 데이터</strong>와 그에 대한 <strong>목적(결과)</strong></li>
<li>학습시에 사용할 피드백을 정의하는 <strong>손실 함수(loss function)</strong></li>
<li>학습 진행 방식을 결정하는 <strong>옵티마이저(optimizer)</strong></li>
</ul>
</li>
</ul>
<h2 id="레이어layer">레이어(Layer)</h2>
<ul>
<li>신경망의 핵심 데이터 구조</li>
<li>하나 이상의 텐서를 입력받아 하나 이상의 텐서를 출력하는 데이터 처리 모듈</li>
<li>상태가 없는 레이어도 있지만, 대부분 <strong>가중치(weight)</strong>라는 레이어 상태를 가짐</li>
<li>가중치는 확률적 경사 하강법에 의해 학습되는 하나 이상의 텐서</li>
</ul>
<ul>
<li>Keras에서 사용되는 주요 레이어<ul>
<li>Dense</li>
<li>Activation</li>
<li>Flatten</li>
<li>Input</li>
</ul>
</li>
</ul>
<img src = "https://img.securityinfowatch.com/files/base/cygnus/siw/image/2019/02/Figure_01.5c7712513151e.png?auto=format,compress&fit=max&q=45&w=950&width=950">

<p><a href="https://www.securityinfowatch.com/video-surveillance/video-analytics/article/21069937/deep-learning-to-the-rescue">https://www.securityinfowatch.com/video-surveillance/video-analytics/article/21069937/deep-learning-to-the-rescue</a></p>
<pre><code class="language-python"># tensorflow.keras.layers에서 주요 레이어들을 import
# Dense: 완전연결계층을 구현하는 레이어
# Activation: 활성화 함수를 적용하는 레이어  
# Flatten: 다차원 입력을 1차원으로 펼치는 레이어
# Input: 모델의 입력을 정의하는 레이어
from tensorflow.keras.layers import Dense, Activation, Flatten, Input</code></pre>
<h3 id="dense">Dense</h3>
<ul>
<li><p>완전연결계층(Fully-Connected Layer)</p>
</li>
<li><p>노드수(유닛수), 활성화 함수(<code>activation</code>) 등을 지정</p>
</li>
<li><p><code>name</code>을 통한 레이어간 구분 가능</p>
</li>
<li><p>가중치 초기화(<code>kernel_initializer</code>)</p>
<ul>
<li>신경망의 성능에 큰 영향을 주는 요소  </li>
<li>보통 가중치의 초기값으로 0에 가까운 무작위 값 사용</li>
<li>특정 구조의 신경망을 동일한 학습 데이터로 학습시키더라도, 가중치의 초기값에 따라 학습된 신경망의 성능 차이가 날 수 있음</li>
<li>오차역전파 알고리즘은 기본적으로 경사하강법을 사용하기 때문에 최적해가 아닌 지역해에 빠질 가능성이 있음</li>
<li>Keras에서는 기본적으로 Glorot uniform 가중치(Xavier 분포 초기화), zeros bias로 초기화</li>
<li><code>kernel_initializer</code> 인자를 통해 다른 가중치 초기화 지정 가능</li>
<li>Keras에서 제공하는 가중치 초기화 종류: <a href="https://keras.io/api/layers/initializers/">https://keras.io/api/layers/initializers/</a></li>
</ul>
</li>
</ul>
<pre><code class="language-python"># 아래 레이어를 통과하면 결과로 10개의 출력 노드가 생성됨.
# Dense 레이어는 이전 레이어의 출력이 어떤 크기든 간에 
# 이를 받아들여 내부적으로 적절히 처리하고 10개의 출력을 생성
# 레이어를 정의할 때 입력 노드의 수를 명시적으로 지정할 필요는 없음.

Dense(10, activation=&#39;softmax&#39;)
Dense(10, activation=&#39;relu&#39;, name=&#39;Dense Layer&#39;)
Dense(10, kernel_initializer=&#39;he_normal&#39;, name=&#39;Dense Layer&#39;)</code></pre>
<h3 id="activation">Activation</h3>
<ul>
<li>Dense layer에서 미리 활성화 함수를 지정할 수도 있지만 필요에 따라 별도 레이어를 만들어줄 수 있음</li>
<li>Keras에서 제공하는 활성화 함수(activation function) 종류: <a href="https://keras.io/ko/activations/">https://keras.io/ko/activations/</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/c4e2ce6a-7f54-4a2b-93ab-8733adae1eba/image.png" alt=""></p>
<pre><code class="language-python"># 사용 예시
dense = Dense(10,activation=&#39;relu&#39;,name=&#39;Dense Layer&#39;)
Activation(dense)</code></pre>
<h3 id="flatten">Flatten</h3>
<ul>
<li>배치 크기(또는 데이터 크기)를 제외하고 데이터를 1차원으로 쭉 펼치는 작업</li>
<li>예시)<pre><code>  (128, 3, 2, 2) -&gt; (128, 12)</code></pre></li>
</ul>
<pre><code class="language-python"># 전체 출력은 (batch_size, height * width * channels) 형태를 가짐. 
# 여기서 첫 번째 차원은 배치 크기를 나타내고, 두 번째 차원은 평탄화된 피처를 나타냄.
# 따라서, Flatten 레이어의 출력 텐서는 랭크가 2인 2D 텐서임.
flatten = Flatten(input_shape=(128,3,2,2))</code></pre>
<pre><code class="language-python">### Input

- 모델의 입력을 정의
- `shape`, `dtype`을 포함
- 하나의 모델은 여러 개의 입력을 가질 수 있음
- `summary()` 메소드를 통해서는 보이지 않음</code></pre>
<pre><code class="language-python"># None: 이 위치의 None은 배치 크기(batch size)를 나타냅니다.
# None이 사용된 이유는 배치 크기가 미리 정의되지 않았고,
# 모델을 실행할 때 어떤 배치 크기도 사용될 수 있음을 의미함.
# 즉, 입력 데이터의 총 수는 가변적이며, 실제 모델 훈련이나 추론시에 결정됩니다.

Input(shape=(8,), dtype=tf.int32)</code></pre>
<h2 id="모델model">모델(Model)</h2>
<ul>
<li>딥러닝 모델은 레이어로 만들어진 비순환 유향(방향이 있는) 그래프(Directed Acyclic Graph, DAG) 구조</li>
</ul>
<h3 id="모델-구성">모델 구성</h3>
<ul>
<li><code>Sequential()</code></li>
<li>서브클래싱(Subclassing) - 클래스를 상속받아서 하는 방법</li>
<li>함수형 API</li>
</ul>
<h4 id="sequential">Sequential()</h4>
<ul>
<li>모델이 순차적인 구조로 진행할 때 사용</li>
<li>간단한 방법<ul>
<li>Sequential 객체 생성 후,<code>add()</code>를 이용한 방법</li>
<li>Sequential 인자에 한번에 추가 방법</li>
</ul>
</li>
<li>다중 입력 및 출력이 존재하는 등의 복잡한 모델을 구성할 수 없음</li>
</ul>
<pre><code class="language-python">from tensorflow.keras.layers import Dense, Input, Flatten
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.utils import plot_model

model = Sequential()
model.add(Input(shape=(28,28)))
model.add(Dense(300, activation=&#39;relu&#39;))
model.add(Dense(100, activation=&#39;relu&#39;))
model.add(Dense(10, activation=&#39;softmax&#39;))
model.summary()</code></pre>
<p>output : 
<img src="https://velog.velcdn.com/images/s_s/post/de5f74e6-9ef7-43ac-8de3-648bdcf45f0e/image.png" alt=""></p>
<p>Param = 가중치(weight, biases)의 총 수
가중치의 총 수 = (입력 유닛 수) x (출력 유닛 수)
바이어스의 총 수 = 출력 유닛 수(각 출력 유닛마다 하나의 바이어스가 있음)
총 파라미터 수 = 가중치의 총 수 + 바이어스의 총 수  </p>
<p>dense_4 = 28<em>300+300
dense_5 = 300</em>100+100
dense_6 = 100*10+10</p>
<pre><code class="language-python">#!pip install pydot
plot_model(model)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f230189a-6422-476d-bac4-e1fd0e8d6bff/image.png" alt=""></p>
<pre><code class="language-python">model = Sequential([
    Input(shape=(28,28)),
    Dense(300, activation=&#39;relu&#39;),
    Dense(100, activation=&#39;relu&#39;),
    Dense(10, activation=&#39;softmax&#39;)])
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/7c2e60ab-03e2-44ae-a89e-641f3b01ac29/image.png" alt=""></p>
<h4 id="함수형-api">함수형 API</h4>
<ul>
<li>가장 권장되는 방법(sequential은 단순하게 순차적으로 쌓이는 구조에서만 동작 가능)</li>
<li>모델을 복잡하고, 유연하게 구성 가능</li>
<li>다중 입출력을 다룰 수 있음</li>
</ul>
<pre><code class="language-python">inputs = Input(shape=(28,28,1))
x = Flatten(input_shape=(28,28,1))(inputs)
x = Dense(300,activation=&#39;relu&#39;)(x)
x = Dense(100,activation=&#39;relu&#39;)(x)
x = Dense(10,activation=&#39;softmax&#39;)(x)

model = Model(inputs=inputs, outputs=x)
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f2b742b9-5348-4973-aa96-584d6d6ef5a4/image.png" alt=""></p>
<pre><code class="language-python">plot_model(model)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/19220895-def9-4b79-99fc-b042f68434a1/image.png" alt=""></p>
<pre><code class="language-python"># 복잡한 모델
from tensorflow.keras.layers import Concatenate

input_layer = Input(shape=(28,28))
hidden1 = Dense(100, activation=&#39;relu&#39;)(input_layer)
hidden2 = Dense(30, activation=&#39;relu&#39;)(hidden1)
concat = Concatenate()([input_layer, hidden2])
output = Dense(1)(concat)

model = Model(inputs=[input_layer], outputs=[output])
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/09521f2f-3147-473e-9d5d-8e9089741afb/image.png" alt=""></p>
<pre><code class="language-python">plot_model(model)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/4cf69349-c30a-45ba-aad7-cd658c995d49/image.png" alt=""></p>
<pre><code class="language-python"># input 두개인 모델
input_1 = Input(shape=(10,10))
input_2 = Input(shape=(10,28))

hidden1 = Dense(100, activation=&#39;relu&#39;)(input_2)
hidden2 = Dense(10, activation=&#39;relu&#39;)(hidden1)
concat = Concatenate()([input_1, hidden2])

output = Dense(1, activation=&#39;sigmoid&#39;)(concat)
model = Model(inputs=[input_1, input_2], outputs=[output])
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/7858271b-2b14-48cb-b0a0-769f6f7a297a/image.png" alt=""></p>
<pre><code class="language-python">plot_model(model)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/9eb1ebd4-5b86-4ec2-b25a-000005d544cf/image.png" alt=""></p>
<pre><code class="language-python"># input 두개인 모델
input_ = Input(shape=(10,10), name = &#39;input_&#39;)

hidden1 = Dense(100, activation=&#39;relu&#39;)(input_)
hidden2 = Dense(10, activation=&#39;relu&#39;)(hidden1)

output = Dense(1, activation=&#39;sigmoid&#39;, name=&#39;main_output&#39;)(hidden2)
sub_output = Dense(1, name=&#39;sub_output&#39;)(hidden2)

model = Model(inputs=[input_], outputs=[output, sub_output])
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/a5a49a83-2e4c-4b45-a496-e7d3ad25fdd1/image.png" alt=""></p>
<pre><code class="language-python">plot_model(model)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/fc322e80-737b-494b-8c04-fca31a501d67/image.png" alt=""></p>
<pre><code class="language-python"># input, output 둘 다 두개인 모델
input_1 = Input(shape=(10,10))
input_2 = Input(shape=(10,28))

hidden1 = Dense(100, activation=&#39;relu&#39;)(input_2)
hidden2 = Dense(10, activation=&#39;relu&#39;)(hidden1)
concat = Concatenate()([input_1, hidden2])

output = Dense(1, activation=&#39;sigmoid&#39;,name=&#39;main_output&#39;)(concat)
sub_out = Dense(1,name=&#39;sub_output&#39;)(hidden2)

model = Model(inputs=[input_1, input_2], outputs=[output, sub_out])
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/0b76a774-91c1-4ed9-bb92-91b0109dd4e7/image.png" alt=""></p>
<pre><code class="language-python">plot_model(model ,show_layer_names=True, show_shapes=True)</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/f355b7a3-d430-4719-9797-5d083341a37b/image.png" alt=""></p>
<pre><code class="language-python">#### 서브클래싱(Subclassing)

- 커스터마이징에 최적화된 방법
- 이미 어느정도 만들어진걸 조금 수정하여 재활용하는 느낌
- Model 클래스를 상속받아 Model이 포함하는 기능을 사용할 수 있음  
  - `fit()`, `evaluate()`, `predict()`
  - `save()`, `load()`
- 주로 `call()` 메소드안에서 원하는 계산 가능
  - for, if, 저수준 연산 등
- 권장되는 방법은 아니지만 어떤 모델의 구현 코드를 참고할 때, 해석할 수 있어야함</code></pre>
<h3 id="모델-가중치-확인">모델 가중치 확인</h3>
<pre><code class="language-python">class MyModel(Model):
    def __init__(self, units=30, activation=&#39;relu&#39;,**kwargs):
        # Model class의 생성자 실행. Model class에 정의된 value 속성도 초기화
        super(MyModel, self).__init__(**kwargs)
        #super().__init__(**kwargs) # 이렇게 가능
        self.dense_layer1 = Dense(300, activation=activation)
        self.dense_layer2 = Dense(100, activation=activation)
        self.dense_layer3 = Dense(units, activation=activation)
        self.output_layer = Dense(10, activation=&#39;softmax&#39;)

    def call(self, inputs):
        x = self.dense_layer1(x)
        x = self.dense_layer2(x)
        x = self.dense_layer3(x)
        x = self.output_layer(x)
        return x

model = MyModel()
# 참고
# def abc(**kwargs):
#     print(kwargs)
# abc(a=3, b=&#39;c&#39;) # {&#39;a&#39;: 3, &#39;b&#39;: &#39;c&#39;}</code></pre>
<pre><code class="language-python">inputs = Input(shape=(28,28,1))
x = Flatten(input_shape=(28,28,1))(inputs)
x = Dense(300, activation=&#39;relu&#39;)(x)
x = Dense(100, activation=&#39;relu&#39;)(x)
x = Dense(10, activation=&#39;softmax&#39;)(x)

model = Model(inputs=inputs, outputs = x)
model.summary()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/8cc1067e-b2f1-4a58-998c-a98cfe9b5941/image.png" alt=""></p>
<pre><code class="language-python">model.layers</code></pre>
<pre><code>output:
[&lt;InputLayer name=input_layer_26, built=True&gt;,
 &lt;Flatten name=flatten_5, built=True&gt;,
 &lt;Dense name=dense_73, built=True&gt;,
 &lt;Dense name=dense_74, built=True&gt;,
 &lt;Dense name=dense_75, built=True&gt;]</code></pre><pre><code class="language-python">hidden_2 = model.layers[2]
hidden_2.name</code></pre>
<p>output : &#39;dense_73&#39;</p>
<pre><code class="language-python"># 가져온 모델 맞는지 검증하기
model.get_layer(&#39;dense_73&#39;) == hidden_2</code></pre>
<p>output : True</p>
<pre><code class="language-python"># 레이어의 weight, bias 확인하기
weights, biases = hidden_2.get_weights()
print(weights.shape)
print(biases.shape)</code></pre>
<p>output :
(784, 300)
(300,)</p>
<pre><code class="language-python">print(weights)</code></pre>
<p>output: 
[[-0.0189958   0.04178642 -0.02973261 ...  0.02392814 -0.06515371
  -0.01094473]
 [-0.06765752  0.05939554  0.05841401 ... -0.03066007  0.07289664
   0.03665416]
 [ 0.02429469  0.06326848  0.03675973 ... -0.06022335  0.03570367
   0.04572336]
 ...
 [ 0.03738911  0.0086766  -0.05104835 ...  0.03970996 -0.00280691
  -0.06960735]
 [ 0.06139068  0.00252346 -0.00775075 ...  0.05078986 -0.03957617
   0.0160888 ]
 [ 0.06548205  0.01272491 -0.05994889 ... -0.05388444  0.06571051
  -0.02650245]]</p>
<pre><code class="language-python">print(biases)</code></pre>
<p>output: 
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]</p>
<pre><code class="language-python">### 모델 컴파일(compile)
#- 모델을 구성한 후, 사용할 손실 함수(loss function), 옵티마이저(optimizer)를 지정
model.compile(loss = &#39;sparse_categorical_crossentropy&#39;,
              optimizer=&#39;sgd&#39;,
              metrics = [&#39;accuracy&#39;])</code></pre>
<h4 id="손실-함수loss-function">손실 함수(Loss Function)</h4>
<ul>
<li>학습이 진행되면서 해당 과정이 얼마나 잘 되고 있는지 나타내는 지표</li>
<li>모델이 훈련되는 동안 최소화될 값으로 주어진 문제에 대한 성공 지표</li>
<li>손실 함수에 따른 결과를 통해 학습 파라미터를 조정</li>
<li>최적화 이론에서 최소화 하고자 하는 함수</li>
<li>미분 가능한 함수 사용</li>
<li>Keras에서 주요 손실 함수 제공<ul>
<li><code>sparse_categorical_crossentropy</code>: 클래스가 배타적 방식으로 구분, 즉 (0, 1, 2, ..., 9)와 같은 방식으로 구분되어 있을 때 사용</li>
<li><code>categorical_cross_entropy</code>: 클래스가 원-핫 인코딩 방식으로 되어 있을 때 사용</li>
<li><code>binary_crossentropy</code>: 이진 분류를 수행할 때 사용</li>
</ul>
</li>
</ul>
<h5 id="평균절대오차mean-absolute-error-mae">평균절대오차(Mean Absolute Error, MAE)</h5>
<ul>
<li>오차가 커져도 손실함수가 일정하게 증가</li>
<li>이상치(Outlier)에 강건함(Robust)<ul>
<li>데이터에서 [입력 - 정답] 관계가 적절하지 않은 것이 있을 경우에, 좋은 추정을 하더라도 오차가 발생하는 경우가 발생</li>
<li>해당 이상치에 해당하는 지점에서 손실 함수의 최소값으로 가는 정도의 영향력이 크지 않음</li>
</ul>
</li>
<li>회귀 (Regression)에 많이 사용</li>
<li>평균절대오차 식:
$ \qquad \qquad E = \frac{1}{n}\sum_{i=1}^n \left | y_i - \tilde{y}_i \right |$<ul>
<li>$y_i$ : 학습 데이터의 $i\ $번째 정답</li>
<li>$\tilde{y}_i$ : 학습 데이터의 입력으로 추정한 $i\ $번째 출력</li>
</ul>
</li>
</ul>
<p><img src="https://miro.medium.com/max/1152/1*8BQhdKu1nk-tAAbOR17qGg.png" alt="">
<br /> </p>
<h5 id="평균제곱오차mean-squared-error-mse">평균제곱오차(Mean Squared Error, MSE)</h5>
<ul>
<li>가장 많이 쓰이는 손실 함수 중 하나</li>
<li>오차가 커질수록 손실함수가 빠르게 증가<ul>
<li>정답과 예측한 값의 차이가 클수록 더 많은 페널티를 부여</li>
</ul>
</li>
<li>회귀 (Regression)에 쓰임</li>
<li>평균제곱오차 식: $ \qquad \qquad E = \frac{1}{n}\sum_{i=1}^n ( y_i - \tilde{y}_i)^2 $<ul>
<li>$y_i$ : 학습 데이터의 $i\ $번째 정답</li>
<li>$\tilde{y}_i$ : 학습 데이터의 입력으로 추정한 $i\ $번째 출력</li>
</ul>
</li>
</ul>
<p><img src="https://miro.medium.com/max/1152/1*EqTaoCB1NmJnsRYEezSACA.png" alt="">
<br /></p>
<h5 id="손실함수-mae와-mse-비교">손실함수 MAE와 MSE 비교</h5>
<p><img src="https://miro.medium.com/max/1400/1*JTC4ReFwSeAt3kvTLq1YoA.png" alt="">
<br /></p>
<h5 id="원-핫-인코딩one-hot-encoding">원-핫 인코딩(One-Hot Encoding)</h5>
<ul>
<li>범주형 변수를 표현할 때 사용</li>
<li>가변수(Dummy Variable)이라고도 함</li>
<li>정답인 레이블을 제외하고 0으로 처리</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/a9a94156-9958-48f2-9227-7a84d30ebc58/image.png" alt=""></p>
<h5 id="교차-엔트로피-오차cross-entropy-error-cee">교차 엔트로피 오차(Cross Entropy Error, CEE)</h5>
<ul>
<li>이진 분류(Binary Classification), 다중 클래스 분류(Multi Class Classification)</li>
<li>소프트맥스(softmax)와 원-핫 인코딩(ont-hot encoding) 사이의 출력 간 거리를 비교</li>
<li>정답인 클래스에 대해서만 오차를 계산</li>
<li>정답을 맞추면 오차가 0, 틀리면 그 차이가 클수록 오차가 무한히 커짐</li>
<li>$y = log(x)$<ul>
<li>$x$가 1에 가까울수록 0에 가까워짐</li>
<li>$x$가 0에 가까울수록 $y$값은 무한히 커짐</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/8b2521b9-132d-4a93-bdbf-e3c6533fda7d/image.png" alt=""></p>
<ul>
<li><p>교차 엔트로피 오차 식: $ \qquad \qquad E = - \frac{1}{N}\sum_{n} \sum_{i}  y_i\ log\tilde{y}_i  $</p>
<ul>
<li>$y_i$ : 학습 데이터의 $i\ $번째 정답 (원-핫 인코딩, one-hot encoding)</li>
<li>$\tilde{y}_i$ : 학습 데이터의 입력으로 추정한 $i\ $번째 출력</li>
<li>$N$ : 전체 데이터의 개수</li>
<li>$i$ : 데이터 하나당 클래스 개수</li>
</ul>
</li>
<li><p>정답 레이블($y_i$)은 원-핫 인코딩으로 정답인 인덱스에만 1이고, 나머지는 모두 0이라서 다음과 같이 나타낼 수 있음</p>
<p>$ \qquad \qquad E = - log\tilde{y}_i  $</p>
<ul>
<li>소프트맥스를 통해 나온 신경망 출력이 0.6이라면 $\ -log0.6 \fallingdotseq -0.51\ $이 되고, 신경망 출력이 0.3이라면  $\ -log0.3 \fallingdotseq -1.2\ $이 됨</li>
<li>정답에 가까워질수록 오차값은 작아짐</li>
<li>학습시, 원-핫 인코딩에 의해 정답 인덱스만 살아 남아 비교하지만, 정답이 아닌 인덱스들도 학습에 영향을 미침. 왜냐하면 다중 클래스 분류는 소프트맥스(softmax) 함수를 통해 전체 항들을 모두 다루기 때문</li>
</ul>
</li>
</ul>
<h5 id="이진-분류-문제의-교차-크로스-엔트로피binary-cross-entropy-bce">이진 분류 문제의 교차 크로스 엔트로피(Binary Cross Entropy, BCE)</h5>
<ul>
<li>이진 분류 문제(Binary Classification Problem)에서도 크로스 엔트로피 오차를 손실함수로 사용 가능
$ \qquad \qquad E = - \sum_{i=1}^2  y_i\ log\tilde{y}_i \
\qquad \qquad \ \ \ = -y_1\ log\ \tilde{y}_1 - (1 - y_1)log(1-\ \tilde{y}_1) $<br>$\qquad \qquad \qquad ( \because y_2 = 1 - y_1)$<ul>
<li>$y_i$ : 학습 데이터의 $i\ $번째 정답 (원-핫 인코딩, one-hot encoding)</li>
<li>$\tilde{y}_i$ : 학습 데이터의 입력으로 추정한 $i\ $번째 출력</li>
</ul>
</li>
</ul>
<h4 id="옵티마이저optimizer">옵티마이저(Optimizer)</h4>
<ul>
<li>손실 함수를 기반으로 모델이 어떻게 업데이트되어야 하는지 결정 (특정 종류의 확률적 경사 하강법 구현)</li>
<li>Keras에서 여러 옵티마이저 제공<ul>
<li><code>keras.optimizer.SGD()</code>: 기본적인 확률적 경사 하강법</li>
<li><code>keras.optimizer.Adam()</code>: 자주 사용되는 옵티마이저</li>
<li>Keras에서 사용되는 옵티마이저 종류: <a href="https://keras.io/ko/optimizers/">https://keras.io/ko/optimizers/</a></li>
</ul>
</li>
<li>보통 옵티마이저의 튜닝을 위해 따로 객체를 생성하여 컴파일    </li>
</ul>
<h5 id="볼록함수convex-function와-비볼록함수non-convex-function">볼록함수(Convex Function)와 비볼록함수(Non-Convex Function)</h5>
<p><img src="https://velog.velcdn.com/images/s_s/post/1c8c424a-949c-4e3f-a33e-babb90a6ce05/image.png" alt="">
<img src="https://velog.velcdn.com/images/s_s/post/5dca4449-8265-48e1-b490-d9dc88ec2629/image.png" alt=""></p>
<ul>
<li>볼록함수(Convex Function)<ul>
<li>어떤 지점에서 시작하더라도 최적값(손실함수가 최소로하는 점)에 도달할 수 있음</li>
</ul>
</li>
<li>비볼록함수(Non-Convex Function)<ul>
<li>비볼록 함수는 시작점 위치에 따라 다른 최적값에 도달할 수 있음</li>
</ul>
</li>
</ul>
<h5 id="경사하강법gradient-decent">경사하강법(Gradient Decent)</h5>
<ul>
<li>미분과 기울기<ul>
<li>스칼라를 벡터로 미분한 것(스칼라 함수의 각 변수에 대한 변미분값들을 모아둔 리스트, 즉 그라디언트 벡터를 의미)</li>
<li>그라디언트 벡터 ∇f(x)=[ f&#39;(x1), f&#39;(x2), f&#39;(x3)]</li>
<li>w new =w−η⋅∇L(w) 예시) 기존 가중치 벡터[0,0,0,0,0], 그라디언트벡터[1,2,3,4,5], 업데이트후 [-1,-2,-3,-4,-5]</li>
</ul>
</li>
</ul>
<h2 id="quad-fracdfxdx--lim_triangle-x-to-0-fracfxtriangle-x---fxtriangle-x">$\quad \frac{df(x)}{dx} = \lim_{\triangle x \to 0} \frac{f(x+\triangle x) - f(x)}{\triangle x}$</h2>
<p><img src="https://velog.velcdn.com/images/s_s/post/9ed01b3e-037f-400f-b8b1-85182a539a8b/image.png" alt=""></p>
<p> $\quad \triangledown f(x) = \left( \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2},\ ... \ , \frac{\partial f}{\partial x_N} \right)$</p>
<ul>
<li><p>변화가 있는 지점에서는 미분값이 존재하고, 변화가 없는 지점은 미분값이 0</p>
</li>
<li><p>미분값이 클수록 변화량이 크다는 의미</p>
<ul>
<li>경사하강법의 과정</li>
<li>경사하강법은 한 스텝마다의 미분값에 따라 이동하는 방향을 결정</li>
<li>$f(x)$의 값이 변하지 않을 때까지 반복</li>
</ul>
<p>$\qquad x_n = x_{n-1} - \eta \frac{\partial f}{\partial x}$</p>
<ul>
<li>$\eta$ : 학습률(learning rate)</li>
<li>즉, <strong>미분값이 0인 지점</strong>을 찾는 방법   </li>
</ul>
</li>
</ul>
<img src="https://i.pinimg.com/originals/63/62/8f/63628f546ad55fd31091e23c623cb9f5.gif">

<h5 id="학습률learning-rate">학습률(learning rate)</h5>
<ul>
<li>적절한 학습률을 지정해야 최저점에 잘 도달할 수 있음</li>
<li>학습률이 너무 크면 발산하고, 너무 작으면 학습이 오래 걸리거나 최저점에 도달하지 않음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/0a64f9a3-5b74-42c7-9d2c-2db15a0ea820/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/s_s/post/8fbb517e-f40d-44db-b8d7-c46539d54937/image.png" alt=""></p>
<h5 id="안장점saddle-point">안장점(Saddle Point)</h5>
<ul>
<li>기울기가 0이지만 극값이 되지 않음</li>
<li>경사하강법은 안장점에서 벗어나지 못함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/ca3fa6cf-e966-4a7d-a978-41baf0b44ced/image.png" alt=""></p>
<h4 id="지표metrics">지표(Metrics)</h4>
<ul>
<li>모니터링할 지표</li>
<li><code>mae</code>나 <code>accuracy</code> 사용</li>
<li>줄여서 <code>acc</code>로도 사용 가능</li>
<li>Keras에서 사용되는 지표 종류: <a href="https://keras.io/ko/metrics/">https://keras.io/ko/metrics/</a></li>
</ul>
<h3 id="모델-학습-평가-및-예측">모델 학습, 평가 및 예측</h3>
<ul>
<li><p><code>fit()</code></p>
<ul>
<li><code>x</code>: 학습 데이터</li>
<li><code>y</code>: 학습 데이터 정답 레이블</li>
<li><code>epochs</code>: 학습 회수</li>
<li><code>batch_size</code>: 단일 배치에 있는 학습 데이터의 크기</li>
<li><code>validation_data</code>: 검증을 위한 데이터</li>
</ul>
</li>
<li><p><code>evaluate()</code></p>
<ul>
<li>테스트 데이터를 이용한 평가</li>
</ul>
</li>
<li><p><code>predict()</code></p>
<ul>
<li>임의의 데이터를 사용해 예측</li>
</ul>
<h4 id="오차역전파-backpropagation">오차역전파 (Backpropagation)</h4>
</li>
<li><p>오차역전파 알고리즘</p>
<ul>
<li>학습 데이터로 정방향(forward) 연산을 통해 손실함수 값(loss)을 구함</li>
<li>각 layer별로 역전파학습을 위해 중간값을 저장</li>
<li>손실함수를 학습 파라미터(가중치, 편향)로 미분하여 마지막 layer로부터 앞으로 하나씩 연쇄법칙을 이용하여 미분</li>
<li>각 layer를 통과할 때마다 저장된 값을 이용</li>
<li>오류(error)를 전달하면서 학습 파라미터를 조금씩 갱신</li>
</ul>
</li>
<li><p>오차역전파 학습의 특징</p>
<ul>
<li>손실함수를 통한 평가를 한 번만 하고, 연쇄법칙을 이용한 미분을 활용하기 때문에 학습 소요시간이 매우 단축</li>
<li>미분을 위한 중간값을 모두 저장하기 때문에 메모리를 많이 사용</li>
</ul>
</li>
<li><p>신경망 학습에 있어서 미분가능의 중요성</p>
<ul>
<li>경사하강법(Gradient Descent)에서 손실 함수(cost function)의 최소값, 즉, 최적값을 찾기 위한 방법으로 미분을 활용</li>
<li>미분을 통해 손실 함수의 학습 매개변수(trainable parameter)를 갱신하여 모델의 가중치의 최적값을 찾는 과정</li>
</ul>
</li>
</ul>
<p><img src="https://i.pinimg.com/originals/5d/13/20/5d1320c7b672710834e63b95a7c1037b.png" alt=""></p>
<ul>
<li><p>합성함수의 미분 (연쇄법칙, chain rule)</p>
<p>$\qquad \frac{d}{dx} [f(g(x))] = f^\prime(g(x))g^\prime(x)$  </p>
<ul>
<li>여러개를 연속으로 사용 가능</li>
</ul>
<p>$ \quad \frac{\partial f}{\partial x} = \frac{\partial f}{\partial u} \times \frac{\partial u}{\partial m} \times \frac{\partial m}{\partial n} \times \ ... \ \frac{\partial l}{\partial k} \times \frac{\partial k}{\partial g} \times \frac{\partial g}{\partial x}
$</p>
<ul>
<li>각각에 대해 *편미분 적용가능</li>
<li>편미분 : 다변수 함수에서 하나의 변수를 제외한 나머지 변수들을 상수로 취급하고, 해당 변수에 대해서만 미분을 수행하는 것
<img src="https://cdn-media-1.freecodecamp.org/images/1*_KMMFvRP5X9kC59brI0ykw.png" alt=""></li>
</ul>
</li>
<li><p>오차역전파의 직관적 이해</p>
<ul>
<li>학습을 진행하면서, 즉 손실함수의 최소값(minimum)을 찾아가는 과정에서 가중치 또는 편향의 변화에 따라 얼마나 영향을 받는지 알 수 있음</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[wireguard 설치 - 1]]></title>
            <link>https://velog.io/@s_s/wireguard-%EC%84%A4%EC%B9%98-1</link>
            <guid>https://velog.io/@s_s/wireguard-%EC%84%A4%EC%B9%98-1</guid>
            <pubDate>Wed, 29 Jan 2025 17:22:26 GMT</pubDate>
            <description><![CDATA[<h3 id="배경">배경</h3>
<p>회사를 그만두고 프리랜서 개발자로 활동하며, 출장이 잦아 집이 아닌 장소에서 개발하는 일이 많아졌습니다.
특히 GPU를 활용한 작업이 필요할 때가 있는데, GPU가 탑재된 노트북은 무겁고 휴대성이 떨어지기 때문에 집에 우분투(Ubuntu) 기반의 데스크톱을 구축하여 원격으로 작업하는 방법을 선택했습니다.</p>
<h3 id="문제점">문제점</h3>
<p>집에서 작업할 때는 내부 네트워크(사설 IP)를 이용하여 원격 접속이 가능하지만,
외부에서는 내부 IP에 직접 접근할 수 없기 때문에 공인 IP를 통해 원격 접속을 해야 합니다.
그러나 공인 IP를 외부에서 접근할 수 있도록 개방하면 보안 취약점이 발생합니다.</p>
<h4 id="보안-위험-요소">보안 위험 요소:</h4>
<ul>
<li>공개된 SSH 포트 → 자동화된 공격(Brute-force, Dictionary Attack) 대상이 될 가능성이 큼.</li>
<li>포트 스캐닝 → 해커가 특정 포트를 통해 원격 접근을 시도할 위험이 있음.</li>
<li>DDoS 공격 위험 → 공인 IP가 알려지면 네트워크 공격 대상이 될 가능성이 있음.</li>
</ul>
<h3 id="대안-vpn을-활용한-원격-접속">대안: VPN을 활용한 원격 접속</h3>
<p>VPN을 사용하면 외부 네트워크에서도 집 내부 네트워크에 연결된 것처럼 동작할 수 있습니다. 즉, 집에서 내부 IP(사설 IP)로만 접근 가능한 장치에도 외부에서 안전하게 접근할 수 있게 됩니다. </p>
<p>따라서 VPN을 쓰기 위해 여러 VPN 서비스에 대해 알아보았습니다. 비용을 지불하고 편리하게 사용할 수 있는 VPN, 직접 설치하여 쓰는 오픈소스 VPN 등이 있었는데, 최신 기술이 적용된 WireGuard가 설정이 간편하면서도 성능이 뛰어나므로 WireGuard를 사용하여 직접 VPN 서버를 구축하기로 결정하게 되었습니다. </p>
<h3 id="설치-및-설정-과정">설치 및 설정 과정</h3>
<p>우선, WireGuard 서버가 실행될 우분투 시스템의 사설 IP(내부 IP)를 고정해야 합니다. (라우터에서 내부 IP를 DHCP로 자동 할당하면 변경될 수 있기 때문에 고정 설정이 필요합니다.)</p>
<h3 id="1-내부-ip-확인">1. 내부 IP 확인</h3>
<pre><code class="language-bash">ip addr show</code></pre>
<h3 id="1-2-출력-예시">1-2. 출력 예시</h3>
<pre><code>2: enp5s0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500
    inet 192.168.45.177/24 brd 192.168.45.255 scope global dynamic enp5s0</code></pre><h3 id="2-내부-ip-고정-설정-방법">2. 내부 IP 고정 설정 방법</h3>
<h4 id="2-1-네트워크-설정-파일-접근">2-1. 네트워크 설정 파일 접근</h4>
<ul>
<li>저의 경우는 /etc/netplan/01-network-manager-all.yaml 경로에 네트워크 설정파일이 위치하고 있습니다. <pre><code>sudo nano /etc/netplan/01-network-manager-all.yaml</code></pre></li>
</ul>
<h4 id="2-2-내용-수정">2-2. 내용 수정</h4>
<ul>
<li>아래 캡쳐된 이미지처럼 사용할 IP를 사설IP 대역에서 설정해주시면 됩니다. </li>
<li>간략히 아래 이미지에 대해 설명드리면 <ul>
<li>dhcp4: no -&gt; 자동할당 비활성화(고정 ip 사용)</li>
<li>gateway4 -&gt; 라우터(공유기 IP)</li>
<li>address(192.168.45.177/24) -&gt; 고정 ip 설정 
24는 네트워크와 호스트를 구분하는 범위를 알려줍니다. </li>
<li>총 32비트(4바이트) 숫자에서 네트워크 부분이 24비트, 호스트 부분이 8비트임을 나타냅니다. 따라서 뒤의 8비트 부분의 숫자를 다르게 하여 192.168.45.1 ~ 192 168.45.254 범위의 IP를 실제 장치에 할당할 수 있습니다.</li>
<li>nameservers -&gt; DNS 서버 설정 (라우터 및 Google DNS)</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/s_s/post/ea73b588-1c45-4579-8d1c-383fd6e87053/image.png" alt=""></p>
<h4 id="2-3-설정-적용">2-3. 설정 적용</h4>
<p>이후 아래 명령어를 통해 변경사항을 적용하면 됩니다.
이제 내부 네트워크에서 192.168.45.177을 항상 유지할 수 있습니다</p>
<pre><code class="language-bash">sudo netplan apply</code></pre>
<h3 id="3-wireguard-설치">3. wireguard 설치</h3>
<p>WireGuard 서버를 우분투에 설치하는 방법은 여러 가지가 있지만,
설정을 간편하게 하기 위해 Docker 컨테이너를 이용하여 설치합니다. </p>
<p>컨테이너를 만들기 위한 도커 이미지는 도커 허브에서 검색하여 쉽게 다운받을 수 있습니다. 아래 링크를 참조하여 도커 허브에서 이미지를 다운받습니다. </p>
<p>공식 이미지 링크 : <a href="https://hub.docker.com/r/linuxserver/wireguard">https://hub.docker.com/r/linuxserver/wireguard</a>
도커 이미지 다운로드 명령어: </p>
<pre><code class="language-bash">docker pull linuxserver/wireguard</code></pre>
<h3 id="4-포트-포워딩-설정">4. 포트 포워딩 설정</h3>
<p>특정 아이피와 포트 설정을 진행합니다. 
<img src="https://velog.velcdn.com/images/s_s/post/72b424ad-104b-464c-8515-d78d21d462d7/image.png" alt=""></p>
<p>외부 포트 시작과 끝은 외부에서 접속할 같은 포트 번호로 작성해주면 됩니다. 내부 ip는 wireguard가 실행되 컴퓨터의 사설(내부)IP로 설정하고 내부 포트 시작 및 끝은 wireguard가 수신하는 포트번호입니다. 마찬가지로 같은 값으로 설정하면 되고 기본값은 51820 입니다. 
내부 ip는 위에서 설정해준 값으로 하시면 됩니다. 어려우시면 1,2,3,4 모두 51820 으로 하셔도 괜찮습니다. 설정 후 외부에서 nmap이라는 프로그램을 설치하시거나 터미널 명령어를 사용해서 해당 포트가 잘 열려있는지 체크해줍니다. 명령어는 다음과 같습니다.</p>
<pre><code class="language-bash">sudo nmap -sU -p 51820 &lt;공인IP&gt; # 우분투 명령어</code></pre>
<p>우분투 방화벽을 사용하는 경우 아래 명령어를 사용하여 UDP 포트를 열어야합니다.</p>
<pre><code class="language-bash">sudo ufw allow 51820/udp
sudo ufw enable</code></pre>
<h3 id="5-wireguard-컨테이너-실행">5. WireGuard 컨테이너 실행</h3>
<p>네트워크 설정이 완료되었으면, 다운받은 wireguard 이미지를 이용해 컨테이너를 생성하고 해당 컨테이너에서 설정을 마무리해줍니다.
저는 아래의 명령어를 이용해 컨테이너를 만들었습니다.</p>
<pre><code class="language-bash">docker run -d \
  --name=wireguard \
  --cap-add=NET_ADMIN \
  -e SERVERURL=&lt;공인IP&gt; \
  -e SERVERPORT=51820 \
  -p 51820:51820/udp \
  -v /path/to/wireguard/config:/config \
  --restart unless-stopped \
  lscr.io/linuxserver/wireguard:latest</code></pre>
<ul>
<li>SERVERURL: 공인 IP 또는 DDNS 주소 입력</li>
<li>SERVERPORT: 51820 (기본값)</li>
<li>-p 51820:51820/udp: 포트 포워딩 적용</li>
</ul>
<p>컨테이너가 만들어졌으면, 해당 컨테이너로 접속합니다. </p>
<pre><code class="language-bash">docker exec -it wireguard bash</code></pre>
<p>이후 wireguard 설정 파일로 진입합니다. </p>
<pre><code class="language-bash">vi /config/wg_confs/wg0.conf</code></pre>
<p>항목 중에서 ListenPort가 설정한 외부포트로 설정되어있는지 확인합니다.</p>
<p>이후 /config/peer1 경로에 접속하여 아래 두 파일을 다운로드 합니다.</p>
<ul>
<li>peer1.conf 파일을 WireGuard 클라이언트에 등록</li>
<li>peer1.png 파일(QR코드)을 모바일에서 WireGuard 앱으로 스캔하여 연결 가능</li>
</ul>
<p>윈도우의 경우 wireguard 클라이언트 파일을 받아 peer1.conf 파일을 업로드하면 VPN 연결이 활성화되는 것을 확인할 수 있었습니다.</p>
<p>하루 정도만 투자하면 무료로 안전한 원격 작업 환경을 구축할 수 있으니, 저와 비슷한 상황이신 분들은 한번쯤 사용해보시길 권장드립니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 4 - 큐러닝]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-4-%ED%81%90%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-4-%ED%81%90%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Sun, 05 Jan 2025 14:53:13 GMT</pubDate>
            <description><![CDATA[<p>Q-Learning은 강화학습의 대표적인 알고리즘 중 하나로, 환경과의 상호작용을 통해 최적의 정책을 학습하는 방법입니다.</p>
<img src="https://www.researchgate.net/profile/Silvia-Ullo/publication/351884746/figure/fig1/AS:1067993664589824@1631640940747/Q-Learning-vs-Deep-Q-Learning.ppm" width="600">

<p>[이미지 출처] <a href="https://www.researchgate.net/figure/Q-Learning-vs-Deep-Q-Learning_fig1_351884746">https://www.researchgate.net/figure/Q-Learning-vs-Deep-Q-Learning_fig1_351884746</a></p>
<h2 id="핵심-개념">핵심 개념</h2>
<ul>
<li><p><strong>Q-함수</strong>: 상태-행동 가치 함수라고도 불리며 상태(s)에서 행동(a)을 취했을 때의 예상 가치를 리턴하는  함수. 벨만 방정식을 기반으로 Q값을 갱신함</p>
</li>
<li><p><strong>벨만 방정식</strong>: Q(s,a) = r + γ * max(Q(s&#39;,a&#39;))</p>
<ul>
<li>Q($s_t$,$a_t$) : 현재 상태 $s_t$에서 행동$a_t$를 했을 때 기대되는 누적 보상</li>
<li>r: 즉각적인 보상</li>
<li>γ: 할인 계수</li>
<li>s&#39;: 다음 상태</li>
<li>a&#39;: 다음 상태에서 가능한 모든 행동</li>
</ul>
</li>
<li><p><strong>Q-함수 업데이트 식</strong>:</p>
<ul>
<li>$$
Q(s_t, a_t) \leftarrow (1-\alpha)Q(s_t, a_t) + \alpha \big(r + \gamma \max_{a&#39;} Q(s_{t+1}, a&#39;) - Q(s_t, a_t)\big)
$$</li>
<li>$\alpha$: 학습률 (0~1 사이의 값으로, 새로운 정보의 반영 정도를 조절)</li>
<li>업데이트는 현재 $Q(s_t, a_t)$ 값을 보상과 다음 상태의 최댓값을 반영하여 조금씩 개선하는 방식으로 진행됩니다.</li>
</ul>
</li>
<li><p><strong>ε-greedy 정책</strong>: 탐험(exploration)과 활용(exploitation)의 균형을 위한 정책</p>
<ul>
<li><p>Exploration (탐험): 아직 시도해보지 않은 행동을 선택하여 더 나은 보상을 찾는 과정.새로운 정보를 얻을 수 있지만, 단기적으로는 보상이 낮을 수 있음.</p>
</li>
<li><p>Exploitation (활용): 이미 알고 있는 정보에서 가장 높은 보상을 줄 것이라고 예상되는 행동을 선택. 현재의 지식으로 최대 보상을 얻으려는 접근. ε-Greedy 정책은 이 두 과정을 아래와 같은 방식으로 혼합합니다:</p>
</li>
<li><p>확률 ε로 랜덤하게 행동을 선택(탐험).<strong>확률 (1−𝜀)</strong>로 현재 가장 높은 보상을 줄 것으로 예상되는 행동 선택(활용)</p>
</li>
</ul>
</li>
</ul>
<h2 id="알고리즘-단계">알고리즘 단계</h2>
<ol>
<li>Q-테이블 초기화</li>
<li>현재 상태 관찰</li>
<li>ε-greedy 정책에 따라 행동 선택</li>
<li>행동 수행 및 보상 획득</li>
<li>Q-함수 업데이트</li>
<li>새로운 상태로 이동</li>
<li>2-6 단계 반복</li>
</ol>
<h2 id="장단점">장단점</h2>
<h3 id="장점">장점:</h3>
<ul>
<li>모델 없이 학습 가능 (모델 프리 접근법)</li>
<li>학습 종료 시 수렴성 보장</li>
</ul>
<h3 id="단점">단점:</h3>
<ul>
<li>큰 상태 공간에서는 비효율적</li>
<li>연속적인 상태나 행동 처리에 제한<ul>
<li><ol>
<li>메모리 요구량 증가 : 큰 상태 공간에서는 Q-테이블의 크기가 기하급수적으로 증가합니다. 각 상태-행동 쌍에 대한 Q-값을 저장해야 하므로, 상태의 수가 증가할수록 필요한 메모리가 급격히 늘어납니다.</li>
</ol>
</li>
<li><ol start="2">
<li>학습 시간 증가 : 상태 공간이 커질수록 모든 상태-행동 쌍을 충분히 탐색하고 Q-값을 업데이트하는 데 필요한 시간이 크게 증가합니다. 이는 학습 속도를 현저히 저하시킵니다.</li>
</ol>
</li>
<li><ol start="3">
<li>탐색-활용 딜레마 : 큰 상태 공간에서는 모든 상태를 충분히 탐색하기 어려워집니다. 이로 인해 최적 정책을 찾는 데 필요한 탐색과 학습된 정책을 활용하는 것 사이의 균형을 맞추기가 더욱 어려워집니다.</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="코드-설명">코드 설명</h2>
<p>제공된 코드는 Q-Learning을 구현한 예제입니다. <code>Env</code> 클래스는 그리드 월드 환경을, <code>QLearningAgent</code> 클래스는 Q-Learning 에이전트를 구현합니다.</p>
<h3 id="주요-함수-설명">주요 함수 설명</h3>
<ol>
<li><p><code>Env</code> 클래스:</p>
<ul>
<li><code>__init__</code>: 환경 초기화</li>
<li><code>step</code>: 행동 수행 및 다음 상태, 보상 반환</li>
<li><code>reset</code>: 환경 초기화</li>
</ul>
</li>
<li><p><code>QLearningAgent</code> 클래스:</p>
<ul>
<li><code>learn</code>: Q-함수 업데이트</li>
<li><code>get_action</code>: ε-greedy 정책에 따른 행동 선택</li>
</ul>
</li>
</ol>
<h3 id="코드-실행-과정">코드 실행 과정</h3>
<ol>
<li>환경과 에이전트 초기화</li>
<li>에피소드 반복:<ul>
<li>상태 초기화</li>
<li>행동 선택 및 수행</li>
<li>Q-함수 업데이트</li>
<li>새로운 상태로 이동</li>
<li>Q-값 출력</li>
</ul>
</li>
<li>1000 에피소드 동안 반복</li>
</ol>
<p>이 코드를 통해 에이전트는 그리드 월드에서 장애물을 피해 목표에 도달하는 최적 경로를 학습합니다.</p>
<ul>
<li>아래 코드는 코랩 환경이 아닌 로컬 파이썬 환경에서 진행해주세요!</li>
<li>코드 출처 : <a href="https://github.com/rlcode/reinforcement-learning-kr">https://github.com/rlcode/reinforcement-learning-kr</a></li>
</ul>
<pre><code class="language-python"># 필요한 라이브러리 임포트
import time  # 딜레이를 추가하거나 시간 측정을 위해 사용
import numpy as np  # 배열 및 수학적 계산에 사용
import tkinter as tk  # GUI 환경을 구성하기 위한 라이브러리
from PIL import ImageTk, Image  # 이미지를 tkinter에서 사용하기 위해 변환

# 랜덤 시드 설정 (재현 가능성 보장)
np.random.seed(1)

# tkinter에서 사용할 이미지 객체
PhotoImage = ImageTk.PhotoImage

# 그리드월드 크기 설정
UNIT = 100  # 각 셀의 크기 (픽셀)
HEIGHT = 5  # 그리드월드의 세로 크기 (셀 단위)
WIDTH = 5  # 그리드월드의 가로 크기 (셀 단위)


class Env(tk.Tk):
    def __init__(self):
        # tkinter의 Tk 클래스를 초기화
        super(Env, self).__init__()
        # 환경에서 사용할 행동들 (상, 하, 좌, 우)
        self.action_space = [&#39;u&#39;, &#39;d&#39;, &#39;l&#39;, &#39;r&#39;]
        self.n_actions = len(self.action_space)  # 행동의 개수
        self.title(&#39;Q Learning&#39;)  # 윈도우 제목
        self.geometry(&#39;{0}x{1}&#39;.format(HEIGHT * UNIT, HEIGHT * UNIT))  # 윈도우 크기
        self.shapes = self.load_images()  # 사용할 이미지 로드
        self.canvas = self._build_canvas()  # 캔버스 초기화
        self.texts = []  # 그리드 안에 텍스트 표시를 위한 리스트

    def _build_canvas(self):
        # 캔버스 생성 (그리드 및 이미지 추가)
        canvas = tk.Canvas(self, bg=&#39;white&#39;,
                           height=HEIGHT * UNIT,
                           width=WIDTH * UNIT)
        # 세로선 그리기
        for c in range(0, WIDTH * UNIT, UNIT):
            x0, y0, x1, y1 = c, 0, c, HEIGHT * UNIT
            canvas.create_line(x0, y0, x1, y1)
        # 가로선 그리기
        for r in range(0, HEIGHT * UNIT, UNIT):
            x0, y0, x1, y1 = 0, r, HEIGHT * UNIT, r
            canvas.create_line(x0, y0, x1, y1)

        # 캔버스에 이미지 추가
        self.rectangle = canvas.create_image(50, 50, image=self.shapes[0])  # 빨간 네모 (에이전트)
        self.triangle1 = canvas.create_image(250, 150, image=self.shapes[1])  # 장애물1
        self.triangle2 = canvas.create_image(150, 250, image=self.shapes[1])  # 장애물2
        self.circle = canvas.create_image(250, 250, image=self.shapes[2])  # 목표 지점

        canvas.pack()  # 캔버스를 tkinter 윈도우에 추가
        return canvas

    def load_images(self):
        # 이미지를 로드하고 크기를 조정하여 반환
        rectangle = PhotoImage(
            Image.open(&quot;../img/rectangle.png&quot;).resize((65, 65)))
        triangle = PhotoImage(
            Image.open(&quot;../img/triangle.png&quot;).resize((65, 65)))
        circle = PhotoImage(
            Image.open(&quot;../img/circle.png&quot;).resize((65, 65)))

        return rectangle, triangle, circle  # 로드된 이미지 반환

    def text_value(self, row, col, contents, action, font=&#39;Helvetica&#39;, size=10,
                   style=&#39;normal&#39;, anchor=&quot;nw&quot;):
        # 그리드 안에 Q 값 표시
        if action == 0:  # 상
            origin_x, origin_y = 7, 42
        elif action == 1:  # 하
            origin_x, origin_y = 85, 42
        elif action == 2:  # 좌
            origin_x, origin_y = 42, 5
        else:  # 우
            origin_x, origin_y = 42, 77

        # 텍스트의 좌표 계산
        x, y = origin_y + (UNIT * col), origin_x + (UNIT * row)
        font = (font, str(size), style)
        text = self.canvas.create_text(x, y, fill=&quot;black&quot;, text=contents,
                                       font=font, anchor=anchor)
        return self.texts.append(text)

    def print_value_all(self, q_table):
        # 현재 Q 테이블 값을 캔버스에 표시
        for i in self.texts:
            self.canvas.delete(i)
        self.texts.clear()  # 이전 텍스트 지우기
        for i in range(HEIGHT):
            for j in range(WIDTH):
                for action in range(0, 4):
                    state = [i, j]
                    if str(state) in q_table.keys():
                        temp = q_table[str(state)][action]
                        self.text_value(j, i, round(temp, 2), action)

    def coords_to_state(self, coords):
        # 캔버스 좌표를 그리드월드의 상태로 변환
        x = int((coords[0] - 50) / 100)
        y = int((coords[1] - 50) / 100)
        return [x, y]

    def state_to_coords(self, state):
        # 그리드월드 상태를 캔버스 좌표로 변환
        x = int(state[0] * 100 + 50)
        y = int(state[1] * 100 + 50)
        return [x, y]

    def reset(self):
        # 환경을 초기 상태로 리셋
        self.update()
        time.sleep(0.5)  # 딜레이 추가
        x, y = self.canvas.coords(self.rectangle)  # 에이전트의 현재 좌표
        self.canvas.move(self.rectangle, UNIT / 2 - x, UNIT / 2 - y)  # 초기 위치로 이동
        self.render()  # 환경 시각화
        return self.coords_to_state(self.canvas.coords(self.rectangle))  # 초기 상태 반환

    def step(self, action):
        # 주어진 행동에 따라 환경의 상태를 변화시키고 보상을 반환
        state = self.canvas.coords(self.rectangle)  # 현재 에이전트 좌표
        base_action = np.array([0, 0])  # 이동 방향 초기화
        self.render()  # 환경 시각화

        # 행동에 따른 이동 방향 결정
        if action == 0:  # 상
            if state[1] &gt; UNIT:
                base_action[1] -= UNIT
        elif action == 1:  # 하
            if state[1] &lt; (HEIGHT - 1) * UNIT:
                base_action[1] += UNIT
        elif action == 2:  # 좌
            if state[0] &gt; UNIT:
                base_action[0] -= UNIT
        elif action == 3:  # 우
            if state[0] &lt; (WIDTH - 1) * UNIT:
                base_action[0] += UNIT

        # 에이전트 이동
        self.canvas.move(self.rectangle, base_action[0], base_action[1])
        self.canvas.tag_raise(self.rectangle)  # 에이전트를 맨 위로 배치
        next_state = self.canvas.coords(self.rectangle)  # 다음 상태

        # 보상 함수
        if next_state == self.canvas.coords(self.circle):  # 목표 도달
            reward = 100
            done = True
        elif next_state in [self.canvas.coords(self.triangle1),
                            self.canvas.coords(self.triangle2)]:  # 장애물 도달
            reward = -100
            done = True
        else:  # 이동만 한 경우
            reward = 0
            done = False

        next_state = self.coords_to_state(next_state)  # 좌표를 상태로 변환
        return next_state, reward, done  # 다음 상태, 보상, 종료 여부 반환

    def render(self):
        # 환경 시각화 및 딜레이 추가
        time.sleep(0.03)
        self.update()</code></pre>
<p>실행파일</p>
<pre><code class="language-python">import numpy as np
import random
from environment import Env  # 그리드 월드 환경을 정의하는 사용자 정의 클래스
from collections import defaultdict  # 기본값이 있는 딕셔너리 생성에 사용

class QLearningAgent:
    def __init__(self, actions):
        # Q-Learning 에이전트를 초기화
        # 행동(actions): [0, 1, 2, 3] 순서대로 상, 하, 좌, 우를 의미
        self.actions = actions  # 에이전트가 선택할 수 있는 행동 리스트
        self.learning_rate = 0.01  # 학습률 α: 새 정보 반영 정도
        self.discount_factor = 0.9  # 할인 계수 γ: 미래 보상의 중요도
        self.epsilon = 0.9  # 탐험 확률 ε: 무작위 행동 선택 비율
        # Q 테이블 초기화: 상태별로 [0.0, 0.0, 0.0, 0.0] 초기값을 가지는 딕셔너리
        self.q_table = defaultdict(lambda: [0.0, 0.0, 0.0, 0.0])

    # &lt;s, a, r, s&#39;&gt; 샘플로부터 Q-함수를 업데이트
    def learn(self, state, action, reward, next_state):
        # 현재 상태(state)와 행동(action)에 대한 Q 값
        q_1 = self.q_table[state][action]
        # 벨만 최적 방정식 기반으로 업데이트 대상 Q 값 계산
        # reward + γ * max(Q(s&#39;, a&#39;)): 현재 보상과 다음 상태에서의 최대 Q 값
        q_2 = reward + self.discount_factor * max(self.q_table[next_state])
        # Q 값 업데이트: 기존 Q 값에 학습률을 곱한 TD 오차를 더함
        self.q_table[state][action] += self.learning_rate * (q_2 - q_1)

    # Q-테이블 기반의 ε-탐욕 정책으로 행동 선택
    def get_action(self, state):
        if np.random.rand() &lt; self.epsilon:
            # 탐험(Exploration): 무작위 행동 선택
            action = np.random.choice(self.actions)
        else:
            # 활용(Exploitation): Q-테이블에서 가장 높은 값을 가진 행동 선택
            state_action = self.q_table[state]
            action = self.arg_max(state_action)
        return action

    @staticmethod
    def arg_max(state_action):
        # Q 값이 최대인 행동을 반환
        max_index_list = []  # 최대 Q 값을 가진 행동들의 인덱스 리스트
        max_value = state_action[0]  # 첫 번째 값을 초기 최대값으로 설정
        for index, value in enumerate(state_action):
            if value &gt; max_value:
                # 새로운 최대값 발견 시 리스트 초기화 후 추가
                max_index_list.clear()
                max_value = value
                max_index_list.append(index)
            elif value == max_value:
                # 최대값과 같은 값을 가진 행동 추가
                max_index_list.append(index)
        return random.choice(max_index_list)  # 최대값 행동 중 무작위로 선택

if __name__ == &quot;__main__&quot;:
    env = Env()  # 사용자 정의 환경 초기화
    agent = QLearningAgent(actions=list(range(env.n_actions)))  # 에이전트 초기화

    for episode in range(1000):  # 1000개의 에피소드 동안 학습 반복
        state = env.reset()  # 환경 초기화 및 초기 상태 반환

        while True:  # 한 에피소드 동안 반복
            env.render()  # 환경 시각화 (현재 상태 출력)

            # 현재 상태(state)에 따른 행동(action) 선택
            action = agent.get_action(str(state))
            # 행동 수행 후 다음 상태(next_state), 보상(reward), 종료 여부(done) 반환
            next_state, reward, done = env.step(action)

            # Q-함수 업데이트: &lt;s, a, r, s&#39;&gt; 샘플로 학습
            agent.learn(str(state), action, reward, str(next_state))
            state = next_state  # 상태 업데이트

            # 모든 상태-행동에 대한 Q 값을 화면에 표시
            env.print_value_all(agent.q_table)

            if done:  # 에피소드 종료 조건
                break</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 3]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-3-k63ft5hi</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-3-k63ft5hi</guid>
            <pubDate>Sun, 05 Jan 2025 14:50:15 GMT</pubDate>
            <description><![CDATA[<h1 id="가치-기반-학습value-based-learning과-정책-기반-학습policy-based-learning">가치 기반 학습(Value-Based Learning)과 정책 기반 학습(Policy-Based Learning)</h1>
<ul>
<li><strong>강화학습의 핵심 목표</strong>: 환경과의 상호작용을 통해 기대되는 누적 보상(expected cumulative reward)을 최대화하는 정책(Policy)을 학습하는 것.</li>
<li>이는 에이전트가 어떤 상태에서 어떤 행동을 선택해야 할지를 학습하여 장기적으로 최적의 결과를 얻는 것을 의미합니다.</li>
</ul>
<p>강화학습은 크게 가치 기반 학습과 정책 기반 학습으로 나뉩니다.</p>
<ol>
<li><p>가치 기반 학습 (Value-Based Learning)</p>
<ul>
<li>상태(state, state-action)의 가치 함수를 학습하고 이를 바탕으로 행동을 선택.</li>
<li>대표적인 알고리즘: Q-Learning, SARSA.</li>
<li>Q-값(Q-Value): 특정 상태 $s$에서 행동 $a$를 취했을 때 기대되는 총 보상 $Q(s, a)$를 학습합니다.</li>
<li>최적 정책은 다음과 같이 정의됩니다: $\pi^*$(s) = $argmax_a Q(s, a)$</li>
</ul>
</li>
<li><p>정책 기반 학습 (Policy-Based Learning)</p>
<ul>
<li>정책 함수 $\pi^*(a|s)$를 직접 학습합니다.</li>
<li>대표적인 알고리즘: Actor-Critic.</li>
<li>연속적인 행동 공간에서도 적용 가능하며, 직접 정책을 최적화합니다.</li>
</ul>
</li>
<li><p>가치 함수(Value Function)와 정책 함수(Policy Function)</p>
</li>
</ol>
<ul>
<li>가치 함수(Value Function): 상태나 상태-행동 쌍의 &quot;가치&quot;를 나타냅니다.<ul>
<li>상태 가치 함수 $V(s)$: 특정 상태 $(s$)의 가치. 지금부터 기대되는 return</li>
<li>행동 가치 함수 $Q(s, a)$: 특정 상태 $(s$)에서 행동 $(a$)를 취했을 때의 가치. 지금 행동으로부터 기대되는 Return</li>
</ul>
</li>
<li>정책 함수(Policy Function): 상태 $(s$)에서 행동 $(a$)를 선택할 확률을 나타냅니다.</li>
</ul>
<ol start="4">
<li>탐험과 활용 문제 (Exploration vs Exploitation)</li>
</ol>
<ul>
<li>탐험(Exploration): 새로운 정보를 탐색하기 위해 무작위 행동을 선택.</li>
<li>활용(Exploitation): 현재 학습된 최적 행동을 선택하여 보상을 극대화.</li>
<li>균형 유지가 중요하며, $epsilon$-탐욕적 정책($epsilon$-greedy)을 사용해 해결합니다.</li>
<li>$epsilon$-탐욕적 정책: 초기에는 탐험을 많이하고 점차 줄여나가는 정책</li>
</ul>
<h1 id="정책-이터레이션">정책 이터레이션</h1>
<ul>
<li><p><strong>정책 이터레이션(Policy Iteration)</strong>은 <strong>가치 기반 학습(Value-Based Learning)</strong>에 속함</p>
</li>
<li><p>정책 이터레이션은 주어진 정책 하에서 벨만 기대 방정식을 사용해 가치를 평가하고 개선</p>
</li>
<li><p>정책 이터레이션은 초기 정책으로 시작해서 점진적으로 개선하여 최적 정책에 도달하는 알고리즘</p>
</li>
<li><p>정책 이터레이션은 정책 평가와 정책 개선 단계를 번갈아 수행하면서 최적의 정책을 찾음</p>
</li>
<li><p>정책 평가(Policy Evaluation):</p>
<ul>
<li>현재 정책 하에 각 상태의 가치를 계산. 이 과정에서, 벨만 기대 방정식을 사용하여 모든 상태에 대해 가치 함수를 반복적으로 계산하며, 이는 현재 정책을 따랐을 때 각 상태에서 기대할 수 있는 장기적인 보상의 총합을 의미함</li>
</ul>
</li>
<li><p>정책 개선(Policy Improvement):</p>
<ul>
<li>계산된 가치 함수 $V^\pi(s)$)를 바탕으로 정책을 업데이트.
정책이 더 이상 개선되지 않을 때(수렴할 때)까지 두 단계를 반복
상태 가치 함수를 사용하여 최적의 정책을 도출</li>
</ul>
<img src="https://raw.githubusercontent.com/zoomKoding/zoomKoding.github.io/source/assets/_posts/RL2-12.png" width="500">


</li>
</ul>
<ul>
<li>Rule : 세모(-1)를 피해 동그라미(1)에 최단 경로로 도달하여 보상을 획득하는 것이 목적임</li>
<li>정책 평가(Evaluate) : 현재 정책에 따라 각 상태의 가치를 계산. 즉, 상태에서 가능한 모든 행동에 대한 기대 보상을 계산하고, 그 값을 바탕으로 상태의 가치를 업데이트</li>
<li>정책 개선(Improve) : 각 상태에서 가능한 행동 중, 가장 큰 보상(즉, 더 높은 상태 가치를 제공하는 행동)을 취할 확률이 더 높도록 정책 개선
Move : 현재까지의 정책을 기반으로 에이전트 이동 시작</li>
</ul>
<h3 id="알고리즘">알고리즘</h3>
<ol>
<li><p><strong>초기화</strong>:</p>
<ul>
<li>초기 정책 $(\pi_0$)를 무작위로 설정.</li>
</ul>
</li>
<li><p><strong>반복</strong>:</p>
<ul>
<li><p><strong>정책 평가</strong>: 현재 정책 $\pi_k$에 대한 가치 함수 $(V^{\pi_k}(s)$) 계산.</p>
<p>$$
^\pi(s) = \sum_{a} \pi(a|s) \sum_{s&#39;} P(s&#39;|s, a) \big[ R(s, a, s&#39;) + \gamma V^\pi(s&#39;) \big]
$$</p>
</li>
<li><p><strong>정책 개선</strong>: 가치 함수 $(V^{\pi_k}(s)$)를 사용해 새로운 정책 $(\pi_{k+1}$) 도출.</p>
<p>$$
\pi_{k+1}(s) = \arg\max_a \sum_{s&#39;} P(s&#39;|s, a) \big[ R(s, a, s&#39;) + \gamma V^{\pi_k}(s&#39;) \big]
$$</p>
</li>
</ul>
</li>
<li><p>정책이 수렴할 때 종료.</p>
</li>
</ol>
<h3 id="특징">특징</h3>
<ul>
<li><strong>장점</strong>: 정책과 가치 함수를 동시에 업데이트하기 때문에 수렴 속도가 빠름.</li>
<li><strong>단점</strong>: 정책 평가 단계가 반복적으로 수행되므로 계산 비용이 클 수 있음.</li>
</ul>
<h1 id="가치-이터레이션">가치 이터레이션</h1>
<ul>
<li><p>가치 이터레이션(Value Iteration)은 가치 기반 학습(Value-Based Learning)에 속함</p>
</li>
<li><p>가치 이터레이션은 각 반복에서 직접적으로 최적의 가치 함수를 추정하고, 이를 바탕으로 최적의 정책을 결정함.</p>
</li>
<li><p>이 방법은 정책 평가와 정책 개선 단계를 하나로 합쳐 더 효율적으로 계산할 수 있게 해줌.</p>
</li>
<li><p>가치 함수 업데이트:</p>
<ul>
<li>모든 상태에 대해 가능한 모든 행동의 결과를 고려하여, 각 상태의 가치 함수를 업데이트함</li>
<li>벨만 최적 방정식을 사용하여, 각 상태에서 가능한 행동 중에서 최대의 기대 가치를 제공하는 행동을 통해 가치 함수를 업데이트함.</li>
<li>이렇게 계산된 가치 함수를 기반으로, 각 상태에서 가능한 행동들 중에서 가장 - 높은 가치를 제공하는 행동을 선택하여 최적의 정책을 결정함.</li>
</ul>
</li>
<li><p>가치 이터레이션은 일반적으로 정책 이터레이션보다 더 빠르게 수렴할 수 있으며, 계산 과정이 좀 더 단순함</p>
<img src="https://raw.githubusercontent.com/zoomKoding/zoomKoding.github.io/source/assets/_posts/RL2-14.png" width="700">


</li>
</ul>
<h3 id="알고리즘-1">알고리즘</h3>
<ol>
<li><p><strong>초기화</strong>:</p>
<ul>
<li>$V(s)$를 임의의 값으로 초기화 (예: $V(s) = 0$).</li>
</ul>
</li>
<li><p><strong>반복</strong>:</p>
<ul>
<li><p>벨만 최적 방정식을 사용해 (V(s))를 업데이트:</p>
<p>$$
V(s) \leftarrow \max_a \sum_{s&#39;} P(s&#39;|s, a) \big[ R(s, a, s&#39;) + \gamma V(s&#39;) \big]
$$</p>
</li>
<li><p>$V(s)$가 수렴할 때까지 반복.</p>
</li>
</ul>
</li>
<li><p>최적 가치 함수 $V^*(s)$를 바탕으로 최적 정책 계산:</p>
</li>
</ol>
<p>$$
     \pi^<em>(s) = \arg\max_a \sum_{s&#39;} P(s&#39;|s, a) \big[ R(s, a, s&#39;) + \gamma V^</em>(s&#39;) \big]
$$</p>
<h3 id="특징-1">특징</h3>
<ul>
<li><strong>장점</strong>: 정책 평가와 정책 개선 단계를 한 번에 수행하므로 계산이 간단하고 직관적.</li>
<li><strong>단점</strong>: 더 많은 반복이 필요할 수 있음.</li>
</ul>
<p>value_iteration.py 파일 실행</p>
<ul>
<li>Rule : 세모(-1)를 피해 동그라미(1)에 최단 경로로 도달하여 보상을 획득하는 것이 목적임</li>
<li>계산(Calculate) : 각 상태에 대해 최적의 행동을 결정하기 위한 가치를 계산하고 업데이트</li>
<li>Print Policy : 현재 추정된 최적의 정책을 시각적으로 표시</li>
<li>Move : 현재까지의 정책을 기반으로 에이전트 이동 시작</li>
<li>Clear : 최초 실행 상태로 초기화</li>
</ul>
<p>위 내용에 대한 실습 코드는 <a href="https://github.com/rlcode/reinforcement-learning-kr">https://github.com/rlcode/reinforcement-learning-kr</a> 에서 참조했습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Streamlit 기초 2]]></title>
            <link>https://velog.io/@s_s/Streamlit-%EA%B8%B0%EC%B4%88-2</link>
            <guid>https://velog.io/@s_s/Streamlit-%EA%B8%B0%EC%B4%88-2</guid>
            <pubDate>Wed, 01 Jan 2025 15:55:48 GMT</pubDate>
            <description><![CDATA[<h1 id="streamlit-서버에서-211로-시작하는-ip가-같은-와이파이에서만-접속-가능한-이유"><strong>Streamlit 서버에서 211로 시작하는 IP가 같은 와이파이에서만 접속 가능한 이유</strong></h1>
<ul>
<li>Streamlit으로 서버를 실행하면 <strong>211로 시작하는 IP</strong>가 출력되는데, 이를 다른 사람들이 접속할 수 있도록 공유해 보셨나요?<br>대부분의 경우, <strong>같은 와이파이에 연결된 사용자들만 접속</strong>이 가능했을 겁니다.</li>
<li>왜 같은 와이파이 사용자만 접속할 수 있었는지 이유와 다른 와이파이 주소를 사용하는 사람도 접속할 수 있도록 관련 네트워크 지식을 배워 봅시다.</li>
</ul>
<h2 id="1-ip-주소의-종류"><strong>1. IP 주소의 종류</strong></h2>
<p>IP 주소는 <strong>공인 IP</strong>와 <strong>사설 IP</strong>로 나뉩니다:</p>
<h3 id="공인-ip"><strong>공인 IP</strong></h3>
<ul>
<li>인터넷 서비스 제공업체(ISP)로부터 할당받는 <strong>고유한 IP 주소</strong>입니다.</li>
<li>전 세계 어디서나 접근할 수 있는 인터넷 주소입니다.</li>
<li>예: <code>125.x.x.x</code>, <code>211.x.x.x</code></li>
</ul>
<h3 id="사설-ip"><strong>사설 IP</strong></h3>
<ul>
<li><strong>로컬 네트워크</strong>(같은 와이파이 또는 공유기)에 속한 기기들끼리 사용할 수 있는 IP입니다.</li>
<li>외부 인터넷에서는 접근할 수 없습니다.</li>
<li>예: <code>192.168.x.x</code>, <code>10.x.x.x</code>, <code>172.16.x.x ~ 172.31.x.x</code></li>
</ul>
<h2 id="2-211로-시작하는-ip는-왜-같은-네트워크에서만-접속-가능한가"><strong>2. 211로 시작하는 IP는 왜 같은 네트워크에서만 접속 가능한가?</strong></h2>
<h3 id="1-공유기와-nat-network-address-translation"><strong>1. 공유기와 NAT (Network Address Translation)</strong></h3>
<ul>
<li>보통 집이나 회사 네트워크의 <strong>공유기</strong>는 외부와 연결된 <strong>공인 IP</strong> 하나를 가지고 있습니다.</li>
<li>내부 네트워크에 연결된 모든 장치는 공유기를 통해 <strong>사설 IP</strong>를 할당받습니다.</li>
<li><strong>211로 시작하는 IP 주소</strong>도 공유기에서 할당된 <strong>로컬 IP</strong>일 수 있으며, 이 경우 같은 네트워크 내에서만 접근이 가능합니다.</li>
</ul>
<h3 id="2-방화벽-설정과-네트워크-라우팅"><strong>2. 방화벽 설정과 네트워크 라우팅</strong></h3>
<ul>
<li>로컬 네트워크에서 서버를 실행하면 <strong>방화벽</strong>이나 <strong>라우팅 설정</strong> 때문에 같은 네트워크에 속한 장치들만 접근할 수 있습니다.</li>
<li>외부에서 접속하려면 해당 IP가 <strong>공인 IP</strong>이어야 하고, <strong>포트 포워딩</strong> 및 네트워크 설정이 필요합니다.</li>
</ul>
<h2 id="3-외부에서-접속하려면-어떻게-해야-하나"><strong>3. 외부에서 접속하려면 어떻게 해야 하나?</strong></h2>
<p>외부에서 Streamlit 서버에 접속하려면 다음 단계를 수행해야 합니다:</p>
<h3 id="1-공인-ip-확인"><strong>1. 공인 IP 확인</strong></h3>
<ul>
<li>공유기나 서버에 할당된 <strong>공인 IP 주소</strong>를 확인합니다.</li>
<li><code>https://whatismyip.com</code> 같은 사이트에서 공인 IP를 확인할 수 있습니다.</li>
</ul>
<h3 id="2-포트-포워딩-설정"><strong>2. 포트 포워딩 설정</strong></h3>
<ul>
<li>공유기 설정에 들어가 <strong>포트 포워딩</strong>을 설정합니다.</li>
<li>Streamlit 서버의 <strong>로컬 IP</strong>와 <strong>포트 번호</strong>를 외부 공인 IP와 연결합니다.<br> <strong>예시</strong>: <code>공인IP:8501</code> → <code>로컬IP:8501</code></li>
</ul>
<h3 id="3-방화벽-해제"><strong>3. 방화벽 해제</strong></h3>
<ul>
<li><p>서버의 <strong>방화벽</strong>이 외부 접근을 차단하지 않도록 허용해야 합니다.</p>
</li>
<li><p><strong>Linux (UFW 방화벽 설정 예시)</strong>:  </p>
<pre><code class="language-bash"> sudo ufw allow 8501
 sudo ufw reload</code></pre>
</li>
<li><p><strong>window</strong>:</p>
<ul>
<li>제어판 → 방화벽 → 인바운드 규칙 추가 → 포트 허용 설정</li>
</ul>
</li>
</ul>
<h3 id="4-도메인-설정-옵션"><strong>4. 도메인 설정 (옵션)</strong></h3>
<ul>
<li>공인 IP 대신 DNS 도메인을 설정하면 접근이 더 쉬워집니다.</li>
<li>예: <a href="http://mydomain.com">http://mydomain.com</a></li>
</ul>
<h3 id="5-결론"><strong>5. 결론</strong></h3>
<ul>
<li><p>Streamlit 서버가 같은 와이파이 사용자에게만 접근 가능한 이유는 사설 IP와 공유기의 NAT 때문입니다.</p>
</li>
<li><p>외부에서도 접속이 가능하도록 하려면:</p>
<ul>
<li>공인 IP 확인</li>
<li>포트 포워딩 설정</li>
<li>방화벽 허용</li>
</ul>
</li>
<li><p>이 과정을 통해 인터넷 어디서든 Streamlit 서버에 접속할 수 있습니다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Streamlit 기초 1]]></title>
            <link>https://velog.io/@s_s/Streamlit-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@s_s/Streamlit-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 01 Jan 2025 15:53:37 GMT</pubDate>
            <description><![CDATA[<h1 id="streamlit">Streamlit</h1>
<h2 id="streamlit-이란"><a href="https://streamlit.io/">Streamlit</a> 이란?</h2>
<ul>
<li><p>Streamlit은 데이터 애플리케이션을 쉽게 만들 수 있도록 도와주는 파이썬 기반의 오픈 소스 프레임워크입니다.</p>
</li>
<li><p>특히 데이터 분석, 모델 시각화, 인터랙티브 대시보드를 빠르고 쉽게 구축할 수 있도록 만들어졌습니다.</p>
</li>
<li><p>Streamlit을 사용하면 복잡한 웹 개발 지식 없이도 직관적인 인터페이스를 갖춘 대화형 웹 애플리케이션을 빠르게 만들 수 있습니다.</p>
<img src="https://blog.zarathu.com/posts/2023-02-01-streamlit/logo.jpeg" widht="500" height="300">

</li>
</ul>
<h2 id="streamlit의-주요-특징">Streamlit의 주요 특징</h2>
<ol>
<li><p>간단한 코드로 인터랙티브 앱 개발:</p>
<ul>
<li>Streamlit은 파이썬 코드만으로 웹 애플리케이션을 만들 수 있습니다.</li>
<li>HTML, CSS, JavaScript와 같은 웹 개발 언어 없이, 파이썬으로 데이터 시각화, 입력 위젯, 상호작용 기능 등을 쉽게 추가할 수 있습니다.</li>
</ul>
<img src="https://images.datacamp.com/image/upload/v1640050216/image34_xrej1c.jpg" width="600">

<img src="https://images.datacamp.com/image/upload/v1640050215/image15_fgtbqf.png" width="600">
</li>
<li><p>빠른 개발 속도:</p>
<ul>
<li>Streamlit은 개발 속도가 매우 빠릅니다.</li>
<li>코드의 몇 줄만으로 완성도 높은 데이터 애플리케이션을 만들 수 있고, 변경사항이 발생하면 즉시 확인할 수 있습니다. 이는 빠르게 프로토타입을 제작하고, 피드백을 반영하며 반복적으로 개선하기에 이상적입니다.</li>
</ul>
</li>
<li><p>직관적인 UI 구성:</p>
<ul>
<li>Streamlit은 버튼, 슬라이더, 파일 업로드와 같은 위젯을 통해 사용자가 데이터를 조작할 수 있게 합니다.</li>
<li>사용자는 이러한 위젯을 통해 간단하게 입력을 제공하고, 모델의 결과를 시각적으로 확인할 수 있습니다.</li>
</ul>
</li>
<li><p>서버 측에서 데이터 처리:</p>
<ul>
<li>Streamlit 앱은 서버에서 실행됩니다.</li>
<li>사용자가 입력을 제공하면 해당 입력은 서버 측에서 처리되어 결과를 렌더링하고, 화면에 즉시 보여주는 방식입니다.</li>
<li>따라서 복잡한 연산이나 모델 추론도 쉽게 구현 가능합니다.</li>
</ul>
</li>
</ol>
<h2 id="streamlit-으로-만들어진-웹-페이지-예시">Streamlit 으로 만들어진 웹 페이지 예시</h2>
<p>streamlit 활용 사례 및 코드 : <a href="https://streamlit.io/gallery">https://streamlit.io/gallery</a></p>
<div style="display: flex; justify-content: space-around;">
<img src = "https://storage.googleapis.com/s4a-prod-share-preview/default/st_app_screenshot_image/378edd13-395a-4da7-9530-3fd2d5c5f19c/Raw_App_Screenshot.png?nf_resize=smartcrop&w=240&h=130" width="300">
<img src = "https://storage.googleapis.com/s4a-prod-share-preview/default/st_app_screenshot_image/e17c0e5c-0cd8-447e-8320-c571d0bedbed/Raw_App_Screenshot.png?nf_resize=smartcrop&w=240&h=130" width="300">
<img src = "https://storage.googleapis.com/s4a-prod-share-preview/default/st_app_screenshot_image/d34e32f8-e7e5-40ab-9714-8aad9717109a/Raw_App_Screenshot.png?nf_resize=smartcrop&w=240&h=130" width="300">  
</div>

<h2 id="streamlit-설치-및-실행-방법-python-38-이상-vscode-환경-실행-권장">streamlit 설치 및 실행 방법 (Python 3.8 이상, VSCODE 환경 실행 권장)</h2>
<ol>
<li><p>Streamlit 설치: VSCode의 터미널에서 다음 명령어를 실행하여 Streamlit을 설치합니다.</p>
<pre><code class="language-bash"> pip install streamlit</code></pre>
<br>
</li>
<li><p>파이썬 파일 생성: VSCode에서 새 파일을 만들고 확장자를 .py로 저장합니다 (예: app.py).</p>
</li>
</ol>
<br>

<ol start="3">
<li>코드 작성: 생성한 파일에 Streamlit 코드를 작성합니다.</li>
</ol>
<pre><code class="language-python">    import streamlit as st

    def main():
        st.title(&quot;내 첫 Streamlit 앱&quot;)

        st.header(&quot;환영합니다!&quot;)
        st.write(&quot;이것은 Streamlit으로 만든 간단한 웹사이트입니다.&quot;)

        name = st.text_input(&quot;당신의 이름을 입력하세요&quot;)
        if name:
            st.write(f&quot;안녕하세요, {name}님!&quot;)

        if st.button(&quot;클릭해보세요&quot;):
            st.balloons()

    if __name__ == &quot;__main__&quot;:
        main()</code></pre>
<p>이 코드는 다음과 같은 기능을 포함하고 있습니다:</p>
<ol>
<li>제목과 헤더를 표시합니다.</li>
<li>사용자에게 이름을 입력받고, 입력 시 인사를 출력합니다.</li>
<li>버튼을 클릭하면 풍선 애니메이션이 나타납니다.</li>
<li>이 코드를 실행하려면, VSCode 터미널에서 다음 명령어를 입력하세요:</li>
</ol>
<br>

<ol start="4">
<li>Streamlit 실행: VSCode의 터미널에서 다음 명령어를 실행하여 Streamlit 앱을 실행합니다.<pre><code class="language-bash">streamlit run app.py</code></pre>
</li>
</ol>
<br>

<ol start="5">
<li>실행하면 기본 웹 브라우저에 새 탭이 열리고 Streamlit 앱이 표시됩니다. 이제 간단한 Streamlit 웹사이트가 완성되었습니다!</li>
</ol>
<p><img src="https://velog.velcdn.com/images/s_s/post/197b5f08-8ee7-45f7-ba7c-06fd12c9ce10/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 3]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-3</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-3</guid>
            <pubDate>Wed, 25 Dec 2024 06:59:28 GMT</pubDate>
            <description><![CDATA[<h1 id="가치-기반-학습value-based-learning과-정책-기반-학습policy-based-learning">가치 기반 학습(Value-Based Learning)과 정책 기반 학습(Policy-Based Learning)</h1>
<ol>
<li>가치 기반 학습 (Value-Based Learning)</li>
</ol>
<ul>
<li>상태(state)의 가치를 학습하여 최적의 정책을 유도하는 방식입니다.</li>
<li>대표적인 알고리즘: Q-Learning, SARSA.</li>
<li>Q-값(Q-Value): 특정 상태 $s$에서 행동 $a$를 취했을 때 기대되는 총 보상 $Q(s, a)$를 학습합니다.</li>
<li>최적 정책은 다음과 같이 정의됩니다: $\pi^*$(s) = $\argmax_a  Q(s, a)$ </li>
</ul>
<ol start="2">
<li>정책 기반 학습 (Policy-Based Learning)</li>
</ol>
<ul>
<li>정책 함수 $\pi^*(a|s)$를 직접 학습합니다.</li>
<li>대표적인 알고리즘: REINFORCE, Actor-Critic.</li>
<li>연속적인 행동 공간에서도 적용 가능하며, 직접 정책을 최적화합니다.</li>
</ul>
<ol start="3">
<li>가치 함수(Value Function)와 정책 함수(Policy Function)</li>
</ol>
<ul>
<li>가치 함수(Value Function): 상태나 상태-행동 쌍의 &quot;가치&quot;를 나타냅니다.<ul>
<li>상태 가치 함수 $V(s)$: 특정 상태 $(s$)의 가치.</li>
<li>행동 가치 함수 $Q(s, a)$: 특정 상태 $(s$)에서 행동 $(a$)를 취했을 때의 가치.</li>
</ul>
</li>
<li>정책 함수(Policy Function): 상태 $(s$)에서 행동 $(a$)를 선택할 확률을 나타냅니다.</li>
</ul>
<ol start="4">
<li>탐험과 활용 문제 (Exploration vs Exploitation)</li>
</ol>
<ul>
<li>탐험(Exploration): 새로운 정보를 탐색하기 위해 무작위 행동을 선택.</li>
<li>활용(Exploitation): 현재 학습된 최적 행동을 선택하여 보상을 극대화.</li>
<li>균형 유지가 중요하며, $epsilon-탐욕적 정책($($epsilon$)-greedy)이나 Boltzmann 탐색을 사용해 해결합니다.</li>
</ul>
<h1 id="정책-이터레이션">정책 이터레이션</h1>
<ul>
<li><p>정책 이터레이션은 주어진 정책 하에서 벨만 기대 방정식을 사용해 가치를 평가하고 개선</p>
</li>
<li><p>정책 이터레이션은 정책 평가와 정책 개선 단계를 번갈아 수행하면서 최적의 정책을 찾음</p>
</li>
<li><p>정책 평가(Policy Evaluation):</p>
<ul>
<li>현재 정책 하에 각 상태의 가치를 계산. 이 과정에서, 벨만 기대 방정식을 사용하여 모든 상태에 대해 가치 함수를 반복적으로 계산하며, 이는 현재 정책을 따랐을 때 각 상태에서 기대할 수 있는 장기적인 보상의 총합을 의미함</li>
</ul>
</li>
<li><p>정책 개선(Policy Improvement):</p>
<ul>
<li>평가된 가치 함수를 바탕으로 각 상태에서 최적의 행동을 선택하는 새로운 정책을 만듦.</li>
<li>이 단계에서는 각 상태에 대해 가능한 모든 행동을 고려하고, 가장 높은 기대 리턴을 제공하는 행동을 선택하여 정책을 개선합니다.
정책이 더 이상 개선되지 않을 때(수렴할 때)까지 두 단계를 반복
상태 가치 함수를 사용하여 최적의 정책을 도출</li>
</ul>
<img src="https://velog.velcdn.com/images/s_s/post/2c222321-d796-44d5-a62f-e5dbaecbcef7/image.png" width="500">


</li>
</ul>
<ul>
<li>Rule : 세모(-1)를 피해 동그라미(1)에 최단 경로로 도달하여 보상을 획득하는 것이 목적임</li>
<li>정책 평가(Evaluate) : 현재 정책에 따라 각 상태의 가치를 계산. 즉, 상태에서 가능한 모든 행동에 대한 기대 보상을 계산하고, 그 값을 바탕으로 상태의 가치를 업데이트</li>
<li>정책 개선(Improve) : 각 상태에서 가능한 행동 중, 가장 큰 보상(즉, 더 높은 상태 가치를 제공하는 행동)을 취할 확률이 더 높도록 정책 개선
Move : 현재까지의 정책을 기반으로 에이전트 이동 시작</li>
</ul>
<p>실행 코드는 <a href="https://github.com/rlcode/reinforcement-learning-kr">https://github.com/rlcode/reinforcement-learning-kr</a> 를 참조해주세요.</p>
<h1 id="가치-이터레이션">가치 이터레이션</h1>
<ul>
<li><p>가치 이터레이션은 각 반복에서 직접적으로 최적의 가치 함수를 추정하고, 이를 바탕으로 최적의 정책을 결정함.</p>
</li>
<li><p>이 방법은 정책 평가와 정책 개선 단계를 하나로 합쳐 더 효율적으로 계산할 수 있게 해줌.</p>
</li>
<li><p>가치 함수 업데이트:</p>
</li>
<li><p>모든 상태에 대해 가능한 모든 행동의 결과를 고려하여, 각 상태의 가치 함수를 업데이트함</p>
</li>
<li><p>벨만 최적 방정식을 사용하여, 각 상태에서 가능한 행동 중에서 최대의 기대 가치를 제공하는 행동을 통해 가치 함수를 업데이트함.</p>
</li>
<li><p>이렇게 계산된 가치 함수를 기반으로, 각 상태에서 가능한 행동들 중에서 가장 - 높은 가치를 제공하는 행동을 선택하여 최적의 정책을 결정함.</p>
</li>
<li><p>가치 이터레이션은 일반적으로 정책 이터레이션보다 더 빠르게 수렴할 수 있으며, 계산 과정이 좀 더 단순함</p>
<img src="https://velog.velcdn.com/images/s_s/post/e16d4da1-53ca-493c-b72a-8919d0fea1b3/image.png" width="700">


</li>
</ul>
<p>value_iteration.py 파일 실행</p>
<ul>
<li>Rule : 세모(-1)를 피해 동그라미(1)에 최단 경로로 도달하여 보상을 획득하는 것이 목적임</li>
<li>계산(Calculate) : 각 상태에 대해 최적의 행동을 결정하기 위한 가치를 계산하고 업데이트</li>
<li>Print Policy : 현재 추정된 최적의 정책을 시각적으로 표시</li>
<li>Move : 현재까지의 정책을 기반으로 에이전트 이동 시작</li>
<li>Clear : 최초 실행 상태로 초기화</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 2]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-2</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-2</guid>
            <pubDate>Wed, 25 Dec 2024 05:39:35 GMT</pubDate>
            <description><![CDATA[<h1 id="강화학습-개념-및-markov-decision-process-mdp-이해">강화학습 개념 및 Markov Decision Process (MDP) 이해</h1>
<h2 id="1-강화학습의-주요-구성-요소">1. 강화학습의 주요 구성 요소</h2>
<p>강화학습은 에이전트가 환경과 상호작용하며 최적의 행동을 학습하는 방식입니다. 이 과정에서 중요한 구성 요소들이 있습니다: <strong>에이전트</strong>, <strong>환경</strong>, <strong>상태</strong>, <strong>행동</strong>, <strong>보상</strong>입니다.</p>
<h3 id="11-에이전트-agent">1.1 에이전트 (Agent)</h3>
<p>에이전트는 <strong>강화학습</strong>의 주체로, 환경과 상호작용하며 행동을 취하고 보상을 받습니다. 에이전트는 주어진 목표를 달성하기 위해 <strong>정책(policy)</strong>을 학습하고, 이를 통해 최적의 행동을 선택합니다.</p>
<ul>
<li><strong>에이전트의 역할</strong>: 주어진 환경에서의 최적의 행동을 학습하는 주체</li>
<li><strong>에이전트의 목표</strong>: 보상을 최대화하는 행동을 찾아내는 것</li>
</ul>
<h3 id="12-환경-environment">1.2 환경 (Environment)</h3>
<p>환경은 에이전트가 상호작용하는 외부 세계입니다. 에이전트의 행동에 반응하여 상태를 변화시키고, 보상을 제공합니다. 환경은 에이전트가 받는 피드백의 출처이며, 에이전트는 이 환경에 의해 행동 결과를 인식합니다.</p>
<ul>
<li><strong>환경의 역할</strong>: 에이전트의 행동에 따라 상태를 변화시키고 보상을 제공</li>
<li><strong>환경의 목표</strong>: 에이전트가 최적의 행동을 학습하도록 돕는 것</li>
</ul>
<h3 id="13-상태-state">1.3 상태 (State)</h3>
<p>상태는 환경의 특정 순간을 나타내며, 에이전트가 어떤 행동을 취할지 결정하는 중요한 정보입니다. 환경은 여러 개의 상태로 구성되며, 에이전트는 현재 상태를 기반으로 행동을 선택합니다.</p>
<ul>
<li><strong>상태의 정의</strong>: 환경의 특정 시점에서의 정보나 조건</li>
<li><strong>상태의 예</strong>: 체스에서의 보드 상태, 로봇의 위치와 속도</li>
</ul>
<h3 id="14-행동-action">1.4 행동 (Action)</h3>
<p>행동은 에이전트가 선택하는 결정으로, 환경에 영향을 미칩니다. 에이전트는 다양한 가능한 행동 중 하나를 선택하고, 그 행동이 결과적으로 상태를 변화시키고 보상을 초래합니다.</p>
<ul>
<li><strong>행동의 정의</strong>: 에이전트가 선택할 수 있는 행동들</li>
<li><strong>행동의 예</strong>: 체스에서의 기물 이동, 로봇의 회전</li>
</ul>
<h3 id="15-보상-reward">1.5 보상 (Reward)</h3>
<p>보상은 에이전트가 행동을 통해 얻는 피드백입니다. 에이전트는 보상을 최대화하려는 목표를 가지고 학습합니다. 보상은 긍정적일 수도, 부정적일 수도 있으며, 장기적으로 보상을 극대화하는 행동을 학습하는 것이 목표입니다.</p>
<ul>
<li><strong>보상의 정의</strong>: 에이전트가 행동을 통해 받는 피드백</li>
<li><strong>보상의 예</strong>: 체스에서 상대의 기물을 잡았을 때 얻는 점수, 로봇이 목표 지점에 도달했을 때 받는 보상</li>
</ul>
<h2 id="2-markov-decision-process-mdp">2. Markov Decision Process (MDP)</h2>
<p>Markov Decision Process(MDP)는 강화학습 문제를 모델링하는 수학적 틀입니다. MDP는 에이전트가 환경과 상호작용하며 최적의 정책을 학습하는 과정을 정의합니다. MDP는 네 가지 주요 구성 요소로 이루어져 있습니다:</p>
<ul>
<li><strong>상태(State, S)</strong>: 환경의 특정 시점에서의 상태</li>
<li><strong>행동(Action, A)</strong>: 에이전트가 선택할 수 있는 행동</li>
<li><strong>전이 확률(Transition Probability, P)</strong>: 에이전트가 특정 상태에서 행동을 선택했을 때, 다음 상태로 전이될 확률</li>
<li><strong>보상 함수(Reward Function, R)</strong>: 에이전트가 특정 상태에서 행동을 취할 때 받는 보상</li>
</ul>
<h3 id="21-markov-property">2.1 Markov Property</h3>
<p>MDP는 <strong>Markov Property</strong>를 따릅니다. 이는 &quot;현재 상태는 이전 상태들과 상관없이 다음 상태를 결정하는 데 충분하다&quot;는 성질입니다. 즉, 에이전트가 현재 상태에서 어떤 행동을 선택하면, 그 결과로 다음 상태와 보상이 결정됩니다. 과거의 상태나 행동은 현재 상태와 보상에 영향을 미치지 않습니다.</p>
<h3 id="22-mdp의-수학적-정의">2.2 MDP의 수학적 정의</h3>
<p>MDP는 5개의 주요 요소로 정의됩니다:</p>
<ol>
<li><strong>S</strong>: 상태 공간 (가능한 모든 상태들의 집합)</li>
<li><strong>A</strong>: 행동 공간 (가능한 모든 행동들의 집합)</li>
<li><strong>P(s&#39;|s, a)</strong>: 전이 확률 함수 (현재 상태 s에서 행동 a를 취했을 때, 상태 s&#39;로 전이될 확률)</li>
<li><strong>R(s, a)</strong>: 보상 함수 (현재 상태 s에서 행동 a를 취했을 때 얻는 보상)</li>
<li><strong>γ (Gamma)</strong>: 할인 인자 (미래의 보상에 대한 현재 가치 평가)</li>
</ol>
<h3 id="23-최적-정책optimal-policy">2.3 최적 정책(Optimal Policy)</h3>
<p>MDP에서 최적의 정책은 에이전트가 각 상태에서 어떤 행동을 선택해야 하는지를 결정합니다. 이를 통해 에이전트는 장기적으로 보상을 최대화할 수 있습니다. 최적 정책을 찾는 과정은 <strong>벨만 방정식</strong>을 통해 해결됩니다.</p>
<h3 id="24-bellman-방정식">2.4 Bellman 방정식</h3>
<ul>
<li><p>벨만 방정식은 상태에서 최적 행동을 선택하는 수학적 방법을 제공합니다. 이 방정식은 각 상태에서 최적의 보상 값을 계산하는 데 사용됩니다. 최적 정책을 찾는 것은 이 방정식을 풀어 최적의 값을 찾아내는 과정입니다.</p>
<h3 id="241-벨만-방정식의-기본-형태">2.4.1 벨만 방정식의 기본 형태</h3>
<p>벨만 방정식은 강화학습에서 <strong>가치 함수</strong>를 계산하고, <strong>최적의 정책(Optimal Policy)</strong>을 찾는 데 중요한 역할을 합니다. 이 방정식은 <strong>벨만 기대방정식 (Bellman Expectation Equation)</strong>과 <strong>벨만 최적방정식 (Bellman Optimality Equation)</strong> 두 가지로 나뉩니다.</p>
<h3 id="242-벨만-기대방정식-bellman-expectation-equation">2.4.2 벨만 기대방정식 (Bellman Expectation Equation)</h3>
<p>벨만 기대방정식은 주어진 정책$\pi$에 대해 각 상태에서의 <strong>가치 함수</strong>를 계산하는 식입니다. 특정 정책$\pi$에 대해 상태 (s)의 가치 $V^\pi$는 다음과 같이 정의됩니다:</p>
<p>$$
V^\pi(s) = \mathbb{E}_{\pi} \left[ R(s, a) + \gamma V^\pi(s&#39;) \right]
$$</p>
<p>여기서:</p>
<ul>
<li>$V^\pi(s)$: 정책 $\pi$에 따른 상태 (s)의 가치 함수</li>
<li>$R(s, a)$: 상태 $s$에서 행동 $a$를 취했을 때 얻는 보상</li>
<li>$\gamma$: 할인 인자 (0과 1 사이의 값)</li>
<li>$s&#39;$: 다음 상태</li>
<li>$\mathbb{E}_{\pi}$: 정책 $\pi$에 따른 기대값</li>
</ul>
<p>이 방정식은 상태 $(s$)에서 특정 정책 $\pi$를 따를 때, 상태 $(s$)의 가치가 <strong>현재 보상</strong>과 <strong>미래 상태의 기대 가치</strong>의 합으로 구성된다는 것을 나타냅니다.</p>
<h3 id="243-벨만-최적방정식-bellman-optimality-equation">2.4.3 벨만 최적방정식 (Bellman Optimality Equation)</h3>
<p>벨만 최적방정식은 <strong>최적 정책</strong>을 구하는 데 사용되는 방정식입니다. 상태 (s)에서의 <strong>최적 가치 함수</strong> $(V^*(s)$)는 다음과 같이 정의됩니다:</p>
<p>$$
V^<em>(s) = \max_a \left[ R(s, a) + \gamma \sum_{s&#39;} P(s&#39;|s, a) V^</em>(s&#39;) \right]
$$</p>
<p>여기서:</p>
<ul>
<li>$V^*(s)$: 상태 $s$에서의 최적 가치 함수</li>
<li>$R(s, a)$: 상태 $s$에서 행동 $a$를 취했을 때 얻는 보상</li>
<li>$\gamma$: 할인 인자(감가율)</li>
<li>$P(s&#39;|s, a)$: 상태 $s$에서 행동 $a$를 취했을 때, 상태 $s&#39;$로 전이될 확률</li>
</ul>
<p>이 방정식은 상태 (s)에서 가능한 모든 행동 중 <strong>가장 높은 보상</strong>을 제공하는 행동을 선택할 때의 가치를 계산합니다. 즉, 최적 가치 함수는 <strong>현재 보상</strong>과 <strong>미래 상태의 가치</strong>를 결합한 값 중 최댓값을 취하게 됩니다.</p>
<h3 id="244-최적-정책-optimal-policy">2.4.4 최적 정책 (Optimal Policy)</h3>
<p>최적 정책 $(\pi^<em>(s)$)는 주어진 상태 (s)에서 최적의 행동 $(a^</em>$)를 선택하는 정책으로, 벨만 최적방정식을 사용하여 계산할 수 있습니다. 최적 정책은 다음과 같이 정의됩니다:</p>
<p>$$
\pi^<em>(s) = \arg\max_a \left[ R(s, a) + \gamma \sum_{s&#39;} P(s&#39;|s, a) V^</em>(s&#39;)   \right]
$$</p>
<p>여기서:</p>
<ul>
<li>$\pi^*(s)$: 상태 (s)에서 최적의 행동을 선택하는 정책</li>
<li>$\arg\max$: 가능한 행동 중 가장 큰 값을 주는 행동을 선택</li>
</ul>
<h2 id="245-벨만-방정식의-활용">2.4.5 벨만 방정식의 활용</h2>
<ul>
<li>벨만 방정식은 <strong>가치 반복(Value Iteration)</strong> 및 <strong>정책 반복(Policy Iteration)</strong> 알고리즘에서 중요한 역할을 하며, 이를 통해 에이전트는 최적 정책을 학습하고, 주어진 환경에서 <strong>장기적인 보상</strong>을 최대화할 수 있습니다.</li>
</ul>
</li>
</ul>
<h2 id="3-결론">3. 결론</h2>
<ul>
<li><strong>에이전트</strong>, <strong>환경</strong>, <strong>상태</strong>, <strong>행동</strong>, <strong>보상</strong>은 강화학습에서 중요한 구성 요소입니다. 에이전트는 환경과 상호작용하며, 상태를 고려하여 행동을 선택하고 보상을 받습니다.</li>
<li><strong>Markov Decision Process(MDP)</strong>는 강화학습 문제를 모델링하는 수학적 틀로, 상태, 행동, 전이 확률, 보상 함수 등의 요소를 통해 최적의 정책을 학습할 수 있게 합니다.</li>
<li><strong>Markov Property</strong>를 따르는 MDP에서는 현재 상태만이 다음 상태를 결정하는 데 필요한 모든 정보를 제공하므로, 이전 상태는 필요하지 않습니다. 이를 통해 에이전트는 장기적인 보상을 최대화하는 최적의 정책을 학습할 수 있습니다.</li>
</ul>
<h2 id="1-openai-gym">1. OpenAI Gym</h2>
<p><strong>OpenAI Gym</strong>은 강화학습 환경을 제공하는 라이브러리로, 다양한 시뮬레이션 환경을 구축하여 강화학습 실험을 쉽게 할 수 있게 돕습니다. Gym은 강화학습을 위한 여러 환경을 제공하고, 이 환경들에서 에이전트를 훈련시킬 수 있습니다.</p>
<h3 id="주요-특징">주요 특징:</h3>
<ul>
<li>다양한 환경(게임, 로봇 시뮬레이션, 제어 문제 등)을 제공합니다.</li>
<li>Python에서 쉽게 사용할 수 있으며, 강화학습 실험을 위한 표준화된 API를 제공합니다.</li>
<li>강화학습 알고리즘을 실험하는 데 필요한 모든 도구를 제공합니다.</li>
</ul>
<h3 id="환경-예시">환경 예시:</h3>
<ul>
<li><strong>CartPole</strong>: 막대기를 수직으로 세우고 균형을 맞추는 환경</li>
<li><strong>MountainCar</strong>: 두 개의 경사를 오르며 목표를 향해 움직이는 환경</li>
<li><strong>Atari Games</strong>: 다양한 아타리 게임을 강화학습 환경으로 제공</li>
</ul>
<h1 id="강화학습-실습-환경-설정-및-기본-사용법">강화학습 실습 환경 설정 및 기본 사용법</h1>
<p>강화학습을 실습하려면 먼저 Python 환경을 설정하고 라이브러리를 설치해야 합니다. 여기서는 <strong>OpenAI Gym</strong>을 사용하여 실습 환경을 설정하는 방법을 소개합니다.</p>
<h2 id="1-python-환경-설정">1. Python 환경 설정</h2>
<ol>
<li><strong>Python 설치</strong>: Python 3.6 이상의 버전이 필요합니다.</li>
<li><strong>필요한 라이브러리 설치</strong>:<ul>
<li><code>gym</code>: OpenAI Gym을 설치합니다.</li>
</ul>
</li>
</ol>
<pre><code>!pip install gym</code></pre><pre><code class="language-python">import gym
import matplotlib.pyplot as plt
from IPython.display import display, clear_output

# 환경 생성
env = gym.make(&#39;CartPole-v1&#39;)

# 환경 초기화
state = env.reset()

# 환경 시뮬레이션 실행
done = False
total_reward = 0

# 시각화를 위한 설정
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111)

while not done:
    # 랜덤 행동 선택
    action = env.action_space.sample()  # 행동을 랜덤으로 선택

    # 환경에 행동을 적용하고 결과를 받음
    next_state, reward, done, info = env.step(action)

    # 총 보상에 추가
    total_reward += reward

    # 화면에 환경을 렌더링 (matplotlib 사용)
    ax.clear()
    ax.imshow(env.render(mode=&#39;rgb_array&#39;))  # 화면을 이미지로 반환
    ax.set_title(f&quot;Total Reward: {total_reward}&quot;)
    display(fig)

    # 이전 출력 지우기
    clear_output(wait=True)

# 시뮬레이션 종료 후 총 보상 출력
print(f&quot;총 보상: {total_reward}&quot;)

# 환경 종료
env.close()</code></pre>
<p><img src="https://velog.velcdn.com/images/s_s/post/599e89d0-86e0-4754-beb4-df66b564b725/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 기초 1]]></title>
            <link>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-1</link>
            <guid>https://velog.io/@s_s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-%EA%B8%B0%EC%B4%88-1</guid>
            <pubDate>Wed, 25 Dec 2024 05:36:14 GMT</pubDate>
            <description><![CDATA[<h1 id="강화학습의-정의-특징-및-응용-분야">강화학습의 정의, 특징 및 응용 분야</h1>
<h2 id="1-강화학습reinforcement-learning-정의">1. 강화학습(Reinforcement Learning) 정의</h2>
<p>강화학습(RL)은 <strong>에이전트(agent)</strong>가 환경과 상호작용을 통해 <strong>보상(reward)</strong>을 최대화하는 방법을 학습하는 알고리즘입니다. 에이전트는 여러 상태(state)에서 가능한 <strong>행동(action)</strong>을 선택하고, 그에 따라 <strong>결과</strong>와 <strong>보상</strong>을 받으며 학습합니다. 이 과정에서 에이전트는 최적의 정책(policy)을 학습하려고 합니다.</p>
<ul>
<li><strong>에이전트(Agent)</strong>: 의사 결정을 내리는 주체</li>
<li><strong>환경(Environment)</strong>: 에이전트가 상호작용하는 대상</li>
<li><strong>상태(State)</strong>: 환경의 현재 상태</li>
<li><strong>행동(Action)</strong>: 에이전트가 선택할 수 있는 가능한 행동</li>
<li><strong>보상(Reward)</strong>: 행동에 대한 피드백, 에이전트가 목표를 얼마나 잘 달성했는지 평가하는 기준<img src="https://deepanshut041.github.io/Reinforcement-Learning/notes/00_Introduction_to_rl/images/intro_to_rl.png">

</li>
</ul>
<h2 id="2-강화학습의-특징">2. 강화학습의 특징</h2>
<ul>
<li><strong>시간적 지연</strong>: 에이전트는 즉각적인 보상을 받지 않고, 장기적인 보상을 고려하여 학습해야 합니다.</li>
<li><strong>탐험과 착오(Exploration vs. Exploitation)</strong>: 에이전트는 새로운 행동을 시도(탐험)할지, 이미 알고 있는 최적의 행동을 반복(착오)할지 결정해야 합니다.</li>
<li><strong>연속적인 학습</strong>: 강화학습은 주어진 작업에 대한 완전한 해답을 찾을 때까지 학습을 지속적으로 수행합니다.</li>
<li><strong>상태 공간과 행동 공간</strong>: 상태와 행동의 공간이 매우 커질 수 있어, 이를 효율적으로 다루는 기술이 필요합니다.</li>
</ul>
<h2 id="3-강화학습의-응용-분야">3. 강화학습의 응용 분야</h2>
<p>강화학습은 다양한 분야에서 강력한 성능을 발휘합니다. 주요 응용 분야는 다음과 같습니다:</p>
<ul>
<li><p><strong>게임</strong>: 알파고(AlphaGo), 체스, 바둑 등에서의 승패 예측 및 최적의 전략 학습</p>
</li>
<li><p><strong>로봇 제어</strong>: 로봇의 동작 및 경로 최적화</p>
</li>
<li><p><strong>자율주행차</strong>: 자율주행차가 주행 경로와 행동을 선택하는 데 강화학습을 사용</p>
</li>
<li><p><strong>금융</strong>: 주식 투자, 포트폴리오 관리, 알고리즘 트레이딩</p>
</li>
<li><p><strong>자연어 처리</strong>: 대화형 에이전트, 챗봇의 학습</p>
</li>
<li><p><strong>의료</strong>: 치료 방법 최적화, 약물 개발</p>
<p><img src="https://jonghyunho.github.io/assets/img/posts/20200505/cartpole_episode_100.gif" width="500"> <img src="https://i.imgur.com/BSUCoUV.gif" width="300" height="340"></p>
</li>
</ul>
<h1 id="강화학습과-지도학습비지도학습-비교">강화학습과 지도학습/비지도학습 비교</h1>
<h2 id="1-지도학습supervised-learning과-강화학습의-비교">1. 지도학습(Supervised Learning)과 강화학습의 비교</h2>
<table>
<thead>
<tr>
<th>특성</th>
<th>지도학습(Supervised Learning)</th>
<th>강화학습(Reinforcement Learning)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>목표</strong></td>
<td>입력과 출력의 관계를 학습하여 주어진 데이터에 대한 예측을 수행</td>
<td>보상을 최대화하는 행동을 학습하여 장기적인 목표 달성</td>
</tr>
<tr>
<td><strong>데이터</strong></td>
<td>레이블이 있는 데이터셋을 사용</td>
<td>보상에 기반한 경험을 통해 학습</td>
</tr>
<tr>
<td><strong>학습 방식</strong></td>
<td>주어진 입력에 대한 정확한 출력 값을 예측</td>
<td>행동을 통해 경험을 쌓고, 보상을 통해 정책을 학습</td>
</tr>
<tr>
<td><strong>에러</strong></td>
<td>예측 오차(손실 함수)를 최소화하는 방향으로 학습</td>
<td>보상(또는 페널티)을 통해 잘못된 행동을 수정하며 학습</td>
</tr>
<tr>
<td><strong>주요 알고리즘</strong></td>
<td>선형 회귀, 로지스틱 회귀, 신경망, SVM 등</td>
<td>Q-learning, Deep Q-Network(DQN), 정책 그라디언트 등</td>
</tr>
</tbody></table>
<h2 id="2-비지도학습unsupervised-learning과-강화학습의-비교">2. 비지도학습(Unsupervised Learning)과 강화학습의 비교</h2>
<table>
<thead>
<tr>
<th>특성</th>
<th>비지도학습(Unsupervised Learning)</th>
<th>강화학습(Reinforcement Learning)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>목표</strong></td>
<td>데이터 내의 숨겨진 패턴을 학습</td>
<td>최적의 행동을 선택하여 보상을 최대화하는 정책을 학습</td>
</tr>
<tr>
<td><strong>데이터</strong></td>
<td>레이블이 없는 데이터셋을 사용</td>
<td>에이전트의 상호작용을 통한 보상 기반 데이터 사용</td>
</tr>
<tr>
<td><strong>학습 방식</strong></td>
<td>데이터 내의 군집화나 차원 축소를 통해 패턴을 찾음</td>
<td>행동을 통해 경험을 쌓고 보상을 통해 학습</td>
</tr>
<tr>
<td><strong>주요 알고리즘</strong></td>
<td>K-평균, DBSCAN, PCA, Autoencoders 등</td>
<td>Q-learning, DQN, REINFORCE 등</td>
</tr>
</tbody></table>
<hr>
<h1 id="결론">결론</h1>
<ul>
<li><strong>강화학습</strong>은 에이전트가 <strong>환경</strong>과 상호작용하며 <strong>보상</strong>을 통해 <strong>정책</strong>을 학습하는 과정으로, 주어진 목표를 달성하는 데 중요한 도구가 됩니다.</li>
<li><strong>지도학습</strong>과 <strong>비지도학습</strong>은 주로 데이터가 주어졌을 때 예측을 하는 데 초점을 맞추며, 강화학습은 <strong>행동</strong>을 통해 경험을 쌓고, <strong>장기적인 보상</strong>을 극대화하는 데 중점을 둡니다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>