toshiのエンジニア日記

できるエンジニアを目指して勉強した内容を日々まとめていきます。

トランプ大統領と金正恩氏の類似度を潜在的意味解析で判定する その2

f:id:ntoshi1900:20171017202249p:plain 前回の記事では、wikipediaから各国の大統領の説明を取得しました。

今回は、このデータを使って潜在的意味解析を行い、トランプ大統領と金正恩氏の類似度を判定していきたいと思います。

以下の手順3からの再開です。

  1. wikipediaから各国の指導者一覧を取得
  2. 各国の指導者のwikipediaページから説明文を抽出
  3. 潜在的意味解析を用いて各指導者を潜在的空間に射影
  4. トランプ大統領と金正恩氏の類似度を判定する

3.潜在的意味解析で各指導者を潜在的空間に射影

まず、文書-単語行列を作成するために、RMeCabパッケージを使用できるようにします。

RMeCabのインストールは以下のページなどを参考にしてください。

RMeCab - RとLinuxと...

注意点としては、MeCabそのものもインストールを忘れないようにしてください。 RMeCabパッケージだけインストールして使用しようとすると、RStudioがエラー落ちしてしまいます。

さて、RMeCabが使えるようになったら、以下のコードで各指導者の文書-単語行列を作成します。

### 3. 潜在的意味解析を実行

# パッケージの読み込み
library("RMeCab")

## 各指導者の説明文から単語-文書行列を作成する

# 説明文が入ったファイルのあるディレクトリを指定
path.dir <- "./desc"

# 単語カウントの最低出現回数を指定
minF <- 2

# 単語-文書行列の作成(名詞と動詞と形容詞のみ使用)
# 重みとしてtf-idfを用い,正規化した値を出力する
docterm <- docMatrix(path.dir, minFreq=minF, 
                     pos = c("名詞", "形容詞"), weight = "tf*idf*norm")

# 先頭2行を削除
docterm.cut <- docterm[-(1:2),]
# minF個以上の文書にある語句のみピックアップ
docterm.select <- docterm.cut[which(apply(docterm.cut, 1, function(x) sum(x > 0) ) >= minF), ]

docMatrix関数でファイルの格納されたフォルダのパスを指定することで、文書-単語行列が生成されます。pos引数で使用する品詞を選択できるのですが、今回は名詞と形容詞のみを使用することとします。

また、weight引数で単語の重み付けのオプションを指定できます。今回はTF-IDFと正規化を指定しています。

TF-IDFは文書内での出現頻度と全体での出現頻度の割合を用いて、全体ではあまり出現しないが、特定の文書にだけ頻出する語句の重みを高くする手法です。これにより、文書をより特徴付ける単語の数値を大きくすることができます。

さて、得られた単語-文書行列の次元と、マトリクスの一部を確認してみましょう。

