합성곱 신경망 (Convolutional Neural Network, CNN)

: CNN은 이미지, 동영상 등을 분석하는데 사용하는 알고리즘입니다. 

 

 

완전연결 계층의 문제점

 

- 기존에 구현했던 완전 계측, 즉 기존 신경망은 데이터의 형상이 무시됩니다. 

글자의 크기가 달라지거나 글자의 회전이 생기면 글자에 변형과 같이 글자에 변형이 조금만 생기더라도 다른 글자로 인식하기 때문에 새로운 학습 데이터를 넣어주지 않으면 좋은 결과 얻을 수 없습니다(패턴을 읽지 못하므로 인식을 위해 다양한 데이터 필요)  

 

- 입력 데이터가 이미지인 경우, 이미지는 3차원(가로, 세로, 채널(색상))으로 구성된 데이터이나 1차원으로 평탄화해줘야 합니다.  



합성곱 계층의 특징


- 원본 이미지를 가지고 여러개 특징 맵(feature map)을 만들어 분류 하는 완전 연결 계층으로, 이미지의 특징을 추출하기 때문에 이미지가 변형이 되더라도 잘 이닉할 수 있습니다(입력 데이터의 형상을 유지).

    * 특징 맵(feature map) : CNN에서 합성곱 계층의 입출력 데이터 


- 이미지도 3차원 데이터로 입력 받으며, 다음 계층에도 3차원 데이터로 전달합니다. 


- 형상을 가진 데이터를 제대로 이해할 가능성이 있는 큽니다. 

 

 

- 합성곱 연산

: 이미지 3차원(세로, 가로, 색상) data의 형상을 유지하면서 연산하는 작업으로, 입력 데이터에 적용한 것입니다. 

 

<- 합성곱 연산을 기호로 표기


- 이미지 처리에서 말하는 필터 연산에 해당 합니다. 
- 데이터와 필터의 형상을 (높이, 너비)로 표기 합니다. 
- 위 예의 경우 입력은 (4, 4), 필터는 (3, 3), 출력은 (2, 2)  
- 문헌에 따라 필터를 커널이라 칭하기도 합니다. 

- 필터를 적용한 후 데이터에 더해집니다.
- 편항은 항상 (1 x 1)만 존재합니다. 

 

 

패딩(padding)

: 합성곱 연산을 수행하기 전에데이터 주변을 특정값(예컨데 0) 으로 채워 늘리는 것을 의미합니다 .

 

- 합성곱 연산에서 자주 이용하는 기법 – 출력 크기를 조정할 목적으로 사용합니다. 

+ 패딩을 하지 않을 경우 data의 크기는 합성곱 계층을 지날 때마다 작아지게 되므로 가장자리 정보들이 사라지는 문제가 발생하기 때문에 패딩을 사용합나다.

 

 

 

-입력 데이터 주위에 0을 채운다 (0 생략함)

 

 

 

 

 

 

- 처음에 크기가 (4, 4)인 입력 데이터에 패딩이 추가되어 (6, 6)이 됩니다. 
- (3, 3) 크기의 필터를 걸면 (4, 4) 크기의 출력 데이터가 생성합니다. 

 

 

스트라이드

: input 데이터에 filter를 적용하는 위치의 간격을 의미합니다. 

 

 

- 필터를 적용하는 위치의 간격


- 스트라이드를 키우면 출력 크기는

  작아짐 

 

 

 

 

 

 

 

 

 

 

 

 

패딩, 스트라이드, 출력 크기 계산

- 입력 크기(H, W)
- 필터 크기(FH, FW)
- 출력 크기(OH, OW)
- 패딩 : P
- 스트라이드 : S

 

 

 

 

3차원 데이터의 합성곱 연산

: 이미지의 색은 보통 흑백이 아니라 RGB(Red, Green, Blue)컬러 이므로 RGB컬러에 대해서 합성곱을 해야합니다. 

 

3차원 데이터 합성곱 연산의 계산 순서

- 3차원의 합성곱 연산에서 주의할 점

 : 입력 데이터의 채널 수와 필터의 채널 수가 같아야 함.

 

- 필터 자체의 크기는 원하는 값으로 설정 가능.

  (단, 모든 채널의 필터가 같은 크기여야 함)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3차원 합성곱 구현 이론 

사진 한장을 RGB 필터로 합성곱하여 2차원 출력행렬(Feature map)을 출력하는 것입니다. 

 

 

합성곱 연산의 처리 흐름(편향 추가)

 

합성곱 연산의 처리 흐름(배치 처리)

이미지를 한 장씩 학습시키는 것은 학습속도가 느립니다. 따라서 100장의 이미지를 묶음으로 나눠서 한번에 학습시킵니다. 이를 minibatch라고 합니다. 

 

하지만 합성곱 계층을 미니 배치로 구현할 때 4차원 행렬의 연산이 됩니다. 따라서 연산의 속도가 느려지므로 행렬 연산을 빠르게 하기 위해 4차원이 아닌 2차원으로 차원을 축소해야할 필요가 있습니다.  이때!! im2col 함수를 사용합니다. 

 

 

 im2col 함수 원리 : 각 4차원 블럭을 R, G, B로 각각 나누어 2차원으로 변환 후 합쳐줍니다. 

 

 

 

 

오차역전파법 (backpropagation)

: 가중치 매개변수의 기울기를 효율적으로 계산하는 방법

  1. 결과값을 손실함수로 변환한다
  2. 손실함수의 기울기를 수치 미분
  3. 기울기가 0이 되는 지점까지 weight를 변화

- 수치 미분을 통하여 기울기를 구하는데, 이는 단순하고 구현하기는 쉽지만 계산 시간이 오래 걸린다는 단점이 있습니다. 따라서, 가중치 매개변수의 기울기를 가장 효율적으로 계산할 수 있는 오차역전파법(backpropagation)을 사용합니다. 

 

순전파 : 왼쪽 -> 오른쪽

역전파 : 오른쪽 > 왼쪽  

 

 

계산 그래프 

: 계산 그래프는 계산 과정을 그래프로 나타낸 자료구조로, 복수의 노드와 에지로 표현합니다. 

 

예제1) 

현빈 군은 슈퍼에서 1개에 100원인 사과를 2개 샀습니다. 이때 지불금액을 구하시오, 단 소비세가 10% 부과됩니다.

최초에 사과를 두 개 사고, 소비세를 곱한 값이 답이 됩니다.(220원). 위의 그래프에서는 x2와 x1.1을 각각 하나의 연산으로 취급했지만, 곱하기만을 연산으로 취급할수도 있습니다. 이렇게 하면 곱한 값 또한 변수가 되어 원 밖에 위치하게 됩니다.

 

  1. 계산 그래프를 구성
  2. 그래프에서 계산을 왼쪽에서 오른쪽으로 진행

여기서 2번, ‘계산을 왼쪽에서 오른쪽으로 진행하는 것’을 순전파이고, 역전파는 반대로(오른쪽에서 왼쪽으로) 계산을 진행합니다.

 

 

예제2)

현빈군은 슈퍼에서 사과를 2개, 귤을 3개 샀습니다. 사과는 1개에 100원, 귤은 1개 150원 입니다. 소비세가 10%일 때 지불 금액을 구하세요.

 

 

계산 그래프의 특징은 '국소적 계산'을 통해 최종 결과를 얻는 것으로 '자신과 직접 관계된' 범위 내에서만 계산이 이루어 집니다. 

 

 

- 계산그래프의 장점

 

국소적 계산을 통해 각 노드의 계산에 집중하여 문제를 단순화

: 아무리 복잡한 최종결과가 이루어진다고 하더라도 계산 그래프로 표현하면 각각의 항목별로 독립적으로 표현하고, 독립적으로 연산합니다. 연관지어서 대상을 표현하는 것이 아니라 독립적으로 표현합니다. 독립적으로 수행한 결과만 합쳐주면 됩니다. 

 

역전파를 통해 '미분'을 효율적으로 계산 



사과 가격이 오르면 최종 금액에 어떠한 영향을 주는가'에 대해서 사과 가격에 대한 지불 금액의 미분을 구해 계산할 수 있습니다.  사과의 값을 ​x, 지불 금액을 ​L라 했을 때, ​ 𝜕L/𝜕x(분수)를 구하는 것으로, 이러한 미분 값은 사과 값(x)가 '아주 조금'올랐을 때 지불 금액(​L)이 얼마나 증가하는지를 나타냅니다. 

 

계산 그래프의 역전파

 

 

               y = f(x)

 

 

 

 

 

역전파의 계산 절차

- 신호 E에 노드의 국소적 미분( 𝜕𝑦/𝜕𝑥 (분수))을 곱한 후 다음 노드로 전달.
- 국소적 미분이란 : 순전파 때의 y = f(x) 계산의 미분을 구한다는 것이며, 이는 x에 대한 y의 미분( 𝜕𝑦/𝜕𝑥(분수))을 구함. 

 

* 𝜕𝑦/𝜕𝑥(분수) 의 값을 구하기 위해서는 x, y 의 값을 알아야 하는데, 계산 그래프를 그려서 확인하면 결과 값들을 보관함으로 빠르게 확인할 수 있습니다. 

 

- 입력에 대한 변화를 미분으로 정의한 것이라고 볼 수 있습니다. 

  

 

연쇠법칙

: 연쇄법칙은 합성 함수의 미분에 대한 성질로, 합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있습니다.

 

