반응형

안녕하세요. 오늘은 RNN을 Pytorch로 직접 구현해보고, Tanh(hyperbolic tangent) 대신, Sigmoid나 ReLU를 사용하면 어떻게 되는지 실험을 한 번 해보겠습니다. 

 

www.youtube.com/watch?v=tlyzfIYvMWE&list=PLSAJwo7mw8jn8iaXwT4MqLbZnS-LJwnBd&index=26

제가 Pytorch를 처음 공부할 때 위 영상을 많이 참고 했습니다. 

 

Pytorch를 처음 공부하시는 분이시라면, 위 딥러닝 홀로서기 Pytorch Kist 영상을 보시는 걸 추천드립니다. 

 

자료도 아낌없이 GIt에 올라와 있고, 라이브 코딩을 하면서 수업이 진행되기 때문에 접근하는 노하우를 기를 수 있다고 생각합니다. 

 

코드는 위 영상 GIthub의 자료를 수정한 내용입니다. 

 

우선 RNN(Recurrent Neural Network)에 구조 대해 이야기 해보겠습니다. 

 

RNN은 Input과 Output을 Sequence 단위로 처리하는 네트워크 구조입니다. 

 

일반 신경망과 달리 순환 신경망은 Cell 부분이 순환하게 됩니다.

Hidden Layer는 바로 이전 Hidden Layer의 입력을 현재 입력으로 사용하는 것이 가장 큰 특징입니다.

 

이런 RNN의 순환 구조 때문에 시계열 데이터에 가장 많이 사용되고 있습니다. 

 

위 구조는 간단한 식으로 표현할 수 있습니다.

 

그렇다면 activation function인 tanh 말고, 지금 가장 많이 사용한다는 ReLU를 사용하면 안될까 라는 궁금증이 생겼습니다. 

 

Sigmoid의 미분의 최대값이 0.25이기 때문에, Deep해질수록 Vanishing Gradient 가 발생합니다. 

 

Vanishing 문제를 해결하기 위해 만든 것이 Tanh 입니다. 

 

Tanh는 0~1인 Sigmoid 확장판이며, 미분의 최대값이 1로 Vanishing Graidient를 해결합니다. 

 

ReLU는 -1~1을 반환하는 Tanh 대신, x가 0보다 크면 그대로 값을 보내게 되어, 속도와 학습률이 향상됐습니다.  

 

하지만, RNN의 내부는 계속 순환하는 구조로 값이 1보다 크게 되면, ReLU 특성상 값이 발산할 수 있기 때문에 적합하지 않다고 합니다.   

 

그렇다면, 간단한 데이터에서 발산하지 않는 환경에서 ReLU를 사용하게 된다면 어떻게 될지 궁금했습니다. 

 

위 영상의 Git 코드를 사용했으며, 학습이 실패한 영상입니다. 

 

수정을 하진 않고, 코드를 그대로 사용했으며, 활성화 함수만 바꿔가면서 학습 결과를 확인했습니다. 

 

import torch
import torch.nn as nn


class RNN(nn.Module):
    def __init__(self, input_dim, output_dim, hid_dim, batch_size): 
        super(RNN, self).__init__()
        
        self.input_dim = input_dim
        self.output_dim = output_dim
        self.hid_dim = hid_dim
        self.batch_size = batch_size
        
        self.wx = nn.Linear(self.input_dim, self.hid_dim, bias=False)
        self.wh = nn.Linear(self.hid_dim, self.hid_dim, bias=False)
        self.wy = nn.Linear(self.hid_dim, self.output_dim, bias=False)
        self.act_fn = nn.Tanh()  # nn.Sigmoid() nn.ReLU() nn.LeakyReLU() 
        
        self.hidden = self.init_hidden()
        
    def init_hidden(self, batch_size=None):
        if batch_size is None:
            batch_size = self.batch_size
        return torch.zeros(batch_size, self.hid_dim)
    
    def forward(self, x):
        h = self.act_fn(self.wx(x) + self.wh(self.hidden))
        y = self.wy(h)
        return y, h

대략 이런 그런 그림이 그려집니다. 분명 Loss 전부 떨어졌으나 모델 성능은 별로 인 것 같습니다. 

 

ReLU가 조금이나마 따라가는 듯하나 값을 예측을 하지 못했습니다. 

 

그래서 LeakyReLU를 사용해 또 결과를 확인 했습니다. 

 

Sinc함수???

LeakyReLU를 사용했을 땐, Sinc 함수가 그려졌습니다. 

 

궁금증을 해결하고자 활성화함수를 바꿔가면서 학습을 시켰으나 오히려 궁금증이 더 커졌습니다.

 

