본문 바로가기

명사 美 비격식 (무리 중에서) 아주 뛰어난[눈에 띄는] 사람[것]

SK 네트웍스 AI 캠프

SK 네트웍스 AI 캠프 - 3 _딥러닝 - Day24_딥러닝개념_프레임워크설치

인공지능

컴퓨터가 사람처럼 생각하고 판단하도록 만드는 기술

학습, 추론, 문제해결, 의사결정, 언어이해, 시각인식등의 능력을 목표로 한다.

https://standout.tistory.com/103

 

약인공지능과 강인공지능

인공지능 artificial intelligence 인간의 지능을 인공적으로 구현하려는 컴퓨터과학의 분야중 하나 https://ko.wikipedia.org/wiki/%EC%9D%B8%EA%B3%B5%EC%A7%80%EB%8A%A5 인공지능 - 위키백과, 우리 모두의 백과사전 위

standout.tistory.com

 

 

AI : 합집합,인간의 지능을 컴퓨터로 구현하는 기술
 └─ Machine Learning: AI의 한분야, 데이터를 보고 스스로 규칙을 학습
      └─ Deep Learning: 머신러닝의 한 분야, 신경망을 여러층 사용, 인공신경망을 이용한 머신러닝 기법

https://standout.tistory.com/1531

 

인공지능, 기계학습, 딥러닝의 차이

인공지능 ⊃ 기계학습 ⊃ 딥러닝인공지능: 사람의 지능을 인공적으로 재현함.기계학습: 인공지능 연구분야 중 하나로 규칙을 찾아내는 기법딥러닝: 사람이 가르쳐주지않아도 스스로 답을 찾아

standout.tistory.com

 

 

지도학습: 정답이 있는 데이터학습

비지도학습: 정답없이 패턴 발견

강화학습: 보상을 이용한 학습

https://standout.tistory.com/1533

 

기계학습과 기계학습의 분야: 지도학습/비지도학습/강화학습

기계학습과 기계학습의 분야 기계학습이란?인공지능(AI)의 한 분야데이터와 통계적 모델링 기법을 사용하여 컴퓨터 시스템이 스스로 학습할 수 있게 만드는 기술https://standout.tistory.com/1529 자동

standout.tistory.com

 

 

딥러닝

기본신경망이 입력층 - 출력층이라면 딥러닝은 입력층 - 은닉층 - 은닉층 - 출력층. 은닉층이 여러개라서 Deep.

고양이를 학습한다면 은닉층을 지나며 점점 복잡한 특징을 만들어가는것. 



모서리

눈/귀

얼굴

고양이

 

딥러닝의 역사

1943, Warren MuCulloch, Walter Pitts '뉴ㅠ런을 수학식을 만들 수 있지않을까?'

x = 입력
w = 가중치
b = 편향
f = 활성화 함수
y = 출력

y = f(wx + b)

* 현재의 chatgpt도 결국 수십억개의 이 연산으로 동작한다.

 

 

퍼셉트론

1958, Frank RosenBlatt '뉴런이 학습까지 하게 만들자', 머신러닝의 시작점

 

AI 겨울

1969, Marvin Minsky, Seymout Papert 퍼셉트론은 XOR 문제를 못푼다는 사실을 발견한다. 신경망에 대한 무관심의 시작과 20년 가까이 연구의 침체

 

딥러닝의 아버지

1990 Geoffrey Hinton '퍼셉트론을 여러층으로 쌓으면 어떨까?; 은닉층의 출연.= Deep Neural Network

 

AlexNet 혁명

2012, AlexNet 등장. 이미지 인식 정확도가 폭발적 향상, 이후 CNN RNN LSTM Transformer가 발전.

 

현재

ChatGPT, Gemini, Claude, Stable Diffusion, Midjourney 같은 생성형 AI시대

https://standout.tistory.com/1528

 

인공지능의 역사: 기원(계산 기계 및 지능). 퍼셉트론, 인공지능의 겨울, 전문가 시스템, 신경망

로봇과 인공지능https://standout.tistory.com/881 로봇을 프로그래밍하다, Flexible Work HoldingFlexible Work Holding 로봇에 프로그래밍해 부품이 바뀌어도 다르게 움직이게 하며 차를 만드는 방식을 업그레이드

standout.tistory.com

 

 

 

인공신경망: 딥러닝은 사람 뇌를 흉내 낸것. 

퍼셉트론은 가장 단 순한 뉴런. y=w1​x1​+w2​x2​+w3​x3​+b, 입력값에 중요도를 곱해서 판단하는 계산기

퍼셉트론은 단순한 문제는 해결하나 XOR같은 복잡한문제를 못풀어 등장한것이 다중퍼셉트론. MLP, 은닉층이 많아질수록 복잡한 패턴을 학습한다 .

 

활성화함수

활성화함수가 없으면 신경망을 100층 쌓아도 결국 하나의 직선 계산과 비슷하다. 비선형성 복잡한 판단을 추가.

sigmoid, tank, relu, leaky relu, softmax

 

딥러닝이 발전한 이유

예전에는 빅데이터가 사진 100장이었다면 현재는 유튜브, sns, 블로그, 쇼핑몰등 수십억장이다.

CPU가 1명작업이라면 GPU로 1000명 동시작업이 가능하다. 행렬곱셈을 많이하는 딥러닝에 GPU가 필수이다. 

대표모델 CNN(챗) RNN LSTM Transformer(siri) GAN 등 알고리즘이 발전했다. 

얼굴인식, 자율주행, 의료영상 등의 딥러닝 활용분야가 늘어났다 .

CNN 이미지
RNN 순차 데이터
LSTM 시계열 데이터
Transformer 자연어 처리, 생성형 AI
GAN 이미지 생성
AutoEncoder 차원축소, 특징추출

 

 

 

 

딥러닝 프레임워크 = 신경망 만드는 도구

대표도구: TensorFlow, Keras, PyTorch.

 

CUDA: NVIDIA 가 만든 기술로 PyTorch가 GPU를 사용하도록 해준다 .

cuDNN: CUDA위에서 동작하는 딥러닝 전용 가속 라이브러리

 

 

 

 

 

 

샘플코드 보스톤 주택가격예측 DNN 모델  housing_pytorch_dnn 을 분석해보자.

 

 

import

time 학습에 걸린 시간을 측정하기 위해 사용한다 

seed 고정

gpu 확인

# ============================================================
# 1. 기본 라이브러리 임포트
# ============================================================

# pandas는 CSV 파일을 읽고, 표 형태의 데이터를 다룰 때 사용하는 대표적인 데이터 분석 라이브러리입니다.
import pandas as pd

# numpy는 수치 계산을 빠르게 처리하기 위한 라이브러리입니다.
# PyTorch Tensor로 변환하기 전에 데이터 타입을 맞출 때 자주 사용합니다.
import numpy as np

