Tiến tới những chuẩn mở trong việc xử lý giọng nói

Chuyển đổi từ ngữ thành XML với Python

Nhiều dự án nguồn mở đã bắt đầu trước khi có sự ra đời của các tiêu chuẩn phần mềm nguồn mở và miễn phí (FOSS), do đó, các tệp tài nguyên và cấu hình của chúng đều là các tệp flat-file đơn giản. Bằng cách chuyển đổi các tệp này sang tiêu chuẩn nguồn mở thích hợp, bạn có khả năng gia tăng tính tương thích, tính linh hoạt và độ tin cậy giữa các dự án. Từ ngữ trong việc nhận dạng giọng nói là một ví dụ thú vị. Trong bài này, hãy tìm hiểu cách sử dụng Python để chuyển đổi các từ ngữ hiện có sang định dạng XML được định nghĩa trong PLS (Pronunciation Lexicon Specification - Đặc tả từ ngữ phát âm) và cách chuyển đổi một tệp PLS mới trở lại một tệp flat-file. Hãy khám phá cách sử dụng định dạng XML để thêm thông tin bổ sung và sự chặt chẽ trong việc duy trì các từ vựng. Các vấn đề như Unicode và việc hợp nhất từ vựng mới với các tệp XML khác trong khi vẫn đang sử dụng dữ liệu trong việc tạo mô hình âm thanh, cũng được giải quyết.

Colin Beckingham, Viết các bài báo và là nhà nghiên cứu, Freelance

Colin Beckingham là một nhà nghiên cứu tự do, là tác giả của các bài viết và là một lập trình viên. Ông sống ở Đông Ontario, Canađa. Có bằng cử nhân của trường Đại học Queen, Kingston và Đại học Windsor, ông đã làm việc trong rất nhiều lĩnh vực bao gồm ngân hàng, làm vườn, đua ngựa, giảng dạy, dịch vụ dân sự, bán lẻ và du hành và du lịch. Tác giả của các ứng dụng cơ sở dữ liệu và các báo, tạp chí và rất nhiều bài báo trực tuyến, các mối quan tâm nghiên cứu của ông gồm lập trình nguồn mở, VoIP và các ứng dụng điều khiển bằng giọng nói trên Linux. Bạn có thể liên hệ với Colin tại colbec@start.ca.



04 02 2013

Giới thiệu

Nhiều dự án phần mềm có từ lâu đã sử dụng các tệp tài nguyên và cấu hình flat-file (thuật ngữ thường dùng để chỉ các ứng dụng lưu trữ dữ liệu lên các tập tin dạng văn bản) trong nhiều năm mà không gặp các vấn đề lớn. Khi các dự án phát triển và trở nên phức tạp hơn, nhu cầu tăng thêm về sự chặt chẽ và tính tương thích càng lớn hơn. Với XML và ứng dụng của XML khi sử dụng các tiêu chuẩn cụ thể, có khả năng là bạn có thể được hưởng lợi từ: tính tương thích, tính chắc chắn và tính mở rộng giữa dự án và giữa nền tảng trong các lĩnh vực chẳng hạn như Unicode.

Các từ viết tắt thông dụng

  • HTK: Hidden Markov Model Toolkit (Bộ công cụ mô hình Markov ẩn)
  • PLS: Pronunciation Lexicon Specification (Đặc tả từ vựng phát âm)
  • XML: eXtensilble Markup Language (Ngôn ngữ đánh dấu mở rộng)

Bằng việc chuyển đổi các tệp flat-file sang tiêu chuẩn nguồn mở thích hợp, bạn cũng có thể làm tăng tính linh hoạt và độ tin cậy. Từ ngữ trong việc nhận dạng giọng nói là một ví dụ hay được sử dụng trong bài này. Dù các dự án mã nguồn mở của bạn có di chuyển sang XML với các tệp tài nguyên hay không, bạn có thể sử dụng các tiêu chuẩn XML trong công việc của mình mà không bị mất đi các đặc tính.

