ディープラーニングで文書分類(2)

はじめに

前回ディープラーニング(CNN)を使って、文書分類を行う方法を示した。
今回は、前回示した方法を使って、Livedoor Newsコーパスをカテゴリー分類する分類器をTensorflowで実装していく。

全体像おさらい

今回作成するモデルの全体像は以下の図の通り。
詳細は前回の記事参照。

インプットデータの作成

Livedoor Newsコーパスの各文書を以下の図のような行列にする。
行=各文書の単語。行数を合わせる為に、500単語で区切っている。
列=各単語をWord2Vecで変換した100次元のベクトル。

models[‘文書名’]で、当該文書の行列が取得できるように事前準備をしておく。
Word2Vecモデル作成時に存在しなかった未知語については、[0,0,0,…0]のベクトルで置き換えておく。
500単語に満たない文章については[0,0,0,…0]でパディングし、また500単語を超える文書については、500単語で切ることで、行数を合わせている。

定数・変数の準備

Tensorflowで利用する定数・変数を用意しておく。

ドロップアウト変数は、出力層の計算をする際にプーリング層の結果を当変数の割合でランダムに間引いて行う為のもの。
過学習を防ぐ為に設定をする。
トレーニングの際は0.5、評価の際は1.0(間引かない)を設定する。

“x_expanded”は、インプット変数の次元を1つ増やしたもの。
TensorFlowのconv2d関数は、4次元のテンソル(バッチ、幅、高さ、チャネル)を入力とする為、ここでインプット変数の次元を1つ増やしている。チャネルとは画像であれば、RGBと3つの値を使うことができ、文書であれば「Word2Vec」「GloVe」などエンベディングの方法別にチャネルを設定することができる。今回は、「Word2Vec」のみを使う為、チャネルは1つとする。

畳み込み層・プーリング層

畳み込み層では、Tensorflowのconv2d関数を使って畳み込みの処理を行う。
フィルタのサイズとしては、3単語、4単語、5単語の3種類で、それぞれ128個のフィルタを使う。
フィルタは、各次元に1つずつスライドさせている。

Pooling層では、畳み込み層の結果をMax-poolingにてプーリング処理を行う。
その後、各フィルタの処理結果をプーリングした値をつなげて、1つにしている。

出力層

最後は、プーリング層の結果をインプットにカテゴリへの分類を行う。
DOC_CLASS_DEFで定義されているカテゴリのうち、predictionsで”1″が立っている場所のカテゴリが分類結果となる。

トレーニング及びテスト

コスト関数の定義とAdamオプティマイザーによる各パラメータの最適化を行う為の定義をする。

ここまでで各処理の定義ができた為、いよいよトレーニング及びテスト(評価)を行っていく。
前回のニューラルネットワークと同様ミニバッチにて、100件ずつランダムにピックアップしたデータをトレーニングデータとして使っている。
イテレーション回数は、14,000回とした。

最終結果としては、評価用データで92%の正答率となり、ニューラルネットワークでの分類結果とほぼ同程度の値となった。
ニューラルネットワークよりもトレーニングの時間が多くかかり、今回入力データやハイパーパラメータの調整までを行うことができたなかった。この辺りをもう少し調整することで、より高い正答率は出せるものと考えられる。

ディープラーニングで文書分類(1)

はじめに

前回前々回とSVM、ニューラルネットワークを使ったLivedoor Newsコーパス文書分類器を作成してきた。
今回からは、いよいよディープラーニングを使った文書分類器を作成してみたいと思う。ディープラーニングの中でも、畳み込みニューラルネットワーク (Convolutional Neural Networks: CNN) を使って、実装を行っていきたい。CNNと言うと、画像分類を思い浮かべる人が多いかと思われるが、最近はCNNを使った自然言語処理に関する研究も盛んになってきている。
まずは、文書分類に対してどのようにCNNを適用するのかを見ていきたい。
尚、CNN自体の説明は、ここでは行わない。

