リモートでJupyter Notebookをつかう

大学のサーバでどうしてもJupyter Notebookが動かしたいのでメモ。

経緯

データをSQLiteで管理しており,これのサイズが10Gを超え始めた。計算などは大学のサーバで行なっているのであるが,RやJupyterなどビジュアルなツールを使いたい時にはいちいちローカルのPCへSQLiteをコピーするのが大変。

以前Xサーバを使ったリモートでの起動を試したが,どうも使いづらい。
Macからだと,XQuartzをあらかじめMacにインストールしておき,
以下のSSHコマンドで接続し,

[mac] $ ssh -Y remotehost

Jupyterを起動するだけ。

[server] $ jupyter notebook

これで,リモートサーバでFirefoxが立ち上がり,MacのXQuartzで表示される。
しかしこれがいかんせん使いづらい。遅いし,サーバ側のFirefoxなので見た目も悪く,日本語入力も一苦労。

設定

Jupyter

まずはここを参考に設定する。

$ jupyter notebook --generate-config

~/.jupyter/jupyter_notebook_config.pyを編集。ポートが8080が空いている場合には以下のように指定する。

c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8080

R

Rも使いたいので,ここを参考にRを起動してインストール。

install.packages(c('repr', 'IRdisplay', 'crayon', 'pbdZMQ', 'devtools'))

devtools::install_github('IRkernel/IRkernel')
IRkernel::installspec()  

起動

$ jupyter notebook

[I 17:43:07.065 NotebookApp] Serving notebooks from local directory: /tmp
[I 17:43:07.066 NotebookApp] 0 active kernels
[I 17:43:07.066 NotebookApp] The Jupyter Notebook is running at:
[I 17:43:07.066 NotebookApp] http://0.0.0.0:8080/?token=37e418b82da8472bf89798f70a92dda90003f61509xxxxx

起動時に現れるこれから,

http://0.0.0.0:8080/?token=37e418b82da8472bf89798f70a92dda90003f61509xxxxx

以下のようにPCのブラウザのアドレスバーに打ち込めばOK

http://server:8080/?token=37e418b82da8472bf89798f70a92dda90003f61509xxxxx

Pythonで誕生日計算


def calc_age(birthdate):
    now=datetime.datetime.now()
    bdate=datetime.datetime.strptime(birthdate,"%Y-%m-%d")
    age=now.year-bdate.year
    if now.timetuple().tm_yday < bdate.timetuple().tm_yday:
        age-=1
    return age

>>> calc_age("2001-11-18")
15
>>> calc_age("2001-10-18")
16

pythonでデバッグログ作成


import re
import logging
logger = logging.getLogger("logger")    #logger名loggerを取得
logger.setLevel(logging.DEBUG)  #loggerとしてはDEBUGで
logging.basicConfig(level=logging.DEBUG,
    filename=re.sub("\..*$","",__file__)+".log",
    format="%(asctime)s %(levelname)-7s %(message)s")


logging.debug("debug log")

正規表現でURLを削除

pythonで正規表現でURLを削除する方法のメモ

ここを参考に作成したのですが,どうもバグっているのか,Pythonと相性が悪いようなので修正した。


import re

str="。少し前ですがhttp://lite-ra.com/2014/11/post-605_2.htmlってどういうことなのでしょうか?"
ret = re.sub(r"(https?|ftp)(:\/\/[-_\.!~*\'()a-zA-Z0-9;\/?:\@&=\+\$,%#]+)", "" ,str)

print ret # 少し前ですがってどういうことなのでしょうか?

cygwin+python+mecab+PyCharmで開発環境を作る

概要

MacからWindwosに乗り換えて一番困るのが開発環境。Pycharmを使ってのpythonの開発はMac以上に面倒です。
WindowsはWindows用のコンパイルされたpythonやmecabを使うのが一般的ですが、モジュールの追加など結構面倒なので、Cygwinを使っている人も多いと思います。
今回、Cygwin上にPythonとMecabをインストールし、それをPycharmから使う環境を作成したのでメモを残します。