Trong bài này, hãy tìm hiểu cách dễ dàng di chuyển giữa các định dạng phẳng và PLS. Các ví dụ cho thấy cách lưu trữ các từ ngữ tùy chỉnh theo định dạng PLS và trích xuất dữ liệu vào flat-file cần thiết.


Ví dụ: Từ ngữ

Các từ ngữ là các danh sách của các từ mà bạn sử dụng trong các công cụ nhận dạng giọng nói. Chúng chứa thông tin về cách từ đó phải được in ra hoặc được biểu diễn bằng đồ họa như thế nào và nó phát ra như thế nào khi sử dụng các âm vị. Từ vựng thường dùng với HTK được sử dụng rộng rãi trong các dự án điều khiển bằng giọng nói (xem phần Tài nguyên). Liệt kê 1 là một đoạn trích của một từ vựng HTK của VoxForge.

Liệt kê 1. Đoạn trích của một từ vựng HTK của VoxForge
AGENCY  [AGENCY]        ey jh ih n s iy
AGENDA  [AGENDA]        ax jh eh n d ax
AGENT   [AGENT] ey jh ih n t
AGENTS  [AGENTS]        ey jh ih n t s
AGER    [AGER]  ey g er
AGES    [AGES]  ey jh ih z

Thêm một khoảng tab nếu bạn muốn sao chép và dán mã trong bài

Điều quan trọng là bạn lấy các từ vựng trực tiếp từ nguồn. Bài này sẽ hiển thị bằng HTML, thay thế các sự phân cách bằng khoảng trống. Nếu bạn sao chép và dán từ bài này, bạn cần phải thay thế nhiều khoảng trống ở giữa bằng một dấu phân cách thẻ đơn (\t) nếu không thì đoạn script sẽ không chạy.

Tệp trong Liệt kê 1 có ba trường được phân cách bằng tab:

  • Nhãn mô tả chung về từ đó.
  • Các dấu ngoặc vuông bao quanh từ khi bạn muốn nó được in hoặc được hiển thị trên màn hình (grapheme - chữ cái đặc trưng cho một âm vị).
  • Một chuỗi các âm vị có phân cách bằng một khoảng trống từ bộ Arpabet (xem phần Tài nguyên) mô tả từ này phát âm như thế nào.

Trong ví dụ trên, các phát âm từ tiếng Anh, rõ ràng được trình bày bởi các ký tự ASCII (Mã tiêu chuẩn Hoa kỳ dùng để trao đổi thông tin)..

Dự án Sphinx của CMU (xem phần Tài nguyên) lưu từ vựng (hoặc từ điển trong bối cảnh Sphinx của CMU) theo cách tương tự. Liệt kê 2 trình bày một đoạn trích dẫn.

Liệt kê 2. Đoạn trích từ vựng Sphinx của CMU
agency  EY JH AH N S IY
agenda  AH JH EH N D AH
agendas AH JH EH N D AH Z
agent   EY JH AH N T
agents  EY JH AH N T S
ager    EY JH ER

Trong Liệt kê 2 chỉ có hai trường: word/grapheme và biểu diễn âm vị của nó. Hai ví dụ trên có một số sự khác biệt khó nhận thấy:

  • Các từ và các âm vị trong các trường hợp khác nhau.
  • Các âm vị có một số khác biệt không đáng kể.
  • Dấu chấm câu (dấu phẩy, dấu chấm than và v.v) được xử lý hơi khác nhau một chút.

Bạn có thể xem toàn bộ từ điển trong tệp cmu07a.dic trong phần tải về hiện tại của PocketSphinx (xem phần Tài nguyên).

Do từ vựng mô tả các cách phát âm cụ thể của các từ, nên bạn có thể cần chỉnh sửa tệp đó cho phù hợp với mọi người hoặc các phương ngữ cụ thể. Theo thời gian, bạn xây dựng vốn kiến thức trong từ vựng tùy chỉnh riêng của mình. Bạn có thể dễ dàng chỉnh sửa flat-file bằng một trình soạn thảo văn bản, nhưng bạn nên cẩn thận vì điều này cũng dễ gây ra lỗi, chẳng hạn như: việc sử dụng một dấu phân cách khác hơn so với tiêu chuẩn dùng cho tệp, việc chèn các ký tự không phải là ASCII, đặt các trường theo thứ tự sai, việc phân loại các bản ghi không chính xác, thiếu các dấu ngoặc vuông cần thiết và v.v.

