KNOU STUDYREAD

한국방송통신대학교 통계데이터과학과 블로그

CS & Department of Statistics and Data Science

통계데이터과학과 및 컴퓨터과학 독서

09. 딥러닝 16-18장

Harryㅤ 2023. 12. 18.

 

정규화(normalization) : 데이터의 폭이 클 경우 적절한 겂으로 분산 정도를 변경하는 과정

 
MNIST 손글씨 인식하기(데이터 전처리)

from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

import matplotlib.pyplot as plt
import sys

# MNIST 데이터셋을 활용
# X_train, y_train : 학습에 사용될 학습셋 데이터
# X_test, y_test : 테스트에 사용될 테스트셋 데이터
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 학습셋과 테스트셋이 각 몇개의 이미지인지 출력
print("학습셋 이미지 수: %d개" % (X_train.shape[0]))
print("테스트셋 이미지 수: %d개" % (X_test.shape[0]))

# 첫 번째 이미지 확인
plt.imshow(X_train[0], cmap='Greys')
plt.show()

for x in X_train[0]:
    for i in x:
        sys.stdout.write("%-3s" % i)
    sys.stdout.write('\n')

# 차원 변환 과정을 실습해 봅니다.
X_train = X_train.reshape(X_train.shape[0], 784)
X_train = X_train.astype('float64')
X_train = X_train / 255

X_test = X_test.reshape(X_test.shape[0], 784).astype('float64') / 255

# 클래스 값 출력
print("class : %d " % (y_train[0]))

# 바이너리화 실행
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

print(y_train[0])

MNIST 손글씨 인식하기(기본 프레임 작성)

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

import matplotlib.pyplot as plt
import numpy as np
import os

# MNIST 데이터 로드
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# 차원 변환 후, 테스트셋과 학습셋으로 분할
X_train = X_train.reshape(X_train.shape[0], 784).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 784).astype('float32') / 255

y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

# 모델 설정. 512개의 은닉층과 10개의 출력층, 각각의 활성함수는 Relu와 소프트맥스 설정
model = Sequential()
model.add(Dense(512, input_dim=784, activation='relu'))
model.add(Dense(10, activation='softmax'))
model.summary()

# 모델 실행 환경 설정. 오차함수로 categorical_crossentropy, 최적화모델 아담, 정확도 설정
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 모델 최적화 설정
modelpath = "./MNIST_MLP.hdf5"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)

# 데이터 셋을 가지고 모델을 설정, 각각의 배치 사이즈를 200개씩
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback, checkpointer])

# 테스트 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))

# 검증셋과 학습셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']

# 그래프 시각화
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')

# 그래프에 그리드와 레이블 추가
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

 

콘볼루션 신경망 CNN : 이미지에서 특징을 다시 한 번 추출하기 위해 커널을 도입해서 합성곱으로 계산함
합성곱 : 커널을 한칸씩 옮기며 설정된 값의 연산에 따라 나오게 되는 결과층 을 콘볼루션(합성곱) 층 이라 함.

케라스 사용시 콘볼루션 층을 추가하는 함수는 Conv2D() 를 사용한다.

ex. model.add(Conv2D(32, kernel_size=(3,3), input_shape=(28,28,1), activation='relu'))

* 첫 인자 32 = > 커널의 개수. 32개의 커널을 적용
* 두번째 인자 kernel_size(3,3) => 적용된 커널의 크기 행, 열
* 세번째 인자 input_shape(28, 28, 1) => 행, 열, 색상이며 입력 이미지가 컬러면 3, 흑백이면 1. 28*28 크기의 흑백(1) 이미지를 입력하겠다는 뜻
* 네번째 인자 activation='relu' => 활성화 함수 정의. Relu 함수 정의

## 기존 층에 추가로 콘볼루션 층을 더 추가할 경우 같은 방식으로 입력한다.
ex. model.add()
      model.add()

컨볼루션 층을 통해 도출했으나 결과가 크고 복잡할 경우 축소과정을 진행 -> 풀링(서브샘플링)

풀링추가시 수행단계 도식화


1) 맥스풀링 : 풀링 진행 시 최대값을 산출 (** 가장 보편적 **)
2) 평균풀링 : 풀링 진행 시 평균값을 산출

맥스 풀링을 사용하는 함수는 MaxPooling2D() 를 사용한다.

