N-gramを実装と共に解説します【自然言語処理】

n-gram

自然言語処理のタスクなどで用いられるN-gram。基礎的な内容なので、理解・実装をできるようになっておきたいところです。この記事ではこのN-gramの解説とPythonで実装する方法を紹介します。

N-gramとは

何らかの列を連続するN個の組の列にした表現をN-gramと呼びます。ここで、Nは自然数です。とくにNが1の場合をuni-gram(ユニグラム)、2の場合をbi-gram(バイグラム)、3の場合をtri-gram(トライグラム)と呼びます。

n-gram-explanation

何らかの列と言いましたが、「何らか」は本当に何でも良いです。英単語のような文字列でも良いですし、アルファベット一文字でも良いです。

目で見たほうがわかりやすいと思うので、いくつか例を挙げてみます。

「“I”, “have”, “an”, “apple”」という4つの英単語から成る「文字列の列」があるとします。この列のbi-gramは「(“I”, “have”), (“have”, “an”), (“an”, “apple”)」となります。また、tri-gramは「(“I”, “have”, “an”), (“have”, “an”, “apple”)」です。

同様に、「a, b, c, d, e」という5つのアルファベットから成る「文字の列」のtri-gramは「(a, b, c), (b, c, d), (c, d, e)」になります。

Pythonで一から実装

N-gram表現を得るような関数をPythonを用いて実装してみます。他の言語で実装する場合もやることは同じです。

def ngrams(seq, n):
    return [seq[i:i+n] for i in range(len(seq)-n+1)]

sent = ["I", "have", "an", "apple"]

print(ngrams(sent, 2))

# [['I', 'have'], ['have', 'an'], ['an', 'apple']]

関数ngramsは対象となる入力列とNの値を引数として取り、入力列のN-gram表現がリストのリストとして返されます。

関数内部は、リスト内包表記を利用して記述されています。リストの先頭から順番にインデックスiを更新していき、i番目からi+N-1番目までのN個の要素を一つのリストとして切り出します。このまとめたリストが出力の各要素になります。

ここで、リストの切り出しにはPythonのスライスを利用しています。Pythonのスライスは半開区間で指定するので注意してください。つまり、seq[i:i+n]はリストseqi番目からi+N-1番目までの要素を切り出したリストを返します。

返り値の型をタプルのリストにしたい場合は、以下のように変更すれば実現できます。

def ngrams(seq, n):
    return [tuple(seq[i:i+n]) for i in range(len(seq)-n+1)]

ライブラリでサクッと実装(NLTK)

とにかくすぐにN-gramを手に入れたいという方はライブラリを使用するのが良いです。ただ、N-gram だけの実装で良いなら自分で書いてしまった方が早いと思います。

今回はNLTK(Natural Language Toolkit)という有名なライブラリを使用する方法を紹介します。NLTKは以下のコマンドでインストール可能です。

$ pip install nltk

次のようにimportするだけですぐに使うことができます。使い方は自作の例で紹介したコードと同様です。返り値がgenerator objectになっているのでその点だけ注意してください。

from nltk import ngrams

sent = ["I", "have", "an", "apple"]

print(list(ngrams(sent, 2)))
# [('I', 'have'), ('have', 'an'), ('an', 'apple')]