
import torch
x = torch.FloatTensor([[1,2],[3,4],[5,6]])
y = torch.FloatTensor([[1,2],[1,2]])
print(x.size(), y.size())
# -> torch.Size([3, 2]) torch.Size([2, 2])
z = torch.matmul(x,y)
print(z.size())
# -> torch.Size([3, 2])
bmm함수는 행렬 곱이 3번 수행되는 연산을 병렬로 동시에 진행 가능하다.
x = torch.FloatTensor(3,3,2)
y = torch.FloatTensor(3,2,3)
z = torch.bmm(x,y)
print(z.size())
# -> torch.Size([3, 3, 3])
W = torch.FloatTensor([[1,2],[3,4],[5,6]])
b = torch.FloatTensor([2,2])
def linear(x, W, b):
    y = torch.matmul(x, W) + b
    return y
x = torch.FloatTensor(4,3)
print(linear(x,W,b).size())
# -> torch.Size([4, 2])
파이토치에는 nn 패키지가 있고 torch.nn.Module 이라는 추상 클래스를 상속 받아 정의 되어 있다.
import torch.nn as nn
class MyLinear(nn.Module):
    # __init__ 과 forward override. 
    # __init__ 은 계층 내부에 필요한 변수 선언 및 다른 계층을 소유할 수 있다.
    def __init__(self, input_dim = 3, output_dim = 2):
        self.input_dim = input_dim
        self.output_dim = output_dim
        super().__init__()
        self.W = torch.FloatTensor(input_dim, output_dim)
        self.b = torch.FloatTensor(output_dim)
    def forward(self, x):
        y = torch.matmul(x, self.W) + self.b
        return y
linear = MyLinear(3,2)
y = linear(x) # forward는 직접 호출할 필요가 없다. __call__과 nn.Module은 매핑되어 있음.
print(y)
'''
tensor([[2.1715e-18, 1.3298e+22],
        [2.1715e-18, 1.3298e+22],
        [2.1715e-18, 1.3298e+22],
        [2.1715e-18, 1.3298e+22]])
'''
for p in linear.parameters():
    print(p)
#- > 아무것도 나오지 않는다.
위의 코드는 내부에 학습할 수 있는 파라미터가 없는 것으로 인식해서 위의 for 문장에 아무것도 출력되지 않는다.
아래의 nn.Parameter 클래스를 활용하여 파라미터가 출력 가능하게 만든다.
class MyLinear(nn.Module):
    # __init__ 과 forward override. 
    # __init__ 은 계층 내부에 필요한 변수 선언 및 다른 계층을 소유할 수 있다.
    def __init__(self, input_dim = 3, output_dim = 2):
        self.input_dim = input_dim
        self.output_dim = output_dim
        super().__init__()
        self.W = nn.Parameter(torch.FloatTensor(input_dim, output_dim))
        self.b = nn.Parameter(torch.FloatTensor(output_dim))
    def forward(self, x):
        y = torch.matmul(x, self.W) + self.b
        return y
for p in linear.parameters():
    print(p)
'''
Parameter containing:
tensor([[1.7377e-35, 0.0000e+00],
        [5.3811e-38, 0.0000e+00],
        [0.0000e+00, 0.0000e+00]], requires_grad=True)
Parameter containing:
tensor([-1.8174e+34,  4.5649e-41], requires_grad=True)
'''
위에는 복잡한 방법으로 선형계층을 보였지만 간단한 방법도 존재한다.
torch.nn에 미리 정의된 선형 계층을 불러 쓰면 간단해 진다.
x = torch.FloatTensor(4,3)
linear = nn.Linear(3,2)
y = linear(x)
for p in linear.parameters():
    print(p)
'''
Parameter containing:
tensor([[-0.2501, -0.4086,  0.3287],
        [ 0.1874, -0.5189,  0.5299]], requires_grad=True)
Parameter containing:
tensor([ 0.2059, -0.0335], requires_grad=True)
'''
class MyLinear(nn.Module):
    # __init__ 과 forward override. 
    # __init__ 은 계층 내부에 필요한 변수 선언 및 다른 계층을 소유할 수 있다.
    def __init__(self, input_dim = 3, output_dim = 2):
        self.input_dim = input_dim
        self.output_dim = output_dim
        super().__init__()
        self.linear = nn.Linear(input_dim, output_dim)
    def forward(self, x):
        y = self.linear(x)
        return y