Python Multiprocessing(Process) 사용한 데이터 처리 속도 개선

 

·       Version : MAC OS, Python 3.X, PIP3

 

대용량 데이터를 효율적으로 처리하기 위해서는 병렬 처리를 활용하는것이 좋다.  파이썬에서 병렬처리를 제공하는 대표적인 라이브러리는 Threading Multiprocessing 모듈이다. Threading 모들은 파이썬의 GIL(Global Interpreter Lock)라는불리우는 잠금 모델을 사용하기 때문에I/O 작업이 아닌 CPU 작업이 많을 경우 오히려 성능이 저하된다. 방식은 Lock 풀고 스레드를 교환하고 다시 Lock 거는 형태의 멀티스레드이기 떄문이다.

파이썬에서는 Multiprocessing 권장하고 있으며,   모듈에는 대표적으로 Pool Process 있지만 이번 글에서는 Process 대해서 다루기로 한다.

 

·       Pool 사용한 처리 속도 개선 : https://sungwookkang.com/1478

 

Process 하나의 프로세스에 하나의 함수를 할당하여 실행한다. Target= 파라메터에 작업을 할당하고, args=(agr1, ) 인수를 할당하여 프로세스 객체를 생성한다. start() 프로세스를 시작하여 join()으로 프로세스의 종료를 기다린다.

import os

import multiprocessing as mp

from multiprocessing import Pool, Process

import threading

import time

import datetime

 

def multiprocess():

    start = int(time.time())

 

    ojbect_list = []

    for i in range(1,12):

        task = Process(target=work_func, args=(i,))

        ojbect_list.append(task)

        task.start()

 

    for task in ojbect_list:

        task.join()

   

    end = int(time.time())

    print("***run time(sec) : ", end-start)   

   

    print("Number of Core : " + str(mp.cpu_count()))

   

 

def work_func(x):

    print("time : " + str(datetime.datetime.today()) +  " value :" + str(x)  + " PID : "  + str(os.getpid()))

 

if __name__ == '__main__':

# execute only if run as a script

   

    multiprocess()

 

 

코드를 실행한 결과를 살펴보면 실행마다 다른 프로세스(PID 각각 다름)에서 실행된것을 있다.

 

Pool Process 차이점은, Pool 경우 실행되어야 작업이 코어수 만큼 분할되고 코어수 만큼 프로세스가 생성되어 힐당받은 작업을 처리하는데, Process 경우 작업마다 새로운 프로세스가 할당되어 작업을 처리한다.

 

 

2021-03-02/ Sungwook Kang / http://sungwookkang.com

 

파이선파이썬, python, 병렬처리, Multiprocessing, Python Multiprocessing, 파이썬 병렬처리, 데이터 처리, Data Processing

 

Python Multiprocessing(Pool) 사용한 데이터 처리 속도 개선

 

·       Version : MAC OS, Python 3.X, PIP3

 

대용량 데이터를 효율적으로 처리하기 위해서는 병렬 처리를 활용하는것이 좋다. 대부분의 머신러닝/딥러닝에 사용되는 프레임워크들은 함수 내부에서 병렬처리가 가능하도록 설계되어 있기 때문에 시스템의 자원을 효율적으로 사용하지만, 일반적으로 많이 사용되는 데이터 가공 모듈인 pandas 같은 모듈은 병렬처리를 기본적으로 제공하지 않기 떄분에 별도의 병렬처리가 가능하도록 코딩을 해야한다.

 파이썬에서 병렬처리를 제공하는 대표적인 라이브러리는 Threading Multiprocessing 모듈이다. Threading 모들은 파이썬의 GIL(Global Interpreter Lock)라는불리우는 잠금 모델을 사용하기 때문에I/O 작업이 아닌 CPU 작업이 많을 경우 오히려 성능이 저하된다. 방식은 Lock 풀고 스레드를 교환하고 다시 Lock 거는 형태의 멀티스레드이기 떄문이다.

파이썬에서는 Multiprocessing 권장하고 있으며,   모듈에는 대표적으로 Pool Process 있지만 이번 글에서는 Pool 대해서 다루기로 한다.

 

