Ini adalah lanjutan dari bagian 1. Sebaiknya baca bagian1 tersebut jika belum mengenal tentang konsep tensor, dimensi, shape pada Keras.
Representasi Teks
Teks perlu dikonversi menjadi angka sebelum menjadi input neural network. Keras menyediakan class Tokenizer. Tokenizer ini berfungsi untuk mengkonversi teks menjadi urutan integer indeks kata atau vektor binary, word count atau tf-idf.
Contoh penggunaannya adalah sebagai berikut:
from keras.preprocessing.text import Tokenizer tokenizer = Tokenizer() texts = ["Budi makan nasi","Rudi makan nasi, nasi goreng."] tokenizer.fit_on_texts(texts) seq = tokenizer.texts_to_sequences(texts) #kalimat baru seq1 = tokenizer.texts_to_sequences(["nasi panas sekali"]) print("Index: "+str(tokenizer.word_index)) print("Seq. corpus:"+str(seq)) print("Seq. untuk 'nasi panas sekali':"+str(seq1))
Hasilnya akan seperti ini:
Indeks: {'rudi': 4, 'budi': 3, 'nasi': 1, 'makan': 2, 'goreng': 5} Seq. corpus':[[3, 2, 1], [4, 2, 1, 1, 5]] Seq. untuk 'nasi panas sekali':[[1]] Catatan: "panas" dan "sekali" tidak ada di kosakata jadi tidak ada indeksnya
Dapat dilihat kosakata corpus diubah menjadi indeks (indeks pertama “nasi”, kedua “makan” dan seterusnya). Kalimat kemudian diubah menjadi list urutan dari indeks. List sequence ini kemudian dapat dikonversi menjadi vektor matriks numpy dengan sequences_to_matrix. Terdapat empat pilihan: tf-idf, binary, count, freq.
Lanjutkan kode sebelumnya untuk mengubah representasi teks berupa urutan indeks menjadi matriks tf-idf sampai frekuensi:
encoded_tfidf = tokenizer.sequences_to_matrix(seq,mode="tfidf") print("tfidf:") print(encoded_tfidf) encoded_binary = tokenizer.sequences_to_matrix(seq,mode="binary") print("binary:") print(encoded_binary) encoded_count = tokenizer.sequences_to_matrix(seq,mode="count") print("count:") print(encoded_count) encoded_freq = tokenizer.sequences_to_matrix(seq,mode="freq") print("freq:") print(encoded_freq)
Hasilnya:
tfidf: [[0. 0.51082562 0.51082562 0.69314718 0. 0. ] [0. 0.86490296 0.51082562 0. 0.69314718 0.69314718]] binary: [[0. 1. 1. 1. 0. 0.] [0. 1. 1. 0. 1. 1.]] count: [[0. 1. 1. 1. 0. 0.] [0. 2. 1. 0. 1. 1.]] freq: [[0. 0.33333333 0.33333333 0.33333333 0. 0. ] [0. 0.4 0.2 0. 0.2 0.2
Hasil sudah berbentuk numpy array. Dapat dilihat padding dilakukan otomatis untuk menyamakan dimensi dengan shape (2,6). Data ini dapat langsung digunakan dalam proses pembuatan model. Alternatif lain adalah menggunakan embedded layer yang akan dibahas dalam posting berikutnya.
Jika proses padding ingin dilakukan secara manual, Keras menyediakan pad_sequences. Contoh penggunaan pad_sequences :
from keras.preprocessing.sequence import pad_sequences print("Sebelum padding:") print(seq) X = pad_sequences(seq) print("Sesudah padding:") print(X) print(X.shape)
Hasilnya:
Sebelum padding: [[3, 2, 1], [4, 2, 1, 1, 5]] Sesudah padding: [[0 0 3 2 1] [4 2 1 1 5]] Shape: (2, 5)
Klasifikasi Teks
Dalam bagian ini, akan dilakukan klasifikasi teks menggunakan data SMS spam berbahasa Indonesia dengan arsitektur yang paling sederhana yaitu feed forward NN.
Data dapat didownload di:http://bit.ly/yw_sms_spam_indonesia
Jumlah sample 1143 dan ada tiga kelas dalam dataset ini:
0: sms normal (569 instance)
1: fraud atau penipuan (335 instance)
2: promo (239 instance)
Langkah pertama adalah meload data dari file csv, dapat digunakan library csv. Tambahkan cell berikut.
import csv nama_file = "C:\\yudiwbs\\data\\sms\\dataset_sms_spam_v1.csv" data = [] label = [] with open(nama_file, 'r', encoding='utf-8') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') next(reader) #skip header for row in reader: data.append(row[0]) label.append(row[1]) #test lihat dua data pertama print(data[:2]) print(label[:2]) #Catatan: parameter encoding dapat dibuang jika muncul error
Alternatif lain adalah menggunakan pandas untuk membaca csv:
import pandas as pd df = pd.read_csv(nama_file).values data = df[:, 0] label = df[:, 1]
Selanjutnya konversi label dari “1”, “2”, “3” menjadi representasi tensor:
from keras.utils import to_categorical label = to_categorical(label) print(label.shape) print(label)
Hasilnya adalah Tensor 2D dengan shape (1143, 3) untuk label, karena ada 1143 instance dengan 3 nilai label yang mungkin (normal, fraud, promo)
Split data menjadi train dan test menggunakan scikit learn, 80% menjadi data train, 20% menjadi data test.
#split jadi train-test from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(data, label, test_size=0.2, random_state=123)
Konversi data teks menjadi tf-idf dan tensor. Pastikan Fit hanya dilakukan pada data train untuk mencegah informasi di data test “bocor”.
#konversi teks ke tfidf from keras.preprocessing.text import Tokenizer tokenizer = Tokenizer() #fit hanya berdasarkan data train tokenizer.fit_on_texts(X_train) #konversi train seq_x_train = tokenizer.texts_to_sequences(X_train) X_enc_train = tokenizer.sequences_to_matrix(seq_x_train,mode="tfidf") #konversi test seq_x_test = tokenizer.texts_to_sequences(X_test) X_enc_test = tokenizer.sequences_to_matrix(seq_x_test,mode="tfidf") print(X_enc_train.shape) print(X_enc_test.shape) print(X_enc_train)
Hasilnya adalah tensor 2D dengan shape (914, 4384) untuk data train dan tensor 2D (229, 4384) untuk data test.
Selanjutnya siapkan model dengan menambahkan layer
from keras import models from keras import layers _,jum_fitur = X_enc_train.shape model = models.Sequential() model.add(layers.Dense(32,activation='relu',input_shape=(jum_fitur,))) model.add(layers.Dense(4,activation='relu')) model.add(layers.Dense(3,activation='softmax')) #karena kelasnya ada 3 model.compile(optimizer="adam", loss='categorical_crossentropy', metrics=['accuracy'])
Ada empat layer: layer pertama adalah layer input hasil encode tf-idf sebelumnya: 4384 fitur. Mengapa input_shape tidak menggunakan sample dimension atau sample axis seperti (914, 4384)? karena jumlah samples tidak penting didefinisikan dalam layer input. Dengan teknik mini-batch, sample dapat diproses sedikit demi sedikit, jadi jumlahnya bisa berbeda-beda.
Activation softmax digunakan karena jumlah label ada 3 (normal, fraud dan promo). Jika jumlah label dua (binary classification) maka dapat digunakan activation sigmoid. Setelah layer didefinisikan, maka layer dapat dicompile. Loss categorical_crossentropy dipilih karena terdapat tiga kelas, sedangkan jika untuk binary class dapat digunakan binary_crossentropy.
Kode untuk melakukan training adalah sebagai berikut:
history = model.fit(X_enc_train,y_train, epochs=3, batch_size=2, validation_split=0.2) results = model.evaluate(X_enc_test, y_test) print("Hasil [loss,acc] untuk data test:") print(results)
Satu epoch adalah satu iterasi yang diperlukan untuk memproses seluruh training data. Jika jumlah data training 1000, dan batch_size 20, maka untuk memproses setiap epoch akan diperlukan 1000/20 = 50 steps update bobot. Pada setiap step bobot network akan di-update.
Semakin kecil batch size, semakin kecil memori yang diperlukan dan proses akan konvergen lebih cepat. Kelemahannya, akan memerlukan semakin banyak steps dalam setiap epoch (waktu training semakin lama). Parameter validation_split menentukan persentase data yang akan digunakan untuk data validasi.
Data validasi diambil dari data train dan digunakan untuk meminimalkan nilai loss pada saat training, val_acc dan val_loss adalah metrik untuk data validasi ini. Setelah proses selesai baru kinerja diukur pada data test.
Setelah training selesai, hasilnya adalah sebagai berikut, untuk data test didapat loss 0.33 dan akurasi 0.926 (komputer yang berbeda dapat menghasilkan hasil berbeda):
Proses training dapat memerlukan waktu lama, untuk menyimpan model dan hasil tokenizer ke dalam file gunakan kode berikut:
import pickle model.save('model_spam_v1.h5') with open('tokenizer.pickle', 'wb') as handle: pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)
Berikut adalah kode untuk me-load model, tokenizer dan memprediksi label untuk data baru:
from keras.models import load_model import pickle model = load_model('model_spam_v1.h5') with open('tokenizer.pickle', 'rb') as handle: tokenizer = pickle.load(handle) s = ["Anda mendapat hadiah 100 juta","Beli paket Flash mulai 1GB", "Nanti ketemuan dimana?"] seq_str = tokenizer.texts_to_sequences(s) enc_str = tokenizer.sequences_to_matrix(seq_str,mode="tfidf") enc_str.shape pred = model.predict_classes(enc_str) print("Prediksi kelas string ' {} ' adalah {}".format(s,pred))
Hasilnya:
Prediksi kelas string ‘ [‘Anda mendapat hadiah 100 juta, ‘Beli paket Flash mulai 1GB’, ‘Nanti ketemuan dimana?’] ‘ adalah [1 2 0]
Bersambung.. (word embedding, RNN)
Update Juni 2020: artikel ini tidak akan dibuat kelanjutannya, tapi materi lengkap tentang ini bisa dilihat di: https://docs.google.com/document/d/1SQkzjjBdjCNO7cexAAy9s0tGvTsKf1WUJiPrkKPLI-4/edit?usp=sharing
Artikel yg sangat bagus, menjelaskan jargon2 deep learning dengan bahasa yg mudah dipahami. Ditunggu artikel yg selanjutnya Pak! p.s. ada sedikit typo di dimensi shape
Makasih. Yg mana ya? tadi dicari nggak kelihatan.
“dimensi dengan shape (2,6)” seharusnya (2,5) bukan? atau saya yang keliru, maklum baru belajar
Kalau melihat ouputnya memang (2,6), walaupun jumlah vocabnya 5. Indeks pertama selalu bernilai nol. Tapi saya juga tidak tahu kenapa shape output sequences_to_matrix tsb tidak (2,5) saja ya. Nanti saya coba lihat dokumentasinya.
artikel yang selalu bermanfaat tentang NLP-Python, sukses terus pak Yudi.
saya mahasiswa dari surabaya yang sedang riset di bidang NLP, sangat terbantu dengan adanya artikel2 dari bapak.
“Bersambung.. (word embedding, RNN)”. Belum ada artikelnya ya pak?
maaf memang belum. Keburu ada beberapa kerjaan yg tidak terkait dengan NLP jadi ya begitu 🙂
Mohon maaf, saya masih belum paham untuk layernya
model.add(layers.Dense(32,activation=’relu’,input_shape=(jum_fitur,)))
model.add(layers.Dense(4,activation=’relu’))
model.add(layers.Dense(3,activation=’softmax’))
Knp layernya diisi dengan 32, 4, 3?
Boleh minta tolong untuk penjelasan detailnya?
Terima kasih banyak
Coba-coba. Ini salah satu masalah utama di deep learning. Perlu coba-coba konfigurasi. Memang sudah ada usaha dan riset tentang ini (coba googling tentang AutoML), tapi sepertinya masih belum optimal juga.
Berarti nilai itu didapat melalui beberapa percobaan ya pak?
Jika dirasa output yg dihasilkan sudah bagus maka nilai itu yg dipakai?
Ya betul, bukan beberapa lagi tapi banyak percobaan.
Maaf Pak, saya mau tanya, apakah ada link google docs untuk modul pelatihan bagian 1? Karena dari link yang terlampir saya hanya dapat mengakses bagian 2 dan 3. Terima kasih, Pak
Bagian pertama tentang machine learning secara umum (non teks)