# matplotlib은 그래프를 그릴 때 사용하는 기본 시각화 라이브러리입니다.
import matplotlib.pyplot as plt

# seaborn은 matplotlib보다 조금 더 보기 좋은 통계 그래프를 쉽게 그릴 수 있게 해주는 라이브러리입니다.
import seaborn as sns

# time은 학습에 걸린 시간을 측정하기 위해 사용합니다.
from time import time

# os는 파일 존재 여부를 확인할 때 사용합니다.
import os

# PyTorch의 핵심 라이브러리입니다.
# Tensor 생성, GPU 이동, 자동미분, 모델 학습 등에 사용합니다.
import torch

# torch.nn은 신경망 계층, 손실 함수 등을 제공하는 모듈입니다.
import torch.nn as nn

# torch.optim은 SGD, Adam 같은 최적화 알고리즘을 제공하는 모듈입니다.
import torch.optim as optim

# TensorDataset은 입력 데이터와 정답 데이터를 하나의 Dataset 형태로 묶어줍니다.
# DataLoader는 데이터를 batch 단위로 나누어 모델에 공급합니다.
from torch.utils.data import TensorDataset, DataLoader

# StandardScaler는 평균 0, 표준편차 1 형태로 데이터를 표준화할 때 사용합니다.
from sklearn.preprocessing import StandardScaler

# train_test_split은 전체 데이터를 학습용과 평가용으로 나눌 때 사용합니다.
from sklearn.model_selection import train_test_split

# mean_squared_error와 r2_score는 회귀 모델 성능 평가에 사용합니다.
from sklearn.metrics import mean_squared_error, r2_score

# 실행 결과를 어느 정도 재현 가능하게 만들기 위해 난수 시드를 고정합니다.
# 딥러닝은 무작위 초기 가중치, 데이터 셔플 등으로 실행할 때마다 결과가 달라질 수 있습니다.
SEED = 42
np.random.seed(SEED)
torch.manual_seed(SEED)

# GPU가 사용 가능한지 확인합니다.
# cuda가 가능하면 GPU를 사용하고, 아니면 CPU를 사용합니다.
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

print('PyTorch 버전:', torch.__version__)
print('사용 장치:', device)

# GPU 사용 가능 시 GPU 이름을 출력합니다.
if torch.cuda.is_available():
    print('GPU 이름:', torch.cuda.get_device_name(0))
else:
    print('GPU를 사용할 수 없어 CPU로 실행합니다.')

 

 

 

 

하이퍼파라미터, epochs, batch_size, learning_rate, text_size 설정

# ============================================================
# 2. 하이퍼파라미터 설정
# ============================================================

# EPOCHS는 전체 학습 데이터를 몇 번 반복해서 학습할지 지정합니다.
EPOCHS = 500

# BATCH_SIZE는 한 번에 몇 개의 샘플을 묶어서 학습할지 지정합니다.
BATCH_SIZE = 64

# LEARNING_RATE는 가중치를 한 번 업데이트할 때 얼마나 크게 수정할지를 의미합니다.
# 너무 크면 학습이 불안정하고, 너무 작으면 학습이 매우 느려질 수 있습니다.
LEARNING_RATE = 0.01

# TEST_SIZE는 전체 데이터 중 평가용 데이터로 사용할 비율입니다.
# 30%를 테스트 데이터로 사용합니다.
TEST_SIZE = 0.3

print('EPOCHS:', EPOCHS)
print('BATCH_SIZE:', BATCH_SIZE)
print('LEARNING_RATE:', LEARNING_RATE)
print('TEST_SIZE:', TEST_SIZE)

 

 

 

 

컬럼 확인, 이중 MEDV는 예제에서 예측해야 하는 정답컬럼이다. 

파일불러오기, 나는 vscode extension을 사용중임으로 google.colab drive를 추가 import해 csv_path를 지정해 pd.read_csv하도록 했다. 

# ============================================================
# 3. housing.csv 데이터 읽기
# ============================================================

# 다음 컬럼명을 사용했습니다.
# MEDV는 주택 가격의 중앙값을 의미하며, 이 예제에서 예측해야 하는 정답 컬럼입니다.
heading = [
    'CRIM',     # 지역별 범죄율
    'ZN',       # 25,000 평방피트 초과 주거지역 비율
    'INDUS',    # 비소매상업지역 면적 비율
    'CHAS',     # 찰스강 인접 여부, 보통 1 또는 0
    'NOX',      # 일산화질소 농도
    'RM',       # 주택당 평균 방 개수
    'AGE',      # 1940년 이전 건축 주택 비율
    'DIS',      # 고용 중심지까지의 가중 거리
    'RAD',      # 방사형 고속도로 접근성 지수
    'TAX',      # 재산세율
    'PTRATIO',  # 학생-교사 비율
    'LSTAT',    # 저소득층 비율
    'MEDV'      # 주택 가격 중앙값, 예측 대상
]

# 현재 작업 폴더에 housing.csv가 있는지 확인합니다.
# Google Colab에서는 왼쪽 파일 탭에 housing.csv를 업로드해야 합니다.
# csv_path = 'housing.csv'

# if not os.path.exists(csv_path):
#     raise FileNotFoundError(
#         "housing.csv 파일을 찾을 수 없습니다. "
#         "Google Colab 왼쪽 파일 탭에 housing.csv를 업로드한 뒤 다시 실행하세요."
#     )

from google.colab import drive
drive.mount('/content/drive')
csv_path = '/content/drive/My Drive/data/housing.csv'

# CSV 파일을 pandas DataFrame으로 읽습니다.
# DataFrame은 엑셀 표와 비슷하게 행과 열로 구성된 데이터 구조입니다.
raw = pd.read_csv(csv_path)

# CSV 파일에 컬럼명이 없거나 컬럼 수가 맞는 경우를 대비해 컬럼명을 보정합니다.
# heading 변수가 있었지만 read_csv에서 names를 직접 사용하지 않았습니다.
# 여기서는 데이터 컬럼 수가 heading과 같고, MEDV 컬럼이 없으면 컬럼명을 지정합니다.
if 'MEDV' not in raw.columns and raw.shape[1] == len(heading):
    raw.columns = heading

print('원본 데이터 모양:', raw.shape)
print('원본 데이터 샘플 10개 출력 확인')
display(raw.head(10))

print('원본 데이터 통계')
display(raw.describe())

 

 

 

 

 

 

결측치확인

컬럼별 데이터타입 확인

문자형으로 읽힌 숫자가 있다면 숫자형으로 변환하고 errors=coerce를 설정하면 변환할수없는 값들은 NaN으로 바뀐다. 

결측치 재확인 만일 있다면 평균값으로 보통 대체한다.

# ============================================================
# 4. 결측치 확인 및 기본 데이터 점검
# ============================================================

