728x90
반응형

연립일차방정식과 가우스 소거법

  • 앞서 행렬은 연립일차방정식의 계수와 변수를 분리하여 사용하게 되면서 등장하였다고 했다.
  • 앞서 행렬의 기본적인 성질은 배웠으니, 이제 연립일차방정식과 행렬이 어떻게 연결되는지에 대해 알아보자.
  • 연립일차방정식을 통해 첨가 행렬을 만들어보고, 가우스 소거법으로 연립일차방정식을 풀이해보자.

 

 

 

 

1. 연립일차방정식(System of Linear equations)

1.1. 일차방정식(Linear equation)

  • 미지수 $x_1, x_2, x_3, ..., x_n$에 관한 일차방정식은 상수 $b$와 계수 $a_1, a_2, a_3, ..., a_n$이 실수일 때, 다음과 같은 꼴로 나타나는 방정식이다.
  • 일차방정식이므로, 미지수 $x_1, x_2, x_3, ..., x_n$의 차수는 1이다.

$$a_1x_1 + a_2x_2 + a_3x_3 + \cdots a_nx_n = b$$

 

1.2. 연립일차방정식(System of Linear equations)

  • 미지수 $x_1, x_2, x_3, ..., x_n$에 대하여 유한개의 일차방정식 모임은 아래와 같다.

$$a_{11}x_1 + a_{12}x_2 + a_{13}x_3 + \cdots a_{1n}x_n = b_1$$

$$a_{21}x_1 + a_{22}x_2 + a_{23}x_3 + \cdots a_{2n}x_n = b_2$$

$$a_{31}x_1 + a_{32}x_2 + a_{33}x_3 + \cdots a_{3n}x_n = b_3$$

$$ \vdots $$

$$a_{m1}x_1 + a_{m2}x_2 + a_{m3}x_3 + \cdots a_{mn}x_n = b_m$$

  • 실수 $b_1, b_2, b_3, ..., b_m$이 모두 0이면 이 연립방정식을 동차(Homogeneous)라 하며, 반대의 경우에는 비동차(non-Homogeneous)라 한다.
  • 미지수 $x_1, x_2, x_3, ..., x_n$에 대하여 어떤 수 $s_1, s_2, s_3, ..., s_n$을 각각 대입하여, 각 방정식이 모두 성립하면 어떤 수 $s_1, s_2, s_3, ..., s_n$을 연립일차방정식의 해(Solution)이라 한다.
  • 연립일차방정식의 해 전체 집합을 연립일차방정식의 해집합(Solution set)이라 한다.
  • 동일한 해집합을 가지는 두 연립일차방정식을 동치(Equivalent)라고 한다.
  • 연립일차방정식의 해에 대하여, 일반적으로 다음 중 하나가 성립한다.
  1. 해를 갖지 않는다.
  2. 유일한 해를 갖는다.
  3. 무수히 많은 해를 갖는다.

 

1.3. 연립일차방정식과 행렬

  • 위 연립일차방정식을 행렬로 나타내면 다음과 같다.

$$A = \begin{pmatrix}
 A_{11}& A_{12} & A_{13}  & ... & A_{1n}\\ 
 A_{21}& A_{22} & A_{23}  & ... & A_{2n}\\ 
 A_{31}& A_{32} & A_{33}  & ... & A_{3n}\\ 
 \vdots & \vdots & \vdots & \ddots & \vdots\\
 A_{m1}& A_{m2} & A_{m3}  & ... & A_{mn}\\
\end{pmatrix},\ \ \  

X = \begin{pmatrix}
x_1\\ 
x_2\\ 
x_3\\ 
\vdots \\
x_n
\end{pmatrix},\ \ \ 

B = \begin{pmatrix}
b_1\\ 
b_2\\ 
b_3\\ 
\vdots \\
b_m
\end{pmatrix}$$

$$AX = B$$

  • 우리가 지금까지 봐왔던, 행렬은 바로 $A$로 이를 계수행렬(Coefficient matrix)라 한다.

 

 

 

 

2. 첨가행렬(Augmented matrix)

  • 계수행렬 $A$와 상수항들이 모여서 만들어진 행렬$B$를 붙이면 첨가행렬(Augmented matrix)가 만들어진다.

$$(A | B) = \begin{pmatrix}
 A_{11}& A_{12} & A_{13}  & ... & A_{1n} | b_1\\ 
 A_{21}& A_{22} & A_{23}  & ... & A_{2n} | b_2\\ 
 A_{31}& A_{32} & A_{33}  & ... & A_{3n} | b_3\\ 
 \vdots & \vdots & \vdots & \ddots & \ \ \ \vdots \ \ | \ \vdots \\
 A_{m1}& A_{m2} & A_{m3}  & ... & A_{mn} | b_m\\
\end{pmatrix}$$

  • 첨가행렬은 행렬 방정식의 풀이와 역행렬 구하기에 응용된다.
  • 가우스 소거법을 사용하여 연립일차방정식을 풀이해보자.

 

 

 

 

3. 가우스 소거법(Gaussian elimination)

  • 선형대수학에서, 가우스 소거법은 연립일차방정식을 풀이하는 알고리즘으로, 풀이 과정에서 일부 미지수가 차츰 소거되어, 남은 미지수에 대한 선형 결합으로 풀이가 완성된다.
  • 가우스 소거법은 보통 행렬을 사용하며, 첨가 행렬을 그와 풀이가 더 간단한 행렬로 변환하여 풀이를 완성한다.
  • 가우스 소거법은 행렬식과 역행렬의 계산에도 응용된다.
  • 가우스 소거법은 맨 위 일차방정식부터 임의의 k를 곱해가며 아래에 있는 일차방정식들을 하나하나 맨 앞의 계수부터 0으로 만들어가는 방법으로, 최종적으로 주대각선의 모든 값이 1인 상삼각행렬을 만들게 된다.
  • 내용이 조금 길기 때문에 손으로 풀어보겠다.

  • 가우스 소거법은 위에서부터 천천히 아래로 내려가면서, 주대각성분이 1인 상삼각행렬을 만들어가면 된다.
  • $①$, 최초 첨가행렬에서 첫 번째 행 $R_1$의 첫 원소인 $A_{11}=4$는 주대각성분이므로 이를 1로 만들어주기 위해, $R_1$에 $\frac{1}{4}$를 곱해주어, $A_{11}=1$로 만들어주었다.
  • $②$, 두 번째 행 $R_2$의 첫 원소인 $A_{21}=2$을 0으로 만들어주기 위해, $R_1$의 인자들에 $-2$를 곱하여 $R_2$에 더해주었다.
  • $③$, 세 번째 행 $R_3$의 첫 원소인 $A_{31}=3$을 0으로 만들어주기 위해, $R_1$의 인자들에 $-3$을 곱하여 $R_3$에 더해주었다.
  • $④$, 두 번째 행 $R_2$의 두 번째 원소인 $A_{22}=\frac{1}{2}$은 주대각성분이므로, $1$로 만들어주기 위해, $2$를 $R_2$에 곱해주었다.
  • $⑤$, 세 번째 행 $R_3$의 두 번째 원소인 $A_{32}=-\frac{33}{4}$를 0으로 만들어주기 위해, $R_2$에 $\frac{33}{4}$를 곱하여, $R_3$에 더해주었다.
  • 이를 통해, 연립일차방정식의 해인 $x=2,\ y=-1,\ z=3$을 찾았다.

 

 

 

 행렬이 연립일차방정식에서 어떻게 나오게 되었고, 행렬을 이용해서 연립일차방정식의 해를 쉽게 찾을 수 있는 가우스 소거법에 대해 간단하게 학습해보았다.

 다음 포스트에서는 가우스 소거법에 대해 좀 더 알아보도록 하자.

728x90
반응형

'Python으로 하는 기초 수학 > 행렬' 카테고리의 다른 글

역행렬(Inverse matrix)  (0) 2021.02.26
영인자(Zero Divisor)  (0) 2021.02.25
행렬의 성질  (0) 2021.02.25
행렬(Matrix)  (0) 2021.02.25
728x90
반응형

역행렬(Inverse matrix)

  • $AA^{-1} = A^{-1}A = E$
  • 선형대수학에서 가역행렬(Invertible matrix)은 그와 곱한 결과가 단위행렬인 행렬$A^{-1}$을 갖는 행렬$A$를 말한다.
  • 이 행렬$A^{-1}$를 행렬 $A$의 역행렬이라고 한다.
  • 고등학교 행렬 문제에서는 교환법칙의 성립 유/무를 묻는 문제가 많이 나온다.
  • 수능 유형 예시)

$$A(A+B)=E \rightarrow AB = BA$$

$$put) A(A + B) = E = (A+B)A\ \rightarrow \ A^2+AB=E=A^2+BA \ \rightarrow \  AB=BA$$

 

 

 

 

1. 역행렬을 구하는 방법

  • 행렬 A가 이차 정사각 행렬일 땐 다음 방법으로 구할 수 있다.

$$A = \begin{pmatrix} a & b\\ c & d \end{pmatrix},\ \ \ A^{-1}=\frac{1}{ad-bc}\begin{pmatrix} d & -b\\ -c & a \end{pmatrix}$$

  • 위 역행렬을 구하는 방법에서 $ad-bc=0$인 경우, 분모가 0이 되므로 역행렬을 만들 수 없다.
  • 즉, $ad-bc$의 값을 통해 역행렬이 존재하는지를 확인할 수 있다.
  • $ad-bc \neq 0$: $A^{-1}$이 존재한다.
  • $ad-bc = 0$: $A^{-1}$이 존재하지 않는다.
  • Python으로 역행렬을 구해보자.
>>> mat = np.array([[2, 1, 5],[0,0,1],[-1,0,2]])
>>> inv_mat = np.linalg.inv(mat)
>>> np.dot(mat, inv_mat)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
  • 만약 역행렬이 존재하지 않는 행렬이라면, 다음과 같은 오류를 반환한다.
>>> mat = np.array([[2, 1],[2,1]])
>>> inv_mat = np.linalg.inv(mat)
---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
<ipython-input-12-72ac304bfc31> in <module>
      1 mat = np.array([[2, 1],[2,1]])
----> 2 inv_mat = np.linalg.inv(mat)

...

---> 88     raise LinAlgError("Singular matrix")
     89 
     90 def _raise_linalgerror_nonposdef(err, flag):

LinAlgError: Singular matrix

 

 

 

 

2. 이차 정사각행렬 역행렬 공식의 증명

  • 역행렬 공식의 증명은 가우스 조던 소거법(Gauss-Jordan elimination method)로 구하는 방법과 전치행렬, 소행렬, 여인자를 이용해서 구하는 방법 두 가지가 있다.
  • 그러나 이차 정사각행렬은 비교적 공식이 간단하므로, 옳바른 증명 방법은 아니긴 하지만, 위 기법들을 사용하지 않고도 구할 수 있다.
  • 행렬 $A$와 역행렬 $A^{-1}$을 다음과 같이 정의하자.

$$A=\begin{pmatrix} a & b\\ c & d \end{pmatrix},\ \ A^{-1}=\begin{pmatrix} x & y\\ z & r \end{pmatrix}$$

$$AA^{-1}=E \rightarrow \begin{pmatrix} a & b\\ c & d \end{pmatrix}\begin{pmatrix} x & y\\ z & r \end{pmatrix} = \begin{pmatrix}1 & 0\\ 0 & 1 \end{pmatrix}$$

$$ax + bz = 1 \cdots ①$$

$$ay + br = 0 \cdots ②$$

$$cx + dz = 0 \cdots ③$$

$$cy + dr = 1 \cdots ④$$

  • 위 식을 기반으로 $(ad-bc)$가 만들어지도록 4개의 식을 유도해보자

$$①*c - ③*a = (acx+bcz-c)-(acx+adz)=0\  \rightarrow \ (bc-ad)z = c \cdots ⑤$$

$$①*d-③*b = (adx+bdz-d)-(bcx+bdz)=0\ \rightarrow \ (ad-bc)x = d \cdots ⑥$$

$$②*c-④*a=(acy+bcr)-(acy+adr-a)=0\ \rightarrow \ (bc-ad)r = -a \cdots ⑦$$

$$②*d-④*b=(ady+bdr)-(bcy+bdr-b)=0\ \rightarrow \ (ad-bc)y = -b \cdots ⑧$$

  • ⑤, ⑥, ⑦, ⑧을 볼 때, $ad-bc \neq 0$이다.
  • $ad-bc=0$인 경우, 행렬 $A$가 영행렬이기 때문에 전제가 성립하지 않는다.
  • ⑤, ⑥, ⑦, ⑧의 양변을 $ad-bc$로 나눠보자.

$$⑤ \rightarrow z = \frac{-c}{ad-bc}$$

$$⑥ \rightarrow x = \frac{d}{ad-bc}$$

$$⑦ \rightarrow r = \frac{a}{ad-bc}$$

$$⑧ \rightarrow y = \frac{-b}{ad-bc}$$

  • ⑤, ⑥, ⑦, ⑧으로부터 유도된 $x,y,z,r$를 $A^{-1}$에 대입하자.

$$A^{-1}=\frac{1}{ad-bc} \begin{pmatrix} d & -b\\ -c & a \end{pmatrix}$$

  • 이차 정사각행렬의 역행렬을 구하는 공식이 유도 되었다.

 

 

 

 

3. 역행렬의 성질