이 부분은 좀 더 공부를 해봐야 할 것 같습니다... 딥러닝 고수분들 피드백 부탁드립니다. 

728x90
반응형
반응형

안녕하세요. 

 

오늘은 LSTM을 이용해서 삼성전자 주가를 예측해보겠습니다. 

 

큰 Dataset은 따로 필요하지 않으니 부담 갖지 않고 하시면 될 것 같습니다. 

 

 

아래는 본문 글입니다.

cnvrg.io/pytorch-lstm/?gclid=Cj0KCQiA6t6ABhDMARIsAONIYyxsIXn6G6EcMLhGnPDxnsKiv3zLU49TRMxsyTPXZmOV3E-Hh4xeI2EaAugLEALw_wcB

 

LSTM이 어떻게 동작을 하는지 자세히 아시고 싶으시면 아래 블로그를 추천드립니다.

dgkim5360.tistory.com/entry/understanding-long-short-term-memory-lstm-kr

 

Long Short-Term Memory (LSTM) 이해하기

이 글은 Christopher Olah가 2015년 8월에 쓴 글을 우리 말로 번역한 것이다. Recurrent neural network의 개념을 쉽게 설명했고, 그 중 획기적인 모델인 LSTM을 이론적으로 이해할 수 있도록 좋은 그림과 함께

dgkim5360.tistory.com

 

 

1. 라이브러리

import numpy as np
import pandas as pd
import pandas_datareader.data as pdr
import matplotlib.pyplot as plt

import datetime

import torch
import torch.nn as nn
from torch.autograd import Variable 

import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

 

no module pandas_datareaderno module named 'pandas_datareader'

pandas가 깔려 있는데, 위 문구가 뜬다면 pip install pandas_datareader로 다운로드합니다. 

 

coding-yoon.tistory.com/56

 

[파이썬 응용] pandas_datareader의 error문 : FutureWarning: pandas.util.testing is deprecated. Use the functions in the pu

안녕하세요. pandas_datareader을 이용해서 데이터 처리를 하기 위해 아래 문구처럼 에러문이 뜨는 경우가 있습니다. FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at..

coding-yoon.tistory.com

옛날에는 Pandas를 깔면 자동으로 깔렸었는데, 이번에 아예 분리가 된 것 같습니다. 

 

 

2. 삼성 전자 주식 불러오기 

start = (2000, 1, 1)  # 2020년 01년 01월 
start = datetime.datetime(*start)  
end = datetime.date.today()  # 현재 

# yahoo 에서 삼성 전자 불러오기 
df = pdr.DataReader('005930.KS', 'yahoo', start, end)
df.head(5)
df.tail(5)
df.Close.plot(grid=True)

 

head(5) : 맨 앞 5개

 

tail(5) : 맨 뒤 5 개
삼성 전자 2000 ~ 2020년

삼성 전자 종가를 2000년부터 2020년으로 한 번에 보니 미쳐 날 뛰네요. 지금이라도 이 흐름을 타야 하지 않을까요.

십 만전자 가자!!

 

혹시 다른 주식도 하고 싶으시면  야후 파이낸시에서 찾아보시는 것도 추천드립니다.

finance.yahoo.com/

 

Yahoo Finance - Stock Market Live, Quotes, Business & Finance News

At Yahoo Finance, you get free stock quotes, up-to-date news, portfolio management resources, international market data, social interaction and mortgage rates that help you manage your financial life.

finance.yahoo.com

그리고 학습된 모델이 성능을 확인하기 위해서 위 데이터(현재 약 5296개)를 Train(학습하고자 하는 데이터)를 0부터 4499까지, Test(성능 테스트하는 데이터)는 4500부터 5295개 까지 데이터로 분류합니다.

 

오늘자 대략, 노란색 선 정도까지 데이터를 가지고 학습을 하고, 노란색 선 이후부터 예측을 할 것입니다. 

과연 내려가고 올라가는 포인트를 잘 예측할 수 있을지 궁금합니다.

 

3. 데이터셋 준비하기

"""
저도 주식을 잘 모르기 때문에 참고해주시면 좋을 것 같습니다. 
open 시가
high 고가
low 저가
close 종가
volume 거래량
Adj Close 주식의 분할, 배당, 배분 등을 고려해 조정한 종가

확실한건 거래량(Volume)은 데이터에서 제하는 것이 중요하고, 
Y 데이터를 Adj Close로 정합니다. (종가로 해도 된다고 생각합니다.)

"""
X = df.drop(columns='Volume')
y = df.iloc[:, 5:6]

print(X)
print(y)

X
y

"""
학습이 잘되기 위해 데이터 정규화 
StandardScaler	각 특징의 평균을 0, 분산을 1이 되도록 변경
MinMaxScaler	최대/최소값이 각각 1, 0이 되도록 변경
"""

