ShimakazeSoft Tech

Python好きの新卒WEBエンジニアが技術記事を執筆するブログ。主にWEB系や機械学習系のことを掲載。

Falcon チュートリアル(翻訳) FirstSteps

f:id:shimamili:20180527174054j:plain
falcon

Tutorial — Falcon 1.4.1 documentation
この記事は上記の翻訳記事になります。
誤訳等があれば、ご指摘いただければ幸いです。



最初に新しいvirtualenvの中にFalconをインストールします。
そのために、 "look"という新規のプロジェクトを作成し、チュートリアルで使用できる仮想環境を設定します。

$ mkdir look
$ cd look
$ virtualenv .venv
$ source .venv/bin/activate
$ pip install falcon

プロジェクトのトップレベルモジュールがプロジェクトと同じように呼び出されるのは通例で、最初のモジュールの中に別の "look"ディレクトリを作成し、空の__init__.pyファイルを作成してPythonモジュールとして認識させましょう。

$ mkdir look
$ touch look/__init__.py

次に、アプリケーションへのエントリポイントとなる新しいファイルを作成しましょう。

$ touch look/app.py

ファイル階層は次のようになります。

look
├── .venv
└── look
    ├── __init__.py
    └── app.py

次に、お好きなテキストエディタでapp.pyを開き、次の行を追加します。

import falcon

api = application = falcon.API()

このコードは、apiとしてWSGIアプリケーションを作成します。apiに好きな変数名を使うことはできますが、Gunicornはデフォルトでapiが呼び出されることを想定しているため、applicationを使用します。

Note

WSGIアプリケーションは、WSGIプロトコルを理解している任意のWebサーバー上でアプリケーションをホストできるように、明確に定義された呼び出し可能なものです。

次に、falcon.APIクラスの中身を見てみましょう。

Pythonをインストールして起動する:

$ pip install ipython
$ ipython

今度は、次のように入力してfalcon.API callableを調べます。

In [1]: import falcon
In [2]: falcon.API.__call__?

又は、Pythonで標準のhelp()関数を使用しても調べることができます。

In [3]: help(falcon.API.__call__)

以下のような結果が表示されるはずです。

WSGI `app` method.

Makes instances of API callable from a WSGI server. May be used to
host an API or called directly in order to simulate requests when
testing the API.

(See also: PEP 3333)

Args:
    env (dict): A WSGI environment dictionary
    start_response (callable): A WSGI helper function for setting
        status and headers on a response.


メソッドシグネチャに注意してください。 envとstart_responseは標準のWSGIパラメータです。 Falconはこれらのパラメータの上に薄い抽象を追加するので、wsgiと直接対話する必要はありません。
Falconフレームワークには、上記の手法を使用して参照できる広範なインラインドキュメントが含まれています。

Tip

IPythonに加えて、Pythonコミュニティには、bpythonやptpythonなど、試してみたい他のスーパーパワーREPLがいくつかあります。

PHPの配列で重複した値の数をカウントするアルゴリズム

php
PHPの配列で、値が格納された配列内で重複している要素の出現回数をカウントしてくれるアルゴリズムを紹介します。
PHPでは既にarray_count_valuesという関数が用意されており、この関数の引数に配列を指定すれば、

PHP: array_count_values - Manual

実際にarray_count_valuesを使用してみたサンプルです。

<?php
$input = array("orange","blue","php","hoge","php","apple","test","orange");
$output = array_count_values($input);
print_r($output);
?>

結果は以下のように出力されます。

Array
(
    [orange] => 1
    [blue] => 1
    [php] => 2
    [apple] => 1
    [test] => 1
)

JavaScriptで配列の重複している項目の数をカウントする
array_count_values以外にも上記URLを参考に、以下のようなPHPで独自に実装したコードもあります。

<?php
$counts = array();
$tmpArray = array("apple","hoge","php","hoge","php","apple","test","orange");
for($i = 0; $i < count($tmpArray); $i++){
    $key = $tmpArray[$i];
    $counts[$key] = (in_array($key, $tmpArray)) ? $counts[$key] + 1 : 1;
}
?>

以下も同じような結果を出力します。