3.1. $(A^{-1})^{n} = (A^{n})^{-1}$

  • $A$의 역행렬이 존재하면, $A^n$의 역행렬도 존재한다.
  • 즉, $A^{100}$의 역행렬이 존재한다면, $A^{20}$의 역행렬도 존재한다는 소리다.

 

3.2. $(kA)^{-1} = \frac{1}{k}A^{-1}$

  • 역원의 개념이므로, 상수 k가 뒤집어진다.
  • $k=0$인 경우, 어차피 영행렬이므로, 역행렬이 존재하지 않는다.

 

3.3. $(AB)^{-1} = B^{-1}A^{-1},\ \ (APB)^{-1} = B^{-1}P^{-1}A^{-1}$

 

3.4. 차수가 같은 두 행렬 A,B 모두 역행렬이 존재한다면, AB역시 역행렬이 존재한다.

  • 역, 대우 모두가 참인 성질이다.
  • 역) A, B중 적어도 하나가 역행렬이 존재하지 않는다면, AB의 역행렬 또한 존재하지 않는다.
  • 대우) AB가 역행렬이 없으면, A, B 중 적어도 하나는 반드시 역행렬이 존재하지 않는다.
728x90
반응형

'Python으로 하는 기초 수학 > 행렬' 카테고리의 다른 글

연립일차방정식과 가우스 소거법(Gaussian elimination)  (0) 2021.02.26
영인자(Zero Divisor)  (0) 2021.02.25
행렬의 성질  (0) 2021.02.25
행렬(Matrix)  (0) 2021.02.25
728x90
반응형

통계학이란?


"통계학이란 불확실한 상황에서 현명한 의사결정을 하기 위한 이론과 방법의 체계이며, 통계학은 자료의 수집·분류·분석과 해석의 체계를 갖는다."
 W.Allen Walls and Harry V.Roberts, Statistics: A New Approach(Glence, Ill., Free Press, 1956), p. 3.
(박정식, 윤영선, 박래수(2010) 현대통계학 제5판 참고)

"통계학은 실증적인 뿌리를 가지고 있으며 실질적 활용에 초점을 맞추고 있기 때문에, 순수수학과 구분되는 응용수학의 일종으로 여겨진다. 통계학의 방법을 통해, 실제 수치들을 왜곡하여 해석하는 것을 막고 연구를 바탕으로 합리적인 의사결정을 할 수 있다."
 Moore, David(1992). <Teaching Statistics as a Respectable Subject>. F. Gordon and S. Gordon. <Statistics for the Twenty-First century>. Washington, DC: The Mathematical Association of America. 14-25쪽. ISBN 978-0-88385-078-7.
(Wikipidia 통계학 참고)


  • 통계학은 Data를 다루는 학문으로, 연구자가 관심 있는 대상을 나타낼 수 있는 다량의 Data를 수집하는 것부터, 그 Data의 모양을 파악하고, 자신의 연구 모델에 맞게 Data를 체계화시켜, 그 안에 숨어 있는 패턴을 찾아내는 모든 과정에 대한 학문이라고 할 수 있다.
  • 통계학은 머신러닝, 딥러닝을 비롯한 데이터를 다루는 각종 학문의 근간이 되므로, 통계학을 제대로 이해하지 못한다면, 다른 학문들의 원리를 제대로 이해할 수 없다.
  • 통계학은 기본적으로 데이터가 어디에 모여 있고, 얼마나 흩어져 있는지를 통해서 데이터를 파악한다.

 

 

 

 

1. 모집단과 표본집단


 모집단(Population): 연구자의 관심 대상이 되는 모든 개체의 집합으로, 연구 대상이 되는 데이터의 전체를 가리킨다.

◎ 표본집단(Sample): 모집단에서 추출해낸 개체의 집합이다.


  • 모집단과 표본집단 예시 1)
    • 연구자가 사람의 발 크기와 키에 대해 어떠한 경향성(Pattern)이 존재할 것이라고 생각했다고 해보자. 이를 확인하고자 한다면, 전 세계에 있는 모든 사람들(모집단)을 대상으로, 데이터를 모아야 할 것이다.
    • 그러나, 이는 막대한 비용과 시간이 소모되기 때문에 불가능한 일이다. 때문에 연구자는 인종, 연령, 성별 등 다양한 변수들을 고려하여, 모집단을 대표할 수 있는 표본집단(Sample)을 추출하여, 모집단을 추정한다.

 

  • 모집단과 표본집단 예시 2)
    • 참치 통조림을 만드는 공장에서 제품에 이상이 있는지 확인하기 위해, X-ray를 비롯한 다양한 도구를 이용해서 전수 조사하였으나, 실제로 제품을 열어서 확인해보는 것이 가장 정확한 결과를 가져온다고 가정해보자.
    • 모든 통조림을 열어서 확인해보는 것이 품질이 떨어지는 제품이 얼마나 발생하는지 알 수 있겠지만, 그렇게 했다간 통조림을 팔 수 없게 될 것이다. 때문에 모집단인 모든 통조림에서 통조림을 일부만 추출(표본집단)해서 모집단을 추정하게 된다.

 

  • 모집단과 표본집단 예시 3)
    • 당신이 통계 유치원 선생님이고, 기린반 어린이 30명과 해바라기반 어린이 25명과 함께 학예회 준비로 동요를 부르려고 하는데, 아이들이 뽀로로 주제가와 핑크퐁 아기 상어 중 무엇을 더 좋아할지를 몰라 이에 대해 고민하고 있다고 가정하자.
    • 당신은 기린반 어린이와 해바라기반 어린이들을 대상으로 선호하는 캐릭터에 대해 조사를 할 수 있다. 이 경우 당신의 관심 대상인 기린반, 해바라기반 어린이들은 55명으로 매우 적기 때문에 모집단 전체에 대해 조사를 할 수도 있다.

 

  • 모집단은 연구자의 마음에 따라 그 규모가 크게 달라지기 때문에, 예시 3)처럼 모집단의 규모가 작다면, 모집단 전체를 대상으로 분석을 할 수 있다. 즉, 반드시 표본집단을 통해서 통계 분석을 진행하는 것은 아니다.
  • 그러나, 모집단이 작은 경우만 존재하지는 않기 때문에, 모집단을 가장 잘 대표할 수 있는 표본집단을 뽑아, 통계적 기법을 통해 모집단의 성격을 추정하는 것이 현대 통계학의 주류다.

 

 

 

 

2. 모수와 통계량


 모수(Parameter): 모집단의 특성을 수치로 나타낸 것이다.

 통계량(Statistic): 표본의 특성을 수치로 나타낸 것이다.


  • 대전 여자 중학생의 평균 팔 굽혀 펴기 횟수를 알고 싶다고 가정해보자.
    • 대전에 사는 여자 중학생들의 평균 팔 굽혀 펴기 횟수를 알기 위해, 완전 무작위로 각 중학교의 학년별 한 반의 여중생들을 대상으로 평균 팔 굽혀 펴기 횟수를 구했다.
    • 모집단인 대전에 사는 모든 여자 중학생의 평균 팔 굽혀 펴기의 횟수(모수)와 표본집단의 평균 팔 굽혀 펴기 횟수(통계량)가 같다고 할 수 있을까?
    • 우연히 샘플로 추출된 여중생들이 팔 굽혀 펴기를 유난히 잘하는 사람들이어서, 모수보다 통계량이 더 클 수도 있고, 반대로 우연히 운동을 너무 싫어하는 사람들이 뽑혀서 통계량이 모수보다 더 작을 수도 있다.
    • 즉, 모수와 통계량은 같은 값이 아니다.

 

  • 모집단의 특성을 나타내는 모수를 구하는 것이 가장 정확하겠으나, 앞서 봤듯 모수를 구하지 못하는 상황에서는 표본집단을 통해 모수를 추정해야 하고, 이때 표본집단의 특성을 나타내는 통계량을 이용하게 된다.
  • 모수나 통계량은 앞서 말한 평균뿐만이 아니라, 각 집단을 나타낼 수 있는 다른 특성인 중위수, 최빈값, 최솟값, 최댓값, 분산, 표준편차 등도 다양한 지표들을 포함하는 개념이다.
  • 모수는 그리스 문자($\mu,\ \sigma^2, \sigma$)로 표기하고, 통계량은 영어 알파벳($\bar{X}, S^2, S$)으로 표기하는 것이 관례다.
  • 단, 모집단의 크기인 개체의 양은 $N$으로, 표본집단의 크기는 $n$으로 표기하는 것이 관례다.

 

 

 

 

3. 기술통계학과 추론통계학


◎ 기술통계학(Descriptive statistics): 데이터를 정리하고 요약하여, 데이터의 특성을 찾아 서술하는 통계학이다. 

◎ 추론통계학(Inferential statistics): 모집단으로부터 추출한 표본집단의 특성(통계량)을 기반으로, 모집단 특성(모수)을 추측해내는 통계학이다.


  • 기술통계학(Descriptive statistics)과 추론통계학(Inferential statistics)은 별개의 것이 아니며, 서로 연결되어 순차적으로 진행된다.
  • 데이터를 분석하는 과정은 연구설계부터 자료수집, 자료 정리, 자료해석을 통해 모집단 또는 표본집단의 특성을 파악하는 기술통계학을 우선 실시한다.
  • 표본을 대상으로 한 경우 표본에서 얻어진 통계량을 기초로 추론통계학을 실시해 모집단의 특성인 모수를 추정하게 된다.
  • 즉, 기술통계량은 모집단, 표본집단 모두를 대상으로, 데이터의 특성(모수, 통계량)을 찾아내고, 대상 집단이 표본집단인 경우 추론통계학을 실시하여, 모수를 추정하게 된다.

 

  • 기술통계학과 추론통계학의 예시)
    • 기술통계학
      우리나라 남성들의 평균 키를 알기 위해, 모집단인 우리나라의 모든 남성들의 신장 Data를 수집하거나, 이는 비효율적이므로 일부를 표본집단으로부터 신장 Data를 추출하여 표본집단의 평균 키나 키의 표준편차를 구하여, 데이터의 특성을 파악한다.
    • 추론통계학
      표본집단에서 구해진 통계량인 평균 키, 키의 표준편차를 기반으로 모집단인 우리나라 모든 남성들의 평균 키(모수)를 추정한다.

 

 

 

 

4. 모수통계학과 비모수통계학


◎ 모수통계학(Parametric statistics): 모집단의 분포에 대한 가정이 필요하며, 연속형 척도를 대상으로 한다.

◎ 비모수통계학(nonParametric statistics): 모집단에 대한 가정이 필요하지 않으며, 질적 자료나 양적 데이터라 할지라도 빈도수와 같은 비연속적인 데이터를 대상으로 한다.


  • 모수통계학은 대상 집단의 모집단이 정규분포와 같은 분포를 따른다는 가정하에 진행된다.
  • 즉, 연구자가 관심 있는 대상인 모집단의 분포 모양이 정규분포의 형태를 그리면서, 연속형 데이터를 대상으로 할 때, 모수통계학을 사용하고, 그 외의 기법들은 비모수통계학이라고 생각하면 된다.
  • 추론통계학에서는 주로 비모수통계학보다 모수통계학을 사용하는데, 이는 모수통계학이 모수 추정에 있어 더 신뢰도가 높기 때문이다.
  • 그러나, 모집단에 대한 정보가 전혀 없거나 부정확하여, 모수통계학의 가정을 만족시키지 못한 상태라면 비모수통계학의 신뢰도가 더 높다.

 

  • 비모수통계학의 특징
    • 분포 무관(Distribution-free) 검정법이라고도 불리며, 말 그대로 모집단의 분포에 상관없이 사용할 수 있다.
    • 범주형 척도 같은 비연속성 데이터라 할지라도 분석할 수 있다.
    • 계산 방법이 단순하여 빠르고 쉽게 통계량을 구할 수 있으며, 결과에 대한 해석과 이해가 쉽다.
    • 표본을 많이 추출하기 어려운 경우에도 사용할 수 있다.
728x90
반응형

'Python으로 하는 기초통계학 > 기본 개념' 카테고리의 다른 글

중심경향치(1) - 최빈값, 중앙값  (0) 2021.03.03
도수분포표와 시각화  (0) 2021.03.02
도수분포표  (0) 2021.03.02
통계 분석을 위한 데이터 준비  (0) 2021.03.01
변수(Variable)  (0) 2021.03.01
728x90
반응형

영인자(Zero Divisor)

  • $A \neq O,\ B \neq O,\ AB = O$
  • $ A \neq O,\ A^2 = O$
  • 행렬 $A$와 행렬 $B$가 영행렬이 아님에도 불구하고, 행렬 $AB$를 행렬곱하였을 때, 영행렬이 나오는 경우
  • 위에서 보듯 $A$는 제곱하였을 때, 영행렬이 나왔다. 영인자의 존재로 인해 행렬에서는 방정식의 근, 지수법칙을 사용할 수 없다.
  • 고등학교 행렬 문제에서 영인자의 존재는 수많은 반례를 가지고 오므로, 요주의 대상이다.

 

 

1. 영인자의 곱 순서

$$AB = O\ \overset{F}{\rightarrow} BA = O$$

$$ AB = O\ \overset{F}{\rightarrow} BA \neq O $$

  • 영인자는 특정한 배열의 곱에서만 영행렬이 된다.
  • 물론, 예외 역시 존재하기 때문에 $AB = BA = O$이 되는 경우도 존재한다.
>>> mat1 = np.array([[0, 0],[2, -2]])
>>> mat2 = np.array([[1, 0],[1, 0]])
>>> np.dot(mat1, mat2)
array([[0, 0],
       [0, 0]])
       