ex. model.add(MaxPooling2D(pool_size=(2,2)))
최대풀링을 사용하며 사이즈는 2*2

많은 양의 학습 != 학습의 개선, 과적합 야기 => 드롭아웃, 플래튼으로 해결
1) 드롭아웃 (Dropout) : 은닉층에서 실행(출력층에선 실행하지 않음),  은닉츠의 일부 노드를 제외하는것(계산하지 않음)

드롭아웃을 적용할 경우 Dropout()을 사용한다. 퍼센테이지를 소수점으로 표시.

ex. model.add(Dropout(0.45))
45%의 노드를 제외

2) 플래튼(Flatten) : 중간에 풀링이나 드롭아웃을 통해 산출된 결과물을 Dense() 함수를 이용해 기본층과 연결할 경우, 문제가 생긴다. 콘볼루션층과 맥스풀링의 결과물은 입력된 이미지를 2차원 배열로 연산하므로, 1차원 배열로 변환이 필요. -> 이때 사용하는 1차원화 작업 과정이 '플래튼' 함수이다.

플래튼을 적용할 경우 Flatten() 함수를 사용한다.

ex. model.add(Flatten())

 

CNN 콘볼루션 신경망으로 실행

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import ModelCheckpoint,EarlyStopping
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

import matplotlib.pyplot as plt
import numpy as np

# 데이터 import
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

# 컨볼루션 신경망(CNN) 설정
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128,  activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

# 모델 실행부 설정, 손실함수는 categorical_crossentropy, 최적화 모델은 아담, 정확도 설정
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

# 모델 최적화 설정
modelpath="./MNIST_CNN.hdf5"
checkpointer = ModelCheckpoint(filepath=modelpath, monitor='val_loss', verbose=1, save_best_only=True)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=10)

# 모델 실행, 학습셋 데이터를 가지고 각 75퍼센트와 25퍼센트로 나누고, 배치 단위는 200개씩
history = model.fit(X_train, y_train, validation_split=0.25, epochs=30, batch_size=200, verbose=0, callbacks=[early_stopping_callback,checkpointer])

# 테스트 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))

# 검증셋과 학습셋 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']

# 그래프 시각화
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')

# 그래프에 그리드와 레이블 추가하여 시각화
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

테스트 정확도가 0.9917 -> 전체 데이터 중에서 9917개의 이미지를 정확하게 분류함


자연어 처리(NLP) : 애플(시리), 구글(어시스턴트), 아마존(알렉사), 네이버(클로바)등 자연어(음성 및 텍스트)를 컴퓨터가 인식하고 처리하는 것을 자연어 처리(NLP)라고 한다. 딥러닝의 방대한 학습이 가능해지면서 자연어 처리 또한 가능하게 됐지만, 이를 위해서는 기본 텍스트 자료를 전처리 하는 선처리 과정 후 적용이 필요.

1) 토큰 : 문장에서 나눠진 부분별 단위
2) 토큰화 : 문장을 잘게 나누는 것

케라스에서 텍스트 데이터를 전처리하여 토큰화 할 경우 text_to_word_sequence() 함수를 사용
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Flatten,Embedding
from tensorflow.keras.utils import to_categorical
from numpy import array

# 케라스의 text_to_word_sequence() 함수 사용
from tensorflow.keras.preprocessing.text import text_to_word_sequence

# 전처리 텍스트 설정
text = '재밌는 딥러닝 공부.. 이병래 교수님 감사합니다.'

# 토큰화
result = text_to_word_sequence(text)
print("\n원문:\n", text)
print("\n토큰화:\n", result)

 

여러  문장 중에서 단어의 중요도(빈도)를 산출 => 토큰화 함수 사용

문장의 중요도 산출할 경우 케라스에서 제공하는 Tokenizer() 함수를 사용한다.

단어 빈도수 추가

# 단어 빈도수 세기

# 전처리 문장 예제 3개 작성
docs = ['2023년 2학기 방송대 컴퓨터과학과 딥러닝 과목 개설.',
       '방송대 컴퓨터과학과 딥러닝 과목의 담당 교수님은 이병래 교수님 입니다',
       '인공지능 안에 머신러닝 안에 딥러닝이 포함된 구조',
       ]

# 토큰화 함수를 이용해 전처리 실행
token = Tokenizer()            # 토큰화 함수 설정(빈도 산출)
token.fit_on_texts(docs)       # 토큰화 함수에 예제 문장 작성

