るぴブロ

備忘録とかです(*'ω'*)

ResNetを使ってみよう!

すごく久しぶりの投稿です。
5月から所属部署の変更があり、バタバタしてましたw(今もですが)

せっかく前回、Machine Learning のHello World をやったので、流行り(?)の ResNet を使ってみましょう。

rupic.hatenablog.com

ResNet

ResNet (Residual Network) は Microsoft Research の Kaiming He さんが2015年に考案したニューラルネットワークのモデルらしい。

2014年の画像認識の分野でトップを争う ImageNet コンペティションにおいて、1位だった GoogLeNet は 22層。ところが、翌年には GoogLeNet スゲーってなってたとこに、まさかの 152層 が出てきて(゚д゚)フォォォァァァァァァァォァオァオァオアオアォアォアア!ってなったみたいです。

ResNet の特徴

  • とにかく層が深い
     → いろんな論文にも書いている通り、CNN では学習における層の深さはとても大事らしい。
  • ショートカット用のルートがある
     → すごく長いニューラルネットワークの入力層に近いノードでは、色々あって勾配消失が発生し学習が止まったり、速度が低下したりしてしまうらしく、それを解決する為の方法との事。

ResNet の弱点

  • すごく強いマシンが欲しい
     → 我が家のマシンは GPU GeForce Quadro P6000 / メモリ 64G ですが、それでも入力が大きいと 152層はしんどいです。
  • 学習に時間がかかる
     → 11class、152層、50 Epoc とかでも5~6時間くらいかかる。
       待っている間は何もしたくなくなる事もあるかもしれない。

en.wikipedia.org



とりあえず、メモ程度ですが学習用のコードを...。
参考にさせて頂いた無限ノック。

github.com

main.py

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
from tensorflow.keras.models import Sequential,Model
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D, Input, BatchNormalization, concatenate, AveragePooling2D, Add, SeparableConv2D
from tensorflow.keras import optimizers
from tensorflow.keras.callbacks import ModelCheckpoint, LearningRateScheduler, ReduceLROnPlateau, CSVLogger, TensorBoard
from tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array

# 好きなモデルを呼び出す
import ResNet152 as resnet

import seaborn as sns
import csv
import os
import numpy as np
import matplotlib.pyplot as plt
import json
import collections as cl
import argparse
import codecs
from datetime import datetime

def PrintLog(log):
    print(log,file=codecs.open('./Log/print.log','a','utf-8'))
    print(log)

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
physical_devices = tf.config.experimental.list_physical_devices('GPU')
if len(physical_devices) > 0:
    for k in range(len(physical_devices)):
        tf.config.experimental.set_memory_growth(physical_devices[k], True)
        print('memory growth:', tf.config.experimental.get_memory_growth(physical_devices[k]))
else:
    print("Not enough GPU hardware devices available")


PATH = r'D:\datasets\original'

train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

# 学習用のサブディレクトリがクラスになる

class_names = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
class_names.sort()

train_dirs = []
validation_dirs = []
for name in class_names:
    train_dirs.append(os.path.join(train_dir, name)),
    validation_dirs.append(os.path.join(validation_dir, name))

num_tr = []
for trdir in train_dirs:
    num_tr.append(len(os.listdir(trdir)))

num_val = []
for valdir in validation_dirs:
    num_val.append(len(os.listdir(valdir)))

total_train = 0
for numtr in num_tr:
    total_train += numtr


total_val = 0
for numval in num_val:
    total_val += numval


PrintLog("--images-----------------------")
for i in range(len(class_names)):
    PrintLog('class : {} images'.format(class_names[i]))
    PrintLog('        total training   {}'.format(num_tr[i]))
    PrintLog('        total validation {}'.format(num_val[i]))


PrintLog("-------------------------------")
PrintLog("Total : images")
PrintLog(("        training   ", total_train))
PrintLog(("        validation ", total_val))


num_classes = 11
channel = 3
batch_size = 11

epochs = 50
IMG_WIDTH = 1080
IMG_HEIGHT = 1080


