공부정리/Deep learnig & Machine learning

밑바닥부터 시작하는 딥러닝 3장 - mnist

sillon 2022. 5. 18. 02:01
728x90
반응형

기존에는 AND게이트나 규칙이 정해져있는 개념은 모두 파이썬 코드로 작성할 수 있다. 규칙이 있었기에 컴퓨터에 쉽게 입력을 할 수 있었다.

하지만, 사람마다 다른 글씨체가 다른 것 처럼 규칙이 정해져있지 않은 것은 어떻게 컴퓨터에 입력할까?

이러한 영역은 머신러닝이라고 부른다. 머신러닝에서는 데이터를 넣어서 학습을 하여 입력된 값이 어떤 수인지 확률을 통해 맞추어볼 수 있다. 머신러닝의 기초 MNIST를 통해서 학습과정은 생략하고 추론과정을 살펴볼 것이다.

MNIST

  • MNIST 데이터셋은 0부터 9까지의 손글씨 이미지로 구성
  • 훈련 데이터가 6만장, 테스트 데이터가 1만장
  • 각 데이터는 이미지와 라벨로 이루어짐
  • 각 이미지는 28×28 해상도의 흑백 사진
  • 각 픽셀은 0에서 255로 밝기 표현

 

MNIST 코드로 살펴보기

MNIST 코드 다운로드 : https://github.com/WegraLee/deep-learning-from-scratch  

 

GitHub - WegraLee/deep-learning-from-scratch: 『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017)

『밑바닥부터 시작하는 딥러닝』(한빛미디어, 2017). Contribute to WegraLee/deep-learning-from-scratch development by creating an account on GitHub.

github.com

PIL 라이브러리로 MNIST 나타내기

# coding: utf-8
import sys, os
sys.path.append("./dataset")  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image #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)
#flatten = Ture 을 하면 하나의 행 출력이 됨, 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)

flatten : 입력 이미지를 1차원 배열로 나타낼 지를 결정한다.

normalize : 이미지 gtayscale을 0~255에서 0.0~1.0 사이의 값으로 정규화 할지 결정한다.

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

위의 코드로 이미지는 일정한 형식을 가진 numpy array로 변할 수 있고, 일정한 형식을 가진 numpy array는 이미지로 변할 수 있다는 것을 알게 됩니다. img_show 함수는 numpy로 구성된 숫자로 된 배열들을 PIL 라이브러리가 읽을 수 있도록 해주는 코드입니다.

load_mnist(flatten=True, normalize=False, one_hot_label=False)

여기서는 이미지를 불러오는 형식에 대해 알 수 있는데,

  1. flatten은 이미지를 1차원 배열로 가져올 것이냐에 대한 옵션입니다. 28x28의 2차원 배열로 이루어진 이미지는 784개의 1차원 배열로 표현될 수 있습니다.
  2. normalize는 입력 이미지의 픽셀 값을 기존의 0~255 값에서 0.0~1.0 사이의 값으로 정규화할지 결정합니다.
  3. one_hot_label은 레이블(정답)을 원핫 인코딩 형태로 저장할지 결정합니다.
img = img.reshape(28, 28)

위의 코드는 flatten 옵션에 의해 불러와진 784개의 1차원 배열로 이루어진 이미지를 다시 28x28로 돌려놓는 함수입니다.

img_show(img)

위 코드는 이전에 설명했던 img_show 함수를 이용하여, 배열로 이루어진 이미지 정보를 pil 라이브러리를 통해 실제로 어떤 이미지인지 우리에게 시각적으로 보여주는 코드입니다.

flatten =True 를 하면 이미지가 3차원이었던 데이터 이미지가 1차원 벡터로 변환이 되어 출력이된다. 이 1차원값은 다시 reshape을 통해서 2차원으로 바꾸어줄 수 있다.

 

flatten = False 를 할 시 코드는 아래와 같다.

 

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=False, normalize=False)
#flatten = False 1차원으로 출력하지 않는다.

img = x_train[0][0] # 훈련형 데이터 이미지 행렬로 출력
label = t_train[0] # 훈련형 데이터 라벨
print(label)  # 5

print(img.shape)  # (28, 28)

img_show(img)

 

# flatten 하는 이유 3차원 텐서라서 True인 상태에서(1차원) reshape으로 해주면 행열의 형태로 출력된다.
# flatten을 하지 않고 출력하면  reshape을 안해주어도 되지만, 행열의 형태로 출력해야하므로
# x_train[0][0] 을 해주어서 해당 0번째층에 있는 행열 출력한다.

 

 