from sklearn.preprocessing import StandardScaler, MinMaxScaler
mm = MinMaxScaler()
ss = StandardScaler()

X_ss = ss.fit_transform(X)
y_mm = mm.fit_transform(y) 

# Train Data
X_train = X_ss[:4500, :]
X_test = X_ss[4500:, :]

# Test Data 
"""
( 굳이 없어도 된다. 하지만 얼마나 예측데이터와 실제 데이터의 정확도를 확인하기 위해 
from sklearn.metrics import accuracy_score 를 통해 정확한 값으로 확인할 수 있다. )
"""
y_train = y_mm[:4500, :]
y_test = y_mm[4500:, :] 

print("Training Shape", X_train.shape, y_train.shape)
print("Testing Shape", X_test.shape, y_test.shape) 

numpy 형태 : 이 상태에서는 학습이 불가능. 

"""
torch Variable에는 3개의 형태가 있다. 
data, grad, grad_fn 한 번 구글에 찾아서 공부해보길 바랍니다. 
"""
X_train_tensors = Variable(torch.Tensor(X_train))
X_test_tensors = Variable(torch.Tensor(X_test))

y_train_tensors = Variable(torch.Tensor(y_train))
y_test_tensors = Variable(torch.Tensor(y_test))

X_train_tensors_final = torch.reshape(X_train_tensors,   (X_train_tensors.shape[0], 1, X_train_tensors.shape[1]))
X_test_tensors_final = torch.reshape(X_test_tensors,  (X_test_tensors.shape[0], 1, X_test_tensors.shape[1])) 

print("Training Shape", X_train_tensors_final.shape, y_train_tensors.shape)
print("Testing Shape", X_test_tensors_final.shape, y_test_tensors.shape) 

학습할 수 있는 형태로 변환하기 위해 Torch로 변환

 

 

4. GPU 준비하기 (없으면 CPU로 돌리면 됩니다.)

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")  # device
print(torch.cuda.get_device_name(0))

 

 

5. LSTM 네트워크 구성하기

class LSTM1(nn.Module):
  def __init__(self, num_classes, input_size, hidden_size, num_layers, seq_length):
    super(LSTM1, self).__init__()
    self.num_classes = num_classes #number of classes
    self.num_layers = num_layers #number of layers
    self.input_size = input_size #input size
    self.hidden_size = hidden_size #hidden state
    self.seq_length = seq_length #sequence length
 
    self.lstm = nn.LSTM(input_size=input_size, hidden_size=hidden_size,
                      num_layers=num_layers, batch_first=True) #lstm
    self.fc_1 =  nn.Linear(hidden_size, 128) #fully connected 1
    self.fc = nn.Linear(128, num_classes) #fully connected last layer

    self.relu = nn.ReLU() 

  def forward(self,x):
    h_0 = Variable(torch.zeros(self.num_layers, x.size(0), self.hidden_size)).to(device) #hidden state
    c_0 = Variable(torch.zeros(self.num_layers, x.size(0), self.hidden_size)).to(device) #internal state   
    # Propagate input through LSTM

    output, (hn, cn) = self.lstm(x, (h_0, c_0)) #lstm with input, hidden, and internal state
   
    hn = hn.view(-1, self.hidden_size) #reshaping the data for Dense layer next
    out = self.relu(hn)
    out = self.fc_1(out) #first Dense
    out = self.relu(out) #relu
    out = self.fc(out) #Final Output
   
    return out 

 

 

위 코드는 복잡해 보이지만, 실상 하나씩 확인해보면 굉장히 연산이 적은 네트워크입니다. 

시계열 데이터이지만, 간단한 구성을 위해 Sequence Length도 1이고, LSTM Layer도 1이기 때문에 굉장히 빨리 끝납니다. 아마 본문 작성자가 CPU환경에서도 쉽게 따라 할 수 있게 간단하게 작성한 것 같습니다. 

 

아래는 Pytorch로 RNN을 사용하는 방법을 적었지만, LSTM과 동일합니다. 

기본 동작 원리만 이해하시면, 쉽게 따라 하실 수 있습니다.

coding-yoon.tistory.com/55

 

[딥러닝] RNN with PyTorch ( RNN 기본 구조, 사용 방법 )

오늘은 Pytorch를 통해 RNN을 알아보겠습니다. https://www.youtube.com/watch?v=bPRfnlG6dtU&t=2674s RNN의 기본구조를 모르시면 위 링크를 보시는걸 추천드립니다. Pytorch document에 RNN을 확인하겠습니다. ht..

coding-yoon.tistory.com

 

 

