[머신러닝] 머신러닝 교과서 Ch09

vector13·2022년 5월 11일
0

머신러닝교과서

목록 보기
12/14
post-custom-banner

웹 애플리케이션에 머신 러닝 모델 내장

(담당파트 9.4, 10.6, 10.8)

이번 장에서는 내가 나중에 프로젝트에서 사용하게 될 flask를 다루고 있어서 더 집중해서 보는 챕터.
머신 러닝 모델의 현재 훈련 상태 저장하고, SQLite 데이터베이스 사용해 데이터 저장하고, Flask 웹 프레임워크 사용해 웹 애플리케이션 개발하고, 웹 서버에 배포까지 해보는 장이다.

학습된 사이킷런 추정기 저장

학습된 모델 재사용 -> pickle 모듈 사용
(파이썬 인터프리터 종료하고 새로운 예측하거나 웹 애플리케이션 재시작마다 모델 다시 훈련은 바람직X)

주의 ** 별도의 웹 서버 사용한다면 서버의 설치된 파이썬과 프로토콜 버전이 호환되는지도 확인해야함


SQLite 데이터베이스 설정

파이썬 표준 라이브러리에 SQLite 데이터베이스 위한 sqlite3 API가 이미 포함되어있음! 그러니 그냥 따로 pip 안주고 import 하면 된다.

import sqlite3
import os

conn = sqlite3.connect('reviews.sqlite')
c = conn.cursor()

c.execute('DROP TABLE IF EXISTS review_db')
c.execute('CREATE TABLE review_db (review TEXT, sentiment INTEGER, date TEXT)')

example1 = 'I love this movie'
c.execute("INSERT INTO review_db (review, sentiment, date) VALUES (?, ?, DATETIME('now'))", (example1, 1))

example2 = 'I disliked this movie'
c.execute("INSERT INTO review_db (review, sentiment, date) VALUES (?, ?, DATETIME('now'))", (example2, 0))

conn.commit()
conn.close()