環境

* windows 10
* cygwin x86_64
* pycharm 2017.1

cygwinのインストール

ここからsetup-x86_64.exeをダウンロードしインストールします。特別なことは何もしません。通常にインストールします。
余裕があればapt-cygを入れておくと便利です。

pythonのインストール

cygwin上にpythonを入れておきます。自分の場合には2.7系を入れます。

teratermのインストール

cygwinのターミナルは使いにくいのでteratermを入れておきます。teratermにはcygwin用のTerminalもついているのでそちらを起動してCygwinにログインします。

mecabのインストール

Mecabのインストールはソースコードからインストールします。Mecabのバージョンは0.996を使いますがそのままではコンパイルできないので、こちらのパッチを用いてインストールします。

$ tar zxvf mecab-0.996.tar.gz
$ patch -p1 -d ./mecab-0.996/ < ./mecab-0.996.patch
$ cd ./mecab-0.996
$ ./configure --with-charset=utf-8; make; make install

nkfのインストール

nkfをソースからインストールします。
こちらからダウンロードしインストールします

$ ./configure
$ make
$ make install

ipadic

こちらからipadicをダウンロードします。ソースコードになります。これをコンパイルするのですがそのままだとどうも文字化けしてしまいます。ので、UTF-8に変換しておきます

$ tar zxvfp mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ for f in *;do nkf --overwrite -w $f ;done
$ ./configure --with-charset=utf-8

環境変数

windowsの環境変数を設定します。システムの詳細設定から、システムのプロパティを開き、詳細設定タブの環境変数からpathに以下を追加しておきます。これでWindows側からもCygwinのコマンドが呼び出せます。

c:\cygwin64\bin
c:\cygwin64\usr\bin
c:\cygwin64\usr\local\bin

python-mecab

pipでインストールするとエラーになります。ソースコードからインストールします。ここからmecab-python-0.996.tar.gzをダウンロードしコンパイルします

$ tar zxvfp mecab-python-0.996.tar.gz
$ cd mecab-python-0.996
$ python setup.py build
$ python setup.py install

pycharm

ここからダウンロードしインストールします。
インストールしたのちにfileメニューのdefault settingsからproject interpreterを選択し、c:\cygwin64\bin\python2.7.exeを設定します

これでPycharmからCygwinのPythonを利用できます。

Pythonのマルチプロセスとマルチスレッド

マルチスレッドとマルチプロセス

マルチスレッドとマルチプロセスは似ているようで違います。マルチプロセス間の実行中の値は、各プロセス間で別になっていますが、マルチスレッドの場合には変数を共用するのでちょっと注意が必要です。

実行環境

  • cray xc40
  • python 2.7.13

マルチプロセス

ここを参考に

03_multi.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

# http://qiita.com/yubais/items/5a9d91fe03fe715b21d0

import multiprocessing as mp
import sys

L = 40000

proc=int(sys.argv[1])

# 各プロセスが実行する計算
def subcalc(queue, p):
    subtotal = 0

    # iの範囲を設定
    ini = L * p / proc
    fin = L * (p+1) / proc

    for i in range(ini, fin):
        for j in range(L):
            subtotal += i * j
    # キューにデータを送る
    queue.put(subtotal)

# キューを作成
queue = mp.Queue()

# 8個のプロセスを用意
ps=[]
for i in range(proc):
  ps.append(mp.Process(target=subcalc, args=(queue, i)))

# すべてを開始
for p in ps:
    p.start()

# キューから結果を回収
total = 0
for i in range(proc):
    total += queue.get()   # キューに値が無い場合は、値が入るまで待機になる

print(total)

36プロセス

03_multi_36.sh

実行スクリプト


#!/bin/sh
#PBS -N 03_multi_36
#PBS -j oe
#PBS -l select=1:ncpus=36
#PBS -l place=scatter
#PBS -q SINGLE


