kerasを使ったreuter記事分類のexampleをなぞる

github.com

kerasにはデータセットが用意されています1

今回はその中からReuter記事データの分類をしてみます。 基本的にはこちらのexampleに示されているコードをなぞる形です。

kerasのインストール

pythonは3系を使います。

TensorFlowのインストール

keras自身のインストールに先立ち、バックエンドとして使用する機械学習ライブラリをインストールする必要があります。TensorFlowやTheano,CNTKなどから選ぶ事ができますが2、今回はTensorFlowを利用します。

$ sudo apt-get install python3-pip python3-dev
$ sudo pip3 install tensorflow

kerasのインストール

$ sudo pip3 install keras

Reuter記事の分類

データセットのロード

from keras.datasets import reuters
(x_train, y_train), (x_test, y_test) = reuters.load_data(num_words=1000, test_split=0.2)

記事のデータセットエンコードされ、配列になっています。それぞれの単語にはデータセット全体の中で頻度の多い方から順にインデックス番号が振られています。

x_train.shape # (8982,)
x_train[0][:10] # [1, 4, 2, 2, 9, 697, 2, 111, 8, 25]

引数のnum_wordsは最頻出上位○○位の値、test_splitはtrainデータとtestデータの割合(今回は4:1)を指定しています。

データの前処理

入力データ(x)は配列から行列の表現に変換する必要があります。

from keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=1000)
x_train = tokenizer.sequences_to_matrix(x_train, mode='binary')
x_test  = tokenizer.sequences_to_matrix(x_test, mode='binary')

tokenizerのイニシャライザには先ほどと同じ値を指定します。sequences_to_matrixmodeにbinaryを指定すると、文中にインデックス番号の単語が含まれていれば1、そうでなければ0が値となるベクトルに変換されます。

教師データ(y)はone-hot表現に変換します。

from keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test  = to_categorical(y_test)

モデルの構築

exampleよりもシンプルなモデルにします。出力のサイズ(分類の数)は46です。

