風待ち

プログラミングの勉強や日々の出来事のログなど

最近では現場でタブレットを使う機会も増えてきた。タブレットにはカメラがついているから保守や修繕のときには証拠写真を撮っておきたいと思うのが人情だろう。その写真をDBに入れるためにクライアントアプリを入れるのは面倒なのでブラウザですませたい。そこでreact-webcamのサンプルをTypeScriptで書いてみる。

mozmorris/react-webcamを使ってブラウザからreactでカメラを利用する。

まずは環境を作る。

$ yarn create react-app webcam-sample --typescript
$ cd webcam-sample/
$ yarn add react-webcam @types/react-webcam

スクリーンショットを撮るサンプルがGitHubにあるので、それを参考にしてTypeScriptで書いてみる。

スクリーンショットを撮ったらとりあえず、imgタグに突っ込んでおく。

仕事で使うならこれをサーバ側に保存したり、canvasに流して加工できるようにする必要があるだろう。

App.tsxを編集する。

import React from "react";
import Webcam from "react-webcam";

interface State {
  img_src: string;
}

export default class App extends React.Component<any, State> {
  constructor(props: any) {
    super(props);

    this.state = {
      img_src: ""
    };
  }

  capture = () => {
    const imageSrc = (this.refs.webcam as Webcam).getScreenshot();
    if (imageSrc) {
      this.setState({ img_src: imageSrc });
    }
  };

  render() {
    const videoConstraints = {
      width: 1280,
      height: 720,
      facingMode: "user"
    };

    return (
      <div>
        <div>
          <Webcam
            audio={false}
            height={350}
            ref={"webcam"}
            screenshotFormat="image/jpeg"
            width={350}
            videoConstraints={videoConstraints}
            onUserMediaError={() => window.alert('cant access your camera')}
          />
        </div>
        <div>
          <button onClick={this.capture}>Capture photo</button>
        </div>
        <div>
          <img src={this.state.img_src} />
        </div>
      </div>
    );
  }
}

これで起動すれば、ブラウザが立ち上がってカメラへのアクセスを許可するか聞いてくる。

$ yarn start

WindowsでGoを使って外部コマンドをCMDで実行するときに、「cd」でカレントディレクトリを移動してから実行したい。

GoというよりはCMDの使い方になるが、コマンドとコマンドの間を「&」でつなぐことで続けて実行できる。

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("cmd", "/c", "cd", "C:\\go", "&", "dir").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s\n", out)
}

実行すると出力される結果が文字化けしているがそれは別の問題なのでここでは気にしない。

実際にこういう使い方をするとき実行したいのはbatファイルやVBScriptだろうし、出力が必要でも日本語でなければいいし。

scikit-learnのテキトーに作ったモデルを読み込んで、Flaskを使ってWebでもアクセスできるようにしてみる。

前に作ったモデルを読み込んで使う。

バッチ処理だけではなんなので、Webアプリに組み込んでみた。

import json

from flask import Flask
from flask import request
from flask import abort

import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.externals import joblib

scaler = joblib.load("foo.scaler")
model = joblib.load("foo.learn")

app = Flask(__name__)


@app.route('/foo', methods=['POST'])
def foo():
    if not request.json:
        abort(400)

    data = pd.DataFrame(request.json)
    for col in data.loc[:, data.dtypes == 'int64']:
        data[col] = data[col].astype(float)

    X = data.loc[:, data.columns].as_matrix()
    X = scaler.transform(X)

    y_predict = model.predict(X)
    return json.dumps(str(y_predict[0]))


if __name__ == "__main__":
    app.run(debug=True, port=5000)

こんなテキトーなものでも動いてるな。

$ curl http://localhost:5000/foo -s -X POST -H "Content-Type: application/json" -d '{"YYYY": ["2018"], "MM": ["08"], "DD": ["05"], "DY": ["0"]}'

試してないけど、jQueryだとこんな感じかな。

