Edited at

N-gramの作り方


はじめに

自然言語処理100本ノックをやるに当たって、N-gramの壁にぶち当たる人もいるかと思うので、簡単にまとめます。


N-gramとは

コトバンクでは以下の様になってました。


任意の文字列や文書を連続したn個の文字で分割するテキスト分割方法.特に,nが1の場合をユニグラム(uni-gram),2の場合をバイグラム(bi-gram),3の場合をトライグラム(tri-gram)と呼ぶ.


要するにテキストの分割方法なんですが、いまいちピンと来ない人もいるかと思います。


文字単位のN-gramの例

例で考えるとわかりやすいです。

例えば、今日はいい天気ですね。という文章をN-gramにしてみます。


N=1 unigram

1文字取り出すだけなので、

'今', '日', 'は', 'い', 'い', '天', '気', 'で', 'す', 'ね', '。'

となります。


N=2 bigram

bigramは2文字単位で取り出すので、

'今日', '日は', 'はい', 'いい', 'い天', '天気', '気で', 'です', 'すね', 'ね。'

となります。ここで、なるほど!となりました。

基準となる文字を1文字ずつずらしながら2文字で分割するんですね。


N=3 trigram

ということは、trigramは3文字単位なので、

'今日は', '日はい', 'はいい', 'いい天', 'い天気', '天気で', '気です', 'ですね', 'すね。'

となるわけです。

(最後は、3文字以上取れなくなったらそこで終わるのか。)


単語単位のN-gramの例

単語単位だと形態素解析して、1単語単位でN-gramとして区切るので、


unigram

'今日', 'は', 'いい', '天気', 'です', 'ね', '。'


bigram

'今日は', 'はいい', 'いい天気', '天気です', 'ですね', 'ね。'


trigram

'今日はいい', 'はいい天気', 'いい天気です', '天気ですね', 'ですね。'

となると。


長所と短所


長所

辞書が必要ない


  • 形態素解析には辞書が必要だが、辞書がなくても単語の切り出しができる。(文字単位の場合のみ)


短所

検索ノイズ


  • 検索結果に誤りが出てしまう


    • 辞書に載っている単語で区切るわけではないので、それを使用して検索しようとするとどうしてもノイズが入ってしまう




自然言語処理100本ノック 04.n-gramのソースコード

def n_gram(target, n):

# 基準を1文字(単語)ずつ ずらしながらn文字分抜き出す
return [ target[idx:idx + n] for idx in range(len(target) - n + 1)]

target = "I am an NLPer"
print(n_gram(target, 1))
# > ['I', ' ', 'a', 'm', ' ', 'a', 'n', ' ', 'N', 'L', 'P', 'e', 'r']
print(n_gram(target, 2))
# > ['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
print(n_gram(target, 3))
# > ['I a', ' am', 'am ', 'm a', ' an', 'an ', 'n N', ' NL', 'NLP', 'LPe', 'Per']

words = target.split(' ')
print(n_gram(words, 1))
# > [['I'], ['am'], ['an'], ['NLPer']]
print(n_gram(words, 2))
# > [['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
print(n_gram(words, 3))
# > [['I', 'am', 'an'], ['am', 'an', 'NLPer']]


まとめ

区切り方がわかればこっちのものですね。とりあえずコードには落とせるようになったのではないでしょうか。


参考

N-gramについて書いてみる