Practical Tips for Machine Learning Engineers
Linux
-
리눅스나 운영체제 관련된 많은 것들을 알고 있으면 가끔씩 도움이 됩니다.
- UFS, /dev/shm의 실제 디바이스, tmpfs, soft/hard link,
free
출력 결과, stdin/stdout/stderr/pipe 등
- UFS, /dev/shm의 실제 디바이스, tmpfs, soft/hard link,
-
swap memory
- 싼 AWS 인스턴스를 돌리거나 기타 등등 다양한 경우에서 램이 부족할 때, 디스크의 일부를 램 대신 쓸 수 있는 swap memory라는 게 있습니다. RAM이 부족해 안 돌아가는 경우를 명령어 몇 줄로 해결할 수 있으므로, 필요한 빈도는 높지는 않아도 필요한 경우는 이것 외에 더 간단한 해결책이 없습니다.
-
.zshrc
, PATH가 뭐고 무슨 역할인지 정도는 알아두기 -
다른 명령어 출력을 텍스트 형태로 다른 명령어에 넣어주는 법
echo `pwd` echo $(pwd) e.g. ln -s `pwd`/* /new_directory/
-
Useful commands
tail -f (your_log_file) # f refers to `follow`. 실시간으로 파일에 찍히는 로그를 관람할 때 유용합니다. ls | wc -l # counting file watch -n1 nvidia-smi # nvidia-smi every 1 sec ps aux # 모든 프로세스를 소유자 정보와 함께 출력. 여러 명이 같이 쓰는 클러스터에서 고부하 작업 돌리는 범인 찾기(?) 혹은 텐서보드 훔쳐보기(?)에 유용함 ps aux | more # --More--과 함께 나와서 스크롤이 가능하도록 출력 ps aux | grep something ps -ef # PPID(parent PID)와 함께 부모-자식 관계 보는 데 사용 top # htop, gtop 등 UI가 다른 것도 존재함 kill -9 (PID) (sleep 50; echo done1) & # & : background processing. 50초후 done1 출력하는 걸 background에서 돌림 du -sh * | sort -h # 현재디렉토리 파일/폴더 용량순정렬 df -lh # human readable하게 저장공간 용량 출력 free -lh # 현재 메모리 Mem,Low,High,Swap 출력.
-
쉘 스크립트는 코파일럿과 chatgpt에게 맡기기
Python basics
-
list comprehension을 잘 쓰자
-
zip, map, filter 같은건 잘 쓰면 쓸모있긴 한데 좀 가독성이 떨어진다고 느껴지긴 합니다. js랑 달리 list.map(fn)이 아니라 map(fn, list) 문법이라 그런가..
-
native 라이브러리인 typing과 mypy, pydantic 섞어쓰면 타입체킹에 좋습니다. enum도 비슷한 맥락에서 쓸모 있음
-
tempfile: 임시파일/폴더 필요할 때 쓸모있습니다. context manager 형태로 간단하게 쓸 수 있습니다.
-
itertools: product, combination, permutation, chain, batched 등 쓸모있는 게 많습니다. batched는 python 3.12부터 됩니다.
-
functools: partial, cache, cachedproperty, lru_cache 같은 거 갖다 바로 쓰기 좋습니다
- decorator을 만들 때
__name__, __doc__
을 옮겨주는 wraps라는 친구가 있습니다. https://stackoverflow.com/questions/308999/what-does-functools-wraps-do- name, doc 옮기는 게 무슨 쓸모인가 생각하실 수도 있는데 본인이 쓴 코드를 본인만 읽을 게 아니라면 읽을 사람이 많아지면 많아질수록 유용할 겁니다.
- decorator을 만들 때
-
pickle: pickle의 원리와 한계를 미리 알고 있으면 좋습니다
- 외부 라이브러리의 객체를 pickle했다가 외부 라이브러리 버전/소스코드가 바뀌면 어떻게 되는지 아시나요?
- c, cpp 관련된 객체가 왜 pickle이 안되는 경우가 있는지 아시나요?
-
contextlib
-
contextlib.contextmanager로 callable을 contextmanager로 만들 수 있고, contextlib.ContextDecorator로 contextmanager을 decorator로 만들 수 있습니다.
-
예시
# logging logger @contextmanager def simple_timed(name: str = "..."): torch.cuda.synchronize() t0 = time.time() yield torch.cuda.synchronize() logger.info(f"[{name}] done in {time.time() - t0:.3f} s", stacklevel=2)
-
-
logging으로 다른 라이브러리의 minimum logging level을 바꾸는 방법
logging.getLogger("filelock").setLevel(logging.INFO)
-
timeit으로 globals() 받는 법
def f(x): return x**2 def g(x): return x**4 def h(x): return x**8 import timeit print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))
-
deque 같은 자료구조 급하게 필요할 때 collections에서 갖다 쓰면 됩니다
-
decimal, fractions 같은 라이브러리는 다른 때는 쓸 때도 있었는데 MLE로서는 쓸 일이 거의 없는듯..
Python intermediate
기초적인 파이썬 문법 정도는 이미 알고 있다고 가정했을 때, 추가로 알면 좋은 것들은 다음과 같습니다.
- https://hyperconnect.github.io/2023/05/30/Python-Performance-Tips.html: 하이퍼커넥트 기술블로그에 올라온 글로 엔지니어링의 비중이 높은 태스크를 수행할수록 이 글의 내용은 매우 중요합니다. 정말 좋은 글이니 꼭 읽어보길 권장합니다.
- 상황에 따라 gc가 병목일 수 있다. 이런 경우 gc 발동 조건을 튜닝할 수 있다.
- Built-in list는 충분히 빠르지 않다. 필요시 array나 numpy를 사용하자.
- multiprocess는 커뮤니케이션 오버헤드가 높기에, low-latency 시나리오에서 조심히 사용해야한다.
- Pytorch를 multiprocess 환경에서 쓴다면 num_threads를 조정하자.
- Pydantic은 아주 느리다. 불필요한 곳에서 가급적 사용하지 말자.
- Pandas DataFrame은 생성에 많은 시간이 걸리므로, 유의해서 사용해야 한다.
- 바닐라 json 패키지는 느리다. orjson이나 ujson을 사용하자.
- Class는 충분히 빠르지 않을 수 있다. 너무 문제가 되면 dict를 사용하자.
- Python 3.11은 덜 느리다.
- (보너스) line profiler 사용법
- 추가로, Pydantic 경우 최근 (2023-06-30) 2.0 버전이 나오며 Rust를 사용해 1.0 버전보다 6배인가 8배인가 빨라졌다는 말을 pydantic docs에서 본 기억이 있습니다. 2.0이 나온 건 위 글이 써진 시점보다 이후로 보입니다.
- 특히 저는 주전공이 자연과학 계열인데, 여기에서는 numpy 써서 10000배 빨라질 코드에 python native list에 nested for loop로 다차원 배열 인덱싱해서 쓰는 코드를 자주 봅니다.
- numpy→python native list→numpy 같은 연산을 반복하지 마세요..
Pytorch
Pytorch는 머신러닝/딥러닝 전반에 있어 기초적으로 사용되는 매우 중요한 라이브러리입니다.
머신러닝 엔지니어로서 몰라도 되는 내용도 있으나, 다양한 상황에 유연하게 대응하기 위해서는 pytorch docs를 정독하고 있어두는 게 좋습니다.
- https://pytorch.org/docs/stable/data.html : 파이토치 쓰면서 여기 나오는 내용은 알고 있는지 한번쯤 확인해봅시다.
- pytorch memory/latency profiler 매우 좋습니다. 사용법도 파이토치 블로그에 적어두고 이거 써서 SAM/llama 최적화는 글도 파이토치 블로그에 있으니 참고하면 좋습니다.
- https://github.com/arogozhnikov/einops, https://github.com/dgasmith/opt_einsum
- einops를 잘 쓰면 인생이 편해질지도 모릅니다. 아님 말고..
데이터 처리
- (중요)일단 https://github.com/huggingface/datasets을 쓰면 많은 것들이 해결됩니다
- 특히 중~대규모 데이터일수록 유용
- parallel/batched map, filter, sharding 등등..
- pyarrow도 내부적으로 잘 쓰고 있어요
- 노드 하나에서 처리할 수 없을 정도 규모의 데이터라면 다른 방식이 필요할 것 같긴 합니다
- multi-node로 데이터셋을 ML 모델을 이용해 GPU를 이용해 처리할 일이 있을 때 https://github.com/huggingface/accelerate도 쓸만합니다
- 그냥 모델/데이터셋 accelerator.prepare로 감싸버리고 쉘에서 accelerate 실행하면 병렬처리가 됨
- pandarallel: parallel pandas apply
- cuDF
- GPU DataFrame library for loading joining, aggregating, filtering, and otherwise manipulating data
- 이미지 처리
- pil vs cv2: 저는 범용적으로 pil, 가끔 성능이 필요한 경우 cv2를 씁니다. pil로 1024x1024 이미지 png encode/decode하면 cv2보다 5배 이상 느려서 한 장에 581ms도 걸리는 걸 본 적이 있어요. 근데 이게 또 jpeg는 둘이 시간이 같아서 참 이상합니다.
- torchvision.transforms vs albumentations: albumentations는 PIL 대신 cv2 위주로 작업하며, 통상 서너배 이상 빠른 속도에 더 다양한 기능을 갖습니다.
병렬처리
- native multithread/multiprocessing
- i/o bound 상황은 multithread 기반, compute bound는 multiprocessing 기반을 쓰되, multiprocess는 프로세스를 만들 때 느리다는 것 등등을 알아둬야 합니다
- joblib
- https://github.com/niedakh/pqdm: Comfortable parallel TQDM using concurrent.futures
- https://github.com/ray-project/ray: 이 친구 병렬처리만 되는 게 아니라 여러가지 기능이 더 있습니다
ML Experiment Management
- Tensorboard, Neptune, Wandb 등
- wandb, neptune 모두를 모른다면 한번 찾아보시길 권장합니다
- config 관리
- argparse, dataclass, json/yaml 등 섞어쓰기
- omegaconf, hydra
- wandb에 config 저장하도록 하기 등
기타 라이브러리들
-
HTTP Client
- urllib, requests, aiohttp, https://github.com/projectdiscovery/httpx, https://github.com/geventhttpclient/geventhttpclient 등등
- https://github.com/geventhttpclient/geventhttpclient 는 triton inference server/client에서 쓰는 친구인데 뭐가 좋아서 쓰는건지는 저도 모릅니다
-
HTTP Server(ml model serving 위주로)
- 요새는 https://github.com/tiangolo/fastapi 가 국룰인 듯
- pydantic과 찰떡궁합
- ray serve
- pythonic하게 fastapi 이용해서 서빙하기
- triton inference server
- grpc, http 어떤 형식으로도 통신 가능하며, onnx/tensorrt/python 등 다양한 백엔드 지원
- 요새는 https://github.com/tiangolo/fastapi 가 국룰인 듯
-
performance/load testing
- triton inference server에 딸린 perf_analyzer가 빠르게 쓰기 쓸만합니다
-
콘솔을 멋있어 보이게 하는 것들
- https://github.com/Delgan/loguru
- logging보다 더 예쁘고 편리한 로거
- https://betterstack.com/community/guides/logging/loguru/#exploring-log-levels-in-loguru
- https://github.com/tqdm/tqdm
-
팁: pandas integration이 존재합니다.
import pandas as pd import numpy as np from tqdm import tqdm df = pd.DataFrame(np.random.randint(0, 100, (100000, 6))) # Register `pandas.progress_apply` and `pandas.Series.map_apply` with `tqdm` # (can use `tqdm.gui.tqdm`, `tqdm.notebook.tqdm`, optional kwargs, etc.) tqdm.pandas(desc="my bar!") # Now you can use `progress_apply` instead of `apply` # and `progress_map` instead of `map` df.progress_apply(lambda x: x**2) # can also groupby: # df.groupby(0).progress_apply(lambda x: x**2)
-
- https://github.com/Delgan/loguru
-
기타
-
https://github.com/ijl/orjson: native json보다 빠른 json 입출력
-
pybase64라고 native base64보다 빠른 친구도 있는데 효용이 얼마나 있는지는 모르겠습니다
-
https://github.com/google/python-fire: argparse보다 좋은(?) argparse
- Python Fire is a library for automatically generating command line interfaces (CLIs) from absolutely any Python object.
import fire def hello(name="World"): return "Hello %s!" % name if __name__ == '__main__': fire.Fire(hello)
DevOps (for MLE)
DevOps 엔지니어나 MLOps 엔지니어면 Ops쪽으로 알아야 할 게 정말 많은데, 여기는 일단 Ops 엔지니어가 아니더라도 알면 좋은 것들을 서술합니다.
- github workflow는 알고 있을거라 생각합니다
- pyproject.toml과 연관된 것들
- https://github.com/python-poetry/poetry: package dependency manager
- 디펜던시를 관리 안해주다시피하는 pip과는 다르게, 디펜던시가 꼬일 일이 없도록 철저히 관리해주는 디펜던시 매니저입니다. 사용법도 매우 간편합니다.
- 개인적으로 1.5.0버전에서 1.7.1버전으로 올렸을때 엄청난 속도 향상을 체감했습니다. 원래 240초 걸리던 poetry lock, poetry add 같은 명령어가 30초가 걸립니다.
- 근데 토치는 어떻게 해야 가장 좋은지 잘 모르겠어요! 토치 외에 다른 라이브러리 디펜던시 관리에는 매우 유용함.
- https://github.com/nat-n/poethepoet:
make
in pyproject.toml - black, isort, ruff: formatter, linter
- 특히 ruff: 기존의 formatter, linter의 기능을 대부분 가지고 있으며, 호환도 되고, rust 기반이라 매우 빠릅니다.
- https://github.com/python-poetry/poetry: package dependency manager
- pytest: for unittest
- https://github.com/python/mypy: for static type check
- 구버전 쓰면 서너배 느릴 수 있습니다. github workflow에서 타입체킹만 10분 걸리는 걸 볼 수 있음..
- 동적 타입체킹을 위한 라이브러리가 또 있다고 들었는데 까먹었습니다
- https://github.com/pre-commit/pre-commit
- https://github.com/pydantic/pydantic
개발 환경 관리하기
- zsh + ohmyzsh 조합: 예전에 nomadcoders에서 리눅스 기초, 블록체인 강의 듣다가 처음 알았던 것 같은데, zsh + ohmyzsh 설치해두고
theme="random"
달아두면매일매일 새로운 예쁜 터미널을 보며 좋아할 수 있습니다. - git, ssh는 당연히 아실텐데, git 유용한 팁 몇가지:
-
하나 이상의 커밋을 조작할 일이 생기면 git filter-branch를 잘 써보자. (”포크레인”으로 git-scm에서는 비유합니다.)
-
모든 commit에서 파일 제거
-
https://git-scm.com/book/ko/v2/Git-도구-히스토리-단장하기#_git_amend 의 하단부에 아래 코드가 나옵니다.
$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) Ref 'refs/heads/master' was rewritten
-
모든 commit에서 author name, email 수정 (이런게 존재한다는 사실만 미리 알아두고 나중에 ChatGPT한테 물어보고 복붙하면 될 듯..?)
$ git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; then GIT_AUTHOR_NAME="Scott Chacon"; GIT_AUTHOR_EMAIL="schacon@example.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
-
https://rtyley.github.io/bfg-repo-cleaner/ : git filter-branch를 간략화한 친구입니다
- Removing Crazy Big Files
- Removing Passwords, Credentials & other Private data
-
-
마지막 커밋에 빠트린 파일이 있을 때 stage한 다음에 amend하면 됩니다. 같은 이치로 직전 커밋 수정하고 싶으면 다 add하고 amend하면 됩니다.
git add forgotten_file git commit --amend
-
배포할 때 git tag를 잘 쓰면 편리합니다
-
git lfs migrate: 이미 여러번 커밋에 올라간 특정 파일을 뒤늦게 git lfs로 관리하고 싶을 때 편리합니다. 해당 파일이 포함된 모든 커밋을 수정해주거든요.
-
IDE(VSCode)
-
Useful VSCode Extensions
- python/jupyter, copilot, indent-rainhow, Material Theme, Everforest(제가 쓰는 테마), black/ruff, path intellisense(소스코드에서 경로입력 도와줌)
-
User settings.json 설정해서 파일 저장하면 자동 포매팅되게 하기
- 개인적으로, vscode-ruff에 formatter args가 생기면 모두 ruff로만 할 예정
- 없는 이유는 포매팅에 커맨드라인 인자를 아예 다 빼버린다고 디자인을 했었기 때문으로 추정(지금은 line-length만 롤백 상태)
"notebook.codeActionsOnSave": { // "source.fixAll": true, "source.organizeImports": true }, "notebook.formatOnSave.enabled": true, "[python]": { "editor.codeActionsOnSave": { "source.fixAll.ruff": true, "source.organizeImports.ruff": true }, // "editor.defaultFormatter": "charliermarsh.ruff", "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true }, "black-formatter.args": ["--line-length=119"], "ruff.lint.args": ["--line-length=119"], "python.analysis.autoFormatStrings": true, "python.analysis.completeFunctionParens": true,
- 개인적으로, vscode-ruff에 formatter args가 생기면 모두 ruff로만 할 예정
가상환경
anaconda
- anaconda 대신 miniconda를 씁시다
- 프로젝트별로 다른 virtual env를 쓰는 게 best practice라고 생각하고, 그럴 경우 base env는 쓸일이 없는데, anaconda는 base env가 무겁습니다
- base env가 아닌 다른 env에서 conda install을 했을 때도 solving environment를 느려지게 하는 주범도 무거운 base env라고 추측하고 있습니다.
- 설치파일 용량만 봐도 아나콘다는 1기가 미니콘다는 100메가입니다..
- mamba, micromamba라는 c로 anaconda를 구현한 친구들도 있지만 사실 pip이 아닌 conda로 패키지 설치할 일이 많지는 않아서 miniconda를 쓰는 것만으로도 필요성을 느낀 적은 없습니다
- https://www.anaconda.com/blog/a-faster-conda-for-a-growing-community: libmamba라는 게 있는데, mamba를 conda의 env solver로 사용할 수 있게 만들어줍니다. 근데 나중에 확인하니 이거 2023.10 버전부터 default solver로 전환되었습니다.
- environment 재현하는 작은 꿀팁
- 하나의 디스크를 공유해서 쓰는 경우처럼, 동료
abc
의 가상환경에 직접적으로 접근이 가능한 경우, 3초 들여서conda create -n myenv —clone /home/abc/miniconda3/envs/myenv
치고 몇초 기다리는게 훨씬 이득일 수도 있습니다.- conda env export 같은 명령어 쳤다가, conda 외부의 것들이 얼마나 다르냐에 따라 재현이 안되는 경우도 잦거든요.
- 또는 심지어 source 명령어를 동료 home에 있는 환경에 대고 쳐도 됩니다
- 하나의 디스크를 공유해서 쓰는 경우처럼, 동료
poetry
- poetry도 가상환경을 가지고 있습니다. 잘 쓰면 프로젝트별로 자동으로 환경이 관리되서 매우 편리합니다
- 참고) poetry는 가상환경을 만들어도 python executable은 symlink를 걸어 씁니다.
- 일부 라이브러리를 빠르게 찍먹하거나(특히 주피터 노트북으로 실험하는 경우) 버전을 빠르게 갈아끼우면서 테스트를 해야하는 경우, poetry add/remove를 쓰지 말고 그냥 pip install uninstall을 하는 게 더 속편할 때도 있습니다. 실험은 pip로 하고, 나중에 커밋이나 배포 올릴때만 poetry로 디펜던시 안 꼬이게 정리하면 됩니다.
ChatGPT
- 정규식, SQL, 쉘 스크립트 짜기, 기타등등을 시키면 잘합니다
사실 코드는 ChatGPT/Copilot이 하고 MLE는 copy&paste만 잘 하면 되는 게 아닐까..?
가끔 인생 살다 필요해서 급하게 쓸만한 ML thing
- https://github.com/facebookresearch/nougat: pdf to latex
- https://segment-anything.com/demo: 인물 사진에서 누끼 딸 때 급하게 쓸만함
- https://github.com/openai/whisper: 급하게 영상 자막 만들 때 쓸만함
- .smi 파일로 export하는 옵션이 있어요
- 대부분의 Text-to-Text task가 급하게 필요할 때: ChatGPT, openai api
기타 MLE들이 범용적으로 읽으면 좋을법한 것들
- https://www.cs.princeton.edu/~smalladi/blog/2024/01/22/SDEs-ScalingRules/
- huggingface blog
- e.g. MoE 설명, qlora finetuning 방법 등 특히 huggingface library와 large-model 관련된 많은 자료들이 존재합니다.
- pytorch blog
- e.g. SAM 같은 모델 분석해서 n배 빠르게 만들기, 트리톤 등