yag's blog

Twitter以上Zenn以下なことを書く場所

Recognizing Textual EntailmentをLSTMで解く

NLPディープラーニング周りの可視化手法が知りたくて色々読んでいる中でReasoning about Entailment with Neural AttentionでRecognizing Textual Entailment(以下RTE)という問題を解いていたので、データセットも公開されていることもあり試しにチャレンジしてみることにしました。

タスク

RTEはテキスト間の含意を推論するタスクです。与えられた2つの文章間の関係性を、幾つかの含意を表すラベルで表現します。データセットによってラベルの表現方法は異なりますが、今回はThe Stanford NLP Groupが公開しているSNLIのデータセットを考えます。

The Stanford Natural Language Inference (SNLI) Corpus

SNLIでは、2つのテキストのペアに対してentailment, contradiction, neutralという3つのラベルが付けられています。

例えば、

Text: A soccer game with multiple males playing.

Hpyothesis: Some men are playing a sport.

という2つの文章があった場合に、どちらも複数の男性がサッカーをしているという意味なので、これはentailmentです。

一方で、

Text: A black race car starts up in front of a crowd of people.

Hypothesis: A man is driving down a lonely road.

の場合、車を運転している状況は同じかもしれませんがcrowdとlonelyが矛盾しているのでcontradictionです。

他にどちらとも言えないというneutralというラベルの合計3つのラベルがあります。

Text:A smiling costumed woman is holding an umbrella.

Hypothesis:A happy woman in a fairy costume holds an umbrella.

いわばKaggleで開催されたQuoraコンペティションの多値バージョンという感じですね。Quoraでは質問文が意味的に同じかどうかを二値分類するタスクでしたが、RTEはその意味的な種類を判定する少し難しい問題になっています。

ただし、RTEも場合によってはcontradictionとneutralをまとめてしまい、entrailemnt/Nonentrailmentの二値分類として解く場合もあるようです。また、京大黒橋研が公開しているTextual Entailment 評価データでは◎、〇、△、×の4クラスに分類しており、こちらも二値分類に変換したデータセットが同様に公開されています。

データセット

データセットはSNLIのページからダウンロードすることができます。

The Stanford Natural Language Processing Group

LSTM構築

今回はQuoraコンペティションabhishekが公開しているDNNの構造を参考にします。下記のコードのように、それぞれの文章の入力をEmbedding layerとLSTMに入れて、それを結合したのちに幾つか層を重ねる形式です。

model1 = Sequential()
model1.add(Embedding(num_words + 1,
                     embedding_dim,
                     weights=[embedding_matrix],
                     trainable=False))
model1.add(LSTM(embedding_dim, recurrent_dropout=0.5, dropout=0.5))

model2 = Sequential()
model2.add(Embedding(num_words + 1,
                     embedding_dim,
                     weights=[embedding_matrix],
                     trainable=False))
model2.add(LSTM(embedding_dim, recurrent_dropout=0.5, dropout=0.5))

model = Sequential()
model.add(Merge([model1, model2], mode="concat"))
model.add(BatchNormalization())

model.add(Dense(300))
model.add(PReLU())
model.add(Dropout(0.5))
model.add(BatchNormalization())

model.add(Dense(300))
model.add(PReLU())
model.add(Dropout(0.5))
model.add(BatchNormalization())

model.add(Dense(300))
model.add(PReLU())
model.add(Dropout(0.5))
model.add(BatchNormalization())

model.add(Dense(3, activation="sigmoid"))
model.compile(loss="categorical_crossentropy",
              optimizer="adam",
              metrics=["accuracy"]
              )

今回使用したソースコード全体はGithubにて公開しています。

https://github.com/yagays/rte_snli

結果

今回train約50万文を学習に使用し、test約1万文に対して評価した結果、Test accuracyが80.5%となりました。また、混合行列は下記のようになりました。

f:id:yag_ays:20170812232350p:plain

Classification Reportはこんな感じ。特段苦手なラベルがあるわけではないものの、neutralはちょっと低いですね。contradictionやentailmentと比較して、neutralはラベル付けの基準が人によって異なっていたり、判断が難しいことが影響しているのかもしれません。