5. 네트워크 파라미터 구성하기 

num_epochs = 30000 #1000 epochs
learning_rate = 0.00001 #0.001 lr

input_size = 5 #number of features
hidden_size = 2 #number of features in hidden state
num_layers = 1 #number of stacked lstm layers

num_classes = 1 #number of output classes 
lstm1 = LSTM1(num_classes, input_size, hidden_size, num_layers, X_train_tensors_final.shape[1]).to(device)

loss_function = torch.nn.MSELoss()    # mean-squared error for regression
optimizer = torch.optim.Adam(lstm1.parameters(), lr=learning_rate)  # adam optimizer

 

6. 학습하기

for epoch in range(num_epochs):
  outputs = lstm1.forward(X_train_tensors_final.to(device)) #forward pass
  optimizer.zero_grad() #caluclate the gradient, manually setting to 0
 
  # obtain the loss function
  loss = loss_function(outputs, y_train_tensors.to(device))

  loss.backward() #calculates the loss of the loss function
 
  optimizer.step() #improve from loss, i.e backprop
  if epoch % 100 == 0:
    print("Epoch: %d, loss: %1.5f" % (epoch, loss.item())) 

 

7. 예측하기

df_X_ss = ss.transform(df.drop(columns='Volume'))
df_y_mm = mm.transform(df.iloc[:, 5:6])

df_X_ss = Variable(torch.Tensor(df_X_ss)) #converting to Tensors
df_y_mm = Variable(torch.Tensor(df_y_mm))
#reshaping the dataset
df_X_ss = torch.reshape(df_X_ss, (df_X_ss.shape[0], 1, df_X_ss.shape[1]))
train_predict = lstm1(df_X_ss.to(device))#forward pass
data_predict = train_predict.data.detach().cpu().numpy() #numpy conversion
dataY_plot = df_y_mm.data.numpy()

data_predict = mm.inverse_transform(data_predict) #reverse transformation
dataY_plot = mm.inverse_transform(dataY_plot)
plt.figure(figsize=(10,6)) #plotting
plt.axvline(x=4500, c='r', linestyle='--') #size of the training set

plt.plot(dataY_plot, label='Actuall Data') #actual plot
plt.plot(data_predict, label='Predicted Data') #predicted plot
plt.title('Time-Series Prediction')
plt.legend()
plt.show() 

 

 

빨간색 선 이후부터 모델이 예측을 한 것인데 나름 비슷하게 나온 것 같습니다.

 

하지만 인공지능이라도 팔만 전자는 예상하지 못했나 봅니다. 

728x90
반응형
반응형
글의 가독성을 위해 아래 링크에 다시 정리함.

https://blog.naver.com/younjung1996/223413266165

 

[딥러닝] Depth-wise Separable Convolution

Depth-wise Separable Convolution은 합성곱 신경망(CNN:Convolution Neural Network)의 효율성과...

blog.naver.com

안녕하세요. 오늘은 CNN에서 Depth-wise Separable Convolution에 대해 이야기해보겠습니다. 

 

Depth-wise Separable Convolution

Depth-wise separable Convolution을 가장 잘 표현한 그림이라고 생각합니다.

 

하지만 CNN에 대해 자세한 이해가 없으면 이 그림을 보더라도 이해가 잘 가지 않습니다. 

 

위 그림을 이해하기 위해서는 Standard(일반) Convolution을 정확히 파악해야 합니다. 

 

Standard Convolution 연산 과정

위 gif는 제 PPT 내용을 짜집어서 Standard Convolution 연산을 설명한 것입니다. 

 

기본적으로 하나의 Convolution을 통과하면 하나의 Feature Map(Activation Map)이 생성됩니다. 

( pytorch에서 out_channels을 통해 Feature Map의 개수를 정할 수 있습니다. )

 

이때 Feature Map 한 개를 생성하는데 Kernel Size x Kernel Size x Input Channel(입력 이미지 개수)의 Parameter가 필요합니다.

 

 

 

 

 

1. Standard Convolution vs Depth-wise Convolution

 

(색으로 비교하시면서 보십시오. )

하나의 Feature Map = Kernel Size x Kernel Size x 3

 

 

 

 

반면, Depth-wise은 아래와 같습니다.

 

차이를 아시겠나요? 

 

Depth-wise Convolution은 한 번 통과하면, 하나로 병합되지 않고, (R, G, B)가 각각 Feature Map이 됩니다. 

 

3 x Feature Map = Kernel Size x Kernel Size x 3

 

정리하게 되면,

 

하나의 Feature Map = Kernel Size x Kernel Size

 

두 개를 논문 Figure로 비교하면, 

 

 Depth-wise Convolution에서 연산량이 Input Channel 만큼 연산량이 줄어듭니다. 

 

 

