Membangun RESTful API untuk Spam Filter Menggunakan Flask dan Scikit-Learn

Muhammad Arslan 30 Desember 2016

Membangun RESTful API untuk Spam Filter Menggunakan Flask dan Scikit-Learn

Masih dengan menggunakan Scikit-Learn dan Python, kita akan mencoba membuat spam filter dengan menggunakan algoritma Naive Bayes dan dataset SMS Spam Collection. Kita akan menggunakan dataset tersebut untuk membuat RESTful API yang akan digunakan untuk memprediksi apakah sebuah teks adalah spam atau ham. Sebagai contoh berikut adalah contoh dataset yang akan kita coba:

"type","text"
"ham","Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat..."
"ham","Ok lar... Joking wif u oni..."
"spam","Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's"
"ham","U dun say so early hor... U c already then say..."
"ham","Nah I don't think he goes to usf, he lives around here though"
"spam","FreeMsg Hey there darling it's been 3 week's now and no word back! I'd like some fun you up for it still? Tb ok! XxX std chgs to send, £1.50 to rcv"
"ham","Even my brother is not like to speak with me. They treat me like aids patent."
"ham","As per your request 'Melle Melle (Oru Minnaminunginte Nurungu Vettam)' has been set as your callertune for all Callers. Press *9 to copy your friends Callertune"
"spam","WINNER!! As a valued network customer you have been selected to receivea £900 prize reward! To claim call 09061701461. Claim code KL341. Valid 12 hours only."
"spam","Had your mobile 11 months or more? U R entitled to Update to the latest colour mobiles with camera for Free! Call The Mobile Update Co FREE on 08002986030"
"ham","I'm gonna be home soon and i don't want to talk about this stuff anymore tonight, k? I've cried enough today."
"spam","SIX chances to win CASH! From 100 to 20,000 pounds txt> CSH11 and send to 87575. Cost 150p/day, 6days, 16+ TsandCs apply Reply HL 4 info"
"spam","URGENT! You have won a 1 week FREE membership in our £100,000 Prize Jackpot! Txt the word: CLAIM to No: 81010 T&C www.dbuk.net LCCLTD POBOX 4403LDNW1A7RW18"
"ham","I've been searching for the right words to thank you for this breather. I promise i wont take your help for granted and will fulfil my promise. You have been wonderful and a blessing at all times."
"ham","I HAVE A DATE ON SUNDAY WITH WILL!!"
"spam","XXXMobileMovieClub: To use your credit, click the WAP link in the next txt message or click here>> http://wap. xxxmobilemovieclub.com?n=QJKGIGHJJGCBL"
"ham","Oh k...i'm watching here:)"
"ham","Eh u remember how 2 spell his name... Yes i did. He v naughty make until i v wet."
"ham","Fine if that's the way u feel. That's the way its gota b"
"spam","England v Macedonia - dont miss the goals/team news. Txt ur national team to 87077 eg ENGLAND to 87077 Try:WALES, SCOTLAND 4txt/ú1.20 POBOXox36504W45WQ 16+"

.......................


1. Persiapan Alat dan Bahan

Disini kita akan mempelajari klasifikasi teks berupa pesan berbahasa inggris yang akan ditebak menggunakan sms spam dataset. Pertama pastikan kamus udah memasang beberapa package Python berikut:
  • Scikit-Learn
  • Scipy
  • Numpy
  • Flask
  • Pandas
Dan untuk mendapatkan dataset, kita hanya perlu mengunduhnya dari https://github.com/stedy/Machine-Learning-with-R-datasets. Untuk struktur proyek, kamu dapat mengikuti perintah berikut:
$ mkdir spam-filter
$ cd spam-filter
$ mkdir data
$ mkdir static
$ touch spam-filter.py
Setelah itu silahkan copy dataset sms spam ke dalam folder spam-filter/data.

2. Mulai Membangun

Berbeda dengan artikel - artikel Scikit-Learn sebelumnya, disini kita akan menggunakan dataset eksternal yang akan dimuat dengan menggunakan Pandas. Pandas adalah sebuah library untuk data analysis yang memudahkan programmer Python untuk bergelut dengan bidang tersebut. Kemudian untuk merepresentasikan teks agar dapat diklasifikasi, akan kita gunakan TfIdfVectorizer. Dan pengklasifikasi yang akan kita gunakan adalah MultinomialNB salah satu varian dari Naive Bayes.