* 합성함수 : 여러 함수로 구성된 함수

 

예시) 

t^2을 미분하면 2t이고, t를 미분하면 1입니다. 그래서, 최종적으로 z를 미분한 값은 2t * 1 = 2t = 2(x+y)입니다.

 

연쇄법칙과 계산 그래프

x의 변화량 분의 z의 변화량의 편미분값

 

덧셈 노드의 역전파 

 

z = x + y 라는 식을 대상으로, 역전파를 살펴보겠습니다. 그래프를 그리면 다음과 같은 모양이 될 것입니다.

x + y 의 식에 대해, 위의 임의의 계산으로 가는 노드는 x를 기준으로 편미분을 하므로, y가 사라지면서 1이 남게 됩니다. 밑의 임의의 계산으로 가는 노드는 y 를 기준으로 편미분을 하므로, x가 사라지면서 1이 남게 됩니다. 따라서, + 노드를 대상으로 하는 역전파는 결과적으로 입력받은 데이터를 그대로 흘려주게 됩니다. 별도의 부정을 수행할 필요 없다는 것이 함축 되어 있습니다. 

 

예제 ) 사과 2개와 귤 3개 구입

from chap05.layer_naive import *

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tex = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward - 순정파
apple_price = mul_apple_layer.forward(apple, apple_num)
orange_price = mul_orange_layer.forward(orange, orange_num)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)
price = mul_tax_layer.forward(all_price, tex)

# backward - 역전파
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice) # 입력의 편미분, 세금
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print("price : " , int(price))
print("dApple : ", dapple)
print("dApple_num" , int(dapple_num))
print("dOrange :", dorange)
print("dOrange_num", int(dorange_num))
print("dTax :", dtax)

< console > 

 

곱셈 노드의 역전파 

 

z = xy라는 식을 생각해보자. 이 식의 미분은 다음과 같습니다.

 - x로 편미분 : y

 - y로 편미분 : x

 

해석학적으로 다음과 같이 미분이 되므로, 그래프로 보았을 때는 이웃 노드의 데이터 * 위에서 흘러내려온 데이터가 내려감을 알 수 있습니다. 

그래프가 있고, 역전파 시 상류에서 1.3이라는 값이 흘러내려온다고 가정, 역전파 그래프는 다음과 같이 나오게 됩니다.

 

즉, 곱하기 노드에서는 순전파 때의 입력 신호들에 ‘서로 바꾼 값’을 곱하여 하류로 보내게 됩니다.

 

예제 ) 곱셈 계층 – 사과 2개 구입

(console) 

 

활성화 함수 계층 구현하기 – ReLU 계층

ReLU :  0이하의 값은 0으로 출력하고, 0이상은 입력값 그대로의 값을 넣어줍니다. 최근에 많이 사용됩니다. 

 

- ReLU 계층의 계산 그래프

 

예시 ) ReLU 함수 이해

( console ) 

 

ReLU의 역전파

 

 

활성화 함수 계층 구현하기 – Sigmoid 계층

 

- 시그모이드 함수 

 

 

 

sigmoid 계층의 계산 그래프(역전파) 

 

 

Sigmoid 계층의 계산 그래프(간소화 버전)

 

순전파의 출력 y만으로 역전파 계산

 

sigmoid의 역전파

- 순전파의 출력을 인스턴스 변수 out에 보관 했다가, 역전파 계산 때 그 값을 사용합니다. 

 

 

Affine 계층 

: 행렬의 내적에서는 대응하는 차원의 원소 수 일치

 

어파인 변환(Affine transformation) : 신경망의 순전파 때 수행하는 행렬의 내적을 기하학에서 일컫는 말.


Affine 계층 : affine 변환을 수행하는 처리.

 

 

 

 

 

 

Affine 계층의 계산 그래프 : 변수가 행렬(다차원 배열)임에 주의

 

 

- 순정파의 계산에서 내적을 계산

 

 

 

 

 

 

 

 

 

 

 

affine의 역전파 

(console) 

 

Softmax-with-Loss 계층의 계산 그래프

 

간소화한 Softmax-with-Loss 계층의 계산 그래프

확률적 경사 하강법 (Stochastic Gradient Descent, SGD) 
: 데이터를 미니배치로 무작위로 선정하여 경사 하강법으로 매개변수를 갱신하는 방법으러 추출된 데이터 한개에 대해서 그래디언트를 계산하고, 경사 하강 알고리즘을 적용합니다. 

 +)  추출된 데이터 한 개에 대해서 error gradient 를 계산하고, Gradient descent 알고리즘을 적용하는 방법

모델의 레이어 층은 하나의 행렬곱으로 생각할 수 있고, 여러개의 묶음 데이터는 행렬이라고 생각 할 수 있습니다.

즉, 여러개의 묶음 데이터를 특정 레이어 층에 입력하는 것은 행렬 x 행렬로 이해할 수 있는데, SGD는 입력 데이터 한 개만을 사용하기 때문에 한 개의 데이터를 '벡터' 로 표현하여 특정 레이어 층에 입력하는 것으로 이해할 수 있고 이는 벡터 x 행렬 연산이 됩니다. 

 

- 전체 데이터를 사용하는 것이 아니라, 랜덤하게 추출한 일부 데이터를 사용한느 것입니다.. 따라서 학습 중산 과정에서 결과의 진폭이 크고 불안정하며, 속도가 매우 빠릅니다. 또한 데이터 하나씩 처리하기 때문에 오 

 

 


SGD 장점 


-  위 그림에서 보이듯이 Shooting 이 일어나기 때문에 local optimal 에 빠질 리스크가 적다.

-  step 에 걸리는 시간이 짧기 때문에 수렴속도가 상대적으로 빠르다.

 

SGD의 단점

 

-  global optimal 을 찾지 못 할 가능성이 있다(오차율이 크다) 

    : 단점을 보완하기 위해 Mini batch를 이용한 방법이며, 활률적 경사 하강법의 노이즈를 줄이면서도 전체 배치보다

     더 효율적으로 수행할 수 있습니다. 

 

-  데이터를 한개씩 처리하기 때문에 GPU의 성능을 전부 활용할 수 없다.

 

 

신경망 학습 절차

전제 - 학습
 : 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정

1 단계 미니배치
 : 훈련 데이터 중 일부를 무작위로 가져오는 미니배치를 통해 손실 함수 값을 줄이는 것이 목표

2 단계 기울기 산출
 : 미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구함 .
  기울기는 손실 함수의 값을 가장 작게 하는 방향을 제시함

3 단계 매개변수 갱신
 : 가중치 매개변수를 기울기 방향으로 아주 조금 갱신함

4 단계 반복
 : 1 ~ 3 단계를 반복함

 

 

신경망 설계 (전제 - 학습)  

1) 생성자 생성 

 

2) predict - 데이터의 흐름에 대한 기능 구현

 

3) 손실함수 활용(교차 엔트로피 오차) 

 

4) 정확도 체크 함수 정의

 

5) 경사하강법

- common 밑에 있는 함수를 호출 한 것인데, 자신 안에 자신과 동일한 이름의 함수가 사용된다면 어떻게 구분할까?


: class 안의 함수나 메서드를 선어하려면 'self.' 을 붙여야지만 선언이 가능한데 같은 자료형 안이어도 'self.' 으로 호출해야지 내가 선언한 함수를 사용되므로 외부의 정의 되어 있는 함수를 찾아서 수행합니다. 

 

 

 

신경망 실행 - 학습 시키기  

위에서 설계한 신경망을 Mnist 데이터를 사용하여 실행하도록 하겠습니다.   

 

1) 데이터 읽기

 

2) 인스턴스화

 

3) 하이퍼 파라미터 

 

4) 학습하면서 나오는 손실값 - 값이 작아지는 것을 확인

 

5)  미니 배치 방식 

 

6) 기울기 계산(미분)

 

7) 매개변수(가중치, 편향) 갱싱 

 

8)  학습 경과 정확도 확인 

 

9) 시각화 

 

신경망 

: 퍼셉트론에서 출발하여 여러 입력신호를 입력받아서 출력을 내보내주는 표현이 퍼셉트론이었는데, 퍼셉트론과의 차이는 활성화 함수를 어떤 것을 사용하느냐에 차이를 가집니다. 퍼셉트론은 스텝 함수를 사용하고, 신경망은 시그모이드 함수를 사용합니다. 스텝 함수는 선형성이고,  시그모이드 비선형입니다. 게이트를 통해서 결과를 확인할 때, XOR는 비선형의 형태를 통해서 코드상으로 구현할 수 있습니다. 코드의 형태를 봤을 때, 입력과 출력의 형태로만 되어있던 것을 은닉층을 통해 다층의 구조를 가져갈 때, 활성화 함수를 시그모이드 함수로 활용하여 선형적으로 절대 분류 할 수 없었던 특징을 비선형으로 구현할 수 있게 되었습니다. 

 

 

 

신경망 학습 : 데이터로부터 매개변수의 값을 정하는 방법

- ex) y = ax + b / a(기울기) , b(절편) 을 획득하라.  

 

신경망의 특징

- 데이터를 보고 학습할 수 있다. 
- 가중치 매개변수의 값을 데이터를 보고 자동으로 결정한다는 뜻. 
- 사람의 개입을 최소화하고, 수집한 데이터로부터 답을 찾고, 패턴을 찾으려는 시도
- 특히, 신경망과 딥러닝은 기존 기계학습에서 사용하던 방법보다 사람의 개입을 더욱 배제할 수 있게 해주는 중요한

  특성을 지님

 

 