2. Separable Convolution

 

Separable Convolution은 Point-wise Convolution 입니다.

 

Point-wise Convolution는 1x1 Convolution입니다. 

 

coding-yoon.tistory.com/116

 

[딥러닝] DeepLearning CNN BottleNeck 원리(Pytorch 구현)

안녕하세요. 오늘은 Deep Learning 분야에서 CNN의 BottleNeck구조에 대해 알아보겠습니다. 대표적으로 ResNet에서 BottleNeck을 사용했습니다. ResNet에서 왼쪽은 BottleNeck 구조를 사용하지 않았고, 오른쪽은..

coding-yoon.tistory.com

1x1 Convolution의 역할을 자세히 알고 싶으시다면 위 글을 참고하시기 바랍니다. 

 

1x1 Convolution은 두 가지 특성을 가지고 있습니다 .

 

(1) 공간적인 특성을 가지고 있지 않다.

(2) 연산량이 가장 적게 필요로 하기 때문에, Feature Map 개수를 조절할 때 사용된다. 

 

 

 

 

 

 

3. Depth-wise Separable Convolution

 

 

(2) Pointwise Convolution에서 BottleNeck구조를 이해하셨으면, 바로 감이 오실 겁니다. 

 

 

BottleNeck의 구조 핵심은 연산량이 높은 곳은 최대한 Feature Map을 적게 생성하고, 연산량이 낮은 곳에서 Feature Map을 가지고 노는 것입니다. 

 

Depth-wise Convolution이 연산량이 적다 하더라도, Point-wise Convolution에 비하면 연산량이 굉장히 많은 편입니다. 

 

Depth-wise Convolution에서 Feature Map을 최대한 적게 뽑아내고, Point-wise Convolution에서 Feature Map을 필요한 만큼 뽑아냅니다.

 

 

 

 

 

3. vs Depth-wise Separable Convolution

 

두 식을 정리하면, 최종적으로 곱셈이 덧셈으로 바뀌게 됩니다. 

 

보통 Kernel Size보다 Output Channel이 훨씬 크기 때문에 계산해보면 약 Kernel size 제곱만큼의 차이가 발생합니다. 

 

이를 Pytorch로 구현하는 방법은 간단합니다. 

class depthwise_separable_conv(nn.Module):
    def __init__(self, input_dim, output_dim):
        super(depthwise_seperable_conv, self).__init__()
        self.depthwise = nn.Conv2d(input_dim, input_dim, kernel_size=3, padding=1,
                                                              groups=input_dim)
        self.pointwise = nn.Conv2d(input_dim, output_dim, kernel_size=1)

    def forward(self, x):
        x = self.depthwise(x)
        x = self.pointwise(x)
        
       return x

 

Convolution에서 groups라는 인자가 있습니다. 

 

말 그대로 Input Channel을 얼마만큼 그룹 지어서 출력할지 정하는 인자입니다. 

 

 Group을 최대로 지으면, 결국 서로 각각의 독립적인 계산을 하게 됩니다. 

 

아래 Pytorch Document를 참고하시면 좋습니다. 

 

pytorch.org/docs/stable/generated/torch.nn.Conv2d.html?highlight=conv2d#torch.nn.Conv2d

 

Conv2d — PyTorch 1.7.0 documentation

Shortcuts

pytorch.org

 

728x90
반응형
반응형

안녕하세요. 오늘은 Deep Learning 분야에서 CNN의 BottleNeck구조에 대해 알아보겠습니다. 

 

대표적으로 ResNet에서 BottleNeck을 사용했습니다. 

 

ResNet에서 왼쪽은 BottleNeck 구조를 사용하지 않았고, 오른쪽은 BottleNeck 구조를 사용했습니다.

 

BottleNeck을 설명하기 전, Convolution의 Parameters을 계산할 줄 알아야 합니다. 이 부분은 다른 글에서 자세히 설명하겠습니다.

 

Convolution Parameters = Kernel Size x Kernel Size x Input Channel x Output Channel

 

BottleNeck의 핵심은 1x1 Convolution입니다. ( Pointwise Convolution 이라고도 합니다. 이는 Depthwise Separable Convolution에서도 똑같은 원리로 이용되기 때문에 잘 알아두면 좋습니다.)

1x1 Convolution의 Parameters는 1 x 1 x Input Channel x Output Channel입니다.

대게 1x1 Convolution은 연산량이 작기 때문에 Feature Map(Output Channel)을 줄이거나 키울 때 사용됩니다.

 

 

BottleNeck 구조

 

 

1. Input Channel = 256인 320x320  Input Image가 있다고 가정합니다. 

