運動不足解消のためランニングマシンの購入を検討しています。ゲームデザイン(GD)部クライアントエンジニアの中村です。
先日PyScriptがオープンソースで公開されましたので、さっそく試してきました。
PyScriptとは
PyScriptはPythonコードをブラウザ上でJavascriptのように実行させることができるフレームワークです。Pythonでの計算処理などをHTMLエレメントへ出力できることに加え、可視化ライブラリのmatplotも利用できてしまいます。さらに機械学習ライブラリであるscikit-learnも含まれているため、ブラウザ上で手軽に機械学習を行うことができます。
ちなみにPyScriptではPyodideで利用できるライブラリがそのまま利用できるようです。そのため tensorflow を利用することはできませんでした(Pyodide Version 0.20.0)。
基本知識
導入
公式サイトにあるように、HTMLでPythonを実行するためには以下のように記述します。これだけでPythonコードのprint
が実行されます。実行された結果はdiv
要素を生成してその中に出力されます。
<html>
<head>
<title>PyScript Hello World</title>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
</head>
<body>
<div>!! PyScript Hello World !!</div>
<py-script>
print('Hello PyScript')
</py-script>
</body>
</html>
出力のコントロール
先ほどの例ではprint
した文字列が新規に生成されたdiv
要素に出力されましたが、Pythonによる計算・処理結果を特定のHTMLタグ内に出力したい場合、以下のように py-script
タグに out
を定義することで可能です。さらにメソッドも定義できます。
この例では now_time()
メソッドで現在時刻を返し、py-script
の最後に実行して結果をoutput-target
のdiv
要素に出力しています。
<py-script output="output-target">
from datetime import datetime
def now_time():
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
now_time()
</py-script>
<div>Now Time is:</div>
<div id="output-target"></div>
DOM操作
py-script
内で定義したメソッドをHTMLのボタンで実行する場合、pys-onClick
属性でメソッド名を指定します。
さらにElement()
で指定したid
の要素を取得でき、input
要素ならそのvalue
を参照できます。div
やp
などの要素に対してはwrite()
メソッドでその要素内に文字列を出力できます。
<div class="flex">
<input id="calc-1" class="border p-1 rounded flex" type="number">
<div class="p-1">+</div>
<input id="calc-2" class="border p-1 rounded" type="number">
<div class="p-1">=</div>
<div id="calc-result" class="p-1"></div>
<button id="new-task-btn" class="p-1 text-white bg-green-600 rounded" type="submit" pys-onClick="run">
Run
</button>
</div>
<py-script>
def run(*ags, **kws):
a = float(Element("calc-1").element.value)
b = float(Element("calc-2").element.value)
Element("calc-result").write(a + b)
</py-script>
Pythonファイルの読み込み
さらにHTMLに直接の記述ではなく、別途Pythonファイルを用意して読み込ませることもできます。このあたりはJavascriptと同じ仕組みです。
<py-script src="./sample_script.py"></py-script>
機械学習ライブラリの利用
ある程度PyScriptの使い方がわかったところで、scikit-learnでの機械学習を試していきたいと思いますが、今回は非常に簡単な例として、多数のx,y座標を近似する線形回帰を記述します。
matplotの利用
まずは数値計算ライブラリのnumpyと可視化ライブラリのmatplotを利用して簡単なグラフを表示します。
numpyで100個の乱数を生成してx座標とし、一次関数に乱数を含ませてy座標とします。これをmatplotにで散布図として表示させています。pyscript.write("mpl", fig1)
と記述することで指定のdiv
内にmatplotの結果を出力します。
Pyodideに含まれるライブラリを利用する場合、head
内にpy-env
タグでライブラリ名を記述する必要があります。そうすることでpy-script
内でimport
することができます。
<head>
<link rel="stylesheet" href="https://pyscript.net/alpha/pyscript.css" />
<script defer src="https://pyscript.net/alpha/pyscript.js"></script>
<py-env>
- numpy
- matplotlib
</py-env>
</head>
<body>
<div id="mpl"></div>
<py-script>
import numpy as np
import matplotlib.pyplot as plt
a = 0.5
b = 2
rand = 0.5
count = 100
x = np.random.rand(count).astype(np.float32)
y = x * a + b + (np.random.rand(count).astype(np.float32) * rand - rand*0.5)
fig1, ax1 = plt.subplots()
tpc = ax1.scatter(x, y, alpha=0.5,color="red")
pyscript.write("mpl", fig1)
</py-script>
</body>
scikit-learnで線形回帰
最後に上記の散布図に対して線形回帰で一次関数の傾きと切片を予測させます。このとき予測させる数値をinput
から入力できるようにしました。ブラウザ上で稼働させているため、こういったUIを簡単に利用できる点が非常に便利です。
簡単な線形回帰ですが、scikit-learnによる機械学習も正しく稼働しました。
<body class="container">
<div class="flex p-2">
<label for="input_param_a" class="p-1">A</label>
<input id="input_param_a" type="number" value="1" class="border p-1 rounded">
<label for="input_param_b" class="p-1">B</label>
<input id="input_param_b" type="number" value="1" class="border p-1 rounded">
</div>
<div class="flex p-2">
<label for="input_param_rand" class="p-1">Random</label>
<input id="input_param_rand" type="number" value="0.5" class="border p-1 rounded">
<label for="input_param_count" class="p-1">Count</label>
<input id="input_param_count" type="number" value="100" class="border p-1 rounded">
</div>
<button id="btn" pys-onClick="runTrain" class="p-1 text-white bg-green-600 rounded">実行</button>
<div id="mpl"></div>
<py-script>
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
def runTrain(*ags, **kws):
a = float(Element("input_param_a").element.value)
b = float(Element("input_param_b").element.value)
rand = float(Element("input_param_rand").element.value)
count = int(Element("input_param_count").element.value)
x_train = np.random.rand(count).astype(np.float32)
y_train = x_train * a + b + (np.random.rand(count).astype(np.float32) * rand - rand*0.5)
lr = LinearRegression()
lr.fit(x_train.reshape(-1, 1), y_train)
prev_W = lr.coef_[0]
prev_W0 = lr.intercept_
fig1, ax1 = plt.subplots()
tpc = ax1.scatter(x_train, y_train, alpha=0.5,color="red")
_x = np.linspace(0, 1, count)
_y = prev_W * _x + prev_W0
ax1.text(0.2, prev_W*0+prev_W0, "y = prev_W * x + prev_W0")
ax1.text(0.2, prev_W*0.1+prev_W0, "prev_W={:.2f}, prev_W0={:.2f}".format(prev_W, prev_W0))
ax1.plot(_x, _y, color="black")
pyscript.write("mpl", fig1)
</py-script>
</body>
まとめ
今回はPyScriptを利用してscikit-learnで線形回帰を試してみました。ブラウザ上で実行するため、inputやbuttonなどのユーザインターフェースを手軽に利用できる点は非常に便利です。普段からPythonに慣れているエンジニアにとってはJavascriptを覚えるより簡単なのではないでしょうか。
しかしながら、動作環境の構築のためかページの読み込みに非常に時間がかかります。まだアルファ版ですのでこれから先の発展を期待しています。