빅데이터 | 머신러닝 | 딥러닝/딥러닝

[딥러닝 기초] 다층 신경망을 통해 의류분류기 만들어보기

냠냠:) 2020. 5. 12. 03:40

[이 글은 "Do it 딥러닝 입문" 책을 보고 공부한 내용을 복습하고자 정리한 글입니다.]

 

bincount : 배열에 있는 정수값의 등장 횟수를 세어 정수값에 해당하는 인덱스 위치에 저장

 

reshape : 사진의 정보는 2차원으로 되어있기 때문에 (-1, "차원을 합친 길이")로 변환

 

to_categorical : 클래스의 구별을 위해 정수값으로 되있는 값들을 원-핫 인코딩 방식으로 변환

 

 

MultiClassNetwork_2020_5_8
In [1]:
class MultiClassNetwork:

    def __init__(self, units=10, batch_size=32, learning_rate =0.1, l1=0,l2=0):
        self.units = units       #은닉층의 뉴런개수
        self.batch_size = batch_size
        self.w1 = None
        self.b1 = None
        self.w2 = None
        self.b2 = None
        self.a1 = None          #은닉층의 활성화 출력
        self.losses = []
        self.val_losses = []    #검증 손실
        self.lr = learning_rate
        self.l1 = l1
        self.l2 = l2
        
    def forpass(self, x):
        z1 = np.dot(x, self.w1) + self.b1
        self.a1 = self.sigmoid(z1)
        z2 = np.dot(self.a1,self.w2) + self.b2
        return z2
    
    def backprop(self, x, err):
        m = len(x)              #샘플 개수
        #출력층의 가중치와 절편에 대한 그레이디언트를 계산합니다.
        w2_grad = np.dot(self.a1.T, err) / m
        b2_grad = np.sum(err) / m
        #시그모이드 함수까지 그레이디언트를 계산합니다.
        err_to_hidden = np.dot(err, self.w2.T) * self.a1 * (1 - self.a1)
        w1_grad = np.dot(x.T, err_to_hidden) / m
        b1_grad = np.sum(err_to_hidden, axis=0) / m
        return w1_grad, b1_grad, w2_grad, b2_grad
    
    def sigmoid(self, z):
        a = 1 / (1 + np.exp(-z))
        return a
    
    def softmax(self, z):
        exp_z = np.exp(z)
        return exp_z / np.sum(exp_z, axis = 1).reshape(-1,1)
    
    def init_weigths(self, n_features, n_classes):
        self.w1 = np.random.normal(0,1,(n_features, self.units))  #(특성 개수, 은닉층 크기)XW
        self.b1 = np.zeros(self.units)
        
        self.w2 = np.random.normal(0,1,(self.units , n_classes))  #(은닉층 크기, 클래스 개수)
        self.b2 = np.zeros(n_classes)
        
    def fit(self, x, y, epochs=100, x_val= None, y_val=None):
        np.random.seed(42)
        self.init_weigths(x.shape[1], y.shape[1])     #은닉층과 출력층의 가중치를 초기화.
        #epochs만큼 반복
        for i in range(epochs):
            loss = 0
            print('.', end='')
            for x_batch, y_batch, in self.gen_batch(x,y):
                a = self.training(x_batch, y_batch)
                a = np.clip(a, 1e-10, 1-1e-10)
                #로그손실과 규제 손실을 더하여 리스트에 추가함.
                loss += np.sum(-y_batch*np.log(a))
            self.losses.append((loss + self.reg_loss()) / len(x))
            self.update_val_loss(x_val, y_val)
        
    #미니 배치 제너레이터
    def gen_batch(self,x,y):
        length = len(x)
        bins = length // self.batch_size  #미니 배치 회수
        if length % self.batch_size:      #나누어 떨어지지 않을 때 
            bins += 1
        indexes = np.random.permutation(np.arange(len(x)))  # 인덱스를 섞음
        x = x[indexes]
        y = y[indexes]
        for i in range(bins):
            start = self.batch_size * i
            end = self.batch_size * (i + 1)
            yield x[start:end], y[start:end]    #batch_Size만큼 슬라이싱하여 반환.
            
    def training(self, x, y):
        m = len(x)
        z = self.forpass(x)
        a = self.softmax(z)
        err=-(y - a)                #오차 계산
        #오차를 역전파하여 그레이디언트를 계산.
        w1_grad, b1_grad, w2_grad, b2_grad = self.backprop(x, err)
        #그레이디언트에서 패널티 항의 미분값을 뺌.
        w1_grad += (self.l1 * np.sign(self.w1) + self.l2 * self.w1) / m
        w2_grad += (self.l1 * np.sign(self.w2) + self.l2 * self.w2) / m
        
        #은닉층의 가중치와 절편 업데이트
        self.w1 -= self.lr * w1_grad
        self.w2 -= self.lr * w2_grad
        
        self.b1 -= self.lr * b1_grad
        self.b2 -= self.lr * b2_grad
        
        return a
    
    def predict(self, x):
        z= self.forpass(x)
        return np.argmax(z,axis=1) #가장 큰 값의 인덱스를 반환
    
    def score(self,x,y):
        #예측과 타깃 열 벡터를 비교하여 True의 비율을 반환
        return np.mean(self.predict(x) == np.argmax(y, axis=1))
        
    def reg_loss(self):
        return self.l1 * (np.sum(np.abs(self.w1)) + np.sum(np.abs(self.w2))) + \
                self.l2 / 2 * (np.sum(self.w1**2) + np.sum(self.w2**2))
    
    def update_val_loss(self, x_val, y_val):
        z = self.forpass(x_val)
        a = self.softmax(z)
        a = np.clip(a, 1e-10, 1-1e-10)
        val_loss = np.sum(-y_val*np.log(a))
        #크로스 엔트로피 손실과 규제 손실을 더해 리스트에 추가
        self.val_losses.append((val_loss+ self.reg_loss())/len(y_val))
        
        
                            
