Bagian 2: Mengklasifikasi Sentimen pada Ulasan Produk¶

Setelah mengumpulkan data di artikel 1, saatnya kita mencoba untuk melakukan klasifikasi terhadap sentimen dari tiap ulasan tersebut, positif, negatif atau netral.

In [1]:
# import lirary yang dibutuhkan
import pandas as pd
import numpy as np
import re
import matplotlib.pyplot as plt
import seaborn as sns
In [2]:
# lokasi dataset
data_path = r'../data/review_data.csv'
In [3]:
# membaca dataset
data = pd.read_csv(data_path)
In [4]:
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 689 entries, 0 to 688
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   review  689 non-null    object
 1   rating  689 non-null    int64 
dtypes: int64(1), object(1)
memory usage: 10.9+ KB
In [5]:
pd.set_option('display.max_colwidth', None)
In [ ]:
 

1. Cleaning / Pembersihan Data¶

Seperti yang sudah kita lihat saat pengumpulan data, dataset kita perlu dibersihkan.

Masalah yang perlu dibersihkan:¶

  1. Upper case (Huruf kapital)
  2. Titik-titik random
  3. Emoticon😁
  4. Tanda-tanda baca
  5. Angka
  6. Spasi berlebih
In [6]:
# fungsi pembersihan data
def text_cleaning(text):
    result = text.lower() # membuat huruf kecil
    result = result.replace('-', ' ') # menghilangkan tanda baca
    result = result.replace('+', ' ')
    result = result.replace('..', ' ')
    result = result.replace('.', ' ')
    result = result.replace(',', ' ')
    result = result.replace('\n', ' ') # menghilangkan garis baru
    result = re.findall('[a-z\s]', result, flags=re.UNICODE) # menghilangkan emoticon
    result = "".join(result)
    final = " ".join(result.split())
    
    return final    # teks bersih
