-
[pytorch] nn.Linear 알아보기AI-ML 2023. 2. 20. 06:52728x90반응형
- 목차
키워드.
- - 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])
반응형'AI-ML' 카테고리의 다른 글
[pytorch] Dropout 알아보기 (0) 2023.08.17 Association Rules (연관규칙) 이해하기 (0) 2023.05.16 [ pytorch ] MaxPool2d, AvgPool2d 알아보기 ( Pooling Layer ) (0) 2023.03.27 [pytorch] torch.cat 알아보기 (0) 2023.03.12 [pytorch] DataLoader 알아보기 (0) 2022.07.18