밑바닥부터 시작하는 딥러닝1 - Chap3. 신경망

2024. 11. 20. 09:49·공부/밑바닥부터 시작하는 딥러닝

퍼셉트론은 복잡한 함수도 표현할 수 있다.

다만 가중치를 설정하는 작업(원하는 결과를 출력하도록 가중치 값을 정하는 작업)은 여전히 사람이 수동으로 한다.

 

신경망은 가중치 매개변수의 적절한 값을 데이터로부터 자동으로 학습하는 능력이 있다.

 

 

신경망을 그림으로 나타낸 것이다. 은닉층의 뉴런은 입력층이나 출력층과 달리 사람 눈에는 보이지 않는다.

0층이 입력층, 1층이 은닉층, 2층이 출력층이 된다.  

※ 3층으로 구성된 신경망이지만, 가중치를 갖는 층은 2개이기 때문에 2층 신경망이라고도 한다.

 

 

 

 

편향 b가 포함된 퍼셉트론 수식

편향을 명시한 퍼셉트론은 이렇게 나타낼 수 있다.

편향을 명시한 퍼셉트론

가중치가 b이고 입력이 1인 뉴런이 추가되었다. x1,x2,1이라는 3개의 신호가 뉴런에 입력되어 각 신호에 가중치를 곱한 후, 다음 뉴런에 전달한다. 

위의 식을 좀 더 간결한 형태로 작성할 수 있다.

입력 신호의 총합이 h(x)라는 함수를 거쳐 변환되어, 그 변환된 값이 y의 출력이 됨을 보여준다. 

 

 

 

활성화 함수

h(x)처럼 입력 신호의 총합을 출력 신호로 변환하는 함수를 활성화 함수라고 한다.  

가중치가 곱해진 입력 신호의 총합을 계산하고, 그 합을 활성화 함수에 입력해 결과를 내는 2단계로 처리된다. 

가중치가 달린 입력 신호와 편향의 총합을 계산하고, 이를 a라 한다. a를  함수 h()에 넣어  y를 출력하는 흐름이다.

 

활성화 함수의 처리 과정

기존 뉴런의 원을 키우고, 그 안에 활성화 함수의 처리 과정을 명시적으로 그려 넣었다. 

가중치 신호를 조합한 결과가 a라는 노드가 되고, 활성화 함수 h()를 통과하여 y 노드로 변환되는 과정이 분명하게 나타나 있다.(뉴런=노드)

 

 

 

활성화 함수는 임계값을 경계로 출력이 바뀌는데, 이런 함수를 계단 함수(step function)이라고 한다. 

그래서 퍼셉트론에서는 활성화 함수로 계단 함수를 이용한다 라고 할 수 있다.

 

이외의 함수 종류에는, 신경망에서 자주 이용하는 시그모이드 함수(sigmoid function)가 있다.

exp(-x) 는 e의 -x승을 의미하며, e는 자연상수이다.

복잡해 보이지만, 이 역시 단순한 함수이다. h(1.0) = 0.731... , h(2.0) = 0.880... 처럼 특정 값을 출력한다.

 

신경망에서는 활성화 함수로 시그모이드 함수를 이용하여 신호를 변환하고, 그 변환된 신호를 다음 뉴런에 전달한다. 

퍼셉트론과 앞으로 볼 신경망의 주된 차이는 이 활성화 함수뿐이라고 한다.

 

계단 함수 구현하기

import math as m
import numpy as np
import pandas as pd
import matplotlib.pylab as plt


def step_function(x):
    if x>0:
        return 1
    else:
        return 0
    
def step_function2(x):
    y = x>0
    return y.astype(np.int64)

x = np.array([-1.0,1.0,2.0])
y = x>0
print(y)

y = y.astype(np.int64)
print(y)

 

실행결과를 보면 x,y부등식을 이용한 결과에 따라 Boolean형의 배열 y가 생성되었다. 

astype() 메서드를 활용하여 이를 int형으로 변환한 것이다.

 

 

계단 함수의 그래프

 

def step_function(x):
    return np.array(x >0,dtype=np.int64)
    
x = np.arange(-5.0,5.0,0.1 )
y = step_function(x)
plt.plot(x,y)
plt.ylim(-0.1, 1.1) #y축의 범위 지정
plt.show()

 

출력 결과

 

 

 

 

시그모이드 함수 구현하기

def sigmoid(x):
    return 1/(1+np.exp(-x))

