아이티윌_데이터 분석 55기/강의내용_딥러닝

#4 4일차_CNN, 얼굴 사진 분류하기, 쇼핑몰 후기 감성 분석

ecosso 2026. 7. 28. 16:06

01 CNN (컨볼루션 신경망)

 

02 유명인사의 얼굴 데이터 분류하기

 -01 이미지 분석 - pca + knn (얼굴 데이터)

 -02 이미지 분석 - ANN(3차원 학습 불가) (얼굴 데이터)

 -03 이미지 분석 - CNN(3차원 학습 가능) (얼굴 데이터)

 -04 새로운 얼굴 사진 예측 함수

 -05 사전학습 모델(EfficientNetB0) + 얼굴 데이터 전이학습

 

03 쇼핑몰 후기 감성 분석


01 CNN (컨볼루션 신경망)

 - 심층신경망에 컨볼루션 망을 추가한 신경망

 - 컨볼루션 망에서 이미지의 정교한 특징을 추출

 - 마스크(=필터=윈도우)에 의해서 재추출된 신호

 - hyper parameter : 컨볼루션층 수, 마스크 수, 마스크 사이즈

 - parameter : 각 마스크 가중치

 

 - 풀링(=서브샘플링) : 기존 이미지 신호 중 일부를 추출하여 보다 단순한 맵으로 결정 -> 과적합 해소

 pool_size는 (2,2), (3,3), (4,4) 등을 사용 <- hyper marameter

 pool에 의해 추출되는 신호는 주로 최댓값(max pooling), 평균값(average pooling) 등

 

 - Dropout : 의도적으로 특정 층의 중요하지 않은 노드(뉴런=Unit)를 꺼버리는 기법 -> 과적합 해소

 size는 보통 0.5, 0.25 등을 주로 사용 <- hyper parameter


02 유명인사의 얼굴 데이터 분류하기

 -01 이미지 분석 - pca + knn (얼굴 데이터)

1) 데이터 로딩
from sklearn.datasets import fetch_lfw_people
import numpy as np


people = fetch_lfw_people(min_faces_per_person=50, resize=0.7)    # 스케일링 된 데이터
X = people['data']
X_images = people['images']
y = people['target']
yname = people['target_names']
n_classes = len(yname)

# 데이터 확인
import matplotlib.pyplot as plt
X_images[0].shape

X_images[0].shape
Out[159]: (87, 65)


plt.imshow(X_images[1], cmap='gray')
yname[y[1]]



2) 데이터 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X, y, random_state=0, stratify=y)



3) 모델링
# 1) knn
from sklearn.neighbors import KNeighborsClassifier
m_knn = KNeighborsClassifier()
m_knn.fit(train_x, train_y)
m_knn.score(train_x, train_y)      # 0.6863
m_knn.score(test_x, test_y)        # 0.4974

# 2) pca + knn
from sklearn.decomposition import PCA
from sklearn.pipeline import make_pipeline

pipe = make_pipeline(PCA(100), KNeighborsClassifier(5))
pipe.fit(train_x, train_y)
pipe.score(train_x, train_y)       # 0.6965
pipe.score(test_x, test_y)         # 0.4974 


 -02 이미지 분석 - ANN(3차원 학습 불가) (얼굴 데이터)
1) 데이터 로딩
from sklearn.datasets import fetch_lfw_people
import numpy as np


people = fetch_lfw_people(min_faces_per_person=50, resize=0.7)
X = people['data']
X_images = people['images']
y = people['target']
yname = people['target_names']
n_classes = len(yname)



2) 데이터 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X, y, random_state=0, stratify=y)

# 원핫 인코딩
import pandas as pd
train_y10 = pd.get_dummies(train_y, prefix='Y').astype(int).values
test_y10  = pd.get_dummies(test_y, prefix='Y').astype(int).values



3) 모델링
# 1) seed 고정
import tensorflow as tf
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

# 2) 모델 정의
from tensorflow.keras.layers import Input, Dense
from keras import Sequential

model = Sequential()
model.add(Input(shape=(train_x.shape[1], )))
model.add(Dense(392, activation='relu'))
model.add(Dense(196, activation='relu'))
model.add(Dense(98, activation='relu'))
model.add(Dense(n_classes, activation='softmax'))

# 3) 오차함수, 최적화 결정
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 4) early stopping rule
from tensorflow.keras.callbacks import EarlyStopping
es = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

# 5) 학습
hist = model.fit(train_x, train_y10, validation_split=0.25,
                  batch_size=10, epochs=50000, callbacks=[es])

# 6) 평가
model.evaluate(train_x, train_y10)[1]    # 0.6871
model.evaluate(test_x, test_y10)[1]      # 0.5948

acc = hist.history['accuracy']
plt.plot(range(1, len(acc)+1), hist.history['accuracy'], label='train')
plt.plot(range(1, len(acc)+1), hist.history['val_accuracy'], label='val')
plt.legend()
plt.xticks(range(1, len(acc)+1))


 -03 이미지 분석 - CNN(3차원 학습 가능) (얼굴 데이터)