>>> np.dot(mat2, mat1)
array([[0, 0],
       [0, 0]])

 

 

 

 

2. 영인자는 역행렬을 가지지 않는다.

2.1. 증명

  • 다음과 같은 정사각행렬 $A$가 있다고 하자.
  • $ A \neq O \rightarrow A^2 = O $
  • 위 조건을 만족하는 행렬 $A$는 영행렬이 아니므로, 영인자이다.
  • 정사각행렬이므로, 케일리 헤밀턴의 정리를 사용해보자
  • $ A^2 - (a+d)A + (ad - bc)E = O $
  • $A^2 = O$이고, $A \neq O,\ E \neq O$이므로, $(ad-bc)E = (a+d)A$가 성립해야한다.
  • 그러나, 단위행렬의 배수는 제곱하여 영행렬이 될 수 없으므로, 영인자인 행렬 $A$는 단위행렬의 배수가 아니다.
  • 그러므로, $(a+b)=0, (ad-bc)=0$이 성립한다.
  • $ad-bc=0$이므로, 역행렬 생성 법칙에 따라, 영인자 $A$는 역행렬을 가질 수 없다.

 

2.2. 케일리 헤밀턴 정리(Cayley–Hamilton theorem)

  • 이차 정사각행렬 $A$에 대하여 케일리 헤밀턴 정리를 사용하면, 행렬의 거듭제곱을 아주 쉽게 구할 수 있다.
  • $A \neq kE$일 때 사용 가능하다(행렬 $A$가 단위행렬의 배수가 아닐 때).

$$ A = \begin{pmatrix}
a & b\\ 
c & d
\end{pmatrix} \neq kE $$

$$ A^2 - (a+d)A + (ad - bc)E = O $$

  • 케일리 헤밀턴 정리 사용 예시

$$A = \begin{pmatrix}
1 & 2\\ 
3 & 4
\end{pmatrix}$$

$$A^2 - 5A -2E = O \rightarrow A^2 = 5A + 2E$$

 

2.3. 영인자와 역행렬 관계

  • 위에서 봤듯이 영인자인 행렬은 역행렬이 존재할 수 없다.
  • 반대로 말하자면, 역행렬이 존재하는 행렬은 영인자가 아니라는 소리다.
  • 만약 행렬 $A$와 $B$가 역행렬이 존재한다면 영인자가 아니라는 의미이므로, 다음 공식이 성립한다.

$$(AB)^2 = A^2B^2 \ \overset{T}{\rightarrow}\ AB=BA$$

$$ABAB = AABB$$

$$A^{-1}ABABB^{-1} = A^{-1}AABBB^{-1}$$

$$BA = AB$$

  • 그러나 행렬은 3차 이상에서부터는 성립하지 않는 경우가 많으므로 위 공식도 주의해야한다.

$$(AB)^3 = A^3B^3 \ \overset{F}{\rightarrow}\ AB=BA$$

$$ABABAB = AAABBB$$

$$A^{-1}ABABABB^{-1} = A^{-1}AAABBBB^{-1}$$

$$BABA = AABB$$

  • $BABA = ABAB$와 $BA = AB$는 다르다.

 

 

 

 

3. 영인자와 지수법칙

  • 영인자의 존재로 인해 행렬에서는 지수법칙을 사용하기가 어렵다.
  • 영인자의 존재로 인해 행렬은 지수에 대하여 다음과 같은 성질을 갖는다.

$$A^n(n\geq 3)\ \overset{F}{\rightarrow}\ A=O$$

$$A^n(n\geq 3)\ \overset{T}{\rightarrow}\ A^2=O$$

  • 영인자의 존재로 인해, 위 명제에서 행렬 $A=O$이라고 할 수 없다.
  • 행렬 $A$가 영인자인 경우 $A^2=O$이므로, 그 이상의 차수에서도 모두 영행렬이 나오게 된다.

 

3.1. 지수법칙이 반드시 성립불가한 것은 아니다.

  • 하나의 행렬에서는 지수법칙이 성립한다.

$$A^m*A^n = A^{m+n},\ (m, n \in N)$$

$$(A^m)^n = A^{mn}$$

  • 그러나, 하나의 행렬이 아닌 경우에는 성립하지 않는다.
  • 이는 교환법칙이 성립하지 않기 때문이다.

$$(AB)^n \neq A^nB^n$$

$$ABABABAB \neq AAAABBBB$$

 

3.2. 영인자와 지수법칙

$$(AB)^n = A^nB^n \overset{F}{\rightarrow} AB = BA$$

  • 영인자의 존재로 인해 위 명제는 성립하지 않는다.

 

3.2.1. 증명

  • $AB = O$로 영인자라고 가정해보자.
  • 영인자의 성질로 인해 $BA \neq O$일 수 있다.

$$(AB)^n = O,\ A^nB^n = AA\cdots AABB\cdots BB = O$$

  • 영인자 AB의 존재로 인해 $(AB)^n = A^nB^n$은 성립하였으나, $AB=BA$는 성립하지 않는다.

 

3.2.2. 역은 성립한다.

$$(AB)^n = A^nB^n \overset{T}{\leftarrow} AB = BA$$

  • $AB = BA$라는 말은 $AB$가 영인자인 경우라 할지라도 $AB=BA=O$이 성립하고, 교환 법칙이 성립할수도 있다는 의미이기 때문이다.
728x90
반응형

'Python으로 하는 기초 수학 > 행렬' 카테고리의 다른 글

연립일차방정식과 가우스 소거법(Gaussian elimination)  (0) 2021.02.26
역행렬(Inverse matrix)  (0) 2021.02.26
행렬의 성질  (0) 2021.02.25
행렬(Matrix)  (0) 2021.02.25
728x90
반응형

행렬의 성질

  • 전제: $M_{(m*n)},\ A \in M,\ B \in M, \ C \in M$

 

 

1. 기본 연산

1.1. 행렬의 덧셈과 뺄셈

  • $A + B \in M$, $A - B \in M$
  • 동형인 행렬끼리만 +, -가 가능하다(행렬은 덧셈 뺄셈에 대하여 닫혀 있다.)
>>> Mat1 = np.arange(0, 15).reshape((3,5))
>>> Mat2 = np.full((3,5), 3)
>>> Mat1 + Mat2
array([[ 3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17]])
       
>>> Mat1 - Mat2
array([[-3, -2, -1,  0,  1],
       [ 2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11]])

 

1.2. 행렬의 곱셈

  • $AB^T \notin M$
  • 행렬끼리 곱을 하면 행과 열의 모양이 바뀐다.
  • 행렬끼리 곱을 하려면, 앞의 행렬의 열과 뒤의 행렬의 행의 크기가 동일해야 한다.
  • 위 조건을 만족할 때, 출력된 행렬의 모양은 앞 행렬의 행($l$)과 뒤 행렬의 열($n$)의 모양인 행렬($l*n$)이 나온다.
  • $(l * m) * (m * n) = (l * n) $ 
  • 행렬은 행렬끼리의 곱에 대하여 닫혀있지 않다고 할 수 있으나, 정방 행렬 간의 곱에 대해서는 닫혀 있다고 할 수 있다.
    $m = n,\ AB \in M$
# 행렬 곱 시, 행렬의 모양이 바뀐다.
>>> Mat1 = np.arange(0, 15).reshape((3,5))
>>> Mat2 = np.full((3,5), 3)
>>> np.dot(Mat1, Mat2.T)
array([[ 30,  30,  30],
       [105, 105, 105],
       [180, 180, 180]])
       
# 정방행렬끼리의 곱을 하는 경우, 행렬 모양이 유지된다.
>>> Mat3 = np.arange(0, 9).reshape((3,3))
>>> Mat4 = np.full((3,3), 3)
>>> np.dot(Mat3, Mat4)
array([[ 9,  9,  9],
       [36, 36, 36],
       [63, 63, 63]])

 

 

 

 

2. 결합 법칙

2.1. 덧셈의 결합 법칙

  • $(A+B)+C = A+(B+C)$
  • 행렬의 덧셈과 뺄셈은 각 행렬의 형태가 동일해야 하며, 그 순서를 어떻게 하는지는 상관없다.
>>> Mat1 = np.arange(0, 15).reshape((3,5))
>>> Mat2 = np.repeat(np.array([1,2,3]), 5).reshape((3,5))
>>> Mat3 = np.array([[1,3,5,7,9],[2,4,6,8,10],[3,6,9,12,15]])
>>> (Mat1 + Mat2) + Mat3 == Mat1 + (Mat2 + Mat3)
array([[ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True]])

 

2.2 곱셈의 결합 법칙

  • $A(B^TC) = (AB^T)C$
>>> Mat1 = np.arange(0, 15).reshape((3,5))
>>> Mat2 = np.array([[1,3],[2,4],[3,6],[4,8],[5,10]])
>>> Mat3 = np.array([[1,3,5,7,9,11,13],[2,4,6,8,10,12,14]])
>>> np.dot(np.dot(Mat1,Mat2),Mat3) == np.dot(Mat1,np.dot(Mat2,Mat3))
array([[ True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True,  True,  True]])

 

 

 

 

3. 항등원(Identity Elementa)

3.1 행렬의 덧셈의 항등원

  • $ A + O = O + A = A$
  • 영행렬은 행렬의 덧셈의 항등원이다.
  • 뺄셈은 부호가 바뀌게 되므로 항등원이 없다.
>>> Mat1 = np.arange(0, 15).reshape((3,5))
>>> Mat2 = np.zeros((3,5))
>>> Mat1 + Mat2 == Mat2 + Mat1
array([[ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True],
       [ True,  True,  True,  True,  True]])

 

3.2. 행렬의 곱셈의 항등원

  • $AE = EA = A$
  • 모든 정방 행렬 $A$에 대하여 단위행렬 $E$를 곱하면 행렬 $A$가 나온다.
>>> Mat1 = np.arange(0, 16).reshape((4,4))
>>> Mat2 = np.identity(4)
>>> np.dot(Mat1, Mat2) == Mat1
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]])
       
>>> Mat1 == np.dot(Mat1, Mat2)
array([[ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True],
       [ True,  True,  True,  True]])

 

 

 

 

4. 역원

4.1. 덧셈에 대한 역원

  • $A+(-A) = (-A) + A = O$
>>> Mat1 = np.arange(0, 12).reshape((4,3))
>>> Mat1 + (-Mat1) == (-Mat1) + Mat1
array([[ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True],
       [ True,  True,  True]])
       
>>> Mat1 + (-Mat1)
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])

 

4.2. 곱셈에 대한 역원

  • $AA^{-1} = A^{-1}A = E$
  • 역행렬을 갖지 않는 경우도 존재하므로, 항상 성립하는 것은 아니다.
  • $AB = BA = E$인 경우 B는 A의 역행렬이다.
>>> Mat = np.array([[0, 1],[2, 3]])
>>> Inv_Mat = np.linalg.inv(Mat)
>>> np.dot(Mat, Inv_Mat)
array([[1., 0.],
       [0., 1.]])

 

4.3. 곱셈에 대한 역원에서 파생된 행렬 성질

  • $A(A+B) = E \Rightarrow AB = BA$

4.3.1. 증명

  • $A(A+B) = E$이므로, $A^{-1}$은 존재한다. $A^{-1} = (A+B)$이므로, 다음 식이 성립한다.

$$A(A + B) = (A + B)A\ \rightarrow \ A^2 + AB = A^2 + BA \ \rightarrow \ AB = BA$$

 

 

 

 

5. 교환 법칙

5.1. 행렬의 교환 법칙은 성립하지 않는다.

  • $AB \neq BA$
  • 교환법칙이 성립하지 않으므로 곱셈법칙, 지수법칙을 적용할 수 없다(항상은 아니며, 가능한 경우도 있으므로, 부분적으로 사용 가능하다).
  • 대우: 곱셈법칙이 성립한다면 $AB = BA$가 성립한다. [거짓]
  • 행렬에서는 대우 명제마저도 성립하지 않을 수 있다.

5.1.1 증명

$$(A+B)^2 = A^2 + 2AB + B^2\ \overset{T}{\rightarrow} \ AB = BA \cdots (1)$$

$$(A+B)^3 =  A^2 + 3A^2B + 3AB^2 + B^3 \ \overset{F}{\rightarrow} \ AB = BA \cdots (2)$$

  • $AB$가 영인자인 경우, $A^2 + 3AAB + 3ABB + B^3 = A^3 + B^3 =  A^2 + 3A^2B + 3AB^2 + B^3$은 성립한다.
  • $AB$는 영인자이므로, $AB=O$은 성립하나, $BA \neq O$이다.
  • 행렬에서는 영인자의 존재로 인해 2차 함수에서 성립하는 것이 3차 함수 이상에서는 성립하지 않을 수 있다.

 

5.2. 교환 법칙 관련 재밌는 공식

  • $A + B = AB$라면 $AB = BA$이다.

5.2.1. 증명

$$A + B = AB \rightarrow AB - A - B = O \rightarrow (A-E)(B-E)-E=O$$

$(A-E)(B-E)=E$이므로, $(A-E)^{-1},\ (B-E)^{-1}$이 존재한다.

$$ (A-E)(B-E)=E=(B-E)(A-E)\rightarrow BA -A-B=O $$

$$ \therefore AB = BA $$

 

 

 

728x90
반응형
728x90
반응형

 

