こんにちは!
消費財メーカーでデータサイエンティストをしているウマたん(@statistics1012)です。
自然言語処理は、機械に人間の言葉を理解させる上で非常に重要な領域。
しかーし、あんまり自然言語処理の中身についてはブラックボックスでよく分かっていないという人が多いのではないでしょうか?
ということで、この記事では自然言語処理についてまとめていきたいと思います。
ところどころでPythonの実装も合わせておこなっていきますよー!
自然言語処理とは

まず、自然言語処理とは何でしょうか?
自然言語処理とは、端的に言うと「人間の言葉を機械が理解するルール作り」です。
言葉は、人類が生み出した虚構であり文化によって全く違う構造を持ちます。
そんな言葉を機械が理解できるようにする領域が自然言語処理なのです。
機械が人間の言葉をしっかり理解するためには、文脈理解と単語理解が必要。
単語理解は、どのような言葉がどのような意味で使われているかをまとめた辞書(コーパスと呼ばれる)が必要になってきます。
そんな辞書と大量の文章をインプットさせて機会が文意を読み取ることができるようになるのです。
そのため、実は自然言語処理の分野では教師データがどれだけリッチかが非常に重要なんです。
その点では中国など個人情報の法規制に緩い国が有利。
なぜなら昨今のデジタル上でのコミュニケーションを仮にテキスト情報としてインプットすることが出来れば、各段に自然言語処理精度が上がるからです。
今のところ、コミュニケーションのテキスト情報をインプット情報として使える企業は少ないです。
日本でも、LINE社は日々の大量のコミュニケーション情報をテキストとして保持していながら個人情報保護の観点から解析に使えない状態になっています。
個人情報の取り扱いに関しては国によって全く方針が違うんですよねー。
Pythonで自然言語処理に関わる手法を実装

続いて、いくつかのトピックを紹介しながらPythonで実装をおこなっていきます。
Mecabで形態素解析
まずは、形態素解析。
形態素解析とは、文章を単語単位で区切りそれぞれの単語に情報を付与する手法です。
形態素解析エンジンにはいくつかの種類がありますが、ここではMecabを使用していきます。
Mecabは古くから有名で最も良く使われている形態素解析エンジン。
PythonだけでなくRやRubyなど他のプログラミング言語も使用することが可能です。
まずは、Mecabを使う環境を整えていきましょう!
以下のURLから「mecab-0.996-64.exe」をダウンロードしてください。
https://github.com/ikegami-yukino/mecab/releases/tag/v0.996
ダウンロードした後は、PythonでMeCabが使えるようにバインディングしていきます。
以下の記事で詳しくまとめられているので参考にしてみてください。
ここまで来たらMeCabを使う準備が整いました。
早速Mecabを利用して文章を単語区切りに分解していきます。
1 2 | m=MeCab.Tagger() m1=m.parse (text) |
たったこれだけで、textを単語区切りにしてくれるんです。
試しにこのtext部分に
”統計ラボはデータサイエンスとWebマーケティングをまとめたサイトです”
と入れてみます。
※スタビジの旧サイト名は統計ラボでした
すると・・・
以下のような結果が返ってきます。素晴らしい!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 統計 名詞,サ変接続,*,*,*,*,統計,トウケイ,トーケイ ラボ 名詞,一般,*,*,*,*,ラボ,ラボ,ラボ は 助詞,係助詞,*,*,*,*,は,ハ,ワ データ 名詞,一般,*,*,*,*,データ,データ,データ サイエンス 名詞,一般,*,*,*,*,サイエンス,サイエンス,サイエンス と 助詞,並立助詞,*,*,*,*,と,ト,ト Web 名詞,固有名詞,組織,*,*,*,* マーケティング 名詞,一般,*,*,*,*,マーケティング,マーケティング,マーケティング を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ まとめ 動詞,自立,*,*,一段,連用形,まとめる,マトメ,マトメ た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ サイト 名詞,一般,*,*,*,*,サイト,サイト,サイト です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス EOS |
返ってきた結果に対して名詞だけ取り出したいため、.splitを使い2つ目の要素が名詞だったら配列に格納するような処理を行っています。
1 2 3 4 5 6 7 8 9 10 | for row in m1.split("n"): word =row.split("t")[0]#タブ区切りになっている1つ目を取り出す。ここには形態素が格納されている if word == "EOS": break else: pos = row.split("t")[1]#タブ区切りになっている2つ目を取り出す。ここには品詞が格納されている slice = pos[:2] if slice == "名詞": word_list = word_list +" "+ word return word_list |
これによりテキスト情報を単語に分解し、名詞だけ格納した配列が出来上がりました。
Mecabに関してより詳しくは以下の記事でまとめています。