In [7]:
# Membersihkan data
data['cleaned_review'] = data.review.apply(lambda r: text_cleaning(r))
In [8]:
data.sample(frac=.1).head(50)
Out[8]:
review rating cleaned_review
287 Rasanya unik. Tapi aku lebih suka indomie rendang ya soalnya kalau rasa salted egg kok kaya amis2 gitu jdi aku kurang suka aja. 3 rasanya unik tapi aku lebih suka indomie rendang ya soalnya kalau rasa salted egg kok kaya amis gitu jdi aku kurang suka aja
14 Baunya tidak enak seperti bau obat ketika dimakan rasanya aneh apalagi ketika bersendawa rasanya sangat tidak nyaman 1 baunya tidak enak seperti bau obat ketika dimakan rasanya aneh apalagi ketika bersendawa rasanya sangat tidak nyaman
483 Kertas minyak ini ga nyerap minyak di muka secara maximal. Masih kalah banget sama clean and clear. Kertasnya pun lebih mudah sobek. Warnanya kaya putih gitu lebih susah di liatnya seberapa minyak yang keserep. Ga kaya clean and clear yang gampang banget di liatnya. Repurchase? No. 2 kertas minyak ini ga nyerap minyak di muka secara maximal masih kalah banget sama clean and clear kertasnya pun lebih mudah sobek warnanya kaya putih gitu lebih susah di liatnya seberapa minyak yang keserep ga kaya clean and clear yang gampang banget di liatnya repurchase no
551 pertama kali coba krn tergiur harganya yg murah. so far sih masih oke buat touch up kl make up 1-2jam sekali. hanya sayang sekali kertasnya mudah robek. malah jatohnya jadi boros kl pake produk ini. 3 pertama kali coba krn tergiur harganya yg murah so far sih masih oke buat touch up kl make up jam sekali hanya sayang sekali kertasnya mudah robek malah jatohnya jadi boros kl pake produk ini
620 Lumayan menyerap keringat di muka guys abis panas2an 4 lumayan menyerap keringat di muka guys abis panasan
634 Aku udah coba ovale face paper,harganya terjanku, bagus untuk menyerap minyak, sayang kertasnya gampang sobek 4 aku udah coba ovale face paper harganya terjanku bagus untuk menyerap minyak sayang kertasnya gampang sobek
379 Indomie mi sedap sarimi saya selalu stok per bulan dengan aneka rasa tapi tetap saya masak paling banyak satu Minggu 2 kali, rasanya mantap anak dan suami suka biasanya tak tambah telur dan sayuran mantaap enak 5 indomie mi sedap sarimi saya selalu stok per bulan dengan aneka rasa tapi tetap saya masak paling banyak satu minggu kali rasanya mantap anak dan suami suka biasanya tak tambah telur dan sayuran mantaap enak
140 Rasanya biasa aja, lebih enakan indomie goreng yang biasa. ini ga berasa salted eggnya sama sekali. tapi kalo dicium sih ada bau2 amisnya gitu dikit. 3 rasanya biasa aja lebih enakan indomie goreng yang biasa ini ga berasa salted eggnya sama sekali tapi kalo dicium sih ada bau amisnya gitu dikit
445 Produk baru Danone Aqua ini must try banget. Pertama kali cobain perpaduan legitnya teh jasmine & harumnya daun pandan. Enak banget. Tapi kalo untuk aku rasanya agak kurang manis sih. Rasanya unik & harus dicoba! 4 produk baru danone aqua ini must try banget pertama kali cobain perpaduan legitnya teh jasmine harumnya daun pandan enak banget tapi kalo untuk aku rasanya agak kurang manis sih rasanya unik harus dicoba
257 rasanya bukan kaya salted egg tp kaya pecel madiun hehehe. entah apa lidah saya yang aneh. tapi ga nampol ga sesuai ekspektasi salted eggnya. mon maap. hehehe semoga bisa semakin baik yaa indomie 3 rasanya bukan kaya salted egg tp kaya pecel madiun hehehe entah apa lidah saya yang aneh tapi ga nampol ga sesuai ekspektasi salted eggnya mon maap hehehe semoga bisa semakin baik yaa indomie
324 Menurut aku ini indomie fav nomor 2 setelah yang original, enak 5 menurut aku ini indomie fav nomor setelah yang original enak
167 Mienya kenyal tapi kurang nendang rasa telur asinnya 3 mienya kenyal tapi kurang nendang rasa telur asinnya
525 Minyak di muka ke angkat sihh tapi gampang rusak kertasnya. Tapi untuk harga segitu sesuai sihh 3 minyak di muka ke angkat sihh tapi gampang rusak kertasnya tapi untuk harga segitu sesuai sihh
461 Pertama kali liat unik dibagian botolnya sih design botolnya keren banget jadi tertarik buat beli. Untuk rasa inituh ringan banget ngga seret ditenggorokan lumayan unik sih rasanya, buat yang penasaran coba deh ini unik banget 5 pertama kali liat unik dibagian botolnya sih design botolnya keren banget jadi tertarik buat beli untuk rasa inituh ringan banget ngga seret ditenggorokan lumayan unik sih rasanya buat yang penasaran coba deh ini unik banget
512 So it wasn't my own purchase. Jadi adek aku yang beli dan aku ikutan nyoba karena micellar water yang biasa dipake abis. Waktu aku pake berasa tingly gitu terus panas-panas gimana. Aku buat hapus eye makeup, GOOOSH it stings so bad! Di adek aku sih gak kenapa-kenapa, cocok aja. Dia oily acne prone skin, sedangkan aku dry sensitive. Aku coba sampai tiga kali pemakaian and I just can't use it anymore. It's just not for me. 2 so it wasnt my own purchase jadi adek aku yang beli dan aku ikutan nyoba karena micellar water yang biasa dipake abis waktu aku pake berasa tingly gitu terus panas panas gimana aku buat hapus eye makeup gooosh it stings so bad di adek aku sih gak kenapa kenapa cocok aja dia oily acne prone skin sedangkan aku dry sensitive aku coba sampai tiga kali pemakaian and i just cant use it anymore its just not for me
572 Ovale face paper membantu aku banget klo lagi harus pakai makeup juga klo lagi cuaca panas. 3 ovale face paper membantu aku banget klo lagi harus pakai makeup juga klo lagi cuaca panas
378 why everybody hates this product?? i do love this hahhahaa\nrasanya creamy enak gitu kok, kaya eneg2 enak gitu, udah gitu ngenyangin banget, enak lah, pakai sosis tambah ena ena 5 why everybody hates this product i do love this hahhahaa rasanya creamy enak gitu kok kaya eneg enak gitu udah gitu ngenyangin banget enak lah pakai sosis tambah ena ena
315 Menurut saya varian baru indomie ini sangat enak, rasa salted egg berasa namun tidak bikin eneg. Ditambah mie nya yg beda dengan indomie biasa, mie lebih keriting dan pipih karena ini termasuk premium collection dari indomie ya.. 5 menurut saya varian baru indomie ini sangat enak rasa salted egg berasa namun tidak bikin eneg ditambah mie nya yg beda dengan indomie biasa mie lebih keriting dan pipih karena ini termasuk premium collection dari indomie ya
213 Pertama dimulai dari ke hebohan di medsos dan teman" yang sudah pernah mencoba Indomie Salted Egg ini. Jadi penasaran. Akhinya ketemu di salahsatu minimarket.\nMenurut saya rasanya lumayan, cuma ada bau aneh seperti bau rambut terbakar di bumbunya, bau yang bikin kurang nyaman ketika makan, dan susah sekali hilang walaupun sesudah makan kita langsung kumur" dan gosok gigi.\nIndomie harus mengevaluasi lagi bumbu Salted Egg ini terutama masalah baunya yang mengganggu. 3 pertama dimulai dari ke hebohan di medsos dan teman yang sudah pernah mencoba indomie salted egg ini jadi penasaran akhinya ketemu di salahsatu minimarket menurut saya rasanya lumayan cuma ada bau aneh seperti bau rambut terbakar di bumbunya bau yang bikin kurang nyaman ketika makan dan susah sekali hilang walaupun sesudah makan kita langsung kumur dan gosok gigi indomie harus mengevaluasi lagi bumbu salted egg ini terutama masalah baunya yang mengganggu
151 wah dari awal produk ini launching udah penasaran banget, setelah hunting online dengan harga yang menggila untuk ukuran Mie Instant, akhirnya dapet juga di supermarket. saya pribadi jauh lebih suka dengan Indomie Soto Mie atau Kari Ayam.\nrasanya yang ini sangat ga cocok, dan belakangan ini saya liihat di beberapa supermarket ada penurunan harga. kenapa ya? 3 wah dari awal produk ini launching udah penasaran banget setelah hunting online dengan harga yang menggila untuk ukuran mie instant akhirnya dapet juga di supermarket saya pribadi jauh lebih suka dengan indomie soto mie atau kari ayam rasanya yang ini sangat ga cocok dan belakangan ini saya liihat di beberapa supermarket ada penurunan harga kenapa ya
198 Mie dengan cita rasa baru dengan bumbu yang melimpah dan tekstur yang kenyal, sayang bumbu yg kebanyakan malah bikin enek 3 mie dengan cita rasa baru dengan bumbu yang melimpah dan tekstur yang kenyal sayang bumbu yg kebanyakan malah bikin enek
70 Tekstur dari mienya sih suka, cuma habis makan mie yg varian ini di tenggorokan ada rasa aneh yg tertinggal gitu 🤢.\nSeperti habis makan daun apa gitu, bahkan sendawa pun rasanya gak enak. 2 tekstur dari mienya sih suka cuma habis makan mie yg varian ini di tenggorokan ada rasa aneh yg tertinggal gitu seperti habis makan daun apa gitu bahkan sendawa pun rasanya gak enak
108 Tekstur mie kenyal dan enak hanya saja aromanya tidak terlalu enak seperti bau obat dan juga rasanya tidak seperti salted egg 2 tekstur mie kenyal dan enak hanya saja aromanya tidak terlalu enak seperti bau obat dan juga rasanya tidak seperti salted egg
688 Waktu itu saya beli karena murah dan isinya banyak. Setengah harga sendiri dari si biru hhe. Daya serapnya ya lumayan lah, untuk serap minyak di wajah saya gak cukup satu yg di tempel :D. Wajah gak minyakan dan kantong gak jebol kalo pake kertas minyak\ndari ovale. 4 waktu itu saya beli karena murah dan isinya banyak setengah harga sendiri dari si biru hhe daya serapnya ya lumayan lah untuk serap minyak di wajah saya gak cukup satu yg di tempel d wajah gak minyakan dan kantong gak jebol kalo pake kertas minyak dari ovale
513 Sempat coba beberapa tahun lalu. Setelah pemakaian kulit wajah terasa panas. Baunya menyengat. 2 sempat coba beberapa tahun lalu setelah pemakaian kulit wajah terasa panas baunya menyengat
19 Entah ya... Lidah ku yg gak bermasalah, atau emang rasanya gak enak.. :( bener2 aromanya bikin aku mual & pas makan bumbunya nempel gitu di tenggorokan, walaupun udah banyak minum & sikat gigi berkali2 tetep aja susah ilang. :( 1 entah ya lidah ku yg gak bermasalah atau emang rasanya gak enak bener aromanya bikin aku mual pas makan bumbunya nempel gitu di tenggorokan walaupun udah banyak minum sikat gigi berkali tetep aja susah ilang
55 Saya sudah mencoba, tidak ada rasa telor asin nya. Rasa nya cukup aneh, sayang varian yg ini booming tapi gagal di rasa. 2 saya sudah mencoba tidak ada rasa telor asin nya rasa nya cukup aneh sayang varian yg ini booming tapi gagal di rasa
116 Tekstur dari mienya sih suka, cuma habis makan mie yg varian ini di tenggorokan ada rasa aneh yg tertinggal gitu 🤢.\nSeperti habis makan daun apa gitu, bahkan sendawa pun rasanya gak enak. 2 tekstur dari mienya sih suka cuma habis makan mie yg varian ini di tenggorokan ada rasa aneh yg tertinggal gitu seperti habis makan daun apa gitu bahkan sendawa pun rasanya gak enak
522 Affordable buat dibeli, lumayan menyarap minyak, amun sayangnya gampang sobek dan butuh lebih banyak lembar untuk menghilangkan minyak diwajah. Tapi aman banget kalo dibawa ke sekolah dan diminta temen-temen, jadi awet dan hemat :) 3 affordable buat dibeli lumayan menyarap minyak amun sayangnya gampang sobek dan butuh lebih banyak lembar untuk menghilangkan minyak diwajah tapi aman banget kalo dibawa ke sekolah dan diminta temen temen jadi awet dan hemat
553 harga lebih murah, mudah dicari di apotek/toko kosmetik terdekat, daya serap minyak kurang harus pake beberapa lembar untuk benar benar menyerap minyak 3 harga lebih murah mudah dicari di apotektoko kosmetik terdekat daya serap minyak kurang harus pake beberapa lembar untuk benar benar menyerap minyak
41 Pas coba langsung gk suka sama rasa indomie goreng salted egg ini krna rasa dan aroma telor asin nya ngebuat mie nya jd amis dan gk enak di lidahku 2 pas coba langsung gk suka sama rasa indomie goreng salted egg ini krna rasa dan aroma telor asin nya ngebuat mie nya jd amis dan gk enak di lidahku
613 Wah ini sih produk legend nih. Andalan banget karena kulit wajah aku tipenya emang yang berminyak banget. walaupun tipis kertasnya, tp ampuh sih. Keserap semua minyak diwajah aku. 4 wah ini sih produk legend nih andalan banget karena kulit wajah aku tipenya emang yang berminyak banget walaupun tipis kertasnya tp ampuh sih keserap semua minyak diwajah aku
25 Kurang terasa seperti salted egg saat dimakan dalam keadaan panas, juga tidak ada rasa khas dari Indomie. Coba makan dalam keadaan dingin 1 kurang terasa seperti salted egg saat dimakan dalam keadaan panas juga tidak ada rasa khas dari indomie coba makan dalam keadaan dingin
2 Waktu pertama melihat produknya, saya cukup excited karena saya suka sekali dengan produk berbau 'salted egg'. Ternyata Indomie varian ini tidak sesuai dengan ekspetasi saya. Perpaduan tekstur mie dan saus salted egg ternyata agak 'menjijikkan'. Sama sekali tidak cocok. 1 waktu pertama melihat produknya saya cukup excited karena saya suka sekali dengan produk berbau salted egg ternyata indomie varian ini tidak sesuai dengan ekspetasi saya perpaduan tekstur mie dan saus salted egg ternyata agak menjijikkan sama sekali tidak cocok
226 Not recommended.. tetep indomie goreng original yg paling juara.. 3 not recommended tetep indomie goreng original yg paling juara
428 Coba ini karena penasaran ada varian teh baru dan unik banget rasanya. Setelah coba rasanya sih sama aja kayak teh biasa tapi memang wanginya unik banget ada yang persis kayak pandan 4 coba ini karena penasaran ada varian teh baru dan unik banget rasanya setelah coba rasanya sih sama aja kayak teh biasa tapi memang wanginya unik banget ada yang persis kayak pandan
505 Sebagai product yang diperuntukkan buat orang orang yang acne prone menurut aku ga sesuai sih, alkoholnya itu kerasa banget kenceng. Selama pemakaian juga ga ngerasa nyaman karena ngerasa berdosa gitu sama muka dikasi produk yang ga ramah :( 2 sebagai product yang diperuntukkan buat orang orang yang acne prone menurut aku ga sesuai sih alkoholnya itu kerasa banget kenceng selama pemakaian juga ga ngerasa nyaman karena ngerasa berdosa gitu sama muka dikasi produk yang ga ramah
199 Enak sih menurutku, tapi kayak ngga ada rasa2 salted egg nya malah seperti rasa indomie kari tapi ngga pakai kuah dan agak seperti keju2 gitu, bingung juga hehe. Tapi kayaknya kalau kebanyakan bisa bikin agak mual karena mungkin saya emang lebih suka yang pedes2 daripada yang asin2. 3 enak sih menurutku tapi kayak ngga ada rasa salted egg nya malah seperti rasa indomie kari tapi ngga pakai kuah dan agak seperti keju gitu bingung juga hehe tapi kayaknya kalau kebanyakan bisa bikin agak mual karena mungkin saya emang lebih suka yang pedes daripada yang asin
547 kurang mampu mengangkat minyak di wajah sehingga harus menggunakan lebih dari satu lembar, lembarannya tipis sehingga mudah robek 3 kurang mampu mengangkat minyak di wajah sehingga harus menggunakan lebih dari satu lembar lembarannya tipis sehingga mudah robek
23 Saya tertarik dengan kemasan yang bagus namun setelah di masak baunya aneh ga seperti telor asin. rasanya neg. pokoknya ga seperti telor asin. padahal aku suka udang telor asin tp di restoran 1 saya tertarik dengan kemasan yang bagus namun setelah di masak baunya aneh ga seperti telor asin rasanya neg pokoknya ga seperti telor asin padahal aku suka udang telor asin tp di restoran
503 Buat aku rasanya kurang enak. Rasa buahnya aneh cenderung masam. 2 buat aku rasanya kurang enak rasa buahnya aneh cenderung masam
73 Thanks Indomie sudah membuat produk yang sesuai dengan favorit masyarakat saat ini.\nTekstur Mie : Oke, seperti biasanya\nRasa : Mengingat saya adalah pencinta makanan berbumbu telur asin, rasa dari Indomie Salted Egg ini belum cukup memuaskan lidah perasa saya, di mana tekstur bumbu dan rasa, cukup berbeda jauh dari real salted Egg. 2 thanks indomie sudah membuat produk yang sesuai dengan favorit masyarakat saat ini tekstur mie oke seperti biasanya rasa mengingat saya adalah pencinta makanan berbumbu telur asin rasa dari indomie salted egg ini belum cukup memuaskan lidah perasa saya di mana tekstur bumbu dan rasa cukup berbeda jauh dari real salted egg
104 Pertama di buka bumbunya bau nyengat. Setelah matang dan saya aduk bumbunya sm mie nya lengket. Rasanya tidak enak, lebih baik indomie goreng yang biasa. 2 pertama di buka bumbunya bau nyengat setelah matang dan saya aduk bumbunya sm mie nya lengket rasanya tidak enak lebih baik indomie goreng yang biasa
301 Aku suka banget sm indomie salted egg bener-bener terasa bgt salted egg, bagi yang penyuka saltedd bisa di coba nih 5 aku suka banget sm indomie salted egg bener bener terasa bgt salted egg bagi yang penyuka saltedd bisa di coba nih
498 Nggak tau apa yang salah sama lidah gue, memang rasanya aneh sih. Kurang begitu suka sama varian yang ini, kalau rasa yang lain udah yahud.. 2 nggak tau apa yang salah sama lidah gue memang rasanya aneh sih kurang begitu suka sama varian yang ini kalau rasa yang lain udah yahud
184 Dari pertama kali coba sudah tidak suka dengan aromanya. Ibu saya bilang aromanya seperti benda terbakar. Lebih enak indomie goreng biasa. Pas sendawa pun masih terasa aroma yang tidak enak itu. Padahal tekstur mienya sangat pas. 3 dari pertama kali coba sudah tidak suka dengan aromanya ibu saya bilang aromanya seperti benda terbakar lebih enak indomie goreng biasa pas sendawa pun masih terasa aroma yang tidak enak itu padahal tekstur mienya sangat pas
614 Dulu iseng beli karena pas aku liat harganya murah, isinya banyak lagi. Produk ini bentuk kertasnya mirip kertas novel (warnanya coklat) tipis banget. Daya serapnya lumayan bagus, cuma perlu di tap-tap aja. Tapi, anyway lebih baik minyak di muka tidak benar-benar hilang ya, karena nanti justru malah lebih berminyak setelahnya. 4 dulu iseng beli karena pas aku liat harganya murah isinya banyak lagi produk ini bentuk kertasnya mirip kertas novel warnanya coklat tipis banget daya serapnya lumayan bagus cuma perlu di tap tap aja tapi anyway lebih baik minyak di muka tidak benar benar hilang ya karena nanti justru malah lebih berminyak setelahnya
584 Pertama kali coba rasanya aneh mungkin karena biasa makan bakmi asli\nNi bakmi instan pertama kali aku coba 3 pertama kali coba rasanya aneh mungkin karena biasa makan bakmi asli ni bakmi instan pertama kali aku coba
327 Pas pertama aku coba, ternyata mie ini emang enak banget, aku suka, dan bikin ketagihan, menurut lidahku si seperti itu guys, 5 pas pertama aku coba ternyata mie ini emang enak banget aku suka dan bikin ketagihan menurut lidahku si seperti itu guys
497 Biasa aja sih menurut aku, gak sebanding sama harganya, kirain yang waah gitu rasanya.. 2 biasa aja sih menurut aku gak sebanding sama harganya kirain yang waah gitu rasanya

Dataset sudah dibersihkan

Stopwords dan merk¶

In [9]:
# file stopwords
stopword_path = r'../data/stopword_indonesia.txt'
In [ ]:
 

Berhubung tidak ada list resmi stopwords dalam bahasa Indonesia, saya menggunakan 2 list stopwords. Satu list dari sastrawi dan list lain dari Tala, F. Z. (2003). A Study of Stemming Effects on Information Retrieval in Bahasa Indonesia. M.S. thesis. M.Sc. Thesis. Master of Logic Project. Institute for Logic, Language and Computation. Universiteti van Amsterdam The Netherlands.

List Stopwords pertama yang digunakan berasal dari library sastrawi.

In [10]:
from Sastrawi.StopWordRemover.StopWordRemoverFactory import StopWordRemoverFactory
In [11]:
# First stopwords list
factory = StopWordRemoverFactory()
sastrawi_stopwords = factory.get_stop_words() 
In [12]:
sastrawi_stopwords[:20]
Out[12]:
['yang',
 'untuk',
 'pada',
 'ke',
 'para',
 'namun',
 'menurut',
 'antara',
 'dia',
 'dua',
 'ia',
 'seperti',
 'jika',
 'jika',
 'sehingga',
 'kembali',
 'dan',
 'tidak',
 'ini',
 'karena']

List stopwords kedua adalah gabungan dari stopwords (Tala, 2003) dengan stopwords sastrawi.

In [13]:
# Second stopwords list
with open(stopword_path) as f:
    teks = f.readlines()
f.close()
In [14]:
tala_stopwords = [x.replace('\n', '') for x in teks]
In [15]:
full_stopwords = tala_stopwords + sastrawi_stopwords
In [16]:
len(full_stopwords)
Out[16]:
884
In [ ]:
 
In [17]:
# Merk dan kata-kata lainnya
kata_lain = ['mie', 'danone', 'indomie', 'aqua', 'salted', 'egg', 'aja', 'gak', 'yg', 'ga', 'tp', 'sih', 'nya']
In [ ]:
 

Membuat fungsi untuk membuang stopwords¶

In [18]:
# Membuang stopwords berdasarkan list dari sastrawi
def manual_stopwords_remover_sastrawi(text):
    teks = text.split(' ')
    teks = [ w for w in teks if w not in sastrawi_stopwords]
    teks = [ w for w in teks if w not in kata_lain]
    teks = " ".join(teks)
    return teks
In [19]:
# Membuang stopwords dari gabungan list Tala dan sastrawi
def manual_stopwords_remover_full(text):
    teks = text.split(' ')
    teks = [ w for w in teks if w not in full_stopwords]
    teks = [ w for w in teks if w not in kata_lain]
    teks = " ".join(teks)
    return teks
In [20]:
# Mencoba membuang stopwords pada sampel data
cth = data.loc[371,'cleaned_review']
cth
Out[20]:
'dari begitu banyak varian indomie varian indomie salted egg ini favorit saya rasa telur asinnya enak padahal saya bukan pecinta telur asin tapi begitu mencicipi indomie salted egg saya ketagihan rasa dan tekstur mie nya mantap'
In [21]:
manual_stopwords_remover_sastrawi(cth)
Out[21]:
'banyak varian varian favorit rasa telur asinnya enak padahal bukan pecinta telur asin mencicipi ketagihan rasa tekstur mantap'
In [22]:
manual_stopwords_remover_full(cth)
Out[22]:
'varian varian favorit telur asinnya enak pecinta telur asin mencicipi ketagihan tekstur mantap'
In [ ]:
 

Membuang stopwords dengan membuat dataframe baru¶

In [23]:
data_lengkap = data.copy()
In [25]:
# Data yang dibersihkan dengan list stopwords sastrawi
data_lengkap['review_no_stopword'] = data_lengkap.cleaned_review.apply(manual_stopwords_remover_sastrawi)
In [ ]:
 
In [26]:
data_singkat = data.copy()
In [27]:
# Data yang dibersihkan dengan list stopwords Tala
data_singkat['review_no_stopword'] = data_singkat.cleaned_review.apply(manual_stopwords_remover_full)
In [ ]:
 
In [ ]:
 

Stemming¶

Dalam bahasa, kita menambahkan berbagai imbuhan untuk memberikan makna yang berbeda. Misal kata dasar "makan" menjadi "makanan", "memakan" dll. Walau memiliki makna yang berbeda, kata-kata yang berimbuhan dapat dianggap memiliki makna yang sama dengan kata dasarnya. Proses mengubah kata berimbuhan ke kata dasar disebut Stemming. Selain menyederhanakan proses analisis, stemming dapat mengurangi jumlah kata yang harus diproses dan menghemat memori.

Proses Stemming dalam proyek ini dilakukan menggunakan fungsi create_stemmer() dari library sastrawi.

In [28]:
from Sastrawi.Stemmer.StemmerFactory import StemmerFactory
def Stemming(text):
  factory= StemmerFactory()
  stemmer = factory.create_stemmer()
  text = text.split()
  hasil = [stemmer.stem(token) for token in text]
  hasil = " ".join(hasil)
  return hasil
In [29]:
from Sastrawi.Stemmer.StemmerFactory import StemmerFactory
factory = StemmerFactory()
stemmer = factory.create_stemmer()

Contoh kalimat hasil Stemming.

In [30]:
contoh = data_lengkap.iloc[131, -1]
contoh
Out[30]:
'dulu sempat hits penasaran blm daerah minimarket langsung deh beli membayangkan nikmatnya rasa berbalut bumbu penggemar makanan berbumbu mencoba sesuai ekspektasi maaf lebih suka rasa varian lebih masuk lidah tetap setia makan'
In [31]:
stemmer.stem(contoh)
Out[31]:
'dulu sempat hits penasaran blm daerah minimarket langsung deh beli bayang nikmat rasa balut bumbu gemar makan bumbu coba sesuai ekspektasi maaf lebih suka rasa varian lebih masuk lidah tetap setia makan'

Perhatikan bahwa kata "membayangkan", "nikmatnya", "berbalut" dan lainnya telah diubah menjadi kaya dasar.

In [32]:
# Melakukan Stemming pada DataFrame
data_lengkap['stemmed_review'] = data_lengkap.review_no_stopword.apply(stemmer.stem)
In [33]:
data_lengkap.head()
Out[33]:
review rating cleaned_review review_no_stopword stemmed_review
0 sepertinya ini termasuk produk indomie yang palaing parah, kenapa rasa salted egg kyk gini, bagian pengujiannya bagaimana ya... kecewa banget mana awal" hrganya wow banget mahal.. 1 sepertinya ini termasuk produk indomie yang palaing parah kenapa rasa salted egg kyk gini bagian pengujiannya bagaimana ya kecewa banget mana awal hrganya wow banget mahal sepertinya termasuk produk palaing parah rasa kyk gini bagian pengujiannya bagaimana kecewa banget mana awal hrganya wow banget mahal seperti masuk produk palaing parah rasa kyk gin bagi uji bagaimana kecewa banget mana awal hrganya wow banget mahal
1 Awal liat tuh kayak enak banget tapi pas udh coba mie ini baru 2 suap dah mau muntah, maaf tapi saya kurang suka dengan rasanya. 1 awal liat tuh kayak enak banget tapi pas udh coba mie ini baru suap dah mau muntah maaf tapi saya kurang suka dengan rasanya awal liat tuh kayak enak banget pas udh coba baru suap dah mau muntah maaf kurang suka rasanya awal liat tuh kayak enak banget pas udh coba baru suap dah mau muntah maaf kurang suka rasa
2 Waktu pertama melihat produknya, saya cukup excited karena saya suka sekali dengan produk berbau 'salted egg'. Ternyata Indomie varian ini tidak sesuai dengan ekspetasi saya. Perpaduan tekstur mie dan saus salted egg ternyata agak 'menjijikkan'. Sama sekali tidak cocok. 1 waktu pertama melihat produknya saya cukup excited karena saya suka sekali dengan produk berbau salted egg ternyata indomie varian ini tidak sesuai dengan ekspetasi saya perpaduan tekstur mie dan saus salted egg ternyata agak menjijikkan sama sekali tidak cocok waktu pertama melihat produknya cukup excited suka sekali produk berbau ternyata varian sesuai ekspetasi perpaduan tekstur saus ternyata menjijikkan sama sekali cocok waktu pertama lihat produk cukup excited suka sekali produk bau nyata varian sesuai ekspetasi padu tekstur saus nyata jijik sama sekali cocok
3 Saya penikmat mie instan, saya suka mencoba banyak rasa mie instan karena saya penasaran. Ketika booming salted egg, semua makanan ada varian salted egg. Jadi saat itu saya beli karena ya Indomie terkenal dengan rasanya yang enak, namun saya mencoba dan jujur ini bukan salted egg yang saya suka. Rasanya saya jauh lebih baik jika makan Indomie gorengnya saja. Mungkin saya saja yang tidak suka. Tapi itu pendapat saya 1 saya penikmat mie instan saya suka mencoba banyak rasa mie instan karena saya penasaran ketika booming salted egg semua makanan ada varian salted egg jadi saat itu saya beli karena ya indomie terkenal dengan rasanya yang enak namun saya mencoba dan jujur ini bukan salted egg yang saya suka rasanya saya jauh lebih baik jika makan indomie gorengnya saja mungkin saya saja yang tidak suka tapi itu pendapat saya penikmat instan suka mencoba banyak rasa instan penasaran booming semua makanan varian jadi beli terkenal rasanya enak mencoba jujur bukan suka rasanya jauh lebih baik makan gorengnya mungkin suka pendapat nikmat instan suka coba banyak rasa instan penasaran booming semua makan varian jadi beli kenal rasa enak coba jujur bukan suka rasa jauh lebih baik makan goreng mungkin suka dapat
4 ini mie aku kira enak karena rasa telur asin kayaknya gurih2 sedep gtu. eh pas coba, baunya menusuk banget di hidung pas dirasain after tastenya enggak enak banget, nyendal di langit-langit mulut dan enggak ilang-ilang rasanya padaha udah minum air banyak banget. sayang untuk rasa ini indomie fail bagt 1 ini mie aku kira enak karena rasa telur asin kayaknya gurih sedep gtu eh pas coba baunya menusuk banget di hidung pas dirasain after tastenya enggak enak banget nyendal di langit langit mulut dan enggak ilang ilang rasanya padaha udah minum air banyak banget sayang untuk rasa ini indomie fail bagt aku kira enak rasa telur asin kayaknya gurih sedep gtu eh pas coba baunya menusuk banget hidung pas dirasain after tastenya enggak enak banget nyendal langit langit mulut enggak ilang ilang rasanya padaha udah minum air banyak banget sayang rasa fail bagt aku kira enak rasa telur asin kayak gurih sedep gtu eh pas coba bau tusuk banget hidung pas dirasain after tastenya enggak enak banget nyendal langit langit mulut enggak ilang ilang rasa padaha udah minum air banyak banget sayang rasa fail bagt
In [ ]:
 
In [35]:
data_singkat['stemmed_review'] = data_singkat.review_no_stopword.apply(stemmer.stem)
In [36]:
data_singkat.head()
Out[36]:
review rating cleaned_review review_no_stopword stemmed_review
0 sepertinya ini termasuk produk indomie yang palaing parah, kenapa rasa salted egg kyk gini, bagian pengujiannya bagaimana ya... kecewa banget mana awal" hrganya wow banget mahal.. 1 sepertinya ini termasuk produk indomie yang palaing parah kenapa rasa salted egg kyk gini bagian pengujiannya bagaimana ya kecewa banget mana awal hrganya wow banget mahal produk palaing parah kyk gini pengujiannya kecewa banget hrganya wow banget mahal produk palaing parah kyk gin uji kecewa banget hrganya wow banget mahal
1 Awal liat tuh kayak enak banget tapi pas udh coba mie ini baru 2 suap dah mau muntah, maaf tapi saya kurang suka dengan rasanya. 1 awal liat tuh kayak enak banget tapi pas udh coba mie ini baru suap dah mau muntah maaf tapi saya kurang suka dengan rasanya liat tuh kayak enak banget pas udh coba suap dah muntah maaf kurang suka liat tuh kayak enak banget pas udh coba suap dah muntah maaf kurang suka
2 Waktu pertama melihat produknya, saya cukup excited karena saya suka sekali dengan produk berbau 'salted egg'. Ternyata Indomie varian ini tidak sesuai dengan ekspetasi saya. Perpaduan tekstur mie dan saus salted egg ternyata agak 'menjijikkan'. Sama sekali tidak cocok. 1 waktu pertama melihat produknya saya cukup excited karena saya suka sekali dengan produk berbau salted egg ternyata indomie varian ini tidak sesuai dengan ekspetasi saya perpaduan tekstur mie dan saus salted egg ternyata agak menjijikkan sama sekali tidak cocok produknya excited suka produk berbau varian sesuai ekspetasi perpaduan tekstur saus menjijikkan cocok produk excited suka produk bau varian sesuai ekspetasi padu tekstur saus jijik cocok
3 Saya penikmat mie instan, saya suka mencoba banyak rasa mie instan karena saya penasaran. Ketika booming salted egg, semua makanan ada varian salted egg. Jadi saat itu saya beli karena ya Indomie terkenal dengan rasanya yang enak, namun saya mencoba dan jujur ini bukan salted egg yang saya suka. Rasanya saya jauh lebih baik jika makan Indomie gorengnya saja. Mungkin saya saja yang tidak suka. Tapi itu pendapat saya 1 saya penikmat mie instan saya suka mencoba banyak rasa mie instan karena saya penasaran ketika booming salted egg semua makanan ada varian salted egg jadi saat itu saya beli karena ya indomie terkenal dengan rasanya yang enak namun saya mencoba dan jujur ini bukan salted egg yang saya suka rasanya saya jauh lebih baik jika makan indomie gorengnya saja mungkin saya saja yang tidak suka tapi itu pendapat saya penikmat instan suka mencoba instan penasaran booming makanan varian beli terkenal enak mencoba jujur suka makan gorengnya suka pendapat nikmat instan suka coba instan penasaran booming makan varian beli kenal enak coba jujur suka makan goreng suka dapat
4 ini mie aku kira enak karena rasa telur asin kayaknya gurih2 sedep gtu. eh pas coba, baunya menusuk banget di hidung pas dirasain after tastenya enggak enak banget, nyendal di langit-langit mulut dan enggak ilang-ilang rasanya padaha udah minum air banyak banget. sayang untuk rasa ini indomie fail bagt 1 ini mie aku kira enak karena rasa telur asin kayaknya gurih sedep gtu eh pas coba baunya menusuk banget di hidung pas dirasain after tastenya enggak enak banget nyendal di langit langit mulut dan enggak ilang ilang rasanya padaha udah minum air banyak banget sayang untuk rasa ini indomie fail bagt enak telur asin kayaknya gurih sedep gtu eh pas coba baunya menusuk banget hidung pas dirasain after tastenya enak banget nyendal langit langit mulut ilang ilang padaha udah minum air banget sayang fail bagt enak telur asin kayak gurih sedep gtu eh pas coba bau tusuk banget hidung pas dirasain after tastenya enak banget nyendal langit langit mulut ilang ilang padaha udah minum air banget sayang fail bagt
In [ ]:
 

Visualisasi Data dengan Word Cloud¶

Proses pembersihan data dan stemming telah selesai dilakukan, untuk mendapatkan gambaran dari sebaran kata-kata dari data saya menggunakan grafik WordCloud.

In [37]:
from wordcloud import WordCloud
import matplotlib.pyplot as plt
In [38]:
wc = WordCloud(background_color="white", max_words=1000, max_font_size=100, random_state=25,
               width=1200, height=800).generate(''.join(data_lengkap['stemmed_review']))
plt.figure(figsize=(12,6))
plt.imshow(wc, interpolation="bilinear")
plt.axis('off')
plt.title('Sebaran kata (Data Lengkap)')
plt.show()

Dari data lengkap terlihat bahwa banyak kata-kata yang memiliki ukuran sama, hal ini menandakan banyak kata yang memiliki frekuensi kemunculan sama. Hanya ada beberapa kata yang mendominasi seperti "rasa", "banget", "enak" dan "bumbu".

In [39]:
wc = WordCloud(background_color="white", max_words=1000, max_font_size=100, random_state=25,
               width=1200, height=800).generate(''.join(data_singkat['stemmed_review']))
plt.figure(figsize=(12,6))
plt.imshow(wc, interpolation="bilinear")
plt.axis('off')
plt.title('Sebaran kata (Data Singkat)')
plt.show()

Pada visualisasi data singkat, terlihat bahwa sebaran kata-kata lebih padat dan lebih banyak kata-kata yang mendominasi (memiliki frekuensi kemunculan tinggi). Hal ini terjadi karena lebih banyak kata-kata stopwords yang dibuang dari data singkat, sehingga ada perbedaan signifikan dari frekuensi kemunculan antar katanya.

Dari kedua visualisasi, kita dapat melihat beberapa kata yang memiliki sentimen berlawanan seperti "banget" dengan "kurang", "suka" dengan "kurang suka".

Vectorizer¶

Karena algoritma Machine Learning hanya bisa memproses data angka, data teks harus diubah menjadi angka atau vektor angka. Salah satu teknik mengubah teks ke angka adalah TF-IDF (term frequency-inverse document frequency. Teknik TF-IDF merupakan teknik vektorisasi yang memberi perhatian lebih besar pada kata-kata dengan frekuensi kemunculan lebih sering.

Untuk melakukan vektorisasi TF-IDF, saya menggunakan fungsi TfidfVectorizer() dari Scikit-Learn.

In [40]:
#import TfidfVectorizer from sklearn
from sklearn.feature_extraction.text import TfidfVectorizer
In [41]:
tf_vectorizer = TfidfVectorizer()
In [42]:
transformed = tf_vectorizer.fit_transform(data_singkat['stemmed_review'])
In [48]:
# contoh indeks kata hasil TF-IDF
tfidf_vector_idx = tf_vectorizer.vocabulary_
dict(list(tfidf_vector_idx.items())[:30])
Out[48]:
{'produk': 1274,
 'palaing': 1194,
 'parah': 1204,
 'kyk': 828,
 'gin': 499,
 'uji': 1693,
 'kecewa': 726,
 'banget': 116,
 'hrganya': 581,
 'wow': 1762,
 'mahal': 900,
 'liat': 870,
 'tuh': 1671,
 'kayak': 724,
 'enak': 405,
 'pas': 1205,
 'udh': 1690,
 'coba': 268,
 'suap': 1523,
 'dah': 294,
 'muntah': 1024,
 'maaf': 895,
 'kurang': 822,
 'suka': 1526,
 'excited': 426,
 'bau': 133,
 'varian': 1710,
 'sesuai': 1458,
 'ekspetasi': 390,
 'padu': 1183}
In [49]:
#Mencetak skor IDF dari tiap kata

all_feature_names = tf_vectorizer.get_feature_names()[:20]

for word in all_feature_names:
    
    #let's get the index in the vocabulary
    indx = tf_vectorizer.vocabulary_.get(word)
    
    #get the score
    idf_score = tf_vectorizer.idf_[indx]
    
    print(f"{word} : {idf_score}")
aain : 6.84354441703136
aama : 6.84354441703136
abang : 6.84354441703136
abiiiss : 6.84354441703136
abis : 4.971742240129768
abisin : 6.84354441703136
abiss : 6.84354441703136
absorbernya : 6.84354441703136
absurd : 6.84354441703136
aceh : 6.438079308923196
acne : 6.438079308923196
adaya : 6.84354441703136
addicted : 6.438079308923196
adek : 6.84354441703136
adekku : 6.84354441703136
aduh : 6.438079308923196
aduhhh : 6.84354441703136
aduk : 5.051784947803305
aer : 6.84354441703136
affordable : 5.74493212836325

Setiap kata dari ulasan memiliki bobotnya masing-masing berdasarkan frekuensi kemumculannya.

Mengubah rating ke sentiment¶

Untuk menyerdahanakan target dari analisis sentimen ini, saya memutuskan untuk mengubah target rating menjadi 3 jenis sentimen. Setelah membaca sejumlah ulasan, diputuskan untuk mengkategorikan ulasan dengan rating 1 dan 2 menjadi kelompok sentimen "Negative", rating 3 menjadi kelompok sentimen "Neutral", serta rating 4 dan 5 menjadi kelompok sentimen "Positive".

In [50]:
data_lengkap["review_sentiment"] = data_lengkap.rating.map({
    1 : "Negative",
    2 : "Negative",
    3 : "Neutral",
    4 : "Positive",
    5 : "Positive"
})
In [51]:
data_singkat["review_sentiment"] = data_singkat.rating.map({
    1 : "Negative",
    2 : "Negative",
    3 : "Neutral",
    4 : "Positive",
    5 : "Positive"
})
In [52]:
data_singkat.review_sentiment.value_counts()
Out[52]:
Neutral     278
Positive    239
Negative    172
Name: review_sentiment, dtype: int64
In [53]:
data_singkat.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 689 entries, 0 to 688
Data columns (total 6 columns):
 #   Column              Non-Null Count  Dtype 
---  ------              --------------  ----- 
 0   review              689 non-null    object
 1   rating              689 non-null    int64 
 2   cleaned_review      689 non-null    object
 3   review_no_stopword  689 non-null    object
 4   stemmed_review      689 non-null    object
 5   review_sentiment    689 non-null    object
dtypes: int64(1), object(5)
memory usage: 32.4+ KB

Membagi Data¶

Sebelum memulai pelatihan model, data dibagi menjadi 80% untuk pelatihan dan 20% untuk pengujian.

In [54]:
from sklearn.model_selection import train_test_split
In [55]:
# data_lengkap dengan list stopwords sastrawi 
train_full_x, test_full_x, train_full_y, test_full_y = train_test_split(
    data_lengkap.stemmed_review, 
    data_lengkap.review_sentiment,
    test_size=0.2,
    stratify=data_lengkap.review_sentiment,
    random_state=50
)
In [56]:
# data_singkat dengan list stopwords sastrawi + list stopwords Tala
train_x, test_x, train_y, test_y = train_test_split(
    data_singkat.stemmed_review, 
    data_singkat.review_sentiment,
    test_size=0.2,
    stratify=data_singkat.review_sentiment,
    random_state=50
)

Melatih Model¶

Model Naive Bayes¶

In [57]:
from sklearn.pipeline import Pipeline
from sklearn.naive_bayes import MultinomialNB
In [58]:
# pipeline TF-IDF dan model Naive Bayes
multinomialNB_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('multi_nb', MultinomialNB()),
])
In [59]:
multinomialNB_pipe.fit(train_x, train_y)
Out[59]:
Pipeline(steps=[('tf-idf', TfidfVectorizer()), ('multi_nb', MultinomialNB())])
In [60]:
multinomialNB_prediction = multinomialNB_pipe.predict(test_x)
In [61]:
from sklearn.metrics import classification_report
In [62]:
print(classification_report(test_y, multinomialNB_prediction))
              precision    recall  f1-score   support

    Negative       0.75      0.18      0.29        34
     Neutral       0.52      0.79      0.62        56
    Positive       0.67      0.62      0.65        48

    accuracy                           0.58       138
   macro avg       0.64      0.53      0.52       138
weighted avg       0.63      0.58      0.55       138

Model Naive Bayes hanya memperoleh akurasi sebesar 58%. Recall dan f1-score pada label Negative yang rendah menunjukkan model ini kesulitan dalam memprediksi ulasan negatif.

Model Naive Bayes pada data lengkap.

In [63]:
multinomialNB_verbose_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('multi_nb', MultinomialNB()),
])
In [64]:
multinomialNB_verbose_pipe.fit(train_full_x, train_full_y)
Out[64]:
Pipeline(steps=[('tf-idf', TfidfVectorizer()), ('multi_nb', MultinomialNB())])
In [65]:
print(classification_report(test_full_y, multinomialNB_verbose_pipe.predict(test_full_x)))
              precision    recall  f1-score   support

    Negative       0.80      0.12      0.21        34
     Neutral       0.49      0.82      0.62        56
    Positive       0.70      0.58      0.64        48

    accuracy                           0.57       138
   macro avg       0.66      0.51      0.49       138