全体像

今回作成するモデルの全体像は、(いくらか省略はしているが)以下の図のようになる。
Livedoor Corpusの各文書をインプットに、畳み込み層(Convolution Layer)、プーリング層(Pooling Layer)と続き、9つのカテゴリーに分類する出力層へとつながっている。

入力層

これまで行ってきた分類問題では、インプットにBag of Wordsを利用してきた。これは、出現する単語全てを1つの袋に入れて、単語の出現回数を1つずつ数えるようなもので、文書のどの位置に単語が出現してきたのかは考慮されない。
それに対して、今回使うインプットは、以下の図のようになる。

行(縦)は、各文書の単語の並びだが、全文書で行数を合わせるために、最初の500単語で切っている。500単語に満たない文書の場合は、足りない部分を0でパディングしている。
列(横)は、以前作成した100次元のWord2Vecのモデルを利用する。
よって、500 x 100の行列がインプットとなる。

フィルタ

畳み込みを行う為のフィルタには、3単語、4単語、5単語の3種類を使うこととした。これにより、文書内に出てくる前後数単語を考慮した特徴量が抽出されることとなる。
またそれぞれの種類に対して128個のフィルタを使うので、合計384個のフィルタを利用することとなる。

畳み込み層

上述のインプットに対して、フィルタを1単語づつスライドさせながら掛け合わせることで畳み込みを行う。
また活性化関数にはReLU関数(ランプ関数)を使う。
結果、384個の特徴量が抽出できる。

プーリング層

畳み込み層で、それぞれのフィルタから計算された特徴量ベクトルからMax Poolingにより副次標本を取り、全ての副次標本をつなげて、ひとつのベクトルとする。

出力層

最後は、プーリング層の結果をインプットにソフトマックス関数を使って9つのカテゴリーに分類させる。

まとめ

今回は、Livedoor Corpusをカテゴリ分けする問題を、ディープラーニング(CNN)を使って行う方法について纏めてみた。
次回は、今回纏めた手法を、実際にTensorflowを用いて実装・テストを行っていきたい。

BoW+ニューラルネットワークで文書分類

はじめに

前回、SVMを使い、Livedoor Corpusのカテゴリ分類を行った。
今回は、シンプルな2層ニューラルネットワークを使って文書分類器を作成してみる。
実装は、Python3、TensorFlowを利用して行った。

データの準備

前回作成したBoWモデルを学習用データ、テスト用データとして利用するため、特にデータの準備は必要ない。
Livedoor Corpusの各文章は、300次元のベクトルになっており、正解ラベル(カテゴリ)が付与されている状態である。

今回作成するニューラルネットワークイメージ

以下のような隠れ層1層+出力層の2層ニューラルネットワークを使う。

隠れ層の活性化関数にはシグモイド関数を利用して、出力層にはソフトマックス関数を利用する。
入力(文書ベクトル)に対して、正しい正解(カテゴリー)が得られるように、Weight、Biasの値を学習していく。

TensorFlowでの実装

まずは各種変数の準備。

隠れ層の活性化関数にはシグモイド関数を使い、出力層はソフトマックス関数を使う。
それぞれTensorFlowに用意されている関数を利用する。

これで、予測カテゴリー(y)が求められ、実際のカテゴリー(y_)との差分がコスト(予測との乖離)となる。
このコストを最小化するような最適なWeight、Biasを学習させることで分類器ができあがる。
尚、ここでは、コスト関数に「y – y_」の二乗誤差ではなく、交差エントロピーを使う。
また、過学習を防ぐために、L2正則化も行っておく。

さて、後は、上記コスト関数を最適化するよう勾配下降法を使う。
TensorFlowで用意されているGradientDescentOptimizer関数を使えば良い。
尚、学習率は、当関数のパラメータとして渡す。
今回は、0.001とした。

