Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
R による自然言語処理入門
Tokyo.R #31 (2013/06/01)
@a_bicky
文書分類
自己紹介
• Takeshi Arabiki
‣ 平凡な Web エンジニア(主に JavaScript)
‣ Twitter & はてな: @a_bicky & id:a_bicky
‣ e-mail:
• 興味など
機械学習、自然言語処理
• ブログ
あらびき日記 http://d.hatena.ne.jp/a_bicky/
R関係の主な発表
Rユーザ会 2011Tokyo.R #16
http://www.slideshare.net/abicky/r-9034336 http://www.slideshare.net/abicky/r-10128090
Tsukuba.R #9
http://www.slideshare.net/abicky/rtwitter
Rによる文書分類入門
基礎知識
形態素解析
• 文を形態素(意味の最小単位)に分割
‣ 日本語自然言語処理における 単語 はたいてい形態素のこと
‣ e.g. 「お酒」→「お」+「酒」(形態素の定義は曖昧)
• 各形態素の品詞推定
‣ e.g. 「お(接頭詞)」+「酒(名詞)」
自然言語で書かれた文を形態素(Morpheme, おおまかにいえば、言語で意
味を持つ最小単位)の列に分割し、それぞれの品詞を判別する作業を指す。
引用: 形態素解析 - Wikipedia
つまり・・・
mecab による形態素解析
$ mecab
ハードルは高ければ高いほどくぐりやすい
ハードル	名詞,一般,*,*,*,*,ハードル,ハードル,ハードル
は	助詞,係助詞,*,*,*,*,は,ハ,ワ
高けれ	 形容詞,自立,*,*,形容詞・アウオ段,仮定形,高い,タカケレ,タカケレ
ば	助詞,接続助詞,*,*,*,*,ば,バ,バ
高い	 形容詞,自立,*,*,形容詞・アウオ段,基本形,高い,タカイ,タカイ
ほど	 助詞,副助詞,*,*,*,*,ほど,ホド,ホド
くぐり	動詞,自立,*,*,五段・ラ行,連用形,くぐる,クグリ,クグリ
やすい	 形容詞,非自立,*,*,形容詞・アウオ段,基本形,やすい,ヤスイ,ヤスイ
EOS
入力文
出力
ベクトル空間モデル
• 素性 (feature) の取り方はいろいろ
• Unigram model (Bag-of-Words model)
‣ 1つの単語を1つの素性とする
‣ 単語の出現順は考慮しない(文書を単語の袋詰めとみなす)
e.g. 「赤ちゃんと僕」と「僕と赤ちゃん」は同じベクトルになる
• N-gram model
‣ 連続する n 単語を1つの素性とする
e.g. 「赤ちゃんと僕」→ <s>赤ちゃん, 赤ちゃんと, と僕, 僕</s>
「僕と赤ちゃん」→ <s>僕, 僕と, と赤ちゃん, 赤ちゃん</s>
そせい
文書をベクトル空間の1点として表現する
単語の重み付け
• TF (Term-Frequency) 値
‣ 各文書における単語の出現頻度
• IDF (Inverse Document Frequency) 値
‣ コーパス(文書集合)内で対象の単語を含む文書の割合の逆数の対数
• TF-IDF 値
‣ TF 値と IDF 値を掛けあわせた値
‣ 特定の文書にだけ出現する単語の値が大きくなる
cf. 情報検索アルゴリズム 3.2 節参照
idf(t, D) = log
|D|
|{d|d ∈ D, t ∈ d}|
tfidf(t, d, D) = tf(t, d)idf(t, D)
tf(t, d) = |{x|x ∈ d, x = t}|
単語文書行列
D =







d1 d2 · · · dM
t1 0 1 . . . 0
t2 2 0 . . . 0
...
...
...
...
...
tV 1 0 . . . 2