훈련 데이터와 시험 데이터

훈련 데이터 (training data) 

: 훈련 데이터만 사용하여 학습하면서 최적의 매개변수를 찾음

시험 데이터 (test data) 
: 앞서 훈련한 모델의 실력을 평가하는 것

훈련 / 시험 데이터 분리 이유
: 우리가 원하는 것은 범용적으로 사용할 수 있는 모델 구현
: 범용 능력을 제대로 평가하기 위해 모델을 찾아내는 것이 기계 학습의 최종 목표

오버피팅 (overfitting) : 한 데이터 셋에만 지나치게 최적화된 상태

- 학습용 데이터를 넣을 때는 정확하지만, 테스트 데이터를 넣었을 때는 결과가 정확하지 않은 것. 

 

 

손실 함수(loss function)

: 손실함수는 신경망을 학습할 때 학습 상태에 대해 측정할 수 있도록 해주는 지표입니다. 신경망의 가중치 매개변수들이 스스로 특징을 찾아 가기에 이 가중치 값의 최적이 될 수 있도록 해야 하며 잘 찾아가고 있는지 볼 때 손실 함수를 보는 것입니다 = 손실 함수의 결과값을 가장 작게 만드는 가중치 매개 변수를 찾는 것이 학습의 목표.

- 데이터와 그래프와의 거리의 오차가 가장 작은 범위를 찾아서 머신러닝/딥러닝을 통해서 찾도록 하는 것입니다. 

 

 

1) 평균 제곱 오차(mean squared error, MSE) - 회귀분석

: 평균제곱오차는 손실 함수로 가장 많이 쓰이며 간단하게 설명하면 예측하는 값이랑 실제 값의 차이(error)를 제곱하여 평균을 낸 것이 평균제곱오차입니다. 예측 값과 실제 값의 차이가 클수록 평균제곱오차의 값도 커진다는 것은 이 값이

작을 수록 예측력이 좋다고 할 수 있습니다. 

 

pi = 실제값 , yi - 예측값 

 

 

MSE(Mean Squared Error)
 
def mean_squared_error(y, t): # p와 y 자리가 바뀌어도 제곱을 할것이기 때문에 문제 없다.
    return np.sum((y-t) ** 2) # 제곱


# 2 : 정답
t = [0,0,1,0,0,0,0,0,0,0] # 답 2 : 원핫인코딩(One-Hot Encording)

# 예측 결과 : 2
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
msq = mean_squared_error(np.array(t),np.array(y)) # 리스트 형태 -> 배열로 변환

print(msq)
출력값 : 0.19500000000000006

# 예측 결과 : 7
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
msq = mean_squared_error(np.array(t),np.array(y)) # 리스트 형태 -> 배열로 변환

print(msq) 
출력값 : 1.195

- 제곱을 한다는 것은 면적을 구하는 것으로, 그 면적의 제곱이 더 작은 기울기를 찾는 것입니다. 왼쪽 보다 오른쪽이 더잘 찾은 그래프 입니다. 

 

2) 교차 엔트로피 오차(cross entropy error, CEE) - 분류분석 
: 교차 엔트로피 오차는 정답일 때의 출력이 전체 값을 정하게 됩니다. 데이터 하나에 대한 손실 함수에서 N개의 데이터로 확장. 다만, N으로 나누어 정규화. 평균 손실 함수, 교차엔트로피는 로그의 밑이 e인 자연로그를 예측값에 씌워서 실제 값과 곱한 후 전체 값을 합한 후 음수로 변환합니다. 실제 값이 원핫인코딩(one-hot encoding; 더미변수처럼 1~9까지 범주로 했을 때 정답이 2일 경우 2에는 '1'을 나머지 범주에는 '0'으로) 방식 일경우에는 2를 제외한 나머지는 무조건 0이 나오므로 실제값일 때의 예측값에 대한 자연로그를 계산하는 식이 됩니다. 실제 값이 2인데 0.6으로 예측했다면 교차 엔트로피 오차는 -log(1*0.6) = -log0.6 이 된다. = 0.51

ti : 정답 레이블 , yi : 예측값 

 

 

 

 

CEE(Cross Entropy Error)

def cross_entropy_error(t, y):
    delta = 1e-7 # NaN 값이 나오지 않기 위함
    return -np.sum(t * np.log(y+delta))
    
# 2 : 정답
t = [0,0,1,0,0,0,0,0,0,0] # 답 2 : 원핫인코딩(One-Hot Encording)
       
# 예측 결과 : 2
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
cee = cross_entropy_error(np.array(t),np.array(y)) # 리스트 형태 -> 배열로 변환
print(cee)
출력값 : 0.510825457099338


# 예측 결과 : 7
y = [0.1, 0.05, 0.1, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0]
cee = cross_entropy_error(np.array(t),np.array(y)) # 리스트 형태 -> 배열로 변환
print(cee) 
출력값 : 2.302584092994546

 

자연로그y=log𝑥의 그래프

: 교차 엔트로피는 출력이 1일 때 0이 되며 x가 커질수록 0에 가까워지고 x가 작아질수록(0에 가까워질수록) 값이 작아집니다(음의방향).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

미니배치 학습

: 한번에 하나만 계산하는게 아니라 일부를 조금씩 가져와서 전체의 '근사치'로 이용하여 일분만 계속 사용하여 학습을 수행하는 방법을 이용합니다. 그 일부를 미니배치 mini-batch 라고 합니다. 훈련 데이터에서 일부를 무작위로 뽑아 학습하는 것은 미니배치 학습입니다. 미니배치는 무작위로 추출하는 것으로 표본을 무작위로 샘플링하는 것과 개념적으로 유사합니다. 

import numpy as np
from dataset.mnist import load_mnist


# 60000개 데이터      10000개 데이터
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) 
# normalize= True : 정규화 , one_hot_label= True : 원핫인코딩 방식으로 처리

print(x_train.shape) # (60000, 784) 60000개 데이터, 784 입력 개수
print(t_train.shape) # (60000, 10) one_hot_label=True - 1이 아닌 10으로 불러온다.

print(t_train.shape[0]) # 60000 : 전체 데이터의 개수를 출력한다.
print(t_train.shape[1]) # 10 : one_hot_label=True 에 의해서 10으로 출력.
batch_size = 100 # 60000개 중에서 batch 100

# 지정한 범위의 수 중에서 무작위로 원하는 개수만 선택(batch_size = 100).
batch_mask = np.random.choice(t_train.shape[0], batch_size)  
# (범위,출력개수)  - 60000개 중에서 100개 index를 출력

print(batch_mask)

출력값:
[10995 25334 55558 55950 43378 42591 59185 42772 37410 15106 19732 25420
 36632  2856 19635 32958 44348  1958  2423 29217 32011 32203  6246  3725
 36512  9584  5884 39660 55198 54392 10831 17019 29137 33830 34056 48376
 40238 45480 37854  1991 26662  4218 24518  2198  3433 58183 49781 32639
 49820 58634 22093  3984 58591 54007 44081 49513 34769 27648  2329 24193
  7408 56742 55155 14269 59064 45151 54338 42582  7501 36434 15837 49116
 26964 21467 29748  3811 55781  1606 16729 15604  3548  5645  8241  5217
 26850 29007 50469 26448 41770 59517 28124 45990 40545 12044 27309 29817
 35131 34838 14644 35117]

 

미분 - 접선의 기울기를 구하는 개념

: 경사법에서는 기울기 값을 기준으로 방향을 정합니다. 

- 미분은 한 순간의 변화량을 계산한 것(x 의 작은 변화 가 함수 f(x) 를 얼마나 변화시키느냐를 의미) 

수치 미분

h는 시간을 뜻하고 이를 한 없이 0에 가깝게 한다는 의미로 lim를 주고, 분자는 a에 대한 변화량을 나타냅니다. 

이와 같은 방식으로 미분을 구하는 것은 수치 미분이라고 합니다. 차분(임의의 두 점에서 함수 값들의 차이)으로 미분을 구하기 때문입니다. 수치 미분은 오차가 포함될 수 밖에 없습니다. 오차를 줄이기 위해서는 x를 중심으로 h 만큼의 함수 f의 차분을 계산하여 구하기도 하며 이를 중심 차분 또는 중앙 차분이라고 합니다. 

 

- 진정한 미분 접선 과 수치 미분 근사로 구한 접선 의 값

 

-  미분의 나쁜 구현 예(문제점 2가지)

: 0.0이 답이 아닌데 출력되는 값이 0.0으로 출력됩니다. 

import numpy as np
import matplotlib.pyplot as plt # 시각화 관련 패키지

# 미분의 나쁜 구현 예(문제점 2가지)
def numerical_diff(f, x):
    h = 10e-50 # 1번째 문제점) 0에 최대한 가깝게 구현.
               # But python의 경우 np.float32(1e-50)는 0.0으로 처리(반올림 오차)
    return (f(x+h)-f(x)) / h # 2번째 문제점) 1e-50)보다 크게 하면 h에 의한 오차 발생.

def function_1(x):
    return 0.01 * x ** 2 +0.1 * x

x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x,y)
# plt.show()

print(numerical_diff(function_1,5))  
출력값 : 0.0

 

- 문제점 해결 수치 미분의 예 