最後に、正答率を計算する式も定義しておく。

これで準備はできたので、後は実際の学習用データを使ってトレーニングを行い、テストデータでの正解率を見てみる。

結果

ここまでの内容を実行してみたところ、トレーニングデータに対しては97%くらいの正解率が出るが、テストデータに対しては87%と、典型的な過学習の状態になっていた。
その後、正則化パラメータのlambdaや隠れ層の数などを調整して、最終的な結果としては以下の通りまずまずの結果となった。

BoW+SVMで文書分類(2)

はじめに

Livedoor Newsコーパスを学習データとして、カテゴリ分類する分類器を作成する記事の続き。

前回、文書分類を行う為の前準備として、各文書を数値化(ベクトル化)するところまでを行った。
今回は、前回作成した文書ベクトルをインプットにSVMを使い文書分類器を作るところまでを行う。

Livedoor Newsコーパスをトレーニングデータとテストデータに分割

Livedoor Newsコーパスは、カテゴリ毎にディレクトリが切られており、その中に1文書1ファイルで作成されている。このディレクトリ名がカテゴリ(教師ラベル)で、各ファイルをベクトル化したものをインプットとして機械学習させる。
また、作成した文書分類器の精度を検証する為のテストデータもこのLivedoor Newsコーパスのデータを使うこととする。
各カテゴリ毎に、80%を学習用データ、20%をテスト用データとして分けておく。

SVMとは

サポートベクターマシン(SVM)とは、教師あり学習データから、各データ点との距離が最大となるマージン最大化超平面を求めることでパラメータを学習する方法である。
話を単純にする為に、下図のような2次元平面の赤点のデータと緑点のデータ(教師ありデータ)を考えてみる。

svm

上図のように赤点と緑点を分割する最適なラインを求めることで、未知のデータに対してもそのラインの左上にあるのなら赤グループ、右下にあるのなら緑グループと分類することができるようになる。
ただし、赤点と緑点を分けるラインは複数考えられれ、上図の例ではいくつかの例としてH1からH3を描いている。
サポートベクターマシンは、各点からラインへ垂直に伸ばした線の距離(マージン)を最大化するようなラインのパラメータを学習する。
今回の例ではマージンが最大となるH1のパラメータが求められる。

上図は単純な2次元の線形サポートベクターマシンの例であるが、今回は300次元のデータに対して、複数カテゴリへの分類を非線形SVMを用いて行ってみる。
尚、SVMには非線形のカーネル関数が複数存在するが、今回はRBFを用いて行う。

Scikit-learnのSVMを用いてパラメータ学習

Pythonの機械学習ライブラリであるScikit-learnを用いて実際に分類器を作成してみる。
SVMでは、分類の精度を高める為に、ハイパーパラメータを決める必要がある。
幸いScikit-learnではGrid Searchの機能があり、複数のパラメータをリスト形式で渡してあげることで、最適なパラメータを見つけてくれる。

  • コストパラメータ(C):誤分類をどれくらい許容するのか決定
  • RBFカーネルのパラメータ(Γ):Γ値が大きいほど複雑な決定境界となり、小さいほど単純な境界となる

またScikit-learnでは、学習データを自動でランダムに分割して交差検定(クロスバリデーション)を行ってくれる。
デフォルトでは3-Fold Cross Validationとなっており、データを3分割しての交差検定を行う。

テストデータで精度の確認

さて、分類器のモデルはできたので、最初に分割した20%のデータで分類器の精度を検証してみる。

結果としては、以下の通りなのだが、最終テストスコアが0.795と思ったほどの精度がでていない。
学習データの再調整やSVMハイパーパラメータの調整を行いつつ、再テストを実施し、精度を上げていく必要がありそうだ。

BoW+SVMで文書分類(1)

はじめに