시그모이드 함수는 파이썬으로 이렇게 작성할 수 있다. 

(Numpy의 브로드캐스트: numpy 배열과 스칼라값의 연산을 numpy 배열의 원소 각각과 스칼라값의 연산으로 바꿔 수행하는 것)

 

x = np.arange(-5.0,5.0,0.1 )
y = sigmoid(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()

 

시그모이드 함수의 그래프

Sigmoid란 'S자 모양'이라는 뜻이다. 계단 함수처럼 모양을 따 이름 지은 것이다. 

 

 

 

 

계단함수와 시그모이드 함수

계단함수와 시그모이드의 함수는 비선형 함수이다. 

신경망에서는 활성화 함수로 비선형 함수를 사용해야 한다. 선형 함수를 이용하면 신경망의 층을 깊게 하는 의미가 없다.

 

 

 

 

ReLU함수(Rectified Linear Unit) :입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하이면 0을 출력하는 함수이다.

ReLU 함수의 그래프

간단한 수식으로 표현 할 수 있다. 

 

def relu(x):
    return np.maximum(0,x)

numpy의 maximum 함수를 이용해, 두 입력 중 큰 값을 선택해 반환한다.

 

 

 

 

 

 

다차원 배열의 계산

A = np.array([1,2,3,4]) #[1 2 3 4]
print(np.ndim(A))       #1
print(A.shape)          #(4, )
print(A.shape[0])       #4

배열의 차원 수는 np.ndim() 함수를 이용해 확인할 수 있다.

 

 

 

2차원 matrix의 코드이다.

B = np.array([[1,2],[3,4],[5,6]])
print(B)
# =============================================================================
# [[1 2]
#  [3 4]
#  [5 6]]
# =============================================================================
np.ndim(B)  #2
B.shape     #(3, 2)

 

 

 

행렬의 곱은 np.dot() 함수를 사용한다.

np.dot(A,B) ≠ np.dot(B,A) 

 

이와 같이 행렬의 곱에서는 대응하는 차원의 원소 수를 일치시켜야 한다. 

A가 2차원 matrix, B가 1차원 matrix의 경우에도 대으아는 차원의 수를 일치시켜야 한다.

 

 

 

 

 

 

신경망에서의 행렬 곱

X =np.array([1,2])
X.shape     #(2, )
W = np.array([[1,3,5],[2,4,6]])
print(W)
# [[1 3 5]
#  [2 4 6]]
W.shape     #(2,3)
Y = np.dot(X,W)
print(Y)    #[5 11 17]

 

 

 

 

 

 

 

3층 신경망 구현하기

3층 신경망: 입력층(0층) 2개, 첫번째 은닉층(1층) 3개, 두번째 은닉층(2층) 2개, 출력층(3층) 2개의 뉴런으로 구성된다.

 

표기법 설명

 

 

 

 

입력층에서 "1층의 첫번째 뉴런"으로 가는 신호 

편향 뉴런(b)인 1이 추가되었다. 이를 수식화 해본다면

이렇게 계산할 수 있다.  행렬의 곱을 이용하면 1층의 '가중치 부분'을 다음 식처럼 간소화할 수 있다.

이때 행렬 A,X,B,W는 각각 다음과 같다.

 

X = np.array([1.0,0.5])
W1 = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
B1 = np.array([0.1,0.2,0.3])

print(W1.shape) #(2, 3)
print(X.shape)  #(2, )
print(B1.shape) #(3, )

A1 = np.dot(X,W1) + B1

W1은 2X3 matrix, X는 원소가 2개인 1차원 matrix이다. W1과 X의 대응하는 차원의 원소 수가 일치한다.

 

 

 

활성화 함수에서의 처리는

입력층에서 1층으로의 신호 전달

 

은닉층에서의 가중치 합(가중 신호와 편향의 총합)을 a로 표기하고 활성화 함수 h()로 변환된 신호를 z로 표기한다. 

이를 통해 1층에서 2층으로 가는 과정은 다음과 같다.

1층에서 2층으로의 신호 전달

 

 

 

마지막으로 2층에서 출력층으로의 신호 전달이다.

2층에서 출력층으로의 신호 전달

h(x)와 출력층의 활성화 함수 시그마는 다르다.

def identity_function(x):
    return x

W3 = np.array([[0.1,0.3],[0.2,0.4]])
B3 = np.array([0.1,0.2])

A3 = np.dot(Z2,W3) + B3
Y = identity_function(A3) #or Y = A3

 

 

 

구현 정리

 

def identity_function(x):
    return x

def init_network():
    network = {}
    network['W1'] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
    network['b1'] = np.array([0.1,0.2,0.3])
    network['W2'] = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
    network['b2'] = np.array([0.1,0.2])
    network['W3'] = np.array([[0.1,0.3],[0.2,0.4]])
    network['b3'] = np.array([0.1,0.2])

    return network

def forward(network,x):
    W1,W2,W3 = network['W1'], network['W2'], network['W3']
    b1,b2,b3 = network['b1'], network['b2'], network['b3']
    
    a1 = np.dot(x,W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1,W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2,W3) + b3
    y = identity_function(a3)
    
    return y

network = init_network()
x = np.array([1.0,0.5])
y = forward(network, x)
print(y) #[0.31682708 0.69627909]

함수 이름이 forward인 이유는 신호가 순방향으로 전달됨(순전파)을 알리기 위함이다.

역방향(backward) 처리는 추후 내용에 나와있다고 한다.

 

 

 

출력층 설계하기

신경망은 분류와 회귀 모두에 이용할 수 있다. 

일반적으로 회귀에는 항등 함수를, 분류에는 소프트맥스 함수를 사용한다. 


 

항등 함수와 소프트맥스 함수 구현하기

항등 함수(identity function)은 입력을 그대로 출력한다. 입력과 출력이 항상 같다.

항등 함수에 의한 변환은 은닉층에서의 활성화 함수와 마찬가지로 화살표로 그린다.

항등함수

 

 

 

소프트맥스 함수(softmax function)의 식은 다음과 같다.

exp(x) 는 e^x 지수함수이다. 

n은 출력층의 뉴런 수, y는 그중 k번째 출력을 뜻한다. 

소프트맥스 함수의 분자는 입력 신호  ak의 지수 함수, 분모는 모든 입력 신호의 지수 함수의 합으로 구성된다.

소프트맥스 함수

 

 

def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

코드로 구현한 소프트맥스 함수이다.

컴퓨터로 사용할 땐 overflow의 결함이 있다. 

지수함수는 매우 큰 값을 가지고, 이 큰값끼리 나눗셈을 하면 수치가 불안정해진다.

 

이 문제를 해결하도록 하는 수식을 보자.

첫 번째 변형에서는 C라는 임의의 정수를 분자와 분모 양쪽에 곱했다.
이후 C를 지수함수 exp()안으로 옮겨 logC를 만든다. 

마지막으로 logC를 C`라는 새로운 기호로 바꾼다.

 

overflow를 막을 목적으로는 입력 신호 중 최댓값을 이용한은 것이 일반적이다.

def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c) #prevent overflow
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

입력 신호 중 최댓값(c)을 빼주면 올바르게 계산할 수 있다.

 

 

 

softmax()의 출력은 0에서 1.0 사이의 실수이다. 또한 출력의 총합은 1이다. 

출력 총합이 1이 된다는 점은 중요한 성질이다. 이 성질 덕분에 '확률'로 해석할 수 있기 때문이다.

신경망을 이용한 분류에서는 일반적으로 가장 큰 출력을 내는 뉴런에 해당하는 클래스로만 인식한다. 

그리고 softmax를 적용해도 출력이 가장 큰 뉴런의 위치는 달라지지 않는다. 결과적으로 신경망으로 분류할 때는 출력층의 softmax 함수를 생략해도 된다. 

현업에서도 지수함수 계산에 드는 자원낭비를 줄이고자 출력층의 softmax는 생략하는것이 일반적이다.

 

 

출력층의 뉴런 수 정하기

출력층의 뉴런 수는 풀려는 문제에 맞게 적절히 정해야 한다. 

분류에서는 분류하고 싶은 클래수 수로 설정하는 것이 일반적이다. 

예를 들어 입력 이미지를 숫자 0~9로 분류하는 문제라면  출력층의 뉴런을 10개로 설정한다.

신경망의 순전파(forard propagation):추론 과정


MNIST 데이터셋

훈련 이미지 60000장, 시험 이미지 10000장으로 구성된 MNIST 데이터셋은 이 훈련 이미지들을 사용하여 모델을 학습하고, 학습한 모델로 시험 이미지들을 얼마나 정확하게 분류하는지를 평가한다. 

 

 

mnist.py

# coding: utf-8
try:
    import urllib.request
except ImportError:
    raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np


url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
    'train_img':'train-images-idx3-ubyte.gz',
    'train_label':'train-labels-idx1-ubyte.gz',
    'test_img':'t10k-images-idx3-ubyte.gz',
    'test_label':'t10k-labels-idx1-ubyte.gz'
}

dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"

train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784


def _download(file_name):
    file_path = dataset_dir + "/" + file_name
    
    if os.path.exists(file_path):
        return

    print("Downloading " + file_name + " ... ")
    urllib.request.urlretrieve(url_base + file_name, file_path)
    print("Done")
    
def download_mnist():
    for v in key_file.values():
       _download(v)
        
def _load_label(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")
    with gzip.open(file_path, 'rb') as f:
            labels = np.frombuffer(f.read(), np.uint8, offset=8)
    print("Done")
    
    return labels

def _load_img(file_name):
    file_path = dataset_dir + "/" + file_name
    
    print("Converting " + file_name + " to NumPy Array ...")    
    with gzip.open(file_path, 'rb') as f:
            data = np.frombuffer(f.read(), np.uint8, offset=16)
    data = data.reshape(-1, img_size)
    print("Done")
    
    return data
    
def _convert_numpy():
    dataset = {}
    dataset['train_img'] =  _load_img(key_file['train_img'])
    dataset['train_label'] = _load_label(key_file['train_label'])    
    dataset['test_img'] = _load_img(key_file['test_img'])
    dataset['test_label'] = _load_label(key_file['test_label'])
    
    return dataset

def init_mnist():
    download_mnist()
    dataset = _convert_numpy()
    print("Creating pickle file ...")
    with open(save_file, 'wb') as f:
        pickle.dump(dataset, f, -1)
    print("Done!")

def _change_ont_hot_label(X):
    T = np.zeros((X.size, 10))
    for idx, row in enumerate(T):
        row[X[idx]] = 1
        
    return T
    

def load_mnist(normalize=True, flatten=True, one_hot_label=False):
    """MNIST 데이터셋 읽기
    
    Parameters
    ----------
    normalize : 이미지의 픽셀 값을 0.0~1.0 사이의 값으로 정규화할지 정한다.
    one_hot_label : 
        one_hot_label이 True면、레이블을 원-핫(one-hot) 배열로 돌려준다.
        one-hot 배열은 예를 들어 [0,0,1,0,0,0,0,0,0,0]처럼 한 원소만 1인 배열이다.
    flatten : 입력 이미지를 1차원 배열로 만들지를 정한다. 
    
    Returns
    -------
    (훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블)
    """
    if not os.path.exists(save_file):
        init_mnist()
        
    with open(save_file, 'rb') as f:
        dataset = pickle.load(f)
    
    if normalize:
        for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].astype(np.float32)
            dataset[key] /= 255.0
            
    if one_hot_label:
        dataset['train_label'] = _change_ont_hot_label(dataset['train_label'])
        dataset['test_label'] = _change_ont_hot_label(dataset['test_label'])    
    
    if not flatten:
         for key in ('train_img', 'test_img'):
            dataset[key] = dataset[key].reshape(-1, 1, 28, 28)

    return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 


if __name__ == '__main__':
    init_mnist()

후반부 있는 load_mnist() 함수를 통해 dataset에 있는 이미지 데이터들을 가져올 수 있다. 

 

load_mnist()는 읽은 MNIST 데이터를 "(훈련 이미지, 훈련 레이블), (시험 이미지, 시험 레이블)" 형식으로 반환한다.

인수로는 normalize, flatten, one_hot_label(모두 boolean)을 설정할 수 있다.

  • normalize: 입력 이미지의 픽셀 값을 0.0~1.0 사이의 값으로 정규화 할 것인지 (False는 입력 이미지를 원래 값 그대로 0~255 값 유지)
  • flatten: 입력 이미지를 평탄하게, 1차원 matrix로 만들 것인지 (False= 1x28x28 3차원 matrix, True= 784개의 원소로 이루어진 1차월 matrix)
  • one_hot_label: 레이블을 원-핫 인코딩 형태로 저장할 것인지 ( [0,0,1,0,0,0,0,0,0,0]처럼 정답을 뜻하는 원소만 1이고(hot하고) 나머지는 모두 0인 배열이다. False면 '7'이나 '2'와 같이 숫자 형태의 레이블을 저장한다. True일시 레이블을 원-핫 인코딩하여 저장한다.)

※ pickle: Python에 있는 기능으로, 프로그램 실행 중에 특정 개체를 파일로 저장하는 기능이다. 저장해둔 pickle 파일을 로드하면 실행 당시의 객체를 즉시 복원할 수 있다. 

※ PIL(Python Lmage Library): 이미지 표시 모듈

 

 

 

mnist_show.py

import numpy as np
import sys, os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
from PIL import Image

# 3.6.1 MNIST 이미지 확인해보기


def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()


(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True,
    normalize=False)

img = x_train[0]
label = t_train[0]
print(label)  # 5
print(img.shape)  # (784,)
img = img.reshape(28, 28)  # 원래 이미지 모양으로 변형
print(img.shape)  # (28, 28)

img_show(img)

mnist_show.py 실행화면

flatten = True 로 설정되어 읽은 이미지는 1차원 numpy matrix로 저장되어 있다. 

이미지를 표시할 때는 다시 28x28 크기로 변형해야 한다. 

reshape() 메서드에 원하는 형상을 인수로 지정하면 넘파이 배열의 형상을 바꿀 수 있다. 

numpy로 저장된 이미지 데이터를 PIL용 데이터 객체로 변환해야 하며, Image.fromarray()가 수행한다.
 

 

 

신경망의 추론 처리

신경망은 입력층 뉴런을 784개, 출력층 뉴런을 10개로 구성한다. 입력층 뉴런은 이미지 크기가 28*28=784, 출력층 뉴런은 0~9까지의 숫자이기 때문이다. 

은닉층은 총 2개로, 첫번째 은닉층에는 50개의 뉴런을, 두번째 은닉층에는 100개의 뉴런을 배치할 것이다.(임의로 정한 값)

 

reuralnet_mnist.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax


def get_data():
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test


def init_network():
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network


def predict(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)

    return y


x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

init_network()에서는 pickle파일에 저장된 '학습된 가중치 매개변수'를 읽어온다. 이 파일에는 가중치와 편향 매개변수가 딕셔너리 변수로 저장되어 있다. 


 

x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

정확도(분류가 얼마나 올바른가) 평가 코드이다. 

x에 저장된 이미지 데이터를 1장씩 꺼내 predict()을 통해 분류한다. 

predict()는 각 레이블의 확률을 numpy matrix로 반환한다. 예를들어 [0.1, 0.3, 0.2,..., 0.04] 같은 matrix가 반환됐다며면, 이는 이미지가 숫자 '0'일 확률이 0.1, '1'일 확률이 0.3 .... 이런식으로 해석한다.

np.argmax()를 통해 matrix에서 값이 가장 큰 (확률이 가장 높은) 원소의 index를 구한다.

이것이 예측 결과이다.

이를 통해 신경망이 예측한 답변과 정답 레이블을 비교하여 맞힌 숫자(accuracy_cnt)를 카운트하고, 이를 전체 이미지 숫자로 나눠 정확도를 구한다.

 

이 코드의 실행 결과는 "Accuracy:0.9353"이다. 정확도가 93.52%라는 것이다. 

load_mnist 인수인 normalize를 True로 설정했다. 0~255범위의 각 픽셀의 값을 0.0~1.0 범위로 변환했다.

이처럼 데이터를 특정 범위로 변환하는 처리를 정규화(normalization)라고 하고, 신경망의 입력 데이터에 특정 변환을 가하는 것을 전처리(pre-processing)라 한다. 

여기서는 입력 이미지 데이터에 대한 전처리 작업으로 정규화를 수행한 것이다.

 

 

 

 

배치 처리

신경망 각 층의 배열 형상의 추이

다차원 배열의 대응하는 차원의 원소 수가 일치함을 확인할 수 있다.(편향은 생략)

전체적으로 보면 원소 784개로 구성된 1차원 matrix(28x28 2차원 matrix)가 입력되어 마지막에는 원소가 10개인 1차원 matrix가 출력되는 흐름이다. 이는 이미지 데이터를 1장만 입력했을 때의 처리 흐름이다.

 

이미지 100장을 한꺼번에 입력하는 경우라면, predict()에 이미지 100개를 묶어 한번에 넘기는 것이다.

x의 형상을 100x784로 바꿔서 100장 분량의 데이터를 하나의 입력 데이터로 표현하면 된다.

 

배치 처리를 위한 배열들의 형상 추이

입력 데이터의 형상은 100x784, 출력 데이터의 형상은 100x10이 된다. 이는 100장 분량 입력 데이터의 결과가 한번에 출력됨을 나타낸다. 

x[0]과 y[0]에는 0번째 이미지가, x[1]과 y[1]에는 첫번째의 이미지와 그 결과가 저장되는 식이다. 

 

이처럼 하나로 묶은 입력 데이터를 배치(batch)라고 한다. 

 

 

x, t = get_data()
network = init_network()

batch_size = 100 # 배치 크기
accuracy_cnt = 0

for i in range(0, len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p = np.argmax(y_batch, axis=1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

range함수에서 0부터 x(데이터의 개수)까지  batch_size(배치 크기 100)으로 입력 데이터를 묶는 과정이다.

x[0:100], x[100:200], x[200:300], ... 과 같이 앞에서부터 100장씩 묶어 꺼내게 된다. 

 

numpy의 argmax()를 통해 최댓값의 인덱스를 가져온다.

axis=1이라는 인수를 추가한 것에 주의하자. 이는 100 x 10의 배열 중 1번째 차원을 구성하는 각 원소에서(첫번째 차원을 축으로) 최댓값의 인덱스를 찾도록 한 것이다. (인덱스가 0부터 시작하니 0번째 차원이 가장 처음 차원이다.) 

 

argmax()와 axis의 예제이다.

x = np.array([[0.1,0.8,0.1],[0.3,0.1,0.6],[0.2,0.5,0.3],[0.8,0.1,0.1]])
y = np.argmax(x,axis=1)
print(y)

실행화면

실행화면에서 보이는 것과 같이, axis=1, 즉 첫번째 차원을 축으로 최댓값의 인덱스를 찾은 것이다.

 

 

 

 

배치 단위로 분류한 결과를 실제 답과 비교해본다. 

numpy matrix끼리 비교하여 True/False로 구성된 boolean matrix를 만들고, 이 결과 matrix에서 True가 몇개인지 센다.

y = np.array([1,2,1,0])
t = np.array([1,2,0,0])
print(y==t)
np.sum(y==t)

실행화면

 

 

 

 

 

 


 

 

※dataset 및 코드 제공(밑바닥부터 시작하는 딥러닝 GitHub: https://github.com/kchcoo/WegraLee-deep-learning-from-scratch/tree/master

 

GitHub - kchcoo/WegraLee-deep-learning-from-scratch

Contribute to kchcoo/WegraLee-deep-learning-from-scratch development by creating an account on GitHub.

github.com

 

 

 

'공부 > 밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글

밑바닥부터 시작하는 딥러닝1 - Chap6. 학습 관련 기술들  (3) 2024.11.29
밑바닥부터 시작하는 딥러닝1 - Chap5. 오차역전파법  (1) 2024.11.27
밑바닥부터 시작하는 딥러닝1 - Chap4. 신경망 학습  (2) 2024.11.20
밑바닥부터 시작하는 딥러닝1 - Chap2. 퍼셉트론  (1) 2024.11.19
밑바닥부터 시작하는 딥러닝1 - Chap1. 헬로 파이썬  (1) 2024.11.19
'공부/밑바닥부터 시작하는 딥러닝' 카테고리의 다른 글
  • 밑바닥부터 시작하는 딥러닝1 - Chap5. 오차역전파법
  • 밑바닥부터 시작하는 딥러닝1 - Chap4. 신경망 학습
  • 밑바닥부터 시작하는 딥러닝1 - Chap2. 퍼셉트론
  • 밑바닥부터 시작하는 딥러닝1 - Chap1. 헬로 파이썬
seungdeng
seungdeng
기록을 해보자
  • seungdeng
    일기장
    seungdeng
  • 전체
    오늘
    어제
    • 홈 (26)
      • 프로젝트 (1)
        • AI기반 구인구직 웹사이트 (1)
      • 일상 (0)
        • 주절주절 (0)
        • 여행 (0)
        • 맛집 (0)
      • 공부 (25)
        • 인공지능 (1)
        • 밑바닥부터 시작하는 딥러닝 (16)
        • 논문 (8)
        • LLM의 시대: 언어모델의 혁신과 변화 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    Inductive Bias
    Zero shot
    Attention
    RNN
    qkv
    fine tuning
    vision
    MLP
    self attention
    MLM
    transformer
    positional embedding
    Bert
    few shot
    NLP
    논문리뷰
    NSP
    GPT
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
seungdeng
밑바닥부터 시작하는 딥러닝1 - Chap3. 신경망
상단으로

티스토리툴바