導入 自然言語処理 (NLP) は困難な分野です。構造化されていないテキストから有用な結論を生成することは困難であり、それぞれ独自の使用例と複雑さを持つ無数の手法とアルゴリズムがあります。 NLP にほとんど触れたことのない開発者にとって、どの方法を使用し、それをどのように実装するかを知ることは困難です。 最小限の労力で最高の結果を出すことができれば。 80/20 の原則を使用して、結果 (80%) を大幅に犠牲にすることなく、ソリューションを迅速に (20%) 提供する方法を紹介します。 - 「80/20の法則とは、少数の原因、入力、または努力が、通常、大多数の結果、出力、または報酬をもたらすというものです」
-『80/20の法則』の著者、リチャード・コッホ どうすればこれを実現できるでしょうか? 世の中には素晴らしい Python ライブラリがいくつかあります。車輪の再発明をするのではなく、巨人の肩に乗って迅速に革新できるかもしれません。事前にテストされた実装と事前にトレーニングされたモデルを使用して、これらの方法を適用し、価値を生み出すことに重点を置きます。 この記事は、自然言語処理をプロジェクトに素早く統合したい開発者を対象としています。使いやすさと迅速な結果を重視しますが、パフォーマンスも低下します。私の経験では、80%のテクニックでプロジェクトには十分ですが、他の場所から関連する方法を探すこともできます。 さっそく始めましょう! NLPとは何ですか? 自然言語処理は、ソフトウェアによるテキストの自動処理を可能にする言語学、コンピューターサイエンス、人工知能の分野です。 NLP により、機械は整理されていない非構造化テキストを読み取り、理解し、応答できるようになります。 多くの人は NLP を機械学習のサブセットだと考えていますが、現実はもっと微妙です。 一部の NLP ツールは機械学習に依存しており、さらにディープラーニングを使用するものもあります。ただし、これらの方法は大規模なデータセットに依存することが多く、実装が困難です。代わりに、開発サイクルをスピードアップするために、よりシンプルなルールベースのアプローチに重点を置きます。 用語 データの最小単位から始めると、文字は単一の文字、数字、または句読点です。単語は文字のリストであり、文は単語のリストです。文書は文のリストであり、コーパスは文書のリストです。 前処理 前処理はおそらく NLP プロジェクトで最も重要なステップであり、モデルがノイズを無視して最も重要なことに集中できるように入力をクリーンアップすることが含まれます。強力な前処理パイプラインはすべてのモデルのパフォーマンスを向上させるため、その価値は過大評価できません。 一般的な前処理手順を次に示します。 - セグメンテーション: 長い文字列が与えられた場合、スペースで文書を、ピリオドで文を、スペースで単語を区切ることができます。実装の詳細はデータセットによって異なります。
- 小文字を使用します。大文字を使用すると、通常はパフォーマンスが向上しず、文字列の比較が難しくなります。したがって、すべてを小文字に変更します。
- 句読点の削除: 意味を付加しないコンマ、引用符、その他の句読点を削除する必要がある場合があります。
- ストップワードを削除する: ストップワードとは、「she」、「the」、「of」などの、テキストに意味を追加せず、キーワードから注意をそらす単語です。
- その他の無関係な単語を削除する: アプリケーションによっては、特定の無関係な単語を削除する必要がある場合があります。たとえば、コースのレビューを評価する場合、「教授」や「コース」などの単語は役に立たない可能性があります。
- 語幹化/見出し語化: 語幹化と見出し語化はどちらも、語形変化した単語の語源を生成します (例: 「running」から「run」へ)。ステミングは高速ですが、語源が英語の単語であるという保証はありません。レンマ化では、コーパスを使用して、速度を犠牲にして、語根が単一の単語であることを確認します。
- 品詞タグ付け: 品詞タグ付けは品詞 (名詞、動詞、前置詞) に基づいて、意味と文脈に応じて単語をマークします。たとえば、キーワード抽出では名詞に焦点を当てることができます。
これらの手順は、前処理を成功させるための基礎となります。データセットとタスクに応じて、いくつかの手順をスキップしたり、新しい手順を追加したりすることができます。前処理を通じてデータを手動で観察し、問題が発生した場合は修正を行います。 Python ライブラリ NLP 用の 2 つの主要な Python ライブラリを見てみましょう。これらのツールは前処理において非常に重要な役割を果たします。 ナタリー Natural Language Toolkit は、Python で最も広く使用されている NLP ライブラリです。 NLTK はペンシルベニア大学で学術目的で開発され、多数の機能とコーパスを備えています。 NLTK はデータの処理や前処理に最適です: https://www.nltk.org/ NLTK は、人間の言語データを処理する Python プログラムを構築するための主要なプラットフォームです。使いやすいAPIを提供します - >>> nltk をインポートする
-
- >>> sentence = 「木曜日の朝8時、アーサーは気分があまり良くありませんでした。」
-
- >>> トークン = nltk.word_tokenize(文)
- >>> トークン
- [ 「午前8時」 、 「木曜日」 、「朝」 、「アーサー」、「した」、 「感じない」、 「とても」 、 「良い」 、 「。」 ]
-
- >>> タグ = nltk.pos_tag(トークン)
- >>> タグ[0:6]
- [( 'At' 、 'IN' )、( 'eight' 、 'CD' )、( "o'clock" 、 'JJ ')、(' or '、 ' IN ')、(' Thursday '、 ' NNP ')、(' morning '、 ' NN')]
以下は、文をトークン化して品詞をマークすることがいかに簡単かを示す NLTK Web サイトからの例です。 スパシー SpaCyはモダンな図書館です - インポートスペース
-
- nlp = spacec.load ( "en_core_web_sm" )をロードします。
- text = ( "セバスチャン・スランが自動運転車の開発に着手したとき、"
- 「2007年のGoogleでは、社外で彼を真剣に受け止める人はほとんどいなかった」
-
- doc = nlp(テキスト)
- doc.ents内のエンティティの場合:
- 印刷(entity.text、entity.label_)
-
- # 出力
- # セバスチャン・スラン
- # Google 組織
- # 2007年日付
SpaCy を使用して、名前付きエンティティの認識を実行できます。 SpaCy API を使用して迅速に実行できるタスクは他にも多数あります。 ジェンシム NLTK や SpaCy とは異なり、GenSim は情報検索 (IR) の問題に特に対処します。 GenSim はメモリ管理に重点を置いて開発されており、潜在的意味インデックス、Word2Vec、FastText など、多くのドキュメント類似性モデルが含まれています。 Gensim は、大規模なコーパスでのトピック モデリング、ドキュメントのインデックス作成、類似性検索のための Python ライブラリです。 以下は、単語の類似性を検出する、事前トレーニング済みの GenSim Word2Vec モデルの例です。面倒な詳細を気にすることなく、すぐに結果を得ることができます。 - gensim.downloader をAPIとしてインポートします
- wv = api.load ( "word2vec-google-news-300" )
-
- ペア = [
- ( '車' 、 'ミニバン' )、# ミニバンは車の一種です
- ( 「車」 、 「自転車」 )、# 車輪付きの乗り物でもある
- ( 「車」 、 「飛行機」 )、# 車輪はないが、それでも乗り物である
- ( '車' 、 'シリアル' )、# ... など。
- ( 「車」 、 「共産主義」 )、
- ]
-
- w1、w2のペアの場合:
- print('%r\t%r\t%.2f % (w1, w2, wv.similarity(w1, w2)))
-
- # 出力
- # '車' 「ミニバン」 0.69
- # '車' 「自転車」 0.54
- # '車' 「飛行機」 0.42
- # '車' 「シリアル」 0.14
- # '車' 「共産主義」 0.06
その他にもいろいろ… このリストは包括的なものではありませんが、いくつかのユースケースをカバーしています。 応用 前処理方法と Python ライブラリについて説明しましたので、いくつかの例を挙げてすべてをまとめてみましょう。それぞれのアルゴリズムについて、いくつかの NLP アルゴリズムを紹介し、迅速な開発という目標に基づいて 1 つを選択し、ライブラリの 1 つを使用して簡単な実装を作成します。 アプリケーション1: 前処理 前処理はあらゆる NLP ソリューションの重要な部分です。そこで、Python ライブラリを使用してプロセスを高速化する方法を見てみましょう。私の経験から言うと、NLTK には独自のユースケースに合わせてカスタマイズされた、必要なツールがすべて揃っています。サンプルコーパスを読み込んでみましょう: - nltk をインポートする
-
- # 茶色のコーパスをロードする
- コーパス = nltk.corpus.brown
-
- # コーパスファイルにアクセスする
- print(corpus.fileids())
-
- # 出力
- [ 'ca01' 、 'ca02' 、 'ca03' 、 'ca04' 、 'ca05' 、 'ca06' 、 'ca07' 、 'ca08' 、 'ca09' 、 'ca10' 、 'ca11' 、 'ca12' 、 'ca13' 、 'ca14' 、 'ca15' 、 'ca16' 、
- 'ca17' 、 'ca18' 、 'ca19' 、 'ca20' 、 'ca21' 、 'ca22' 、 'ca23' 、 'ca24' 、 'ca25' 、 'ca26' 、 'ca27' 、 'ca28' 、 'ca29' 、 'ca30' 、 'ca31' 、 'ca32' 、
- 'ca33' 、 'ca34' 、 'ca35' 、 'ca36' 、 'ca37' 、 'ca38' 、 'ca39' 、 'ca40' 、 'ca41' 、 'ca42' 、 'ca43' 、 'ca44' 、 'cb01' 、 'cb02' 、 'cb03' 、 'c...
上記で定義したパイプラインに従って、NLTK を使用してセグメンテーションを実装し、句読点やストップワードを削除し、ステミングなどを実行できます。ストップワードを削除するのがいかに簡単かをご覧ください: - nltk.corpusからストップワードをインポート
-
- sw = stopwords.words( "英語" )
- sw += "" # 空の文字列
-
- def remove_sw(doc):
- 文 = []
- ドキュメント内の文:
- 文 = [文中の単語が単語でない場合、 sw]で
- sentences.append(文)
- 戻り文
-
- print( "ストップワード付き" )
- 印刷(doc1[1])
- 印刷()
-
- doc1 = 削除_sw(doc1)
-
- print( "ストップワードなし" )
- 印刷(doc1[1])
-
- # 出力
- # ストップワードがあります
- # [ 'the' 、 'jury' 、 'さらに' 、 'said' 、 'in' 、 'presentments' 、 'that' 、 'the' 、 'city' 、 'executive' 、 'committee' 、 'which' 、 'had' 、
- # 「起訴」 、 「の」 、 「選挙」、「値する」 、 「の」 、 「賞賛」 、 「そして」 、 「感謝」 、 「の」 、 「都市」 、 「の」 、 「アトランタ」 、 「のために」 、
- # 「その」 、 「方法」 、 「で」 、 「その」 、 「選挙」 、 「行われた」 ]
-
- # ストップワードなし
- # [ '陪審員' 、 '発言' 、 'プレゼンテーション' 、 '市' 、 '執行部' 、 '委員会' 、 '告発' 、 '選挙' 、 'ふさわしい' 、 '賞賛' 、 '感謝' 、 '市' 、
- # 「アトランタ」 、 「方法」 、 「選挙」 、 「実施」 ]
前処理パイプライン全体に必要なのは、40 行未満の Python です。完全なコードはこちらをご覧ください。これは一般的な例であり、特定のユースケースに応じて必要に応じてフローを変更する必要があることに注意してください。 アプリケーション2: ドキュメントクラスタリング ドキュメントのクラスタリングは自然言語処理における一般的なタスクなので、いくつかのアプローチについて説明しましょう。ここでの基本的な考え方は、各ドキュメントに、議論されたトピックを表すベクトルを割り当てることです。 ベクトルが 2 次元の場合、ドキュメントを上記のように視覚化できます。この例では、ドキュメント A と B は密接に関連していますが、ドキュメント D と F は緩く関連していることがわかります。これらのベクトルが 3 次元、100 次元、または 1000 次元であっても、距離メトリックを使用して類似度を計算できます。 次の質問は、非構造化テキスト入力を使用して各ドキュメントに対してこれらのベクトルをどのように構築するかということです。最も単純なものから最も複雑なものまで、いくつかのオプションを次に示します。 - Bag of Words: それぞれの一意の単語にインデックスを割り当てます。特定のドキュメントのベクトルは、各単語が出現する頻度です。
- TF-IDF: 他のドキュメントでの単語の一般的度に基づいて表現を強化します。 2 つの文書がまれな単語を共有している場合、一般的な単語を共有している場合よりも類似性が高くなります。
- 潜在的意味索引 (LSI): Bag of Words と TF-IDF は高次元のベクトルを作成する可能性があるため、距離測定の精度が低下します。 LSI は、情報の損失を最小限に抑えながら、これらのベクトルをより扱いやすいサイズに圧縮します。
- Word2Vec: ニューラル ネットワークを使用して、大規模なテキスト コーパスから単語の関連付けを学習します。次に、各単語のベクトルを合計してドキュメント ベクトルを取得します。
- Doc2Vec: Word2Vec をベースに構築されていますが、単語ベクトルのリストからドキュメント ベクトルを近似するより優れた方法を使用します。
Word2Vec と Doc2Vec は非常に複雑であり、単語の埋め込みを学習するには大規模なデータセットが必要です。事前にトレーニングされたモデルを使用することもできますが、私たちの分野のタスクにうまく適合しない可能性があります。代わりに、Bag of Words、TF-IDF、LSI を使用します。 それではライブラリを選択しましょう。 GenSim はこのタスク専用に構築されており、3 つのアルゴリズムすべてのシンプルな実装が含まれているため、GenSim を使用しましょう。 この例では、再び Brown コーパスを使用しましょう。 「冒険」「社説」「ニュース」など、15 のテキスト カテゴリのドキュメントがあります。 NLTK 前処理ルーチンを実行した後、GenSim モデルの適用を開始できます。 まず、ID を一意のインデックスにマッピングする辞書を作成します。 - gensimからコーパス、モデル、類似点をインポート
-
- 辞書 = corpora.Dictionary(コーパス)
- dictionary.filter_n_most_frequent(1) # ""を削除します
- num_words = len(辞書)
- 印刷(辞書)
- 印刷()
-
- print( "最も頻繁に使用される単語" )
- top10 = sorted(dictionary.cfs.items(), key =lambda x: x[1], reverse= True )[:10]
- enumerate(top10)のi, (id, freq)について:
- print(i, freq, 辞書[id])
-
- # 出力
- # 辞書(33663 個の固有トークン: [ '1' 、 '10' 、 '125' 、 '15th' 、 '16' ]...)
-
- # 最も頻繁に使用される単語
- # 0 3473 1
- # 1 2843 は
- # 2 2778 言う
- # 3 2327 作る
- # 4 1916年頃
- # 5 1816 行く
- # 6 1777年は
- # 7 1665 新着
- # 8 1659年
- # 9 1575 テイク
次に、Bag of Words、TF-IDF、潜在的意味インデックスを繰り返し適用します。 - corpus_bow = [dictionary.doc2bow(doc)コーパス内のdocの場合]
- print(len(corpus_bow[0]))
- 印刷(corpus_bow[0][:20])
-
- # 出力
- #6106
- # [(0, 1), (1, 3), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 2), (10, 1), (11, 1), (12, 2), (13, 2), (14, 2), (15,
- [#1)、(16、2)、(17、2)、(18、3)、(19、1)]
-
- tfidf_model = models.TfidfModel(corpus_bow)
- コーパス_tfidf = tfidf_model[コーパス_bow]
- print(len(corpus_tfidf[0]))
- 印刷(corpus_tfidf[0][:20])
-
- # 出力
- #5575
- # [(0, 0.001040495879718581), (1, 0.0011016669638018743), (2, 0.002351365659027428), (3, 0.002351365659027428), (4,
- # 0.0013108697793088472)、(5、0.005170600993729588)、(6、0.003391861538746009)、(7、0.004130105114011007)、(8、
- # 0.003391861538746009)、(9、0.008260210228022013)、(10、0.004130105114011007)、(11、0.001955787484706956)、(12、
- # 0.0015918258736505996)、(13、0.0015918258736505996)、(14、0.008260210228022013)、(15、0.0013108697793088472)、(16、
- # 0.0011452524080876978)、(17、0.002080991759437162)、(18、0.004839366251287288)、(19、0.0013108697793088472)]
-
- lsi_model = models.LsiModel(corpus_tfidf、id2word =辞書、num_topics = 20)
- corpus_lsi = lsi_model[corpus_tfidf]
- print(len(corpus_lsi[0]))
- 印刷(corpus_lsi[0])
-
- # 出力
- #15
- # [(0, 0.18682238167974372), (1, -0.4437583954806601), (2, 0.22275580411969662), (3, 0.06534575527078117), (4,
- # -0.10021080420155845)、(5、0.06653745783577146)、(6、0.05025291839076259)、(7、0.7117552624193217)、(8、-0.3768886513901333)、(9、
- # 0.1650380936828472)、(10、0.13664364557932132)、(11、-0.03947144082104315)、(12、-0.03177275640769521)、(13、
- # -0.00890543444745628)、(14、-0.009715808633565214)]
約 10 行の Python コードで、3 つの個別のモデルを処理し、ドキュメントのベクトル表現を抽出しました。コサイン類似度を使用してベクトルを比較することで、最も類似したドキュメントを見つけることができます。 - カテゴリ = [ "冒険" 、 "文学" 、 "論説" 、 "フィクション" 、 "政府" 、
- 「趣味」 、 「ユーモア」 、 「学問」 、 「伝承」 、 「ミステリー」 、 「ニュース」 、 「宗教」 、
- [「レビュー」 、 「ロマンス」 、 「SF」 ]
- num_categories = len(カテゴリ)
-
-
- iが範囲(3)内にある場合:
- 印刷(カテゴリ[i])
- sims =インデックス[lsi_model[corpus_bow[i]]]
- top3 = sorted(enumerate(sims), key =lambda x: x[1], reverse= True ,)[1:4]
- jの場合、トップ3のスコア:
- print(スコア, カテゴリ[j])
- 印刷()
-
- # 出力
- #アドベンチャー
- # 0.22929086 フィクション
- # 0.20346783 ロマンス
- # 0.19324714 謎
-
- #美しい手紙
- # 0.3659389 編集
- # 0.3413822 伝承
- # 0.33065677 ニュース
-
- #社説
- # 0.45590898 ニュース
- # 0.38146105 政府
- # 0.2897901 ベルズレター
すると、結果が出ました。冒険小説と恋愛小説は最も類似しており、社説はニュースや政府と類似しています。 アプリケーション3: 感情分析 感情分析とは、構造化されていないテキストを肯定的、否定的、または中立的として解釈することです。感情分析は、レビューの分析、ブランドの測定、AI チャットボットの構築などに役立つツールです。 ドキュメントクラスタリングとは異なり、感情分析では前処理は使用しません。句読点、流れ、段落の文脈は雰囲気について多くのことを明らかにするので、それらを削除したくありません。 シンプルかつ効果的に保つために、パターンベースの感情分析を使用することをお勧めします。これらのモデルは、特定のキーワード、文の構造、句読点を検索することで、テキストの肯定性または否定性を測定します。感情分析機能が組み込まれた 2 つのライブラリを以下に示します。 VADER感情分析: VADER は、Valence Aware Dictionary と sEntiment Recognizer の略称で、感情分析用の NLTK の拡張です。特に絵文字やテキストスラングの感情を計算するためにパターンを使用します。実装も非常に簡単です。 - vaderSentiment.vaderSentimentからSentimentIntensityAnalyzer をインポートします
-
- アナライザー = SentimentIntensityAnalyzer()
-
- print(analyzer.polarity_scores( "このクラスは私のお気に入りです!!!" ))
- print(analyzer.polarity_scores( "このクラスは嫌いです :(" ))
-
- # 出力
- # { 'neg' : 0.0、 'neu' : 0.508、 'pos' : 0.492、 'compound' : 0.5962}
- # { 'neg' : 0.688, 'neu' : 0.312, 'pos' : 0.0, 'compound' : -0.765}
TextBlob感情分析: 同様のツールとして、感情分析用の TextBlob があります。 TextBlob は、実際には NLTK や SpaCy に似た多目的ライブラリです。感情分析ツールに関しては、感情の極性と主観性を報告する点で VADER とは異なります。私の個人的な経験から言うと、私は VADER の方が好きですが、人それぞれに長所と短所があります。 TextBlob の実装も非常に簡単です。 - textblobからTextBlobをインポート
-
- testimonial = TextBlob( "このクラスは私のお気に入りです!!!" )
- print(testimonial.sentiment)
-
- testimonial = TextBlob( "このクラスは嫌いです :( " )
- print(testimonial.sentiment)
-
- # 出力
- # 感情(極性=0.9765625、主観性=1.0)
- # 感情(極性=-0.775、主観性=0.95)
注: パターンベースのモデルは、上記の例のような小さなテキストではうまく機能しません。平均 4 文のテキストに対して感情分析を実行することをお勧めします。 その他のアプリケーション ここでは、開発をスピードアップするためのいくつかの追加トピックと、役立つアルゴリズムおよびツールを紹介します。 - キーワード抽出: SpaCy を使用した固有表現抽出 (NER)、ntlk-rake を使用した高速自動キーワード抽出 (RAKE)
- テキスト要約: PyTextRank SpaCy 拡張機能を使用した TextRank (PageRank に類似)、GenSim を使用した TF-IDF
- スペルチェック: PyEnchant、SymSpell Python ポート
これらの例が、Python での自然言語処理に利用できる膨大なリソースを示すのに役立つことを願っています。問題が何であれ、誰かがプロセスを簡素化するライブラリを開発しました。これらのライブラリを使用すると、短時間で素晴らしい結果が得られます。 ヒントとコツ NLP の紹介、Python ライブラリの概要、およびいくつかのサンプルアプリケーションを学習すれば、自分の課題に取り組む準備がほぼ整います。最後に、これらのリソースを最大限に活用するためのヒントとコツをいくつか紹介します。 - Python ツール: 依存関係管理には Poetry、新しいモデルのテストには Jupyter Notebook、コード スタイルの維持には Black および/または Flake8、バージョン管理には GitHub をお勧めします。
- 整理された状態を保つ: あるライブラリから別のライブラリに移動したり、現在作成中のコード テストにコードをコピーしたりするのは実装が簡単ですが、良い方法ではありません。急いでいるあまり良い解決策を見逃したくないので、適切な行動をとる際には、より慎重なアプローチを取ることをお勧めします。
- 前処理: ゴミを入れればゴミが出る。入力をクリーンアップするための強力な前処理パイプラインを実装することが非常に重要です。処理されたテキストを視覚的に検査して、すべてが期待どおりに動作することを確認します。
- 結果の提示: 結果をどのように提示するかによって、大きな違いが生じる可能性があります。テキスト出力が少し粗い場合は、集計統計や数値結果を表示することを検討してください。
|