Skip to main content
0
Artificial Intelligence / Data scienceData Analytics

Sentiment Analysis โดยใช้ Bert ฉบับเน้น Concept พร้อม code

สวัสดีครับในวันนี้ผมจะมาสอนทุกท่านทำ Sentiment Analysis ด้วย Bert แบบ คร่าว ๆ กันนะครับ โดยในบทความนี้จะประกอบไปด้วย

  • Task Setting โดยเราจะมาพูดถึงตัว Sentiment Analysis กันครับว่าตัว Sentiment Analysis คืออะไร ทำไปเพื่ออะไร และเราจะต้องใช้ข้อมูลอะไรบ้างเป็น input-output

  • Getting to know Bert:เราจะมาพูดถึงเจ้าตัว Bert แบบคร่าว ๆ เอาแค่จุดประสงค์ของมันจุดเด่นของมันไม่ลงลึกถึงวิธีการคำนวณ เพื่อให้เรารู้พอที่จะใช้ Bert เป็นเท่านั้นครับ

  • Getting to know the data: เราจะมาดูว่าวันนี้เราจะใช้ datasets อะไร และต้องเตรียมตัวอะไรก่อนทำ model บ้าง

  • Getting it done:เราจะมาพูดถึงแต่ละ step ที่เราทำว่าเราทำอะไรบ้างเพื่อจุดประสงค์อะไร

เอาหละครับ เรามาเริ่มเข้าสู่ตัวบทความจริง ๆ กันดีกว่า

โดยคุณ ณัฐพล มณีโชติ (เฟรม)
นักศึกษา ป.โท จุฬาฯ ที่จะมาเรียนรู้ไปพร้อม ๆ กับคุณ
“All I know is that I know nothing” -Socrates

Task Setting

ก่อนที่เราจะมาทำตัว model เราก็ต้องจำเป็นที่จะรู้ก่อนใช่ไหมครับว่า Sentiment Analysis เนี่ย คืออะไร และ Analysis ในแง่ไหน

 

Sentiment Analysis

Sentiment Analysis คือ การที่เราจะนำเอาประโยคไปดูครับว่าประโยคเนี่ยมีความคิดเห็นยังไงชอบ ไม่ชอบ หรือ เฉย ๆ กับสิ่งที่เขากำลังพูดถึง ยกตัวอย่างเช่น

  • “อาหารพอกินได้นะ” =>”เฉย ๆ ”

  • “จานนี้อร่อยมากเลยนะครับ” => “ชอบ”

  • “ผมว่าอาหารต้องปรับปรุงนะ” => “ไม่ชอบ”

 

แล้วการที่เรามี automate tool เพื่อที่จะบอกว่าคนเขียนรู้สึกยังไงมันดีตรงไหนกันหละ ??

การที่มี automate tool เพื่อที่จะทำ Sentiment Analysis เนี่ยเป็นผลดีกับร้านค้าต่างๆ อย่างมากนะครับ ลองนึกภาพถ้าเราทำ survey ของร้านค้ามา เราก็จะได้รู้ว่าเขาพูดถึงร้านค้าโดยรวมไปในทางที่ดีหรือทางที่แย่ และเรายังสามารถขุดไปให้ลึกว่าเดิมได้ โดยอาจจะเข้าไปดูในกลุ่มที่บอกว่าแย่ ว่าร้านค้าเราควรปรับปรุงตรงไหน หรือ เข้าไปดูในกลุ่มที่บอกว่าดีเพื่อหาจุดเด่นของร้านค้าในการนำไปพัฒนาต่อก็ได้นั่นเองครับ

Data requirements

เอาหละครับในเรื่องของข้อมูลที่เราจะต้องใช้ในการ train ตัว Sentiment Analysis Model ก็ คือ ตัวประโยค กับ ความคิดเห็นของประโยคนั่นเองครับโดย input คือตัวประโยค และ output คือ ความคิดเห็นบนประโยคนั้น

ทีนี้เมื่อเราได้รู้ว่า input output เป็นยังไงแล้ว เราก็จะไปดูกลไกหลักของ model เรากันครับ นั่นก็คือตัว Bert นั่นเอง

 

Getting to know Bert

ขอย้ำอีกสักทีนะครับว่าวันนี้เราจะไม่ได้มาเจาะลึกที่ตัว Bert กันแต่จะเป็นแค่การพูดถึงคร่าว ๆ ว่าใช้ทำอะไรยังไง และมีข้อดียังไงครับผม

 

Bert คืออะไร?