Array
(
    [orange] => 1
    [blue] => 1
    [php] => 2
    [apple] => 1
    [test] => 1
)

上記二つのarray_count_valuesを使った方法とそうじゃない方法を使って実行時間を比べてみます。
まず最初に0~9の4桁の数値を1000個を乱数で生成します。数値一つ一つは文字列型に変換して配列に格納します。

「array_count_valuesを使った方法とそうじゃない方法」のそれぞれの処理を1000回繰り返し、合計時間と平均時間を出力して比べてみます。

<?php

$MAX = 1000;
function RandStr($length) {
    $str = array_merge(range('0', '9'));

    $r_str = null;
    for ($i = 0; $i < $length; $i++) {
        $r_str .= $str[rand(0, count($str) - 1)];
    }
    return $r_str;
}

$tmpArray = array();
for($i = 0; $i < $MAX; $i++){
    array_push($tmpArray, RandStr(4));
}

$time = 0;
$time_start = microtime(true);
for($i = 0; $i < 1000; $i++){
    $counts = array_count_values($tmpArray);
}
$time = (microtime(true) - $time_start);
echo "処理時間:".sprintf("%.20f", $time)."\n";

$time = $time/1000;
echo "処理時間:".sprintf("%.20f", $time)."\n";

echo "\n";


$time = 0;
$time_start = microtime(true);
for($d = 0; $d < 1000; $d++){
    $counts = array();
    for($i = 0; $i < count($tmpArray); $i++){
        $key = $tmpArray[$i];
        $counts[$key] = (in_array($key, $tmpArray)) ? $counts[$key] + 1 : 1;
    }
}
$time = (microtime(true) - $time_start);
echo "処理時間:".sprintf("%.20f", $time)."\n";

$time = $time/1000;
echo "処理時間:".sprintf("%.20f", $time)."\n";
?>


上記処理を自分の環境で実行したところ、実行処理時間は以下のようになりました。(PHP7.1の環境で試しました。)

//array_count_valuesを使った処理
合計時間:0.02799391746520996094秒
平均時間:0.00002799391746520996秒

//array_count_valuesを使わなかった処理
合計時間:18.34730386734008789062秒
平均時間:0.01834730386734008747秒

array_count_valuesを使ったほうが断然早いことがわかります。array_count_valuesで使われているアルゴリズムが、どんな実装されているかは今度見てみようと思います。

Laravel PHPでのマイグレーションファイルの作成方法

Laravel PHP上でのテーブル定義を行うときに便利なのが、マイグレーションという機能です。

マイグレーションというものを簡易的に説明すると、マイグレーションとはテーブル定義を行える機能のことです。
つまり、データーベース上にテーブルを作成したり、改修したりなどが行える機能のことを言います。マイグレーションという機能は、基本的にどのフレームワークに搭載されています。

マイグレーションファイルの作成

マイグレーションを行うには、マイグレーションファイルというものを作成しなければいけません。
以下のコマンドでマイグレーションファイルの作成が行えます。

$ php artisan make:migration {マイグレーション名}

マイグレーションファイル名は以下のようにして指定するのが慣例的であり、以下の場合は指定したマイグレーションファイル名のキャメルケースがクラス名となります。

$ php artisan make:migration create_examples_table

上記のコマンドを実行すると、以下のディレクトリのようにdatabase/migrationsに内にマイグレーションファイルが作成されます。

database/
  └── migrations
          └── 2018_06_05_133752_create_examples_table.php

create_examples_tableとファイル名で指定したため、create_examples_tableのキャメルケースであるCeateExamplesTableがクラス名となります。
上記のコマンドで作成されたファイルの中身は以下のようになっています。

<?php
//  2018_06_06_133752_create_examples_table.php
 
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
 
class CeateExamplesTable extends Migration {
 
    public function up()
    {
        //
    }
 
    public function down()
    {
        //
    }
 
}

upメソッドの中にテーブル作成時の処理を記述していき、down() メソッドの中にはテーブル削除時の処理を記述していきます。


また以下のコマンドのように-createオプションを指定することで、作成するテーブル名を指定することが可能です。
ちなみにテーブル名は複数形にして命名することが慣例的となっています。