各文書ベクトルを列に並べた行列
e.g. あるコーパスに対する TF 値による単語文書行列
文書 の文書ベクトル
文書 における単語 の出現回数
d2
dM tV
単語文書行列の作成
> library(RMeCab)
>
> pos <- c("名詞", "動詞", "形容詞", "副詞", "連体詞")
> texts <- c("ハードルは高ければ高いほどくぐりやすい",
+ "タダより高いものはない")
>
> D1 <- docMatrixDF(texts, pos = pos, weight = "tf")
to make data frame
# 抽出する形態素の品詞
RMeCab を使うことで手軽に単語文書行列が作成可能
# TF 値による単語文書行列を作成
> D1
OBS.1 OBS.2
1 0
0 1
0 1
1 0
0 1
1 0
2 1
単語文書行列の作成
くぐる
ない
もの
やすい
タダ
ハードル
高い
RMeCab を使うことで手軽に単語文書行列が作成可能
N-gram による行列
>
> D2 <- t(docNgramDF(texts, type=1, pos=pos, N=2))
number of extracted terms = 7
to make matrix now
> D2
Row1 Row2
1 0
0 1
0 1
1 0
1 0
0 1
1 0
[くぐる-やすい]
[もの-ない]
[タダ-高い]
[ハードル-高い]
[高い-くぐる]
[高い-もの]
[高い-高い]
# 2-gram (bigram) による行列を作成
疎行列
• 単語文書行列は多くの場合疎行列
• 非零要素のインデックスとその値だけを保持(省メモリ化)
‣ データの格納形式には CSC、CSR、COO などがある
• R の代表的なパッケージは Matrix
‣ 基本的な操作は普通の行列と同様に行うことが可能
‣ 疎行列に対応していない関数は普通の行列に変換する必要あり
‣ 普通の行列では R の制限で作成できない大規模な行列も作成可能
行列の多くの要素が 0 である行列
疎行列への変換
> library(Matrix)
> (sm <- as(D1, "CsparseMatrix"))
7 x 2 sparse Matrix of class "dgCMatrix"
OBS.1 OBS.2
1 .
. 1
. 1
1 .
. 1
1 .
2 1
くぐる
ない
もの
やすい
タダ
ハードル
高い
# CSC 形式の疎行列に変換
> str(sm)
Formal class 'dgCMatrix' [package "Matrix"] with 6
slots
..@ i : int [1:8] 0 3 5 6 1 2 4 6
..@ p : int [1:3] 0 4 8
..@ Dim : int [1:2] 7 2
..@ Dimnames:List of 2
.. ..$ : chr [1:7] "くぐる" "ない" "もの" "やすい" ...
.. ..$ : chr [1:2] "OBS.1" "OBS.2"
..@ x : num [1:8] 1 1 1 2 1 1 1 1
..@ factors : list()
疎行列の構造
行のインデックス (0-origin)
列の開始ポインタ (0-origin)
非零の要素の値
CSC 形式
CSC (= Compressed Sparse Column)
i 0 3 5 6 1 2 4 6
x 1 1 1 2 1 1 1 1
(i,j) (0,0) (3,0) (5,0) (6,0) (1,1) (2,1) (4,1) (6,1)
p 0 4 8
0 列目はここから
1 列目はここから
疎行列の操作
> sm[c(3, 5), ]
2 x 2 sparse Matrix of class "dgCMatrix"
OBS.1 OBS.2
. 1
. 1
> colSums(sm)
[1] 5 4
> rowSums(sm)
[1] 1 1 1 1 1 1 3
> as.matrix(sm[c(3, 5), ])
OBS.1 OBS.2
0 1
0 1
# 3行目と5行目を抽出
もの
タダ
# 各文書の単語数を算出
# 各単語の出現回数を算出
もの
タダ
# 普通の行列に変換
基本的な操作は普通の行列と同様に行うことが可能
文書分類
文書分類
• 今回扱うのは2クラス分類
‣ e.g. 受信したメールがスパムメールかどうか
• 扱う手法
‣ 決定木(CART)
‣ ナイーブベイズ
‣ 最大エントロピー (ME) モデル
‣ サポートベクターマシン (SVM)
サンプルデータ
> library(maxent) # cf. help(maxent)
> data <- read.csv(system.file("data/NYTimes.csv.gz",
+ package = "maxent"))
>
> subdata <- subset(data, Topic.Code %in% c(16L, 20L))
>
> topic <- factor(subdata$Topic.Code)
> corpus <- Corpus(VectorSource(subdata$Title))
>
> D <- as.matrix(t(DocumentTermMatrix(corpus)))
maxent パッケージの New York Times データを使用
# 2クラス分類なので Topic.Code が 16 と 20 のものだけ使用
# クラスラベル
# 単語文書行列
サンプルデータ
>
> rownames(D)[which(rownames(D) == "...")] <- "X..."
> set.seed(0)
>
> trainIndex <- sample.int(ncol(D), ncol(D) * 0.5)
> trainData <- t(D[, trainIndex])
> testData <- t(D[, -trainIndex])
> trainTopic <- topic[trainIndex]
> testTopic <- topic[-trainIndex]
# データの半分を学習データとして使用
# rpart でエラーになるので単語名を変更
maxent パッケージの New York Times データを使用
ナイーブベイズ
ナイーブベイズ
データの生成確率(条件付き確率)を最大化するクラスを選択
• 単純なモデルなので実装が楽(R で10行!!)
‣ cf. 10行でナイーブベイズ ∼Rって便利だね!∼ - あらびき日記
• 学習も高速
• 特定のクラスに出現しない単語の対処が必要(ディスカウンティング)
ˆy = arg max
y
N
i=1
log P(wi|y) + log P(y)
確率的言語モデル
• 単語列 の同時確率 を与えるモデル
• 文書の生成方法をモデル化
‣ に従って単語列を生成することで文書を生成可能
wN
1 = w1w2 · · · wN
w N
M
unigram の例(各単語が独立と仮定)
P(wN
1 )
P(wN
1 )
P(d) = P(wN
1 ) =
N
i=1
P(wi)
Unigram Mixtures
w N
M
クラスごとに unigram を割り当てるモデル
• カテゴリの異なる文書は単語の出現確率も異なるはず
• 各文書は1つのクラス(トピック)から生成されると仮定
P(d) =