Bert นั้นพูดง่าย ๆ ก็คือการเปลี่ยนคำให้กลายเป็น vector (word embedding) โดยที่ vector เหล่านี้จะต้องสามารถที่จะ represent คุณลักษณะหรือความหมายของคำ ๆ นั้นเพื่อทำให้ model เข้าใจและนำไปใช้ต่อได้นั่นเองครับ

Bert word embedding เก็บความหมายของคำได้ยังไง?

เราจะมาพูดถึง concept หลัก ๆ ของ Bert โดยไม่ลงไปที่ math นะครับ โดยหลัก ๆ ก็คือ Bert จะถูกสอนให้เรียนการเติมคำในช่องว่าโดยใช้คำรอบ ๆ (Cloze task) เหมือนที่เราเคยทำกันตอนเด็ก ๆ ตอนที่เราเรียนภาษาอังกฤษกันนั่นหละครับถ้าจำไม่ได้ก็จะเป็นแบบในตัวอย่างด้านล่างครับ

โดยการทำ cloze task นี้เนี่ยถ้าทำได้ก็จะแสดงว่าเราเข้าใจความหมายของคำรอบ ๆ และคำที่เติมเพราะฉะนั้น word embedding ที่ออกมาจึงพอจะเข้าใจได้ว่าเป็นความหมายของคำ ๆ นั้นแล้วนั่นเองครับผม

นอกจากนี้ bert ยังทำตัว Next Sentence Prediction task หรือการทำการเดาว่าประโยคนั้นต่อกันหรือเปล่า เพื่อเป็นตัวแทนของความเข้าใจการสรุปผลของ bert อีกด้วยครับ โดยความหมายของประโยคนี้จะอยู่ใน vector ที่เรียกว่า CLS embedding เพื่อเป็นตัวแทนของประโยคอีกด้วยครับ

 

Getting to know the data

ข้อมูลที่เราจะนำมาใช้ในวันนี้คือตัว Amazon Musical Instrument dataset ครับ โดยจะประกอบไปตัว review ของ เครื่องดนตรี และ rating ของ review นั้น ๆ จากการที่เราต้องการความรู้สึกของประโยคแทนที่จะเป็น rating เราเลยจำเป็นที่จะต้องเปลี่ยน rating ให้กลายเป็นความรู้สึกก่อนครับ โดยถ้า rating มากกว่าสามเราจะคิดว่าเขาชอบ rating น้อยกว่า 3 เราจะคิดว่าเขาไม่ชอบและ rating เท่ากับสามคือเฉย ๆ นั่นเองครับ

 

Getting it done

ท้ายที่สุดนะครับผมจะมาพูดกระบวนการต่าง ๆ ในการทำโดยนำเสนอพร้อมกับโค้ดให้ทุก ๆ ท่านหวังว่าทุก ๆ ท่านจะนำไปปรับใช้ได้นะครับ โดยผมจะอธิบายแค่โค้ดส่วนใหญ่ ๆ นะครับผม

Import and setting module

import pandas as pd
import os
import pickle as pkl
import numpy as np
import gzip
import tensorflow as tf
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from tokenizers import BertWordPieceTokenizer
from transformers import BertTokenizer, TFBertModel, BertConfig

encoder = TFBertModel.from_pretrained("bert-base-uncased")

ในโค้ดส่วนนี้เราทำการ import modules ที่เราใช้ทั้งหมดมาครับ แล้วก็ทำการสร้างตัวแปรสำหรับ bert word embedding layer ของเราครับผม

import data and preprocessing

def parse(path):
  g = gzip.open(path, 'rb')
  for l in g:
    yield eval(l)

def getDF(path):
  i = 0
  df = {}
  for d in parse(path):
    df[i] = d
    i += 1
  return pd.DataFrame.from_dict(df, orient='index')

df = getDF('/content/reviews_Musical_Instruments_5.json.gz')
df = df[['reviewText','overall']]

def splitSentiment(x):
  if x<3:
    return 0
  elif x==3:
    return 1
  elif x>3:
    return 2

good = df[df['overall']>=4]
neutral = df[df['overall']==3]
bad = df[df['overall']<=2]
_min = min(good.shape[0],neutral.shape[0],bad.shape[0])
good = good.sample(n=_min)
neutral = neutral.sample(n=_min)
bad = bad.sample(n=_min)
df = pd.concat([good,neutral,bad])
df = df.sample(frac=1)

encoder = TFBertModel.from_pretrained("bert-base-uncased")