URLからスクレイピングでテキストを引っ張ってきて、それらをMecabで形態素解析し、テキスト類似度を測るプログラムを以下で公開していますのでぜひチェックしてみてください!

RNNを使ってみる
続いて、ディープラーニングの領域に足を踏み入れてみましょう!
RNNはリカレントニューラルネットワークの略で、ディープラーニングの層構造に再帰性を取り入れたもの。
通常のディープラーニングでは、時系列要素のあるデータを上手く扱えないという欠点があるんです。

そんなディープラーニングの構造に対して、時系列要素を加えて順番を考慮しようよというのがRNNの考え方。
時系列処理はテキストの文脈を読み取る上で非常に重要です。
例えば英語で、he said “I’m () ” という文章があった時、()の中身を当てるのは相当難しい。
しかし、その後ろにこんな文脈があったらどうでしょう?
Tom came home, and he said “I’m () “.
()の中にどのような文章が入るかほとんどの人が分かると思います。
おそらくTom came home, and he said “I’m home “でしょう。
このようにテキストの文脈からワードを推論するのは周りの文脈が非常に重要なんです。
それを実現できるのがRNNと考えてください。
今までのディープラーニングでは、それぞれのインプットがそれぞれの中間層に与えられていましたが、RNNでは同一の中間層を用いて再帰的にインプットが行われます。
再帰的という部分がReccurentと言われるゆえんです。
こんなイメージ

同じレイヤーh1を用いているのがミソです。
これにより前のワードの情報をレイヤに記憶させ後続へとつなぐことができます。
自然言語処理ではないですが、RNNを使って時系列問題を解くことができるというところを見ていきましょう!
使うデータセットはKaggleのホームページが落とせる航空会社の乗客数データ!
1949年から1960年までの月別乗客数がデータとして入っています。
149行2列のシンプルなデータセット。
1変数の時系列データを基に過去のデータから未来の値を予測します。
この時、tflearnというライブラリを使ってRNN(正確にはLSTM)を実装していきます。tflearnはkerasと似たようなライブラリでディープラーニングの実装が感覚的に容易にできます。
実際にモデルを構築していきましょう!
最終的な評価はRMSE(Root Mean Square Error)で算出しています。
RMSEは0.10079201となりました。それなりに良い予測ができてる!
RNNについて詳しくは以下の記事でまとめています。