행렬(Matrix)

 수학에서 행렬은 1개 이상의 수 또는 다항식 등을 사각형 모양으로 배열한 것이다. 가로 줄은 행(Row), 세로 줄은 열(Column)이라 부른다.

 아서 케일리와 윌리엄 로원 해밀턴이 발명했으며, 행렬식의 값에 따라 연립방정식의 해가 다르게 나오는 것을 보고, 연립 방정식의 계수와 변수를 분리하여 사용하게 되면서 행렬이 등장하게 되었다.

 

 

 

1. 행렬의 정의

  • $m * n$ 행렬은 각 행 $i \in \{1,...,m\}$ 및 열 $j \in \{1,...,n\}$의 순서쌍 $(i, j)$에 대하여, 원소 $A_{ij} \in R$을 대응시키는 함수 $A = (A_{ij})_{ij}$이다.
  • 행렬 $A$는 모든 성분을 사각형으로 배열하고 소괄호 또는 대괄호를 추가하여 다음과 같이 표기한다.

$$ A = \begin{bmatrix}
 A_{11}& A_{12} & A_{13}  & ... & A_{1n}\\ 
 A_{21}& A_{22} & A_{23}  & ... & A_{2n}\\ 
 A_{31}& A_{32} & A_{33}  & ... & A_{3n}\\ 
 \vdots & \vdots & \vdots & \ddots & \vdots\\
 A_{m1}& A_{m2} & A_{m3}  & ... & A_{mn}\\
\end{bmatrix} $$

 

$$ A = \begin{pmatrix}
 A_{11}& A_{12} & A_{13}  & ... & A_{1n}\\ 
 A_{21}& A_{22} & A_{23}  & ... & A_{2n}\\ 
 A_{31}& A_{32} & A_{33}  & ... & A_{3n}\\ 
 \vdots & \vdots & \vdots & \ddots & \vdots\\
 A_{m1}& A_{m2} & A_{m3}  & ... & A_{mn}\\
\end{pmatrix} $$

  • $A_{ij}$를 $A$의 $i$번째 행 $j$번째 열의 성분(Entry) 또는 원소(Element) 또는 계수(Coefficient)라 한다.
  • 행렬 $A$의 각 성분은 행과 열의 번째 수를 첨수로 사용해, $A_{ij},\ A_{i,j},\ a_{ij},\ a_{i,j},\ A(i,j),\ A[i,j]$ 등과 같이 표현한다.
  • 행렬은 실수에 대해 닫혀 있으며, 행렬 연산 시 실수가 나온다.
  • 행과 열의 번째수가 동일한 성분 $A_{ii}\ (i \in \{1, ..., min\{m,n\}\})$을 $A$의 대각 성분(Diagonal entry) 또는 대각 원소(Diagonal element), 대각 요소, 주대각 성분이라고 한다.

 

  • 행렬 $A$의 크기(Size)는 행과 열의 수의 순서쌍 $(m,n)$ 또는 $m*n$으로 나타낸다.
  • 만약 행과 열의 수가 같다면($m=n$) 행렬$A$를 정사각 행렬(Square matrix) 또는 정방 행렬이라 부른다.
  • 행과 열의 수가 다르다면($m \neq n$) 직사각 행렬(Rectangular matrix) 또는 장방 행렬이라 부르는데, 일반적으로 행렬은 직사각형이므로, 정방 행렬과 구분하고자 하는 경우가 아니라면 그냥 행렬이라 부른다.

 

  • 만약, $m=1$이라면 행렬 $A$를 $1*n$ 행 백터(Row vector)라고 한다.
  • 만약, $n=1$이라면 행렬 $A$를 $m*1$ 열 벡터(Column vector)라고 한다.
  • 만약, $m=1,\ n=1$인 행렬 $A$는 스칼라(Scalar)라고 한다.

$$X = \begin{bmatrix} x_1 & x_2 & x_3 & ... &x_n \end{bmatrix}$$

$$X = \begin{bmatrix}
x_1\\ 
x_2\\ 
x_3\\ 
\vdots \\
x_n
\end{bmatrix}$$

$$X = \begin{bmatrix} x_1 \end{bmatrix}$$

  • 즉, 벡터와 스칼라 역시 수학적으로는 행렬에 속한다.

 

 

 

 

2. Python과 다양한 형태의 행렬

  • Python에서는 Numpy 라이브러리를 사용해서 행렬, 벡터를 만든다.
  • Python의 Numpy라이브러리를 사용해서 다양한 형태의 행렬을 만들어보도록 하겠다.
import numpy as np

2.1. 직사각 행렬(Rectangular matrix)

>>> np.arange(0, 18).reshape((3,6))
array([[ 0,  1,  2,  3,  4,  5],
       [ 6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17]])
       
       
# 랜덤한 값으로 만들수도 있다.
>>> np.random.randint(0, 20, size=(3, 6))
array([[16,  9,  1,  6,  7, 18],
       [ 4, 15, 15, 16, 13, 16],
       [ 8,  1,  2,  8, 11,  8]])
  • 직사각형 모양의 행렬로, 장방 행렬이라고도 부르며, 가장 일반적인 행렬이다.

2.2. 정사각 행렬(Square matrix)

>>> np.arange(0, 16).reshape((4,4))
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
       
       
>>> np.random.randint(0, 20, size=(3, 3))
array([[ 5,  8,  7],
       [13,  9,  0],
       [ 8, 17,  8]])
  • 정사각형 모양의 행렬로, 정방 행렬이라고도 부른다.

2.3. 영행렬(Zero matrix)

>>> np.zeros((4, 5))
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])
  • 모든 원소가 0인 행렬로, 행렬 곱에서 영원으로 작용하는 행렬이다.

2.4. 항등행렬(Identity matrix)

>>> np.identity(4)
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
  • 대각 성분은 모두 1이며, 나머지 원소는 0인 행렬이다.
  • 행렬 곱셈에서 항등원으로 작용하며, 다른 행렬에 곱하면, 곱한 행렬이 그대로 나오게 된다.
  • 단위행렬이라고도 부른다.

2.5. 대각행렬(Diagonal matrix)

# 대각행렬 만들기
>>> diag_M = np.diag((4, 2, 2, 5, 1, 6))
>>> diag_M
array([[4, 0, 0, 0, 0, 0],
       [0, 2, 0, 0, 0, 0],
       [0, 0, 2, 0, 0, 0],
       [0, 0, 0, 5, 0, 0],
       [0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 6]])

# 대각성분 뽑기
>>> np.diagonal(diag_M)
array([4, 2, 2, 5, 1, 6])

# 대각성분이 1인 행렬 만들기
>>> np.eye(3, 3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
       
# 장방행렬로 만들기
>>> np.eye(5, 3)
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.],
       [0., 0., 0.],
       [0., 0., 0.]])
  • 대각행렬은 주대각선 원소를 제외한 모든 원소가 0인 정방행렬이다.
  • 영행렬, 단위 행렬은 대각 행렬에 포함된다.
  • 장방행렬 역시 주대각 성분을 제외한 나머지 원소가 0인 경우 대각행렬에 포함된다.

2.6. 스칼라 행렬(Scalar matrix) 

# 대각 성분이 모두 1인 대각행렬
>>> np.identity(4)
array([[1., 0., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]])
       
# 대각 성분이 모두 같은 대각행렬
>>> np.diag(np.full(4, 5))
array([[5, 0, 0, 0],
       [0, 5, 0, 0],
       [0, 0, 5, 0],
       [0, 0, 0, 5]])
  • 주대각 성분이 모두 같은 원소로 된 대각행렬

2.7. 삼각행렬(Triangular matrix)

# 정방행렬
>>> Mat = np.arange(0, 36).reshape((6,6))
>>> Mat
array([[ 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, 32, 33, 34, 35]])
       
# 상삼각행렬
>>> np.triu(Mat, 0)
array([[ 0,  1,  2,  3,  4,  5],
       [ 0,  7,  8,  9, 10, 11],
       [ 0,  0, 14, 15, 16, 17],
       [ 0,  0,  0, 21, 22, 23],
       [ 0,  0,  0,  0, 28, 29],
       [ 0,  0,  0,  0,  0, 35]])
       
# 하삼각행렬
>>> np.tril(Mat, 0)
array([[ 0,  0,  0,  0,  0,  0],
       [ 6,  7,  0,  0,  0,  0],
       [12, 13, 14,  0,  0,  0],
       [18, 19, 20, 21,  0,  0],
       [24, 25, 26, 27, 28,  0],
       [30, 31, 32, 33, 34, 35]])
  • 정방행렬의 특수한 경우로, 주대각선을 기준으로 대각항의 위쪽이나 아래쪽 항들의 값이 모두 0인 경우를 의미한다.
  • 주대각선 성분을 기준으로 아래가 모두 0이면 상삼각행렬(Upper Triangular matrix)이다.
  • 주대각선 성분을 기준으로 위가 모두 0이면 하삼각행렬(Lower Triangular matrix)이다.

2.8. 전치행렬(Transpose Matrix)

>>> Mat = np.arange(0, 16).reshape((4,4))
>>> Mat
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
       
# 전치행렬 만들기
>>> Mat.T
array([[ 0,  4,  8, 12],
       [ 1,  5,  9, 13],
       [ 2,  6, 10, 14],
       [ 3,  7, 11, 15]])
       
>>> np.transpose(Mat)
array([[ 0,  4,  8, 12],
       [ 1,  5,  9, 13],
       [ 2,  6, 10, 14],
       [ 3,  7, 11, 15]])
  • 행렬 $A$의 전치행렬은 $A^T$로 표기한다.
  • 주대각 성분을 기준으로 행과 열을 대칭으로 바꾼 행렬
  • 주대각 성분을 기준으로 하므로, 주대각 성분은 변하지 않는다.
  • 정사각행렬 $A$가 $A^T=A$를 만족하면 행렬 $A$는 대칭행렬이다.
    (전치행렬인 $A^T$는 주대각 성분을 기준으로 행과 열을 대칭으로 바꾸기 때문이다.)
  • 반대로, $A^T=-A$를 만족하면 반대칭행렬(Skew symmetric matrix) 또는 교대행렬(Alternating matrix)이라 한다.

2.9. 직교행렬(Orthogonal matrix)

>>> Ort_M = np.array([[0,0,1],[1,0,0],[0,1,0]])
>>> Ort_M
array([[0, 0, 1],
       [1, 0, 0],
       [0, 1, 0]])
       
# 행렬 Ort_M과 전치행렬 Ort_M을 행렬곱하였을 때, 단위 행렬이 나오는 행렬 Ort_M
>>> np.dot(Ort_M, Ort_M.T)
array([[1, 0, 0],
       [0, 1, 0],
       [0, 0, 1]])
  • 행렬 A의 역행렬이 A의 전치행렬인 행렬 A를 직교행렬이라 한다.
  • 직교행렬과 직교행렬의 전치행렬을 행렬곱하면 단위행렬이 나온다.
  • $A^{-1} = A^T, \ A^TA = I$
728x90
반응형
728x90
반응형

 이전 포스트까지 Wide & Deep Learning 모델을 사용해서 다중 입력 모델을 만들어보았다. 이번엔 이전 모델에 추가로 출력층을 추가하여 출력층을 2개로 만들어 보도록 하자.

 

 

다중 출력 모델

  • 일반적으로 다중 출력 모델을 사용하는 이유는 다음과 같다.

  1. 프로세스가 서로 독립적인 경우:
    동일한 데이터 셋에서 두 개 이상의 출력층을 생성하지만, 출력된 결과는 전혀 다른 작업을 위해 실행되는 경우로, 예를 들어 인터넷 사용 Log 데이터를 기반으로, 대상의 관심사 파악(분류 모델)과 인터넷 사용률 예측(회귀 모델)은 별개의 목적을 위해 진행된다.
  2. 프로세스가 한 목적을 위해 상호 보완적으로 이루어지는 경우:
    하나의 목적을 이루기 위해 두 개 이상의 출력 값이 필요하여, 동일한 데이터 셋에서 두 개 이상의 출력층을 뽑아내는 경우로, 예를 들어 영상 속 물건을 인식하는 모델을 만든다면, 영상 속 물건의 위치(회귀 모델)와 물건의 종류(분류 모델)를 동시에 파악해야 한다.
  3. 규제 도구로써의 다중 출력 모델:
    하위 네트워크가 나머지 네트워크에 의존하지 않고, 그 자체로 유용한 성능을 내는지 확인을 하기 위한 규제 기법으로 사용

 이번 포스트에서는 Wide & Deep Learning model에 다중 출력 모델을 규제 도구로써 사용해보도록 하자.

 

 

 

 

1. 규제 도구로써의 다중 출력 모델

  • 보조 출력층(Auxiliary Output)을 통해, 하위 네트워크가 나머지 네트워크에 의존하지 않고 그 자체로 유용한 것을 학습하는지 확인한다. 위 모델대로라면, Deep model에서의 출력된 값이 Wide model과의 연결로 인해, 원하는 결과를 뽑아내는 것인지, Deep model으로 인해 그러한 결과가 나왔는지 확인할 수 있다.
  • 이를 통해, 과대 적합을 감소시키고, 모델의 일반화 성능이 높이도록 학습에 제약을 가할 수 있다.

 

 

 

 

2. 모델 생성 이전까지의 코드 가져오기

  • 이전 포스트에서 만들었던, 모델 이전까지의 코드를 모두 가지고 온다.
  • 입력층이 2개일 때는 입력 데이터를 목적에 맞게 나눴어야 했지만, 이번엔 출력층이 다른 데이터를 출력하는 것이 아니라, 보조 출력층(Auxiliary Output)과 주 출력층(Main Output)이 동일한 정답에 대해 어느 정도의 손실 값과 정확도를 내보내는지 알고 싶은 것이므로, Label dataset을 나누지 않아도 된다.