# 写真のセンターをいい感じに切り出す
def crop_center(pil_img, crop_width, crop_height):
    img_width, img_height = pil_img.size
    return pil_img.crop(((img_width - crop_width) // 2,
                        (img_height - crop_height) // 2, 
                        (img_width + crop_width) // 2, 
                        (img_height + crop_height) // 2))

# 写真の縦の長さで正方形に切り出す
def crop_max_square(pil_img):
    return crop_center(pil_img, min(pil_img.size), min(pil_img.size))

def preprocess(x):
    im = array_to_img(x)
    im_new = crop_max_square(im)    
    return img_to_array(im_new)

# 学習データセットに変化をつける
train_image_generator = ImageDataGenerator(
                        rescale=1./255,
                        rotation_range=5,
                        width_shift_range=.15,
                        height_shift_range=.15,
                        zoom_range=0.5,
                        brightness_range=[0.3,1.0],
                        preprocessing_function=preprocess
                        ) # 学習データのジェネレータ

train_image_generator.mean = np.array([123.68/255,116.779/255,103.939/255], dtype=np.float32).reshape((1,1,3))

# 必要な大きさにリサイズする
train_data_gen = train_image_generator.flow_from_directory(batch_size=batch_size,
                                                           directory=train_dir,
                                                           shuffle=True,
                                                           target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                           classes=class_names,
                                                           class_mode='categorical')


# 検証データをクロップ
validation_image_generator = ImageDataGenerator(rescale=1./255, preprocessing_function=preprocess) # 検証データのジェネレータ
validation_image_generator.mean = np.array([123.68/255,116.779/255,103.939/255], dtype=np.float32).reshape((1,1,3))

val_data_gen = validation_image_generator.flow_from_directory(batch_size=batch_size,
                                                              directory=validation_dir,
                                                              shuffle=False,
                                                              target_size=(IMG_HEIGHT, IMG_WIDTH),
                                                              classes=class_names,
                                                              class_mode='categorical')

# クラスをJsonに書き出しとく
dic = cl.OrderedDict()
for class_index in train_data_gen.class_indices:
    dic[class_index] = cl.OrderedDict({"index":train_data_gen.class_indices[class_index],"name":class_index})

with open('./model/class_map.json','w') as fw:
    json.dump(dic,fw,indent=2)


#モデルの作成
model = resnet.ResFunction(IMG_HEIGHT, IMG_WIDTH, channel, num_classes)

#ラーニングレートを可変にする
def lr_schedule(epoch):
    lr = 1e-3
   
    if epoch > 44:
        lr *= 1e-6
    elif epoch > 39:
        lr *= 1e-5
    elif epoch > 34:
        lr *= 1e-4
    elif epoch > 29:
        lr *= 1e-3
    elif epoch > 24:
        lr *= 1e-2
    elif epoch > 19:
        lr *= 1e-1

    PrintLog('Next Epoc: {}, Learning rate: {}'.format(epoch + 1, lr))
    return lr


# ADAM オプティマイザーと binary cross entropy 損失関数を選択
# 各学習エポックの学習と検証の精度を表示するために、metrics 引数を渡す
decay=1e-6
momentum=9e-1

model.compile(optimizer=optimizers.SGD(lr_schedule(0), momentum=momentum, decay=decay, nesterov=True),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# modelのネットワークレイヤーを見たい場合の関数
with open('./Log/network_layer','a') as f:
    model.summary(print_fn = lambda x: f.write(x + '\r\n'))

# モデルの学習

# ImageDataGenerator クラスの fit_generator メソッドを使用して、ネットワークを学習します。
# CallBackにモデル補正用のメソッドを指定してラーニングレートを調整する
save_dir = os.path.join(os.getcwd(), 'saved_models')
model_name = 'ResNetv152_model.{epoch:03d}.h5'
if not os.path.isdir(save_dir):
    os.makedirs(save_dir)
filepath = os.path.join(save_dir, model_name)

checkpoint = ModelCheckpoint(filepath=filepath,
                             monitor='val_accuracy',
                             verbose=1,
                             save_best_only=False)

lr_scheduler = LearningRateScheduler(lr_schedule)

#各エポックの結果をcsvへ保存する
csv_logger = CSVLogger(filename='./Log/training.log',
                       separator=',',
                       append=True)

callbacks = [checkpoint, lr_scheduler, csv_logger]

history = model.fit(
    train_data_gen,
    epochs=epochs,
    validation_data=val_data_gen,
    validation_steps=total_val // batch_size,
    steps_per_epoch=total_train // batch_size,
    callbacks=callbacks
)

#モデルを保存
model.save('./model')

#Confusion_Matrixを出力
Y_pred = model.predict(val_data_gen)
y_pred = np.argmax(Y_pred, axis=1)

PrintLog('\r\nConfusion Matrix\r\n')

cm = tf.math.confusion_matrix(val_data_gen.classes, y_pred)
PrintLog(cm)
sns.heatmap(cm, annot=True, fmt='d', cbar=False, cmap='Blues')
plt.savefig('./model/confusion_matrix.png')

# 学習結果の可視化
epochs_range = range(epochs)

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

plt.figure(figsize=(8, 8))
plt.subplot(1, 2, 1)
plt.plot(epochs_range, acc, label='Training Accuracy')
plt.plot(epochs_range, val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, loss, label='Training Loss')
plt.plot(epochs_range, val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.savefig('./model/figure.png')
plt.show()

ResNet152.py

from __future__ import absolute_import, division, print_function, unicode_literals

import tensorflow as tf
from tensorflow.keras.models import Sequential,Model
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten, Conv2D, MaxPooling2D, Input, BatchNormalization, concatenate, GlobalAveragePooling2D, Add, SeparableConv2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator

def ResFunction(IMG_HEIGHT, IMG_WIDTH, channel, num_classes):

    def ResBlock(x, in_f, f_1, out_f, stride=1, name="res"):
        res_x = Conv2D(f_1, [1, 1], strides=stride, padding='same', activation=None, name=name + "_conv1")(x)
        res_x = BatchNormalization(name=name + "_bn1")(res_x)
        res_x = Activation("relu")(res_x)

        res_x = Conv2D(f_1, [3, 3], strides=1, padding='same', activation=None, name=name + "_conv2")(res_x)
        res_x = BatchNormalization(name=name + "_bn2")(res_x)
        res_x = Activation("relu")(res_x)

        res_x = Conv2D(out_f, [1, 1], strides=1, padding='same', activation=None, name=name + "_conv3")(res_x)
        res_x = BatchNormalization(name=name + "_bn3")(res_x)
        res_x = Activation("relu")(res_x)

        if in_f != out_f:
            x = Conv2D(out_f, [1, 1], strides=1, padding="same", activation=None, name=name + "_conv_sc")(x)
            x = BatchNormalization(name=name + "_bn_sc")(x)
            x = Activation("relu")(x)

        if stride == 2:
            x = MaxPooling2D([2, 2], strides=2, padding="same")(x)
        
        x = Add()([res_x, x])
        x = Activation("relu")(x)

        return x
        
    
    inputs = Input((IMG_HEIGHT, IMG_WIDTH, channel))
    x = inputs
    
    x = Conv2D(64, [7, 7], strides=3, padding='same', activation=None, name="conv1")(x)
    x = BatchNormalization(name="bn1")(x)
    x = Activation("relu")(x)
    x = MaxPooling2D([5, 5], strides=3, padding='same')(x)

    x = ResBlock(x, 64, 64, 256, name="res2_1")
    x = ResBlock(x, 256, 64, 256, name="res2_2")
    x = ResBlock(x, 256, 64, 256, name="res2_3")

    x = ResBlock(x, 256, 128, 512, stride=2, name="res3_1")

    # 各ブロックの繰り返し数
    for i in range(7):
        x = ResBlock(x, 512, 128, 512, name="res3_{}".format(i + 2))

    x = ResBlock(x, 512, 256, 1024, stride=2, name="res4_1")
    for i in range(35):
        x = ResBlock(x, 1024, 256, 1024, name="res4_{}".format(i + 2))

    x = ResBlock(x, 1024, 512, 2048, stride=2, name="res5_1")
    x = ResBlock(x, 2048, 256, 2048, name="res5_2")
    x = ResBlock(x, 2048, 256, 2048, name="res5_3")

    # 一つ上の層の出力に合わせて
    x = GlobalAveragePooling2D()(x)
    #x = Flatten()(x)
    x = Dense(num_classes, activation='softmax', name="fc")(x)

    model = Model(inputs=inputs, outputs=x)

    return model

Hello World of Machine Learning!!

こんばんは。
会社から帰って家の事して色々してるといつもこの時間になってしまいますね。

福岡県ではコロナウイルスの感染者が 12 人に増えてしまいました💦
しっかり食べて、しっかり寝て、適度に運動して、うがい手洗いもして、出来るだけ人が集まるところは避けながらコロナに負けない生活習慣を心掛けたいです。

さて、先日までに Windows マシンの機械学習環境の構築をやっていきましたが、どうしてもやらないといけない事があって(ジャンプを読みたくて)動作確認をしてませんでしたので動作確認をしていきたいと思います。

rupic.hatenablog.com

機械学習Hello World → MNIST の画像分類

機械学習はじめまして!な方用として良く使われる MNIST 。
様々な Hello World の中でもトップクラスの感動を得られますw
※個人的に一番感動したのは Lチカでした

MNIST

Mixed National Institute of Standards and Technology database
28x28 ピクセルの手書き数字画像のデータセットで 6,0000 個のトレーニングデータと 1,0000 個のテストデータが収容されています。

詳しく知りたい方は以下の記事で確認してみてください。

udemy.benesse.co.jp

なにするの?

Tensorflow のチュートリアルにある、 MNIST の6,0000 個のトレーニングデータを学習して機械学習モデルを作成し、テストデータを用いてモデル精度を確認します。 www.tensorflow.org

学習モデルを作成する

Anaconda Prompt 等で Tensorflow-gpu 2.1.0 をインストールします。

pip install tensorflow-gpu==2.1.0

f:id:rupic:20200327004400p:plain

main.py

# 必要なライブラリをインポートする
import tensorflow as tf
from tensorflow.keras import datasets, layers, models

# MNIST データセットをダウンロード
(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()

# データ数、縦px、横px、チャンネル(グレースケール:1、カラー:3)
train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))

# ピクセルの値を 0~1 の間に正規化
train_images = train_images / 255.0
test_images = test_images / 255.0

# モデルを作成
model = models.Sequential()
# 畳み込み:活性化関数 relu → 入力 x が x<=0 であれば 0 を、x>0 であれば x を返す
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
# 出力をダウンスケール
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

# 3x3x64 次元のテンソル → 576個の要素を持つ1次元のベクトルに変換
model.add(layers.Flatten())
# 全結合層 
model.add(layers.Dense(64, activation='relu'))
# softmax関数 → 0~1 の範囲で値を算出
model.add(layers.Dense(10, activation='softmax'))

# モデルの概要を出力
model.summary() 

# モデルをコンパイル
# optimizer:最適化アルゴリズム
# loss:損失関数 (スパースなマルチクラス分類交差エントロピー関数)
# metrics:評価関数
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 学習開始 epoc は学習回数
model.fit(train_images, train_labels, epochs=5)

#モデルを保存
model.save('./model')

# テストデータを用いて精度の確認
test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

print(test_acc)

f:id:rupic:20200327014028p:plain
model.summary() の表示内容
f:id:rupic:20200327005802p:plain
実行するとすごい勢いで文字が流れていきます

f:id:rupic:20200327013647p:plain
指定 学習回数(5 epoch) が終了すると、テストデータを用いて精度を確認

最後に

言語系の Hello World と違ってデータセットを準備したり、少し長めのコードを書く必要がありますが、実際に動かした時の感動は中々なものですね。
もっと精度を上げるには学習の回数を変えてみたり、レイヤーを変えてみたり、最適化アルゴリズムを変えてみたり、学習レートを調整してみたり、データを可視化してみるのもいいですね…。調整出来る箇所が多いですが、色々と試してみてどんな変化があるか見てみるといいかもしれません😊

Windows 10 に CUDA + cuDNN をインストール

こんばんは。
昨日に引き続き PC の設定のお話です。
今日は NVIDIAGPU開発環境である CUDA と CUDA を使って Deep Learning の計算を高速で行うためのライブラリである cuDNN をインストールしていきますたいと思います!!
特にハマりポイントはないと思うのでさくっと入れていきます。

CUDAってなに?

CUDA(Compute Unified Device Architecture:クーダ)とは、NVIDIAが開発・提供している、GPU向けの汎用並列コンピューティングプラットフォーム(並列コンピューティングアーキテクチャ)およびプログラミングモデルである[3][4][5]。専用のC/C++コンパイラ (nvcc) やライブラリ (API) などが提供されている。なおNVIDIAGPUにおいては、OpenCL/DirectComputeなどの類似APIコールは、すべて共通のGPGPUプラットフォームであるCUDAを経由することになる[6]。 【Wikipedia引用】

www.nvidia.co.jp

要は、NVIDIA が開発している GPU 上でプログラミングをするためのソフトウェアプラットフォームで、例えば複雑で計算量の多い処理を並列処理したい時に、GPU を使って無駄なく効率的に処理をする為のものだと思ってます。

CUDA のほかにも OpenCL っていう GPGPU(GPUによる汎用計算) があるんですが、NVIDIA の性能を最大限活かす為に CUDA が作られてるだろうから NVIDIAGPU を利用しているのであれば、CUDA を利用する方がいいのではないかと思います。

www.khronos.org

CUDA Toolkit のダウンロード

CUDA の最新版は 10.2 ですが、Tensorflow 2.1 の環境に合わせて 10.1 をインストールしたいと思います。

www.tensorflow.org


以前のバージョンはこちらのアーカイブからダウンロードしましょう。

developer.nvidia.com

f:id:rupic:20200324005057p:plain
自身の環境にあったものをダウンロードしてください

CUDA Toolkit のインストール

f:id:rupic:20200324005157p:plain
特にこだわりがなければそのままOKで

f:id:rupic:20200324005305p:plain
同意して続行します

f:id:rupic:20200324005424p:plain
カスタムでインストールしたいものだけに絞る事も出来ますがそのままいきます

f:id:rupic:20200324010009p:plain
完了です。次へをクリックしましょう

f:id:rupic:20200324010043p:plain
このまま閉じます

システム環境変数の設定 PATH

自動で PATH が追加されていることを確認します。

f:id:rupic:20200324011534p:plain

システム環境変数の設定 CADA_PATH

自動でCUDA_PATH と CUDA_PATH_V10_1 が追加されていることを確認します。

f:id:rupic:20200324010447p:plain

インストールの確認

f:id:rupic:20200324012222p:plain
nvcc にパスが通ってることを確認

cuDNN ってなに?

CUDA をつかって Deep Learning の計算を高速で行うためのライブラリ
様々なフレームワークに対応しています。

対応一覧
https://www.arcbrain.jp/support/NVIDIA/Deep_Learning/Frameworks/#Another_Frameworks

cuDNN のダウンロード

ダウンロードするにはユーザー登録が必要です。
必ず CUDA と同じバージョンに対応した cuDNN をインストールしてください

ダウンロードリンク
https://developer.nvidia.com/rdp/cudnn-download

f:id:rupic:20200324013111p:plain
Download cuDNN をクリック
f:id:rupic:20200324013153p:plain
アカウントがなければ Join で作成しましょう

f:id:rupic:20200324014246p:plain
バージョンに注意!

f:id:rupic:20200324014325p:plain
自身の環境にあったものをクリックしてダウンロード

cuDNN のインストール

ダウンロードした『cudnn-10.1-windows10-x64-v7.6.5.32.zip』を解凍したフォルダの中にある『cuda』フォルダを開きます。

先ほどインストールした CUDA のディレクトリに中身のフォルダ毎コピペしちゃいましょう。
CUDA Toolkit のインストールディレクトリは変更してなければ以下

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1

f:id:rupic:20200324015406j:plain

システム環境変数の設定 CUDNN_PATH

CUDNN_PATHを追加

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1

f:id:rupic:20200324015548p:plain
環境変数の追加

インストールの確認

f:id:rupic:20200324015830p:plain
cudnn64_7.dll にパスが通っている事を確認

インストール完了

お疲れさまでした。
これでCUDA と cuDNN のインストールは完了です。

Windows Tarminal に Anaconda Prompt を表示する

こんばんは。
新しい PC に機械学習環境の構築を時間のある時にぼちぼちやってます。

rupic.hatenablog.com

会社で使ってる機械学習用マシンも最近新調したので、最近同じような事を何度もやってます😅
今回は Python のインストール → Anaconda Prompt をWindows Tarminal に追加をやっていきます。

Python のインストール

こちらは Anaconda をインストールしましょう。

Anaconda

Anaconda は Continuum Analytics って会社が提供している Python と conda というパッケージマネージャーを含む Python ディストリビューション
以下のサイトからインストーラーをダウンロードしてインストールします。

www.anaconda.com

f:id:rupic:20200322221234j:plain

f:id:rupic:20200322221249p:plain
PythonWindows の環境を選択

基本的にインストーラーの指示に従ってインストールすればOKです。
インストールの確認は Anaconda Prompt から以下を入力して Python のバージョンを確認で OK でしょう。

python -V

f:id:rupic:20200322224535p:plain
インストールされてますね

Python のライブラリをインストールしたり、仮想環境を作成したりする際に Anaconda Prompt を利用するんですが、起動するのに Windows メニューからアイコンをクリックするのが面倒なので、僕は Windows Tarminal に入れています。

Windows Tarminal

コマンドプロンプトだったり、Power Shell だったり、Cloud Shellだったり、似たようなコマンドラインツールが多いですよね。
そんなコマンドラインツール達を一括管理してくれるのが、Windows Tarminal !! これは便利ですね!!
ちなみに現在は Preview v0.9 ですが、正式版に備える予定の全機能が実装されているそう。

www.atmarkit.co.jp

github.com

インストール

Windows Store からインストールしましょう。

www.microsoft.com

システム要件

f:id:rupic:20200322222554p:plain

呼び出し

Win + R → 『wt』と入力して Enter で Windows Terminal が起動します。

f:id:rupic:20200322225236p:plain
コマンドプロンプト『cmd』を呼び出す感じでいいですね

f:id:rupic:20200322225419p:plain
Power Shell が起動しましたね

ちなみにタブの所にある『∨』をクリックすると、コマンドプロンプトや Azure Cloud Shell が選択できます。
Ctrl + Shift + 数字キーで切り替え可能です。
Windows Tarminal の機能は色々あるんですが、そこは本線から外れてしまいますので適宜ggって下さい。

Anaconda Prompt を追加する

タブの所にある『∨』をクリックして Setting をクリックすると Setting.json が開きます。
※ Ctrl + ,でも OK です。

Setting.json
 guid:一意のキー
 name:表示名
 commandline:呼び出すコマンドラインツール

// To view the default settings, hold "alt" while clicking on the "Settings" button.
// For documentation on these settings, see: https://aka.ms/terminal-documentation

{
    "$schema": "https://aka.ms/terminal-profiles-schema",

    "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",

    "profiles":
    {
        "defaults":
        {
            // Put settings here that you want to apply to all profiles
        },
        "list":
        [
            {
                // Make changes here to the powershell.exe profile
                "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
                "name": "Windows PowerShell",
                "commandline": "powershell.exe",
                "hidden": false
            },
            {
                // Make changes here to the cmd.exe profile
                "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
                "name": "cmd",
                "commandline": "cmd.exe",
                "hidden": false
            },
            {
                "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}",
                "hidden": false,
                "name": "Azure Cloud Shell",
                "source": "Windows.Terminal.Azure"
            }
        ]
    },

    // Add custom color schemes to this array
    "schemes": [],

    // Add any keybinding overrides to this array.
    // To unbind a default keybinding, set the command to "unbound"
    "keybindings": []
}

既存の設定をコピーする

f:id:rupic:20200323000536j:plain
Power Shell の設定をコピペする

guid を変更する

一意になれば良いので、末尾の文字を変更すれば OK

 変更前:"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
 変更後:"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44b9}",          ※16進で記載です

表示名を変更する

好きな名前にするといいと思う

 変更前:"name": "Windows PowerShell",
 変更後:"name": "Anaconda Prompt",

コマンドを変更する

Anaconda Prompt を呼び出す為のコマンドを変更する

Windows メニューから Anaconda プロンプトを右クリック→その他→ファイルの場所を開く
f:id:rupic:20200323005556j:plain
② Anaconda プロンプトを右クリック→プロパティ
f:id:rupic:20200323005843j:plain
③ リンク先をコピー
f:id:rupic:20200323005919j:plain
④ メモ帳などに張り付けて不要な部分を削除する
f:id:rupic:20200323010356j:plain
⑤ ④を Setting.json の commandline に張り付け
f:id:rupic:20200323010637p:plain

確認してみる

Win + R → 『wt』で Windows Tarminal 呼び出し(呼び出しが楽!!)

f:id:rupic:20200323010840p:plain
Anaconda Prompt が増えてる!!
f:id:rupic:20200323011006p:plain
しっかり動きますね

余談

よく見ると先ほど追加した Anaconda Prompt には アイコンがないんですよね。
Setting.json で設定できるプロパティが色々ある様で、どうやら icon に何かしら設定すれば良さそうです。

プロパティ一覧 github.com

// To view the default settings, hold "alt" while clicking on the "Settings" button.
// For documentation on these settings, see: https://aka.ms/terminal-documentation

{
  "$schema": "https://aka.ms/terminal-profiles-schema",

  "defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",

  "profiles": {
    "defaults": {
      // Put settings here that you want to apply to all profiles
    },
    "list": [
      {
        // Make changes here to the powershell.exe profile
        "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
        "name": "Windows PowerShell",
        "commandline": "powershell.exe",
        "hidden": false
      },
      {
        // Make changes here to the cmd.exe profile
        "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
        "name": "cmd",
        "commandline": "cmd.exe",
        "hidden": false
      },
      {
        "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}",
        "hidden": false,
        "name": "Azure Cloud Shell",
        "source": "Windows.Terminal.Azure"
      },
      {
        "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44b9}",
        "name": "Anaconda Prompt",
        "commandline": "cmd.exe /K C:\\Users\\rupic\\anaconda3\\Scripts\\activate.bat",
        "backgroundImage": "C:\\Users\\rupic\\OneDrive\\画像\\anaconda.png",
        "colorScheme": "Solarized Light",
        "useAcrylic": true,
        "icon": "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png",
        "hidden": false
      }
    ]
  },

  // Add custom color schemes to this array
  "schemes": [],

  // Add any keybinding overrides to this array.
  // To unbind a default keybinding, set the command to "unbound"
  "keybindings": []
}

f:id:rupic:20200323014153p:plain
自分の好きな様に改造してみるといいかもです

Macbookpro 入院 → 代替え機購入

こんばんは!
愛用している Macbookpro のキーボードの調子が宜しくなく久しぶりのブログです😢
どう調子が悪いかというとキー入力する際にキーを押した際にがチャタリングしている様な動作をするんです。。。
例えば『こんにちは』と入力しようとすると『こおおんにちはああ』みたいに不特定のキーが連続して入力されるような状態でコーディングはおろか通常のWEB検索すら困難ですw

これはあかんと思い Apple サポートに問い合わせたところ無償での修理をしてくれることに。
それは助かるのですが修理が完了するまでに最短で 7 営業日かかるところ、現在コロナの影響で部品が届かない事が考えられるため納期不明と…。
納期不明って💦
AzureにVM立ててiPadからーと考えましたが、メイン機として使うにはそこそこ辛いと思うのでここは代替え機を準備する事にしました。

f:id:rupic:20200321010925p:plain
こちらです。

仕様


ノートPCにしては珍しいメカニカルキーボードを採用しているモデルでキーの打鍵感がしっかりしていて入力しやすい。
残念ポイントはキーピッチを広く取りすぎてキー配列がちょっと使いずらいところと、メーカー製PCでは相変わらずのWindows 10 Homeなところ。 テンキー無くせばいいのになと思うけどゲーム用途と考えるとゲーム操作に影響の出ないキーはずらしちゃいますよねw

でも動作も軽快だし、使いやすいのでしばらくはこのマシンをガンガン使っていこうと思う。
せっかくGPUつんでるんだし、VR ヘッドマウント欲しいなぁ・・・。
と思いつつ、機械学習環境のセットアップしながらまたブログを書いていこうと思います。

Ink Recognizerを使ってみよう ( Cognitive Services 復習 )

こんばんは!
コロナウイルスの影響で各地のイベントは自粛ムード…
我が家は出来るだけ人が多い所を避けて、前々から嫁に頼まれてたやつをDIYしています。

さて、勉強会も中々開催できないので、時間がある時に Microosoft Azure のサービスで一番好きな Cognitive Services について不定期でのんびりブログに書いていこうと思います。

Cognitive Services

Microsoft の提供するサービスである、 Cognitive Services は人間の認知する機能の一部(見たり、聞いたり、話したり、意思決定したり、検索したり)を機械で擬似的に再現したサービスで Web API として利用できる AI パーツ。

2020.02.26 現在のサービス一覧 (27 Services ※5 preview)

  • 決定:よりスマートな意思決定をすばやく行う
  • 言語:非構造化テキストから意味を取り出す
    • Immersive Reader(preview):音声や視覚的な合図を使用して、あらゆる能力の読み手がテキストを理解できるようにする
    • Language Understanding自然言語を解釈する機能を提供
    • QnA Maker:ナレッジベースから質疑応答を、会話形式で行うことができる
    • Text Analytics:センチメント、キー フレーズ、名前付きエンティティを検出
    • Translator Text:60 を超えるサポート言語を検出し、リアルタイムで翻訳
  • 音声:音声処理をアプリやサービスに統合する
  • 視覚:画像、ビデオ、デジタル インク内のコンテンツを識別し、分析する
    • Computer Vision:画像内のコンテンツを分析
    • Custom Vision:ビジネス上のニーズに合うよう画像認識をカスタマイズ
    • Face:イメージ内の人物とその感情を検出して識別
    • Form Recognizer(preview):ドキュメントからテキスト、キーと値のペア、テーブルを抽出
    • Ink Recognizer(preview):デジタル インクや手書き入力を認識し、一般的なシェイプの位置特定
    • Video Indexer:ビデオのビジュアルとオーディオ チャネルを分析し、そのコンテンツにインデックスを設定
  • 検索:World Wide Web から探しているものを見つける
    • Bing Autosuggest:インテリジェントな先行入力機能が追加され、ユーザーはよりすばやくクエリを入力する
    • Bing Custom Search:検索結果に広告が表示されないカスタムの検索エンジンを作成
    • Bing Entity Search:名前付きエンティティを識別して分類し、それに基づいて検索結果を探す
    • Bing Image Search:さまざまな画像検索オプションをアプリに追加
    • Bing News Search:アプリをニュースの検索リソースに変更
    • Bing Spell Check:ユーザーが単語の切れ目、スラング、名前、同音異義語、ブランドを識別して修正
    • Bing Video Search:高度なビデオ検索機能をアプリに追加
    • Bing Visual Search:ユーザーが画像を使用して検索
    • Bing Web Search:安全で広告なしの、位置認識機能を備えた検索をユーザーが利用できるようにし、Web の検索結果、画像、ニュース、ビデオ、ビジュアルから関連情報を表示

Ink Recognizer

初回となる今回は、タイトル通り Ink Recognizer API についてです。

手書きの文字、図形、インク ドキュメントのレイアウトなどのデジタル インク コンテンツを認識できる AI サービス

ちょっと何言ってるかわかりませんね。

要は Ink Recognizer API を使用すると、アプリケーション内の手書きコンテンツを簡単に認識する事ができるんです。
あぁ、OCRかw と思われる方もいらっしゃるかと思いますが(僕もそう思いました)、タブレットPCなどにペンや手書きで書いた文字の軌跡を時間軸で並べ渡してあげる事で、図形や文字を認識してくれるというものらしいです。

f:id:rupic:20200224220125p:plain

できる事

手書き認識

63 の主要な言語とロケールの手書きコンテンツを認識

docs.microsoft.com

レイアウト認識

コンテンツを手書き領域、段落、行、単語、箇条書きに分割します。 これにより、お使いのアプリケーションでそのレイアウト情報を使用して、リストの自動書式設定や図形の配置などの追加機能を構築

図形認識

一般的な幾何学図形を認識

docs.microsoft.com

図形とテキストの認識

どのインク ストロークが図形または手書きコンテンツに属しているかを認識し、それらを個別に分類

価格

現在 Preview 中なので価格設定は変わる事があるかもしれませんが以下となっています。

こちら、200 ストロークの要求で 1 トランザクションでして、例えば『石橋裕太』という文字の場合、合計 37 ストロークという感じだと思います。
なので Free プランでも十分に楽しめるかと!!

実際に使ってみる

Azure Portal で Ink Recoognizer のリソースを作成

f:id:rupic:20200226173943p:plain
全てのサービス>AI+Machine Learning>Cognitive Services (検索する方が早いw)

f:id:rupic:20200226174200p:plain
Ink Recognizer のリソースを作成

f:id:rupic:20200226174234p:plain
必要事項を良い感じに入力してください

f:id:rupic:20200226174320p:plain
キーとエンドポイント>キー1をコピーしとく

サンプルコード

github.com

こちらのコードをローカルに git clone して VS Code で開きます。

git clone https://github.com/Azure-Samples/cognitive-services-REST-api-samples
cd SampleCode\cognitive-services-REST-api-samples\javascript\InkRecognition\javascript-app\src
code .

f:id:rupic:20200226174833p:plain
coonfig.js の SUBSCRIPTION_KEY にキー1を貼付け

あとは sample.html を開けば良いのですが、ここまで来て気づいた事としてペンで入力出来る端末がない事でしたww (Macbookなのでタッチついてない…)
とりあえず、App Service に GitHub からデプロイして iPad で動作を確認してみましょう。

動作を確認

f:id:rupic:20200226194847p:plain
左側:ペンで書いた文字、右側:Response JSON

Request
ストローク毎にポイントが時系列順で渡されてますね

{
  "version": 1,
  "language": "ja-JP",
  "strokes": [
    {
      "id": 63,
      "points": "48.6895,163.1115,47.3666,163.1115,46.0437,162.1193,46.0437,161.8015,45.3822,161.4630,45.3822,161.1452,46.7052,160.4682,50.0125,159.8145,55.9656,158.8198,63.2416,157.4891,73.1635,156.4943,83.0854,155.1843,91.6843,153.8511,100.2833,152.8589,105.5750,152.2026,108.2208,152.2026,109.5437,152.2026,109.5437,152.2026,108.2208,152.8589,106.8979,154.1896"
    },
    {
      "id": 64,
      "points": "78.4552,159.1582,78.4552,158.8198,77.7937,159.1582,77.1322,160.1504,76.4708,162.4552,75.8093,166.0932,74.4864,171.3797,71.8406,177.0021,67.2104,184.2937,61.9187,191.5671,55.9656,200.1687,48.6895,206.7677,44.0593,211.7338,40.0906,215.0515,37.4447,216.3641,36.1218,216.7025,36.1218,216.0256"
    },
    {
      "id": 65,
      "points": "65.8875,197.1895,65.8875,196.1973,65.8875,195.8588,65.8875,196.8510,65.8875,198.8406,67.2104,203.1323,68.5333,207.7625,70.5177,212.3901,73.1635,216.0256,74.4864,218.6895,75.8093,220.6765,77.1322,221.9865,77.7937,222.6427,78.4552,222.9812"
    },
    {
      "id": 66,
      "points": "74.4864,199.8328,73.1635,198.8406,73.1635,198.8406,74.4864,198.8406,77.1322,198.1843,83.0854,197.5073,88.3770,196.5151,94.3302,195.5410,99.6218,195.2026,102.9291,196.1973,104.2520,197.1895,105.5750,198.1843,105.5750,199.8328,104.9135,201.8171,103.5906,204.1245,101.6062,206.7677,100.2833,209.4291,99.6218,211.0776,98.2989,213.7208,96.9760,215.7078,96.3145,217.0203"
    },
    {
      "id": 67,
      "points": "85.7312,219.0073,83.0854,219.0073,82.4239,219.0073,83.0854,219.3458,85.0697,219.3458,89.7000,217.6947,96.9760,216.3641,102.9291,215.7078,104.2520,215.7078"
    },
    {
      "id": 68,
      "points": "115.4968,171.3797,115.4968,171.0619,114.1739,170.4030,114.1739,170.0671,115.4968,169.7287,118.1427,168.7365,124.0958,168.0802,131.3718,167.4239,136.6635,166.0932,140.6322,165.0984,141.2937,164.7806,140.6322,164.7806,137.3250,164.4447,134.0177,163.4500,131.3718,161.8015,130.0489,160.1504,129.3875,158.1635,129.3875,156.8328,129.3875,156.4943,129.3875,157.1713,129.3875,159.8145,129.3875,167.0854,128.7260,174.6948,128.0645,182.9604,128.0645,191.2286,128.0645,199.1739,128.0645,205.1193,128.0645,210.0854,128.7260,213.0645,129.3875,214.3771,129.3875,214.3771,129.3875,214.0593"
    },
    {
      "id": 69,
      "points": "131.3718,176.3458,131.3718,176.3458,130.7104,176.0254,130.0489,176.0254,129.3875,176.3458,129.3875,177.3380,128.0645,179.3250,128.0645,181.6323,125.4187,186.5984,122.1114,190.5723,118.8041,193.8719,116.1583,196.8510,114.1739,198.5021,112.8510,198.8406,112.1895,197.5073"
    },
    {
      "id": 70,
      "points": "129.3875,175.6895,128.0645,175.0332,127.4031,174.6948,126.7416,174.6948,128.0645,174.6948,129.3875,175.6895,132.6947,177.9969,135.3406,179.9813,138.6479,181.9682,141.2937,183.6167,142.6166,184.2937"
    },  ...長いので省略
  ]
}

Response
こちらは alternates の recognizedString に候補となる文字列が帰ってきてたり、一致率が一番高い文字列がわかったり、文字のある場所が座標として帰ってきてますね

{
  "recognitionUnits": [
    {
      "alternates": [
        {
          "category": "inkWord",
          "recognizedString": "右"
        },
        {
          "category": "inkWord",
          "recognizedString": "后"
        },
        {
          "category": "inkWord",
          "recognizedString": "谷"
        },
        {
          "category": "inkWord",
          "recognizedString": "厄"
        },
        {
          "category": "inkWord",
          "recognizedString": "名"
        },
        {
          "category": "inkWord",
          "recognizedString": "古"
        },
        {
          "category": "inkWord",
          "recognizedString": "百"
        },
        {
          "category": "inkWord",
          "recognizedString": "召"
        },
        {
          "category": "inkWord",
          "recognizedString": "不"
        }
      ],
      "boundingRectangle": {
        "height": 70.79000091552734,
        "topX": 36.119998931884766,
        "topY": 152.1999969482422,
        "width": 73.43000030517578
      },
      "category": "inkWord",
      "class": "leaf",
      "id": 4,
      "parentId": 3,
      "recognizedText": "石",
      "rotatedBoundingRectangle": [
        {
          "x": 38.619998931884766,
          "y": 149.52999877929688
        },
        {
          "x": 109.52999877929688,
          "y": 152.1999969482422
        },
        {
          "x": 106.83000183105469,
          "y": 224.0399932861328
        },
        {
          "x": 35.90999984741211,
          "y": 221.3699951171875
        }
      ],
      "strokeIds": [
        63,
        64,
        65,
        66,
        67
      ]
    },
    {
      "alternates": [
        {
          "category": "inkWord",
          "recognizedString": "槗"
        },
        {
          "category": "inkWord",
          "recognizedString": "楿"
        },
        {
          "category": "inkWord",
          "recognizedString": "橎"
        }, ...長いので省略
  ]
}

f:id:rupic:20200226200825p:plain
認識した文字列と場所をCanvas上に反映させるとこんな感じになります

f:id:rupic:20200226200936p:plain
ちなみにこんな図形を書いてみると…
f:id:rupic:20200226201007p:plain
良い感じに図形の形や場所などを認識してくれます

まとめ

  • Ink Recognizer API を使用すると、アプリケーション内の手書き文字を良い感じに認識してくれる!
  • 認識精度はかなり高めだと思う ※ヘナヘナな文字でもしっかり認識してくれて嬉しいw
  • 会員登録とか紙に文字で書いた後に PC に入力するっていう感じの業務は置き換え可能かも?
  • Macboook + iPad ある時は AppService にデプロイしなくても SideCar 使えば良かったんだと後から気付きましたw(泣
  • あ、Surface Pro 買えば良いのか(白目

f:id:rupic:20200226201646p:plain
SideCar で iPad を液タブとして使えるの忘れてたw

AR Fukuoka リアルタイム手書き数字認識AIを作って遊ぼうに参加してきました!

こんにちは → こんばんは!!
今日はエンジニアカフェでこちらに参加、少しだけお手伝いしてきました😊

xr-fukuoka.connpass.com

f:id:rupic:20200215131833p:plain エンジニアカフェでイベント開催すると黒板にイラストつきな紹介を書いてくれてるんですが、何気にこれ嬉しいですよね!!

今回イベントに参加しながらブログを書いているので、ちょっと長めになっちゃってます🙇🏻‍♂️

イベントの雰囲気

f:id:rupic:20200215132053p:plain
師匠によるいつもの挨拶

f:id:rupic:20200215132206p:plain
IST 甲斐さんから今日やることのご紹介

ディープラーニングとは

f:id:rupic:20200215133007p:plain
ガッキーは可愛いので正義

本日のお題

Tensorflow + Keras + Opencv を利用して、PCのカメラで撮影した手書き文字をリアルタイムで認識

  • 難しい事はさておいて、ざっくり説明すると Tensorflow はゲームのハード、Keras がゲームソフト

まずはMNISTで遊んでみよう

MNISTをKerasで使える形にする

import numpy as np
from keras.datasets import mnist
from keras.utils import np_utils

#MNISTデータセット呼び出し
(X_train, y_train), (X_test, y_test) = mnist.load_data()

#データの正規化
X_train = np.array(X_train)/255.
X_test = np.array(X_test)/255.

#CNNに入れるために次元を追加
X_train = np.expand_dims(X_train, axis=-1)
X_test = np.expand_dims(X_test, axis=-1)

#ラベルのバイナリ化
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)

モデルの構築

from keras.layers import Input, Dense, Conv2D, MaxPooling2D
from keras.models import Model
from keras.layers.core import Flatten

inputs = Input(shape=(28,28,1)) #入力層

conv1 = Conv2D(16, (3, 3), padding='same', activation='relu')(inputs) #畳込み層1
pool1 = MaxPooling2D((2, 2))(conv1)
conv2 = Conv2D(32, (3, 3), padding='same', activation='relu')(pool1) #畳込み層2
pool2 = MaxPooling2D((2, 2))(conv2)

flat = Flatten()(pool2) #全結合層に渡すため配列を一次元化
dense1 = Dense(784, activation='relu')(flat) #全結合層

predictions = Dense(10, activation='softmax')(dense1) #出力層

model = Model(inputs=inputs, outputs=predictions) #モデルの宣言(入力と出力を指定)

モデルのコンパイル、学習実行

model.compile(optimizer="sgd", loss="categorical_crossentropy", metrics=["accuracy"])
hist = model.fit(X_train, y_train, batch_size=16, verbose=1, epochs=1, validation_split=0.9)

学習結果の評価

score = model.evaluate(X_test, y_test, verbose=1)
print("正解率(acc):", score[1])

モデルを保存

model.save("MNIST_test.h5")

作成されたモデルの確認

f:id:rupic:20200215135707p:plain
モデルが作成された!

f:id:rupic:20200215140253p:plain

作成した学習モデルを利用して評価する

from keras.models import load_model
import cv2
import numpy as np

model = load_model("mnist.h5") # 学習済みモデルをロード

Xt = []
img = cv2.imread("6.jpg", 0)
img = cv2.resize(img,(28, 28), cv2.INTER_CUBIC)

Xt.append(img)
Xt = np.array(Xt)/255
Xt = np.expand_dims(Xt, axis=-1)

result = model.predict(Xt, batch_size=1)
print(result[0])

画像加工処理をやってみよう

OpenCVを利用して、撮影している画像をリアルタイムにKerasで認識できる形に加工する

f:id:rupic:20200215140837p:plain
こんなイメージ。分かり易い!

f:id:rupic:20200215142807p:plain
皆さん真剣に聴かれてますね!

まずはOpenCVを使って画像を表示してみる

f:id:rupic:20200215143715p:plain
界隈では有名人な Lenna さんの画像を利用

import cv2

#Lennaを召喚する呪文:0→グレースケール、1→カラー
image = cv2.imread('Lenna.png',1)
#testウインドウにLennaを召喚
cv2.imshow('test',image)
#何かしら操作があるまで待つ:0なので無限に待つよ
cv2.waitKey(0)
#ウインドウを破棄する
cv2.destroyAllWindows()
#Macの人は以下を入れとかないとエラー
cv2.waitKey(1)

f:id:rupic:20200215144434p:plain
実行するとこんな感じですね!美人さんです。

teratail.com

読み込んだ画像をグレーに変えてみる

*方法は簡単。2行ほど追記するだけ!

#グレースケールに変換
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)

#グレー用のウインドウを追加
cv2.imshow('gray',gray)

f:id:rupic:20200215150328p:plain
先ほどのカラーとグレースケールのLennaさんが表示されます。

次は画像に資格の枠を表示する

  • OpenCV は画像の左上を原点として座標を指定します。
  • 以下のコードを追加
#四角を表示する
cv2.rectangle(image,(50,50),(100,100),(255,0,0))

f:id:rupic:20200215150924p:plain
こんな感じに枠が表示されますね

画像に文字を書く

#(窓,書き込む文字列,座標,フォント,サイズ,色,太さ,線の種類)
cv2.putText(image,'Hello World',(0,30),cv2.FONT_HERSHEY_SIMPLEX,1,(255,255,255),1,cv2.LINE_AA)

f:id:rupic:20200215151804p:plain
日本語などの2バイト文字を表示する為には少し工夫が必要

画像を二値化→白黒反転→ブラーをかける

#大津の方法で二値化する
_, th = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)
#白黒反転
th = cv2.bitwise_not(th)
#ブラーを書ける
th = cv2.GaussianBlur(th,(9,9),0)

f:id:rupic:20200215152420p:plain
二値化した画像

f:id:rupic:20200215152538p:plain
白黒反転した画像

f:id:rupic:20200215152650p:plain
ブラーをかけた画像

画像をトリミング

th = th[50:150,50:150]

f:id:rupic:20200215153043p:plain
指定したサイズにトリミングした画像

画像を縮小

#50 x 50 にトリミング
th = cv2.resize(th,(50,50),cv2.INTER_CUBIC)

f:id:rupic:20200215153226p:plain
指定サイズに縮小する

MNISTの中身をみてみよう

ここまでの内容を合体させて、先ほど利用したMNISTの中身の画像をOpenCVを利用して見てみましょう

from keras.datasets import mnist
import cv2

#MNISTを画像部分だけロード
(X_train,_),(_,_) = mnist.load_data()
#X_trainの100番目を取得して表示
cv2.imshow('mnist',X_train[100])

cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.waitKey(1)

f:id:rupic:20200215154008p:plain
指定した配列番号のもじが表示される

PC内臓のカメラの画像を表示する

  • PCのカメラにアクセスする為には、ご利用の環境によって段階を踏まないといけません。
    ① どのカメラを利用するか指定する
    ② カメラから画像を読み込む
    ③ カメラの画像を表示する
    ④ カメラを開放する

静止画

import cv2

#①カメラを指定
cap = cv2.VideoCapture(0)
#②カメラから画像を読み込む
ret,frame = cap.read()
#③カメラの画像を表示する
cv2.imshow('frame',frame)

cv2.waitKey(0)

#④カメラを開放する
cap.release()
cv2.waitKey(1)

cv2.destroyAllWindows()
cv2.waitKey(1)

f:id:rupic:20200215160523p:plain
油断してるとおかしな顔が取れるので気をつけてください

動画

  • OpenCVでは静止画をひたすら撮影→表示し続ける事で動画となるという思想なので以下で動画に対応できます
  • 終了判定がないと無限に動画が表示されてしまうので、今回はキーボードのQが入力されると終了するっていうコードに、静止画コードの②と③を変更します。
while(True):
    ret,frame = cap.read()
    cv2.imshow('frame',frame)

    #キーボードのQが押されたら終了させる
    k =  cv2.waitKey(1) & 0xFF
    if  k == ord('q'):break

動画の中心に四角を表示する

f:id:rupic:20200215162925p:plain
考え方はこちらです

while (True):
    ret,frame = cap.read()

    #サイズでかいとき対策として半分にする
    h,w,_=frame.shape[:3]
    frame = cv2.resize(frame, (int(w/2), int(h/2)))
    
    #画像の形(高さ,幅,チャンネル数)
    h,w,_=frame.shape[:3]
    #y軸の中心点を取得
    h_center=h//2
    #x軸の中心点を取得
    w_center=w//2
    
    cv2.rectangle(frame,(w_center-71,h_center-71),(w_center+71,h_center+71),(255,0,0))

リアルタイム手書き数字認識を作成する

  • ここまでくれば色々な事に応用できますね😊
  • 本日の本題であるリアルタイム文字認識を作成してみましょう!!
  • コツはPCのInカメラを利用した時に自分の動きが左右反転してしまうので、frame = cv2.flip(frame,1)で反転してあげると良い

完成コード

import cv2
from keras.models import load_model
import numpy as np

#学習モデルを読み込む
model = load_model('mnist.h5')

#カメラを指定する
cap = cv2.VideoCapture(0)

while (True):
    #判定用データを格納する変数
    Xt = []
    Yt = []
        
    ret,frame = cap.read()

    #サイズでかいとき対策として半分にする
    h,w,_=frame.shape[:3]
    frame = cv2.resize(frame, (int(w/2), int(h/2)))
    
    #画像の形(高さ,幅,チャンネル数)
    h,w,_=frame.shape[:3]
    #y軸の中心点を取得
    h_center=h//2
    #x軸の中心点を取得
    w_center=w//2
    
    cv2.rectangle(frame,(w_center-31,h_center-31),(w_center+31,h_center+31),(255,0,0))
    
    #トリミング(y,x)なので注意
    im=frame[h_center-30:h_center+30,w_center-30:w_center+30]
    #グレースケール化
    im=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
    #二値化
    _,im =cv2.threshold(im,0,255,cv2.THRESH_OTSU)
    #白黒反転
    im=cv2.bitwise_not(im)
    #ブラーをかける
    im=cv2.GaussianBlur(im,(9,9),0)
    #28 x 28 にリサイズする
    im=cv2.resize(im,(28,28),cv2.INTER_CUBIC)
    
    Xt.append(im)
    Xt = np.array(Xt)/255
    Xt = np.expand_dims(Xt,axis=-1)
    #Xtと学習したデータを比較して判定結果を返す
    result = model.predict(Xt,batch_size=1)
    
    #画面の反転
    frame = cv2.flip(frame,1)
    
    for  i in range(10):
        #小数点以下第二位までで四捨五入
        r = round(result[0,i],2)
        #OpenCVで表示できる型に変換
        r  = "{0:0.2f}".format(r)
        #その数字が何かと一致する確率をセットで格納
        Yt.append([i,r])
        #Ytをソートする
        Yt = sorted(Yt,key=lambda x:(x[1]))
    
    #判定結果の上位1番目を画面上に表示する
    cv2.putText(frame,'1st:'+str(Yt[9]),(10,50),cv2.FONT_HERSHEY_SIMPLEX,0.8,(255,255,255),1,cv2.LINE_AA)
    
    cv2.startWindowThread()
    cv2.imshow("frame",frame)
    cv2.imshow("debug",im)
    key = cv2.waitKey(30) & 0xFF
    if key ==ord('q') :
        break

#カメラを開放する
cap.release()
#フリーズ対策としてwaitKeyで挟む
cv2.waitKey(1)
cv2.destroyAllWindows()
cv2.waitKey(1)

f:id:rupic:20200215171908p:plain
完成!!!
f:id:rupic:20200215172659p:plain
画面キャプチャはこんな感じ

まとめ

  • 甲斐さんの説明がめっちゃ丁寧だし分かり易いし感動!!!!
  • やっぱりガッキーは可愛い
  • ブログ書きながらは忙しすぎてしんどい事が判明w
  • いち早くビール飲みたい

祝 xAI Meetup #1 開催しました

こんばんは!
2月7日(金)はxAI Meetupの記念すべき第一回の開催でした!

f:id:rupic:20200209110603j:plain

xai.connpass.com

xAI Meetupって?

f:id:rupic:20200209105814p:plain

  • 参加してくれた全員にAIという物をもっと身近に感じてもらいたい。
  • もっと簡単なものだと知ってもらいたい。
  • そして、目的としてのAIではなく、手段としてのAIを体験してもらいたい。
  • AI開発のプロ達とAIを使う方達が交流する事で、それぞれのレイヤーの方達に気づきがあれば。

そんな想いで2019年末に立ち上げたコミュニティです。

xai.connpass.com

会場の様子

f:id:rupic:20200209110519j:plain
大盛況!!

タイムテーブル

f:id:rupic:20200209111220p:plain
スピーカー陣も内容も豪華!!

セッション

既にブログにまとめて頂いているのでこちらをご覧ください🙇🏻‍♂️
本当に有難う御座います!!

木村さんのブログ

showm001.hatenablog.com

うしまるさんのブログ

usimaru.net

Twitter まとめ

togetter.com

Welcom to xAI Meetup

石橋裕太

参加頂ける全ての人のAIに対する『わからない』を解決する事で、AIというものをもっと身近に感じてもらいたいという事、xAIでこれからやっていきたい事をお話しさせて頂きました。

www.slideshare.net

Alibaba Cloud ET Brain

知念裕子さん

知念さんはAribaba Cloud のMVPアワードを受賞されており、Aribaba CloudのAIサービスであるET Brainについてご紹介頂きました!!
中でもET City Brainはリアルタイム都市データを利用した事故渋滞検出なんかできて、すごく気になるサービスだと思いました。

jp.alibabacloud.com

RC Car x AI

山田美穂さん

急遽登壇をお願いしたのにも関わらず、快諾して頂き有難う御座います🙇🏻‍♂️
RC Car(ラジコン)にラズパイとカメラを搭載し、人間がコントローラーで運転した内容を学習させてコースを自動走行させてみたお話しです!
めっちゃ興味あるのでうちの財務大臣に稟申しよう。。。(切実

speakerdeck.com

テキスト書き起こし&読み上げLINE ボット

松本典子さん

いつも思うのは松本さんってAIに関わらず、テクノロジーを利用する人たちの理想の姿だと思うんです。
すごい技術も限られた人たちだけが使えるってのは本当の意味で普及したと言えないと思うので、こんな風にもっとみんなが自然と技術を利用できる事ができる様になれば幸せだなと思います。

www.slideshare.net

資本論 x AI

阿久沢崇さん

資本論!!!!
AI開発の導入は儲かる事を考える事が大前提です。
機械学習を利用した案件は比較的シンプルなものが多く、エンジニアの方も企画〜プロジェクトに関わっていく事で無駄な手戻りをなくす事が出来るので是非!
資本論については池上彰先生の著書がわかりやすくておすすめです!!!

池上彰の講義の時間 高校生からわかる「資本論」

池上彰の講義の時間 高校生からわかる「資本論」

www.slideshare.net

Container x AI

加藤司さん

AIの利用を想定したインフラ周り+ガ●ダムをCustom Visionで判定するお話し。
機械学習を利用したプロジェクトというと一般的にモデル開発の方に注目しがちですが、しっかりとしたインフラが基盤にあってからこそ成り立つと思います。

www.slideshare.net

まとめ

f:id:rupic:20200209110803j:plain
記念撮影
記念すべき第一回開催は大盛況で幕を閉じました!!!!

今回、平日夜の勉強会開催にも関わらず、connpassでイベントをアップしてすぐに満席になり、キャンセル待ちも発生するというなんともありがたいと思うと同時に、この分野の勉強会には皆さん興味があるのだと驚きました。
次回ももっと楽しんで頂ける様にイベントを企画したいと思います!!

参加して頂いた皆様、運営、登壇して頂いた皆様、本当に有難う御座いました!
これからもよろしくお願い致します🙇🏻‍♂️

Fukuoka.NET #18 ~ようこそ令和2年~ 開催しました

こんばんは。
水曜、木曜と久々の高熱で死んでましたが、点滴と栄養ドリンクといっぱいの睡眠で復活しました僕ですw
年に 1 〜 2 回くらいしか風邪ひかないので久々にしんどかったです😢

本日、1月17日(金) は今年初のふくてんでした㊗️

場所はエンジニアカフェのメインホールをお借りしての開催です! fukuten.connpass.com 資料はこちら fukuten.connpass.com

あけましておめでとうございます

f:id:rupic:20200118013534p:plain
ゆる〜とオープニング

ふくてんはまず始めに自己紹介タイムがあります!

誰かの前で何かを話す事はすごくドキドキしますが、それが自己紹介だったとしても自身がつくし、なおかつ参加者同士の属性がわかるとお互いに話すきっかけになったりしていいですよね😊

f:id:rupic:20200118013934p:plain
自己紹介タイムです

今回初参加の方が3名ほどいらっしゃいました!
なんとも嬉しい限りです!ありがとうございます(^^)

タイムテーブル

f:id:rupic:20200118015730p:plain
20分 * 3、10分 * 1、LT * 1 ですね!

改めてC#でできることを振り返る

オルターブース 松村さん f:id:rupic:20200118015609p:plain

いまC#でできることをまとめたお話。
その中でも端末に .NET Core Runtime をインストールしなくても実行可能な
Self contained deployment
Windows Sandbox を起動し、実際に.NET Core Runtime が インストールされていない環境で動作する事を確認できる Demo は参加者の反応も良かったと思う。

www.slideshare.net

既にお兄さんのブログも公開されてますので詳細はこちらです! tsubalog.hatenablog.com

ML.NETのお話し

トヨタ自動車九州 石橋

f:id:rupic:20200118022248p:plain
木村さんの写真お借りました!ありがとうございます!!

タイトルをつけ忘れた事にセッション始まってから気づくなんて何とも申し訳ないw
『今すぐ試せる!既存のソリューションにML.NETで機械学習を実装してみよう!!』 的な感じでしょうか。 ML.NETの紹介と、Visual Studio 2019 から Model Builder を利用した画像分類の Demo をさせて頂きました😊
資料はこちら

Ml.NET from ru pic
www.slideshare.net

xAI Meetup #1 はこちら! xai.connpass.com

.NET Frameworkで​C# 8って使える?​YESとNO!

ジェイエムテクノロジー ジョニーさん f:id:rupic:20200118023257p:plain

『新規開発なのに .NET Framework 4.x? もう2020年ですよ!』と、現在.NET Framework の新規開発案件を担当されているジョニーさん😢
C# 8.0 の新機能 + .NET Framework でもC# 8.0 を利用する為に試行錯誤された内容を発表されていました!ジョニーさんすごいなぁ😲

f:id:rupic:20200118023701p:plain
C# 8.0 互換性表

Serverless Framework で C# を使ってみる

オルターブース 木村さん

f:id:rupic:20200118024706p:plain
木村さんは1月からオルターブースさんにJoinされました㊗️

木村さんは前職で主にperlPHPpythonといったいわゆるスクリプト系言語での開発をされていらっしゃったとの事で、C#はハンズオン程度で利用したことがある程度。

これからは業務で使うことが増えていくので、しっかりC#自体を勉強すると共に、最新動向もキャッチアップしていかないといけません。そのためには登壇を申し込み、それに向けて必死でネタを仕込むというのが一番です(スパルタですが)。

さすがすぎます🥰

今回は、ServerlessFramework で C# を使ってみてアレコレハマったところや、解決できたところを非常に丁寧にわかり易くご紹介して頂きました!!

www.slideshare.net

しかも誰よりも早くブログも書いて頂きました!
ありがとうございます😊
showm001.hatenablog.com

Xamarin で人生が変わった話

放浪軍師さん

f:id:rupic:20200118030932p:plain
放浪軍師さんのLT!!

f:id:rupic:20200118031222p:plain
放浪軍師さんをggると確実に1つの時代を気付き上げてきた感じが伝わりますね

というのはおいといて、
元々放浪軍師さんの努めてらっしゃった会社はゴリゴリの vb6 開発をされていたみたいで、独学でXamarin を勉強し始めたとの事。
その内容をブログに上げながら学習していたがそもそも何をどうしていいのかすらわからない。
勤めている会社と意見が合わず色々揉めてたりしている中、そんな時に軍師さんを助けてくれたのがコミュニティやブログを購読してくれてた方達だったそうで、Xamarin をどんどん覚えて行くうちに最終的には転職までしちゃいました!というサクセスストーリーでした😊

コツコツと愚直に何かができる人の事は必ず誰かが見れくれているし、認めてくれるんだなと感動しました㊗️いい話が聞けて良かったー!本当ありがとうございました!!

次回の JXUG は以下から申し込み可能です! jxug.connpass.com

まとめ

今年初めてのふくてんは参加人数は少ないながらも内容は濃かった様な気がします。っていうか自分的にすごく楽しかった!!
本当はもっと .NET を使ってらっしゃる企業さんとかあると思うので是非そんな方達ともお話ししてみたいなと思うけどそんな方にどうアプローチしていいものか、今の自分には思いつかないのでどなたかご紹介ください。

次回のふくてんは2月20日(木) 19:00 〜 開催です!

fukuten.connpass.com

お待ちしてます😊

2020年!開けましたので…

あけましておめでとうございます!
今年もよろしくお願い致します🙇🏻‍♂️
皆様にとってより良い一年になります様に😊

f:id:rupic:20200101005359p:plain
今年は年男なので、嫁が書いてくれたw

今年の目標

  • xAI Meetup を盛り上げるべく色々企画する
  • 毎月ふくてんを開催
  • 積極的に登壇する
  • 今年は北九州でもイベント開催できたらいいな
  • ブログ月3で書く
  • 長女のランドセルを全力で探す
  • 自転車再開して体重を減らす

色々やりたいけど楽しむ事が一番だと思うので、のんびり頑張ります😊

2019年を振り返って・・・

おはようございます☀️
晦日です!!
2019年も残すところあと1日となりました。
公私共にいろんな事があった年だったので、一年の振り返りをしたいと思います。

家族のこと

  • 長男:7歳 → 小学校に入学 / マイクラしたいらしいので ノート PC を購入 / Twitter デビューでタイピングの練習を始めるw / わからない事はぐぐる事を覚える / 子供は好きな事に対しての適応力が半端ないw / 最近の流行りは家から30秒の古墳跡地に友達と秘密基地を作る事。怪我だけはしないで欲しい…
  • 長女:4歳 → 年中さん / 女の子という事もありとにかくよく喋るw / 好きな事に対しての集中力が半端ない。/ 頭の中は年中お姫様。/ 平仮名、カタカナでの読み書きと本を読む事をマスターした模様で最近は次女に本読んであげてるのが可愛いw
  • 次女:1歳 → 末っ子で上二人がよく喋る事もあり、既に普通に喋るw 「ママ、チーズ食べたい」とか急に言い出してるのみた時は天才かよと思った。/ 性格はやんちゃで男の子みたい。/ パパにもう少し寄ってきて欲しい😢まじでw

    仕事で帰宅が遅かったり、勉強会や飲み会で家を開ける事が多いのに理解してくれて、いつも協力してくれる家族に本当に感謝しかない。
    f:id:rupic:20191230203812p:plain
    いつもありがとう😊来年も家族みんなが健康に過ごせます様に🥰

仕事のこと

2019年1月〜自身が所属する部が、品質管理部 品質企画室 → 塗装•組立エンジニアリング部 検査エンンジニアリング室に変更になりました。名前が長くて覚えきれません。
個人が異動したわけではなく、元の部署全員+生技部署が合体した感じだったので、当初は何が変わったかと言われると、良く分からないけど部の名前とロケーションが変わったくらいかな?くらいに思ってましたwww
でも1年間たってみて今までの職場に比べると他部署との関わり方が変わってやりづらい気はするが、関係する部署の人達が増えた事で得られる情報量が多くなったかなと。
正直、今の職場では自分の立ち位置がよくわからないっていうのもあるので来年はそこら辺を明確にできたらいいな。。

お手伝いさせて頂いている次世代室については、6月には天神イムズ地下 1F に Garraway F のオープンがあり学生さんや初めましてな方との交流が増えた事、恐らく社内では初の Microsoft Azure を利用した Webサイトの構築ができた事、かなり充実した1年になったと思う。
もし、来年も引き続きお手伝いできる事があれば積極的に関わっていけたらなと思う。

garrawayf.com

コミュニティのこと

Fukuoka.NET(ふくてん)

fukuten.connpass.com

Visual Stuudio 2019 Launch Event」、「Fukuoka.NET Conf 2019」とグローバルカンファレンスのローカルイベントを開催し大勢の方にご参加頂けた事、「Microsoft Learn」を利用したもくもく会を始め、初学者の方もふくてんに参加しやすい様な取組を行いました。

Visual Stuudio 2019 Launch Event rupic.hatenablog.com

Fukuoka.NET Conf 2019
rupic.hatenablog.com

f:id:rupic:20191231002545p:plain
無事?オダショーさんにエンカウントしてヨーヨーもGetできました!
来年も引き続き大勢の皆様のご参加をお待ちしています🙇🏻‍♂️

最近、福岡で開催されている勉強会に行くと「ふくてんの方ですよね!」と声をかけて頂けることが増えまして、皆さんにふくてんを知って頂けているという事がとても嬉しいです😊

ふくてん以外での活動

今年は出来るだけ色んなイベントに参加させて頂き、積極的に登壇やLTもさせて頂きました。
というのも、福岡には本当に色々なITコミュニティがあって毎日の様にどこかで勉強会が開催されています。
自分が興味がある事に対して、本やネットだけで知識を得るよりも勉強会などに参加して参加されている方とお話しする事で自身の刺激になると思うし、誰かの前でお話しするって事は実際に手を動かして資料を纏めて…という事をやらないとなので、さらに理解が深まると。

f:id:rupic:20191231004858p:plain
くまあずで登壇させて頂いた時の写真!またお邪魔します😊
f:id:rupic:20191231005026p:plain
ALGYANに参加させてもらいReButtonのハンズオン!楽しかったー!!
f:id:rupic:20191231005402p:plain
AR_Fukuoka ハンズオンのお手伝い!
f:id:rupic:20191210003412j:plain
Fukuoka.LT では運営の方達とたくさんお話しできて楽しかった😊
こちらに関しても来年も引き続き継続していきたいと思っています!!
どこかで見かけた時は、話しかけて頂けるとめっちゃ喜びます😁

新しいコミュニティの立ち上げ

先日、Facebookの方でお知らせさせて頂きました。
xAI Meetup です。

xai.connpass.com

お手軽 AI

福岡で AI 系のコミュニティーは既にあるのですが、もう少し身近に利用できる AI として Cognitive Services や LINE Brain 、Amazon Rekognition などのサービスを利用して、自分の得意な分野に少しだけ Intelligence な機能をかけあわせてみたってやつもあってもいいのかなと。
色々なコミュニティとの共同開催っていうのもやりやすいと思うので是非よろしくお願い致します。

やりがい

僕は普段会社で知らない誰かに評価してもらう事、認めてもらう事ってほとんどないんです。
勉強会で登壇したり、ブログ書いたり、活動を行っていく上で色んな人が僕の意見に共感してくれたり、認めてくれるんです。…多分。
そういう時はすごく嬉しいし、ドキドキします。ワクワクします。
そんな体験をできる様なコミュニティにしたいと思いますし、純粋に楽しんで頂ける様なコミュニティになればと思います。

イベント

1月末〜2月頭くらいに初のイベントを予定しています😊 connpassでグループを作成していますので、皆さんのご参加お待ちしています。
登録して頂けると嬉しいです!

まとめ

ざっくりとしか書けていませんが、楽しい事もいっぱいありましたし悲しい事もありましたが、今年一年は本当に色々な方に出会い、たくさんお話をする事ができました。
色々な体験ができた事で自分自身としても昨年よりも成長できたのかなと思います。

今年一年お世話になった皆様、助けて頂いた皆様、本当にありがとうございました。
来年も是非よろしくお願い致します🙇🏻‍♂️
良いお年を!!

Custom Vision と Tensorflow2.0 を比べてみました

この記事は Azure AI Advent Calendar 2019 の 22日目の記事となります。

qiita.com

AI を使ってみたいけど…。

近年 AI 技術の急速な発達にともない、様々な AI サービスが誕生しており機械学習ディープラーニングなどの技術により、AI は様々な分野で活用されるようになっています。
こういった流れの中で自社でも AI を活用した事例を増やしたいと思うのは自然な事だと思いますが、 実際に開発を行うとなると非常に専門的な知識が必要だったり、コストがかかったり…
ちょっとやってみようレベルでは中々と敷居が高いかと思います。

Cognitive Services がおすすめ

Cognitive Services は人間の認知する機能の一部を機械で擬似的に再現したサービスで Web API として利用できる AI パーツ。

Microsoft Azure のサービスの中で一番好きなサービス😊
だって、Web App でも、IoT でも、Logic Apps でも、Bot でも、XRでも、VUI でも何でも繋げることが出来るなんて素敵すぎると思いませんか?

2019.12.22 現在のサービス一覧 (27 Services ※5 preview)

  • 決定:よりスマートな意思決定をすばやく行う
  • 言語:非構造化テキストから意味を取り出す
    • Immersive Reader(preview):音声や視覚的な合図を使用して、あらゆる能力の読み手がテキストを理解できるようにする
    • Language Understanding自然言語を解釈する機能を提供
    • QnA Maker:ナレッジベースから質疑応答を、会話形式で行うことができる
    • Text Analytics:センチメント、キー フレーズ、名前付きエンティティを検出
    • Translator Text:60 を超えるサポート言語を検出し、リアルタイムで翻訳
  • 音声:音声処理をアプリやサービスに統合する
  • 視覚:画像、ビデオ、デジタル インク内のコンテンツを識別し、分析する
    • Computer Vision:画像内のコンテンツを分析
    • Custom Vision:ビジネス上のニーズに合うよう画像認識をカスタマイズ
    • Face:イメージ内の人物とその感情を検出して識別
    • Form Recognizer(preview):ドキュメントからテキスト、キーと値のペア、テーブルを抽出
    • Ink Recognizer(preview):デジタル インクや手書き入力を認識し、一般的なシェイプの位置特定
    • Video Indexer:ビデオのビジュアルとオーディオ チャネルを分析し、そのコンテンツにインデックスを設定
  • 検索:World Wide Web から探しているものを見つける
    • Bing Autosuggest:インテリジェントな先行入力機能が追加され、ユーザーはよりすばやくクエリを入力する
    • Bing Custom Search:検索結果に広告が表示されないカスタムの検索エンジンを作成
    • Bing Entity Search:名前付きエンティティを識別して分類し、それに基づいて検索結果を探す
    • Bing Image Search:さまざまな画像検索オプションをアプリに追加
    • Bing News Search:アプリをニュースの検索リソースに変更
    • Bing Spell Check:ユーザーが単語の切れ目、スラング、名前、同音異義語、ブランドを識別して修正
    • Bing Video Search:高度なビデオ検索機能をアプリに追加
    • Bing Visual Search:ユーザーが画像を使用して検索
    • Bing Web Search:安全で広告なしの、位置認識機能を備えた検索をユーザーが利用できるようにし、Web の検索結果、画像、ニュース、ビデオ、ビジュアルから関連情報を表示

実際ににどうなの?

プレビルドのモデルを活用する事を不安に思われる方もいらっしゃるかもしれません。
なので、AI技術の中でもっとも実用化が進んでいる画像認識をPython+TensorFlowとCustom Vision で比べてみました。

Dataset

何でも良かったのですが、せっかく?なので車を使ってみました。 学習枚数はそれぞれ10枚、テストに10枚です。

f:id:rupic:20191222125826p:plain
こんな感じ

datasetsーー test
    |   ∟ ES                 
    |  |
    |   ∟ RX 
    |  
    ∟_ train
        ∟ ES                 
       |
        ∟ RX 

Tensor Flow 2.0

ネットワークは ResNet の 50 層を使ってみます。

def Res50():

    def ResBlock(x, in_f, f_1, out_f, stride=1):
        res_x = Conv2D(f_1, [1, 1], strides=stride, padding='same', activation=None)(x)
        res_x = BatchNormalization()(res_x)
        res_x = Activation("relu")(res_x)

        res_x = Conv2D(f_1, [3, 3], strides=1, padding='same', activation=None)(res_x)
        res_x = BatchNormalization()(res_x)
        res_x = Activation("relu")(res_x)

        res_x = Conv2D(out_f, [1, 1], strides=1, padding='same', activation=None)(res_x)
        res_x = BatchNormalization()(res_x)
        res_x = Activation("relu")(res_x)

        if in_f != out_f:
            x = Conv2D(out_f, [1, 1], strides=1, padding="same", activation=None)(x)
            x = BatchNormalization()(x)
            x = Activation("relu")(x)

        if stride == 2:
            x = MaxPooling2D([2, 2], strides=2, padding="same")(x)
        
        x = Add()([res_x, x])

        return x
        
    
    inputs = Input((img_height, img_width, channel))
    x = inputs
    
    x = Conv2D(64, [7, 7], strides=2, padding='same', activation=None)(x)
    x = BatchNormalization()(x)
    x = Activation("relu")(x)
    x = MaxPooling2D([3, 3], strides=2, padding='same')(x)

    x = ResBlock(x, 64, 64, 256)
    x = ResBlock(x, 256, 64, 256)
    x = ResBlock(x, 256, 64, 256)

    x = ResBlock(x, 256, 128, 512, stride=2)
    x = ResBlock(x, 512, 128, 512)
    x = ResBlock(x, 512, 128, 512)
    x = ResBlock(x, 512, 128, 512)

    x = ResBlock(x, 512, 256, 1024, stride=2)
    x = ResBlock(x, 1024, 256, 1024)
    x = ResBlock(x, 1024, 256, 1024)
    x = ResBlock(x, 1024, 256, 1024)
    x = ResBlock(x, 1024, 256, 1024)
    x = ResBlock(x, 1024, 256, 1024)

    x = ResBlock(x, 1024, 512, 2048, stride=2)
    x = ResBlock(x, 2048, 256, 2048)
    x = ResBlock(x, 2048, 256, 2048)

    x = AveragePooling2D([img_height // 32, img_width // 32], strides=1, padding='valid')(x)
    x = Flatten()(x)
    x = Dense(num_classes, activation='softmax', name='out')(x)

    model = Model(inputs=inputs, outputs=[x])

    return model

f:id:rupic:20191222130542p:plain
うーん…な感じですがまぁ良いでしょう。

適当な画像を与えてみます。

f:id:rupic:20191222133233p:plain
LEXUS ES

f:id:rupic:20191222133755p:plain
いい感じにESです(ラベル 0 がES)

Custom Vision

www.customvision.ai

Custom Vision は上記のサイトから新しいプロジェクトを作成して画像をアップロードしてあげる事で簡単にモデルの構築が出来ます。テストも同じサイトから可能。

f:id:rupic:20191222134437p:plain
プロジェクトを作成

f:id:rupic:20191222134724p:plain
画像アップロード時にタグ付けします

f:id:rupic:20191222134817p:plain
右上にあるTrainをクリックする事で学習してくれます

結果的には少し不安ですが、このままいってみます。
今回は quick train で学習したんですが、学習時間を指定してモデルの精度をあげる事もできます。

f:id:rupic:20191222135103p:plain
しっかり ES と判断してくれてますね

f:id:rupic:20191222135148p:plain
これくらいきわどい画像でもしっかり判定してくれてます(RXで正解)

まとめ

最終的な精度だけ見てみるとそんなに変わらないですが、何といっても学習にかかる手間や時間が大幅に短縮されます。※Custom Visionの学習時間は 10 秒程度
学習したモデルを Export して自身のアプリケーションに実装する事も可能なので、ちょっとやってみたいって時にすぐに実装する事ができますね😊

AI を活用したサービスは画像だけでなく、

  • 自社で溜めた知見を元にユーザーからの問合せをチャットボットで行ったり
  • 問合せの内容からセンチメントを分析したり
  • カメラで撮影した画像を判定したり
  • 会話の中から話している言葉や人の特定を行ったり

それはもう何でも AI でいい感じに出来るんじゃないか?なんて思ってしまいがちですが、AI は決して銀の弾丸や魔法の杖ではありません。

課題設定がしっかりできている事、事実に基づいた質の良いデータが十分にある事、しかるべき場所に実装する事で威力を発揮してくれる物なので特性を理解してしっかり利用していきたいですね!