1) 데이터 로딩
from sklearn.datasets import fetch_lfw_people
import numpy as np


people = fetch_lfw_people(min_faces_per_person=50, resize=0.7)
X = people['data']
X_images = people['images']
y = people['target']
yname = people['target_names']
n_classes = len(yname)
h, w = X_images.shape[1], X_images.shape[2]



2) 데이터 분리 및 변환
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_images, y, random_state=0, stratify=y)

# ** Conv2D 층은 (높이, 너비, 채널) 형태 요구 (흑백 -> 채널:1)
train_x = train_x.reshape((train_x.shape[0], h, w, 1))
test_x  = test_x.reshape((test_x.shape[0], h, w, 1))

import pandas as pd
train_y10 = pd.get_dummies(train_y).astype(int).values
test_y10  = pd.get_dummies(test_y).astype(int).values



3) 모델링
# 1) seed 고정
import tensorflow as tf
seed = 0
np.random.seed(seed)
tf.random.set_seed(seed)

# 2) 모델 정의
from tensorflow.keras.layers import Input, Dense, Conv2D, Dropout, Flatten, MaxPooling2D
from keras import Sequential

model = Sequential()
model.add(Input(shape=(h, w, 1)))
model.add(Conv2D(32, kernel_size=(2, 2), activation='relu'))
model.add(Conv2D(32, kernel_size=(2, 2), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(n_classes, activation='softmax'))

# 3) compile
model.compile('adam', loss='categorical_crossentropy', metrics=['accuracy'])

# 4) 정지규칙
from tensorflow.keras.callbacks import EarlyStopping
es = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)

# 5) 학습
hist = model.fit(train_x, train_y10, validation_split=0.25,
                  batch_size=10, epochs=50000, callbacks=[es])

# 6) 평가
model.evaluate(train_x, train_y10)[1]    # 0.9393
model.evaluate(test_x, test_y10)[1]      # 0.7051

acc = hist.history['accuracy']
plt.plot(range(1, len(acc)+1), hist.history['accuracy'], label='train')
plt.plot(range(1, len(acc)+1), hist.history['val_accuracy'], label='val')
plt.legend()
plt.xticks(range(1, len(acc)+1))


 -04 새로운 얼굴 사진 예측 함수

# 데이터 경로에 다음 사진을 넣어준다.


from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


plt.rcParams['font.family'] = 'Malgun Gothic'        # Windows
plt.rcParams['axes.unicode_minus'] = False           # 마이너스 기호 깨짐 방지

def f_predict_face(img_path, model, h, w, yname, X_images, y):


    # 1) 이미지 로딩 및 흑백 변환
    img = Image.open(img_path).convert('L')          # 흑백(그레이스케일) 변환

    # 2) 크기 맞추기 (학습 데이터와 동일한 h, w)
    img = img.resize((w, h))                         # PIL은 (width, height) 순서

    # 3) 배열 변환 및 스케일링 (0~255 -> 0~1)
    img_arr = np.array(img) / 255.0

    # 4) CNN 입력 형태로 변환 (1, h, w, 1)
    img_input = img_arr.reshape((1, h, w, 1))

    # 5) 예측
    pred = model.predict(img_input)
    pred_class = pred.argmax(axis=1)[0]
    pred_name = yname[pred_class]
    pred_prob = pred[0][pred_class]

    print(f"예측 인물: {pred_name}, 확률: {pred_prob:.4f}")

    # 6) 입력 사진 vs 예측된 클래스의 실제 사진 비교 출력
    real_img = X_images[y == pred_class][0]           # 예측 클래스의 실제 이미지 하나 추출

    fig, ax = plt.subplots(1, 2)
    ax[0].imshow(img_arr, cmap='gray')
    ax[0].set_title('입력 사진')
    ax[0].axis('off')

    ax[1].imshow(real_img, cmap='gray')
    ax[1].set_title(f'예측: {pred_name}')
    ax[1].axis('off')

# 사용 예시
f_predict_face('image_sample.jpg', model, h, w, yname, X_images, y)

f_predict_face('image_sample.jpg', model, h, w, yname, X_images, y)
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 60ms/step
예측 인물: Junichiro Koizumi, 확률: 0.7375

 -05 사전학습 모델(EfficientNetB0) + 얼굴 데이터 전이학습

1) 데이터 로딩 (컬러로 로드해야 사전학습 모델 입력에 맞음)
from sklearn.datasets import fetch_lfw_people
import numpy as np

people = fetch_lfw_people(min_faces_per_person=50, resize=0.7, color=True)
X_images = people['images']         # (n, h, w, 3)
y = people['target']
yname = people['target_names']
n_classes = len(yname)

h, w = X_images.shape[1], X_images.shape[2]



2) 데이터 분리
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_images, y, random_state=0, stratify=y)