Có một bất lợi khó thấy khác của các flat-file là khi bạn xây dựng tệp tùy chỉnh của mình, bạn vẫn không tương thích với các dự án nhận dạng văn bản khác. Một từ vựng theo một định dạng XML tiêu chuẩn như PLS, nếu được cả hai dự án công nhận, thì ngay lập tức đều tương thích trong cả hai.


Đặc tả từ vựng phát âm

PLSA có một định dạng cơ bản, đơn giản như trong Liệt kê 3.

Liệt kê 3. Định dạng PLS cơ bản
<?xml version="1.0" encoding="UTF-8"?>
<lexicon version="1.0" 
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon 
        http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"
      alphabet="ipa" xml:lang="en-US">
  <lexeme ...>
    <grapheme>...</grapheme>
    <phoneme ...>...</phoneme>
  </lexeme>
</lexicon>

XML mô tả phần tử gốc lexicon (từ vựng) có thể chứa nhiều phần tử con lexeme (từ vị). Mỗi phần tử con lexeme có thể chứa nhiều phần tử grapheme và nhiều phần tử phoneme (âm vị). Đặc tả này cho phép bạn ghi đè lên thuộc tính alphabet (bảng chữ cái) nhưng không cho phép bạn ghi đè lên thuộc tính ngôn ngữ xml:lang. Để lưu các từ vị cho các ngôn ngữ khác nhau, chắc chắn bạn cần tách riêng các tệp từ vựng PLS. Bảng chữ cái mặc định trong từ vựng này là ipa, nói đến hệ thống IPA (International Phonetic Alphabet – Bảng chữ cái phiên âm quốc tế) để biểu diễn các âm thanh (xem phần Tài nguyên). Biểu diễn IPA về các âm vị là các ký tự Unicode nhiều byte. Cả hai HTK và Sphinx đều sử dụng các mã ASCII thuần. Nhận xét quan trọng này được giải quyết ở phần sau trong bài này.

Lợi thế của việc sử dụng đặc tả PLS là ở chỗ nó thêm cấu trúc chặt chẽ hơn và cho phép bạn lưu trữ thêm thông tin, chẳng hạn như thành phần của tiếng nói và các bảng chữ cái cụ thể. Chi tiết về thành phần của tiếng nói là quan trọng trong tiếng Anh vì một số từ có thể được viết theo chính tả giống nhau (các từ cùng chữ) lại được phát âm khác nhau tùy thuộc vào vai trò ngữ pháp của chúng. Ví dụ, đối với từ perfect, nếu nó là một tính từ thì sẽ phát âm khác với khi nó là một động từ vì trọng âm được đặt ở một vị trí khác nhau. Thông tin bổ sung được lưu trữ trong các thuộc tính cho phép bạn trích ra các bản ghi cụ thể từ toàn bộ tệp, tùy theo yêu cầu. Khi sử dụng phương pháp này, bạn có thể tìm kiếm một bảng chữ cái cụ thể trong số nhiều phần tử phoneme.

Hãy xem từ vựng PLS như là một cơ sở dữ liệu về thông tin từ vựng mà từ đó bạn có thể trích ra thông tin chi tiết liên quan đến công cụ giọng nói mà bạn sử dụng. Liệt kê 4 là một ví dụ về định dạng PLS.

Liệt kê 4. Một từ theo định dạng PLS
<?xml version="1.0" encoding="UTF-8"?>
<lexicon version="1.0" 
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon 
        http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"
      alphabet="ipa" xml:lang="en">
  <lexeme role="noun">
    <grapheme>agency</grapheme>
    <phoneme alphabet="x-htk-voxforge">ey jh ih n s iy</phoneme>
    <phoneme alphabet="x-cmusphinx">EY JH AH N S IY</phoneme>
  </lexeme>
</lexicon>