$ php artisan make:migration create_examples_table --create=examples

上記コマンドを実行すると、以下のような内容でマイグレーションファイルが作成されます。
Schema::createから始まるスキーマビルダーが指定されて出力されます。

<?php
// 2018_06_06_133752_create_examples_table.php
 
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
 
class CeateExamplesTable extends Migration {
 
    public function up()
    {
        Schema::create('examples', function(Blueprint $table)
        {
            $table->increments('id');
            $table->timestamps();
        });
    }
 
    public function down()
    {
        Schema::drop('examples');
    }
 
}

マイグレーションを実行

スキーマビルダーにフィールドを追加していき、テーブル定義を行なっていきましょう。
以下のようにstring型(文字列型)のtitleフィールドと、interger型(整数型)のnumberフィールドを指定します。

<?php
// 2018_06_06_133752_create_examples_table.php
 
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
 
class CeateExamplesTable extends Migration {
 
    public function up()
    {
        Schema::create('examples', function(Blueprint $table)
        {
            $table->increments('id');
            $table->string('title');
            $table->integer('number');
            $table->timestamps();
        });
    }
 
    public function down()
    {
        Schema::drop('examples');
    }
 
}

スキーマビルダーの指定の仕方などの詳細は以下に詳しく載っています。
スキーマビルダー 5.0 Laravel


以下のコマンドを実行することでマイグレーションが実行され、実際にexamplesテーブルが作成されます。

$ php artisan migrate

Migrated: 2018_06_06_133752_create_examples_table

Falcon チュートリアル(翻訳)

f:id:shimamili:20180527174054j:plain

Tutorial — Falcon 1.4.1 documentation

この記事は上記の翻訳記事になります。

チュートリアル

このチュートリアルでは簡単な画像共有サービスのためのAPIを構築します。途中、Falconの主な機能について説明し、フレームワークで使用される用語を紹介します。

  1. First Steps
  2. Hosting Your App
  3. Creating Resources
  4. Testing your application
  5. Request and Response Objects
  6. Refactoring for testability
  7. Functional tests
  8. Serving Images
  9. Introducing Hooks
  10. Error Handling
  11. What Now?

Falcon リクエストのバリデーションチェック, シリアライザミドルウェア(marshmallow) (翻訳)

falconロゴ
Falcon

この記事は以下URLの翻訳記事です。
Falcon framework - request data validation, serializer middleware (marshmallow) - Przemysław `eshlox` Kołodziejczyk


Falconにはリクエストの内容をバリデーション(検証)する仕組みは備えていませんが、幸いにもその機能を追加することは簡単です。

marshmallowを使ってリクエストに付随するデータを検証します。これにより、スキーマを作成して、JSONデータが正しいかどうかを検証します。

ミドルウェアの使い方がわからない場合は、こちらでFalconミドルウェアのドキュメントが読めます。


それではまずカスタムのHTTPErrorクラスを作ることから始めましょう。
なぜ最初にそれらをする必要があるのか。
それは、送信されてきたデーターが何が問題なのかをメッセージで返したい場合、以下の様にして返さなければならないからです。

{
    "title": "422 Unprocessable Entity",
    "errors": {
        "date_start": ["Missing data for required field."]
    }
}

上記データーのtitleフィールドはHTTPエラーの説明ですが、エラーはmasrhmallowから送られてきます。以下は、marshmallowがどのようにしてデータをバリデーション(検証)するかの例です。

from marshmallow import fields, Schema, ValidationError


class UserSchema(Schema):
    name = fields.Str(required=True)


try:
    UserSchema(strict=True).load({})
except ValidationError as err:
    print(err.messages)


上記のとおり、単一のnameフィールドを持つUserSchemaクラスを作成しています。nameフィールドは必須項目です。
nameフィールドを渡さずにUserSchemaオブジェクトを作成しようとした場合、ValidationErrorが発生するはずです。

上記スクリプトの出力結果(バリデーションエラーが発生した時)

{'name': ['Missing data for required field.']}