: 중앙차분 , 오차를 줄일 수 있도록 합니다(반드시 오차가 줄어드는 것은 아닙니다).

def numerical_diff(f,x):
    h = 1e-4 # 0.0001
    return (f(x+h) -f(x-h))/2*h
    # x를 중심으로 그 전후의 차분을 계산한다. : 중심차분 혹은 중앙차분
    # 오차는 존재하지만 접선의 기울기를 그리는 것과 같은 효과를 낼 수 있다.

def function_1(x):
    return 0.01 * x ** 2 +0.1 * x

x = np.arange(0.0, 20.0, 0.1)
y = function_1(x)
plt.xlabel("x")
plt.ylabel("f(x)")
plt.plot(x,y)
# plt.show()

print(numerical_diff(function_1,5)) 
# 1.9999999999908982e-09

 


편미분 

: 변수가 여러 개인 함수에 대한 미분을 편미분이라고 합니다. 편미분 역시 변수가 하나인 미분과 동일하게 특정 장소에 대한 기울기를 구합니다. 목표 변수 하나에 초점을 맞추고 다른 변수는 값을 고정합니다. 

# - f(x0,x1) = x0**2 + x1**2
def function_2(x):
    return x[0]**2 + x[1]**2
    # 또는 return np.sum(x ** 2)

# x0 = 3, x1 = 4 일때, x0에 편미분
def function_tmp1(x0):
    return x0 * x0 + 4.0 ** 2.0

# x0 = 3, x1 = 4 일때, x1에 편미분
def function_tmp2(x1):
    return 3 * 2 + x1 * x1

print(numerical_diff(function_tmp1,3))
# 수치미분값(편미분) - x0 출력값 :  6.000000000003781e-08

print(numerical_diff(function_tmp2,4))
# 수치미분값(편미분) - x1 출력값 : 7.999999999999119e-08

 

 

기울기 (Grandient)  

: 모든 변수의 편미분을 벡터로 정리한 것을 기울기 gradient라고 합니다. 기울기는 가장 낮은 장소를 가리키지만 각 지점에서 낮아지는 방향을 의미합니다. 기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 줄이는 방향이라고 할 수 있습니다. 

 

모든 변수의 편미분을 동시에 계산하고 싶다을 때

양쪽의 편미분을 묶어서 계산 합니다. 

 

 

 

 


경사하강법 

: 손실함수를 기준으로 오차값을 보고 음수쪽으로 이동할지 양수쪽으로 이동할지를 판단합니다. 손실합수의 접선의 기울기를 구하여 양수가 나오면 음수를 넣어주고, 음수가 나오면 양수를 넣어줘서 접선의 기울기가 0이 나오도록 합니다. 손실을 최소화 하는 값을 추적해가는 알고리즘입니다. 

- 기울기를 잘 이용해 함수의 최소값 또는 가능한 한 작은 값 을 찾으려는 것 이 경사 하강법 

- 접선의 기울기가 0이 되는 지점이 최소값이 됩니다(음수가 나오면 양수로 이동 , 양수가 나오면 음수가 나오도록

  값을 조정합니다).

 

학습률 = 얼마 만큼 이동(간격)할 것인가? 값을 넣어주는 것은 분석하는 사람의 역할입니다. 

import numpy as np
import matplotlib.pyplot as plt

# 경사하강법 (손실을 최소화)

def  numerical_gradient_no_batch(f,x): 
# 배치단위가 아닌 미분 값을 구현되어지게 만듦. - 중앙차분(미분)
     h = 1e-4
     grad = np.zeros_like(x) 
     # _like를 가진 함수의 공통점 : _like(배열) 지정한 배열과 동일한 shape의 행렬을 만듦
     # 입력으로 전달되는 shape 과 같은 shape를 초기값을 0으로 생성

     for idx in range(x.size):
         tmp_val = x[idx] # x값을 꺼내와서 변수에 담아주었다.

         # f(x+h) 계산
         x[idx] = float(tmp_val) + h
         fxh1 = f(x)

         # f(x-h) 계산
         x[idx] = float(tmp_val) - h
         fxh2 = f(x)

         grad[idx] = (fxh1 - fxh2) / (2 * h)  # 중앙차분
         x[idx] = tmp_val

     return grad


def numerical_gradient(f,x): # 편미분 정의 함수  (함수,입력값(단항,다항))

    if x.ndim == 1 : # 단항
        return numerical_gradient_no_batch(f,x)

    else:
        grade = np.zeros_like(x) 
        # _like를 가진 함수의 공통점 : _like(배열) 지정한 배열과 동일한 shape의 행렬을 만듦
        # 입력으로 전달되는 shape 과 같은 shape를 초기값을 0으로 생성
                                 
        for idx, z in enumerate(x): 
# enumerate() : 입력으로 전달 받은 값을 자돟으로 인덱스 값을 0부터 증가하면서 x의 담긴 값을 반환
            grade[idx] = numerical_gradient_no_batch(f, x)

        return grade


def gradient_descent(f, init_x, lr, step_num): # 함수, 초기값, learning rate, 횟수 - 경사하강법
    x = init_x # 초기값
    x_history = [] # 학습을 통해서 나온 값들을 저장하여, 시각화 할 때 사용

    for i in range(step_num): # 20번 반복
        x_history.append(x.copy()) # 초기값을 가져와 저장한 이후 입력으로 전달되는 데이터 추가.

        grad = numerical_gradient(f,x) # 편미분
        x -= lr * grad

    return x, np.array(x_history) # 반복이 끝난 이후!!!!
                                  # 튜플의 가로 생략 된것이지 2개가 return이 되는 것이 아니다.

def function_2(x): # f(x0, x1) = x0^2 + x1^2
    return x[0]**2 + x[1]**2

if __name__ =="__main__":
    init_x = np.array([-3.0, 4.0]) 
    # 초기값 셋팅(내부에서 처리 될 때, float으로 처리 되기 때문에 float형(실수)로 넣어줘야한다.

    lr = 0.1 # learning rate(학습률)
    step_num = 20 # 경사하강법의 학습을 시킬 때, 학습시킬 횟수의 값(20회)

    x, x_history = gradient_descent(function_2, init_x, lr, step_num)   
    # 함수, 초기값, learning rate, 회수 / 튜플로 반환해서 튜플로 받은 것(변수 2개x)


    plt.plot([-5,5],[0,0],'--b')
    plt.plot([0, 0], [-5, 5], '--b')
    plt.plot(x_history[:,0], x_history[:,1], 'o')

    plt.xlim(-3.5, 3.5)
    plt.ylim(-4.5, 4.5)
    plt.xlabel("X0")
    plt.ylabel("X1")
    plt.show()

f(x0, x1) = x0^2 + x1^2 

경사하강법을 이용하여 (0.0)으로 조정 

 

신경망에서의 기울기

- 가중치 매개변수에 대한 손실 함수의 기울기
- 예 ) 형상이 2 x 3, 가중치 W, 손실 함수 L 인 신경망의 경우

 

각각을 독립적으로 편미분으로 수행되어지게 만들주면 최소가 되도록 만들어줍니다. 

 

신경망 (neural network)

: 인공신경망( artificial neural network, ANN)은 기계학습 인지과학에서 생물학의 신경망(동물의 중추신경계중 특히 )에서 영감을 얻은 통계학적 학습 알고리즘입니다. 

 

 

신경망은 아래와 같이 왼쪽부터 Input(입력층), Hidden(은닉층), Output(출력층)으로 표현할 수 있습니다.

은닉층은 양쪽의 입력층과 출력층과는 달리 우리 눈에는 보이지 않기 때문에 'Hidden(은닉)'이라고 합니다. 

 

 

퍼셉트론 복습

- y는 출력 신호
- x1 과 x2는 입력 신호
- w1 과 w2는 가중치(weight) : 각 신호가 결과에

  주는 영향력을 조절하는 요소로 작용
- b(편향) : 뉴런이 얼마나 쉽게 활성화 제어
                                                                                        -뉴런(혹은 노드) : 그림의 원

 

 

Bias(편향)를 명시한 퍼셉트론

 

 

y = h(b + w1x1 + w2+x2)

 

 

h(x) =  - > 0 ( x <= 0 )

          - > 1 ( x  >  0 )

 

 

 

 

 

 

 

 

퍼셉트론이 하나의 뉴런 단위로 다루어진다면 각 뉴런이 모여 하나의 뇌가 되는 것과 같은 신경망은 퍼셉트론의 하위요소로 볼 수 있습니다. 단층 퍼셉트론이 하나의 나무와 같이 신경망은 나무가 모여 숲을 이룬 것과 같은 느낌으로 이해하면 됩니다. 

 

 

 

활성화 함수 (Activation Function)

: 입력 신호의 총합을 출력 신호로 변환하는 함수 , 입력 신호의 총합이 활성화를 일으키는지를 정하는 역할합니다. 

- 활성화 함수를 통해서 비선형성을 가지게 됩니다.( 경계면의 부근의 모양을 결정 ) 

 

 

 

 

  a = b + w1x1 + w2x2 

 

  y = h(a) 

 (a는 입력 신호의 총합, h()는 활성화 함수, y는 출력)

 

 

 

 

 

 

 

 

 

 

계단함수 (step function) 

