본문 바로가기

Deep Learning

[Deep Learning] 220805 학습일기

 

밑바닥부터 시작하는 딥러닝 - YES24

직접 구현하고 움직여보며 익히는 가장 쉬운 딥러닝 입문서 이 책은 라이브러리나 프레임워크에 의존하지 않고, 딥러닝의 핵심을 ‘밑바닥부터’ 직접 만들어보며 즐겁게 배울 수 있는 본격 딥

www.yes24.com

 

1.

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

h(x)는 입력이 0을 넘으면 1을 돌려주고 그렇지 않으면 0울 돌려준다.

여기서 이러한 h(x) 같은 함수를 활성화 함수 라고 한다.

 

활성화 함수는 입력 신호의 총합이 활성화를 일으키는지를 정하는 역할을 한다.

 

 

 

2.

1번과 같이 임계값을 경계로 출력이 바뀌는 함수를 계단 함수(step function)이라고 한다.

 

계단함수를 파이썬으로 구현해보자.

 

먼저 완전 단순하게

def step_function(x):
    if x>0:
        return 1
    else:
        return 0

근데 이 경우 인수 x는 실수(부동소수점)만 받아들인다.

step_function(4.0)은 되지만 넘파이 배열은 인수로 넣을 수 없다.

예를 들어 step_function(np.array([1.0, 2.0]))는 안된다.

def step_function(x):
    if x>0:
        return 1
    else:
        return 0
print(step_function(4.0))

#출력값
1

import numpy as np

def step_function(x):
    if x>0:
        return 1
    else:
        return 0
        
print(step_function(np.array([1.0,2.0])))

<출력값>

Traceback (most recent call last):
  File "c:\Users\tina0\Desktop\python 실습들\Deep Learning-밑시딥\3.2 활성화함수.py", line 9, in <module>
    print(step_function(np.array([1.0,2.0])))
  File "c:\Users\tina0\Desktop\python 실습들\Deep Learning-밑시딥\3.2 활성화함수.py", line 4, in step_function
    if x>0:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

 

 

넘파이 배열도 지원하도록 수정하고 싶다면 다음과 같이 구현하면 된다.

import numpy as np
def step_function(x):
    y = x>0
    return y.astype(np.int)

솔직히 이게 처음 보면 뭔 말인가 싶다.

 

앞에서 배웠던 내용을 이용하면 차례대로 이해가 된다.

 

앞에서

import numpy as np
A = np.array([[1,2], [10,21], [12,60]])
Af = A.flatten()
print(Af)
print(Af>15)

의 실행결과가

[ 1  2 10 21 12 60]
[False False False  True False  True]

였다.

 

이것도 같은 원리를 이용한 것이다.

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

print(x)
print(y)

<출력값>

[-1.  1.  2.]
[False  True  True]

넘파이 배열에 부등호 연산을 수행하면 배열의 원소 각각에 부등호 연산을 수행한 bool 배열이 생성된다.

배열 x의 원소 각각이 0보다 크면 True, 0이하이면 False로 변환한 배열 y가 생성된다.

 

여기서 y는 bool 배열인데 우리가 원하는 계단함수는 0이나 1의 int형을 출력하는 함수이므로 배열 y의 원소를 bool에서 int형으로 바꿔준다.

 

import numpy as np
x = np.array([-1.0,1.0,2.0])
y= x>0
y1= y.astype(np.int)

print(x)
print(y)
print(y1)

<출력값>

[-1.  1.  2.]
[False  True  True]
[0 1 1]

넘파이 배열의 자료형을 변환할 때에는 astype() 메소드를 사용한다.

이 메소드도 자동완성이 되지는 않으니 줏대있게 쓰면 되겠다.

원하는 자료형(여기서는 np.int)을 인수로 지정하면 된다.

 

그리고 파이썬에서는 bool을 int로 변환할 때 True는 1로, False는 0으로 변환된다.

 

 

3.

앞에서 정의한 계단함수를 그래프로 그려보자.

import numpy as np
import matplotlib.pylab as plt
def step_function(x):
    return np.array(x>0, dtype= np.int)

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()