아래 실습코드는 1에서 12까지 숫자를 1 간격으로 출력하는데, for 문을 사용한 싱글처리와 Pool 사용하여 병렬 처리를 하였을때의 처리 시간 할당된 프로세스를 확인한다.

import os

import multiprocessing as mp

from multiprocessing import Pool

import threading

import time

import datetime

 

def non_multiprocess():

    print("non multiprocess")

    start = int(time.time())

   

    for i in range(1,12):

        work_func(i)

 

    end = int(time.time())

 

    print("Number of Core : " + str(mp.cpu_count()))

    print("***run time(sec) : ", end-start)   

 

def multiprocess():

    print("non multiprocess")

 

     #멀티 프로세싱을 위한 CPU 숫자 확인 만들기

    num_cores = mp.cpu_count()

    pool = Pool(num_cores)

 

    start = int(time.time())

 

    ojbect_list = []

    for i in range(1,100):

        ojbect_list.append(i)

    #멀티 프로세싱 워커 호출

    pool.map(work_func, ojbect_list)

 

    end = int(time.time())

 

    #메모리 방지 위해 사용

    pool.close()

    pool.join()

 

    print("Number of Core : " + str(mp.cpu_count()))

    print("***run time(sec) : ", end-start)   

 

def work_func(x):

    print("time : " + str(datetime.datetime.today()) +  "value :" + str(x)  + " PID : "  + str(os.getpid()))

   

    time.sleep(1)

 

if __name__ == '__main__':

# execute only if run as a script

    #non_multiprocess()

    multiprocess()

   

 

 

코드를 실행하면 아래와 같은 결과를 확인할 있다. non multiprocess 라고 되어 있는 결과는 for문을 사용한 싱글 프로세스이며, 개의 프로세스(PID : 49650) 사용되었으며, 순차적으로 실행되어 12초의 시간이 소요된 것을 확인할 있다. Multiprocess 경우 동시에 6개의 프로세스(필자의 테스트 컴퓨터는 6core이다.) 할당되었으며 PID 다른것을 확인할 있다. 전체 실행시간은 2초가 소요된 것을 확인할 있다.

 

간단한 테스트에서는 Multiprocessing 대한 효과를 크게 느끼지 못할수도 있으나 데이터가 기하급수적으로 커질때에는 데이터 처리 속도에 따른 시간이 엄청나게 차이난다.

 

 

2021-03-01/ Sungwook Kang / http://sungwookkang.com

 

파이선파이썬, python, 병렬처리, Multiprocessing, Python Multiprocessing, 파이썬 병렬처리, 데이터 처리, Data Processing

 

파이썬과 OpenCV 이용한 성별 연령 판별하기

 

·       Version : Python 3.X, OpenCV

 

파이썬과 OpenCV 라이브러리를 활용하여 성별 연령을 판별해 보도록 한다. 이번 포스트에서는 프로그램 코드에 대한 설명이나 알고리즘에 대한 설명은 다루지 않는다. 참고 사이트에서 다운로드 받은 소스를 단순히 실행하고 결과를 얻음으로써, 이미지 분석에 대한 흥미를 유발하는데 목적이 있다.

 

CV (Computer Vision) 컴퓨터가 인간처럼 디지털 이미지와 비디오를 보고 식별할 있게 하는 연구분야 이다. Computer Vision 디지털 이미지를 수집, 처리, 분석 이해하여 실제 세계에서 고차원적 데이터를 추출하여 결정에 사용할 있는 기호 또는 숫자 정보를 생성한다. 과정에는 물체 인식, 비디오 추적, 모션 추정 이미지 복원과 같은 사례가 포함된다.

Open CV Open Source Computer Vision 약자이다. 라이브러리는 실시간 이미지 비디오를 처리하는 동시에 분석 기능을 제공한다. 딥러닝 프레임워크인 TensorFlow, Caffe PyTorch 지원한다.

이번 프로젝트에서 사용되는 알고리즘은 CNN (Convolutional Neural Network)으로 이미지 인식 처리 NLP 목적으로 사용되는 DNN(Deep Neural Network)이다. ConvNet이라고도하는 CNN에는 입력 출력 레이어와 여러 개의 숨겨진 레이어가 있으며 다수는 컨볼루션이다. 실습에 사용된 파이썬 프로젝트의 CNN 3개의 컨볼루션 레이어가 있다.