上記の情報をユーザーに返したいとします。
デフォルトのHTTPErrorでは、文字列部分を説明文としてのみ指定できますが、今回は辞書型(dict)で返したいと考えています。
そのため、デフォルトのHTTPErrorクラスの内容を少し変更します。

import falcon


class HTTPError(falcon.HTTPError):
    """
    HTTPError that stores a dictionary of validation error messages.
    バリデーションエラーメッセージの辞書を格納するHTTPErrorクラス
    """

    def __init__(self, status, errors=None, *args, **kwargs):
        self.errors = errors
        super().__init__(status, *args, **kwargs)

    def to_dict(self, *args, **kwargs):
        """
        Override `falcon.HTTPError` to include error messages in responses.

        レスポンス内にエラーメッセージに格納するために`falcon.HTTPError`をオーバーライドしてください。
        """

        ret = super().to_dict(*args, **kwargs)

        if self.errors is not None:
            ret['errors'] = self.errors

        return ret

上記の新しいHTTPErrorをミドルウェア内で使用することで、バリデーションエラーを返すことができます。それではミドルウェア部分の処理を書きましょう。

import falcon.status_codes as status

from marshmallow import ValidationError

from core.errors import HTTPError  # it's our new HTTPError


class SerializerMiddleware:

    def process_resource(self, req, resp, resource, params):
        req_data = req.context.get('request') or req.params

        try:
            serializer = resource.serializers[req.method.lower()]
        except (AttributeError, IndexError, KeyError):
            return
        else:
            try:
                req.context['serializer'] = serializer().load(
                    data=req_data
                ).data
            except ValidationError as err:
                raise HTTPError(status=status.HTTP_422, errors=err.messages)

このミドルウェアは、リクエストされたクエリ文字列とボディからデータをバリデーション(検証)します。

デフォルトreq.context内にリクエストはありませんが、ここでのリクエストではありません。
別のミドルウェアを使用してユーザーからJSONデータを読み込んでそこに設定しましたが、このミドルウェアではJSONデーターを読み込むことができます。
また、全てのHTTPメソッドに対して個別のスキーマ(バリデータ)を設定することもできます。
最後に、シリアライザーデータをコンテキストに設定すると、APIエンドポイントのデータを読み取ることができます。
データが正しくない場合、APIはmarshmallowから返されたバリデーションメッセージでHTTP 422エラーを返します。それでは簡単な例を書きましょう。

まず最初に、Falconアプリケーションにミドルウェアを登録します。

import falcon

from core.middleware.serializers import SerializerMiddleware


app = falcon.API(middleware=[
    SerializerMiddleware(),
])

次に、単純なmarshmallowスキーマを作成します。

from marshmallow import fields, Schema

class BookPostSchema(Schema):
    class Meta:
        strict = True

    title = fields.Str(required=True)

class BookDeleteSchema(Schema):
    class Meta:
        strict = True

    book_id = fields.Integer(required=True)

単純なAPIエンドポイント

from book.serializers import BookDeleteSchema, BookPostSchema


class BookAPI:
    serializers = {
        'post': BookPostSchema,
        'delete': BookDeleteSchema
    }

    def on_post(self, req, resp):
        serializer = req.context['serializer']
        # req.context['serializer'] contains data sent by user
        # for example: print(serializer['title'])

    def on_delete(self, req, resp):
        serializer = req.context['serializer']
        print(serializer['book_id'])

    def on_put(self, req, resp):
        # no schema for delete method == no data validation

上記の通り、これでエンドポイントの全てのHTTPメソッドに異なるスキーマを割り当てることができます。データーが正しい場合、req.context['serializer']にアクセスが可能です。そうでない場合、APIはHTTPエラーを返します。




一部、誤訳も含めているかもしれないためご指摘いただければ修正します。

GitHubにサンプルのソースコードを上げました。ご参考になれば幸いです。
The world’s leading software development platform · GitHubgithub.com

Pythonで機械学習に軽く振れる その1(NumPyの使い方・導入編)

f:id:shimamili:20180330022959j:plain

まずは機械学習をやってみる中で、よく使うライブラリがNumPyです。