# 결측치는 비어 있는 값을 의미합니다.
# 딥러닝 모델은 NaN 값을 직접 학습할 수 없으므로 반드시 확인해야 합니다.
print('컬럼별 결측치 개수')
print(raw.isnull().sum())

# 모든 컬럼이 숫자형인지 확인합니다.
# 딥러닝 모델은 문자 데이터를 그대로 처리할 수 없으므로 숫자형 변환이 필요합니다.
print('컬럼별 데이터 타입')
print(raw.dtypes)

# 혹시 문자형으로 읽힌 숫자 데이터가 있다면 숫자형으로 변환합니다.
# errors='coerce'는 변환할 수 없는 값이 있으면 NaN으로 바꿉니다.
for col in raw.columns:
    raw[col] = pd.to_numeric(raw[col], errors='coerce')

# 숫자형 변환 후 생긴 결측치가 있다면 평균값으로 채웁니다.
# 실무에서는 결측치 처리 방식을 문제 특성에 맞게 신중하게 결정해야 합니다.
if raw.isnull().sum().sum() > 0:
    raw = raw.fillna(raw.mean(numeric_only=True))
    print('결측치를 각 컬럼의 평균값으로 대체했습니다.')
else:
    print('결측치가 없습니다.')

 

 

 

 

 

 

standardScaler를 통한 정규화 표준화 수행 및 fit_transform. 

standardSclaer의 fit_transform 결과는 Numpy 배열이다. 이때 원래 컬럼명들이 사라짐으로 dataframe으로 복구해 어떤열이 무엇인지 알기 쉽게 세팅한다 . 이를 통해 Z_data['age']등으로 접근할 수 있다 .다만 사실 딥러닝 학습만 할거라면 복구할 필요가없다. 딥러닝 모델은 컬럼명를 보지않고 숫자배열만 보기때문이다 .우리가 결과확인이 가능하고 컬럼별 분석이 쉽고, 실무에서 데이터 검증하기 좋으니 복구과정을 거치는것. 

array([
    [0.12, -0.53, 1.24],
    [0.34, -1.02, 0.88],
    ...
])

# ============================================================
# 5. Z-점수 표준화 수행
# ============================================================

# StandardScaler는 각 컬럼을 평균 0, 표준편차 1이 되도록 변환합니다.
# 딥러닝에서는 입력 변수의 단위와 범위가 너무 다르면 학습이 불안정해질 수 있으므로
# 표준화 또는 정규화를 자주 사용합니다.
scaler = StandardScaler()

# fit_transform은 두 가지 작업을 동시에 수행합니다.
# fit: 각 컬럼의 평균과 표준편차를 계산합니다.
# transform: 계산된 평균과 표준편차를 사용하여 실제 데이터를 변환합니다.
Z_array = scaler.fit_transform(raw)

# StandardScaler 결과는 numpy 배열입니다.
# 컬럼명이 사라지므로 다시 pandas DataFrame으로 변환하면서 컬럼명을 복구합니다.
Z_data = pd.DataFrame(Z_array, columns=raw.columns)

print('정규화된 데이터 샘플 10개 확인')
display(Z_data.head(10))

print('정규화된 데이터 통계')
display(Z_data.describe())

 

 

 

 

입력데이터, 정답데이터 분리

# ============================================================
# 6. 입력 데이터 X와 정답 데이터 y 분리
# ============================================================

# MEDV는 예측해야 하는 주택 가격 컬럼입니다.
# X_data는 MEDV를 제외한 나머지 입력 변수입니다.
# y_data는 모델이 맞혀야 하는 정답값입니다.
print('분리 전 데이터 모양:', Z_data.shape)

X_data = Z_data.drop('MEDV', axis=1)
y_data = Z_data['MEDV']

print('입력 데이터 X 모양:', X_data.shape)
print('정답 데이터 y 모양:', y_data.shape)

# train_test_split을 사용하여 학습용 데이터와 평가용 데이터를 나눕니다.
# random_state를 고정하면 매번 같은 방식으로 데이터가 나뉘어 결과 비교가 쉬워집니다.
X_train, X_test, y_train, y_test = train_test_split(
    X_data,
    y_data,
    test_size=TEST_SIZE,
    random_state=SEED
)

print('학습용 입력 데이터 모양:', X_train.shape)
print('학습용 정답 데이터 모양:', y_train.shape)
print('평가용 입력 데이터 모양:', X_test.shape)
print('평가용 정답 데이터 모양:', y_test.shape)

 

 

 

 

 

 

정규화가 잘 됬는지 확인해보자. boxploat을 사용한다. 표준화가 잘 되었다면 컬럼이 대부분 0의 중시믕로 비슷한 스케일을 갖는다 .

# ============================================================
# 7. 정규화된 데이터 분포 확인 - 상자 그림
# ============================================================

# 상자 그림은 각 컬럼의 데이터 분포와 이상치를 확인할 때 사용합니다.
# 표준화가 잘 되었다면 대부분의 컬럼이 평균 0 근처를 중심으로 비슷한 스케일을 갖습니다.
plt.figure(figsize=(14, 6))
sns.boxplot(data=Z_data)
plt.title('Standardized Housing Data Boxplot')
plt.xticks(rotation=45)
plt.show()

 

 

 

 

 

데이터를 PyTorch Tensor로 변환하고

TensorDataset으로 입력과 정답을 한쌍으로 묶어

DataLoader에 batch 크기만큼 잘라 사용할 수 있도록 세팅한다. 

# ============================================================
# 8. pandas/numpy 데이터를 PyTorch Tensor로 변환
# ============================================================

# PyTorch 모델은 pandas DataFrame을 직접 입력받지 않습니다.
# 따라서 numpy 배열로 바꾼 뒤 torch.tensor로 변환해야 합니다.

# 입력 데이터는 실수형 연산을 위해 float32 타입으로 변환합니다.
X_train_tensor = torch.tensor(X_train.values, dtype=torch.float32)
X_test_tensor = torch.tensor(X_test.values, dtype=torch.float32)

# 정답 데이터도 float32로 변환합니다.
# 회귀 문제에서는 정답도 실수형 값입니다.
# view(-1, 1)은 shape을 [샘플수]에서 [샘플수, 1] 형태로 바꿉니다.
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).view(-1, 1)
y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).view(-1, 1)

print('X_train_tensor 모양:', X_train_tensor.shape)
print('y_train_tensor 모양:', y_train_tensor.shape)
print('X_test_tensor 모양:', X_test_tensor.shape)
print('y_test_tensor 모양:', y_test_tensor.shape)

# TensorDataset은 입력 Tensor와 정답 Tensor를 한 쌍으로 묶어줍니다.
train_dataset = TensorDataset(X_train_tensor, y_train_tensor)

