自然言語とは、人間が通常会話で使用する言語(反対語:人工言語=プログラミング言語など)。自然言語処理とはそのような言語を処理する技術。
spaCyは多言語対応の自然言語処理ライブラリ。GiNZAはspaCyから利用可能な日本語の自然言語処理。リクルートと国立国語研究所が共同で開発。
pip install ja-ginza
spacy.Loadでnlpオブジェクト(Languageクラス)を生成し、それに文章を与えることで、Docクラスを生成する。
import spacy nlp = spacy.load('ja_ginza') text = '今日は晴れです。明日は雨ですか? いいえ曇りです。' doc = nlp(text)
docクラスのsentsは文章のリストとなる。以下は一文ずつ区切って表示される。
for s in doc.sents: print(s.text)
上の例の場合、以下のように表示される。
今日は晴れです。 明日は雨ですか? いいえ曇りです。
Docクラスをfor文の対象にすることでtoken(単語)を得ることが出来る。
text = '藤井聡太は将棋が好きです。' doc = nlp(text) for token in doc: print(token.text, token.pos_, token.tag_) #単語、品詞(英語)、品詞(日本語)
ADJ | 形容詞(adjective) |
ADP | 設置詞(adposition) |
ADV | 副詞(adverb) |
AUX | 助動詞(auxiliary) |
CONJ | 接続詞(conjunction) |
CCONJ | 等位接続詞(coordinating conjunction) |
DET | 限定詞(determiner) |
INTJ | 間投詞、感嘆詞(interjection) |
NOUN | 名詞(noun) |
NUM | 数詞(numeral) |
PART | 助詞(particle) |
PRON | 代名詞(pronoun) |
PROPN | 固有名詞(proper noun) |
PUNCT | 句読点(punctuation) |
SCONJ | 従属接続詞(subordinating conjunction) |
SYM | シンボル(symbol) |
VERB | 動詞(verb) |
X | 他(other) |
SPACE | 空白(space) |
単語の品詞とその依存関係を表示する。
from spacy import displacy displacy.render(doc)
エンティティ=現実世界のオブジェクト。以下でテキスト中に埋め込んで可視化。
from spacy import displacy displacy.render(doc, style="ent")
エンティティを一つ一つ表示するにはdoc.entsをfor文で取り出す。
for ent in doc.ents: print(ent.text, type(ent))
名詞句を一つ一つ表示するにはdoc.noun_chunksをfor文で取り出す。
doc2 = nlp('藤井聡太は偉大な棋士です。') for chunk in doc2.noun_chunks: print(chunk.text, type(chunk))
文章の意味を数値化するのがベクトル化。ベクトルとは数値の配列。これを比較すると似ているかどうかが分かる。
ベクトル化の意味を学習するために、Bag of Words (BoW)によるベクトル化を実装してみる。これは単語の出現数を配列化したものである。
import numpy as np # 入力テキストのサンプル(単語を区切りやすいように空白区切り) documents = [ "熊本 の ゆるキャラ は くまモン", "くまモン は かわいい ゆるキャラ", "熊本城 は 熊本 の 城" ] # 3つの文書から全単語を抽出 vocabulary = [] for doc in documents: words = doc.split() # 各ドキュメントを単語に分割 for word in words: if word not in vocabulary: vocabulary.append(word) # 各文書をベクトル化 def vectorize(doc): words = doc.split() vector = np.zeros(len(vocabulary)) # 全単語の数だけ0の要素を持つ配列 for word in words: if word in vocabulary: # 単語がある index = vocabulary.index(word) vector[index] += 1 # カウントアップ return vector # 全文書をベクトル化して表示 for doc in documents: print(f"テキスト: {doc}") print(f"ベクトル: {vectorize(doc)}\n")
ベクトルを比較し、コサイン類似度を求める。コサイン類似度は-1~1までの値。1に近いほど文章が似ている。
# 全文書をベクトル化し、配列に入れる。 vectors = np.array([vectorize(doc) for doc in documents]) # コサイン類似度を計算する関数 (numpyを使用) def cosine_similarity(vec1, vec2): if np.linalg.norm(vec1) == 0 or np.linalg.norm(vec2) == 0: return 0.0 return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2)) # 各ドキュメント間のコサイン類似度を計算 for i in range(len(vectors)): for j in range(i + 1, len(vectors)): similarity = cosine_similarity(vectors[i], vectors[j]) print(f"コサイン類似度 {i+1} と {j+1}: {similarity:.4f}")
doc1 = nlp('ロアッソ熊本はJリーグに所属しています') print(doc1.vector)
Docクラスのsimilarityにて、文章をベクトル化した場合のコサイン類似度を求める。コサイン類似度は-1~1までの値。1に近いほど文章が似ている。
doc1 = nlp('ロアッソ熊本はJリーグに所属しています') doc2 = nlp('ロアッソ熊本はサッカーチームです') doc3 = nlp('熊本城は加藤清正が築城しました') print(doc1.similarity(doc2)) print(doc1.similarity(doc3)) print(doc2.similarity(doc3))
nlplotは自然言語処理において、さまざまな可視化ができるライブラリ。
pip install nlplot nbformat plotly
※対象となるファイルを用意。下の例では「走れメロス」を青空文庫よりダウンロード。
まず、ファイルを1行ずつ読み込み、行毎に単語を分割する。それをリストに入れる。
import spacy # ファイル読み込み with open("hashire_merosu.txt", 'r', encoding='utf-8') as file: lines = file.readlines() # 単語の取得 nlp = spacy.load("ja_ginza") include_pos = ('NOUN', 'VERB', 'ADJ') # 名刺、動詞、副詞を対象(固有名詞対象は PROPN も追加) stopwords = ('する', 'ある', 'ない', 'いう', 'もの', 'こと', 'よう', 'なる', 'ほう', 'いる','る','\n\n','\n ') # 対象外 wordlist = [] for text in lines: doc = nlp(text) words = [token.lemma_ for token in doc if token.pos_ in include_pos and token.lemma_ not in stopwords] wordlist.append(" ".join(words)) wordlist
できあがったリストをDataFrameに入れる。
import pandas as pd import numpy as np df = pd.DataFrame(wordlist, columns = ['text']) #空白をNaNに置き換え df['text'].replace('', np.nan, inplace=True) #Nanを削除 inplace=Trueでdf上書き df.dropna(subset=['text'], inplace=True) df[:15]
頻出上位の単語を示すグラフを表示する。
import nlplot npt = nlplot.NLPlot(df, target_col='text') npt.bar_ngram( title='uni-gram', xaxis_label='word_count', yaxis_label='word', ngram=1, top_n=50, )
頻出上位の単語を示すツリーマップを表示する。
npt.treemap( title='Tree of Most Common Words', ngram=1, top_n=30, # stopwords=stopwords, #前処置にて除去しているため適用していない )
一行にどれぐらいの単語があるかを示すヒストグラムを表示する。
npt.word_distribution( title='number of words distribution', xaxis_label='count', )
単語がどの単語とどれぐらいの絡みがあるかを示すネットワーク図を表示する。
※plotly をインストールしていない場合 pip install plotly
import nlplot npt = nlplot.NLPlot(df, target_col='text') # 共起ネットワーク用のデータフレーム作成。min_edge_frequencyで最低隣接数を指定する。この数が小さいとグラフの表示に時間がかかる npt.build_graph(min_edge_frequency=3) ax = npt.co_network(title="共起ネットワーク") from plotly.offline import iplot iplot(ax)
どのようなノードがあり、どのぐらいの単語数と絡みがあるかはnpt.node_df に入っている。
npt.node_df
また、どのノードとどのノードがどれぐらい絡みがあるかはnpt.edge_df に入っている。
npt.edge_df
import nlplot npt = nlplot.NLPlot(df, target_col='text') wc = npt.wordcloud( max_words=100, max_font_size=100, colormap='tab20_r', # stopwords=stopwords, ) import matplotlib.pyplot as plt plt.figure(figsize=(8, 4)) plt.imshow(wc) plt.axis("off") plt.tight_layout(pad=0) plt.show()
文章をベクトル化し、類似度をそれぞれ表示してみる。
類似度はコサイン類似度として表示される。コサイン類似度は-1~1で表され、1に近いほど最も似ている。-1に近いほど真逆である。0に近いほど無関係である。
import spacy import numpy as np from sklearn.metrics.pairwise import cosine_similarity texts = [ "好きな食べ物はカレーです?", "熊本に住んでいます", "今日は晴れです", "年齢は二十歳です", "好きなアニメは【推しの子】です", ] nlp = spacy.load("ja_ginza") query = input("質問:") # 質問と全テキストをベクトル化 query_embedding = [nlp(query).vector] chunk_embeddings = [nlp(t).vector for t in texts] # コサイン類似度を計算 similarities = cosine_similarity(query_embedding, chunk_embeddings)[0] # 類似度を表示 print(similarities)
質問好きなアニメは [0.7768518 0.6222656 0.6632121 0.5997186 0.8742658]
import spacy import numpy as np from sklearn.metrics.pairwise import cosine_similarity texts = [ "好きな食べ物はカレーです?", "熊本に住んでいます", "今日は晴れです", "年齢は二十歳です", "好きなアニメは【推しの子】です", ] nlp = spacy.load("ja_ginza") # 最も類似度が高いものを返す関数 def get_top_similar(query, texts): # 質問と全テキストをベクトル化 query_embedding = [nlp(query).vector] chunk_embeddings = [nlp(t).vector for t in texts] # コサイン類似度を計算 similarities = cosine_similarity(query_embedding, chunk_embeddings)[0] # 類似度が高い1件を取得 index = np.argsort(similarities)[-1] return texts[index] query = input('質問:') similar = get_top_similar(query, texts) print(similar)