y
P(y)
N
i=1
P(wi|y)
ˆy = arg max
y
N
i=1
P(wi|y)P(y)
ナイーブベイズはこの値を最大化するクラスを真のクラスと推定する
y
ディスカウンティング
• ナイーブベイズの問題点
‣ あるクラスの学習データに存在しない単語を含む文書は決してその
クラスに分類されない(ゼロ頻度問題)
- e.g. 0.9 x 0.95 x 0.8 x 0.99 x 0 = 0
• 加算法
‣ 全単語に関して出現頻度を一律 +δ する
‣ あるクラスの学習データに現れない単語でも δ回出現しことになる
• 単純グッド・チューリング推定法
‣ 加算法より洗練されたディスカウンティング方法
‣ cf. 単純グッド・チューリング推定法 (Simple Good-Turing
Estimation) とは何ぞや? - あらびき日記
ナイーブベイズを実装
myNaiveBayes - function(x, y) {
lev - levels(y)
ctf - sapply(lev, function(label) {
colSums(x[y == label,])
})
ctp - t(t(ctf + 1) / (colSums(ctf) + nrow(ctf)))
nc - table(y, dnn = NULL)
cp - nc / sum(nc)
structure(list(lev = lev, cp = cp, ctp = ctp),
class = myNaiveBayes)
}
# 各クラスにおける単語出現頻度
# ラプラススムージングによるクラスごとの単語の出現確率
# 各クラスの生成確率
# 各クラスに所属する文書数
ナイーブベイズを実装
predict.myNaiveBayes - function(model, x) {
prob - apply(x, 1, function(x) {
colSums(log(model$ctp) * x)
})
prob - prob + log(as.numeric(model$cp))
level - apply(prob, 2, which.max)
model$lev[level]
}
# 予測用の関数
R でナイーブベイズ
 model - myNaiveBayes(trainData, trainTopic)
 pred - predict(model, testData)
 (tbl - table(pred, truth = testTopic))
truth
pred 16 20
16 193 33
20 27 166
 sum(diag(tbl)) / sum(tbl)
[1] 0.8568019
# 正解率
決定木 (CART)
決定木 (CART) の概要
根ノードから各ノードの判定基準に従って
り着いた葉ノー ドによりクラスを分類
Yes No
Yes No
C1
C1 C2
A0
A1
• 量的変数と質的変数の混在する素性を容易に扱える
• 構築したモデルの解釈や分類結果の要因の解明がしやすい
• 大規模な素性を扱うには不向き
• R では CART が使われる
• CART は二分木を生成する
• 不純度の減少を最大化させるようにノードを生成
= ノード生成後の不純度を最小化させるようにノードを生成
‣ 不純度:
はノード A に存在する i 番目のクラスのデータの割合
‣ Gini 係数:
‣ 情報エントロピー:
クラスの分布に偏りがあるほど不純度は小さくなる(小さい程良い)
‣ 不純度の減少量:
CART の学習
I(A) =

i
f(piA)
∆I = p(A)I(A) − p(AL)I(AL) − p(AR)I(AR)
f(p) = p(1 − p)
f(p) = −p log(p)
piA
CART の学習例
Yes No
AL AR
A
(50, 50)
(15, 32) (35, 18)
素性1  0
クラス1とクラス2のデータが50個ずつある場合
クラス1とクラス2のデータが50個ずつある場合
Yes No
AL AR
A
(50, 50)
(15, 32) (35, 18)
p1AL
=
15
47
p2AL
=
32
47
p1AR
=
35
53
p2AR
=
18
53
p(AL) =
47
100 p(AR) =
53
100
p1A =
50
100
p2A =
50
100
p(A) = 1
素性1  0
∆I = p(A)
2
i=1
piA(1 − piA) − p(AL)
2
i=1
piAL
(1 − piAL
) −
2
i=1
piAR
(1 − piAR
)
= 0.058
CART の学習例
Yes No
AL AR
A
(50, 50)
(7, 29) (43, 21)
p1A =
50
100
p2A =
50
100
p(A) = 1
素性2  0
∆I = p(A)
2
i=1
piA(1 − piA) − p(AL)
2
i=1
piAL
(1 − piAL
) −
2
i=1
piAR
(1 − piAR
)
p1AL
=
7
36
p2AL
=
29
36
p1AR
=
43
64
p2AR
=
21
64
p(AL) =
36
100 p(AR) =
64
100
= 0.105 こちらの分割方法の方が不純度の減少量が大きい(良い分割方法)
CART の学習例
クラス1とクラス2のデータが50個ずつある場合
1-SE ルール
1 2 3 4 5 6
Yes No
Yes No
C1
C2
A0
A1
Yes No
Yes No
A2
A3 A4
A5C2
葉ノードの数
CVエラー
クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り
CVエラーの最小値
枝刈りすることで過学習を抑制
1 2 3 4 5 6
Yes No
Yes No
C1
C2
A0
A1
Yes No
Yes No
A2
A3 A4
A5C2
CVエラーの最小値+標準偏差
クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り
葉ノードの数
CVエラー
枝刈りすることで過学習を抑制
1-SE ルール
1 2 3 4 5 6
Yes No
Yes No
C1
C2
A0
A1
Yes No
Yes No
A2
A3 A4
A5C2
CVエラーの最小値+標準偏差を最初に下回る点
クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り
葉ノードの数
CVエラー
枝刈りすることで過学習を抑制
1-SE ルール
1 2 3 4 5 6
Yes No
Yes No
C1
C2
A0
A1
Yes No
Yes No
A3 A4
A5C2
枝刈り C1
クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り
葉ノードの数
CVエラー
枝刈りすることで過学習を抑制
1-SE ルール
R で決定木
 library(mvpart)
 model - rpart(topic ~ .,
+ data.frame(trainData, topic = trainTopic))
 pred - predict(model, data.frame(testData),
+ type = class)
 (tbl - table(pred, truth = testTopic))