B=Batch SIze 

 

 

 

2. Channel Compression ( 채널 압축 )

Input Channel 256 -> Output Channel 64

256을 64로 채널을 강제로 축소한 이유는 오로지 연산량을 줄이기 위함입니다. 

 

1x1 Convolution에는 Spatial(공간적인) 특징을 가지고 있지 않습니다. Convolution 연산이 공간적인 특징을 추출하기 위해선 Kernel이 최소 2 이상 되어야 합니다. 

 

 

 

 

 

3. 특징 추출

 

Input Channel 64 -> Output Channel 64 

 

3x3 Convolution은 특성을 추출하는 역할을 합니다.

3x3 Convolution 연산은 = 3 x 3 x Input Channel x Output Channel 입니다. ( 3 x 3 x 64 x 64 )

 

3x3 Convolution은 1x1 Convolution 보다 9배 연산량이 많기 때문에, 1x1 Convolution에서 채널을 줄인 후에 

3x3 Convolution에서 특성을 추출합니다. 

 

 

 

 

4. Channel Increase( 채널 증가 )

Input Channel 64 -> Output Channel 256

 

CNN은 Feature Map의 특성이 많으면 많을수록 학습이 잘 되기 때문에, 1x1 Convolution으로 강제적으로 채널을 증가시켜줍니다. 

 

BottleNeck의 구조는 1x1 Convolution으로 장난을 치면서 연산량을 최소화하는 것입니다. 

 

하지만 강제로 채널을 줄이고 늘리는 것은 정보 손실을 일으킵니다. 

정보 손실은 모델의 정확성을 떨어뜨립니다. 

 

연산량과 정보손실은 서로 tradeoff 관계이기 때문에 서로의 합의점을 찾는 것이 중요합니다. 

ResNet이 제시한 두 구조를 Pytorch로 구현해 Parameter를 확인하겠습니다. 

 

Standard는 Channel 수가 적을지라도, 3x3 Convolution을 두 번 통과했고, 

BottleNeck은 1x1, 3x3, 1x1 순으로 Convolution을 통과하고, Channel 수는 4배 정도 많지만, Parameter가 세 배 정도 적습니다. 

 

그리고 형성된 것이 도로의 병목 현상과 비슷하다 하여 BottleNeck 구조라고 불립니다.

병목 현상

 

 

cf) Pytorch Code

# standard
class Standard(nn.Module):
    def __init__(self, in_dim=256, mid_dim=64, out_dim=64):
        super(BuildingBlock, self).__init__()
        self.building_block = nn.Sequential(
            nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=out_dim, kernel_size=3, padding=1, bias=False),
        )
        self.relu = nn.ReLU()

    def forward(self, x):
        fx = self.building_block(x)  # F(x)
        out = fx + x  # F(x) + x
        out = self.relu(out)
        return out
# BottleNeck
class BottleNeck(nn.Module):
    def __init__(self, in_dim=256, mid_dim=64, out_dim=256):
        super(BottleNeck, self).__init__()
        self.bottleneck = nn.Sequential(
            nn.Conv2d(in_channels=in_dim, out_channels=mid_dim, kernel_size=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=mid_dim, kernel_size=3, padding=1, bias=False),
            nn.ReLU(),
            nn.Conv2d(in_channels=mid_dim, out_channels=in_dim, kernel_size=1, bias=False),
        )

        self.relu = nn.ReLU()

    def forward(self, x):
        fx = self.bottleneck(x)  # F(x)
        out = fx + x  # F(x) + x
        out = self.relu(out)
        return out
728x90
반응형
반응형

안녕하세요. 

 

제가 평소에 자주 즐겨보는 빵형의 개발도상국에서 재미있는 딥러닝 예제가 있어서 가져 왔습니다.

 

www.youtube.com/watch?v=VxRCku4Bkgg

평소에는 눈으로만 보다가 재밌어 보여서, 실제로 저도 한 번 해보기로 했습니다.

 

댓글을 보니, 많은 사람들이 데이터 프로세싱 부분에서 힘들어 하십니다.

 

저도 따라해보았더니, 학습이 진또배기가 아니라 데이터 전처리 부분이 이 동영상의 꽃이란걸 알게 됐습니다. 

(역시 딥러닝은 데이터가 문제...)

 

 

 

1. 데이터셋 받기

 

www.kaggle.com/jessicali9530/celeba-dataset

 

CelebFaces Attributes (CelebA) Dataset

Over 200k images of celebrities with 40 binary attribute annotations

www.kaggle.com

1GB Face Dataset

하나 주의사항 !

 

컴퓨터 용량은 충분하신가요?

 

실제로 데이터를 받아보면, 1G 바이트 약간 넘습니다. 

 