: 퍼셉트론은 활성화 함수로 step function(계단 함수)를 이용합니다. 특정 임계값을 넘기면 활성화되는 함수입니다. 아래 왼쪽(a)가 계단 함수이다. 0에서 멈추어있다가 어느 기점에서 1로 바뀝니다. 임계값을 경계로 출력이 바뀌는 활성화되며, 활성화 함수를 계단함수에서 다른 함수로 변경하는 것이 신경망의 세계로 나아가는 열쇠가 됩니다. 

 

 

 

h(x) =  0 ( x <= 0 )

          1 ( x > 0 )

 

 

 

 

 

 

 

 

 

 

 

# 계단 함수(Step Function)

import numpy as np

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

if __name__ =="__main__":
    x = step_function(3)
    print(x) # 1

    x = step_function(-3)
    print(x) # 0

 

주의 )  error : if 을 통해서 하나의 값이 비교가 되어야 하는데 배열로 다수의 값이 오기 때문에 처리 되지 않습니다. 

# 계단 함수(Step Function)

import numpy as np

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

if __name__ =="__main__":
    x = step_function(3)
    print(x) # 1

    x = step_function(-3)
    print(x) # 0

    z = np.array([-1,1,2])
    x = step_function(z)
    print(x)

 

해결점 1)  함수를 정의해서 boolean 값으로 출력.  

# 계단 함수(Step Function)

import numpy as np

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

def step_func_ndarray(x):
    y = x > 0
    print(y)


if __name__ =="__main__":
    x = step_function(3)
    print(x) # 1

    x = step_function(-3)
    print(x) # 0

    z = np.array([-1,1,2])
    step_func_ndarray(z)

 

해결점 2) int로 변환하여 0, 1 출력. 

# 계단 함수(Step Function)

import numpy as np

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

def step_func_ndarray(x):
    y = x > 0
    return y.astype(np.int)


if __name__ =="__main__":
    x = step_function(3)
    print(x) # 1

    x = step_function(-3)
    print(x) # 0

    z = np.array([-1,1,2])
    print(step_func_ndarray(z))

 

- 시각화 

x = np.arange(-5,5,0.1) # -5 ~ 4.9 까지.
y = step_func_ndarray(x

plt.plot(x,y)
plt.ylim(-0.1, 1.1)
plt.show()

 

 

 

시그모이드 함수 (sigmoid fuction)

: 신경망에서 자주 이용하는 활성화 함수로 시그모이드 함수를 이용하여 신호를 변환하고, 그 변환된 신호를 다음 뉴런에 전달합니다. 변화와 간격이 완만하게 하는데 이것이 비선형성을띄게 되는 것입니다. 

 

영역을 구분하는 일차 함수의 선형성이 완만하게 가게 되면서 굴곡이 가해질 수 있게 되었습니다. - 비선형성

import numpy as np

# - sigmoid(활성화) 함수의 특징
# 1) 음수 입력 값의 경우 양수로 변환
# 2) 0 < y < 1 사이의 연속적인 실수 값으로 변환

def sigmoid(x):
    return  1 / (1 + np.exp(-x)) # np.exp(-x) : 배열로 전달 받아도 전처리 과정 없이 한번에 수행된다

if __name__ == "__main__":
    x = np.array([-1, 0, 1, 2])
    y = sigmoid(x)
    print(y) 출력값 :  [0.26894142 0.5 0.73105858 0.88079708] - 실수 값으로 출력.

 

 

계단 함수 (Step Function)   vs    시그모이드 함수 (Sigmoid Function) 

퍼셉트론과 신경망의 주된 차이는

활성화 함수의 차이


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

 
- 즉, 퍼셉트론에서는 뉴런 사이에 0 혹은 1이 흘렀다면, 신경망에서는 연속적인 실수가 흐릅니다. 


- 두 함수 모두 비선형 함수입니다. 


- 신경망에서는 활성화 함수로 비선형

  함수를 사용해야 합니다. 

시그모이드 함수는 값을 실수형으로 가지는 것을 볼 수 있습니다. 시그모이드 함수의 매끄러움은 가중치 값을 전달할 때 좀 더 부드럽게 양을 조절해서 전달할 수 있습니다. 

 비선형 함수를 사용해야 하는 이유?

: 선형함수를 사용했을 때는 은닉층을 사용하는 이점이 없기 때문입니다.  즉, 선형함수를 여러층으로 구성한다 하더라도 이는 선형함수를 세번 연속 반복한 것에 지나지 않는다는 의미와 같기 때문입니다.  y = ax라는 선형함수가 있다고 한다면 이 것을 3층으로 구성하면 y = a(a(a(x))) 와 동일한 것으로 이는 y = a3(x)와 같습니다. 굳이 은닉층 없이 선형함수로 네트워크를 구성하는 것은 의미가 없어집니다. 

 

 

 

ReLU 함수(Rectified Linear Unit Function)


: 최근 신경망 분야에서 이용하는 함수로 입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하이면 0을 출력하는 함수입니다. 

 

 

h(x) = x ( x > 0 )

         0 ( x <= 0 )

 

 

 

 

 

 

 

 

 

 

 

import numpy as np
import matplotlib.pyplot as plt

def relu(x): #  입력이 0을 넘으면 그 입력을 그대로 출력하고, 0 이하이면 0을 출력
    return np.maximum(0, x) # np.maximum : 두 입력 중 큰 값을 선택해 반환하는 함수


if __name__=="__main__":
    print(relu(5)) #5
    print(relu(-5)) #0

    x = np.arange(-5,5,0.1) # 배열형
    y = relu(x)
    print(y)

 

- 시각화 

    x = np.arange(-5,5,0.1) # 배열형
    y = relu(x)
    print(y)

    plt.plot(x,y)
    plt.ylim(-1.0,5.5)
    plt.show()

 

 

다차원 배열

 

Vector(1차원 배열)

X = np.array([1,2,3,4,5])
print(X.shape) 출력값 : (5,) 1차원의 배열 - 열 개수 카운팅 / ndarray

 

3x2 행렬과 2x3 행렬의 내적(Matrix: 2차원 배열)

A = np.array([[1,2],[3,4],[5,6]])
print(A.shape) 출력값 :  (3, 2) 3행 2열
B = np.array(([[1,2,3],[4,5,6]]))
print(B.shape) 출력값 :  (2, 3) 2행 3열

Z = np.dot(A,B)
print(Z)

- 출력값 -
[[ 9 12 15] (1x1 + 2x4) (1x2 + 2x5) (1x3 + 2x6)
 [19 26 33] (3x1 + 4x4) (3x2 + 4x5) (3x3 + 4x6)   
 [29 40 51]] (5x1 + 6x4) (5x2 + 6x5) (5x3 + 6x6)

 

- 교환 법칙 성립 X

A = np.array([[1,2],[3,4],[5,6]]) # 3행 2열
B = np.array(([[1,2,3],[4,5,6]])) # 2행 3열 

Y = np.dot(A,B) # 3x2 - 2x3 = 3x3
Z = np.dot(B,A) # 2x3 - 3x2 = 2x2

print(Y) # 3행 3열 
- 출력값 -
[[ 9 12 15]
 [19 26 33]
 [29 40 51]]


print(Z) # 2행 2열
- 출력값 -
[[22 28]
 [49 64]]

 

- A 2차원 행렬, B 1차원 배열 곱

A = np.array([[1,2],[3,4],[5,6]]) # 3행 2열
B = np.array([7,8]) # 1행 2열로 보이지만 내적의 곱을 할 때 2행 1열로 구성된다.

print(B.shape) 출력값 : (2,) - 열
print(A.shape) 출력값 : (3, 2) - 행, 열 


print(np.dot(A,B)) 출력값 :  [23 53 83] 

: 1행 2열로 보이지만 내적의 곱을 할 때 2행 1열로 구성된다.

 

 

Array(3차원 배열 이상) 2 3 4

K = np.array([[[1,2,3,4],[5,6,7,8],[9,10,11,12]],[[13,14,15,16],[17,18,19,20],[21,22,23,24]]])
print(K .shape) 출력값 : (2, 3, 4) - 2면 3행 4열
print(K )

- 출력값 - 
[[[ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12]]

 [[13 14 15 16]
  [17 18 19 20]
  [21 22 23 24]]]

print(np.ndim(K ))  출력값 : 3(차원)

 

 

신경망의 내적

X = np.array([1,2]) # x1 = 1, x2 = 2
W = np.array([[1,3,5],[2,4,6]])
Y = np.dot(X,W) # 1행 2열 x 2행 3열 -- # Y = X * W + B

print(Y) 출력값 : [ 5 11 17] - (1x1+ 2x2) (1x3 + 2x4) (1x5 + 2x6)

 

- 각 층의 신호 전달 구현 – 입력층에서 1층으로 신호 전달

 

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

import numpy as np

# 1층에서의 활성화 함수 처리
def sigmoid(x): # 시그모이드 함수 정의
    return 1/ (1 + np.exp(-x))

# 입력층에서 1층오로의 신호 전달
X = np.array([1.0,0.5]) # x1 , x2
W1 = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]]) # 2행 3열
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 # =  A(1) = XW(1) + B(1)

Z1 = sigmoid(A1)
print(A1) 츨력값: [0.3 0.7 1.1] # 활성화 함수를 거치기 전
print(Z1) 츨력값: [0.57444252 0.66818777 0.75026011] # 활성화 함수를 거친후, 0 초과 1미만의 값.

# x : 0.3 0.7 1.1 - y : 0.57444252 0.66818777 0.75026011

 

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

W2 = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]]) # 3행 2열
B2 = np.array([0.1,0.2])