Ví dụ trong Liệt kê 4 chỉ lưu trữ một từ có hai biểu diễn âm vị có thể. Bạn có thể lọc ra một trong số các chuỗi phoneme bằng cách sử dụng thuộc tính alphabet. Phần tử lexeme cho thấy thuộc tính role của noun (danh từ). Trong khi việc này cung cấp nhiều thông tin, thì trong trường hợp này nó lại quá dư thừa vì từ đó chỉ được sử dụng như một danh từ, không có các kịch bản phát âm phức tạp nào.

Bằng cách các biểu diễn phoneme từ hai nguồn khác nhau ở cạnh nhau, bạn có thể thấy rõ sự khác biệt tinh tế. Thông tin này có thể có ích trong việc giải quyết các vấn đề nhận dạng tiếng nói.

Không phải Sphinx của CMU hay HTK có thể sử dụng trực tiếp một từ vựng PLS, mà phần đầu của simon (xem phần Tài nguyên) với bộ công cụ HTK mới có thể làm được điều đó. Nếu bạn đang sử dụng trực tiếp HTK hoặc Sphinx, bạn cần chắc chắn rằng có thể dễ dàng chuyển từ văn bản thuần sang PLS và ngược lại mà không mất thông tin.

Các phần sau đây cho thấy cách sử dụng Python để chuyển từ một flat-file sang PLS và ngược lại. Giả sử bạn đã tùy chỉnh thông tin trong một từ vựng flat-file.


Chuyển đổi sang PLS

Mã trong Liệt kê 5 sử dụng Python, nhưng bạn có thể thực hiện điều tương tự theo nhiều cách khác. (hãy xem hướng dẫn trên developerWorks về XSLT (Extensible Stylesheet Language Transformations - Các chuyển đổi ngôn ngữ bản định kiểu mở rộng) trong phần Tài nguyên). Một số người muốn sử dụng các thư viện để kiểm tra tính chắc chắn của XML ở mỗi bước nhỏ để biết thêm thông tin phản hồi trực tiếp chỉ ra nơi có vấn đề, đặc biệt là đối với các tệp nguồn lớn và có thể chứa các lỗi và các xung đột. Ví dụ dưới đây để lại việc kiểm tra vào bước cuối cùng, hàm ý một quy ước là các flat-file đều đúng định dạng.

Liệt kê 5. Chuyển đổi sang PLS
from elementtree.ElementTree import parse
import string as str
import sys
import cgi
#
# call with 
#	python flat2pls.py vox
# or 
#	python flat2pls.py spx
#
if len(sys.argv) == 2:
  src = sys.argv[1]
else:
  exit("wrong args")
#
outfile = "mylex"+src+".pls"
print "out is "+outfile
out = open(outfile,"w")
out.write('<?xml version="1.0" encoding="UTF-8"?>\n\
<lexicon version="1.0"\n \
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"\n\
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n \
      xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon\n \
        http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"\n\
      alphabet="ipa" xml:lang="en">')
# now the lexemes
if src == "vox":
  f = open("vf.lex","r")
  for line in f:
    line = str.strip(line)
    word = str.split(line,"\t")
    #gr = str.strip(word[1],"[]")
    gr = cgi.escape(word[0])
    out.write('\n\
  <lexeme>\n\
    <grapheme>'+gr+'</grapheme>\n\
    <phoneme alphabet="x-htk-voxforge">'+word[2]+'</phoneme>\n\
  </lexeme>')
else: # src is sphinx
  f = open("cmu.dic","r")
  for line in f:
    line = str.strip(line)
    word = str.split(line,"\t")
    gr = cgi.escape(word[0])
    out.write('\n\
  <lexeme>\n\
    <grapheme>'+gr+'</grapheme>\n\
    <phoneme alphabet="x-cmusphinx">'+word[1]+'</phoneme>\n\
  </lexeme>')
# ended lexemes
out.write('\n</lexicon>\n')
out.close()
# now check the output is ok
tree = parse(outfile)
lexicon = tree.getroot()
mylexcount = 0
for lexeme in lexicon:
  mylexcount += 1
print 'Found %(number)d lexemes' % {"number":mylexcount}