$('#btn').click(function() {
  var d = {"YYYY": ["2018"], "MM": ["08"], "DD": ["05"], "DY": ["0"]};
  $.ajax({
    url: 'http://localhost:5000/foo',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify(d),
  }).done(function (data) {
    console.log(JSON.parse(data));
  });
});

バッチ処理だけでは利用用途が限られるのと、未来の予測に必要なデータを作り込むのが大変だけど、こうやってWebで使えるようになると、入力に反応してリアルタイムに使う道が増えるから一気に活用の場所が広がるな。

あと、初めてFlaskを使ったけど、小さいものを作る分にはものすごく楽。

scikit-learnのテキトーに作ったモデルを読み込んで使ってみる。

前に作ったモデルを読み込んで使ってみる。

まずは予測したい列を除いたCSVファイル(「target.csv」って名前にする)を用意する。 前に作った時には「target」カラムがあったけど、今回はそれを予測するためのデータなのでその行はない。

YYYY,MM,DD,DY
2018,1,9,火
2018,1,10,水
2018,1,11,木
2018,1,12,金
2018,1,13,土

作ったモデルを読み込んで、予測したあと、もう一度、CSVを読んで結果を追記して「result.csv」に出力する。

import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.externals import joblib

target_csv_file = "target.csv"

data = pd.read_csv(target_csv_file, encoding="SHIFT-JIS", sep=",")

le = LabelEncoder()
for col in data.loc[:, data.dtypes == 'object']:
    data[col] = le.fit_transform(data[col])

for col in data.loc[:, data.dtypes == 'int32']:
    data[col] = data[col].astype(float)

X = data.loc[:, data.columns].as_matrix()

scaler = joblib.load("foo.scaler")
X = scaler.transform(X)

model = joblib.load("foo.learn")
y_predict = model.predict(X)

data = pd.read_csv(target_csv_file, encoding="SHIFT-JIS", sep=",")
tmp = []
for i in range(len(y_predict)):
    tmp.append(y_predict[i])

data["result"] = tmp

data.to_csv("result.csv", encoding="SHIFT-JIS", sep=",")

CSVで入出力してしまえば、どの程度のデータ量が必要かはおいといてDBから出して加工して入れるっていうこれまでよく作ってきたバッチ処理と同じだから実際のシステムに組み込む場合のイメージがつきやすいな。

テキトーなCSVファイルを用意してscikit-learnをテキトーに使ってみる

機械学習の本を読んでサンプルをいじっているだけでは、わかったつもりになるだけで実際に使えるようにはならないので、本当にテキトーなデータで機械学習してみる。

まず、テキトーなCSVデータ(「train.csv」って名前で保存)を用意する。下のは一部でこんな感じでたくさん用意する。

YYYY,MM,DD,DY,target
2018,1,20,土,2038
2018,1,21,日,2039
2018,1,22,月,2040
2018,1,23,火,2041
2018,1,24,水,2042
2018,1,25,木,2043

このCSVの「target」行を予測するモデルを作ってみる。

import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsRegressor
from sklearn.externals import joblib

train_csv_file = "train.csv"
target_column_name = "target"

data = pd.read_csv(train_csv_file, encoding="SHIFT-JIS", sep=",")

le = LabelEncoder()
for col in data.loc[:, data.dtypes == 'object']:
    data[col] = le.fit_transform(data[col])

for col in data.loc[:, data.dtypes == 'int32']:
    data[col] = data[col].astype(float)

X = data.loc[:, data.columns != target_column_name].as_matrix()
y = data[target_column_name].as_matrix()

scaler = StandardScaler()
scaler.fit(X)
X = scaler.transform(X)

X_train, X_test, y_train, y_test = train_test_split(X, y)

model = KNeighborsRegressor(n_neighbors=5)
model.fit(X_train, y_train)

print("Train score: {:.2f}".format(model.score(X_train, y_train)))
print("Test  score: {:.2f}".format(model.score(X_test, y_test)))

joblib.dump(scaler, "foo.scaler")
joblib.dump(model, "foo.learn")

こんななんの役にも立たないモデルでも一応、動くものを作った方が勉強にはなるだろう。