Flask로 웹서버를 임시로 기동하면

WARNING : This is a development server. Do not use it in a production deployment.

Use a production WSGI server instead.

라는 문구를 볼 수 있다.

 

개발환경과 같이 단 건의 요청을 처리하는데는 flask를 써도 상관없지만

상용과 같이 high load 환경에서 flask는 분산처리가 제대로 되지 않아 요청을 처리하는데 실패하거나, 처리가 지연돼서 read timeout이 발생하기 쉽다.

 

이러한 문제를 해결하기 위해 nginx와 WSGI 서버를 사용하는 것이 정석이다.

나의 경우 API 서버라서 nginx를 사용하지 않고 WSGI 서버만 사용하고싶었다.

 

다른 출처에서는 gunicorn으로 flask의 app.py를 한번 더 감싸는 방법을 소개하는데,

flask의 app.py를 gunicorn 모듈로 실행하는 방법도 있다.

명령어는 

gunicorn --workers=1 --threads=4 --bind 0:5000 --timeout=0 file_name:app

이고, 

nohup 명령어를 이용해서 백그라운드로 실행할 수도 있다.

 

Flask나 Locust와 같이 웹서버를 기동하는 경우 flask run 이나 locust 명령어 실행시 특정환경에서

Fatal error in launcher: Unable to create process using "실행경로"와 같이 나오는 경우가 있다.

 

이 경우 python -m 이라는 명령어 (모듈로 인식)를 쓰면 정상적으로 동작한다.

[sklearn] cython 코드 수정하기

 

sklearn 패키지의 일부 알고리즘은 속도를 위해 c언어 기반의 python으로 작성되어있다.

일반적으로 이 수준의 알고리즘을 변경할 일이 거의 없어서 그런지 site-package에 있는 코드는 이미 컴파일이 된 상태라서 수정이 불가능했다. 환경은 ubuntu이고 python은 3.8에서 테스트하였다.

 

수정을 하기 위해서는

1. sklearn 의 깃 저장소에서 컴파일되지 않은 코드 _cd_fast.pyx 파일을 다운받는다.

2. 컴파일된 파일과 같은 경로에 추가한다.

3. _cd_fast.pyx 를 입맛대로 바꿔서 다시 컴파일한다.

 

3. 의 과정을 자세히 설명하자면,

우선, pyx 파일을 컴파일하기 위해 cython이 설치되어 있어야한다.

그리고 setup.py 파일을 만들어 pyx 파일을 컴파일하는 코드를 추가해야한다.

import numpy
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("_cd_fast.pyx"),
    include_dirs=[numpy.get_include()]
)

이 코드를 실행시키는 코드는 아래와 같으며, cython 문서를 참조하면 된다.

python setup.py build_ext --inplace

 

코드 수정 과정에서 발생한 에러와 해결방법은 아래와 같다.

 

not allowed without gil error

cython에서는 gil (global interpreter lock) 이라는 cpu processing 방식이 존재하는데 코드 중 with nogil 이 있는곳에 코드를 임의로 수정하는 경우 아래와 같은 에러가 발생할 수 있다. 이 경우 with nogil의 코드블럭을 주석처리하면 에러가 사라진다. 

 

gcc failed with exit status 1

컴파일 과정에서 꽤 자주 발생할 것 같은 에러이다. gcc가 설치 되어있다는 가정하에 아래와 같은 에러가 뜬다면 코드를 추가하여 해결할 수 있다.

from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("_cd_fast.pyx")
)

import numpy
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("_cd_fast.pyx"),
    include_dirs=[numpy.get_include()]
)

include_dirs=[numpy.get_include()]를 추가하면 에러를 해결할 수 있다.

 

[SimPy] 파이썬 시뮬레이션: 기초 알아보기

 

 

1. Environment

Environment 클래스를 객체화(선언)하는 것은 새로운 게임을 생성하는 것과 같다.

 

import simpy

env = simpy.Environment()

 

위와 같이 simpy의 Environment 클래스를 인스턴스화 하여 environment를 정의한다.

 

2. Process Interaction

Process는 다른 프로세스를 기다리거나 중간에 interrupt를 걸 수 있게 하는 env의 기능이다. 

import simpy

env = simpy.Environment()
car = Car(env)
env.run(until=15)

이 차는 전기차여서 충전시간이 필요하다. 충전은 charge()라는 함수로 구현하고 '5'라는 충전시간이 지나면 차는 출발한다.

class Car(object):
    def __init__(self, env):
    	self.env = env
        self.action = env.process(self.run())
        
    def run(self):
    	charge_duration = 5
    	yield self.env.timeout(self.charge(charge_duration))
    	
    def charge(self, duration):
    	yield self.env.timeout(duration)

