MeCabの制約付き解析モードを利用する

MeCabの制約付き解析モードを利用する

やりたいこと

標準の辞書だけだと、どうしても不要な単語まで分割してしまう。

かといって、辞書ファイルの内容をいじるのは割と手間がかかる作業だ。

そこで、「制約つき解析モード」を利用することにした。工藤さんによる解説

やったこと

  • 形態素境界制約つき(任意の場所にインデックスで境界を指定できる)で分割してみる
  • 入力フォーマットを工夫して制約付き解析にする

形態素境界制約つきの単語分割をする

python wrapperで制約付きモードを試してみた記事にyukinoiさんの記事があった。

MeCabの単語境界を明示的にして単語分割するモードをPythonで呼び出す方法を解説している。

試してみるものの。。。こける。。。

どうも  end_position = start_position + len(token.encode(charset))の行でエンコードエラーが起きているらしい。

よく考えてみれば、入力はutf-8なのだから、わざわざエンコードする必要はあるのかな・・・・?と思い、

end_position = start_position + len(token)に書き換えてみた。

動いた。

システムに組み込もうとしてみる

紹介記事では、2次元のリストで、入力文を与えているため、システム的に使おうと思うと、2次元リストにしないといけない。

しかも、既知の単語があった場合は、適時リストに分割していく必要がある。(つまり、既知の単語を探索しながら、unicode→リストに放り込むという作業が必要になる)

いろいろ考えてみたが、正規表現でなんとかなった。

import re
s = "めちゃめちゃいけてるに出ているよい子の濱口はよく海に行く人でした。"
split_list = re.split(r'(めちゃめちゃ|よい子の濱口)', s)

for i in labeled_tokens: print i[0]

とすると、

めちゃめちゃ
いけてるに出ている
よい子の濱口
はよく海に行く人でした。

と分割できる。成功だ。

解析結果も

めちゃめちゃ 名詞,形容動詞語幹,*,*,*,*,めちゃめちゃ,メチャメチャ,メチャメチャ
いけてるに出ている 名詞,一般,*,*,*,*,*
よい子の濱口 名詞,一般,*,*,*,*,*
はよく海に行く人でした。 名詞,サ変接続,*,*,*,*,*
BOS/EOS,*,*,*,*,*,*,*,*

となってうまくいった。。。

かのように見えるが。。。

このコードはうまくいく時とうまくいかない時があって、非常に謎。

例えば、

    import re
    s = "月がーでたでったーよいよいよであらっさのほいほいほい"
    split_list = re.split(r'(よいよいよ|よい子の濱口)', s)
    labeled_tokens = [[item] for item in split_list if item != '']
    

    for node in tagger.feature_constraint_parse(labeled_tokens):
        print node.surface, node.feature

で試してみると、

月がーでたでったー 感動詞,*,*,*,*,*,*

しか出てこない。

どうやら、unicodeutf-8の変換のところでしくじっており、正しい境界で切れていないらしい。

その他、文字化けが頻発したりしたので、諦めた。

もっと安直な制約付き解析にする

せっかく-pモードが用意されているので、これを使ってしまうことにする。

再掲になるが、工藤さんによる解説

ポイントは

  • 制約をつけたい単語はタブ区切りで記述(単語\t品詞)
  • 普通にラティスを展開してほしいところは何もつけない
  • 文の最後にはEOSを記述すること

の3つ。

この3点を踏まえて動かしてみると、

import MeCab
# インスタンス作成時に-pをつけておく
m = MeCab.Tagger("-Ochasen -p")
print m.parse("月\t名詞\nがーでたでったーよいよいよ\t感動詞\nであらっさ\t感動詞\nのほいほいほい\nEOS")
月  ツキ  月 名詞-一般       
がーでたでったーよいよいよ がーでたでったーよいよいよ がーでたでったーよいよいよ 感動詞       
であらっさ であらっさ であらっさ 感動詞       
の ノ の 助詞-連体化        
ほ ホ ほる  動詞-自立   五段・ラ行 体言接続特殊2
い イ いる  動詞-非自立    一段  連用形
ほ ホ ほる  動詞-自立   五段・ラ行 体言接続特殊2
い イ いる  動詞-非自立    一段  連用形
ほ ホ ほる  動詞-自立   五段・ラ行 体言接続特殊2
い イ いる  動詞-非自立    一段  連用形
EOS

となり、きちんと制約つけながら分割できた。

(文字列を整えたりするのに、きたないコードになったけど、そこはご愛嬌)

USERS 顧客主義の終焉と企業の命運を左右する7つの戦略 を読んだ

