NLTK 提供了強大的工具,能有效處理語言學研究中常用的 Toolbox 資料格式。Toolbox 格式儲存了詞彙、語法、語義等豐富的語言學資訊,Python 搭配 NLTK 能夠解析、查詢、轉換這些資料,方便進行後續分析。理解 Toolbox 資料結構和 NLTK 的 toolbox 模組,對於語言學研究者和自然語言處理工程師至關重要,能大幅提升資料處理效率,並能將 Toolbox 資料轉換成其他格式,例如 XML 或 HTML,方便資料交換與呈現。此外,利用上下文無關文法驗證詞典結構,更能確保資料品質與一致性,有助於建立更可靠的語言模型。

使用Python處理語言學資料:深入探索NLTK與Toolbox資料

在語言學研究和自然語言處理(NLP)領域中,處理結構化資料是一項基本任務。特別是在處理語言詞典和語言資料時,Toolbox格式是一種常見的資料格式。本文將探討如何使用Python和NLTK函式庫來處理Toolbox資料,並進行各種有用的分析。

Toolbox資料簡介

Toolbox是一種由SIL(國際語言學夏令營)開發的語言資料管理工具,主要用於語言學研究。它的資料格式是一種半結構化的文字格式,能夠儲存豐富的語言學資訊,如詞彙、語法和語義等。

載入Toolbox資料

首先,我們需要使用NLTK函式庫來載入Toolbox資料。NLTK(Natural Language Toolkit)是一個強大的Python函式庫,用於NLP任務。

>>> from nltk.corpus import toolbox
>>> lexicon = toolbox.xml('rotokas.dic')

內容解密:

這段程式碼的作用是從NLTK的語料函式庫中載入名為rotokas.dic的Toolbox詞典資料。toolbox.xml()函式將資料解析為XML格式,方便後續處理。

存取Toolbox資料

Toolbox資料可以透過索引或路徑來存取。

>>> lexicon[3][0]
<Element lx at77bd28>
>>> lexicon[3][0].tag
'lx'
>>> lexicon[3][0].text
'kaa'

內容解密:

這段程式碼展示瞭如何透過索引存取Toolbox資料中的特定條目。lexicon[3]存取第四個詞條(因為索引從0開始),而lexicon[3][0]則存取該詞條的第一個欄位。.tag屬性表示欄位的名稱,而.text屬性則表示欄位的內容。

使用路徑存取資料

我們可以使用XPath表示式來存取特定的欄位。

>>> [lexeme.text.lower() for lexeme in lexicon.findall('record/lx')]
['kaa', 'kaa', 'kaa', 'kaakaaro', 'kaakaaviko', 'kaakaavo', 'kaakaoko',
'kaakasi', 'kaakau', 'kaakauko', 'kaakito', 'kaakuupato', ..., 'kuvuto']

內容解密:

這段程式碼使用findall()方法查詢所有record/lx路徑下的欄位,並提取其文字內容,轉換為小寫後組成列表。這是一種快速取得特定欄位內容的方法。

將Toolbox資料轉換為XML格式

>>> import sys
>>> from nltk.etree.ElementTree import ElementTree
>>> tree = ElementTree(lexicon[3])
>>> tree.write(sys.stdout)
<record>
<lx>kaa</lx>
<ps>N</ps>
<pt>MASC</pt>
<cl>isi</cl>
<ge>cooking banana</ge>
<tkp>banana bilong kukim</tkp>
<pt>itoo</pt>
<sf>FLORA</sf>
<dt>12/Aug/2005</dt>
<ex>Taeavi iria kaa isi kovopaueva kaparapasia.</ex>
<xp>Taeavi i bin planim gaden banana bilong kukim tasol long paia.</xp>
<xe>Taeavi planted banana in order to cook it.</xe>
</record>

內容解密:

這段程式碼將第四個詞條轉換為XML格式並輸出到標準輸出。ElementTree類別用於建立XML樹,而write()方法則將XML內容寫入指定的檔案物件(在這裡是sys.stdout)。

生成HTML表格

>>> html = "<table>\n"
>>> for entry in lexicon[70:80]:
... lx = entry.findtext('lx')
... ps = entry.findtext('ps')
... ge = entry.findtext('ge')
... html += " <tr><td>%s</td><td>%s</td><td>%s</td></tr>\n" % (lx, ps, ge)
>>> html += "</table>"
>>> print(html)
<table>
<tr><td>kakae</td><td>???</td><td>small</td></tr>
<tr><td>kakae</td><td>CLASS</td><td>child</td></tr>
<tr><td>kakaevira</td><td>ADV</td><td>small-like</td></tr>
<tr><td>kakapikoa</td><td>???</td><td>small</td></tr>
<tr><td>kakapikoto</td><td>N</td><td>newborn baby</td></tr>
<tr><td>kakapu</td><td>V</td><td>place in sling for purpose of carrying</td></tr>
<tr><td>kakapua</td><td>N</td><td>sling for lifting</td></tr>
<tr><td>kakara</td><td>N</td><td>arm band</td></tr>
<tr><td>Kakarapaia</td><td>N</td><td>village name</td></tr>
<tr><td>kakarau</td><td>N</td><td>frog</td></tr>
</table>