Berikut adalah kode inisial untuk aplikasi spam filter yang akan kita bangun. Silahkan buat kode berikut di dalam file spam-filter.py:

from flask import Flask, Response, request
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.cross_validation import train_test_split
from sklearn import metrics

import json
import datetime
import pandas as pd

app = Flask(__name__, static_url_path = "/static", static_folder = "static")

# menyiapkan dataset
spam_dataset = pd.read_csv("data/sms_spam.csv", delimiter=',', header=None)
X_train = spam_dataset[1]
y_train = spam_dataset[0]

# mengatur classifier
vect = TfidfVectorizer()
X_train = vect.fit_transform(X_train)

clf = MultinomialNB()
clf = clf.fit(X_train, y_train)

Pada kode diatas, kita gunakan kolom 1 sebagai feature, dan kolom 0 sebagai class. Kemudian ditransformasi oleh TfIdfVectorizer lalu dilatih oleh pengklasifikasi. Sekarang kita akan membuat sebuah endpoint "/info" yang memberitahukan seoptimal apa model pengklasifikasi yang kita siapkan. Kita membutuhkan modul metrics yang dimiliki oleh Scikit-Learn. Sebelum dilanjut, buatlah sebuah folder dengan nama static yang ditempatkan bersama file spam-filter.py.

Silahkan tambahkan sesuaikan kode sebelumnya dengan kode berikut:


.............................................................


@app.route('/info')
def info():
    # memilih dataset test
    X_train_raw, X_test_raw, y_train, y_test = train_test_split(spam_dataset[1], spam_dataset[0])

    # menguji dataset test
    vect = TfidfVectorizer()
    X_train = vect.fit_transform(X_train_raw)
    X_test = vect.transform(X_test_raw)

    clf = MultinomialNB()
    clf = clf.fit(X_train, y_train)
    predicted = clf.predict(X_test)

    # mengukur hasil test
    accuracy_ratio = metrics.accuracy_score(predicted, y_test)
    report = metrics.classification_report(y_test, predicted, target_names=['ham', 'spam'])
    confusion_matrix = metrics.confusion_matrix(y_test, predicted)

    # menyimpan ke dalam file untuk report
    f = open('static/classification_report.txt', 'w')
    for line in report:
        f.write(line)
    f.close()

    f = open('static/confusion_matrix.txt', 'w')
    for line in confusion_matrix.tolist():
        f.write("\t".join([str(x) for x in line]))
        f.write("\n")
    f.close()

    # menghasilkan response hasil pengukuran
    msg = {
        "data":{
            "accuracy": accuracy_ratio,
            "classification_report":"http://localhost:5000/static/classification_report.txt",
            "confusion_matrix":"http://localhost:5000/static/confusion_matrix.txt",
        } 
    }

    resp = Response(response=json.dumps(msg),
                    status=200, \
                    mimetype="application/json")

    return resp


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

Pada bagian function info(), kita siapkan dataset pengujian yang diambil juga dari dataset sms spam. Kemudian kita prediksi hasilnya dengan pengklasifikasi yang telah dibangun. Lalu kita ukur akurasinya dengan membandingkan antara hasil prediksi dengan target yang telah ditentukan. Kita juga buat laporan klasifikasi dan laporan confusion matrix yang di tulis ke dalam file yang disimpan di folder static.

Lalu kita berikan hasil pengukuran model kita dengan memberikan URL hasil laporan yang telah dibuat kedalam file .txt. Berikut adalah contoh hasil eksekusinya:

Selection_001 Selection_003 Selection_002

3. Membuat service untuk mengklasifikasi spam

Terakhir kita tambahkan endpoint "/predict" yang akan menerima sebuah array pesan yang akan diprediksi oleh mesin pengklasifikasi. Setiap teksnya akan diklasifikasi lalu dikemas ulang ke dalam list dan di-dump menjadi response JSON yang memperlihatkan pesan tersebut apakah spam atau ham. Silahkan tambahkan endpoint "/predict" ke dalam kode sebelumnya:

.............................................................

@app.route('/predict', methods=["POST"]) def predict(): # mengambil post data berupa json req_body = request.get_json(silent=True)

# menguji teks dari post data
test_data = vect.transform(req_body['data'])
predicted = clf.predict(test_data)
print predicted

# mengemas response
result = []

for doc, category in zip(req_body['data'], predicted):
    result.append( str('%r => %s' % (doc, category)) )

# mengirim response
msg = { 
    "data": result
}

resp = Response(response=json.dumps(msg),
                status=200, \
                mimetype="application/json")

return resp

if name=="main": app.run(debug=True)

Pada kode diatas, array pesan yang masuk akan ditransformasi oleh TfIdfVectorizer sebelum diklasifikasi. Berikut adalah contoh akses terhadap endpoint "/predict" dengan sejumlah teks yang akan diprediksi:

Selection_004

4. Kode lengkap

Untuk menghindari kesalahpahaman pada penulisan kode, berikut adalah kode full dari tutorial ini:
from flask import Flask, Response, request
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.cross_validation import train_test_split
from sklearn import metrics

import json import datetime import pandas as pd

app = Flask(name, static_url_path = "/static", static_folder = "static")

menyiapkan dataset

spam_dataset = pd.read_csv("data/sms_spam.csv", delimiter=',', header=None) X_train = spam_dataset[1] y_train = spam_dataset[0]

mengatur classifier

vect = TfidfVectorizer() X_train = vect.fit_transform(X_train)

clf = MultinomialNB() clf = clf.fit(X_train, y_train)

@app.route('/info') def info(): # memilih dataset test X_train_raw, X_test_raw, y_train, y_test = train_test_split(spam_dataset[1], spam_dataset[0])

# menguji dataset test
vect = TfidfVectorizer()
X_train = vect.fit_transform(X_train_raw)
X_test = vect.transform(X_test_raw)

clf = MultinomialNB()
clf = clf.fit(X_train, y_train)
predicted = clf.predict(X_test)

# mengukur hasil test
accuracy_ratio = metrics.accuracy_score(predicted, y_test)
report = metrics.classification_report(y_test, predicted, target_names=['ham', 'spam'])
confusion_matrix = metrics.confusion_matrix(y_test, predicted)

# menyimpan ke dalam file untuk report
f = open('static/classification_report.txt', 'w')
for line in report:
    f.write(line)
f.close()

f = open('static/confusion_matrix.txt', 'w')
for line in confusion_matrix.tolist():
    f.write("\t".join([str(x) for x in line]))
    f.write("\n")
f.close()

# menghasilkan response hasil pengukuran
msg = {
    "data":{
        "accuracy": accuracy_ratio,
        "classification_report":"http://localhost:5000/static/classification_report.txt",
        "confusion_matrix":"http://localhost:5000/static/confusion_matrix.txt",
    } 
}

resp = Response(response=json.dumps(msg),
                status=200, \
                mimetype="application/json")

return resp

@app.route('/predict', methods=["POST"]) def predict(): # mengambil post data berupa json req_body = request.get_json(silent=True)

# menguji teks dari post data
test_data = vect.transform(req_body['data'])
predicted = clf.predict(test_data)
print predicted

# mengemas response
result = []

for doc, category in zip(req_body['data'], predicted):
    result.append( str('%r => %s' % (doc, category)) )

# mengirim response
msg = { 
    "data": result
}

resp = Response(response=json.dumps(msg),
                status=200, \
                mimetype="application/json")

return resp

if name=="main": app.run(debug=True)

5. Penutup

Dengan memanfaatkan Scikit-Learn, kamu sudah dapat memulai membuat aplikasi pembelajaran mesin khususnya dalam topik klasifikasi teks. Materi yang diulas memang belum lengkap seperti preprocessing, cross validation, model testing, perbandingan dengan metode lain, ataupun memilih sumber dataset yang tepat. Namun kamu dapat memulainya lebih dini sambil mempelajari teknik lainnya dalam pembelajaran mesin.

Selamat mencoba :D.

6. Referensi

  • https://flask.pocoo.org
  • http://scikit-learn.org/stable/documentation.html
  • http://www.scipy-lectures.org/
(arslan/scikit-learn)