하지만, 데이터 전처리를 하게 되면

 

지옥을 맛보시게 될겁니다. SSD 빵빵한거나 서버가 없으면 뒤로 돌아가시는걸 추천드립니다.

 

 

 

2. Git에서 코드 받기

 

github.com/kairess/super_resolution

 

kairess/super_resolution

Super resolution with Subpixel CNN using Keras. Contribute to kairess/super_resolution development by creating an account on GitHub.

github.com

 

download
오늘의 주인공

오늘은 preprocess.ipynb 전처리 부분을 봐보겠습니다. 

 

옵션사항입니다. 

 

저는 Pycharm으로 코딩을 할 예정이기 때문에 .ipynb를 .py로 변경했습니다.

 

jupyter nbconvert --to scripy preprocess.ipynb ( 해당 경로에서 )

 

 

 

3. 경로/디렉토리 준비하기

 

빵빵한 SSD를 준비합니다. 

 

 

저는 E 드라이브에 준비하겠습니다.

 

E 드라이브(자신의 드라이브)에 dataset 폴더를 하나 만듭니다.

 

1. E:\dataset 

제 기본 경로가 되겠습니다.

 

dataset 폴더에 이렇게 폴더를 만들어 주겠습니다. 

2. E:\dataset\img

 

 

우선 img 폴더는 학습을 시킬 실제 데이터가 들어가 있습니다. 아까 kaggle에서 받은 img_align_celeba를 옮겨서 이름만 바꿔준 것입니다. 그대로 사용하셔도 상관없습니다. 저는 간단한게 좋아서 img로 바꿨습니다. 

이 img 폴더에 있는 사진들은 학습할 데이터입니다. 간단히 X_data라 부르겠습니다.

 

X_data는 Train용, Validation용, Test용으로 나뉩니다. 

 

3. E:\dataset\list_eval_partion.csv

 

이미지에 각각 라벨링 한 것을 csv파일로 저장되있습니다. 다른 csv 파일은 그냥 휴지통에 던져 줍니다.

 

partition : 1 => train (162770장)

partition : 2 => val (19867장)

partition : 3 => test (19962장)

 

 

4. E:\dataset\x_train

 

이제 데이터를 전처리하게 되면 train, validation, test 용으로 데이터가 나뉘기 때문에 

 

저 이름 그대로 폴더를 만들어 주어야 합니다. 

 

x_train

x_val

x_test

y_train

y_val

y_test

 

 

이 폴더 안에는 학습하기 좋게끔 numpy(.npy) 로 전처리되어 저장될 예정입니다. 

 

굳이 몰라도 되지만, TMI로 scipy나, matplotlib 등 여러 과학, 수학, 딥러닝에서 numpy를 제공하는 이유는

 

numpy가 C코드로 작성된 라이브러리이기 때문에 파이썬 내장 list보다 속도가 훨씬 빠릅니다. 

 

파이썬 개발한 사람이 C 개발자였으니... 

 

 

4. 코드

 

import os, cv2
import numpy as np
from skimage.transform import pyramid_reduce 


# 경로
base_path = r'E:\dataset'  # E:\dataset
img_path = os.path.join(base_path, 'img')  # E:\dataset\img

eval_list = np.loadtxt(os.path.join(base_path, 'list_eval_partition.csv'), dtype=str, delimiter=',', skiprows=1)
print(eval_list[0])


# 이미지 확인
img_sample = cv2.imread(os.path.join(img_path, eval_list[0][0]))
print(os.path.join(img_path, eval_list[0][0]))
h, w, _ = img_sample.shape


# 이미지 전처리 
crop_sample = img_sample[int((h-w)/2):int(-(h-w)/2), :]
resized_sample = pyramid_reduce(crop_sample, downscale=4)

pad = int((crop_sample.shape[0] - resized_sample.shape[0]) / 2)

padded_sample = cv2.copyMakeBorder(resized_sample, top=pad, bottom=pad, left=pad, right=pad, borderType=cv2.BORDER_CONSTANT, value=(0,0,0))

print(crop_sample.shape, padded_sample.shape)


# main
downscale = 4
n_train = 162770
n_val = 19867
n_test = 19962

