개발자로 후회없는 삶 살기
[최적화] GPU 풀 구현기 2 본문
서론
사설망에서 서버 간 통신으로 GPU 풀을 이용하는 과정을 기록합니다.
본론
-> 깃허브
https://github.com/boostcampaitech6/level2-objectdetection-cv-05/pull/20
- 컨슈머 백 그라운드로 실행
컨슈머는 백 그라운드에서 터미널이 꺼져도 실행되어야 합니다. nohup으로 컨슈머를 먼저 실행하고 이후 훈련 실험할 메세지가 있을 경우 퍼블리셔에서 큐로 메세지를 푸시하게 됩니다.
컨슈머만 실행했을 경우 대기하고 있으며 퍼블리셔를 실행하니 학습 중 stdout이 나옵니다. 시스템 출력은 이후 로거를 활용하여 Cost를 제거할 것입니다.
🚨 컨슈머 동시 요청 문제 인지
1) 컨슈머 2대가 메세지를 waiting
2) 퍼블리셔에서 메세지 적재
문제 상황은 이러합니다. 이 경우 여러 대의 컨슈머가 동시에 하나의 메세지에 접근하여 두 개의 서버에서 하나의 메세지로 학습을 진행할 수도 있는 문제가 있습니다.
-> 상황 구현
2개의 컨슈머를 다른 프로세스로 실행시키고 메세지를 waiting 하고 있는 상황을 만들었습니다.
이때 퍼블리셔가 메세지 하나를 넣으면 두 컨슈머가 동시에 학습을 시작할 거라고 예상했습니다. 하지만 결과적으로 하나의 컨슈머에서만 메세지를 받고 메세지를 받은 순간 큐에는 더 이상 메세지가 없으니 다른 컨슈머는 waiting 하고 있었습니다.
퍼블리셔에서 메세지를 하나 더 넣으니 정상적으로 동작합니다. 이제 퍼블리셔에서 메세지를 원하는 실험 개수만큼 넣어놓으면 컨슈머에서 순서대로 메세지를 받아 학습을 진행하고 훈련이 끝나면 다음 메세지로 학습할 것입니다.
- 메세지 푸시 구현
앞서 테스트를 위해 퍼블리셔 코드에 메세지를 dict 타입 변수로 나타냈었습니다. 하지만 이제 1대의 퍼블리셔와 5대의 컨슈머가 원격 사설망에서 동작하고 AI 연구자가 퍼블리셔를 가지고 있는 게 아니기에 이제부터는 AI 연구자가 원하는 메세지를 직접 작성하고 퍼블리셔에게 전달하는 과정이 필요합니다.
-> 푸시 기능 구현
conn = redis.Redis(host="퍼블리셔 IP", port=퍼블리셔 redis port 번호, db=0, password = "redis 비밀번호")
sleep_time = 1
def run_queue():
# 원하는 메세지
config = {
"arch": {
"type": "CustomModel",
"args": {
"num_classes": 11
}
},
"data_loader": {
"type": "CustomDataLoader",
"args":{
"batch_size": 3,
"shuffle": False,
"num_workers": 2
}
},
"dataset": {
"type": "CustomDataset",
"args":{
"annotation" : "../dataset/train.json",
"data_dir" : "../dataset",
"resize" : 1024
}
},
"trainer": {
"type": "CustomTrainer",
"args":{
"epochs": 2,
"save_path": "./checkpoints/faster_rcnn_torchvision_checkpoints.pth"
}
}
}
threading.Thread(target=sender, args=(config, )).start()
def sender(config: dict):
time.sleep(sleep_time)
conn.rpush("config", json.dumps(config))
if(__name__ == "__main__"):
run_queue()
사용자가 원하는 메세지를 config 변수에 작성하고 메인 메서드를 호출하면 퍼블리셔에 있는 레디스 큐에 메세지가 적재되도록 할 것입니다.
🚨 Connection Refused
redis.conf 셋팅
redis password 셋팅
redis IP 외부 접속 가능한 상태로 셋팅
redis 포트 번호 셋팅
로컬에서 원격 서버로 접속하려고 하니 연결 오류가 발생했습니다. 해결한 과정은 위 순서이며, 서버 간 통신을 할 때 주의해야 할 부분을 알 수 있었습니다.
1. redis IP 외부 접속 가능한 상태로 셋팅
최초에 redis 서버는 127.0.0.1의 상태로 요청을 LISTEN 하고 있었습니다. 이는 내부 요청만 받을 수 있는 상태입니다. 따라서 외부 요청을 받을 수 있도록 Conf를 바꿔줘야 했습니다.
2. conf 셋팅
conf를 변경하였습니다. 하지만 netstat 결과가 동일했습니다.
환경 셋팅에 문제가 있다는 것은 설치부터 문제가 있을 수도 있습니다. warning을 보면 conf 파일이 셋팅이 안되어 있고
TCP 연결을 할 수 없을 거라고 합니다.
서버 설치
Conf 파일 생성
Conf 파일 수정 -> IP 변경 확인, 포트 번호 확인
따라서 설치부터 Conf 셋팅부터 다시 다 해주었으며, 이 과정에서 서버 환경 셋팅 순서를 알 수 있었습니다. 위 3개가 거의 모든 DB 서버 셋팅에서 기본이면 정확히 이루어졌는 지 IP 변경 확인, 포트 번호 확인 등으로 테스트 해봐야 합니다.
셋팅 결과 IP와 포트 모두 외부에서 접속 가능하게 된 것을 확인하였습니다.
-> 원격 통신 테스트
로컬에서 redis의 IP와 포트를 바꾸고 실행을 해보면 이전에는 연결 오류가 발생했는데 마지막 호출은 정상적으로 종료합니다.
conn = redis.Redis(host='localhost', port=30080, db=0, password = "비밀번호")
sleep_time = 0.5
컨슈머 코드도 변경한 후 실행을 해보면
로컬에서 받은 메세지를 통해 학습이 진행되는 것을 볼 수 있습니다.
- remote와 동기화
이제는 AI 개발자가 본인 소유의 GPU 서버를 한 대 소유하는 것이 아닌 공용으로 사용하는 것이기 때문에 코드를 동기화해야하는 요구사항이 발생하였습니다.
따라서, 클라이언트-서버 구조로 어플리케이션을 변경하였습니다. 로컬에서 작성한 코드를 원격으로 배포하고 서버에서 학습을 돌리기 전에 원격의 코드를 받아 동기화를 진행합니다. 이후 로컬에서 퍼블리셔에 메세지를 적재하면 컨슈머에서 수정된 코드와 메세지로 모델 학습을 진행합니다.
def train_process() -> None:
"""큐에서 메세지를 추출하여 모델 학습 수행"""
config: bytes = handler.pop_message()
if config is not None:
command = construct_config(config)
logger.info("학습 중 입니다.")
git_synchronize()
train_log = train(command)
git 동기화 이후 모델 학습을 진행하면, GPU를 공용으로 사용할 수 있습니다.
'[AI] > [딥러닝 | 이슈해결]' 카테고리의 다른 글
데이터 제작 PART.데이터 처리로만 모델 성능 향상시키기 (0) | 2024.01.26 |
---|---|
[최적화] 학습 속도 개선(AMP, Prefetch) (0) | 2024.01.18 |
MMLab PART.커스텀 파이프라인 제작 (0) | 2024.01.09 |
Augmentation PART.albumentation 활용 (2) | 2023.12.19 |
[최적화] GPU 풀 구현기 1 (0) | 2023.12.09 |