·       Convolutional layer; 96 nodes, kernel size 7

·       Convolutional layer; 256 nodes, kernel size 5

·       Convolutional layer; 384 nodes, kernel size 3

 

각각 512개의 노드가 있는 2개의 완전히 연결된 레이어와 softmax 유형의 최종 출력 레이어가 있다. 이번 프로젝트에서는 아래와 같은 분석 결과를 도출한다.

·       얼굴 감지

·       남성 / 여성으로 분류

·       8 가지 연령대 하나로 분류

·       결과를 이미지에 놓고 표시

 

프로젝트에는 Adience 데이터셋을 사용한다. 데이터셋은 얼굴 사진의 벤치 마크 역할을 하여 노이즈, 조명, 포즈 모양과 같은 다양한 실제 이미징 조건을 포함한다. 이미지는 Flicker 앨범에서 수집되어 CC(Creative Commons) 라이선스에 따라 배포되었다. 8가지 연령대((0 – 2), (4 – 6), (8 – 12), (15 – 20), (25 – 32), (38 – 43), (48 – 53), (60 – 100)) 2,284명의 피사체에 대한 26,580장의 사진이 있으며 크기는 1GB 이다. 프로젝트에서 사용된 모델은 데이터셋을 사용하여 훈련되었다. (그래서인지 동양인에 대한 나이 예측을 하지 못한다.)

 

프로젝트를 실행하기 위해서는 파이썬이 설치되어 있어야 하며, 아래 패키지를 PIP 사용하여 설치 있도록 한다.

pip install opencv-python

 

프로젝트를 다운로드 한다.

·       Download : https://drive.google.com/file/d/1yy_poZSFAPKi0y2e2yj9XDe1N8xXYuKB/view)

 

프로젝트에는 아래와 같은 파일로 이루어져 있다.

·       opencv_face_detector.pbtxt : 얼굴을 감지 하기 위한 파일. 텍스트 형식

·       opencv_face_detector_uint8.pb : 얼굴을 감지 하기 위한 파일. 이진 형식

·       age_deploy.prototxt : 연령에 대한 네트워크 구성 설정

·       age_net.caffemodel : 연령에 대한 레이어 매개 변수의 내부 상태를 정의

·       gender_deploy.prototxt :성별에 대한 네트워크 구성

·       gender_net.caffemodel : 성별에 대한 레이어 매개 변수의 내부 상태를 정의

·       a few pictures to try the project on

 

프로그램을 실행하여 사진속의 인물 성별과 연령이 맞는지 확인해 본다. (필자의 실험에서는 사진에 필터가 적용되어 있거나, 포샵을 과하게 한경우 정확한 인식을 하지 못하였다.)

python gad.py --image woman1.jpg

 

다운로드 받은 프로젝트에 포함되어 있는 woman1.jpg 결과.

 

 

다운로드 받은 프로젝트에 포함되어 있는 girl1.jpg 결과.

 

 

 

필자의 얼굴을 Snow 앱을 사용하여 뽀샤시 필터를 적용한 결과

 

 

[참고자료]

·       Interesting Python Project of Gender and Age Detection with OpenCV : https://data-flair.training/blogs/python-project-gender-age-detection/?fbclid=IwAR1d43CEOg-5dnzxPTxE06uB60leOzBKe30bRZewVvsM88V7v6TmTN9GDYI

·       Adience Benchmark Gender And Age Classification : https://www.kaggle.com/ttungl/adience-benchmark-gender-and-age-classification

 

 

 

2020-03-02/ Sungwook Kang / http://sungwookkang.com

 

 

 

파이썬, Python, 얼굴인식, 파이썬 이미지 처리, 이미지 프로세싱, 연령 분석, 성별 분석, 이미지 분석, Convolutional, OpenCV, Computer Vision, 머신러닝, 딥러닝, Neural Network

  1. omty 2020.03.24 23:33 신고

    15~20세... 항상 많이 배우고 갑니다 좋은 글 감사합니다~

  2. Rena 2020.12.31 17:02

    많이 배우고 갑니다. 다만 얼굴을 인식하는 창 크기를 조절하고 싶은데 처음보는 구조라서 잘 모르겠네요.
    전체화면으로 하고 싶은데..
    알려주실 수 있으신가요?

