Ashigaruコンピューター道

ソフトウェアの話とか、キャリアの話とか

Blockchain Applicationの課題について

目的

Ethereumを使ってtotoのようにJリーグに賭けられるアプリケーションを作成している。 まだ設計の段階だがそこで出てきた課題について残しておく

アプリケーションについて

Design Dodcument

docs.google.com

Mock

EthereumToTo - Google ドライブ

出てくる課題

ethereum blockchainを使った場合、以下が課題として出てきた。

  • transatctionの処理に数分かかるときがある(遅い)
  • ユーザがtransfer時にgasを支払う必要がある。
  • どのデータ、処理をblockchainに置いて、どれをserverに置くか適切に設計する必要がある。
    • blockchain上のcontructに置いたデータは単なる変数なので、複雑な検索などはできない。
    • データの使いやすさを求め contruct data -> server dbにコピーする場合、タイミング、方法などの検討が必要 

設計について

まだ実装はしていないが、数分のデータ表示の遅延が許容されるならば以下のような設計はどうだろうか

f:id:jjjjpppp25:20181014120630p:plain

ポイントは

  • 価値の交換に関する重要なアクション、データのみコントラクトに置く。
  • contractに置いたデータは検索性が低いので、RDBに同期する。
  • ユーザへのデータ表示はRDB経由のみとする。(アドミン機能は直接contractをみる場合あり)

blockchain上でRDBのように複雑なデータ構造を表現できるようにならないと、全てblockchain上で完結するのは難しそう。

AWS Batchでの起動時間について

概要

新規プロダクト作成において、非同期にデータ収集、加工処理をする必要がありAWS Batchの検討をしたが、起動時間 が問題で使用できなかったのでその情報を残しておく。

AWS Batchとは

フルマネージドのバッチ処理基盤

aws.amazon.com

キューとセットになっているので、SQSを組み合わせたりする必要がなく、ここにjobを放り込むとキュー管理、実行までやってくれる。 裏側としてはEC2が使われているらしく、

想定使用用途

Webサービスからの依頼で、データの収集、加工、送付をしてくれる非同期処理を作成したい。 作成されるデータ量的には5MB - 10MBぐらいでまあ長くても数分で終わる処理想定。

問題点

ダミーのコンテナを作って、動かして見たが処理開始までの起動時間がネックになってきた。 大体はキューを積んでから数十秒以内にrunnable -> starting -> runningとステータスが遷移して、処理されるのだが たまに、runnableのまま数分放置される場合がある。runningしている処理がないにも関わらずだ。

runningなにもない状態 f:id:jjjjpppp25:20181006102529p:plain

startingも何もない状態 f:id:jjjjpppp25:20181006102602p:plain

しかしrunnbableにはたくさんキューがある f:id:jjjjpppp25:20181006102645p:plain

数分待つと。。。

処理が開始される。キューが積まれてから処理開始まで8分かかっている。 f:id:jjjjpppp25:20181006102751p:plain

代案

数分で終わる処理において、数分待たされるのはシステム要件的に許されなそうなので以下構成の方が良さそう。 この構成にすると SQSにキューを積むとほぼ同時にlambda functionが起動するので、あとはコンテナを立ち上げる時間だけ。 なんどやっても1分ほどで起動開始されていた。

f:id:jjjjpppp25:20181006104807p:plain

Blogger -> Hatena Blogへの移行のしかた

Bloggerでブログを書いていたが、コードの貼り付けなどをサポートしていない、マークダウンで書けないなどの理由で Hatena Blogに移行したので、その移行の仕方をのちの人のために残しておく。

直接移行することはできないのだが、一度WordPressを経由することにより export -> input を実現できる。

事前準備

wordperssの環境を用意するためにAWSを利用した。そのためAWSのアカウントが必要になる。

STEP 1 bloggerのデータをwordpress方式でexport

setting -> other から"Back up content"をクリック f:id:jjjjpppp25:20180922114530p:plain

xmlファイルを保存するか聞かれるので適当なフォルダに保存 f:id:jjjjpppp25:20180922114541p:plain