Liệt kê 5 bắt đầu bằng cách nhập khẩu các mô đun từ thư viện phân tích cú pháp XML elementtree (xem phần Tài nguyên) và một số thư viện hỗ trợ. Việc nhập khẩu ElementTree trên các bản phân phối khác nhau có thể liên quan đến cú pháp hơi khác nhau một chút, tùy thuộc vào cách bạn cài đặt mô đun. Đoạn mã ví dụ đến từ openSUSE với mô đun cài đặt từ nguồn, nhưng Ubuntu cũng có thể yêu cầu from xml.etree.ElementTree import parse. Mô đun str cho phép một số thao tác chuỗi, sys cho bạn quyền truy cập vào các tệp và cgi cung cấp một thường trình đặt dấu gạch chéo ngược rất cần thiết trong việc xử lý dữ liệu cho XML. Mã này hy vọng sẽ nhận được một đối số giao diện dòng lệnh (CLI) để nói cho nó biết liệu có chuyển dịch từ định dạng Sphinx của CMU hoặc HTK/VoxForge không. Sau đó, đoạn mã ví dụ mở tệp để xuất ra và viết đoạn mở đầu XML thích hợp cho PLS. Vì bạn không lưu trữ bất kỳ các ký tự Unicode nào ở giai đoạn này, nên việc mở tệp chỉ để truy cập ASCII thuần là đủ.

Lúc này, mã trong Liệt kê 5:

  • Xử lý từng dòng của tệp nguồn, chia tách các trường thành các chuỗi riêng biệt và viết các thành phần lexeme, graphemephoneme.
  • Xác định phoneme bằng thuộc tính alphabet="x-htk-voxforge" nếu dữ liệu gửi đến từ từ vựng VoxForge và bằng alphabet="x-cmusphinx" nếu dữ liệu là từ Sphinx.
  • Giữ nguyên trường hợp của các âm vị.

Khi trường đầu tiên được nhập khẩu, nó có thể chứa các ký tự, chẳng hạn như dấu "và" (&), gây ra các vấn đề trong XML, trừ khi được đặt dấu gạch chéo ngược với cgi.escape().

Cuối cùng, đoạn mã:

  • Viết các thẻ đóng.
  • Đóng tệp PLS và sau đó nạp lại nó như là một tệp XML.
  • Đọc hết tệp đếm các phần tử lexeme.
  • Báo cáo tổng số đếm các từ vị.

Nếu tổng số đếm được báo cáo, thì XML xuất hiện là chắc chắn và có định dạng đúng.

Liệt kê 6 là một đoạn trích kết quả của từ vựng HTK của VoxForge.

Liệt kê 6. Đoạn trích kết quả của từ vựng HTK của VoxForge
...
<lexeme>
  <grapheme>AGENDA</grapheme>
  <phoneme alphabet="x-htk-voxforge">ax jh eh n d ax</phoneme>
</lexeme>
<lexeme> 
  <grapheme>AGENT</grapheme>
  <phoneme alphabet="x-htk-voxforge">ey jh ih n t</phoneme>
</lexeme>
...

Chuyển đổi từ PLS

Điều quan trọng là bạn có thể dễ dàng chuyển ngược lại từ định dạng PLS sang một flat-file. Mã trong Liệt kê 7 giả định rằng bạn có từ vựng của mình được lưu trong một tệp có định dạng PLS và dự án nhận dạng tiếng nói của bạn chỉ có thể sử dụng một flat-file hoặc theo định dạng HTK hoặc theo định dạng Sphinx của CMU.

Liệt kê 7. Chuyển đổi từ PLS
from elementtree.ElementTree import parse
import string as str
import sys
#
# call with 
#	python pls2flat.py x-htk-voxforge > mylexicon
# or 
#	python pls2flat.py x-cmusphinx > mylexicon.dic
#
if len(sys.argv) > 1:
  alpha = sys.argv[1]
#
if alpha == "x-htk-voxforge":
  tree = parse("mylexvox.pls")
else:
  tree = parse("mylexspx.pls")