ต่อมาเราก็ทำการ import data และจัดให้อยู่ในรูปที่ง่ายต่อการทำงานครับ และเราก็ทำการเปลี่ยน rating ให้กลายเป็น sentiment แบบที่เคยได้เล่าสู่กันฟังตอนแรกด้วยครับ

Data sampling & splitting

ก่อนจะเริ่มเข้าสู่ตัว model เราก็ต้องทำการ down sampling ข้อมูลของในแต่ละคลาส (ประโยคที่ชอบ/ไม่ชอบ/เฉย ๆ ) ของเราให้เท่ากันก่อนครับ เนื่องจากข้อมูลที่มากไปในคลาสใดคลาสหนึ่งอาจทำให้ model ของเราเกิดความเข้าใจผิดและทำนายเป็น class นั้นเยอะ ๆ ก็เป็นได้ ทำให้การที่มันเก่งไม่ใช่เพราะเข้าใจประโยคแต่เป็นเพราะ class นั้นมันมีเยอะสุดเลยเดาเป็น class นั้นไป

ยกตัวอย่างเช่นผมจะสร้างโมเดลที่เอาไว้ทายว่าหวยเลขนี้โดนกินหรือเปล่า model นั้นก็สามารถเดาได้ง่าย ๆ เลยว่าโดนกินแน่ ๆ เนื่องจากจำนวนหวยที่ถูกนั้นมีน้อยกว่าหวยที่โดนกินมาก ๆ ทำให้แม้ว่า model จะเดาตอบแต่โดนกินก็ยังได้ accuracy สูงอยู่นั้นเองครับ

good = df[df['overall']>=4]
neutral = df[df['overall']==3]
bad = df[df['overall']<=2]
_min = min(good.shape[0],neutral.shape[0],bad.shape[0])
good = good.sample(n=_min)
neutral = neutral.sample(n=_min)
bad = bad.sample(n=_min)
df = pd.concat([good,neutral,bad])
df = df.sample(frac=1)

encoder = TFBertModel.from_pretrained("bert-base-uncased")

def StratifiedSplit(dat,label_name,split_ratio):
  total_row=dat.count()[0]
  labels=dat[label_name].unique()
  ratio_dict={}
  for i in labels:
ratio_dict[i]=dat.where(dat[label_name]==i).dropna().count()[0]/total_row
  num_sample=int(total_row*split_ratio)
  test_df=pd.DataFrame(columns=dat.columns)
  train_df=pd.DataFrame(columns=dat.columns)
  for i in labels:
    num_sample_label =int(num_sample*ratio_dict[i])
    dat_sm=dat.where(dat[label_name]==i).dropna()
    dat_sm=dat_sm.sample(frac=1)
    msk = np.random.rand(dat_sm.count()[0]) < 
(num_sample_label/dat_sm.count()[0])
    test_df=test_df.append(dat_sm[msk])
    train_df=train_df.append(dat_sm[~msk])
return (train_df, test_df)

  หลังจากทำการ down sampling แล้ว เราจะแบ่งข้อมูล เป็นสามชุด คือ train, validation แล้วก็ test ก่อนครับ โดยการแบ่งข้อมูลสำหรับ task นี้เนี่ยเราจะใช้การแบ่งที่เรียกว่า stratified splitting ซึ่งจะทำให้ข้อมูลที่ sample ออกมามีสัดส่วนของ class ที่เท่ากับอันเดิมที่เป็น source data ครับ การทำแบบนี้จะเป็นการรักษาสัดส่วนที่เราทำการ down sampling เอาไว้ให้เท่าเดิมไม่เสียความเท่าเทียมของข้อมูลที่เราทำไว้ครับ

word preprocessing

การเตรียมคำไว้สำหรับแปลงเป็น vector นั้นเราจะต้องแปลงคำให้เป็น index ของคำแล้วเติมสิ่งที่ Bert จำเป็นต้องใช้เข้าไปครับเช่น CLS token, attention mask ต่าง ๆ ครับ ในส่วนนี้เราสามารถให้ module haggingface จัดการให้เราได้เลยครับผม

### LOAD BERT TOKENIZERS ###
# Save the slow pretrained tokenizer
slow_tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
save_path = "bert_base_uncased/"
if not os.path.exists(save_path):
    os.makedirs(save_path)
slow_tokenizer.save_pretrained(save_path)

# Load the fast tokenizer from saved file
tokenizer = BertWordPieceTokenizer("bert_base_uncased/vocab.txt", lowercase=True)
x_ids_tr=[tokenizer.encode(i).ids for i in train_["reviewText"]]
x_ids_tr=pad_sequences(x_ids_tr,maxlen=max_seq,padding="post")
x_msk_tr=[tokenizer.encode(i).attention_mask for i in train_["reviewText"]]
x_msk_tr=pad_sequences(x_msk_tr,maxlen=max_seq,padding="post")
y_tr=to_categorical(train_["overall"].values)