for i, e in enumerate(eval_list):
    filename, ext = os.path.splitext(e[0])
    
    img_path = os.path.join(img_path, e[0])
    
    img = cv2.imread(img_path)
    
    h, w, _ = img.shape
    
    crop = img[int((h-w)/2):int(-(h-w)/2), :]
    crop = cv2.resize(crop, dsize=(176, 176))
    resized = pyramid_reduce(crop, downscale=downscale)

    norm = cv2.normalize(crop.astype(np.float64), None, 0, 1, cv2.NORM_MINMAX)
    
    if int(e[1]) == 0:
        np.save(os.path.join(base_path, 'x_train', filename + '.npy'), resized)  
        np.save(os.path.join(base_path, 'y_train', filename + '.npy'), norm)  
    elif int(e[1]) == 1:
        np.save(os.path.join(base_path, 'x_val', filename + '.npy'), resized)
        np.save(os.path.join(base_path, 'y_val', filename + '.npy'), norm)
    elif int(e[1]) == 2:
        np.save(os.path.join(base_path, 'x_test', filename + '.npy'), resized)
        np.save(os.path.join(base_path, 'y_test', filename + '.npy'), norm)

 

 

 

5. 오류

 

아마 유튜브를 보고 따라하시는 분들 중에 대부분이 경로 문제입니다. 

 

 

----------------------------------------------------------------------------------------------------------------------------

 

첫 번째 오류 

대표적으로 이 분이  경로 문제로 막히신겁니다. 

 

Git에서 코드를 받으실 때 DataGenerator.py 도 같이 받으십니다. 

DataGenerator는 x_train 등 전처리한 .npy 파일을 불러와서 Batch로 묶고 shuffle 하여 데이터셋을 만드는 코드입니다.

 

하지만 에러문을 보게 되면, splited = ID.split('/') 에서 오류가 발생합니다. 

 

ID.split('/')는 불러온 경로를 '/' 기준으로 나누는 것입니다. 

 

예를 들어 

 

'E:/dataset/img' 가 있습니다.

 

'E:/dataset/img'.split('/')을 하게 되면 ["E:", "dataset", "img"]로 나뉩니다. 

 

눈치 빠르신 분이 있을까요??

 

제 코드에서 기본 경로는  r'E:\dataset' 입니다. 

 

r'E:\dataset'.split('/")는 어떻게 될까요?

 

결과는 ["r'E:\dataset'.split('/")"] 입니다. 우리들은 기본 경로를 절대경로로 표시했습니다.

 

"\"는 "/" 아닙니다!!!!!  우리는 DataGenerator.py에 들어가서 splited = ID.split('/')를 

 

splited = ID.split('\')로 변경해주면 해결됩니다.

 

ID가 왜 경로죠? 라고 궁금해하는 분이 계실 수도 있습니다. 

 

DataGenerator.py 45줄

__data_generation 함수에서 list_IDS_temp라는 파라미터를 받는 것을 알 수 있습니다. 

 

list_IDS_temp가 무엇인지 확인하겠습니다. 

DataGenerator.py 34줄

list_IDs에서 받아온거네요. list_IDs가 무엇인지 역추적해보겠습니다. 

 

DataGenerator.py 9줄

아하 DataGenerator에서 생성자에서 파라미터로 받는 것을 확인했습니다. 맨 처음 파라미터니까 찾기 쉽겠네요.

 

아마 데이터를 생성하는 클래스이기 때문에 train.py에서 사용했을겁니다. 

 

train.py

네. import 한 것을 바로 확인할 수 있습니다. 

 

 

x_train_list = list_IDs 라는 것을 역추적해 알 수 있게 되었습니다. 

 

위 댓글 분의 오류의 원인은 경로 문제라는 것을 제대로 확인할 수 있습니다. 

 

 

 

----------------------------------------------------------------------------------------------------------------------------

 

 

두번 째 오류

'NoneType' object has no attribute 'shape'

 

shape라는 속성이 없다라는 오류입니다. 

 

아마 shape를 쓴 걸로 보아 NoneType 자리에는 Numpy가 들어갈 수 있다는걸 추측할 수 있습니다. 

 

그런데 NoneType 이라는 것은 해당 경로에 .npy를 불러 왔지만 경로가 잘못되어 아무 것도 불러오지 못하였다고 말할 수 있습니다. 

 

 

----------------------------------------------------------------------------------------------------------------------------

 

 

이것도 경로 문제입니다. 

 

에러문에 나와있네요.

 

No such file or directory  파일이나 폴더를 찾을 수 없다...!

 

아마 경로를 확인해보니 첫 번째 오류와 비슷한 것 같습니다. 

 

경로구분이 "\\"으로 되있는데 split은 "/" 이니 제대로 분리가 되지 않았을겁니다. 

 

np.load( 경로 ) 에서 막힌 듯 싶습니다. !! 

 

 

----------------------------------------------------------------------------------------------------------------------------

 

이건 제가 올린 답글... 

 

도움이 되셨으면 좋겠습니다... 글을 마치도록 하겠습니다아아... 빵형 개발도상국님 항상 재밌게 잘 보고 있습니다!

728x90
반응형

+ Recent posts