「USERS 顧客主義の終焉と企業の命運を左右する7つの戦略」を読んだ

USERS 顧客主義の終焉と企業の命運を左右する7つの戦略

USERS 顧客主義の終焉と企業の命運を左右する7つの戦略

内容的には、「いかにしてユーザーが集まり、ビジネス的に成功するweb媒体を構築するか?」というテーマが述べられている本

対象は、Web業界だけでなく、すべての企業(小売店も含む)に話題が及んでいる。

ビジネス的に成功するweb媒体を運営するには

筆者の考えを簡潔に2行でまとめると、

  1. 適切なタイミングでサービスを世に出す
  2. 競合よりもユーザー満足が高いサービスを作る

になる。

「適切なタイミングで世に出す」とは?

簡潔に2行でまとめると、

  1. 通信インフラが(ユーザーにとって)十分に整っている
  2. ユーザーがサービスの質をweb上で簡単に共有できる地盤が整っている

になる。

考えてみれば、当たり前で、YoutubeISDN全盛期に登場したとしても、きっとサービスはヒットしなかっただろう。

おもしろい動画を投稿するマーケティング手法(ドコモの3秒エビフライなど)もユーザーが拡散する地盤がなければ、話題にならなかっただろう。

「競合よりもユーザー満足が高いサービスを作る」とは?

この書籍の大半が「競合よりもユーザー満足が高いサービスを作る」には?というテーマにあてられている。

これも私なりに簡潔に2行でまとめると

  1. シンプルさを提供する
  2. ユーザーに信頼してもらう

この2点になると思う。

もちろん、どうやって「シンプルさを提供するか?」「どうやって信頼してもらうか?」もきちんと網羅されて記述されていた。

nltkでタプルのbigramカウントをする

やりたいこと

(word, POS)のタプルの状態でbi-gramのカウントを取りたい

解決法

nltkで普通にできた。

import nltk
list_input = [('I', 'Noun'), ('feel', 'Verb'), ('happy', 'Adj'), ('You', 'Noun'), ('feel', 'Verb'), ('happy', 'Adj')]
corpus = nltk.Text(list_input)
bigrams = nltk.bigrams(corpus)
cfd = nltk.ConditionalFreqDist(bigrams)
In [20]:cfd.viewitems()

Out[20]:
dict_items([(('happy', 'Adj'), FreqDist({('You', 'Noun'): 1})), (('feel', 'Verb'), FreqDist({('happy', 'Adj'): 2})), (('You', 'Noun'), FreqDist({('feel', 'Verb'): 1})), (('I', 'Noun'), FreqDist({('feel', 'Verb'): 1}))])

キーがタプルになってるとエラーでるかなー。と思っていたから意外だった。

スゴいと噂のsparkを動かしてみる

やりたいこと

sparkとかいう分散処理系の何かがスゴいらしいので、とりあえず動かしてみたい

sparkって何ができるの??

一言「分散処理」

ただ、分散処理でも、Hadoopと違って、ストレージに保存せずにon memoryで分散処理を実行できる

いちいちストレージに保存する必要がない処理、例えば機械学習のイテレート処理で効果を発揮する。

紹介記事

元資料


やってみたこと

sparkのインストール

まずはsparkをインストールする

とは、言っても、バイナリファイルを落としてきて、移動するだけだけら、問題はない・・・はず

ここから、まずは落としてくる

で、tarファイルを展開して、任意の場所に移動しておく

とりあえずは/usr/local/share/spark/に移動しておいた

パスが通っていなかったら、通しておいてくださいね

参考記事

コマンドラインインタラクティブに実行する

公式マニュアルを見ながらやってみる

sparkを置いてあるパスに移動して、bin/spark-shellを実行するだけ(localマシンをmasterにして、sparkが立ち上がる)

--master local[スレッド数]と指定すると、スレッド数を指定して実行できる

pythonの表記で実行したい場合は./bin/pysparkを実行する。また別の機会に取り組んでみる

% cd /usr/local/share/spark
% bin/spark-shell

// ここらへんになんかいっぱいログが出る。長いので省略
// あと、こんなかっこいいロゴがでる

Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 1.2.1
      /_/

// コマンドの待機状態になったら、起動終了
scala> 

基本集計っぽいことをやってみる

// ファイルをRDDの上に乗っけてみる。RDDってのは、HDFSみたいなもの。まぁ、メモリだ
// sparkのルートディレクトリ、つまり、今回は`/usr/local/share/spark/`から見た相対パスなので要注意
scala> val textFile = sc.textFile("README.md")  


// 読み込んでるっぽいログ

scala> textFile.count()  // 行数を数えるコマンド
// なんかログ
res1: Long = 98

