ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [pytorch] nn.Linear 알아보기
    AI-ML 2023. 2. 20. 06:52
    728x90
    반응형

    - 목차

     

    키워드.

    • - pytorch
    • - Linear
    • - Dot Product

     

    들어가며.

    pytorch 는 Neural Network 상에서 Linear Layer 를 구성할 수 있도록 관련 모듈을 제공합니다.

    torch.nn 모듈 내부의 Linear 클래스가 존재하며, 통상적으로 nn.Linear 와 같은 형식으로 사용됩니다.

    nn.Linear 모듈은 Input Tensor 와 nn.Linear 의 weight 를 Dot Product 한 결과를 출력합니다.

    그래서 nn.Linear 는 곧 Dot Product 를 수행하는 수학적인 함수라고 생각하셔도 무방합니다.

    한번 예를 들어볼까요 ?

    아래 예시는 A 행렬과 B 행렬의 Dot Product 와 Linear Transformation 을 적용한 결과들입니다.

    A 와 B 가 1 과 2 라는 1개의 값을 가지는 경우에 Dot Product 와 Linear Transformation 의 결과는 2로 출력됩니다.

    import numpy as np
    
    A_matrix = [
        [1.0]
    ]
    B_matrix = [
        [2.0]
    ]
    
    print(f"numpy Dot Product : {np.dot(A_matrix, B_matrix)}")
    
    import torch 
    import torch.nn as nn
    
    A = nn.Linear(1, 1, bias=False)
    A.weight.data = torch.tensor(A_matrix)
    B = torch.tensor(B_matrix)
    
    print(f"A Linear Layer Weight {A.weight}")
    print(f"torch Linear Output : {A(B)}")
    numpy Dot Product : [[2]]
    A Linear Layer Weight Parameter containing:
    tensor([[1.]], requires_grad=True)
    torch Linear Output : tensor([[2.]], grad_fn=<MmBackward0>)

     

    조금 더 큰 사이즈의 행렬을 계산해보겠습니다.

    2x4 행렬과 4x3 행렬을 내적하여 numpy 와 nn.Linear 는 동일한 결과를 보입니다.

    import numpy as np
    
    A_matrix = [
        [1.0, 1.0, 1.0, 1.0], 
        [2.0, 2.0, 2.0, 2.0]
    ]
    B_matrix = [
        [1.0, 1.0, 1.0], 
        [2.0, 2.0, 2.0], 
        [3.0, 3.0, 3.0], 
        [4.0, 4.0, 4.0]
    ]
    
    print(f"numpy Dot Product : {np.dot(A_matrix, B_matrix)}")
    
    import torch 
    import torch.nn as nn
    
    B = nn.Linear(4, 3, bias=False)
    B.weight.data = torch.tensor(B_matrix).T
    A = torch.tensor(A_matrix)
    
    print(f"A Linear Layer Weight {B.weight}")
    print(f"torch Linear Output : {B(A)}")
    numpy Dot Product : [[10. 10. 10.]
     [20. 20. 20.]]
    A Linear Layer Weight Parameter containing:
    tensor([[1., 2., 3., 4.],
            [1., 2., 3., 4.],
            [1., 2., 3., 4.]], requires_grad=True)
    torch Linear Output : tensor([[10., 10., 10.],
            [20., 20., 20.]], grad_fn=<MmBackward0>)

     

    즉, nn.Linear Layer 는 Dot Product 를 수행하는 Neural Net 의 Layer 이구요.

    Input Tensor 와 nn.Linear 의 Weight Tensor 의 내적으로 Output Tensor 가 출력됩니다.

     

    이어지는 내용에서 상세한 설정과 여러 예시를 작성해보도록 하겠습니다.

     

    nn.Linear 사용해보기.

    nn.Linear 클래스의 사용법은 아래와 같습니다.

    torch.nn 모듈 내의 Linear 클래스를 통해서 nn.Linear Layer 객체를 생성할 수 있습니다.

    import torch 
    import torch.nn as nn
    
    linear = nn.Linear(in_features=4, out_features=3, bias=False)

     

    in_features=4 와 out_features=3 으로 설정되어 있는데요.

    이 동작 방식은 행렬의 내적과 동일하다고 말씀드렸죠 ?

    따라서 dimension 의 길이가 4인 Input Tensor 만이 nn.Linear 의 입력으로 사용될 수 있습니다.

     

    예를 들어볼까요 ?

    아래의 input_tensor 는 1x4 인 Tensor 이고, nn.Linear 은 4x3 인 Layer 입니다.

    결과는 아래처럼 1x3 인 Tensor 가 출력됩니다.

    import torch 
    import torch.nn as nn
    
    linear = nn.Linear(in_features=4, out_features=3, bias=False)
    
    input_tensor = torch.tensor([[1.0, 2.0, 3.0, 4.0]])
    
    print(f"torch Linear Output : {linear(input_tensor)}")
    torch Linear Output : tensor([[-0.6756,  0.7807, -0.7505]], grad_fn=<MmBackward0>)

     

    만약 Input Tensor 의 사이즈와 nn.Linear Layer 의 in_features 가 맞지 않다면, 아래와 같은 유형의 에러가 발생하게 됩니다.

    RuntimeError: mat1 and mat2 shapes cannot be multiplied (1x5 and 4x3)

     

    in_features.

    nn.Linear 클래스는 in_features 인자를 가집니다.

    in_features 인자는 Input Tensor 의 마지막 dimension 의 갯수를 의미합니다.

    마지막 dimension 은 2-d Tensor 인 행렬의 경우에 Column 의 갯수를 의미하게 되고,

    아래와 같이 4차원의 Tensor 라고 할지라고 가장 안쪽의 배열의 길이가 in_features 와 동일해야합니다.

    # torch.Size([1, 1, 1, 4])
    input_tensor = torch.tensor([
        [
            [
                [1.0, 2.0, 3.0, 4.0]
            ]
        ]
    ])
    print(input_tensor.shape)
    print(f"torch Linear Output : {linear(input_tensor)}")
    torch.Size([1, 1, 1, 4])
    torch Linear Output : tensor([[[[ 2.3263, -2.5094,  1.5137]]]], grad_fn=<UnsafeViewBackward0>)
    input_tensor = torch.tensor([
        [
            [
                [
                    [
                        [1.0, 2.0, 3.0, 4.0]
                    ]
                ]
            ]
        ]
    ])
    print(input_tensor.shape)
    print(f"torch Linear Output : {linear(input_tensor)}")
    torch.Size([1, 1, 1, 1, 1, 4])
    torch Linear Output : tensor([[[[[[ 2.3263, -2.5094,  1.5137]]]]]], grad_fn=<UnsafeViewBackward0>)

     

     

    out_features.

    out_features 는 nn.Linear Layer 로부터 출력되는 Tensor 의 feature 갯수를 의미합니다.

    일반적으로 Regression 문제의 경우에는 out_features 를 1로 설정하는 경우가 많구요.

    반면, Classification 문제의 경우에는 결과의 범주 갯수만큼 out_features 를 설정하곤 합니다.

     

    아래 예시는 Linear Regression 문제는 pytorch 간단히 구현한 결과입니다.

    이렇게 Linear Regression 과 같은 문제는 out_features 는 1 로 설정하곤 합니다.

    import torch 
    import torch.nn as nn
    import torch.optim as optim
    X = torch.tensor(list(range(10)), dtype=torch.float32).reshape(-1, 1)
    y = X * 10 + 5
    
    class my_model(nn.Module):
        def __init__(self):
            super(my_model, self).__init__()
            self.linear = nn.Linear(1, 1, bias=True)
            # self.relu = nn.relu()
    
        def forward(self, x):
            out = self.linear(x)
            return out
    
    model = my_model()
    criterion = nn.MSELoss() 
    optimizer = optim.SGD(model.parameters(), lr=0.01)
    
    for epoch in range(1000):
        outputs = model(X)
        optimizer.zero_grad()
        loss = criterion(outputs, y)
        loss.backward()
        optimizer.step()
    
    
    model.linear.weight, model.linear.bias
    (Parameter containing:
     tensor([[10.0017]], requires_grad=True),
     Parameter containing:
     tensor([4.9891], requires_grad=True))

     

    위 결과와 같이 weight 는 10.0017 그리고 bias 는 4.9891 로 y = X * 10 + 5 인 Train Dataset 과 유사한 결과를 보입니다.

     

    bias.

    nn.Linear 모듈은 bias 라는 인자를 가집니다.

    bias 는 Input Tensor 와 nn.Linear Weight 의 Dot Product 의 결과와 더해집니다.

    즉, Output Tensor 에 부가적인 영향력을 제공합니다.

    아래의 예시와 같이 out_features 의 값에 해당하는 크기만큼 bias Tensor 가 생성됩니다.

    import torch 
    import torch.nn as nn 
    
    linear = nn.Linear(in_features=10, out_features=1, bias=True)
    print(linear.weight)
    print(linear.bias)
    Parameter containing:
    tensor([[-0.0365,  0.1214, -0.2508, -0.2174, -0.1147, -0.0663, -0.2425, -0.2478,
              0.2966, -0.2276]], requires_grad=True)
    Parameter containing:
    tensor([0.0310], requires_grad=True)

     

    만약 out_features 의 값이 5 로 설정된다면, bias Tensor 의 크기는 5가 됩니다.

    linear = nn.Linear(in_features=10, out_features=5, bias=True)
    print(linear.bias)
    Parameter containing:
    tensor([ 0.0717, -0.1589,  0.2427,  0.0313, -0.0126], requires_grad=True)

     

    그리고 bias 의 값이 False 로 설정된다면, bias Tensor 는 None 이 됩니다.

    linear = nn.Linear(in_features=10, out_features=5, bias=False)
    print(linear.bias)
    None

     

    여러가지 활용 예시.

    3x3 Tensor -> 3x6 Tensor.

    Feature 길이가 3인 Tensor 가 존재한다고 가정합니다.

    그리고 DataLoader 의 batch_size 가 3이라면, DataLoader 에 의해서 Iteration 마다 출력되는 Tensor 는 3x3 의 Tensor 가 됩니다.

    예시는 아래와 같습니다.

    import torch
    import torch.nn as nn
    from torch.utils.data import DataLoader
    
    dataset = torch.tensor([
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
        [10, 11, 12],
        [13, 14, 15],
    ], dtype=torch.float32)
    
    loader = DataLoader(dataset, batch_size=3, shuffle=False)
    
    tensor = next(iter(loader))
    # tensor([[1., 2., 3.],
    #         [4., 5., 6.],
    #         [7., 8., 9.]])

     

    그리고 3 -> 6 으로 Linear Transformation 이 수행되는 nn.Linear Transformation 을 거치면,

    아래의 결과와 같이 3x6 인 Tensor 로 변환됩니다.

    linear = nn.Linear(3, 6)
    linear(tensor)
    # tensor([[-0.5625, -1.7144, -0.0318,  0.9240,  1.7183, -0.2757],
    #         [-1.1148, -4.9342,  0.2390,  2.6098,  5.4051, -1.6341],
    #         [-1.6672, -8.1539,  0.5098,  4.2956,  9.0919, -2.9925]],
    #        grad_fn=<AddmmBackward0>)

     

    3x28x28 Tensor -> 3x10x10 Tensor.

    MNIST 의 digits 이미지는 28x28 사이즈의 이미지들로 구성됩니다.

    아래 예시는 MNIST 손글씨 데이터를 다운로드하여 Shape 를 출력합니다.

    조회한 Tensor 는 1x28x28 의 크기를 가지구요.

    이는 1개의 Channel 과 width, height 가 각각 28 픽셀로 구성됩니다.

    import torch
    import torchvision
    from torchvision import datasets
    from torchvision import transforms
    from torch.utils.data import DataLoader
    
    trainset = datasets.MNIST(
        root="/tmp/mnist",
        train=True,
        transform=transforms.ToTensor(),
        download=True
    )
    
    
    # trainset 의 첫번째 Tensor
    trainset[0][0].shape
    # torch.Size([1, 28, 28])
    
    # 첫번째 데이터의 1번 값은 Label 에 해당함.
    trainset[0][1]
    # 3

     

    그리고 batch_size 가 3 인 DataLoader 를 사용하게 되면, 각 Iteration 마다 3x1x28x28 인 Tensor 를 얻게 됩니다.

    loader = DataLoader(trainset, batch_size=3, shuffle=False)
    train, label = next(iter(loader))
    train.shape
    # torch.Size([3, 1, 28, 28])

     

    그 이후에 28 -> 100 으로 Linear Transform 을 수행하는 nn.Linear 를 적용시키게 되면,

    아래와 같이 Tensor 의 차원이 변형되게 됩니다.

    linear = torch.nn.Linear(28, 100)
    linear(train).shape
    # torch.Size([3, 1, 28, 100])

     

     

    반응형
Designed by Tistory.