# {"contradiction":0, "entailment":1, "neutral":2} 
              precision    recall  f1-score   support

          0       0.86      0.78      0.82      3237
          1       0.78      0.86      0.82      3368
          2       0.77      0.77      0.77      3219

avg / total       0.80      0.80      0.80      9824

現在SNLIプロジェクトページにて公開されているSOTAが88.8%なので、まだまだですね。まあSNLI論文のベンチマークの77.6%よりは上回ったので、今回は良しとしましょう。

参考

コマンドラインからEC2のインスタンスの状態を確認できるec2instを作りました

概要

最近は機械学習の計算やJupyter Notebookなどを、ローカルやVPSではなくAWSのEC2で動かしたりしているのですが、そうなると気になるのがインスタンスの状態だったりパブリックIPアドレスだったりするわけです。普通ならブラウザからAWSのコンソールを開けば万事解決なのですが、自分の場合はブラウザのタブを開きすぎててどこに行ったか分からなかったり、あとはAWSコンソールのトップページからEC2のインスタンス一覧を見るには2回ほど画面遷移が必要だったりと、何かと不便に感じることが多くなってきました。

そこで、ターミナルのコマンドラインからEC2のインスタンス一覧を取得して、いい感じにリストアップしてくれるスクリプトを作りました。といっても、そもそもAWSaws-cliというコマンドラインインターフェイスを作成していたり、boto3というPythonSDKを作っていたりするので、今回作成したのはいわばその機能の一部を使って結果を見やすくするという、ラッパー程度のものです。n番煎じ、もしくは車輪の再発明になってしまったとは思うのですが、まあ勉強になったので良いでしょう……。

インストール

PyPIから取得します。

$ pip install ec2inst

基本的に上記の方法でインストールして問題ありませんが、以下のようにgithubから直接pipでインストールすることもできます。

$ pip install git+https://github.com/yagays/ec2inst.git  

準備

ec2instはaws-cliもしくはboto3の設定ファイルを読み込んで、その中に登録されているaws_access_key_idaws_secret_access_keyを利用します。そのため、まず始めにAWSのウェブコンソールからアクセスキーIDと秘密アクセスキーを取得し、aws-cliで登録しておきましょう。ここでの設定は~/.aws/configおよび~/.aws/credentialsに登録されます。なお、もし複数のプロファイルを使い分ける場合にはaws configure --profile yag_aysのようにプロファイルに名前を付けておくと後々切り分けが楽になります。

$ aws configure
AWS Access Key ID [None]: XXX...
AWS Secret Access Key [None]: YYY...
Default region name [None]: ap-northeast-1
Default output format [None]: json

使い方

ec2instコマンドで以下のような結果が返ってきます。

$ ec2inst
instance_id  instance_type  image_id      instance_name    public_ip_address  private_ip_address  instance_state  
-----------  -------------  ------------  ---------------  -----------------  ------------------  --------------  
i-00000000   t2.medium      ami-00000000  instance_name_1  xxx.xxx.xxx.xxx    172.20.xxx.xxx      running         
i-11111111   g2.2xlarge     ami-11111111  gpu_instance     -                  172.20.yyy.yyy      stopped         
i-22222222   c3.xlarge      ami-22222222  -                zzz.zzz.zzz.zzz    172.20.zzz.zzz      running         

ここでは色が付いていないですが、実際にはインスタンスの状態に色が付きます。

f:id:yag_ays:20161010005309p:plain

あとは-p/--profileコマンドでaws-cli/boto3のプロファイルを指定したり、-c/--columnsで表示させるカラムを設定することができます。詳しくはec2inst -hを参考下さい。

$ ec2inst -p yag_ays -c instance_type,instance_name,instance_state 
instance_type  instance_name    instance_state  
-------------  ---------------  --------------  
t2.medium      instance_name_1  running         
g2.2xlarge     gpu_instance     stopped         
c3.xlarge      -                running         

まとめ

EC2の利用は計画的に😇

参考

bitFlyerのticker情報をInfluxDBに入れてGrafanaで可視化する

概要

前回ビットコインのデータをバルクで配布しているサイトから直接ダウンロードして可視化しましたが、今回は自分でデータを取ってきて蓄積し、可視化までの流れをやってみたいと思います。