np.arange(-5.0,5.0,0.1)은 -5.0에서 5.0전까지 0.1간격의 넘파이 배열을 생성한다.

step_function()은 인수로 받은 넘파이 배열의 원소 각각을 인수로 계단 함수를 실행해, 그 결과를 다시 배열로 만들어 돌려준다.

plt.ylim()은 y축의 범위를 지정하는 메소드이다.

그리고 이 x,y 배열을 그래프로 그리면(plot) 

아래와 같이 그래프가 보여진다(show)

이 계단함수는 0을 경계로 출력이 0에서 1로 바뀐다.

그래서 계단함수다.

 

 

4.

numpy의 브로드캐스트 기능을 사용하면 넘파이 배열과 스칼라값의 연산을 넘파이 배열의 원소 각각과 스칼라값의 연산으로 바꿔 수행할 수 있다.

import numpy as np
a = np.array([1.0,2.0,3.0])
print(a+1)
print(a*2)
print(a/2)

 

근데 이 a라는 배열에 exponential(지수함수, e의 x제곱)을 씌우고 싶다면 어떻게 해야할까?

위와 같이 그냥 exp(a) 하면 되는 걸까?

한 번 해보자.

from cmath import exp
import numpy as np
a = np.array([1.0,2.0,3.0])
print(exp(a))

<출력>

Traceback (most recent call last):
  File "c:\Users\tina0\Desktop\python 실습들\Deep Learning-밑시딥\3.2 활성화함수.py", line 52, in <module>
    print(exp(a))
TypeError: only length-1 arrays can be converted to Python scalar

cmath라는 외장 라이브러리에서 exp를 가져와도 오류가 뜬다.

배열에 exponential 함수를 적용시키려면 단순히 exp()를 해서는 안된다.

 

 

 

그럼 어떻게 해야하냐

import numpy as np
a = np.array([1.0,2.0,3.0])
print(np.exp(a))

np.exp()를 해주면 된다.

numpy 라이브러리에 exp() 메소드가 있다!

그럼 출력은 다음과 같이 된다.

[ 2.71828183  7.3890561  20.08553692]

np.exp()를 사용하면 원소 하나하나에 exp 연산이 진행되어 배열로 결과가 나온다.

 

 

 

5.

신경망에서 자주 이용하는 활성화 함수인 시그모이드 함수는 이렇게 생겼다.

 

그럼 이제 시그모이드 함수를 구현하고 그려보자.

import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
    return 1/(1+np.exp(-x))

x= np.arange(-5.0,5.0,0.1)
y= sigmoid(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1) #y축 범위 지정
plt.show()

결과는 아래와 같다.

 

시그모이드(Sigmoid) 는 'S자 모양'이라는 뜻이다,

 

 

 

6.

계단함수와 시그모이드 함수를 비교하기 위해 두 그래프를 그려봤다.

import numpy as np
import matplotlib.pyplot as plt

def step_function(x):
    return np.array(x>0, dtype= np.int)

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

x= np.arange(-5.5,5.5,0.1)
y1= step_function(x)
y2 = sigmoid(x)

plt.plot(x,y1,label = "step",linestyle = "--")
plt.plot(x,y2,label = "sigmoid")

plt.legend()
plt.show()

 

그려진 그래프는 다음과 같다.

차이점부터 알아보자.

시그모이드 함수는 부드로운 곡선이며 입력에 따라 출력이 연속적으로 변화한다.

시그모이드 함수의 매끈함이 신경망 학습에서 아주 중요한 역할을 한다고 한다

한편 계단 함수는 0을 경계로 출력이 갑자기 바뀐다

 

계단 함수가 0과 1 중 하나의 값만 돌려주는 반면 시그모이드 함수는 실수를 돌려준다. 

=> 퍼셉트론에서는 뉴런 사이에 0 혹은 1이 흘렀다면 신경망에서는 연속적인 실수가 흐른다.

 

 

두 함수의 공통점도 알아보자.

큰 관점에서 보면 둘은 같은 모양을 하고 있다.

둘 다 입력이 작을 때의 출력은 0이거나 0에 가깝고, 입력이 커지면 1에 가까워지다가 1이 되기도 한다.

 