#################################### Import Module ####################################
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import callbacks
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import (Input, Dense, concatenate)
########################################################################################





#################################### Import Dataset ####################################
# 캘리포니아 데이터 가져오기
Rawdict = fetch_california_housing()
CaliFornia_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
########################################################################################




#################################### Data Handling #####################################
# 데이터를 쪼개기 좋게 변수의 순서를 바꾸자
CaliFornia_DF = CaliFornia_DF[["HouseAge", "Population", "Latitude", "Longitude", "MedInc",
                               "AveRooms", "AveBedrms", "AveOccup"]]

# train, validation, test set으로 쪼갠다.
X_train_all, X_test, y_train_all, y_test = train_test_split(CaliFornia_DF.values, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 정규화시킨다.
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

# 데이터 셋을 input layer의 수만큼 쪼갠다.
X_train_A, X_train_B = X_train[:, :1], X_train[:,1:]
X_valid_A, X_valid_B = X_valid[:, :1], X_valid[:,1:]
X_test_A, X_test_B = X_test[:, :1], X_test[:,1:]
########################################################################################

 

 

 

 

3. 모델 생성

######################################## Model ########################################
input_A = Input(shape=[1], name = "deep_input")
input_B = Input(shape=[7], name = "wide_input")
hidden1 = Dense(30, activation="relu", name = "hidden1")(input_A)
hidden2 = Dense(30, activation="relu", name = "hidden2")(hidden1)
concat = concatenate([input_B, hidden2], name = "concat")
output = Dense(1, name="main_output")(concat)

# 보조 출력층 생성
aux_output = Dense(1, name="aux_output")(hidden2)
model = keras.Model(inputs=[input_A, input_B], outputs=[output, aux_output])
########################################################################################
>>> model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
deep_input (InputLayer)         [(None, 1)]          0                                            
__________________________________________________________________________________________________
hidden1 (Dense)                 (None, 30)           60          deep_input[0][0]                 
__________________________________________________________________________________________________
wide_input (InputLayer)         [(None, 7)]          0                                            
__________________________________________________________________________________________________
hidden2 (Dense)                 (None, 30)           930         hidden1[0][0]                    
__________________________________________________________________________________________________
concat (Concatenate)            (None, 37)           0           wide_input[0][0]                 
                                                                 hidden2[0][0]                    
__________________________________________________________________________________________________
main_output (Dense)             (None, 1)            38          concat[0][0]                     
__________________________________________________________________________________________________
aux_output (Dense)              (None, 1)            31          hidden2[0][0]                    
==================================================================================================
Total params: 1,059
Trainable params: 1,059
Non-trainable params: 0
__________________________________________________________________________________________________
  • 앞서 학습했던 방법처럼 보조 출력층 생성 역시 Keras API 함수로 구현하는 것은 꽤 단순하다.
  • 보조 출력층(aux_output)과 데이터를 받는 층(hidden2)을 연결시키고, model에서 outputs을 2개 다 잡아주면 된다.

 

 

 

 

4. 모델 컴파일

  • 다중 입력 모델과 달리 다중 출력 모델에서는 모델 컴파일 방법이 바뀌게 된다.
  • 이는, 컴파일에서 출력층에서 모델을 평가하게 되는 손실 함수를 결정하기 때문이고, 이 손실 함수는 출력층마다 다르게 설정해야 하기 때문이다.
# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = ["msle", "msle"],
              metrics=["accuracy"],
              loss_weights=[0.9, 0.1])
  • 최적화나 모델 평가 지표는 출력층의 수와 상관없기 때문에 바뀌지 않는다.
  • 손실 함수는 출력층이 2개가 되었으므로, 2개를 잡아줘야 한다(만약, 손실 함수를 하나만 잡아준다면, 모든 출력의 손실 함수가 같다고 가정한다 - 위 경우에는 출력층이 회귀모형이므로, msle로 같은 손실 함수를 사용하므로, msle 하나만 사용해도 된다).
  • loss_weights: 출력 층별 손실 값의 가중치를 정해준다. 케라스는 기본적으로 출력된 손실 값들을 모두 더해 최종 손실을 구하며, 이를 기반으로 학습을 한다. 여기서 사용된 보조 출력은 규제로 사용되었기 때문에 주 출력이 더 중요하다. 그러므로, 주 손실 값에 더 많은 가중치를 부여하였다.

 

 

 

 

5. 모델 학습 및 평가

  • 모델 학습과 평가에서의 차이는 Label 역시 각 출력층에 맞게 2개가 들어가야 한다는 것이다.
  • 해당 다중 출력 모델은 규제 목적으로 보조 출력층을 추가한 것이므로, 동일한 데이터를 label로 사용해도 된다.
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

# 학습
history = model.fit([X_train_A, X_train_B], [y_train, y_train],
                    epochs=300,
                    batch_size = 32,
                    validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]),
                    callbacks=[early_stop])
Epoch 1/300
362/362 [==============================] - 3s 6ms/step - loss: 0.2162 - main_output_loss: 0.1988 - aux_output_loss: 0.3726 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0023 - val_loss: 0.0697 - val_main_output_loss: 0.0633 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 2/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0650 - main_output_loss: 0.0584 - aux_output_loss: 0.1243 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0619 - val_main_output_loss: 0.0544 - val_aux_output_loss: 0.1295 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 3/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0584 - main_output_loss: 0.0509 - aux_output_loss: 0.1260 - main_output_accuracy: 0.0027 - aux_output_accuracy: 0.0027 - val_loss: 0.0598 - val_main_output_loss: 0.0522 - val_aux_output_loss: 0.1279 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 4/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0555 - main_output_loss: 0.0480 - aux_output_loss: 0.1229 - main_output_accuracy: 0.0018 - aux_output_accuracy: 0.0018 - val_loss: 0.0591 - val_main_output_loss: 0.0514 - val_aux_output_loss: 0.1281 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048

...

Epoch 54/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0522 - main_output_loss: 0.0444 - aux_output_loss: 0.1227 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0556 - val_main_output_loss: 0.0476 - val_aux_output_loss: 0.1276 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 55/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0524 - main_output_loss: 0.0446 - aux_output_loss: 0.1225 - main_output_accuracy: 0.0026 - aux_output_accuracy: 0.0026 - val_loss: 0.0540 - val_main_output_loss: 0.0460 - val_aux_output_loss: 0.1263 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
Epoch 56/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0525 - main_output_loss: 0.0448 - aux_output_loss: 0.1215 - main_output_accuracy: 0.0024 - aux_output_accuracy: 0.0024 - val_loss: 0.0539 - val_main_output_loss: 0.0458 - val_aux_output_loss: 0.1265 - val_main_output_accuracy: 0.0048 - val_aux_output_accuracy: 0.0048
Epoch 57/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0535 - main_output_loss: 0.0457 - aux_output_loss: 0.1234 - main_output_accuracy: 0.0033 - aux_output_accuracy: 0.0033 - val_loss: 0.0543 - val_main_output_loss: 0.0463 - val_aux_output_loss: 0.1264 - val_main_output_accuracy: 0.0045 - val_aux_output_accuracy: 0.0048
>>> model.evaluate([X_test_A, X_test_B], [y_test, y_test])

194/194 [==============================] - 0s 1ms/step - loss: 0.0533 - main_output_loss: 0.0454 - aux_output_loss: 0.1241 - main_output_accuracy: 0.0029 - aux_output_accuracy: 0.0032
[0.05328081175684929,
 0.04541592299938202,
 0.12406466901302338,
 0.0029069767333567142,
 0.0032299740705639124]
  • 이전과 달리 값이 굉장히 많이 나오기 때문에 이를 파악하기 어려울 수 있는데, 그 내용은 생각보다 상당히 단순하다.
  • 손실 값은 loss: 0.0533, main_output_loss: 0.0454, aux_output_loss: 0.1241이 나왔다.
  • 여기서 loss만 신경 쓰면 된다. 위에서 우리는 main_output_loss와 aus_output_loss의 가중치를 0.9, 0.1로 부여하였는데, loss는 각 손실 값에 해당하는 가중치를 곱하여 합한 값이기 때문이다.

$$ 0.0533 = 0.9*0.0454 + 0.1*0.1241 $$

  • Deep model의 손실 값과 Wide & Deep Learning model의 손실 값을 동시에 반영하여, 총 손실 값을 계산하였으므로, Deep model이 Wide model과의 결합 없이도 우수한 성능을 보이는 것을 알 수 있다.
  • 주 출력층의 Accuracy는 0.0029, 보조 출력층의 Accuracy도 0.0032로 Deep model, Wide & Deep Learning model 모두 Accuracy가 괜찮게 나왔다. 이로 인해 Deep model 자체만으로도 우수한 성능을 보이는 것을 알 수 있다.

5.1. 손실 값과 정확도를 시각화해보자.

def Drawing_Scalars(history_name):
    
    history_DF = pd.DataFrame(history_name.history)
    # 그래프의 크기와 선의 굵기를 설정해주었다.
    history_DF.plot(figsize=(12, 8), linewidth=3)

    # 교차선을 그린다.
    plt.grid(True)

    plt.legend(loc = "upper right", fontsize =15)

    plt.title("Learning Curve", fontsize=30, pad = 30)
    plt.xlabel('Epoch', fontsize = 20, loc = 'center', labelpad = 20)
    plt.ylabel('Variable', fontsize = 20, rotation = 0, loc='center', labelpad = 40)

    # 위 테두리 제거
    ax=plt.gca()
    ax.spines["right"].set_visible(False) # 오른쪽 테두리 제거
    ax.spines["top"].set_visible(False) # 위 테두리 제거
    
    plt.show()
    
    
Drawing_Scalars(history)

  • 굉장히 많은 지표들이 추가되었지만, Early Stopping의 기준으로 사용한 val_loss를 본다면, patience가 30이었으므로, epochs 27에서 모델이 수렴하였다는 것을 알 수 있다.

 

[참고 자료]

 

 

 이번 포스트에서는 다중 출력 모델을 Wide & Deep Learning model에서의 규제 기법으로 사용해보았다. Deep model의 결괏값에 대한 평가가 모델 전체 평가에 반영되었으므로, Deep Model의 일반화가 잘 이루어진 모델이 만들어졌다고 할 수 있다. 

 지금까지 Wide & Deep Learning 모델을 기반으로 다중 입력, 다중 출력 모델을 만드는 방법과 이를 통해 Wide & Deep Learning Model을 더 잘 사용할 수 있도록 해보았다.

728x90
반응형
728x90
반응형

 이전 포스트에서는 Wide & Deep Learning 모델에 대한 간단한 설명과 함수형 API를 사용해서 기초적인 Wide & Deep Learning 모델을 만들어보았다. 이때는 완전히 동일한 Input 데이터를 바라보았으나, Wide & Deep Learning 모델은 다른 Input Layer를 만들어 Wide model과 Deep model에 따로 학습시킬 수 있다.

 

 

1. 다중 입력 Wide & Deep Learning

  • Wide & Deep Learning model의 특징은 Wide model에 전달되는 Feature와 Deep model에 전달되는 Feature를 다르게 할 수 있다는 것이다.
  • 즉, 입력되는 Feature에서 일부 특성은 Wide model로 나머지 특성은 Deep model로 전달할 수 있다.
    (각 모델로 전달되는 특성은 중복 가능하다).
  • Deep model은 입력된 모든 데이터를 MLP에 있는 모든 층에 통과시키다 보니, 간단한 패턴이 연속된 변환으로 인해 왜곡될 위험이 있다.
  • 때문에 Wide & Deep Learning model에서는 일반적으로 간단한 패턴이 있는 Column은 Wide model 쪽으로, 복잡한 패턴이 있는 Column은 Deep model 쪽으로 보낸다.
  • 간단한 패턴이 있어 Wide model로 보낸 Column 역시 Deep model에서 숨겨진 패턴을 찾는데 도움이 될 수 있으므로, Wide model로 Input 될 데이터의 일부 혹은 전체를 Deep model에 같이 넣기도 한다.
  • 이를 도식화해보면 다음과 같다.

  • 위 모델을 보면 Input Layer가 2개 존재하는 것을 볼 수 있는데, 이처럼 한 번에 2개 이상의 Input이 존재하여 동시에 여러 입력을 하는 것을 다중 입력 모델이라 한다.
  • 다중 입력 모델은 Keras의 함수형 API를 사용하면 쉽게 구현할 수 있다.

 

 

 

 

2. 데이터의 각 변수별 상세 정보 파악

  • 이전 포스팅에서 해당 모델을 Input Layer 1개로 넣었을 때, 이미 Accuracy 0.0034, Loss 0.0242로 꽤 괜찮은 결과가 나왔었다. 굳이 데이터를 나눠서 넣을 필요는 없으나, Keras API를 이용해서 다중 입력하는 방법을 학습하기 위해, 데이터 셋을 쪼개 보도록 하겠다.
  • 해당 포스트의 참고 논문(Heng-Tze Cheng et al.(2016))에서 예시로 든 단순한 패턴은 "설치한 앱"과 "열람한 앱"을  Cross-product transformation으로 생성한 희소 특징(Sparse feature)이다.
  • 그러나, 해당 데이터셋에는 그 대상이 될 수 있는 범주형 데이터가 따로 없으므로, 데이터 패턴을 보는 가장 기초적인 방법인 히스토그램과 독립변수와 종속변수 간의 산점도(상관관계)를 만들어, 패턴이 아주 단순한 경우는 Wide model로, 그렇지 않은 경우는 Deep model로 넣어보겠다.

 