// "Spark"が含まれる行だけをフィルタリングして、別の変数に格納(実際は、変数linesWithSparkは新しいRDDの上に生成される)
scala> val linesWithSpark = textFile.filter(line => line.contains("Spark"))
linesWithSpark: org.apache.spark.rdd.RDD[String] = FilteredRDD[2] at filter at <console>:14

// フィルタリングとアクションはつなげて(chainして)実行することもできる
// "Spark"がある行をフィルタリングして、"Spark"の出現回数を数える
scala> textFile.filter(line => line.contains("Spark")).count()
res3: Long = 15

map/reduceをやってみる

公式マニュアルを見ながらやってみる

さっきのコードの続き。textFileオブジェクトは引き継ぎ

scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b)
res4: Long = 15

やっていることは、「1行あたりの最大の単語数を探す」

map()メソッド内でmap処理を実行している

map処理の段階で新しいRDDが生成される。

で、reduceメソッドで、RDDを呼び出し、最大の単語数を探す命令を実行している

単語の出現頻度をカウントする

val wordCounts = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b)

flatMapメソッドは、mapと似てるけど、integer以外も返せる命令。今は単語(string)を返している

mapメソッド(word, 1)のタプルを作り出している(初期化)

reduceByKeyメソッドは集計関数を引数に渡せるメソッド。集計関数は「input:(K, V)のペア。Kでまとめて(V, V) => Vを実行して(つまり集計)する。output:(K, V)」を渡す。今回は「input:(string, int)のペア。stringでまとめて、(a, b) => a + b(つまり、加算)をする。output:(string, int)」という関数

最後に集計結果を見る

scala> wordCounts.collect()
res6: Array[(String, Int)] = Array((means,1), (under,2), (this,3), (Because,1), (Python,2), (agree,1), (cluster.,1), ...)

collectメソッドは、「dataをarrayの形で返す。」っていうメソッド。普通は集計の後に実行する。

これで、単語の出現頻度の集計おしまい。あらら、簡単ですわぁ

その他、transformationメソッドの説明はここに。

sparkを呼び出すscalaコードを書く

次に、scalaのコードで処理を書いて、スタンドアローンに実行する場合。

流れを書くと

  • 1 scalaでsparkを呼び出して演算するコードを書く
  • 2 sbt assemblyとかsbt packageとかで.jarファイル化する
  • 3 spark-submitコマンドで.jarファイルを呼び出して、処理をする

になる。

ここの記事の通りにやってみた

その他sparkを使った実例など

おもしろい実例だとtwitterの集計につかっている

まとめ

今回はローカルマシン1台でやったから価値が実感できてないけど、sparkになんかすごそうだね!

次はpythonでの呼び出しとか、spark-sqlとか、複数台の分散処理とかやってみるか

sbtでプロジェクト管理をする話

やりたいこと

scalaのプロジェクトを管理したい。

(なんだかよくわからないけど、みんながsbtを使っているので、自分も使ってみたい。)


やってみたこと

sbtのインストール

macなら、

brew install sbt

まあ、公式マニュアルの通りなんですけどね。

sbtで管理するscalaのディレクトリ構造

sbtで管理する時には、ディレクトリ構造は下記のように設定しなくてはいけない。

build.sbtファイルについては後述する。

./
├── build.sbt  // プロジェクトを管理するsbtファイル
└── src
        ├── main // 本番用のコードを置くディレクトリ
        │   ├── java // java用のコードを置くディレクトリ
        │   ├── resources  // 必要なファイルを置くディレクトリ
        │   └── scala  // scala用のコードを置くディレクトリ
        └── test // テストのコードを置くディレクトリ
            ├── java // java用のコードを置くディレクトリ
            ├── resources  // 必要なファイルを置くディレクトリ
            └── scala  // scala用のコードを置くディレクトリ

公式マニュアルに記載されている構造

scalaのコードを書く

とりあえず、なんでもいいので、コードを書く

ここも公式マニュアルに沿う。

object Hi {
  def main(args: Array[String]) = println("Hi!")
}

こいつをhello.scalaとする。

hello.scalasrc/main/scala/src/test/scala/に置くこと。

Configurationファイルを記述する

プロジェクトのコンパイル方法を指示したレシピを書く(たぶん、そういう解釈で合ってる)

.sbtファイルはBuild configurationファイルという名前で呼ばれている。

同じ内容をscalaのコードlikeに書けるFull configurationもあるのだが、後述

よく使う(らしい)コマンドを記述してみる

Build.sbtの中身に下記を記述する

