Skip to content
This repository has been archived by the owner on Dec 24, 2022. It is now read-only.

Latest commit

 

History

History
executable file
·
188 lines (122 loc) · 6.86 KB

week03-ngram.md

File metadata and controls

executable file
·
188 lines (122 loc) · 6.86 KB

N-gram

N-gramとは

N-gramは、隣同士の文字や語、つまり文字や語の「連なり」を単位とした分析で、連なりの頻度分布を測定したり利用したりします。―Pythonによるテキストマイニング入門p132

文字や語の連なりごとに分析することで、文字や語がよくつながる例を発見できる。

  • 名詞Aと名詞Bはよくセットで使われる
  • 名詞Cには、「は」や「が」が来ることが多い。なら、名詞Cは主語として使われることが多いのでは?と推測できる

といったの活用がある

単語を単位としたN-gram

単語を単位として連なりを求めるには、文字と違い、まず単語別に分解する必要がある。そのためには、Mecab等を使い形態素解析を施す。

データには自然言語処理 - Wikipedia の冒頭を使用する。

不要な文字を削除したテキストファイルを用意した。

ファイルはこちら

import MeCab

# -Owakati:分かち書きのみの形式
m = MeCab.Tagger("-Owakati")

# ファイルの文字コードに合わせる
with open('../datasets/nlp-wikipedia-text.txt', 'r', encoding='utf-8') as f:
    data = f.read()

# コーパス
data = m.parse(data).split(' ')
# print('wakati=%s' % data)

# 3-gram
for i, d in enumerate(data[1:-2]):
    # tuple-data
    t = (data[i], d, data[i+2])
    print(f"{i}, {t}")

実行結果

0, ('自然', '言語', '処理')
1, ('言語', '処理', 'し')
2, ('処理', 'し', 'ぜん')
3, ('し', 'ぜん', 'げん')
4, ('ぜん', 'げん', 'ごしょ')
5, ('げん', 'ごしょ', 'り')
6, ('ごしょ', 'り', '人間')
7, ('り', '人間', 'が')
8, ('人間', 'が', '日常')
9, ('が', '日常', '的')
10, ('日常', '的', 'に')
11, ('的', 'に', '使っ')
12, ('に', '使っ', 'て')
13, ('使っ', 'て', 'いる')
14, ('て', 'いる', '自然')
15, ('いる', '自然', '言語')
16, ('自然', '言語', 'を')
17, ('言語', 'を', 'コンピュータ')
18, ('を', 'コンピュータ', 'に')
19, ('コンピュータ', 'に', '処理')
20, ('に', '処理', 'さ')
~~以下省略~~

Exercise

Ex1

「文字」を単位とした3-gramを作成せよ。

データには、nlp-wikipedia-textを使用してもよい。

外部のデータや新聞記事などを利用してもよいが、アップロードには十分に注意すること。

Ex2

語を単位とした2-gramを作成しなさい。

ただし、以下の例のように頻度(重複した2-gramの回数)とともに出力すること。

(('自然', '言語'), 5)

なお、本問題以降は以下のデータを使用すること。

本データは、青空文庫に保存されている「羅生門/芥川龍之介」を予め、演習用に加工したものである。

ngram-ex-input.txt

Hint2-1

リストに2-gramデータを格納することが最優先事項。上の3-gramからどこを削除すればよいだろうか?

Hint2-2

list_a.count(target_b)

とすることでリストAに対するターゲットBがいくつあるのか数えることができる。すなわち、重複した回数を求めることができる。

出力例を見るに、2-gramと同じ階層に頻度を格納している。

Hint2-3

ただ頻度とともに2-gramを出力すると、同じ2-gram、頻度が複数出力されてしまう。

Pythonには、「集合(set)」という重複を許さないデータ構造が存在する。

大いに活用して、1つ1つ削除することのないようにしよう。

Ex3

Ex2の結果を利用して簡単な文章生成を行いたい。

Ex2にて出現頻度を求めた。これは、ある語に対する次に来る語の確率と考えることができる。(語A, 語B)が3回、(語A, 語C)が2回、(語A, 語D)が1回出現したとすると、語Aに対する語B,C,Dはそれぞれ、1/2, 1/3, 1/6の確率といえる。求めた確率のうち、高いものを選んでいくと、元の文章に近いものが生成できるのでは?という考え方に基づいている。

初期データを「下人」として文章を生成しなさい。句点が見つかる、もしくは30語以上つながることができれば、終了とする。

ここでは、つなげる語として最も頻出している語を選定する。

Hint3-1

重要なポイントを除いたソースコードを提示する。

XXX, YYY, ZZZをコメントを頼りに埋めよ。

やり方は複数、存在する。以下のコードと異なっていようとも大いに結構。

また、本コードは「つながる言葉が見つからない」という例外が起きないことを前提としている。

word = '下人'
output = [word]
for i in range(30):
    # 語Aを持つ2-gramのみを列挙する
    byrams = XXX
    # 頻度の最も高い2-gramを取得する
    bygram = YYY
    output.append(bygram[1]) # 2-gramのうち、「次の言葉」を追加する
    word = bygram[1] # 次の探索対象とする
    # 句点「。」であれば、forから脱出する
    if ZZZ:
        break
# 1文つなげて出力する
print(''.join(output))

Hint3-2

上のソースコードの例には、語Aを持つと書いた。

詳しくは2-gramのうち、前方(調査対象)に持つものを列挙する必要がある。

また、filterを使うと、2-gramリストのうち条件に合ったもののみを列挙できる。

無名関数lambdaと合わせて、使い方をもう一度、勉強しよう。

Pythonの無名関数(ラムダ式、lambda)の使い方

Hint3-3

「最も大きい」という条件には、maxを使う。

maxには、keyというオプションが存在する。これを利用して、頻度に基づくようにしよう。

keyには、関数を用いる。無名関数lambdaを活用しよう。

得られたものは、2-gramではなく、(2-gram, 頻度)のタプルである。注意しよう。

おまけ

つなげる語を選定するルールが固定されては面白くない。

そこで、頻度×乱数値(0.1~2.0)を計算し、最も値の大きい語を選定するルールに変更せよ。

また、つなげることができない場合は、その時点で句点を返し、終了できるようにしなさい。

TOPへ戻る