NumPyはPython数値計算を効率的に行うためのモジュールです。主にベクトル演算などを行う際などに使われます。内部はC言語で書かれているため、多次元配列などを扱った計算には非常に高速です。

Numpyの導入

pip install numpy

pipでnumpyをインストールします。

numpyをインストール後、全てのコードでnumpyを呼び出せるようにimportしておきます。

import numpy as np

Numpyで配列を作成して操作

numpyにはndarrayという配列があり、ndarrayは多次元配列を扱うためのnumpyのオブジェクトです。

1,2,3という要素を持った配列(ベクトル)を作成する場合、以下のようにします。

arr = np.array([1,2,3])
print(arr)

# [1, 2, 3]

2次元の行列を作る際は、配列を要素として以下のようにカンマで区切ります。

matrix = np.array([[1,2,3],[4,5,6]])
print(matrix)

# [[1 2 3]
#  [4 5 6]]

numpyの配列であるnumpy.ndarrayはPython自体が元々持つリスト、タプル、セット、辞書(ディクショナリ)などの配列類よりも高速に動作する上、便利な関数や属性が多く用意されています。

配列の各次元の要素数を知りたい場合

ndarrayの便利な機能の一つとしてshapeメソッドがあります。ndarray.shapeは各次元の要素数を知りたい場合に使用します。

以下の場合は要素数が3つの3列のデーターであることがわかります。

arr = np.array([1,2,3])
print(arr.shape)

# (3,)

以下の場合は要素数が2行3列のデーターであることがわかります。

matrix = np.array([[1,2,3],[4,5,6]])
print(matrix.shape)

# (2, 3)

基本的な演算

それでは実際にNumpyを使って基本的な演算処理のコードをいくつか書いてみましよう。

加算処理

numpyの配列を使って、足し算をやってみます。
以下は2次元配列[[1,2,3],[2,3,4]]と[[3,4,5],[4,5,6]]を足し合わせた例です。
ndarray配列のオブジェクト同士を足しあわせてもできますが、numpy独自の足し算用のメソッドとしてnp.addというメソッドが既にあります。

# 加算
arr1 = np.array([[1,2,3],[2,3,4]])
arr2 = np.array([[3,4,5],[4,5,6]])

arr = arr1 + arr2
print(arr)
# [[ 4  6  8]
#  [ 6  8 10]]

# 上記と以下の処理は全く同じ
arr = np.add(arr1,arr2)
print(arr)
# [[ 4  6  8]
#  [ 6  8 10]]
[ [1, 2, 3]
  [2, 3, 4] ]

[ [3, 4, 5]
  [4, 5, 6] ]

こちらの配列、つまり行列とも言いますが、これら二つの配列を足しあわせた結果が以下です。

[ [ 4, 6, 8]
  [ 6, 8, 10] ]


今回はNumpyの扱い方について軽く解説しました。
機械学習自体はNumpyを必ずしも使わなきゃいけないわけではありませんが、Pythonであらゆる数値計算処理をやる上では、Numpyはほぼ必須となっています。次回からNumpyについて少し深く触れていきます。

Pythonで機械学習に軽く振れる その0(機械学習の概要)

f:id:shimamili:20180329024027p:plain
超初心者向けの機械学習チュートリアルを書いてみたかった・・・

最近はニュースでも人工知能(AI)の話題が出れば、ターミネーターの時代が近いなど、いろいろ騒がれて話題になっているAIですが、AIの中で特に話題となっているのは機械学習の技術です。

去年の2017年は機械学習ブームでもあり、エンジニア界隈でも「機械学習やってみたい!」と思う方がいるものの、実際本を一冊買って中身を見てみると、「何がなんだかわからん」「数式読めない」と言う方は多くいたと思います。

 

そんな自分も機械学習をやっているものの、深い所までいくと「さっぱりわからん」と感じるものです。

自分は、ある方に教えてもらう機会があり、機械学習にほんの数ヶ月だけガッツリ触れていた期間がありました。(ガッツリといっても本格的に長く触れてる方からしたら初歩的なものです)

 

今回は「数学よくわからん」「機械学習の本読んで挫折した」という方向けに、機械学習に軽くでも触れられるようなチュートリアルを作っていきます。

