import

import torch
import pandas as pd
import matplotlib.pyplot as plt

Define some funtions

- 활성화함수들

sig = torch.nn.Sigmoid()
soft = torch.nn.Softmax(dim=1)
tanh = torch.nn.Tanh()
_x = torch.linspace(-5,5,100)
plt.plot(_x,tanh(_x))
plt.title("tanh(x)", size=15)
Text(0.5, 1.0, 'tanh(x)')

hyperblic tangent(https://en.wikipedia.org/wiki/Hyperbolic_functions)

  • sigmoid(범위가0 ~ 1)와 차이점(범위가 -1 ~ 1)

- 문자열 -> 숫자로 바꾸는 함수

def f(txt,mapping):
    return [mapping[key] for key in txt] 

(사용예시1)

txt = ['a','b','a']
mapping = {'a':33,'b':-22}
print('변환전: %s'% txt)
print('변환후: %s'% f(txt,mapping))
변환전: ['a', 'b', 'a']
변환후: [33, -22, 33]

(사용예시2)

txt = ['a','b','a']
mapping = {'a':[1,0],'b':[0,1]}
print('변환전: %s'% txt)
print('변환후: %s'% f(txt,mapping))
변환전: ['a', 'b', 'a']
변환후: [[1, 0], [0, 1], [1, 0]]

Exam1: ab

data

txt = list('ab')*100
txt[:10]
['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b']
txt_x = txt[:-1]
txt_y = txt[1:]
txt_x[:5],txt_y[:5]
(['a', 'b', 'a', 'b', 'a'], ['b', 'a', 'b', 'a', 'b'])

선형모형을 이용한 풀이

(풀이1) 1개의 파라메터 -- 실패

- 데이터정리

x = torch.tensor(f(txt_x,{'a':0,'b':1})).float().reshape(-1,1)
y = torch.tensor(f(txt_y,{'a':0,'b':1})).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[0.],
         [1.],
         [0.],
         [1.],
         [0.]]),
 tensor([[1.],
         [0.],
         [1.],
         [0.],
         [1.]]))

- 학습 및 결과 시각화

net = torch.nn.Linear(in_features=1,out_features=1,bias=False)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(5000):
    ## 1
    yhat = net(x) 
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()
plt.plot(y[:5],'o')
plt.plot(net(x).data[:5])
[<matplotlib.lines.Line2D at 0x7fe528354e20>]
  • 잘 학습이 안되었다.

- 학습이 잘 안된 이유

pd.DataFrame({'x':x[:5].reshape(-1),'y':y[:5].reshape(-1)})
x y
0 0.0 1.0
1 1.0 0.0
2 0.0 1.0
3 1.0 0.0
4 0.0 1.0

현재 $\hat{y}_i = \hat{w}x_i$ 꼴의 아키텍처이고 $y_i \approx \hat{w}x_i$ 가 되는 적당한 $\hat{w}$를 찾아야 하는 상황

  • $(x_i,y_i)=(0,1)$ 이면 어떠한 $\hat{w}$를 선택해도 $y_i \approx \hat{w}x_i$를 만드는 것이 불가능
  • $(x_i,y_i)=(1,0)$ 이면 $\hat{w}=0$일 경우 $y_i \approx \hat{w}x_i$로 만드는 것이 가능

상황을 종합해보니 $\hat{w}=0$으로 학습되는 것이 그나마 최선

0에 무엇을 곱하든 0이 되어서 학습이 안 돼

(풀이2) 1개의 파라메터 -- 성공, but 확장성이 없는 풀이

- 0이라는 값이 문제가 되므로 인코딩방식의 변경

x = torch.tensor(f(txt_x,{'a':-1,'b':1})).float().reshape(-1,1) 
y = torch.tensor(f(txt_y,{'a':-1,'b':1})).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[-1.],
         [ 1.],
         [-1.],
         [ 1.],
         [-1.]]),
 tensor([[ 1.],
         [-1.],
         [ 1.],
         [-1.],
         [ 1.]]))
net = torch.nn.Linear(in_features=1,out_features=1,bias=False)
loss_fn = torch.nn.MSELoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(2000):
    ## 1
    yhat = net(x) 
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()

- 결과는 성공

