Chuẩn hóa văn bản
Các quy trình của chuẩn hóa văn bản
Tách văn bản thành nhiều câu (Segmenting sentence)
Tách (phân đoạn) từ - Tokenizing (segmenting) words
Chuẩn hóa từ
1. Chuẩn hóa từ - Word Normalization
1.1. Slide chi tiết
1.2. Các notebook quan trọng
Note book Lemmatization:
https://colab.research.google.com/drive/1WV7xXZNRQTRiPxFpAGEQU7HCdA4lnfdL?usp=sharing
Notebook Stopwords Filtering:
https://colab.research.google.com/drive/16uOMMttbjGfI0_i_WK_D_hsgeEgkiYYd#scrollTo=-vxHKceK20GP
Jcard Distance vs Edit Distance
https://colab.research.google.com/drive/1ILNYMooaKTJk08IKaTHNzYFl9UkdDSeF?usp=sharing
1.3. Video Zoom
1.4. Video Zoom NLP 02 - 10 - 7 - 2024
2. Lemmatization
2.1. Giải thích lemmatization
Lemmatization là task xác định hai từ có cùng gốc.
Ví dụ say, said, saying cùng là dạng của từ say.
Say là lemma của 3 từ này.
Chúng ta sẽ xây dựng một lemmatizer mapping tất cả các từ này về nguyên bản.
2.2. 9 cách thực hiện Lemmatization
9 cách thực hiện Lemmatization:
https://www.geeksforgeeks.org/python-lemmatization-approaches-with-examples/
3. Case Folding
3.1. Case Folding
Case Folding là xử lý văn bản viết hoa và viết thường.
Trong một số trường hợp chúng ta có thể đưa văn bản viết hoa thành viết thường và ngược lại.
4. Spacy cho Tiếng Việt
4.1. Một số lưu ý khi làm việc với Tiếng Việt
4.2. Spacy cho Tiếng Việt
5. Thư viện Underthesea
5.1. Giới thiệu thư viện Underthesea
🌊 Bộ công cụ NLP tiếng Việt - Underthesea là một bộ công cụ bằng ngôn ngữ Python mã nguồn mở bao gồm các mô-đun, bộ dữ liệu và hướng dẫn hỗ trợ nghiên cứu và phát triển trong lĩnh vực Xử lý Ngôn ngữ Tự nhiên tiếng Việt. Thư viện có các API dễ sử dụng để nhanh chóng áp dụng các mô hình NLP tiền huấn luyện cho văn bản tiếng Việt của bạn, như phân đoạn từ (Word segmentation), gán phần loại từ (PoS), nhận diện thực thể đặt tên (Named entity recognition - NER), phân loại văn bản (Text Classification) và phân tích phụ thuộc (Dependency Parsing).
Chi tiết thư viện tại đây.
Tách văn bản thành các câu
>>> from underthesea import sent_tokenize
>>> text = 'Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng. Amanda cũng thoải mái với mối quan hệ này.'
>>> sent_tokenize(text)
[
"Taylor cho biết lúc đầu cô cảm thấy ngại với cô bạn thân Amanda nhưng rồi mọi thứ trôi qua nhanh chóng.",
"Amanda cũng thoải mái với mối quan hệ này."
]
Chuẩn hóa văn bản
>>> from underthesea import text_normalize
>>> text_normalize("Ðảm baỏ chất lựơng phòng thí nghịêm hoá học")
"Đảm bảo chất lượng phòng thí nghiệm hóa học"
Vị trí dấu được điều chỉnh phù hợp.
Tách câu thành từ
Điểm đặc biệt trong Tiếng Việt đó là các từ hay đi với nhau để tạo thành cụm từ
>>> from underthesea import word_tokenize
>>> text = "Chàng trai 9X Quảng Trị khởi nghiệp từ nấm sò"
>>> word_tokenize(text)
["Chàng trai", "9X", "Quảng Trị", "khởi nghiệp", "từ", "nấm", "sò"]
>>> word_tokenize(sentence, format="text")
"Chàng_trai 9X Quảng_Trị khởi_nghiệp từ nấm sò"
>>> text = "Viện Nghiên Cứu chiến lược quốc gia về học máy"
>>> fixed_words = ["Viện Nghiên Cứu", "học máy"]
>>> word_tokenize(text, fixed_words=fixed_words)
"Viện_Nghiên_Cứu chiến_lược quốc_gia về học_máy"
POS Tagging - Đánh nhãn từ
>>> from underthesea import pos_tag
>>> pos_tag('Chợ thịt chó nổi tiếng ở Sài Gòn bị truy quét')
[('Chợ', 'N'),
('thịt', 'N'),
('chó', 'N'),
('nổi tiếng', 'A'),
('ở', 'E'),
('Sài Gòn', 'Np'),
('bị', 'V'),
('truy quét', 'V')]
Chợ là danh từ, Sài gòn là danh từ riêng, truy quét là động từ.
Nhận diện tên thực thể
>>> from underthesea import ner
>>> text = 'Chưa tiết lộ lịch trình tới Việt Nam của Tổng thống Mỹ Donald Trump'
>>> ner(text)
[('Chưa', 'R', 'O', 'O'),
('tiết lộ', 'V', 'B-VP', 'O'),
('lịch trình', 'V', 'B-VP', 'O'),
('tới', 'E', 'B-PP', 'O'),
('Việt Nam', 'Np', 'B-NP', 'B-LOC'),
('của', 'E', 'B-PP', 'O'),
('Tổng thống', 'N', 'B-NP', 'O'),
('Mỹ', 'Np', 'B-NP', 'B-LOC'),
('Donald', 'Np', 'B-NP', 'B-PER'),
('Trump', 'Np', 'B-NP', 'I-PER')]
Phân loại chủ đề câu
>>> from underthesea import classify
>>> classify('HLV đầu tiên ở Premier League bị sa thải sau 4 vòng đấu')
['The thao']
>>> classify('Hội đồng tư vấn kinh doanh Asean vinh danh giải thưởng quốc tế')
['Kinh doanh']
>> classify('Lãi suất từ BIDV rất ưu đãi', domain='bank')
['INTEREST_RATE']
Phân tích cảm xúc câu
>>> from underthesea import sentiment
>>> sentiment('hàng kém chất lg,chăn đắp lên dính lông lá khắp người. thất vọng')
'negative'
>>> sentiment('Sản phẩm hơi nhỏ so với tưởng tượng nhưng chất lượng tốt, đóng gói cẩn thận.')
'positive'
>>> sentiment('Đky qua đường link ở bài viết này từ thứ 6 mà giờ chưa thấy ai lhe hết', domain='bank')
['CUSTOMER_SUPPORT#negative']
>>> sentiment('Xem lại vẫn thấy xúc động và tự hào về BIDV của mình', domain='bank')
['TRADEMARK#positive']
Các ví dụ được trích xuất từ trang chủ của Underthesea.
6. Đọc thêm - Thư viện Spacy
6.1. Giới thiệu thư viện Spacy
SpaCy là một thư viện xử lý ngôn ngữ tự nhiên mã nguồn mở cho Python. Nó được thiết kế để thực hiện xử lý ngôn ngữ tự nhiên. SpaCy cung cấp các mô hình được đào tạo trước cho nhiều ngôn ngữ và nó nổi tiếng với tốc độ và hiệu suất của mình khi xử lý lượng văn bản lớn.
Các tính năng
Tách token (Tokenization): SpaCy có thể phân đoạn văn bản thành từ hoặc đơn vị con từ, giúp dễ dàng phân tích và xử lý các yếu tố ngôn ngữ.
POS-Tagging: Gán các loại (ví dụ: danh từ, động từ, tính từ) cho mỗi từ trong một câu.
Nhận dạng thực thể tên thực thể ( Name Entity Recognition - NER): SpaCy có thể xác định và phân loại các thực thể như người, tổ chức, địa điểm, ngày tháng và nhiều hơn nữa trong một đoạn văn bản cho trước.
Phân tích phụ thuộc (Dependency Parsing): Nó phân tích cấu trúc ngữ pháp của một câu, xác định mối quan hệ giữa các từ và phụ thuộc ngữ pháp của chúng.
Lemmatization: SpaCy có thể giảm các từ về gốc ví dụ running từ gốc là run , điều này hữu ích cho các nhiệm vụ như chuẩn hóa văn bản.
Vector hóa từ: SpaCy cung cấp các vector từ đã được đào tạo trước có thể được sử dụng cho các nhiệm vụ như tính toán độ tương đồng và phân cụm tài liệu.
Tùy chỉnh: Người dùng có thể đào tạo các mô hình riêng của họ bằng cách sử dụng SpaCy cho các lĩnh vực hoặc ngôn ngữ cụ thể.
Cài đặt và thử tách token
# Install spaCy
!pip install spacy
# Download the English language model
!python -m spacy download en_core_web_sm
# Import spaCy
import spacy
# Load the English language model
nlp = spacy.load("en_core_web_sm")
# Text to be tokenized
text = "SpaCy is a powerful natural language processing library for Python."
# Process the text with spaCy
doc = nlp(text)
# Access the tokens
tokens = [token.text for token in doc]
# Print the tokens
print(tokens)
Kết quả thu được:
['SpaCy', 'is', 'a', 'powerful', 'natural', 'language', 'processing', 'library', 'for', 'Python', '.']
Ngoài ra Spacy còn hỗ trợ sử dụng bộ tách token của Bert
Bước 1:
Cài đặt thư viện tokenizers
!pip install tokenizers
Bước 2:
Tải từ điển của Bert: https://github.com/microsoft/SDNet/blob/master/bert_vocab_files/bert-base-uncased-vocab.txt
Như bạn đã biết Bert sử dụng thuật toán WordPiece để tách từ. Xem chi tiết về thuật toán này tại đây.
Một phần của từ điển:
jerry
##cing
forehead
mp
##ens
manage
schedule
totally
remix
##ii
forests
occupation
print
nicholas
brazilian
strategic
vampires
engineers
76
roots
Bước 3:
Chạy đoạn code sau:
from tokenizers import BertWordPieceTokenizer
from spacy.tokens import Doc
import spacy
class BertTokenizer:
def __init__(self, vocab, vocab_file, lowercase=True):
self.vocab = vocab
self._tokenizer = BertWordPieceTokenizer(vocab_file, lowercase=lowercase)
def __call__(self, text):
tokens = self._tokenizer.encode(text)
words = []
spaces = []
for i, (text, (start, end)) in enumerate(zip(tokens.tokens, tokens.offsets)):
words.append(text)
if i < len(tokens.tokens) - 1:
# If next start != current end we assume a space in between
next_start, next_end = tokens.offsets[i + 1]
spaces.append(next_start > end)
else:
spaces.append(True)
return Doc(self.vocab, words=words, spaces=spaces)
nlp = spacy.blank("en")
nlp.tokenizer = BertTokenizer(nlp.vocab, "bert-base-uncased-vocab.txt")
doc = nlp("Justin Drew Bieber is a Canadian singer, songwriter, and actor.")
print(doc.text, [token.text for token in doc])
# [CLS]justin drew bi##eber is a canadian singer, songwriter, and actor.[SEP]
# ['[CLS]', 'justin', 'drew', 'bi', '##eber', 'is', 'a', 'canadian', 'singer',
# ',', 'songwriter', ',', 'and', 'actor', '.', '[SEP]']
Câu đầu vào
Justin Drew Bieber is a Canadian singer, songwriter, and actor.
Danh sách token:
['[CLS]', 'justin', 'drew', 'bi', '##eber', 'is', 'a', 'canadian', 'singer', ',', 'songwriter', ',', 'and', 'actor', '.', '[SEP]']
Mã:
[101, 6796, 3881, 12170, 22669, 2003, 1037, 3010, 3220, 1010, 6009, 1010, 1998, 3364, 1012, 102]
6.2. Kiến trúc Spacy
Kiến trúc lõi của Spacy bao gồm các thành phần sau
Lớp Language
Lớp này sử dụng để xử lý văn bản và chuyển thành đối tượng Doc. Biến sử dụng là
nlp
Lớp Vocab
Vector của từ và các thuộc tính từ vựng nằm bên trong Vocab
Lớp Doc
Đối tượng Doc chứa chuỗi tokens và nhãn tương ứng.
6.3. Lớp Token
Lớp Token là lớp cơ bản nhất của Spacy
Các hàm chi tiết: https://spacy.io/api/token
Colab thực hành
https://colab.research.google.com/drive/1shlumEN5v_2hec2tsG8Lug9GY9NL0vix#scrollTo=5-5MgD0GkWuO
Cách chuyển văn bản thành chuỗi các Token
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("Give it back! He pleaded.")
token = doc[0]
assert len(token) == 4
Token "Give" có chiều dài bằng 4.
Bạn hoàn toàn có thể truy cập vào vector của token thông qua thuộc tính sau:
doc.vector
Giá trị:
array([-0.44362444, -0.42243072, 0.24426362, -0.5250266 , -0.6161702 ,
0.02204059, 0.7193219 , -0.06913738, 0.73015636, -0.04132657,
0.8562692 , 0.01586492, 0.23967174, 0.6728299 , -0.3021869 ,
-0.42327446, -0.63429636, -0.23890944, 0.55411714, -0.04030436,
-0.5412909 , 0.1478707 , -0.10700747, -0.4568573 , -0.29876074,
0.1954168 , -0.3442414 , 0.3816016 , -0.23591556, 0.56868124,
-0.37514243, 0.3038376 , 0.45029303, 0.08828391, 0.00681289,
1.0138968 , 0.02713898, -0.4370896 , 0.19944692, 0.06195561,
0.5192183 , 0.43405744, -0.03709241, -0.0522776 , -0.26715213,
-0.63727957, 0.8962819 , 0.20477763, 1.1272476 , -0.28066662,
0.28926829, -0.35164338, 0.23985343, -0.04558332, 0.39460275,
-0.512814 , 0.07970776, -0.73342353, 0.07322592, 0.07566743,
0.2871618 , -0.15763725, -0.38315025, 0.83427066, 0.30780405,
0.40762582, -0.42318252, -0.62158394, -0.46482322, -0.1420876 ,
-0.06470852, -0.1149256 , -0.66078633, -0.19819617, 1.0459654 ,
-0.12772019, 0.12497108, 0.40949053, -0.28095913, -0.51823884,
-0.02905612, -0.41069874, -0.7595876 , 0.54306173, 0.04127673,
0.5971903 , 0.19734517, -0.03522544, -0.64774954, 0.59700423,
-0.12963165, -0.3792712 , 0.2612036 , 0.28475076, -0.10839538,
0.22010735], dtype=float32)
6.4. Lớp Doc
Lớp Doc là lớp tạo ra những đối tượng chứa chuỗi các token.
6.5. Lớp Vocab
Xây dựng từ điển từ các văn bản
Từ nhiều văn bản ta xây dựng được từ điển thông qua việc tạo ra ánh xạ giữa các token với số hoặc giá trị băm hash.
Ví dụ trong trường hợp này:
Token I có giá trị 46904
Token love có giá trị 37020
Colab thực hành:
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("I love coffee")
print(doc.vocab.strings["coffee"]) # 3197928453018144401
print(doc.vocab.strings[3197928453018144401]) # 'coffee'
Trong trường hợp này hash của token "coffee" là 3197928453018144401
Hiển thị từ đã được đưa vào từ điển
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("I love coffee")
for word in doc:
lexeme = doc.vocab[word.text]
print(lexeme.text, lexeme.orth, lexeme.shape_, lexeme.prefix_, lexeme.suffix_,
lexeme.is_alpha, lexeme.is_digit, lexeme.is_title, lexeme.lang_)
Kết quả:
I 4690420944186131903 X I I True False True en
love 3702023516439754181 xxxx l ove True False False en
coffee 3197928453018144401 xxxx c fee True False False en
Đơn vị lưu trữ trong Vocab dưới dạng từ vựng (Lexeme). Một từ vựng có các thuộc tính sau:
Văn bản: Văn bản gốc của từ vựng.
Orth: Giá trị băm của từ vựng.
Shape: Hình dạng từ ngữ trừu tượng của từ vựng.
Prefix: Theo mặc định, là chữ cái đầu tiên của chuỗi từ.
Suffix: Theo mặc định, là ba chữ cái cuối cùng của chuỗi từ.
is alpha: Liệu từ vựng có bao gồm các ký tự chữ cái không?
is digit: Liệu từ vựng có bao gồm các chữ số không?
Giá trị băm của một token không thể decode để ra được từ đó và luôn phải có từ điển để ánh xạ ngược lại.
import spacy
from spacy.tokens import Doc
from spacy.vocab import Vocab
nlp = spacy.load("en_core_web_sm")
doc = nlp("I love coffee") # Original Doc
print(doc.vocab.strings["coffee"]) # 3197928453018144401
print(doc.vocab.strings[3197928453018144401]) # 'coffee' 👍
empty_doc = Doc(Vocab()) # New Doc with empty Vocab
# empty_doc.vocab.strings[3197928453018144401] will raise an error :(
empty_doc.vocab.strings.add("coffee") # Add "coffee" and generate hash
print(empty_doc.vocab.strings[3197928453018144401]) # 'coffee' 👍
new_doc = Doc(doc.vocab) # Create new doc with first doc's vocab
print(new_doc.vocab.strings[3197928453018144401]) # 'coffee' 👍
Kết quả:
3197928453018144401
coffee
coffee
coffee
6.6. Thiết kế Pipeline
Văn bản đi qua Spacy có thể trải qua các bước được định nghĩa trong pipeline
Các thành phần trong pipeline bạn có thể tùy biến
Tagger: part-of-speech tags
Parser: Thêm phụ thuộc
Ner: Phát hiện và đánh nhãn các thực thể
Lemmatizer: Đưa về dạng gốc
Textcat: Đánh nhãn văn bản
custom: Bổ sung các thuộc tính, hàm tùy chỉnh vào trong pipeline
7. Đọc thêm - Mô hình đề xuất bởi Kiss và Strunk
7.1. Từ viết tắt - Abbreviation
3 yếu tố chính của từ viết tắt:
Từ viết tắt sẽ trông dưới dạng các ký từ đi liền với dấu chấm. Ví dụ Mrs. hay Mr. hay Dr.
Từ viết tắt có xu hướng ngắn. Từ càng dài thì khả năng là từ viết tắt càng thấp.
Dấu chấm xuất hiện nhiều ở giữa từ viết tắt
Hệ thống phát hiện dấu chấm của Punkt phát hiện được 99.38% từ viết tắt. Tuy nhiên những từ viết tắt này chưa bao gồm
Chữ viết tắt tên ví dụ John F Kennedy - J.F.K hoặc Dr. J. Smith hoặc J.K. Rowling
Số thứ tự (Ordinal Numbers) như 1. 2.
Với việt kết luận được từ viết tắt thì tất cả các dấu chấm đứng sau những từ không phải từ viết tắt có thể coi là kết thúc câu (End of a Sentence).
Tuy nhiên có trường hợp dấu chấm vừa là kết thúc từ viết tắt cũng như là kết thúc câu.
7.2. Mô hình phân loại
Pipeline để phát hiện dấu chấm câu
Trích dẫn: https://aclanthology.org/J06-4003.pdf
Nguồn: https://aclanthology.org/J06-4003.pdf
1) Khâu đầu tiên - Type-based classification stage:
Phát hiện từ viết tắt thông qua:
Collocational bond: Từ viết tắt hay đi với dấu chấm đằng sau
Length: Chiều dài của từ viết tắt
Internal Periods: Từ viết tắt có dấu chấm câu bên trong
Occurrences without a final period: Từ viết tắt không có dấu chấm kết thúc
Sau bước này các từ sẽ được phân loại thành
<A>: Từ viết tắt
<E>: Dấu 3 chấm (...)
<S>: Dấu chấm mà không đi sau từ viết tắt thì quy thành kết thúc câu
2) Khâu thứ hai - Token-based classification stage
Phát hiện từ viết tắt ở cuối câu: <A> thành <A><S>
Phát hiện dấu ba chấm ở cuối câu: <E> thành <E> <S>
Cách phát hiện: sử dụng Regex:
\.\.+$
Phát hiện ký tự đầu câu: <S> thành <A> và <A> thành <A>. Ví dụ
A.
hayB.
Cách phát hiện: sử dụng Regex:
[^\W\d]\.$
Phát hiện số thứ tự: <S> Thành <A>. Ví dụ
1.
hay1000.
hay-1.
Cách phát hiện: sử dụng Regex:
^-?[\.,]?\d[\d,\.-]*\.?$
3) Khâu thứ ba
Kết luật từ đó có phải từ viết tắt hay không với các nhãn. <A>, <E>, <S>, <A><S>, <E><S>
7.3. Dunning log-likelihood
Dunning log-likelihood dùng làm gì?
Dunning log-likelihood so sánh tần suất của một feature (trường hợp này là một từ) trong hai bộ ngữ liệu khác nhau.
Công thức:
Với trường hợp có hai sự kiện:
Các giá trị cần được giải thích thêm
Likelihood: Trong thống kê, likelihood là giá trị đo lường khả năng quan sát dữ liệu cho trước của một mô hình
Log-Likelihood: Hàm log thường sử dụng để tránh các giá trị nhỏ nhân với nhau
Ratio được sử dụng bằng việc chia likelihood của một giả thuyết thay thế (alternative hypothesis) cho likelihood của giả thiết không (null hypothesis).
Giá trị E được tính như sau:
Code:
https://github.com/dhmit/gender_novels/blob/master/gender_novels/analysis/dunning.py
Ví dụ về Dunning log-likelihood khi so sánh từ riêng lẻ
Có 2 tập ngữ liệu m và f
Tập N1 có tất cả 1000 từ
Tập N2 có tất cả 5000 từ
Từ "Ngọc" xuất hiện trong tập N1 10 lần tức là O1 = 10
Từ "Ngọc" xuất hiện trong tập N2 30 lần tức là O2 = 30
Trong trường hợp này:
Cách tính E1 của Ngọc sẽ như sau:
C: Số lượng Ngọc trong cả 2 tập: 10 + 30 = 40 lần
N1: 100
N: tổng size của hai dataset: 1000 + 5000 = 6000 lần
E1 = 40 x 100 / 6000 = 2/3
Cách tính E2 của Ngọc sẽ như sau
E2 = 40 x 5000 / 6000 = 100/3
Giá trị Dunning Log Likelihood sẽ bằng
2 x (O1 x ln(O1/E1) + O2 x ln(O2/E2))
7.4. Likelihood là lõi
Cả khâu một và hai đều sử dụng khả năng (Likelihood) để xác định mối quan hệ giữa các từ viết tắt và dấu chấm của nó cũng như mối quan hệ của dấu chấm kết thúc câu và những từ đi sau nó và những từ xung quanh một dấu chấm. Chú ý từ w trong bài viết này là từ viết tắt.
1) Sử dụng Likelihood trong Khâu 1
Log-likelihood đề xuất bởi Dunning năm 1993 kiểm tra liệu xác suất của một từ có phụ thuộc với việc với loại từ ở phía trước không.
Nguồn: https://aclanthology.org/J06-4003.pdf
Giả thuyết Null: P(•|w) = p = P(•|¬w)
Xác suất xuất hiện dấu chấm không phụ thuộc vào có hay không từ phía trước. Ký hiệu ¬ là không có từ đó
Giả thuyết thay thế: P(•|w) = p1 khác p2 = P(•|¬w)
Có sự phụ thuộc giữa dấu chấm và từ trước nó
Log-likelihood
Tuy nhiên tác giả đã thay đổi công thức này
Công thức số (4) gần với công thức (1)
Công thức số (5) phản ánh rằng chúng ta không chỉ yêu cầu một dấu chấm xuất hiện cùng với một từ viết tắt nhiều, mà còn dấu chấm gần như luôn xuất hiện sau một từ. Việc cài đặt giá trị 0.99 thay vì 1, chúng ta cho rằng một từ viết tắt thi thoảng sẽ không có dấu chấm đi kèm trong ngữ liệu.
Cách lập trình như sau:
def _dunning_log_likelihood(count_a, count_b, count_ab, N):
"""
A function that calculates the modified Dunning log-likelihood
ratio scores for abbreviation candidates. The details of how
this works is available in the paper.
"""
p1 = count_b / N
p2 = 0.99
null_hypo = count_ab * math.log(p1 + 1e-8) + (count_a - count_ab) * math.log(
1.0 - p1 + 1e-8
)
alt_hypo = count_ab * math.log(p2) + (count_a - count_ab) * math.log(1.0 - p2)
likelihood = null_hypo - alt_hypo
return -2.0 * likelihood
Trong trường hợp này
count_a sẽ là số lần từ này xuất hiện với dấu chấm câu ở cuối và số lần không xuất hiện dấu chấm ở cuối
count_b sẽ là số lượng dấu chấm
count_ab sẽ là số lần từ này xuất hiện với dấu chấm ở cuối
count_a - count_ab = số lần từ này xuất hiện với dấu chấm không ở cuối
N là số lượng tất cả các tokens trong dataset
p1: Xác suất xuất hiện dấu chấm ở cuối
p2: Xác suất không xuất hiện dấu chấm ở cuối
Lập trình Giả thuyết null:
Sử dụng phân phối nhị thức với count_a lần xảy ra
Từ đi sau dấu chấm ở cuối với xác suất p1 xảy ra count_ab lần với xác suất p1
Từ đi sau không có dấu chấm ở cuối xảy ra count_a - count_ab lần với xác suất 1 - p1
null_hypo = count_ab * math.log(p1 + 1e-8) + (count_a - count_ab) * math.log(1.0 - p1 + 1e-8)
Lập trình Giả thuyết thay thế:
Tương tự giả thuyết null
alt_hypo = count_ab * math.log(p2) + (count_a - count_ab) * math.log(1.0 - p2)
Tính giá trị likelihood
likelihood = -2.0 * (null_hypo - alt_hypo)
2) Sử dụng Likelihood trong Khâu 2
Với một cặp từ w1 và w2 xung quanh một dấu chấm và chúng ta kiểm tra rằng liệu có việc xuất hiện thường xuyên giữa chúng hay không.
Giả thuyết Null: P(w2 | w1) = p = P(w2 | ¬w1). Hai từ không bao giờ đi liền với nhau
Giả thuyết thay thế: P(w2 | w1) = p1 khác p2 = P(w2 | ¬w1)
Giá trị log λ sẽ lớn nếu xác suất p1 và p2 khác nhau nhiều tuy nhiên ta sẽ quan tâm tới trường hợp p1 lớn hơn nhiều p2 hay p1 >> p2 với ý nghĩa w2 xuất hiện sau w1 nhiều hơn kỳ vọng.
Nếu p1 < p2 ý nghĩa w2 xuất hiện sau w1 ít hơn kỳ vọng
Vì trường hợp p1 << p2 hiếm nên ta sẽ coi là p1 >> p2.
Công thức sử dụng:
Vế phải là w2 hay đi sau w1
Vế trái là w2 đi một mình
Nếu hai vế của phương trình này khác nhau, giá trị log λ sẽ lớn hơn 0. Giá trị log λ lớn có nghĩa rằng w1 và w2 hay đi với nhau hay vế phải của (6) lớn hơn vế trái. Nếu vế trái lớn hơn vế phải, giá trị log λ vẫn lớn hơn 0 tuy nhiên phản ánh w2 không xuất hiện thường xuyên sau w1 như kỳ vọng.
def _col_log_likelihood(count_a, count_b, count_ab, N):
"""
A function that will just compute log-likelihood estimate, in
the original paper it's described in algorithm 6 and 7.
This *should* be the original Dunning log-likelihood values,
unlike the previous log_l function where it used modified
Dunning log-likelihood values
"""
# count_a: Số lượng w1 xuất hiện
# count_b: Số lượng w2 xuất hiện
# count_ab: Số lượng w1w2
# N - count_a: Số lượng không phải w1
# N - count_a - count_b + count_ab: Không có w1 và w2. Cách viết khác: N - count_a - (count_b - count_ab)
# count_b - count_ab: w2 đứng một mình không đi với w1
# count_a - count_ab: w1 đứng một mình không đi với w2
# p: Xác suất w2 đứng một mình, sử dụng cho null hypothesis
p = count_b / N
# p1: P(w2|w1)
p1 = count_ab / count_a
try:
# p2: P(w2|¬w1)
p2 = (count_b - count_ab) / (N - count_a)
except ZeroDivisionError:
p2 = 1
try:
# Binomial null hypothesis p. P(w2|w1)
summand1 = count_ab * math.log(p) + (count_a - count_ab) * math.log(1.0 - p)
except ValueError:
summand1 = 0
try:
# Binomial null hypothesis p. P(w2|¬w1)
summand2 = (count_b - count_ab) * math.log(p) + (
N - count_a - count_b + count_ab
) * math.log(1.0 - p)
except ValueError:
summand2 = 0
if count_a == count_ab or p1 <= 0 or p1 >= 1:
summand3 = 0
else:
# Binomial alternative hypothesis p1: P(w2|w1)
summand3 = count_ab * math.log(p1) + (count_a - count_ab) * math.log(
1.0 - p1
)
if count_b == count_ab or p2 <= 0 or p2 >= 1:
summand4 = 0
else:
# Binomial alternative hypothesis p2 P(w2|¬w1)
summand4 = (count_b - count_ab) * math.log(p2) + (
N - count_a - count_b + count_ab
) * math.log(1.0 - p2)
likelihood = summand1 + summand2 - summand3 - summand4
return -2.0 * likelihood
Chú ý đọc kỹ comment code để hiểu rõ cách tính số lượng sự kiện kèm theo xác suất sự kiện đó xảy ra.
7.5. Type-based Classification
3 yếu tố xác định một từ viết tắt
Hay đi cùng với dấu chấm ở cuối
Ngắn
Rất nhiều từ viết tắt chứa dấu chấm bên trong
Một số ví dụ để cùng phân tích
Các từ viết nghiêng không phải từ viết tắt. Tuy nhiên từ ounces rất hay xuất hiện đi kèm dấu chấm đằng sau (4 lần) mà không bao giờ xuất hiện nếu không có dấu chấm đi kèm.
Cho nên cách làm chỉ đơn giản dùng 3 tiêu chí này chưa đủ để đánh giá từ ounces là một từ viết tắt.
Chiều dài của từ viết tắt ngắn hơn so với từ không viết tắt.
2) Mô hình hóa các yếu tố
2.1) Yếu tố đầu tiên: Kết thúc bằng dấu chấm
2.2) Yếu tố thứ hai: Chiều dài của từ
Chiều dài của từ sẽ loại bỏ số lượng các dấu chấm trong từ.
Trường hợp này chiều dài sẽ bằng 3 vì đã loại bỏ số lượng dấu chấm. Tính chất từ viết tắt ngắn dựa trên số lượng ký tự không bao gồm dấu chấm.
Giá trị mô phỏng được lựa chọn dưới dạng hàm mũ vì nó phản ánh chính xác khả năng là từ viết tắt khi chiều dài giảm đi.
Biểu đồ thể hiện mối quan hệ giữa chiều dài của từ và khả năng từ đó là từ viết tắt, mối quan hệ dưới dạng hàm mũ. Càng ngắn thì khả năng là từ viết tắt càng cao.
2.3) Yếu tố thứ ba: Số lượng dấu chấm trong từ
Hàm đếm số lượng dấu chấm trong từ. Từ càng nhiều dấu chấm bên trong thì có khả năng càng cao là từ viết tắt.
3) Tổng kết các yếu đố và đưa ra kết luận
0.3 là một threshold đặt trước.