계단함수와 시그모이드 함수 모두 입력이 중요하면 큰 값을 출력하고, 입력이 중요하지 않으면 작은 값을 출력한다.

 

계단함수와 시그모이드는 둘 다 비선형 함수이다. 시그모이드 함수는 곡선, 계단함수는 계단처럼 구부러진 직선이다.

 

 

 

7.

층을 쌓는 혜택을 얻고 싶다면 활성화 함수로는 반드시 비선형 함수를 사용해야 한다.

 

 

8.

시그모이드 함수는 신경망 분야에서 오래전부터 이용해왔으나

최근에는 ReLU(Rectified Linear Unit) 함수를 주로 이용한다.

 

ReLU는 입력이 0을 넘으면 그 입력을 그대로 출력하고, 0이하이면 0을 출력한다.

 

식은 다음과 같다.

 

 

이제 파이썬으로 구현해보자.

import numpy as np

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

넘파이의 maximum 함수를 사용하였다.

numpy의 maximum은 두 입력 중 큰 값을 선택해 반환한다.

파이썬 내장함수중 max와 기능이 비슷하다.

 

그래프도 그려봤다.

import numpy as np
import matplotlib.pyplot as plt

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

x= np.arange(-5.5,5.5,0.1)
y = relu(x)

plt.plot(x,y)
plt.show()

 

 

9.

숫자가 한 줄로 늘어선 것, 직사각형으로 늘어놓은 것, 3차원으로 늘어놓은 것,

더 일반화하여 N차원으로 나열한 것을 통틀어 다차원 배열 이라고 한다.

 

먼저 1차원 배열

import numpy as np
A = np.array([1,2,3,4])
print(A)
print(np.ndim(A))
print(A.shape)
print(type(A.shape))

<출력값>

[1 2 3 4]
1
(4,)
<class 'tuple'>

np.ndim()은 배열의 차원 수를 반환한다.

 

인스턴스 변수인 shape를 이용해 배열의 형상도 알 수 있다.

A.shape()은 튜플을 반환한다.

1차원 배열이어서 여기서는 원소가 한 개인 튜플을 반환한 것이다.

2차원 배열일 때에는 (4,3), 3차원 배열일 때는 (4,3,2) 같은 튜플을 반환한다.

 

 

2차원 배열도 작성해보자.

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

<출력값>

[[1 2]
 [3 4]
 [5 6]]
2
(3, 2)

여기서는 3x2 배열인 B를 작성했다.

3X2  배열은 처음 차원에는 원소가 3개, 다음 차원에는 원소가 2개 있다는 의미이다.

이때 처음 차원은 0번째 차원, 그 다음 차원은 1번째 차원에 대응한다.

2차원 배열은 특히 행렬(matrix)라 부른다.

 

 

10. 행렬(2차원 배열)의 곱

 

행렬의 곱은 np.dot()을 이용한다.

import numpy as np
A = np.array([[1,2],[3,4]])
B = np.array([[5,6],[7,8]])
print(A.shape)
print(B.shape)
print(np.dot(A,B))

<출력값>

(2, 2)
(2, 2)
[[19 22]
 [43 50]]

dot product의 의미가 내적인데 이와 관련이 있는 걸까?

그런데 이런 의문이 무색하게 바로 다음장에 답이 나와있다.

 

맞다!

 

np.dot()은 입력이 1차원 배열이면 벡터를, 2차원 배열이면 행렬 곱을 계산한다.

여기서 주의할 것은 np.dot(A,B)와 np.dot(B,A)는 다른 값이 될 수 있다.

행렬의 곱에서 교환법칙이 성립 안 된다는 것을 생각하면 당연한 말이다.

 

 

'Deep Learning' 카테고리의 다른 글

[Deep Learning] 220808 학습일기  (0) 2022.08.08
[Deep Learning] 220806 학습일기  (0) 2022.08.08
[Deep Learning] 220803 학습일기  (0) 2022.08.03
[Deep Learning] 220730 학습일기  (0) 2022.07.30
[Deep Learning] 220729 학습일기  (0) 2022.07.30