flask를 이용하여 모델 서빙하기

uchan·2021년 7월 28일
0

해당 글에서는 <프로그래머스-미술작품 분류하기>의 dataset으로 모델서빙을 진행할 것이다. 저작권 관련하여 해당 프로그래머스 과제관에서 주어진 데이터세트를 토대로 모델을 학습하였고, 데이터세트는 test용으로 한개의 이미지만을 사용하였음을 밝힙니다. 이와 관련하여 저작권 문제 발생시 댓글 또는 yj2221@naver.com으로 보내주시면 조치를 취하도록 하겠습니다.

지금까지...

저번 시간에 <프로그래머스-미술작품 분류하기> 과정을 통해 모델을 학습시키고 accuracy metric을 통해 모델의 성능을 측정해보았다. 오늘은 저번에 훈련시킨 모델을 활용하여 간단한 모델서빙을 하는 웹페이지를 만드는 시간을 가져보겠다.

environment

python 3.9.5
pytorch 1.8.1 gpu version, cuda(11.0)
flask 2.0.1

웹페이지 만들기

기본 지식으로 html과 flask 사용법을 알아야 된다. 필자는 form 태그를 통해 파일을 업로드하고 flask를 통해 index.html을 렌더링할 때 filename에 값을 넣어주는 방식으로 index.html을 작성하였다. 해당 코드는 밑에 app.py와 같이 참고하기를 바란다.

<!DOCTYPE html>
<head>
    <title> 
        home
    </title>
<body>
    <div class="container">
    <h1>machine learning</h1>
    <h2>image upload</h2>
    {% if filename %}
        <div>
            <img src="{{filename}}">
        </div>
    
        <div>
            
            <p>{{label}} {{probability}}</p>
            
        </div>
    {% endif %}
    <form method="post" action="/upload" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="submit">
    </form>
    </div>
</body>
</head>

app 제작

preprocessing & model

우선 app.py를 제작하기 전 간단한 image preprocessing 작업과 model load 작업을 해주어야 한다. 코드는 다음과 같다.

def solution2(filename):
    classes = ['dog', 'elephant', 'giraffe', 'guitar', 'horse', 'house', 'person']
    model = MyModel()
    model.load_state_dict(torch.load('model/latest.pth'), strict=False)
    transform = create_transform()
    image = cv2.imread('.' + filename, cv2.IMREAD_COLOR)
    image = np.array(image)
    image = transform(image=image)['image']
    image = image.unsqueeze(0)
    output = model(image)
    output = torch.softmax(output.detach(), dim=1)
    index = torch.argmax(output)
    label = classes[index]
    #return redirect("label : {}, prob : {}".format(label, output[index]))
    #return render_template('index.html', label = label, probability = output[index])
    return label, str(output[0][index].item())[:5]

우선 <프로그래머스-미술작품 분류하기> 데이터 세트에 맞게 class들을 선언해준다. 그리고 모델을 load해주고 파일 업로드를 통해 서버에 저장된 file을 이용하여 image을 프로세싱해준다. 그후 batch는 1로 만들어주고 model를 통과시킨후 softmax과정을 통해 가장 높은 확률의 class를 반환해준다.

app

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'

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

@app.route('/upload', methods = ['POST'])
def upload():
    
    file = request.files['file']
    filename = file.filename
    file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    img_src = url_for('static', filename = 'uploads/' + filename)
    
    start_time = time.time()
    label, prob = solution2(img_src)
    end_time = time.time()
    app.logger.info(end_time-start_time)
    return render_template('index.html', filename=img_src, label=label, probability=prob)

if __name__=='__main__':
    app.run('0.0.0.0', port=5000, debug=True)

기본 페이지를 index.html로 하고 html에서 form 태그를 보면 action=upload 이므로 파일을 업로드 하면 @app.route('/upload', ~) 부분이 실행된다.
upload 메소드를 보면 request.files를 통해 file을 가져오고 서버 config에 지정된 폴더로 저장하는 것을 확인할 수 있다. 그리고 위 preprocessing & model 작업을 해주고 index.html로 filename과 lable argument를 포함하여 렌더링해준다.

execute

서버를 실행하면 다음과 같이 출력이 된다.

172.30.1.22:5000으로 접속하면 내가 제작한 웹페이지를 확인할 수 있다.(참고로 IP와 port는 임의로 지정되므로 다르게 출력될 수 있다.


위와 같이 웹페이지가 뜰텐데 파일업로드 후 submit을 해보자


기타 이미지를 제대로 분류하는 것을 확인할 수 있다.

Discuss

사실 필자처럼 코드를 짜면 안정성 및 효율성이 떨어진다. 예를 들어 이미지만을 파일업로드 해야되지만 그렇지 않을 경우 대처하는 코드가 없다. 단순히 if문을 구현하는 방법도 있지만 flask에서 제공하는 메소드도 있다. 또한 효율성인 측면에서도 문제가 발생할 수 있다. 예를 들어, 실시간 영상에서 object detection하는 웹페이지를 만든다고 가정을 하자. 필자처럼 코드를 작성할 경우 데이터 처리가 느려져 실시간으로 작동되지 않을 수 있다. 따라서 데이터를 병렬로 처리하는 작업도 필요할 것이다(이와 별개로 모델의파라미터 및 연산 최적화도 필요함).

1개의 댓글

comment-user-thumbnail
2022년 11월 18일

preprocessing & model 이 부분은 app.py에 작성하여 실행하는건가요?

답글 달기