from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense(512, input_shape=(1000,), activation='relu'))
model.add(Dense(256, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(46, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

学習

model.fit(x_train, y_train, batch_size=32, epochs=5, verbose=2, validation_data=(x_test, y_test))

(メモ: GCEのn1-standard-1タイプインスタンスで各エポックあたり6s)

評価

score = model.evaluate(x_test, y_test, verbose=0)
score[0] # 損失関数の値: 1.0874391948125879
score[1] # 正答率: 0.7738201247191493

コード全体 → reuter_mnist.py · GitHub

疑問

  • modeの値にbinary以外を指定するとどうなるんだろう...

Refs

publickeyを使ったsshの設定

1.鍵を用意する

$ ssh-keygen -t rsa

2.公開鍵をインスタンス上の ~/.ssh/authorized_keys に追記する

$ cat ~/.ssh/ssh_test.pub | ssh ([USER]@)[HOSTNAME] "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys"

3.秘密鍵を使ってsshする

$ ssh -i ~/.ssh/ssh_test ([USER]@)[HOSTNAME]

4.~/.ssh/config を設定する

Host ssh-test
         HostName [HOSTNAME]
         User [USER]
         IdentityFile /path/to/home/.ssh/ssh_test
$ ssh ssh-test

Refs

Solrについて

Solrとは

Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene™.

Apache Solr -

こちらの記事が要点を抑えていてわかりやすいです。

qiita.com

形態素解析

文章を単語ごとに分解する方法。

例:「東京特許許可局」 →「東京」「特許」「許可局」

意味のわからないトークンが作られない一方、例えば「京特」で調べた時に引っかからないという事が起こる。

N-gram

文章をN文字で機械的に分解する方法。

例:「東京特許許可局」N=2 →「東京」「京特」「特許」「許許」「許可」「可局」

検索する文字に対して漏れなく結果を返せる反面、それが全く関連のないノイズとなりえる。

転置インデックス

データソースとトークンの間のM:N関係。

動かしてみる

こちらの記事を参考にdockerで動かしてみます。

$ docker pull solr
$ docker run -d --name=solr -p8983:8983 solr

NOTE: docker run をすると8983ポートでsolrが起動します。もし以下のTutorialのようにSolrCloudを試したい場合は違うポートを繋いでおき、コンテナ内で別プロセスを立ち上げるのが楽です。またその後任意のコマンドを実行する際にはポートを指定する必要があるので注意。

疑問

  • Schemaの書き方
  • 検索の適合度
  • 地理検索機能の実例

Refs

読書リスト2017

kentakudo.hatenablog.com

あっと言う間すらないほど早い一年でした。2017年版です。

  1. チームが機能するとはどういうことか
  2. Webを支える技術
  3. 大規模開発サービス技術入門
  4. Webサービスの作り方
  5. まつもとゆきひろ 言語の仕組み
  6. ゼロから作るDeep Learning
  7. Server-Side Swift
  8. きつねさんでもわかるLLVM
  9. 開眼!JavaScript
  10. JavaScript本格入門
  11. Reactビギナーズガイド
  12. ふつうのLINUX
  13. 新しいLinuxの教科書
  14. プロになるためのWeb技術入門
  15. できるPRO Apache Webサーバー
  16. Real world http
  17. 計算理論の基礎
  18. オペレーションシステムの仕組み
  19. JavaScriptデザインパターン
  20. 最短経路の本
  21. アルゴリズムサイエンス 出口からの超入門
  22. たった2日でわかるLinux
  23. PHPはどのように動くのか
  24. プログラミングコンテストチャレンジブック
  25. アルゴリズムクイックリファレンス
  26. 達人に学ぶSQL徹底指南書
  27. 達人に学ぶDB設計徹底指南書
  28. The Go Programming Language
  29. SQLパズル
  30. プログラマのためのSQL
  31. SQLアンチパターン

計31冊(減ってるやん。。。)


前半は相変わらず乱読を続けていましたが、秋頃から一つのテーマを掘り下げるスタイルにしました。 こちらの方がいろんな知識を線で捉えられるため体系的に学びやすいと感じているのでとりあえず続けてみようと思います。

春から夏にかけてJavaScriptに興味が湧いたため手をつけていたのですが、仕事で使わないだけでなくプライベートで何かを作る、ということもしなかったため中途半端な知識になってしまったのが残念です。反省。

今は「トレンドを追うよりもコンピュータサイエンスに近いところを学んで足腰を鍛えることを優先した方が後々効率が良いのではないか」という気がしているので、以前よりもだいぶ慎重に読む本を選んでいます。

昨年「2017年学びたいこと」として

をあげていました。達成度は30%以下です。反省。

来年は

あたりを中心に読みたいなと思っています。

また、最近こちらのエントリを読んで感心しました。

qiita.com

「より効率的な読書法」も同時に求めていきたいと思います。

Varnishについて

今週FastlyのStockholm(BMA)で障害が発生1し、私たちのサイトも影響を受けました。その際出てきた「varnish」というミドルウェアについて調べたときのメモです。

Varnishとは

github.com

Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy.

Introduction to Varnish

  • HTTPレベルでキャッシュを行うHTTPアクセラレータ。
  • カーネルの機能を最大限利用することで高速化を図っている。
  • ディスクへのデータ書き込みをデフォルトでは一切しないので、プロセス終了時に全てのキャッシュが消える。
  • 設定はVCL(Varnish Configuration Language)というDSLで記述する。動的に変更可能。
  • ログもデフォルトではファイルに書き込まれない。
  • Fastlyの中心技術2。各リージョンにVarnishキャッシュサーバを置いていると雑に考えても間違いではなさそう。
  • SSLが使えないのでnginxを前に置く構成が多いらしい。

動かしてみる

varnishをnginxの手前に置いて、nginxのindex.htmlをvarnish経由で返してみる。

参考記事: Getting Started with Varnish Cache

環境

1. コンテナの起動 3

$ docker run --privileged -d -p80:80 --name=varnish centos /sbin/init

2. nginxのインストール&起動 4

$ sudo vi /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1
$ yum install -y nginx
$ systemctl start nginx

3. Varnish Cashのインストール

$ yum install -y epel-release
$ yum install -y varnish

4. nginxのポートを8080に変更

etc/nginx/conf.d/default.conf

server {
    listen       8080;

    ...
}

リロード

$ systemctl reload nginx

5. varnishのポートを80に変更

etc/varnish/varnish.params

VARNISH_LISTEN_PORT=80

起動

$ systemctl start varnish

6. アクセスしてみる

$ curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/1.13.6
Date: Sun, 19 Nov 2017 10:46:05 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 10 Oct 2017 15:59:40 GMT
ETag: "59dcee6c-264"
X-Varnish: 22 20
Age: 3
Via: 1.1 varnish-v4
Connection: keep-alive

2回目アクセス以降X-Varnishの値が2つ返っていればキャッシュされている5

また、nginxのアクセスログに2回目以降のアクセスログが残らないことも確認できた。(キャッシュ間隔のデフォルトは5分。)

疑問

  • (リバースプロキシとしている)nginxでも同じことができるのではないか。
    • わざわざnginxの後ろにVarnishを置く理由。

参考

JSON-RPCについて

JSON-RPCについて調べた際のメモです。

特徴

JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol.

JSON-RPC 2.0 Specification

  • データのエンコードJSONを利用したRPC(Remote Procedure Call)1プロトコルの一種。
  • レスポンスを期待しない「通知」、複数の呼び出しを一度に行う「Batch」が可能。
  • リクエスト、レスポンスのjsonに含めるべきフィールドが決められており(プロトコルなので)、それを満たしさえすればよい。
  • HTTPメソッドは全てPOSTとするのが通例。

リクエス

以下、例は主にJSON-RPC 2.0 Specificationから拝借いたしました。

  • jsonrpc [string]: JSON-RPCのバージョン(現在2.0)
  • method [string]: 呼び出したいメソッド名
  • params [array|object]: メソッドの引数(単純に列挙する場合はarray、ラベルを使う場合はobject)
  • id [string|int]: リクエストを識別するID、レスポンスに同じ値が含まれる、よってBatch(後述)で送ってもどのリクエストに対するレスポンスかを識別できる
{
  "jsonrpc": "2.0",
  "method": "subtract",
  "params": [42, 23],
  "id": 1
}

ラベル付き

{
  "jsonrpc": "2.0",
  "method": "subtract",
  "params": {
    "subtrahend": 23,
    "minuend": 42
  },
  "id": 3
}

レスポンス

成功時:

  • jsonrpc [string]: JSON-RPCのバージョン
  • result [object]: 成功時のデータ、特にフォーマットの指定はない
  • id [string|int]: リクエストID

失敗時:

  • jsonrpc [string]: JSON-RPCのバージョン
  • error [object]: 失敗時のデータ、フォーマットは以下の通り
  • id [string|int]: リクエストID

errorのフォーマット

  • code [int]: エラーコード、あらかじめ定義されている → error object
  • message [string]: エラーメッセージ
  • data [any]: 追加情報用のフィールド、型の指定もなく、省略も可能

成功時:

{
  "jsonrpc": "2.0",
  "result": 19,
  "id": 1
}

失敗時:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32601,
    "message": "Method not found"
  },
  "id": "1"
}

通知

リクエスト時にIDフィールドを省略すると、レスポンスを期待しないと解釈され、何も返ってこない。これを「通知」と呼んでいる。

{
  "jsonrpc": "2.0",
  "method": "update",
  "params": [1,2,3,4,5]
}

Batch

トップレベルを配列にし、リクエストを複数一気に送ることができる。

リクエスト:

[
  {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
  {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
  {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
  {"foo": "boo"},
  {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
  {"jsonrpc": "2.0", "method": "get_data", "id": "9"} 
]

レスポンス:

[
  {"jsonrpc": "2.0", "result": 7, "id": "1"},
  {"jsonrpc": "2.0", "result": 19, "id": "2"},
  {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
  {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
  {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]

GoでJSON-RPCを扱う代表的なパッケージは以下の3つ

【思ったより手間取っているので追記予定】

疑問

  • JSON-RPCを使う理由
    • URL設計が鍵になるRestAPIとメソッド設計が鍵になるJSON-RPCだと大分性格が違うと感じたが、どのケースはRestAPI向きでどのケースはJSON-RPC向きなのか
  • 一般的なidの割り振り方

参考


  1. RPCの例 wiki

ISUCONカンニングペーパー

【10/23追記】 f:id:KK462:20171024034517p:plain

残念ながら本選に出場することはできませんでした。

インデックス貼ったりN+1を直したりはできたのでせめて最後にスコアを残したかったと後悔しています。

とはいえ8時間があっという間に感じるくらい楽しかったです。 夜中の2時のサイクリングも最高にエキサイティングでした。

準備不足につきます。 来年こそは本選出場できるように頑張ります。


サーバーの状態確認コマンド群

pstree
ps auxwf
top
htop
dstat -ta
glances

ユーザーの追加

adduser username
usermod -aG sudo username

git

git init
git add .
git commit -m "first commit"

ファイルの転送

remote → local

# file
scp your_username@remotehost.edu:foobar.txt /some/local/directory
# directory
scp -r your_username@remotehost.edu:/some/remote/directory/foo bar

local → remote

# file
scp foobar.txt your_username@remotehost.edu:/some/remote/directory
# directory
scp -r foo your_username@remotehost.edu:/some/remote/directory

ディレクトリのときは

local$ tar -czvf local.tar directory/
local$ scp local.tar user@remote:/directory
ssh user@remote
remote$ tar -xzvf local.tar

がよい。

言語の切り替え

systemdに登録されたサービスは /etc/systemd/system/ 以下。

sudo -s
systemctl stop XXX
systemctl start YYY
journalctl -f

netdata の設定

bash <(curl -Ss https://my-netdata.io/kickstart-static64.sh)

して <host>:19999 にアクセス。

alp の設定

1.Releases · tkuchiki/alp · GitHub で最新版を確認する

2.インストール

wget https://github.com/tkuchiki/alp/releases/download/v0.3.1/alp_linux_amd64.zip
unzip alp_linux_amd64.zip
sudo install ./alp /usr/local/bin

3.access.log の設定 /etc/nginx/nginx.conf を以下のように修正

...

http {
    log_format ltsv "time:$time_local"
                "\thost:$remote_addr"
                "\tforwardedfor:$http_x_forwarded_for"
                "\treq:$request"
                "\tstatus:$status"
                "\tmethod:$request_method"
                "\turi:$request_uri"
                "\tsize:$body_bytes_sent"
                "\treferer:$http_referer"
                "\tua:$http_user_agent"
                "\treqtime:$request_time"
                "\tcache:$upstream_http_x_cache"
                "\truntime:$upstream_http_x_runtime"
                "\tapptime:$upstream_response_time"
                "\tvhost:$host";
    access_log /var/log/nginx/access.log ltsv;
}

...

4.再起動

rm /var/log/nginx/access.log && systemctl reload nginx

5.実行

alp --sum -r -f /var/log/nginx/access.log --aggregates='/keyword/.*'

slow query

1.my.conf の設定

以下を追記

[mysqld]
...
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 0

2.mysql、アプリの再起動

systemctl restart mysql
systemctl restart XXX

3.percona-toolkit のインストール (Debian8: jessie)

wget https://www.percona.com/downloads/percona-toolkit/3.0.3/binary/debian/jessie/x86_64/percona-toolkit_3.0.3-1.jessie_amd64.deb
apt install libdbd-mysql-perl libdbi-perl libio-socket-ssl-perl libnet-ssleay-perl libterm-readkey-perl
dpkg -i percona-toolkit_3.0.3-1.jessie_amd64.deb

4.解析

pt-query-digest --limit 10 /var/log/mysql/slow.log

再起動用のスクリプト

#!/bin/sh
set -e

now=`date +%Y%m%d-%H%M%S`
mv /var/log/nginx/access.log /var/log/nginx/access.log.$now
systemctl reload nginx

mv /var/log/mysql/slow.log /var/log/mysql/slow.log.$now
mysqladmin -uisucon -pisucon flush-logs

systemctl restart XXX

journalctl -f

静的データのnginxでの配信

location / {} の前に、

location ^/(img|css|js|favicon.ico) {
    root /path/to/static/files;
}

my.cnf

innodb_buffer_pool_size = 1G
innodb_flush_log_at_trx_commit = 0
innodb_flush_method=O_DIRECT

nginx.conf

とりあえず、

# Worker connections
events {
    worker_connections XXX;
    use epoll;
    multi_accept on;
}

http {
    ...

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    etag off;

    ...
}

worker_connections = ulimit -n

あとは How to Configure nginx for Optimized Performance

autostart (with sysetmd)

check

systemctl is-enabled <SERVICE_NAME>

enable

systemctl enable <SERVICE_NAME>

参照


雑記

  • systemctl restartsystemctl reload の違い

reload: Unitに対して設定ファイルの再読み込みを促す。(対象のUnitがreload動作に対応している必要がある)

restart: 起動中のUnitを停止後、起動(stop -> start)する。対象のUnitが停止中である場合、起動操作のみ実施する。

「Systemd」を理解する ーシステム管理編ー | ギークを目指して