# Import Module
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import callbacks
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import (Input, Dense, Concatenate, concatenate)

 

2.1. 데이터 셋 정보 보기

  • sklearn에서 기본으로 제공하는 데이터에서는 아주 쉽게 메타 정보를 볼 수 있다.
>>> Rawdict = fetch_california_housing()
>>> print(Rawdict.DESCR)
.. _california_housing_dataset:

California Housing dataset
--------------------------

**Data Set Characteristics:**

    :Number of Instances: 20640

    :Number of Attributes: 8 numeric, predictive attributes and the target

    :Attribute Information:
        - MedInc        median income in block
        - HouseAge      median house age in block
        - AveRooms      average number of rooms
        - AveBedrms     average number of bedrooms
        - Population    block population
        - AveOccup      average house occupancy
        - Latitude      house block latitude
        - Longitude     house block longitude

    :Missing Attribute Values: None

This dataset was obtained from the StatLib repository.
http://lib.stat.cmu.edu/datasets/

The target variable is the median house value for California districts.

This dataset was derived from the 1990 U.S. census, using one row per census
block group. A block group is the smallest geographical unit for which the U.S.
Census Bureau publishes sample data (a block group typically has a population
of 600 to 3,000 people).

It can be downloaded/loaded using the
:func:`sklearn.datasets.fetch_california_housing` function.

.. topic:: References

    - Pace, R. Kelley and Ronald Barry, Sparse Spatial Autoregressions,
      Statistics and Probability Letters, 33 (1997) 291-297
  • 캘리포니아 주택 데이터 셋의 각 Row인 객체는 블록이다.
  • MedInc: 소득의 중앙값
  • HouseAge: 건물 연령 중앙값
  • AveRooms: 평균 객실 수
  • AveBedrms: 평균 침실 수
  • Population: 인구수
  • AveOccup: 평균 주택 점유율
  • Latitude: 위도
  • Longitude: 경도
  • 종속변수(Target): MedHouseVal: 캘리포니아 지역의 주택 가격 중앙값

 

2.1. 변수별 히스토그램 보기

# 독립변수의 히스토그램 그리기
CaliFornia_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
CaliFornia_DF.hist(figsize=(12,8), linewidth=3)
plt.show()

# 종속변수 히스토그램 그리기
plt.hist(Rawdict.target)
plt.show()

 

2.2 변수별 종속변수와의 산점도

fig, axes = plt.subplots(nrows=4, ncols=2, figsize = (12, 20))
ax = axes.ravel()

xlabels = Rawdict.feature_names
plt.suptitle("Predict Variable & MedHouseVal Scatter", y=0.93, fontsize=25)

for i in range(Rawdict.data.shape[1]):

    X = Rawdict.data[:,i]
    Y = Rawdict.target

    ax[i].scatter(X, Y, alpha = 0.025)
    ax[i].set_title(xlabels[i], fontsize=15, fontweight ="bold")

  • Row가 20,640으로 단순하게 산점도로 그리기엔 그 양이 너무 많아, alpha를 0.025로 주어, 데이터가 동일한 위치에 많이 존재하지 않는다면 연하게, 많이 겹칠수록 진하게 그려지도록 설정하였다.
  • 각 변수별 히스토그램과 독립변수 & 종속변수의 산점도를 볼 때, 단순한 패턴을 갖는 것은 AveRooms, AveBedrms, Population, AveOccup로 이들은 히스토그램에서도 데이터가 한쪽에 지나치게 모여있기 때문에 딱히 패턴이 존재하지 않는다. 그러므로, 이들을 Wide model에 넣겠다.
  • MedInc은 그 정도가 강하다고 할 수 있는 수준은 아니지만, 위 데이터 중 가장 뚜렷한 경향성을 가지고 있다. 이를 단순한 패턴으로 인식할지도 모르니, Wide model과 Deep model 양쪽에 넣어보자.

 

 

 

 

3. 데이터셋 쪼개기

  • 입력층이 2개이므로, Train dataset, Validation dataset, Test dataset 모두 2개로 쪼개져야 한다.
  • 출력층은 1개이므로, Label은 쪼개지 않아도 된다.
# 데이터를 쪼개기 좋게 변수의 순서를 바꾸자
CaliFornia_DF = CaliFornia_DF[["HouseAge", "Latitude", "Longitude", "MedInc",
                               "AveRooms", "AveBedrms", "Population", "AveOccup"]]

# train, validation, test set으로 쪼갠다.
X_train_all, X_test, y_train_all, y_test = train_test_split(CaliFornia_DF.values, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 정규화시킨다.
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

# 데이터 셋을 input layer의 수만큼 쪼갠다.
X_train_A, X_train_B = X_train[:, :4], X_train[:,3:]
X_valid_A, X_valid_B = X_valid[:, :4], X_valid[:,3:]
X_test_A, X_test_B = X_test[:, :4], X_test[:,3:]
>>> X_train_A.shape
(11558, 4)

>>> X_train_B.shape
(11558, 5)

 

 

 

 

4. 모델 생성하기

input_A = Input(shape=[4], name = "deep_input")
input_B = Input(shape=[5], name = "wide_input")
hidden1 = Dense(30, activation="relu", name = "hidden1")(input_A)
hidden2 = Dense(30, activation="relu", name = "hidden2")(hidden1)
concat = concatenate([input_B, hidden2], name = "concat")
output = Dense(1, name="output")(concat)
model = keras.Model(inputs=[input_A, input_B], outputs=[output])
  • 다중 입력 모델 생성 시, 주의사항은 층을 연결하는 부분인 concatenate layer다.
  • 이전 포스트에서는 Concatenate layer를 사용하였고, 이번 포스트에선 맨 앞이 소문자인 concatenate layer를 사용하였는데, 둘 사이의 차이는 inputs(At least 2)라는 파라미터 여부다.
  • 참고 1: tf.keras.layers.concatenate 공식 API 문서
  • 참고 2: tf.keras.layers.Concatenate 공식 API 문서
>>> model.summary()

Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
deep_input (InputLayer)         [(None, 4)]          0                                            
__________________________________________________________________________________________________
hidden1 (Dense)                 (None, 30)           150         deep_input[0][0]                 
__________________________________________________________________________________________________
wide_input (InputLayer)         [(None, 5)]          0                                            
__________________________________________________________________________________________________
hidden2 (Dense)                 (None, 30)           930         hidden1[0][0]                    
__________________________________________________________________________________________________
concat (Concatenate)            (None, 35)           0           wide_input[0][0]                 
                                                                 hidden2[0][0]                    
__________________________________________________________________________________________________
output (Dense)                  (None, 1)            36          concat[0][0]                     
==================================================================================================
Total params: 1,116
Trainable params: 1,116
Non-trainable params: 0
__________________________________________________________________________________________________

 

 

 

 

5. 모델 학습 및 평가하기

# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = "msle",
              metrics=["accuracy"])

early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

# 학습
history = model.fit((X_train_A, X_train_B), y_train,epochs=300,
                    validation_data=((X_valid_A, X_valid_B), y_valid),callbacks=[early_stop])
Epoch 1/300
362/362 [==============================] - 2s 5ms/step - loss: 0.1601 - accuracy: 0.0015 - val_loss: 0.0413 - val_accuracy: 0.0028
Epoch 2/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0390 - accuracy: 0.0018 - val_loss: 0.0376 - val_accuracy: 0.0028
Epoch 3/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0367 - accuracy: 0.0017 - val_loss: 0.0364 - val_accuracy: 0.0028
Epoch 4/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0342 - accuracy: 0.0022 - val_loss: 0.0354 - val_accuracy: 0.0028

...

Epoch 277/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0256 - accuracy: 0.0029 - val_loss: 0.0291 - val_accuracy: 0.0028
Epoch 278/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0247 - accuracy: 0.0020 - val_loss: 0.0290 - val_accuracy: 0.0028
Epoch 279/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0258 - accuracy: 0.0027 - val_loss: 0.0295 - val_accuracy: 0.0028
Epoch 280/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0256 - accuracy: 0.0019 - val_loss: 0.0288 - val_accuracy: 0.0028
>>> model.evaluate((X_test_A, X_test_B), y_test)
194/194 [==============================] - 0s 753us/step - loss: 0.0300 - accuracy: 0.0040
[0.02998056262731552, 0.004037467762827873]
def Drawing_Scalars(history_name):
    
    history_DF = pd.DataFrame(history_name.history)
    # 그래프의 크기와 선의 굵기를 설정해주었다.
    history_DF.plot(figsize=(12, 8), linewidth=3)

    # 교차선을 그린다.
    plt.grid(True)

    plt.legend(loc = "upper right", fontsize =15)

    plt.title("Learning Curve", fontsize=30, pad = 30)
    plt.xlabel('Epoch', fontsize = 20, loc = 'center', labelpad = 20)
    plt.ylabel('Variable', fontsize = 20, rotation = 0, loc='center', labelpad = 40)

    # 위 테두리 제거
    ax=plt.gca()
    ax.spines["right"].set_visible(False) # 오른쪽 테두리 제거
    ax.spines["top"].set_visible(False) # 위 테두리 제거
    
    plt.show()
    
    
Drawing_Scalars(history)

  • 이전 포스트에서는 모든 데이터 셋을 Wide model과 Deep model에 넣었을 때는 Accuracy: 0.0034, Loss: 0.0242, epochs: 63에서 학습이 끝났으나, 이번 방법으로는 Accuracy: 0.0040, Loss: 0.0300, epochs: 280으로 기대에 미치는 성능이 나오진 않았다.
  • 학습에 대한 평가는 큰 차이가 없으나, 도리어 epochs가 63에서 280으로 학습 시간이 크게 늘어, 성능이 도리어 크게 떨어진 모습을 보여주었다.

 

 

 

 

6. 위 문제를 해결해보자.

  • 조기종료가 된 epochs가 63에서 280으로 증가했다는 소리는 주어진 데이터 셋이 이전보다 적합하지 않아, 학습하는데 시간이 오래 걸렸다는 소리다.
  • 그렇다면, 위에서 보았던 독립변수의 데이터 분포(히스토그램)와 독립변수와 종속변수의 상관관계(산점도)로 데이터 패턴의 단순한 정도를 본 방법이 틀린 것일까?
  • 앞서 우리가 학습하였던, "Tensorflow-4.0. 다층 퍼셉트론을 이용한 회귀모형 만들기"에서 회귀모형(Regression model)은 독립변수와 종속변수 간에 어떠한 경향성이 있다는 전제하에 독립변수가 변할 때, 종속변수가 변하고 이를 가장 잘 나타내는 계수를 찾는 것이 목적이라고 했다.
  • 딥러닝 역시 회귀모형의 원리와 매우 유사하게 돌아가므로, 이번에는 독립변수와 종속변수의 산점도(상관관계)에서 어떠한 경향성도 갖지 않는 "HouseAge", "AveRooms", "AveBedrms", "Population", "AveOccup", "Latitude", "Longitude" 모두를 Wide model에 넣고, 약한 선형 관계를 보이는 "MedInc"만 Deep model에 넣어보도록 하겠다.
# Dataset 생성
# 데이터를 쪼개기 좋게 변수의 순서를 바꾸자
CaliFornia_DF = CaliFornia_DF[["HouseAge", "Population", "Latitude", "Longitude", "MedInc",
                               "AveRooms", "AveBedrms", "AveOccup"]]

# train, validation, test set으로 쪼갠다.
X_train_all, X_test, y_train_all, y_test = train_test_split(CaliFornia_DF.values, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 정규화시킨다.
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

# 데이터 셋을 input layer의 수만큼 쪼갠다.
X_train_A, X_train_B = X_train[:, :1], X_train[:,1:]
X_valid_A, X_valid_B = X_valid[:, :1], X_valid[:,1:]
X_test_A, X_test_B = X_test[:, :1], X_test[:,1:]
# Model 
input_A = Input(shape=[1], name = "deep_input")
input_B = Input(shape=[7], name = "wide_input")
hidden1 = Dense(30, activation="relu", name = "hidden1")(input_A)
hidden2 = Dense(30, activation="relu", name = "hidden2")(hidden1)
concat = concatenate([input_B, hidden2], name = "concat")
output = Dense(1, name="output")(concat)
model = keras.Model(inputs=[input_A, input_B], outputs=[output])


# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = "msle",
              metrics=["accuracy"])

early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

# 학습
history = model.fit((X_train_A, X_train_B), y_train,epochs=300,
                    validation_data=((X_valid_A, X_valid_B), y_valid),callbacks=[early_stop])
Epoch 1/300
362/362 [==============================] - 2s 5ms/step - loss: 0.2047 - accuracy: 0.0032 - val_loss: 0.0548 - val_accuracy: 0.0014
Epoch 2/300
362/362 [==============================] - 1s 1ms/step - loss: 0.0520 - accuracy: 0.0031 - val_loss: 0.0470 - val_accuracy: 0.0014
Epoch 3/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0477 - accuracy: 0.0036 - val_loss: 0.0451 - val_accuracy: 0.0014
Epoch 4/300
362/362 [==============================] - 1s 2ms/step - loss: 0.0439 - accuracy: 0.0041 - val_loss: 0.0443 - val_accuracy: 0.0014