Googleが公表したBERTを使ってみる
最後にGoogleが2018年に公表し、2019年に検索アルゴリズム適応させたBERTについて見ていきましょう!
BERTとは「Bidirectional Encoder Representations from Transformers」の略であり、Googleが新たに開発した自然言語処理のロジックになります。
BERTの登場によりGoogleはより長く複雑な文章を理解できるようになり、文脈を読み取ることができるようになりました。
実際にGoogleのリリースにBERT導入前と導入後のGoogle検索の違いが記載されています。
(引用元:Google-”Understanding searches better than ever before”)
ブラジル人が米国へ渡航する際のビザの有無を知りたいのですが、BERT実装前は英文における”to”の意味を明確に捉えることができずアメリカ人がブラジルへ旅行する場合の結果を返していました(左側)。
BERT実装後は、しっかりアメリカ渡航の際の結果を返すことができています。
このように検索結果が、より検索者の検索意図を読み取れるように進化してきているのです。
それでは、そんなBERTを実際に実装してみましょう!
BERTを使って以下の日本語文章の空白を予測してみます。
※当サイト【スタビジ】は昔統計ラボというサイトでした
[‘統計’, ‘ラボ’, ‘は’, ‘*’, ‘を’, ‘発信’, ‘する’, ‘サイト’, ‘です’]
京都大学の黒橋・河原・村脇研究室の作成した強力な形態素解析器JUMAN++をインストールしていきます。
こちらは、先ほどのMeCabよりも口語に対応している形態素解析です。
BERTを実装するためには日本語における相当な量の事前学習が必要なのですが京都大学の黒橋・河原・村脇研究室が公開してくれています。
非常にありがたいですね!
1 2 3 4 | import urllib.request kyoto_u_bert_url = "http://nlp.ist.i.kyoto-u.ac.jp/nl-resource/JapaneseBertPretrainedModel/Japanese_L-12_H-768_A-12_E-30_BPE.zip" urllib.request.urlretrieve(kyoto_u_bert_url, "Japanese_L-12_H-768_A-12_E-30_BPE.zip") |
続いてZIPファイルを解凍していきます。
1 | !unzip Japanese_L-12_H-768_A-12_E-30_BPE.zip |
これで、日本語モデルの準備は完了です。
1 2 3 4 5 | config = BertConfig.from_json_file('/content/drive/My Drive/bert/japan_test/Japanese_L-12_H-768_A-12_E-30_BPE/bert_config.json') model = BertForMaskedLM.from_pretrained('/content/drive/My Drive/bert/japan_test/Japanese_L-12_H-768_A-12_E-30_BPE/pytorch_model.bin', config=config) bert_tokenizer = BertTokenizer('/content/drive/My Drive/bert/japan_test/Japanese_L-12_H-768_A-12_E-30_BPE/vocab.txt', do_lower_case=False, do_basic_tokenize=False) |
必要なパラメータやモデル、BERTが扱いやすい形に加工するための変換処理などを先ほどダウンロードした日本語Pretrainedモデルから取得します。
1 2 3 4 | text = "スタビジは*を発信するサイトです" result = jumanpp.analysis(text) tokenized_text = [mrph.midasi for mrph in result.mrph_list()] print(tokenized_text) |
続いて、JUMAN++によって品詞分解を行います。
[‘統計’, ‘ラボ’, ‘は’, ‘*’, ‘を’, ‘発信’, ‘する’, ‘サイト’, ‘です’]
1 2 3 4 5 | tokenized_text.insert(0, '[CLS]') tokenized_text.append('[SEP]') masked_index = 4 tokenized_text[masked_index] = '[MASK]' print(tokenized_text) |
文章の区切れやマスクする部分をBERTに明示的に知らせるために、SEPやMASKを挿入していきます。
CLSは文章の頭に、SEPは文章の区切れに、MASKは隠したい部分に適用します。
[‘[CLS]’, ‘統計’, ‘ラボ’, ‘は’, ‘[MASK]’, ‘を’, ‘発信’, ‘する’, ‘サイト’, ‘です’, ‘[SEP]’]
1 2 | tokens = bert_tokenizer.convert_tokens_to_ids(tokenized_text) tokens_tensor = torch.tensor([tokens]) |
そして、得られたテキストをBERT用に変換します。
1 2 3 4 5 6 7 | model.eval() with torch.no_grad(): outputs = model(tokens_tensor) predictions = outputs[0] _,predicted_indexes = torch.topk(predictions[0, masked_index], k=10) predicted_tokens = bert_tokenizer.convert_ids_to_tokens(predicted_indexes.tolist()) print(predicted_tokens) |
そして、マスクされた部分の予測を行います。
結果は・・・
[‘統計’, ‘情報’, ‘データ’, ‘ニュース’, ‘それ’, ‘調査’, ‘分析’, ‘レポート’, ‘トレンド’, ‘ランキング’]
上手く予測できていることが分かります!
ところどころ省略しているので詳しくは以下の記事をチェックしてみてください!

自然言語処理 まとめ
自然言語処理についてまとめてきました!
自然言語処理は、機械が自然な会話を実現できるようになるために非常に重要な分野。
これからの研究が期待される領域なんですねー!
例えば、複数人で会話をする時に人間であれば複数人の音声が重なっていても切り分けて処理することができますが、現状の機械ではそれは厳しいです。
音声分離の技術はまだまだこれからの研究課題になっています。
もしディープラーニングや機械学習、Pythonに興味があれば以下の記事で勉強法をまとめていますのでぜひチェックしてみてください!