#Bert
input_ids = tf.keras.layers.Input(shape=(max_seq,), dtype=tf.int32)
attn_mask = tf.keras.layers.Input(shape=(max_seq,), dtype=tf.int32)
## CLS=1 All word emb=0
wrd_embedding = encoder(input_ids, attention_mask=attn_mask)[1]

drop=tf.keras.layers.Dropout(0.8)(wrd_embedding)
res=tf.keras.layers.Dense(8,activation="relu")(drop)
res2=tf.keras.layers.Dense(8,activation="relu")(res)

### Result Dense
res3=tf.keras.layers.Dense(3,activation="softmax")(res2)
model=tf.keras.Model(inputs=[input_ids,attn_mask] ,outputs=res3)

Model

ในส่วนของ ตัว model เนี่ยเราจะเอา bert pretrained layer มา fine tune(fine tune คือการ train ตัว word embedding ให้เปลี่ยนแปลงไปเล็กน้อยเพื่อให้เหมาะกับ task เฉพาะทางที่ทำ) ให้เข้ากับ task ของเราครับโดยเราจะเอาตัว CLS ที่เป็นตัวแทนของประโยคไปผ่าน Muti layer perceptrons สองชั้นและชั้นสุดท้ายของ Muti layer perceptrons จะมี softmax activation function ซึ่งเป็นฟังก์ชันที่ทำให้ค่าในแต่ละมิติของ vector บวกกันได้ 1 ครับเพื่อให้ vector ที่แทน probability disturbution ของ แต่ละ class(ชอบ, ไม่ชอบ, เฉย ๆ ) ที่โมเดลทายนั่นเองครับ

ในส่วนของ model นั้นเราจะใช้ตัว tensorflow จัดการเรื่อง loss function และ backpropagation ต่าง ๆ ให้ครับ


“`โค้ดสำหรับกำหนด loss function

model.compile(optimizer=tf.keras.optimizers.Adam(0.001),loss=tf.keras.loss
es.CategoricalCrossentropy(),metrics=["accuracy"])


“`โค้ดสำหรับ train model

history=model.fit(x=[x_ids_tr,x_msk_tr],y=y_tr,epochs=10,validation_data=(
[x_ids_t,x_msk_t],y_t),verbose=1,batch_size=8,callbacks=callbacks)

สรุป

เท่านี้คุณก็ทำ Sentiment analysis ด้วย Bert ของคุณเองได้แล้วหละครับ ขอบคุณที่เข้ามาอ่านจนถึงจุดนี้นะครับ หากมีข้อผิดพลาดประการใดขออภัยมา ณ ที่นี้ด้วยนะครับผม

Code เต็ม: https://github.com/Natthapolmnc/SentimentAnalysis

หากคุณสนใจพัฒนา สตาร์ทอัพ แอปพลิเคชัน
และ เทคโนโลยีของตัวเอง ?

อย่ารอช้า ! เรียนรู้ทักษะด้านดิจิทัลเพื่ออัพเกรดความสามารถของคุณ
เริ่มตั้งแต่พื้นฐาน พร้อมปฏิบัติจริงในรูปแบบหลักสูตรออนไลน์วันนี้

BorntoDev

Author BorntoDev

BorntoDev Co., Ltd.

More posts by BorntoDev

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

    ประเภทของคุกกี้มีความจำเป็นสำหรับการทำงานของเว็บไซต์ เพื่อให้คุณสามารถใช้ได้อย่างเป็นปกติ และเข้าชมเว็บไซต์ คุณไม่สามารถปิดการทำงานของคุกกี้นี้ในระบบเว็บไซต์ของเราได้
    รายละเอียดคุกกี้

  • คุกกี้สำหรับการติดตามทางการตลาด

    ประเภทของคุกกี้ที่มีความจำเป็นในการใช้งานเพื่อการวิเคราะห์ และ นำเสนอโปรโมชัน สินค้า รวมถึงหลักสูตรฟรี และ สิทธิพิเศษต่าง ๆ คุณสามารถเลือกปิดคุกกี้ประเภทนี้ได้โดยไม่ส่งผลต่อการทำงานหลัก เว้นแต่การนำเสนอโปรโมชันที่อาจไม่ตรงกับความต้องการ
    รายละเอียดคุกกี้

บันทึกการตั้งค่า