weighted avg       0.64      0.57      0.52       138

Akurasi model Naive Bayes ketika dilatih dengan data lengkap mengalami kenaikan menjadi 57% walaupun masih mengalami masalah pada ulasan negatif.

Model Random Forest Classifier¶

In [66]:
from sklearn.ensemble import RandomForestClassifier
In [67]:
# Model Random forest classifier dengan data singkat
random_forest_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('random forest', RandomForestClassifier(random_state=50)),
])

# Training
random_forest_pipe.fit(train_x, train_y)

# Make prediction
random_forest_prediction = random_forest_pipe.predict(test_x)

print(classification_report(test_y, random_forest_prediction))
              precision    recall  f1-score   support

    Negative       0.79      0.44      0.57        34
     Neutral       0.56      0.61      0.58        56
    Positive       0.59      0.71      0.64        48

    accuracy                           0.60       138
   macro avg       0.64      0.59      0.60       138
weighted avg       0.62      0.60      0.60       138

Model Random Forest memperoleh akurasi dan f1-score yang lebih baik dari model Naive Bayes

In [69]:
# Model random forest dengan data lengkap
random_forest_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('random forest', RandomForestClassifier(random_state=50)),
])

# Training
random_forest_pipe.fit(train_full_x, train_full_y)

# Make prediction
random_forest_prediction = random_forest_pipe.predict(test_full_x)