Build an image and run it as one container

 

·         Version : Docker

 

문서는 Docker 공식 문서를 번역한 내용이며 필자의 생각과 의견이 반영되어 있습니다. 자세한 내용은 원문을 참고 바랍니다.

 

Introduction

Docker 방식으로 앱을 제작할 차례이다. 응용 프로그램은 계층 구조 아래부터 시작한다. 이번 포스트에서 다루는 응용 프로그램 내용은 컨테이너이다. 레벨 위에는 컨테이너가 프로덕션 환경에서 작동하는 방식을 정의하는 서비스가 있다. 마지막 최상위 단계는 스택이며 다루는 모든 서비스의 상호 작용을 정의한다.

Stack

Service

Container (이번 포스트는 여기)

 

 

Your new development environment

과거에는 Python 응용프로그램을 작성하기 위해 가장 먼저 하는 번째 순서는 Python 런타임을 시스템에 설치하는 것이었다. 그리고 이러한 프로그램이 정상적으로 작동하려면 개발 컴퓨터 환경과 프로덕션 환경이 일치해야 하는 상황이 필요하다.

Docker 사용하면 이식 가능한 Python 런타임을 이미지로 가져올 있으며 설치가 필요 없다. 그런 다음 빌드는 코드와 함께 기본 Python 이미지를 포함 있으므로 종속성 런타임이 함께 이동한다. 휴대용 이미지는 Dockerfile이라고 하는 무언가에 의해 정의된다.

 

 

Define a container with Dockerfile

Dockerfile 컨테이너 내부의 환경을 정의한다. 네트워킹 인터페이스 디스크 드라이브와 같은 리소스에 대한 액세스는 시스템의 나머지 부분과 격리 되어있는 환경에서 가상화 되므로 포트를 외부에 매핑하고 사용자가 설정한 파일을 복사하여 환경을 구성한다. 작업을 수행한 후에는 Dockerfile 정의된 앱의 빌드가 어디서나 똑같이 작동한다고 기대할 있다.

 

Dockerfile

디렉터리를 만든다. 디렉터리 경로를 새로운 디렉터리로 변경( cd) 하고 Dockerfile이라는 파일을 작성한 다음 내용을 해당 파일에 복사하여 붙여 넣고 저장한다. 아래 코드는 Dockerfile 문장을 설명하는 주석이 함께 기록되어 있다.

# Use an official Python runtime as a parent image

FROM python:2.7-slim

 

# Set the working directory to /app

WORKDIR /app

 

# Copy the current directory contents into the container at /app

COPY . /app

 

# Install any needed packages specified in requirements.txt

RUN pip install --trusted-host pypi.python.org -r requirements.txt

 

# Make port 80 available to the world outside this container

EXPOSE 80

 

# Define environment variable

ENV NAME World

 

# Run app.py when the container launches

CMD ["python", "app.py"]

 

Dockerfile 아직 작성하지 않은 가지 파일, app.py requirements.txt 포함한다. 다음 단계에서 해당 파일을 만들어 본다.

 

The app itself

Dockerfile 같은 폴더에 requirements.txt app.py 파일을 두개 만든다.  위의 Dockerfile 이미지에 내장되면 app.py copy 명령으로 인해 app.py requirements.txt 존재하고 EXPOSE명령으로 HTTP 통해 app.py 출력에 액세스 있다.

 

[requirements.txt]

Flask

Redis

 

[app.py]

from flask import Flask

from redis import Redis, RedisError

import os

import socket

 

# Connect to Redis

redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

 

app = Flask(__name__)

 

@app.route("/")

def hello():

    try:

        visits = redis.incr("counter")

    except RedisError:

        visits = "<i>cannot connect to Redis, counter disabled</i>"

 

    html = "<h3>Hello {name}!</h3>" \

           "<b>Hostname:</b> {hostname}<br/>" \

           "<b>Visits:</b> {visits}"

    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

 

if __name__ == "__main__":

    app.run(host='0.0.0.0', port=80)

 

