鍍金池/ 教程/ iOS/ 語言標(biāo)簽
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開發(fā)
Collection View 動(dòng)畫
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫
常見的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

語言標(biāo)簽

當(dāng)我們處理自然語言(相對(duì)于程序語言而言)的時(shí)候會(huì)遇到一項(xiàng)挑戰(zhàn),即涵義模棱兩可。程序語言是被設(shè)計(jì)成為有且只有一個(gè)可能解釋的語言,而人類語言可能由于模糊性和不確定性衍生出很多問題。這是由于有時(shí)候你并不想確切地告訴別人你對(duì)某事物的想法。在社交場(chǎng)合這完全沒有問題,但是當(dāng)你試圖使用計(jì)算機(jī)來處理人類語言的話,就會(huì)非常痛苦。

詞法標(biāo)識(shí)(token)就是一個(gè)簡(jiǎn)單的例子。程序語言的詞法分析對(duì)于標(biāo)識(shí)表示什么,它是什么類型(語句分隔符,標(biāo)識(shí)符,保留關(guān)鍵字等等)是什么有著明確的規(guī)則。而自然語言則遠(yuǎn)不能如此清晰可辯。can’t 是一個(gè)還是兩個(gè)標(biāo)識(shí)?并且根據(jù)你做出的判斷,cannot 或者 can not 這兩個(gè)應(yīng)該是相同意思的詞又各是幾個(gè)標(biāo)識(shí)呢?很多復(fù)合詞都可以寫成一個(gè)詞(比如:bookshelf),或者兩個(gè)詞(比如:lawn mower),甚至還可以用連字符來連接(比如:life-cycle)。有些字符 (比如說連字符或者右肩單撇號(hào)),可以有很多種解釋,而如何選擇正確字符往往取決于上下文語言環(huán)境(撇號(hào)在一個(gè)單詞的最后是表示所有格符號(hào)還是后單引號(hào)?)

句子的情況同樣不怎么好:如果簡(jiǎn)單認(rèn)為句號(hào)是用來結(jié)束一個(gè)句子的話,在我們使用縮寫或是序數(shù)的時(shí)候就悲劇了。雖然通常情況下,我們是可以解決這個(gè)問題的,但是對(duì)有些句子而言,除非將整個(gè)段落徹底分析,否則無法真正確定這些句子的意思。我們?nèi)祟惿踔烈矡o法有意識(shí)地考慮這些問題。

不過我們希望能夠處理人類語言,因?yàn)樵诟浖涣鞯臅r(shí)候,使用人類語言對(duì)用戶更加友好。我們更愿意直接告訴計(jì)算機(jī)要做什么,讓計(jì)算機(jī)為我們分析報(bào)紙文章,并對(duì)我們感興趣的新聞做個(gè)總結(jié),而不是通過敲擊鍵盤或者點(diǎn)擊小小的按鈕(或者在小小的虛擬鍵盤上打字)來讓計(jì)算機(jī)為我們做這些事。其中有些還在我們的能力范圍之外(至少在蘋果為我們提供與 Siri 交互的 API 之前)。但是有些已經(jīng)成為可能,那就是 NSLinguisticTagger

NSLinguisticTagger 是 Foundation 框架中命名極為不當(dāng)?shù)念愔?,這是因?yàn)樗h(yuǎn)遠(yuǎn)不止是一個(gè)小小的詞性 tagger,而是集詞法分析,分詞器,命名實(shí)體識(shí)別及詞性標(biāo)注為一體的類。換句話說,它幾乎可以滿足你處理某些計(jì)算機(jī)語言處理的全部要求。

為了展示 NSLinguisticTagger 類的用法,我們會(huì)開發(fā)一個(gè)靈活的工具用來搜索。我們有一個(gè)充滿了文本(比如新聞,電郵,或者其他的任意文本)的集合,然后我們輸入一個(gè)單詞,這個(gè)單詞將返回所有包含這個(gè)單詞的句子。我們會(huì)忽略功能詞(比如 the,of 或者 and),因?yàn)樗鼈冊(cè)谶@個(gè)語言環(huán)境中太過于常見,沒有什么用處。我們目前要實(shí)現(xiàn)的是第一步:從一個(gè)單獨(dú)文件中提取相關(guān)單詞。由此可以迅速地?cái)U(kuò)展到提供完整功能。

GitHub 上有源代碼和樣本文本。這是《衛(wèi)報(bào)》上一篇關(guān)于中英貿(mào)易的文章。當(dāng)用軟件分析這份文本時(shí),你會(huì)發(fā)現(xiàn),它并不是總是運(yùn)行良好,不過,出現(xiàn)運(yùn)行故障完全正常:人類語言和任何正式語言都不同,人類語言凌亂復(fù)雜,無法簡(jiǎn)單劃歸到整齊劃一的規(guī)則系統(tǒng)。很多理論問題(哪怕就像詞性一樣基礎(chǔ)的問題)在某種程度上是無法解決的,這是由于我們?nèi)匀粚?duì)如何才能最好地描述語言還所知甚少。比如說,詞的分類是以拉丁語為依據(jù)的,但這并不意味著就必定適合英語。它們充其量只是大概近似而已。不過從很多實(shí)際的目的來看,這樣就已經(jīng)足夠了,不需要讓人怎么擔(dān)心了。

標(biāo)簽體系 (Tag Schemes)

注釋和標(biāo)記文本的核心方法就是標(biāo)簽體系的核心方法。以下是幾個(gè)可用的標(biāo)簽體系:

  • NSLinguisticTagSchemeTokenType
  • NSLinguisticTagSchemeLexicalClass
  • NSLinguisticTagSchemeNameType
  • NSLinguisticTagSchemeNameTypeOrLexicalClass
  • NSLinguisticTagSchemeLemma
  • NSLinguisticTagSchemeLanguage
  • NSLinguisticTagSchemeScript

NSLinguisticTagger 實(shí)例掃描文本中的所有條目,并調(diào)用一個(gè)包含被請(qǐng)求的標(biāo)簽體系值的 block。最基礎(chǔ)的是 NSLinguisticTagSchemeTokenType:詞,標(biāo)點(diǎn),空格,或是“其他”。我們可以使用這個(gè)來識(shí)別哪些是真正的詞,那么我們?cè)趹?yīng)用程序中就可以簡(jiǎn)單地忽略其他那些不是有效詞的語素。NSLinguisticTagSchemeLexicalClass 和詞性有關(guān),是一組非常基礎(chǔ)的標(biāo)簽(就嚴(yán)格意義上的語言分析而言,這組標(biāo)簽還遠(yuǎn)遠(yuǎn)不夠精細(xì)),我們可以使用這組標(biāo)簽來分辨我們想要的實(shí)詞(名詞,動(dòng)詞,形容詞,副詞)和我們想忽略的虛詞(連詞,介詞,冠詞等等)。在 NSLinguisticTagger 類的文檔中寫明了全套可能值。

NSLinguisticTagSchemeNameType 是指命名實(shí)體識(shí)別:我們可以知道一個(gè)詞是不是表示人物,地點(diǎn)或者組織。同樣的,這相對(duì)于自然語言的處理而言是相當(dāng)基本,但卻非常有用的,比如說你想搜索一個(gè)特定的人物或者地點(diǎn)。還有一種潛在的應(yīng)用是“給我一份文本中所提到的所有政治家的名錄”,你可以瀏覽這份文本中的人名,然后查閱數(shù)據(jù)庫(kù)(比如維基)來核對(duì)他們是否確實(shí)是政治家。這也可以跟 lexical 類相結(jié)合,因?yàn)檫@往往包含一個(gè)分類叫做“名字”。

NSLinguisticTagSchemeLemma 是詞匯的標(biāo)準(zhǔn)形式,或者說是其基本形式。對(duì)英語而言,這不是什么大問題,不過對(duì)于其它語言而言卻重要得多。原型基本上就是你在詞典中查的到的那個(gè)形式。比如說,tables 是一個(gè)復(fù)數(shù)名詞,它的基本形式是單數(shù)的 table。同樣的,動(dòng)詞 running 是由 run 變形而來的不定式。如果你想要以同樣的方式處理各種詞類的變形,使用原形就非常有用,事實(shí)上這也是我們要為我們的示例應(yīng)用程序所做的 (因?yàn)檫@可以有助于保持索引不過于龐大)。

NSLinguisticTagSchemeLanguage 和我們所使用的語言相關(guān)。如果你使用iOS(截至iOS7),目前只能處理英語。使用OS X(截至10.9 / Mavericks)你可以稍微多幾種語言可以選擇。+[NSLinguisticTagger availableTagSchemesForLanguage:] 方法為我們列舉了對(duì)于給定語言的所有可用體系。對(duì)于在 iOS 中對(duì)應(yīng)語言數(shù)量限制的原因很可能是資源文件要占用大量空間。在筆記本或者臺(tái)式電腦上不是什么大問題,但是在手機(jī)或者平板上的話就不太妙了。

NSLinguisticTagSchemeScript 是書寫體系,比如拉丁字母 (Latin),西里爾字母 (Cyrillic) 等等。對(duì)于英語,我們將使用拉丁字母。如果你知道你將處理哪種語言,使用 setOrthography 方法可以改善標(biāo)簽的結(jié)果,特別對(duì)相對(duì)較短的字符而言更是如此。

標(biāo)簽選項(xiàng)

目前我們已經(jīng)知道 NSLinguisticTagger 可以為我們識(shí)別什么了,我們需要告訴它我們想要什么,以及我們想如何獲得。這里有幾個(gè)可以定義 tagger 行為的選項(xiàng),它們都是 NSUInteger 類型的,并且可以使用位運(yùn)算 OR 組合使用。

第一個(gè)選項(xiàng)是“省略單詞”,除非你只想看標(biāo)點(diǎn)或者其它非詞類,否則這個(gè)選項(xiàng)毫無意義。比較有用的是下面的三個(gè)選項(xiàng):“省略標(biāo)點(diǎn)(omit punctuation)”,“省略空格(omit whitespace)”以及“省略其他(omit other)”。除非你想要對(duì)文本做全面語言分析,否則你基本上只會(huì)對(duì)單詞感興趣,而對(duì)其中的逗號(hào)句號(hào)則興趣不大。有了這些選項(xiàng),就可以輕輕松松讓 tagger 對(duì)單詞作出限制,再也不用掛慮在心。最后一個(gè)選項(xiàng)是“連接名字(join names)”,因?yàn)槊钟袝r(shí)不僅僅是一個(gè)標(biāo)識(shí)。這個(gè)選項(xiàng)會(huì)將它們結(jié)合在一起,作為一個(gè)獨(dú)立的語言單位來處理。這個(gè)選項(xiàng)可能不會(huì)總是用得上,但是確實(shí)非常有用。舉個(gè)例子,在樣本文本中,字符串“Owen Patterson”被識(shí)別為一個(gè)名稱,并且作為一個(gè)獨(dú)立的語言單位被返回。

處理架構(gòu)

程序會(huì)給一定數(shù)量的文本在獨(dú)立文件中建立索引(我們假設(shè)是使用UTF-8編碼)。我們將使用一個(gè) FileProcessor 類來處理一個(gè)單獨(dú)文件,將文件內(nèi)容分為一個(gè)一個(gè)單詞,再把這些單詞傳遞給另一類來進(jìn)行處理。后一個(gè)類將實(shí)現(xiàn) WordReceiver 接口,其中包括一個(gè)方法:

-(void)receiveWord:(NSDictionary*)word

我們不是使用 NSString 來表示單詞,而是使用字典,這是因?yàn)橐粋€(gè)單詞會(huì)有很多屬性,包括實(shí)際標(biāo)識(shí),詞性或名稱類型,原型,所在句子的數(shù)目,句子中的位置等。為了建立索引,我們還想儲(chǔ)存文件名。調(diào)用 FileProcessor 的這個(gè)方法:

- (BOOL)processFile:(NSString*)filename

將觸發(fā)分析,如果一切進(jìn)行順利的話,返回 YES,在出現(xiàn)錯(cuò)誤的時(shí)候返回 NO。它首先由文件創(chuàng)建一個(gè) NSString,然后將其傳遞給一個(gè) NSLinguisticTagger 實(shí)例來處理。

NSLinguisticTagger 主要做的是的在一個(gè) NSString 中進(jìn)行掃描并對(duì)尋找到的每一個(gè)元素調(diào)用 block。為了稍作簡(jiǎn)化,我們首先將文本分解為一個(gè)個(gè)的句子,然后分別掃描每一個(gè)句子。這樣比較容易追蹤句子的 ID。至于標(biāo)簽,我們會(huì)處理大量的 NSRange,它們可以被用來界定源文件中文本的注解。我們從在第一個(gè)句子范圍內(nèi)創(chuàng)建一個(gè)搜索范圍開始,并使用其在最大程度上獲得初始語句的標(biāo)簽。

NSRange currentSentence = [tagger sentenceRangeForRange:NSMakeRange(0, 1)];

一旦句子處理結(jié)束,就檢查是否成功完成全部的文本,或者是否還有更多的句子等待處理:

if (currentSentence.location + currentSentence.length == [fileContent length]) {
    currentSentence.location = NSNotFound;
} else {
    NSRange nextSentence = NSMakeRange(currentSentence.location + currentSentence.length + 1, 1);
    currentSentence = [tagger sentenceRangeForRange:nextSentence];
}

如果已經(jīng)到了文本的末尾,我們將使用 NSNotFound 來對(duì) while 循環(huán)發(fā)出終止信號(hào)。如果我們使用一個(gè)超出文本之外的范圍,NSLinguisticTagger 將拋出一個(gè)異常并且直接崩潰。

句子處理循環(huán)中的主要方法調(diào)用如下:

while (currentSentence.location != NSNotFound) {
    __block NSUInteger tokenPosition = 0;
    [tagger enumerateTagsInRange:currentSentence
                          scheme:NSLinguisticTagSchemeNameTypeOrLexicalClass
                         options:options
                      usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) 
    {
        NSString *token = [fileContent substringWithRange:tokenRange];
        NSString *lemma = [tagger tagAtIndex:tokenRange.location 
                                      scheme:NSLinguisticTagSchemeLemma 
                                  tokenRange: NULL 
                               sentenceRange:NULL];
        if (lemma == nil) {
            lemma = token;
        }
        [self.delegate receiveWord:@{
            @"token": token, 
            @"postag": tag, 
            @"lemma": lemma, 
            @"position": @(tokenPosition), 
            @"sentence": @(sentenceCounter), 
            @"filename": filename
        }];
        tokenPosition++;
    }];
}

我們讓 tagger 處理 NSLinguisticTagSchemeNameTypeOrLexicalClass,指定一組選項(xiàng)(連接名字,省略標(biāo)點(diǎn)和空格)。然后我們獲取這個(gè)標(biāo)簽,以及搜索到的每一項(xiàng)條目的范圍,并進(jìn)一步檢索信息。標(biāo)識(shí)(token)是字符串一部分,僅僅由字符范圍來描述。lemma 是基本形式,如果不可能用的這個(gè)值會(huì)是 nil,所以我們需要做檢查,并使用標(biāo)識(shí)字符串作為候補(bǔ)值。一旦收集到這個(gè)信息,我們就可以將其打包到一個(gè)字典中,然后發(fā)送給 delegate 進(jìn)行處理。

在我們的示例應(yīng)用中,我們僅僅輸出了我們接收到的單詞,但是我們?cè)谶@里基本上可以做任何我們想做的一切。為了實(shí)現(xiàn)搜索,我們可以過濾掉除了名詞,動(dòng)詞,形容詞,副詞和名字以外的所有詞,并且在索引數(shù)據(jù)庫(kù)中儲(chǔ)存這些單詞的位置。使用原形,而不使用標(biāo)識(shí)值,可以使我們合并各種詞的變形 (pigpigs),這可以保持索引不過于龐大,并且與僅只匹配實(shí)際標(biāo)識(shí)詞相比,也可以檢索出更相關(guān)的詞。請(qǐng)記住,你可能還要將所有查詢按照原形變化進(jìn)行歸類,否則,搜索 pigs 的話將不會(huì)返回任何結(jié)果。

為了更加真實(shí),我在樣本文本頭部信息中加進(jìn)了一些基本 HTML 標(biāo)簽,比如確定標(biāo)題,署名,日期。在通過 tagger 運(yùn)行的時(shí)候出現(xiàn)一個(gè)問題,即 NSLinguisticTagger 是不知道關(guān)于 HTML 的東西的,并試圖將這些 HTML 標(biāo)記當(dāng)做文本來處理。下面是最前面的三個(gè)檢索詞。

{
    filename = "/Users/oliver/tmp/guardian-article.txt";
    lemma = "<";
    position = 0;
    postag = Particle;
    sentence = 0;
    token = "<";
}
{
    filename = "/Users/oliver/tmp/guardian-article.txt";
    lemma = h1;
    position = 1;
    postag = Verb;
    sentence = 0;
    token = h1;
}
{
    filename = "/Users/oliver/tmp/guardian-article.txt";
    lemma = ">";
    position = 2;
    postag = Adjective;
    sentence = 0;
    token = ">";
}

不僅僅是標(biāo)簽被分成了幾個(gè)部分,被當(dāng)做詞來處理,而且還得到了奇怪和完全錯(cuò)誤的標(biāo)簽。所以,如果你在處理包含標(biāo)記的文件,最好先將其過濾出來。或許,你想要識(shí)別出標(biāo)簽,并返回覆蓋標(biāo)簽區(qū)域的 NSRange,而不是像我們之前處理示例應(yīng)用一樣將整個(gè)文本分成一個(gè)個(gè)句子?;蛘哒f,如果存在內(nèi)嵌標(biāo)簽(比如加粗,斜體,超鏈接),將標(biāo)簽全部剔除出來會(huì)更好些。

結(jié)果

就算是用 tagger 來處理通用語言,其表現(xiàn)也出人意料的優(yōu)秀。如果你僅僅處理某一個(gè)領(lǐng)域(比如技術(shù)文本)的話,你可以做出一些在處理不受限制的文本時(shí)無法做到的假設(shè)。但是蘋果的 tagger 必須在無法預(yù)知會(huì)遇到什么的情況下也能工作,鑒于如此,它偶爾也會(huì)出錯(cuò),不過相對(duì)來說是非常少的。很顯然,很多名稱無法識(shí)別,比如說 Chengdu 這樣的地名。但另一方面,文本中大多數(shù)人名的處理都是非常不錯(cuò)的。由于某些原因,日期(Wednesday 4 December 2013 10.35 GMT)被當(dāng)做了人名來處理,可能是來源于魯賓遜?克魯索的命名習(xí)慣吧。環(huán)境大臣 Owen Patterson 可以被識(shí)別出來,但是,一般被認(rèn)為更加重要的首相 David Cameron 卻沒有被識(shí)別出來,盡管 David 是個(gè)更為常見的名字。

這是概率 tagger 的問題:有時(shí)候很難理解為什么某些詞以特定的方式被加上標(biāo)簽。也沒有什么像鉤子一樣的東西可以掛靠 tagger,可以讓你提供比如說已知的地點(diǎn),人物或者組織的名稱列表。你只能用默認(rèn)設(shè)置進(jìn)行處理。因此,最好使用大量數(shù)據(jù)來測(cè)試那些帶有 tagger 的應(yīng)用程序,通過觀察結(jié)果,你可以大概知道哪些可以正常運(yùn)行,哪些會(huì)遇到問題。

概率

有很多種方法來實(shí)現(xiàn)詞性標(biāo)簽:兩個(gè)主要的途徑,一個(gè)是規(guī)則性的,一個(gè)是隨機(jī)性。兩種途徑都有一套相當(dāng)龐大的規(guī)則來告訴你,形容詞的后面是名詞,而不是冠詞,或者有一個(gè)概率矩陣告訴你某一個(gè)特定的標(biāo)簽會(huì)出現(xiàn)在一個(gè)特定的語言環(huán)境中的可能性有多大。你也可以使用基于概率性的模型,同時(shí)添加一些規(guī)則來修正反復(fù)出現(xiàn)的典型錯(cuò)誤,這就是所謂的混合 tagger。由于為不同語言開發(fā)規(guī)則集比自動(dòng)學(xué)習(xí)隨機(jī)語言模型的成本要高得多,所以我猜測(cè) NSLinguisticTagger 應(yīng)該是基于完全的隨機(jī)模型。這個(gè)實(shí)現(xiàn)細(xì)節(jié)也可以從下面的方法中窺探一二:

- (NSArray *)possibleTagsAtIndex:(NSUInteger)charIndex 
                          scheme:(NSString *)tagScheme 
                      tokenRange:(NSRangePointer)tokenRange 
                   sentenceRange:(NSRangePointer)sentenceRange 
                          scores:(NSArray **)scores

這說明了一個(gè)事實(shí),那就是有時(shí)候(其實(shí)是大多數(shù)時(shí)候)會(huì)出現(xiàn)多個(gè)可能的標(biāo)簽值,tagger 必須判斷哪個(gè)可能是錯(cuò)誤的。使用這個(gè)方法,你可以獲得一份選項(xiàng)列表和概率得分。得分最高的詞則被 tagger 選中,但是如果你想要?jiǎng)?chuàng)建一套基于規(guī)則的后處理來改善 tagger 工作,你依然可以訪問得分第二的詞或者其他候選項(xiàng)。

對(duì)于這個(gè)方法要提高警惕,其中有個(gè) bug,實(shí)際上它并沒有返回任何的分?jǐn)?shù)。不過在 OS X 10.9 / Mavericks 中這個(gè) bug 已被修復(fù)。所以,如果你需要支持 OS X 10.9 / Mavericks 之前的版本,會(huì)提示你無法使用這個(gè)方法。順帶一提,在 iOS 7 中這個(gè)方法可以良好運(yùn)行。

下面是幾個(gè) When is the next train…: 的輸出案例:

When is the next train
Pronoun, 0.9995162 Verb, 1 Determiner, 0.9999986 Adjective, 0.9292629 Noun, 0.8741992
Conjunction, 0.0004337671 Adverb, 1.344403e-06 Adverb, 0.0636334 Verb, 0.1258008
Adverb, 4.170838e-05 Preposition, 0.007003677
Noun, 8.341675e-06 Noun, 0.0001000525

正如你所見,在這個(gè)例子中到現(xiàn)在為止,正確的 tag 擁有最高的概率。對(duì)于大多數(shù)應(yīng)用程序而言,你可以保持程序簡(jiǎn)單,并認(rèn)可 tagger 所提供的標(biāo)簽,而不對(duì)概率進(jìn)行深究。不過你得承認(rèn) tagger 偶然也是會(huì)出錯(cuò)的,而你也可以訪問到這些識(shí)別結(jié)果,并做出相應(yīng)處理。 當(dāng)然,如果你不親自檢查的話,你就不會(huì)知道 tagger 什么時(shí)候會(huì)出錯(cuò)。然而,其中一個(gè)線索是概率差:如果概率非常接近(和上面的例子不同),說不定就表示可能出錯(cuò)了。

結(jié)論

處理自然語言是很困難的,蘋果給我們提供了一個(gè)非常好的工具,這個(gè)工具可以簡(jiǎn)便地支持絕大多數(shù)使用情況。當(dāng)然,它也不是完美無缺的,即使最先進(jìn)的語言處理工具也不是完美無缺的。iOS 目前只支持英語,不過隨著技術(shù)改善,以及如果有足夠大的內(nèi)存來儲(chǔ)存(毫無疑問會(huì)很大的)語言模型的話,這將有所改變。在此之前,我們會(huì)受到一些限制。不過還是有很多方法可以給應(yīng)用程序添加語言支持。在文本編輯器中突出動(dòng)詞,理解用戶鍵入的內(nèi)容,或者處理外部數(shù)據(jù)文件等工作還是很簡(jiǎn)單的,NSLinguisticTagger 可以幫助你做到這一點(diǎn)。

上一篇:截圖測(cè)試