# 단어의 빈도수를 계산한 결과를 각 옵션에 맞추어 출력
# Tokenizer()의 word_counts 함수는 순서를 기억하는 OrderedDict 클래스를 사용합니다.
print("\n단어 카운트:\n", token.word_counts)

# 출력되는 순서는 랜덤입니다.
print("\n문장 카운트: ", token.document_count)
print("\n각 단어가 몇 개의 문장에 포함되어 있는가:\n", token.word_docs)
print("\n각 단어에 매겨진 인덱스 값:\n",  token.word_index)

 

원핫 인코딩 : 문장 속 단어가 다른 단어와 가지는 관계를 알아보기 위해 사용한다. 0과 1로 표현

text="딥러닝은 방송대 컴퓨터과학과 4학년 2학기 이병래 교수님 과목입니다"
token = Tokenizer()
token.fit_on_texts([text])
print(token.word_index)

x=token.texts_to_sequences([text])
print(x)

# 인덱스 수에 하나를 추가해서 원-핫 인코딩 배열 생성 후 출력
word_size = len(token.word_index) + 1
x = to_categorical(x, num_classes=word_size)
print(x)

 

단어 임베딩 : 원핫 인코딩 사용할 경우 벡터의 길이가 길어짐 => 공간 낭비 해결을 위해 사용.
주어진 배열을 정해진 길이로 압축시킴으로서 문제를 해결함(단어간 유사도 등 활용)
* 단어간 유사도 계산 = 오차 역전파를 이용하며 최적 유사도를 계산하는 학습과정으로 구현

단어 임베딩을 하기 위해서는 케라스에서 제공하는 Embedding() 함수를 사용한다.

ex.
model = Sequential()
model.add(Embedding(16,4))

입력될 단어수와 출력될 벡터크기를 지정해야하며,
위의 경우 16개의 입력될 단어수와 임베딩 과정을 통해 출력될 벡터 크기를 4로 지정한 것


영화 리뷰의 부정과 긍정 예측하기

from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Embedding
from tensorflow.keras.utils import to_categorical

from numpy import array

# 텍스트 리뷰 자료설정
docs = ["너무 재밌네요","최고예요","참 잘 만든 영화예요","추천하고 싶은 영화입니다","한번 더 보고싶네요","글쎄요","별로예요","생각보다 지루하네요","연기가 어색해요","재미없어요"]

# 긍정 리뷰는 1, 부정 리뷰는 0으로 클래스화
classes = array([1,1,1,1,1,0,0,0,0,0])

# 토큰화
token = Tokenizer()
token.fit_on_texts(docs)
print(token.word_index)

x = token.texts_to_sequences(docs)
print("\n리뷰 텍스트, 토큰화 결과:\n", x)

# 패딩, 서로 다른 길이의 데이터를 4로 통일
padded_x = pad_sequences(x, 4)  
print("\n패딩 결과:\n", padded_x)

# 임베딩에 입력될 단어의 수 지정
word_size = len(token.word_index) + 1

# 단어 임베딩을 포함해 딥러닝 모델을 만들고 결과출력
model = Sequential()
model.add(Embedding(word_size, 8, input_length=4))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
model.summary()

model.compile(optimizer='adam', loss='binary_crossentropy', 
metrics=['accuracy'])
model.fit(padded_x, classes, epochs=20)
print("\n Accuracy: %.4f" % (model.evaluate(padded_x, classes)[1]))

순환 신경망(RNN Recurrent Neural Network) : 기존 인식 방법은 무작위로 입력된, 정제된 데이터의 입력에만 기반한 방식이었으나, 문장을 학습할 때 선후관계를 따져야 하는 문제가 발생. 순환 신경망은 여러 데이터가 순차 입력된 뒤 입력받은 데이터를 잠시 기억처리 한 뒤 개별 가중치를 주고 다음 데이터로 넘어감

LSTM(Long Short Term Memory) : RNN의 한계를 극복. 기존 RNN의 경우 기울기 소실 발생에 따른 문제를 개선.
반복 직전 다음층으로 기억할 값을 넘길지의 여부를 관리하는 단계를 추가한 모델이다. 

 
LSTM으로 로이터 뉴스 카테고리 분류

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM, Embedding
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.datasets import reuters
from tensorflow.keras.callbacks import EarlyStopping