STEP 2 Wordpressの環境を用意して、ログイン

AWSAmazon Lightsailを使用してWordpressを用意する。

Lightsailの画面までいき"Create instance"をクリック f:id:jjjjpppp25:20180922114852p:plain

Linux / Wordpressを選ぶ f:id:jjjjpppp25:20180922114939p:plain

instanseのサイズは一番小さいものでOK f:id:jjjjpppp25:20180922115019p:plain

Pending -> RunningになればOK f:id:jjjjpppp25:20180922115203p:plain

f:id:jjjjpppp25:20180922115217p:plain

passwordを調べるためにconnectから接続 f:id:jjjjpppp25:20180922115602p:plain

以下のコマンドでパスワードを調べる f:id:jjjjpppp25:20180922120142p:plain

たてたインスタンスwordpressにアクセス

ユーザ : user

パスパード : 先ほどログインしたもの

f:id:jjjjpppp25:20180922120306p:plain

ログインできると以下のような画面になる f:id:jjjjpppp25:20180922120348p:plain

STEP 3 ファイルをコンバート

tool -> bloger を選択 f:id:jjjjpppp25:20180922120641p:plain

blogerからダウンロードしたファイルを選択してインポート f:id:jjjjpppp25:20180922120756p:plain

インポート後All Postsを見てみて、今までの投稿があれば成功している。 f:id:jjjjpppp25:20180922120849p:plain

Tool -> Export で "All Content"を選びExport、適当なフォルダに保存する。 f:id:jjjjpppp25:20180922121010p:plain

STEP 4 htenablogでファイルをインポート

設定 -> インポートからwordpress 形式を選びインポート f:id:jjjjpppp25:20180922121341p:plain

STEP 5 忘れずにAWSインスタンスを削除

f:id:jjjjpppp25:20180922121440p:plain

Fine tuning (VGG16モデル) (Keras + Jupyter Notebook + tensorflow)

概要

Keras公式サイトの 
をやってみる。
少ないサンプルからディープラーニングで優位なモデルを作る。
ステップとしては、
  • クラッチで作る。
  • bottleneck featureで学習済みモデル使う
  • Fine tuningを使う

画像の用意

まず、クラス分けする画像の用意をする。今回は猫と犬のクラス分け。
kaggleからcats and dogsのデータをダウンロードする。このデータセットは25000枚あるが、今回は少ないデータセットでのモデル構築が目的なので、トレーニングデータとしてcats, dogs 1000枚ごと、テストデータとして400枚ごと取り出してフォルダに分ける。

- cats_and_dogs_1000
  - train
    - cats (1000枚)
    - dogs (1000枚)
  - validation
    - cats (400枚)
    - dogs (400枚)

切り分けたコマンド
ll dog* | head -1000 | awk '{print $9}' | xargs -i cp -p {} ../../cats_and_dogs_1000/train/dogs/
ll cat* | head -1000 | awk '{print $9}' | xargs -i cp -p {} ../../cats_and_dogs_1000/train/cats/
ll dog* | tail -400 | awk '{print $9}' | xargs -i cp -p {} ../../cats_and_dogs_1000/validation/dogs/
ll cat* | tail -400 | awk '{print $9}' | xargs -i cp -p {} ../../cats_and_dogs_1000/validation/cats/

前処理とデータ増加

画像を少し加工しながらデータを増やす。これには過学習を防いでモデルを、一般化する効果があるらしい。
Kerasでは keras.preprocessing.image.ImageDataGenerator class を使ってこれを実現できる。

必要なライブラリのインポート
<code>
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras import backend as K
<code>

画像増やす。
<code>
# this is the augmentation configuration we will use for testing:
# only rescaling
test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary')
<code>

クラッチの畳み込み演算トレーニング



<code>
model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])
<code>

正解率は以下のようになった。

Epoch 50/50
125/125 [==============================] - 13s - loss: 0.4843 - acc: 0.7900 - val_loss: 0.4923 - val_acc: 0.7950