できる限り数式を省き、難しい所も省いて解説できるように心構えていきます。

最初にやるべきこととは何か?

まず初心者は何をすべきか?個人的な意見ではありますが、まずはライブラリなどを使って実際に機械学習をやってみて感覚を掴んでいくことが大事だと思います。

深く理解していくには理論も大事ですが、機械学習初心者が挫折しちゃうのは、大体は最初に理論を全て理解しようとして「なるほどわからん」と思って辞めちゃう方が多いのかなと思います。すべての理論を理解しようとしなくても良いと思います。

まず最初は機械学習は「何ができるか」「どんな手順なのか」を理解していき、そこから理論を紐解いていくのが良いのかなと思います。

このチュートリアルで使うプログラミング言語

タイトルにもある通り、Pythonを使います。だいたい機械学習となると、Pythonを使っていくのがほとんどです。
しかし、決してPythonでなくてはいけないわけではありません。
Pythonは、機械学習をするのに便利なツール類が一式揃っているため、大抵機械学習関連の資料となるとPythonを使っていくものが占めていきます。

このチュートリアルで扱うライブラリ

機械学習用のライブラリには、Googleが公開した「Tensorflow」、国内企業であるPFNが公開した「Chainer」などいろいろあり、どれを選べばいいかわからないという方が多いと思います。

最初は「scikit-learn」を扱えるようになるのがオススメです。scikit-learn自体はいろんな機能を備えており、何より使いやすいのが特徴です。本シリーズでも「scikit-learn」を使っていきます。

機械学習って何?

ここまでタイトルを引っ張っておいて「機械学習とは〜」と説明するのも今さらですが、一応のために説明しておきます。

wikipediaなどによれば、以下の内容を機械学習というそうです。

人口知能における研究課題の一つで、人間が自然に行っている学習能力と同様の機能をコンピューターで実現しようとする技術・手法のことである

 
機械学習とは | SAS

他にもこの記事には以下のような記述もありました。

さまざまなアルゴリズムを用いてデータから反復的に「学習」するため、人間が探すべき場所を明示的にプログラムしなくても、コンピューターが自律的にデータから洞察を導き出せるようになります。

簡単に言えば、データーを学習させることにより、結果を導き出す処理を自動でやってくれるというものです。

大抵何かしらの動作をさせるプログラムを書きたいと思うとき、自らコーディングをして、「もし〜こうなら」というif文のような条件分岐を大量に自分で書かなくてはいけません。
しかし、そういった条件分岐を自動で生成してくれるのが機械学習と考えればよろしいと思います。
ただ、この記事に辿り着いた人の多くは大体のことはわかっているかと思うので、この辺は軽く飛ばします。

機械学習の流れ

機械学習のプログラムを作成する上での最終的な目標は「学習モデル」を生成することです。

学習モデルとは、未知のデーターが与えられた際に、そのデーターを分類するためのの判定器(分類器)です。前の段階で、大量のデーターを元に作られています。

機械学習を行う上での全体の大まかな流れを表すと以下のようになります。

  1. データーを収集
  2. データーを整形する
  3. 集めたデーターを元に学習モデルを作成
  4. 検証・調整

1でまず機械学習に必要なデーターを集めます。データーの集め方としては、どこかで配布されているデーターだったり、いわゆるスクレイピングと呼ばれる手法でデーターを一気に集めたりします。

2では、集めたデーターを機械学習で使える形式に整形します。大抵いらないものが含まれてたりすることがあるため、それらを除去したりします。

3で2で整形したデーターを元にモデルを作成していきます。選択したアルゴリズムを元に実際にデーターを学習させて、モデルを作ります。
これにより、学習させたデーター以外の未知のデーターを入れても、自動で分類などが行えるようになります。

4では3で生成したモデルがそれなりに精度が良いものなかを検証したり、良い精度が出るようにチューニングなどを行っていきます。


一通りに流れを説明しましたが、実際にやってみるのが良いと思います。
このチュートリアルでは、最初は機械学習関連で使うツール類の使い方から解説していき、そこから徐々に機械学習の実践方法に触れていきたいと思います。