內容解密:

這段程式碼生成了一個HTML表格,包含了第71到80個詞條的lxpsge欄位的內容。這對於將語言資料釋出到網上非常有用。

新增新欄位到Toolbox資料

from nltk.etree.ElementTree import SubElement
import re

def cv(s):
 s = s.lower()
 s = re.sub(r'[^a-z]', r'_', s)
 s = re.sub(r'[aeiou]', r'V', s)
 s = re.sub(r'[^V_]', r'C', s)
 return s

def add_cv_field(entry):
 for field in entry:
 if field.tag == 'lx':
 cv_field = SubElement(entry, 'cv')
 cv_field.text = cv(field.text)

>>> lexicon = toolbox.xml('rotokas.dic')
>>> add_cv_field(lexicon[53])
>>> print(nltk.to_sfm_string(lexicon[53]))
\lx kaeviro
\ps V
\pt A
\ge lift off
\ge take off
\tkp go antap
\sc MOTION
\vx1
\nt used to describe action of plane
\dt03/Jun/2005
\ex Pita kaeviroroe kepa kekesia oa vuripierevo kiuvu.
\xp Pita i go antap na lukim haus win i bagarapim.
\xe Peter went to look at the house that the wind destroyed.
\cv CVVCVCV

內容解密:

這段程式碼定義了一個cv()函式,用於將詞彙轉換為CV(子音-母音)序列。add_cv_field()函式則為每個詞條增加了一個新的cv欄位,包含了對應的CV序列。這對於語言學分析非常有用。

驗證Toolbox詞典

>>> fd = nltk.FreqDist(':'.join(field.tag for field in entry) for entry in lexicon)
>>> fd.items()
[('lx:ps:pt:ge:tkp:dt:ex:xp:xe', 41), ('lx:rt:ps:pt:ge:tkp:dt:ex:xp:xe', 37),

內容解密:

這段程式碼使用FreqDist類別來計算詞典中不同欄位序列的頻率。這對於檢查詞典的一致性和發現異常條目非常有用。

隨著NLP技術的不斷進步,我們可以預期會有更多高效、智慧的工具和方法出現,用於處理和分析語言資料。未來,我們可能會看到更多根據深度學習的技術被應用於語言資料的處理和分析,從而進一步提高語言研究和NLP任務的效率和準確性。

附錄:完整程式碼示例

import nltk
from nltk.corpus import toolbox
from nltk.etree.ElementTree import ElementTree, SubElement
import re
import sys

# 載入Toolbox資料
lexicon = toolbox.xml('rotokas.dic')

# 定義cv函式
def cv(s):
 s = s.lower()
 s = re.sub(r'[^a-z]', r'_', s)
 s = re.sub(r'[aeiou]', r'V', s)
 s = re.sub(r'[^V_]', r'C', s)
 return s

# 定義新增cv欄位的函式
def add_cv_field(entry):
 for field in entry:
 if field.tag == 'lx':
 cv_field = SubElement(entry, 'cv')
 cv_field.text = cv(field.text)

# 新增cv欄位到特定詞條
add_cv_field(lexicon[53])

# 輸出修改後的詞條
print(nltk.to_sfm_string(lexicon[53]))

內容解密:

這段完整的程式碼示例展示瞭如何載入Toolbox資料、定義cv()函式、新增cv欄位到特定詞條,並輸出修改後的詞條。這是一個綜合性的例子,涵蓋了本文討論的多個重要概念。

使用上下文無關文法驗證辭彙資料

在處理語言資料時,建立一個上下文無關文法(Context-Free Grammar, CFG)來驗證辭彙條目的結構是一個非常有效的方法。辭彙資料通常包含多個欄位,如詞彙、詞性、註解和範例等,這些欄位之間存在一定的層級結構。透過定義一個合適的CFG,可以自動檢查辭彙條目是否符合預定的結構。

CFG的定義與應用

在範例11-3中,我們定義了一個CFG來描述辭彙條目的結構。這個文法包含多個產生式規則,用於描述辭彙條目的層級結構。例如,S -> Head PS Glosses Comment Date Sem_Field Examples 表示一個完整的辭彙條目(S)由多個部分組成,包括標頭(Head)、詞性(PS)、註解(Comment)、日期(Date)、語義欄位(Sem_Field)和範例(Examples)等。

grammar = nltk.parse_cfg('''
S -> Head PS Glosses Comment Date Sem_Field Examples
Head -> Lexeme Root
Lexeme -> "lx"
Root -> "rt" |
PS -> "ps"
Glosses -> Gloss Glosses |
Gloss -> "ge" | "tkp" | "eng"
Date -> "dt"
Sem_Field -> "sf"
Examples -> Example Ex_Pidgin Ex_English Examples |
Example -> "ex"
Ex_Pidgin -> "xp"
Ex_English -> "xe"
Comment -> "cmt" | "nt" |
''')

內容解密:

  1. S -> Head PS Glosses Comment Date Sem_Field Examples:定義辭彙條目的整體結構,包含標頭、詞性、註解、日期、語義欄位和範例。
  2. Head -> Lexeme Root:標頭由詞彙(Lexeme)和詞根(Root)組成。
  3. Lexeme -> "lx":詞彙欄位由標記 “lx” 表示。
  4. Glosses -> Gloss Glosses |:註解(Glosses)可以包含一個或多個 Gloss,或者為空。
  5. Date -> "dt":日期欄位由標記 “dt” 表示。
  6. Examples -> Example Ex_Pidgin Ex_English Examples |:範例部分可以包含多個範例,或者為空。每個範例由 “ex”、“xp” 和 “xe” 組成。

驗證辭彙資料

定義好CFG後,可以使用NLTK的RecursiveDescentParser來解析辭彙條目,並檢查它們是否符合文法規則。

def validate_lexicon(grammar, lexicon, ignored_tags):
    rd_parser = nltk.RecursiveDescentParser(grammar)
    for entry in lexicon:
        marker_list = [field.tag for field in entry if field.tag not in ignored_tags]
        if rd_parser.nbest_parse(marker_list):
            print("+", ':'.join(marker_list))
        else:
            print("-", ':'.join(marker_list))

>>> lexicon = toolbox.xml('rotokas.dic')[10:20]
>>> ignored_tags = ['arg', 'dcsv', 'pt', 'vx']
>>> validate_lexicon(grammar, lexicon, ignored_tags)

內容解密:

  1. rd_parser = nltk.RecursiveDescentParser(grammar):建立一個遞迴下降解析器,用於解析辭彙條目。
  2. marker_list = [field.tag for field in entry if field.tag not in ignored_tags]:提取辭彙條目中的欄位標記,並過濾掉不需要的標記。
  3. if rd_parser.nbest_parse(marker_list):檢查解析結果,判斷辭彙條目是否符合文法規則。如果符合,輸出 “+",否則輸出 “-"。

使用區塊解析器處理辭彙資料

除了使用CFG,還可以使用區塊解析器(Chunk Parser)來處理辭彙資料。區塊解析器在識別部分結構和報告已識別的部分結構方面更加有效。

grammar = r"""
lexfunc: {<lf>(<lv><ln|le>*)*}
example: {<rf|xv><xn|xe>*}
sense: {<sn><ps><pn|gv|dv|gn|gp|dn|rn|ge|de|re>*<example>*<lexfunc>*}
record: {<lx><hm><sense>+<dt>}
"""
>>> db = toolbox.ToolboxData()
>>> db.open(nltk.data.find('corpora/toolbox/iu_mien_samp.db'))
>>> lexicon = db.parse(grammar, encoding='utf8')
>>> toolbox.data.indent(lexicon)
>>> tree = ElementTree(lexicon)
>>> output = open("iu_mien_samp.xml", "w")
>>> tree.write(output, encoding='utf8')
>>> output.close()

內容解密:

  1. lexfunc: {<lf>(<lv><ln|le>*)*}:定義區塊文法規則,用於識別辭彙功能(lexfunc)。
  2. example: {<rf|xv><xn|xe>*}:定義規則,用於識別範例(example)。
  3. sense: {<sn><ps><pn|gv|dv|gn|gp|dn|rn|ge|de|re>*<example>*<lexfunc>*}:定義規則,用於識別詞義(sense)。
  4. record: {<lx><hm><sense>+<dt>}:定義規則,用於識別完整的辭彙記錄(record)。

使用OLAC後設資料描述語言資源

開放語言檔案社群(OLAC, Open Language Archives Community)提供了一種標準化的方式來描述語言資源。OLAC後設資料根據都柏林核心後設資料(Dublin Core Metadata)擴充套件,專門用於描述語言資源。

OLAC後設資料的重要性

OLAC後設資料允許研究人員跨不同的數位儲存函式庫搜尋和檢索語言資源。這些後設資料包括標題、建立者、主題、描述、發布者、貢獻者、日期、型別、格式、識別符號、來源、語言、關係、覆寫範圍和權利等元素。

OLAC的應用

OLAC後設資料可以用於描述語言資料和工具,無論是實體還是數位格式。這些後設資料確保了跨儲存函式庫的統一描述,從而促進了語言資源的發現和分享。