3) 사전학습 모델 입력 크기(224x224)로 리사이즈
import tensorflow as tf

def resize_batch(x, size=(224, 224)):
    x = tf.image.resize(x, size)
    return x.numpy()

train_x_rs = resize_batch(train_x)
test_x_rs  = resize_batch(test_x)

# EfficientNet은 0~255 입력을 받아 내부적으로 전처리하므로 /255 하지 않음
# (fetch_lfw_people은 0~1로 스케일링되어 있으므로 되돌려줌)
train_x_rs = train_x_rs * 255
test_x_rs  = test_x_rs * 255

# 원핫 인코딩
import pandas as pd
train_y10 = pd.get_dummies(train_y).astype(int).values
test_y10  = pd.get_dummies(test_y).astype(int).values



4) 사전학습 모델 로딩 (특성 추출기로 사용, 가중치 고정)
from tensorflow.keras.applications import EfficientNetB0
from tensorflow.keras.layers import Input, Dense, Dropout, GlobalAveragePooling2D
from tensorflow.keras.models import Model

base_model = EfficientNetB0(include_top=False,           # ImageNet 1000개 클래스용 마지막 분류층을 빼고, 출력층을 새로 붙이겠다는 옵션
                            weights='imagenet',          # 이미지 분류 연구용으로 만든 대규모 데이터셋(1,000개 클래스, 약 120만 장)
                            input_shape=(224, 224, 3))   # ImageNet 학습 크기이므로 사용 권장(변경가능)
base_model.trainable = False                             # 사전학습 가중치 고정 (feature extractor로만 사용)

# 모델 생성(base_model 자체가 이미 완성된 하나의 모델이므로 Sequential 방식 사용 불가 -> Functional API 방식 사용)
# Functional API : 각 층을 함수처럼 다루면서 입력과 출력을 직접 연결하는 방식
inputs = Input(shape=(224, 224, 3))
x = base_model(inputs, training=False)
x = GlobalAveragePooling2D()(x)
x = Dense(128, activation='relu')(x)
x = Dropout(0.5)(x)
outputs = Dense(n_classes, activation='softmax')(x)      # 얼굴 데이터 출력 층 연결

model = Model(inputs, outputs)
model.compile('adam', loss='categorical_crossentropy', metrics=['accuracy'])



5) 학습
from tensorflow.keras.callbacks import EarlyStopping
es = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)

hist = model.fit(train_x_rs, train_y10, validation_split=0.25,
                  batch_size=16, epochs=50, callbacks=[es])



6) 평가
model.evaluate(train_x_rs, train_y10)[1]     # 0.9820
model.evaluate(test_x_rs, test_y10)[1]       # 0.9102


03 쇼핑몰 후기 감성 분석

import pandas as pd
df = pd.read_table('shopping_ratings_total.txt', names = ['ratings', 'reviews'])
df.head()

df.head()
Out[328]: 
   ratings                                            reviews
0        5                                            배공빠르고 굿
1        2                      택배가 엉망이네용 저희집 밑에층에 말도없이 놔두고가고
2        5  아주좋아요 바지 정말 좋아서2개 더 구매했어요 이가격에 대박입니다. 바느질이 조금 ...
3        2  선물용으로 빨리 받아서 전달했어야 하는 상품이었는데 머그컵만 와서 당황했습니다. 전...
4        5                  민트색상 예뻐요. 옆 손잡이는 거는 용도로도 사용되네요 ㅎㅎ
...

 

df.shape

df.shape
Out[329]: (200000, 2)

 

2) target 변수 변환 (평점 -> 이진데이터)
import numpy as np
np.unique(df['ratings'])

np.unique(df['ratings'])
Out[6]: array([1, 2, 4, 5])

 

3) 샘플링
# 실제 분석 과정에서는 생략한다.
# 금일 실습에서는 20만건 학습이 불가능한 상황이기 때문에 3만건만 우선 랜덤샘플링을 진행한다.

df = df.sample(n=30000, random_state=0).reset_index(drop=True) # 랜덤 샘플링이기 때문에 index를 초기화한다.

4) 데이터 분리 (train/test)
from sklearn.model_selection import train_test_split
train_test_aplit(df['reviews'], df['ratings'], random_state=0, stratify=df['ratings'])

5) 전처리 (정제 -> 형태소 분석)
# pip install konlpy
import konlpy.tag
Okt = konlpy.tag.Okt()  # RuntimeError: Java versioin too old. Java 9 or later is required
                        # cmd에서 java -version

# ** java 최신버전 설치
https://www.oracle.com/java/technologies/downloads/#jdk26-windows
# 설치 후 명령 프롬포트는 다시 실행한 다음에 버전확인이 적용된다.
# 이후 path 등록을 진행해야 한다. (시스템 환경 변수 편집 - 환경 변수 - path - 다운로드 받은 C:\Program Files\Java\jdk-26.0.2\bin 추가 후 확인)