이제 pip install -r requirements.txt Python Flask Redis 라이브러리를 설치하고 app socket.gethostname() 대한 호출 결과와 함께 환경변수 NAME 인쇄하는지 확인한다. 마지막으로 Redis 실행되지 않기 때문에 (Redis 자체가 아닌 Python 라이브러리만 설치했기 때문에) 여기에서 사용하려는 시도가 실패하고 오류 메시지가 나타난다.

참고 : 컨테이너 내부에서 호스트 이름에 액세스하면 실행중인 실행 파일의 프로세스ID 같은 컨테이너ID 검색된다.

 

 

Build the app

우리는 앱을 만들 준비가 되었다. 디렉터리의 최상위 레벨에 있는지 확인한다. ls명령어를 실행하였을 여기에 아래 그림과 같이 표시되어야 한다.

ls

 

 

이제 빌드 명령을 실행한다. 이렇게 하면 Docker 이미지가 생성되며 이미지는 -t 태그를 사용하여 사용자에게 친숙한 이름을 부여할 있다.

docker build . -t friendlyhello

 

 

내장된 이미지는 어디에 있을까? 컴퓨터의 로컬Docker이미지 레지스트리에 있다.

Docker images

 

 

Run the app

응용프로그램을 실행하여 -p 사용하여 컴퓨터 포트 4000 컨테이너의 게시 포트 80 매핑한다.

docker run -p 4000:80 friendlyhello

 

 

 

Python http://0.0.0.0:80에서 앱을 제공한다는 메시지가 표시되어야 한다. 하지만 메시지는 컨테이너 내부에서 들어 오는데 컨테이너의 포트 80 4000으로 매핑했는지 모르므로 실제 테스트에서는 올바른 URL http://localhost:4000  사용 한다. 브라우저에서 해당URL 이동하여 페이지에 표시된 디스플레이 내용을 확인한다.

 


참고 : Windows7에서 Docker Toolbox 사용하는 경우 localhost 대신 Docker Machine IP 사용한다.

Ex ) http://192.168.99.100:4000/ IP 주소를 찾으려면 docker-mchine ip 명령을 사용한다.

 

 

쉘에서도 curl 명령을 사용하여 동일한 내용을 있다.

$ curl http://localhost:4000

 

<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>

 

4000:80 포트 재매핑은 Dockerfile내의 EXPOSE docker run -p publish 값이 설정된 값의 차이를 보여준다. 나중 단계에서 호스트의 포트 4000 컨테이너 포트 80 매핑하고 http://localhost 사용. CTRL+C 눌러 종료한다.

 

Windows에서는 명시적으로 컨테이너를 중지한다. Windows 시스템에서 CTRL+C 컨테이너를 중지하지 않는다. 따라서 CTRL+C 입력하여 프롬프트를 다시 얻거나 다른 쉘을 다음 실행중인 컨테이너 목록을 표시하려면 docker container ls 입력하고 컨테이너를 중지하려면 docker container stop <Container NAME or ID> 입력한다. 그렇지 않으면 다음 단계에서 컨테이너를 다시 실행하려고 시도할 데몬에서 오류 응답을 받는다.

 

이제 백그라운드에서 분리 모드로 앱을 실행해 본다,

docker run -d -p 4000:80 friendlyhello

 


터미널에서는 앱에 대한 컨테이너 ID 나타낸다. 컨테이너가 백그라운드에서 실행 중이다. docker container ls 명령을 사용하여 축약된 컨테이너 ID 수도 있다. ( 명령을 실행 상호 교환 적으로 작동한다.)

docker container ls

 

 

Container ID http://localhost:4000 있는것과 일치한다. 다음과 같이 Container ID 사용하여 프로세스를 종료하려면 docker container stop 사용한다.

docker container stop 58427d7fd3dc

 

 

Share your image

방금 만든 이미지의 이식성을 보여주기 위해 빌드 이미지를 업로드하고 다른 곳으로 옴긴다. 결국 프로덕션에 컨테이너를 배포하려는 경우 레지스트리로 푸시하는 방법을 알아야 한다. 레지스트리는 저장소 모음이며 저장소는 이미 만들어진 코드를 제외하고는 Github 저장소와 같은 종류의 이미지 모음이다. 레지스트리의 계정은 많은 리포지토리를 생성할 있다. docker CLI 기본적으로 Docker 공개 레지스트리를 사용한다.