print(classification_report(test_full_y, random_forest_prediction))
              precision    recall  f1-score   support

    Negative       0.81      0.38      0.52        34
     Neutral       0.60      0.66      0.63        56
    Positive       0.60      0.75      0.67        48

    accuracy                           0.62       138
   macro avg       0.67      0.60      0.60       138
weighted avg       0.65      0.62      0.61       138

Model Random yang dilatih dengan data lengkap kembali memperoleh akurasi yang lebih baik yakni 62%.

Model Gradient Boosting Classifier¶

In [70]:
from sklearn.ensemble import GradientBoostingClassifier
In [71]:
# Model Gradient Boosting dengan data singkat
gradient_boosting_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('gradient boosting', GradientBoostingClassifier(random_state=50)),
])

# Training
gradient_boosting_pipe.fit(train_x, train_y)

# Make prediction
gradient_boosting_prediction = gradient_boosting_pipe.predict(test_x)

print(classification_report(test_y, gradient_boosting_prediction))
              precision    recall  f1-score   support

    Negative       0.61      0.41      0.49        34
     Neutral       0.58      0.55      0.57        56
    Positive       0.56      0.73      0.64        48

    accuracy                           0.58       138
   macro avg       0.59      0.56      0.57       138
weighted avg       0.58      0.58      0.57       138