plt.plot(y[:5],'o')
plt.plot(net(x).data[:5])
[<matplotlib.lines.Line2D at 0x7fe5282827f0>]
  • 딱봐도 클래스가 3개일 경우 확장이 어려워 보인다.

원핫인코딩해줘야 좋은데 그러면 마지막 무조건 softmax 그러면 loss는 BCELoss

로지스틱 모형을 이용한 풀이

(풀이1) 1개의 파라메터 -- 실패

- 데이터를 다시 a=0, b=1로 정리

mapping = {'a':0,'b':1}
x = torch.tensor(f(txt_x,mapping)).float().reshape(-1,1)
y = torch.tensor(f(txt_y,mapping)).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[0.],
         [1.],
         [0.],
         [1.],
         [0.]]),
 tensor([[1.],
         [0.],
         [1.],
         [0.],
         [1.]]))

- 학습

net = torch.nn.Linear(in_features=1,out_features=1,bias=False)
loss_fn = torch.nn.BCEWithLogitsLoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(5000):
    ## 1
    yhat = net(x) 
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()

- 결과

plt.plot(y[:10],'o')
plt.plot(sig(net(x)).data[:10],'--o')
[<matplotlib.lines.Line2D at 0x7fe5281f4e80>]

- 결과해석: 예상되었던 실패임

  • 아키텍처는 $\hat{y}_i = \text{sig}(\hat{w}x_i)$ 꼴이다.
  • $(x_i,y_i)=(0,1)$ 이라면 어떠한 $\hat{w}$을 선택해도 $\hat{w}x_i=0$ 이다. 이경우 $\hat{y}_i = \text{sig}(0) = 0.5$ 가 된다.
  • $(x_i,y_i)=(1,0)$ 이라면 $\hat{w}=-5$와 같은 값으로 선택하면 $\text{sig}(-5) \approx 0 = y_i$ 와 같이 만들 수 있다.
  • 상황을 종합하면 net의 weight는 $\text{sig}(\hat{w}x_i) \approx 0$ 이 되도록 적당한 음수로 학습되는 것이 최선임을 알 수 있다.
net.weight # 적당한 음수값으로 학습되어있음을 확인
Parameter containing:
tensor([[-2.8288]], requires_grad=True)

(풀이2) 2개의 파라메터 + 좋은 초기값 -- 성공

- 동일하게 a=0, b=1로 맵핑

mapping = {'a':0,'b':1}
x = torch.tensor(f(txt_x,mapping)).float().reshape(-1,1)
y = torch.tensor(f(txt_y,mapping)).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[0.],
         [1.],
         [0.],
         [1.],
         [0.]]),
 tensor([[1.],
         [0.],
         [1.],
         [0.],
         [1.]]))

- 네트워크에서 bias를 넣기로 결정함

net = torch.nn.Linear(in_features=1,out_features=1,bias=True)
loss_fn = torch.nn.BCEWithLogitsLoss()
optimizr = torch.optim.Adam(net.parameters())

- net의 초기값을 설정 (이것은 좋은 초기값임)

net.weight.data = torch.tensor([[-5.00]])
net.bias.data = torch.tensor([+2.500])
net(x)[:10]
tensor([[ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000]], grad_fn=<SliceBackward0>)

- 학습전 결과

plt.plot(y[:10],'o')
plt.plot(sig(net(x)).data[:10],'--o')
[<matplotlib.lines.Line2D at 0x7fe5281dcbb0>]

bias 쓰게 되면서 한쪽을 뭉개주는 효과?

- 학습후결과

for epoc in range(5000):
    ## 1
    yhat = net(x) 
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()
plt.plot(y[:10],'o')
plt.plot(sig(net(x)).data[:10],'--o')
[<matplotlib.lines.Line2D at 0x7fe5281419d0>]

(풀이3) 2개의 파라메터 + 나쁜초기값 -- 성공

- a=0, b=1

mapping = {'a':0,'b':1}
x = torch.tensor(f(txt_x,mapping)).float().reshape(-1,1)
y = torch.tensor(f(txt_y,mapping)).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[0.],
         [1.],
         [0.],
         [1.],
         [0.]]),
 tensor([[1.],
         [0.],
         [1.],
         [0.],
         [1.]]))

- 이전과 동일하게 바이어스가 포함된 네트워크 설정

