Trong bài chia sẻ này, Lập Trình Không Khó sẽ trình bày một số kiến thức liên quan tới việc xử lý tiếng Việt trong Python phục vụ cho các bài toán liên quan đến dữ liệu tiếng Việt (có dấu), đặc biệt là các bài toán trong lĩnh vực xử lý ngôn ngữ tự nhiên.
Đầu tiên, trước khi đi vào chi tiết các bài toán xử lý tiếng Việt với Python thì mình muốn trình bày sự khác nhau giữa Python 2 (Đã khai tử) và Python 3 đối với dữ liệu tiếng Việt.
[sc_box][sc_icon class=”fa fa-hand-point-right”][/sc_icon]Dành cho bạn: Khóa học lập trình Python cơ bản [/sc_box]
Python 2 và Python 3
Đầu tiên, mình xin nhấn mạnh rằng Python 2 đã bị khai tử (Chi tiết xem tại đây). Do đó, nếu bạn bắt đầu với Python, hãy sử dụng Python phiên bản 3 nhé.
Đối với Python 2
Trong Python 2, encoding mặc định là ASCII. Do đó, khi bạn làm việc với dữ liệu tiếng Việt, bắt buộc bạn phải khai báo encoding UTF-8 cho nó ở đầu file code .py của bạn:
# -*- coding: utf-8 -*-
Và đây là một số điều đặc biệt khi xử lý dữ liệu tiếng Việt với Python 2:
>>> txt = "Hiếu" >>> for c in txt: ... print c ... H i � � � u >>> len(txt) 6
Ký tự ‘ế’ trong chữ ‘Hiếu’ bao gồm 3 ký tự khác nhau, dẫn đến độ dài của txt là 6. Đó là bởi chuỗi tiếng Việt của chúng ta sử dụng Unicode, trong khi string của Python coi nó là ASCII.
Để làm việc với Unicode trong Python 2, bạn cần làm như sau:
>>> txt = u"Hiếu" >>> for c in txt: ... print c ... H i ế u >>> len(txt) 4
Với Python 3 thì sao?
Sang đến Python 3, encoding mặc định của các file .py là UTF-8. Do đó, bạn chỉ phải khai báo `# -*- coding: utf-8 -*-` khi bạn không ở trong sự mặc định đó, hoặc nếu các công cụ khác (như IDE hoặc trình soạn thảo văn bản của bạn) cần sử dụng thông tin đó.
Và việc sử dụng tiếng Việt cũng hết sức đơn giản:
$ python3 Python 3.7.3 (default, Apr 24 2020, 18:51:23) [Clang 11.0.3 (clang-1103.0.32.62)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> txt = "Hiếu" >>> for c in txt: ... print(c) ... H i ế u >>> len(txt) 4
Sau phần này, tất cả các kiến thức và kỹ thuật mình trình bày đều được áp dụng trên Python 3 nhé.
Xử lý tiếng Việt trong Python
Sử dụng regex với tiếng Việt
Yêu cầu: Mục này yêu cầu bạn cần có hiểu biết cơ bản về regex, bạn có thể đọc bài viết regex là gì trước khi tiếp tục nếu chưa rõ nhé.
Để sử dụng regex trong Python 3 với tiếng Việt, bạn có một số lưu ý như sau:
1. Để so khớp một từ tiếng Việt bất kỳ, bạn không thể sử dụng b[a-zA-Z]+b
được. Bởi vì tiếng Việt của chúng ta có những ký tự có thanh sắc. Đây là danh sách đầy đủ ở dạng viết thường cho bạn:
áàảãạăắằẳẵặâấầẩẫậéèẻẽẹêếềểễệóòỏõọôốồổỗộơớờởỡợíìỉĩịúùủũụưứừửữựýỳỷỹỵđ
Do đó, nếu bạn muốn so khớp một từ sử dụng thư viện re
, hãy sử dụng regex này:
b[a-záàảãạăắằẳẵặâấầẩẫậéèẻẽẹêếềểễệóòỏõọôốồổỗộơớờởỡợíìỉĩịúùủũụưứừửữựýỳỷỹỵđ]+b
Trong trường hợp bạn cần so khớp không phân biệt hoa thường thì hãy dùng thêm flag IGNORE_CASE nhé:
>>> import re >>> txt = "Lập trình không khó" # (?i) là flag ignore case >>> re.findall(r'(?i)b[a-záàảãạăắằẳẵặâấầẩẫậéèẻẽẹêếềểễệóòỏõọôốồổỗộơớờởỡợíìỉĩịúùủũụưứừửữựýỳỷỹỵđ]+b', txt) ['Lập', 'trình', 'không', 'khó']
2. Có một cách khác đơn giản hơn, là cài bổ sung thư viện regex
thay vì dùng thư viện re
có sẵn. Nó cung cấp cho chúng ta khả năng làm việc với tiếng Việt tốt hơn.
$ pip install regex
Sau đó bạn có thể sử dụng nó để so khớp chuỗi tiếng Việt đơn giản hơn.
>>> import regex >>> txt = "Lập trình không khó" >>> regex.findall(r'(?i)bp{L}+b', txt) ['Lập', 'trình', 'không', 'khó']
Ở đây, p{L}
sẽ khớp với một chữ cái bất kỳ trong bảng chữ cái của bất kỳ ngôn ngữ nào.
Chuẩn hóa bảng mã tiếng Việt
Đã bao giờ bạn gặp lỗi hai từ trông giống hệt nhau nhưng lại không giống nhau chưa? Hãy thử xem ví dụ này (Bạn có thể copy và chạy thử).
>>> 'hiếu' == 'hiếu' True >>> 'hiếu' == 'hiếu' False
Nguyên nhân có lẽ là do người dùng sử dụng bộ mã khác nhau khi gõ tiếng Việt. Cụ thể đa số chúng ta đang dùng Unikey với bộ mã Unicode (dựng sẵn). Nhưng ở đâu đó, bộ mã Unicode tổ hợp vẫn được sử dụng. Do đó, đối với các nguồn dữ liệu thu thập trên internet thường bị lẫn cả 2 cách gõ này.
Cách khắc phục: Thay thế cách gõ Unicode tổ hợp bằng cách gõ của Unicode dựng sẵn.
Dưới đây là script dùng để thực hiện công việc thay thế kể trên. Ngoài ra, bạn cũng có thể dùng chức năng convert có sẵn trong Unikey trên Windows của anh Phạm Kim Long.
import regex as re uniChars = "àáảãạâầấẩẫậăằắẳẵặèéẻẽẹêềếểễệđìíỉĩịòóỏõọôồốổỗộơờớởỡợùúủũụưừứửữựỳýỷỹỵÀÁẢÃẠÂẦẤẨẪẬĂẰẮẲẴẶÈÉẺẼẸÊỀẾỂỄỆĐÌÍỈĨỊÒÓỎÕỌÔỒỐỔỖỘƠỜỚỞỠỢÙÚỦŨỤƯỪỨỬỮỰỲÝỶỸỴÂĂĐÔƠƯ" unsignChars = "aaaaaaaaaaaaaaaaaeeeeeeeeeeediiiiiooooooooooooooooouuuuuuuuuuuyyyyyAAAAAAAAAAAAAAAAAEEEEEEEEEEEDIIIOOOOOOOOOOOOOOOOOOOUUUUUUUUUUUYYYYYAADOOU" def loaddicchar(): dic = {} char1252 = 'à|á|ả|ã|ạ|ầ|ấ|ẩ|ẫ|ậ|ằ|ắ|ẳ|ẵ|ặ|è|é|ẻ|ẽ|ẹ|ề|ế|ể|ễ|ệ|ì|í|ỉ|ĩ|ị|ò|ó|ỏ|õ|ọ|ồ|ố|ổ|ỗ|ộ|ờ|ớ|ở|ỡ|ợ|ù|ú|ủ|ũ|ụ|ừ|ứ|ử|ữ|ự|ỳ|ý|ỷ|ỹ|ỵ|À|Á|Ả|Ã|Ạ|Ầ|Ấ|Ẩ|Ẫ|Ậ|Ằ|Ắ|Ẳ|Ẵ|Ặ|È|É|Ẻ|Ẽ|Ẹ|Ề|Ế|Ể|Ễ|Ệ|Ì|Í|Ỉ|Ĩ|Ị|Ò|Ó|Ỏ|Õ|Ọ|Ồ|Ố|Ổ|Ỗ|Ộ|Ờ|Ớ|Ở|Ỡ|Ợ|Ù|Ú|Ủ|Ũ|Ụ|Ừ|Ứ|Ử|Ữ|Ự|Ỳ|Ý|Ỷ|Ỹ|Ỵ'.split( '|') charutf8 = "à|á|ả|ã|ạ|ầ|ấ|ẩ|ẫ|ậ|ằ|ắ|ẳ|ẵ|ặ|è|é|ẻ|ẽ|ẹ|ề|ế|ể|ễ|ệ|ì|í|ỉ|ĩ|ị|ò|ó|ỏ|õ|ọ|ồ|ố|ổ|ỗ|ộ|ờ|ớ|ở|ỡ|ợ|ù|ú|ủ|ũ|ụ|ừ|ứ|ử|ữ|ự|ỳ|ý|ỷ|ỹ|ỵ|À|Á|Ả|Ã|Ạ|Ầ|Ấ|Ẩ|Ẫ|Ậ|Ằ|Ắ|Ẳ|Ẵ|Ặ|È|É|Ẻ|Ẽ|Ẹ|Ề|Ế|Ể|Ễ|Ệ|Ì|Í|Ỉ|Ĩ|Ị|Ò|Ó|Ỏ|Õ|Ọ|Ồ|Ố|Ổ|Ỗ|Ộ|Ờ|Ớ|Ở|Ỡ|Ợ|Ù|Ú|Ủ|Ũ|Ụ|Ừ|Ứ|Ử|Ữ|Ự|Ỳ|Ý|Ỷ|Ỹ|Ỵ".split( '|') for i in range(len(char1252)): dic[char1252[i]] = charutf8[i] return dic dicchar = loaddicchar() def covert_unicode(txt): return re.sub( r'à|á|ả|ã|ạ|ầ|ấ|ẩ|ẫ|ậ|ằ|ắ|ẳ|ẵ|ặ|è|é|ẻ|ẽ|ẹ|ề|ế|ể|ễ|ệ|ì|í|ỉ|ĩ|ị|ò|ó|ỏ|õ|ọ|ồ|ố|ổ|ỗ|ộ|ờ|ớ|ở|ỡ|ợ|ù|ú|ủ|ũ|ụ|ừ|ứ|ử|ữ|ự|ỳ|ý|ỷ|ỹ|ỵ|À|Á|Ả|Ã|Ạ|Ầ|Ấ|Ẩ|Ẫ|Ậ|Ằ|Ắ|Ẳ|Ẵ|Ặ|È|É|Ẻ|Ẽ|Ẹ|Ề|Ế|Ể|Ễ|Ệ|Ì|Í|Ỉ|Ĩ|Ị|Ò|Ó|Ỏ|Õ|Ọ|Ồ|Ố|Ổ|Ỗ|Ộ|Ờ|Ớ|Ở|Ỡ|Ợ|Ù|Ú|Ủ|Ũ|Ụ|Ừ|Ứ|Ử|Ữ|Ự|Ỳ|Ý|Ỷ|Ỹ|Ỵ', lambda x: dicchar[x.group()], txt)
Chuẩn hóa kiểu gõ dấu tiếng Việt
Hiện nay, tiếng Việt có 2 kiểu gõ dấu khác nhau. Chúng lần lượt có tên là kiểu mới (ex: oà, uý) và kiểu cũ (ex: òa, úy). Chi tiết sự khác nhau bạn có thể xem tại đây.
Do đó, các bộ gõ tiếng Việt phổ biến hiện nay cũng cho phép tùy chỉnh chọn một trong hai cách gõ dấu kể trên. Dẫn đến các từ giống nhau nhưng lại có cách viết khác nhau khi chúng ta sử dụng dữ liệu văn bản vào các bài toán học máy. Do đó, việc cần làm là đưa chúng về một bộ gõ tiêu chuẩn.
Theo kinh nghiệm bản thân cũng như các văn bản, tài liệu mình tiếp xúc hàng ngày thì kiểu gõ cũ vẫn chiếm ưu thế tuyệt đối. Và mặc định các bộ gõ tiếng Việt cũng dùng kiểu cũ luôn.
Cách khắc phục: Đưa về kiểu gõ dấu cũ đối với các từ dùng kiểu gõ mới.
Script python dưới đây cung cấp cho bạn khả năng chuẩn hóa kiểu gõ về kiểu cũ, đồng thời không làm thay đổi cấu trúc dữ liệu gốc (giữ nguyên hoa thường, dấu ngắt câu,…).
def chuan_hoa_dau_tu_tieng_viet(word): if not is_valid_vietnam_word(word): return word chars = list(word) dau_cau = 0 nguyen_am_index = [] qu_or_gi = False for index, char in enumerate(chars): x, y = nguyen_am_to_ids.get(char, (-1, -1)) if x == -1: continue elif x == 9: # check qu if index != 0 and chars[index - 1] == 'q': chars[index] = 'u' qu_or_gi = True elif x == 5: # check gi if index != 0 and chars[index - 1] == 'g': chars[index] = 'i' qu_or_gi = True if y != 0: dau_cau = y chars[index] = bang_nguyen_am[x][0] if not qu_or_gi or index != 1: nguyen_am_index.append(index) if len(nguyen_am_index) < 2: if qu_or_gi: if len(chars) == 2: x, y = nguyen_am_to_ids.get(chars[1]) chars[1] = bang_nguyen_am[x][dau_cau] else: x, y = nguyen_am_to_ids.get(chars[2], (-1, -1)) if x != -1: chars[2] = bang_nguyen_am[x][dau_cau] else: chars[1] = bang_nguyen_am[5][dau_cau] if chars[1] == 'i' else bang_nguyen_am[9][dau_cau] return ''.join(chars) return word for index in nguyen_am_index: x, y = nguyen_am_to_ids[chars[index]] if x == 4 or x == 8: # ê, ơ chars[index] = bang_nguyen_am[x][dau_cau] # for index2 in nguyen_am_index: # if index2 != index: # x, y = nguyen_am_to_ids[chars[index]] # chars[index2] = bang_nguyen_am[x][0] return ''.join(chars) if len(nguyen_am_index) == 2: if nguyen_am_index[-1] == len(chars) - 1: x, y = nguyen_am_to_ids[chars[nguyen_am_index[0]]] chars[nguyen_am_index[0]] = bang_nguyen_am[x][dau_cau] # x, y = nguyen_am_to_ids[chars[nguyen_am_index[1]]] # chars[nguyen_am_index[1]] = bang_nguyen_am[x][0] else: # x, y = nguyen_am_to_ids[chars[nguyen_am_index[0]]] # chars[nguyen_am_index[0]] = bang_nguyen_am[x][0] x, y = nguyen_am_to_ids[chars[nguyen_am_index[1]]] chars[nguyen_am_index[1]] = bang_nguyen_am[x][dau_cau] else: # x, y = nguyen_am_to_ids[chars[nguyen_am_index[0]]] # chars[nguyen_am_index[0]] = bang_nguyen_am[x][0] x, y = nguyen_am_to_ids[chars[nguyen_am_index[1]]] chars[nguyen_am_index[1]] = bang_nguyen_am[x][dau_cau] # x, y = nguyen_am_to_ids[chars[nguyen_am_index[2]]] # chars[nguyen_am_index[2]] = bang_nguyen_am[x][0] return ''.join(chars) def is_valid_vietnam_word(word): chars = list(word) nguyen_am_index = -1 for index, char in enumerate(chars): x, y = nguyen_am_to_ids.get(char, (-1, -1)) if x != -1: if nguyen_am_index == -1: nguyen_am_index = index else: if index - nguyen_am_index != 1: return False nguyen_am_index = index return True def chuan_hoa_dau_cau_tieng_viet(sentence): """ Chuyển câu tiếng việt về chuẩn gõ dấu kiểu cũ. :param sentence: :return: """ sentence = sentence.lower() words = sentence.split() for index, word in enumerate(words): cw = re.sub(r'(^p{P}*)([p{L}.]*p{L}+)(p{P}*$)', r'1/2/3', word).split('/') # print(cw) if len(cw) == 3: cw[1] = chuan_hoa_dau_tu_tieng_viet(cw[1]) words[index] = ''.join(cw) return ' '.join(words) """ End section: Chuyển câu văn về cách gõ dấu kiểu cũ: dùng òa úy thay oà uý Xem tại đây: https://vi.wikipedia.org/wiki/Quy_tắc_đặt_dấu_thanh_trong_chữ_quốc_ngữ """ if __name__ == '__main__': print(chuan_hoa_dau_cau_tieng_viet('anh hoà, đang làm.. gì'))
Ngoài ra, script chuẩn hóa (bảng mã, cách gõ dấu) còn có phiên bản ngôn ngữ Java, bạn có thể xem đầy đủ cả 2 phiên bản Java và Python tại đây.
Trên đây là một số kỹ thuật xử lý tiếng Việt trong Python do bản thân mình ghi chép lại. Nếu bạn có những kỹ thuật khác thì đừng ngại chia sẻ dưới mục thảo luận của bài viết nhé.
Tài nguyên xử lý tiếng Việt
Ngoài các kỹ thuật mình giới thiệu ở trên, mục này mình sẽ để lại một số tài nguyên hữu ích cho bài toán xử lý tiếng Việt.
- Thư viện chuẩn hóa văn bản Tiếng Việt (Có sẵn wrapper cho Python) của anh langman (ai chơi Cộng đồng C Việt chắc khá quen nickname này). Thư viện này giúp bạn xử lý một số vấn đề thiếu nhất quán trong tiếng Việt, và một số vấn đề khác như sửa lỗi chính tả. Xem tại: https://github.com/langmaninternet/VietnameseTextNormalizer
- Danh sách 400 từ teencode tiếng Việt phổ biến nhất, có thể dùng làm từ điển để chuẩn hóa text mạng xã hội. Xem tại: https://gist.github.com/nguyenvanhieuvn/7d9441c10b3c2739499fc5a4d9ea06fb
- Updating…
Đọc thêm:
Để lại một bình luận