ก่อนที่จะป้อนข้อมูลของเราไปยังโมเดล ข้อมูลจะต้องถูกแปลงเป็นรูปแบบที่โมเดลเข้าใจได้
อย่างแรก ตัวอย่างข้อมูลที่เรารวบรวมอาจเรียงลำดับตามที่ระบุ เราไม่ต้องการให้ข้อมูลที่เกี่ยวข้องกับการจัดลำดับตัวอย่างมีผลต่อความสัมพันธ์ระหว่างข้อความและป้ายกำกับ เช่น หากชุดข้อมูลได้รับการจัดเรียงตามคลาสและจากนั้นแยกเป็นชุดการฝึก/ชุดการตรวจสอบ ชุดเหล่านี้จะไม่แสดงถึงการกระจายข้อมูลโดยรวม
แนวทางปฏิบัติแนะนำง่ายๆ ที่จะทำให้มั่นใจได้ว่าโมเดลจะไม่ได้รับผลกระทบจากลำดับข้อมูลคือการสับเปลี่ยนข้อมูลทุกครั้งก่อนที่จะทำอย่างอื่น หากข้อมูลแบ่งออกเป็นชุดการฝึกและการตรวจสอบแล้ว โปรดแปลงข้อมูลการตรวจสอบแบบเดียวกับที่แปลงข้อมูลการฝึก แต่หากยังไม่มีชุดการฝึกและการตรวจสอบที่แยกกัน คุณก็แยกตัวอย่างได้หลังจากสับเปลี่ยนแล้ว โดยทั่วไปจะใช้ตัวอย่าง 80% สำหรับการฝึกและ 20% สำหรับการตรวจสอบ
ประการที่ 2 อัลกอริทึมแมชชีนเลิร์นนิงจะนำตัวเลขเป็นอินพุต ซึ่งหมายความว่าเราจะต้องแปลงข้อความเป็นเวกเตอร์ตัวเลข ซึ่งมี 2 ขั้นตอนดังนี้
การแปลงข้อมูลเป็นโทเค็น: แบ่งข้อความออกเป็นคำหรือข้อความย่อยเล็กๆ ซึ่งจะช่วยให้เข้าใจความสัมพันธ์ที่ดีระหว่างข้อความและป้ายกำกับได้ ซึ่งจะเป็นตัวกำหนด "คำศัพท์" ของชุดข้อมูล (ชุดของโทเค็นที่ไม่ซ้ำกันที่อยู่ในข้อมูล)
เวกเตอร์: กำหนดการวัดเชิงตัวเลขที่ดีเพื่อกำหนดลักษณะของข้อความเหล่านี้
เรามาดูวิธีทำ 2 ขั้นตอนนี้สำหรับทั้งเวกเตอร์ n-gram และเวกเตอร์ลำดับ และวิธีเพิ่มประสิทธิภาพการแสดงเวกเตอร์โดยใช้การเลือกฟีเจอร์และเทคนิคการทำให้เป็นมาตรฐาน
เวกเตอร์ N-gram [Option A]
ในย่อหน้าถัดๆ ไป เราจะดูวิธีการแปลงข้อมูลเป็นโทเค็นและเวกเตอร์สำหรับโมเดล n-gram นอกจากนี้ เรายังจะพูดถึงวิธีการเพิ่มประสิทธิภาพของการนำเสนอ n-gram โดยใช้การเลือกฟีเจอร์และเทคนิคการปรับมาตรฐาน
ในเวกเตอร์ n-gram ข้อความจะแสดงเป็นกลุ่มของ n-gram ที่ไม่ซ้ำ ซึ่งก็คือกลุ่มของโทเค็นที่อยู่ติดกัน n อัน (โดยทั่วไปคือคำ) ลองใช้ข้อความ The mouse ran
up the clock
ที่นี่
- คำว่ายูนิแกรม (n = 1) คือ
['the', 'mouse', 'ran', 'up', 'clock']
- คำว่า Bigrams (n = 2) คือ
['the mouse', 'mouse ran', 'ran up', 'up the', 'the clock']
- เป็นต้น
การแปลงข้อมูลเป็นโทเค็น
เราพบว่าการแปลงข้อมูลเป็นโทเค็นให้เป็นแบบ Word unigram + Bigrams จะมีความแม่นยำที่ดีและใช้เวลาประมวลผลน้อยกว่า
เวกเตอร์
เมื่อเราแยกตัวอย่างข้อความออกเป็น n-กรัมแล้ว เราจำเป็นต้องเปลี่ยน n-กรัมเหล่านี้ให้เป็นเวกเตอร์เชิงตัวเลขที่โมเดลแมชชีนเลิร์นนิงของเราประมวลผลได้ ตัวอย่างด้านล่างแสดงดัชนีที่กำหนดให้กับ Unigram และ Bigrams ที่สร้างขึ้นสำหรับข้อความ 2 ข้อความ
Texts: 'The mouse ran up the clock' and 'The mouse ran down' Index assigned for every token: {'the': 7, 'mouse': 2, 'ran': 4, 'up': 10, 'clock': 0, 'the mouse': 9, 'mouse ran': 3, 'ran up': 6, 'up the': 11, 'the clock': 8, 'down': 1, 'ran down': 5}
เมื่อกำหนดดัชนีให้กับ n-กรัม แล้ว เรามักจะกำหนดค่าเวกเตอร์โดยใช้ตัวเลือกใดตัวเลือกหนึ่งต่อไปนี้
การเข้ารหัสแบบ One-hot: ข้อความตัวอย่างทุกข้อความจะแสดงเป็นเวกเตอร์ที่ระบุว่ามีหรือไม่มีโทเค็นในข้อความ
'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]
การเข้ารหัสการนับ: ข้อความตัวอย่างทุกข้อความจะแสดงเป็นเวกเตอร์ที่ระบุจำนวนโทเค็นในข้อความ โปรดทราบว่าตอนนี้องค์ประกอบที่เกี่ยวข้องกับ Unigram "the" จะแสดงด้วย 2
เนื่องจากคำว่า "the" ปรากฏ 2 ครั้งในข้อความ
'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1]
การเข้ารหัส Tf-idf: ปัญหาของวิธีทั้ง 2 อย่างข้างต้นคือ คำทั่วไปที่เกิดขึ้นในความถี่ใกล้เคียงกันในเอกสารทั้งหมด (เช่น คำที่ไม่ซ้ำกับตัวอย่างข้อความในชุดข้อมูล) จะไม่ได้รับการลงโทษ เช่น คำว่า "a" จะปรากฏบ่อยมากในข้อความทั้งหมด ดังนั้น จำนวนโทเค็นสำหรับ "the" ที่สูงกว่า คำอื่นๆ ที่มีความหมายมากกว่าจึงไม่มีประโยชน์มากนัก
'The mouse ran up the clock' = [0.33, 0, 0.23, 0.23, 0.23, 0, 0.33, 0.47, 0.33, 0.23, 0.33, 0.33]
(ดู Scikit-learn TfidfTransformer)
มีการนำเสนอเวกเตอร์อื่นๆ อีกจำนวนมาก แต่ 3 รูปแบบก่อนหน้านี้คือรูปแบบที่ใช้กันโดยทั่วไปมากที่สุด
เราสังเกตเห็นว่าการเข้ารหัส tf-idf ดีกว่าอีก 2 ด้านในแง่ความถูกต้อง (โดยเฉลี่ยคือสูงกว่า 0.25-15%) และแนะนำให้ใช้วิธีการนี้สำหรับการทำเวกเตอร์ n-gram อย่างไรก็ตาม โปรดทราบว่าระบบจะใช้หน่วยความจำมากกว่า (เนื่องจากใช้การแสดงจุดลอยตัว) และใช้เวลาในการประมวลผลนานกว่า โดยเฉพาะชุดข้อมูลขนาดใหญ่ (อาจนานกว่า 2 เท่าในบางกรณี)
การเลือกฟีเจอร์
เมื่อเราแปลงข้อความทั้งหมดในชุดข้อมูลเป็นโทเค็น Word uni+bigram เราอาจได้โทเค็นเป็นหลายหมื่นรายการ โทเค็น/ฟีเจอร์บางส่วน ไม่ได้มีส่วนในการคาดการณ์ป้ายกำกับ เราจึงวางโทเค็นบางรายการได้ เช่น โทเค็นที่เกิดขึ้นน้อยมากในชุดข้อมูล นอกจากนี้ เรายังสามารถวัดความสำคัญของฟีเจอร์ (แต่ละโทเค็นมีส่วนช่วยในการคาดการณ์ป้ายกำกับมากน้อยเพียงใด) และระบุเฉพาะโทเค็นที่ให้ข้อมูลมากที่สุดเท่านั้น
มีฟังก์ชันทางสถิติหลายอย่างที่ใช้ฟีเจอร์ต่างๆ และป้ายกำกับที่เกี่ยวข้องและแสดงคะแนนความสำคัญของฟีเจอร์ ฟังก์ชันที่ใช้กันโดยทั่วไป 2 รายการคือ f_classif และ chi2 การทดสอบของเราแสดงให้เห็นว่าฟังก์ชันทั้งสองนี้มีประสิทธิภาพเท่ากัน
ที่สำคัญไปกว่านั้น เราพบว่าความแม่นยำสูงราวๆ 20,000 ฟีเจอร์สำหรับชุดข้อมูลจำนวนมาก (ดูรูปที่ 6) การเพิ่มฟีเจอร์มากกว่าเกณฑ์นี้จะทำให้เกิดการใช้งานน้อยมากและบางครั้งอาจนำไปสู่ความไม่สอดคล้องและทำให้ประสิทธิภาพลดลง
รูปที่ 6: ฟีเจอร์ K ยอดนิยมเทียบกับความแม่นยำ ข้อมูลจากชุดข้อมูลต่างๆ จะหมายถึงที่ราบสูงความแม่นยำที่มีฟีเจอร์ยอดนิยมประมาณ 20, 000 แห่ง
การปรับให้สอดคล้องตามมาตรฐาน
การปรับให้เป็นมาตรฐานจะแปลงค่าฟีเจอร์/ตัวอย่างทั้งหมดเป็นค่าที่เล็กน้อยและคล้ายกัน ซึ่งจะช่วยลดความซับซ้อนของลู่เข้าที่การไล่ระดับสีลดลงในอัลกอริทึมการเรียนรู้ จากที่เราได้เห็น ดูเหมือนว่าการปรับให้เป็นมาตรฐานระหว่างการประมวลผลข้อมูลล่วงหน้าจะไม่ช่วยเพิ่มคุณค่ามากนักในปัญหาในการจำแนกข้อความ เราขอแนะนำให้ข้ามขั้นตอนนี้
โค้ดต่อไปนี้จะรวมขั้นตอนด้านบนทั้งหมดไว้ด้วยกัน
- แปลงตัวอย่างข้อความให้เป็นโทเค็นของคำว่า uni+bigram
- เวกเตอร์โดยใช้การเข้ารหัส tf-idf
- เลือกเฉพาะคุณลักษณะ 20,000 อันดับแรกจากเวกเตอร์ของโทเค็นโดยการทิ้งโทเค็นที่ปรากฏน้อยกว่า 2 ครั้ง และใช้ f_classif เพื่อคำนวณความสำคัญของคุณลักษณะ
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import f_classif # Vectorization parameters # Range (inclusive) of n-gram sizes for tokenizing text. NGRAM_RANGE = (1, 2) # Limit on the number of features. We use the top 20K features. TOP_K = 20000 # Whether text should be split into word or character n-grams. # One of 'word', 'char'. TOKEN_MODE = 'word' # Minimum document/corpus frequency below which a token will be discarded. MIN_DOCUMENT_FREQUENCY = 2 def ngram_vectorize(train_texts, train_labels, val_texts): """Vectorizes texts as n-gram vectors. 1 text = 1 tf-idf vector the length of vocabulary of unigrams + bigrams. # Arguments train_texts: list, training text strings. train_labels: np.ndarray, training labels. val_texts: list, validation text strings. # Returns x_train, x_val: vectorized training and validation texts """ # Create keyword arguments to pass to the 'tf-idf' vectorizer. kwargs = { 'ngram_range': NGRAM_RANGE, # Use 1-grams + 2-grams. 'dtype': 'int32', 'strip_accents': 'unicode', 'decode_error': 'replace', 'analyzer': TOKEN_MODE, # Split text into word tokens. 'min_df': MIN_DOCUMENT_FREQUENCY, } vectorizer = TfidfVectorizer(**kwargs) # Learn vocabulary from training texts and vectorize training texts. x_train = vectorizer.fit_transform(train_texts) # Vectorize validation texts. x_val = vectorizer.transform(val_texts) # Select top 'k' of the vectorized features. selector = SelectKBest(f_classif, k=min(TOP_K, x_train.shape[1])) selector.fit(x_train, train_labels) x_train = selector.transform(x_train).astype('float32') x_val = selector.transform(x_val).astype('float32') return x_train, x_val
เมื่อใช้การแสดงเวกเตอร์ n-gram เราจะทิ้งข้อมูลจำนวนมากเกี่ยวกับลำดับคำและไวยากรณ์ (อย่างดีที่สุด เราสามารถเก็บข้อมูลการจัดเรียงบางส่วนไว้ได้เมื่อ n > 1) วิธีนี้เรียกว่าวิธีการแบบถุงคำ การนำเสนอนี้จะใช้ร่วมกับโมเดลที่ไม่ได้คำนึงถึงลำดับ เช่น การถดถอยแบบโลจิสติก เปอร์เซปตรอนแบบหลายเลเยอร์ เครื่องเพิ่มระดับสี และเครื่องเวกเตอร์ที่รองรับ
เวกเตอร์ของลำดับ [ตัวเลือก B]
ในย่อหน้าถัดๆ ไป เราจะดูวิธีการแปลงข้อมูลเป็นโทเค็นและเวกเตอร์สำหรับโมเดลลำดับ นอกจากนี้ เราจะพูดถึงวิธีเพิ่มประสิทธิภาพการแสดงลำดับโดยใช้การเลือกฟีเจอร์และเทคนิคการปรับให้เป็นรูปแบบมาตรฐานด้วย
สำหรับตัวอย่างข้อความบางรายการ ลำดับคำมีความสำคัญอย่างยิ่งต่อความหมายของข้อความ เช่น ประโยคที่ว่า "ฉันเคยเกลียดการเดินทางของฉัน จักรยานคันใหม่ผมก็เปลี่ยนไปอย่างสิ้นเชิง" จะเข้าใจได้ก็ต่อเมื่ออ่านตามลำดับ โมเดลอย่าง CNN/RNN สามารถอนุมานความหมายได้จากลำดับของคำในตัวอย่าง สำหรับโมเดลเหล่านี้ เราจะแสดงข้อความเป็นลำดับโทเค็นโดยคงลำดับไว้
การแปลงข้อมูลเป็นโทเค็น
ข้อความอาจแสดงเป็นลำดับอักขระหรือชุดคำก็ได้ เราพบว่าการใช้การนำเสนอระดับคำจะให้ประสิทธิภาพ ที่ดีกว่าโทเค็นของอักขระ นี่เป็นบรรทัดฐานทั่วไป ตามอุตสาหกรรมอีกด้วย การใช้โทเค็นอักขระจะเหมาะสมก็ต่อเมื่อข้อความมีการพิมพ์ผิดจำนวนมาก ซึ่งปกติแล้วมักจะไม่เป็นเช่นนั้น
เวกเตอร์
เมื่อเราแปลงตัวอย่างข้อความเป็นลำดับคำแล้ว เราต้องเปลี่ยนลำดับเหล่านี้เป็นเวกเตอร์ตัวเลข ตัวอย่างด้านล่างแสดงดัชนีที่กำหนดให้กับยูนิแกรมที่สร้างขึ้นสำหรับข้อความ 2 รายการ และตามด้วยลำดับของดัชนีโทเค็นที่มีการแปลงข้อความแรก
Texts: 'The mouse ran up the clock' and 'The mouse ran down'
ดัชนีที่กำหนดสำหรับทุกโทเค็น:
{'clock': 5, 'ran': 3, 'up': 4, 'down': 6, 'the': 1, 'mouse': 2}
หมายเหตุ: คำว่า "the" เกิดขึ้นบ่อยที่สุด จึงมีการกำหนดค่าดัชนีเป็น 1 ไลบรารีบางแห่งจองดัชนีเป็น 0 สำหรับโทเค็นที่ไม่รู้จัก
ลำดับดัชนีโทเค็น
'The mouse ran up the clock' = [1, 2, 3, 4, 1, 5]
มี 2 ตัวเลือกที่ใช้ในการกำหนดลำดับโทเค็นเป็นเวกเตอร์ ดังนี้
การเข้ารหัสแบบฮอตเดียว: ลำดับจะแสดงโดยใช้เวกเตอร์ของคำในพื้นที่มิติ n = ขนาดของคำศัพท์ การนำเสนอแบบนี้ได้ผลมาก เมื่อเราแปลงข้อมูลเป็นตัวละคร คำศัพท์จึงน้อย เมื่อเราแปลงข้อมูลเป็นคำเป็นโทเค็น คำศัพท์มักมีโทเค็นเป็นหมื่นๆ โทเค็น ทำให้เวกเตอร์ที่มีความร้อนเพียงเส้นเดียวนั้นไม่มีประโยชน์นัก เช่น
'The mouse ran up the clock' = [
[0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0]
]
การฝังคำ: คำต่างๆ มีความหมายเชื่อมโยงอยู่ ดังนั้น เราจึงแสดงโทเค็นคำในพื้นที่เวกเตอร์ที่หนาแน่นได้ (จำนวนจริงประมาณ 200-300 รายการ) โดยที่ตำแหน่งและระยะห่างระหว่างคำจะระบุความหมายของคำเหล่านั้น (ดูรูปที่ 7) การนำเสนอลักษณะนี้เรียกว่า การฝังคำ
รูปที่ 7: การฝังคำ
โมเดลลำดับมักมีเลเยอร์ที่ฝังเป็นเลเยอร์แรก เลเยอร์นี้จะเรียนรู้วิธีเปลี่ยนลำดับของดัชนีคำเป็นเวกเตอร์การฝังคำระหว่างกระบวนการฝึก กล่าวคือ ดัชนีคำแต่ละคำจะจับคู่กับเวกเตอร์หนาแน่นของค่าจริงซึ่งแสดงตำแหน่งของคำในช่องว่างทางอรรถศาสตร์ (ดูรูปที่ 8)
รูปที่ 8: การฝังเลเยอร์
การเลือกฟีเจอร์
คำบางคำในข้อมูลของเราไม่ได้มีผลต่อการคาดการณ์ป้ายกำกับ เราสามารถเพิ่มประสิทธิภาพกระบวนการเรียนรู้ของเรา โดยลบคำที่หายากหรือไม่เกี่ยวข้องออกจากคำศัพท์ของเรา ในความเป็นจริง เราสังเกตเห็นว่าการใช้ฟีเจอร์บ่อยที่สุด 20,000 รายการนั้นถือว่าเพียงพอแล้ว กรณีนี้ก็ใช้ได้กับโมเดล n-gram ด้วยเช่นกัน (ดูรูปที่ 6)
มาลองพิจารณาขั้นตอนด้านบนทั้งหมดในการทำเวกเตอร์ตามลำดับกัน โค้ดต่อไปนี้จะทำงานเหล่านี้
- แปลงข้อความให้เป็นคำ
- สร้างคำศัพท์โดยใช้โทเค็น 20,000 อันดับแรก
- แปลงโทเค็นเป็นเวกเตอร์ตามลำดับ
- เลื่อนลำดับให้มีความยาวคงที่
from tensorflow.python.keras.preprocessing import sequence from tensorflow.python.keras.preprocessing import text # Vectorization parameters # Limit on the number of features. We use the top 20K features. TOP_K = 20000 # Limit on the length of text sequences. Sequences longer than this # will be truncated. MAX_SEQUENCE_LENGTH = 500 def sequence_vectorize(train_texts, val_texts): """Vectorizes texts as sequence vectors. 1 text = 1 sequence vector with fixed length. # Arguments train_texts: list, training text strings. val_texts: list, validation text strings. # Returns x_train, x_val, word_index: vectorized training and validation texts and word index dictionary. """ # Create vocabulary with training texts. tokenizer = text.Tokenizer(num_words=TOP_K) tokenizer.fit_on_texts(train_texts) # Vectorize training and validation texts. x_train = tokenizer.texts_to_sequences(train_texts) x_val = tokenizer.texts_to_sequences(val_texts) # Get max sequence length. max_length = len(max(x_train, key=len)) if max_length > MAX_SEQUENCE_LENGTH: max_length = MAX_SEQUENCE_LENGTH # Fix sequence length to max value. Sequences shorter than the length are # padded in the beginning and sequences longer are truncated # at the beginning. x_train = sequence.pad_sequences(x_train, maxlen=max_length) x_val = sequence.pad_sequences(x_val, maxlen=max_length) return x_train, x_val, tokenizer.word_index
เวกเตอร์ของป้ายกำกับ
เราได้ดูวิธีแปลงข้อมูลข้อความตัวอย่างเป็นเวกเตอร์ของตัวเลขแล้ว โดยต้องใช้กระบวนการที่คล้ายกันกับป้ายกำกับด้วย เราสามารถแปลงป้ายกำกับเป็นค่าในช่วง [0, num_classes - 1]
ได้ เช่น หากมี 3 คลาส เราสามารถใช้ค่า 0, 1 และ 2 เพื่อแทนค่าเหล่านั้นได้ ภายในเครือข่าย เครือข่ายจะใช้เวกเตอร์เพียงค่าเดียวเพื่อแสดงค่าเหล่านี้ (เพื่อหลีกเลี่ยงการอนุมานความสัมพันธ์ที่ไม่ถูกต้องระหว่างป้ายกำกับ) การนำเสนอนี้ขึ้นอยู่กับฟังก์ชันการสูญเสียและฟังก์ชันการเปิดใช้งานเลเยอร์สุดท้ายที่เราใช้ในโครงข่ายประสาท เราจะดูข้อมูลเพิ่มเติมเกี่ยวกับ
กลยุทธ์เหล่านี้ในส่วนถัดไป