Notice
Recent Posts
Recent Comments
Link
250x250
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
Tags
- mrc
- ODQA
- Bart
- 딥러닝
- 데이터 구축
- AI 경진대회
- Data Viz
- pyTorch
- KLUE
- Optimization
- Bert
- Transformer
- 2023 현대차·기아 CTO AI 경진대회
- Ai
- N2N
- passage retrieval
- matplotlib
- 데이터 시각화
- nlp
- N21
- GPT
- AI Math
- Attention
- RNN
- word2vec
- Self-attention
- 현대자동차
- dataset
- seaborn
- 기아
Archives
- Today
- Total
쉬엄쉬엄블로그
Custom Model 제작 본문
728x90
이 색깔은 주석이라 무시하셔도 됩니다.
Python List vs PyTorch ModuleList
파이썬에도 List가 있는데 굳이 PyTorch에서는 ModuleList를 별도로 만들어둔 이유?
- 기능적으로는 동일하지만 nn.Module의 submodule로 등록이 되느냐 마느냐의 차이가 있음
- Python List는 nn.Module의 submodule로 등록이 안됨
- PyTorch ModuleList는 물론 ModuleList 내부에 담긴 Module들이 nn.Module의 submodule로 등록됨
- list를 사용할 경우 모델을 출력해도 nn.Module 내부의 어떤 module도 출력되어 나오지 않음
- nn.Module 내부에서 새로운 변수를 만들 때 “변수 = 값”의 형태로 코드를 적으면 “setattr”라는 특수 메서드가 호출됨
- “nn.Module” 클래스의 “setattr” 함수에서는 “값”의 타입을 체크해서 모듈인지 아닌지 체크하는 과정이 있어서 모듈이면 submodule로 등록하고 아니면 무시하고 넘어감
- list는 모듈이 아니기 때문에 무시되어서 등록이 되지 않음
- list를 써서 submodule로 등록이 안되면 모델을 저장할 때 list에 포함된 어떠한 module들도 저장이 되지 않음
- PyTorch의 nn.Module들은 Graph의 Node처럼 서로 연결되어서 유기적으로 동작하고 관리되기 때문에 list를 사용해서 module간에 연결고리가 끊어지는 일이 없도록 조심해야 함
- 자신이 무슨 일을 하는지 명확히 알 때만 list를 사용하자
Tensor vs Parameter
tensor가 아니라 굳이 Parameter라는 별개의 클래스를 사용하는 이유?
- Paramter를 이용해서 파라미터를 만들 경우에만 gradient를 계산하는 함수인 grad_fn가 생성된다.
- 즉, 학습에서 tensor로 파라미터를 생성한다면 backward propagation을 활용할 수 없게 될 것이다.
Buffer
tensor를 학습 파라미터로 사용하지 않고 값만 저장하고 싶다면?
- buffer에 tensor를 등록해준다.
self.register_buffer('buffer', self.tensor)
모델 분석해보기
named_children vs named_modules
- “childern”은 한 단계 아래의 submodule까지만 표시
- “modules”는 자신에게 속하는 모든 submodule들을 표시
- “named_modules”, “named_children”은 module의 이름도 반환해줌
- module만 필요할 땐 “modules”, “children”을 사용
Parameter
Parameter 목록 보기
for name, parameter in model.named_parameters(): print(f"[ Name ] : {name}\n[ Parameter ]\n{parameter}") print("-" * 30)module에 속하는 parameter 가져오기
# TODO : Function_B에 속하는 Parameter W1을 가져오세요! parameter = model.get_parameter('ab.b.W1') # 아래 코드는 수정하실 필요가 없습니다! if parameter == 10: print("🎉🎉🎉 성공!!! 🎉🎉🎉") else: print("🦆 다시 도전해봐요!")module에 속하는 buffer 가져오기
# TODO : named_buffers를 사용해서 model에 속하는 buffer 전체 목록을 가져오세요! for name, buffer in model.named_buffers(): print(f"[ Name ] : {name}\n[ Buffer ] : {buffer}") print("-" * 30) # TODO : buffers를 사용해서 model에 속하는 buffer 전체 목록을 가져오세요! for buffer in model.buffers(): print(f"[ Buffer ] : {buffer}") print("-" * 30) # TODO : Function_C에 속하는 Buffer를 가져오세요! buffer = model.get_buffer('cd.c.duck') # 아래 코드는 수정하실 필요가 없습니다! if buffer == 7: print("🎉🎉🎉 성공!!! 🎉🎉🎉") else: print("🦆 다시 도전해봐요!")
self.register_buffer('duck', ...)후에self.getbuffer('duck')이 아니라self.duck으로도 접근이 가능
Docstring
def func_a():
''' docstring 작성하기 '''
'''
docstring
작성하기
'''
return 0
print(func_a.__doc__)
부덕이 모델 수정하기 - module 출력 내용 변경하기
extra_repr
nn.Module에 구현되어 있는 함수- document의 소스코드를 참고해보니
__repr__함수에서 호출된다. - 기본 코드는
''를 return하도록 작성되어 있다. nn.Module을 상속받은 클래스 내에서extra_repr함수를 입력받은 문자열을 반환하도록 재정의(오버라이딩?)하여 module에 원하는 이름을 붙여주도록 만들었다.
hook
- 패키지화된 코드에서 자신 또는 다른 프로그래머가 custom 코드를 중간에 실행시킬 수 있도록 만들어놓은 인터페이스
- 활용 시기
- 프로그램의 실행 로직을 분석할 때
- 프로그램에 추가적인 기능을 제공하고 싶을 때
self.hooks(예시)에 등록된 함수가 있으면hooks에 등록된 함수 실행
등록된 함수가 없으면 무시
- 활용 시기
PyTorch의 hook
Tensor에 적용하는 hook
backward hook만 존재한다.
_backward_hooks에서 확인 가능
import torch tensor = torch.rand(1, requires_grad=True) def tensor_hook(grad): pass tensor.register_hook(tensor_hook) # 🦆 tensor는 backward hook만 있어요! tensor._backward_hooks
Module에 적용하는 hook
nn.Module에 등록하는 모든 hook은__dict__를 사용하면 한 번에 확인 가능하다.from torch import nn class Model(nn.Module): def __init__(self): super().__init__() def module_hook(grad): pass model = Model() model.register_forward_pre_hook(module_hook) model.register_forward_hook(module_hook) model.register_full_backward_hook(module_hook) # 🦆 __dict__에는 module의 모든 변수와 parameter, hook 등의 중요한 정보가 담겨있어요! # module이 정보의 저장소로 이용하는 공간인만큼 자세한 잊지 말고 나중에 필요할 때 사용해보세요! model.__dict__ ''' {'training': True, '_parameters': OrderedDict(), '_buffers': OrderedDict(), '_non_persistent_buffers_set': set(), '_backward_hooks': OrderedDict([(3, <function __main__.module_hook(grad)>)]), '_is_full_backward_hook': True, '_forward_hooks': OrderedDict([(2, <function __main__.module_hook(grad)>)]), '_forward_pre_hooks': OrderedDict([(1, <function __main__.module_hook(grad)>)]), '_state_dict_hooks': OrderedDict(), '_load_state_dict_pre_hooks': OrderedDict(), '_load_state_dict_post_hooks': OrderedDict(), '_modules': OrderedDict()} '''__dict__- forward_pre_hooks
- forward_hooks
- backward_hooks # deprecated
- full_backward_hooks
- state_dict_hooks
- used internally
load_state_dict에서 내부적으로 사용된다.
- used internally
- forward에는 pre_hook과 hook 존재
- backward에는 hook만 존재
응용
- gradient값의 변화를 시각화
- gradient값이 특정 임계값을 넘으면 gradient exploding 경고 알림
- 특정 tensor의 gradient값이 너무 커지거나 작아지는 현상이 관측되면
- 해당 tensor 한정으로 gradient clipping
apply
import torch
from torch import nn
@torch.no_grad()
def init_weights(m): # m : module
print(m)
if type(m) == nn.Linear:
m.weight.fill_(1.0)
print(m.weight)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
- apply를 통해 적용하는 함수는 module을 입력으로 받음
- 모델의 모든 module들을 순차적으로 입력받아서 처리
- 일반적으로 가중치 초기화(Weight Initialization)에 많이 사용
- Parameter로 지정한 tensor의 값을 원하는 값으로 지정해주는 것을 의미
모델 apply - repr 수정하기
현재 모델을 출력해보면 다음과 같이 나와요!
❌ 실제 출력 결과
Model(
(ab): Layer_AB(
(a): Function_A()
(b): Function_B()
)
(cd): Layer_CD(
(c): Function_C()
(d): Function_D()
)
)
이걸 다음처럼 출력되게 만들고 싶어요!
✅ 원하는 이상적인 출력
Model(
(ab): Layer_AB(
(a): Function_A(name=plus)
(b): Function_B(name=substract)
)
(cd): Layer_CD(
(c): Function_C(name=multiply)
(d): Function_D(name=divide)
)
)
apply를 이용해서 repr 출력 메세지를 수정해봐요!apply는 apply가 적용된 module을 return 해준다.
위에서 module에 원하는 이름을 붙여준 것처럼 함수를 만들어야 하는데 이번에는 여러 module에 적용하기 위해서 apply를 사용한다.
module의
extra_repr함수에function_repr를 적용해주기 위해partial라는 함수를 사용하여 저장한다.model = Model() # TODO : apply를 이용해서 부덕이가 원하는대로 repr 출력을 수정해주세요! from functools import partial def function_repr(self): return f'name={self.name}' def add_repr(module): module_name = module.__class__.__name__ if module_name.split('_')[0] == "Function": module.extra_repr = partial(function_repr, module) # 🦆 apply는 apply가 적용된 module을 return 해줘요! returned_module = model.apply(add_repr)partial를 사용하지 않고 처리할 방법이 없을까?
partial
- 하나 이상의 인수가 이미 채워진 새 버전의 함수를 만들 때 사용하는 함수
- 참고
🔥🔥🔥 apply - Function 수정하기 🔥🔥🔥
새로운 문제
현재 4개의 Function A, B, C, D가 있어요!
- A : x + W
- B : x - W
- C : x * W
- D : x / W
이걸 다음처럼 linear transformation처럼 동작하도록 바꿔보래요!
- A : x @ W + b
- B : x @ W + b
- C : x @ W + b
- D : x @ W + b
W는 이미 각 Function에 생성된 Parameter이고
b는 새롭게 만들어야 하는 Parameter에요!
연산 수식이 동일할 필요는 없지만 계산 결과는 같아야 한다고 하더라구요!
직접 "nn.Linear" 모델을 이용해서 제대로 만들었는지 검증한대요!
결과 비교를 위해서 W과 b는 모두 1로 값을 초기화한다고 해요!
아! 이제 tensor에 담긴 값은 scalar가 아니라
2*2크기의 matrix라는 점에 주의해요!굉장히 어려웠다.
입력으로 2, 2 tensor가 들어오기 때문에 bias는 2, 1로 만들어줌
- broadcasting 연산이 적용될 것
# TODO : apply를 이용해 Parameter b를 추가해보세요! def add_bias(module): module_name = module.__class__.__name__ if module_name.split('_')[0] == "Function": module.b = Parameter(torch.rand(2, 1)) # elif module_name.split('_')[0] == "Layer": # print("@@@@@@@@@@")
- broadcasting 연산이 적용될 것
W처럼 b도 1로 초기화
# TODO : apply를 이용해 추가된 b도 값을 1로 초기화해주세요! def weight_initialization(module): module_name = module.__class__.__name__ add_bias(module) if module_name.split('_')[0] == "Function": module.W.data.fill_(1.) module.b.data.fill_(1.)apply를
linear_transformation에 적용하기 때문에linear_transformation에서 hook을 등록해주는register_forward_hook을 실행hook에서 전파되는 값을 input으로 받아오고 output을 바꿔준다.- input은 tuple형태로 tensor 값을 담고 있어서 input[0]으로 꺼낸다.
- 입력받은 output 값을 바꿔서 return 해줘야 원하는 계산 결과가 나온다.
input 값과 $W^T$(parameter) 값을 행렬곱 연산하고 b(bias)를 더해서 output으로 만들어준다.
# TODO : apply를 이용해 모든 Function을 linear transformation으로 바꿔보세요! # X @ W + b def linear_transformation(module): module_name = module.__class__.__name__ if module_name.split('_')[0] == "Function": module.register_forward_hook(hook) pass def hook(module, input, output): print(input) print("@ : ", input[0]) # output = input[0] @ module.W.T output = torch.mm(input[0], module.W.T) output = torch.add(output, module.b) return output returned_module = model.apply(add_bias) returned_module = model.apply(weight_initialization) returned_module = model.apply(linear_transformation)
출처: 부스트캠프 AI Tech 4기(NAVER Connect Foundation)
'부스트캠프 AI Tech 4기' 카테고리의 다른 글
| Datasets과 Dataloaders (0) | 2023.05.18 |
|---|---|
| AutoGrad와 Optimizer (0) | 2023.05.15 |
| PyTorch 프로젝트 구조 이해하기 (0) | 2023.05.12 |
| PyTorch Basics (0) | 2023.05.11 |
| PyTorch 소개 (2) | 2023.05.10 |
Comments