truth
pred 16 20
16 88 6
20 132 193
 sum(diag(tbl)) / sum(tbl)
[1] 0.6706444
ME モデル
最大エントロピー (ME) モデル
P(y|x) =
1
Z(x)
exp


i
λifi(x, y)

=
1
Z(x)
exp

λT
f(x, y)

Z(x) =

y∈Y
exp

λT
f(x, y)

(規格化定数)
経験的分布の特性を満たしつつ
エントロピーを最大にするモデル
• 柔軟に素性を設定することが可能
• 出力が確率
cf. 確率的言語モデル 6章
素性関数
fb,s(x, y) =

1 if b(x) is true and y = s
0 otherwise
素性の有無を表す2値関数
• e.g.
文書のクラスが class1 で最初が数字で始まれば1,そうでなければ0
• 単語文書行列を扱う場合は「コーパスの語彙数」x 「クラス数」個の素
性関数が存在し、出現頻度などを返す関数
fbegins−with−number,class1
ME モデルの学習
対数尤度を最大化するパラメータを求める
• 解探索アルゴリズム
‣ L-BFGS(省メモリな準ニュートン法)
‣ GIS (Generalized Iterative Scaling)
• L1、L2 正則化で過学習を防ぐことも
‣ L2 正則化をする場合の目的関数は次のとおり
L(P|λ) =

(xi,yi)
log P(yi|xi)
L(P|λ) =

(xi,yi)
log P(yi|xi) −
1
2σ2
||λ||2
← 凸関数
R で ME モデル
 library(maxent)
 model - maxent(trainData, trainTopic)
 pred - predict(model, testData)[, labels]
 (tbl - table(pred, truth = testTopic))
truth
pred 16 20
16 190 36
20 30 163
 sum(diag(tbl)) / sum(tbl)
[1] 0.8424821
SVM
サポートベクターマシン(SVM)
2つのクラス間のマージンを最大化させるモデル
y = sgn [f(x)] =

1 if f(x) ≥ 0
−1 if f(x)  0
f(x) =

xi∈SV
αik(xi, x)
• パラメータ数の割に過学習に陥りにくい
• カーネル関数により複雑な素性を容易に扱うことが可能
• 構築したモデルの解釈や分類結果の要因の解明が難しい
• 多クラス分類をするには一工夫必要
cf. カーネル多変量解析 4章
=
=
=
高次元空間に写像することで非線形問題を線形問題にする
変換
0
0
0
0
0
線形分離不可能(非線形問題) 線形分離可能(線形問題)
例
カーネル法の概要
カーネル法の概要
入力 特徴ベクトル 線形多変量解析
入力空間 特徴空間入力空間
x f(x) = wT
φ(x)
単純に高次元空間に写像すると計算量が増える
入力 特徴ベクトル カーネル多変量解析
入力空間 特徴空間入力空間
x f(x) =
n
i=1
αik

x(i)
, x)

= wT
φ(x)

(カーネル関数)
k

x(i)
, x

= φ(x(i)
)T
φ(x)
k
カーネル関数が高次元空間での計算を隠 (カーネルトリック)
カーネル法の概要
カーネル関数
• 線形カーネル
‣ 現在の特徴空間で SVM などを適用させたい時に使用
• ガウスカーネル
‣ データの特性を維持したまま無限次元空間に写像
• 多項式カーネル
‣ 素性間の相互作用も考慮した空間に写像
‣ 文字列カーネル cf. 文字列カーネルSVMによる辞書なしツイート分類
‣ 文字列を部分文字列の出現回数から成る空間に写像
cf. 自然言語処理におけるカーネル法の利用 (PDF)
カーネル関数によって写像する特徴空間は異なる
k(x, x
) = xT
x
k(x, x
) = exp(−βx − x
2
)
k(x, x
) = (xT
x
+ c)p
SVM の学習
dカーネル空間で2クラス間のマージン を最大化するように学習
‣ ソフトマージン最大化
‣ 誤分類されるものにはペナルティを設けることで誤分類を許容
‣ 凸二次計画問題に帰着できる(SMO などで解く)
||w||2
= αT
Kα kij = k(xj, xi)K = (kij), ,
カーネル関数
min
ξ,α
1
2
||w||2
+ C
n
i
ξi
Subject to ∀
i : yif(x) ≥ 1 − ξi
サポートベクトル
f(x) =

xi∈SV
αik(xi, x)
R で SVM
 library(kernlab)
 model - ksvm(trainData, trainTopic,
+ kernel = vanilladot, kpar = list(),
+ scaled = FALSE)
 pred - predict(model, testData)
 (tbl - table(pred, truth = testTopic))
truth
pred 16 20
16 184 28
20 36 171
 sum(diag(tbl)) / sum(tbl)
[1] 0.8472554
まとめ
・・・の前に
ごめんなさい
m(_ _)m
やれなかったこと
• LSI cf. 潜在的意味インデキシング(LSI)徹底入門 - あらびき日記
• LDA(topicmodels パッケージ)
• 文書クラスタリング cf. Rで学ぶクラスタ解析
‣ 階層的クラスタリング(hclust 関数)
‣ k-means(kmeans 関数)
• 系列ラベリング cf. HMM, MEMM, CRF まとめ - あらびき日記
‣ HMM
‣ MEMM
• データの前処理
‣ 文書中の URL の除去(置換)
‣ Unicode 正規化 cf. abicky/RUnicode · GitHub
• KNB コーパスを使ってごにょごにょ cf. KNB corpus parser for R
まとめ
自然言語処理おもしろいですね!

