TreeTaggerをインストールしてPython2.7で英語の形態素解析をしてみた

英語の形態素解析をするにあたり、TreeTaggerをいれてみました。

NLTK、Stanford NLPというのもあるそうなのですが、なんとなくTreeTaggerを選んでみました。

TreeTaggerをインストー

/usr/local/srcにインストールする場合の例です。 公式サイトから必要なものをダウンロードしてインストールします。

$ cd /usr/local/src
$ mkdir tree-tagger
$ cd tree-tagger/
# TreeTaggerのインストールに必要なものをダウンロード
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/tree-tagger-linux-3.2.tar.gz
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/tagger-scripts.tar.gz
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/english-par-linux-3.2-utf8.bin.gz
# インストール用シェルスクリプトをダウンロード
$ wget http://www.cis.uni-muenchen.de/~schmid/tools/TreeTagger/data/install-tagger.sh
# パーミッションを変更してシェルスクリプトを実行
$ chmod u+x install-tagger.sh
$ ./install-tagger.sh

必須ではないですが、.bashrcに追記をしてパスを通しておきます。

$ vi ~/.bashrc
    #以下の内容を追記
    PATH="$PATH":/usr/local/src/tree-tagger/cmd
    PATH="$PATH":/usr/local/src/tree-tagger/bin
$ bash

以下が実行できればOKです。

$ echo 'Hello world!' | tree-tagger-english 

PythonでTreeTaggerを使う方法

TreeTaggerには有志の人が作成したPythonラッパーがあるのでそれを利用します。

$ pip install treetaggerwrapper

pipでのインストールが成功したら、Pythonから使えることを確かめてみます。

$ echo "This is the sentence." | python -m treetaggerwrapper --pipe --debug

  ERROR Failed to find TreeTagger from automatic directories list.
  ERROR If you installed TreeTagger in a standard place, please contact the treetaggerwrapper author to add this place to this list.
  ERROR To continue working, setup TAGDIR env var to TreeTagger directory.
  ERROR Can't locate TreeTagger directory (and no TAGDIR specified).

TreeTaggerがインストールされているディレクトリが見つからなかったよとのことです。

TreeTaggerWrapperのドキュメントによると、TreeTageerオブジェクトに引数でTAGDIRを指定して渡せば良いそうなので下記のようなexample.pyを作成して実行してみます。

import treetaggerwrapper
tagger = treetaggerwrapper.TreeTagger(TAGLANG='en',TAGDIR='/usr/local/src/tree-tagger')
tags = tagger.TagText("This is the sentence.")
for tag in tags:
    print tag

スクリプトを実行。

$ python example.py

すると、以下のようなエラーが発生。Unicodeじゃないとダメだよってことらしいです。

treetaggerwrapper.TreeTaggerError: Must use *unicode* string as text to tag.

Python3だとデフォルトがUnicodeらしいのですが、Python2の場合はu"string"というように「u」をつけてあげる必要があります。

なので、先ほどのスクリプトを以下のように変更します。

tags = tagger.TagText(u"This is the sentence.")

そして、実行。

$ python example.py
  This      DT   this
  is        VBZ  be
  the       DT   the
  sentence  NN   sentence

うまく形態素解析ができました。

TreeTagger公式サイト

rsyncでpermission denied (publickey) となった時の対処法

rsyncでローカル環境とリモートサーバーのディレクトリを同期したい!

ということありますよね。そう思い、rsyncを使ってみたらつまずいた時の対処法です。

エラーの内容と原因

ローカル環境の現在のディレクトリと、リモートサーバーの/home/wwwを同期しよう思い、実行したのが以下のコマンド。