Model Gradient Boosting ternyata memperoleh akurasi di bawah model Random Forest. Arsitektur model yang lebih rumit dibandingkan dengan random forest memang membuatnya rentan akan overfitting. Untuk melihat apakah model Gradient Bossting mengalami overfitting harus dilihat terlebih dahulu akurasi pada data pelatihan.

In [73]:
print(classification_report(train_y, gradient_boosting_pipe.predict(train_x)))
              precision    recall  f1-score   support

    Negative       0.98      0.89      0.94       138
     Neutral       0.94      0.96      0.95       222
    Positive       0.96      0.99      0.98       191

    accuracy                           0.96       551
   macro avg       0.96      0.95      0.95       551
weighted avg       0.96      0.96      0.96       551

Model Gradient Boosting ternyata memperoleh akurasi yang tinggi pada data pelatihan, sehingga dapat model Gradient Boosting kemungkinan besar mengalami overfitting.

In [76]:
# Model Gradient Boosting dengan data lengkap
gradient_boosting_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('random forest', GradientBoostingClassifier(random_state=50)),
])

# Training
gradient_boosting_pipe.fit(train_full_x, train_full_y)

# Make prediction
gradient_boosting_prediction = gradient_boosting_pipe.predict(test_full_x)

print(classification_report(test_full_y, gradient_boosting_prediction))
              precision    recall  f1-score   support

    Negative       0.72      0.38      0.50        34
     Neutral       0.56      0.64      0.60        56
    Positive       0.61      0.71      0.65        48

    accuracy                           0.60       138
   macro avg       0.63      0.58      0.58       138
