imports

import numpy as np
import matplotlib.pyplot as plt 
import tensorflow as tf 
import tensorflow.experimental.numpy as tnp 
tnp.experimental_enable_numpy_behavior()
import graphviz
def gv(s): return graphviz.Source('digraph G{ rankdir="LR"'+s + '; }')

$x \to \hat{y}$ 가 되는 과정을 그림으로 그리기

- 단순회귀분석의 예시

  • $\hat{y}_i = \hat{\beta}_0 + \hat{\beta}_1 x_i, \quad i=1,2,\dots,n$

(표현1)

gv(''' 
    "1" -> "β̂₀ + xₙ*β̂₁,    bias=False"[label="* β̂₀"]
    "xₙ" -> "β̂₀ + xₙ*β̂₁,    bias=False"[label="* β̂₁"]
    "β̂₀ + xₙ*β̂₁,    bias=False" -> "ŷₙ"[label="identity"]

    "." -> "...................................."[label="* β̂₀"]
    ".." -> "...................................."[label="* β̂₁"]
    "...................................." -> "..."[label=" "]

    "1 " -> "β̂₀ + x₂*β̂₁,    bias=False"[label="* β̂₀"]
    "x₂" -> "β̂₀ + x₂*β̂₁,    bias=False"[label="* β̂₁"]
    "β̂₀ + x₂*β̂₁,    bias=False" -> "ŷ₂"[label="identity"]
    
    "1  " -> "β̂₀ + x₁*β̂₁,    bias=False"[label="* β̂₀"]
    "x₁" -> "β̂₀ + x₁*β̂₁,    bias=False"[label="* β̂₁"]
    "β̂₀ + x₁*β̂₁,    bias=False" -> "ŷ₁"[label="identity"]
''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G 1 1 β̂₀ + xₙ*β̂₁,    bias=False β̂₀ + xₙ*β̂₁,    bias=False 1->β̂₀ + xₙ*β̂₁,    bias=False * β̂₀ ŷₙ ŷₙ β̂₀ + xₙ*β̂₁,    bias=False->ŷₙ identity xₙ xₙ xₙ->β̂₀ + xₙ*β̂₁,    bias=False * β̂₁ . . .................................... .................................... .->.................................... * β̂₀ ... ... ....................................->... .. .. ..->.................................... * β̂₁ 1 1 β̂₀ + x₂*β̂₁,    bias=False β̂₀ + x₂*β̂₁,    bias=False 1 ->β̂₀ + x₂*β̂₁,    bias=False * β̂₀ ŷ₂ ŷ₂ β̂₀ + x₂*β̂₁,    bias=False->ŷ₂ identity x₂ x₂ x₂->β̂₀ + x₂*β̂₁,    bias=False * β̂₁ 1   1   β̂₀ + x₁*β̂₁,    bias=False β̂₀ + x₁*β̂₁,    bias=False 1  ->β̂₀ + x₁*β̂₁,    bias=False * β̂₀ ŷ₁ ŷ₁ β̂₀ + x₁*β̂₁,    bias=False->ŷ₁ identity x₁ x₁ x₁->β̂₀ + x₁*β̂₁,    bias=False * β̂₁

1.

$f(x) = x$ 로 표현이 되는 것을

$f(\hat{\beta}_0 + x_1 \hat{\beta}) = \hat{y}_1$ 이렇게 항등함수를 취해서 구해준다.

그러면 $\hat{\beta}_0 + x_1 \hat{\beta} = \hat{y}_1$ 이게 되겠지?

2.

..

n.

..

- 표현1의 소감?

  • 그저 고생해서 만든것 같음
  • 그런데 그냥 다 똑같은 그림의 반복이라 사실 고생한 의미가 없음.
    • $\hat{y}$를 만드는 rule이 바뀌지 않기 때문, 모든 observation에 똑같이 적용
    • 그 rule이 다르다면 반복하면 안 되겠지.(여기서 rule은 식을 의미)

(표현2)

- 그냥 아래와 같이 그리고 "모든 $i=1,2,3,\dots,n$에 대하여 $\hat{y}_i$을 아래의 그림과 같이 그린다"고 하면 될것 같다.

gv(''' 
    "1" -> "β̂₀ + xᵢ*β̂₁,    bias=False"[label="* β̂₀"]
    "xᵢ" -> "β̂₀ + xᵢ*β̂₁,    bias=False"[label="* β̂₁"]
    "β̂₀ + xᵢ*β̂₁,    bias=False" -> "ŷᵢ"[label="identity"]

''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G 1 1 β̂₀ + xᵢ*β̂₁,    bias=False β̂₀ + xᵢ*β̂₁,    bias=False 1->β̂₀ + xᵢ*β̂₁,    bias=False * β̂₀ ŷᵢ ŷᵢ β̂₀ + xᵢ*β̂₁,    bias=False->ŷᵢ identity xᵢ xᵢ xᵢ->β̂₀ + xᵢ*β̂₁,    bias=False * β̂₁

(표현3)

- 그런데 "모든 $i=1,2,3,\dots,n$에 대하여 $\hat{y}_i$을 아래의 그림과 같이 그린다" 라는 언급자체도 반복할 필요가 없을 것 같다. (어차피 당연히 그럴테니까) 그래서 단순히 아래와 같이 그려도 무방할듯 하다.

gv(''' 
    "1" -> "β̂₀ + x*β̂₁,    bias=False"[label="* β̂₀"]
    "x" -> "β̂₀ + x*β̂₁,    bias=False"[label="* β̂₁"]
    "β̂₀ + x*β̂₁,    bias=False" -> "ŷ"[label="identity"]

''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G 1 1 β̂₀ + x*β̂₁,    bias=False β̂₀ + x*β̂₁,    bias=False 1->β̂₀ + x*β̂₁,    bias=False * β̂₀ β̂₀ + x*β̂₁,    bias=False->ŷ identity x x x->β̂₀ + x*β̂₁,    bias=False * β̂₁

(표현4)

- 위의 모델은 아래와 같이 쓸 수 있다. ($\beta_0$를 바이어스로 표현)

  • $x \hat{\beta}_1 + \hat{\beta}_0$
  • input $\times$ parameter + bias

gv('''
"x" -> "x*β̂₁,    bias=True"[label="*β̂₁"] ;
"x*β̂₁,    bias=True" -> "ŷ"[label="indentity"] ''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G x x x*β̂₁,    bias=True x*β̂₁,    bias=True x->x*β̂₁,    bias=True *β̂₁ x*β̂₁,    bias=True->ŷ indentity
  • 실제로는 이 표현을 많이 사용함

(표현5)

- 벡터버전으로 표현하면 아래와 같다. 이 경우에는 ${\bf X}=[1,x]$에 포함된 1이 bias의 역할을 해주므로 bias = False 임.

gv('''
"X" -> "X@β̂,    bias=False"[label="@β̂"] ;
"X@β̂,    bias=False" -> "ŷ"[label="indentity"] ''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G X X X@β̂,    bias=False X@β̂,    bias=False X->X@β̂,    bias=False @β̂ X@β̂,    bias=False->ŷ indentity
  • 교수님 선호 표현

$\begin{bmatrix} 1 & X_1 \end{bmatrix} \begin{bmatrix} \hat{\beta}_0 \\ \hat{\beta}_1 \end{bmatrix} \rightarrow$ vector version & bias $\rightarrow\hat{\beta}_0 + x_1 \hat{\beta}_1 + \hat{\alpha}$

  • 기본적으로 직선으로 보겠다는 뜻.
  • $\hat{y}_1 = \hat{\beta}-0 + x_1 \hat{\beta}_1$
  • $\hat{y}_1 = \hat{\alpha}_0 + \hat{\beta}_0 + x_1 \hat{\beta}_1$ 이렇게 표현한다면? 굳이 bias를 두 개의 변수나 쓸 필요가 있을까? 싶은거지.

(표현5)`

- 딥러닝에서는 $\hat{\boldsymbol{\beta}}$ 대신에 $\hat$을 라고 표현한다.

  • $\bf{W}$는 weight를 의미하지~
  • $\hat{y}_i = \hat{w}_0 + x\hat{w}_1$

gv('''
"X" -> "X@Ŵ,    bias=False"[label="@Ŵ"] ;
"X@Ŵ,    bias=False" -> "ŷ"[label="identity"] ''')
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> G X X X@Ŵ,    bias=False X@Ŵ,    bias=False X->X@Ŵ,    bias=False @Ŵ X@Ŵ,    bias=False->ŷ identity

- 실제로는 표현4 혹은 표현5를 외우면 된다.

Layer의 개념

- (표현4) 혹은 (표현5)의 그림은 레이어로 설명할 수 있다.

glm 추상화...?

- 레이어는 항상 아래와 같은 규칙을 가진다.

  • 첫 동그라미는 레이어의 입력이다.
  • 첫번째 화살표는 선형변환을 의미한다.
  • 두번째 동그라미는 선형변환의 결과이다. (이때 bias가 false인지 true인지에 따라서 실제 수식이 조금 다름)
  • 두번째 화살표는 두번째 동그라미에 어떠한 함수 $f$를 취하는 과정을 의미한다. (우리의 그림에서는 항등함수 취하니까 $f(x)=x$)
  • 세번째 동그라미는 레이어의 최종출력이다.

- 엄청 복잡한데, 결국 레이어를 만들때 위의 그림들을 의미하도록 하려면 아래의 4개의 요소만 필요하다.

  1. 레이어의 입력차원
  2. 선형변환의 결과로 얻어지는 차원
  3. 선형변환에서 바이어스를 쓸지? 안쓸지?
  4. 함수 $f$

- $\star$주목: 1,2가 결정되면 자동으로 $\hat$의 차원이 결정된다.

(예시)

  • 레이어의 입력차원($p$)=2, 선형변환의 결과로 얻어지는 차원=1: $\hat{\bf W}$는 (2,1) 매트릭스
    • $(n,p) \times \bf{W} = (n,1) \rightarrow (1,2) \times (2,1) = (1,1)$
  • 레이어의 입력차원($p$)=20, 선형변환의 결과로 얻어지는 차원=5: $\hat{\bf W}$는 (20,5) 매트릭스
    • $(n,p) \times \bf{W} = (n,1) \rightarrow (1,20) \times (20,5) = (1,5)$
  • 레이어의 입력차원($p$)=2, 선형변환의 결과로 얻어지는 차원=50: $\hat{\bf W}$는 (2,50) 매트릭스
    • $(n,p) \times \bf{W} = (n,1) \rightarrow (1,2) \times (2,50) = (1,50)$

- $\star$주목2: 이중에서 절대 생략불가능 것은 "2. 선형변환의 결과로 얻어지는 차원" 이다. (1,3,4는 생략가능)

  • 1 레이어의 입력차원 생략? $\rightarrow$ 레이어의 입력차원: 실제 레이어에 데이터가 들어올 때 데이터의 입력차원을 컴퓨터 스스로 체크하여 $\hat{\bf W}$의 차원을 결정할 수 있음.
    • ex) 선형변환의 결과로 얻어지는 차원이 5인 걸 아는데 입력차원이 10이야? 그럼 $\hat{\bf{W}}$를 $(10,5)$로 매트릭스 맞추면 되지.
    • 사용자에게 가장 편리함을 가져다 준 조건(?)..
  • 3 선형변환에서 바이어스를 쓸지? 안쓸지? 생략? $\rightarrow$ 바이어스를 쓸지? 안쓸지? 기본적으로 쓴다고 가정한다.
  • 4 함수 $𝑓$ 생략? $\rightarrow$ 함수 $f$: 기본적으로 항등함수를 가정하면 된다.

Keras를 이용한 풀이

- 기본뼈대: net생성 $\to$ add(layer) $\to$ compile(opt,loss) $\to$ fit(data,epochs)

  • loss function을 정의하고 그 loss function을 minimize하는 반복 알고리즘을 결정하여 학습한다.

- 데이터정리

$${\bf y}\approx 2.5 +4*x$$

tnp.random.seed(43052)
N= 200 
x= tnp.linspace(0,1,N)
epsilon= tnp.random.randn(N)*0.5 
y= 2.5+4*x +epsilon
X=tf.stack([tf.ones(N,dtype='float64'),x],axis=1)

풀이1: 스칼라버전

(0단계) 데이터정리

y = y.reshape(-1)
x = x.reshape(-1)

스칼라니까 이전에 배웠듯이 이렇게 해야하지 않아?

차원 에러가 뜬다.

길이가 200인 벡터가 아니라 (200,1)인 벡터로 만들어줘야 한다.

y=y.reshape(N,1)
x=x.reshape(N,1)
x.shape,y.shape
(TensorShape([200, 1]), TensorShape([200, 1]))

(1단계) net 생성

net = tf.keras.Sequential() 

(2단계) net.add(layer)

  • tf.keras.layers.Dense(1) 여기서 1이란, 선형결과로 얻어지는 차원을 의미함, 1차원
  • 입력차원? 데이터를 넣어보고 결정, 바이어스=디폴드값을 쓰겠음 (use_bias=true), 함수도 디폴트값을 쓰겠음 $(f(x)=x)$
layer = tf.keras.layers.Dense(1) 
net.add(layer)

(3단계) net.compile(opt,loss_fn)

net.compile(tf.keras.optimizers.SGD(0.1), tf.keras.losses.MSE) 

(4단계) net.fit(x,y,epochs)

  • batch_size=N 일 경우에 경사하강법이 적용, batch_size!=N 이면 확률적 경사하강법 적용
  • 우리가 구현한 것과 같은 결과를 얻기 위해서 batch_size를 200으로 설정

🤓 질문!

  • 확률적 경사하강법 - batch_size!=N N 개 중 batch_size 만큼만
    • 샘플 데이터에 대해서만 경사gradient 계산
    • 최적해에 정확히 도닳하지 못할 가능성
    • 확률적 경사 하강법은 속도가 매우 빠르고 메모리를 적게 먹는다는 장점이 있으나, 경사를 구할 때, 무작위성을 띄므로 지역 최솟값에서 탈출하기 쉬우나, 전역 최솟값에 다다르기 힘들다는 단점 -> 미니 배치 경사 하강법 등장.
  • 경사하강법 - batch_size=N N 개 중 N개 모두
    • 경사하강법 대상이 배치 크기와 동일
    • 안정적으로 수혐하나 이 때문에 최소해local minimum에 빠지더라도 헤어나오기 힘들다. 즉 local optima 문제가 발생할 가능성이 높다.
    • 학습 데이터셋이 커질수록 시간과 리소스 소모가 지나치게 크다.
net.fit(x,y,epochs=1000,verbose=0,batch_size=N)
<keras.callbacks.History at 0x7f7ce00c7dc0>

(결과확인)

net.weights
[<tf.Variable 'dense/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[3.9330251]], dtype=float32)>,
 <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([2.5836723], dtype=float32)>]

풀이2: 벡터버전

(0단계) 데이터정리

X.shape,y.shape
(TensorShape([200, 2]), TensorShape([200, 1]))

(1단계) net 생성

net = tf.keras.Sequential() 

(2단계) net.add(layer)

vector 구할 때 bias는 false로, 굳이 bias 나누지 말고 합치자

layer = tf.keras.layers.Dense(1,use_bias=False) 
net.add(layer)

(3단계) net.compile(opt,loss_fn)

net.compile(tf.keras.optimizers.SGD(0.1), tf.keras.losses.MSE) 

(4단계) net.fit(x,y,epochs)

net.fit(X,y,epochs=1000,verbose=0,batch_size=N) # batch_size=N 일 경우에 경사하강법이 적용, batch_size!=N 이면 확률적 경사하강법 적용 
<keras.callbacks.History at 0x7f7c786169e0>

(결과확인)

net.weights
[<tf.Variable 'dense_1/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5836723],
        [3.9330251]], dtype=float32)>]

net = tf.keras.Sequential() 
layer = tf.keras.layers.Dense(1) 
net.add(layer)
net.compile(tf.keras.optimizers.SGD(0.1), tf.keras.losses.MSE) 
net.fit(X,y,epochs=1000,verbose=0,batch_size=N) 
net.weights
[<tf.Variable 'dense_2/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[0.7650072],
        [3.9330268]], dtype=float32)>,
 <tf.Variable 'dense_2/bias:0' shape=(1,) dtype=float32, numpy=array([1.818664], dtype=float32)>]

이것은 실수!

  • $\bf{y} \approx 2.5 + 4 \times x$ 우리가 결과적 얻을 모형
  • 하지만 $\hat{\beta}_1$은 한 개 해놓고, 2.5 부분은 $\hat{\alpha}_0 + \hat{\beta}_0 \approx $대략 $ 2.5 =1.818664+0.7650072$ 이렇게 해주면 어떡해~

잠시문법정리

tf.keras.layers.Dense?

Init signature:
tf.keras.layers.Dense(
    units,
    activation=None,
    use_bias=True,
    kernel_initializer='glorot_uniform',
    bias_initializer='zeros',
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    **kwargs,
)
Docstring:     
Just your regular densely-connected NN layer.

`Dense` implements the operation:
`output = activation(dot(input, kernel) + bias)`
where `activation` is the element-wise activation function
passed as the `activation` argument, `kernel` is a weights matrix
created by the layer, and `bias` is a bias vector created by the layer
(only applicable if `use_bias` is `True`). These are all attributes of
`Dense`.

Note: If the input to the layer has a rank greater than 2, then `Dense`
computes the dot product between the `inputs` and the `kernel` along the
last axis of the `inputs` and axis 0 of the `kernel` (using `tf.tensordot`).
For example, if input has dimensions `(batch_size, d0, d1)`,
then we create a `kernel` with shape `(d1, units)`, and the `kernel` operates
along axis 2 of the `input`, on every sub-tensor of shape `(1, 1, d1)`
(there are `batch_size * d0` such sub-tensors).
The output in this case will have shape `(batch_size, d0, units)`.

Besides, layer attributes cannot be modified after the layer has been called
once (except the `trainable` attribute).
When a popular kwarg `input_shape` is passed, then keras will create
an input layer to insert before the current layer. This can be treated
equivalent to explicitly defining an `InputLayer`.

Example:

>>> # Create a `Sequential` model and add a Dense layer as the first layer.
>>> model = tf.keras.models.Sequential()
>>> model.add(tf.keras.Input(shape=(16,)))
>>> model.add(tf.keras.layers.Dense(32, activation='relu'))
>>> # Now the model will take as input arrays of shape (None, 16)
>>> # and output arrays of shape (None, 32).
>>> # Note that after the first layer, you don't need to specify
>>> # the size of the input anymore:
>>> model.add(tf.keras.layers.Dense(32))
>>> model.output_shape
(None, 32)

Args:
  units: Positive integer, dimensionality of the output space.
  activation: Activation function to use.
    If you don't specify anything, no activation is applied
    (ie. "linear" activation: `a(x) = x`).
  use_bias: Boolean, whether the layer uses a bias vector.
  kernel_initializer: Initializer for the `kernel` weights matrix.
  bias_initializer: Initializer for the bias vector.
  kernel_regularizer: Regularizer function applied to
    the `kernel` weights matrix.
  bias_regularizer: Regularizer function applied to the bias vector.
  activity_regularizer: Regularizer function applied to
    the output of the layer (its "activation").
  kernel_constraint: Constraint function applied to
    the `kernel` weights matrix.
  bias_constraint: Constraint function applied to the bias vector.

Input shape:
  N-D tensor with shape: `(batch_size, ..., input_dim)`.
  The most common situation would be
  a 2D input with shape `(batch_size, input_dim)`.

Output shape:
  N-D tensor with shape: `(batch_size, ..., units)`.
  For instance, for a 2D input with shape `(batch_size, input_dim)`,
  the output would have shape `(batch_size, units)`.
File:           ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/layers/core/dense.py
Type:           type
Subclasses:     Dense

- 잠깐 Dense layer를 만드는 코드를 정리해보자.

(1) 아래는 모두 같은 코드이다.

  • tf.keras.layers.Dense(1)
  • tf.keras.layers.Dense(units=1) , 선형결과로 얻어지는 차원
  • tf.keras.layers.Dense(units=1,activation='linear') // identity 가 더 맞는것 같은데.. 교수님's opinion...
    • activation: Activation function to use.
    • If you don't specify anything, no activation is applied
    • (ie. "linear" activation: a(x) = x). -> 아무것도 지정 안 하면 항등함수로 지정된다.
  • tf.keras.layers.Dense(units=1,activation='linear',use_bias=True)

(2) 아래의 코드1,2는 (1)의 코드들과 살짝 다른코드이다. (코드1과 코드2는 같은코드임) -> 레이어의 입력차원을 쓰는 경우

  • tf.keras.layers.Dense(1,input_dim=2) # 코드1
  • tf.keras.layers.Dense(1,input_shape=(2,)) # 코드2

(3) 아래는 사용불가능한 코드이다.

  • tf.keras.layers.Dense(1,input_dim=(2,)) # 코드1
  • tf.keras.layers.Dense(1,input_shape=2) # 코드2

- 왜 input_dim이 필요한가?

net1 = tf.keras.Sequential()
net1.add(tf.keras.layers.Dense(1,use_bias=False)) 
net2 = tf.keras.Sequential()
net2.add(tf.keras.layers.Dense(1,use_bias=False,input_dim=2))
net1.weights
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [25], in <cell line: 1>()
----> 1 net1.weights

File ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/training.py:2542, in Model.weights(self)
   2532 @property
   2533 def weights(self):
   2534   """Returns the list of all layer variables/weights.
   2535 
   2536   Note: This will not track the weights of nested `tf.Modules` that are not
   (...)
   2540     A list of variables.
   2541   """
-> 2542   return self._dedup_weights(self._undeduplicated_weights)

File ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/training.py:2547, in Model._undeduplicated_weights(self)
   2544 @property
   2545 def _undeduplicated_weights(self):
   2546   """Returns the undeduplicated list of all layer variables/weights."""
-> 2547   self._assert_weights_created()
   2548   weights = []
   2549   for layer in self._self_tracked_trackables:

File ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/sequential.py:471, in Sequential._assert_weights_created(self)
    468   return
    469 # When the graph has not been initialized, use the Model's implementation to
    470 # to check if the weights has been created.
--> 471 super(functional.Functional, self)._assert_weights_created()

File ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/training.py:2736, in Model._assert_weights_created(self)
   2728   return
   2730 if ('build' in self.__class__.__dict__ and
   2731     self.__class__ != Model and
   2732     not self.built):
   2733   # For any model that has customized build() method but hasn't
   2734   # been invoked yet, this will cover both sequential and subclass model.
   2735   # Also make sure to exclude Model class itself which has build() defined.
-> 2736   raise ValueError(f'Weights for model {self.name} have not yet been '
   2737                    'created. '
   2738                    'Weights are created when the Model is first called on '
   2739                    'inputs or `build()` is called with an `input_shape`.')

ValueError: Weights for model sequential_3 have not yet been created. Weights are created when the Model is first called on inputs or `build()` is called with an `input_shape`.

안 나오는 이유: 모르니까..

  • 입력차원input_dim 혹은 input_shape 지정 안 해주면 weights를 불러오지 못함
net2.weights
[<tf.Variable 'dense_4/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[0.4367702],
        [0.8878907]], dtype=float32)>]
net1.summary()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [27], in <cell line: 1>()
----> 1 net1.summary()

File ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/training.py:2579, in Model.summary(self, line_length, positions, print_fn, expand_nested)
   2559 """Prints a string summary of the network.
   2560 
   2561 Args:
   (...)
   2576     ValueError: if `summary()` is called before the model is built.
   2577 """
   2578 if not self.built:
-> 2579   raise ValueError(
   2580       'This model has not yet been built. '
   2581       'Build the model first by calling `build()` or by calling '
   2582       'the model on a batch of data.')
   2583 layer_utils.print_summary(
   2584     self,
   2585     line_length=line_length,
   2586     positions=positions,
   2587     print_fn=print_fn,
   2588     expand_nested=expand_nested)

ValueError: This model has not yet been built. Build the model first by calling `build()` or by calling the model on a batch of data.

네트워크가 입력차원을 모르니 완전히 결정되지 않은..

net2.summary()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_4 (Dense)             (None, 1)                 2         
                                                                 
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0
_________________________________________________________________

풀이3: 스칼라버전, 임의의 초기값을 설정

(0단계) 데이터정리

y=y.reshape(N,1)
x=x.reshape(N,1)
x.shape,y.shape
(TensorShape([200, 1]), TensorShape([200, 1]))

(1단계) net생성

net = tf.keras.Sequential() 

(2단계) net.add(layer)

layer = tf.keras.layers.Dense(1,input_dim=1)

출력은 1 입력은 1 bia는 true(기본 옵션, 안 건들어)

net.add(layer)

초기값을 설정

net.weights
[<tf.Variable 'dense_5/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[1.2078832]], dtype=float32)>,
 <tf.Variable 'dense_5/bias:0' shape=(1,) dtype=float32, numpy=array([0.], dtype=float32)>]
net.get_weights()
[array([[1.2078832]], dtype=float32), array([0.], dtype=float32)]
  • weight, bias순으로 출력 (바로 위의 결과도 마찬가지 array([[1.2078832]] - weight, array([0.] - bias)
net.set_weights?

Signature: net.set_weights(weights)
Docstring:
Sets the weights of the layer, from NumPy arrays.

The weights of a layer represent the state of the layer. This function
sets the weight values from numpy arrays. The weight values should be
passed in the order they are created by the layer. Note that the layer's
weights must be instantiated before calling this function, by calling
the layer.

For example, a `Dense` layer returns a list of two values: the kernel matrix
and the bias vector. These can be used to set the weights of another
`Dense` layer:

>>> layer_a = tf.keras.layers.Dense(1,
...   kernel_initializer=tf.constant_initializer(1.))
>>> a_out = layer_a(tf.convert_to_tensor([[1., 2., 3.]]))
>>> layer_a.get_weights()
[array([[1.],
       [1.],
       [1.]], dtype=float32), array([0.], dtype=float32)]
>>> layer_b = tf.keras.layers.Dense(1,
...   kernel_initializer=tf.constant_initializer(2.))
>>> b_out = layer_b(tf.convert_to_tensor([[10., 20., 30.]]))
>>> layer_b.get_weights()
[array([[2.],
       [2.],
       [2.]], dtype=float32), array([0.], dtype=float32)]
>>> layer_b.set_weights(layer_a.get_weights())
>>> layer_b.get_weights()
[array([[1.],
       [1.],
       [1.]], dtype=float32), array([0.], dtype=float32)]

Args:
  weights: a list of NumPy arrays. The number
    of arrays and their shape must match
    number of the dimensions of the weights
    of the layer (i.e. it should match the
    output of `get_weights`).

Raises:
  ValueError: If the provided weights list does not match the
    layer's specifications.
File:      ~/anaconda3/envs/stbda2022/lib/python3.10/site-packages/keras/engine/base_layer.py
Type:      method
  • layer_b.set_weights(layer_a.get_weights()) 와 같은방식으로 쓴다는 것이구나?

- 한번따라해보자.

net.set_weights(net.get_weights())
net.get_weights()
[array([[1.2078832]], dtype=float32), array([0.], dtype=float32)]

일단 그대로네

_w = net.get_weights()
_w
[array([[1.2078832]], dtype=float32), array([0.], dtype=float32)]
_w?
Type:        list
String form: [array([[1.2078832]], dtype=float32), array([0.], dtype=float32)]
Length:      2
Docstring:  
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
type(_w[0])
numpy.ndarray
[_w[0],_w[1]]
[array([[1.2078832]], dtype=float32), array([0.], dtype=float32)]

아래와 같이 만들면 초기값 만들 수 있겠는걸?

[np.array([[1.2078832]],dtype=np.float32),np.array([[0.]],dtype=np.float32)]
[array([[1.2078832]], dtype=float32), array([[0.]], dtype=float32)]
  • 길이가 2인 리스트이고, 각 원소는 numpy array 임

net.set_weights(
    [np.array([[10.0]],dtype=np.float32), # weight, β1_hat
     np.array([-5.0],dtype=np.float32)] # bias, β0_hat 
)
net.weights
[<tf.Variable 'dense_5/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[10.]], dtype=float32)>,
 <tf.Variable 'dense_5/bias:0' shape=(1,) dtype=float32, numpy=array([-5.], dtype=float32)>]

(3단계) net.compile()

net.compile(tf.keras.optimizers.SGD(0.1),tf.losses.MSE) 

(4단계) net.fit()

net.fit(x,y,epochs=1000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7bc0425f00>

결과확인

net.weights
[<tf.Variable 'dense_5/kernel:0' shape=(1, 1) dtype=float32, numpy=array([[3.933048]], dtype=float32)>,
 <tf.Variable 'dense_5/bias:0' shape=(1,) dtype=float32, numpy=array([2.58366], dtype=float32)>]

풀이4: 벡터버전, 임의의 초기값을 설정

(0단계) 데이터정리

X.shape, y.shape
(TensorShape([200, 2]), TensorShape([200, 1]))

(1단계) net생성

net = tf.keras.Sequential()

(2단계) net.add(layer)

layer = tf.keras.layers.Dense(1,use_bias=False,input_dim=2) 

출력은 1 입력은 2(x의 차원이 2니까) bia는 false

net.add(layer)

초기값을 설정하자

net.get_weights()
[array([[-0.5538332],
        [-1.1720978]], dtype=float32)]

설정된 것을 바꿔주자

[np.array([[ -5.0],[10.0]], dtype=np.float32)]
[array([[-5.],
        [10.]], dtype=float32)]
net.set_weights([np.array([[ -5.0],[10.0]], dtype=np.float32)])
net.get_weights()
[array([[-5.],
        [10.]], dtype=float32)]

(3단계) net.compile()

net.compile(tf.keras.optimizers.SGD(0.1), tf.losses.MSE) 

(4단계) net.fit()

net.fit(X,y,epochs=1000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7bb05d3fd0>

벡터니까 X, 신경쓰자

net.weights
[<tf.Variable 'dense_7/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.58366 ],
        [3.933048]], dtype=float32)>]

- 사실 실전에서는 초기값을 설정할 필요가 별로 없음.

net = tf.keras.Sequential()
layer = tf.keras.layers.Dense(1,use_bias=False,input_dim=2) 
ner.set_weights([np.array([[-5.0],[10.0]],dtype=np.float32)])
net.compile(optimizer = tf.keras.optimizers.SGD(0.1),loss='mse')
net.fit(X,y,rpochs=1000,verbose=0,batchsize=200)

풀이5: 벡터버전 사용자정의 손실함수

(0단계) 데이터정리

X.shape, y.shape
(TensorShape([200, 2]), TensorShape([200, 1]))

(1단계) net생성

net = tf.keras.Sequential()

(2단계) net.add(layer)

layer = tf.keras.layers.Dense(1,use_bias=False) 

초기값 설정도 안 할거고.. 출력 차원 지정 안 해줄래

net.add(layer)

(3단계) net.compile()

loss_fn = lambda y,yhat: (y-yhat).T @ (y-yhat) / N

tf.losses.MSE(np.array([0.0,0.0,0.0]),np.array([1.0,2.0,3.0]))
<tf.Tensor: shape=(), dtype=float64, numpy=4.666666666666667>
(1**2 + 2**2 + 3**2)/3
4.666666666666667
loss_fn(np.array([0.0,0.0,0.0]),np.array([1.0,2.0,3.0]))*200/3
4.666666666666667

loss_fn 정의해준거로 봐보면~

net.compile(tf.keras.optimizers.SGD(0.1), loss_fn) 

(4단계) net.fit()

net.fit(X,y,epochs=1000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7bb060fca0>
net.weights
[<tf.Variable 'dense_8/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5836723],
        [3.9330251]], dtype=float32)>]

풀이6: 벡터버전, net.compile의 옵션으로 손실함수 지정

풀이5보다 조금 편한..?

(0단계) 데이터정리

X.shape, y.shape
(TensorShape([200, 2]), TensorShape([200, 1]))

(1단계) net생성

net = tf.keras.Sequential()

(2단계) net.add(layer)

net.add(tf.keras.layers.Dense(1,use_bias=False))

(3단계) net.compile()

net.compile(tf.keras.optimizers.SGD(0.1), loss='mse') 

내장되어 있는 mse 찾아서 알아서 compile 한다

(4단계) net.fit()

net.fit(X,y,epochs=1000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7bb0681060>
net.weights
[<tf.Variable 'dense_9/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5836723],
        [3.9330251]], dtype=float32)>]
net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1,use_bias=False))
net.compile(tf.keras.optimizers.SGD(0.1),loss='mse')
net.fit(X,y,epochs=1000,verbose=0,batch_size=200)
net.weights

풀이7: 벡터버전, net.compile의 옵션으로 손실함수 지정 + 옵티마이저 지정

(0단계) 데이터정리

X.shape, y.shape
(TensorShape([200, 2]), TensorShape([200, 1]))

(1단계) net생성

net = tf.keras.Sequential()

(2단계) net.add(layer)

net.add(tf.keras.layers.Dense(1,use_bias=False))

(3단계) net.compile()

net.compile(optimizer='sgd', loss='mse') 
#net.optimizer.lr = tf.Variable(0.1,dtype=tf.float32)
#net.optimizer.lr = 0.1

(4단계) net.fit()

net.fit(X,y,epochs=1000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7bb03413c0>
net.weights
[<tf.Variable 'dense_12/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.8416367],
        [3.451351 ]], dtype=float32)>]
  • 값이 잘 수렴이 안 된다?
net.optimizer.learning_rate
<tf.Variable 'learning_rate:0' shape=() dtype=float32, numpy=0.01>
  • 학습률 0.1 에 1000번 돌리면 수렴 잘 되었잖아..
  • 근데 기본이 0.01로 되어 있는 기본 학습률.

방법 1) 학습률을 수정한다. 근데 이럴려면 굳이.. sgd옵션 안 쓰는 게 낫지..

net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1,use_bias=False))
net.compile(optimizer='sgd', loss='mse') 
net.optimizer.learning_rate = tf.Variable(0.1,dtype=tf.float32)
net.fit(X,y,epochs=1000,verbose=0,batch_size=N)
net.weights
[<tf.Variable 'dense_13/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5836723],
        [3.9330251]], dtype=float32)>]

방법2) epoc을 늘린다. 하지만 시간이 더 소요되겠지

net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1,use_bias=False))
net.compile(optimizer='sgd', loss='mse') 
net.fit(X,y,epochs=5000,verbose=0,batch_size=N) 
<keras.callbacks.History at 0x7f7b90477fa0>
net.weights
[<tf.Variable 'dense_14/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5848687],
        [3.9307933]], dtype=float32)>]

여러가지 회귀모형의 적합과 학습과정의 모니터링

예제1

model: $y_i \approx \beta_0 +\beta_1 x_i$

np.random.seed(43052) 
N= 100 
x= np.random.randn(N) 
epsilon = np.random.randn(N)*0.5 
y= 2.5+4*x +epsilon
X= np.stack([np.ones(N),x],axis=1)
y= y.reshape(N,1)
plt.plot(x,y,'o') # 관측한 자료 
[<matplotlib.lines.Line2D at 0x7f7b9048eb00>]
beta_hat = np.array([-3,-2]).reshape(2,1)

beta_hat이 이게 아닐까? 시도!

yhat = X@beta_hat 
plt.plot(x,y,'o')
plt.plot(x,yhat.reshape(-1),'-') 
[<matplotlib.lines.Line2D at 0x7f7b903e50c0>]

더 좋은 적합선을 얻기위해서!

  • loss 값을 가장 작게 하는 선을 찾아보는 것!
slope = (2*X.T@X@beta_hat - 2*X.T@y)/ N 
beta_hat2 = beta_hat - 0.1*slope  
yhat2 = X@beta_hat2
plt.plot(x,y,'o')
plt.plot(x,yhat.reshape(-1),'-') 
plt.plot(x,yhat2.reshape(-1),'-') 
[<matplotlib.lines.Line2D at 0x7f7b9025ca60>]

초록색이 좀 더 나아보인다.

beta_hats은 update된 기록해서 beta_hat을 넣기 위한 변수. 업데이트 될 때마다 나오는 beta들의 모임이라 보면 되겠다.

beta_hat = np.array([-3,-2]).reshape(2,1) 
beta_hats = beta_hat 
beta_hats
array([[-3],
       [-2]])

(-3,-2) 가 첫 iteration, (2,1) 매트릿이기도 하다. 베타를 모으기 위해 열을 늘려볼까. -> 총 차원은 유지되면서 두 번째 차원 변경 concatenate

  • beta_hats의 열을 늘려버려~ 새로운 베타 추가
beta_hat = np.array([-3,-2]).reshape(2,1) 
beta_hats = beta_hat # beta_hats = beta_hat.copy() 가 더 안전한 코드입니다. 
for i in range(1,30):
    yhat = X@beta_hat 
    slope = (2*X.T@X@beta_hat - 2*X.T@y) / N 
    beta_hat = beta_hat - 1.0*slope # 0.1은 적당, 0.3은 쪼금빠르지만 그래도 적당, 0.9는 너무 나간것같음, 1.0 은 수렴안함, 1.2 
    beta_hats = np.concatenate([beta_hats,beta_hat],axis=1) 

np.concatenate?

Docstring:
concatenate((a1, a2, ...), axis=0, out=None, dtype=None, casting="same_kind")

Join a sequence of arrays along an existing axis.

Parameters
----------
a1, a2, ... : sequence of array_like
    The arrays must have the same shape, except in the dimension
    corresponding to `axis` (the first, by default).
axis : int, optional
    The axis along which the arrays will be joined.  If axis is None,
    arrays are flattened before use.  Default is 0.
out : ndarray, optional
    If provided, the destination to place the result. The shape must be
    correct, matching that of what concatenate would have returned if no
    out argument were specified.
dtype : str or dtype
    If provided, the destination array will have this dtype. Cannot be
    provided together with `out`.

    .. versionadded:: 1.20.0

casting : {'no', 'equiv', 'safe', 'same_kind', 'unsafe'}, optional
    Controls what kind of data casting may occur. Defaults to 'same_kind'.

    .. versionadded:: 1.20.0

Returns
-------
res : ndarray
    The concatenated array.

See Also
--------
ma.concatenate : Concatenate function that preserves input masks.
array_split : Split an array into multiple sub-arrays of equal or
              near-equal size.
split : Split array into a list of multiple sub-arrays of equal size.
hsplit : Split array into multiple sub-arrays horizontally (column wise).
vsplit : Split array into multiple sub-arrays vertically (row wise).
dsplit : Split array into multiple sub-arrays along the 3rd axis (depth).
stack : Stack a sequence of arrays along a new axis.
block : Assemble arrays from blocks.
hstack : Stack arrays in sequence horizontally (column wise).
vstack : Stack arrays in sequence vertically (row wise).
dstack : Stack arrays in sequence depth wise (along third dimension).
column_stack : Stack 1-D arrays as columns into a 2-D array.

Notes
-----
When one or more of the arrays to be concatenated is a MaskedArray,
this function will return a MaskedArray object instead of an ndarray,
but the input masks are *not* preserved. In cases where a MaskedArray
is expected as input, use the ma.concatenate function from the masked
array module instead.

Examples
--------
>>> a = np.array([[1, 2], [3, 4]])
>>> b = np.array([[5, 6]])
>>> np.concatenate((a, b), axis=0)
array([[1, 2],
       [3, 4],
       [5, 6]])
>>> np.concatenate((a, b.T), axis=1)
array([[1, 2, 5],
       [3, 4, 6]])
>>> np.concatenate((a, b), axis=None)
array([1, 2, 3, 4, 5, 6])

This function will not preserve masking of MaskedArray inputs.

>>> a = np.ma.arange(3)
>>> a[1] = np.ma.masked
>>> b = np.arange(2, 5)
>>> a
masked_array(data=[0, --, 2],
             mask=[False,  True, False],
       fill_value=999999)
>>> b
array([2, 3, 4])
>>> np.concatenate([a, b])
masked_array(data=[0, 1, 2, 2, 3, 4],
             mask=False,
       fill_value=999999)
>>> np.ma.concatenate([a, b])
masked_array(data=[0, --, 2, 2, 3, 4],
             mask=[False,  True, False, False, False, False],
       fill_value=999999)
Type:      function
np.concatenate((np.array([[1, 2], [3, 4]]),np.array([[5, 6]]).T),axis=1)
array([[1, 2, 5],
       [3, 4, 6]])

beta_hats
array([[-3.        ,  7.12238255, -1.2575366 ,  5.73166742, -0.1555309 ,
         4.86767499,  0.51106397,  4.36611576,  0.87316777,  4.12348617,
         1.01165173,  4.07771926,  0.97282343,  4.19586617,  0.77814101,
         4.46653491,  0.4299822 ,  4.89562729, -0.08537358,  5.50446319,
        -0.79684366,  6.32975688, -1.74933031,  7.42517729, -3.00603683,
         8.86442507, -4.6523303 , 10.74592463, -6.80132547, 13.19938129],
       [-2.        ,  8.70824998,  0.16165717,  6.93399596,  1.62435964,
         5.72089586,  2.63858056,  4.86387722,  3.37280529,  4.22385379,
         3.94259478,  3.70397678,  4.43004465,  3.23363047,  4.89701606,
         2.75741782,  5.39439054,  2.22728903,  5.96886945,  1.59655409,
         6.66836857,  0.81489407,  7.54676324, -0.17628423,  8.66856437,
        -1.44867655, 10.11401544, -3.09256176, 11.98507323, -5.22340389]])
beta_hat = np.array([-3,-2]).reshape(2,1) 
beta_hats = beta_hat # beta_hats = beta_hat.copy() 가 더 안전한 코드입니다. 
for i in range(1,30):
    yhat = X@beta_hat 
    slope = (2*X.T@X@beta_hat - 2*X.T@y) / N 
    beta_hat = beta_hat - 0.1*slope # 0.1은 적당, 0.3은 쪼금빠르지만 그래도 적당, 0.9는 너무 나간것같음, 1.0 은 수렴안함, 1.2 
    beta_hats = np.concatenate([beta_hats,beta_hat],axis=1) 

0.3이나 0.5는 빨리 loss 값 찾는다고 해서.. 좋은 값이라고 할 수 없다. 1은 수렴하는 듯 터져버리며 수렴 안 함

beta_hats
array([[-3.        , -1.98776175, -1.16054651, -0.48448286,  0.06808892,
         0.51975837,  0.88897604,  1.19081334,  1.43758253,  1.63934274,
         1.80431301,  1.93920945,  2.04952049,  2.13973166,  2.2135091 ,
         2.27384947,  2.32320238,  2.36357035,  2.39659058,  2.42360161,
         2.44569792,  2.46377445,  2.47856304,  2.49066214,  2.50056123,
         2.5086606 ,  2.51528766,  2.52071021,  2.52514732,  2.52877816],
       [-2.        , -0.929175  , -0.05089843,  0.66940348,  1.26010705,
         1.74449975,  2.14169103,  2.46736052,  2.73437247,  2.95328049,
         3.13274182,  3.27985761,  3.40045219,  3.4993023 ,  3.58032529,
         3.64673351,  3.70116104,  3.74576767,  3.78232418,  3.81228235,
         3.83683236,  3.85694989,  3.87343472,  3.88694243,  3.89801039,
         3.90707901,  3.91450928,  3.92059703,  3.92558472,  3.92967104]])

20번 만에 수럼했네!

beta_hats = beta_hat보다 beta_hats = beta_hat.copy() 가 더 안전한 코드입니다.

  • 지금 문제 안 생기는 이유? $\rightarrow$ np.concatenate가 반복실행이 되면서 새로운 object를 만들어서 .copy()와 비슷한 역할을 한다.
  • append option을 쓴다? beta_hats 뿐만 아니라 beta_hat 자체도 업데이트되어서 꼬여버리는 현상...
beta_hats[0]
array([-3.        , -1.98776175, -1.16054651, -0.48448286,  0.06808892,
        0.51975837,  0.88897604,  1.19081334,  1.43758253,  1.63934274,
        1.80431301,  1.93920945,  2.04952049,  2.13973166,  2.2135091 ,
        2.27384947,  2.32320238,  2.36357035,  2.39659058,  2.42360161,
        2.44569792,  2.46377445,  2.47856304,  2.49066214,  2.50056123,
        2.5086606 ,  2.51528766,  2.52071021,  2.52514732,  2.52877816])
beta_hats[1]
array([-2.        , -0.929175  , -0.05089843,  0.66940348,  1.26010705,
        1.74449975,  2.14169103,  2.46736052,  2.73437247,  2.95328049,
        3.13274182,  3.27985761,  3.40045219,  3.4993023 ,  3.58032529,
        3.64673351,  3.70116104,  3.74576767,  3.78232418,  3.81228235,
        3.83683236,  3.85694989,  3.87343472,  3.88694243,  3.89801039,
        3.90707901,  3.91450928,  3.92059703,  3.92558472,  3.92967104])
b0hats = beta_hats[0].tolist()
b1hats = beta_hats[1].tolist()

_a,_b = np.meshgrid(np.array([0,1,2]),np.array([4,5,6]))
_a,_b
(array([[0, 1, 2],
        [0, 1, 2],
        [0, 1, 2]]),
 array([[4, 4, 4],
        [5, 5, 5],
        [6, 6, 6]]))

앞에 array는 행으로 쌓여지고 뒤에 array는 열으로 쌓이네?

_a,_b = np.meshgrid(np.array([0,1,2]),np.array([4,5,6]),indexing='ij')
_a,_b
(array([[0, 0, 0],
        [1, 1, 1],
        [2, 2, 2]]),
 array([[4, 5, 6],
        [4, 5, 6],
        [4, 5, 6]]))

바뀌었다..?

$loss(\beta_0,\beta_1)$ 에 수월하게 넣기 위해 meshgrid 함수를 사용하자

_a.reshape(-1)
array([0, 0, 0, 1, 1, 1, 2, 2, 2])
_b.reshape(-1)
array([4, 5, 6, 4, 5, 6, 4, 5, 6])

$\beta_0$ 과 $\beta_1$의 $N \times 1$ 행렬로 변환

map(lambda x,y: x+y, _a.reshape(-1),_b.reshape(-1))
<map at 0x7f7ce00c5180>

뭔가 계산이 된다?

list(map(lambda x,y: x+y, _a.reshape(-1),_b.reshape(-1)))
[4, 5, 6, 5, 6, 7, 6, 7, 8]

나왔다, _a랑 _b의 각 위치에서의 값들

(y-b0-b1*x)

벡터인데

np.sum((y-b0-b1*x)**2

스칼라 돼


np.linalg.inv(X.T@X) @ X.T @ y
array([[2.5451404 ],
       [3.94818596]])

$\beta_0$에 대한 최소값과 $\beta_1$에 대한 최소값

pyhton
ax2.scatter(2.5451404,3.94818596,loss_fn(2.5451404,3.94818596),s=200,marker='*')

여기 넣자


from matplotlib import animation 
plt.rcParams["animation.html"] = "jshtml" 
fig = plt.figure(); fig.set_figheight(5); fig.set_figwidth(12)
<Figure size 864x360 with 0 Axes>
ax1= fig.add_subplot(1,2,1)
ax2= fig.add_subplot(1,2,2,projection='3d')
# ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,b0hats[0] + b1hats[0]*x) # b0hats[0] + b1hats[0]*x 각 초기값들 넣어
# ax2: 오른쪽그림 
β0,β1 = np.meshgrid(np.arange(-6,11,0.25),np.arange(-6,11,0.25),indexing='ij') # -6에서 11까지 0.25의 간격으로 만들어줘~ 임의 설정임
β0=β0.reshape(-1)
β1=β1.reshape(-1)
loss_fn = lambda b0,b1: np.sum((y-b0-b1*x)**2)
loss = list(map(loss_fn, β0,β1))
ax2.scatter(β0,β1,loss,alpha=0.02) # 학습률도 조정해보자 0.1은 적당 0.3은 빠르지만 적당, 0.9는 너무 나감.., 1이면? 수렴하는 듯 터져버림go off
ax2.scatter(2.5451404,3.94818596,loss_fn(2.5451404,3.94818596),s=200,marker='*') # s=200이 의미하는 것은  size지~

def animate(i):
    line.set_ydata(b0hats[i] + b1hats[i]*x) # 매 iteration 마다 새로운 선을 왼쪽에 그리자
    ax2.scatter(b0hats[i],b1hats[i],loss_fn(b0hats[i],b1hats[i]),color="grey")  # 매 iteration 마다 새로운 점을 오른쪽에 그리자

ani = animation.FuncAnimation(fig,animate,frames=30) 
ani
</input>

학습률 1일때 터져버리는..

beta_hat = np.array([-3,-2]).reshape(2,1) 
beta_hats = beta_hat # beta_hats = beta_hat.copy() 가 더 안전한 코드입니다. 
for i in range(1,30):
    yhat = X@beta_hat 
    slope = (2*X.T@X@beta_hat - 2*X.T@y) / N 
    beta_hat = beta_hat - 1*slope # 0.1은 적당, 0.3은 쪼금빠르지만 그래도 적당, 0.9는 너무 나간것같음, 1.0 은 수렴안함, 1.2 
    beta_hats = np.concatenate([beta_hats,beta_hat],axis=1) 
b0hats = beta_hats[0].tolist()
b1hats = beta_hats[1].tolist()
fig = plt.figure(); fig.set_figheight(5); fig.set_figwidth(12)
<Figure size 864x360 with 0 Axes>

ax1= fig.add_subplot(1,2,1)
ax2= fig.add_subplot(1,2,2,projection='3d')
# ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,b0hats[0] + b1hats[0]*x) # b0hats[0] + b1hats[0]*x 각 초기값들 넣어
# ax2: 오른쪽그림 
β0,β1 = np.meshgrid(np.arange(-6,11,0.25),np.arange(-6,11,0.25),indexing='ij') # -6에서 11까지 0.25의 간격으로 만들어줘~ 임의 설정임
β0=β0.reshape(-1)
β1=β1.reshape(-1)
loss_fn = lambda b0,b1: np.sum((y-b0-b1*x)**2)
loss = list(map(loss_fn, β0,β1))
ax2.scatter(β0,β1,loss,alpha=0.02) # 학습률도 조정해보자 0.1은 적당 0.3은 빠르지만 적당, 0.9는 너무 나감.., 1이면? 수렴하는 듯 터져버림go off
ax2.scatter(2.5451404,3.94818596,loss_fn(2.5451404,3.94818596),s=200,marker='*') # s=200이 의미하는 것은  size지~

def animate(i):
    line.set_ydata(b0hats[i] + b1hats[i]*x) # 매 iteration 마다 새로운 선을 왼쪽에 그리자
    ax2.scatter(b0hats[i],b1hats[i],loss_fn(b0hats[i],b1hats[i]),color="grey")  # 매 iteration 마다 새로운 점을 오른쪽에 그리자

ani = animation.FuncAnimation(fig,animate,frames=30) 
ani
</input>

예제2

model: $y_i \approx \beta_0 +\beta_1 e^{-x_i}$

  • linear 모델이 확장성이 좋다.
  • $\begin{bmatrix} y_1 \\ y_2 \\ \dots \\ y_n \end{bmatrix} = \begin{bmatrix} 1 & exp(x_1) \\ 1 & exp(x_2) \\ \dots \\ 1 & exp(x_n) \end{bmatrix} \begin{bmatrix} \beta_0 \\ \beta_1 \end{bmatrix}$
  • $y = X\beta + \epsilon$의 꼴이 될 텐데, $X$를 최소화하는 $\beta$는 여전히 $X^\top (XX^\top)^{-1}Y$ ,,$\beta$ 그대로.
np.random.seed(43052) 
N= 100 
x= np.linspace(-1,1,N)
epsilon = np.random.randn(N)*0.5 
y= 2.5+4*np.exp(-x) +epsilon
plt.plot(x,y,'o')
[<matplotlib.lines.Line2D at 0x7f7b8b7c4b20>]
X= np.stack([np.ones(N),np.exp(-x)],axis=1)
y= y.reshape(N,1)
beta_hat = np.array([-3,-2]).reshape(2,1)
beta_hats = beta_hat.copy() # shallow copy, deep copy <--- 여름 방학 특강 
for i in range(1,30): 
    yhat = X@beta_hat 
    slope = (2*X.T@X@beta_hat - 2*X.T@y) /N 
    beta_hat = beta_hat - 0.05*slope 
    beta_hats = np.concatenate([beta_hats,beta_hat],axis=1) 
beta_hats
array([[-3.        , -1.74671631, -0.82428979, -0.14453919,  0.35720029,
         0.72834869,  1.0036803 ,  1.20869624,  1.36209751,  1.47759851,
         1.56525696,  1.63244908,  1.68458472,  1.72563174,  1.75850062,
         1.78532638,  1.80767543,  1.82669717,  1.84323521,  1.85790889,
         1.8711731 ,  1.88336212,  1.89472176,  1.90543297,  1.91562909,
         1.92540859,  1.93484428,  1.94399023,  1.9528867 ,  1.96156382],
       [-2.        , -0.25663415,  1.01939241,  1.95275596,  2.63488171,
         3.13281171,  3.49570765,  3.75961951,  3.95098231,  4.08918044,
         4.18842797,  4.2591476 ,  4.30898175,  4.34353413,  4.36691339,
         4.38213187,  4.39139801,  4.39633075,  4.39811673,  4.3976256 ,
         4.3954946 ,  4.3921905 ,  4.38805511,  4.3833386 ,  4.37822393,
         4.37284482,  4.36729887,  4.36165718,  4.35597148,  4.35027923]])
b0hats= beta_hats[0].tolist()
b1hats= beta_hats[1].tolist()
np.linalg.inv(X.T@X)@X.T@y
array([[2.46307644],
       [3.99681332]])
fig = plt.figure(); fig.set_figheight(5); fig.set_figwidth(12)
<Figure size 864x360 with 0 Axes>
ax1= fig.add_subplot(1,2,1)
ax2= fig.add_subplot(1,2,2,projection='3d')
# ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,b0hats[0] + b1hats[0]*np.exp(-x)) # 식에 따라 수정할 곳
# ax2: 오른쪽그림 
β0,β1 = np.meshgrid(np.arange(-6,11,0.25),np.arange(-6,11,0.25),indexing='ij')
β0=β0.reshape(-1)
β1=β1.reshape(-1)
loss_fn = lambda b0,b1: np.sum((y-b0-b1*np.exp(-x))**2) # 식에 따라 수정할 곳
loss = list(map(loss_fn, β0,β1))
ax2.scatter(β0,β1,loss,alpha=0.02) 
ax2.scatter(2.46307644,3.99681332,loss_fn(2.46307644,3.99681332),s=200,marker='*') # 여기도 계산해서 바꿔주자 np.linalg.inv 결과

def animate(i):
    line.set_ydata(b0hats[i] + b1hats[i]*np.exp(-x)) # 식에 따라 수정할 곳
    ax2.scatter(b0hats[i],b1hats[i],loss_fn(b0hats[i],b1hats[i]),color="grey") 

ani = animation.FuncAnimation(fig,animate,frames=30) 
ani
</input>

예제3

model: $y_i \approx \beta_0 +\beta_1 e^{-x_i} + \beta_2 \cos(5x_i)$

exp, cos있으니까 선형 아닌 거 아냐?

  • basis가 1, exp(-x),xos(5x)
  • coef = beta_0, beta_1,beta_2
  • 두 개가 연결되어 있지!
  • 연결되어 있어야지!
  • 따라서 여전히 linear model이다.
  • 적합 가능~
np.random.seed(43052) 
N= 100 
x= np.linspace(-1,1,N)
epsilon = np.random.randn(N)*0.5 
y= 2.5+4*np.exp(-x) + 5*np.cos(5*x) + epsilon
plt.plot(x,y,'o')
[<matplotlib.lines.Line2D at 0x7f7b8af10460>]

전통적인 방식, 그림을 그려보고 베타 적절히 넣어서 찾아볼까?

X=np.stack([np.ones(N),np.exp(-x),np.cos(5*x)],axis=1) 
y=y.reshape(N,1)
beta_hat = np.array([-3,-2,-1]).reshape(3,1) 
beta_hats = beta_hat.copy() # 기록 남기자~
for i in range(1,30):
    yhat = X@beta_hat 
    slope = (2*X.T@X@beta_hat -2*X.T@y) /N 
    beta_hat = beta_hat - 0.1 * slope 
    beta_hats= np.concatenate([beta_hats,beta_hat],axis=1)
beta_hats
array([[-3.        , -0.71767532,  0.36255782,  0.89072137,  1.16423101,
         1.31925078,  1.41819551,  1.48974454,  1.54713983,  1.59655416,
         1.64091846,  1.68167278,  1.71956758,  1.75503084,  1.78833646,
         1.81968188,  1.84922398,  1.877096  ,  1.90341567,  1.92828934,
         1.95181415,  1.97407943,  1.99516755,  2.01515463,  2.0341111 ,
         2.05210214,  2.06918818,  2.08542523,  2.10086524,  2.11555643],
       [-2.        ,  1.16947474,  2.64116513,  3.33411605,  3.66880042,
         3.83768856,  3.92897389,  3.98315095,  4.01888831,  4.04486085,
         4.06516144,  4.08177665,  4.09571971,  4.10754954,  4.1176088 ,
         4.12613352,  4.13330391,  4.13926816,  4.14415391,  4.14807403,
         4.15112966,  4.1534121 ,  4.15500404,  4.15598045,  4.15640936,
         4.15635249,  4.15586584,  4.15500014,  4.15380139,  4.1523112 ],
       [-1.        , -0.95492718, -0.66119313, -0.27681968,  0.12788212,
         0.52254445,  0.89491388,  1.24088224,  1.55993978,  1.85310654,
         2.12199631,  2.36839745,  2.59408948,  2.8007666 ,  2.99000967,
         3.16327964,  3.32192026,  3.46716468,  3.60014318,  3.72189116,
         3.83335689,  3.93540864,  4.02884144,  4.11438316,  4.19270026,
         4.26440288,  4.33004965,  4.39015202,  4.44517824,  4.49555703]])
b0hats,b1hats,b2hats = beta_hats # unpacking이라 함
b0hats = beta_hats[0]
b1hats = beta_hats[1]
b2hats = beta_hats[2]
# 같은 결과
np.linalg.inv(X.T@X) @ X.T @ y
array([[2.46597526],
       [4.00095138],
       [5.04161877]])
fig = plt.figure(); fig.set_figheight(5); fig.set_figwidth(12)
<Figure size 864x360 with 0 Axes>
ax1= fig.add_subplot(1,2,1)
ax2= fig.add_subplot(1,2,2,projection='3d')
# ax1: 왼쪽그림 
ax1.plot(x,y,'o')
line, = ax1.plot(x,b0hats[0] + b1hats[0]*np.exp(-x) + b2hats[0]*np.cos(5*x)) # 식에 따라 수정
# ax2: 오른쪽그림 3차원으로 표현하기 복잡하니 pass하자...
# β0,β1 = np.meshgrid(np.arange(-6,11,0.25),np.arange(-6,11,0.25),indexing='ij')
# β0=β0.reshape(-1)
# β1=β1.reshape(-1)
# loss_fn = lambda b0,b1: np.sum((y-b0-b1*np.exp(-x))**2)
# loss = list(map(loss_fn, β0,β1))
# ax2.scatter(β0,β1,loss,alpha=0.02) 
# ax2.scatter(2.46307644,3.99681332,loss_fn(2.46307644,3.99681332),s=200,marker='*') 

def animate(i):
    line.set_ydata(b0hats[i] + b1hats[i]*np.exp(-x) + b2hats[i]*np.cos(5*x)) # 식에 따라 수정
    # ax2.scatter(b0hats[i],b1hats[i],loss_fn(b0hats[i],b1hats[i]),color="grey") 

ani = animation.FuncAnimation(fig,animate,frames=30) 
ani
</input>

예제3: 케라스로 해보자!

model: $y_i \approx \beta_0 +\beta_1 e^{-x_i} + \beta_2 \cos(5x_i)$

여전히 $y = X \times W$ 이다.

np.random.seed(43052) 
N= 100 
x= np.linspace(-1,1,N)
epsilon = np.random.randn(N)*0.5 
y= 2.5+4*np.exp(-x) + 5*np.cos(5*x) + epsilon
X=np.stack([np.ones(N),np.exp(-x),np.cos(5*x)],axis=1) 
y=y.reshape(N,1)
net = tf.keras.Sequential() # 1: 네트워크 생성
net.add(tf.keras.layers.Dense(1,use_bias=False)) # 2: add layer 
net.compile(tf.optimizers.SGD(0.1), loss='mse') # 3: compile
net.fit(X,y,epochs=30, batch_size=N) # 4: fit 
Epoch 1/30
1/1 [==============================] - 0s 113ms/step - loss: 47.3029
Epoch 2/30
1/1 [==============================] - 0s 2ms/step - loss: 22.2354
Epoch 3/30
1/1 [==============================] - 0s 2ms/step - loss: 15.0294
Epoch 4/30
1/1 [==============================] - 0s 2ms/step - loss: 11.8758
Epoch 5/30
1/1 [==============================] - 0s 2ms/step - loss: 9.8359
Epoch 6/30
1/1 [==============================] - 0s 2ms/step - loss: 8.2522
Epoch 7/30
1/1 [==============================] - 0s 2ms/step - loss: 6.9509
Epoch 8/30
1/1 [==============================] - 0s 3ms/step - loss: 5.8656
Epoch 9/30
1/1 [==============================] - 0s 3ms/step - loss: 4.9569
Epoch 10/30
1/1 [==============================] - 0s 3ms/step - loss: 4.1953
Epoch 11/30
1/1 [==============================] - 0s 3ms/step - loss: 3.5569
Epoch 12/30
1/1 [==============================] - 0s 3ms/step - loss: 3.0217
Epoch 13/30
1/1 [==============================] - 0s 3ms/step - loss: 2.5731
Epoch 14/30
1/1 [==============================] - 0s 3ms/step - loss: 2.1969
Epoch 15/30
1/1 [==============================] - 0s 3ms/step - loss: 1.8815
Epoch 16/30
1/1 [==============================] - 0s 3ms/step - loss: 1.6171
Epoch 17/30
1/1 [==============================] - 0s 3ms/step - loss: 1.3954
Epoch 18/30
1/1 [==============================] - 0s 3ms/step - loss: 1.2095
Epoch 19/30
1/1 [==============================] - 0s 3ms/step - loss: 1.0537
Epoch 20/30
1/1 [==============================] - 0s 3ms/step - loss: 0.9230
Epoch 21/30
1/1 [==============================] - 0s 4ms/step - loss: 0.8134
Epoch 22/30
1/1 [==============================] - 0s 4ms/step - loss: 0.7215
Epoch 23/30
1/1 [==============================] - 0s 4ms/step - loss: 0.6444
Epoch 24/30
1/1 [==============================] - 0s 3ms/step - loss: 0.5797
Epoch 25/30
1/1 [==============================] - 0s 4ms/step - loss: 0.5255
Epoch 26/30
1/1 [==============================] - 0s 4ms/step - loss: 0.4800
Epoch 27/30
1/1 [==============================] - 0s 4ms/step - loss: 0.4419
Epoch 28/30
1/1 [==============================] - 0s 4ms/step - loss: 0.4099
Epoch 29/30
1/1 [==============================] - 0s 4ms/step - loss: 0.3830
Epoch 30/30
1/1 [==============================] - 0s 4ms/step - loss: 0.3605
<keras.callbacks.History at 0x7f7b8abb7610>
net.weights
[<tf.Variable 'dense_17/kernel:0' shape=(3, 1) dtype=float32, numpy=
 array([[2.3145626],
        [4.0243015],
        [4.567635 ]], dtype=float32)>]
plt.plot(x,y,'o') 
plt.plot(x,(X@net.weights).reshape(-1),'--')
[<matplotlib.lines.Line2D at 0x7f7b8821e500>]

숙제

예제2: 케라스를 이용하여 아래를 만족하는 적절한 $\beta_0$와 $\beta_1$을 구하라. 적합결과를 시각화하라. (애니메이션 시각화 X)

model: $y_i \approx \beta_0 +\beta_1 e^{-x_i}$

np.random.seed(43052) 
N= 100 
x= np.linspace(-1,1,N)
epsilon = np.random.randn(N)*0.5 
y= 2.5+4*np.exp(-x) +epsilon
X = np.stack([np.ones(N),np.exp(-x)],axis=1)
y = y.reshape(N,1)
net = tf.keras.Sequential()
net.add(tf.keras.layers.Dense(1,use_bias=False))
net.compile(tf.optimizers.SGD(0.1),loss='mse')
net.fit(X,y,epochs=50,batch_size=N,verbose=0)
<keras.callbacks.History at 0x7f7b8841bac0>
net.weights
[<tf.Variable 'dense_78/kernel:0' shape=(2, 1) dtype=float32, numpy=
 array([[2.5452802],
        [3.9386759]], dtype=float32)>]
plt.plot(x,y,'o')
plt.plot(x,(X@net.weights).reshape(-1),'--')
[<matplotlib.lines.Line2D at 0x7f75ae685c60>]