Bottleneck feature

学習済みモデルの最終層を削除して、そのモデルを特徴抽出として使うことをbottleneck featureと呼ぶらしい
ここはよく理解できなかったのでスキップ

Fine Tuning

最後に、FineTuningで予測して見る。
ステップとして

  • VGG16モデルを読み出して、パラメータをロードする。
  • 前で作ったモデルをトップに積んで、パラメータをロードする。
  • VGG16の層をfreezeする。
  • モデルのコンパイル
  • トレーニング


必要なKerasのクラスロード
<code>
from keras import applications
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras.models import Model
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
<code>

使用する変数の定義
<code>
# path to the model weights files.
weights_path = '../keras/examples/vgg16_weights.h5'
top_model_weights_path = 'bottleneck_fc_model.h5'
# dimensions of our images.
img_width, img_height = 150, 150

train_data_dir = 'images/cats_and_dogs_1000/train'
validation_data_dir = 'images/cats_and_dogs_1000/validation'
nb_train_samples = 2000
nb_validation_samples = 800
epochs = 50
batch_size = 16
<code>

VGG16モデルをロードして、bottleneck featureで作成したモデルをトップに積む。
<code>
# build the VGG16 network
base_model = applications.VGG16(weights='imagenet', include_top= False, input_shape=(150, 150, 3))
print('Model loaded.')

# build a classifier model to put on top of the convolutional model
top_model = Sequential()
top_model.add(Flatten(input_shape=base_model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(1, activation='sigmoid'))

# note that it is necessary to start with a fully-trained
# classifier, including the top classifier,
# in order to successfully do fine-tuning
top_model.load_weights(top_model_weights_path)

# add the model on top of the convolutional base
# model.add(top_model)
model = Model(input= base_model.input, output= top_model(base_model.output))
<code>

最初の25レイヤーはパラメータをアップデートしないようにする。
<code>
# set the first 25 layers (up to the last conv block)
# to non-trainable (weights will not be updated)
for layer in model.layers[:25]:
    layer.trainable = False
<code>

モデルのコンパイル
<code>
# compile the model with a SGD/momentum optimizer
# and a very slow learning rate.
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
              metrics=['accuracy'])
<code>

画像データの用意
<code>
# prepare data augmentation configuration
train_datagen = ImageDataGenerator(
    rescale=1. / 255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_height, img_width),
    batch_size=batch_size,
    class_mode='binary')

validation_generator = test_datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_height, img_width),
    batch_size=batch_size,
    class_mode='binary')
<code>

モデルの学習
<code>
# fine-tune the model
model.fit_generator(
    train_generator,
    samples_per_epoch=nb_train_samples,
    epochs=epochs,
    validation_data=validation_generator,
    nb_val_samples=nb_validation_samples)
<code>

 結果は以下のようになり、精度が上がったことが確認できた。

Epoch 50/50
125/125 [==============================] - 151s - loss: 0.5791 - acc: 0.9250 - val_loss: 1.1987 - val_acc: 0.8813

参考

Android Studio でプロジェクト新規作成、読み込みできない時

Macを新しいものに変えたごAndroid Studioでプロジェクトが新規作成、読み込みできなくなって調べたのでメモ


なかなかググっても解決できなかったが、Stuck over flowのこのページに答えがズバリ書いてあった。

裏でgradleのダウンロードが走っていてそれがstuckしてるように見えるようだ。
しかし10minも反応が帰ってこなければ、普通強制終了してしまうな。
「xxxダウンロード中」と表示させるだけで解決しそうな問題だ、UX, UI大事。

Web API Mockサーバをサクッと立てたいときのjson-server

フロントの開発や、サーバサイドで他Web APIを叩くときにAPI側の開発が完了してないときや、いろいろな返却値を試したいときがある。

そのときにサクッとMockを立てられるOSSがあったので紹介する。


json-server
node.jsで書かれたWeb API MockサーバでREST APIをコーディングなしでサクッと立てられる。公式に書いてあった

