Category: Python

  • GPU 사용하기

    일단 컴퓨터에 장착한다. 나의 경우에는 메인보드의 VGA로 모니터에 연결해서 VGA에 따로 모니터를 연결하지는 않았다.

    NVIDA 홈페이지에 있는 방법대로 CUDA를 다운로드 한다.

    PyTorch 홈페이지에 가서 역시 CUDA를 이용한 PyTorch를 재설치한다. NVIDIA는 CUDA 11.1이고 PyTorch 11.0인데 일단 상관없이 잘 된다.

    배치 사이즈를 조절한다. 큰 숫자를 넣으면 메모리가 부족하다고 한다. 적절한 범위까지 꽤 낮추어 준다. 메모리가 잘 줄어들지 않으면 가장 간단하게 Notebook을 종료시켰다가 다시 실행한다. 충분히 줄어들어서 실행이 되면 기다린다. GPU만 쓰는게 아니라 중간에 CPU도 쓰는게 있다. 병행 작업은 너무 빡신것은 실행하지 않는 것이 좋을 것 같다.

    학습 속도가 엄청 빠르다.

  • Adam 최적화

    SGD건 Adam이건, 다른 것들이건 동작은 잘한다고 알려져 있다. 하지만, Adam이 왠만하면 먹히는 것이라고 하니 Adam을 이용해 보기로 한다.

    초보자 수준에서 SGD와 Adam의 가장 큰 차이는 학습률을 직접 조절하는 부분이 있는지에 대한 것일 것이다. SGD는 학습률 감소 부분을 설정해 주는 부분(scheduler)이 있어야 하지만, Adam에서는 없어도 된다. PyTorch에서 제공하는 Adam은 기본 값이 지정되어 있기 때문에, 별도로 입력해 주지 않아도 된다.

    https://pytorch.org/docs/stable/optim.html

    torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

    원래 학습 부분 코드에서 scheduler 부분을 주석 처리하여 학습시키면 된다.

  • DenseNet 전이 학습하기

    일단 전이 학습에 대한 한글 자료는 네이버에 많다. 여기서 전이 학습을 찾아보면 십중 팔구 PyTorch의 ants와 bees를 구별하는 글을 번역한 것을 볼 수 있다. 실제로 자기 컴퓨터에 맞게 수정한 글들도 있지만, 거의 복불 같은 느낌이다.

    https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html

    일단 이 코드를 기반으로 DenseNet에 대하여 해보면 오류가 발생한다. 처음에 한글로 된 자료를 찾지 못하여 영어 자료를 찾다가 다음의 글에서 해결책을 찾을 수 있었다. 그 전에 무엇이 문제였는지부터 알아보자.

    https://pytorch.org/tutorials/beginner/finetuning_torchvision_models_tutorial.html

    우선 ResNet 모델을 보면, 마지막 부분에 (fc)라는 부분이 있을 것이다.

    import torchvision
    model_ft = torchvision.models.resnet18(pretrained=True)
    print(model_ft)
    ----
        )
      )
      (avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
      (fc): Linear(in_features=512, out_features=1000, bias=True)
    )

    그래서 ResNet 전이학습에서는 이 부분을 연결하기 위해서 다음의 코드가 들어간다.

    num_ftrs = model_ft.fc.in_features

    그럼 DenseNet를 확인해 본다. 마지막에 classifier라고 되어 있는 것을 볼 수 있다.

    import torchvision
    model_ft = torchvision.models.densenet161(pretrained=True)
    ----
        )
        (norm5): BatchNorm2d(2208, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
      (classifier): Linear(in_features=2208, out_features=1000, bias=True)
    )

    그렇다면 classifier가 추가되어야 할 것이다. 위에서 언급한 링크에는 다음과 같이 예시되어 있다. 인터넷 자료에는 다른 classifier가 있기는 한데, 일단 공식(?) 자료는 간단하게 다음의 한 줄을 예로 들어 놓았다.

    model.classifier = nn.Linear(1024, num_classes)

    이제 실행해보면 될 것이다.

  • multiprocessing

    우선 내가 multiprocessing을 사용하는 것은 여러 이미지 파일에서 동일한 처리를 한 번에 많이 시행하기 위함이다. 우선 이를 위하여 이미지 처리하는 부분을 따로 분리하여 함수로 지정하였다.

    Pool 방법과 Process 방법이 있는데, Pool 방법이 나에게 더 맞는 것 같아서 이 방법으로 해보기로 했다. 그리고 Pool 방법에 map, async_map 등의 방법이 더 있다. 나는 병렬 처리 한 결과를 따로 받아서 처리하지 않기 때문에 코딩하기 쉬운 map 방법으로 선택하였다.

    import multiprocessing
    
    def find1(i):
        ~~~
    
    multiprocessing.Pool(os.cpu_count()).map(find1, files)
    

    os.cpu_count()는 현재 CPU의 코어 숫자를 확인하는 것이다. 메모리가 많이 필요한 분석은 아니기 때문에, 신속한 분석을 위해서 다 사용한다. files는 분석을 할 파일 목록들이다.

    믿을 수 없게 저 간단한 방법으로 동시에 6개 파일을 처리할 수 있다. Python에 대한 xkcd의 유명한 내용인 https://xkcd.com/353/ 이 생각났다.

    단일 코어를 사용하는 기존 방법 분석 때 제대로 확인 안했기는 하지만, 단일 파일 처리할 때와는 달리 core 에서 약 30~40% 정도는 system이 사용한다. 아마 60~70% 성능으로 6개를 분석하는 것이니까 대충 4배 정도의 속도 향상을 기대할 수 있을 것 같다. 하이퍼쓰레딩을 켜고 사용해 봤는데 40~50% 정도 사용에 12개 코어이다. 그런데 체감은 더 느린 것 같다. 그래서 다시 하이퍼쓰레딩을 껐다.

    내가 불필요한 정보를 계속 기억하게 했는지, 시간이 지나면서 메모리가 증가하는 문제가 생겼다. 일단 저 병렬처리 부분이 종료되면 메모리가 다시 원상태로 돌아온다. 그래서 한 번에 분석할 파일 목록을 줄여서 처리하는 방법으로 메모리 누수 문제를 해결했다.

    work_list_number = 400
    for i in range(0, len(files), work_list_number):
        work_list = files[i:i+work_list_number]
        multiprocessing.Pool(os.cpu_count()).map(find1, work_list)