운전자가 충전을 다 기다리지 않고 출발을 원한다면 어떻게 구현해야 할까? 충전은 run() 함수 안에서 돌아가므로, run()에 interrupt를 거는 함수를 만들 필요가 있다.

 

2-1 Interrupting another process

interrupting을 구현하기 위해 driver라는 함수를 만들었다.

def driver(env, car):
    yield env.timeout(3)
    car.action.interrupt()

 이 함수는 클래스 내부에서 선언하지 않았다. 이 함수는 car라는 클래스 인스턴스를 파라미터로 받아 env.process를 interrupt 한다.

import simpy

env = simpy.Environment()
car = Car(env)
env.process(driver(env, car))
env.run(until=15)

 

3. Resources

리소스란 사용 가능한 자원이다. 예를 들어, 충전기가 5개인 충전소에 여러대의 차들이 충전을 하러 오는 상황을 생각해 볼 수 있다. 그리고 일반적인 카페와 같이 만들어서 파는 생산자-소비자 관계가 있을 수 있다.

def car(env, name, station, driving_time, charge_duration):
	yield env.timeout(driving_time) # 차가 돌아다니다가 들어올 시간
    
    print(f'{name} 차가 충전소에 {env.now}에 도착했습니다.)
    with station.request() as req:
    	yield req
        
        print(f'{name} 차가 충전을 시작한 시간 {env.now}')
        yield env.timeout(charge_duration)
        print(f'{name} 차의 충전이 완료된 시간 {env.now}')
        
import simpy
env = simpy.Environment()
station = simpy.Resource(env, capacity=2)

for i in range(4):
	env.process(car(env, f'Car {i}', station, i*2, 5))

env.run()    

 

Resource클래스를 station으로 인스턴스화했고 Resource 인스턴스는 request() 함수를 사용할 수 있다. 이 함수는 리소스인 station이 다시 사용가능해질 때까지 기다리게 한다. station의 리소스 총량은 capacity 값으로 정할 수 있다.

여기서 충전소가 차에게 리소스를 제공하는 형태는 FIFO(first in first out) 방식이다.

   

[python] pip install - urlopen error [SSL: CERTIFICATE_VERIFY_FAILED]


When I installed egenix-mx-base package, urlopen error occurred like below.



I managed to find out the solution. It is not to upgrade pip...


You can see the download url in the picture. It starts with https://~, but you should modify this url to http://


So, "pip install http://downloads.egenix.com/python/egenix-mx-base-3.2.9-py2.7-win32-prebuilt.zip"


Then the package will be installed! 


[Tensorflow] 텐서플로우 자료형


텐서플로우에서는 일반적으로 파이썬에서 하듯이 x = 3 이렇게 변수를 정의하면 안된다.


텐서플로우의 자료형


상수형(Constant)


import tensorflow as tf


a = tf.constant([3], dtype=tf.float32)

b = tf.constant([5], dtype=tf.float32)


이런 식으로 tf.~로 정의하는데 짜증나는건 dtype도 float32가 아니라 tf.float32이다.

a의 타입을 찍어보면

print type(a)

>> <class 'tensorflow.python.framework.ops.Tensor'>


연산가능한 타입이 아니므로, a+b 와 같은 일반적인 방법으로는 연산이 안된다는 것을 알 수 있다.


그래프와 세션

그러면 a + b 의 값을 얻고 싶으면 어떻게 해야할까?
c = a + b라고 할 때, a + b를 그래프(Graph)라고 한다.

실제로 값을 뽑아내려면, 세션(Session)에 그래프를 넣어서 실행해야 한다.

session = tf.Session()
result = session.run(b)

print result
>> [5.]
드디어 숫자가 눈에 보인다. 하지만 왜 배열 형태일까?
맨 처음에 a = tf.constant([3], ...)이렇게 정의했기 때문이다.
만약에 tf.constant([1,2,3],~)의 배열의 개수가 1이 아니엇다면 배열의 개수만큼 결과값이 나온다.
연산하려는 행렬의 개수가 안맞으면 에러가 발생한다.


플레이스홀더

그렇다면 상수 대신에 변수를 이용하여 연산을 하려면 어떻게 해야할까?
여기서 Placeholeder라는 것을 사용한다.
플레이스홀더는 Variable을 담는 그릇이고 여기에는 학습 데이터를 피딩(하나씩 넣음)한다.
tf.placeholder(shape, dtype, name) 여기엔 shape, dtype, name의 속성이 필요하다.

import tensorflow as tf

x = tf.placeholder("float", None)
y = x * 2

with tf.Session() as session:
    result = session.run(y, feed_dict={x: [1, 2, 3]})
    print(result)

http://learningtensorflow.com/lesson4/에서 참조한 예제이다.

x는 float type의 placeholder이고 y = x * 2라는 그래프가 있다.

tf.Session()을 session이라고 정의하여 result = session.run(Graph, feed_dict) 로 실행한다.
상수 연산만 할 때(feed_dict가 없을 때) 였다면 run(y)만 했을 것이다. 

여기서는 x라는 플레이스홀더에 데이터를 넣어줘야 하기 때문에 run(y, 넣어줄 데이터)를 한다.
with문 대신 이렇게 써도 상관없다.
session = tf.Session()
result = session.run(y, feed_dict={x: [1, 2, 3]})
print(result)  

변수형

y = W * x라는 그래프를 만들어보자.
import tensorflow as tf

input_data = [1,2,3,4]
x = tf.placeholder("float", None)
W = tf.Variable([2],dtype=tf.float32)
y = W * x

session = tf.Session()
init = tf.initialize_all_variables()
session.run(init)
result = session.run(y, feed_dict={x:input_data})
print(result)

여기서 주의해야할 점은 Variable은 변수 초기화를 해줘야 [2]라는 값이 W에 저장된다는 것이다.
저 코드 없이 실행하면 에러가 발생한다.

텐서플로우는 일반적인 파이썬 자료형과 다르기 때문에
기본 개념을 알고 넘어가야 머신러닝이 수월해질 것 같아서 정리했다.


[Python] Virtualenv 가상환경 구성하기


virtualenv를 사용하는 이유는 라이브러리가 꼬이는걸 막고 각 프로젝트별로 개발환경을 분리시키기 위함입니다.

예를 들어, 새 프로젝트에는 구버전 라이브러리를 사용해야 하는 경우 패키지를 지웠다가 설치할 필요 없이 venv를 사용합니다.


1. pip install virtualenv 로 설치

2. 프로젝트를 만들  폴더를 만듭니다.

3. 해당 폴더로 이동하여 virtualenv venv를 치면 venv 폴더가 생깁니다.

4. 가상 환경을 activate 합니다.

   cd Scripts - activate

그러면 위의 사진과 같이 (venv)가 활성화 됩니다.

이 상태에서 라이브러리를 설치하면 패키지가 가상환경에만 설치됩니다.


비활성화 하는 방법은 deactivate 입니다.

[Docker] 윈도우 10에 Docker 설치 후 Tensorflow 환경 만들기(1)


VMware의 리눅스에 도커를 설치할지 윈도우에 설치할지 고민하다가 윈도우에 설치를 하기로 했다.


Docker란 가상 머신을 어플리케이션처럼 돌릴 수 있는 환경을 말한다.

Tensorflow를 실행하기 위한 방법으로 널리 사용되며, 이미지만 탑재해서 돌리면 개발환경이 만들어지기 때문에 개발환경 구축이 매우 간편하다.


1. 우선 도커 툴박스를 다운받는다. 

     https://www.docker.com/products/docker-toolbox



2. 설치를 쭉 하고 Docker Quickstart Terminal을 실행한다.


3. 처음에 자동으로 디폴트 가상머신이 만들어진다. 새로운 도커 가상머신을 만들려면 터미널에 아래와 같이 입력한다.

  $  docker-machine create vdocker -d virtualbox     

약간의 시간이 걸린다.

docker-machine --help라고 치면 도움말을 볼 수있다.



4.  $docker-machine ls라고 치면 만들어진 도커 가상머신을 볼 수 있다.


5. $docker-machine start vdocker를 입력하여 가상머신을 실행시킨다.

    그리고 $ docker run -it -p 8888:8888 --name tensorflow gcr.io/tensorflow/tensorflow:latest-devel

   를 입력해서 텐서플로우 이미지를 받는다.

   --name으로 이미지의 이름을 지을 수 있다.


이미지 다운로드가 완료되면 자동으로 접속된다.

exit를 치면 다시 컨테이너 밖으로 나올 수 있다.

6. $ docker ps -a 를 입력하면 컨테이너 목록을 볼 수 있다.


7. 조금전에 exit를 했기때문에 Status가 exit일 것이다.

   그래서 docker restart tensorflow(컨테이너 아이디 or 이름)을 하면 접속이 가능한 상태가 된다.


8. 접속하기

   $ docker exec -it tensorflow /bin/bash



[Django] Error: Import by filename is not supported


Django 프로젝트 생성 시 Importlib 에서 

Import by filename is not supported 이런 에러가 떴었다.


인터넷을 싹싹 뒤져도 해결을 못했다.

virenv를 만들어도 안됐었다. Django를 재설치해도 안됐었다. 기존에 만들어둔 프로젝트의 runserver도 같은 에러가 발생했다.


환경변수 - 시스템 변수에 DJANGO_SETTINGS_MODULE를 삭제 했더니 정상적으로 실행이 됐다.

아마 settings.py의 경로가 잘못 잡혔던것 같다.(내가 한거 아님)


문제를 해결해 버려서 스크린샷이 없다..ㅠ

[Python GUI PyQt4 vs WxPython]



Python으로 GUI Application을 만드는데에는 몇 가지 선택지가 있다.


Mobile이면 Kivy도 좋은 선택지라고 생각한다. 

Kivy는 터치가 가능한 어플리케이션을 만들기 좋고 멀티터치도 가능하다.


반면, Desktop Application을 만들거라면 PyQt4와 WxPython으로 좁혀진다.


둘 다 멋진 라이브러리이고, 만들고자하는 것의 대부분을 만족 시킬 수 있을 것이다.


나는 PyQt4를 먼저 사용해보고 WxPython으로 넘어간 케이스이다.


PyQt 이하 Qt를 사용하며 느낀점은, UI Component를 배치하는 방식이 쉽다. 복잡하고 정교하게도 가능하다. 

self.layout = QtGui.QGridLayout()
self.label_currentDB = QtGui.QLabel("Current Database : ")
self.label_currentDB.setFixedWidth(130)
self.label_newDB = QtGui.QLabel("New Database : ")
self.label_newDB.setFixedWidth(130)
self.Edit_currentDB = QtGui.QLineEdit()
self.Edit_currentDB.setFixedWidth(130)
self.Edit_currentDB.setText(ChangeDB.currentDB)
self.Edit_newDB = QtGui.QLineEdit()
self.Edit_newDB.setFixedWidth(130)
self.button_change = QtGui.QPushButton("Change")
self.button_change.clicked.connect(self.changeDB)
self.Edit_newDB.returnPressed.connect(self.changeDB)
self.button_cancel = QtGui.QPushButton("Cancel")
self.button_cancel.clicked.connect(self.close)
self.myQMenuBar = QtGui.QMenuBar(self)
self.layout.addWidget(self.label_currentDB, 0, 0, 1, 1)
self.layout.addWidget(self.label_newDB, 1, 0, 1, 1)
self.layout.addWidget(self.Edit_currentDB, 0, 1, 1, 1)
self.layout.addWidget(self.Edit_newDB, 1, 1, 1, 1)
self.layout.addWidget(self.button_change, 4, 0, 1, 1)
self.layout.addWidget(self.button_cancel, 4, 1, 1, 1)
self.setLayout(self.layout)

Wx는 느낌이 아래와 같이 Boxsizer라는 것을 사용해서 배치를 한다.
Qt도 레이아웃이라는 개념을 사용해서 레이아웃 안에 레이아웃과 같이 배치를 정교하게 할 수 있다.
vbox = wx.BoxSizer(wx.VERTICAL)
box = wx.StaticBox(panel, -1, 'Change DB')
bsizer = wx.StaticBoxSizer(box, wx.VERTICAL)
nmbox = wx.BoxSizer(wx.HORIZONTAL)
fn = wx.StaticText(panel, -1, "Input IP :")
nmbox.Add(fn, 0, wx.ALL | wx.CENTER, 5)
self.nm1 = wx.TextCtrl(panel, -1, style=wx.ALIGN_LEFT)
nmbox.Add(self.nm1, 0, wx.ALL | wx.CENTER, 5)
bsizer.Add(nmbox, 0, wx.ALL | wx.CENTER, 10)
hbox = wx.BoxSizer(wx.HORIZONTAL)
okButton = wx.Button(panel, -1, 'Change')
hbox.Add(okButton, 0, wx.ALL | wx.LEFT, 10)
cancelButton = wx.Button(panel, -1, 'Cancel')
hbox.Add(cancelButton, 0, wx.ALL | wx.LEFT, 10)
vbox.Add(bsizer, 0, wx.ALL | wx.CENTER, 5)
vbox.Add(hbox, 0, wx.ALL | wx.CENTER, 5)
panel.SetSizer(vbox)
self.Centre()
panel.Fit()
self.Show()
okButton.Bind(wx.EVT_BUTTON, self.OnOk)
cancelButton.Bind(wx.EVT_BUTTON, self.OnCancel)

사용해본 바로는 내가 구현하고 싶은 방법으로 구현하는 데에는 Wx가 낫다고 평가하고 싶다.
Staticbox와 같은 컴포넌트나, 몇 가지 세부적인 UI 컴포넌트가 좀더 있었다.
그리고 dialog창에서 OK를 눌렀을 때 parent에서 어떻게 동작하는지 정하기도 그렇고,
Wx를 좀 더 깊게 파서 그런지 몰라도 나는 Wx가 더 풍부하게 활용 가능하다고 생각한다.

처음에 Qt를 사용할지 Wx를 사용할지 고민 많이하다가 둘다 사용해 본 바로는 Wx에 손을 들어주고 싶다.



+ Recent posts