참고 : Docker 공용 레지스트리는 무료이며 미리 구성되어 있기 때문에 여기에서 사용한다. 그러나 선택할 있는 공용 레지스트리가 많으며 Docker Trusted Registry 사용하여 개인 레지스트리를 설정할 수도 있다.

 

 

Log in with your Docker ID

Docker 계정이 없다면 hub.docker.com 가입한다. 로컬 시스템의 Docker 공용 레지스트리에 로그인 한다.

docker login

 

 

Tag the image

로컬 이미지를 레지스트리의 저장소와 연관시키는 표기법은 username/repository:tag이다. 태그는 선택사항이지만 Docker 이미지에 버전을 제공하는데 사용하는 메커니즘이기 때문에 권장된다. 저장소에 제공하고 get-started:part2 같이 컨텍스트에 의미있는 이름을 태그한다. 그러면 이미지가 get-started 저장되고 part2 태그가 지정된다. 이제 이미지를 모두 태그를 추가한다. Docker tag image 원하는 대상에 업로드되도록 사용자 이름, 저장소 태그 이름으로 docker tag image 실행한다. 명령 구문은 다음과 같다.

Syntax : docker tag image username/repository:tag

Ex : docker tag friendlyhello gordon/get-started:part2

 

docker images 실행하면 새로 태그가 추가된 이미지를 있다.

docker images

 

 

Publish the image

태그가 지정된 이미지를 저장소에 업로드 한다. 완료되면 업로드 결과가 공개된다. Docker Hub 로그인하면 끌어오기 명령과 함께 이미지가 표시된다.

docker push username/repository:tag

 

 

 


Pull and run the image from the remote repository

이제부터는 다음 명령어로 모든 컴퓨터에서 docker run하고 앱을 실행할 있다.

docker run -p 4000:80 username/repository:tag

 

 

이미지가 로컬 컴퓨터에서 사용할 없는 경우 Docker 저장소에서 이미지를 가져온다.

 

Docker run 되는 위치에 관계 없이 Python requirements.txt 모든 종속성과 함께 이미지를 가져와서 코드를 실행한다. 모든 것이 깔끔한 작은 패키지로 함께 진행되므로 Docker 호스트 시스템을 실행하기 위해 호스트 시스템에 아무것도 설치할 필요가 없다.

 

Recap and cheat sheet [optional]

이번 포스트에서 다룬 내용은 아래와 같다.

동영상 : https://asciinema.org/a/blkah0l4ds33tbe06y4vkme6g

 

아래는 이번 포스트에 사용된 Docker 명령어 목록이다.

docker build -t friendlyhello .  # Create image using this directory's Dockerfile

docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80

docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode

docker container ls                                # List all running containers

docker container ls -a             # List all containers, even those not running

docker container stop <hash>           # Gracefully stop the specified container

docker container kill <hash>         # Force shutdown of the specified container

docker container rm <hash>        # Remove specified container from this machine

docker container rm $(docker container ls -a -q)         # Remove all containers

docker image ls -a                             # List all images on this machine

docker image rm <image id>            # Remove specified image from this machine

docker image rm $(docker image ls -a -q)   # Remove all images from this machine

docker login             # Log in this CLI session using your Docker credentials

docker tag <image> username/repository:tag  # Tag <image> for upload to registry

docker push username/repository:tag            # Upload tagged image to registry

docker run username/repository:tag                   # Run image from a registry

 

 

[참고자료]

https://docs.docker.com/get-started/part2

 

 

2018-12-20 / Sungwook Kang / http://sqlmvp.kr

 

 

containerspythoncodecodingbuildpushrun

'SW Engineering > Docker' 카테고리의 다른 글

Docker Stack  (0) 2019.03.26
Docker Swarms  (0) 2019.03.26
Docker Services  (0) 2019.03.26
Build an image and run it as one container  (0) 2019.03.26
Docker Orientation and Setup  (0) 2019.03.26
Docker란 무엇인가?  (0) 2019.03.26

+ Recent posts