kerasを使ったreuter記事分類のexampleをなぞる
今回はその中から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_matrix
のmode
に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"
$ 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™.
- オープンソースの全文検索エンジン
- 検索プログラムのLuceneがベース
- Standaloneモードとクラウド運用に特化したSolrCloudモードがある
- データセットはcore(standalone)またはcollection(SolrCloud)と呼ばれる
- データをインポートする際スキーマは指定しなくても自動で解析してくれる
- 転置インデックスという方式でインデックスを作成する
- トークナイズの方法には形態素解析やN-gramがある
- SQLのGROUP BYのような検索をする、ファセット機能をもつ
- 地理空間検索ができる
こちらの記事が要点を抑えていてわかりやすいです。
文章を単語ごとに分解する方法。
例:「東京特許許可局」 →「東京」「特許」「許可局」
意味のわからないトークンが作られない一方、例えば「京特」で調べた時に引っかからないという事が起こる。
文章を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
あっと言う間すらないほど早い一年でした。2017年版です。
- チームが機能するとはどういうことか
- Webを支える技術
- 大規模開発サービス技術入門
- Webサービスの作り方
- まつもとゆきひろ 言語の仕組み
- ゼロから作るDeep Learning
- Server-Side Swift
- きつねさんでもわかるLLVM
- 開眼!JavaScript
- JavaScript本格入門
- Reactビギナーズガイド
- ふつうのLINUX
- 新しいLinuxの教科書
- プロになるためのWeb技術入門
- できるPRO Apache Webサーバー
- Real world http
- 計算理論の基礎
- オペレーションシステムの仕組み
- JavaScriptデザインパターン
- 最短経路の本
- アルゴリズムサイエンス 出口からの超入門
- たった2日でわかるLinux
- PHPはどのように動くのか
- プログラミングコンテストチャレンジブック
- アルゴリズムクイックリファレンス
- 達人に学ぶSQL徹底指南書
- 達人に学ぶDB設計徹底指南書
- The Go Programming Language
- SQLパズル
- プログラマのためのSQL
- SQLアンチパターン
計31冊(減ってるやん。。。)
前半は相変わらず乱読を続けていましたが、秋頃から一つのテーマを掘り下げるスタイルにしました。 こちらの方がいろんな知識を線で捉えられるため体系的に学びやすいと感じているのでとりあえず続けてみようと思います。
春から夏にかけてJavaScriptに興味が湧いたため手をつけていたのですが、仕事で使わないだけでなくプライベートで何かを作る、ということもしなかったため中途半端な知識になってしまったのが残念です。反省。
今は「トレンドを追うよりもコンピュータサイエンスに近いところを学んで足腰を鍛えることを優先した方が後々効率が良いのではないか」という気がしているので、以前よりもだいぶ慎重に読む本を選んでいます。
昨年「2017年学びたいこと」として
をあげていました。達成度は30%以下です。反省。
来年は
- デザインパターン
- OS、言語処理系
- デザイン
あたりを中心に読みたいなと思っています。
また、最近こちらのエントリを読んで感心しました。
「より効率的な読書法」も同時に求めていきたいと思います。
Varnishについて
今週FastlyのStockholm(BMA)で障害が発生1し、私たちのサイトも影響を受けました。その際出てきた「varnish」というミドルウェアについて調べたときのメモです。
Varnishとは
Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy.
- 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(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 }
レスポンス
成功時:
失敗時:
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つ
- jsonrpc - The Go Programming Language
- json2 - Gorilla, the golang web toolkit
- GitHub - osamingo/jsonrpc: The jsonrpc package helps implement of JSON-RPC 2.0
【思ったより手間取っているので追記予定】
疑問
- JSON-RPCを使う理由
- 一般的なidの割り振り方
参考
ISUCONカンニングペーパー
【10/23追記】
残念ながら本選に出場することはできませんでした。
インデックス貼ったり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>
参照
- ISUCON 夏期講習 2017 を開催しました(当日の資料あり) : ISUCON公式Blog
- ISUCON4 予選でアプリケーションを変更せずに予選通過ラインを突破するの術 - Hateburo: kazeburo hatenablog
- How to Configure nginx for Optimized Performance
- How To Create a Sudo User on Ubuntu [Quickstart] | DigitalOcean
雑記
systemctl restart
とsystemctl reload
の違い
reload: Unitに対して設定ファイルの再読み込みを促す。(対象のUnitがreload動作に対応している必要がある)
restart: 起動中のUnitを停止後、起動(stop -> start)する。対象のUnitが停止中である場合、起動操作のみ実施する。