・このコードの短期的な目的は、下記です。
「文章をおのおの形態素に分け、各文章内での各形態素の出現回数をカウントする」
試しに、質問のコードの最後に
py
1print(vocabulary)
2print(bow)
という2行を追加して実行してみましょう。
すると、下記のような結果が出力されます。
txt
1{'私': 0, 'は': 1, 'の': 2, 'こと': 3, 'が': 4, '好き': 5, 'な': 6, 'あなた': 7, 'です': 8,
2'ラーメン': 9, '富士山': 10, '日本一': 11, '高い': 12, '山': 13}
3
4 [[2, 1, 1, 1, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0],
5[1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0],
6[0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1]]
1つ目(vocabulary)は、「形態素(token)がキー、連番が値」となっている辞書です。
2つ目(bow)は、長さ14の配列が3つ格納されています。
実は2つ目(bow)は、1つ目の token の、3つの文章における出現回数を表しています。
整理すると下記のような表になります。
token(キー) | 私 | は | の | こと | が | 好き | な | あなた | です | ラーメン | 富士山 | 日本一 | 高い | 山 |
---|
インデックス(値) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
文章1 | 2 | 1 | 1 | 1 | 2 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
文章2 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
文章3 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
・2行目は、各 token に対して、(数字がかぶらないように)割り振った一意のID番号のようなものです。
・3行目以降は、各文章における各 token の出現回数を表しています。
質問文のコードは、言ってしまえば結局上のような表を作る処理に過ぎません。
ではなぜこのように整理するか=長期的な目的はなにかというと、
・各文章を特徴づけている単語を抽出し、文書の類似度を比較可能にする
といったようなことです。(TF-IDF等を利用)
この目的のため、文書の集合/文書/形態素を、一定のルールでもって、計算しやすいように数値化し整理している、ということです(ベクトル化)
上の表とコードをよく見比べると、コードの意味が分かってくると思います。
(1)「token:vocablaryに登録した順」、となる辞書として代入していく意味でしょうか?
コードのこの部分ですね
if token not in vocabulary:
vocabulary[token] = len(vocabulary)#(1)
見てお分かりの通り、token(形態素)をキーとする辞書として vocabulary を作成しています。
新しい token が見つかる度、その時点の vocablary の長さを、token の値として設定しています。
キーが追加されるたび、辞書 vocablary の長さは1ずつ増えていきます。
つまりこの部分は、tokenに一意のindexを付番しているということです。
(2)コード全体の意味がわかりません。また [0] * n_vocabの意味が特に分かりません。
#2の部分は、内包表記を使用した、2次元配列の初期化処理です。
参照:
https://qiita.com/oyoshi0022/items/7475951f465d20ad4970#%E8%A7%A3%E6%B1%BA%E6%B3%95
#単語の出現回数をカウントするためのループ
bow = [[0] * n_vocab for i in range(len(tokenized_texts))] #(2)
for i,tokenized_text in enumerate(tokenized_texts):
for token in tokenized_text:
index = vocabulary[token]
bow[i][index] += 1
[[0] * n_vocab for i in range(len(tokenized_texts))]
を分解すると
・len(tokenized_texts)) は 文章の数=文章は全部で3つなので「3」
・n_vocab は、前述のコードを見ると、tokenを格納完了後の辞書vocabulary内のキー(token)の数=「14個」
これによって
bow = [[0] * 14 for i in range(3)]
となります。
これにより、14列×3行の2次元配列が作られます。(冒頭の[0]は初期化する値ですので、中身がゼロで初期化された2次元配列になっています)
作成直後のbow(初期化された2次元配列):
インデックス | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|
[0] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
[1] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
[2] | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
あとは、直後の
for i,tokenized_text in enumerate(tokenized_texts):
for token in tokenized_text:
index = vocabulary[token]
bow[i][index] += 1
の部分で、各文章ごとに token の出現回数をカウントし、2次元配列に格納しています。
処理後のbow:
インデックス | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---|
[0] | 2 | 1 | 1 | 1 | 2 | 2 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
[1] | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
[2] | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
※インデックスとtokenの関係は、辞書vocabularyによって保持されています。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2022/04/13 12:52