django Web 개발 - IRIS Prediction
Page content
개요
- Python Django와 Sklearn을 활용하여 간단한 iris prediction 웹을 만들어본다.
사전준비
- 머신러닝 기본 이론 및 원리는 어느정도 알고 있다고 가정한다.
- Django 앱에 대해 어느정도 알고 있다고 가정한다.
무엇을 배우는가?
- 머신러닝 모델을 활용하여 배포하는 과정을 배운다.
가상환경 설정
- 가상환경을 생성한다.
$ virtualenv venv
created virtual environment CPython3.9.1.final.0-64 in 475ms
creator CPython3Posix(dest=/Users/evan/Desktop/django-iris-tutorial/venv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/Users/evan/Library/Application Support/virtualenv)
added seed packages: pip==22.1.1, setuptools==62.3.2, wheel==0.37.1
activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
- 만들어진 가상환경에 접속한다.
$ source venv/bin/activate
(venv) $
- 크게 3개의 라이브러리를 설치한다.
- jupyterlab : 머신러닝 개발 과정을 진행할 에디터로 활용한다.
- sklearn : 머신러닝 개발 관련 라이브러리이다.
- django : django 웹 프레임워크 라이브러리이다.
(venv) $ pip install jupyterlab sklearn django
머신러닝 개발
- iris 데이터를 불러오고 sklearn을 활용하여 모형 개발을 진행한다.
- 모형 개발 시, 주요 Feature Engineering 과정은 생략한다.
(1) 모형 개발
- jupyterlab을 실행한다.
(venv) $ python -m jupyterlab
- 아래와 같이 코드를 입력한다.
# Module 불러오기
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
import pandas as pd
# 데이터셋 불러오기
df = pd.read_csv("data/iris.csv")
# 독립변수와 종속변수 분리
X = df[['sepal_length','sepal_width','petal_length','petal_width']]
y = df['classification']
# 훈련데이터와 종속데이터 분리
X_train, X_test, Y_train, Y_test = train_test_split(X, y, test_size=0.20, random_state=1)
# 모형 학습
model = SVC(gamma='auto')
model.fit(X_train, Y_train)
# 모형 예측
sepal_length = float(1.5)
sepal_width = float(5)
petal_length = float(4)
petal_width = float(3)
result = model.predict([[sepal_length,sepal_width,petal_length,petal_width]]) # input must be 2D array
print(result)
['Iris-virginica']
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/sklearn/base.py:445: UserWarning: X does not have valid feature names, but SVC was fitted with feature names
warnings.warn(
(2) 모형 저장
- pickle을 통해 모형 저장을 할 수 있다.
pd.to_pickle(model, r'models/svc_model.pickle')
Django 시작
- django 웹사이트 프로젝트를 시작한다.
(venv) $ django-admin startproject iris
(venv) $ cd iris
(venv) $ python manage.py startapp predict
settings.py
iris/settings.py
를 열고 아래와 같이 수정한다.
.
.
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'predict',
]
.
.
urls.py
iris/urls.py
를 열고 아래와 같이 수정한다.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('predict.urls', namespace='predict'))
]
predict/urls.py
를 새로 생성하고 아래와 같이 코드를 추가한다.
from django.urls import path
from . import views
app_name = 'predict'
urlpatterns = [
path('', views.predict, name='predict'),
]
views.py
- 이제
predict/views.py
에서predict
함수를 만들어 백엔드 처리를 진행하고, 최종 결괏값을predict.html
로 돌려주는 함수를 구현할 것이다.
from django.shortcuts import render
# Create your views here.
def predict(request):
return render(request, 'predict.html', {})
predict.html
predict/templates
폴더를 만들고,predict.html
파일을 새로 생성한다.
hello
- predict.html 파일이 잘 열리는지 확인한다.
(venv) iris$ python manage.py runserver
- 현재 트리 구조는 아래와 같다.
$ tree -L 2
.
├── db.sqlite3
├── iris
│ ├── __init__.py
│ ├── __pycache__
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── predict
├── __init__.py
├── __pycache__
├── admin.py
├── apps.py
├── migrations
├── models.py
├── templates
├── tests.py
├── urls.py
└── views.py
6 directories, 14 files
html 파일 추가
-
크게 2개의 html 파일을 추가한다.
- base.html, results.html, predict.html
-
base.html 코드 추가는 아래와 같이 한다.
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<title>Iris prediction</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="/">Prediction <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/results">DB</a>
</li>
</ul>
</div>
</nav>
{% block main %}
{% endblock %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>
- predict.html 파일에 다음과 같이 코드를 추가한다.
{% extends "base.html" %}
{% block main %}
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Prediction Results</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<h5>Prediction Input:</h5>
<div>Sepal Length: <span id="sl"></span></div>
<div>Sepal Width: <span id="sw"></span></div>
<div>Petal Length: <span id="pl"></span></div>
<div>Petal width: <span id="pw"></span></div>
<h5 class="pt-3">Prediction Classification:</h5>
<div id="prediction"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<a class="btn btn-primary" href="/results" role="button">View DB</a>
</div>
</div>
</div>
</div>
<div class="container pt-5">
<div class="row justify-content-md-center">
<div class="col-md-4">
<h1>Iris Prediction</h1>
<form action="" id="post-form">
{% csrf_token %}
<div class="form-group">
<label for="sepal_length">Sepal Length</label>
<input type="number" step="0.1" class="form-control" id="sepal_length" placeholder="" required>
</div>
<div class="form-group">
<label for="Sepal Width">Sepal Width</label>
<input type="number" step="0.1" class="form-control" id="sepal_width" placeholder="" required>
</div>
<div class="form-group">
<label for="petal_length">Petal Length</label>
<input type="number" step="0.1" class="form-control" id="petal_length" placeholder="" required>
</div>
<div class="form-group">
<label for="petal_width">Petal Width</label>
<input type="number" step="0.1" class="form-control" id="petal_width" placeholder="" required>
</div>
<button type="submit" value="Submit" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">Submit</button>
</form>
</div>
</div>
</div>
<script>
$(document).on('submit', '#post-form',function(e){
e.preventDefault();
$.ajax({
type:'POST',
url:'{% url "predict:submit_prediction" %}',
data:{
sepal_length:$('#sepal_length').val(),
sepal_width:$('#sepal_width').val(),
petal_length:$('#petal_length').val(),
petal_width:$('#petal_width').val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
action: 'post'
},
success:function(json) {
document.forms["post-form"].reset();
document.getElementById("prediction").innerHTML = json['result']
document.getElementById("sl").innerHTML = json['sepal_length']
document.getElementById("sw").innerHTML = json['sepal_width']
document.getElementById("pl").innerHTML = json['petal_length']
document.getElementById("pw").innerHTML = json['petal_width']
},
error : function(xhr,errmsg,err) {
}
});
})
</script>
{% endblock %}
- predict/urls.py를 열고 아래와 같이 코드를 추가한다.
from django.urls import path
from . import views
app_name = "predict"
urlpatterns = [
path('', views.predict, name='prediction_page'),
path('predict/', views.predict_chances, name='submit_prediction'),
path('results/', views.view_results, name='results'),
]
- results.html 파일을 추가한다.
{% extends "base.html" %}
{% block main %}
<div class="container pt-5">
<div class="row">
<h1>Prediction Results</h1>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Sepal length</th>
<th scope="col">Sepal width</th>
<th scope="col">Petal length</th>
<th scope="col">Petal width</th>
<th scope="col">Prediction</th>
</tr>
</thead>
<tbody>
{% for data in dataset %}
<tr>
<th scope="row">{{ data.id }}</th>
<td>{{ data.sepal_length }}</td>
<td>{{ data.sepal_width }}</td>
<td>{{ data.petal_length }}</td>
<td>{{ data.petal_width }}</td>
<td>{{ data.classification }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
predict/views.py
- 위 파일을 열고 아래와 같이 함수를 추가한다.
from django.shortcuts import render
from django.http import JsonResponse
import pandas as pd
from .models import PredResults
# Create your views here.
def predict(request):
return render(request, 'predict.html', {})
def predict_chances(request):
if request.POST.get('action') == 'post':
# Receive data from client
sepal_length = float(request.POST.get('sepal_length'))
sepal_width = float(request.POST.get('sepal_width'))
petal_length = float(request.POST.get('petal_length'))
petal_width = float(request.POST.get('petal_width'))
# Unpickle model
model = pd.read_pickle(r"/Users/evan/Desktop/django-iris-tutorial/models/svc_model.pickle")
# Make prediction
result = model.predict([[sepal_length, sepal_width, petal_length, petal_width]])
classification = result[0]
PredResults.objects.create(sepal_length=sepal_length, sepal_width=sepal_width, petal_length=petal_length,
petal_width=petal_width, classification=classification)
return JsonResponse({'result': classification, 'sepal_length': sepal_length,
'sepal_width': sepal_width, 'petal_length': petal_length, 'petal_width': petal_width},
safe=False)
def view_results(request):
# Submit prediction and show all
data = {"dataset": PredResults.objects.all()}
return render(request, "results.html", data)
predict/models.py
- 위 파일을 열고 아래와 같이 코드를 추가한다.
from django.db import models
# Create your models here.
class PredResults(models.Model):
sepal_length = models.FloatField()
sepal_width = models.FloatField()
petal_length = models.FloatField()
petal_width = models.FloatField()
classification = models.CharField(max_length=30)
def __str__(self):
return self.classification
predict.admin.py
- 위 파일을 열고 아래와 같이 코드를 추가한다.
from django.contrib import admin
from .models import PredResults
# Register your models here.
admin.site.register(PredResults)
배포
- 배포 전, 아래와 같이 터미널에서 명령어를 추가한다.
(venv) iris $ python manage.py makemigrations
(venv) iris $ python manage.py migrate
(venv) iris $ python manage.py createsuperuser
- 배포 전, 아래와 같이 배포가 된 것을 확인한다.
(venv) iris $ python manage.py runserver