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
で試してみると、
月がーでたでったー 感動詞,*,*,*,*,*,*
しか出てこない。
どうやら、unicodeとutf-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つの戦略」を読んだ
- 作者: アーロン・シャピロ,萩原雅之,梶原健司,伊藤富雄
- 出版社/メーカー: 翔泳社
- 発売日: 2013/09/03
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
内容的には、「いかにしてユーザーが集まり、ビジネス的に成功するweb媒体を構築するか?」というテーマが述べられている本
対象は、Web業界だけでなく、すべての企業(小売店も含む)に話題が及んでいる。
ビジネス的に成功するweb媒体を運営するには
筆者の考えを簡潔に2行でまとめると、
- 適切なタイミングでサービスを世に出す
- 競合よりもユーザー満足が高いサービスを作る
になる。
「適切なタイミングで世に出す」とは?
簡潔に2行でまとめると、
- 通信インフラが(ユーザーにとって)十分に整っている
- ユーザーがサービスの質をweb上で簡単に共有できる地盤が整っている
になる。
考えてみれば、当たり前で、YoutubeがISDN全盛期に登場したとしても、きっとサービスはヒットしなかっただろう。
おもしろい動画を投稿するマーケティング手法(ドコモの3秒エビフライなど)もユーザーが拡散する地盤がなければ、話題にならなかっただろう。
「競合よりもユーザー満足が高いサービスを作る」とは?
この書籍の大半が「競合よりもユーザー満足が高いサービスを作る」には?というテーマにあてられている。
これも私なりに簡潔に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になんかすごそうだね!
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.scalaはsrc/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
(ここを見れ)
- libraryDependenciesの書式は
番外編: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コマンドを使う
やりたいこと
解決策
まずは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年まで生産されていたベストセラー車だから、マダガスカルにも多く存在しているのだろう。
アンタナナリボの道に開く穴。アンタナナリボの街中ではいたる所にこんな穴がある。多く雨量と排水設備の不備でアスファルトに穴が空いてしまうようだ。この穴が原因で、アンタナナリボでは毎日、渋滞が発生する。
マダガスカルのテレビで放映されていた「ナニコレ珍百景」。情報紹介番組の中で取り上げられていた。しかし、なぜハングル字幕がついているのか?・・・まったくわからない
アンタナナリボにある日本料理店「さっぽろ」。しかし、オーナーは韓国人女性である
「さっぽろ」で「鮭の照り焼き」を頼んだら、こういうのが出てきた。なるほど、間違いではない・・・
ちなみにみそ汁を頼むと、こうなった。冷たいように見えるけど、暖かかった。白味噌だった。
マダガスカルで見かけたバイク集
独立大通りに止めてあったロイヤルエンフィールド。インド資本の企業もマダガスカルで商売しているのだろうか
同じく独立大通りにあった。スーパーテネレ。ヨーロッパヤマハ仕様だった。
同じく独立大通りでCB400を見た。日本とほぼ仕様だった(ように見えた)。ヨーロッパ系の人が乗っていた。きっとフランス人だろう。
ムラマンガで見たCBR600(の改造車) アンタナナリボ-ムラマンガの道を100km/h近い速度で走っていた。運転していたのはマダガスカル人(と思わしき人)だった。