A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
print(A2) 출력값 :  [0.51615984 1.21402696]
print(Z2) 출력값 :  [0.62624937 0.7710107 ]

 

- 2층에서 3(출력층)으로의 신호 전달

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

A3 = np.dot(Z2,W3) + B3 출력값 : [0.31682708 0.69627909]

 

출력층 설계 – 항등함수와 소프트맥스 함수 구현

 

 

항등함수(identity function) : 입력을 그대로 출력, 회귀(연속적인 값) 

임시적으로 버퍼를 하는 것으로, 잠깐 보관했다가 한번에 보내주는 역할을 합니다. 

항등 함수의 정의 - 출력단 
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)
print(Y) # [0.31682708 0.69627909]    

 

입력 부터 출력까지의 모듈

import numpy as np

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

# 1층에서의 활성화 함수 처리
def sigmoid(x): # 시그모이드 함수 정의
    return 1/ (1 + np.exp(-x))

# 항등 함수의 정의 - 출력단
def identity_function(x):
    return x

# 데이터 전달
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

if __name__ == "__main__":
    network = init_network()

    x = np.array([1.0,0.5])
    y = forward(network, x)

    print(y) 출력값 : [0.31682708 0.69627909]

 

- 소프트맥스 함수

: 비율적으로 빠르게 변환할 수 있도록 도와주는 함수입니다. 

소프트맥스 함수의 특징

- 소프트맥스 함수는 0에서 1사이의 실수로 출력합니다. 
- 출력의 총합은 1 입니다.  
- 출력을 확률로 해석할 수 있습니다. 
- 신경망을 학습시킬 때는 출력층에서 소프트맥스 함수를 사용합니다. 
- 추론 단계에서는 출력층의 소프트맥스 함수를 생략하는 것이 일반적 입니다. 
    각 원소의 대소 관계는 변하지 않습니다.  
    지수 함수 y = exp(x)가 단조 증가 함수이기 때문입니다.

import numpy as np

a = np.array([0.3,2.9,4.0])
exp_a = np.exp(a)
print(exp_a) 출력값 :  [ 1.34985881 18.17414537 54.59815003] : 분자

sum_exp_a = np.sum(exp_a)
print(sum_exp_a) 출력값 :  74.1221542101633 : 분모

y = exp_a / sum_exp_a
print(y) 출력값 :  [0.01821127 0.24519181 0.73659691] 
         -> 비율 ( 1.8% , 24.5% ,73.6% )

 

소프트맥스 함수 구현 시 주의점


- 오버플로 문제 ( 출력되는 값이 아주 크게 나올 때) 
: 소프트맥스의 지수 함수를 계산할 때 어떤 중수를 더하거나 빼도 결과는 바뀌지 않는다는 것을 이용합니다. 
: 오버플로를 막을 목적으로는 입력 신호 중 최댓값을 이용하는 것이 일반적입니다. 

a = np.array([1010,1000,990])

def softmax_computer(a):
    max =  np.max(a)
    exp_a = np.exp(a-max) # 오버플로 처리 - 작은 값으로 바꿈.
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

y = softmax_computer(a)
print(y) 
출력값 : [9.99954600e-01 4.53978686e-05 2.06106005e-09]

 

 

 

 

 

넘파이 (NumPy , Numerical Python) 

: 파이썬에서 선형대수 기반의 프로그램을 쉽게 만들 수 있도록 지원하는 대표적 인 패키지로 대량 데이터의 배열 연산을 가능하게 하므로 빠른 배열 연산 속도를 보장합니다.  

 

넘파이 특징
- C/C++ 과 같은 저수준 언어 기반의 호환 API 를 제공합니다. 
- 파이썬 언어 자체가 가지는 수행 성능의 제약이 있으므로 수행 성능이 매우 중요한 부분은 C/C++ 기반의 코드로

  작성하고 이를 넘파이에서 호출하는 방식으로 쉽게 통합 가능합니다. 
- 다양한 데이터 핸들링 기능도 제공합니다. 
- 넘파이 array() 함수는 파이썬의 리스트와 같은 다양한 인자를 입력 받아서 ndarray 로 변환하는 기능을 수행합니다.


넘파이 모듈의 임포트 방법 
- import numpy as np 
- 넘파이 기반 데이터 타 입 : ndarray 

 

1. 배열 생성 

1차원 배열(Vector) 정의

arr = np.array([1,2,3])
print(arr) # [1 2 3]
print(type(arr)) # <class 'numpy.ndarray'>


출력값 
[1 2 3]

 

② 2차원 배열(Matrix) 정의

arr2 = np.array([[1,2,3],[4,5,6]]) # 2행 3열
print(arr2)

출력값 
[[1 2 3]
 [4 5 6]]
 

print(type(arr2)) 출력값 : <class 'numpy.ndarray'>
print("arr2.shape:{0}".format(arr2.shape)) 출력값 : (2, 3)

 

③ 3차원 배열(Array) 정의

 # 2면 2행 3열
arr3 = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print(arr3)

출력값 

[[[ 1  2  3]
  [ 4  5  6]]

 [[ 7  8  9]
  [10 11 12]]]

print("arr3.shape:{0}".format(arr3.shape)) 출력값: (2, 2, 3) 2면 2행 3열 

 

2. 배열 생성 및 초기화 

① zeros((행, 열)): 0으로 채우는 함수

arr_zeros = np.zeros((3,4)) 3행 4열 
print(arr_zeros)

출력값 
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]

 

ones((, )): 1로 채우는 함수

arr_ones = np.ones((2,2)) # 2행 2열
print(arr_ones)

출력값 
[[1. 1.]
 [1. 1.]]

 

③ full((행, 열), 값): 값으로 채우는 함수

arr_full = np.full((3, 4), 7) # 3행 4열
print(arr_full)

- 출력값 - 
[[7 7 7 7]
 [7 7 7 7]
 [7 7 7 7]]

 

④ eye(N): (N,N)의 단위 행렬 생성

arr_eye = np.eye(5)
print(arr_eye)

- 출력값 -
[[1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 1.]]

 

⑤ empty((행, 열)): 초기화 없이 기존 메모리 값이 들어감

arr_empty = np.empty((3,3))
print(arr_empty)

- 출력값 -
[[0.00000000e+000 0.00000000e+000 0.00000000e+000]
 [0.00000000e+000 0.00000000e+000 5.92878775e-322]
 [4.45042952e-307 2.55896735e-307 9.79101081e-307]]

 

⑥ _like(배열) 지정한 배열과 동일한 shape의 행렬을 만듦

arr_sample = np.array([[1,2,3],[4,5,6]])
arr_like = np.ones_like(arr_sample)
print(arr_like)

- 출력값 -
[[1 1 1]
 [1 1 1]]

- 종류: np.zeros_like(), np.ones_like(), np.full_like(), np.empty_like()

 

 

⑦ 배열 데이터 생성 함수

- np.linspace(시작, 종료, 개수): 개수에 맞게끔 시작과 종료 사이에 균등하게 분배

arr_linspace = np.linspace(1,10,5)
print(arr_linspace) 출력값 : [ 1. 3.25 5.5 7.75 10.  ]

plt.plot(arr_linspace, 'o') # 그래프를 그려주는 함수 마커를 원('o')으로
plt.show()                  # 만든 그래프를 보여줌.

 

- np.arange(시작, 종료, 스텝): 시작과 종료 사이에 스텝 간격으로 생성

arr_arange = np.arange(1,20,2)
print(arr_arange)
출력값 : [ 1  3  5  7  9 11 13 15 17 19]

 

3. list vs ndarray(1차원 배열(Vector))

# list
x1 = [1,2,3]
y1 = [4,5,6]
print(x1+y1) 출력값 :  [1, 2, 3, 4, 5, 6]

# ndarray(1차원 배열)
x2 = np.array([1,2,3])
y2 = np.array([4,5,6])
print(x2+y2) 출력값 : [5 7 9]

print(type(x1)) 출력값 : <class 'list'>
print(type(x2)) 출력값 :  <class 'numpy.ndarray'>


print(x2[2]) 출력값 :  요소의 참조: 3

x2[2] = 10   -  요소의 수정
print(x2)  출력값 :  [ 1  2 10]

 

- 연속된 정수 벡터의 생성

print(np.arange(10))  출력값 : [0 1 2 3 4 5 6 7 8 9]
print(np.arange(5,10)) 출력값 : [5 6 7 8 9]

x = np.array([10,11,12])
for i in np.arange(1,4): # 1~3 
    print(i) # 1~3
    print(i+x) # 1 + (([10,11,12])) , 2 + ([10,11,12]) , 3 + ([10,11,12])
    
- 출력값 -
1
[11 12 13]
2
[12 13 14]
3
[13 14 15]

 

- ndarray형의 주의점

a = np.array([1,1])
b = a  # 주소값 복사

print('a = '+ str(a)) 출력값 :  [1 1]
print('b = '+ str(b)) 출력값 : [1 1]

b[0] = 100
print('a = '+ str(a)) 출력값 : [100 1]
print('b = '+ str(b)) 출력값 : [100 1]

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

a = np.array([1,1])
b = a.copy()

print('a = '+ str(a)) 출력값 : [1 1]
print('b = '+ str(b)) 출력값 : [1 1]

b[0] = 100
print('a = '+ str(a)) 출력값 :  [1 1]
print('b = '+ str(b)) 출력값 :  [100 1]

 