if [ "${PBS_O_WORKDIR}" != "" ];then
  cd ${PBS_O_WORKDIR}
fi
export OMP_NUM_THREADS=36

. ~/.bashrc

APRUN="aprun -n 1 -d $OMP_NUM_THREADS"

date
$APRUN python 03_multi.py 36
date

結果

2017年  6月  6日 火曜日 09:33:12 JST
639968000400000000
2017年  6月  6日 火曜日 09:33:17 JST

1プロセス

03_multi_1.sh

#!/bin/sh
#PBS -N 03_multi_1
#PBS -j oe
#PBS -l select=1:ncpus=1
#PBS -l place=scatter
#PBS -q SINGLE


if [ "${PBS_O_WORKDIR}" != "" ];then
  cd ${PBS_O_WORKDIR}
fi
export OMP_NUM_THREADS=1

. ~/.bashrc

APRUN="aprun -n 1 -d $OMP_NUM_THREADS"

date
$APRUN python 03_multi.py 1
date

結果

2017年  5月 31日 水曜日 10:44:19 JST
639968000400000000
2017年  5月 31日 水曜日 10:45:35 JST

かなり速度が違います

マルチスレッド

ここを参考

03_multi.py


#!/usr/bin/env python
# -*- coding: utf-8 -*-

# http://qiita.com/yubais/items/5a9d91fe03fe715b21d0
# http://kaworu.jpn.org/python/Pythonのマルチスレッドプログラミング
import threading
import sys
#http://ja.pymotw.com/2/Queue/
import Queue
L = 40000

proc=int(sys.argv[1])

# 各プロセスが実行する計算
def subcalc(queue, p):
    subtotal = 0

    # iの範囲を設定
    ini = L * p / proc
    fin = L * (p+1) / proc

    for i in range(ini, fin):
        for j in range(L):
            subtotal += i * j
    # キューにデータを送る
    queue.put(subtotal)

# キューを作成
queue = Queue.Queue()

# 8個のプロセスを用意
ps=[]
for i in range(proc):
  ps.append(threading.Thread(target=subcalc, args=(queue, i)))

# すべてを開始
for p in ps:
    p.start()
    p.join()

# キューから結果を回収
total = 0
for i in range(proc):
    total += queue.get()   # キューに値が無い場合は、値が入るまで待機になる

print(total)

36プロセス

04_thread_36.sh

実行スクリプト


#!/bin/sh
#PBS -N 04_thread_36
#PBS -j oe
#PBS -l select=1:ncpus=36
#PBS -l place=scatter
#PBS -q SINGLE


if [ "${PBS_O_WORKDIR}" != "" ];then
  cd ${PBS_O_WORKDIR}
fi
export OMP_NUM_THREADS=36

. ~/.bashrc

APRUN="aprun -n 1 -d $OMP_NUM_THREADS"

date
$APRUN python 04_thread.py 36
date

結果

2017年  6月  6日 火曜日 09:39:57 JST
639968000400000000
2017年  6月  6日 火曜日 09:41:13 JST

1プロセス

04_thread_1.sh

実行スクリプト


#!/bin/sh
#PBS -N 04_thread_1
#PBS -j oe
#PBS -l select=1:ncpus=1
#PBS -l place=scatter
#PBS -q SINGLE


if [ "${PBS_O_WORKDIR}" != "" ];then
  cd ${PBS_O_WORKDIR}
fi
export OMP_NUM_THREADS=1

. ~/.bashrc

APRUN="aprun -n 1 -d $OMP_NUM_THREADS"

date
$APRUN python 04_thread.py 1
date

結果

017年  6月  6日 火曜日 09:39:57 JST
639968000400000000
2017年  6月  6日 火曜日 09:41:13 JST

python+SQLiteでlike文

ここを参考に、

Asciiの場合


cursor.execute("SELECT * FROM posts WHERE tags LIKE ?", ('%{}%'.format(tag),))