More Related Content

R による文書分類入門

  • 1. R による自然言語処理入門 Tokyo.R #31 (2013/06/01) @a_bicky 文書分類
  • 2. 自己紹介 • Takeshi Arabiki ‣ 平凡な Web エンジニア(主に JavaScript) ‣ Twitter & はてな: @a_bicky & id:a_bicky ‣ e-mail: • 興味など 機械学習、自然言語処理 • ブログ あらびき日記 http://d.hatena.ne.jp/a_bicky/
  • 3. R関係の主な発表 Rユーザ会 2011Tokyo.R #16 http://www.slideshare.net/abicky/r-9034336 http://www.slideshare.net/abicky/r-10128090 Tsukuba.R #9 http://www.slideshare.net/abicky/rtwitter
  • 6. 形態素解析 • 文を形態素(意味の最小単位)に分割 ‣ 日本語自然言語処理における 単語 はたいてい形態素のこと ‣ e.g. 「お酒」→「お」+「酒」(形態素の定義は曖昧) • 各形態素の品詞推定 ‣ e.g. 「お(接頭詞)」+「酒(名詞)」 自然言語で書かれた文を形態素(Morpheme, おおまかにいえば、言語で意 味を持つ最小単位)の列に分割し、それぞれの品詞を判別する作業を指す。 引用: 形態素解析 - Wikipedia つまり・・・
  • 7. mecab による形態素解析 $ mecab ハードルは高ければ高いほどくぐりやすい ハードル 名詞,一般,*,*,*,*,ハードル,ハードル,ハードル は 助詞,係助詞,*,*,*,*,は,ハ,ワ 高けれ 形容詞,自立,*,*,形容詞・アウオ段,仮定形,高い,タカケレ,タカケレ ば 助詞,接続助詞,*,*,*,*,ば,バ,バ 高い 形容詞,自立,*,*,形容詞・アウオ段,基本形,高い,タカイ,タカイ ほど 助詞,副助詞,*,*,*,*,ほど,ホド,ホド くぐり 動詞,自立,*,*,五段・ラ行,連用形,くぐる,クグリ,クグリ やすい 形容詞,非自立,*,*,形容詞・アウオ段,基本形,やすい,ヤスイ,ヤスイ EOS 入力文 出力
  • 8. ベクトル空間モデル • 素性 (feature) の取り方はいろいろ • Unigram model (Bag-of-Words model) ‣ 1つの単語を1つの素性とする ‣ 単語の出現順は考慮しない(文書を単語の袋詰めとみなす) e.g. 「赤ちゃんと僕」と「僕と赤ちゃん」は同じベクトルになる • N-gram model ‣ 連続する n 単語を1つの素性とする e.g. 「赤ちゃんと僕」→ <s>赤ちゃん, 赤ちゃんと, と僕, 僕</s> 「僕と赤ちゃん」→ <s>僕, 僕と, と赤ちゃん, 赤ちゃん</s> そせい 文書をベクトル空間の1点として表現する
  • 9. 単語の重み付け • TF (Term-Frequency) 値 ‣ 各文書における単語の出現頻度 • IDF (Inverse Document Frequency) 値 ‣ コーパス(文書集合)内で対象の単語を含む文書の割合の逆数の対数 • TF-IDF 値 ‣ TF 値と IDF 値を掛けあわせた値 ‣ 特定の文書にだけ出現する単語の値が大きくなる cf. 情報検索アルゴリズム 3.2 節参照 idf(t, D) = log |D| |{d|d ∈ D, t ∈ d}| tfidf(t, d, D) = tf(t, d)idf(t, D) tf(t, d) = |{x|x ∈ d, x = t}|
  • 10. 単語文書行列 D =        d1 d2 · · · dM t1 0 1 . . . 0 t2 2 0 . . . 0 ... ... ... ... ... tV 1 0 . . . 2        各文書ベクトルを列に並べた行列 e.g. あるコーパスに対する TF 値による単語文書行列 文書 の文書ベクトル 文書 における単語 の出現回数 d2 dM tV
  • 11. 単語文書行列の作成 > library(RMeCab) > > pos <- c("名詞", "動詞", "形容詞", "副詞", "連体詞") > texts <- c("ハードルは高ければ高いほどくぐりやすい", + "タダより高いものはない") > > D1 <- docMatrixDF(texts, pos = pos, weight = "tf") to make data frame # 抽出する形態素の品詞 RMeCab を使うことで手軽に単語文書行列が作成可能 # TF 値による単語文書行列を作成
  • 12. > D1 OBS.1 OBS.2 1 0 0 1 0 1 1 0 0 1 1 0 2 1 単語文書行列の作成 くぐる ない もの やすい タダ ハードル 高い RMeCab を使うことで手軽に単語文書行列が作成可能
  • 13. N-gram による行列 > > D2 <- t(docNgramDF(texts, type=1, pos=pos, N=2)) number of extracted terms = 7 to make matrix now > D2 Row1 Row2 1 0 0 1 0 1 1 0 1 0 0 1 1 0 [くぐる-やすい] [もの-ない] [タダ-高い] [ハードル-高い] [高い-くぐる] [高い-もの] [高い-高い] # 2-gram (bigram) による行列を作成
  • 14. 疎行列 • 単語文書行列は多くの場合疎行列 • 非零要素のインデックスとその値だけを保持(省メモリ化) ‣ データの格納形式には CSC、CSR、COO などがある • R の代表的なパッケージは Matrix ‣ 基本的な操作は普通の行列と同様に行うことが可能 ‣ 疎行列に対応していない関数は普通の行列に変換する必要あり ‣ 普通の行列では R の制限で作成できない大規模な行列も作成可能 行列の多くの要素が 0 である行列
  • 15. 疎行列への変換 > library(Matrix) > (sm <- as(D1, "CsparseMatrix")) 7 x 2 sparse Matrix of class "dgCMatrix" OBS.1 OBS.2 1 . . 1 . 1 1 . . 1 1 . 2 1 くぐる ない もの やすい タダ ハードル 高い # CSC 形式の疎行列に変換
  • 16. > str(sm) Formal class 'dgCMatrix' [package "Matrix"] with 6 slots ..@ i : int [1:8] 0 3 5 6 1 2 4 6 ..@ p : int [1:3] 0 4 8 ..@ Dim : int [1:2] 7 2 ..@ Dimnames:List of 2 .. ..$ : chr [1:7] "くぐる" "ない" "もの" "やすい" ... .. ..$ : chr [1:2] "OBS.1" "OBS.2" ..@ x : num [1:8] 1 1 1 2 1 1 1 1 ..@ factors : list() 疎行列の構造 行のインデックス (0-origin) 列の開始ポインタ (0-origin) 非零の要素の値
  • 17. CSC 形式 CSC (= Compressed Sparse Column) i 0 3 5 6 1 2 4 6 x 1 1 1 2 1 1 1 1 (i,j) (0,0) (3,0) (5,0) (6,0) (1,1) (2,1) (4,1) (6,1) p 0 4 8 0 列目はここから 1 列目はここから
  • 18. 疎行列の操作 > sm[c(3, 5), ] 2 x 2 sparse Matrix of class "dgCMatrix" OBS.1 OBS.2 . 1 . 1 > colSums(sm) [1] 5 4 > rowSums(sm) [1] 1 1 1 1 1 1 3 > as.matrix(sm[c(3, 5), ]) OBS.1 OBS.2 0 1 0 1 # 3行目と5行目を抽出 もの タダ # 各文書の単語数を算出 # 各単語の出現回数を算出 もの タダ # 普通の行列に変換 基本的な操作は普通の行列と同様に行うことが可能
  • 20. 文書分類 • 今回扱うのは2クラス分類 ‣ e.g. 受信したメールがスパムメールかどうか • 扱う手法 ‣ 決定木(CART) ‣ ナイーブベイズ ‣ 最大エントロピー (ME) モデル ‣ サポートベクターマシン (SVM)
  • 21. サンプルデータ > library(maxent) # cf. help(maxent) > data <- read.csv(system.file("data/NYTimes.csv.gz", + package = "maxent")) > > subdata <- subset(data, Topic.Code %in% c(16L, 20L)) > > topic <- factor(subdata$Topic.Code) > corpus <- Corpus(VectorSource(subdata$Title)) > > D <- as.matrix(t(DocumentTermMatrix(corpus))) maxent パッケージの New York Times データを使用 # 2クラス分類なので Topic.Code が 16 と 20 のものだけ使用 # クラスラベル # 単語文書行列
  • 22. サンプルデータ > > rownames(D)[which(rownames(D) == "...")] <- "X..." > set.seed(0) > > trainIndex <- sample.int(ncol(D), ncol(D) * 0.5) > trainData <- t(D[, trainIndex]) > testData <- t(D[, -trainIndex]) > trainTopic <- topic[trainIndex] > testTopic <- topic[-trainIndex] # データの半分を学習データとして使用 # rpart でエラーになるので単語名を変更 maxent パッケージの New York Times データを使用
  • 24. ナイーブベイズ データの生成確率(条件付き確率)を最大化するクラスを選択 • 単純なモデルなので実装が楽(R で10行!!) ‣ cf. 10行でナイーブベイズ ∼Rって便利だね!∼ - あらびき日記 • 学習も高速 • 特定のクラスに出現しない単語の対処が必要(ディスカウンティング) ˆy = arg max y N i=1 log P(wi|y) + log P(y)
  • 25. 確率的言語モデル • 単語列 の同時確率 を与えるモデル • 文書の生成方法をモデル化 ‣ に従って単語列を生成することで文書を生成可能 wN 1 = w1w2 · · · wN w N M unigram の例(各単語が独立と仮定) P(wN 1 ) P(wN 1 ) P(d) = P(wN 1 ) = N i=1 P(wi)
  • 26. Unigram Mixtures w N M クラスごとに unigram を割り当てるモデル • カテゴリの異なる文書は単語の出現確率も異なるはず • 各文書は1つのクラス(トピック)から生成されると仮定 P(d) = y P(y) N i=1 P(wi|y) ˆy = arg max y N i=1 P(wi|y)P(y) ナイーブベイズはこの値を最大化するクラスを真のクラスと推定する y
  • 27. ディスカウンティング • ナイーブベイズの問題点 ‣ あるクラスの学習データに存在しない単語を含む文書は決してその クラスに分類されない(ゼロ頻度問題) - e.g. 0.9 x 0.95 x 0.8 x 0.99 x 0 = 0 • 加算法 ‣ 全単語に関して出現頻度を一律 +δ する ‣ あるクラスの学習データに現れない単語でも δ回出現しことになる • 単純グッド・チューリング推定法 ‣ 加算法より洗練されたディスカウンティング方法 ‣ cf. 単純グッド・チューリング推定法 (Simple Good-Turing Estimation) とは何ぞや? - あらびき日記
  • 28. ナイーブベイズを実装 myNaiveBayes - function(x, y) { lev - levels(y) ctf - sapply(lev, function(label) { colSums(x[y == label,]) }) ctp - t(t(ctf + 1) / (colSums(ctf) + nrow(ctf))) nc - table(y, dnn = NULL) cp - nc / sum(nc) structure(list(lev = lev, cp = cp, ctp = ctp), class = myNaiveBayes) } # 各クラスにおける単語出現頻度 # ラプラススムージングによるクラスごとの単語の出現確率 # 各クラスの生成確率 # 各クラスに所属する文書数
  • 29. ナイーブベイズを実装 predict.myNaiveBayes - function(model, x) { prob - apply(x, 1, function(x) { colSums(log(model$ctp) * x) }) prob - prob + log(as.numeric(model$cp)) level - apply(prob, 2, which.max) model$lev[level] } # 予測用の関数
  • 30. R でナイーブベイズ model - myNaiveBayes(trainData, trainTopic) pred - predict(model, testData) (tbl - table(pred, truth = testTopic)) truth pred 16 20 16 193 33 20 27 166 sum(diag(tbl)) / sum(tbl) [1] 0.8568019 # 正解率
  • 32. 決定木 (CART) の概要 根ノードから各ノードの判定基準に従って り着いた葉ノー ドによりクラスを分類 Yes No Yes No C1 C1 C2 A0 A1 • 量的変数と質的変数の混在する素性を容易に扱える • 構築したモデルの解釈や分類結果の要因の解明がしやすい • 大規模な素性を扱うには不向き • R では CART が使われる • CART は二分木を生成する
  • 33. • 不純度の減少を最大化させるようにノードを生成 = ノード生成後の不純度を最小化させるようにノードを生成 ‣ 不純度: はノード A に存在する i 番目のクラスのデータの割合 ‣ Gini 係数: ‣ 情報エントロピー: クラスの分布に偏りがあるほど不純度は小さくなる(小さい程良い) ‣ 不純度の減少量: CART の学習 I(A) = i f(piA) ∆I = p(A)I(A) − p(AL)I(AL) − p(AR)I(AR) f(p) = p(1 − p) f(p) = −p log(p) piA
  • 34. CART の学習例 Yes No AL AR A (50, 50) (15, 32) (35, 18) 素性1 0 クラス1とクラス2のデータが50個ずつある場合
  • 35. クラス1とクラス2のデータが50個ずつある場合 Yes No AL AR A (50, 50) (15, 32) (35, 18) p1AL = 15 47 p2AL = 32 47 p1AR = 35 53 p2AR = 18 53 p(AL) = 47 100 p(AR) = 53 100 p1A = 50 100 p2A = 50 100 p(A) = 1 素性1 0 ∆I = p(A) 2 i=1 piA(1 − piA) − p(AL) 2 i=1 piAL (1 − piAL ) − 2 i=1 piAR (1 − piAR ) = 0.058 CART の学習例
  • 36. Yes No AL AR A (50, 50) (7, 29) (43, 21) p1A = 50 100 p2A = 50 100 p(A) = 1 素性2 0 ∆I = p(A) 2 i=1 piA(1 − piA) − p(AL) 2 i=1 piAL (1 − piAL ) − 2 i=1 piAR (1 − piAR ) p1AL = 7 36 p2AL = 29 36 p1AR = 43 64 p2AR = 21 64 p(AL) = 36 100 p(AR) = 64 100 = 0.105 こちらの分割方法の方が不純度の減少量が大きい(良い分割方法) CART の学習例 クラス1とクラス2のデータが50個ずつある場合
  • 37. 1-SE ルール 1 2 3 4 5 6 Yes No Yes No C1 C2 A0 A1 Yes No Yes No A2 A3 A4 A5C2 葉ノードの数 CVエラー クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り CVエラーの最小値 枝刈りすることで過学習を抑制
  • 38. 1 2 3 4 5 6 Yes No Yes No C1 C2 A0 A1 Yes No Yes No A2 A3 A4 A5C2 CVエラーの最小値+標準偏差 クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り 葉ノードの数 CVエラー 枝刈りすることで過学習を抑制 1-SE ルール
  • 39. 1 2 3 4 5 6 Yes No Yes No C1 C2 A0 A1 Yes No Yes No A2 A3 A4 A5C2 CVエラーの最小値+標準偏差を最初に下回る点 クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り 葉ノードの数 CVエラー 枝刈りすることで過学習を抑制 1-SE ルール
  • 40. 1 2 3 4 5 6 Yes No Yes No C1 C2 A0 A1 Yes No Yes No A3 A4 A5C2 枝刈り C1 クロスバリデーション (CV) エラーとその標準偏差を基準に枝刈り 葉ノードの数 CVエラー 枝刈りすることで過学習を抑制 1-SE ルール
  • 41. R で決定木 library(mvpart) model - rpart(topic ~ ., + data.frame(trainData, topic = trainTopic)) pred - predict(model, data.frame(testData), + type = class) (tbl - table(pred, truth = testTopic)) truth pred 16 20 16 88 6 20 132 193 sum(diag(tbl)) / sum(tbl) [1] 0.6706444
  • 43. 最大エントロピー (ME) モデル P(y|x) = 1 Z(x) exp i λifi(x, y) = 1 Z(x) exp λT f(x, y) Z(x) = y∈Y exp λT f(x, y) (規格化定数) 経験的分布の特性を満たしつつ エントロピーを最大にするモデル • 柔軟に素性を設定することが可能 • 出力が確率 cf. 確率的言語モデル 6章
  • 44. 素性関数 fb,s(x, y) = 1 if b(x) is true and y = s 0 otherwise 素性の有無を表す2値関数 • e.g. 文書のクラスが class1 で最初が数字で始まれば1,そうでなければ0 • 単語文書行列を扱う場合は「コーパスの語彙数」x 「クラス数」個の素 性関数が存在し、出現頻度などを返す関数 fbegins−with−number,class1
  • 45. ME モデルの学習 対数尤度を最大化するパラメータを求める • 解探索アルゴリズム ‣ L-BFGS(省メモリな準ニュートン法) ‣ GIS (Generalized Iterative Scaling) • L1、L2 正則化で過学習を防ぐことも ‣ L2 正則化をする場合の目的関数は次のとおり L(P|λ) = (xi,yi) log P(yi|xi) L(P|λ) = (xi,yi) log P(yi|xi) − 1 2σ2 ||λ||2 ← 凸関数
  • 46. R で ME モデル library(maxent) model - maxent(trainData, trainTopic) pred - predict(model, testData)[, labels] (tbl - table(pred, truth = testTopic)) truth pred 16 20 16 190 36 20 30 163 sum(diag(tbl)) / sum(tbl) [1] 0.8424821
  • 47. SVM
  • 48. サポートベクターマシン(SVM) 2つのクラス間のマージンを最大化させるモデル y = sgn [f(x)] = 1 if f(x) ≥ 0 −1 if f(x) 0 f(x) = xi∈SV αik(xi, x) • パラメータ数の割に過学習に陥りにくい • カーネル関数により複雑な素性を容易に扱うことが可能 • 構築したモデルの解釈や分類結果の要因の解明が難しい • 多クラス分類をするには一工夫必要 cf. カーネル多変量解析 4章
  • 50. カーネル法の概要 入力 特徴ベクトル 線形多変量解析 入力空間 特徴空間入力空間 x f(x) = wT φ(x) 単純に高次元空間に写像すると計算量が増える
  • 51. 入力 特徴ベクトル カーネル多変量解析 入力空間 特徴空間入力空間 x f(x) = n i=1 αik x(i) , x) = wT φ(x) (カーネル関数) k x(i) , x = φ(x(i) )T φ(x) k カーネル関数が高次元空間での計算を隠 (カーネルトリック) カーネル法の概要
  • 52. カーネル関数 • 線形カーネル ‣ 現在の特徴空間で SVM などを適用させたい時に使用 • ガウスカーネル ‣ データの特性を維持したまま無限次元空間に写像 • 多項式カーネル ‣ 素性間の相互作用も考慮した空間に写像 ‣ 文字列カーネル cf. 文字列カーネルSVMによる辞書なしツイート分類 ‣ 文字列を部分文字列の出現回数から成る空間に写像 cf. 自然言語処理におけるカーネル法の利用 (PDF) カーネル関数によって写像する特徴空間は異なる k(x, x ) = xT x k(x, x ) = exp(−βx − x 2 ) k(x, x ) = (xT x + c)p
  • 53. SVM の学習 dカーネル空間で2クラス間のマージン を最大化するように学習 ‣ ソフトマージン最大化 ‣ 誤分類されるものにはペナルティを設けることで誤分類を許容 ‣ 凸二次計画問題に帰着できる(SMO などで解く) ||w||2 = αT Kα kij = k(xj, xi)K = (kij), , カーネル関数 min ξ,α 1 2 ||w||2 + C n i ξi Subject to ∀ i : yif(x) ≥ 1 − ξi サポートベクトル f(x) = xi∈SV αik(xi, x)
  • 54. R で SVM library(kernlab) model - ksvm(trainData, trainTopic, + kernel = vanilladot, kpar = list(), + scaled = FALSE) pred - predict(model, testData) (tbl - table(pred, truth = testTopic)) truth pred 16 20 16 184 28 20 36 171 sum(diag(tbl)) / sum(tbl) [1] 0.8472554
  • 58. やれなかったこと • LSI cf. 潜在的意味インデキシング(LSI)徹底入門 - あらびき日記 • LDA(topicmodels パッケージ) • 文書クラスタリング cf. Rで学ぶクラスタ解析 ‣ 階層的クラスタリング(hclust 関数) ‣ k-means(kmeans 関数) • 系列ラベリング cf. HMM, MEMM, CRF まとめ - あらびき日記 ‣ HMM ‣ MEMM • データの前処理 ‣ 文書中の URL の除去(置換) ‣ Unicode 正規化 cf. abicky/RUnicode · GitHub • KNB コーパスを使ってごにょごにょ cf. KNB corpus parser for R