Category: Python

  • 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)

  • Telegram API를 이용하여 나에게 메세지 보내기

    ncube.net/telegram%ED%85%94%EB%A0%88%EA%B7%B8%EB%9E%A8-%EB%B4%87%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EB%A9%94%EC%84%B8%EC%A7%80-%EC%A0%84%EC%86%A1%ED%95%98%EA%B8%B0/

    vmpo.tistory.com/85

    이 두 개의 홈페이지 내용을 바탕으로 Telegram에서 나에게 메시지 발송하는 것 성공했다. 쉽네. 카카오톡은 뭔가 손이 많이 가는데
    Telegram은 매우 간단하게 할 수 있다.

    @BotFather에게 말을 건다.
    그 다음 /newbot를 선택한다.
    대화창 이름을 고르고 나서 Bot 이름을 입력한다.
    그럼 BotFather가 길다란 메세지와 함께 HTTP API: 로 시작하는 Token을 표시하여 준다.

    https://api.telegram.org/bot[Token]/getUpdates

    웹 브라우져에서 대괄호는 빼고 bot 글자에 붙여서 Token을 입력한다.

    {"ok":true,"result":[]}
    

    이렇게 나오면 성공이다. 그 다음 Bot과 한 마디 해준다. 그 다음 위의 웹 페이지에 새로 고침을 해본다. 이 예시는 인터넷에 있는 것이다.

    {"ok":true,"result":[{"update_id":314515826,"message":{"message_id":2,"from":{"id":4200110535,"is_bot":false,"first_name"
    

    “id”: 로 시작하는 숫자가 내가 나의 bot과 이야기하는 chat_id이다.

    https://api.telegram.org/bot[Token]/sendmessage?chat_id=4200110535&text=good
    

    이렇게 접속하면 good이라는 메세지가 bot을 통하여 나에게 전송되는 것을 알 수 있다. Python을 이용하여 메세지를 전송하려면 pip를 통하여 python-telegram-bot를 설치한다.

    pip install python-telegram-bot

    다음의 내용으로 코드를 짠다.

    import telegram
    bot = telegram.Bot(token = 'Token')
    bot.sendMessage(chat_id = '4200110535', text="안녕하세요")

    이것을 실행하면 나에게 메세지가 온다.

    Linux라면 curl을 이용하여 보낼 수 있다.

    curl -X POST -H 'Content-Type: application/json' -d '{"chat_id": "4200110535", "text": "This is a test from curl by BSJ"}' https://api.telegram.org/bot[Token]/sendMessage

    그런데 이렇게 기~~인 명령문을 여러 프로그램에 계속 넣는 것도 좋은 방법은 아닌 것 같다. 내용만 정해두면 해당 메세지가 발송되도록 하는 것이 좋을 것 같았다. 알아두어야 하는 것은 bot.sh 뒤에 입력되는 구절을 스크립트에서 받도록 하는 것이고, 이것을 수 많은 따옴표가 있는 구문에 넣는 방법이다.

    #! /bin/bash
    message=${1}
    curl -X POST -H 'Content-Type: application/json' -d '{"chat_id": "1086690584", "text": "'"${message}"'"}' 

    단어 한 개를 보낼 경우에는 따옴표가 없어도 된다. 그런데 두 단어 이상이라면 큰 따옴표나 작은 따옴표로 묶어야 한다. 이렇게 첫 번째로 입력되는 값은 ${1}로 받을 수 있다. 그 다음의 문제는 -d 옵션 이후의 따옴표 처리. 보통의 예시는 큰 따옴표로 되어 있는데, 저렇게 ${message} 부분만 작은 따옴표를 큰 따옴표로 묶는 방법을 이용하면 편하게 적용 가능하다.