# DataLoader는 Dataset에서 데이터를 batch_size만큼 잘라서 반복적으로 꺼내줍니다.
# shuffle=True는 매 epoch마다 데이터 순서를 섞어 과적합을 줄이고 학습 안정성을 높입니다.
train_loader = DataLoader(
    dataset=train_dataset,
    batch_size=BATCH_SIZE,
    shuffle=True
)

print('학습 배치 개수:', len(train_loader))

 

 

 

 

 

 

HousingDNN 함수를 만들어보자. 

nn.Moule을 받아 초기화함수를 호출한다. 

sequential 로 신경망을 순차적으로 수행하며 nn.Linear, nn.ReLU, nn.Linear, nn.RdLU, nn.Linear를 거친다 .

# ============================================================
# 9. PyTorch DNN 모델 정의
# ============================================================

# nn.Module은 PyTorch에서 신경망 모델을 만들 때 상속해야 하는 기본 클래스입니다.
class HousingDNN(nn.Module):
    def __init__(self, input_dim):
        # 부모 클래스인 nn.Module의 초기화 함수를 호출합니다.
        super(HousingDNN, self).__init__()

        # 원본 Keras 모델 구조:
        # Dense(200, activation='relu')
        # Dense(1000, activation='relu')
        # Dense(1)
        #
        # PyTorch에서는 nn.Linear가 Keras의 Dense와 같은 역할을 합니다.
        self.net = nn.Sequential(
            # 첫 번째 완전연결층입니다.
            # input_dim개의 입력 변수를 받아 200개의 뉴런으로 변환합니다.
            nn.Linear(input_dim, 200),

            # ReLU는 음수는 0으로 만들고 양수는 그대로 통과시키는 활성화 함수입니다.
            # 딥러닝에서 비선형 관계를 학습하기 위해 사용합니다.
            nn.ReLU(),

            # 두 번째 완전연결층입니다.
            # 200개의 값을 받아 1000개의 뉴런으로 확장합니다.
            nn.Linear(200, 1000),

            # 두 번째 은닉층 뒤에도 ReLU를 적용합니다.
            nn.ReLU(),

            # 출력층입니다.
            # 주택 가격 하나를 예측해야 하므로 출력 뉴런은 1개입니다.
            # 회귀 문제에서는 마지막에 sigmoid나 softmax를 사용하지 않습니다.
            nn.Linear(1000, 1)
        )

    def forward(self, x):
        # forward 함수는 모델에 입력값 x가 들어왔을 때 어떤 계산을 할지 정의합니다.
        # nn.Sequential에 정의된 계층을 순서대로 통과시킨 결과를 반환합니다.
        return self.net(x)

# 입력 변수 개수는 X_train의 컬럼 수입니다.
input_dim = X_train.shape[1]

# 모델 객체를 생성한 후 GPU 또는 CPU 장치로 이동합니다.
model = HousingDNN(input_dim).to(device)

print(model)

# 모델의 전체 학습 파라미터 수를 계산합니다.
# 파라미터는 학습 과정에서 값이 바뀌는 가중치와 편향을 의미합니다.
total_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print('학습 가능한 파라미터 수:', total_params)

 

 

 

 

 

 

MSELoss 평균제곱오차로 실제값과 예측값의 차이를 제곱한 뒤 평균을 구하여 손실함수를 확인한다. 

optim.SGD 는 확률적 경사하강법이다. 이를 활용해 손실을 줄이는 방향으로 모델의 가중치를 조금씩 수정하도록하자.

# ============================================================
# 10. 손실 함수와 최적화 함수 설정
# ============================================================

# MSELoss는 평균 제곱 오차입니다.
# 실제값과 예측값의 차이를 제곱한 뒤 평균을 구합니다.
# 회귀 문제에서 가장 기본적으로 사용하는 손실 함수입니다.
criterion = nn.MSELoss()

# SGD는 확률적 경사하강법입니다.
# 손실을 줄이는 방향으로 모델의 가중치를 조금씩 수정합니다.
# 원본 Keras 코드의 optimizer='sgd'와 같은 역할을 합니다.
optimizer = optim.SGD(model.parameters(), lr=LEARNING_RATE)

print('손실 함수:', criterion)
print('최적화 함수:', optimizer)

 

 

 

 

 

 

딥러닝 학습시작.

epoch에 따라 range for문을 돌려 model.train()한다 .

running_loss 누적 손실값을 세팅해 추후 return하도록한다. 

batch 단위로 데이터를 꺼내 device로 이동후 zero_grad() 앞선 batch에서의 기울기를 초기화.

model(batch_x) 해 예측값 계산

loss = criterion() MSE 계산 및 loss.backward()로 파라미터 기울기계산, optimizer.step() 바니영

평균 솔실값을 계산해 출력.

# ============================================================
# 11. DNN 모델 학습
# ============================================================

print('DNN 딥러닝 학습 시작')
begin = time()

# epoch별 평균 손실값을 저장할 리스트입니다.
# 학습 후 손실값 변화 그래프를 그리는 데 사용합니다.
train_losses = []

# EPOCHS만큼 전체 학습 데이터를 반복 학습합니다.
for epoch in range(1, EPOCHS + 1):
    # model.train()은 모델을 학습 모드로 전환합니다.
    # Dropout, BatchNorm 같은 계층이 있을 때 학습/평가 동작이 달라지므로 습관적으로 사용합니다.
    model.train()

    # 한 epoch 동안의 누적 손실값입니다.
    running_loss = 0.0

    # train_loader에서 batch 단위로 데이터를 꺼냅니다.
    for batch_X, batch_y in train_loader:
        # 입력과 정답 Tensor를 GPU 또는 CPU 장치로 이동합니다.
        batch_X = batch_X.to(device)
        batch_y = batch_y.to(device)

        # 이전 batch에서 계산된 기울기가 남아 있지 않도록 초기화합니다.
        # PyTorch는 기본적으로 gradient를 누적하기 때문에 매번 zero_grad가 필요합니다.
        optimizer.zero_grad()

        # 순전파: 입력 데이터를 모델에 넣어 예측값을 계산합니다.
        outputs = model(batch_X)

        # 손실 계산: 예측값과 실제값 사이의 MSE를 계산합니다.
        loss = criterion(outputs, batch_y)

        # 역전파: 손실값을 기준으로 각 파라미터의 기울기를 계산합니다.
        loss.backward()

        # 가중치 업데이트: 계산된 기울기를 이용해 모델 파라미터를 수정합니다.
        optimizer.step()

        # 현재 batch의 손실값에 batch 데이터 수를 곱해 누적합니다.
        running_loss += loss.item() * batch_X.size(0)

    # epoch 전체 평균 손실값을 계산합니다.
    epoch_loss = running_loss / len(train_loader.dataset)
    train_losses.append(epoch_loss)

    # 학습 진행 상황을 50 epoch마다 출력합니다.
    # 첫 번째 epoch도 확인할 수 있도록 조건에 포함합니다.
    if epoch == 1 or epoch % 50 == 0:
        print(f'Epoch [{epoch:4d}/{EPOCHS}] - Train MSE Loss: {epoch_loss:.6f}')