In [2]:
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
tf.__version__
Out[2]:
'2.0.0-beta1'
In [3]:
(x_train_all, y_train_all), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()
print(x_train_all.shape, y_train_all.shape)
(60000, 28, 28) (60000,)
In [4]:
plt.imshow(x_train_all[0], cmap='gray')
plt.show()
In [5]:
#y값 확인해보기
print(y_train_all[:10])
[9 0 0 3 0 2 7 2 5 5]
In [6]:
print(y_train_all[0]) # 9 == 앵글 부츠 
9
In [7]:
#bincount = 배열에 있는 정수값의 등장 횟수를 세어 정수값에 해당하는 인덱스 위치에 저장 
np.bincount(y_train_all)
Out[7]:
array([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000],
      dtype=int64)
In [8]:
#훈련 세트와 검증 세트로 나누기
from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val = train_test_split(x_train_all,y_train_all, stratify =y_train_all,
                                                  test_size =0.2, random_state =42)
np.bincount(y_train) # 8:2로 나뉨
Out[8]:
array([4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800],
      dtype=int64)
In [9]:
# 0~1의 값을 갖게 하려고
x_train = x_train / 255
x_val = x_val / 255

#reshape으로 훈련세트와 검증세트를 2,3번째 차원을 합친 길이로 바꿈
x_train = x_train.reshape(-1, 784)  #28 * 28
x_val = x_val.reshape(-1, 784)
print(x_train.shape, x_val.shape)
(48000, 784) (12000, 784)
In [10]:
# 타깃 데이터를 원-핫 인코딩으로 변환
# 예시
tf.keras.utils.to_categorical([0,1,3])
Out[10]:
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 0., 1.]], dtype=float32)
In [11]:
#원-핫 인코딩
y_train_encoded = tf.keras.utils.to_categorical(y_train)
y_val_encoded = tf.keras.utils.to_categorical(y_val)
print(y_train_encoded.shape, y_val_encoded.shape)
(48000, 10) (12000, 10)
In [12]:
fc = MultiClassNetwork(units=100, batch_size=256)
fc.fit(x_train,y_train_encoded,x_val=x_val, y_val= y_val_encoded, epochs=40)
........................................
In [13]:
plt.plot(fc.losses)
plt.plot(fc.val_losses)
plt.legend(['train_loss', 'val_loss'])
Out[13]:
<matplotlib.legend.Legend at 0x14bcf951a08>
In [14]:
#정확도
fc.score(x_val,y_val_encoded)
Out[14]:
0.8150833333333334
In [15]:
#홀로 해보기
class_names =['티셔츠/윗도리','바지', '스웨터','드레스','코트','샌들','셔츠','스니커즈','가방','앵글부츠']
(x_train_all, y_train_all), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

plt.imshow(x_train_all[0], cmap='gray')
plt.show()
test = x_train_all[0].reshape(1,784)
print(fc.predict(test))
[9]
C:\Users\USER\Anaconda3\lib\site-packages\ipykernel_launcher.py:35: RuntimeWarning: overflow encountered in exp
In [17]:
image = tf.keras.preprocessing.image
img = image.load_img('C:\jupyterNotebook\DeepLearning of DoIT\s.jpg', target_size=(28,28))
plt.imshow(img)
Out[17]:
<matplotlib.image.AxesImage at 0x14bc97fb088>
In [18]:
img_tensor = image.img_to_array(img)# 이미지 배열화
a = np.resize(img_tensor,(1,28,28)) #(1,28,28)
a = a / 255                         #0~1의 값
a = np.resize(a,(1, 784))           #벡터화
fc.predict(a)                       #예측
Out[18]:
array([2], dtype=int64)
In [19]:
print(class_names[fc.predict(a)[0]])
스웨터
In [20]:
img2 = image.load_img('C:/jupyterNotebook/DeepLearning of DoIT/a.jpg',target_size=(28,28))
plt.imshow(img2)
Out[20]:
<matplotlib.image.AxesImage at 0x14bc9860248>
In [22]:
img_tensor2 = image.img_to_array(img2)
a = np.resize(img_tensor2,(1,28,28))
a = a/255
a = np.resize(a,(1,784))
fc.predict(a)
print(class_names[fc.predict(a)[0]])
가방

만든 의류분류기로 내가 지정한 사진이 무엇인지 분류해봤다. 셔츠를 가방으로 인식하는게 조금 아쉽지만 다음 모델은 좀 더 정교하게 만들어 제대로 분류할 수 있게 만들어 봐야겠다. 스스로 해보는 과정이 얼마나 중요한지 다시 상기하게 됐고 배울것이 아직 너무 많다라는 것도 느끼게 되었다.

반응형