실행결과

 

 

훈련데이터 0번째에 있는 숫자 5가 출력이 된다.

여기서 print(img)를 해보자.

 

이렇게 5가 그려진 곳에는 0보다 큰 숫자로 출력이 되는 것을 알 수 있다.

이는 255로 각 픽셀의 밝기를 표현한것이므로 0~255로 나타내고있다. 이렇게 0~255까지의 수로 출력이 되는것은 해당 자료형이 uint8로, 

 

np.uint8(255)

uint은 0을 포함한 양수 혹은 부호가 없는 정수를 나타낸다.

uint8은 2^8개만큼의 표현이 가능하다.(0~255)

 

 

이렇게 코드를 실행해보면 수가 0~255사이로만 출력이 되는 것을 볼 수 있다.

만약 256을 출력하면 255가 최대이므로 다시 0으로 돌아와 값을 출력한다.

 

그럼 위의 이미지의 흑백을 반전하여 출력하려면 어떻게할까?

답은 간단하다.

 

 

print(np.uint8(255-img))
img_show(np.uint8(255-img))

단순히 img에 해당되는 값들을 빼주면 흑백이 바뀌어 출력이 된다.

더보기
# coding: utf-8
import sys, os
sys.path.append("./dataset")  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image #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)
#flatten = Ture 을 하면 하나의 행 출력이 됨, False 는 행열로 출력
#normalize = True 을 하면

img = x_train[0] # 훈련형 데이터 이미지
label = t_train[0] # 훈련형 데이터 라벨
print(label)  # 5

print(img.shape)  # (784,) 28X28 을 곱한 값으로 flatten = False 를 하면 (1,28,28) 출력
img = img.reshape(28, 28)  # 형상을 원래 이미지의 크기로 변형
print(img.shape)  # (28, 28)

img_show(np.uint8(255-img))

 

실행 결과

 

 

matploltlib로 MNIST 나타내기

import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
from dataset.mnist import load_mnist
import matplotlib.pyplot as plt

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

plt.figure()
plt.imshow(x_train[0][0])
plt.colorbar()
plt.show()


plt.figure(figsize=(10,10)) # 도화지 사이즈 10*10
for i in range(25):
    plt.subplot(5,5,i+1) # 5 x 5 바둑판 생성 1번~25번째 까지
    plt.xticks([]) #눈금 설정
    plt.yticks([]) #눈금 설정
    plt.imshow(x_train[i][0], cmap=plt.cm.binary) #색깔 뽑아내기
    plt.xlabel(t_train[i]) #라벨
plt.show()

 

실행결과

 

 

잘못 분류한 결과 출력하기

전체 소스코드 

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
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()
error=[]
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p != t[i]: # 오답 분류하기
        error.append(i)
        
print(error)

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x[error[i]].reshape(28,28), cmap=plt.cm.binary)
    plt.xlabel(t[error[i]])
plt.show()

 

자세히 봐야할 코드

x, t = get_data()
network = init_network()
error=[]
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 확률이 가장 높은 원소의 인덱스를 얻는다.
    if p != t[i]: # 오답 분류하기
        error.append(i)
        
print(error)

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x[error[i]].reshape(28,28), cmap=plt.cm.binary)
    plt.xlabel(t[error[i]])
plt.show()

출력 결과 (x레이블에 테스트 이미지가 적힌 원래 숫자가 출력됨)

y 레이블에 mnist가 분류 했던 값 넣기

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

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x[error[i]].reshape(28,28), cmap=plt.cm.binary)
    plt.ylabel(sorted_mnist[i])
    plt.xlabel(t[error[i]])
plt.show()

 

 

 

 

 

REFERENCE

https://pbj0812.tistory.com/286

 

https://www.youtube.com/watch?v=ojMAFoH7MxA&list=PLBiQZMT3oSxW1RS1hn2jWBgswh0nlcgQZ&index=4 

 

https://velog.io/@jakeseo_me/%EB%B0%91%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-%EB%94%A5%EB%9F%AC%EB%8B%9D-2-2-MNIST-%EC%86%90%EA%B8%80%EC%94%A8-%EC%88%AB%EC%9E%90-%EC%9D%B8%EC%8B%9D

 

728x90
반응형