end = time()
print(f'총 딥러닝 학습 시간: {end - begin:.1f}초')

 

 

 

 

 

 

 

위 손실값 변화 시각화

# ============================================================
# 12. 학습 손실값 변화 시각화
# ============================================================

# 손실값이 epoch가 증가할수록 대체로 감소하면 모델이 학습되고 있다는 의미입니다.
plt.figure(figsize=(10, 5))
plt.plot(train_losses)
plt.title('Training Loss Curve')
plt.xlabel('Epoch')
plt.ylabel('MSE Loss')
plt.grid(True)
plt.show()

 

 

 

 

model.ecal() 평가모드 전환

마찬가지로 device로 이동해 model() 예측값을 계산 및 criterion MSE 를 계산.

Tensor를 numpy 배열로 변환해 nn.sqrt() import한 mean_squared_error, r2_score 를 사용해 성능평가.

# ============================================================
# 13. 테스트 데이터 평가
# ============================================================

# model.eval()은 모델을 평가 모드로 전환합니다.
model.eval()

# torch.no_grad()는 평가 단계에서 기울기를 계산하지 않도록 합니다.
# 평가에서는 가중치 업데이트가 필요 없으므로 메모리 사용량과 계산 시간을 줄일 수 있습니다.
with torch.no_grad():
    # 테스트 입력 데이터를 장치로 이동합니다.
    X_test_device = X_test_tensor.to(device)
    y_test_device = y_test_tensor.to(device)

    # 테스트 데이터에 대한 예측값을 계산합니다.
    test_pred_tensor = model(X_test_device)

    # PyTorch 기준 테스트 MSE를 계산합니다.
    test_loss = criterion(test_pred_tensor, y_test_device).item()

print(f'DNN 테스트 평균 제곱 오차(MSE): {test_loss:.6f}')

# sklearn 평가 지표 계산을 위해 Tensor를 CPU의 numpy 배열로 변환합니다.
y_test_np = y_test_tensor.numpy()
pred_np = test_pred_tensor.cpu().numpy()

# RMSE는 MSE의 제곱근입니다.
# 원래 값의 단위와 비슷하게 해석할 수 있어 회귀 모델 평가에서 자주 사용합니다.
rmse = np.sqrt(mean_squared_error(y_test_np, pred_np))

# R² Score는 모델이 정답의 변동성을 얼마나 설명하는지 보여주는 지표입니다.
# 1에 가까울수록 좋고, 0에 가까우면 평균값 예측과 비슷하다는 뜻입니다.
r2 = r2_score(y_test_np, pred_np)

print(f'RMSE: {rmse:.6f}')
print(f'R² Score: {r2:.6f}')
DNN 테스트 평균 제곱 오차(MSE): 0.113640
RMSE: 0.337105
R² Score: 0.871252

* 평균제곱오차 MSE은 실제값과 예측값의 차이를 제곱한 후 평균해 낸 값으로 오차는 0.1. 나쁘지않다. 

RMSE, MSE에 제곱근을 띄운값으로 예측이 실제값에서 평균적으로 0.3정도 벗어났다는 의미.

r_2score 결정계수, 가장 많이 보는 지표로 1에 가까울수록 완벽한 예측이다 .

87%로 꽤좋은 성능이다 .

 

 

 

 

위 실제값과 예측값의 비교 산포도를 그려보자.


# ============================================================
# 14. 실제값과 예측값 비교 산포도
# ============================================================

# 실제값과 예측값이 비슷할수록 점들이 대각선 방향에 가깝게 분포합니다.
plt.figure(figsize=(7, 6))
sns.regplot(x=y_test_np.flatten(), y=pred_np.flatten())
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.title('Actual vs Predicted Values')
plt.grid(True)
plt.show()

 

 

 

 

 

예측결과 일부확인

flatten()  다차원 배열을 1차원 배열로 펼치는 함수.

# ============================================================
# 15. 예측 결과 일부 확인
# ============================================================

# 실제값과 예측값을 하나의 표로 묶어서 비교합니다.
# 현재 값들은 MEDV까지 표준화된 값이므로 원래 가격 단위가 아니라 Z-score 기준 값입니다.
result_df = pd.DataFrame({
    'Actual_MEDV_Zscore': y_test_np.flatten(),
    'Predicted_MEDV_Zscore': pred_np.flatten(),
    'Error': y_test_np.flatten() - pred_np.flatten()
})

print('예측 결과 샘플 10개')
display(result_df.head(10))

 

 

 

 

 

 

원래 단위로 복원해 출력하기

# ============================================================
# 16. 원래 MEDV 단위로 예측값 복원하기
# ============================================================

# 이 노트북은 원본 코드와 동일하게 MEDV까지 포함한 전체 데이터를 StandardScaler로 표준화했습니다.
# 따라서 모델 예측 결과도 표준화된 MEDV 값입니다.
# 수업에서는 원래 주택 가격 단위로 해석하는 방법도 함께 확인하는 것이 좋습니다.

# scaler.mean_과 scaler.scale_에는 각 컬럼의 평균과 표준편차가 저장되어 있습니다.
# MEDV 컬럼의 위치를 찾습니다.
medv_index = list(raw.columns).index('MEDV')
medv_mean = scaler.mean_[medv_index]
medv_std = scaler.scale_[medv_index]

# Z-score를 원래 값으로 복원하는 공식은 다음과 같습니다.
# 원래값 = Z값 * 표준편차 + 평균
actual_medv_original = y_test_np.flatten() * medv_std + medv_mean
pred_medv_original = pred_np.flatten() * medv_std + medv_mean

original_result_df = pd.DataFrame({
    'Actual_MEDV_Original': actual_medv_original,
    'Predicted_MEDV_Original': pred_medv_original,
    'Error_Original': actual_medv_original - pred_medv_original
})

print('원래 MEDV 단위로 복원한 예측 결과 샘플 10개')
display(original_result_df.head(10))

# 원래 단위 기준의 MSE, RMSE, R²를 계산합니다.
original_mse = mean_squared_error(actual_medv_original, pred_medv_original)
original_rmse = np.sqrt(original_mse)
original_r2 = r2_score(actual_medv_original, pred_medv_original)

print(f'원래 단위 기준 MSE: {original_mse:.6f}')
print(f'원래 단위 기준 RMSE: {original_rmse:.6f}')
print(f'원래 단위 기준 R² Score: {original_r2:.6f}')

 

 

 

 

 

 

원래값으로 다시 그래프. 단위만 달라진다. 

