Thêm dấu tiếng việt là một trong những bài toán khá hay trong xử lý ngôn ngữ tự nhiên. Ở đây, mình đã tiến hành thu thập dữ liệu bài báo của nhiều nguồn khác nhau như zing.vn, vnexpress, kenh14.vn … làm kho ngữ liệu và xây dựng mô hình.
Để tiến hành thực nghiệm, mình sẽ lấy một số đoạn văn mẫu ở trang tin tức của thế giới di động (https.www.thegioididong.com) (mình không crawl nội dung tin tức ở trang này làm dữ liệu học).
Ở bài viết link https://www.thegioididong.com/tin-tuc/3-ngay-cuoi-tuan-mua-laptop-online-tang-them-pmh-den-400k-tra-gop-0--1151334, mình lấy đoạn mở đầu “Từ ngày 15/3 đến 17/3, nhiều mẫu laptop tại Thế Giới Di Động sẽ được ưu đãi mạnh, tặng phiếu mua hàng đến 400 ngàn đồng, trả góp 0% và nhiều quà tặng hấp dẫn khác khi mua theo hình thức ONLINE. Nếu đang có nhu cầu mua laptop, bạn hãy nhanh chóng xem qua danh sách sản phẩm dưới đây nhé.”, bỏ dấu của câu đi, thì mình được câu
“Tu ngay 15/3 den 17/3, nhieu mau laptop tai The Gioi Di Dong se duoc uu dai manh, tang phieu mua hang den 400 ngan dong, tra gop 0% va nhieu qua tang hap dan khac khi mua theo hinh thuc ONLINE. Neu dang co nhu cau mua laptop, ban hay nhanh chong xem qua danh sach san pham duoi day nhe.”
Sử dụng mô hình mình đã huấn luyện, thu được kết quả như sau:
“Từ ngày 15/3 đến 17/3 m t m, nhiều mẫu laptoP tạI thế giỚi di động sẽ được ưu đãi mạnh, tang phiếu mua hàng đến 400 ngàn đồng, trả góp 0 r% và nhiều quà tặng hấp dẫn khác khi mua theo hìNH THỨc Onfine. nếu đang có nhu cầu mua laptop, bạn hãy nhanh chóng xem qua danh sách sản phẩm dưới”
Kết quả khá khả quan phải không các bạn, còn một số lỗi nhỏ ở phần nhận dạng ký tự hoa nữa. Mình sẽ fix lại ở các bài viết sau.
Mình thí nghiệm tiếp với phần đầu bài viết https://www.thegioididong.com/tin-tuc/apple-ban-ra-thi-truong-35-trieu-cap-tai-nghe-airpods-nam-2018-1155181. Đoạn “Hôm nay, báo cáo của Counterpoint Research cho thấy, trong năm 2018 Apple đã bán được khoảng 35 triệu cặp tai nghe không dây AirPods. Theo hãng phân tích này, AirPods hiện là tai nghe không dây phổ biến nhất.”, bỏ dấu tiếng việt là thu được “Hom nay, bao cao cua Counterpoint Research cho thay, trong nam 2018 Apple da ban duoc khoang 35 trieu cap tai nghe khong day AirPods. Theo hang phan tich nay, AirPods hien la tai nghe khong day pho bien nhat.”
Kết quả của mô hình: “Hôm nay, bạo cáo của Coorteenria eEeeroa c ttt, trong năm 2018 apple đã bán được khoảng 35 triệu cặp tại nghe không đầy aitcoDs. theo Hàng phân tích này, airxoDs Hiện là tai nghe không dạy phổ biến nhất.”
Mô hình của mình cho lặp 50 lần. Mình tiến hành thí nghiệm và publish mô hình ở lần lặp thứ 10.
Mã nguồn file predict
1from keras.models import load_model
2model = load_model('a_best_weight.h5')
3
4from collections import Counter
5
6import numpy as np
7
8import utils
9import string
10import re
11
12alphabet = set('\x00 _' + string.ascii_lowercase + string.digits + ''.join(utils.ACCENTED_TO_BASE_CHAR_MAP.keys()))
13
14print("alphabet",alphabet)
15codec = utils.CharacterCodec(alphabet, utils.MAXLEN)
16
17def guess(ngram):
18 text = ' '.join(ngram)
19 text += '\x00' * (utils.MAXLEN - len(text))
20 if utils.INVERT:
21 text = text[::-1]
22 preds = model.predict_classes(np.array([codec.encode(text)]), verbose=0)
23 rtext = codec.decode(preds[0], calc_argmax=False).strip('\x00')
24 if len(rtext)>0:
25 index = rtext.find('\x00')
26 if index>-1:
27 rtext = rtext[:index]
28 return rtext
29
30
31def add_accent(text):
32 # lowercase the input text as we train the model on lowercase text only
33 # but we keep the map of uppercase characters to restore cases in output
34 is_uppercase_map = [c.isupper() for c in text]
35 text = utils.remove_accent(text.lower())
36
37 outputs = []
38 words_or_symbols_list = re.findall('\w[\w ]*|\W+', text)
39
40 # print(words_or_symbols_list)
41
42 for words_or_symbols in words_or_symbols_list:
43 if utils.is_words(words_or_symbols):
44 outputs.append(_add_accent(words_or_symbols))
45 else:
46 outputs.append(words_or_symbols)
47 # print(outputs)
48 output_text = ''.join(outputs)
49
50 # restore uppercase characters
51 output_text = ''.join(c.upper() if is_upper else c
52 for c, is_upper in zip(output_text, is_uppercase_map))
53 return output_text
54
55def _add_accent(phrase):
56 grams = list(utils.gen_ngram(phrase.lower(), n=utils.NGRAM, pad_words=utils.PAD_WORDS_INPUT))
57
58 guessed_grams = list(guess(gram) for gram in grams)
59 # print("phrase",phrase,'grams',grams,'guessed_grams',guessed_grams)
60 candidates = [Counter() for _ in range(len(guessed_grams) + utils.NGRAM - 1)]
61 for idx, gram in enumerate(guessed_grams):
62 for wid, word in enumerate(re.split(' +', gram)):
63 candidates[idx + wid].update([word])
64 output = ' '.join(c.most_common(1)[0][0] for c in candidates if c)
65 return output.strip('\x00 ')
66
67
68
69# print(add_accent('do,'))
70# print(add_accent('7.3 inch,'))
71# print(add_accent('Truoc do, tren san khau su kien SDC 2018, giam doc cao cap mang marketing san pham di dong cua Samsung, ong Justin Denison da cam tren tay nguyen mau cua thiet bi nay. Ve co ban, no chang khac gi mot chiec may tinh bang 7.3 inch, duoc cau thanh tu nhieu lop phu khac nhau nhu polyme, lop man chong soc, lop phan cuc voi do mong gan mot nua so voi the he truoc, lop kinh linh hoat va mot tam lung da nang co the bien thanh man hinh. Tat ca se duoc ket dinh bang mot loai keo cuc ben, cho phep chiec may nay co the gap lai hang tram ngan lan ma khong bi hu hong.'))
72# print(add_accent('man hinh. Tat ca se duoc ket dinh bang mot loai keo cuc ben, cho phep chiec may nay co the gap lai hang tram ngan lan ma khong bi hu hong.'))
73print(add_accent('Hom nay, bao cao cua Counterpoint Research cho thay, trong nam 2018 Apple da ban duoc khoang 35 trieu cap tai nghe khong day AirPods. Theo hang phan tich nay, AirPods hien la tai nghe khong day pho bien nhat.'))
Mã nguồn file utils
1import re
2import string
3import time
4from contextlib import contextmanager
5import numpy as np
6
7
8
9# maximum string length to train and predict
10# this is set based on our ngram length break down below
11MAXLEN = 32
12
13# minimum string length to consider
14MINLEN = 3
15
16# how many words per ngram to consider in our model
17NGRAM = 5
18
19# inverting the input generally help with accuracy
20INVERT = True
21
22# mini batch size
23BATCH_SIZE = 128
24
25# number of phrases set apart from training set to validate our model
26VALIDATION_SIZE = 100000
27
28# using g2.2xl GPU is ~5x faster than a Macbook Pro Core i5 CPU
29HAS_GPU = True
30
31PAD_WORDS_INPUT = True
32
33### Ánh xạ từ không dấu sang có dấu
34
35ACCENTED_CHARS = {
36 'a': u'a á à ả ã ạ â ấ ầ ẩ ẫ ậ ă ắ ằ ẳ ẵ ặ',
37 'o': u'o ó ò ỏ õ ọ ô ố ồ ổ ỗ ộ ơ ớ ờ ở ỡ ợ',
38 'e': u'e é è ẻ ẽ ẹ ê ế ề ể ễ ệ',
39 'u': u'u ú ù ủ ũ ụ ư ứ ừ ử ữ ự',
40 'i': u'i í ì ỉ ĩ ị',
41 'y': u'y ý ỳ ỷ ỹ ỵ',
42 'd': u'd đ',
43}
44
45### Ánh xạ từ có dấu sang không dấu
46ACCENTED_TO_BASE_CHAR_MAP = {}
47for c, variants in ACCENTED_CHARS.items():
48 for v in variants.split(' '):
49 ACCENTED_TO_BASE_CHAR_MAP[v] = c
50
51# \x00 ký tự padding
52
53### Những ký tự cơ bản, bao gồm ký tự padding, các chữ cái và các chữ số
54BASE_ALPHABET = set('\x00 _' + string.ascii_lowercase + string.digits)
55
56### Bộ ký tự bao gồm những ký tự cơ bản và những ký tự có dấu
57ALPHABET = BASE_ALPHABET.union(set(''.join(ACCENTED_TO_BASE_CHAR_MAP.keys())))
58
59
60def is_words(text):
61 return re.fullmatch('\w[\w ]*', text)
62
63# Hàm bỏ dấu khỏi một câu
64def remove_accent(text):
65 """ remove accent from text """
66 return u''.join(ACCENTED_TO_BASE_CHAR_MAP.get(char, char) for char in text)
67
68#hàm thêm padding vào một câu
69def pad(phrase, maxlen):
70 """ right pad given string with \x00 to exact "maxlen" length """
71 return phrase + u'\x00' * (maxlen - len(phrase))
72
73
74def gen_ngram(words, n=3, pad_words=True):
75 """ gen n-grams from given phrase or list of words """
76 if isinstance(words, str):
77 words = re.split('\s+', words.strip())
78
79 if len(words) < n:
80 if pad_words:
81 words += ['\x00'] * (n - len(words))
82 yield tuple(words)
83 else:
84 for i in range(len(words) - n + 1):
85 yield tuple(words[i: i + n])
86
87def extract_phrases(text):
88 """ extract phrases, i.e. group of continuous words, from text """
89 return re.findall(r'\w[\w ]+', text, re.UNICODE)
90
91
92@contextmanager
93def timing(label):
94 begin = time.monotonic()
95 print(label, end='', flush=True)
96 try:
97 yield
98 finally:
99 duration = time.monotonic() - begin
100 print(': took {:.2f}s'.format(duration))
101
102class CharacterCodec(object):
103 def __init__(self, alphabet, maxlen):
104 self.alphabet = list(sorted(set(alphabet)))
105 self.index_alphabet = dict((c, i) for i, c in enumerate(self.alphabet))
106 self.maxlen = maxlen
107
108 def encode(self, C, maxlen=None):
109 maxlen = maxlen if maxlen else self.maxlen
110 X = np.zeros((maxlen, len(self.alphabet)))
111 for i, c in enumerate(C[:maxlen]):
112 X[i, self.index_alphabet[c]] = 1
113 return X
114
115 def try_encode(self, C, maxlen=None):
116 try:
117 return self.encode(C, maxlen)
118 except KeyError:
119 return None
120
121 def decode(self, X, calc_argmax=True):
122 if calc_argmax:
123 X = X.argmax(axis=-1)
124 return ''.join(self.alphabet[x] for x in X)
link donwnload mô hình ở lần lặp thứ 10 ở https://github.com/AlexBlack2202/alexmodel/blob/master/a_best_weight.h5?raw=true
À, kết quả của câu nói phần mở đầu là “mẹ nói rằng em rất đậm đang”. Hi hi, may quá.
Cảm ơn các bạn đã theo dõi. Hẹn gặp bạn ở các bài viết tiếp theo.
Comments