LoginSignup
12
10

More than 5 years have passed since last update.

Scratchできかいがくしゅう

Last updated at Posted at 2018-12-17

TL;DR

ScratchのPython拡張を使ってKerasを呼び出すことにより、Scratchを機械学習のクライアントとして使ってみました。
適当な文章なので詳細な内容はよくわからないかもしれませんが、画像を見れば何となく変なことをしているのがわかると思います

背景

先日アプリコンテストに参加して、小学生がScratchを使って立派なアプリケーションを作成しているのを見て感化されました。そこで、小学生に負けないように、大人の威厳を見せつけてやろうというのが、この記事のモチベーションです(大人げない)。

環境構築

Scratchのインストール

以下のリンクから必要なものをダウンロードしてオフラインエディタでScratchが動作するようにします
Scratch 2.0 Offline Editor

Pythonのインストール

Python3をインストールします。( Windows | Mac )

ライブラリ群のインストール

pipを使って機械学習フレームワークのKerasをインストールします。
pipenvが使える場合はML-on-scratchをcloneしてpipenv installすると環境構築が完了します。

$ pip install tensorflow
$ pip install keras

Scratch用のPython拡張を作成するためのライブラリをインストールします。

$ pip install blockext

ScratchのPython拡張を作成

BlockExtの使い方

まずは、簡単なコードを書いて拡張機能が正しく動作することを確認してみます。
blockextからrun, reporter, commandをインポートして、デコレータ記法で使います。
@commandはScratchの命令ブロックを作ることができます。
@reporterはScratchのデータブロックを作ることができます。
run()で最後の引数にポート番号を指定して実行するとサーバが立ち上がります。

extension_test.py
from blockext import run, reporter, command


message = ""


@command("set message %s")
def set_message(m):
    global message
    message = m


@reporter("get message")
def get_message():
    return message


if __name__ == "__main__":
    run("Extension Test", "extension_test", 5678)

プログラムを実行するとブラウザなどからアクセスしてみて確認することができます。

$ python extension_test.py
Listening on 5678

スクリーンショット 2018-12-17 14.37.21.png

Download Scratch 2.0 extensionをクリックしてs2eファイルを保存します。

スクリーンショット 2018-12-17 14.17.55.png

このあともサーバが必要なので、ダウンロードが完了してもプログラムを終了してはいけません。

Scratchから読み込み

Scratchからs2eファイルを読み込んでPython拡張を有効化します。
Shiftを押しながらファイルを開くと実験的なHTTP拡張を読み込みという項目が現れます。そこから保存したs2eを選択して読み込みます。

スクリーンショット 2018-12-17 14.21.30.png

すると、その他に自作の関数が現れます。

スクリーンショット 2018-12-17 14.29.28.png

set messageで適当なテキストを設定して、get messageで取得して出力するようにScratchのブロックを組んでみると、以下のように動作します。

スクリーンショット 2018-12-17 14.49.00.png

あとは機械学習するだけ!

あとは内容を機械学習に変えるだけで、Scratchから機械学習ができるようになります。

XOR

機械学習の入門としてXORを解くプログラムを作ってみます。

$x_{1}$ $x_{2}$ $y$
0 0 0
1 0 1
0 1 1
1 1 0
xor.py
from blockext import run, reporter, command, predicate
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np


class Model:
    def __init__(self):
        self.x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
        self.y = np.array([0, 1, 1, 0])
        self.is_busy = False
        self.score = [0, 0]


    def build(self):
        self.model = Sequential()
        self.model.add(Dense(2, input_dim=2))
        self.model.add(Activation('sigmoid'))
        self.model.add(Dense(1))
        self.model.add(Activation('sigmoid'))


    def train(self):
        self.model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])
        self.model.fit(self.x, self.y, epochs=10000)
        self.score = self.model.evaluate(self.x, self.y)


@command("start training")
def train():
    model.is_busy = True
    model.build()
    model.train()
    model.is_busy = False


@predicate("is busy")
def is_busy():
    return model.is_busy


@reporter("get score")
def get_score():
    return "%d" % (model.score[1] * 100)


if __name__ == "__main__":
    model = Model()
    run("XOR", "xor", 5678)
$ python xor.py
Using TensorFlow backend.
Listening on 5678

Scratch側の処理

先程と同じようにs2eファイルを生成してScratchから読み込みます。

スクリーンショット 2018-12-17 16.48.50.png

こんな感じでブロックを並べてやると、旗をクリックするとXORの学習が始まって、完了すると猫が精度を教えてくれます。

スクリーンショット 2018-12-17 16.49.48.png

任意の入力で試せるように

せっかくなので、任意の入力を受け取ってそれに対する予測値を返せるようにしてみます。
予測を返す関数predict()と入力値をセットする関数set_x1(), set_x2()を追加しました。

xor.py
from blockext import run, reporter, command, predicate
from keras.models import Sequential
from keras.layers import Dense, Activation
import numpy as np


class Model:
    def __init__(self):
        self.x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
        self.y = np.array([0, 1, 1, 0])
        self.test = [0, 0]
        self.is_busy = False
        self.score = [0, 0]


    def build(self):
        self.model = Sequential()
        self.model.add(Dense(2, input_dim=2))
        self.model.add(Activation('sigmoid'))
        self.model.add(Dense(1))
        self.model.add(Activation('sigmoid'))


    def train(self):
        self.model.compile(loss='mse', optimizer='adam', metrics=['accuracy'])
        self.model.fit(self.x, self.y, epochs=10000)
        self.score = self.model.evaluate(self.x, self.y)


    def predict(self):
        if hasattr(self, 'model') and not self.is_busy:
            x = np.array([self.test])
            y = self.model.predict(x)
            return y[0][0]
        return "None"


@command("start training")
def train():
    model.is_busy = True
    model.build()
    model.train()
    model.is_busy = False
    print(model.predict())


@command("set x1 %n")
def set_x1(n):
    model.test[0] = n


@command("set x2 %n")
def set_x2(n):
    model.test[1] = n


@predicate("is busy")
def is_busy():
    return model.is_busy


@reporter("get score")
def get_score():
    return "%d" % (model.score[1] * 100)


@reporter("predict")
def predict():
    return model.predict()


if __name__ == "__main__":
    model = Model()
    run("XOR", "xor", 5678)

実行して、

$ python xor.py
Using TensorFlow backend.
Listening on 5678

Scratch側で以下のようにブロックを並べると、入力$x_{1}, x_{2}$に応じて$y$の値が出力できます。

スクリーンショット 2018-12-17 18.14.52.png

これで、XORを解く問題をScratchで実装することができました。本当はトレーニングデータも自由に入力できるようにしてANDやORも試せるようにしたかったのですが、時間的な問題からここまでで力尽きました。
とりあえず、Scratchを機械学習のクライアントに使えるということは証明できたのではないでしょうか。

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10