name := "hello"
version := "1.0"
scalaVersion := "2.10.0"
libraryDependencies += "org.scalatest" %% "scalatest_2.10" % "1.9.1" % "test"

それぞれの意味は下記の通り

  • nameはプロジェクトの名前
  • versionはプロジェクトのバージョン
  • scalaVersionコンパイルに使うscalaのバージョン
  • libraryDependenciesはプロジェクトが依存しているライブラリ(ライブラリが利用可能な状態でなければ、自動的にダウンロードされてインストールされる。ダウンロードされたライブラリのファイルは~/.ivy2/以下に保存される)
    • libraryDependenciesの書式はlibraryDependencies += groupID % artifactID % revisionここを見れ

番外編:Full configurationファイルを使う

configurationファイルはscalaのコードlikeにも書ける

このファイルはFull configurationファイルと呼ばれて、Build.scalaの名前で、project/の配下に保存する(.sbtファイルと置く場所が違うので注意)

プロジェクトのルートディレクトリ直下にproject/をまだ作っていなかったら、project/を作成する

project/Build.scalaに下記を記述する

import sbt._
import Keys._
 
object MyBuild extends Build {
  lazy val root = Project (
    "hello",
    file("."),
    settings = Seq(
      version := "1.0",
      scalaVersion := "2.10.0"
      libraryDependencies ++= Seq(
      "org.scalatest" % "scalatest_2.10" % "1.9.1" % "test")
  )
}

書いてある内容はBasic configurationと同じなので、説明は省略する

イマイチ.sbtとの違いがわかっていないが、きっと何かしろのメリットがあるんだろう。

コンパイルして実行する

ターミナルで、sbt compileコンパイルを実行する

libraryDependenciesに何かを記述している場合はダウンロードしているメッセージが表示されると思う

無事にコンパイルが終了したら、[success] Total time: ** s, completed yyyy/mm/dd hh:mm:ssというログが出て、コマンドが打てる状態に戻る

エラーメッセージが出ていたら、当たり前だけど、何かを失敗している。エラーメッセージを見て、修正を行なう

コンパイル済みのプログラムを実行するにはsbt runを実行する

きっと、Hiとターミナルに表示されるはず


その他

jarファイルを作る

sbtでjarファイルの生成もできる

そのためには、project/assembly.sbtを作成しておく。中身は

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")

と記述する(バージョンが変わった時は適時、変更をー。記述すべき内容は公式ページに書いてあるので)

次にプロジェクトのルート・ディレクトリにもassembly.sbtを作成する

import AssemblyKeys._ // put this at the top of the file

assemblySettings

最後に、build.sbtにも、assembly用のコマンドを追加する

追記内容は、

import AssemblyKeys._

// 通常のbuild.sbtと同じ内容をここに記述しておくー

jarName in assembly := "something.jar" // 生成されるjarファイルの名前

あとは、sbt assemblyのコマンドを実行する。

うまくいったら、target/something.jarが生成されているはず

sbtのコマンドラインで謎のwarningメッセージが出ていた件

インストール直後のsbtでは、走らせると下記のようなwarningが出力されていた。

Use of ~/.sbtconfig is deprecated, please migrate global settings to /usr/local/etc/sbtopts
[warn] The global sbt directory is now versioned and is located at /Users/kensuke-mi/.sbt/0.13.
[warn]   You are seeing this warning because there is global configuration in /Users/kensuke-mi/.sbt but not in /Users/kensuke-mi/.sbt/0.13.
[warn]   The global sbt directory may be changed via the sbt.global.base system property.

解決策を探したら、ここにあった。

単に~/.sbtにある内容をまるごと、~/.sbt/0.13/に移せばいいだけだった。

参考記事

インストールから基本的な使いまで

macでtreeコマンドを使う

やりたいこと

macコマンドラインでtreeコマンドを使う

解決策

まずはbrewでtreeをインストール

% brew install tree

特定の階層数で制限をつけて表示するときは、--dirsfirst-L 階層数のオプションをつけて実行する。

% tree -d  --dirsfirst -L 2

試しに、scalaのsbtプロジェクトディレクトリを表示すると、

.
├── project
│   ├── project
│   └── target
├── src
│   ├── main
│   └── test
└── target
    ├── resolution-cache
    ├── scala-2.10
    ├── scala-2.11
    └── streams

これで快適にディレクトリ構造を見れるね。やったね。たえちゃん。

マダガスカルという国に行ってきたーその4(マダガスカルという国とマダガスカル人という人々)

滞在中に見たり、聞いたりしたマダガスカルという国とマダガスカル人という人々について書いていこうと思う。

ただし、あくまで個人的な見解であり、いわば何の根拠もないクソ以下の感想も混じっている。

なので、クソみたいな雑記と解釈しながら読んでいただけると幸いです。

マダガスカル人という人々

よく旅行本にも書いてあるが、マダガスカル人は、アジア人である。

それは肌の色から見てもアジア人だと言える。大陸の人に比べて黒さがはるかに薄いのだ。

加えて、個人的な見解を述べると、マダガスカルの気質は日本人に似ているように思う。

それは、「細かいところに気を配れる」「時間にシビア」という点だと思う。

「旅行社の責任ではないのに、ツアーが中止になったので、割引をしてくれた」「ランドクルーザーに乗ったら、座席には綺麗に水が置いてあった」という話は書いたと思うが、その他にもマダガスカル人の気配りに感心することは多々あった。

いくつか挙げると、 * 土産屋で瓶詰めを買うと、丁寧にビニールを2重3重にして包んでくれる。もちろん、何もお願いしたわけではない * 旅行日程表を数枚入れるためだけに小さくて綺麗なバックを用意する * そんなに高いわけでもないケーキも1つ1つきっちり作り、綺麗に並べている

などなど、思い返すだけでも感心した点は多かった。

「時間にシビア」という点も日本人的だと感じた。

ムラマンガに行くときに、旅行社の車を使ったのだが、きちんと約束の時間の5分前には来て待っていて、非常に驚いた。

この後も、「〜時」と指定したら、きちんとその数分前には準備をしているほどだった。

友人が言うには、「マダガスカル人は人に迷惑をかけることを嫌う」らしい。

時間が遅れれば、人に迷惑がかかる。マダガスカル人はそれを嫌うらしい。

マダガスカルで仕事をして、マダガスカル人とたくさん会っている友人が言うのだから、きっと本当なのだろう。

マダガスカルという国

マダガスカルという国は、基本的に貧しい。

2012年のJICAのレポート複数の統計観点から調査をしている。

1日1ドル以下で生活する人口割合、栄養失調ラインの人口割合、教育水準のレベル比較・・と調査をしているが、結論として「貧しい」。

しかし、資源は十分にあり、ポテンシャルは十分にある国である。

レポートでも、貧困の原因は「政治要因」「未発達の社会保障」「国際経済の不況」「自然災害」を挙げている。

つまり、やろうと思えばどうにかなる問題が原因になっているようだ(それをやるのが難しいのだけど)

そんなマダガスカルを良くするために頑張っているJICAさんには頭が下がる思いである。


その他、雑多な写真

シトロエン2CVマダガスカルの道を行く車は6割近く(体感値)は2CVだった。90年まで生産されていたベストセラー車だから、マダガスカルにも多く存在しているのだろう。 f:id:kensuke-mi:20150205183527j:plain

アンタナナリボの道に開く穴。アンタナナリボの街中ではいたる所にこんな穴がある。多く雨量と排水設備の不備でアスファルトに穴が空いてしまうようだ。この穴が原因で、アンタナナリボでは毎日、渋滞が発生する。 f:id:kensuke-mi:20150205182551j:plain

マダガスカルのテレビで放映されていた「ナニコレ珍百景」。情報紹介番組の中で取り上げられていた。しかし、なぜハングル字幕がついているのか?・・・まったくわからない f:id:kensuke-mi:20150205185357j:plain

アンタナナリボにある日本料理店「さっぽろ」。しかし、オーナーは韓国人女性である f:id:kensuke-mi:20150205181441j:plain

「さっぽろ」で「鮭の照り焼き」を頼んだら、こういうのが出てきた。なるほど、間違いではない・・・ f:id:kensuke-mi:20150205204916j:plain

ちなみにみそ汁を頼むと、こうなった。冷たいように見えるけど、暖かかった。白味噌だった。 f:id:kensuke-mi:20150205203910j:plain


マダガスカルで見かけたバイク集

独立大通りに止めてあったロイヤルエンフィールド。インド資本の企業もマダガスカルで商売しているのだろうか f:id:kensuke-mi:20150205115008j:plain

同じく独立大通りにあった。スーパーテネレ。ヨーロッパヤマハ仕様だった。 f:id:kensuke-mi:20150205120614j:plain

同じく独立大通りでCB400を見た。日本とほぼ仕様だった(ように見えた)。ヨーロッパ系の人が乗っていた。きっとフランス人だろう。 f:id:kensuke-mi:20150205121928j:plain

ムラマンガで見たCBR600(の改造車) アンタナナリボ-ムラマンガの道を100km/h近い速度で走っていた。運転していたのはマダガスカル人(と思わしき人)だった。 f:id:kensuke-mi:20150208114521j:plain