net = torch.nn.Linear(in_features=1,out_features=1,bias=True)
loss_fn = torch.nn.BCEWithLogitsLoss()
optimizr = torch.optim.Adam(net.parameters())

- 초기값설정 (이 초기값은 나쁜 초기값임)

net.weight.data = torch.tensor([[+5.00]])
net.bias.data = torch.tensor([-2.500])
net(x)[:10]
tensor([[-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000],
        [-2.5000],
        [ 2.5000]], grad_fn=<SliceBackward0>)

- 학습전상태: 반대모양으로 되어있다.

plt.plot(y[:10],'o')
plt.plot(sig(net(x)).data[:10],'--o')
[<matplotlib.lines.Line2D at 0x7fe5280b33a0>]

- 학습

for epoc in range(5000):
    ## 1
    yhat = net(x) 
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()
plt.plot(y[:10],'o')
plt.plot(sig(net(x)).data[:10],'--o')
[<matplotlib.lines.Line2D at 0x7fe52808f640>]
  • 결국 수렴하긴 할듯

(풀이4) 3개의 파라메터를 쓴다면?

- a=0, b=1로 코딩

mapping = {'a':0,'b':1}
x = torch.tensor(f(txt_x,mapping)).float().reshape(-1,1)
y = torch.tensor(f(txt_y,mapping)).float().reshape(-1,1)
x[:5],y[:5]
(tensor([[0.],
         [1.],
         [0.],
         [1.],
         [0.]]),
 tensor([[1.],
         [0.],
         [1.],
         [0.],
         [1.]]))

- 3개의 파라메터를 사용하기 위해서 아래와 같은 구조를 생각하자.

