티스토리 뷰
딥러닝 복습
ML은 주어진 데이터를 바탕으로 의사결정 규칙을 만든다.
수제작이기 때문에 피처에 대한 설명이 용이하다. 그러나 error를 줄이는 데 도움이 될 지는 모델링을 해 봐야 안다.
충분한 개수의 특징을 제작했는지 모른다.
좋은 피처는 error를 줄여주고, 설명이 용이하며, 모델링하기에 충분히 많아야 한다.
그러나 복잡한 모델일 수록 설명 하는데 있어서 어려워진다.
딥러닝은 피처를 추출하고, 러닝을 하는 과정을 둘 다 한다.
피처 러닝 + 모델 트레이닝 => 예측
딥러닝의 깊이는 feature learning의 '수준'을 결정한다.
딥러닝의 너비는 feature의 '양'을 결정한다.
공간만 만들어주면, 예측에 도움이 되는 특징을 만들어준다.
Tabular Data(정형 데이터)는 정제되어 있는 게 많기에, ML이 DL보다 성능이 좋거나 비슷하다.
피처 엔지니어링이 불가능한 이미지와 같은 비정형 데이터에 DL이 적합하다.
Text Data도 피처 엔지니어링이 사실상 불가. 소리 데이터도 마찬가지.
무엇이 언제 왜 필요한지를 알아야 한다.
딥러닝의 계보 학파
기호주의 : If-else
유추
베이지안
진화
연결주의 : DL
Intro to Computer Vision
왜 컴퓨터 비전이어야 하는가?
OCR : 문서 텍스트 식별
Vision Diometics : 홍채 패턴 인식
Object Recognition : 제품 인식 및 분리
Special Effects : 모션 캡처 및 모양 캡처
3d printing, image capture : 영화, 건축, 구조 등에
Sports : 영상 판독
Smart Cars : 사물 및 사람 인식
Medical Imaging : 3D 이미지화
이런 작업들에 컴퓨터 비전이 쓰일 수 있다.
컴퓨터가 이미지를 이해하게 하려면?
사진에 무엇이 있는가(recognition)
어떤 행동을 하는가(feature extraction)
사진에 대한 묘사(언어화)
사람은 바로 이해할 수 있지만, 컴퓨터는 어떠한 이미지를 볼 때 무조건 0과 1로 표현한다.
사진에서 경계(edge)라는 패턴을 가르쳐야 한다.
어떠한 이미지가 밝은지, 어두운지를 알아야 한다.
그러면 어떻게? -> 미분이 급격하게 변화하는 곳이 회색조 이미지에 대해서 맞는 말이다.
그러나 예외의 경우가 많고, 노이즈가 너무 많다.
이미지의 각도를 어떻게 찍느냐에 따라서도 데이터가 달라진다.
그렇게 받는 영향들 : 빛, 각도, 노이즈, 경계, 자세, 물건, 배경, 여러 마리
그러면 어떻게 특징을 묶어줘야 하는가?
딥러닝의 역사
1960년 : edge detection
1970년 : 3d 모델 재해석의 발전(외곽,그룹화,뗴어내기, 재현화) 등등
계층의 이해, 모션 캡처의 기본 뼈대, 사물의 움직임을 이해시켰다.
1990년 : 인식 기술의 확장(사물의 스케일을 이해)
2000년 : 각 이미지의 조각으로 분류 (Feature의 등장)
문제는 알고리즘이 아닌, 데이터
1. 데이터의 양이 많아야 한다.
2. 데이터 레이블링이 잘 되어 있어야 한다.
2015년 ResNet의 등장으로 이미지 recognition은 평정
Yann Lecun, Geoffrey Hinton, Yoshua Bengio, Andew Ng(딥러닝 교육의 대중화)
CNN의 등장
이미지를 3개의 분류로 나눈다(RGB).
각각에 대해서 일정한 공간을 구축한다(필터, subsapling)
중간에 활성화 함수를 사용한다.
이 필터를 한 칸씩 옆으로 반복(stride)해 가면서, 특징들을 추출한다.
그렇게 만들어진 피처들을 평탄화(input shape)하여 바꾸고, 전결합을 통해 딥러닝 네트워크를 생성한다.
이를 통해 데이터 레이블링 문제를 해결한다.
필터를 cnn이 스스로 학습한다.
conv, relu, conv, relu, pool의 반복
합성곱, 활성화, 합성곱, 활성화, 풀링
풀링 층(Pooling Layer): 공간 차원을 줄이고 위치 불변성을 높이기 위해 사용된다. 주로 최대 풀링(max pooling)이나 평균 풀링(average pooling)이 사용됩니다.
32*32*1을 쓴다면, 5*5의 필터를 쓸 때 stride가 1이면
[(32-5+1)/1] = 28
[]는 버림이다.
resolution = 28
classes = 10
x = np.transpose(x, (2, 0, 1))
print(x.shape)
x = x.reshape( (-1, resolution, resolution, 1) )
>keras에 넣어줄 때는 (이미지의 수, 가로, 세로)로 들어가야 한다.
그렇기에 바꿔준다.
>((28, 28, 18724), (18724,))에서,
>(18724, 28, 28)
스케일링 할 때는 minmax scaler 쓰지 말고, 직접적으로 적어줘야 한다.
max_n, min_n = x_train.max(), x_train.min()
x_train = (x_train - min_n) / (max_n - min_n)
x_test = (x_test - min_n) / (max_n - min_n)
y를 원핫 인코딩을 하려면, to_categorical을 사용한다.
from keras.utils import to_categorical
class_n = len(np.unique(y_train))
y_train = to_categorical(train_y, class_n)
y_test = to_categorical(test_y, class_n)
모델 만들기
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense,BatchNormalization,Dropout
keras.utils.clear_session()
model = Sequential()
model.add(keras.layers.Input(shape=nfeature))
model.add(Flatten())
model.add( Dense(256 ,activation='relu'))
model.add( BatchNormalization())
model.add( Dropout(0.2))
model.add( Dense(256,activation='relu' ))
model.add( BatchNormalization())
model.add( Dropout(0.2))
model.add( Dense(10,activation='softmax' ))
model.summary()
batchNormalization를 중간 사이에 적용하여 모델을 만든다.
모델 컴파일
# 4. 컴파일
model.compile(optimizer='adam', loss=keras.losses.categorical_crossentropy,
metrics=['accuracy']
)
모델 컴파일을 할 때는 keras의 categrical_crossentropy를 써야 오류가 안난다.
from keras.callbacks import EarlyStopping
min_val_loss_delta = 0.1 #오차의 최소값에서 변화량이 최소 몇 이상 되어야 하는지를 지정한다(기본값은 0)
enough = 5 #얼마만큼 기다려 줄 것인지
es = EarlyStopping(monitor = 'val_loss',
min_delta = min_val_loss_delta,
verbose=1,
restore_best_weights=True,
patience = enough)
earlystopping을 할 때는 callback에 넣어준다.
model.fit(x_train,y_train, validation_split=0.2,
epochs=10000,
verbose=1,
callbacks=[es])
Batch의 의미
각 에포크마다 뜨는 숫자 = Batch(전체 데이터 덩어리)를 iterlation만큼 나눈 숫자.
하나의 데이터만 이용해서 GD(Gradient Descent)를 하자 -> Stochastic GD
이건 너무 극단적이다.
그러면 조금 작은 덩어리를 가져 보자 -> Mini-batch GD
기본적인 디폴트는 batch가 32개씩 나눠지고, 그 mini-batch 업데이트를 375번 반복한다.
모델 평가
model.evaluate(x_test,y_test)
>[0.3403639495372772, 0.9044058918952942]
keras에서는 아주 쉽게 된다.
이 함수는 입력 데이터셋 x_test에 대한 예측값과 실제 레이블 y_test를 비교하여 모델의 성능 지표를 계산한다.
주로 분류 문제에서는 정확도(accuracy)와 같은 지표가 반환되며, 회귀 문제에서는 평균 제곱 오차(Mean Squared Error)와 같은 지표가 반환될 수 있다.
첫번째 값은 손실(loss) 또는 오차.
일반적으로 이 값이 작을수록 모델이 좋은 성능을 보인다.
손실 함수는 모델의 유형과 문제 유형에 따라 다르며, 분류 문제의 경우에는 크로스 엔트로피(Cross Entropy) 또는 이진 교차 엔트로피(Binary Cross Entropy)가 일반적으로 사용.
두번째 값은 accuracy
y_pred = model.predict(x_test)
# 원핫 인코딩 한 것을 다시 묶어주는 코드
# 평가 지표 및 실제 데이터 확인을 위해 필요
y_pred_arg = np.argmax(y_pred, axis=1)
test_y_arg = np.argmax(test_y, axis=1)
실제 데이터를 확인한다.
letters_str = "ABCDEFGHIJ"
rand_idx = np.random.randint(0, len(y_pred_arg))
test_idx = test_y_arg[rand_idx]
pred_idx = y_pred_arg[rand_idx]
class_prob = np.floor( y_pred[rand_idx]*100 )
print(f'idx = {rand_idx}')
print(f'해당 인덱스의 이미지는 {letters_str[test_idx]}')
print(f'모델의 예측 : {letters_str[pred_idx]}')
print(f'모델의 클래스별 확률 : ')
print('-------------------')
for idx, val in enumerate(letters_str) :
print(val, class_prob[idx])
print('=================================================')
if test_y_arg[rand_idx] == y_pred_arg[rand_idx] :
print('정답')
else :
print('땡')
plt.imshow(x_test[rand_idx], cmap='gray')
plt.show()
틀린 이미지만 확인
temp = (test_y_arg == y_pred_arg) 다른 곳들을 담아서
false_idx = np.where(temp==False)[0] #각 idx를 만든다.
false_len = len(false_idx)
false_len
letters_str = "ABCDEFGHIJ"
rand_idx = false_idx[np.random.randint(0, false_len)]
test_idx = test_y_arg[rand_idx]
pred_idx = y_pred_arg[rand_idx]
class_prob = np.floor( y_pred[rand_idx]*100 )
print(f'idx = {rand_idx}')
print(f'해당 인덱스의 이미지는 {letters_str[test_idx]}')
print(f'모델의 예측 : {letters_str[pred_idx]}')
print(f'모델의 클래스별 확률 : ')
print('-------------------')
for idx, val in enumerate(letters_str) :
print(val, class_prob[idx])
print('=================================================')
if test_y_arg[rand_idx] == y_pred_arg[rand_idx] :
print('정답')
else :
print('땡')
plt.imshow(x_test[rand_idx], cmap='gray')
plt.show()
CNN의 개념
이미지 데이터를 flatten하면, 차원이 바뀌므로 위치 정보의 소실이 된다.
CNN 층을 거쳐 Feature map이 만들어진다.
Feature map - (Height,Width,Channel)
이것은 결국, 층이 나뉘어진 필터의 특징 지도들을 겹쳐서 만들어진 것이다.
Convolutional Layer를 거쳐 만들어진, 서로 다른 Feature의 모임이다.(10개의 채널)
Convolutional Layer Filter가 슬라이딩 하게 된다.
좌측 상단에서부터 한 칸씩 움직인다.
그래서 필터의 사이즈와 같은 랜덤한 어떤 shape와 곱해진다.
그 모든 값들을 전부 각 위치에 맞게 곱해준 후 합쳐준다.
그러면 5*5에서, 필터의 크기만큼(3*3)의 결과인 값이 도출된다.
그렇게 만들어진 것이 Feature map이다.
Output size=((Input size−Filter size+2×padding)/ ( Stride) ) +1
Stride
이동 보폭을 조절하는 개념.
5*5에서 stride가 2면 2*2가 된다.
즉, [(5-3 /2) +1]가 된다. 버림이 된다.
Padding
cnn은 Stride 떄문에 크기가 줄어드는 구조라서, 보안하기 위해 주변에 0을 붙여준다.
그러므로 padding을 2를 주면, strude가 2일때 [(5-3+2*2)/2 +1] 가 된다.
즉, 5*5 이미지가 7*7로 바뀐다고 생각하면 편하다.
2가지의 목적이다.
1.Feature map 크기의 유지
2. 외곽 정보를 더 반영하기 위해서
5*5의 이미지에서 3*3 필터가 있다고 해보자.
1,1행에 대해서, stride가 1이라면, 1번 연산
2,2행에 대해서는 4번 연산
3,3행에 대해서는 9번 연산
만약 padding이 있다면?
1,1행이 2,2행이 되므로, 4번으로 증강
2,2행이 3,3행이 되므로, 9번으로 증강
3,3행은 3*3 필터이므로 9번 그대로이다.
그러면 7*7에서 [(7-3) /1 +1] => 5*5가 된다.
Convolutional Layer의 채널은 input을 따라간다.
CNN에서의 계산 예시
처음에 32*32*3 이미지가 있다고 하자.
RGB로 분리. => 각각 32*32*1
32*32*3
필터의 개수 = 32(하이퍼 파라미터 값. 자신이 정해줘야 한다)
필터 사이즈 = 5*5*3
padding = X면,
Output size=(Input size−Filter size+2×padding)/ ( Stride)+1
28 = (32-5+2*0+1) / (1)
28*28*32
즉, 이미지 사이즈 * 필터 사이즈가 된다.
그러면 앞의 28*28*32를 넣어 뒤를 반영하려면 어떻게 해야 할까?
28*28*32
필터의 개수 = 64
필터 사이즈 = 5*5*32
padding= X면
필터 사이즈가 5*5*32인 이유가 뭘까? 당연하게도 앞에서 받아서 채널 수를 32개를 넣어주었기 때문이다.
필터 사이즈의 채널 수는 Input Shape에서와 똑같다.
그러므로 5*5*32의 필터 사이즈로 28*28*32를 훑게 되면 정해준 필터의 개수는 64개이므로 24*24*64개로 나오게 된다.
패딩이 있다면?
앞의 값과 뒤의 값이 같도록 그대로 나온다.
Pooling Layer
가로 세로만 영향.
학습 속도를 높이기 위해서 쓴다.
28*28*128 => 14*14*128
연산량을 줄이는 데 있어서 가장 중요한 정보만 남겼으면 좋겠다
ReLU에서 값이 크면 클수록 그 영향이 더 클것이라는 전제가 있다.
그런 것처럼 나머지는 덜어내고, 큰값을 가져간다 -> MaxPooling
풀링도 풀링 필터가 따로 있다.
Pooling Filter size=2
stride=2 (기본적으로 풀링 필터 사이즈를 따라간다)
2*2 필터에서 가장 큰 값을 가져와서 가져온다.
만약에 큰 값만 가져오고 싶지 않다면, 2*2를 avg를 가져온다.
이게 바로 avgPooling이다.
특징을 편향되지 않게 가져오고 싶다는 의미이다.
f=2면 필터 사이즈
keras에서는 Pool_size=(2,2)라고 의미
28*28*6이고, 풀링 후에 14*14*6이면
[(28-2+2*0+1)/2]+1 = 14이다.
막간 - 필터의 개수
컨볼루션 신경망에서 필터(커널)의 개수를 결정하는 방식은 주로 다음과 같은 요인들을 고려합니다.
- representational 능력 (representational capacity) 더 많은 필터를 사용하면 모델이 더 복잡한 패턴을 학습할 수 있습니다. 필터 개수가 적으면 학습 능력이 제한될 수 있습니다.
- 계산 복잡도 (computational complexity) 필터 개수가 많아지면 모델의 파라미터 수가 증가하고, 이에 따라 계산 복잡도와 메모리 요구량이 높아집니다.
- 과적합 (overfitting) 방지 적절한 수준의 필터 개수는 과적합 위험을 줄여줍니다. 너무 많은 필터는 과적합 가능성을 높입니다.
- 경험과 실험 많은 경우 문제의 복잡성과 데이터 크기에 따라 적절한 필터 개수를 결정하는 경험적인 방법이 사용됩니다. 반복적인 실험을 통해 최적의 필터 개수를 탐색합니다.
일반적으로 초기 레이어에서는 적은 수의 필터를 사용하고, 레이어가 깊어질수록 점차 많은 필터를 사용하는 것이 보편적입니다. 초기 레이어에서는 저수준 특징을 추출하고, 후기 레이어에서는 고수준 추상적 특징을 학습하기 때문입니다.
최적의 필터 개수는 문제의 복잡성, 데이터 크기, 하드웨어 제약 등을 균형있게 고려하여 실험적으로 결정하는 것이 일반적입니다.
실습 코드
shape를 가장 먼저 살펴본다. 그리고 h,w를 구한다.
train_x.shape, train_y.shape
>((28, 28, 18724), (18724,))
_, h, w = train_x.shape
print(h, w)
reshape를 해준다.
train_x = train_x.reshape(train_x.shape[0], h, w, 1)
test_x = test_x.reshape(test_x.shape[0], h, w, 1)
print(train_x.shape, train_y.shape, test_x.shape, test_y.shape)
스케일링 및 원핫 인코딩
max_n, min_n = train_x.max(), train_x.min()
train_x = (train_x - min_n) / (max_n - min_n)
test_x = (test_x - min_n) / (max_n - min_n)
from keras.utils import to_categorical
class_n = len(np.unique(train_y))
class_n
train_y = to_categorical(train_y, class_n)
test_y = to_categorical(test_y, class_n)
모델링
from keras.backend import clear_session
from keras.models import Model
from keras.layers import Input, Dense, Flatten, Conv2D, MaxPool2D
from keras.callbacks import EarlyStopping
Sequential()로 CNN 만들기
clear_session()
model = Sequential()
model.add (Input(shape=(h,w,1)))
model.add ( Conv2D( filters=32, #필더의 개수
kernel_size=(3,3), #필터의 가로세로 사이즈
strides=(1,1), #스트라이드
padding='same', #패딩
activation='relu', #활성화 함수
))
model.add ( Conv2D( filters=32, #필더의 개수
kernel_size=(3,3) , #필터의 가로세로 사이즈
strides=(1,1) , #스트라이드
padding='same', #패딩
activation='relu', #활성화 함수
))
model.add(MaxPool2D(pool_size=(2,2) , #max pooling layer 필터의 가로 세로 사이즈
strides= (2,2), #max pooling layer 필터의 보폭
))
model.add(BatchNormalization())
model.add(Dropout(0.25))
model.add ( Conv2D( filters=32, #필더의 개수
kernel_size=(3,3), #필터의 가로세로 사이즈
strides=(1,1), #스트라이드
padding='same', #패딩
activation='relu', #활성화 함수
))
model.add ( Conv2D( filters=32, #필더의 개수
kernel_size=(3,3), #필터의 가로세로 사이즈
strides=(1,1), #스트라이드
padding='same', #패딩
activation='relu', #활성화 함수
))
model.add(MaxPool2D(pool_size=(2,2) , #max pooling layer 필터의 가로 세로 사이즈
strides= (2,2), #max pooling layer 필터의 보폭
))
model.add(BatchNormalization())
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
model.summary()
Functional API로 만들어보기
## Functional API
# 1. 세션 클리어 : 메모리에 모델 구조가 남아있으면 지워줘.
clear_session()
# 2. 레이어 엮기 : 사슬처럼!
il = Input(shape=(28,28,1) )
hl = Conv2D(filters=128, # 새롭게 제작하려는 feature map의 수!
kernel_size=(3,3), # Convolutional Filter의 가로세로 사이즈!
strides=(1,1), # Convolutional Filter의 이동 보폭!
padding='same', # 패딩 적용 유무!
activation='relu' # 활성화 함수 반드시!
)(il)
hl = Conv2D(filters=128, # 새롭게 제작하려는 feature map의 수!
kernel_size=(3,3), # Convolutional Filter의 가로세로 사이즈!
strides=(1,1), # Convolutional Filter의 이동 보폭!
padding='same', # 패딩 적용 유무!
activation='relu' # 활성화 함수 반드시!
)(hl)
hl = MaxPool2D(pool_size=(2,2), # Pooling Filter의 가로세로 크기
strides=(2,2) # Pooling Filter의 이동 보폭! (None은 기본적으로 pool_size를 따라감)
)(hl)
hl = Conv2D(filters=64, # 새롭게 제작하려는 feature map의 수!
kernel_size=(3,3), # Convolutional Filter의 가로세로 사이즈!
strides=(1,1), # Convolutional Filter의 이동 보폭!
padding='same', # 패딩 적용 유무!
activation='relu' # 활성화 함수 반드시!
)(hl)
hl = Conv2D(filters=64, # 새롭게 제작하려는 feature map의 수!
kernel_size=(3,3), # Convolutional Filter의 가로세로 사이즈!
strides=(1,1), # Convolutional Filter의 이동 보폭!
padding='same', # 패딩 적용 유무!
activation='relu' # 활성화 함수 반드시!
)(hl)
hl = MaxPool2D(pool_size=(2,2), # Pooling Filter의 가로세로 크기
strides=(2,2) # Pooling Filter의 이동 보폭! (None은 기본적으로 pool_size를 따라감)
)(hl)
hl = Flatten()(hl)
hl = Dense(128, activation='relu')(hl)
ol = Dense(10, activation='softmax')(hl)
# 3. 모델의 시작과 끝 지정
model = Model(il, ol)
# 4. 컴파일
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy']
)
모델 학습
es = EarlyStopping(monitor='val_loss', # 얼리스토핑 적용할 관측 대상
min_delta=0, # Threshold. 설정한 값 이상으로 변화해야 개선되었다 간주.
patience=3, # 성능 개선이 발생하지 않을 때, 몇 Epochs 더 볼 것인지.
verbose=1,
restore_best_weights=True # 가장 성능이 좋게 나온 Epoch의 가중치로 되돌림
)
hist = model.fit(train_x, train_y, epochs=5, verbose=1,
validation_split=0.2, # 학습 데이터로부터 validation set을 생성!
callbacks=[es]
)
예측값 생성
pred_train = model.predict(train_x)
pred_test = model.predict(test_x)
single_pred_train = pred_train.argmax(axis=1)
single_pred_test = pred_test.argmax(axis=1)
train_y_arg = train_y.argmax(axis=1)
test_y_arg = test_y.argmax(axis=1)
logi_train_accuracy = accuracy_score(train_y_arg, single_pred_train)
logi_test_accuracy = accuracy_score(test_y_arg, single_pred_test)
print('CNN')
print(f'트레이닝 정확도 : {logi_train_accuracy*100:.2f}%' )
print(f'테스트 정확도 : {logi_test_accuracy*100:.2f}%' )
정답 확
'''
성능 확인을 위해
Ctrl+Enter를 이용하여
반복 실행 해보자!
'''
id = rd.randrange(0,10000)
print(f'id = {id}')
print(f'다음 그림은 숫자 {test_y_arg[id]} 입니다.')
print(f'모델의 예측 : {single_pred_test[id]}')
print(f'모델의 카테고리별 확률 : {np.floor(pred_test[id]*100)}')
if test_y_arg[id] == single_pred_test[id] :
print('정답입니다')
else :
print('틀렸어요')
plt.imshow(test_x[id].reshape([28,-1]), cmap='gray')
plt.show()
총 정리
Sequential API = 순차적으로만 레이어 블록 조립
Funtional API = 상대적으로 유연하게 모델링 가능
Conv2D와 Maxpool2D를 배웠다.
Conv2D = (filters, #새롭게 제작하려는 feature map의 수
kernel_size, # 필터의 사이즈(케라스에서 depth는 보정을 해준다)
padding, #외곽의 정보를 더 많이 반영, 크기 유지
strides, #필터의 이동 보폭
activation) #활성화 함수
크게 다섯가지
Maxpool2D = (pool_size, #풀링 필터의 사이즈
strides #풀링 필터의 이동 보폭
)