機械学習を用いドキュメント分類を行う為には、文書自体を数値(ベクトル)として扱う必要がある。文書を数値として扱う代表的な方法としては、Bag of Words(BoW)がある。これは、全文書中に登場する単語を並べて、各単語の出現頻度をベクトルで表す表現である。
今回、Livedoor Newsコーパスの各文書をBoWでベクトル化し、そのデータを用いて、カテゴリ分けする文書分類器を作成してみる。

Livedoor Newsコーパスについて

以下のURLよりLivedoor Newsコーパスをダウンロードできる。
Livedoor Newsコーパスは、ニュースカテゴリ毎にディレクトリが分かれている為、文書分類器作成の学習データとして適している。

Livedoor Newsコーパス
9カテゴリ、7386文書

各種インストール

Python3及び以下のライブラリ群を用いて実装した。

  • Mecab、mecab-python: 形態素解析
  • Gensim: Python用トピックモデルライブラリ
  • scikit-learn:Python用機械学習ライブラリ

手順

BoW+SVMでの文書分類器作成までの大まかな手順は以下の通り。

  1. Livedoor Newsコーパスの各文書をベクトル表現に変換
  2. Livedoor Newsコーパスをトレーニングデータとテストデータに分割
  3. SVMを使い学習
  4. SVMのパラメータ調整
  5. テストデータに適用

Livedoor Newsコーパスの各文書をベクトル表現に変換

BoWで各文書をベクトル表現に変換

機械学習を行う前準備として、各文書をBoWにてベクトル表現に変換する。
「はじめに」でも述べた通り、BoWとは全文書中に登場する単語を並べて、各文章でのそれぞれの単語の出現頻度を表したものである。
例えば、以下の2つの文書から各文書のベクトルを求めてみる。

これら2つの文書に登場する全単語を並べると以下の通りとなる。

上記から各文章のベクトルは、1番目の要素=単語”John”の出現回数、2番目の要素=単語”likes”の出現回数、、、といった形で表せる。
ちなみに上記(1)、(2)をベクトルで表すと以下の通りとなる。

実際には、文書量が多いと登場する単語数も膨大となる為、「全文書での出現頻度が極端に少ない単語」「どの文書にも登場するような一般的すぎるような単語」などは外してベクトルを作成する。
今回は、「出現頻度が20文書未満の単語」及び、「30%以上の文書で登場する単語」を排除した。この時点で、登場する総単語数は約9000となり、9000次元のベクトルとなった。

TF-IDFによる重み付け

ここで作成したBoWモデルに対して、より文書の特徴を捉えられるようTF-IDFを用いて重み付けを行った。
TFとはTerm Frequencyの略で、ある文書中の単語の出現頻度を表している。
DFとはDocument Frequencyの略で、どれくらいの文書でその単語が現れたのかを表しており、IDFとは、DFに対して対数を取ったものである。
この2つの値を掛けたものをそれぞれの単語の重みとしている。

LSIによる次元削減

さて、これである文書の特徴を表しているベクトルが作成できたわけであるが、このままではベクトルの次元数が大きすぎる為、学習コストが非常にかかってしまう。また有益な結果を得る為のサンプル数も非常に多くなってしまうのも問題である。
その為、文章の特徴を損なわなずにベクトルの次元削減を行う必要がある。

今回は、LSI(Latent Semantic Indexing)を用いて、次元削減を行ってみる。
LSIとは、SVD(Singular Value Decomposition:特異値分解)を用いた次元圧縮の方法で、単語の持つ潜在的な意味を軸に取ろうという発想である。
LSIを用いて、9000次元から300次元まで圧縮を行った。

今回は、機械学習の前準備として、文書群をそれぞれの文書の特徴を表しているベクトルに変換するところまでを行った。
次回は、これらのデータを用いて、学習及びパラメータ調整を行ってみる。

Word2Vec

はじめに

ここ数ヶ月、日本語の類義語分類に使えないかと、Word2Vecについて色々と調べてきたので以下に纏めてみる。

Word Embeddings