Unicodeの場合

tagはunicode string


cursor.execute("SELECT * FROM posts WHERE tags LIKE ?", (u"%{}%".format(tag),))

ValueError: unsupported format character ‘Y’ (0x59) at index 51

MySQL+Pythonのエラー

ValueError: unsupported format character 'Y' (0x59) at index 51

このようなエラーが出るときの対応。


import MySQLdb
ymd="2017-05-19"

con=MySQLdb.connect(user="root",password="",host="localhost",db="test")
cur=con.cursor()
cur.execute("set names utf8")   # 文字化け対応
cur.execute("select * from test_table where date_format(ymd,'%%Y-%%m-%%d')=%s",(ymd,))
for row in cur.fetchall():
  print(row[0])

これだと上記エラーが発生します。

ここに解決策が載っていました。

ポイントはsql文の文字列をformat()するだけ


import MySQLdb
ymd="2017-05-19"

con=MySQLdb.connect(user="root",password="",host="localhost",db="test")
cur=con.cursor()
cur.execute("set names utf8")   # 文字化け対応
cur.execute("select * from test_table where date_format(ymd,'%%Y-%%m-%%d')=%s".format(),(ymd,))
for row in cur.fetchall():
  print(row[0])

Tensorflowのエラー

Tensorflowをバージョンアップすると、昔動いていたスクリプトが動かなくなったりします

    cell = tf.nn.rnn_cell.BasicLSTMCell(size, forget_bias=0.0)
AttributeError: 'module' object has no attribute 'rnn_cell'

こんなエラーとか。

こちらを参照し修正します。

これを

tf.nn.rnn_cell.BasicLSTMCell

これに変更

tf.contrib.rnn.BasicLSTMCell

SQLiteをPythonからインメモリで使う

SQLite、手軽で便利なデータベースですが激しく使っていると速度が気になる時もあります。

SQLiteはインメモリデータベースもサポートしているので、既存のSQLiteのデータベースからインメモリ化して読み取り専用にすると早くなります。

データベース準備

適当に大きなデータベースを用意します。


#!/bin/env python
# coding:utf-8

import sqlite3

con=sqlite3.connect("test.db")
con.cursor().execute("CREATE TABLE test( key integer, val integer , primary key(key))")
con.commit()


for key in range(1,10000000+1):
  con.cursor().execute("insert into test values(?,?)",(key,key+1,))
con.commit()

1000万レコードのデータベースを作成しました。


$ sqlite3 test.db 
SQLite version 3.7.6.3
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select count(*) from test;
10000000

プログラム

こちらを参考にプログラムを作成します。


#!/bin/env python
# coding:utf-8

import sqlite3
from StringIO import StringIO
import time
import random

# in memory化
con=sqlite3.connect("test.db")
tempfile=StringIO()
for line in con.iterdump():
    tempfile.write("%s\n" % line)
tempfile.seek(0)
mcon=sqlite3.connect(":memory:")
mcon.cursor().executescript(tempfile.read())
mcon.commit()
mcon.row_factory=sqlite3.Row


current_milli_times = lambda: int(round(time.time() * 1000))

print "ready.."
# normal
# select 10000 times
N=10000
nstart=current_milli_times()
for i in range(N):
    key=random.randrange(10000000)
    res=con.cursor().execute("select * from test where key=?",(key,))

nend=current_milli_times()
print "normal:"+str(nend-nstart)


# inmemory
mstart=current_milli_times()
for i in range(N):
    key=random.randrange(10000000)
    res=mcon.cursor().execute("select * from test where key=?",(key,))
mend=current_milli_times()
print "inmemory:"+str(mend-mstart)

conのコネクションが通常のデータベースアクセス、mconがデータベースファイルをインメモリ化したものになります。10000回ランダムにSELECTしてみます。

結果

ready..
normal:414
inmemory:171

大学のスパコンで計算したのですが2倍以上の差が出ました。