...

Epoch 46/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0438 - accuracy: 0.0019 - val_loss: 0.0438 - val_accuracy: 0.0014
Epoch 47/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0446 - accuracy: 0.0036 - val_loss: 0.0439 - val_accuracy: 0.0014
Epoch 48/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0446 - accuracy: 0.0033 - val_loss: 0.0444 - val_accuracy: 0.0014
Epoch 49/300
362/362 [==============================] - 0s 1ms/step - loss: 0.0430 - accuracy: 0.0029 - val_loss: 0.0440 - val_accuracy: 0.0014
>>> model.evaluate((X_test_A, X_test_B), y_test)
194/194 [==============================] - 0s 966us/step - loss: 0.0468 - accuracy: 0.0024
[0.04679189994931221, 0.002422480611130595]

>>> Drawing_Scalars(history)

  • Deep model로는 1개의 변수만 집어넣었고, Wide model로 7개 변수를 넣었더니, Accuracy: 0.0024, Loss: 0.0468, epochs: 49(EarlyStopping의 patience인자가 30이므로, 실제 수렴은 epoch 19에서 한 것을 알 수 있다)로 수렴하였다.
  • 모든 변수를 Deep model과 Wide model로 보내는 경우, Accuracy: 0.0034, Loss: 0.0242, epochs: 63으로, 정확도가 미미한 수준으로 더 좋아졌고, 수렴 속도가 보다 빨라졌으나, 큰 차이가 있진 않다.
  • Deep Learning은 아주 많은 양의 데이터가 있을 때, 그 안에 우리가 인식하지 못하는 패턴을 찾아내는 것이므로, 위와 같은 밀집 특징(Dense Feature)에 대해서는 모든 데이터를 넣는 것을 추천한다.
  • 희소 특징(Sparse Feature)은 Deep model을 통과하며, 학습이 제대로 이루어지지 않을 수 있으므로, Wide model에는 희소 특징만 넣도록 하자.

 

 

 

 

7. 참고 - 일반적인 다중 입력 모델의 사용 예

  • 위 그림처럼 다중 입력은, 전혀 다른 데이터 셋 들로부터 하나의 결과를 도출해낼 수도 있다.
  • 예를 들어, 물건 판매 웹사이트 사용자들에 대해 패턴을 뽑고자 한다면, 사용자들의 정보는 Dense 모델로, 사용자의 제품 후기는 RNN 모델로, 사용자의 관심 있는 제품 사진들을 CNN 모델로 학습시키고 이를 하나로 합쳐 하나의 결괏값을 뽑아낼 수도 있다.

 

 

[참고 자료]

arxiv.org/abs/1606.07792

 

Wide & Deep Learning for Recommender Systems

Generalized linear models with nonlinear feature transformations are widely used for large-scale regression and classification problems with sparse inputs. Memorization of feature interactions through a wide set of cross-product feature transformations are

arxiv.org

ai.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

 

Wide & Deep Learning: Better Together with TensorFlow

Posted by Heng-Tze Cheng, Senior Software Engineer, Google Research The human brain is a sophisticated learning machine, forming rules by me...

ai.googleblog.com

 

 

 지금까지 Wide & Deep Learning model을 Input Layer를 2개로 하여 다중 입력 모델로 만들어보았다. Keras API로 모델을 만드는 경우, 위처럼 쉬운 방법으로 다중 입력, 다중 출력 모델을 만들 수 있으므로, Keras로 모델을 만들 땐, 가능한 API로 만들도록 하자.

 지금까지 Wide & Deep Learning model의 개념을 어느 정도 알아보았으니, 다음 포스트에서는 좀 더 가볍게 해당 모델로 다중 입력, 다중 출력 모델을 만들어보도록 하겠다.

728x90
반응형
728x90
반응형

 DataFrame을 가지고 노려면, 기본적으로 Index와 Column 그리고 내가 원하는 값을 가지고 오는 법을 알아야 한다. 이전 포스트에서 Index를 가지고 노는 법에 대해 알아보았으니, 이번 포스트에서는 Column을 가지고 노는 방법에 대해 알아보도록 하겠다.

 

 

데이터 프레임과 칼럼(Column)

 이전 포스트에서 알아보았던, Index는 내가 원하는 Row(행, 레코드, 튜플)를 가지고 놀 수 있는 방법이었고, 이번에 학습할 Column은 열(변수, 속성, 필드)을 가지고 놀 수 있는 방법이다.

 DataFrame은 각 Column마다 담겨 있는 데이터의 양은 같으나, 담을 수 있는 데이터가 다르므로, 칼럼을 잘 가지고 놀 수 있어야만, DataFrame을 조작하기 쉬워진다.

 

 

 

 

1. 데이터 프레임의 기본 칼럼

  • 이전 "Pandas-데이터프레임 만들기"에서는 칼럼의 이름을 다 지정해줬었지만, 만약 데이터 프레임의 칼럼을 지정해주지 않는다면 어떻게 될까?
# Import Module
import pandas as pd
import numpy as np
# 기본 데이터 만들기
>>> np.random.seed(1234)
>>> matrix = np.random.randint(10, 100, size = (5,4))
>>> matrix
array([[57, 93, 48, 63],
       [86, 34, 25, 59],
       [33, 36, 40, 53],
       [40, 36, 68, 79],
       [90, 83, 57, 60]])
  • numpy의 array를 이용해서 단순한 행렬을 만들어보았다.
  • np.random.seed(): seed를 정해서 고정된 난수가 출력되도록 한다. 자신이 원하는 아무 숫자를 안에 넣고 실행 시, 그 seed에 대해 출력되는 난수는 동일한 값만 나오게 된다.
  • np.random.randint(low, high, size, dtype): low에서 high까지 자신이 지정한 size(shape)로 랜덤한 값이 출력된다.
>>> DF = pd.DataFrame(matrix)
>>> DF

  • 칼럼을 지정하지 않고 DataFrame을 만들면 Index와 마찬가지로 0부터 시작하는 값이 매겨진다.

 

1.1. RangeIndex

  • 이렇게 0부터 시작하는 index가 매겨지는 것을 RangeIndex라고 하는데, Python의 기본 함수인 range랑 그 기능이 거의 비슷하다.
# RangeIndex 생성
>>> pd.RangeIndex(10)
RangeIndex(start=0, stop=10, step=1)

# RangeIndex의 값을 list로 바꿔주자.
>>> list(pd.RangeIndex(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# range와 비교
>>> range(10)
range(0, 10)

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • RangeIndex 역시 range처럼 특정 간격을 가지고 Data를 생성할 수 있으며, 객체의 값을 활성화해주지 않으면, 그 내용물이 튀어나오지 않는다.
# 시작, 끝, 간격을 조정해보자.
>>> pd.RangeIndex(start=10, stop = 30, step=2)
RangeIndex(start=10, stop=30, step=2)

>>> list(pd.RangeIndex(start=10, stop = 30, step=2))
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]

# 위 기능은 range와 같다.
>>> range(10, 30, 2)
range(10, 30, 2)

>>> list(range(10, 30, 2))
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
  • index는 많은 양의 Data를 담는 DataFrame 특성 상, 그 숫자가 매우 커질 수 있기 때문에 range와 마찬가지로 이를 바로 출력하지 않고, 호출 시 출력한다.
  • RangeIndex는 크게 신경 쓸 것은 아니지만, DataFrame을 조작하다가 칼럼의 이름을 지정할 수 없는 경우가 있다. 이럴 때는 RangeIndex가 출력되므로, 이를 조작할 수는 있어야 한다.

 

 

 

 

2. Column의 이름 바꾸기

  • DataFrame의 Column이름을 바꾸는 방법은 크게 두 가지가 있다.
  1. 한 번에 다 바꾸기
  2. 내가 원하는 Column의 이름만 바꾸기

 

2.1. Column의 이름 한 번에 다 바꾸기

>>> DF.columns = ["C1", "C2", "C3", "C4"]
>>> DF

>>> DF.columns
Index(['C1', 'C2', 'C3', 'C4'], dtype='object')

>>> type(DF.columns)
pandas.core.indexes.base.Index
  • DataFrame.columns: DataFrame의 Column을 조작하는 가장 기본적인 방법으로 DataFrame의 Column을 pandas index라는 type으로 가지고 온다. array와 유사하므로, array라고 생각해도 큰 문제는 없다.

 

2.2. 특정 Column의 이름만 바꾸기

  • 칼럼 C1은 math로 C3는 English로 바꿔보자.
>>> DF.rename(columns={"C1":"math", "C3":"English"}, inplace = True)
>>> DF

  • DataFrame.rename(columns={"old":"new"}, inplace=False): DataFrame의 old 컬럼을 new 컬럼으로 이름을 바꾼다.
  • 이번에는 C2를 math로 바꿔보자.
>>> DF.rename(columns={"C2":"math"}, inplace = True)
>>> DF

  • DataFrame의 컬럼 역시 Index처럼 이름이 중복된다.
  • 뒤에서 다시 다루겠지만, Index처럼 같은 이름의 컬럼 출력 시, 동일한 컬럼이 생성된다.
>>> DF.math

 

 

 

 

3. Column이름들을 꾸며주기

  • DataFrame의 컬럼의 이름은 위에서 봤듯이 중복될 수도 있고, DataFrame.columns라는 함수를 통해, array를 다루듯 조작할 수도 있다.
  • 또한, Column의 이름들에 특정한 패턴으로 한 번에 조작을 해줄 수도 있다.
  • 해당 예시를 보기 전에 조작이 쉽도록, 변수들의 이름을 전부 바꿔주었다.
DF.columns = ["mid math", "mid English", "fin math", "fin English"]

 

3.1. 컬럼명의 특정 문자 바꿔주기

>>> DF.columns = DF.columns.str.replace(" ", "_")
>>> DF

  • DataFrame.columns.str.replace(pat, repl): 판다스의 str모듈은 문자열 데이터를 다루는데 특화된 함수의 모음으로, 보통 칼럼 각각에 적용하지만, 칼럼의 이름에도 적용 가능하다.
  • 컬럼을 다룰 때, 공백이 있는 경우 조작이 조금 불편해지므로, 가능한 공백은 언더바("_")로 치환하도록 하자.
  • str 모듈은 굉장히 유용한 함수들을 모아놓은 모듈이므로, 추후 자세히 다루도록 하겠다.

 

3.2. 모든 컬럼명 앞에 공통 문자 붙이기

>>> DF.add_prefix("A__")

 

3.3. 모든 컬럼명 뒤에 공통 문자 붙이기

>>> DF.add_suffix("_Dummy")

  • 컬럼명의 앞이나 뒤에 공통된 문자를 붙여주는 기능은 데이터 전처리 시, 더미 변수를 만들거나, One-Hot Vector 등을 만들 때, 쉽게 쓸 수 있다.
  • 지금처럼 모든 변수들에게 특정 문자를 붙이는 것보다는, 특정 컬럼들만 선택하여, 공통 문자를 바꾸는 것이 더 효과적이다. 이는 추후 컬럼 조회를 학습하면서 공부하도록 하겠다.

 

 

 

 지금까지 컬럼명을 가지고 노는 방법에 대해 학습해 보았다. 다음 포스트에서는 컬럼명을 가지고 놀 수 있는 다른 방법에 대해 학습해보도록 하겠다.

728x90
반응형
728x90
반응형

 이전 포스트에서 다뤘던 선형 회귀 모형은 단일 입력층과 단일 출력층을 갖는 형태였다. 그러나, Keras를 사용하는 Tensorflow의 가장 큰 장점인 함수형 API모델을 사용하면, 다중 입력과 다중 출력을 갖는 모델을 만들 수 있다.

이번 포스트에서는 Keras의 장점인 함수형 API로 대규모 회귀 및 분류 모델에서 주로 사용되는 Wide & Deep Learning 모델을 만들어보자.

 

 

와이드 & 딥(Wide & Deep) 신경망

  • 2016년 헝쯔 청(Heng-Tze Cheng et al.)의 논문에 소개된 신경망이다.
    Heng-Tze Cheng et al., "Wide & Deep Learning for Recommender Systems." Proceedings of the First Workshop on Deep Learning for Recommender Systems (2016): 7 - 10. http://homl.info/widedeep
  • 인간은 일반화와 암기를 통해 학습을 한다.

  • 일반화(Deep Models): 인간은 "참새는 날 수 있다.", "비둘기는 날 수 있다."를 통해 "날개를 가진 동물은 날 수 있다"라 일반화를 시킨다.
  • 암기(Wide Models): 반면에 "펭귄은 날 수 없다.", "타조는 날 수 없다." 등과 같은 예외 사항을 암기하여, 일반화된 규칙을 더욱 세분화시킨다.
  • Wide & Deep Learning은 이러한 인간의 일반화와 암기를 결합하여 학습하는 점에 착안하여, 만들어진 기계 학습 방법이다.
  • 앞서 우리가 학습해왔던 심층 신경망(Deep Network)은 대상으로부터 공통된 패턴을 찾아내어 일반화시키지만, 그 패턴을 가지고 있으나, 이에 해당하지 않는 많은 반례들이 존재한다.
    (일반적으로 딥 러닝에서는 이를 감안한 데이터 셋을 준비해 학습한다.)
  • Wide & Deep Learning은 여기서 더 나아가 넓은 선형 모델을 공동으로 학습시켜, 일반화(Deep Learning)와 암기(Wide Linear model)의 장점을 얻는다.
  • Wide & Deep Learning은 추천 시스템, 검색 및 순위 문제 같은 많은 양의 범주형 특징(Categorical Feature)이 있는 데이터를 사용하는 대규모 회귀, 분류 모델에서 유용하게 사용된다.
  • Wide & Deep Learning은 위 그림처럼 앞서 학습했던 다층 퍼셉트론(MLP - Deep way)에 입력 데이터의 일부 또는 전체 데이터가 출력층에 바로 연결(Wide way)되는 부분이 추가된 것이다.
  • 이로 인해, Wide & Deep Learning은 복잡한 패턴과 간단한 규칙을 모두 학습할 수 있다.
    (MLP에서는 간단한 패턴이 연속된 변환으로 인해 왜곡될 수 있다.)

 

 

 

 

1. Tensorflow의 함수형 API를 사용하여 Wide & Deep Learning을 구현해보자.

  • sklearn에서 제공하는 캘리포니아 주택 가격 데이터 셋을 사용해보자.
  • sklearn에서 제공하는 기초 함수로 데이터 셋을 분리해보자.
  • sklearn에서 제공하는 기초 함수로 데이터 셋의 스케일 조정을 실시해보자.

  • Wide model과 Deep model이 같은 데이터를 바라보는 Input이 1개인 Wide & Deep Learning Model을 만들어보자.

1.1 데이터셋 준비

# import Module
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import pandas as pd
# 캘리포니아 주택 가격 데이터 셋 확인
Rawdict = fetch_california_housing()

Cal_DF = pd.DataFrame(Rawdict.data, columns=Rawdict.feature_names)
Cal_DF

# Data의 모든 컬럼들의 data type을 확인한다.
>>> Cal_DF.dtypes
MedInc        float64
HouseAge      float64
AveRooms      float64
AveBedrms     float64
Population    float64
AveOccup      float64
Latitude      float64
Longitude     float64
dtype: object


# Data의 모든 컬럼들의 결측값 개수를 확인한다.
>>> Cal_DF.isnull().sum()
MedInc        0
HouseAge      0
AveRooms      0
AveBedrms     0
Population    0
AveOccup      0
Latitude      0
Longitude     0
dtype: int64
  • 데이터 셋은 모두 소수(float64)로 구성되어 있다.
  • 결측 값은 모든 칼럼에 존재하지 않는다.

1.2 데이터셋 분리

>>> X_train_all, X_test, y_train_all, y_test = train_test_split(Rawdict.data, Rawdict.target, test_size = 0.3)
>>> X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)
>>> print("Train set shape:", X_train.shape)
>>> print("Validation set shape:", X_valid.shape)
>>> print("Test set shape:", X_test.shape)

