Chapter 05

文区切り / 形態素フィルター

ikawaha
ikawaha
2020.12.14に更新

形態素解析にかける前に文の単位に区切ったり、形態素解析をおこなった後、品詞情報によって形態素をふるいにかけたりする作業が必要ななることがあります。kagome ではそのような場合のフィルターをいくつか用意しています。

文に区切る

filter.ScanSentences を Go の Scanner#Split にセットすると、Reader から文区切りをした文字列を取り出すことが出来ます。文区切りのデフォルトの設定は次のようになっていますが、変更することも可能です。

オプション デフォルト値 説明
Delim , , , !, , ? 文区切りの目印となる記号
Follower ., , , , ), , , }, , 文区切りの後に存在する場合につづけて同じ文に入れる記号
SkipWhiteSpace true 途中にある空白を除去するかどうか
DoubleLineFeedSplit true 改行だけが2回続けて現れる場合は文区切りと見なすか
MaxRuleLen 128 最大文長となる文字数
	sampleText := ` 人魚は、南の方の海にばかり棲んでいるのではあ
                         りません。北の海にも棲んでいたのであります。
                          北方の海うみの色は、青うございました。ある
                         とき、岩の上に、女の人魚があがって、あたりの景
                         色をながめながら休んでいました。

                         小川未明作 赤い蝋燭と人魚より`

	scanner := bufio.NewScanner(strings.NewReader(sampleText))
	scanner.Split(filter.ScanSentences)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		panic(err)
	}
	// Output:
	// 人魚は、南の方の海にばかり棲んでいるのではありません。
	// 北の海にも棲んでいたのであります。
	// 北方の海うみの色は、青うございました。
	// あるとき、岩の上に、女の人魚があがって、あたりの景色をながめながら休んでいました。
	// 小川未明作赤い蝋燭と人魚より

形態素の素性情報でふるいにかける

検索で形態素解析を利用する場合など、形態素解析した後、助詞などの形態素を stop word として落としたいことなどがあります。kagome ではそのような場合に形態素用のフィルターを用意し、フィルターにマッチした形態素を Keep するか Drop するかしてふるいにかける仕組みを用意しています。

形態素表記でふるいにかける

WordFilter に形態素表記を指定しておくと、登録してある表記と一致する形態素をふるいにかけることができます。フィルターに指定された形態素列はフィルターで変更されることに気をつけてください。

	t, err := tokenizer.New(d, tokenizer.OmitBosEos())
	if err != nil {
		panic(err)
	}
	stopWords := filter.NewWordFilter([]string{"私", "は", "が", "の", "。"})
	tokens := t.Tokenize("私の猫の名前はアプロです。")
	stopWords.Drop(&tokens)
	for _, v := range tokens {
		fmt.Println(v.Surface)
	}
	// Output:
	// 猫
	// 名前
	// アプロ
	// です

品詞情報でふるいにかける

品詞は4つ組で表現されています。以下のサンプルでは、第1要素が 名詞 であり、第3要素が 人名 であるものを指定します。このとき、第2要素はワイルドカード(filter.Any)とします。また、第1要素が 形容詞 である形態素も指定して Keep します。

	t, err := tokenizer.New(d, tokenizer.OmitBosEos())
	if err != nil {
		panic(err)
	}
	posFilter := filter.NewPOSFilter([]filter.POS{
		{"名詞", filter.Any, "人名"},
		{"形容詞"},
	}...)
	tokens := t.Tokenize("赤い蝋燭と人魚。小川未明")
	posFilter.Keep(&tokens)
	for _, v := range tokens {
		fmt.Println(v.Surface, v.POS())
	}
	// Output:
	// 赤い [形容詞 自立 * *]
	// 小川 [名詞 固有名詞 人名 姓]
	// 未明 [名詞 固有名詞 人名 名]

フィルターをかけない解析結果は次のようになります:

$ echo 赤い蝋燭と人魚。小川未明 | kagome
赤い	形容詞,自立,*,*,形容詞・アウオ段,基本形,赤い,アカイ,アカイ
蝋燭	名詞,一般,*,*,*,*,蝋燭,ロウソク,ローソク
と	助詞,並立助詞,*,*,*,*,と,ト,ト
人魚	名詞,一般,*,*,*,*,人魚,ニンギョ,ニンギョ
。	記号,句点,*,*,*,*,。,。,。
小川	名詞,固有名詞,人名,姓,*,*,小川,オガワ,オガワ
未明	名詞,固有名詞,人名,名,*,*,未明,ミメイ,ミメイ
EOS

同一のフィルターに、大分類で指定した品詞 {"名詞"} と、より詳細な品詞 {"名詞", "一般"} が指定されている場合には、{"名詞"} の条件がより強く働いて、{"名詞", "一般"} は無視されてしまうことに気をつけてください。

素性全般にフィルターをかける

filter.NewFeatureFilter が利用できます。