その他
    ホーム 職種別 エンジニア PyScriptで線形回帰してみた
    PyScriptで線形回帰してみた
     

    PyScriptで線形回帰してみた

    運動不足解消のためランニングマシンの購入を検討しています。ゲームデザイン(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>
    hello_world.html結果

    出力のコントロール

    先ほどの例ではprintした文字列が新規に生成されたdiv要素に出力されましたが、Pythonによる計算・処理結果を特定のHTMLタグ内に出力したい場合、以下のように py-script タグに out を定義することで可能です。さらにメソッドも定義できます。

    この例では now_time() メソッドで現在時刻を返し、py-scriptの最後に実行して結果をoutput-targetdiv要素に出力しています。

    <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>
    hello_time.html結果

    DOM操作

    py-script内で定義したメソッドをHTMLのボタンで実行する場合、pys-onClick属性でメソッド名を指定します。

    さらにElement()で指定したidの要素を取得でき、input要素ならそのvalueを参照できます。divpなどの要素に対しては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>
    hello_input.html結果

    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>
    hello_plt.html結果

    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>
    hello_scikit.html結果

    まとめ

    今回はPyScriptを利用してscikit-learnで線形回帰を試してみました。ブラウザ上で実行するため、inputやbuttonなどのユーザインターフェースを手軽に利用できる点は非常に便利です。普段からPythonに慣れているエンジニアにとってはJavascriptを覚えるより簡単なのではないでしょうか。

    しかしながら、動作環境の構築のためかページの読み込みに非常に時間がかかります。まだアルファ版ですのでこれから先の発展を期待しています。

    記事を共有