Get a full fake REST API with zero coding in less than 30 seconds (seriously)
 も嘘でなかった。

環境構築

必要なのはnode.js
この辺を参考にnvmを入れて環境作っておくとよい。

その後npmでjson-serverを入れる。
グローバルに入れたくない場合、まず適当なフォルダを作ってnpm initでパッケージのリスト作る。

jun-mac:json-server jun-ishioka$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See npm help json for definitive documentation on these fields
and exactly what they do.

Use npm <span style="color: #dad085;">install</span> &lt;pkg&gt; <span style="color: #e28964;">-</span><span style="color: #e28964;">-</span>save afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (json-seerver-test) json-server-test
version: (1.0.0)
description: for test
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/jun-ishioka/temp/json-server/package.json:

{
"name": "json-server-test",
"version": "1.0.0",
"description": "for test",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "",
"license": "ISC"
}


Is this ok? (yes) yes

その後json-serverを入れる。

jun-mac:json-server jun-ishioka$ npm install --save-dev json-server

いろいろパッケージが入るのでこれで完了


使用方法

単純なREST APIを構築したいときは以下のようなJsonを用意する。

{
"users": [
{ "id": 1, "name": "kageki", "genger": "man" },
{ "id": 2, "name": "kageki_2", "genger": "man" }
]
}

以下コマンドで立ち上げ

jun-mac:json-server jun-ishioka$ node node_modules/json-server/bin/index.js -w db.json

{^_^}/ hi!

Loading db.json
Done

Resources
http://localhost:3000/users

Home
http://localhost:3000

Type s + enter at any time to create a snapshot of the database
Watching...

リクエストを投げてみるとjsonが返却される。

jun-mac:~ jun-ishioka$ curl -O GET http://localhost:3000/users
curl: Remote file name has no length!
curl: try 'curl --help' or 'curl --manual' for more information
[
{
"id": 1,
"name": "kageki",
"genger": "man"
},
{
"id": 2,
"name": "kageki_2",
"genger": "man"
}
]


応用的な使い方

今回自分がそうだったが、たまにPOSTリクエストだがデータ取得に使われるAPIなどがある。json-serverはRESTの原則に従っているので、POSTが投げられるとこのjson.dbにデータが追加されてしまう。

それを避けるためにPOSTをGETに曲げる必要があるが、それは独自ファイルを作り起動するとできる。

このページに詳しく書いてある。







1つのサーバに2つのrailsアプリを共存させる方法

個人で作成しているKindleセール本まとめサイトで、railsアプリを1サーバに共存させる必要が出てきたのでその方法。

概要

http://kinsume.infoにアクセスした時はrails5.0.1のアプリに飛ばして、http://kinsume.info/kinsume_blogにアクセスされた時は、rails4.2.7のアプリに飛ばしたい。
kinsume_blogで動くアプリはオープンソースCMS refinery-cmsを使用したかったのだが、rails4.x系にしか対応していなかったので、苦肉の策ではある。

環境はUbuntu, Nginx, Unicorn

手順

まず、すでに動いているrails5.x系, ruby 2.3.xの環境には影響を与えたくなかったので、ruby 2.2.7をインストールして、ruby周りの環境を整える。


cd ~/repos/kinsume_blog
rbenv local install 2.2.7
gem install bundler
bundle install

Unicornの設定ファイルを作成

新しく設定したいアプリの下で、vim config/unicorn.rbでファイルを新規作成。


app_path = File.expand_path(File.dirname(FILE) + '/..')$
$
# workerをいくつ立ち上げるか。ここではCMSであまりアクセスないことを$
# 想定していて、メモリの空きもないので1にしている。$
worker_processes 1$
$
# どのソケットで連携するかNginxの設定ファイルにも書くので覚えておく$
listen app_path + '/tmp/kinsume_blog.sock', backlog: 64$
timeout 300$
working_directory app_path$
$
# この辺もすでに動いているアプリと被らないようにする$
pid app_path + '/tmp/kinsume_blog.pid'$
stderr_path app_path + '/log/kinsume_blog.log'$
stdout_path app_path + '/log/kinsume_blog.log'$
$
preload_app true$
$
GC.respond_to?(:copy_on_write_friendly=) &&$
GC.copy_on_write_friendly = true$
$
before_fork do |server, worker|$
defined?(ActiveRecord::Base) &&$
ActiveRecord::Base.connection.disconnect!$
end$
$
after_fork do |server, worker|$
defined?(ActiveRecord::Base) &&$
ActiveRecord::Base.establish_connection$
end$

