Giới thiệu về pyltp
pyltp là một thư viện Python cung cấp giao diện lập trình (wrapper) cho LTP (Language Technology Platform) - một hệ thống xử lý ngôn ngữ tự nhiên tiếng Trung mạnh mẽ do Viện Công nghệ Cáp Nhĩ Tân (HIT) phát triển. Thư viện này tích hợp sẵn 5 công cụ cốt lõi bao gồm: tách câu, phân tách từ, gán nhãn từ loại, nhận diện thực thể có tên, phân tích cú pháp phụ thuộc và gán nhãn vai trò ngữ nghĩa.
Hướng dẫn cài đặt
Môi trường thử nghiệm: Windows 10 (64-bit), Python 3.6+.
Việc cài đặt trực tiếp thông qua lệnh pip install pyltp trên hệ điều hành Windows thường xuyên gặp lỗi do thiếu trình biên dịch C++ phù hợp. Giải pháp tối ưu nhất là sử dụng tệp wheel đã được biên dịch sẵn. Bạn có thể tải xuống tệp pyltp-0.2.1-cp36-cp36m-win_amd64.whl từ các kho lưu trữ mã nguồn mở và cài đặt bằng lệnh sau:
pip install pyltp-0.2.1-cp36-cp36m-win_amd64.whl
Chuẩn bị dữ liệu và Lưu ý về mã hóa
Trước khi sử dụng, bạn cần tải xuống bộ mô hình (models) của LTP, ví dụ phiên bản ltp_data_v3.4.0.
Yêu cầu mã hóa: Toàn bộ dữ liệu văn bản đầu vào và kết quả trả về của pyltp bắt buộc phải sử dụng chuẩn UTF-8. Nếu truyền vào văn bản có mã hóa khác, hệ thống có thể trả về kết quả rỗng. Ngoài ra, do Terminal mặc định của Windows sử dụng mã hóa GBK, việc in trực tiếp kết quả tiếng Trung ra màn hình console sẽ gây ra lỗi hiển thị (mojibake). Để khắc phục, hãy lưu kết quả ra file text định dạng UTF-8 hoặc thay đổi code page của console bằng lệnh chcp 65001.
1. Tách câu (Sentence Splitting)
Module SentenceSplitter giúp phân tách một đoạn văn bản dài thành các câu đơn lẻ dựa trên các dấu ngắt câu.
from pyltp import SentenceSplitter
paragraph = "Trí tuệ nhân tạo đang thay đổi thế giới. Chúng ta cần chuẩn bị gì cho tương lai? Hãy bắt đầu học ngay hôm nay!"
sentences = SentenceSplitter.split(paragraph)
# Nối các câu lại bằng dấu gạch ngang để kiểm tra
print(" || ".join(sentences))
Kết quả:
Trí tuệ nhân tạo đang thay đổi thế giới. || Chúng ta cần chuẩn bị gì cho tương lai? || Hãy bắt đầu học ngay hôm nay!
2. Phân tách từ (Word Segmentation)
Phân tách từ là bước chia nhỏ câu thành các từ vựng có nghĩa. Module Segmentor xử lý tác vụ này.
import os
from pyltp import Segmentor
MODEL_DIR = r'D:\nlp_models\ltp_data_v3.4.0'
cws_model_file = os.path.join(MODEL_DIR, 'cws.model')
segmenter = Segmentor()
segmenter.load(cws_model_file)
text = "Học viện Công nghệ Cáp Nhĩ Tân phát triển nền tảng này"
tokens = segmenter.segment(text)
# Chuyển đổi kiểu dữ liệu native VectorOfString sang list của Python
token_list = list(tokens)
print(" / ".join(token_list))
segmenter.release()
Kết quả:
Học viện / Công nghệ / Cáp Nhĩ Tân / phát triển / nền tảng / này
Sử dụng từ điển tùy chỉnh
Để cải thiện độ chính xác cho các thuật ngữ chuyên ngành, bạn có thể nạp thêm từ điển ngoài. Tạo một file text (ví dụ custom_lexicon.txt) mã hóa UTF-8, mỗi dòng chứa một từ:
Cáp Nhĩ Tân
nền tảng
Đoạn mã tích hợp từ điển:
segmenter.load_with_lexicon(cws_model_file, 'custom_lexicon.txt')
custom_tokens = segmenter.segment("Trụ sở chính đặt tại Cáp Nhĩ Tân")
print(" | ".join(custom_tokens))
segmenter.release()
3. Gán nhãn từ loại (POS Tagging)
Sau khi có danh sách từ, module Postagger sẽ phân loại chức năng ngữ pháp của từng từ (danh từ, động từ, tính từ...) dựa trên bộ thẻ từ loại 863.
import os
from pyltp import Postagger
MODEL_DIR = r'D:\nlp_models\ltp_data_v3.4.0'
pos_model_file = os.path.join(MODEL_DIR, 'pos.model')
tagger = Postagger()
tagger.load(pos_model_file)
# Sử dụng kết quả từ bước phân tách từ
input_words = ['Học', 'viện', 'Công', 'nghệ', 'phát', 'triển']
pos_tags = tagger.postag(input_words)
for word, tag in zip(input_words, pos_tags):
print(f"{word}: {tag}")
tagger.release()
4. Nhận diện thực thể có tên (NER)
Module NamedEntityRecognizer trích xuất các thực thể như tên người (Nh), địa danh (Ns), hoặc tổ chức (Ni). Kết quả tuân theo chuẩn gán nhãn BIESO (B-Bắt đầu, I-Giữa, E-Kết thúc, S-Đơn lẻ, O-Không phải thực thể).
import os
from pyltp import NamedEntityRecognizer
MODEL_DIR = r'D:\nlp_models\ltp_data_v3.4.0'
ner_model_file = os.path.join(MODEL_DIR, 'ner.model')
ner_engine = NamedEntityRecognizer()
ner_engine.load(ner_model_file)
input_words = ['Trụ', 'sở', 'đặt', 'tại', 'Bắc', 'Kinh']
input_pos = ['n', 'n', 'v', 'p', 'ns', 'ns']
entities = ner_engine.recognize(input_words, input_pos)
print(list(entities))
ner_engine.release()
Kết quả: ['O', 'O', 'O', 'O', 'B-Ns', 'E-Ns'] (Nhận diện "Bắc Kinh" là địa danh).
5. Phân tích cú pháp phụ thuộc (Dependency Parsing)
Parser xây dựng cấu trúc cây cú pháp của câu, xác định mối quan hệ giữa các từ (ví dụ: chủ ngữ, vị ngữ, tân ngữ).
import os
from pyltp import Parser
MODEL_DIR = r'D:\nlp_models\ltp_data_v3.4.0'
parser_model_file = os.path.join(MODEL_DIR, 'parser.model')
syntax_parser = Parser()
syntax_parser.load(parser_model_file)
input_words = ['Tôi', 'đọc', 'sách']
input_pos = ['r', 'v', 'n']
arcs = syntax_parser.parse(input_words, input_pos)
# arc.head: chỉ số của từ cha (0 là ROOT)
# arc.relation: loại quan hệ cú pháp (SBV: Chủ vị, VOB: Động tân...)
for idx, arc in enumerate(arcs):
print(f"Token: {input_words[idx]} | Head Index: {arc.head} | Relation: {arc.relation}")
syntax_parser.release()
6. Gán nhãn vai trò ngữ nghĩa (Semantic Role Labeling)
Module SementicRoleLabeller xác định "ai làm gì, ở đâu, khi nào". Lưu ý: Trên hệ điều hành Windows, bạn phải sử dụng mô hình có tên pisrl_win.model.
import os
from pyltp import SementicRoleLabeller, Parser
MODEL_DIR = r'D:\nlp_models\ltp_data_v3.4.0'
srl_model_file = os.path.join(MODEL_DIR, 'pisrl_win.model')
parser_model_file = os.path.join(MODEL_DIR, 'parser.model')
srl_engine = SementicRoleLabeller()
srl_engine.load(srl_model_file)
# Khởi tạo parser để lấy cấu trúc cú pháp làm đầu vào cho SRL
syntax_parser = Parser()
syntax_parser.load(parser_model_file)
input_words = ['Công', 'ty', 'phát', 'triển', 'phần', 'mềm']
input_pos = ['n', 'n', 'v', 'v', 'n', 'n']
arcs = syntax_parser.parse(input_words, input_pos)
semantic_roles = srl_engine.label(input_words, input_pos, arcs)
for predicate in semantic_roles:
print(f"Predicate Index: {predicate.index} ({input_words[predicate.index]})")
for arg in predicate.arguments:
print(f" - Role: {arg.name}, Span: {arg.range.start} to {arg.range.end}")
srl_engine.release()
syntax_parser.release()
Quy trình xử lý toàn diện (Full Pipeline)
Để tối ưu hóa việc quản lý tài nguyên và dễ dàng tích hợp vào các dự án lớn, bạn nên đóng gói toàn bộ các module của pyltp vào một Class. Cấu trúc dưới đây minh họa cách khởi tạo, thực thi và giải phóng bộ nhớ một cách khoa học.
import os
from pyltp import SentenceSplitter, Segmentor, Postagger, Parser, NamedEntityRecognizer, SementicRoleLabeller
class LTPAnalyzer:
def __init__(self, model_base_dir):
self.model_dir = model_base_dir
# Khởi tạo các engine
self.segmentor = Segmentor()
self.postagger = Postagger()
self.parser = Parser()
self.ner = NamedEntityRecognizer()
self.srl = SementicRoleLabeller()
# Tải các mô hình tương ứng
self.segmentor.load(os.path.join(self.model_dir, 'cws.model'))
self.postagger.load(os.path.join(self.model_dir, 'pos.model'))
self.parser.load(os.path.join(self.model_dir, 'parser.model'))
self.ner.load(os.path.join(self.model_dir, 'ner.model'))
self.srl.load(os.path.join(self.model_dir, 'pisrl_win.model'))
def process_text(self, raw_text):
sentences = SentenceSplitter.split(raw_text)
for sent in sentences:
tokens = list(self.segmentor.segment(sent))
postags = list(self.postagger.postag(tokens))
arcs = self.parser.parse(tokens, postags)
netags = list(self.ner.recognize(tokens, postags))
roles = self.srl.label(tokens, postags, arcs)
print(f"[*] Câu gốc: {sent}")
print(f" -> Phân tách từ: {' | '.join(tokens)}")
print(f" -> Từ loại: {', '.join([f'{t}/{p}' for t, p in zip(tokens, postags)])}")
print(f" -> Thực thể (NER): {netags}")
for role in roles:
args_str = ", ".join([f"{arg.name}:[{arg.range.start}:{arg.range.end}]" for arg in role.arguments])
print(f" -> Vai trò ngữ nghĩa (Vị ngữ '{tokens[role.index]}'): {args_str}")
print("-" * 50)
def cleanup(self):
# Bắt buộc giải phóng bộ nhớ sau khi hoàn tất
self.segmentor.release()
self.postagger.release()
self.parser.release()
self.ner.release()
self.srl.release()
# Khởi chạy Pipeline
if __name__ == "__main__":
MODEL_PATH = r'D:\nlp_models\ltp_data_v3.4.0'
analyzer = LTPAnalyzer(MODEL_PATH)
sample_text = "Tập đoàn công nghệ này vừa ra mắt sản phẩm mới tại Thượng Hải. Doanh thu dự kiến sẽ tăng trưởng mạnh."
analyzer.process_text(sample_text)
analyzer.cleanup()