Train set shape: (11558, 8)
Validation set shape: (2890, 8)
Test set shape: (6192, 8)
  • sklearn.model_selection.train_test_split(array, test_size, shuffle): dataset을 쉽게 나눌 수 있는 sklearn 함수로 일반적으로 dataset, label 이 두 array를 동시에 넣고 사용한다.
  • test_size: test Dataset의 비율(float)이나 총 숫자(int)로 나눠준다.
  • shuffle: Dataset을 쪼개기 전에 섞을지 여부를 정해준다.

1.3. 데이터셋 스케일 조정

# 스케일 조정
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
  • sklearn.preprocessing.StandardScaler(): 표준 정규분포로 스케일링할 준비를 한다.
  • sklearn.prepocessing은 이밖에도 MinMaxScaler, MaxAbsScaler, RobustScaler 등 다양한 스케일링 기법을 제공한다.
  1. MinMaxScaler(): 최소-최대 스케일 변환, 이상치 영향이 크다.
  2. MaxAbsScaler(): 최대 절대 값을 1로 변환(-1.0 ~ 1.0으로 만드나, 전부 양수인 경우, MinMaxscaler와 동일하다), 이상치 영향이 크다.
  3. StandardScaler(): 표준 정규분포 스케일 변환
  4. RobustScaler(): 중앙값과 사분위 범위(Interquartile range)를 사용하며, StandardScaler보다 표준화 후 동일한 값을 더 넓게 분포 시킴

 

  • StandardScaler().fit_transform(): 데이터셋을 표준 정규분포화 한다. 단, fit_transform에 들어가는 Dataset의 평균과 표준편차를 기준으로 저장하게 된다.
  • StandardScaler().transform(): 데이터셋을 표준 정규분포화 한다. 표준 정규분포화하는 평균과 표준편차는 fit 된 Dataset을 따른다.
  • 앞서 학습하였듯, 스케일링 조정의 기준은 훈련 셋(Train set)이다. 그러므로, fit_transform()에는 Train set이 들어가야 하고, transform에는 검증 셋(Validation set)이나 시험 셋(Test set)이 들어가야 한다.

 

 

 

 

2. 모델 만들기

  • Wide & Deep Learning 같은 순차적이지 않은 신경망을 만들기 위해선 keras의 특징인 함수형 API를 이용해서 Layer를 만들어야 한다.
  • 이번 모델은 1개의 Input Layer와 1개의 Output Layer를 갖는 Wide-Deep Learning 모델을 만들어보겠다.
from tensorflow import keras
from tensorflow.keras.layers import (Input, Dense, Concatenate)
# Model 생성하기
input_ = Input(shape=X_train.shape[1:])
hidden1 = Dense(30, activation="relu")(input_)
hidden2 = Dense(30, activation="relu")(hidden1)
concat = Concatenate()([input_, hidden2])
output = Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])
  • 함수형 API 모델 생성 방법
  1. Input 객체 생성: Input layer는 배치 크기를 포함하지 않는, array의 모양을 정한다. 즉, Data의 크기가 아닌, 한 Row의 Data의 모양만 반영된다. 예를 들어, shape = (32,)가 되면, 32차원의 벡터가 입력된다는 의미다.
    (변수 이름에 _가 들어간 것은 파이썬에서 언더스코어(_)의 기능을 이용한 것으로, 단순하게 input함수와 충돌되지 않도록 이렇게 만든 것이다.)
  2. 은닉층 객체 생성: 기존처럼 Layer를 add로 만들어진 모델에 은닉층을 쌓는 것이 아닌, 함수로 만들어 변수에 담는다. 여기서 케라스에서 층이 연결될 방법만 선언하였고, 그 어떤 데이터 처리도 발생하지 않은 상태이다.
  3. 결합층 객체 생성: Wide-Deep Learning은 선형 모델과 딥러닝 모델을 동시에 사용하는 방법이므로, 입력될 객체인 input_과 hidden2를 연결해준다.
  4. 출력층 객체 생성: 결합층에서 전달된 내용을 출력함
  5. 모델 생성: 입력과 출력이 지정된 Keras 모델 생성
  • tf.keras.layers.Concatenate(): 연결층이 Tensor를 연결하는 방법은 아래와 같다.
>>> x = np.arange(1, 41, 2).reshape(2,2,5)
>>> x
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39]]])


>>> y = np.arange(2, 21, 2).reshape(2,1,5)
>>> y
array y:
 [[[ 2  4  6  8 10]]

 [[12 14 16 18 20]]]


>>> tf.keras.layers.Concatenate(axis=1)([x, y])
<tf.Tensor: shape=(2, 3, 5), dtype=int32, numpy=
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19],
        [ 2,  4,  6,  8, 10]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39],
        [12, 14, 16, 18, 20]]])>
   
   
>>> np.concatenate((x, y), axis=1)
array([[[ 1,  3,  5,  7,  9],
        [11, 13, 15, 17, 19],
        [ 2,  4,  6,  8, 10]],

       [[21, 23, 25, 27, 29],
        [31, 33, 35, 37, 39],
        [12, 14, 16, 18, 20]]])
  • tf.keras.layers.Concatenate()는 np.concatenate() 함수와 동일한 역할을 하며, axis를 어떻게 잡느냐에 따라, 출력되는 Tensor의 모양을 다르게 할 수 있다.

 

 

 

 

3. 모델 학습 및 최종 코드 정리

  • 지금까지 작성했던 코드와 이전에 사용했던 괜찮은 기능들을 합쳐 코드를 정리해보자.
# Import Module
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import callbacks
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import (Input, Dense, Concatenate)
# Dataset 생성
Rawdict = fetch_california_housing()
X_train_all, X_test, y_train_all, y_test = train_test_split(Rawdict.data, Rawdict.target, test_size = 0.3)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_all, y_train_all, test_size = 0.2)

# 스케일 조정
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
# 모델 생성
input_ = Input(shape=X_train.shape[1:])
hidden1 = Dense(30, activation="relu")(input_)
hidden2 = Dense(30, activation="relu")(hidden1)
concat = Concatenate()([input_, hidden2])
output = Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])

# 모델 컴파일
model.compile(optimizer=Adam(learning_rate=0.005),
              loss = "msle",
              metrics=["accuracy"])
# 학습
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=30, restore_best_weights=True)

history = model.fit(X_train, y_train, 
                    epochs=100,
                    batch_size=32,
                    validation_data=(X_valid, y_valid),
                    callbacks=[early_stop])
Epoch 1/100
362/362 [==============================] - 3s 5ms/step - loss: 0.1195 - accuracy: 0.0031 - val_loss: 0.0360 - val_accuracy: 0.0014
Epoch 2/100
362/362 [==============================] - 1s 1ms/step - loss: 0.0340 - accuracy: 0.0031 - val_loss: 0.0342 - val_accuracy: 0.0014
Epoch 3/100
362/362 [==============================] - 1s 1ms/step - loss: 0.0329 - accuracy: 0.0027 - val_loss: 0.0326 - val_accuracy: 0.0014
Epoch 4/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0336 - accuracy: 0.0034 - val_loss: 0.0321 - val_accuracy: 0.0014

...

Epoch 60/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0219 - accuracy: 0.0045 - val_loss: 0.0263 - val_accuracy: 0.0014
Epoch 61/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0221 - accuracy: 0.0033 - val_loss: 0.0285 - val_accuracy: 0.0014
Epoch 62/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0244 - accuracy: 0.0033 - val_loss: 0.0267 - val_accuracy: 0.0010
Epoch 63/100
362/362 [==============================] - 0s 1ms/step - loss: 0.0227 - accuracy: 0.0032 - val_loss: 0.0264 - val_accuracy: 0.0010
>>> model.evaluate(X_test, y_test)
194/194 [==============================] - 0s 750us/step - loss: 0.0242 - accuracy: 0.0034
[0.02424260601401329, 0.0033914728555828333]
def Drawing_Scalars(history_name):
    
    history_DF = pd.DataFrame(history_name.history)
    # 그래프의 크기와 선의 굵기를 설정해주었다.
    history_DF.plot(figsize=(12, 8), linewidth=3)

    # 교차선을 그린다.
    plt.grid(True)

    plt.legend(loc = "upper right", fontsize =15)

    plt.title("Learning Curve", fontsize=30, pad = 30)
    plt.xlabel('Epoch', fontsize = 20, loc = 'center', labelpad = 20)
    plt.ylabel('Variable', fontsize = 20, rotation = 0, loc='center', labelpad = 40)

    # 위 테두리 제거
    ax=plt.gca()
    ax.spines["right"].set_visible(False) # 오른쪽 테두리 제거
    ax.spines["top"].set_visible(False) # 위 테두리 제거
    
    plt.show()
    
    
Drawing_Scalars(history)

  • 위 모델의 학습 결과를 보면, accuracy는 빠르게 0에 가깝게 떨어진 것을 볼 수 있다.
  • model.evaluate()에서 test set을 대상으로 모델을 평가한 결과 Accuracy는 0.0034가 나왔다.
  • 분류 모델을 사용할 때와, 위와 같은 연속형 데이터에 대한 회귀 모델은 Accuracy의 기준이 다르므로, 위 경우엔 Accuracy, loss 모두 0에 가깝게 나오는 것이 좋다.
  • 참고자료: "머신러닝-5.2. 손실함수(3)-평균제곱근오차(RMSE)"
  • loss는 model.evaluate()에서 0.0242로 최종 손실 값이 0에 가깝게는 나왔으나, 기존 모델보다는 크게 나왔다. 그러나, 손실 값은 상대적인 기준이므로, 0에 가까울수록 좋은 것이긴 하나, 단순하게 나쁘게 봐서는 안된다.

 

[참고 자료]

arxiv.org/abs/1606.07792

 

Wide & Deep Learning for Recommender Systems

Generalized linear models with nonlinear feature transformations are widely used for large-scale regression and classification problems with sparse inputs. Memorization of feature interactions through a wide set of cross-product feature transformations are

arxiv.org

ai.googleblog.com/2016/06/wide-deep-learning-better-together-with.html

 

Wide & Deep Learning: Better Together with TensorFlow

Posted by Heng-Tze Cheng, Senior Software Engineer, Google Research The human brain is a sophisticated learning machine, forming rules by me...

ai.googleblog.com

 

 

 

 지금까지 Input layer가 1개인 Wide & Deep Learning model을 만들어보았다. 그러나, Wide & Deep Learning model의 가장 큰 특징은 Input 되는 층이 지금까지처럼 1개의 동일한 Layer가 아니라, 암기(Linear Learning)를 위한 데이터와 일반화(Deep Learning)를 위한 데이터가 Input 될 층을 따로 구성될 수 있다는 것이다.

 다음 포스트에서는 Input layer가 2개인 경우에 대하여 Wide & Deep Learning model을 만들어보면서, Wide & Deep Learning model에 대해 더 알아보도록 하자.

728x90
반응형

+ Recent posts