weighted avg       0.62      0.60      0.59       138

Performa model Gradient Boosting pada data lengkap masih di bawah model Random Forest.

In [ ]:
 
In [ ]:
 

Model Multi Layer Perceptron Classifier¶

In [77]:
from sklearn.neural_network import MLPClassifier
In [78]:
# Model MLP dengan data singkat
mlp_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('MLPClassifier', MLPClassifier(random_state=50)),
])

# Training
mlp_pipe.fit(train_x, train_y)

# Make prediction
mlp_prediction = mlp_pipe.predict(test_x)

print(classification_report(test_y, mlp_prediction))
              precision    recall  f1-score   support

    Negative       0.67      0.65      0.66        34
     Neutral       0.64      0.64      0.64        56
    Positive       0.65      0.67      0.66        48

    accuracy                           0.65       138
   macro avg       0.65      0.65      0.65       138
weighted avg       0.65      0.65      0.65       138

Model MLP merupakan model terbaik sejauh ini dengan akurasi 65%.

In [79]:
# Model MLP dengan data lengkap
mlp_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('MLP', MLPClassifier(random_state=50)),
])

# Training
mlp_pipe.fit(train_full_x, train_full_y)

# Make prediction
mlp_prediction = mlp_pipe.predict(test_full_x)

print(classification_report(test_full_y, mlp_prediction))
              precision    recall  f1-score   support

    Negative       0.78      0.62      0.69        34
     Neutral       0.61      0.70      0.65        56
    Positive       0.70      0.69      0.69        48

    accuracy                           0.67       138
   macro avg       0.70      0.67      0.68       138