まず大前提として、自然言語を機械学習処理で扱う為には、文書や単語を数値として扱う必要がある。

$W : words \to \mathbb{R}^n$.

通常、自然言語処理に出てくる単語を数値で表す場合、高次元のベクトルを使って表す。(200〜500次元程度、word2vecのデフォルトは100次元)
例えば、「花」という単語が[0, 0, 0, 1, 0, 0.8, ….]といったベクトルで表すことができるようになる。
このように単語からベクトルへ変換する方法は多数存在しており、単純に全出現単語を並べて各次元でその単語かどうかを判断する方法やニューラルネットワークを使うものなどがある。

Word Embeddingとは、このうちニューラルネットワークなどを用いて単語の素性値を機械学習によって学習させ、ベクトル化する方法であり、分散表現とも呼ばれている。
Word Embeddingを使うことで、自然言語処理の各種タスクを効率よく行うことができるようになる。
Word Embeddingを使った有名な話としては、
$W(King)-W(Queen) \simeq W(Man) - W(Woman)$.
といったように、数式で単語を表せてしまうというものがある。

Word2Vec

Word2Vecとは、いわゆるWord Embeddingを作成する為の2層ニューラルネットワークのことであり、単語の特徴を文章のコンテキストから学習させている。
Word2Vecの学習後のモデルを使うことで、各単語を数百次元程度のベクトルとして表せるようになり、ベクトルのコサイン類似度を計算することで、単語同士の類似度を表すことができる。

例えば、「王」という単語に似ている単語の一覧も学習済みのWord2Vecのモデルを使うことで簡単に出すことができる。

又、2つの単語の類似度の計算も非常に低コストで行うことができる。
例えば、我々が学習させたWord2Vecのモデルでの「買う」と「購入する」の類似度は、
0.905990661338
なんて、計算される。

この結果を見る限り、類義語の抽出ができるのではと考えられるのだが、実際にはWord2Vecのみで類義語かどうかを判断するのは難しい。
例えば、「買う」と「売る」のコサイン類似度は、
0.847663039921
と非常に高い数値となっており、反対の意味を表す単語も抽出されてしまう。

これは、Word2Vecのアルゴリズムに由来しており、以下の説明を見ていただくと理解できると思う。

Continuous Bag-of-Words Model(CBOW)

Word2VecにはSkip-gramとContinuous Bag of Words(CBOW)の2つのアルゴリズムがある。

CBOWモデルは、コンテクスト(前後の単語)を使って対象語を推測する方法であり、$w_{t}$を推測する為の入力は、$w_{t-2}$, $w_{t-1}$, $w_{t+1}$, $w_{t+2}$となる。
前後いくつの単語を使って対象語を推測するのかを「ウィンドウサイズ」と言う。
Googleの研究者であり、Word2Vecの考案者であるMikolovによるとCBOWは学習時間がSkip-gramよりも少なく、頻出する単語に対してはSkip-gramより若干精度が高いとのこと。

CBOW

Skip-gram

反対にSkip-gramは、ある語を使って、前後のコンテクストを推測する方法である。
入力は$w_{t}$であり、アウトプットが、$w_{t-2}$, $w_{t-1}$, $w_{t+1}$, $w_{t+2}$となる。
Skip-gramは学習データが少なくてもある程度の精度がでるとされている。

skip-gram

まとめ

上記アルゴリズムを見ていただければわかる通り、Word2Vecはあくまで文脈から単語の特徴を計算しているため、類似度が高いからといって単語の意味が近いとは限らない。
どちらかと言うと同じような文脈で登場する単語同士は類似度が高い傾向にあり、例えば「好き」、「嫌い」といったように同じ文脈で登場するような単語は反対の意味であったとしても類似度が高い傾向にある。
とは言え、Word2Vecを使って単語をベクトル化させることで、文書分類など他の自然言語処理へのインプットとさせることは有用であると考える。