- 행렬(2차원)

x = np.array([[1,2,3],[4,5,6]]) # 2행 3열
print(x)

- 출력값 - 
[[1 2 3]
 [4 5 6]]

print(type(x)) 출력값 : <class 'numpy.ndarray'>
print(x.shape) 출력값 : (2, 3) - 튜플

w, h = x.shape
print(w) 출력값 :  2
print(h) 출력값 : 3
print(x[1,2]) 출력값 : 6

x[1,2] = 10 # 요소의 수정
print(x)

- 출력값 -
[[ 1  2  3]
 [ 4  5 10]]

 

- 행렬의 크기 변경

a = np.arange(10)
print(a)
출력값 : [0 1 2 3 4 5 6 7 8 9]

a_arange = a.reshape(2,5) # 행렬의 크기를 변형 : reshape
print(a_arange)

- 출력값 - 
[[0 1 2 3 4]
 [5 6 7 8 9]]

print(type(a_arange)) 출력값 : <class 'numpy.ndarray'>
print(a_arange.shape) 출력값 :  (2, 5)

 

- 스칼라 x 행렬

스칼라 : 값이 한개인 변수 

x = np.array([[4,4,4],[8,8,8]])
scar_arr = 10 * x
print(scar_arr)

- 출력값 -  
[[40 40 40]
 [80 80 80]]

 

 

산술함술 : np.exp(x) - 지수 함수 , np.sqrt() - 루트 씌운 값, np.log() - 로그 함수 , np.round() - 반올림,
              np.mean() - 평균,        np.std() - 표준편차,       np.max() - 최대 ,      mp.min() - 최소

x = np.array([[4,4,4],[8,8,8]])
print(np.exp(x)) # 지수 함수 : y = e(x제곱)

- 출력값 -
[[  54.59815003   54.59815003   54.59815003]
 [2980.95798704 2980.95798704 2980.95798704]]

 

- 내적의 곱 

x = np.array([[1,2,3],[4,5,6]])
y = np.array([[7],[7],[7]])
print(x.dot(y))

- 출력값 -

 (2x1) 
[[ 42]    - > (1*7)+(2*7)+(3*7)
 [105]]   - > (4*7)+(5*7)+(6*7)


x = np.array([[1,2,3],[4,5,6]])
y = np.array([[7,2],[7,2],[7,2]])
print(x.dot(y))

- 출력값 - 

 (2x2)
[[ 42  12]     - > (1*7)+(2*7)+(3*7) (1*2)+(2*2)+(3*2)
 [105  30]]    - > (4*7)+(5*7)+(6*7) (4*2)+(5*2)+(6*2)

 

- 원소 접근 

data = np.array([[51,55],[14,19],[0,4]])
print(data)
print(data.shape) # (3, 2)
print(data[0][1]) # 55 행렬의 인덱스로 접근

for row in data:
    print(row)

- 출력값 - 
[51 55]
[14 19]
[0 4]


y = data.flatten() --> data를 1차원 배열로 변환(평탄화)
print(y) 출력값 :  [51 55 14 19  0  4] // ( 3 , 2 ) -> 1차원 배열

 

- 슬라이싱 

x = np.arange(10)
print(x[:5]) 출력값 : [0 1 2 3 4]
print(x[5:]) 출력값 : [5 6 7 8 9]
print(x[3:8]) 출력값 : [3 4 5 6 7]
print(x[3:8:2]) 출력값 : [3 5 7]
print(x[::-1]) 출력값 : [9 8 7 6 5 4 3 2 1 0]

y = np.array([[1,2,3],[4,5,6],[7,8,9]]) # 3행 3열
print(y[:2,1:2])  출력값 : (0,1),(1,1)

- 출력값 - 
[[2]
 [5]]

 

- 조건을 만족하는 데이터 수정

- bool 배열 사용

x = np.array([1,1,2,3,4,5,8,15]) # list -> array
print(x > 3) # array 와 비교 연산사를 사용하면 boolean 값이 출력 
출력값 : [False False False False  True  True  True  True]

y = x[x > 3] # true인 데이터만 선택된다.
print(y) 출력값 : [ 4  5  8 15]

x[x > 3] = 555 # 조건이 일치하면 555 담겨진다.
print(x) 출력값 :  [  1   1   2   3 555 555 555 555]

# - Numpy에서 np.sum 한수의 axis 이해
arr = np.arange(0,4*2*4) # start = 0, end = 31
print(len(arr)) # 32(0~31)

v = arr.reshape([4,2,4]) # 차원의 변경(depth(z축), row(x축), colum(y축)) - (4면, 2행, 4열)
print(v)

- 출력값 -
[[[ 0  1  2  3]
  [ 4  5  6  7]]

 [[ 8  9 10 11]
  [12 13 14 15]]

 [[16 17 18 19]
  [20 21 22 23]]

 [[24 25 26 27]
  [28 29 30 31]]]


print(v.shape) 출력값 : (4, 2, 4) : 4 면 2행 4열

print(v.ndim) 출력값 : v의 차원 : 3차원

print(v.sum()) 출력값 : 496 : 모든 요소의 합 : (0 ~ 31) 까지 더한 값

 

- axis 

print(v.sum(axis=0))  # axis=0 : row 축(단위)로 계산 / 면단 위로

- 출력값 -
[[48 52 56 60]    ->  (0+8+16+24) (1+9+17+25) (2+10+18+26) (3+11+19+27)
 [64 68 72 76]]   ->  (4+12+20+28) (5+13+21+29) (6+14+22+30) (7+15+23+31)


print(v.sum(axis=1)) # axis=1 : colum 축(단위)로 계산 / 행 당위

- 출력값 - 
[[ 4  6  8 10]    ->  (0+4) (1+5) (2+6) (3+7)
 [20 22 24 26]    ->  (8+12) (9+13) (10+14) (11+15)
 [36 38 40 42]    ->  (16+20) (17+21) (18+22) (19+23)
 [52 54 56 58]]   ->  (24+28) (25+29) (26+30) (27+31)


print(v.sum(axis=2)) # axis=2 : depth 축(단위)로 계산 / 열 단위

- 출력값 -
[[  6  22]   ->  (0+1+2+3) (4+5+6+7)
 [ 38  54]   ->  (8+9+10+11) (12+13+14+15)
 [ 70  86]   ->  (16+17+18+19) (20+21+22+23)
 [102 118]]  ->  (24+25+26+27) (28+29+30+31) 

 

4. 그래프 그리기 

- 데이터 준비

x = np.arange(0,6,0.1) # start 0 ~ last 5 , step = 0.1 (0.1~5.9)
y1 = np.sin(x) # 사인함수 를 이용하여 0.1단위로 계산해서 y1에 저장
y2 = np.cos(x) # 코사인함수 를 이용하여 0.1단위로 계산해서 y2에 저장

 

- 그래프 그리기

plt.plot(x, y1, label = "sin")
plt.plot(x, y2, linestyle = "--", label = "cos") # cos 함수는 점선으로 그리기
plt.xlabel("x") # x축 이름
plt.ylabel("y") # y축 이름
plt.title("sin & cos") # 제목
plt.legend() # 범례
plt.show() # 출력

 

- 꺽은선 그래프

x = np.arange(10)
y = np.random.rand(10)
plt.plot(x,y) # 꺽은선 그래프를 등록 / 10개의 난수를 plot 함수를 이용하여 꺽은석 그래프로 피드백.
plt.show() # 그래프 그리기

 

- 3차 함수 f(x) = (x-2) x (x+2)

def f(x):
    return (x-2) * x * (x+2) # 3차 함수 정의

print(f(0)) 출력값 :  0
print(f(2)) 출력값 : 0
print(f(-2)) 출력값 : 0

# x값에 대해 ndarray  배열이며 각각에 대한 f를 한꺼번에 ndarray로 돌려줍니다.
print(f(np.array([1,2,3]))) 출력값 : [-3  0 15]
print(type(f(np.array([1,2,3])))) 출력값 : <class 'numpy.ndarray'>

#  그래프를 그리는 x의 범위를 -3 ~ 3까지로 하고 , 간격 0.5
x = np.arange(-3,3.5,0.5)
plt.plot(x,f(x))
plt.show()

 

- 그래프를 장식

x = np.linspace(-3,3,100) # x를 100 분할하기

# 차트 묘사
plt.plot(x, f2(x,2), color = "black")
plt.plot(x, f2(x,1), color = "blue")
plt.legend(loc="upper left") # 범례의 위치도 정해줄 수 있다.
plt.ylim(-15,15) # y축 범위
plt.title("f2(x, w) 함수")
plt.xlabel("x축")
plt.ylabel("y축")
plt.grid(True) # 그리드(눈금)
plt.show()

 

- 그래프를 여러 개 보여주기

plt.figure(figsize=(10,3)) # 전체 영역의 크기를 지정
plt.subplots_adjust(wspace=0.5, hspace=0.5) # 그래프의 간격을 지정

for i in range(6):  # 0 ~ 5
    plt.subplot(2, 3, i+1) # 그래프 위치를 지정 # 2행 3열
    plt.title(i+1)
    plt.plot(x,f2(x,i))
    plt.ylim(-20,20)
    plt.grid(True)

plt.show()

 

- 이미지 표시하기

from matplotlib.image import imread

img = imread('image/2.png') # 이미지 읽어오기

plt.imshow(img)
plt.show()

 

퍼셉트론(perceptron)

