EMNISTをPythonで学習した後,TensorFlow.jsでブラウザ上で文字認識を行うところまでの備忘録.
環境
Jupyter環境の整備
jupyterhubをインストールしてサービスを作成します.
# apt install nodejs npm
# npm install -g configurable-http-proxy
# python3 -m venv /usr/local/share/jupyterhub
# source /usr/local/share/jupyterhub/bin/activate
(jupyterhub)# pip install jupyter jupyterhub jupyterlab matplotlib numpy
jupyterhubの設定ファイルを作成します.venvを利用しているので,jupyterhub-singleuserのパスを通すか以下編集します.
(jupyterhub)# mkdir -p /etc/jupyterhub
(jupyterhub)# cd /etc/jupyterhub
(jupyterhub)# jupyterhub --generate-config
(jupyterhub)# vi /etc/jupyterhub/jupyterhub_config.py
c.Spawner.cmd = ['/usr/local/share/jupyterhub/bin/jupyterhub-singleuser']
jupyterhubのサービスを作成します.ServiceUserを作成した方が良いでしょう.
# vi /etc/systemd/system/jupyterhub.service
[Unit]
Description=JupyterHub
After=network.target
[Service]
User=root
ExecStart=/usr/local/share/jupyterhub/bin/jupyterhub -f /etc/jupyterhub/jupyterhub_config.py
WorkingDirectory=/var/lib/jupyterhub
Restart=always
[Install]
WantedBy=multi-user.target
jupyterhubのサービスを起動します.
# mkdir -p /var/lib/jupyterhub
# systemctl daemon-reload
# systemctl start jupyterhub
Python3.8とTensorFlowのインストール
TensorFlow.jsがPython3.8でないとうまく動かないという情報があったため,Python3.8をインストールして利用する.またjupyterから利用するためにkernelを登録する
# add-apt-repository ppa:deadsnakes/ppa
# apt update
# apt install -y python3.8 python3.8-distutils python3.8-venv
$ python3.8 -v venv tensorflow
$ source tensorflow/bin/activate
(tensorflow)$ pip install tensorflow tensorflowjs matplotlib ipykernel
(tensorflow)$ python3 -m ipykernel install --user --name tensorflow --display-name "Python 3.8 (TensorFlow)"
学習
kaggleのemnistデータセットをダウンロードします.
$ #!/bin/bash
curl -L -o ~/Downloads/archive.zip\
https://www.kaggle.com/api/v1/datasets/download/crawford/emnist
$ unzip archive.zip
適当にモデルを作成,学習します.
import tensorflow as tf
import numpy as np
from tensorflow.keras import layers
from tensorflow.keras import losses
import os
import pandas as pd
DATA_DIR = "."
TRAIN_CSV = os.path.join(DATA_DIR, "emnist-letters-train.csv")
TEST_CSV = os.path.join(DATA_DIR, "emnist-letters-test.csv")
# データの読み込み
def load_emnist_data(train_csv, test_csv):
# トレーニングデータの読み込み
train_df = pd.read_csv(train_csv, header=None)
test_df = pd.read_csv(test_csv, header=None)
# 特徴量(画像データ)とラベルに分割
train_images = train_df.iloc[:, 1:].values.astype(np.float32)
train_labels = train_df.iloc[:, 0].values.astype(np.int32) # 最初の列がラベル
test_images = test_df.iloc[:, 1:].values.astype(np.float32)
test_labels = test_df.iloc[:, 0].values.astype(np.int32)
# ピクセル値を 0-1 に正規化
train_images /= 255.0
test_images /= 255.0
# 画像を28x28にリシェイプ
train_images = train_images.reshape(-1, 28, 28).transpose(0,2,1)
test_images = test_images.reshape(-1, 28, 28).transpose(0,2,1)
return train_images, train_labels, test_images, test_labels
train_images, train_labels, test_images, test_labels = load_emnist_data(TRAIN_CSV, TEST_CSV)
# モデルの定義
model = tf.keras.models.Sequential([
layers.Flatten(input_shape=(28, 28)),
layers.Dense(128, activation='relu'),
layers.Dropout(0.2),
layers.Dense(62)
])
# モデルのコンパイル
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
# モデルの学習
model.fit(train_images, train_labels, epochs=10, batch_size=64, validation_split=0.1)
# モデルの評価
test_loss, test_accuracy = model.evaluate(test_images, test_labels, verbose=2)
print(f"Test accuracy: {test_accuracy}")
# モデルの保存
model.save('emnist_model.keras')
変換
tensorflowjs_convertを利用してkerasモデルをtensorflowjsモデルに変換します.
$ tensorflowjs_converter --input_format=keras emnist_letters_model.keras tfjs_model
推論
変換したモデルを読み込みtensorを投げてレスポンスが帰ってくるところまで確認します.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>TFJS</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
</head>
<body>
<script>
let canvas = document.createElement("canvas")
canvas.width = 120
canvas.height = 120
let ctx = canvas.getContext("2d")
console.log(ctx)
console.log(tf)
tf.loadLayersModel("/model/model.json").then((model) => {
console.log(model)
ctx.fillStyle = "black"
ctx.fillRect(0, 0, 120, 120)
let image = ctx.getImageData(0, 0, 120, 120)
let tensor = tf.browser.fromPixels(image, 3)
.toFloat()
.resizeBilinear([28, 28])
.mean(2, true)
.div(tf.scalar(255))
.reshape([1, 28, 28])
let data = model.predict(tensor)
.dataSync()
console.log(tf.argMax(data).arraySync())
})
</script>
</body>
</html>
動作確認だけ.