สรุปสั้น ๆ
อยากมีเว็บสำหรับใช้งาน Machine Learning แบบไว ๆ ไม่ใช่เรื่องยาก ทำยังไงไปดูกัน !!!
เขียนโดย
Sirasit Boonklang (Aeff)
Tech and Coding Consultant
บทความนี้ตีพิมพ์ และ เผยแพร่เมื่อ 31 มีนาคม 2566
ในปัจจุบันการพัฒนาโมเดล Machine Learning ได้กลายเป็นส่วนสำคัญในการเอามาต่อกับแอปพลิเคชันต่าง ๆ โดยส่วนใหญ่มักจะมีการทำโมเดลออกมาเป็น API แล้วนำมาต่อกับแอปพลิเคชันเพื่อใช้งานความสามารถของโมเดลนั้น ๆ เดี๋ยววันนี้เราจะมาลองทำหน้าเว็บที่เรียกใช้งานโมเดล Machine Learning ด้วย Flask กัน
Flask คืออิหยัง?
Flask คือเป็นเว็บเฟรมเวิร์กที่มีน้ำหนักเบา ยืดหยุ่น และใช้งานง่ายสำหรับ Python ช่วยให้เหล่าเดฟภาษา Python อย่างเรา ๆ สามารถพัฒนาเว็บแอปพลิเคชันได้อย่างรวดเร็วด้วย เหมาะสำหรับเว็บแอปพลิเคชันขนาดกลางและเล็ก
เริ่มต้นใช้งาน Flask
1. ติดตั้ง Python version 3.x
2. ทำการติดตั้ง Flask ด้วยคำสั่ง
pip install flask
3.เริ่มจากทำให้เว็บแสดงผลแบบง่าย ๆ ด้วย Hello, World! ก่อน โดยไฟล์นี้จะชื่อว่า app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, World!"
if __name__ == '__main__':
app.run()
โดยโค้ดนี้จะมีการกำหนด route เดียวที่มีการ return คำว่า “Hello, World!” กลับมา
4.ทำการรันด้วยคำสั่ง
python app.py
หากหน้าตาของเว็บแสดง Hello World ขึ้นมาแล้วแบบนี้ แปลว่า Flask สามารถใช้งานได้แล้ว 🎉
เชื่อมต่อกับ Machine Learning API ด้วย Flask
เมื่อเราได้ทบทวนการสร้าง Flask แอปเบื้องต้นไปแล้ว ต่อมาเรามาดูวิธีการในการเชื่อมต่อกับ Machine Learning API กับแอปของเรากัน ในตัวอย่างนี้ผมจะเอา Endpoints ของ Machine Learning API มาจากการเอาโมเดลด้าน Text Classification ที่ชื่อว่า distilbert-base-uncased-finetuned-sst-2-english เป็นโมเดลที่มีค่าความถูกต้อง (accuracy อยู่ที่ 91.3) สำหรับใช้งานในการวิเคราะห์ข้อความที่เป็นภาษาอังกฤษ โดยเราสามารถทำการใส่ input ที่เป็นข้อความ แล้วตัวโมเดลจะทำการรีเทิร์นค่าที่เป็น Label 1 หรือ Label 0 ที่แทนว่าค่านั้นผลลัพธ์เป็น POSITIVE (เชิงบวก) หรือ NEGATIVE (เชิงลบ) ใครอยากลองทดสอบโมเดลสามารถไปลองใช้งานได้ที่ลิงก์นี้ https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english
และการเอาโมเดลนี้ไป Deploy บนคลาวด์ Microsoft Azure เพื่อนำ Endpoints หรือ API ของโมเดลนี้มาใช้
ด้วยการไปที่ Portal.azure สร้าง Hugging Face on Auzre ML แล้วนำ model ID หรือชื่อโมเดลมาใส่
เมื่อ Service ถูกสร้างขึ้นเรียบร้อยแล้ว เราสามารถเปิดตัว Azure Machine Learning ของโมเดล แล้วไปที่ Endpoints
แล้วไปที่ Consume เราจะได้ REST endpoint มาเป็นที่เรียบร้อยแล้ว ต่อไปค่าที่เราจะนำมาใช้งานนั้นมี 2 ส่วนนั้นก็คือ REST endpoint และ Primary key
เมื่อเราได้ REST endpoint กับ key มาแล้วให้เดี๋ยวเราจะมาทำ Interface หรือหน้าเว็บสำหรับเชื่อมต่อกับ Machine Learning API ด้วย Flask กันมาเริ่มกันเลยยย
กลับมาที่โค้ด Hello World ของเรากันครับ เริ่มต้นที่ทำหน้าเจ้าตัว Flask มัน Render หน้าเว็บขึ้นมาก่อน โดยการสร้างโฟล์เดอร์ templates ขึ้นมาแล้วก็สร้างไฟล์ Static file ไว้ในโฟลเดอร์นี้
จากนั้นเราก็เขียนโค้ด HTML ง่าย ๆ ลงไปเพื่อทดสอบการทำงานของหน้าเว็บ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Pages</title>
</head>
<body>
My Page on Flask Project
</body>
</html>
from flask import Flask, render_template
ให้ทำการแสดงผลเป็นกลับมาเป็นหน้า HTML โดยใช้คำสั่ง render_template(’ชื่อไฟล์ html ในโฟลเดอร์ templates’)
return render_template('index.html')
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello():
return render_template('index.html')
if __name__ == '__main__':
app.run()
เมื่อลองทำการรันด้วยคำสั่ง flask run และไปที่ 127.0.0.1:5000 จะเห็นได้ว่าตอนนี้ Flask Application ของเราสามารถแสดงหน้า HTML ของเราออกมาได้แล้ว หลังจากนั้นเราจะมาสร้างหน้าฟอร์มแบบง่าย ๆ ในการกรอกข้อมูลที่เป็นข้อความ ปุ่มในการกด submit และข้อความแสดงผลลัพธ์ในไฟล์ HTML ดังนี้
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sentiment Analysis</title>
</head>
<body>
<h1>Sentiment Analysis</h1>
<form action="/" method="POST">
<textarea name="text" placeholder="Enter text for sentiment analysis" required></textarea>
<br>
<button type="submit">Analyze Sentiment</button>
</form>
{% if sentiment %}
<div id="sentiment">
<p><strong>Sentiment Result: {{ sentiment | safe }}</strong></p>
</div>
{% endif %}
</body>
</html>
<h1>Sentiment Analysis</h1>: คือแท็ก header ที่แสดงชื่อ “Sentiment Analysis” บนหน้าเว็บ
<form action=”/” method=”POST”>: คือ HTML ฟอร์มที่จะส่งข้อมูลไปยัง URL ที่เป็น Root Path โดยใช้ Method POST เมื่อผู้ใช้คลิกปุ่ม “Analyze Sentiment” ข้อมูลที่ป้อนในพื้นที่ข้อความจะถูกส่งไปยังเซิร์ฟเวอร์
<textarea name=”text” placeholder=”Enter text for sentiment analysis” required></textarea>:
เป็นแท็กสำหรับผู้ใช้สามารถป้อนข้อความลงไป โดยข้อมูลที่ถูกป้อนลงไปในฟิลด์นี้จะถูกส่งไปยังเซิร์ฟเวอร์
<button type=”submit”> อันนี้เป็นแท็กสำหรับสร้างปุ่ม
{% if sentiment %}: คำสั่งนี้เป็น Jinja2 เทมเพลตที่เราสามารถใส่เงื่อนไข if else เพื่อตรวจสอบเงื่อนไขได้
<p><strong>Sentiment Result: {{ sentiment | safe }}</strong></p>: ส่วนนี้คือตัวแปร Jinja2 เทมเพลตที่แสดงค่าของตัวแปร sentiment | safe ถูกใช้เพื่อป้องกันไม่ให้มีโค้ดที่เป็นอันตรายที่อาจรวมอยู่ในตัวแปร sentiment ถูกเรียกใช้งานในเบราว์เซอร์
หน้าตาของเว็บตอนนี้ก็จะเป็นประมาณนี้ครับ
สามารถเติม CSS เพื่อความสวยงามของหน้าเว็บเข้าไปได้ โดยการที่เราจะเพิ่มโฟลเดอร์ CSS ไปหากต้องการแยกไฟล์ CSS ในโปรเจกต์ Flask จะต้องสร้างโฟลเดอร์ที่ชื่อว่า static และภายในจะมีโฟลเดอร์ชื่อว่า css แล้วภายในมีไฟล์ main.css โดยผมได้เขียนและปรับให้หน้าตาดูดีขึ้นโดยใช้โค้ดดังนี้
body {
font-family: Arial, sans-serif;
background-color: white;
color: #333;
text-align: center;
margin: 0;
padding: 0;
}
h1 {
margin-top: 50px;
}
form {
margin: 50px;
}
textarea {
width: 80%;
height: 100px;
margin: 10px;
padding: 10px;
resize: none;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
}
button {
font-size: 16px;
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: #007bff;
color: white;
cursor: pointer;
}
#sentiment {
font-size: 24px;
margin: 50px;
}
หลังจากที่สร้างโฟลเดอร์และไฟล์ CSS แล้ว เราต้องไปเรียกใช้งานในไฟล์ HTML ด้วยนะ ด้วยคำสั่ง
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
หน้าตาของเว็บที่ได้จะเป็นแบบนี้ 👇
แต่ ๆ ตอนนี้เรายังไม่สามารถรันได้ เพราะในลอจิกการทำงานที่ไฟล์ Python เรายังไม่เสร็จนะ สำหรับโปรเจกต์นี้จะมีการใช้ Endpoints และ Key แต่เราไม่ควรจะใส่ทั้งคู่ไว้ใน Source Code ตรง ๆ โดยผมจะสร้างไฟล์ .env ขึ้นมาและสร้างชื่อตัวแปรมา 2 ตัวของผมใช้ชื่อว่า MODEL_URL และ API_KEY
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv('API_KEY')
MODEL_URL = os.getenv('MODEL_URL')
โดยโค้ดนี้จะเรียกใช้โมดูลสำหรับโหลด ตัวแปรสภาพแวดล้อมจากไฟล์ .env มาโดยโค้ดแต่ละส่วนมีการทำงานดังนี้
- import os: เรียกใช้โมดูล os สำหรับอ่านหรือเขียนไฟล์
- from dotenv import load_dotenv: เรียกใช้โมดูล dotenv เพื่อจะใช้ฟังก์ชัน load_dotenv() ในการโหลดตัวแปรสภาพแวดล้อมจากไฟล์ .env
- load_dotenv(): ฟังก์ชันนี้จะอ่านไฟล์ .env และตั้งค่าตัวแปรสภาพแวดล้อม
- os.getenv() เป็นฟังก์ชันสำหรับอ่านค่าของตัวแปรสภาพแวดล้อมจากสภาพแวดล้อมของ OS หากไม่พบตัวแปรสภาพแวดล้อม ระบบจะคืนค่า none
ต่อมาผมจะสร้างมาอีกไฟล์ชื่อว่า sentiment_analysis.py โดยโค้ดนี้จะเป็นส่วนกำหนดฟังก์ชันที่ HTTP Request POST ไปยัง API ของเราและรับ Response กลับมา
import urllib.request
import json
import ssl
from config import API_KEY, MODEL_URL
import os
def allow_self_signed_https(allowed):
if allowed and not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None):
ssl._create_default_https_context = ssl._create_unverified_context
def get_sentiment(text):
allow_self_signed_https(True)
data = {"inputs": text}
body = str.encode(json.dumps(data))
headers = {
'Content-Type': 'application/json',
'Authorization': ('Bearer ' + API_KEY),
'azureml-model-deployment': 'main'
}
req = urllib.request.Request(MODEL_URL, body, headers)
try:
response = urllib.request.urlopen(req)
result = response.read()
sentiment_output = json.loads(result)
sentiment = sentiment_output[0]['label']
return sentiment
except urllib.error.HTTPError as error:
return {"error": str(error.code)}
- urllib.request: โมดูลนี้กำหนดฟังก์ชันและคลาสที่ช่วยในการเปิด URL (ส่วนใหญ่เป็น HTTP)
- import json: โมดูลนี้สำหรับการทำงานกับข้อมูล JSON ใน Python
- import ssl: โมดูลนี้ให้การเข้าถึงโปรโตคอลการเข้ารหัส Transport Layer Security (TLS) และ Secure Socket Layer (SSL) ที่ให้บริการโดยไลบรารี OpenSSL
- from config import API_KEY, MODEL_URL: เป็นการใช้ตัวแปรสองตัว API_KEY และ MODEL_URL จากไฟล์ config.py
- import os: โมดูลนี้ใช้การอ่านหรือเขียนไฟล์
- def allow_self_signed_https(allowed): ฟังก์ชันนี้ใช้อาร์กิวเมนต์ที่เป็น boolean ชื่อว่า allowed และตั้ง context SSL เพื่ออนุญาตเฉพาะ HTTPS
- data = {“inputs”: text}: กำหนดให้ข้อมูล Input เป็น Dictionary ที่มี Key – Value เดียว
- body = str.encode(json.dumps(data)): เป็นฟังก์ชันเข้ารหัสข้อมูลเป็นสตริง JSON และแปลงเป็น Object
- headers = {…}: สร้าง header ของข้อมูลที่มี header ที่จำเป็นสำหรับ HTTP Request ที่ใช้ Methods POST ไปยัง API ส่วนหัวการให้สิทธิ์ถูกตั้งค่าเป็นตัวแปร API_KEY และ Content-Type ตั้งค่าเป็น application/json
- req = urllib.request.Request(MODEL_URL, body, headers): สร้าง Object ที่มี Request ที่มี URL ของ API, ใน body จะมีข้อมูล JSON และ headers รวมอยู่ด้วยเพื่อเตรียมข้อมูลส่งไปยัง API
- response = urllib.request.urlopen(req): ส่ง Request ไปยัง API ของโมเดลและรับ Response กลับมา
- result = response.read(): อ่านค่าเนื้อหาที่มากับ Response
- Sentiment_output = json.loads (result): ถอดรหัส Response ที่เป็น JSON กลับมาเป็น Object
- Sentiment = Sentiment_output[0][‘label’]: แยก Key กับ label จากลิสต์ตัวแรก แล้วเอาค่าไปไว้ในตัวแปร Sentiment
- except urllib.error.HTTPError as error:… : บล็อกนี้จัดการข้อยกเว้นที่อาจเกิดขึ้นเมื่อส่ง Request ไปยัง API หาก HTTP เกิดข้อผิดพลาด มันจะแสดง Error กลับมา
สุดท้ายแล้วให้เรากลับมาที่ไฟล์หลักนั่นคือ app.py สำหรับตั้งค่า Flask Application ที่อนุญาตให้ user กดปุ่มเพื่อส่งข้อความไปยัง API แล้วนำผลลัพธ์มาแสดงผลในหน้าเว็บ โดยทำการแก้ไขโค้ด app.py มาดังนี้
from flask import Flask, request, render_template
from sentiment_analysis import get_sentiment
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
text = request.form['text']
sentiment = get_sentiment(text)
return render_template('index.html', sentiment=sentiment)
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
ในส่วนของฟังก์ชัน index() เราแก้ไขโดยทำการเพิ่มเงื่อนไขในการตรวจสอบว่าเป็น Methods POST มั้ย ถ้าใช่ จะดึงข้อมูลที่ป้อนของผู้ใช้จากฟอร์มโดยใช้ request.form[‘text’] ส่งผ่านไปยังฟังก์ชัน get_sentiment เพื่อให้โมเดลนำไปประมวลผลข้อความและส่งผลลัพธ์กลับกลับมา เมื่อแก้ไขเสร็จแล้วก็ทำการรันได้เลยด้วยคำสั่ง flask run ได้เลย โดยเราลองใส่ข้อความลงไปแล้วกดปุ่ม Analyze Sentiment
จะเห็นได้ว่าผลลัพธ์ก็จะออกมาแล้วโดยใส่คำว่า “500 Internal server error” เข้าไปมันก็จะได้ผลลัพธ์ออกมาเป็น NEGATIVE นั่นเอง
ระบบฝึกทักษะ การเขียนโปรแกรม
ที่พร้อมตรวจผลงานคุณ 24 ชั่วโมง
- โจทย์ปัญหากว่า 200 ข้อ ที่รอท้าทายคุณอยู่
- รองรับ 9 ภาษาโปรแกรมหลัก ไม่ว่าจะ Java, Python, C ก็เขียนได้
- ใช้งานได้ฟรี ! ครบ 20 ข้อขึ้นไป รับ Certificate ไปเลย !!