눈여겨 볼 것

  • cursor 메소드 : 데이터베이스 커서 만든다. 커서를 통해 다양한 SQL 문법으로 데이터베이스 레코드 조작한다.
  • SQLite 전용 데이터베이스 브라우저가 있음 (http://sqlitebrower.org/) 로 데이터베이스 위한 GUI 제공

플라스크 웹 애플리케이션 개발

플라스크는 파이썬으로 만들어진 웹 프레임워크이다. 2010년에 릴리즈 되었고 링크드인과 핀터레스트 등에서 플라스크를 사용한다. 플라스크는 마이크로프레임워크로도 알려져있는데, 핵심 모듈은 가볍고 심플하지만 다른 라이브러리들과 연결해 쉽게 확장가능하기 때문이다.
파이썬에서 플라스크 라이브러리는 pip install flask 또는 아나콘다에서는 conda install flask 로 설치가능


위처럼 간단한 웹 애플리케이션 만드는거 코드는 저자의 git 을 참고하면되고 요약하자면
https://github.com/rickiepark/python-machine-learning-book-3rd-edition/tree/master/ch09/1st_flask_app_1

app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('first_app.html')

if __name__ == '__main__':
    app.run(debug=True)

app.py 파일은 플라스크 웹 애플리케이션 구동위한 파이썬 인터프리터로 실행할 핵심 코드 담고있음. 여기서 templates 디렉터리에서 html 파일 찾아서 출력하도록 코드를 구성하고있다.
실행은 python app.py 로 실행할 수 있고
http://127.0.0.1:5000/ 에서 서버가 출력하는 웹페이지 확인가능하다.

만일 html 폼 요소를 추가하고 싶다면 WTForms 라이브러리 사용하면 된다.
pip install wtforms 또는 conda install wtforms로 설치

1st_flask_app_2/ 디렉터리 코드는 여기를 확인하고
https://github.com/rickiepark/python-machine-learning-book-3rd-edition/tree/master/ch09/1st_flask_app_2

app.py

from flask import Flask, render_template, request
from wtforms import Form, TextAreaField, validators

app = Flask(__name__)

class HelloForm(Form):
    sayhello = TextAreaField('',[validators.DataRequired()])

@app.route('/')
def index():
    form = HelloForm(request.form)
    return render_template('first_app.html', form=form)

@app.route('/hello', methods=['POST'])
def hello():
    form = HelloForm(request.form)
    if request.method == 'POST' and form.validate():
        name = request.form['sayhello']
        return render_template('hello.html', name=name)
    return render_template('first_app.html', form=form)

if __name__ == '__main__':
    app.run(debug=True)

app.py를 보면 TextAreaField로 텍스트 필드 추가한다. (TextAreaField클래스는 입력 텍스트 안전한지 아닌지 자동 확인한다. )

hello() 함수로 폼 통해 전달 내용 검증해 hello.html 페이지 출력.

파일의 구조는 다음과 같지만 코드로 보는게 더 쉬운듯..

실행은 동일하게 python app.py로 실행시키고 위의 예제와 같은 주소인 http://127.0.0.1:5000/ 에서 렌더링 결과 볼 수 있다.

주목할 것

  • 플라스크는 MCV 패턴 따르는 프레임워크와 달리 모델이 없다! (경량 프레임워크) @app.route 데코레이터로 지정된 함수가 비즈니스 로직 담당 컨트롤러다. 템플릿이 뷰 역할 담당하며 컨트롤러에서 전달한 데이터 이용해 html 생성한다. 만들어진 html은 컨트롤러 함수의 return 문을 통해 웹 브라우저로 전송

영화 리뷰 분류기를 웹 애플리케이션으로 만들기

폼으로 사용자가 영화 리뷰 입력하면 -> 전송해서 모델을 통해 사용자가 입력한 영화 리뷰 텍스트와 예측한 클래스 레이블을 sqlite 에 저장 -> 새 웹페이지에서 예측 클래스 레이블과 예측 확률 확인 -> 사용자가 맞음 또는 틀림 버튼 눌러 예측결과 피드백 보냄 -> 전달된 클래스 레이블 추출해 정수 레이블로 변환하고 분류기 업데이트 -> sqlite 데이터베이스에 새로운 레코드로 피드백 추가 -> thanks.html 템플릿 출력

하는 예제이다.

디렉터리 구조는

와 같고
https://github.com/rickiepark/python-machine-learning-book-3rd-edition/tree/master/ch09/movieclassifier
페이지에서 코드를 확인할 수 있음
(style 위한 css 태그는 안볼것임 )

app.py

from flask import Flask, render_template, request
from wtforms import Form, TextAreaField, validators
import pickle
import sqlite3
import os
import numpy as np

# import HashingVectorizer from local dir
from vectorizer import vect

app = Flask(__name__)

######## Preparing the Classifier
cur_dir = os.path.dirname(__file__)
clf = pickle.load(open(os.path.join(cur_dir,
                'pkl_objects',
                'classifier.pkl'), 'rb'))
db = os.path.join(cur_dir, 'reviews.sqlite')

def classify(document):
   label = {0: 'negative', 1: 'positive'}
   X = vect.transform([document])
   y = clf.predict(X)[0]
   proba = np.max(clf.predict_proba(X))
   return label[y], proba

def train(document, y):
   X = vect.transform([document])
   clf.partial_fit(X, [y])

def sqlite_entry(path, document, y):
   conn = sqlite3.connect(path)
   c = conn.cursor()
   c.execute("INSERT INTO review_db (review, sentiment, date)"\
   " VALUES (?, ?, DATETIME('now'))", (document, y))
   conn.commit()
   conn.close()

######## Flask
class ReviewForm(Form):
   moviereview = TextAreaField('',
                               [validators.DataRequired(),
                               validators.length(min=15)])

@app.route('/')
def index():
   form = ReviewForm(request.form)
   return render_template('reviewform.html', form=form)

@app.route('/results', methods=['POST'])
def results():
   form = ReviewForm(request.form)
   if request.method == 'POST' and form.validate():
       review = request.form['moviereview']
       y, proba = classify(review)
       return render_template('results.html',
                               content=review,
                               prediction=y,
                               probability=round(proba*100, 2))
   return render_template('reviewform.html', form=form)

@app.route('/thanks', methods=['POST'])
def feedback():
   feedback = request.form['feedback_button']
   review = request.form['review']
   prediction = request.form['prediction']

   inv_label = {'negative': 0, 'positive': 1}
   y = inv_label[prediction]
   if feedback == 'Incorrect':
       y = int(not(y))
   train(review, y)
   sqlite_entry(db, review, y)
   return render_template('thanks.html')

if __name__ == '__main__':
   app.run(debug=True)

app.py 살펴보기

  • clf = pickle.load() : pickle 모듈로 분류모델 복원, 설정 (저장된 로지스틱 회귀 분류기 복원)

  • def classify() : 텍스트 문서 들어오면 예측 클래스 레이블 lable[y]과 예측확률 proba 반환

  • def train() : 문서와 클래스 레이블 주어지면 분류기 clf.partial_fit으로 업데이트

  • sqlite_entry() : 사용자 입력 영화 리뷰, 클래스 레이블, 타임스탬프(DATETIME) 를 하나의 레코드로 db에 저장.

  • @app.route('/') 로 컨트롤러인거 알리기

  • def index() : ReviewForm 클래스 객체 생성하고 reviewform.html 템플릿 파일 출력

  • class ReviewForm : 리뷰 입력 최소 15자 제한 O

  • feedback() : 사용자가 맞음/ 틀림 피드백 누르면 예측 클래스 레이블 추출하고, 정수 클래스 레이블로 변환하고 train 이용해 분류기 업데이트. 그리고 splite_entry() 이용해 db 업데이트 하고 thanks.html 출력


공개 서버에 웹 애플리케이션 배포

이제까지는 로컬 컴퓨터에서 테스트이고 공개 웹서버에 배포도 가능한데 여기서는 PythonAnywhere 웹 호스팅 서비스를 사용. ( 파이썬 웹 애플리케이션의 호스팅에 특화되어있고 간단함.)
무료 계정인 비기너 계정은 SSH로 원격서버 접속 X

방법은
계정 생성후 -> 새로운 애플리케이션 생성 후 -> file 탭에서 분류기 디렉터리 업로드 (movieclassifier) -> Web 탭으로 이동, Reload~ 버튼 눌러줌. .pythonanywhere.com 으로 접근 가능.

profile
HelloWorld! 같은 실수를 반복하지 말기위해 적어두자..
post-custom-banner

0개의 댓글