lexicon = tree.getroot()
for lexeme in lexicon:
  for child in lexeme:
    #print child.tag
    if child.tag[-8:] == 'grapheme':
      if alpha == 'x-htk-voxforge':
	gr = str.upper(child.text)
	print gr,"\t","["+gr+"]","\t",
      else:
	gr = child.text
	print gr,"\t",
    if child.tag[-7:] == 'phoneme':
      if child.get('alphabet') == alpha:
	print child.text

Kịch bản lệnh ngắn này sử dụng thư viện elementtree để phân tích cú pháp tệp XML của PLS. Nó thiết lập phần tử gốc và sau đó lặp lại qua các phần tử con lexeme, tìm kiếm graphemephoneme và ghi các giá trị vào một tệp văn bản theo định dạng thích hợp. Kịch bản lệnh yêu cầu 8 ký tự cuối cùng trong thẻ dùng cho grapheme do sẽ có một tiền tố vùng tên được trả về với thẻ. Nó tạo lại định dạng ba trường cho HTK và hai trường cho Sphinx của CMU.


Việc hợp nhất và xử lý Unicode

Kịch bản lệnh trong Liệt kê 8 sử dụng hai tệp PLS để tạo ra một tệp PLS chung có chứa thông tin cho cả hai tệp ban đầu. Nó cũng chuyển dịch chuỗi phoneme của VoxForge sang Unicode và lưu trữ phiên bản Unicode trong một phần tử phoneme riêng biệt được xác định bằng thuộc tính alphabet="ipa".

Liệt kê 8. Việc hợp nhất và Unicode
#! /usr/bin/python -u
# -*- coding: utf-8 -*-
#
# challenge is to merge two pls files
# given two pls files, merge them into one
# 
import elementtree.ElementTree as ET
from elementtree.ElementTree import parse
import string as str
import codecs
import cgi
#
treevox = ET.parse("mylexvox.pls")
treespx = ET.parse("mylexspx.pls")
#
lexvox = treevox.getroot()
lexspx = treespx.getroot()
#
phons = { 'aa':u'ɑ','ae':u'æ','ah':u'ʌ','ao':u'ɔ','ar':u'ɛr','aw':u'aʊ',
'ax':u'ə','ay':u'aɪ','b':u'b','ch':u'tʃ','d':u'd','dh':u'ð','eh':u'ɛ',
'el':u'ɔl','en':u'ɑn','er':u'ər','ey':u'eɪ','f':u'f',
'g':u'ɡ','hh':u'h','ih':u'ɪ','ir':u'ɪr','iy':u'i','jh':u'dʒ','k':u'k','l':u'l',
'm':u'm','n':u'n','ng':u'ŋ','ow':u'oʊ','oy':u'ɔɪ','p':u'p','r':u'r','s':u's',
'sh':u'ʃ','t':u't','th':u'θ','uh':u'ʊ','ur':u'ʊr','uw':u'u','v':u'v',
'w':u'w','y':u'j','z':u'z','zh':u'ʒ','sil':'' }
#
def to_utf(s):
  myp = str.split(s)
  myipa = []
  for p in myp:
    myipa.append(phons[p])
  return str.join(myipa,'')
#
outfile = "my2lexmrg.pls"
out = codecs.open(outfile, encoding='utf-8', mode='w')
#
out.write('<?xml version="1.0" encoding="UTF-8"?>\n\
<lexicon version="1.0"\n \
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"\n\
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n \
      xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon\n \
        http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"\n\
      alphabet="ipa" xml:lang="en">')
#
# scan the two pls, create dictionary
voxdict = {}
for lexeme in lexvox:
  gr = str.lower(lexeme[0].text)
  ph = lexeme[1].text
  voxdict[gr] = {ph,}
#
for lexeme in lexspx:
  gr = lexeme[0].text
  ph = lexeme[1].text
  if gr in voxdict:
    voxdict[gr].add(ph)
  else:
    voxdict[gr] = {ph,}
#
for gr in sorted(voxdict.iterkeys()):
  out.write('\n\
  <lexeme>\n\
    <grapheme>'+cgi.escape(gr)+'</grapheme>')
  #print "%s: %s" % (key, voxdict[key])
  for ph in sorted(voxdict[gr]):
    alph = 'x-htk-voxforge' if ph.islower() else 'x-cmusphinx'
    out.write('\n\
    <phoneme alphabet="'+alph+'">'+ph+'</phoneme>')
    if ph.islower():
      phipa = to_utf(ph)
      out.write(u'\n\
    <phoneme alphabet="ipa">'+phipa+'</phoneme>')
  out.write('\n\
  </lexeme>')