$ rsync --delete -avzc ./* www@11.22.33.444:/home/www
 Permission denied (publickey).
 rsync: connection unexpectedly closed (0 bytes received so far) [sender]
 rsync error: unexplained error (code 255)

見事にPermission deniedされてますね。

もちろん、~/.ssh/configには以下のように設定を追記済みで、ssh remoteで接続できることも確認済です。

Host remote
  HostName 11.22.33.444
  User www
  Port 22
  IdentityFile ~/.ssh/id_rsa

rsyncのエラー解決策

rsyncは、裏側でsshをデフォルトでは使っているそうです。そこで、rsyncのeオプションを使い秘密鍵を指定してssh接続をしてみたいと思います。

eオプションを使いダブルクオーテーションで囲むことでsshのiオプションを使うことができます。

-e "ssh -i /Users/sample_user/.ssh/id_rsa"

コマンド全体としては↓のような感じになります。

$rsync --delete -avzc -e "ssh -i /Users/sample_user/.ssh/id_rsa"  ./* www@11.22.33.444:/home/www

これで実行したところ…できた!

秘密鍵がちゃんと指定できなかったことが問題だったようです。rsyncはあまり使ったことがないので、割りと雑な対処ですがこれでなんとか同期ができました。

Transient APIがW3TCでAPCを有効にすると動作しない件

Transient APIを使ってキャッシュをしようしても正常に動作しない。

環境と事象

環境:
PHP 5.4.45
APC 3.1.15dev
WordPress 4.3.1
W3 Total Cache 0.9.4.1

具体的な事象としては、

  • set_transient()をするとtrue
  • get_transient()で保存したキャッシュを取得しようとするとfalse
  • W3 Total CacheのObject Cache Method:をDiskにすると正常に動作

といった感じです。

TransientAPIが動作しない原因

WordPressのバージョンを更新した時に発生したので、おそらくW3 Total CacheのオブジェクトキャッシュとWordPress本体のTransient APIが何らかのコンフリクトを起こしているのかと思いました。

これを詳しく調べると泥沼にはまりそう&原因がわかってもWordPressプラグイン側の問題だとしたらあまり手を出したくないので、今回は以下の解決策で対処しました。

TransientAPIのエラー解決策

1. オブジェクトキャッシュにAPCを使わない

Diskにするとパファーマンスは落ちますが、正常に動作します。

eAcceleratorやmemcachedは試していないので不明です。

2. APCを直接操作する

オブジェクトキャッシュをAPCのユーザーキャッシュにストアするには、なにもTransient APIを使う必要はありません。

APCが有効されているPHPでは、apc_store()やapc_fetch()といった関数でAPCのキャッシュを操作できます。

ぞれぞれ、下記のようにset_transient()など同じように引数をとります。APCを利用する場合は、ほぼ同様に使うことができるのではないでしょうか。

apc_store ( string $key , mixed $var [, int $ttl = 0 ] )
apc_fetch ( mixed $key [, bool &$success ] )

$key:一意なキー
$var:保存する値
$ttl:保存期間

なので、set_transient()をapc_store()に、get_transient()をapc_fetch()にそれぞれ変更すれば動作すると思います。

注意点:APCPHP CLIからは実行できない

PHP CLIからはAPCは動かないので、テスト用スクリプトを書いてターミナルで実行してもfalseになるので注意が必要です。

(これで数時間無駄にしました。。。)

Crondの(CRON) bad commandエラーの対処法

cronを設定したはずなのに、動かない時の対処方法です。ログを確認する前にcronの状態とファイルパーミッションを確認します。

$ service crond status

crondがrunning状態であることを確認。

$ ls -la

また実行ファイル(シェルスクリプト)のパーミッションを確認して、ファイルの実行権限が正しく設定されているか確認します。

ここまで問題なければ、cronのログファイル(/var/log/cron)を確認します。

$ tail /var/log/cron

確認すると以下のようなbad commandエラーが出ていました。

crond[1668]: (CRON) bad command (/etc/cron.d/sample)

cronの実行エラーの原因・対処法

原因は、/etc/cron.d/sampleのファイル末尾に空行がなかったせいでした。

以下のようにファイル末尾には空の行を1つ入れないとbad commandとなってしまうようです。

0 3 * * * /backup/bin/mysql_dump.sh

これはcrontab -e コマンドを使わずに、以下のように直接ファイルを作成したことによるものでした。

$ echo "0 3 * * * /backup/bin/sample.sh" > /etc/cron.d/sample

対処法は簡単で作成したファイルの末尾に行を追加すれば良いだけです。

Webhook+PHPを利用したGitデプロイ自動化

BitbucketのWebhook+PHPスクリプトによる外部サービスを使わない簡単なデプロイ自動化をしてみます。

今回の環境は以下のような感じ。nginxですが、ApacheでもSSHキーの部分を変更すれば対応できるかと思います。

▼環境
Bitbucket
CentOS 6
nginx
php-fpm

PHPでシェルコマンドを実行するexec()関数が使える前提

デプロイ自動化のイメージ

  1. ローカルからBitbucketにpush
  2. BitbucketからWebhookでリモートサーバーのdeploy.phpにリクエス
  3. deploy.phpがexec()でgitコマンドを実行
  4. git fetch && git checkoutで更新を反映
ローカル → Bitbucket → リモートサーバー

サーバーにGitレポジトリをクローン

$ mkdir /git
$ cd /git
$ git clone --mirror git@bitbucket.org:accountname/myproject.git

git clone –mirror とするとリポジトリのミラーが作成されます。

$ cd /git/myproject.git
$ GIT_WORK_TREE=/var/www/html git checkout -f

WordPressのテーマだけホストする場合は、GIT_WORK_TREEを/var/www/html/wp-content/themes/twentyfourteenというようにします。

デプロイ用のPHPスクリプトを作成

まずデプロイ用のPHPスクリプトを作ります。PHPでシェルコマンドを実行するので、exec()関数使います。

実際にexec()で実行するシェルコマンドは以下のような感じ。

$ cd /git/myproject.git/
$ git fetch
$ GIT_WORK_TREE=/var/www/html/ git checkout -f

実際のPHPスクリプトは以下の通りです。今回は、ログ出力もなしのでミニマムのスクリプトを紹介します。

$git_path, $www_path, $secret_keyを環境に合わせて変更してください。

//GitとWebのディレクトリへのフルパス
$git_path = '/git/myproject.git/';
$www_path = '/var/www/html';

//example.com/deploy.php?key=以下の任意のキー
$secret_key = 'YOUR_SECRET_KEY';

//keyパラメータと$secret_keyが合っていればexec()
if ($_GET[key] === $secret_key)  {
    $command = "cd {$git_path} && git fetch && GIT_WORK_TREE={$www_path} git checkout -f";
    exec($command);
}

/var/www/html以下にdeploy.phpとして保存します。

$ vi /var/www/html/deploy.php

nginxのSSHキーをssh-keygenで作成

リモートサーバーのSSHキーをBitbucketに登録します。

外部(Bitbucket)からのHTTPリクエストを受けて、deploy.phpが実行されるのでSSHキーはPHPを実行するnginxのものを作ります。

もしApacheを使っている場合は、Apacheに置き換えればOKかと。

sshキーの置き場所は、Apacheのデフォルトと同じ/var/www/.sshにします。

nginxのデフォルトだと/var/cache/nginx/.sshになっていて、キャッシュ用ディレクトリのせいなのか2度ほど消えてしまったことがあるためです。

$ mkdir /var/www/.ssh
$ chmod 777 .ssh
$ sudo -u nginx ssh-keygen
Enter file in which to save the key (/var/cache/nginx/.ssh/id_rsa):

とでるので、/var/www/.ssh/id_rsaと入力してEnter。パスワードは空のままなのでEnterします。

Generating public/private rsa key pair.
Enter file in which to save the key (/var/cache/nginx/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/cache/nginx/.ssh/id_rsa.
Your public key has been saved in /var/cache/nginx/.ssh/id_rsa.pub.
The key fingerprint is:
aa:jd:87:40:58:cn:as:jd:73:8f:a0 nginx@example.com
The key's randomart image is:
+--[ RSA 2048]----+
|          .      |
|         .       |
|     . + *       |
|    .   S o      |
|    = . = .  oE  |
|       .   .=.+o |
|        S...o= oo|
+-----------------+

SSHキーは/var/www/.ssh/に作成されました。

パーミッションを変更しておきます。

$ chown -R nginx:nginx .ssh
$ chmod 700 -R .ssh

あとはSSHを登録。

$ ssh-agent /bin/bash
$ ssh-add /var/www/.ssh/id_rsa

登録されていることを確認します。

$ ssh-add -l
  2048 aa:jd:87:40:58:cn:as:jd:73:8f:a0/var/www/.ssh/id_rsa (RSA)

Bitbucketのデプロイキー登録&Webhookの設定

$ cat /var/www/.ssh/id_rsa.pub

Settings > デプロイ鍵 > 鍵を追加をクリック。catした文字列をコピペ。

bitbucket-auto-deploy4

さらに、Webhookを設定します。Settings > Webhooks > Add webhookをクリック。

タイトルは、「Deploy to exmaple.com」など好きなタイトルにして、URLは「http://example.com/deploy.php?key=SECRET_KEY」を入力します。

bitbucket-auto-deploy3

フックとなるアクションはpush以外にもmergeなどもあるので、開発サーバーのフックはdevelopブランチへのpush、本番サーバーのフックはmasterブランチへのmergeなど分けることもできます。

パーミッションを変更

/gitのパーミッションと/var/www/.sshを変更します。

$ chown -R nginx:nginx /git/
$ chown nginx:nginx /var/www/html/deploy.php

ssh_configの編集

nginxのsshキーの場所をデフォルトから変更したので、ssh_configを編集します。

~/.ssh/configはユーザー用、ssh_configはシステム共通のsshの設定ファイルです。

$ vi /etc/ssh/ssh_config

以下の内容を追記します。

Host bitbucket.org
  User git
  IdentityFile /var/www/.ssh/id_rsa

ローカル開発環境からpush

最後にローカル開発環境からpushします。README.mdなど好きなファイルを編集して、pushしてください。

$ git add README.md
$ git commit -m 'change README.md
$ git push -u origin master

あとは、サーバー側も更新されていればOK!ファイルサイズによっては、デプロイに時間がかかる場合もあります。

BitbucketのWebhookが動かない場合のトラブルシューティング

以下の順で確認していきます。

1. Webhookが200を返しているか
2. PHPスクリプトは間違っていないか
3. ファイルパーミッションの確認
4. デプロイキーが正常に登録されているか
5. exec()でgitコマンドが実行されない

1. Webhookが200を返しているか

Bitbucketのレポジトリ > Settings > Webhook からView requestsをクリック。

bitbucket-auto-deploy1

Webhookのリクエスト履歴が見れるので、右端のステータスコードが200になっていることを確認。

bitbucket-auto-deploy2

2. PHPスクリプトは間違っていないか

サーバーにssh接続して、ターミナルから直接PHPを実行してみます。これで更新が反映されているようなら、PHPには問題ないはずです。

$ cd /var/www/html/
$ php deploy.php

3. ファイルパーミッションの確認

ファイルパーミッションが正しく設定されているか確認します。ls -laで確認するか、chown、chmodコマンドを再実行してみてください。

4. デプロイキーが正常に登録されているか

そのユーザーのssh鍵の設定が正しく行われているか確認します。実際にnginxユーザーになってgit fetchして確認するのが簡単なのでやってみます。

$ su -s /bin/bash nginx
$ cd /git/myproject.git
$ git fetch

ついでに、nginxユーザーでphp deploy.phpをして正常に動くことも確認しておくと良いと思います。

5. exec()でgitコマンドが実行されない

意外と落とし穴なのが、HTTPリクエストで実行されるPHPスクリプトがnginxじゃなくてApacheだったといった単純な罠。

これを確認するために、シェルコマンドのwhoamiを利用します。/var/www/htmlにtest.phpなどを作って以下をコピペ。

echo exec("whoami");

http://example.com/test.phpにブラウザからアクセスするとPHPスクリプトがどのユーザーで実行されたのか表示されます。

もし実行結果がapacheとなっていれば、service nginx statusとservice httpd statusでnginxが起動していて、apacheが止まっていることを確認。

さらに、/etc/php-fpm.d/www.confを開いてuserとgroupがnginxになっていることを確認します。

user = nginx
group = nginx

ここまで確認できればある程度のエラーには対応できるかなと思います。

MySQLが「Fatal error: cannot allocate memory for the buffer pool」となり起動しない問題

開発環境のWordPressがいきなり「データベース接続確立エラー」となり、mysqlが起動しなくなってしまった時の対処法です。

原因:buffer poolのメモリ割当が少なかったせい

mysqlを起動すると以下のようにエラーに。

$ service mysqld start
MySQL Daemon failed to start.
Starting mysqld:                                           [FAILED]

まずは、/var/log/mysqld.logを確認。すると以下のようなエラーがでていました。

151117  9:55:58 InnoDB: Initializing buffer pool, size = 128.0M
InnoDB: mmap(137363456 bytes) failed; errno 12
151117  9:55:58 InnoDB: Completed initialization of buffer pool
151117  9:55:58 InnoDB: Fatal error: cannot allocate memory for the buffer pool
151117  9:55:58 [ERROR] Plugin 'InnoDB' init function returned error.
151117  9:55:58 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
151117  9:55:58 [ERROR] Unknown/unsupported storage engine: InnoDB
151117  9:55:58 [ERROR] Aborting

原因となりそうなのはこの部分。

151117  9:55:58 InnoDB: Fatal error: cannot allocate memory for the buffer pool

対処法:my.cnfのinnodb_buffer_pool_sizeを増やす

メモリの割当ができなかったようなので、/etc/my.cnfを編集してメモリの割当を増やします。とりあえず、512Mにすることに。

innodb_buffer_pool_size = 512M

そこで、MySQLを再起動。

$ service mysqld start
Starting mysqld:   [  OK  ]

これでMySQLが再起動しました。

*注意* innodb_log_file_sizeは変更しない

ちなみにinnodb_log_file_sizeを合わせて変更しようとすると、MySQL 5.6未満だとエラーとなるようです。

特に理由がなければ、innodb_buffer_pool_sizeのメモリを増やすだけで良いかなと思います。

[MySQL] innodb_log_file_sizeを変更した時のエラー対処

innodb_log_file_sizeを変更した時のエラー対処

/tmp/my.cnfのinnodb_log_file_sizeを変更すると、エラーになりmysqlが起動できないという現象に。

mysqld.logを見てみると以下のようなエラーに。

InnoDB: Error: log file ./ib_logfile0 is of different size 0 5242880 bytes
InnoDB: than specified in the .cnf file 0 134217728 bytes!

my.cnfで指定したメモリのサイズとib_logfile0のサイズが違うとのこと。ib_logfile0はInnoDB ログファイルです。

どうやら、MySQL 5.6 未満でmy.cnfのinnodb_log_file_sizeを変更すると発生するようです。

いきなり消すのは怖いので、/tmpに一旦退避させます。

$ mv /var/lib/mysql/ib_logfile0 /tmp
$ mv /var/lib/mysql/ib_logfile1 /tmp

そして、またmysqlを再起動すると以下のようなエラーに。

151118  7:04:09  InnoDB: Log file ./ib_logfile0 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile0 size to 128 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100
151118  7:04:09  InnoDB: Log file ./ib_logfile1 did not exist: new to be created
InnoDB: Setting log file ./ib_logfile1 size to 128 MB
InnoDB: Database physically writes the file full: wait...
InnoDB: Progress in MB: 100
151118  7:04:09 InnoDB: Cannot initialize created log files because
151118  7:04:09 InnoDB: data files are corrupt, or new data files were
151118  7:04:09 InnoDB: created when the database was started previous
151118  7:04:09 InnoDB: time but the database was not shut down
151118  7:04:09 InnoDB: normally after that.

調べてみると、ib_logfile0などを消すのはあまり良くない対処のよう。

公式の、nnoDB ログファイルの数またはサイズの変更、および InnoDB テーブルスペースのサイズの変更を参考に対処しました。