weighted avg       0.68      0.67      0.68       138

Dengan data lengkap, akurasi model MLP mencapai 68%. Jelas model MLP merupakan model terbaik untuk proyek ini.

In [ ]:
 

Menganalisis hasil prediksi model MLP dengan confusion matrix.

In [80]:
from sklearn.metrics import confusion_matrix, accuracy_score, f1_score
In [81]:
unique_elements, counts_elements = np.unique(mlp_prediction, return_counts=True)
print("Frequency of unique values of the said array:")
print(np.asarray((unique_elements, counts_elements)))
Frequency of unique values of the said array:
[['Negative' 'Neutral' 'Positive']
 ['27' '64' '47']]
In [82]:
classes = ['Negative', 'Neutral', 'Positive']
In [83]:
conf_mat = confusion_matrix(test_full_y, mlp_prediction)
fig, ax = plt.subplots(figsize=(8,6))
sns.heatmap(conf_mat, annot=True, fmt='d', xticklabels=classes, yticklabels=classes)
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.title('MLP Classifier Confusion Matrix')
plt.show()

Terlihat bahwa model sudah cukup baik dalam membedakan ulasan positif dengan negatif, namun masih kesulitan dengan ulasan netral. Hal ini dapat terjadi karena ulasan netral banyak mengandung kata-kata yang memiliki tendensi positif/negatif.

