Pytorch 설치하기
Multi-GPU 환경에서 특정 GPU만 default로 사용하기
bash에서 다음을 실행
export CUDA_VISIBLE_DEVICES=4
혹은 python에서 다음을 실행
os.environ["CUDA_VISIBLE_DEVICES"] = 4
또는
torch.cuda.set_device(4)
Tensorboard로 graph그리기
Tensorboard 실행하기
Tensorboard 설치 문제 해결
완전히 Deterministic하게 실험 재현하기
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed) # if you are using multi-GPU.
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
AttributeError: cannot assign module before Module.__init__() call 에러 해결
이 에러는 nn.Module을 상속받은 class가 __init__에서 먼저 super의 __init__을 호출하지 않아서 발생하는 문제이다.
class BaseNetwork(nn.Module):
def __init__(self):
super().__init__()
위와 같이 호출하면 된다.
https://discuss.pytorch.org/t/attributeerror-cannot-assign-module-before-module---init---call/1446
특정 value의 first occurance index 찾기
eos_detection = (pred_text == EOS_token).float()
index_tensor = torch.arange(eos_detection.shape[-1], 0, -1)
eos_index = eos_detection * index_tensor
valid_length = torch.argmax(eos_index, dim=-1) + 1
원리는 torch.arange를 이용해서 0또는 1로 구성된 detection랑 곱해서 index로 바꾸고 argmax를 이용하는 것.
두 Tensor가 서로 동일한 값을 갖는지 체크하기
Tensor 주소값 가져오기
tensor.data_ptr() -> 첫번쨰 element의 pointer를 return해서 tensor의 id값으로 쓸 수 있음.
https://pytorch.org/docs/stable/generated/torch.Tensor.data_ptr.html
detach() vs clone()
Conv에서 padding이 계산되는 순서
nn.conv에서 padding 파라미터의 경우, 먼저 convolution을 하기 전에 input data에 대해 padding을 적용하는 옵션이다. 이때 지정한 padding 사이즈가 n이라면, 이 n만큼의 0값을 좌우에 각각 더한다. 따라서 padding 1을 입력했다면 왼쪽에 1칸, 오른쪽에 1칸을 붙여서 총 2칸을 더 붙이게 되는 것이다.
https://discuss.pytorch.org/t/padding-for-convolutions/5881
Conv1d와 ConvTranspose1d 의 output shape 계산하기
def conv1d_output_shape(data_len, kernel_size=1, stride=1, padding=0, dilation=1, **kwargs):
"""
https://discuss.pytorch.org/t/utility-function-for-calculating-the-shape-of-a-conv-output/11173/5
Utility function for computing output of convolutions
"""
return (data_len + (2 * padding) - (dilation * (kernel_size - 1)) - 1)// stride + 1
def convtrans1d_output_shape(data_len, kernel_size=1, stride=1, padding=0, output_padding=0, dilation=1, **kwargs):
"""
https://discuss.pytorch.org/t/utility-function-for-calculating-the-shape-of-a-conv-output/11173/5
Utility function for computing output of transposed convolutions
"""
return (data_len - 1) * stride - 2 * padding + dilation * (kernel_size-1) + output_padding + 1
ConvTranspose의 파라미터 이해하기
https://medium.com/@santi.pdp/how-pytorch-transposed-convs1d-work-a7adac63c4a5
nn.Softmax 와 nn.CrossEntropyLoss의 차이
nn.Softmax는 학습에 사용되는 loss와는 전혀 무관하다. Attention등에서 prob을 구하거나, RL의 policy에서 action sampling을하기 위해 사용되는 등, 즉 모델 아키텍쳐 상에 softmax prob 계산이 필요할 때에만 사용해야한다. 이와 동치인 구현으로는 모델이 내놓은 last activation(=logit)에 대해 nn.functional.F.softmax를 사용해도 된다. 중요한 점은 nn.Softmax가 내놓은 prob에 대해서는 바로 NLLloss 적용이 불가능하다는 것이다. 이론 상으로는 이 둘을 결합하면 CrossEntropyLoss가 되지만, 이런 식의 구현은 numerically 매우 불안정하기 때문에 Nan값이 쉽게 발생한다.
따라서 권장되는 구현은 classification task 학습에는 nn.Softmax의 호출없이 model의 output에 nn.CrossEntropyLoss만 사용해야하고, softmax prob계산이 필요한 경우에 한해서 nn.Softmax를 쓰거나 F.softmax를 사용해야한다.
그밖에 두번 째 방법으로는 nn.LogSoftmax와 nn.NLLloss를 결합해서 CrossEntropyLoss로 사용하는 것은 가능하고, 이 경우 model이 내놓은 output에 대해 torch.exp()를 취해주어야 한다.
https://discuss.pytorch.org/t/trouble-getting-probability-from-softmax/26764/6
* F.log_softmax는 N번 반복해서 사용해도 1개의 F.log_softmax와 동치이다.
log_softmax에서 취해지는 log는 자연상수 e를 밑으로 하는 자연로그 이다. 따라서 softmax에서 생기는 지수 e^x는 자연로그에 의해 상쇄되어, log_softmax(log_softmax(x)) = log_softmax(x) 이 된다.
이는 sigmoid에서도 동일하다. 즉, log_sigmoid(log_sigmoid(x)) = log_sigmoid(x) 이다.
즉 F.log_softmax(x)를 한 다음, nn.CrossEntorpyLoss = F.nll_loss(F.log_softmax(x))를 취할 경우, 아래와 같은 수식이 된다.
F.nll_loss(F.log_softmax(F.log_softmax(x))) = F.nll_loss(F.log_softmax(x)) 이므로, 결국 이 F.log_softmax를 사용해도 nn.CrossEntorpyLoss를 쓴것과 동일한 Loss가 계산된다.
다만 주의할 점은, F.log_softmax()를 사용한 output임을 인지하고서, 확률 계산시 exp()만 취하여 softmax prob을 계산해야한다는 점이 있다.
또한 이렇게 구현하더라도 실제 loss가 같은지 다른 사람들이 모를 수 있기 때문에, 가능하면 CrossEntropy보다는 직관적인 NLLloss를 쓰는게 좋다.
마지막으로 F.log_softmax + CE가 수학적으로는 그냥 CE와 동치일 수 있지만 softmax에 의해서 numerical error가 발생할 수 있기 때문에 가능하면 중복 연산은 피하는 게 좋다.
기본 nn.module의 RNN들에 구현 된 Dropout은 사용하면 안된다.
왜냐하면 RNN에 dropout을 적용하려면 time step이 흘러도 dropout mask가 바뀌지 않는 방식으로 구현되어야만 올바르게 동작한다.
그런데 GRU나 LSTM에 구현된 dropout은 mask가 매 time step마다 바뀌게 구현되어 있다고 한다.
따라서 직접 mask를 fix시켜서 구현해주거나, LockedDropout과 같은 모듈을 사용해야한다.
https://towardsdatascience.com/learning-note-dropout-in-recurrent-networks-part-2-f209222481f8
https://discuss.pytorch.org/t/dropout-in-lstm/7784
# nn.RNN모듈에 구현된 일반 Dropout
이미지 출처: https://nmhkahn.github.io/RNN-Regularizations
# LockedDropout == Variational Dropout == RNN Dropout
이미지 출처: https://nmhkahn.github.io/RNN-Regularizations
Network 파라미터 중 key가 똑같은 일부만 로딩하기
saved_state = state["conf.network"]
current_state = conf.network.state_dict()
for k, v in saved_state.items():
if k in current_state.keys():
current_state.update({k:v})
conf.network.load_state_dict(current_state)
nn.init
conv1 = nn.Conv1d(1, out_channels=32, kernel_size=60, stride=3, dilation=2) # groups
torch.nn.init.normal_(conv1.weight, mean=0, std=1/math.sqrt(conv1.weight.shape[0]))
https://stackoverflow.com/questions/49433936/how-to-initialize-weights-in-pytorch
nn.functional.pad
Padding mode에는 constant, reflect, replicate, circular 등이 있고, 많은 경우 0 value, constant mode 즉 zero-padding을 가장 많이 사용한다.
Reflection padding은 아래와 같이 가장 가까운 픽셀로부터 거울 상(원점을 중심으로 점대칭)으로 값을 복사해오는 것이다. 이러면 결과적으로 동일한 이미지가 한번 더 그려지게 된다
Replication padding은 아래와 같이 가장 가까운 픽셀의 값을 복사해오는 방식이다. 단, padding하는 위치가 멀어지면 그만큼 복사해오는 대상 픽셀의 위치도 멀어진다. 이렇게 하면 결과적으로 반전된 이미지가 새로 그려지게 된다.
data_loader에 pin_memory 사용
Data_loader의 get_item에서 heavy한 CPU load를 caching하기
def __init__(self):
self.data_list = defaultdict((lambda: (None, None)))
def __len__(self):
return int(len(self.wave_chunk_list) * self.conf.data_usage)
def __getitem__(self, index):
xy_pair = self.data_list[index]
if xy_pair[0] is not None and self.conf.data_caching:
x, y = xy_pair
else:
x = slow_cpu_load(x)
self
.data_list[index] = x,
yreturn
x,
y
Jit을 이용해서 Pytorch 모델을 다른 언어에서 serving하기
Script
일반적으로 쓸 수 있음
Trace
Automatic Mixed Precision(AMP)
이 실험에 따르면 일반적인 학습 속도가 2배 정도 빨라진다고 보고되었다.
pytorch 공식 AMP 예제를 참고하여 간단한 형태의 구현을 해본다.
from torch.cuda.amp import autocast, GradScaler
self.grad_scaler = GradScaler(self.conf.autocast)
with autocast(self.conf.autocast):
loss, exp_result = self.do_one_batch(cnt_i, batch_dict, e_step, run_mode)
self.grad_scaler.scale(loss).backward()
음 그런데 실험해보니, with autocast(): 안에서 forward만 사용하는 inference상황에서는 오히려 속도가 더 느려지는 현상이 있었다.
이는 아마도 fp16과 fp32를 변환하는 과정에서 속도저하가 있는 것으로 보인다. 이런 경우에는 model.half()로 모델 자체를 FP16으로 변환시켜놓고, 데이터만 매번 data = data.half()로 변환해서 넣어주면 느려지는 문제를 해결할 수 있다.(약간의 속도 개선이 된다.)
Mixed precision에서 속도가 느려지는 정확한 현상
Multi-GPU
용어
참고 예제
DataParallel
Multi-GPU를 사용하게되면 N개의 gpu로 동일한 모델이 카피되고, 데이터셋은 배치 dim을 따라서 N등분되어 들어가진다.
Batch-normalization에 의한 속도 저하
DDP vs DP
pytorch 이미지넷 예제
Data Parallel의 버그
Pytorch CUDA Stream
stream = torch.cuda.Stream(device=GlobalVar.Device)
with torch.cuda.stream(stream):
t = t.to("cpu", non_blocking=True).data.numpy()
torch.cuda.Stream을 사용하면 GPU <-> CPU 로 데이터를 전송할 때 non_blocking으로 데이터를 주고 받을 수 있다. 그래서 latency를 10~20% 정도 줄일 수 있는데, 다만 간헐적으로 아무런 값도 전달받지 못하고 그냥 0.0의 값만 전달받는 경우가 존재한다. 따라서 이에 대한 예외처리가 반드시 필요하다.
'A. Development > for Machine Learning' 카테고리의 다른 글
Numpy Optimization and Parallelization (0) | 2020.01.17 |
---|---|
Pandas (0) | 2018.06.29 |
Numpy 문법, API, 환경설정 / HDF5를 위한 H5PY API (0) | 2015.12.01 |
댓글