コンピュータはことばを理解できる?
コンピュータと人間が自然な会話ができるようになるとしたらどんなことができるようになるでしょうか?2022年11月に公開されたChatGPTというAIシステムはその夢を現実に近づけました。ChatGPTは自然言語処理(Natural Language Processing, NLP)という技術を使っています。自然言語処理とは人間が使う言葉(自然言語)をコンピュータで処理する技術のことです。このブログ記事では自然言語処理の仕組みについて何回かに分けて解説していきたいと思います。
前回の記事
自然言語処理の常識を覆した会話型生成AIへの道のり[2] “文字が描く最初の世界”
単語
単語分割(Word Segmentation)
さて、これまでは文字単位での処理をしてきましたが、もう少し大きな単位で処理をすることも考えられます。それが2つ目、単語単位で比較をする方法です。ただ日本語は英語のように単語境界が明瞭ではないのでテキストを単語で区切る必要が出てきます。この処理を単語分割(Word Segementation)と呼びます。プログラミングの観点から言うと単語分割のみを行うライブラリは少なく大抵は形態素解析[注釈1]をする過程で分割された結果を単語分割の結果として用いることが多いです[注釈2]。日本語の形態素解析が可能なツールは以下の通りです。
ツール/ライブラリ | 説明 | 特徴 |
---|---|---|
GiNZA[注釈3] | spaCyフレームワークをベースにした日本語自然言語処理ライブラリ。 | トークン化、品詞タグ付け、依存構造解析、固有表現抽出などの自然言語処理タスクが可能。 |
JUMAN[注釈4] | 京都大学黒橋・河原研究室によって開発された形態素解析ツール。 | UTF-8対応、Wikipediaから抽出した辞書を使用可能、Webテキストから自動的に取得した辞書を使用可能。 |
Jagger[注釈5] | 東京大学の吉永直樹氏によって開発された日本語形態素解析器。 | 大規模なコーパスから自動的に学習されたモデルを使用して高精度な形態素解析を実現する。 |
Janome[注釈6] | 純粋なPythonで書かれた形態素解析ツール。 | シンプルなAPI、アプリケーションに簡単に統合できるよう設計されている。 |
Kuromoji[注釈7] | Javaで開発されたオープンソースの日本語形態素解析エンジン。 | 日本語テキストを形態素に分割し、各形態素に品詞タグを割り当てられる。動詞や形容詞の基本形を表示することもできる。Apache Software Foundationに寄贈され、Apache LuceneおよびApache Solr 3.6および4.0リリースの日本語サポートを提供しているが、別途使用することもできる。 |
KyTea[注釈8] | テキストデータ分析用のオープンソースツールキット。 | 単語分割、読み推定、品詞推定が可能であり、Sモデルトレーニングが簡単であるため、ドメイン適応が簡単である。単語(または形態素)分割が必要な言語(例えば日本語)向けの汎用テキストアナライザーである。 |
MeCab[注釈9] | オープンソースの日本語形態素解析エンジン。 | 言語非依存、辞書非依存、コーパス非依存。様々な言語や辞書で使用可能。 |
RakutenMA[注釈10] | 日本語と中国語の両方に対応した形態素解析ツール。 | - |
Sudachi[注釈11] | Works Applications Co., Ltd.によって開発された日本語形態素解析ツール。 | GiNZA NLPライブラリと併用して、日本語の自然言語処理が可能。 |
Vibrato[注釈12] | 日本語の自然言語処理ツールキット。 | 形態素解析、係り受け解析、固有表現抽出などの自然言語処理タスクをサポートする。 |
nagisa[注釈13] | Pythonで書かれた日本語の単語分割・品詞タグ付けライブラリ。 | ニューラルネットワークを使用しており、高速かつ高精度な単語分割・品詞タグ付けが可能。 |
まず、文字単位の処理で用いた2つのテキストを単語分割してみましょう。先ほど示した日本語形態素解析器の中からインストールの簡単さと未知語への対応の良さという観点でnagisaを使うことにします。ターミナルでpip install nagisaを実行すると日本語単語分割・品詞タグ付ツールnagisaをインストールできます。下記のword_segmentation.pyは、これまでの例に出てきた3つのテキストペアを単語分割するPythonスクリプトです。
''' 単語分割を行うPythonスクリプト word_segmentation.py スクリプト実行の前にpip install nagisaの実行が必要です。''' import nagisa pairs = [ {'s1': '猫が魚を食べた', 's2': 'ネコが魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '猫ちゃんが魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '熊が魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '魚が猫を食べた'} ] for pair in pairs: s1 = pair['s1'] s2 = pair['s2'] print(s1+' ->', nagisa.tagging(s1).words) print(s2+' ->', nagisa.tagging(s2).words) print()
このスクリプトを実行すると、「猫が魚を食べた」は「猫」「が」「魚」「を」「食べ」「た」と分割され、「ネコが魚を食べた」は「ネコ」「が」「魚」「を」「食べ」「た」に分割されることが分かりました。
Jaccard係数、Dice係数、Simpson係数
ではこの2つの単語列をどう比較すればよいでしょうか。以前説明した文字単位の処理に出てきた集合を使って類似度を計算できそうです。下記のword_based_similarity.pyは、3パターンのテキストの組み合わせに対する類似度をJaccard係数とDice係数とSimpson係数を使用して計算するPythonスクリプトです。このスクリプトを実行するにはこのファイルと同じ階層にset_similarity.pyが必要です。
''' 単語分割後に集合を使用して類似度を計算するPythonスクリプト word_based_similarity.py ''' import nagisa import set_similarity pairs = [ {'s1': '猫が魚を食べた', 's2': 'ネコが魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '猫ちゃんが魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '熊が魚を食べた'}, {'s1': '猫が魚を食べた', 's2': '魚が猫を食べた'} ] for pair in pairs: s1 = nagisa.tagging(pair['s1']).words s2 = nagisa.tagging(pair['s2']).words print(f'{s1}と{s2}のJaccard係数による類似度は {set_similarity.jaccard_similarity(s1, s2)}') print(f'{s1}と{s2}のDice係数による類似度は {set_similarity.dice_similarity(s1, s2)}') print(f'{s1}と{s2}のSimpson係数による類似度は {set_similarity.simpson_similarity(s1, s2)}')
このスクリプトを実行した結果を次の表にまとめました。
テキストのペア | 望ましい類似度 | Jaccard係数 | Dice係数 | Simpson係数 |
---|---|---|---|---|
猫が魚を食べた ネコが魚を食べた |
1.0 | 0.71 | 0.83 | 0.83 |
猫が魚を食べた 猫ちゃんが魚を食べた |
1.0 | 0.86 | 0.92 | 1.0 |
猫が魚を食べた 熊が魚を食べた |
0.0 | 0.71 | 0.83 |
0.83 |
猫が魚を食べた 魚が猫を食べた |
0.0 | 0.71 | 0.83 | 0.83 |
文字単位に処理した結果と比べると、3つ目のテキストペア以外の類似度は同等か改善されているように見えます。例えば、「猫が魚を食べた」「猫ちゃんが魚を食べた」の類似度は、どの係数の類似度も1.0に近付き、改善されたと言えます。この結果を良く眺めると、改善できるポイントが見えてきます。つまり、単語単位で類似度を計算するときに「猫」と「ネコ」と「猫ちゃん」が同義語であり、「猫」と「熊」が同義語でないと認識できれば類似度の値を改善できそうだということが分かります。
Bag-of-Words(BoW)
さて、文字列を文字の集合とみなすということは、見方を変えると、文字を文字コードの昇順に並べた表を作って、文字列での出現の有無を該当する列に記入していることと同じです。
文字列 | A | ... | Z | a | ... | z | ... | が | ... | た | ... | べ | ... | を | ... | 猫 | ... | 食 | ... | 魚 | ... |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
猫が魚を食べた | 0 | ... | 0 | 0 | ... | 0 | ... | 1 | ... | 1 | ... | 1 | ... | 1 | ... | 1 | ... | 1 | ... | 1 | ... |
この表では、文字列中に含まれる文字に対応する列に1を、含まれない文字に対応する列に0を入れています。
同様に、テキストを単語の集合とみなし、単語をすべて列挙した表に、テキストでの出現の有無を該当する列に記入するとします。すると、これまでのテキストは次のように表せます。
テキスト | が | た | ちゃん | を | ネコ | 熊 | 猫 | 食べ | 魚 | ... |
---|---|---|---|---|---|---|---|---|---|---|
猫が魚を食べた | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | ... |
ネコが魚を食べた | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | ... |
猫ちゃんが魚を食べた | 1 | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | ... |
熊が魚を食べた | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | ... |
魚が猫を食べた | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | ... |
この表ではテキスト中に含まれる単語に対応する列に1を、含まれない単語に対応する列に0を入れています。
このようにテキストをテキスト中の単語の出現頻度の数値の並びで表す(≒ベクトル化する)ことをBag-of-Words(BoW)[注釈14]と呼びます。下記のbow.pyは、上記の表を生成するPythonスクリプトです。スクリプト実行の前にターミナルでpip install -U scikit-learnを実行して機械学習ライブラリscikit-learn[注釈15]とpip install pandasを実行してデータ解析ライブラリpandas[注釈16]のインストールが必要です。
''' テキストのBag-of-Wordsを求めるPythonスクリプト bow.py スクリプト実行の前にpip install -U scikit-learnとpip install pandasの実行が必要です ''' import nagisa from sklearn.feature_extraction.text import CountVectorizer import pandas as pd texts = [ '猫が魚を食べた', 'ネコが魚を食べた', '猫ちゃんが魚を食べた', '熊が魚を食べた', '魚が猫を食べた', ] corpus = [' '.join(nagisa.tagging(x).words) for x in texts] vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w+\\b') X = vectorizer.fit_transform(corpus) terms = vectorizer.get_feature_names_out() df = pd.DataFrame(X.toarray(), columns=terms, index=texts) # df.to_csv('./bag-of-words.csv', encoding='Shift_JIS') #コメントアウトを外せばファイルに出力します。 df
先ほどの表では4つのテキストに含まれる単語しか考慮していないので、日本語の単語をすべて列挙するためにWikipedia日本語版のデータを使おうと思います。まず、Wikipedia日本語版の全データをダウンロードします[注釈17]。ファイルサイズは3GBを超えているので、ダウンロードとそれ以降の作業をするにはあらかじめドライブの空き容量を確保しておいてください。ファイルを展開するとxmlファイルが生成されるので、適宜xmlファイルを解析するツールで必要な情報を取得していくことになります。ここではファイルを展開することなく中身を取り出せるWikiExtractor[注釈18]を使うことにします。pip install wikiextractorでツールをインストールし、ターミナルでwikiextractor --json jawiki-latest-pages-articles.xml.bz2と実行すると、textフォルダが自動生成され、その中に抽出されたテキストがjson形式で保存されます。簡易的で良ければ次のコマンドをターミナルで実行すると、全ファイルをテキスト化した上で結合したファイルwikipedia.txtが生成されます。
find ./text/ -name "wiki*" -exec sh -c "cat {} | jq" \; > ./wikipedia.txt
このファイルをさらにフィルタリング処理して約10万行のcorpus.txtを生成しました。次に、nagisaで単語分割し単語数をカウントしたいと思います。下記のword_segmentation2.pyの実行した結果、総単語数は238,468個となりました。スクリプトの最後に実際に出力された単語リストの一部をコメントアウトで入れました。単語の横の数字は単語に割り振られた識別番号です。
''' corpus.txtを単語分割し単語数をカウントして一部を出力するPythonスクリプト word_segmentation2.py ''' import nagisa from multiprocessing import Pool from sklearn.feature_extraction.text import CountVectorizer def word_segmentaion(text): return ' '.join(nagisa.tagging(text).words) corpus_filepath = './corpus.txt' with Pool() as p: corpus = p.map(func=word_segmentaion, iterable=open(corpus_filepath)) vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w+\\b') X = vectorizer.fit_transform(corpus) print(f'単語数={len(vectorizer.vocabulary_)})') print([x for x in vectorizer.vocabulary_.keys()][:10]) #[('昆虫', 168851), # ('節足', 200918), # ('動物', 131211), # ('甲殻', 192204), # ('類', 232624), # ('貝', 218477), # ('魚類', 235826), # ('両生', 117601), # ('爬虫', 187693), # ('鳥類', 236608)]
潜在意味解析(Latent Semantic Analysis)
corpus.txtの語彙数が238,468であったので、この語彙に基づいてBowでテキストを表すと、どんなテキストも238,468次元のベクトルで表すことができますが、このままではベクトル空間が非常に高次元で計算量も膨大になりますし、なにより「猫」と「ネコ」が同じ意味であると認識できていません。しかしベクトル化することでテキストに数学的な操作を加えられるようになったため、似たような出現パターンを持つ単語をグループ化することができます。これが潜在意味解析(Latent Semantic Analysis、潜在的意味解析)[注釈19]と呼ばれる技術です。情報検索の分野では潜在的意味索引または潜在意味インデックス(Latent Senamtic Indexing、LSI)とも呼ばれています。
BoWの説明に出てきた表では行がテキストを(行数 は4)、列が単語(列数 は238,468)、各要素は単語の頻度を表していました。これを 行 列のテキスト-単語行列 とみなし特異値分解(Singular Value Decomposition)[注釈20]を用いて低次元の空間に射影すると、高次元空間での情報の一部を失う代わりに計算量を大幅に削減しデータの取り扱いを容易にすることができます。この際、選択される次元数はトピック数と呼ばれます。そして潜在意味解析はトピックモデル[注釈21]の1つとされています。特異値分解では任意の × 行列Xは次のように3つの行列の積に分解されます。
ここで、 は左特異ベクトルを列に持つ × の直行行列、 は × の対角行列(対角成分が特異値)、 は右特異ベクトルを行に持つ × の直行行列の転置行列です。
では、先ほどのコーパスを使って潜在意味解析をします。「猫」と「ネコ」と「猫ちゃん」が同義であること、「猫」と「熊」が同義でないこと、そしてそれ以外の概念をまとめて欲しい、という意図でトピック数を3に設定しました。下記のlsa.pyは、潜在意味解析を行うPythonスクリプトです。
''' 潜在意味解析を行うPythonスクリプト lsa.py ''' import nagisa from multiprocessing import Pool from sklearn.feature_extraction.text import CountVectorizer from sklearn.decomposition import TruncatedSVD def word_segmentaion(text): return ' '.join(nagisa.tagging(text).words) corpus_filepath = './corpus.txt' with Pool() as p: corpus = p.map(func=word_segmentaion, iterable=open(corpus_filepath)) vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w+\\b') X = vectorizer.fit_transform(corpus) n_topics = 3 lsa = TruncatedSVD(n_components=n_topics, n_iter=5) lsa.fit_transform(X) terms = vectorizer.get_feature_names_out() for i, component in enumerate(lsa.components_): zipped = zip(terms, component) top_terms_key = sorted(zipped, key = lambda x: x[1], reverse=True)[:140] top_terms_list = list(dict(top_terms_key).keys()) print(f"トピック {i}: ", top_terms_list)
実際に得られたトピックとそのトピックにグループ化された単語を表にまとめました。数が多いため見やすくするために一部のみ出力しています。
トピック | 単語 |
---|---|
0 | の に は を た ... 犬 ... 魚 ... 猫 ... |
1 | 1 0 2 3 9 年 4 5 石 6 月 7 8 日 万 し た 藩 ... |
2 | の 0 で 石 ある ... 魚 ... 犬 ... 猫 ... ネコ ... |
トピック2には「の」「に」「は」等の単語がグループ化されました。その中に「猫」と「ネコ」がありこれらが同義語であることを認識できたように見えますが、「犬」や「魚」も同じくトピック0にグループ化されており、これらの区別がついていないように見えます。トピック0も同様です。
一方、トピック数を3にしたことでテキストは238,468次元ベクトルから3次元ベクトルで表せるようになりました。単語を低次元の実数ベクトルで表現することを埋め込み[注釈22]と言い、単語を埋め込んだベクトルは単語埋め込みベクトルとも呼ばれます。ベクトルの類似度はコサイン類似度[注釈23]、ユークリッド距離[注釈24]、マンハッタン距離[注釈25]等で測ることができますが、ここでは簡単なコサイン類似度を使用するとします。下記のcos_similarity.pyは、テキストをベクトル化しそれぞれのコサイン類似度を計算するPythonスクリプトです。
''' テキストベクトルのコサイン類似度を計算するPythonスクリプト cos_similarity.py ''' import nagisa from multiprocessing import Pool from sklearn.feature_extraction.text import CountVectorizer from sklearn.decomposition import TruncatedSVD from sklearn.metrics.pairwise import cosine_similarity def word_segmentaion(text): return ' '.join(nagisa.tagging(text).words) corpus_filepath = './corpus.txt' with Pool() as p: corpus = p.map(func=word_segmentaion, iterable=open(corpus_filepath)) vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w+\\b') X = vectorizer.fit_transform(corpus) n_topics = 3 lsa = TruncatedSVD(n_components=n_topics, n_iter=5) lsa.fit_transform(X) texts = [ '猫が魚を食べた', 'ネコが魚を食べた', '猫ちゃんが魚を食べた', '熊が魚を食べた', '魚が猫を食べた', ] segmented_texts = [' '.join(nagisa.tagging(x).words) for x in texts] text_vectors = lsa.transform(vectorizer.transform(segmented_texts)) print(cosine_similarity(text_vectors))
このスクリプトを実行すると、4つのベクトルの間のペアワイズコサイン類似度が出力されます。この出力をテキストのペアと共に表示した結果が次の表です。
テキストのペア | コサイン類似度 |
---|---|
猫が魚を食べた ネコが魚を食べた |
0.99997884 |
猫が魚を食べた 猫ちゃんが魚を食べた |
0.99999932 |
猫が魚を食べた 熊が魚を食べた |
0.99993591 |
猫が魚を食べた 魚が猫を食べた |
1.0 |
この表によると、「猫」と「ネコ」が同義語であること、「猫」と「猫ちゃん」が同義語であることを正しく認識していることは分かりましたが、「猫」と「熊」は辛うじて同義語でないと認識されたように見えます。つまり以前示したトピックとそのトピックに属する単語の表での推測が裏付けられた結果となりました。そして4つ目のペアの結果からは主語と述語の逆転した関係を認識できていないことが分かります。
この結果を良くするにはどうしたら良いでしょうか。LSAの適用先であるテキスト-単語行列の要素は単語の頻度でしたので、これを使う代わりに単語の重要性をより考慮した値を用いるのが良さそうです。単語の頻度はそのまま単語の重要度に関連するため単語の重要度は頻度に比例すると仮定できますが、一方で多くのテキストに現れる単語は希少性が低く重要性が低いと解釈することもできます。そこで、多くのテキストに現れる単語の重要度を下げ、滅多に登場しない単語の重要度を上げることにします。この考えを指標化したものがtf-idfです。
tf-idf
tf-idf(TF*IDF, TFIDF, TF–IDF, Tf–idf)はTerm Frequency-Inverse Document Frequencyの略で、文書中の単語の重要度を評価するための手法です。tf-idfは2つの統計量Term Frequency(TF)とInverse Document Frequency(IDF)の積で定義されます。
TFは文書 における単語 の出現頻度を表します。具体的には文書 における単語tの出現回数 を文書 に含まれる全単語数で割ったものです。
IDFは単語 が出現する文書数 に対する逆数の対数をとったものです。 は全文書数です。
したがって、TF-IDFは次のように定義されます。
このように、TF-IDFは文書中に多く出現する単語ほど高い値を持ちますが、他の文書でも頻繁に出現する単語は低い値となります。例えば日本語の助詞である「て」「に」「を」「は」等はどの文書にも頻繁に出現するものなので重要度が低くなるという仕掛けです。これにより特定の文書に特徴的な単語を抽出することができます。
下記のtf-idf_lsa.pyは、テキスト中の単語の重要度としてtf-idfを用いてLSAを計算するPythonスクリプトです。
''' tf-idfを用いてLSAを計算し、トピックを表示し、 テキストベクトルのコサイン類似度を計算するPythonスクリプト tf-idf_lsa.py ''' import nagisa from multiprocessing import Pool from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer from sklearn.decomposition import TruncatedSVD from sklearn.metrics.pairwise import cosine_similarity def word_segmentaion(text): return ' '.join(nagisa.tagging(text).words) corpus_filepath = './corpus.txt' with Pool() as p: corpus = p.map(func=word_segmentaion, iterable=open(corpus_filepath)) vectorizer = CountVectorizer(token_pattern='(?u)\\b\\w+\\b') counts = vectorizer.fit_transform(corpus) transformer = TfidfTransformer(smooth_idf=False) X = transformer.fit_transform(counts) n_topics = 3 lsa = TruncatedSVD(n_components=n_topics, n_iter=5) lsa.fit_transform(X) terms = vectorizer.get_feature_names_out() for index, component in enumerate(lsa.components_): zipped = zip(terms, component) top_terms_key=sorted(zipped, key = lambda x: x[1], reverse=True)[:140] top_terms_list=list(dict(top_terms_key).keys()) print(f"トピック {i}: ", top_terms_list) texts = [ '猫が魚を食べた', 'ネコが魚を食べた', '猫ちゃんが魚を食べた', '熊が魚を食べた', '魚が猫を食べた', ] segmented_texts = [' '.join(nagisa.tagging(x).words) for x in texts] text_vectors = lsa.transform(transformer.transform(vectorizer.transform(segmented_texts))) print(cosine_similarity(text_vectors))
実際に得られたトピックとそのトピックにグループ化された単語を表にまとめました。数が多いため見やすくするために一部のみ出力していますが、前回と比べてトピックにグループ化された単語に少し一貫性が見えてきて理解しやすくなったように思います。例えばトピック0はテキスト中で主語になるようなものがグループ化され、トピック1には数えられるものがグループ化され、トピック2は海軍に纏わるものがグループ化されたように見えます。
トピック | 単語 |
---|---|
0 | の に た を は ... 犬 ... 魚 ... 猫 ... ネコ ... |
1 | 1 2 0 年 9 月 日 3 4 5 8 7 6 魚雷 第 昭和 ... |
2 | た 魚雷 を 艦 し 発射 本 船 攻撃 命中 隻 日 駆逐 機 丸 艇 ... |
また、tf-idfによるトピックに基づいたテキストベクトルで計算したペアワイズコサイン類似度は以下のような結果となりました。
テキストのペア | コサイン類似度 |
---|---|
猫が魚を食べた ネコが魚を食べた |
0.99914416 |
猫が魚を食べた 猫ちゃんが魚を食べた |
0.99969583 |
猫が魚を食べた 熊が魚を食べた |
0.993524 |
猫が魚を食べた 魚が猫を食べた |
1.0 |
tf-idfを使うことで「猫」と「熊」が同義語でないことが少し反映されているようにも見えますが依然として僅差であって区別が付きづらく良くありません。そして4つ目のペアの結果を見る限り主語と述語の逆転した関係を認識できていないことが分かります。これも良くありません。
BoWで単語頻度の代わりにtf-idfによる文書単語行列を特異値分解するLSAでは、単語の意味や関係をうまく捉えられない、単語の順序や文法を考慮しない、新しい単語を扱えない等の欠点があります。そこでこれらの問題を解決するword2vecという技術があります。この技術は「単語の意味はその周辺の単語によって決まる」という考え方に基づいています。
次回へ続く
自然言語処理の常識を覆した会話型生成AIへの道のり[4-最終回] “文脈が開く無限の世界”
出典
- https://ja.wikipedia.org/wiki/形態素解析
- 形態素と単語は異なる概念です。形態素は言語学の用語で、意味を持つ表現要素の最小単位です。ある言語においてそれ以上分解したら意味をなさなくなるところまで分割して抽出された音素のまとまりの1つ1つを指します。一方、単語は文法的に独立して機能する最小の言語単位であり、一つ以上の形態素から構成されることがあります。例えば「ヤマカゼ(山風)」という単語は「ヤマ(山)」と「カゼ(風)」という2つの形態素から構成されていると見なすことができます。しかし形態素解析の基となる文法規則と辞書と形態素解析アルゴリズムによってはそのように分割することはせず「山風」までの分割までとするものもあります。
- https://megagonlabs.github.io/ginza/
- https://nlp.ist.i.kyoto-u.ac.jp/?JUMAN
- http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/jagger/index.ja.html
- https://mocobeta.github.io/janome/
- https://www.atilika.com/ja/kuromoji/
- http://www.phontron.com/kytea/index-ja.html
- https://taku910.github.io/mecab/
- https://github.com/rakuten-nlp/rakutenma
- https://github.com/WorksApplications/Sudachi
- https://github.com/daac-tools/vibrato
- https://github.com/taishi-i/nagisa
- https://en.wikipedia.org/wiki/Bag-of-words_model
- https://scikit-learn.org/stable/index.html
- https://pandas.pydata.org/
- https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-pages-articles.xml.bz2
- https://github.com/attardi/wikiextractor
- https://ja.wikipedia.org/wiki/潜在意味解析
- https://ja.wikipedia.org/wiki/特異値分解
- https://en.wikipedia.org/wiki/Topic_model
- https://ja.wikipedia.org/wiki/埋め込み_(数学)
- https://en.wikipedia.org/wiki/Cosine_similarity
- https://ja.wikipedia.org/wiki/ユークリッド距離
- https://ja.wikipedia.org/wiki/マンハッタン距離
この記事を書いた人
亀谷 展
株式会社サン・フレアのリサーチサイエンティスト。
深層学習による自然言語処理やビッグデータ処理を担当。