Mencoba beragam hyperparameter MLPClassifier untuk mendapatkan performa yang lebih baik¶

In [84]:
# Mencari jumlah node pada hidden layer yang memberikan akurasi tertinggi
hidden_layer = []
mlp_acc_collection = []

for x in range(100, 500, 20):
    mlp_pipe = Pipeline(
        steps=[
            ('tf-idf', TfidfVectorizer()),
            ('MLP', MLPClassifier(random_state=50, hidden_layer_sizes=(x,),)),
        ])
    mlp_pipe.fit(train_full_x, train_full_y)
    hidden_layer.append(x)
    mlp_prediction = mlp_pipe.predict(test_full_x)
    mlp_acc = accuracy_score(test_full_y, mlp_prediction)
    mlp_acc_collection.append(mlp_acc)
In [85]:
plt.figure(figsize=(10, 8))
sns.lineplot(x=hidden_layer, y=mlp_acc_collection, )
plt.title('MLPClassifier\'s Accuracy vs hidden layer')
plt.xlabel('Hidden Layers')
plt.ylabel('Test Accuracy')

plt.show()
In [86]:
hidden_layer = []
mlp_acc_collection = []

for x in range(500, 700, 20):
    mlp_pipe = Pipeline(
        steps=[
            ('tf-idf', TfidfVectorizer()),
            ('MLP', MLPClassifier(random_state=50, hidden_layer_sizes=(x,),)),
        ])
    mlp_pipe.fit(train_full_x, train_full_y)
    hidden_layer.append(x)
    mlp_prediction = mlp_pipe.predict(test_full_x)
    mlp_acc = accuracy_score(test_full_y, mlp_prediction)
    mlp_acc_collection.append(mlp_acc)
In [87]:
plt.figure(figsize=(10, 8))
sns.lineplot(x=hidden_layer, y=mlp_acc_collection, )
plt.title('MLPClassifier\'s Accuracy vs hidden layer')
plt.xlabel('Hidden Layers')
plt.ylabel('Test Accuracy')

plt.show()

Saya memutuskan untuk memilih hidden layer dengan 580 node yang memiliki akurasi tertinggi.

In [ ]:
 

Melatih model MLP dengan hidden layer baru, alpha baru dan data lengkap

In [88]:
# Create pipeline
mlp_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('MLP', MLPClassifier(random_state=50, hidden_layer_sizes=(580), alpha=0.0001, max_iter=200),),
])

# Training
mlp_pipe.fit(train_full_x, train_full_y)

# Make prediction
mlp_prediction = mlp_pipe.predict(test_full_x)

print(classification_report(test_full_y, mlp_prediction))
              precision    recall  f1-score   support

    Negative       0.81      0.62      0.70        34
     Neutral       0.63      0.71      0.67        56
    Positive       0.71      0.73      0.72        48

    accuracy                           0.70       138
   macro avg       0.72      0.69      0.70       138
weighted avg       0.71      0.70      0.70       138

In [89]:
accuracy_score(test_full_y, mlp_prediction)
Out[89]:
0.6956521739130435
In [91]:
# Model MLP dengan alpha lebih kecil
mlp_pipe = Pipeline(steps=[
    ('tf-idf', TfidfVectorizer()),
    ('MLP', MLPClassifier(random_state=50, hidden_layer_sizes=(580), alpha=0.00001, max_iter=200),),
])

# Training
mlp_pipe.fit(train_full_x, train_full_y)

# Make prediction
mlp_prediction = mlp_pipe.predict(test_full_x)

print(classification_report(test_full_y, mlp_prediction))
              precision    recall  f1-score   support

    Negative       0.81      0.62      0.70        34
     Neutral       0.65      0.71      0.68        56
    Positive       0.72      0.75      0.73        48

    accuracy                           0.70       138
   macro avg       0.72      0.69      0.70       138
weighted avg       0.71      0.70      0.70       138

In [92]:
accuracy_score(test_full_y, mlp_prediction)
Out[92]:
0.7028985507246377

Model MLP dengan hidden layer = 580, aplha = 0.00001 memperoleh akurasi terbaik sebesar 72%.

In [93]:
conf_mat = confusion_matrix(test_full_y, mlp_prediction)
fig, ax = plt.subplots(figsize=(8,6))
sns.heatmap(conf_mat, annot=True, fmt='d', xticklabels=classes, yticklabels=classes)
plt.ylabel('Actual')
plt.xlabel('Predicted')
plt.title('MLP Classifier Confusion Matrix')
plt.show()
In [ ]:
 

Mengkekspor model MLP¶

In [94]:
import joblib as jb
In [95]:
mlp_pipe_name = '../model_file/MLP_TF_Model.sav'
In [96]:
jb.dump(mlp_pipe, mlp_pipe_name)
Out[96]:
['../model_file/MLP_TF_Model.sav']
In [ ]:
 
In [ ]:
 

Catatan:¶

  1. Dari proyek ini, saya merasa bahwa membuang stopwords tidak membantu model belajar dari data, justru membuang banyak informasi. Berbeda denga bahasa inggris, stopword dalam bahasa Indonesia merupakan kata-kata yang mengandung informasi yang sangat penting. Tapi hal ini masih dugaan saya, saya perlu membuktikkannya di dataset lain.
  2. Penggunaan TF-IDF untuk mengubah teks ke angka merupakan pendekatan yang hanya memperhitungkan frekuensi kemunculan setiap kata tanpa memperhatikan konteks dan makna semantik tiap kata. Di proyek selanjutnya saya akan mencoba melakukan vektorisasi kata menggunakan word2vec menggunakan gensim.
  3. Akurasi akhir dari model masih rendah, ukuran dataset yang sangat kecil membatasi kemampuan model untuk belajar. Saya harus mencari dataset dengan jumlah yang lebih besar lagi.
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: