前文對SharpICTCLAS中的一些主要內容做了介紹,本文介紹一下SharpICTCLAS中一些其它考慮,包括事件機制以及如何使用SharpICTCLAS。
1、SharpICTCLAS中的事件
分詞過程比較復雜,所以很可能有人希望能夠追蹤分詞的過程,設置代碼斷點比較麻煩,因此SharpICTCLAS中提供了事件機制,可以在分詞的不同階段觸發相關事件,使用者可以訂閱這些事件并輸出中間結果供查錯使用。
事件的階段被定義在SegmentStage枚舉當中,代碼如下:
{
BeginSegment, //開始分詞
AtomSegment, //原子切分
GenSegGraph, //生成SegGraph
GenBiSegGraph, //生成BiSegGraph
NShortPath, //N最短路徑計算
BeforeOptimize, //對N最短路徑進一步整理得到的結果
OptimumSegment, //初始OptimumSegmentGraph
PersonAndPlaceRecognition, //人名與地名識別后的OptimumSegmentGraph
BiOptimumSegment, //生成BiOptimumSegmentGraph
FinishSegment //完成分詞,輸出結果
}
分別對應分詞過程中的10個階段。
SharpICTCLAS中還定義了一個EventArgs,里面包含了兩個元素,分別用來記錄該事件元素所處的分詞階段以及該階段的相關中間結果信息。中間結果信息使用的是string類型數據,日后可以考慮采用更復雜的表示形式輸出中間結果。該事件元素定義如下:
{
public SegmentStage Stage;
public string Info = "";
......
}
剩下的工作就是定義委派并發布事件了。由于分詞過程主要集中在兩個類中:WordSegment類與Segment類,而用戶通常只需要與WordSegment類打交道,因此WordSegment類中轉發了Segment類產生的事件。
委派的定義以及事件的定義如下(部分):
public delegate void SegmentEventHandler(object sender, SegmentEventArgs e);
//---定義事件
public event SegmentEventHandler OnSegmentEvent;
//---發布事件的方法
private void SendEvents(SegmentEventArgs e)
{
if (OnSegmentEvent != null)
OnSegmentEvent(this, e);
}
//---開始分詞
private void OnBeginSegment(string sentence)
{
SendEvents(new SegmentEventArgs(SegmentStage.BeginSegment, sentence));
}
......
//---結束分詞
private void OnFinishSegment(List<WordResult[]> m_pWordSeg)
{
StringBuilder sb = new StringBuilder();
for (int k = 0; k < m_pWordSeg.Count; k++)
{
for (int j = 0; j < m_pWordSeg[k].Length; j++)
sb.Append(string.Format("{0} /{1} ", m_pWordSeg[k][j].sWord,
Utility.GetPOSString(m_pWordSeg[k][j].nPOS)));
sb.Append("\r\n");
}
SendEvents(new SegmentEventArgs(SegmentStage.FinishSegment, sb.ToString()));
}
有了這些事件,用戶可以根據需要訂閱不同的事件來獲取分詞的中間結果,極大方便了程序調試工作。
2、SharpICTCLAS的使用
下面是一個使用SharpICTCLAS的示例代碼:
using System.Collections.Generic;
using System.Text;
using SharpICTCLAS;
public class WordSegmentSample
{
private int nKind = 1; //在NShortPath方法中用來決定初步切分時分成幾種結果
private WordSegment wordSegment;
//=======================================================
// 構造函數,在沒有指明nKind的情況下,nKind 取 1
//=======================================================
public WordSegmentSample(string dictPath) : this(dictPath, 1) { }
//=======================================================
// 構造函數
//=======================================================
public WordSegmentSample(string dictPath, int nKind)
{
this.nKind = nKind;
this.wordSegment = new WordSegment();
//---------- 訂閱分詞過程中的事件 ----------
wordSegment.OnSegmentEvent += new SegmentEventHandler(this.OnSegmentEventHandler);
wordSegment.InitWordSegment(dictPath);
}
//=======================================================
// 開始分詞
//=======================================================
public List<WordResult[]> Segment(string sentence)
{
return wordSegment.Segment(sentence, nKind);
}
//=======================================================
// 輸出分詞過程中每一步的中間結果
//=======================================================
private void OnSegmentEventHandler(object sender, SegmentEventArgs e)
{
switch (e.Stage)
{
case SegmentStage.BeginSegment:
Console.WriteLine("\r\n==== 原始句子:\r\n");
Console.WriteLine(e.Info + "\r\n");
break;
case SegmentStage.AtomSegment:
Console.WriteLine("\r\n==== 原子切分:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.GenSegGraph:
Console.WriteLine("\r\n==== 生成 segGraph:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.GenBiSegGraph:
Console.WriteLine("\r\n==== 生成 biSegGraph:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.NShortPath:
Console.WriteLine("\r\n==== NShortPath 初步切分的到的 N 個結果:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.BeforeOptimize:
Console.WriteLine("\r\n==== 經過數字、日期合并等策略處理后的 N 個結果:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.OptimumSegment:
Console.WriteLine("\r\n==== 將 N 個結果歸并入OptimumSegment:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.PersonAndPlaceRecognition:
Console.WriteLine("\r\n==== 加入對姓名、翻譯人名以及地名的識別:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.BiOptimumSegment:
Console.WriteLine("\r\n==== 對加入對姓名、地名的OptimumSegment生成BiOptimumSegment:\r\n");
Console.WriteLine(e.Info);
break;
case SegmentStage.FinishSegment:
Console.WriteLine("\r\n==== 最終識別結果:\r\n");
Console.WriteLine(e.Info);
break;
}
}
}
從中我們可以看出,首先添加對SharpICTCLAS命名空間的引用,然后創建WordSegment類的一個實例。如果需要攔截分詞過程中的事件的話,那么可以訂閱WordSegment類的OnSegmentEvent事件,上面的代碼用OnSegmentEventHandler方法訂閱了事件,并且輸出了所有分詞Stage的中間結果。
WordSegmentSample類中的 nKind 屬性是在NShortPath方法中用來決定初步切分時分成幾種結果。如果不特殊指明,nKind取1,用戶也可以自己定義一個1~10之間的整數(超過10,系統自動取10),數越大分詞準確率越高(可以參考張華平的論文),但系統執行效率會下降。
WordSegment類的InitWordSegment方法主要用來初始化各個詞典,用戶在這里需要提供詞典所在的目錄信息,系統自動到該目錄下搜索所有詞典。
有了WordSegmentSample類,主程序如下:
using System.Collections.Generic;
using System.Text;
using System.IO;
using SharpICTCLAS;
class Program
{
static void Main(string[] args)
{
List<WordResult[]> result;
string DictPath = Path.Combine(Environment.CurrentDirectory, "Data") +
Path.DirectorySeparatorChar;
Console.WriteLine("正在初始化字典庫,請稍候...");
WordSegmentSample sample = new WordSegmentSample(DictPath, 5);
result = sample.Segment(@"王曉平在1月份灤南大會上說的確實在理");
//---------- 輸出結果 ----------
//Console.WriteLine("\r\n==== 最終識別結果:\r\n");
//for (int i = 0; i < result.Count; i++)
//{
// for (int j = 0; j < result[i].Length; j++)
// Console.Write("{0} /{1} ", result[i][j].sWord, Utility.GetPOSString(result[i][j].nPOS));
// Console.WriteLine();
//}
Console.Write("按下回車鍵退出......");
Console.ReadLine();
}
}
內容比較簡單,此處就不再多說。由于我們在WordSegmentSample中訂閱了所有階段的事件,因此程序會輸出整個過程各個階段的中間結果,也包括最終分詞結果,因此上面代碼中我將輸出結果部分的代碼注釋起來了。如果沒有訂閱任何事件的話,可以使用注釋起來的這段代碼輸出分詞最終結果。
該程序的執行結果如下:
//==== 原始句子:
王曉平在1月份灤南大會上說的確實在理
//==== 原子切分:
始##始, 王, 曉, 平, 在, 1, 月, 份, 灤, 南, 大, 會, 上, 說, 的, 確, 實, 在, 理, 末##末,
//==== 生成 segGraph:
row: 0, col: 1, eWeight: 329805.00, nPOS: 1, sWord:始##始
row: 1, col: 2, eWeight: 218.00, nPOS: 0, sWord:王
row: 2, col: 3, eWeight: 9.00, nPOS: 0, sWord:曉
row: 3, col: 4, eWeight: 271.00, nPOS: 0, sWord:平
row: 4, col: 5, eWeight: 78484.00, nPOS: 0, sWord:在
row: 5, col: 6, eWeight: 0.00, nPOS: -27904, sWord:未##數
row: 6, col: 7, eWeight: 1900.00, nPOS: 0, sWord:月
row: 6, col: 8, eWeight: 11.00, nPOS: 28160, sWord:月份
row: 7, col: 8, eWeight: 1234.00, nPOS: 0, sWord:份
row: 8, col: 9, eWeight: 1.00, nPOS: 27136, sWord:灤
row: 9, col: 10, eWeight: 813.00, nPOS: 0, sWord:南
row: 10, col: 11, eWeight: 14536.00, nPOS: 0, sWord:大
row: 10, col: 12, eWeight: 1333.00, nPOS: 28160, sWord:大會
row: 11, col: 12, eWeight: 6136.00, nPOS: 0, sWord:會
row: 11, col: 13, eWeight: 469.00, nPOS: 0, sWord:會上
row: 12, col: 13, eWeight: 23706.00, nPOS: 0, sWord:上
row: 13, col: 14, eWeight: 17649.00, nPOS: 0, sWord:說
row: 14, col: 15, eWeight: 358156.00, nPOS: 0, sWord:的
row: 14, col: 16, eWeight: 210.00, nPOS: 25600, sWord:的確
row: 15, col: 16, eWeight: 181.00, nPOS: 0, sWord:確
row: 15, col: 17, eWeight: 361.00, nPOS: 0, sWord:確實
row: 16, col: 17, eWeight: 357.00, nPOS: 0, sWord:實
row: 16, col: 18, eWeight: 295.00, nPOS: 0, sWord:實在
row: 17, col: 18, eWeight: 78484.00, nPOS: 0, sWord:在
row: 17, col: 19, eWeight: 3.00, nPOS: 24832, sWord:在理
row: 18, col: 19, eWeight: 129.00, nPOS: 0, sWord:理
row: 19, col: 20, eWeight:2079997.00, nPOS: 4, sWord:末##末
//==== 生成 biSegGraph:
row: 0, col: 1, eWeight: 4.18, nPOS: 1, sWord:始##始@王
row: 1, col: 2, eWeight: 11.46, nPOS: 0, sWord:王@曉
row: 2, col: 3, eWeight: 13.93, nPOS: 0, sWord:曉@平
row: 3, col: 4, eWeight: 11.25, nPOS: 0, sWord:平@在
row: 4, col: 5, eWeight: 3.74, nPOS: 0, sWord:在@未##數
row: 5, col: 6, eWeight: -27898.79, nPOS: -27904, sWord:未##數@月
row: 5, col: 7, eWeight: -27898.75, nPOS: -27904, sWord:未##數@月份
row: 6, col: 8, eWeight: 9.33, nPOS: 0, sWord:月@份
row: 7, col: 9, eWeight: 13.83, nPOS: 28160, sWord:月份@灤
row: 8, col: 9, eWeight: 9.76, nPOS: 0, sWord:份@灤
row: 9, col: 10, eWeight: 14.46, nPOS: 27136, sWord:灤@南
row: 10, col: 11, eWeight: 5.19, nPOS: 0, sWord:南@大
row: 10, col: 12, eWeight: 10.17, nPOS: 0, sWord:南@大會
row: 11, col: 13, eWeight: 7.30, nPOS: 0, sWord:大@會
row: 11, col: 14, eWeight: 7.30, nPOS: 0, sWord:大@會上
row: 12, col: 15, eWeight: 2.11, nPOS: 28160, sWord:大會@上
row: 13, col: 15, eWeight: 8.16, nPOS: 0, sWord:會@上
row: 14, col: 16, eWeight: 3.42, nPOS: 0, sWord:會上@說
row: 15, col: 16, eWeight: 4.07, nPOS: 0, sWord:上@說
row: 16, col: 17, eWeight: 4.05, nPOS: 0, sWord:說@的
row: 16, col: 18, eWeight: 7.11, nPOS: 0, sWord:說@的確
row: 17, col: 19, eWeight: 4.10, nPOS: 0, sWord:的@確
row: 17, col: 20, eWeight: 4.10, nPOS: 0, sWord:的@確實
row: 18, col: 21, eWeight: 11.49, nPOS: 25600, sWord:的確@實
row: 19, col: 21, eWeight: 11.63, nPOS: 0, sWord:確@實
row: 18, col: 22, eWeight: 11.49, nPOS: 25600, sWord:的確@實在
row: 19, col: 22, eWeight: 11.63, nPOS: 0, sWord:確@實在
row: 20, col: 23, eWeight: 3.92, nPOS: 0, sWord:確實@在
row: 21, col: 23, eWeight: 10.98, nPOS: 0, sWord:實@在
row: 20, col: 24, eWeight: 10.97, nPOS: 0, sWord:確實@在理
row: 21, col: 24, eWeight: 10.98, nPOS: 0, sWord:實@在理
row: 22, col: 25, eWeight: 11.17, nPOS: 0, sWord:實在@理
row: 23, col: 25, eWeight: 5.62, nPOS: 0, sWord:在@理
row: 24, col: 26, eWeight: 14.30, nPOS: 24832, sWord:在理@末##末
row: 25, col: 26, eWeight: 11.95, nPOS: 0, sWord:理@末##末
//==== NShortPath 初步切分的到的 N 個結果:
始##始, 王, 曉, 平, 在, 1, 月份, 灤, 南, 大, 會上, 說, 的, 確實, 在, 理, 末##末,
始##始, 王, 曉, 平, 在, 1, 月份, 灤, 南, 大會, 上, 說, 的, 確實, 在, 理, 末##末,
始##始, 王, 曉, 平, 在, 1, 月份, 灤, 南, 大, 會上, 說, 的, 確實, 在理, 末##末,
始##始, 王, 曉, 平, 在, 1, 月份, 灤, 南, 大會, 上, 說, 的, 確實, 在理, 末##末,
始##始, 王, 曉, 平, 在, 1, 月, 份, 灤, 南, 大, 會上, 說, 的, 確實, 在, 理, 末##末,
//==== 經過數字、日期合并等策略處理后的 N 個結果:
始##始, 王, 曉, 平, 在, 1月份, 灤, 南, 大, 會上, 說, 的, 確實, 在, 理, 末##末,
始##始, 王, 曉, 平, 在, 1月份, 灤, 南, 大會, 上, 說, 的, 確實, 在, 理, 末##末,
始##始, 王, 曉, 平, 在, 1月份, 灤, 南, 大, 會上, 說, 的, 確實, 在理, 末##末,
始##始, 王, 曉, 平, 在, 1月份, 灤, 南, 大會, 上, 說, 的, 確實, 在理, 末##末,
始##始, 王, 曉, 平, 在, 1月, 份, 灤, 南, 大, 會上, 說, 的, 確實, 在, 理, 末##末,
//==== 加入對姓名、翻譯人名以及地名的識別:
row: 0, col: 1, eWeight: 329805.00, nPOS: 1, sWord:始##始
row: 1, col: 2, eWeight: 218.00, nPOS: 0, sWord:王
row: 1, col: 4, eWeight: 10.86, nPOS: -28274, sWord:未##人
row: 2, col: 3, eWeight: 9.00, nPOS: 0, sWord:曉
row: 2, col: 4, eWeight: 13.27, nPOS: -28274, sWord:未##人
row: 3, col: 4, eWeight: 271.00, nPOS: 0, sWord:平
row: 4, col: 5, eWeight: 78484.00, nPOS: 0, sWord:在
row: 5, col: 7, eWeight: 0.00, nPOS: -29696, sWord:未##時
row: 5, col: 8, eWeight: 0.00, nPOS: -29696, sWord:未##時
row: 7, col: 8, eWeight: 1234.00, nPOS: 0, sWord:份
row: 8, col: 9, eWeight: 1.00, nPOS: 27136, sWord:灤
row: 8, col: 10, eWeight: 20.37, nPOS: -28275, sWord:未##地
row: 9, col: 10, eWeight: 813.00, nPOS: 0, sWord:南
row: 10, col: 11, eWeight: 14536.00, nPOS: 0, sWord:大
row: 10, col: 12, eWeight: 1333.00, nPOS: 28160, sWord:大會
row: 11, col: 13, eWeight: 469.00, nPOS: 0, sWord:會上
row: 12, col: 13, eWeight: 23706.00, nPOS: -27904, sWord:未##數
row: 13, col: 14, eWeight: 17649.00, nPOS: 0, sWord:說
row: 14, col: 15, eWeight: 358156.00, nPOS: 0, sWord:的
row: 15, col: 17, eWeight: 361.00, nPOS: 0, sWord:確實
row: 17, col: 18, eWeight: 78484.00, nPOS: 0, sWord:在
row: 17, col: 19, eWeight: 3.00, nPOS: 24832, sWord:在理
row: 18, col: 19, eWeight: 129.00, nPOS: 0, sWord:理
row: 19, col: 20, eWeight:2079997.00, nPOS: 4, sWord:末##末
//==== 生成 biSegGraph:
row: 0, col: 1, eWeight: 4.18, nPOS: 1, sWord:始##始@王
row: 0, col: 2, eWeight: 2.88, nPOS: 1, sWord:始##始@未##人
row: 1, col: 3, eWeight: 11.46, nPOS: 0, sWord:王@曉
row: 1, col: 4, eWeight: 3.88, nPOS: 0, sWord:王@未##人
row: 3, col: 5, eWeight: 13.93, nPOS: 0, sWord:曉@平
row: 2, col: 6, eWeight: -28270.43, nPOS: -28274, sWord:未##人@在
row: 4, col: 6, eWeight: -28270.43, nPOS: -28274, sWord:未##人@在
row: 5, col: 6, eWeight: 11.25, nPOS: 0, sWord:平@在
row: 6, col: 7, eWeight: 4.01, nPOS: 0, sWord:在@未##時
row: 6, col: 8, eWeight: 4.01, nPOS: 0, sWord:在@未##時
row: 7, col: 9, eWeight: -29690.16, nPOS: -29696, sWord:未##時@份
row: 8, col: 10, eWeight: -29690.16, nPOS: -29696, sWord:未##時@灤
row: 9, col: 10, eWeight: 9.76, nPOS: 0, sWord:份@灤
row: 8, col: 11, eWeight: -29690.17, nPOS: -29696, sWord:未##時@未##地
row: 9, col: 11, eWeight: 9.76, nPOS: 0, sWord:份@未##地
row: 10, col: 12, eWeight: 14.46, nPOS: 27136, sWord:灤@南
row: 11, col: 13, eWeight: -28267.95, nPOS: -28275, sWord:未##地@大
row: 12, col: 13, eWeight: 5.19, nPOS: 0, sWord:南@大
row: 11, col: 14, eWeight: -28266.85, nPOS: -28275, sWord:未##地@大會
row: 12, col: 14, eWeight: 10.17, nPOS: 0, sWord:南@大會
row: 13, col: 15, eWeight: 7.30, nPOS: 0, sWord:大@會上
row: 14, col: 16, eWeight: 4.81, nPOS: 28160, sWord:大會@未##數
row: 15, col: 17, eWeight: 3.42, nPOS: 0, sWord:會上@說
row: 16, col: 17, eWeight: -27898.75, nPOS: -27904, sWord:未##數@說
row: 17, col: 18, eWeight: 4.05, nPOS: 0, sWord:說@的
row: 18, col: 19, eWeight: 4.10, nPOS: 0, sWord:的@確實
row: 19, col: 20, eWeight: 3.92, nPOS: 0, sWord:確實@在
row: 19, col: 21, eWeight: 10.97, nPOS: 0, sWord:確實@在理
row: 20, col: 22, eWeight: 5.62, nPOS: 0, sWord:在@理
row: 21, col: 23, eWeight: 14.30, nPOS: 24832, sWord:在理@末##末
row: 22, col: 23, eWeight: 11.95, nPOS: 0, sWord:理@末##末
//==== 最終識別結果:
王曉平 /nr 在 /p 1月份 /t 灤南 /ns 大會 /n 上 /v 說 /v 的 /uj 確實 /ad 在 /p 理 /n
非常高興在這最后一篇文章寫完之時得到了張華平老師的授權。我會盡可能快的將SharpICTCLAS源文件放上來供大家測試使用的。
來源:http://www.cnblogs.com/zhenyulu/category/85598.html