railsアプリのルートパスを変える。refinery-cmsのパスを変えるには下記ファイルを変更。

vim config/initializers/refinery/core.rb


  # Specify a different Refinery::Core::Engine mount path than the default of "/".$
# Make sure you clear the tmp/cache directory after changing this setting.$
config.mounted_path = "/kinsume_blog"$


わかりやすくなるように、静的ファイルのパスを元のアプリと変える。

vim config/environments/production.rb


  config.assets.prefix = '/static'$

Nginxの設定ファイルは以下


upstream tagosaku{$
server unix:/home/ishioka/repos/tagosaku/tmp/tagosaku.sock fail_timeout=0;$
}$

# 先ほど作成したアプリのソケットファイルをここで指定
upstream kinsume_blog{$
server unix:/home/ishioka/repos/kinsume_blog/tmp/kinsume_blog.sock fail_timeout=0;$
}$
$
server {$
error_log /var/log/nginx/error.log debug;$
listen 80;$
$
root /home/ishioka/repos/tagosaku;$
index index.html index.htm;$
$
keepalive_timeout 300;$
client_max_body_size 4G;$

 # kinsume_blogないで使っているgemから走るアクセスパスをどうしても変えられなかったので悲しみのrewriteで対応
rewrite ^/wymiframe$ /kinsume_blog/wymiframe last;$
$
# ここでもkinsume_blogないのgemから走るアクセスを変えられなかったので、いったんassetsを見てふぁいるがなければ/static/を見に行くように変更
location ~ ^/assets/(.*) {$
root /home/ishioka/repos/tagosaku/public/;$
try_files $uri /static/$1 =404;$
}$
$
 # staticへのアクセスはkinsume_blogの静的ファイルへのアクセスなのでロケーションを変更
location /static/ {$
root /home/ishioka/repos/kinsume_blog/public/;$
}$

location / {$
# First attempt to serve request as file, then$
# as directory, then fall back to displaying a 404.$
#try_files $uri $uri/ =404;$
$
# Uncomment to enable naxsi on this location$
# include /etc/nginx/naxsi.rules$
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;$
proxy_set_header Host $http_host;$
proxy_set_header X-Forwarded_Proto $scheme;$
proxy_redirect off;$
$
# This passes requests to unicorn, as defined in /etc/nginx/nginx.conf$
proxy_set_header Host $http_host;$
proxy_pass http://tagosaku;$
proxy_read_timeout 300s;$
proxy_send_timeout 300s;$
}$
$
location /kinsume_blog {$
# First attempt to serve request as file, then$
# as directory, then fall back to displaying a 404.$
#try_files $uri $uri/ =404;$
$
# Uncomment to enable naxsi on this location$
# include /etc/nginx/naxsi.rules$
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;$
proxy_set_header Host $http_host;$
proxy_set_header X-Forwarded_Proto $scheme;$
proxy_redirect off;$
$
# This passes requests to unicorn, as defined in /etc/nginx/nginx.conf$
proxy_set_header Host $http_host;$
proxy_pass http://kinsume_blog;$
proxy_read_timeout 300s;$
proxy_send_timeout 300s;$
}$
$
error_page 500 502 503 504 /500.html;$
$
location = /500.html {$
root /home/ishioka/repos/tagosaku/public;$
}$
}


refinery-cmsのpluginからリクエストされる静的ファイルのパスを/から/kinsume_blogへ変更することができなかったので、nginxのrewriteを駆使して対応。 

あまりアクセスパスを変更されることを想定されてない作りっぽいので、PR出していきたい。