色々と方法はあると思いますが、せっかくなので自身の勉強のためにも、今回は時系列DBとして有名なInfluxDBをデータベースとして、可視化には相性の良いGrafanaを選択しました。取得するデータはbitFlyerのtickerの情報です。API経由でask/bidの価格や量などの情報を取得することができます(link)。

方法

環境

  • macOS El capitan 10.11.6
  • InfluxDB v1.0.0
  • Grafana Version 3.1.1

InfluxDBを導入する

ここではインストールの具体的な手順は詳細は割愛します。MacならHomeBrewで入りますし、Linuxも公式ウェブサイトからパッケージをダウンロードして展開するだけでインストール自体は完了します。

さて、InfluxDBが使えるようになったところで、データを投入する前に一通り設定をしておきましょう。ウェブのQuery実行画面(http://localhost:8083/)で以下のSQLを実行し、root/rootのユーザと、bitcoinという名前のデータベースを作成しておきます。CLIから実行しても問題ありません。

CREATE USER "root" WITH PASSWORD 'root'
CREATE DATABASE "bitcoin"

pythonからInfluxDBへデータを投入する

さて、データベースの準備が一通り整ったので、InfluxDBへデータを投入していきます。InfluxDBにはPythonのクライアントがあるので、今回はそれを利用します。

また、bitFlyerからのデータ取得は、拙作のpybitflyerを利用しています。本来ならばInfluxdbはRESTful APIによるデータ投入ができるので、bitFlyerのWebAPIから直接流すという選択肢もあるのですが、データの加工やカラム追加が容易という理由で、一度Pythonで処理するという形を取っています。

from influxdb import InfluxDBClient
import pybitflyer

api = pybitflyer.API(api_key="XXX...",
                     api_secret="YYY...")
ticker = api.ticker(product_code="BTC_JPY")

user = 'root'
password = 'root'
dbname = 'bitcoin'
json_body = [
    {
        "measurement": "bf_ticker",
        "fields":ticker
    }
]

client = InfluxDBClient("localhost", "8086", user, password, dbname)
client.write_points(json_body)

bitFlyer Lightning APIから取得したデータをticker変数に入れています。それをデータ投入用のjson_bodyに含め、client.write_points()にてデータベースへ書き込んでいます。measurementMySQLなどで言うところのテーブルに相当するもので、bitcoinというデータベース内に作成されます。他にも書き込む内容としてtagsやtimeなどの情報を加えることができますが、ここでは指定していません。

きちんとデータが格納されているか確認してみましょう。InfluxDBのウェブページでデータベースをbitcoinに設定したうえでselect * from bf_tickerで問い合わせて、何かしらデータが表示されていれば問題ありません。レスポンスが{"results":[{}]}でなかったらとりあえずは大丈夫でしょう。なお、この確認作業は以下のようにHTTP APIからでも実行可能です。

$ curl "http://localhost:8086/query?q=select+*+from+bf_ticker&db=bitcoin" | jq 
{
  "results": [
    {
      "series": [
        {
          "name": "bf_ticker",
          "columns": [
            "time",
            "best_ask",
            "best_ask_size",
            "best_bid",
            "best_bid_size",
            "ltp",
            "pair",
            "product_code",
            "tick_id",
            "timestamp",
            "total_ask_depth",
            "total_bid_depth",
            "volume",
            "volume_by_product"
          ],
[...]

これで上記スクリプトを実行したタイミングでのデータ投入が完了しました。あとは、このコードを定期的に動かすことで、ask/bidの価格を時系列で眺めたり、値を可視化することができます。

定期実行の方法としては、定番のcrontabや、最近出てきたジョブ管理ツールであるJenkinsLuigiAzkabanなどがあります。試しに動かすだけならwhile True:の中で上記スクリプトを動かしつつtime.sleep(30)などで定期実行するのもアリですね。

Grafanaで可視化

それでは最後に、Grafanaを使ってInfluxDBからデータを取り出し可視化します。なお、InfluxDBと同じようにインストールの方法はここでは割愛します。

まずはDataSourcesでInfluxDBの接続設定をします。画面に表示されているTypeからInfluxDBを選択して、Http SettingsのURL、InfluxDB DetailsのDatabaseとUser、Passwordをそれぞれ入力します。これで、ダッシュボードからInfluxDBの対象データベースを選択して情報を取得することができます。

あとはDashboardで新規作成し、可視化したいパネルを作成していきます。今回はbitFlyerのtickerに含まれるbest_ask、best_bid、ltpの3種類の情報をグラフで表示させます。ADD ROWののちに左端に少し出ている緑色のハンバーガーメニューからAdd panel→Graphを選択し、Metricsを以下のように設定します。

  • Metrics:SQL:SELECT best_bid,ltp,best_ask FROM bf_ticker
  • Metrics:Panel Data Source: InfluxDB(上記で接続設定をしたものです)

f:id:yag_ays:20160926151557p:plain

これでbitFlyerのask/bidなどの価格情報を可視化することができました!他にも生のデータを表示するTableを表示させて、一旦は以下のような形になりました。

f:id:yag_ays:20160926151424p:plain

まとめ

簡単にではありますが、InfluxDB/Grafanaを使ったデータ取得/蓄積/可視化を一通りやってみました。InfluxDB/GrafanaはSQLによる集計機能も充実しているので、あとは移動平均線を書いたり、取引量を一緒に可視化してみたり、まだまだ出来ることがいっぱいあります。単体の情報量であったり情報スピードなどは本家のトレード画面に劣るものの、複数のデータソースを一度に可視化できたりとカスタマイズできるのはかなり強力です。自分もこれから色々と試してみようと思います。

参考

補足ですが、InfluxDBは最近メジャーバージョンアップにより1.0.0となったことで、テーブル作成等のRESTful API部分の仕様がガラッと変わっています。qiitaや各種ブログ等の記事は0.9未満のものが多く、そのままでは動かないコード/クエリがありますので、実行する前にはバージョンの確認をしたほうが良いでしょう。

Bitcoinchartsでbitcoinの過去の取引履歴を取得し可視化する

f:id:yag_ays:20160904192600p:plain

今回はbitcoinの取引所での取引履歴のデータを取得してみます。

前回bitFlyer LightningのAPIを使えるパッケージを作った時のように、各取引所が公開しているデータや提供しているAPI等がありますが、今回はBitcoinchartsから取得してみます。Bitcoinchartsは各取引所の各通貨のデータを収集、可視化しているウェブサイトで、そこで利用しているデータも公開しています。

データ取得

WebAPIから取得する

1つ目の方法として、URLを叩いてWebAPIから取得する方法があります。データを取得するには、以下のURLにアクセスするだけです。SYMBOLの所には取得したい取引所&通貨の情報を指定します。取得対象とするデータの開始日をあわらすstart=UNIXTIMEは必須ではありません。

例えば、coinckeckのBTC/JPYの場合はcoincheckJPYなので、http://api.bitcoincharts.com/v1/trades.csv?symbol=coincheckJPY にアクセスすればいいわけです。すると、以下のように生のcsv形式でデータが表示されるので、ブラウザのダウンロード等を利用して保存することができます。

f:id:yag_ays:20160904192441p:plain

もちろん、次のようにcURLwgetのようにコマンドラインからURLを指定してダウンロードすることもできます。

$ wget "http://api.bitcoincharts.com/v1/trades.csv?symbol=coincheckJPY"

最新の情報のみを取得したい時などは、WebAPI形式での取得が簡単です。

csv形式で直にデータを取得する

2つ目の方法として、Bitcoinchartsが所有するすべてのデータを一度に取得する方法もあります。以下のURLにアクセスすると、様々な取引所と通貨の取引履歴がリストアップされており、データを選択して保存、ローカルで解凍することで簡単にデータを手に入れることができます。なかにはそもそもデータの無いものやMt.Goxのように取引所が閉鎖され更新が止まっているものなどもありますので、データの最終更新日やファイルサイズを確認した上で、データを利用したほうが良さそうです。

btc/jpyに関しては以下の5つがあります。

これを見ると、正常にデータ取得ができていて継続的に利用できるのはcoincheckのみのようですね。

データ形式

さて、実際に中身を見てみましょう。

1472948762,62163.000000000000,0.251085070000

データはcsv形式で保存されており、各カラムは以下の情報を表しています。

  • 1カラム目:時刻 (unixtime)
  • 2カラム目:価格 (price)
  • 3カラム目:取引量 (amount)

なので、この取引は2016/09/04 09:26:02に0.251085070000 btcを62163 btc/jpyで取引されたということを表しています。

取引値を可視化

さて、最後に取得したデータを可視化してみましょう。今回は、WebAPI形式で取得したデータを可視化してみます。可視化にはPythonのpandas&matplotlibを使用し、データはWebAPI経由での過去5日分のデータを使用しています。

import pandas as pd
from datetime import datetime

df = pd.read_csv("http://api.bitcoincharts.com/v1/trades.csv?symbol=coincheckJPY",
                 header=None,
                 parse_dates=True,
                 date_parser=lambda x: datetime.fromtimestamp(float(x)),
                 index_col='datetime',
                 names=['datetime', 'price', 'amount'])
df["price"].plot()

すると、以下のような取引値の変動を可視化することができます。2016/9/4午前に起きた価格高騰の様子がよく分かりますね……。

f:id:yag_ays:20160904193330p:plain

なお、上記コードをJupyter notebookで動かしたものをgistでも公開していますので、参考下さい。

参考

bitFlyer LightningのAPIをPythonから使えるパッケージ「pybitflyer」を作りました

最近ようやく重い腰をあげてBitcoinに入門しました。最初はBlockchainの仕組みなどを"Mastering Bitcoin"で学んでいたのですが、実際に取引してみないとピンと来ないということで、bitFlyerという取引所に口座を開設し、日本円をBitcoinに換金して遊ぶことに。

素人が売り買いをして価値がだんだん下がっていく数字を見ながら悲しんでいたのですが、そうなるとエンジニアとしてはシステムトレードがしたくなるのが世の常です(?)。幸いにもbitFlyerには、HTTP APIというURLを叩くことで情報を得たり取引をしたりできるシステムがあるではありませんか。しかしながらその仕組みは複雑で、セキュリティのための暗号化が必要だったり、HTTP APIで返ってくるものを処理したりと、そのまま扱うには何かと大変です。

そこで、bitFlyerREST APIPythonから気軽に利用することができるラッパーを作成しました。

まだまだ作りかけでテストすらきちんとできていない状態ですが、一通り動くことが確認できたので公開してみました。

インストール

パッケージはPyPIに登録しているので、pipでインストールすることができます。現在のバージョンは0.1.0です。

$ pip install pybitflyer

使い方

使いはじめる前に

まずま何よりbitFlyerのアカウントが必要ですので、作成します。

bitFlyer ビットコインを始めるなら安心・安全な取引所で

PythonのようなプログラムからbitFlyer Lightningを操作する際には、ログイン時に使用するようなメールアドレスとパスワードでははく、専用のAPI KeyとAPI Secretという2つの情報が必要です。これらは、bitFlyer Lightningの設定ページから作成することができるので、利用する前に作っておきましょう。

詳しくはAPI Documentationを参照ください。

f:id:yag_ays:20160828215226p:plain

f:id:yag_ays:20160828214806p:plain

これで準備は万全です!

基本的な使い方

まずはpybitflyerをインポートして、HTTP APIインスタンスを作成します。この際に上で取得したAPI KyeとAPI Secretを指定します(以下のコード例では...で埋めているので適宜置き換えて下さい)。

import pybitflyer

api = pybitflyer.API(api_key="...", api_secret="...")


なお、板情報や取引所の状態などはAPI KEYを必要としないため、pybitflyer.API()だけで取得することができます。

板情報を取得する

まずは下記のように、BTC_JPYを指定して情報を取得します。

board = api.board(product_code="BTC_JPY")

すると、以下のように辞書形式でasks(買値), bids(売値), mid_price(中間価格)という3つの情報を取得することができます(asksとbidsの途中を省略しています)。こういった取得できる情報はすべてAPI Documentationの「レスポンス」という箇所に記載されているので、利用する際はそちらと照らしあわせてみてください。

{'asks': [{'price': 58372.0, 'size': 1.0},
  {'price': 58373.0, 'size': 0.5},
  {'price': 58382.0, 'size': 2.0},
...
  {'price': 717600.0, 'size': 0.001}],
 'bids': [{'price': 58357.0, 'size': 6.3},
  {'price': 58356.0, 'size': 6.475},
...  
   {'price': 5754.0, 'size': 0.001}],
 'mid_price': 58364.0}

試しに少しデータをいじってみましょう。例えば買値の最小値と最大値を出したいときには、以下のようにasksの中のすべてのpriceを取り出してminとmaxで値を取り出せばよいというわけですね。

In [13]: min([p["price"] for p in board["asks"]])
Out[13]: 58372.0

In [14]: max([p["price"] for p in board["asks"]])
Out[14]: 717600.0

他にも幾つか機能を見てみましょう。

Tickerを取得する

次に、Tickerの情報も取得してみましょう。先ほどの板情報と同じように、今度は.ticker()というメソッドを使用します。

ticker = api.ticker(product_code="BTC_JPY")

すると、以下のようなデータがレスポンスとして返ってきます。

{'best_ask': 58372.0,
 'best_ask_size': 1.0,
 'best_bid': 58357.0,
 'best_bid_size': 5.0,
 'ltp': 58357.0,
 'product_code': 'BTC_JPY',
 'tick_id': 1215580,
 'timestamp': '2016-08-28T12:01:32.367',
 'total_ask_depth': 2238.88215243,
 'total_bid_depth': 2414.34489585,
 'volume': 42780.14047168,
 'volume_by_product': 17621.67437353}

新規注文を出す

それでは肝心の売買の注文を出してみましょう。なお、このHTTP APIはPrivateと呼ばれ、API KeyとAPI Secretを取得してインスタンス時に作成しておくことが必要ですのでご注意ください。

buy_btc = api.sendchildorder(product_code="BTC_JPY",
                             child_order_type="MARKET",
                             side="BUY",
                             size=0.001,
                             minute_to_expire=10000,
                             time_in_force="GTC"
                             )

product_codeは注文するプロダクトを指定します。BTC_JPYやFX_BTC_JPY、またはETH_BTCを選択します。

child_order_typeは注文のタイプで、指値注文なら"LIMIT"、成行注文なら"MARKET"を指定します。

sideには売り買いを"SELL"か"BUY"で、sizeに取引額を指定します。

なお、minute_to_expireやtime_in_forceは必須ではありません。

新規注文の確認

さて、先ほどの注文は無事に完了したでしょうか?buy_btcに以下のような情報が入って入れば取引成功です。

{'child_order_acceptance_id': 'JRF20160828-121631-076088'}

実際にbitFlyer Lightningでも以下のように確かめることができます。

f:id:yag_ays:20160828214929p:plain

他にできること

このpybitflyerパッケージはbitFlyerが提供しているHTTP APIを使いやすくしたWrapperですので、できることはすべてHTTP APIに準拠します。bitFlyerでは以下のHTTP APIの機能が提供されています。

  • 基本情報
    • 板情報
    • Ticker
    • 約定履歴
    • 取引所の状態
    • チャット
  • API
    • API キーの権限を取得
  • 資産
    • 資産残高を取得
    • 証拠金の状態を取得
  • 入出金
  • トレード
    • 新規注文を出す
    • 注文をキャンセルする
    • 新規の親注文を出す(特殊注文)
    • 親注文をキャンセルする
    • すべての注文をキャンセルする
    • 注文の一覧を取得
    • 親注文の一覧を取得
    • 親注文の詳細を取得
    • 約定の一覧を取得
    • 建玉の一覧を取得

(https://lightning.bitflyer.jp/docsより)

ウェブページからできることは大抵できるようになっているのではないでしょうか?中には「銀行口座一覧取得」なんてものもあります。このpybitflyerを作っている最中に試してみたのですが、本当に私が登録している銀行の名前や口座番号がレスポンスで返ってきて、少しびっくりしました……。

この記事でご紹介できた機能はほんの一部ですので、ぜひともbitFlyer Lightningの色々な機能を使ってみてください。

参考