# ============================================================
# 17. 원래 단위 기준 실제값과 예측값 비교 그래프
# ============================================================

plt.figure(figsize=(7, 6))
sns.regplot(x=actual_medv_original, y=pred_medv_original)
plt.xlabel('Actual MEDV Original Scale')
plt.ylabel('Predicted MEDV Original Scale')
plt.title('Actual vs Predicted MEDV - Original Scale')
plt.grid(True)
plt.show()

 

 

 

 

 

 

!!!ㅗㅑ~

 

 

 

 

 

 

 

 

샘플코드 Heart Disease Classification - PyTorch DNN 을 분석해보자.

 

 

import

# ============================================================
# 1. 기본 라이브러리 임포트
# ============================================================

# pandas는 표 형태의 데이터를 다루기 위한 라이브러리입니다.
# CSV 파일을 읽거나, 데이터프레임 형태로 데이터를 확인할 때 사용합니다.
import pandas as pd

# matplotlib.pyplot은 그래프를 그리기 위한 기본 시각화 라이브러리입니다.
import matplotlib.pyplot as plt

# seaborn은 matplotlib보다 더 보기 좋은 통계 그래프를 쉽게 그릴 수 있게 해주는 라이브러리입니다.
import seaborn as sns

# time 함수는 학습 시작 시각과 종료 시각을 측정하여 전체 학습 시간을 계산할 때 사용합니다.
from time import time

# numpy는 수치 계산용 라이브러리입니다.
# 난수 고정이나 배열 계산에 사용합니다.
import numpy as np

# torch는 PyTorch의 핵심 라이브러리입니다.
# 텐서 생성, 자동 미분, GPU 연산 등을 수행할 수 있습니다.
import torch

# torch.nn은 신경망 계층, 활성화 함수, 손실 함수 등을 제공하는 모듈입니다.
from torch import nn

# train_test_split은 데이터를 학습용과 평가용으로 나누는 함수입니다.
from sklearn.model_selection import train_test_split

# StandardScaler는 평균 0, 표준편차 1이 되도록 Z-점수 정규화를 수행합니다.
from sklearn.preprocessing import StandardScaler

# f1_score는 분류 모델의 성능을 평가하는 지표입니다.
# 특히 클래스 불균형이 있을 때 정확도보다 더 유용할 수 있습니다.
from sklearn.metrics import f1_score, accuracy_score, confusion_matrix, classification_report

 

 

 

 

하이퍼파라미터 설정

# ============================================================
# 2. 하이퍼파라미터 및 실행 옵션 설정
# ============================================================

# 입력 특성 개수입니다.
# heart.csv 데이터는 일반적으로 age, sex, cp 등 13개의 입력 컬럼과 target 컬럼을 가집니다.
INPUT_DIM = 13

# 은닉층 뉴런 개수입니다.
# 값이 클수록 모델 표현력이 커지지만, 학습 시간이 길어지고 과적합 위험도 증가합니다.
MY_HIDDEN = 1000

# 전체 학습 반복 횟수입니다.
# epoch는 전체 학습 데이터를 한 번 모두 사용하여 학습하는 단위입니다.
MY_EPOCH = 1000

# 학습률입니다.
# 가중치를 한 번 업데이트할 때 얼마나 크게 이동할지 결정합니다.
LEARNING_RATE = 0.01

# 결과 재현을 위한 난수 고정값입니다.
# 같은 코드를 실행했을 때 최대한 비슷한 결과가 나오도록 도와줍니다.
SEED = 111

# pandas 출력 옵션입니다.
# 데이터프레임을 출력할 때 모든 컬럼이 보이도록 설정합니다.
pd.set_option('display.max_columns', None)

# PyTorch 난수 시드를 고정합니다.
torch.manual_seed(SEED)

# NumPy 난수 시드를 고정합니다.
np.random.seed(SEED)

# GPU 사용 가능 여부를 확인합니다.
# CUDA가 사용 가능하면 GPU를 사용하고, 아니면 CPU를 사용합니다.
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

print("사용 장치:", device)

 

 

 

 

 

 

 

데이터 파일 읽기

# ============================================================
# 3. 데이터 파일 읽기
# ============================================================

# heart.csv 파일을 pandas 데이터프레임으로 읽어옵니다.
# 데이터프레임은 엑셀 표와 비슷하게 행과 열로 구성된 데이터 구조입니다.
# raw = pd.read_csv('heart.csv')

from google.colab import drive
drive.mount('/content/drive')
raw = pd.read_csv('/content/drive/My Drive/data/heart.csv')

# 원본 데이터의 앞 10개 행을 출력하여 데이터가 정상적으로 읽혔는지 확인합니다.
print('원본 데이터 샘플 10개')
display(raw.head(10))

# 원본 데이터의 기초 통계량을 확인합니다.
# count: 데이터 개수
# mean: 평균
# std: 표준편차
# min: 최솟값
# 25%, 50%, 75%: 사분위수
# max: 최댓값
print('원본 데이터 통계')
display(raw.describe())

# 데이터프레임의 전체 행/열 개수를 확인합니다.
print("원본 데이터 모양:", raw.shape)

 

 

 

 

 

데이터 컬럼 분리 및 학습용/평가용 분리

# ============================================================
# 4. 입력 데이터와 정답 데이터 분리
# ============================================================

# target 컬럼은 모델이 맞혀야 하는 정답값입니다.
# 따라서 입력 데이터 X_data에서는 target 컬럼을 제거합니다.
X_data = raw.drop('target', axis=1)

# Y_data에는 정답값인 target 컬럼만 저장합니다.
# 일반적으로 0은 심장질환 없음, 1은 심장질환 있음으로 해석합니다.
Y_data = raw['target']

# 입력 컬럼명을 저장합니다.
# 정규화 후 다시 데이터프레임으로 만들 때 컬럼명을 복원하기 위해 사용합니다.
names = X_data.columns

# 입력 컬럼명을 출력합니다.
print("입력 컬럼명:")
print(names)

# 입력 데이터와 정답 데이터의 모양을 확인합니다.
print("입력 데이터 모양:", X_data.shape)
print("정답 데이터 모양:", Y_data.shape)
# ============================================================
# 5. 학습용 데이터와 평가용 데이터 분리
# ============================================================

# train_test_split은 전체 데이터를 학습용과 평가용으로 나누는 함수입니다.
# test_size=0.3은 전체 데이터 중 30%를 평가용 데이터로 사용한다는 뜻입니다.
# random_state를 지정하면 데이터를 나누는 방식이 고정되어 결과 재현성이 좋아집니다.
# stratify=Y_data는 target의 0/1 비율이 학습용과 평가용에 비슷하게 유지되도록 합니다.
X_train, X_test, Y_train, Y_test = train_test_split(
    X_data,
    Y_data,
    test_size=0.3,
    random_state=SEED,
    stratify=Y_data
)

