Thêm Dấu Tiếng Việt Cho Câu Không Dấu

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