お前誰や

  • みずかみ ひろき (データサイエンティスト)
  • @piroyoung (データサイエンティスト)
  • アドテクのデータ分析屋さん・数理モデル屋さん (データサイエンティスト)
  • python / R / SQL / AWS / Tableau / Scala / Spark
  • そう、僕がデータサイエンティストさ( ´ ▽ ` )ノ

 何するんや

  • 役に立つ話をします。
  • 自然言語の前処理って重い。
  • 並列で処理したいね
  • iPythonではmultiprocessing.Poolが使えない
  • でもiPythonでも並列処理ができることに気づいた
  • なので並列化分かち書きのデモします。

ipythonで並列処理をする話 @piroyoung

ipythonってなんや

  • これ
  • 分析者御用たちのpythonの対話環境
  • ブラウザで動いてかなりクール
  • EC2で動かす事ができて、お使いのブラウザが開発環境に!
  • R3インスタンス > スポットでさせば メモリ122G が0.2$ / hour
In [17]:
import MeCab
import time
In [2]:
t = MeCab.Tagger("-Owakati")

わかちがきした結果を返す

In [3]:
wakati = lambda x: t.parse(x).rstrip()
In [22]:
wakati('すもももももご注文はうさぎですかももももんももんんもももとしまりんもももももももももももものうちスヤァ')
Out[22]:
'すもも もも も ご 注文 は うさぎ です か もも も もん も もん ん も もも と しま りんも もも も もも も もも も もも の うち スヤァ'

青空文庫の斜陽を読み込む

In [5]:
with open('shayo.txt') as fp:
    text = [line.replace('\u3000','').rstrip() for line in fp.readlines()]
In [6]:
text[:30]
Out[6]:
['朝、食堂でスウプを一さじ、すっと吸ってお母さまが、',
 '「あ」',
 'と幽かすかな叫び声をお挙げになった。',
 '「髪の毛?」',
 'スウプに何か、イヤなものでも入っていたのかしら、と思った。',
 '「いいえ」',
 'お母さまは、何事も無かったように、またひらりと一さじ、スウプをお口に流し込み、すましてお顔を横に向け、お勝手の窓の、満開の山桜に視線を送り、そうしてお顔を横に向けたまま、またひらりと一さじ、スウプを小さなお唇のあいだに滑り込ませた。ヒラリ、という形容は、お母さまの場合、決して誇張では無い。婦人雑誌などに出ているお食事のいただき方などとは、てんでまるで、違っていらっしゃる。弟の直治なおじがいつか、お酒を飲みながら、姉の私に向ってこう言った事がある。',
 '「爵位しゃくいがあるから、貴族だというわけにはいかないんだぜ。爵位が無くても、天爵というものを持っている立派な貴族のひともあるし、おれたちのように爵位だけは持っていても、貴族どころか、賤民せんみんにちかいのもいる。岩島なんてのは(と直治の学友の伯爵のお名前を挙げて)あんなのは、まったく、新宿の遊廓ゆうかくの客引き番頭よりも、もっとげびてる感じじゃねえか。こないだも、柳井やない(と、やはり弟の学友で、子爵の御次男のかたのお名前を挙げて)の兄貴の結婚式に、あんちきしょう、タキシイドなんか着て、なんだってまた、タキシイドなんかを着て来る必要があるんだ、それはまあいいとして、テーブルスピーチの時に、あの野郎、ゴザイマスルという不可思議な言葉をつかったのには、げっとなった。気取るという事は、上品という事と、ぜんぜん無関係なあさましい虚勢だ。高等御おん下宿と書いてある看板が本郷あたりによくあったものだけれども、じっさい華族なんてものの大部分は、高等御乞食おんこじきとでもいったようなものなんだ。しんの貴族は、あんな岩島みたいな下手な気取りかたなんか、しやしないよ。おれたちの一族でも、ほんものの貴族は、まあ、ママくらいのものだろう。あれは、ほんものだよ。かなわねえところがある」',
 'スウプのいただきかたにしても、私たちなら、お皿さらの上にすこしうつむき、そうしてスプウンを横に持ってスウプを掬すくい、スプウンを横にしたまま口元に運んでいただくのだけれども、お母さまは左手のお指を軽くテーブルの縁ふちにかけて、上体をかがめる事も無く、お顔をしゃんと挙げて、お皿をろくに見もせずスプウンを横にしてさっと掬って、それから、燕つばめのように、とでも形容したいくらいに軽く鮮やかにスプウンをお口と直角になるように持ち運んで、スプウンの尖端せんたんから、スウプをお唇のあいだに流し込むのである。そうして、無心そうにあちこち傍見わきみなどなさりながら、ひらりひらりと、まるで小さな翼のようにスプウンをあつかい、スウプを一滴もおこぼしになる事も無いし、吸う音もお皿の音も、ちっともお立てにならぬのだ。それは所謂いわゆる正式礼法にかなったいただき方では無いかも知れないけれども、私の目には、とても可愛かわいらしく、それこそほんものみたいに見える。また、事実、お飲物は、口に流し込むようにしていただいたほうが、不思議なくらいにおいしいものだ。けれども、私は直治の言うような高等御乞食なのだから、お母さまのようにあんなに軽く無雑作むぞうさにスプウンをあやつる事が出来ず、仕方なく、あきらめて、お皿の上にうつむき、所謂正式礼法どおりの陰気ないただき方をしているのである。',
 'スウプに限らず、お母さまの食事のいただき方は、頗すこぶる礼法にはずれている。お肉が出ると、ナイフとフオクで、さっさと全部小さく切りわけてしまって、それからナイフを捨て、フオクを右手に持ちかえ、その一きれ一きれをフオクに刺してゆっくり楽しそうに召し上がっていらっしゃる。また、骨つきのチキンなど、私たちがお皿を鳴らさずに骨から肉を切りはなすのに苦心している時、お母さまは、平気でひょいと指先で骨のところをつまんで持ち上げ、お口で骨と肉をはなして澄ましていらっしゃる。そんな野蛮な仕草も、お母さまがなさると、可愛らしいばかりか、へんにエロチックにさえ見えるのだから、さすがにほんものは違ったものである。骨つきのチキンの場合だけでなく、お母さまは、ランチのお菜さいのハムやソセージなども、ひょいと指先でつまんで召し上る事さえ時たまある。',
 '「おむすびが、どうしておいしいのだか、知っていますか。あれはね、人間の指で握りしめて作るからですよ」',
 'とおっしゃった事もある。',
 '本当に、手でたべたら、おいしいだろうな、と私も思う事があるけれど、私のような高等御乞食が、下手に真似まねしてそれをやったら、それこそほんものの乞食の図になってしまいそうな気もするので我慢している。',
 '弟の直治でさえ、ママにはかなわねえ、と言っているが、つくづく私も、お母さまの真似は困難で、絶望みたいなものをさえ感じる事がある。いつか、西片町のおうちの奥庭で、秋のはじめの月のいい夜であったが、私はお母さまと二人でお池の端のあずまやで、お月見をして、狐きつねの嫁入りと鼠ねずみの嫁入りとは、お嫁のお支度がどうちがうか、など笑いながら話合っているうちに、お母さまは、つとお立ちになって、あずまやの傍そばの萩はぎのしげみの奥へおはいりになり、それから、萩の白い花のあいだから、もっとあざやかに白いお顔をお出しになって、少し笑って、',
 '「かず子や、お母さまがいま何をなさっているか、あててごらん」',
 'とおっしゃった。',
 '「お花を折っていらっしゃる」',
 'と申し上げたら、小さい声を挙げてお笑いになり、',
 '「おしっこよ」',
 'とおっしゃった。',
 'ちっともしゃがんでいらっしゃらないのには驚いたが、けれども、私などにはとても真似られない、しんから可愛らしい感じがあった。',
 'けさのスウプの事から、ずいぶん脱線しちゃったけれど、こないだ或ある本で読んで、ルイ王朝の頃の貴婦人たちは、宮殿のお庭や、それから廊下の隅すみなどで、平気でおしっこをしていたという事を知り、その無心さが、本当に可愛らしく、私のお母さまなども、そのようなほんものの貴婦人の最後のひとりなのではなかろうかと考えた。',
 'さて、けさは、スウプを一さじお吸いになって、あ、と小さい声をお挙げになったので、髪の毛?とおたずねすると、いいえ、とお答えになる。',
 '「塩辛かったかしら」',
 'けさのスウプは、こないだアメリカから配給になった罐詰かんづめのグリンピイスを裏ごしして、私がポタージュみたいに作ったもので、もともとお料理には自信が無いので、お母さまに、いいえ、と言われても、なおも、はらはらしてそうたずねた。',
 '「お上手に出来ました」',
 'お母さまは、まじめにそう言い、スウプをすまして、それからお海苔のりで包んだおむすびを手でつまんでおあがりになった。',
 '私は小さい時から、朝ごはんがおいしくなく、十時頃にならなければ、おなかがすかないので、その時も、スウプだけはどうやらすましたけれども、食べるのがたいぎで、おむすびをお皿に載せて、それにお箸はしを突込み、ぐしゃぐしゃにこわして、それから、その一かけらをお箸でつまみ上げ、お母さまがスウプを召し上る時のスプウンみたいに、お箸をお口と直角にして、まるで小鳥に餌えさをやるような工合ぐあいにお口に押し込み、のろのろといただいているうちに、お母さまはもうお食事を全部すましてしまって、そっとお立ちになり、朝日の当っている壁にお背中をもたせかけ、しばらく黙って私のお食事の仕方を見ていらして、',
 '「かず子は、まだ、駄目なのね。朝御飯が一番おいしくなるようにならなければ」',
 'とおっしゃった。']

ここからが並列化の話(ipython.parallel.Client)

  • まずipythonのホームからクラスターを起動
In [7]:
from IPython.parallel import Client

c = Client() #クライアントオブジェクトのインスタンスを作る
cluster = c[:] #全のーどを使用して
len(cluster)
Out[7]:
8
In [8]:
cluster.execute('''
import time
''')
Out[8]:
<AsyncResult: finished>

mapping

  • 配列・リスト などの全ての要素におなじ処理を適用させること
  • 並列化との相性が非常にいい
  • だから帰納的な処理以外はfor文よりmap(or apply)を使ったほうがいい事がおおい
In [9]:
res = cluster.map(lambda x: x**2 ,range(10)) #マッピング!
In [10]:
res.get()
Out[10]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

[検証1]速度比較 for文 vs iPython.parallel

In [11]:
start = time.time()

for i in range(8):
    time.sleep(1)

elapsed_time = time.time() - start
print(elapsed_time)
8.02717399597168
In [12]:
start = time.time()

res = cluster.map(lambda x: time.sleep(1), range(8)).get()

elapsed_time = time.time() - start
print(elapsed_time)
1.0132977962493896

わかちがき

[検証2]速度比較 コンプリヘンション vs iPython.parallel

In [18]:
start = time.time()

parsed = [wakati(line) for line in text]

elapsed_time = time.time() - start
print(elapsed_time)
1.438910961151123
In [19]:
cluster.execute('''
import MeCab
t = MeCab.Tagger("-Owakati")
''')
Out[19]:
<AsyncResult: execute>
In [20]:
start = time.time()

res = cluster.map(wakati, text)
parsed = res.get()

elapsed_time = time.time() - start
print(elapsed_time)
0.40604114532470703
In [16]:
parsed[:5]
Out[16]:
['朝 、 食堂 で スウプ を 一 さじ 、 すっと 吸っ て お母さま が 、',
 '「 あ 」',
 'と 幽 かすか な 叫び声 を お 挙げ に なっ た 。',
 '「 髪の毛 ? 」',
 'スウプ に 何 か 、 イヤ な もの でも 入っ て い た の かしら 、 と 思っ た 。']

並列化と変数のスコープ

In [33]:
a = 1
b = 2

cluster.map(lambda x :(a + b), range(10)).get()
[0:apply]: 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)<string> in <module>()
/usr/local/python3/lib/python3.4/site-packages/IPython/parallel/client/remotefunction.py in <lambda>(f, *sequences)
    232             if self._mapping:
    233                 if sys.version_info[0] >= 3:
--> 234                     f = lambda f, *sequences: list(map(f, *sequences))
    235                 else:
    236                     f = map
<ipython-input-33-38d136022dd7> in <lambda>(x)
NameError: name 'a' is not defined

[1:apply]: 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)<string> in <module>()
/usr/local/python3/lib/python3.4/site-packages/IPython/parallel/client/remotefunction.py in <lambda>(f, *sequences)
    232             if self._mapping:
    233                 if sys.version_info[0] >= 3:
--> 234                     f = lambda f, *sequences: list(map(f, *sequences))
    235                 else:
    236                     f = map
<ipython-input-33-38d136022dd7> in <lambda>(x)
NameError: name 'a' is not defined

[2:apply]: 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)<string> in <module>()
/usr/local/python3/lib/python3.4/site-packages/IPython/parallel/client/remotefunction.py in <lambda>(f, *sequences)
    232             if self._mapping:
    233                 if sys.version_info[0] >= 3:
--> 234                     f = lambda f, *sequences: list(map(f, *sequences))
    235                 else:
    236                     f = map
<ipython-input-33-38d136022dd7> in <lambda>(x)
NameError: name 'a' is not defined

[3:apply]: 
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)<string> in <module>()
/usr/local/python3/lib/python3.4/site-packages/IPython/parallel/client/remotefunction.py in <lambda>(f, *sequences)
    232             if self._mapping:
    233                 if sys.version_info[0] >= 3:
--> 234                     f = lambda f, *sequences: list(map(f, *sequences))
    235                 else:
    236                     f = map
<ipython-input-33-38d136022dd7> in <lambda>(x)
NameError: name 'a' is not defined

... 4 more exceptions ...
In [35]:
a = 1
b = 2

cluster["a"] = a
cluster["b"] = b

cluster.map(lambda x :(a + b), range(10)).get()
Out[35]:
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3]

どう使うのかは貴方次第