# 최종 데이터 모양을 출력하여 제대로 분리되었는지 확인합니다.
print('\n학습용 입력 데이터 모양:', X_train.shape)
print('학습용 출력 데이터 모양:', Y_train.shape)
print('평가용 입력 데이터 모양:', X_test.shape)
print('평가용 출력 데이터 모양:', Y_test.shape)

# 학습용/평가용 정답 비율을 확인합니다.
print("\n학습용 target 비율")
print(Y_train.value_counts(normalize=True))

print("\n평가용 target 비율")
print(Y_test.value_counts(normalize=True))



 

 

 

 

데이터 정규화 standardScalder.다시 dataframe화 및 정규화된 데이터 시각화

# ============================================================
# 6. 입력 데이터 Z-점수 정규화
# ============================================================

# StandardScaler는 각 컬럼을 평균 0, 표준편차 1이 되도록 변환합니다.
# 서로 단위가 다른 컬럼들이 있을 때 신경망 학습을 안정적으로 만드는 데 도움이 됩니다.
scaler = StandardScaler()

# fit_transform은 학습 데이터의 평균과 표준편차를 계산한 뒤 정규화까지 수행합니다.
# 주의: scaler는 반드시 학습 데이터에만 fit 해야 합니다.
# 평가 데이터까지 함께 fit하면 평가 데이터 정보가 학습 과정에 새는 데이터 누수 문제가 생깁니다.
X_train_scaled = scaler.fit_transform(X_train)

# transform은 학습 데이터에서 계산한 평균과 표준편차를 평가 데이터에 그대로 적용합니다.
# 평가 데이터에는 fit_transform이 아니라 transform만 사용해야 합니다.
X_test_scaled = scaler.transform(X_test)

# 정규화 결과는 NumPy 배열이므로 다시 pandas 데이터프레임으로 변환합니다.
# 컬럼명을 복구하면 출력과 시각화가 이해하기 쉬워집니다.
X_train_scaled = pd.DataFrame(X_train_scaled, columns=names)
X_test_scaled = pd.DataFrame(X_test_scaled, columns=names)

# 정규화된 학습용 데이터 샘플을 출력합니다.
print('\n정규화된 학습용 데이터 샘플 10개')
display(X_train_scaled.head(10))

# 정규화된 학습용 데이터의 통계를 확인합니다.
# 평균이 거의 0, 표준편차가 거의 1에 가까운지 확인합니다.
print('정규화된 학습용 데이터 통계')
display(X_train_scaled.describe())
# ============================================================
# 7. 정규화된 데이터 분포 시각화
# ============================================================

# 그래프 글자 크기를 설정합니다.
sns.set(font_scale=1.2)

# 그래프 크기를 지정합니다.
plt.figure(figsize=(16, 6))

# boxplot은 각 컬럼의 분포, 중앙값, 이상치를 확인하는 그래프입니다.
# 정규화 후 대부분의 컬럼이 비슷한 범위에 놓이는지 확인할 수 있습니다.
sns.boxplot(data=X_train_scaled, palette="colorblind")

# x축 글자들이 겹치지 않도록 회전합니다.
plt.xticks(rotation=45)

# 그래프 제목을 지정합니다.
plt.title("Z-score Normalized Training Data Distribution")

# 그래프를 화면에 출력합니다.
plt.show()

 

 

 

 

 

 

 

은닉층 있는 DNN 모델 정의

# ============================================================
# 8. PyTorch DNN 모델 정의
# ============================================================

# nn.Sequential은 여러 신경망 계층을 순서대로 쌓을 때 사용하는 방식입니다.
# 입력 데이터가 첫 번째 계층부터 마지막 계층까지 순서대로 지나갑니다.
model = nn.Sequential(
    # 첫 번째 완전연결층입니다.
    # 입력 특성 13개를 받아서 MY_HIDDEN개 뉴런으로 변환합니다.
    nn.Linear(INPUT_DIM, MY_HIDDEN),

    # Tanh 활성화 함수입니다.
    # 신경망이 비선형 패턴을 학습할 수 있도록 도와줍니다.
    nn.Tanh(),

    # 두 번째 완전연결층입니다.
    # 은닉층 출력 MY_HIDDEN개를 다시 MY_HIDDEN개로 변환합니다.
    nn.Linear(MY_HIDDEN, MY_HIDDEN),

    # 두 번째 활성화 함수입니다.
    nn.Tanh(),

    # 출력층입니다.
    # 이진 분류이므로 최종 출력 뉴런은 1개입니다.
    nn.Linear(MY_HIDDEN, 1),

    # Sigmoid 함수는 출력값을 0~1 사이 확률로 변환합니다.
    # 0.5보다 크면 1, 작거나 같으면 0으로 분류할 수 있습니다.
    nn.Sigmoid()
)

# 모델을 CPU 또는 GPU 장치로 이동합니다.
model = model.to(device)

# 모델 구조를 출력합니다.
print('\nDNN 요약')
print(model)

# 모델의 전체 파라미터 수를 계산합니다.
# 파라미터는 학습 과정에서 수정되는 가중치와 편향을 의미합니다.
total_params = sum(p.numel() for p in model.parameters())

print('총 파라미터 수: {:,}'.format(total_params))

 

 

 

 

 

torch.option.SGD() 손실함수정의, nn.MSELoss() 평균제곱오차 최적화함수 정의

tensor변환

# ============================================================
# 9. 손실 함수와 최적화 함수 설정
# ============================================================

# SGD는 확률적 경사하강법입니다.
# 손실값이 작아지는 방향으로 모델의 가중치를 조금씩 수정합니다.
optimizer = torch.optim.SGD(model.parameters(), lr=LEARNING_RATE)

# MSELoss는 평균제곱오차입니다.
# 원본 코드와 동일하게 유지하기 위해 MSELoss를 사용합니다.
# 참고: 이진 분류에서는 일반적으로 BCELoss 또는 BCEWithLogitsLoss를 더 많이 사용합니다.
criterion = nn.MSELoss()
# ============================================================
# 10. pandas / numpy 데이터를 PyTorch 텐서로 변환
# ============================================================

# PyTorch 모델은 pandas 데이터프레임을 직접 입력으로 받을 수 없습니다.
# 따라서 values로 NumPy 배열을 꺼낸 뒤 torch.tensor로 변환합니다.

# 학습용 입력 데이터를 float32 텐서로 변환합니다.
X_train_tensor = torch.tensor(X_train_scaled.values).float().to(device)

# 학습용 정답 데이터를 float32 텐서로 변환합니다.
# MSELoss는 출력과 정답이 모두 실수형이어야 하므로 float으로 변환합니다.
Y_train_tensor = torch.tensor(Y_train.values).float().to(device)