# done, close files
out.write('\n</lexicon>')
out.close()
# now check the output is ok
tree = parse(outfile)
lexicon = tree.getroot()
mylexcount = 0
for lexeme in lexicon:
  mylexcount += 1
print 'Found %(number)d lexemes' % {"number":mylexcount}

Bạn bắt đầu bằng một biểu thức không chính thức (hashban) (#!) tiếp theo là một bộ chỉ số đặc biệt cho trình thông dịch Python, trên dòng thứ hai, mã này chứa các ký tự Unicode. Sau đó kịch bản lệnh nhập khẩu một số mô đun, gồm có elementtree, codecscgi, rất có ích khi xử lý Unicode. Bạn nói cho trình thông dịch biết hai tệp PLS nằm ở đâu và trỏ tới các phần tử gốc của chúng.

Biến phons lưu một từ điển đặc biệt có chứa một ánh xạ từ các mã Arpabet của CMU đến một tổ hợp Unicode tương đương. Từ điển này dịch các chuỗi phoneme hiện có sang một phiên bản Unicode. Xin cứ tự nhiên sửa đổi ánh xạ cho các mục đích riêng của bạn—ví dụ, bạn có thể cảm thấy rằng chữ tương đương với 'aa' trong Unicode là u'ɑ:', có một âm a kéo dài.

Chỉ có một hàm được định nghĩa là to_utf(), hàm này dịch một chuỗi Arpabet ASCII sang Unicode. Phần cuối cùng của nền tảng sẽ mở một tệp để lưu kết quả đầu ra, bảo đảm rằng tệp này biết rằng nó phải sẵn sàng chấp nhận Unicode và viết đoạn mở đầu PLS vào nó.

Bây giờ bạn đã sẵn sàng để xử lý các tệp bằng cách tạo ra hai từ điển Python đặc biệt bên trong, một từ điển từ một trong các tệp PLS, bằng cách quét chúng với thư viện elementtree. Người ta giả định rằng grapheme sẽ là phần tử con đầu tiên và phoneme là phần tử con thứ hai của từ vị. Kịch bản lệnh thêm tất cả các bản ghi từ tệp đầu tiên vào một từ điển hợp nhất mới. Khi quét tệp thứ hai, nếu khóa đã tồn tại trong từ điển hợp nhất mới thì bạn thêm vào tập hợp các âm vị của nó. Nếu không, bạn tạo một mục khóa mới trong từ điển hợp nhất. Ở cuối vòng lặp, từ điển hợp nhất mới có chứa các khóa từ cả hai các tệp ban đầu và một tập hợp kèm theo của một hoặc hai chuỗi phoneme.

Ghi tệp PLS mới từ việc hợp nhất mà bạn vừa tạo ra. Khi bạn quét qua từ điển, bạn thêm thuộc tính alphabet để phân biệt một phoneme này với phoneme khác. Khi đã ghi lại các âm vị hiện có, bạn tạo ra một chuỗi phoneme mới, là tương đương Unicode của chuỗi Arpabet của CMU, mà bạn có thể nhận được nó từ phiên bản HTK hoặc phiên bản Sphinx (hoặc cả hai) theo nhu cầu của bạn.

Cuối cùng, đóng phần tử gốc, đóng tệp và phân tích lại cú pháp nó như bạn đã làm trước đây để kiểm tra xem nó có đúng định dạng không.

Kết quả sẽ tương tự như Liệt kê 9.

Liệt kê 9. Các kết quả hợp nhất
...
  <lexeme>
    <grapheme>agenda</grapheme>
    <phoneme alphabet="x-cmusphinx">AH JH EH N D AH</phoneme>
    <phoneme alphabet="x-htk-voxforge">ax jh eh n d ax</phoneme>
    <phoneme alphabet="ipa">ədʒɛndə</phoneme>
  </lexeme>
  <lexeme> 
    <grapheme>agendas</grapheme>
    <phoneme alphabet="x-cmusphinx">AH JH EH N D AH Z</phoneme>
  </lexeme>
  <lexeme> 
    <grapheme>agent</grapheme>
    <phoneme alphabet="x-cmusphinx">EY JH AH N T</phoneme>
    <phoneme alphabet="x-htk-voxforge">ey jh ih n t</phoneme> 
    <phoneme alphabet="ipa">eɪdʒɪnt</phoneme>
  </lexeme>
  <lexeme>
    <grapheme>agent's</grapheme>
    <phoneme alphabet="x-cmusphinx">EY JH AH N T S</phoneme>
  </lexeme>
...

Với từ điển PLS hợp nhất, bạn có thể áp dụng XSL (Extensible Stylesheet Language - Ngôn ngữ bản định kiểu mở rộng) hoặc bất kỳ thủ tục khác nào để tạo ra các kết quả mà bạn cần, cho dù là flat-file hay một tệp PLS đặc trưng mới. Về lý thuyết, bạn cũng có thể lưu các chuỗi phoneme khác trong tệp này, thậm chí từ các ngôn ngữ khác. Tuy nhiên, đó là một cách sử dụng đặc tả PLS không chuẩn.

Kai Schott đã làm rất nhiều việc với PLS và có một số tệp đã được chuẩn bị để tải về dưới dạng các ngôn ngữ khác nhau, đặc biệt là tiếng Đức (xem phần Tài nguyên).


Các vấn đề chưa được giải quyết

Mặc dù bạn có thể nhận được rất nhiều thông tin từ các flat-file, các vấn đề sau đây vẫn chưa được giải quyết.

Lựa chọn từ nhiều chữ cái đặc trưng cho một âm vị
Thỉnh thoảng, bạn cần xử lý nhiều cách viết chính tả cho một từ trong cùng một ngôn ngữ. Vai trò và âm vị giống hệt nhau cho cả hai phép chính tả, vì vậy bạn có nhiều chữ cái đặc trưng cho một âm vị trong cùng một từ vị. Tuy nhiên, không có gì trong PLS cho phép bạn thêm một thuộc tính cho phần tử grapheme như có với các thuộc tính alphabet của phần tử phoneme.
Các từ viết tắt
Các từ vựng thường chứa các từ viết tắt. PLS xử lý các từ viết tắt này trong một phần tử con của lexeme gọi là một <alias>. Để xây dựng tự động một PLS từ một flat-file, bạn cần một cách để phân biệt các từ viết tắt với các từ thực trong từ vựng. Các flat-file không nhất thiết phải có thông tin đó.
Vai trò/thành phần của tiếng nói
Như với các từ viết tắt, thông tin về thành phần tiếng nói không có sẵn từ các flat-file để gắn các thuộc tính role cố định vào PLS.

Kết luận

Trong bài viết này, bạn đã học cách di chuyển dễ dàng giữa các định dạng phẳng và PLS. Việc lưu trữ các từ vựng theo định dạng PLS có nhiều lợi thế tiềm năng. Các dự án nguồn mở có thể hoặc không thể di chuyển sang XML với các tệp tài nguyên. Đó là một quyết định dành cho các nhà quản lý dự án thực hiện—chỉ riêng họ có thể đánh giá các tài nguyên và áp dụng chúng theo cách tốt nhất thường được chấp nhận trong các cộng đồng của họ.

Trong khi chờ đợi, bạn có thể sử dụng các tiêu chuẩn XML trong công việc riêng của mình mà không mất các đặc tính. Đối với các từ vựng và các từ điển được sử dụng trong việc nhận dạng giọng nói, bạn có thể gia tăng tính tương thích, tính chắc chắn và tiện ích giữa dự án bằng cách lưu trữ các từ vựng tùy chỉnh theo các định dạng PLS. Bạn có thể dễ dàng trích ra dữ liệu khi cần vào flat-file cần thiết.

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=857292
ArticleTitle=Tiến tới những chuẩn mở trong việc xử lý giọng nói
publish-date=02042013