torch.nn.Sequential(
    torch.nn.Linear(in_features=1,out_features=1,bias=True),
    torch.nn.ACTIVATION_FUNCTION(),
    torch.nn.Linear(in_features=1,out_features=1,bias=False)
)
`

위와 같은 네트워크를 설정하면 3개의 파라메터를 사용할 수 있다. 적절한 ACTIVATION_FUNCTION을 골라야 하는데 실험적으로 tanh가 적절하다고 알려져있다. ($\to$ 그래서 우리도 실험적으로 이해해보자)


(예비학습1) net(x)와 사실 net.forwardx(x)는 같다.

net(x)[:5] # 풀이3에서 학습한 네트워크임
tensor([[-0.1584],
        [ 0.1797],
        [-0.1584],
        [ 0.1797],
        [-0.1584]], grad_fn=<SliceBackward0>)
net.forward(x)[:5] # 풀이3에서 학습한 네트워크임
tensor([[-0.1584],
        [ 0.1797],
        [-0.1584],
        [ 0.1797],
        [-0.1584]], grad_fn=<SliceBackward0>)

그래서 net.forward를 재정의하면 net(x)의 기능을 재정의 할 수 있다.

net.forward = lambda x: 1 
  • "lambda x: 1" 은 입력이 x 출력이 1인 함수를 의미 (즉 입력값에 상관없이 항상 1을 출력하는 함수)
  • "net.forward = lambda x:1" 이라고 새롭게 선언하였므로 앞으론 net.forward(x), net(x) 도 입력값에 상관없이 항상 1을 출력하게 될 것임
net(x)
1

(예비학습2) torch.nn.Module을 상속받아서 네트워크를 만들면 (= "class XXX(torch.nn.Module):" 와 같은 방식으로 클래스를 선언하면) 약속된 아키텍처를 가진 네트워크를 찍어내는 함수를 만들 수 있다.

(예시1)

class Mynet1(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        self.a1 = torch.nn.Sigmoid()
        self.l2 = torch.nn.Linear(in_features=1,out_features=1,bias=False)
    def forward(self,x):
        yhat = self.l2(self.a1(self.l1(x)))
        return yhat

이제

net = Mynet1()

는 아래와 같은 효과를 가진다.

net = torch.nn.Sequential(
    torch.nn.Linear(in_features=1,out_features=1,bias=True),
    torch.nn.Sigmoid(),
    torch.nn.Linear(in_features=1,out_features=1,bias=False)
)

(예시2)

class Mynet2(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        self.a1 = torch.nn.ReLU()
        self.l2 = torch.nn.Linear(in_features=1,out_features=1,bias=False)
    def forward(self,x):
        yhat = self.l2(self.a1(self.l1(x)))
        return yhat

이제

net = Mynet2()

는 아래와 같은 효과를 가진다.

net = torch.nn.Sequential(
    torch.nn.Linear(in_features=1,out_features=1,bias=True),
    torch.nn.RuLU(),
    torch.nn.Linear(in_features=1,out_features=1,bias=False)
)

(예시3)

class Mynet3(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.l1 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        self.a1 = torch.nn.Tanh()
        self.l2 = torch.nn.Linear(in_features=1,out_features=1,bias=False)
    def forward(self,x):
        yhat = self.l2(self.a1(self.l1(x)))
        return yhat

이제

net = Mynet3()

는 아래와 같은 효과를 가진다.

net = torch.nn.Sequential(
    torch.nn.Linear(in_features=1,out_features=1,bias=True),
    torch.nn.Tanh(),
    torch.nn.Linear(in_features=1,out_features=1,bias=False)
)

클래스에 대한 이해가 부족한 학생을 위한 암기방법

step1: 아래와 코드를 복사하여 틀을 만든다. (이건 무조건 고정임, XXXX 자리는 원하는 이름을 넣는다)

class XXXX(torch.nn.Module):
    def __init__(self):
        super().__init__()
        ## 우리가 사용할 레이어를 정의 

        ## 레이어 정의 끝
    def forward(self,x):
        ## yhat을 어떻게 구할것인지 정의 

        ## 정의 끝
        return yhat
  • net(x)에 사용하는 x임, yhat은 net.forward(x) 함수의 리턴값임
  • 사실, x/yhat은 다른 변수로 써도 무방하나 (예를들면 input/output 이라든지) 설명의 편의상 x와 yhat을 고정한다.

step2: def __init__(self):에 사용할 레이어를 정의하고 이름을 붙인다. 이름은 항상 self.xxx 와 같은 식으로 정의한다.

class XXXX(torch.nn.Module):
    def __init__(self):
        super().__init__()
        ## 우리가 사용할 레이어를 정의 
        self.xxx1 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        self.xxx2 = torch.nn.Tanh()
        self.xxx3 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        ## 레이어 정의 끝
    def forward(self,x):
        ## yhat을 어떻게 구할것인지 정의 

        ## 정의 끝
        return yhat

step3: def forward:에 "x --> yhat" 으로 가는 과정을 묘사한 코드를 작성하고 yhat을 리턴하도록 한다.

class XXXX(torch.nn.Module):
    def __init__(self):
        super().__init__()
        ## 우리가 사용할 레이어를 정의 
        self.xxx1 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        self.xxx2 = torch.nn.Tanh()
        self.xxx3 = torch.nn.Linear(in_features=1,out_features=1,bias=True)
        ## 레이어 정의 끝
    def forward(self,x):
        ## yhat을 어떻게 구할것인지 정의 
        u = self.xxx1(x) 
        v = self.xxx2(u)
        yhat = self.xxx3(v) 
        ## 정의 끝
        return yhat

예비학습 끝


- 우리가 하려고 했던 것: 아래의 아키텍처에서

torch.nn.Sequential(
    torch.nn.Linear(in_features=1,out_features=1,bias=True),
    torch.nn.ACTIVATION_FUNCTION(),
    torch.nn.Linear(in_features=1,out_features=1,bias=False)
)

ACTIVATION의 자리에 tanh가 왜 적절한지 직관을 얻어보자.

- 실험결과1(Sig): Sigmoid activation을 포함한 아키텍처로 학습시킨 25개의 적합결과

fig, ax = plt.subplots(5,5,figsize=(10,10))
for i in range(5):
    for j in range(5):
        net = Mynet1()
        loss_fn = torch.nn.BCEWithLogitsLoss()
        optimizr = torch.optim.Adam(net.parameters())
        for epoc in range(1000):
            ## 1
            yhat = net(x)
            ## 2
            loss = loss_fn(yhat,y)
            ## 3
            loss.backward()
            ## 4 
            optimizr.step()
            optimizr.zero_grad()
        ax[i][j].plot(y[:5],'o')
        ax[i][j].plot(sig(net(x[:5])).data,'--o')
fig.suptitle(r"$a_1(x):=Sigmoid(x)$",size=20)
fig.tight_layout()

큰 폭 -> 학습 속도가 빠르다

- 실험결과2(ReLU): RuLU activation을 포함한 아키텍처로 학습시킨 25개의 적합결과

fig, ax = plt.subplots(5,5,figsize=(10,10))
for i in range(5):
    for j in range(5):
        net = Mynet2()
        loss_fn = torch.nn.BCEWithLogitsLoss()
        optimizr = torch.optim.Adam(net.parameters())
        for epoc in range(1000):
            ## 1
            yhat = net(x)
            ## 2
            loss = loss_fn(yhat,y)
            ## 3
            loss.backward()
            ## 4 
            optimizr.step()
            optimizr.zero_grad()
        ax[i][j].plot(y[:5],'o')
        ax[i][j].plot(sig(net(x[:5])).data,'--o')
fig.suptitle(r"$a_2(x):=ReLU(x)$",size=20)
fig.tight_layout()

- 실험결과3(Tanh): Tanh activation을 포함한 아키텍처로 학습시킨 25개의 적합결과

fig, ax = plt.subplots(5,5,figsize=(10,10))
for i in range(5):
    for j in range(5):
        net = Mynet3()
        loss_fn = torch.nn.BCEWithLogitsLoss()
        optimizr = torch.optim.Adam(net.parameters())
        for epoc in range(1000):
            ## 1
            yhat = net(x)
            ## 2
            loss = loss_fn(yhat,y)
            ## 3
            loss.backward()
            ## 4 
            optimizr.step()
            optimizr.zero_grad()
        ax[i][j].plot(y[:5],'o')
        ax[i][j].plot(sig(net(x[:5])).data,'--o')
fig.suptitle(r"$a_2(x):=Tanh(x)$",size=20)        
fig.tight_layout()

- 실험해석

  • sig: 주황색선의 변동폭이 작음 + 항상 0.5근처로 머무는 적합값이 존재
  • relu: 주황색선의 변동폭이 큼 + 항상 0.5근처로 머무는 적합값이 존재
  • tanh: 주황색선의 변동폭이 큼 + 0.5근처로 머무는 적합값이 존재X

- 실험해보니까 tanh가 우수한것 같다. $\to$ 앞으로는 tanh를 쓰자.

$x \to wx \to \tanh \to wx \to sig \to y$

  • x가 양이면 wx 양수 이런 식으로 y로 가게끔 설정하면 설명의 여지가 존재(?)

(서연 필기)sigmoid하면 0에 머무르는 값 존재해서 0.5에 머무르는 경향, 조금 사용하면 학습 능력이 떨어지기도

소프트맥스로 확장

(풀이1) 로지스틱모형에서 3개의 파라메터 버전을 그대로 확장

mapping = {'a':[1,0],'b':[0,1]}
x = torch.tensor(f(txt_x,mapping)).float().reshape(-1,2)
y = torch.tensor(f(txt_y,mapping)).float().reshape(-1,2)
x[:5],y[:5]
(tensor([[1., 0.],
         [0., 1.],
         [1., 0.],
         [0., 1.],
         [1., 0.]]),
 tensor([[0., 1.],
         [1., 0.],
         [0., 1.],
         [1., 0.],
         [0., 1.]]))
net = torch.nn.Sequential(
    torch.nn.Linear(in_features=2,out_features=1),
    torch.nn.Tanh(),
    torch.nn.Linear(in_features=1,out_features=2,bias=False)
)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())
for epoc in range(5000):
    ## 1
    yhat = net(x)
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()
soft(net(x))[:2]
tensor([[0.0048, 0.9952],
        [0.9953, 0.0047]], grad_fn=<SliceBackward0>)
y[:5][:,1]
tensor([1., 0., 1., 0., 1.])
plt.plot(y[:5][:,1],'o')
plt.plot(soft(net(x[:5]))[:,1].data,'--r')
[<matplotlib.lines.Line2D at 0x7fe52156aaf0>]

b,a,b,a,,,...

fig,ax = plt.subplots(1,2)
ax[0].imshow(y[:5])
ax[1].imshow(soft(net(x[:5])).data)
<matplotlib.image.AxesImage at 0x7fe521441490>

비슷하게 나왔다, 학습이 잘 되었다(중간 대체과제 참고)

Embedding Layer

motive

- 결국 최종적으로는 아래와 같은 맵핑방식이 확장성이 있어보인다.

mapping = {'a':[1,0,0],'b':[0,1,0],'c':[0,0,1]} # 원핫인코딩 방식 

- 그런데 매번 $X$를 원핫인코딩하고 Linear 변환하는것이 번거로운데 이를 한번에 구현하는 함수가 있으면 좋겠다. $\to$ torch.nn.Embedding Layer가 그 역할을 한다.

x dimension은 3(원핫인코딩)

mapping = {'a':0,'b':1,'c':2}
x = torch.tensor(f(list('abc')*100,mapping))
y = torch.tensor(f(list('bca')*100,mapping))
x[:5],y[:5]
(tensor([0, 1, 2, 0, 1]), tensor([1, 2, 0, 1, 2]))
torch.manual_seed(43052)
ebdd = torch.nn.Embedding(num_embeddings=3,embedding_dim=1)
ebdd(x)[:5]
tensor([[-0.8178],
        [-0.7052],
        [-0.5843],
        [-0.8178],
        [-0.7052]], grad_fn=<SliceBackward0>)

- 그런데 사실 언뜻보면 아래의 linr 함수와 역할의 차이가 없어보인다.

torch.manual_seed(43052)
linr = torch.nn.Linear(in_features=1,out_features=1)
linr(x.float().reshape(-1,1))[:5]
tensor([[-0.8470],
        [-1.1937],
        [-1.5404],
        [-0.8470],
        [-1.1937]], grad_fn=<SliceBackward0>)

- 차이점: 파라메터수에 차이가 있다.

파라메터 적게 쓰는게 비용측면에서 좋으니까

ebdd.weight
Parameter containing:
tensor([[-0.8178],
        [-0.7052],
        [-0.5843]], requires_grad=True)
linr.weight, linr.bias
(Parameter containing:
 tensor([[-0.3467]], requires_grad=True),
 Parameter containing:
 tensor([-0.8470], requires_grad=True))

결국 ebdd는 아래의 구조에 해당하는 파라메터들이고

  • $\text{x[:5]}= \begin{bmatrix} 0 \\ 1 \\ 2 \\ 0 \\ 1 \end{bmatrix} \Longrightarrow \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix} \quad net(x)= \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ 1 & 0 & 0 \\ 0 & 1 & 0 \end{bmatrix}\begin{bmatrix} -0.8178 \\ -0.7052 \\ -0.5843 \end{bmatrix} = \begin{bmatrix} -0.8178 \\ -0.7052 \\ -0.5843 \\ -0.8178 \\ -0.7052 \end{bmatrix} $

linr는 아래의 구조에 해당하는 파라메터이다.

  • $\text{x[:5]}= \begin{bmatrix} 0 \\ 1 \\ 2 \\ 0 \\ 1 \end{bmatrix} \quad net(x)= \begin{bmatrix} 0 \\ 1 \\ 2 \\ 0 \\ 1 \end{bmatrix} \times (-0.3467) + (-0.8470)=\begin{bmatrix} -0.8470 \\ -1.1937 \\ -1.5404 \\ -0.8470 \\ -1.1937 \end{bmatrix}$

연습 (ab문제 소프트맥스로 확장한 것 다시 풀이)

- 맵핑

mapping = {'a':0,'b':1}
x = torch.tensor(f(txt_x,mapping))
y = torch.tensor(f(txt_y,mapping))
x[:5],y[:5]
(tensor([0, 1, 0, 1, 0]), tensor([1, 0, 1, 0, 1]))

- torch.nn.Embedding 을 넣은 네트워크

num_embedding이 2인 이유 a,b만 있어서

net = torch.nn.Sequential(
    torch.nn.Embedding(num_embeddings=2,embedding_dim=1),
    torch.nn.Tanh(),
    torch.nn.Linear(in_features=1,out_features=2)
)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())

- 학습

for epoc in range(5000):
    ## 1
    yhat = net(x)
    ## 2 
    loss = loss_fn(yhat,y)
    ## 3 
    loss.backward()
    ## 4 
    optimizr.step()
    optimizr.zero_grad()
plt.plot(y[:5],'o')
plt.plot(soft(net(x[:5]))[:,1].data,'--r')
[<matplotlib.lines.Line2D at 0x7fe5214c6220>]
plt.imshow(soft(net(x[:5])).data)
<matplotlib.image.AxesImage at 0x7fe52160a2b0>