# 次元の確認
dim(docterm.select

# いくつか要素のの確認
print(docterm.select[1:10, 1:2])
> dim(docterm.select)
[1] 2341  273
> print(docterm.select[1:10, 1:2])
      docs
terms  アイスランド 首相 ビャルニ・ベネディクトソン.txt アイスランド 大統領 グズニ・ヨハンネソン.txt
  こと                                       0.06853151                                   0.03073494
  回復                                       0.18593731                                   0.00000000
  獲得                                       0.13186948                                   0.00000000
  議席                                       0.20001793                                   0.00000000
  月                                         0.09031433                                   0.01620163
  後                                         0.09847178                                   0.00000000
  首相                                       0.09794949                                   0.00000000
  進歩                                       0.36285404                                   0.00000000
  政権                                       0.08484629                                   0.00000000
  選挙                                       0.12390810                                   0.01852339

273個の文書に対し、2341種類の単語が存在していることがわかります。 また、各文書に対して単語が関連付けられていることが確認できます。「こと」や「月」などのどの文書にも含まれそうな単語が入っていますが、TF-IDFの効果もあり、重みは小さめになっていますね。

それでは、この文書-単語行列をSVDで低ランク近似しましょう。

## svdによる低ランク近似を実行
# 特異値の累積和が何割にいくまでを残すかのパラメータ
d.base <- 0.5

# svdを実行
svd.docterm <- svd(docterm.select)
# 低ランク近似
index <- which(cumsum(svd.docterm$d / sum(svd.docterm$d)) <= d.base)
svd.docterm.low <- NULL
svd.docterm.low$u <- svd.docterm$u[, index]
svd.docterm.low$v <- svd.docterm$v[, index]
svd.docterm.low$d <- svd.docterm$d[index]
rownames(svd.docterm.low$u) <- rownames(docterm.select)
rownames(svd.docterm.low$v) <- colnames(docterm.select)

SVDによる低ランク近似では、特異値の大きさが擬似的に潜在トピックの重要度を表すため、特異値の低いものは価値の低い潜在トピックとし、行列から取り除きます。 ここでは、どれくらいまで次元を落とすかという基準に、特異値の累積和が特異値全体の何割に到達するまでを残すかという基準を用います。

この値はハイパーパラメータになるため、本来は最適化すべき項目ですが、取り合えず0.5でやってみました。

では、低ランク近似後の行列の次元を確認してみましょう。

# 次元の確認
dim(svd.docterm.low$v)
> dim(svd.docterm.low$v)
[1] 273  82

2341次元あった単語ベクトルが、82次元まで削減できていますね。

では、この低ランク行列を保存して、余計な変数を削除しておきましょう。

# 低ランク行列の保存
save(svd.docterm.low, file="./svd_docterm_low.Rdata")

# 要らない変数を削除
rm(list=ls())
gc(); gc();

4.トランプ大統領と金正恩氏の類似度を判定する

ではついに!本題のトランプ大統領と金正恩氏の類似度を判定して見ましょう!

まずは、任意の2文書の全ての組み合わせでcosine類似度を計算します。

### 4. トランプ大統領と金正恩総書記の類似度を計算する

# 低ランク行列の読み込み
load("./svd_docterm_low.Rdata")

# cosine類似度の計算
mat.v <- t(svd.docterm.low$v)
matrix.cos <- apply(mat.v, 2, function(x){
  apply(mat.v, 2, function(y){
    crossprod(x, y) / (sqrt(crossprod(x) * crossprod(y)))
  })
})
diag(matrix.cos) <- NaN

これで、任意の組の類似度が得られるようになりました。

では、トランプ大統領と金正恩氏の類似度を出力してみましょう!

# トランプ大統領の番号を取得
index.trump <- which(rownames(svd.docterm.low$v) == "アメリカ合衆国 大統領 ドナルド・トランプ.txt")

# 金正恩総書記の番号を取得
index.kim <- which(rownames(svd.docterm.low$v) == "北朝鮮 朝鮮労働党委員長 金正恩.txt")

# トランプ大統領と金正恩総書記の類似度を出力
cat(matrix.cos[index.trump, index.kim])
> cat(matrix.cos[index.trump, index.kim])
0.05124007

んー、微妙ですね。

似ていない場合はマイナスの値をとるため、似ていないこともない、といったところでしょうか……

数値だけだとよくわからないので、トランプ類似度偏差値を求めてみます。

# トランプ大統領の類似度偏差値を計算
cos.trump <- matrix.cos[index.trump, ]
dev.trump <- (cos.trump - mean(cos.trump[-index.trump])) / sd(cos.trump[-index.trump]) * 10 + 50

# 金正恩総書記のトランプ偏差値を表示
print(dev.trump[index.kim])

# 金正恩総書記のトランプ順位の表示
trump.order <- dev.trump[order(-dev.trump)]
which(names(trump.order) == "北朝鮮 朝鮮労働党委員長 金正恩.txt")
> print(dev.trump[index.kim])
北朝鮮 朝鮮労働党委員長 金正恩.txt 
                          53.88065 
> which(names(trump.order) == "北朝鮮 朝鮮労働党委員長 金正恩.txt")
[1] 58

偏差値53.88……。とっても微妙ですね。

順位は273人中58位。悪くはないですが……期待したほどではない結果です。

では、トランプ大統領に最も似た指導者はいったい誰なのでしょうか?出力してみましょう。

# トランプ偏差値上位10人表示
print(trump.order[1:10])
> print(trump.order[1:10])
    フィリピン 大統領 ロドリゴ・ドゥテルテ.txt       パラグアイ 大統領 オラシオ・カルテス.txt 
                                     129.29781                                       94.31164 
                        韓国 大統領 文在寅.txt             ボツワナ 大統領 イアン・カーマ.txt 
                                      88.77724                                       84.07969 
      ベネズエラ 大統領 ニコラス・マドゥロ.txt キプロス 大統領 ニコス・アナスタシアディス.txt 
                                      77.19171                                       76.18902 
                バチカン 教皇 フランシスコ.txt                 日本 内閣総理大臣 安倍晋三.txt 
                                      73.40518                                       73.26178 
        ジンバブエ 大統領 ロバート・ムガベ.txt         カナダ 首相 ジャスティン・トルドー.txt 
                                      72.60202                                       71.23118 

なんと……類似度1位はフィリピンのドゥテルテ大統領で、なんとその偏差値129!!

大学受験とかで考えたらとんでもない化け物ですね!

そんなに似ているのか……とドゥテルテ大統領wikipediaページを見てみると、以下のような記述がありました。

選挙戦中は、同時期に進行していたアメリカ大統領選挙に立候補する共和党候補者を選出する予備選挙で過激な発言を行う人物として注目されていた共和党ドナルド・トランプになぞらえ、「フィリピンのトランプ」とも揶揄された。

まんま記述があるじゃないですか!

おそらく、この「トランプ」という単語とか、「大統領」という単語などが共通しているため、類似度が高くなったようですね。

それぞれの文書を特徴付ける上位10単語を確認してみましょう。

## 各指導者をもっとも特徴付ける単語を調べる

# 全指導者の文書-単語行列を低ランク行列で再現
docterm.app <- svd.docterm.low$u %*% diag(svd.docterm.low$d) %*% t(svd.docterm.low$v)

# トランプ大統領をもっとも特徴付ける単語を取得
term.trump <- docterm.app[,index.trump]

# トランプ偏差値トップの人をもっとも特徴付ける単語を取得
index.toptrumper <- which(rownames(svd.docterm.low$v) == names(trump.order)[1])
term.toptrumper <- docterm.app[,index.toptrumper]

# それぞれの特徴語の表示
print(term.trump[order(-term.trump)][1:10])
print(term.toptrumper[order(-term.toptrumper)][1:10])
> print(term.trump[order(-term.trump)][1:10])
  トランプ       中国     北朝鮮       こと       発言 フィリピン         者       米国     大統領         人 
0.34969567 0.20357833 0.17085699 0.13165502 0.12459490 0.10411936 0.10155474 0.09888835 0.09590375 0.09159767 
> print(term.toptrumper[order(-term.toptrumper)][1:10])
      中国   トランプ     大統領     北朝鮮         年 フィリピン       依存       発言       こと       政策 
0.18541337 0.15940956 0.11098858 0.08911449 0.07826024 0.07240096 0.06509865 0.06227812 0.06085533 0.05706989 

やはり、トランプ、大統領、といった単語が含まれていますね。

他には、中国、北朝鮮、といった単語が共通して出ており、体外的な交渉相手などが似ているということでしょうか。

まぁここに関しては、日本のwikipediaを解析しているため、日本から見た動きが記載されやすい、というバイアスは大きく受けている気はします。

実際に似ているかはともかく、日本から見た影響力の大きさ、という点では似ているのかもしれませんね。

おまけ:理想の指導者を探してみる

潜在的意味解析の面白いところは、文書の検索もできるところです。

任意の文書をクエリとして与え、そこに含まれる単語を潜在意味空間に射影することで、文書との距離を計算できるようになります。

では実際に、理想の指導者に最も類似する指導者を探してみましょう。

まずは、クエリを与えると、それに最も近い5つの文書を返す関数を定義しましょう。

## 理想の指導者を探してみる

# 理想の指導者検索関数の設定
searchLeader <- function(query, docterm){
  
  # 長さ1の文字列のみ受付
  if(mode(query) != "character") return(NULL)
  if(length(query) > 1) return(NULL)
  
  # 形態素解析
  query.term <- RMeCabC(query) %>% unlist()
  
  # 動詞,形容詞だけ取り出す
  query.term <- query.term[names(query.term) %in% c("名詞", "形容詞")]
  
  # 文書-単語行列に含まれるもののみを取り出し,ベクトルを生成
  term.list <- matrix(0, nrow=nrow(docterm$u))
  rownames(term.list) <- rownames(docterm$u)
  term.list[rownames(term.list) %in% query.term] <- 1
  
  # 使用される単語を確認
  cat("Using terms:\n")
  print(term.list[which(term.list[,1] == 1), ])
  
  # 文書-単語行列に全く一致する単語がない場合はNULLを返す
  if(sum(term.list) == 0) return(NULL)
  
  # 潜在意味空間中での質問文ベクトルを計算
  query.vec <- t(term.list) %*% docterm$u %*% solve(diag(docterm$d))
  query.vec <- as.vector(query.vec)
  
  # 潜在意味空間中でのcosine類似度を計算
  mat.v <- t(docterm$v)
  query.cos <- apply(mat.v, 2, function(x){
    crossprod(x, query.vec) / (sqrt(crossprod(x) * crossprod(query.vec)))
  })
  rank.leader <- round(sort(query.cos,de=T),3)
  
  return(rank.leader[1:5])
}

では、この関数を使用して、理想の指導者を探してみましょう。

以下のようなクエリを与えてみます。

# 指導者検索してみる
query <- "効果的な政策を実現することができ,経済の発展に貢献し,人々から賞賛され,尊敬を集める"

これが理想的な指導者かどうかは微妙ですが、すばらしい人物には違いないですね!

では検索してみましょう!

res <- searchLeader(query, svd.docterm.low)
print(res)
> res <- searchLeader(query, svd.docterm.low)
Using terms:
こと   的 経済 効果 貢献 実現 人々 政策 尊敬 発展 
   1    1    1    1    1    1    1    1    1    1 
> print(res)
  スリランカ 大統領 マイトリーパーラ・シリセーナ.txt     ジョージア 首相 ギオルギ・クヴィリカシヴィリ.txt 
                                               0.477                                                0.427 
ジョージア 大統領 ギオルギ・マルグヴェラシヴィリ.txt             インドネシア 大統領 ジョコ・ウィドド.txt 
                                               0.377                                                0.372 
    コスタリカ 大統領 ルイス・ギジェルモ・ソリス.txt 
                                               0.328 

スリランカの大統領が一番該当すると出てきました。

ちょっとどんな人物か良くわからないので、wikipediaページを見てみましょう。

マイトリーパーラ・シリセーナ - Wikipedia

んー、特別クエリに該当しているようには思えないんですが……

取り合えず文章が短めなので、出現する単語の種類が少なくなり、「政策」とか単語の重みが強くなったんでしょうか。

試しにほかのクエリでもやって見ます。

query <- "親日で日本が大好きで友好的"
res <- searchLeader(query, svd.docterm.low)
print(res)
> res <- searchLeader(query, svd.docterm.low)
Using terms:
  的 日本 友好 
   1    1    1 
> print(res)
    シンガポール 首相 リー・シェンロン.txt     パラオ 大統領 トミー・レメンゲサウ.txt 
                                     0.914                                      0.475 
モルディブ 大統領 アブドゥラ・ヤミーン.txt             日本 内閣総理大臣 安倍晋三.txt 
                                     0.457                                      0.320 
        ツバル 総督 イアコバ・イタレリ.txt 
                                     0.190 

最も親日な指導者はシンガポールの首相でした。

リーシェンロン首相はこんな人です。

リー・シェンロン - Wikipedia

うーん、また文章が短く、日本に関する記述が目立ちますね。

どうも、検索のような短いクエリと距離を計算すると、文書が短く、たまたまクエリに近い単語を含んでいる文書が上位に上がってきてしまうようです。

今回のような各文書に含まれる単語数が大きく違う場合は、一工夫必要そうです。

まとめ

今回は潜在的意味解析を用いて、トランプ大統領と金正日氏の類似度を判定してみました。

その結果、実はトランプ大統領が似ているのは金正日氏ではなく、ダントツでフィリピンのドゥテルテ大統領であることがわかりました(飽くまで今回の解析の結果です)。

今回は日本のwikipediaの文書を解析したため、日本の視点から見た行動、というバイアスのかかった結果になってしまいましたが、例えばTwitter上での指導者に対する意見を分析するなどすれば、世間からどう思われているかという点での類似度が計算できるかもしれません。

ちなみに潜在的意味解析の改良法で確率的潜在意味解析(PLSA)という手法もあるので、時間があれば勉強してみたいですね。

トランプ大統領と金正恩氏の類似度を潜在的意味解析で判定する その1

f:id:ntoshi1900:20171016212430j:plain

こんにちは、雑魚エンジニアのtoshiです。

最近、トランプ大統領と金正恩氏が似ていると、ネット上で言われるようになってきました。 その証拠に、Google検索で「トランプ 金正日」と入力すると、サジェストに「似てる」という単語が上位に出てきます。

お互い過激な発言が目立ちますから、仕方ないかなぁ、といったところですね。

さて、今回はそんな二人が本当に似ているのかどうか、潜在的意味解析という技術を使って解析してみたいと思います。

潜在的意味解析とは

潜在的意味解析(LSA)とは、文書に存在する単語を用いて、潜在的なトピックを抽出する技法です。

具体的に言うと、文書に存在する単語を出現回数を数え上げたベクトルに変換(Bag of Words)し、SVDによる低ランク近似を行います。

そうすることで、類義語のような、同じような使われ方をする単語が潜在的なトピックとして畳み込まれます。

そのため、単純に文書-単語行列によって類似度を判定するのと比べ、より柔軟に類似度判定を行うことができます。

潜在的意味解析の詳細な説明は省きますが、もう少しわかりやすく知りたければ、以下のあらびきさんのエントリが分かりやすいです(潜在的意味インデキシング(LSI)と書かれていますが、潜在的意味解析と同じです)。

abicky.net

作業環境と手順

以下の環境で作業を行います。

  • OS  : Windows7
  • 言語 : R言語 (バージョン3.4.2)
  • IDE  : Rstudio (バージョン1.1.383)

以下のような手順で解析を行っていきます

  1. wikipediaから各国の指導者一覧を取得
  2. 各国の指導者のwikipediaページから説明文を抽出
  3. 潜在的意味解析を用いて各指導者を潜在的空間に射影
  4. トランプ大統領と金正恩氏の類似度を判定する

では、早速やっていきましょう!

1.wikipediaから各国の指導者一覧を取得

Webスクレイピングを行うには、rvestというパッケージが利用できます。 CRANからインストールできますので、しておきましょう。

install.package("rvest")

ちなみにDBpediaからSPARQLでデータ引っ張ってくることも考えたのですが、DBpedia上の各国の指導者一覧ページが最新でなかったため、wikipediaからスクレイピングすることにしました。

さて、各国の指導者一覧のHTMLを読み込むコードを以下に示します。

### 0. 準備
# カレントディレクトリの設定
setwd("~/R_workspace/LSA")

# パッケージの読み込み
library("rvest")

### 1. wikipediaから各国の指導者一覧を取得
# 各国の指導者一覧HTMLの取得
url <- "https://ja.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E5%90%84%E5%9B%BD%E3%81%AE%E6%8C%87%E5%B0%8E%E8%80%85%E4%B8%80%E8%A6%A7"
data.html <- read_html(url)

# 指導者テーブルの取得
html.table <- data.html %>% html_nodes(xpath="//table") %>% .[1] %>% 
  iconv(from = "UTF-8", to="UTF-8")

read_html関数でHTMLを読み込んだ後、html_nodes関数でタグを指定してやることで、そのタグを持つHTMLコードだけを取得できます。 今回はtableタグを指定しているので、テーブルだけが取得できています。

目的のテーブルは1番目のテーブルなので、1番目の要素を取り出し、iconv関数で文字化けを解消しておきます。 iconv関数は文字コードを変換するためのものですが、これをしておかないととんでもなく文字化けするので、重要です。

さて、本来であればここからhtml_tableという関数を用いることでテーブルの各セルの要素を取り出すことができるのですが、

  • html_table関数では結合セルをうまく扱えない
  • html_table関数では要素だけをdata.frameに変換するためハイパーリンクが取り出せない

ことから、自力でHTMLコードを解析していきたいと思います。

少し煩雑になりますが、以下がテーブルのHTMLから各要素を取り出すコードです。

# trタグで分割して、各行のデータを取り出す
html.leaders <- html.table %>% strsplit("<tr>") %>% .[[1]]

# 各指導者ごとにデータを作成
len <- length(html.leaders)
table.leader <- data.frame("国名"=rep(NA, len - 2), 
                           "役職"=rep(NA, len - 2), 
                           "名前"=rep(NA, len - 2), 
                           "URL"=rep(NA, len - 2))

# HTMLを自力で分解する
# 1,2行目はヘッダなのでスルー
for(index.loop in 3:len){
  # tdタグで分割
  td.vec <- html.leaders[index.loop] %>% strsplit("<td") %>% .[[1]]
  # td.vec[1]から国名を取得
  # 結合セルの関係で既に埋められている場合スルー
  if(is.na(table.leader[index.loop - 2, "国名"])){
    # 結合セルだった場合,先のデータも埋めるため変数にrowspanを代入
    if(length(grep("rowspan", td.vec[1])) > 0){
      rowspan <- td.vec[1] %>% strsplit("rowspan=\"") %>% .[[1]] %>% 
        .[2] %>% strsplit("\"") %>% .[[1]] %>% .[1] %>% as.numeric()
    } else {
      rowspan <- 1
    }
    # タグの後ろにある要素を取り出す
    td.split <- td.vec[1] %>% strsplit(">") %>% .[[1]]
    for(sub.loop in 1:length(td.split)){
      # 国名はaタグの終わり前にあるため,</で分割できた0文字以上の要素を取出
      temp.data <- td.split[sub.loop] %>% strsplit("</") %>% .[[1]]
      if(length(temp.data) > 1 & nchar(temp.data[1]) > 1){
        # 結合セルの場合、複数セル分の国名を代入する
        start <- index.loop - 2
        end <- start + rowspan - 1
        table.leader[start:end , "国名"] <- temp.data[1]
        break
      }
    }
  }
  # td.vec[2]から役職を取得
  # 結合セルの関係で既に埋められている場合スルー
  if(is.na(table.leader[index.loop - 2, "役職"])){
    # 結合セルだった場合,先のデータも埋めるため変数にrowspanを代入
    if(length(grep("rowspan", td.vec[2])) > 0){
      rowspan <- td.vec[2] %>% strsplit("rowspan=\"") %>% .[[1]] %>% 
        .[2] %>% strsplit("\"") %>% .[[1]] %>% .[1] %>% as.numeric()
    } else {
      rowspan <- 1
    }
    # タグの後ろにある要素を取り出す
    td.split <- td.vec[2] %>% strsplit(">") %>% .[[1]]
    for(sub.loop in 1:length(td.split)){
      # 役職名もaタグの終わり前にあるため,</で分割できた0文字以上の要素を取出
      temp.data <- td.split[sub.loop] %>% strsplit("</") %>% .[[1]]
      if(length(temp.data) > 1 & nchar(temp.data[1]) > 0){
        # 結合セルの場合、複数セル分の役職名を代入する
        start <- index.loop - 2
        end <- start + rowspan - 1
        table.leader[start:end, "役職"] <- temp.data[1]
        break
      }
    }
    # 名前が入っているデータのインデックスを設定
    name.index <- 3
  } else {
    # 役職が結合セルの場合,名前が入っているインデックスがずれるので補正
    name.index <- 2
  }
  # 名前とURLを取得
  # タグの後ろにある要素を取り出す
  td.split <- td.vec[name.index] %>% strsplit(">") %>% .[[1]]
  for(sub.loop in 1:length(td.split)){
    # リンクが存在する要素にリンクと名前が入っているため、href="で分割
    temp.data <- td.split[sub.loop] %>% strsplit("href=\"") %>% .[[1]]
    if(length(temp.data) > 1){
      # URLと名前をそれぞれ取り出して記録
      temp.data <- temp.data[2] %>% strsplit("\"") %>% .[[1]]
      table.leader[index.loop - 2, "URL"] <- 
        paste0("https://ja.wikipedia.org", temp.data[1])
      table.leader[index.loop - 2, "名前"] <- 
        td.split[sub.loop + 1] %>% strsplit("</") %>% .[[1]] %>% .[1]
      break
    }
  }
}

# 一人の人物が複数の役職を兼任している場合,2つ目の役職の名前とURLにNAが入っているので削除
table.leader <- na.omit(table.leader)

だいぶ愚直に書きましたが、いちおうこれでテーブルのHTMLから各国の指導者情報を取り出すことができます。 HTMLの構造が変わると対応できない等、結構クソコードな気はしますが……

まぁ、スクレイピング部分は今回のお題の本質的な部分ではないので良しとしましょう(^_^;)

さて、中身を確認してみましょう。

# ちゃんと取れているか確認
View(table.leader)

f:id:ntoshi1900:20171016230220p:plain

どうやらちゃんと取れてそうですね。

では、このデータを保存して、余分な変数を削除しておきましょう。

# table.leaderの保存
save(table.leader, file="./table_leader.Rdata")

# 要らない変数を削除
rm(list=ls())
gc(); gc();

2.各国の指導者の各ページから説明文を抽出

各国の指導者の説明文も、rvestパッケージで取得することができます。

前回はテーブルの読み込みだったため html_nodes関数においてtableタグを指定しましたが、今回はpタグを指定して読み込みましょう。

以下に、それぞれの説明文を取得するコードを示します。

### 2. 各指導者のWikipediaページから説明文を取得し保存する
# table.leaderの読み込み
load("./table_leader.Rdata")

# 説明文の保存先作成
path.dir <- "./desc"
if(!file.exists(path.dir)) dir.create(path.dir)

# 全指導者の説明文を取得し保存
for(index.loop in 1:nrow(table.leader)){
  
  # URL設定
  url <- table.leader[index.loop,4]
  
  # タイムアウトの回数を記録する変数の作成
  timeout.count <- 0
  
  # データの取得
  while(TRUE){
    data.html <- try(read_html(url), silent = FALSE)
    # データが取得できた場合と,404エラーの場合は次の処理へ
    if(class(data.html)[1] != "try-error"){
      break
    } else if(length(grep("error 404", data.html[1])) > 0){
      break
    }
    # タイムアウト等のエラーの場合は5秒まってリトライ
    Sys.sleep(5)
    
    # タイムアウトの回数を記録し、10回連続失敗したら諦める
    timeout.count <- timeout.count + 1
    if(timeout.count >= 10) break
  }
  
  # 記事が存在しない場合はスルー
  if (class(data.html)[1] != "try-error" & timeout.count < 10) {
    # 本文の取得
    sentence.main <- data.html %>% html_nodes(xpath="//p") %>% 
      iconv(from = "UTF-8", to="UTF-8")
    
    # 全文の結合
    sentence.main.all <- paste(sentence.main, collapse = "")
    
    # タグと参考文献の削除
    sentence.main.notag <- gsub("\\[.*?]", "", 
                                gsub("<.*?>", "", sentence.main.all))
    
    # 括弧書きの削除
    sentence.main.notag <- gsub("(.*?)", "", 
                                gsub("\\(.*?)", "", sentence.main.notag))
    
    # 説明文を保存
    filename <- paste0(path.dir, "/", 
                       paste(table.leader[index.loop,1:3], collapse=" "), ".txt")
    write(sentence.main.notag, filename)
    
  }
  
  cat("finish : ", index.loop, "/", nrow(table.leader), "\n")
}

# 要らない変数を削除
rm(list=ls())
gc(); gc();

後にRMeCabを用いて文書-単語行列に変換するために、それぞれの説明文を「国名 役職名 人名」のファイル名で保存しています。

さて、ちゃんと取れているか、保存したテキストファイルを確認してみましょう。

f:id:ntoshi1900:20171016234227p:plain

長いですが、取れてそうですね。

続きは次回の記事で

まだ下準備しかできていませんが、だいぶ長くなってしまったので、続きは次回記事で紹介したいと思います。

果たしてトランプ大統領と金正恩氏は似ているのか!?

乞うご期待です!

次回記事はこちら

CEATEC 2017 に行ってきた

f:id:ntoshi1900:20171006214844p:plain

毎度おなじみ、IT技術とエレクトロニクスの国際展示会のCEATECに行ってきました!

個人的に気になったやつを紹介していきます!

CASIOさんの2.5次元プリンター Mofrel

f:id:ntoshi1900:20171006214922j:plain

これは一目見てすごい!といえる機器でした。

2.5次元という名の通り、表面形状を自由に変化させ、凹凸を表現できるプリンターです。

これの凄いところが、3Dプリンターのように物質を積層するのでなく、紙そのものを膨張させて立体を表現しているところです。

そのため、3Dプリンターのように多大な時間をかけることなく、3~6分程度で立体を表現できてしまいます。

それだけスピーディにも関わらず、精巧さはかなりのものでした。

f:id:ntoshi1900:20171006214944j:plain
立体的に印刷されたカメレオン。一瞬本物かと見紛うほどのクオリティ

ただ、弱点として

  • 凹凸の表現が1.8mmまでが限界
  • 専用紙が1枚1000円ほどと高額

などがあるそうです。

今は製品プロトタイプの作成などで使ってほしいとのことでしたが、コストもかかりますし、どういう分野に活用していけるかを考えるのが今後の課題になりそうですね。

残念ながらコンシューマ向けに出てくることは当分ないでしょう。

これで年賀状作ってみたかったな(´・ω・`)

ASUKANETさんの空中ディスプレイ

f:id:ntoshi1900:20171006221952j:plain

これも、かなりインパクトのある製品でした。

表示している画面が、空中に浮き出て見えるディスプレイです。

そこにあるのに触れない、という不思議な感覚を味わうことができます。

実に未来感のある製品ですね!

f:id:ntoshi1900:20171006221959j:plain
サイネージの例。写真だと微妙ですが、リアルに見るといい感じに飛び出して見えます

視野角は結構限定されていて、特定の位置に立たないと立体には見えづらいです。

逆にそれを利用して、ATMの操作インターフェースに使おうという考えもあるみたいですね。

ちなみに、逆側の展示できれいなお姉さんが飛び出る展示をやっていましたが、超絶混雑で見れませんでした……

Twitterとかでかなり話題になっていましたね。

2次嫁と生活できる日も、そう遠くはないかも?笑

4年連続!オムロンさんの卓球ロボ フォルフェウス

f:id:ntoshi1900:20171006215417j:plain
おなじみのアイツ。今年も体験ができました

CEATEC参加者にはおなじみですね。4年連続の出展のフォルフェウスさんです。

今年はサーブ機能とスマッシュ機能が搭載されたらしいです。

サーブはちゃんとトス→打球でできていて凄かったです。ボールのトスまで時間がかかるのがネックですが……

スマッシュは見れなかったですね。

ちなみに私も体験してみましたが、どこに打っても大体返してくれるので凄いです。ロングは少し苦手なようですが。

ただ、ラケットのラバーがツルツルのカスカスですごいやりづらい……打っても全然飛びません

がんばってループドライブ打ってみたところ、普通にオーバーしていたので、まだ回転には対応できてないみたいですね。

来年にはどこが進化するのか楽しみです。

話題のブロックチェーンMUFGさんのMUFGコイン

f:id:ntoshi1900:20171006215015j:plain
説明板の横に自販機で水が買えるデモが展示されていました。

満を持しての登場ですね。かなり前から話題になっていました。

MUFGコインは1コイン=1円と固定された価値をもつコインなので、実質電子マネーみたいなものですね。ビットコインみたいな投機には使われません。

電子マネーとの違いは、個人間でコインのやりとりができるところで、例えば割り勘のときに皆からコインを集めたりできます。

送金手数料もかからないので、かなり取り回しは良さそうですね。

ではどうやって儲けるのか、というと、このコインでの収益は一切期待していないそうです。

MUFGがこのコインを出した理由としては、ブロックチェーン技術に対する危機感。

ブロックチェーン技術が、もしかしたら銀行の存在価値を奪ってしまうかもしれない。

そんな破壊的イノベーションが起こったときに、生きる術としてブロックチェーンに乗り換えられるように今のうち技術を作っておく、というアプローチのようですね。

対応サービスが増えればなかなか使えるようになると思います。

流行のチャットボット

WatsonサミットやビッグデータEXPOなんかでもそうでしたが、相変わらず流行ってますねチャットボット。

CEATECでは会話できるロボットやARのキャラクターなどが多数展示されていました。

Watson先生万歳かと思いきや、意外と内製のエンジン使ってるとこも多かったです。

Nextremerさんの浅草観光案内ロボ AI-SAMURAI

f:id:ntoshi1900:20171006215009j:plain
無骨な鎧姿に、顔だけめっちゃサイバー。ちょっと怖い

浅草の観光情報を学習したチャットボットを搭載した観光案内ロボ。

内製のエンジンを使用しておりDeepLearningは使ってないらしいですが、精度は良かったです。

浅草のオススメのお土産を聞いてみたら芋ようかんをおすすめされました 笑

豆蔵さんのチャットボットエンジン

f:id:ntoshi1900:20171006225652j:plain
ロボ自体に特別な意味があるわけではなく、しょぼいハードウェアでも動きますよというデモ

とにかく軽量で、スタンドアロンでも動きます、ということを売りにしたチャットボットエンジン。

簡単に辞書も追加できて、クラウドに接続しなくてもいいです、という非常に取り回しの利く感じはよさそうだった。

ラズパイでも動くというのがすごい。

Panasonicさんの幼児向けソーシャルロボット cocotto

f:id:ntoshi1900:20171006215052j:plain
凄いスムーズにぐるぐる動き回ります。移動の機構がすごい

ものすごく軽快に動き、ひときわ目を引いていたロボット。

この笑顔といい、とってもかわいらしいですね。一人暮らしの寂しさを紛らわすにはいいかも?笑

動きのデモだけだったので、会話性能は不明。

バンダイナムコさんのガンシェルジュ ハロ

f:id:ntoshi1900:20171006215000j:plain
めっちゃコアなガンダムのクイズ出してきます。まったく分からん

これはいろいろと話題でしたね。

私はあんまりガンダム詳しくないですが、再現度は高いのではないでしょうか。

会話内容がめっちゃガンダムなので、ガンダム好きにはたまらない一品だと思います。

KDDIさんのヨリソイ型ハーフヒューマノイド レナ

f:id:ntoshi1900:20171006215100j:plain
三太郎って言ったらちゃんと反応してくれました

5Gの説明をしてくれるARキャラクターです(かわいい)

LTEの説明ができないなど、中身は結構へっぽこでしたが…… 笑

詳しく聞いてみると、他社製のエンジンをつかっているらしく、適当なことを言うと取り合えずなんか返事しちゃうあたりDeepLearningっぽい挙動でした。

まとめ

やはりCEATECは未来感を感じさせてくれる楽しいイベントです。

今回もいろいろとワクワクさせられました。

空中ディスプレイなんかは特に、サイネージのあり方を大きく変えそうです。

今回は紹介しませんでしたが、VRの展示もかなり流行っていて、いろいろなコンテンツが紹介されていました。

エンターテインメント分野での発展も、今後楽しみですね。

Amazon Echoを買う前に考えたい3つのリスク

f:id:ntoshi1900:20171003224128p:plain

こんにちは。 雑魚エンジニアのtoshiです。

遂に、Amazon Echoの日本での発売が発表されましたね!

news.yahoo.co.jp

米国でも品切れ状態が続いているらしく、大注目の商品です。

個人的にも、こういう未来感のあるデバイスはワクワクします。

さて、いろいろと期待が膨らむAmazon Echoですが、実は米国ではいろいろな問題が発生しています。

今回は日本に上陸する前に、Amazon Echoのリスクに関してまとめてみたいと思います。

リスク1:テレビの声に反応して誤動作する

Amazon Echoには個人認証機能がありません。

それゆえ、所有者本人でなくても、テレビの登場人物であっても操作が可能なのです。

実際の事例として、アニメの声に反応して誤動作した事例がありました。

www.dream-seed.com

これは、米国の国民的アニメ“サウスパーク”の一場面で、アニメ中のキャラクターがAmazon Echoへ命令した結果、実際にTVの前に設置したAmazon Echoが反応してしまった事例です。

今回はカートに変なものが追加される程度でしたが、その気になればアニメDVDを大量に購入させることもできるかもしれません。

対策としては、Amazon Echoは設定を変更することで起動ワードを変えたり注文時にパスワードを求めるようにしたりできるそうです。

可用性と機密性のバランスをしっかりと考えて、初期設定を万全に行いたいですね。

リスク2:子供に操作されてしまう

問題点1とも関連しますが、使い方さえ覚えてしまえば子供でもAmazon Echoに命令を出すことができます。

実際の事例として、アメリカの6歳の女の子が親に内緒で160ドル分のドールハウスとクッキーを注文してしまったという事例があります。

www.lonestar.jp

最近は子供が親のスマホのロックを突破して、勝手に使ってしまうといった話も聞きますよね。

それと同じように、たとえAmazon Echoにパスワードを設定したところで、子供は親の注文のときのセリフを覚えてAmazon Echoのロックを突破してしまうかもしれません。

注意しておかないと、親は知らぬうちに子供に大量のプレゼントをしてしまうハメになってしまうかも……

また、そのニュースをテレビで放映したところ、視聴者のAmazon Echoがそれにまた反応してドールハウスを注文しようとしたという笑えない話もあります。

まさに問題点1が発現した事例ですね。

news.livedoor.com

これは実際に注文までいってしまった人もいるらしく、番組に相当数の苦情が入ったとか。

対策としては、Amazon Echoは声による注文をオフにすることができるようなので、安全性を考えると注文はできない設定にするのが良いかもしれません。

自分もまったく注文できなくなるのは残念ですが……

リスク3:DolphinAttackにより攻撃される

音声認識のデバイスを攻撃する手段としてDolphinAttack(ドルフィンアタック)と呼ばれるものがあります。

【ドルフィンアタック】スマートスピーカーはハッキング可能!? 「人間に聞こえない音声で操作可能」との論文が発表 | ロボスタ

これは、人間には聞こえない音声で音声認識バイスを攻撃する方法で、持ち主に気づかれずに任意の命令を実行させることが可能です。

DolphinAttackに関して書かれた論文では、iPhoneのSiriやAmazon Echoなど、16種類のハードウェア、7種類のAI音声アシスタントで操作できることが報告されています。

iPhoneなどは基本的に常に携帯するのものなので、もしDolphinAttackをかけられても音声の返答があるため異変に気付くことは容易です。

しかし家に常備するAmazon Echoは留守中など、そばを離れていると時に操作されると気付くことができません。

使わないときは必ず電源をオフにしておくなど、厳密な管理が必要となるかもしれませんね。

まとめ

Amazon Echoのリスクを3つ紹介しました。

どれも金銭的な実害を及ぼす可能性があり、危険性は小さくありません。

しかし、Amazon Echo自体はすばらしい製品だと思いますし、人々の生活を変えていく可能性にワクワクしています。

ユーザみんながリスクをちゃんと把握して、正しい設定と正しい運用方法で対策をしていくことが重要ですね。

日本での発売日が楽しみです!

Mercari Tech Conf 2017 に行ってきた

f:id:ntoshi1900:20171001184526p:plain

昨日、Mercari Tech Conf 2017に行ってきました。

スタートアップからどうやってここまで成長してきたのか、どういったことを大切にしてきたのか、といった話があり、非常に参考になりました。

また、最近元Facebook社VPのジョン・ラーゲリン氏がCBOに就任した等のニュースで海外展開も注目を集めている同社ですが、今回の聴講で海外に対する本気度が良くわかりました。 今後グローバルでも順調に拡大していきそうです。

今回のカンファレンスで参考になった点をまとめていきます。

スタートアップからどうやって急成長を遂げたのか

メルカリは創業からまだ4年半ほどにも関わらず、時価総額は1000億円以上ともいわれ、とんでもない急成長を遂げています。

しかも、創業時点で競合アプリはかなりあったらしく、それらを乗り越えてここまできています。

その急成長の背景には、主に二つの理由があるようです。

とにかく素早く

  • ミーティングはほとんどせず、週一回の定例ミーティングくらいで収める
  • インフラのスケールを初めから想定するのでなく、破棄する前提で構築
  • 初期の段階では検索機能をつけない

かなり大胆な意思決定をされています。

特にインフラの話が凄まじく、いろいろ後のことを考えて導入が遅くなるくらいなら、シンプルな構成でとりあえず参入しよう、という実に勇気ある決断です。

f:id:ntoshi1900:20171001180043p:plain
この画像が出たときには会場が笑いに包まれました

また検索の話も驚きで、アプリの特性上絶対に必須じゃないの、と私なんかは思ってしまいます。

しかし、検索機能の追加はエンジニアとしては非常に重労働で、検索精度を高く保つのも大変です。 なので、中途半端なものを作ってお客様を落胆させるくらいなら、いっそ作らない!という男気ある決断をしたそうです。

やはりリソースを裂くところをしっかり見極めることは、かなり重要ですね。

“顧客の出品体験”というUXを最重要視する

  • 画像を軽量化して表示速度を向上
  • HTML5ではなくNativeで開発することでタイムラインの表示を高速化

表示速度 >>>>> エンジニアの手間 という考えで、どれだけ手間をかけてでもUX改善のために表示速度の向上にこだわったそうです。

他者と比べて圧倒的な“快適な出品体験”を実現できたことが、ひしめく競合の中を抜きん出られた最大の要因のようですね。

グローバルで成功する戦略

国ごとにUIを最適化しているようで、なんと驚くべきことに海外向けにUIをスクラッチで再開発したそうです!

ただこれ、かなり効果は抜群だと思われます。

かのパズドラが大成功を収めたのも、「電車でつり革につかまりながら片手でプレーできる」といった、ユーザの背景をしっかりと想定して作られたことが大きかったと言われています。

国ごとに違うユーザの背景にあわせて、手間隙惜しまず最適なUIを作りこんでいく。

このUXへのこだわり、海外でも成功する未来が見える気がします。

f:id:ntoshi1900:20171001192408j:plain
国ごとに異なるUI。米国では返品が多いため、返品機能を強化したりしているようです

未来が垣間見える機械学習の活用

メルカリは大量に蓄積される商品画像を用いて機械学習の活用も進めているようです。

確かに、説明文や価格、購買の有無など様々な付加情報の付いた画像データが大量にあるわけだから、活用しない手はないですね。

現在、商品画像からのカテゴリ識別、ブランド予測、価格推定などを行っているようです。これができれば、出品物の写真を取るだけで多くの項目をAIが自動的に埋めてくれるようになるわけですね。めっちゃいいなー

f:id:ntoshi1900:20171001192752j:plain
ブランド推定結果。ちゃんとブランドが認識できてます

カテゴリ認識はCNNを用いており、Inception-v3(2014年の一般物体認識のコンペで優勝したGoogLeNetの改良版)をファインチューニングしたみたいです。

カテゴリ認識のエラーレートは29.3%と、一般物体認識に比べると精度は少し低めです。

どうしようもないものとしては、その他カテゴリの商品や、女性用上着 or 男性用上着 or 子供用上着の分類などがあるみたいです。 画像だけではサイズがわからないため、分類できないみたいですね。

こういうどうしようもないのを除くと、やはりマイナーな商品が難しいようで、例としてルーピングという幼児向けのおもちゃが紹介されていました。

↓こんなやつ www.amazon.co.jp

確かにマイナーそう……

データが足りないからデータを増やせるようにがんばります!といっていましたが、データオーギュメンテーションとかはしてるんだろうか? まぁ登壇者は相当機械学習に詳しそうだったし、当然トライはしてるよね。

ちなみに画像データは5000万枚くらい使ったりしてるそうです。それで足りないとは恐ろしいですね。 まぁ出品物によって偏りが出るのは仕方ないし、スキューなデータをいかにうまく扱うのかというのがデータサイエンティストの腕の見せ所ですね。

まとめ

急成長の裏には、顧客のUXを最大化するというしっかりとした戦略があったことがわかりました。

海外展開にも同様の戦略の元、手間を惜しまず取り組まれており、海外でも明るい未来があるように感じます。

また、機械学習を活用してワクワクするような機能を開発されており、今後の機能拡張が楽しみです。 (機械学習の適応先として、問い合わせへの一次返答ってあったけど、C2Cの問い合わせも対応してるのかな。あらかじめいくつかの項目埋めとけば、あとはAIが勝手に問い合わせ処理してくれるとか超ワクワクだよね)

今後のメルカリの発展に、目が離せませんね。

上場マダカナー

おまけ

おみやげいっぱいゲットしました。

f:id:ntoshi1900:20171001202359j:plain
メルカリTシャツ。来年はこれ着ていけばいいのかな 笑

f:id:ntoshi1900:20171001202405j:plain
スキル缶バッジ。これつけてると何の専門かすぐわかるのでめっちゃいいですね!

f:id:ntoshi1900:20171001202302j:plain
メルカリどら焼き。受付でもらった袋の下のほうにこっそり入ってました。

f:id:ntoshi1900:20171001200520j:plain
会場にはめっちゃ可愛いドーナツが!一瞬で売り切れてました 笑

来年も是非参加したいです!

気まぐれでブログを作ってみたのでとりあえず自己紹介

はじめまして。社会人4年目、雑魚エンジニアのtoshiです。

これまでのうのうと雑魚エンジニア生活を過ごしてきましたが、最近組織変更があり「Ruby on Rails!?何それ!?」とか「GO言語!?それって美味しいの!?」とか言ってたら周りから白い目で見られるようになってきたので、勉強しなきゃという危機感を覚えております。

今までもMOOC1なんかを利用してちまちま勉強してきましたが、いかんせんやる気が出きらずにあまり進まず、雑魚レベルから抜け出せずにいました。

そこで、一念発起、技術ブログを始めて「ブログのネタを作るために勉強しなきゃ!」とやる気を出す作戦に出たのです!

可能な限り、取り組んでいった技術的な内容を記事にしていきたいと思っているので、よろしくお願いします。

自己紹介

  • 職業   : 某メーカー エンジニア

  • 専門   : データ解析、機械学習
    得意技は「とりあえずRandomForest」

  • 使用言語 : R、Python、C#
    仕事ではR、趣味ではC#を使うことが多いです。
    PythonDeep Learningのために仕方なく……

  • 資格   : 基本情報処理技術者、応用情報処理技術者、ダイエット検定1級(プロフェッショナルアドバイザー)
    ダイエット検定は“健康なエンジニア”をアピールするために取りました(笑)

  • 趣味   : ゲーム、筋トレ
    ゲームは音ゲーをまったりやってました(beatmania SP9段、pop'n music 最高47安定45、チュウニズム レート12)。最近はご無沙汰ですが……
    あと最近、英語物語というスマホアプリをしています。楽しく英語が学べていいですね!
    筋トレは“健康なエンジニア”をアピールするために(ry

何はともあれ、とりあえずは継続していけるようにがんばります!


  1. Massive open online courseの略で、インターネットを用いた大規模公開オンライン講座のこと。