Keras vs TF vs PyTorch
- Computational Graph의 차이가 큼. 그 외에는 거의 비슷함.
Naver boostcamp aitech 강의영상에서 가져옴
라는 식이 있다면
-TF (Define and Run) : 그래프를 먼저 정의하고, 실행 시점에 데이터를 feed
- PyTorch (Define by Run, Dynamic Computational Graph) : 실행을 하면서 그래프를 생성 -> 즉시 확인 가능 -> Pythonic Code
- 그 외에 TF는 production과 scalability에 장점이 있어서 serving 하는 데에 많이 쓰인다고 함 (+mlops)
- 그리고 PyTorch는 GPU support, Good API and community -> 사용하기 편한 장점이 가장 큼.
PyTorch Operation
- PyTorch = numpy + AutoGrad
- Tensor : 다차원 arrays를 표현하는 PyTorch 클래스. 사실상 numpy.ndarray와 동일. -> 생성함수도 거의 동일
- 다른 점은 gpu를 쓸 수 있는 tensor는 무엇이 있느냐의 차이.
torch.reshape vs torch.view
- view는 형태만 새로이 함. memory는 그대로 가져감. 그래서 새로운 값을 넣으면 값이 변함.
- reshape는 새로운 값을 넣어도 값이 안 변함. 즉, copy를 하는 것.
- view 쓰는 것을 권장
torch.squeeze vs torch.unsqueeze
Naver boostcamp aitech 강의영상에서 가져옴
torch.matmul vs torch.mm
- torch에서는 행렬곱이 .dot이 아닌 .matmul or .mm으로 실행함.
- .dot은 스칼라곱, 벡터곱에서는 됨. 반면 .mm은 스칼라곱, 벡터곱은 연산이 안 되고, 행렬곱에서만 됨.
- .mm은 broadcasting이 안 되고, .matmul은 자동으로 broadcasting이 됨.
a= torch.rand(5, 2, 3) b = torch.rand(3) a.mm(b) => Error a.matmul(b) => output : (5, 2, 1)
PyTorch의 다른 기능들
- torch.gather
- torch.index_select
- torch.is_tensor
- torch.numel
- torch.from_numpy
- torch.zeros
- torch.zeros_like
- torch.chunk
- torch.swapdims
- torch.Tensor.scatter_
이외에도 엄청 많음.. 적재적소에 필요한 것을 사용하는 능력이 필요할 듯..
torch.nn의 기능
- torch.nn.Linear()
- torch.nn.Identity() : 조건에 따라 어떤 layer를 실행하고자 할 때 더 간결히 쓰기 위함?
- torch.nn.LazyLinear() : forward call이 있기 전까지는 uninitialized 된 상태이다가 forward call이 생기면 그제서야 initialize하고, nn.Linear와 같은 기능을 함.
- torch.nn.ModuleList / ModuleDict : 일반적인 list, dict를 쓰면 왜 안될까?
AutoGrad
- .backward 함수 사용.
- requires_grad = True (거의 기본적으로 default값)
프로젝트 구조 이해하기
- 영원히 세발 자전거(Jupyter)를 탈 수 없다.
- 프로젝트 개발 초기 단계에서는 jupyter가 유리하지만 배포 및 공유에서는 notebook 공유가 어려움 -> 쉬운 재현의 어려움, 실행순서 꼬임.
- .py 로 프로젝트를 배포하는 능력이 필요함.
colab - local 연결하기 (ngrok)
- ngrok 회원가입 or 로그인
- get started -> setup -> connect your account의 authtoken 값 복사
- colab 환경에서 authtoken을 넣어주고, password 지정
NGROK_TOKEN = '{authtoken 값}' PASSWORD = '{사용자 정의 password}' from colab_ssh import launch_ssh launch_ssh(NGROK_TOKEN, PASSWORD)
- OUTPUT으로 HOSTNAME, Port 가 나옴.
- VSCODE 에서 ctrl+shift+p -> remote-ssh:Add New SSH Host 선택
ssh root@{HOSTNAME} -p {Port}
- 입력 후 config 지정
- ctrl + shift + p -> remote-ssh:Connect to Host 선택
nn.Module 이란
- 딥러닝을 구성하는 layer의 base class
- Input, Output, Forward, Backward 정의해야 함.
- 학습의 대상이 되는 parameter(tensor) 정의해야 함.
nn.Parameter
- Tensor 객체의 상속 객체
- nn.Module 내에 attribute가 될 때는 required_grad = True로 지정되어 학습대상이 되는 Tensor
- 우리가 직접 지정할 일은 거의 없음. 대부분 layer에는 weights 값들이 지정되어 있음.
- Parameter vs Tensor : Parameter는 미분의 대상이 됨. Tensor는 그렇지 않음.
Backward
- Layer에 있는 Parameter들의 미분을 수행
- Forward의 결과값과 실제값 간의 차이에 대해 미분을 수행
- 해당 값으로 Parameter 업데이트
- AutoGrad가 되어 직접 미분을 할 필요는 없지만 순서는 이해해야 함.
Dataset Class
- 데이터 입력 형태를 정의
- 데이터 입력 방식의 표준화
- Image, Text, Audio 등에 따라 다른 입력 정의
- 데이터 형태에 따라 각 함수를 다르게 정의함 (__init__, __getitem__,__len__)
- 모든 것을 데이터 생성 시점에 처리할 필요 없음 -> image의 Tensor 변화는 학습에 필요한 시점에 변환.
DataLoader Class
- Data의 Batch를 생성해주는 클래스
- 학습직전 (GPU feed 전) 데이터의 변환을 책임
- Tensor로 변환, Batch 처리가 메인 업무
- 병렬적인 데이터 전처리 코드의 고민 필요
- 참고 자료
학습 결과 공유 및 저장
- model.save()
- 모델 형태와 파라미터를 저장
- 모델 학습 중간 과정의 저장을 통해 최선의 결과모델 선택
- 모델 공유하여 재연성 올리기
Checkpoints
- 학습의 중간 결과를 저장하여 최선의 결과를 선택
- earlystopping 기법 사용 시 이전 학습의 결과물을 저장
- loss / metric 값을 지속적으로 확인 저장
Pretrained Model / Transfer Learning
- 다른 데이터셋으로 만든 모델을 현재 데이터에 적용
- 일반적으로 대용량 데이터셋으로 만들어진 모델의 성능이 좋음
- 현재의 DL에서는 가장 일반적인 학습 기법
- backbone architecture가 잘 학습된 모델에서 일부분만 변경하여 학습을 수행함
- CV는 torchvision / NLP는 huggingface 에 pretrained model이 많음.
- Freezing : 모델의 일부분을 frozen 시킴.
Monitoring tools - Tensorboard
- 학습 그래프, metric, 학습 결과의 시각화 지원
- PyTorch도 연결 가능 -> DL 시각화 핵심 기본
- scalar : metric등 상수 값의 연속을 표시
- graph : 모델의 computational graph 표시
- histogram : weight 등 값의 분포를 표현
- image : 예측 값과 실제 값을 비교 표시
- mesh : 3d 형태의 데이터를 표현하는 도구
from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter(logs_base_dir) for n_iter in range(num_iterations): writer.add_scalar('Loss/train', train_loss, n_iter) writer.add_scalar('Loss/test', test_loss, n_iter) writer.add_scalar('Accuracy/train', train_acc, n_iter) ...# tensorboard 실행 %load_ext tensorboard %tensorboard --logdir {logs_base_dir}
Monitoring tools - WandB
- 머신러닝 실험을 원활히 지원하기 위한 상용도구
- 협업, code versioning, 실험 결과 기록 등 제공
- MLOps의 대표적인 툴로 저변 확대
- 공부해보자!
a = torch.zeros(5, 6)
print(a)
>>> tensor([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.]])
for i in range(5):
for j in range(6):
print(a[i][j].data_ptr(), end = ' ')
print()
>>> 113793280 113793284 113793288 113793292 113793296 113793300
113793304 113793308 113793312 113793316 113793320 113793324
113793328 113793332 113793336 113793340 113793344 113793348
113793352 113793356 113793360 113793364 113793368 113793372
113793376 113793380 113793384 113793388 113793392 113793396
print(a.is_contiguous())
>>> True
print('='* 50)
>>> ============================================================
b = a.t()
print(b)
>>> tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
for i in range(6):
for j in range(5):
print(b[i][j].data_ptr(), end = ' ')
print()
>>> 113793280 113793304 113793328 113793352 113793376
113793284 113793308 113793332 113793356 113793380
113793288 113793312 113793336 113793360 113793384
113793292 113793316 113793340 113793364 113793388
113793296 113793320 113793344 113793368 113793392
113793300 113793324 113793348 113793372 113793396
print(b.is_contiguous())
>>> False
print('='* 50)
>>> ============================================================
c = a.t().reshape(30)
print(c)
>>> tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
for i in range(30):
print(c[i].data_ptr(), end = ' ')
print()
>>> 113793408 113793412 113793416 113793420 113793424 113793428 113793432 113793436 113793440 113793444 113793448 113793452
... 113793456 113793460 113793464 113793468 113793472 113793476 113793480 113793484 113793488 113793492 113793496 113793500
... 113793504 113793508 113793512 113793516 113793520 113793524
print(c.is_contiguous())
>>> True
print('='* 50)
>>> ============================================================
d = a.reshape(30)
print(d)
>>> tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
for i in range(30):
print(d[i].data_ptr(), end = ' ')
print()
>>> 113793280 113793284 113793288 113793292 113793296 113793300 113793304 113793308 113793312 113793316 113793320 113793324
... 113793328 113793332 113793336 113793340 113793344 113793348 113793352 113793356 113793360 113793364 113793368 113793372
... 113793376 113793380 113793384 113793388 113793392 113793396
print(d.is_contiguous())
>>> True
print('='* 50)
>>> ============================================================
a[0,0] = 100.
print(a)
>>> tensor([[100., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0.]])
print(b)
>>> tensor([[100., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
print(c)
>>> tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
print(d)
>>> tensor([100., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
contiguous는 deepcopy이고, reshape는 할 경우 deepcopy를 지원하지 않고, 메모리 주소까지 그대로 복사해간다.
근데 .t().reshape()의 경우 deepcopy이면서 contiguous하고, .reshape()는 메모리 주소를 복사해가지만 contiguous하다.
contiguous와 deepcopy를 같은 개념이라고 생각했는데 (정확히는 포함관계라고 생각했는데) .t()의 유무에 따라 메모리 주소를 복사할 수도 있고, 안 할 수도 있는 것이 이해가 잘 안 갔다.
나중에 질문에 대한 답이 오면 그 후에 해답을 쓰겠다
열심히 써놨는데 저장 안 돼서 날라감,,, 대충만 듣고 느꼈던 점을 정리하겠다..
겉으로 보았을 때 아무 문제 없고, 포트폴리오만 보았을 때 하고자 했던 것을 다 이룬 사람이라고 보이는 변성윤 마스터님도 그 속엔 많은 고민들과 불합격이 가득했던 나와 같은 평범한 사람이었다.
그 중에 나와 다르다고 생각했던 것은 하고 싶은 것을 하는 것, 남들의 시선에 구애받지 않는 것, 나 자신을 정확히 이해하는 것, 시간이 유한하다는 것을 알고 있지만 그 시간마저 나 자신을 위해 투자할 수 있는 것. 많은 부분이 다르다.
생각해보니 이 4가지를 모두 가지고 있었기 때문에 유기적으로 이것들이 시너지를 발휘하지 않았을까 싶다.
나 역시도 이번 부스트캠프의 목표를 내가 무엇을 하고 싶고, 무엇을 해야 잘 할지 생각하고 결정하는 것으로 삼았는데, 이를 위해서는 메타인지가 굉장히 중요한 것 같다. 부스트캠프의 목표가 하나 더 생겼다. 우선 내가 무엇을 좋아하고 어떤 것을 할 때 행복해하고, 스트레스 받는 사람인지 이해하자. 그래야지 남들이 뭐라 하든 간에 내가 확신을 갖고 나의 길을 걸을 수 있을 것이라 생각한다.

변성윤 마스터님 PPT에서 발췌함.
개선을 어떻게 할까? 정확한 TODO를 세운다.
도움되는 사이트
Multi-GPU
- Single vs Multi
- GPU vs Node(System) : Node는 컴퓨터라고 생각하자
- Single Node Single GPU
- Single Node Multi GPU (우리가 가장 많이 접하는 상황)
- Multi Node Multi GPU (ex. 서버실)
Model Parallel
- 다중 GPU에 학습을 분산하는 두 가지 방법 : 모델을 나누기 / 데이터를 나누기
- 모델을 나누는 것은 생각보다 예전부터 썼음 (AlexNet)
- 모델의 병목, 파이프라인의 어려움 등으로 인해 모델 병렬화는 고난이도 과제
AlexNet 모델 구조, 부스트캠프 강의 영상에서 발췌함
위의 사진 처럼 모델 병렬화를 하면 도움이 안됨. 아래의 사진처럼 잘 겹치면서 병렬화를 해야 병렬화의 의미가 있음. 부스트캠프 강의 영상에서 발췌함
Data Parallel
- 데이터를 나눠 GPU에 할당 후 결과의 평균을 취하는 방법
- mini batch 수식과 유사한데 한 번에 여러 GPU에서 수행
- PyTorch - DataParallel : 단순히 데이터를 분배한 후 평균을 취함 -> GPU 사용 불균형 문제 발생, Batch 사이즈 감소 (평균을 취해주는 한 GPU가 병목), GIL(Global Interpreter Lock)
- PyTorch - DistributedDataParallel - 각 CPU마다 process 생성하여 개별 GPU에 할당 -> 기본적으로 DataParallel로 하나 개별적으로 연산의 평균을 냄
Hyperparameter Tuning
- 요즘은 그럴 일 없지만 예전엔 hyperparameter가 조금만 달라져도 성능에 큰 차이가 생기기도 함
- Grid search vs Random search vs Bayesian Search
Hyperparameter Tuning Tool - Ray
- Multi Node Multi Processing 지원 모듈
- ML/DL의 병렬 처리를 위해 개발된 모듈
- 기본적으로 현재의 분산병렬 ML/DL 모듈의 표준
- Hyperparameter Tuning을 위한 다양한 모듈 제공
# config 지정 config = {"l1" : tune.sample_from(lambda _:2 ** np.random.randint(2, 9)), "l2" : tune.sample_from(lambda _:2 ** np.random.randint(2, 9)), "lr" : tune.loguniform(1e-4, 1e-1), "batch_size" : tune.choice([2, 4, 8, 16])} # Scheduler 지정 # ASHAScheduler : 일정 epoch마다 가망 없는 hyperparameter 조합은 버리고 진행함. scheduler = ASHAScheduler(metric = "loss", mode = "min", max_t = max_num_epochs, grace_period = 1, reduction_factor = 2) # Reporter 지정 : log 기록을 어떻게 할 지 정함 reporter = CLIReporter( metric_columns = ["loss", "accuracy", "training_iteration"]) # ray run result = tune.run( partial(train_cifar, data_dir = data_dir), resources_per_trial = {"cpu" : 2, "gpu" : gpus_per_trial}, config = config, num_samples = num_samples, scheduler = scheduler, progress_reporter = reporter)
PyTorch Troubleshooting - OOM
- OOM은 왜, 어디서 발생했는지 알기 어려움
- Error backtracking이 이상한 데로 감
- 메모리의 이전상황의 파악이 어려움
가장 많이 사용하는 해결법
- Batch Size 낮추고, GPU cache clean, Kernel Restart, 그리고 Run
- 이런 방법으로 해결되면 괜찮음
- 그 외에 발생할 수 있는 문제가 있음.
ex. iteration마다 GPU memory가 줄어드는 현상
다른 해결법 - GPUUtil 사용하기
- nvidia-smi처럼 GPU 상태를 보여주는 모듈
- iteration 마다 메모리가 늘어나는지 확인
!pip install GPUUtil import GPUUtil GPUUtil.showUtilization()
다른 해결법 - torch.cuda.empty_cache() 사용하기
- 사용되지 않는 GPU상 cache를 정리
- 가용 메모리를 확보
- del 과는 구분이 필요
- reset 대신 쓰기 좋음
다른 해결법 - iteration에 tensor로 축적되는 변수 확인하기
- tensor로 처리된 변수는 GPU 메모리 사용
- 해당 변수 loop 안에 연산이 있을 때 GPU에 computational graph 생성 -> 메모리 잠식
total_loss = 0 for i in range(iterations): optimizer.zero_grad() output = model(input) loss = criterion(output, target) loss.backward() optimizer.step() total_loss += loss # >>> loss는 tensor # >>> loss.item() 을 사용해야 GPU 메모리 사용 X
다른 해결법 - 그 외
- del 명령어를 적절히 사용하기
- 가능 batch 사이즈 실험해보기
- inference 시 torch.no_grad() 사용하기
- colab에서 너무 큰 사이즈의 모델 실행 하지 않기 (Linear, CNN, LSTM)
- tensor의 float precision을 16bit로 줄이기
예상치 못한 에러 메세지
- CUDNN_STATUS_NOT_INIT : GPU 설치가 잘못 되었거나 안 되었을 때 발생
- device-side-assert : OOM과 비슷한 에러
- How to clean GPU memory after a RuntimeError?
- FREQUENTLY ASKED QUESTIONS about GPU Error
- 이유를 알 수 없는 GPU 에러 정리
강의를 다 들어서 과제 정리를 좀 해보려 하는데 다른 페이지에 할거다. 그거 쓰면 링크 넣어놓겠음!
코딩을 못하면 ML/DL이 어렵나요?
- 성능을 올리기 위한 모델 싸움은 정리 중
- 그 모델을 어떻게 사용할 것인가, 어떻게 산업에 적용할 것인가가 더 중요하다.
- 즉, 모델 개발 / hyperparameter tuning 싸움
Issue - Data
- 실제 ML 프로젝트에서는 양질의 데이터 확보가 관건, 데이터는 누가 만들어주지 않고, 내가 만들어 쓴다..!
- Production time 데이터와 Experiment 데이터가 다른 문제도 발생
- 끊임없이 데이터를 관리하고 확보하는 노력이 필요
- User Generated Data: inputs, clicks for recommendation
- System Generated Data : logs, metadata, prediction
- Data Flywheel (데이터 선순환) : 사용자들이 참여로 데이터를 개선
- Data Augmentation : 데이터를 임의로 추가 확보
- Data Drift : 시간이 지나면서 데이터는 계속 바뀐다! -> 양질의 데이터를 구하거나 데이터에 강건한 모델을 만들거나..
- 테슬라 vs 웨이모 : Tesla가 훨씬 데이터가 양질이기 때문에 더 좋다. 테슬라는 유저가 데이터를 만들어주고, 웨이모는 시뮬레이션(NERF?)으로 데이터를 만듦
Data Feedback Loop
- 사용자로부터 오는 데이터를 자동화하여 모델에 피딩해주는 체계가 필요
- 단순히 ML/DL 코드로만 이루어지는게 아님 -> 네트워크 하드웨어 부터 데이터 플랫폼까지의 이해가 필요
- 앞으로의 많은 ML/DL 엔지니어가 가져야할 역량 중 하나
- 특히 대용량 데이터를 다뤄본 경험이 중요할 것
앞으로 알아야 할 것들
- MLOPs
- Database
- Cloud-AWS, GCP, Azure
- Spark (+Hadoop)
- Linux + Docker + 쿠버네티스
- 선택 : 스케줄링 도구들 (쿠브플로우, MLFlow, AirFlow)
하나의 시스템으로서 ML/DL 개발
- 단순히 모델을 잘 만드는 것이 아닌, 그 앞 뒤의 과정이 중요한 시대
ML/DL 기획 - 역시 잘 정리된 DATA는 많지 않다
- 앞으로 ML/DL 시스템은 다양한 영역에서 확장
- 데이터의 체계적 수립 (Feedback loop 체계 수립)
- 디지털화 but 데이터화 되어 있지 않은 데이터화
- MLOPs 엔지니어와 함께 AI 기반 시스템 설계 인력의 필요성이 증대
- 모델은 AutoML을...
정리
- 앞으로 알고리즘 연구자보다 ML/DL 엔지니어의 필요성이 더 증대
- 단순히 ML/DL 코드 작성을 넘어서 자동화, 데이터와 연계, 실험결과를 기반으로 설득, 시스템화 (Digital Transforamtion / AI Transformation)
- 좋은 엔지니어이자 좋은 기획자적인 요소들이 필요 -> 아직 AI화 되지 않은 영역의 AI화 (데이터를 어떻게 먹일 것인가!)
- Sheel Script, 네트워크, 클라우드 등 기본적으로 알아야 할 것들이 많음
느낀 점
- 단순한 모델링을 잘하는 엔지니어는 이제 더 이상 필요하지 않구나
- 기획을 할 줄 알고 빈틈을 팔 줄 알아야 한다.
- 계속해서 느껴왔지만 Linux, Docker, AWS는 기본으로 해야한다. 그 외에도 해야 할 것이 많구나..!
- 최성철 교수님 재밌다. 수업 들어보고 싶다..!