- 프랑크 로젠블라트(Frank Rosenblatt)가 1957년에 고안한 알고리즘


- 신경망(딥러닝)의 기원이 되는 알고리즘


- 퍼셉트론의 구조를 배우는 것은 신경망과 딥러닝으로 나아가는 데 중요한 아이디어를 배우는 일

 

- 퍼셉트론은 모든 학습 데이터를 정확히 분류시킬 때까지 학습이 진행되기 때문에 학습 데이터가 선형적으로 분리될 수

  있을 때 적합한 알고리즘

 

출처: https://www.slideshare.net/jbhuang/lecture-29-convolutional-neural-networks-computer-vision-spring2015

 

● 퍼셉트론(perceptron : 인공 뉴런 / 단순 퍼셉트론)

- 다수의 신호를 입력으로 받아 하나의 신호를 출력
- 신호 : 전류나 강물처럼 흐름이 있는 것을 상상
- 퍼셉트론 신호도 흐름을 만들고 정보를 앞으로 전달
- 퍼셉트론 신호는 ‘흐른다(1로 표현) / 안 흐른다(0으로 표현)의 두 가지 값으로 표현

 

 

퍼셉트론의 동작 방식  

: 각 노드의 가중치와 입력치를 곱한 것을 모두 합한 값이 활성함수에 의해 판단되는데, 그 값이 임계치(보통 0)보다 크면 뉴런이 활성화되고 결과값으로 1을 출력합니다. 뉴런이 활성화되지 않으면 결과값으로 -1을 출력합니다. 

 

- 입력으로 2개의 신호를 받은 퍼셉트론의 예

- x1 과 x2는 입력 신호
- y는 출력 신호
- w1 과 w2는 가중치(weight) : 각 신호가 결과에 주는 영향력을

                                        조절하는 요소로 작용

- 각 입력신호에는 고유한 weight가 부여되며 weight가 클수록

  해당 신호가 중요 
- 뉴런(혹은 노드) : 그림의 원
- θ(theta) : 임계값(뉴런의 활성화 기준값)

  θ : 흐르는지 흐르지 않는지에 대한 기준이 되는 값    

  θ < y  신호가 흐른다 / θ >= y 신호가 흐르지 않는다. 

 

 

- 신호가 흐르지 않는다.

 

- 신호가 흐른다. 

 

 

퍼셉트론의 출력 값은 앞에서 말했듯이 1 또는 0(or -1)이기 때문에 선형 분류(linear classifier) 모형이라고도 볼 수 있습니다. 보통 실수형의 입력 벡터를 받아 이들의 선형조합을 계산하는 것이며, 선형 분류 평면 상에 선을 쫙 그어서 여기 넘으면 A, 못 넘으면 B 이런식으로 분류합니다. 


y = ax + b 의 기원이 되는 것으로 a(기울기) = w 값,  b(절편) = x 값 입니다. 

y = w1x1 + w2x2 , 2개의 feature(colum) 를 통해서 계산 하는 것입니다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

- 컴퓨터에서 가장 많이 존재합니다. 변화가 일어날때만 출력하기 때문에 일정한 상태가 지속되어 질때, 이벤트를 감지합니다. 

 

 

 

 

 

 

 

 

컴퓨터의 cpu가 위의 모습으로 되어 있기 때문에 이러한 모습을 컴퓨터의 언어로 만들어준 시초가 퍼셉트론으로  빠른 명령을 내려서 피드백 받을 수 있도록 하기 위한 것이었다. 

 

 

perceptron -> 신경망 : 20년이라는 과도기가 있었습니다. 

gate - AND , NAND , OR , NOR

 

1차 암흑기) 

 

 

- ○ (동그라미) 와 △ (세모)는 일차 함수로 나눌 수 없었기 때문에 과도기가 존재했습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

AND , NAND , OR 를 구성하여 XOR를 구현 

 

 

 

문제 해결

: 단층으로 선형으로만 가능할 것이라는 고정관념에서 다층으로 가져감으로 비선형성을 가지는 것을 발견되었습니다. 

 

 

노드를 다층으로 가져가므로 물리적으로  gate를 조합함으로 분류가 가능하도록 되었다. 비선형의 모형으로 구분할 수 있도록 합니다. 

 

- 다층(선형) -> 다층(비선형) 

 

 

 

 

 

 

 

 

 

- 다층 구조 

XOR gate : 다층 퍼셉트론

 

다층 퍼셉트론의 동작 설명


 1. 0층의 두 뉴런이 입력 신호를 받아 1층의 뉴런으로 신호를 보냅니다. 
 2. 1층의 뉴런이 2층의 뉴런으로 신호를 보내고, 2층의 뉴런은이 입력신호를 바탕으로 y를 출력합니다. 
 3. 단층 퍼셉트론으로는 표현하지 못한 것을 층을 하나 늘려 구현할 수 있었습니다. 
 - 퍼셉트론은 층을 쌓아(깊게하여) 더 다양한 것을 표현할 수 있습니다. 

 

 

 

 

1) 단순한 논리 회로 – AND 게이트

 - 퍼셉트론을 활용한 간단한 문제 적용
 - AND 게이트를 퍼셉트론으로 표현하기
 - 즉, w1, w2, θ 의 값 구하기

 - 사람이 매개변수 값을 정함

 

 

 

 

 

 

def AND(x1, x2):
    w1, w2, theta = 0.5, 0.5, 0.7 # 사람이 매개변수의 값을 정함.
    tmp = w1 * x1 + w2 * x2

    if tmp <= theta:
        return 0
    else:
        return 1
if __name__ == "__main__":
    for xs in [(0,0),(0,1),(1,0),(1,1)]:
        y = AND(xs[0],xs[1])
        print(str(xs)+" : "+ str(y))

 

앞의 퍼셉트론 수식에서 나오는 세타θ를 -b로 치환하여 좌변으로 넘기면 

 

    b + w1x1 + w2x2 <0    => 0

    b + w1x1 + w2x2 >=0  => 1

 

과 같이 되며 여기에서 b를 편향(bias)라고 할 수 있습니다. 

 

import numpy as np

def AND(x1, x2):
    x = np.array([x1,x2]) # 배열형으로 형변환 해서 x에 담다아줌
    w = np.array([0.5,0.5])
    b = -0.7

    tmp = np.sum(w * x) + b # b(bias : 편향/절편)
    if tmp <=0:
        return 0
    else:
        return 1

if __name__ == "__main__":
    result = AND(0,0)
    print(result) # 0 
    result = AND(0,1)
    print(result) # 0
    result = AND(1,0)
    print(result) # 0
    result = AND(1,1)
    print(result) # 1

 

2) 단순한 논리 회로 – NAND 게이트

- 퍼셉트론을 활용한 간단한 문제 적용
- NAND(Not AND) 게이트를 퍼셉트론으로 표현하기
- 즉, w1, w2, θ 의 값 구하기 – 사람이 매개변수 값을 정함.

- 실제 하드웨어의 동작(부하를 최소화 하기 위해 반대로 동작)

   의미를 가질 때 : 0 

   의미를 가지지 않을 때 : 1 

 

 

 

 

import numpy as np

# NAND Gate - AND의 반대
def NAND(x1, x2):
    x = np.array([x1,x2])
    w = np.array([-0.5,-0.5])
    b = 0.7 # bias

    tmp = np.sum(w * x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

if __name__ == "__main__":
    result = NAND(0,0)
    print(result)
    result = NAND(1,0)
    print(result)
    result = NAND(0,1)
    print(result)
    result = NAND(1,1)
    print(result)

 

3) 단순한 논리 회로 – OR 게이트

-퍼셉트론을 활용한 간단한 문제 적용
- OR 게이트를 퍼셉트론으로 표현하기
- 즉, w1, w2, θ 의 값 구하기

– 사람이 매개변수 값을 정함

 

 

 

 

 

 

def OR(x1, x2):
    x = np.array([x1,x2])
    w = np.array([0.5,0.5])
    b = -0.2
    
    tmp = np.sum(w * x) + b
    if tmp <=0:
        return 0
    else: 
        return 1


if __name__ == "__main__":
    result = OR(0,0)
    print(result)  # 0
    result = OR(1, 0)
    print(result)  # 1
    result = OR(0, 1)
    print(result)  # 1
    result = OR(1, 1)
    print(result)  # 1

 

 

4) XOR gate - 다층 퍼셉트론 

 

import numpy as np

# AND gate
def AND(x1, x2):
    x = np.array([x1,x2]) # 배열형으로 형변환 해서 x에 담다아줌
    w = np.array([0.5,0.5])
    b = -0.7

    tmp = np.sum(w * x) + b # b(bias : 편향/절편)
    if tmp <=0:
        return 0
    else:
        return 1
# NAND gate
def NAND(x1, x2):
    x = np.array([x1,x2])
    w = np.array([-0.5,-0.5])
    b = 0.7 # bias

    tmp = np.sum(w * x) + b
    if tmp <= 0:
        return 0
    else:
        return 1
# OR gate
def OR(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.2

    tmp = np.sum(w * x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

def XOR(x1, x2):
    s1 = NAND(x1,x2)
    s2 = OR(x1,x2)
    y  = AND(s1,s2)

    return y

if __name__ == "__main__":
    print(XOR(0,0)) # 0
    print(XOR(0,1)) # 1
    print(XOR(1,0)) # 1
    print(XOR(1,1)) # 0

+ Recent posts