import numpy as np
import matplotlib.pyplot as plt

# 데이터를 불러와 학습셋, 테스트셋 분할
(X_train, y_train), (X_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2)

# 데이터를 확인
category = np.max(y_train) + 1
print(category, '카테고리')
print(len(X_train), '학습용 뉴스 기사')
print(len(X_test), '테스트용 뉴스 기사')
print(X_train[0])

# 단어의 수를 맞춤
X_train = sequence.pad_sequences(X_train, maxlen=100)
X_test = sequence.pad_sequences(X_test, maxlen=100)

# 원-핫 인코딩
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

# 모델 구조설정. LSTM 은닉층 활성화 함수는 tanh 함수, 출력층엔 경사기울기소실(RNN) 문제 보완을 위해 소프트맥스
model = Sequential()
model.add(Embedding(1000, 100))
model.add(LSTM(100, activation='tanh'))
model.add(Dense(46, activation='softmax'))

# 모델 실행 설정
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 학습 조기 중단 설정(과적합 방지)
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=5)

# 모델 실행
history = model.fit(X_train, y_train, batch_size=20, epochs=200, validation_data=(X_test, y_test), callbacks=[early_stopping_callback])

# 테스트 정확도를 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(x_test, y_test)[1]))

# 학습셋과 테스트셋의 오차를 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']

# 그래프 시각화
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')

# 그래프에 그리드와 레이블 추가
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()
71퍼센트의 정확도

16 에포크에서 학습 조기중단했으며 이를 그래프로 시각화


어텐션 : 기존 RNN의 한계에 따라 문장의 길이가 길어질 경우 마지막 부분에서 문맥 벡터에 너무 많은 입력값이 누적되어 디코더에 제대로 전달하지 못하는 문제 발생 => 입력층과 출력층 사이에 가중치를 계산하는 층을 추가해 중점도를 계산.  즉, 각 문맥별 중요치 순으로 집중하는 것으로 기존 RNN의 한계를 극복

어텐션을 적용하기 위해 라이브러리를 설치한 뒤, Attention() 함수를 통해 사용한다.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Embedding, LSTM, Conv1D, MaxPooling1D
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing import sequence
from tensorflow.keras.callbacks import EarlyStopping
from attention import Attention # 어텐션 라이브러리 설정

import numpy as np
import matplotlib.pyplot as plt

# 데이터를 불러와 학습셋, 테스트셋 분할
(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=5000)

# 단어의 수를 맞춤
X_train = sequence.pad_sequences(X_train, maxlen=500)
X_test = sequence.pad_sequences(X_test, maxlen=500)

# 모델 구조 설정
model = Sequential()
model.add(Embedding(5000, 500))
model.add(Dropout(0.5))
model.add(LSTM(64, return_sequences=True))
model.add(Attention()) # 어텐션 적용
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

# 모델 실행 옵션 설정
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

# 학습 조기 중단 설정
early_stopping_callback = EarlyStopping(monitor='val_loss', patience=3)

# 모델 실행
history = model.fit(X_train, y_train, batch_size=40, epochs=100, validation_data=(X_test, y_test), callbacks=[early_stopping_callback])

# 정확도 출력
print("\n Test Accuracy: %.4f" % (model.evaluate(X_test, y_test)[1]))

# 학습셋과 테스트셋의 오차 저장
y_vloss = history.history['val_loss']
y_loss = history.history['loss']

# 그래프 시각화
x_len = np.arange(len(y_loss))
plt.plot(x_len, y_vloss, marker='.', c="red", label='Testset_loss')
plt.plot(x_len, y_loss, marker='.', c="blue", label='Trainset_loss')

# 그래프에 그리드, 레이블 추가
plt.legend(loc='upper right')
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

어텐션을 사용해 정확도를 88%까지 개선할 수 있다
* RNN - LSTM - ATTENTION

'통계데이터과학과 및 컴퓨터과학 독서' 카테고리의 다른 글

01. 인공지능 및 파이썬 1장  (0) 2024.02.19
10. 딥러닝 19-21장  (0) 2023.12.25
08. 딥러닝 13-15장  (1) 2023.12.11
07. 딥러닝 10-12장  (0) 2023.12.04
06. 딥러닝 7-9장  (0) 2023.11.27