# 평가용 입력 데이터를 float32 텐서로 변환합니다.
X_test_tensor = torch.tensor(X_test_scaled.values).float().to(device)

# 평가용 정답 데이터는 성능 평가 시 scikit-learn 함수에 넣기 위해 NumPy 또는 pandas 형태로 보관해도 됩니다.
# 여기서는 비교를 쉽게 하기 위해 별도 변수로 복사합니다.
Y_test_array = Y_test.values

 

 

 

 

 

 

 

모델 학습과 역전파 및 가중치 계산해 반영

# ============================================================
# 11. DNN 모델 학습
# ============================================================

# 학습 시작 시간을 기록합니다.
begin = time()

print('\nDNN 학습 시작')

# epoch는 전체 학습 데이터를 몇 번 반복해서 학습할지를 의미합니다.
for epoch in range(MY_EPOCH):
    # -----------------------------
    # 1) 순전파
    # -----------------------------
    # 학습용 입력 데이터를 모델에 넣어 예측값을 계산합니다.
    output = model(X_train_tensor)

    # output의 shape는 일반적으로 (데이터개수, 1)입니다.
    # Y_train_tensor의 shape는 (데이터개수,)입니다.
    # 손실 계산을 위해 output을 1차원으로 줄입니다.
    output = torch.squeeze(output)

    # -----------------------------
    # 2) 손실값 계산
    # -----------------------------
    # 예측값 output과 실제 정답 Y_train_tensor의 차이를 계산합니다.
    loss = criterion(output, Y_train_tensor)

    # 10 epoch마다 현재 손실값을 출력합니다.
    if epoch % 10 == 0:
        print('에포크: {:4d}, 손실: {:.6f}'.format(epoch, loss.item()))

    # -----------------------------
    # 3) 역전파 및 가중치 갱신
    # -----------------------------
    # 이전 epoch에서 계산된 기울기가 남아 있지 않도록 초기화합니다.
    optimizer.zero_grad()

    # 손실값을 기준으로 각 파라미터의 기울기를 계산합니다.
    loss.backward()

    # 계산된 기울기를 이용하여 모델의 가중치와 편향을 수정합니다.
    optimizer.step()

# 학습 종료 시간을 기록합니다.
end = time()

print('최종 학습 시간: {:.1f}초'.format(end - begin))

 

 

 

 

 

 

model() 모델입력으로 예측 확률 계산 모델이 답을 맞추면서 성공확률을 얼마나 확신하느냐를 확인할 수 있다.

, numpy 배열로 변환해 0.5보다 크면 1, 작으면 0으로 변환.

정확도계산

f1계산 .

# ============================================================
# 12. DNN 모델 평가
# ============================================================

# 평가 단계에서는 가중치를 수정하지 않으므로 torch.no_grad()를 사용합니다.
# no_grad를 사용하면 불필요한 기울기 계산을 하지 않아 메모리와 속도 면에서 효율적입니다.
with torch.no_grad():
    # 평가용 데이터를 모델에 입력하여 예측 확률을 계산합니다.
    pred_prob = model(X_test_tensor)

# 예측 결과를 CPU로 이동한 뒤 NumPy 배열로 변환합니다.
# GPU 텐서는 바로 NumPy로 변환할 수 없으므로 cpu()를 먼저 호출합니다.
pred_prob = pred_prob.cpu().numpy()

# Sigmoid 출력값은 0~1 사이 확률입니다.
# 0.5보다 크면 1, 아니면 0으로 변환합니다.
pred_class = (pred_prob > 0.5).astype(int).flatten()

# 예측 결과 일부를 출력합니다.
print("예측 클래스:")
print(pred_class)

# F1 점수를 계산합니다.
# F1 점수는 정밀도와 재현율의 조화평균입니다.
f1 = f1_score(Y_test_array, pred_class)

# 정확도도 함께 계산합니다.
acc = accuracy_score(Y_test_array, pred_class)

print("\n최종 정확도(Accuracy): {:.3f}".format(acc))
print("최종 F1 점수: {:.3f}".format(f1))

 

 

 

 

 

confusion_matrix() 혼동행렬 확인 후 heatmap 시각화 및 classification_report() 분류리포트 확인

# ============================================================
# 13. 혼동행렬과 분류 리포트 확인
# ============================================================

# 혼동행렬은 실제값과 예측값이 어떻게 맞고 틀렸는지 보여줍니다.
# [[TN, FP],
#  [FN, TP]]
# TN: 실제 0을 0으로 맞힘
# FP: 실제 0을 1로 잘못 예측
# FN: 실제 1을 0으로 잘못 예측
# TP: 실제 1을 1로 맞힘
cm = confusion_matrix(Y_test_array, pred_class)

print("혼동행렬:")
print(cm)

# classification_report는 precision, recall, f1-score를 클래스별로 출력합니다.
print("\n분류 리포트:")
print(classification_report(Y_test_array, pred_class))

# 혼동행렬을 heatmap으로 시각화합니다.
plt.figure(figsize=(5, 4))
sns.heatmap(cm, annot=True, fmt="d", cmap=None)

plt.xlabel("Predicted Label")
plt.ylabel("Actual Label")
plt.title("Confusion Matrix")
plt.show()

 

 

 

 

예측 확률 데이터프레임으로 정리해 확인

histplolt로 시각화

# ============================================================
# 14. 예측 확률 분포 시각화
# ============================================================

# 예측 확률을 데이터프레임으로 정리합니다.
result_df = pd.DataFrame({
    "actual": Y_test_array,
    "pred_prob": pred_prob.flatten(),
    "pred_class": pred_class
})

# 결과 일부를 확인합니다.
display(result_df.head(10))

# 예측 확률이 0과 1 사이에서 어떻게 분포하는지 확인합니다.
plt.figure(figsize=(8, 5))
sns.histplot(data=result_df, x="pred_prob", hue="actual", bins=20, kde=True)

# 0.5 기준선을 표시합니다.
# 이 선보다 오른쪽이면 1, 왼쪽이면 0으로 분류합니다.
plt.axvline(0.5, linestyle="--")

plt.title("Prediction Probability Distribution")
plt.xlabel("Predicted Probability")
plt.ylabel("Count")
plt.show()

* 파란색은 정답이 0인 데이터, 주황색은 정답이 1인 데이터이다 .

점섬이 분류기준값.

좋은 분류모델은 왼쪽 0 근처, 1근처에 데이터가 몰려있으며 따라서 꽤 잘 분리된 모델이다 .일부 겹치는 구간이 존재한다. 실제는 0인데 1처럼 보이거나 실제로 1인데 0처럼 보이는 데이터가 존재한다는 뜻.preprob를 확인했을때 전체적으로 좋으나 

8 0 0.964780 1 처럼 틀렸음에도 강하게 확신할 경우의 샘플을 분석하면 모델 개선이 가능하겠다.