Google Colaboratoryでファイルを読み込む

Google Colaboratoryでファイルを読み込む

前回のエントリの続きになります。 uepon.hatenadiary.com

前回はGoogleColaboratoryを設定してみたものでした。ただ、これでは画像ファイルを認識させるために読み込ませるようなことができません。ネット上のサーバに置いておくという手もありますが、それもまた面倒です。ということで、その方法をググって見ました。

以下のエントリを参考にしています。ありがとうございます。

qiita.com

ファイルをインスタンスにアップロードする

簡単な方法はpythonのコードでnotebook上にUIを作り出し、ファイルをアップロードするというものです。 以下のようなコードを書いて実行すると…

from google.colab import files
uploaded = files.upload()

f:id:ueponx:20180321135030p:plain

notebook上にアップロード操作用のUIが現れます。 【ファイルの操作】ボタンをクリックするとファイル選択のダイアログが表示されますのでファイルを選択して【OK】をクリックしてください。

f:id:ueponx:20180321135511p:plain

では以下のようなCSV(郵便番号データ)を読み込ませてみます。ファイル名は23AICHI.CSVとなります。

www.post.japanpost.jp

f:id:ueponx:20180321140407p:plain

ファイル選択のダイアログでファイル名を選択し【OK】ボタンをクリックすると以下のようにnotebookが表示が変更します。

f:id:ueponx:20180321141038p:plain

この操作では一応タイムアウト機能もあるようです。再生ボタンの周りがくるくる回っている間は問題ありませんが、タイムアウト後にファイルのアップロード操作を行おうとすると以下の様にアップロード状況などは表示されないようです。(ボタンなどを操作するとファイル操作ダイアログは表示され、ファイルの選択は行われますが、それ以上なにかされることはありません。)

【処理待ちの状態】 f:id:ueponx:20180321141528p:plain

タイムアウト後に操作してもなにも処理は発生しない】 f:id:ueponx:20180321140834p:plain

ファイルが正常にアップロードされたかを確認するにはshコマンドで以下の様に実行すれば無事にアップロードされていることがわかります。

!ls -l

f:id:ueponx:20180321141218p:plain

ちなみにインスタンスのデフォルトではlessコマンドはありませんし、moreコマンドでもエンコードShift_JISのため?文字化けしています。

f:id:ueponx:20180321141939p:plain

では、参考にしたエントリではcsvファイルをpandasで読み込ませていたので自分もやってみます。(日本語なのでかなり不安ですが)

pandasは初めて使うので調べてみました。

Pandasは、プログラミング言語Pythonにおいて、データ解析を支援する機能を提供するライブラリである。特に、数表および時系列データを操作するためのデータ構造と演算を提供する。PandasはBSDライセンスのもとで提供されている。

Python Data Analysis Library — pandas: Python Data Analysis Library

ってことらしい、入力時の面倒くささを解決してくれるような感じなのかな? では実験です。

import pandas as pd
import io
data = pd.read_csv(io.StringIO(uploaded['sample.csv'].decode('utf-8')), header=-1)
data.head()

f:id:ueponx:20180321143454p:plain

おや?文字化けはするかなと思っていたのですが、エラーが発生しました。それも原因がファイル名。とはいえErrorが出たらStackOverFlowに検索にいけボタンがあるというのはすごいですね。

CSVファイルのエンコードutf-8にしてしまえば、正常にいくかと思えば全く同じ状況でした。 怪しいのはファイル名の頭にある数字?あと小文字化も必要なのかなってことで以下の様にファイル名を変えてみました。 23AICHI.CSV → aichi.csv

これを変更してみると…moreも正常になりました。なんだろこれ?

f:id:ueponx:20180321145428p:plain

先程のpandasのコードも以下のように修正して実行すると

import pandas as pd
import io
data = pd.read_csv(io.StringIO(uploaded['aichi.csv'].decode('utf-8')), header=-1)
data.head()

f:id:ueponx:20180321150543p:plain

うまくいきました。こんなところで引っかかるなんて…

【追記】

以下とすれば問題ありませんでした。

import pandas as pd
import io
data = pd.read_csv('23AICHI.CSV', header=-1)
data.head()

GoogleDriveにあるディレクトリをマウントする

インスタンスへのディレクトリのマウントにはFUSE filesystem over Google Driveというものを使用します。

github.com

要約するとGoogleDriveのディレクトリをLinuxファイルシステムにマウントするもののようです。

以下参考リンク。

stackoverflow.com

https://colab.research.google.com/notebook#fileId=1srw_HFWQ2SMgmWIawucXfusGzrj1_U0q&scrollTo=c99EvWo1s9-x

↑これと同じことをそのまま行えばいいようです。

1)google-drive-ocamlfuseのインストール

インストールに必要となるパッケージも含めてインストールを行います。

# Install a Drive FUSE wrapper.
# https://github.com/astrada/google-drive-ocamlfuse
!apt-get install -y -qq software-properties-common python-software-properties module-init-tools
!add-apt-repository -y ppa:alessandro-strada/ppa 2>&1 > /dev/null
!apt-get update -qq 2>&1 > /dev/null
!apt-get -y install -qq google-drive-ocamlfuse fuse

【実行の様子】

f:id:ueponx:20180321155839p:plain

2)Colabratory用の認証トークンの生成

以下のコードを実行すると認証のリンクが表示され、リンク先に行って認証を行います。

# Generate auth tokens for Colab
from google.colab import auth
auth.authenticate_user()

【実行の様子】

実行するとリンクが表示されるので、それをブラウザの別タブで開き

f:id:ueponx:20180321161310j:plain

対象となるユーザを選択し、

f:id:ueponx:20180321161514p:plain

アクセスの許可を与えます。【OK】ボタンをクリックします。

f:id:ueponx:20180321161615p:plain

すると、認証トークンが発行されるので、これをコピーして

f:id:ueponx:20180321161751p:plain

colab側の入力ボックスにペーストしてEnterを押します。

3)Drive FUSE library用の証明書の生成

以下のコードを実行すると認証のリンクが表示され、リンク先で認証を行います。

# Generate creds for the Drive FUSE library.
from oauth2client.client import GoogleCredentials
creds = GoogleCredentials.get_application_default()
import getpass
!google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret} < /dev/null 2>&1 | grep URL
vcode = getpass.getpass()
!echo {vcode} | google-drive-ocamlfuse -headless -id={creds.client_id} -secret={creds.client_secret}

【実行の様子】

実行するとリンクが表示されるので、それをブラウザの別タブで開き

f:id:ueponx:20180321162502j:plain

対象となるアカウントを選択し、

f:id:ueponx:20180321162120p:plain

Google Driveのアクセス許可を与えます。【OK】ボタンをクリックします。

f:id:ueponx:20180321163044j:plain

すると、認証コードが発行されるので、これをコピーしてcolab側の入力ボックスにペーストしてEnterを押します。

f:id:ueponx:20180321163225p:plain

認証が終わると以下のような状態になります。

f:id:ueponx:20180321163405j:plain

4)インスタンスdriveというディレクトリを作り、そこにGoogle Driveをマウントする

# Create a directory and mount Google Drive using that directory.
!mkdir -p drive
!google-drive-ocamlfuse drive

print('Files in Drive:')
!ls drive/

# Create a file in Drive.
!echo "This newly created file will appear in your Drive file list." > drive/created.txt

※ リンクそのままではPython2系のprintを使用しているのでPython3系に合わせる編集をしています。

【実行の様子】

f:id:ueponx:20180321160448p:plain

5)マウントの確認

マウントされたGoogle Driveの状況をlsコマンドで確認してみるとこんな感じになります。

f:id:ueponx:20180321163545p:plain

無事にマウントが行われたようです。

では先程のpandasを使ったcsvをオープンするコードでも確かめてみます。予めGoogleDriveに先程使用したcsvファイルを保存しておきます。

f:id:ueponx:20180321164147p:plain

import pandas as pd
import io
data = pd.read_csv('drive/Colab Notebooks/aichi.csv', header=-1)
data.head()

【実行の様子】

f:id:ueponx:20180321164645p:plain

【注意点】 このマウントはインスタンスの永続限界時間である12時間を超えるとインスタンスが初期化されるためマウントも解除されてしまいます。 ファイルが削除されるわけではないので、再度インスタンスの接続を行ったらマウント処理を行うことを忘れないようにしてください。 アイドル状態が90分続くと停止しますが、12時間を超えていなければ大丈夫なのかなと思っています。

終わりに

tensorflowとKerasを使ってアップルとオレンジを判別するコードを実行してみます。

以前のエントリーで「AI Business Challenge Day:第4回 機械学習実践勉強会 画像認識技術ハンズオン」で使用したコードをGoogle Colabで動作させてみます。 画像はGoogle Driveに格納させています。

www.softopia.or.jp

uepon.hatenadiary.com

画像の格納されたパスの部分のみ変更していますが、基本的には同じです。

from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout
from keras.utils.np_utils import to_categorical
from keras.optimizers import Adagrad
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os

image_list = []
label_list = []

basePath = "drive/Colab Notebooks/" 

# トレーニングデータを読み込む
for dir in os.listdir(basePath + "data/train"):

    traindir = basePath + "data/train/" + dir
    if os.path.isdir(traindir) == False:
        continue

    label = 0              # 正解ラベル

    if dir == "apple":
        label = 0          # りんごの場合は、0
    elif dir == "orange":
        label = 1          # オレンジの場合は、1

    for file in os.listdir(traindir):
        if file != ".DS_Store":

            label_list.append(label)            # 正解ラベルを配列に入れる

            filepath = traindir + "/" + file  # ファイルパス

            resized_img = Image.open(filepath).resize((25, 25))                                                    # 画像を25x25にリサイズする
            image = np.array(resized_img)                                                                       # 25x25の2次元配列にする→[[R,G,B], [R,G,B]...]
            image = image.transpose(2, 0, 1)                                                                 # 配列を次元を変換する→[[R,R,R,...], [G,G,G,...], [B,B,B,...]]
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]     # 1次元配列に変換→[R,R,R,...,G,G,G,...,B,B,B]
            image_list.append(image / 255.)                                                            # 0.0〜1.0までの値にして配列に入れる

image_list = np.array(image_list)       # 画像リストをnumpy配列に変換

Y = to_categorical(label_list)          # 正解ラベルを配列にする(0→[1,0], 1→[0,1])

# 層を構築
model = Sequential()
# 入力層
model.add(Dense(200, input_dim=1875))
model.add(Activation("relu"))
model.add(Dropout(0.2))

# 隠れ層
model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

# 出力層
model.add(Dense(2))
model.add(Activation("softmax"))

# オプティマイザにAdamを使用
opt = Adam(lr=0.001)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# nb_epoch: 学習回数
# batch_size: 1度に処理する分量(GPUモードの際は、メモリ制限がある場合がある)
model.fit(image_list, Y, nb_epoch=1500, batch_size=100, validation_split=0.1)
# model.fit(image_list, Y, nb_epoch=10, batch_size=100, validation_split=0.1)

total = 0.
ok_count = 0.

for dir in os.listdir(basePath + "data/test"):
    
    testdir = basePath + "data/test/" + dir
    if os.path.isdir(testdir) == False:
        continue

    label = 0

    if dir == "apple":
        label = 0          # りんごの場合は、0
    elif dir == "orange":
        label = 1          # オレンジの場合は、1

    for file in os.listdir(testdir):
        if file != ".DS_Store":
            label_list.append(label)
            filepath = testdir + "/" + file

            resized_img = Image.open(filepath).resize((25, 25))    
            image = np.array(resized_img)
            image = image.transpose(2, 0, 1)
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]

            # 予測する
            print(filepath)
            result = model.predict_classes(np.array([image / 255.]))
            print("label:", label, "result:", result[0])

            total += 1.

            if label == result[0]:
                ok_count += 1.

print(ok_count / total * 100, "%")

【実行の様子】

f:id:ueponx:20180321175209p:plain

(略)

f:id:ueponx:20180321175408p:plain

問題なく動いたようです。 インスタンスのマシンスペックの凄さはあんまりわかりませんでしたが、セットアップをほとんどしなくてもここまでできるのは本当に嬉しいことです。

関連エントリー

uepon.hatenadiary.com uepon.hatenadiary.com

Google Colaboratoryを使ってみた

Google Colaboratoryを使ってみた

今年はじめに以下のようなニュースがでていました。気にはなっていたのですが試す時間がなかったので試してみました。 Google Colaboratoryが正しいのかGoogle Colabが正しいのか…

控えめに言ってもすごすぎ。その他のプラットフォームを殺すつもりできてるのかと思いました。

以下に説明というか、FAQページのリンクを貼っておきます。

Colaboratory – Google

簡単に説明すると、ChromeなどのブラウザからJupyter Notebook環境を使えて、更にGPUリソースが使えるという優れものでした。 もう、初学者はanacondaもインストールせずこれ一択でいいと思います。

このエントリーは

qiita.com

を参考に作成しています。

とりあえず使ってみる

とりあえず、使用してみましょう。インストールも何もいりません。Googleアカウントだけは必要でなので事前にログオンはしておきましょう。 以下のURLにアクセスすれば準備OKです。

Colaboratory – Google

アクセスすると以下のような画面になると思います。

f:id:ueponx:20180319233415j:plain

おめでとうございます!これでJupyter Notebook環境が使えることになりました!インストールばっかりしている自分にとってはこれはあまりにも衝撃的ですが、目的はインストールすることではないはずのでこれが理想的な姿だと思います。

ダイアログの下のほうにある【ノートブックの新規作成】をクリックします。

f:id:ueponx:20180319233912j:plain

すると、プルダウンで【PYTHON2の新しいノートブック】と【PYTHON3の新しいノートブック】が表示されます。もうPythonはVersion3でやっていきましょう!(今年の目標)

f:id:ueponx:20180319234142j:plain

【PYTHON3の新しいノートブック】を選びます。これだけでほぼ準備完了です。あとはpythonのコードを打ち込っていけばいいのです。 コードは画面内の入力ボックスに記入します。環境としてはJupyter NotebookなのでREPLの様に改行をしても実行はされません。

f:id:ueponx:20180319234732j:plain

打ち込んだコードを実行するには【Shift + Enter】または左側の再生ボタン(実行ボタン?)になります。まずは、Hello Worldを画面内に表示してみます。コードを行のところに

print('Hello World')

f:id:ueponx:20180320001208j:plain

と入力し、実行(【Shift + Enter】)してみます。以下の様に出力されます。 初回の実行にはインスタンスへの接続が行われるため時間はかかりますが、それ以降ではサクサクと実行されるのでご安心を。

実行中は下記のように表示が読み込み中のような表示になります。

f:id:ueponx:20180320001232j:plain

【実行結果】

Hello World

f:id:ueponx:20180320001249j:plain

実行は成功です。 続いてもう少しコードを書いてみます。今日の日付を出力するのですが、datetimeモジュールをimportをしている点が先程と違います。

import datetime

today = datetime.date.today()
print(today)

これを実行すると以下の様になります。

f:id:ueponx:20180320001500p:plain

通常のpythonの開発も問題なさそうですね。OSモジュールなども正常にインストールできていました。

あとはノートブックを保存してみましょう。作成時には【Untitled**.ipynb】というような名前でノートブックが作成されますのでブラウザの左上の名前の部分を編集します。

f:id:ueponx:20180320002128j:plain

今回は【sample.ipynb】という名前に変更してみました。入力ボックスに名前を入れれば作業は完了するのですが、保存されているのはgoogle colabの環境化になります。実際にはGoogleDriveに保存しておくのが普通だと思いますので、そちらに保存します。

f:id:ueponx:20180320002420j:plain

f:id:ueponx:20180320002727j:plain

メニューバーから【ファイル】→【保存】を選択します。

f:id:ueponx:20180320002828j:plain

すると自分のGoogleDriveのマイドライブに【Colab Notebooks】というフォルダが作成(すでにあれば再作成はされません)され先程つけた名前のipynbファイルが保存されます。これを読み込めば同じ状態から作業を始めることができるようになります。

f:id:ueponx:20180320002950j:plain

接続するインスタンスを調べてみる

先程、pythonの初回実行時にはインスタンスに接続することでpythonのプログラムが実行されるという話をしました。ではこのインスタンスに直接アクセスすることはできるのでしょうか。コマンド行にの先頭に!をいれてshコマンドを入れると実行することができるようです。

いろいろネットを調べてみるとこのインスタンス強力すぎます。

  • Ubuntu 17.10
  • n1-highmem-2 instance
  • 2vCPU @ 2.2GHz
  • 13GB RAM
  • 40GB Free Space
  • GPU NVIDIA Tesla K80

*1 90分間なにもしないとインスタンスはシャットダウンします。また、最大でも12時間しか使用できません。

上記のK80を無料で使えるのはすごい!(間違ってる?)ただ、通常に起動した場合にはGPUはOFFになっています。既に作成したnodebookでGPUをONにするには以下の作業が必要になります。

メニューバーから【ランタイム】→【ランタイムのタイプを変更】を選択します。

f:id:ueponx:20180320005852j:plain

すると以下のようなダイアログが表示されますので、【ハードウエアアクセラレータ】のプルダウン【None】から【GPU】に変更します。念のため【このノートブックを保持する際にコードセルの出力を除外する】にチェックをを付けておきます。設定が終わったら【保存】ボタンをクリックします。

f:id:ueponx:20180320005855j:plain

ページ再読み込みのダイアログが出ますので、ここも【再読み込み】ボタンをクリックします。

f:id:ueponx:20180320005858j:plain

これでGPUが有効になります。

f:id:ueponx:20180320005901j:plain

見た目にはほとんど変化はありませんが、先ほど実行したコードの実行結果が消えています。

f:id:ueponx:20180320010014j:plain

ではGPUがONになっているかを確認してみます。 確認方法はtensorflow経由となりますが、以下のようなコードで確認できます。

import tensorflow as tf
tf.test.gpu_device_name()

【実行結果:GPU設定あり】

'/device:GPU:0'

f:id:ueponx:20180320122650p:plain

問題なさそうです。

ちなみにGPUがNoneの場合には以下のような結果になります。

【実行結果:GPU設定なし】

''

f:id:ueponx:20180320123451p:plain

あとはpythonのバージョンなどを調べてみましょう。最初に言っていたインスタンスのshコマンドを実行する場合には冒頭に!をつけて実行を行います。 pythonのバージョンを調べるときにはpython --versionと打込むので、今回は以下のように入力します。

!python --version

【実行結果】

Python 3.6.3

f:id:ueponx:20180320124219p:plain

ということで、更にインスタンスのハードウエアスペックを見てみます。

!cat /proc/cpuinfo

【実行結果】

processor  : 0
vendor_id   : GenuineIntel
cpu family  : 6
model       : 63
model name  : Intel(R) Xeon(R) CPU @ 2.30GHz
stepping    : 0
microcode   : 0x1
cpu MHz     : 2300.000
cache size  : 46080 KB
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 1
apicid      : 0
initial apicid  : 0
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms xsaveopt
bugs        :
bogomips    : 4600.00
clflush size    : 64
cache_alignment : 64
address sizes   : 46 bits physical, 48 bits virtual
power management:

processor   : 1
vendor_id   : GenuineIntel
cpu family  : 6
model       : 63
model name  : Intel(R) Xeon(R) CPU @ 2.30GHz
stepping    : 0
microcode   : 0x1
cpu MHz     : 2300.000
cache size  : 46080 KB
physical id : 0
siblings    : 2
core id     : 0
cpu cores   : 1
apicid      : 1
initial apicid  : 1
fpu     : yes
fpu_exception   : yes
cpuid level : 13
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc eagerfpu pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms xsaveopt
bugs        :
bogomips    : 4600.00
clflush size    : 64
cache_alignment : 64
address sizes   : 46 bits physical, 48 bits virtual
power management:

f:id:ueponx:20180320010028j:plain

CPUがXeonなのか…

それではGPUの情報は…

!cat /proc/driver/nvidia/gpus/0000:00:04.0/information

【実行結果】

Model:          Tesla K80
IRQ:         33
GPU UUID:    GPU-9743217b-7464-7627-8efc-0f60f5217deb
Video BIOS:      80.21.25.00.01
Bus Type:    PCI
DMA Size:    40 bits
DMA Mask:    0xffffffffff
Bus Location:    0000:00:04.0
Device Minor:    0

情報通りのTesla K80すげー!

f:id:ueponx:20180320010046j:plain

では、pythonのパッケージpip経由で見てみます。

!pip freeze

【実行結果】

absl-py==0.1.11
astor==0.6.2
beautifulsoup4==4.6.0
bleach==1.5.0
cachetools==2.0.1
certifi==2018.1.18
chardet==3.0.4
crcmod==1.7
cycler==0.10.0
decorator==4.2.1
dill==0.2.7.1
entrypoints==0.2.3
future==0.15.2
futures==3.0.5
gapic-google-cloud-datastore-v1==0.15.3
gapic-google-cloud-error-reporting-v1beta1==0.15.3
gapic-google-cloud-logging-v2==0.91.3
gast==0.2.0
google-api-core==0.1.4
google-api-python-client==1.6.5
google-auth==1.3.0
google-auth-httplib2==0.0.3
google-auth-oauthlib==0.2.0
google-cloud==0.32.0
google-cloud-bigquery==0.30.0
google-cloud-bigquery-datatransfer==0.1.1
google-cloud-bigtable==0.28.1
google-cloud-container==0.1.1
google-cloud-core==0.28.1
google-cloud-datastore==1.4.0
google-cloud-dns==0.28.0
google-cloud-error-reporting==0.28.0
google-cloud-firestore==0.28.0
google-cloud-language==1.0.1
google-cloud-logging==1.4.0
google-cloud-monitoring==0.28.1
google-cloud-pubsub==0.30.1
google-cloud-resource-manager==0.28.1
google-cloud-runtimeconfig==0.28.1
google-cloud-spanner==0.29.0
google-cloud-speech==0.30.0
google-cloud-storage==1.6.0
google-cloud-trace==0.17.0
google-cloud-translate==1.3.1
google-cloud-videointelligence==1.0.1
google-cloud-vision==0.29.0
google-colab==0.0.1a1
google-gax==0.15.16
google-resumable-media==0.3.1
googleapis-common-protos==1.5.3
grpc-google-iam-v1==0.11.4
grpcio==1.10.0
h5py==2.7.1
html5lib==0.9999999
httplib2==0.10.3
idna==2.6
ipykernel==4.6.1
ipython==5.5.0
ipython-genutils==0.2.0
Jinja2==2.8
jsonschema==2.5.1
jupyter-client==5.2.3
jupyter-core==4.4.0
Keras==2.1.5
Markdown==2.6.11
MarkupSafe==1.0
matplotlib==2.1.2
mistune==0.8.3
mpmath==1.0.0
nbconvert==5.3.1
nbformat==4.4.0
networkx==2.1
nltk==3.2.1
notebook==5.2.2
numpy==1.14.2
oauth2client==4.1.2
oauthlib==2.0.6
olefile==0.45.1
opencv-python==3.4.0.12
pandas==0.22.0
pandas-gbq==0.3.1
pandocfilters==1.3.0
patsy==0.5.0
pexpect==4.4.0
pickleshare==0.7.4
Pillow==4.0.0
plotly==1.12.12
ply==3.8
portpicker==1.2.0
prompt-toolkit==1.0.15
proto-google-cloud-datastore-v1==0.90.4
proto-google-cloud-error-reporting-v1beta1==0.15.3
proto-google-cloud-logging-v2==0.91.3
protobuf==3.5.2
psutil==4.3.1
ptyprocess==0.5.2
pyasn1==0.4.2
pyasn1-modules==0.2.1
Pygments==2.1.3
pyparsing==2.2.0
python-dateutil==2.5.3
pytz==2016.7
PyWavelets==0.5.2
PyYAML==3.11
pyzmq==16.0.4
requests==2.18.4
requests-oauthlib==0.8.0
rsa==3.4.2
scikit-image==0.13.1
scikit-learn==0.19.1
scipy==0.19.1
seaborn==0.7.1
simplegeneric==0.8.1
six==1.11.0
statsmodels==0.8.0
sympy==1.1.1
tensorboard==1.6.0
tensorflow==1.6.0
termcolor==1.1.0
terminado==0.8.1
testpath==0.3.1
tornado==4.5.3
traitlets==4.3.2
uritemplate==3.0.0
urllib3==1.22
wcwidth==0.1.7
webencodings==0.5.1
Werkzeug==0.14.1
xgboost==0.7.post3

f:id:ueponx:20180320010048j:plain

おお! ネットの情報ではkeraspipで別途インストールという情報でしたが、既にデフォルト状態でインストールされている用です。最高! 使いそうなモジュールのバージョンを実行してみました。(2018/03/19現在)

f:id:ueponx:20180320010055j:plain

OpenCVは3系、tensorflowも1.6と新しく、kerasも2系と素晴らしい!

念のため、TensorflowでHello Worldもしてみました!

f:id:ueponx:20180320125729p:plain

問題ないようです。

おわりに

今後のAI関連の環境としてはこちらが定番になりそうです。環境などの構築にかける時間も不要なので、本来の目的へストレートに進めればいいのだと思います。

後は画像処理などを考えるとGoogleDriveとの連携などがわかるといいのかもしれません。

とはいえ、これまで機種依存などがあり勉強会などでもかなり苦労していた部分のほとんどが解消されるかなと思います。

関連エントリー

uepon.hatenadiary.com uepon.hatenadiary.com

*1:90分間なにもしないとインスタンスはシャットダウンします。また、最大でも12時間しか使用できません。

ハッカソンの運営をした話

ご注意

今回のエントリーは一個人の感想あるいはポエムになりますので、所属する会社とは全く関係はありません。 ご承知おきください。


ハッカソンの運営をした話

f:id:ueponx:20180306000944j:plain

先月、2月17日、24日-25日というスケジュールで会社で開催するハッカソンイベントHACK-CHU!の運営をしていました。 東海地区では今ハッカソン熱が少し盛り上がっていて、自社では今回で2回目のイベントとなります。

他にもここ一年では

も開催されています。

ハッカソンの運営は昨年に引き続きなので、社内にも経験者が増え、運営は楽になるのかなと思っていたのですが、 全くそんなこともなく運営する上では辛い感じの人事異動。メンバーは旧メンバーが1減、新規にハッカソンを全く知らない2名追加(約5名) での船出でした。また、スケジュールに関しても前回が3月と年度末で会社としてもひとのアサインは難しく、参加者のアンケートの結果から 参加者も結構辛そうな印象もあって、2月に開催することになりました。つまり、準備期間が1か月短いw。

とはいえ、開催自体は決まっていたので、告知は知り合い伝手にいったり、東海地方のIT勉強会コミュニティーのLTタイムを使ったりして かけずりまわってなんとかできた感じです。放送告知も一応行ったのですが、実は全くの以下略状態だったので、これでよかったのかという先行きの不安。 ハッカソンの参加者は自社がリーチができていない層であることは自覚しているので、なんとかリーチしたいいう希望があったのですが、 放送告知の効果がないことが、如実に現れるとかなり厳しい…。

募集を開始した12月某日、前回はもっと早くやってたんだけどなーという不安しかない状態。 ただ少し安心できたのは会場準備部分。前回一回やっていたのを資料として残していた点とチームメンバでのTrelloの使用がかなり活きました。

そんなこんなでいろいろなところでLTをやってきました…CodeforAichi、GeekBar、JAWS名古屋…。 それでもなかなか集まらない参加者、まだ決められていない審査員。問題山積のまま年末年始のお休みへ。 締め切りを2/1にしていたので正味あと一か月。傾向としては申し込みは締め切り近くにアップするのは知ってはいるのですが、それでも不安でした。

そんな中、子供連れの参加はありでしょうかという相談がありました。 運営側としては広い世代の方に参加していただきたいというのは当初からありましたので、 「参加については問題ないですよ」という感じでお答えしています。 今後もそのことに関しては変更はしないです。(お弁当が子供向けメニューになりにくいのはご容赦ください)

運命の2/1の17:00の締め切り、100名を超える方の参加があり少し肩の荷がおりました(ここでおろしてはいけない)が、 そこからは連絡作業が大量に。うれしい悲鳴なんですけど、参加者に大量の情報を伝えなくては行けないことも含めて連絡をしていました。

また今回そのころには素敵な審査員も決定し、スケジュールが近づくにつれて「自分が参加したくなるようなハッカソンを開催したい!やるぞ!」という気持ちに 切り替わって変わっていったような気がします。


少し話は変わるのですが、今回のテーマは「テクノロジーで暮らしを楽しくオモシロク!」というものでした。 この決定に関しても、いろいろありました。昨年同様にタッグを組んだ番組のテーマに近いところに据えるか、 はたまたテレビ局らしく番組を面白くするということにするか、あるいは地域振興(名古屋市)のような形にするか。 このあたりは社内でもかなり議論になりました。 議論なったのは、会社が開催をするにあたりどういうことを思ってテーマを設定するかということでした。

(このあたりからかなりポエム) 会社のスローガンが少し前に変更となり「あなたの真ん中へ」というものになったことをなんとなく覚えていました。

そこで、このスローガンがどうやったらこのイベントで参加者に伝わるかを考えれば、 テーマや自社でやる意義につながっていくのでないかと感じました。 その中で、より身近にあることをテーマにしないと問題意識が起きにくいのではないか、 自分の腹落ちした事柄でなければ議論が難しくないかというような方向で考えていき、 地域より街、街より暮らしというようなより近いキーワードにしていけば、 自分事の悩みや発想につながり「あなたの真ん中」に響いていくのではないかと思うようになりました。

(テーマ決めの前にMashupAward名古屋予選のテーマ決めや CivicTechMeetup 2017 Kanazawaに参加して刺激を もらったような気がします。)

そして二転三転のダメ出しの末、「テクノロジーで暮らしを楽しくオモシロク!」に決定しました。 参加者からはふわっとしすぎという話は結構もらったんですが、このテーマでいろいろなアイデアが出てきたので個人的には気に入ってます。

f:id:ueponx:20180306000805j:plain


参加者への連絡し後は前日の会場の準備を行い開催日を待つだけとなるのですが、今年は新たなチャレンジにも挑戦してみました。

前回のイベント時にテクノロジーサポート企業の方々の提供サービスの説明が数分と短くなかなか発想に伝わらないという悩みを参加者からは聞いていました。 確かに事前にかなりインプットをしていかないとすぐにアイデアと結びつけていくのはハードルがあるなあとは思っていました。 そこで今回は直前にテクノロジー企業の方で前日入りをしていただける方々にお願いし、プレイベントを開催しました。

mashup-nagoya.connpass.com

通常の倍以上の時間をかけて各企業様にはテクノロジーの説明をいただき、参加者も金曜日の夜にもかかわらず50名に迫る人数でした。 IBM様、ウフル様、Seeed様、Twilio様、Microsoft様、お忙しいところありがとうございました。

この時の作者の気持ちを20文字程度で答えよ。

なんで自分はこのハッカソンに参加できないんだろう。


初日のアイデアソンの受付は正直すこし不安でした。この近辺のコミュニティ勉強会を含めてですが、 欠席率が少ないというのが珍しい地域特性ではあるのですが、それでも不安でした。 季節がらインフルエンザも流行っているし…。 実際にスタートの時間での参加者は101名でした。前回も多いなぁとは思っていたのですが、 今回は100人の大台を超えたので数字にびっくりしていました。 遠方からも関東から九州までという放送エリアを超えたところからの参加もあり、本当うれしい限りです。

そして、開会宣言… 開催前日の夕方に話すことが急遽決まったのでうまく考えがまとまらず、気の利いた事いえなくてすみませんでした。

f:id:ueponx:20180305235454j:plain


イデアソン・ハッカソンはイベントが始まってしまうと、運営側が予想できない状況になります。

ただ、去年もそうでしたが、今年もやんわりした感じでアイデアを出していただけたんじゃないかなと思います。 更にハッカソンでもいろいろな方々との出会いや奇抜なアイデアにもあえて本当に良かったです。 また、会社の中からも2名ではありますが、参加者が出せたのは、このイベントの開催が社内にも少しは響いて きているのかなという感覚で収穫だったかなと思います。

開催の内容に関しては いろいろな方のブログエントリーがありますのでそちらをご覧いただいたほうが臨場感あるかなと思います。

mashupaward.jp

chris4403.hateblo.jp

kuxumarin.hatenablog.com

ghz2000.com

復活するエンジニア — 中京テレビハッカソンhack-chuに参加して来ました。...

hiiiiiiihikaru.hatenadiary.com

hiiiiiiihikaru.hatenadiary.com

niwasawa.hatenablog.jp

niwasawa.hatenablog.jp

niwasawa.hatenablog.jp

twilio.kddi-web.com

thefilament.jp


取り留めのないエントリーポエムではありますが、 今後もこのエリアでまたハッカソンに限らずIT系のイベントが開催ができないか模索しておりますので、お会いした際にはアドバイスなどいただけると助かります。 あと、個人活動としてイベントのお手伝いもできるかもです。

f:id:ueponx:20180306001507j:plain

御礼

伴野さま、まなみさま

フィラメント 角さま、牧さま

ご協賛の凸版印刷

審査員をお引き受け頂いたの粟生様、加藤様、久下様、江龍様、栗栖様

テクノロジーサポート企業のみなさま(アマゾン様、アプレッソ様、ヴァル研究所様、ウフル様、エーアイ様、KDDIウェブコミュニケーションズ様、サイボウズ様、Seeed様、IBM様、Microsoft様、Yahoo!様)

ご協力いただいた、たくさんの方々

そして、参加者のみなさま

本当にありがとうございました!

f:id:ueponx:20180305234851j:plain

RaspberryPiにOpenCV3.4+Tensorflow1.4+keras2.1をインストールする

RaspberryPiにOpenCV3.4+Tensorflow1.4+keras2.1をインストールする

以前のエントリーではRaspberryPiで新しめのtensorflowを使ってみようということでインストール設定を行いました。いろいろWarningが出ながらも動作できるようになり、あとはやるだけなんだよと思いつつ、腰はまだ上がりませんでした。

そんな時大垣のソフトピアで

www.softopia.or.jp

なるイベントが開催され参加することもできましたので、今回はPCではなくRaspberryPiでやってみようと思いました。PCではAnacondaでの設定だけど、RaspberryPiでは自分の設定したものでできるなあとタカをくくっていて、あとはOpenCVを入れれば実験できるなあとかなり軽く考えていました。

ただ、python3.5ではOpenCVの設定がなくあれー?(お手軽じゃないじゃんの意)って感じになり、仕方なくソースコードからOpenCVをインストールすることにました。

uepon.hatenadiary.com

茨の道に入ったような気がしますが、色々(2週間ほどビルド地獄だったw)あり、違う方法も含めて実験には成功できましたので、そのときのメモ。

OpenCVインストール放浪編【結論を求めるなら読む必要のない情報】

OpenCVをソースからインストールするということで以下のサイトを参考にしてインストールしてみました。

www.pyimagesearch.com

基本的な流れとしては

  1. ビルドに必要なパッケージのインストール
  2. OpenCVのソース関連の取得
  3. VirtualEnv環境のセッティング
  4. numpyのインストール
  5. ビルド
  6. ビルド後の設定

こんな感じになります。

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install build-essential cmake pkg-config
$ sudo apt-get install libjpeg-dev libtiff5-dev libjasper-dev libpng12-dev
$ sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
$ sudo apt-get install libxvidcore-dev libx264-dev
$ sudo apt-get install libgtk2.0-dev
$ sudo apt-get install libatlas-base-dev gfortran
$ sudo apt-get install python2.7-dev python3-dev
$ wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.4.0.zip
$ wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.4.0.zip
$ unzip opencv.zip
$ unzip opencv_contrib.zip
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python3 get-pip.py
$ sudo pip3 install virtualenv virtualenvwrapper
$ sudo rm -rf ~/.cache/pip
$ echo -e "\n# virtualenv and virtualenvwrapper" >> ~/.profile
$ echo "export WORKON_HOME=$HOME/.virtualenvs" >> ~/.profile
$ echo "source /usr/local/bin/virtualenvwrapper.sh" >> ~/.profile
$ source ~/.profile
$ mkvirtualenv cv-python3 -p python3
$ workon cv-python3
$ pip install numpy  
$ cd opencv-3.4.0/
$ mkdir build
$ cd build/
$ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D INSTALL_PYTHON_EXAMPLES=ON -D OPENCV_EXTRA_MODULES_PATH=~/opencv/opencv_contrib-3.4.0/modules -D BUILD_EXAMPLES=ON ..
$ make clean
$ time make

ここまででビルドがされますが、とても時間がかかります。 参考サイトではmake -j4となっていますが、発熱やmakeする順序などによっては失敗する様です。自分は3回失敗したので-j2としてみましたが、それでもダメ。最終的にmakeのみにしています。(makeだけでは失敗したことはないです)実際かかる時間はこんな感じなので、寝る前にやりましょう。

real    283m10.666s
user    246m21.650s
sys     6m20.950s

ビルドが終了したらインストールを行います。

$ sudo make install
$ sudo ldconfig
$ ls -l /usr/local/lib/python3.5/site-packages/
$ cd /usr/local/lib/python3.5/site-packages/
$ sudo mv cv2.cpython-35m.so cv2.so
$ cd ~/.virtualenvs/cv-python3/lib/python3.5/site-packages/
$ ln -s /usr/local/lib/python3.5/site-packages/cv2.so cv2.so

これでインストール完了になりました。

あとは確認となるのですが…

$ workon cv-python3
(cv-python3)  $ python
Python 3.5.3 (default, Jan 19 2017, 14:11:04)
[GCC 6.3.0 20170124] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'3.4.0'
>>>

無事インストール成功です。ちゃんといっているのですが…

OpenCV以外のパッケージが消えていますw。前のエントリーで折角インストールしたTensorflowとkerasも消えてます。

(cv-python3)  $ pip list
DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning.
numpy (1.14.0)
pip (9.0.1)
setuptools (38.4.1)
wheel (0.30.0)

うわああああ

f:id:ueponx:20180211120348p:plain

この手順をいろいろと5回ほど実験したところ(w)VirtualEnv環境を設定したあたりでこの状況になるようです。この手順を外せば行けそうです。

ただ、なんとなくこれではつらみがあるので、再度ググって設定方法を見直したいと思います。

再度見直し

再度ネットをググってビルド済みのパッケージも含めて探してみました。

opencv3.4のインストールパッケージ

https://github.com/mt08xx/files/raw/master/opencv-rpi/libopencv3_3.4.0-20180115.1_armhf.deb

tensorflow1.4.1のインストールパッケージ

github.com

python2.7

https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.4.1/tensorflow-1.4.1-cp27-none-linux_armv7l.whl

python3.5

https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.4.1/tensorflow-1.4.1-cp35-none-linux_armv7l.whl

インストール作業へ

今回使用したRaspbianのバージョンはこちらになります。

  • 2017-11-29-raspbian-stretch.img

バージョン情報を確認すると…

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 9.1 (stretch)
Release:        9.1
Codename:       stretch

$ python --version
Python 2.7.13
$ python3 --version
Python 3.5.3

pythonが3.5.3になっているで前回のエントリーの設定では動作はするのですが、Warningが出るというものでした。

OpenCV3.4のインストール

以下のブログがありました。OpenCVの構築にほとんどの時間が割かれるのでこれは助かりました。

qiita.com

今から思えば、最初からこれを使えばよかったのではないのかと思います。

$ wget https://github.com/mt08xx/files/raw/master/opencv-rpi/libopencv3_3.4.0-20180115.1_armhf.deb
$ sudo apt install -y ./libopencv3_3.4.0-20180115.1_armhf.deb
$ sudo ldconfig

これでOpenCV3.4をインストールができました。この作業でPython2系もPython3系も使用可能になっています。

Tensorflow1.4.1のインストール

以下の作業ではTensorflowとともにkerasもインストールします。

TensorflowのWhlパッケージを作成されている方がいらっしゃいました。tensorflowのバージョンが1.4.1でかつpython3.5様なのも嬉しいところです。大概のパッケージがpython3.4なのが辛いです(minicondaにあわせているのかも?)

github.com

python2.7で使用する場合

$ sudo apt-get install libblas-dev liblapack-dev python-dev libatlas-base-dev gfortran python-setuptools
$ sudo pip install https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.4.1/tensorflow-1.4.1-cp27-none-linux_armv7l.whl
$ sudo apt-get install python-h5py
$ sudo pip install keras

これで完了です。REPL環境でチェックしてみます。

$ python
Python 2.7.13 (default, Jan 19 2017, 14:48:08)
[GCC 6.3.0 20170124] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> import cv2
>>> import tensorflow
>>> import keras
Using TensorFlow backend.
>>> numpy.__version__
'1.12.1'
>>> cv2.__version__
'3.4.0'
>>> tensorflow.__version__
'1.4.1'
>>> keras.__version__
'2.1.3'
>>>

python3.5で使用する場合

$ pip3 install numpy==1.13
$ sudo apt-get install libblas-dev liblapack-dev python3-dev libatlas-base-dev gfortran python3-setuptools
$ sudo pip3 install https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.4.1/tensorflow-1.4.1-cp35-none-linux_armv7l.whl
$ sudo apt-get install python3-h5py
$ sudo pip3 install keras

一行目のnumpyのバージョンを1.13をしています。3.5系ではtensorflowをimportするとWarningが発生します。

Warningは

>>> import tensorflow
RuntimeError: module compiled against API version 0xb but this version of numpy is 0xa

こんな感じで表示されます。インストール後にpipでバージョンアップしても問題は無いようです。

ただ、バージョンアップをすればいいと思って、以下のようにnumpyを2018.02.10現在以下のようにアップグレートすると

$ pip install --upgrade numpy 

インストールが終わると1.14がインストールされ、tensorflowのimport時にWarningが発生するので、この設定は必須になります。

ではREPL環境でバージョンの確認を行ってみます。

$ python3
Python 3.5.3 (default, Jan 19 2017, 14:11:04)
[GCC 6.3.0 20170124] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> import cv2
>>> import tensorflow
>>> import keras
Using TensorFlow backend.
>>> numpy.__version__
'1.13.0'
>>> cv2.__version__
'3.4.0'
>>> tensorflow.__version__
'1.4.1'
>>> keras.__version__
'2.1.3'
>>>

前回のエントリーではなんとなくWarningもでていましたが、今回のインストール方法では全く問題はなく、すんなり完了できたと思います。

サンプルの実行

OpenCVのテスト

f:id:ueponx:20180212091005p:plain

画像は表示されるのですが以下のようなWarningがでます。

** (image:1479): WARNING **: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files

org.a11y.BusのWarningは解決手段があまりないようでしたので動くので良しとしました。

Tensorflowのテスト

TensorflowのHello worldの実行になります。

【ソース】

import tensorflow as tf
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

hello = tf.constant('Hello, Tensorflow')
sess = tf.Session()

print(sess.run(hello))

【実行結果】

b'Hello, Tensorflow'

OpenCV&Tensorflow&kerasのサンプル

【07tf.py】

from keras.models import Sequential
from keras.layers import Activation, Dense, Dropout
from keras.utils.np_utils import to_categorical
from keras.optimizers import Adagrad
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os

image_list = []
label_list = []

# トレーニングデータを読み込む
for dir in os.listdir("data/train"):

    traindir = "data/train/" + dir
    if os.path.isdir(traindir) == False:
        continue

    label = 0              # 正解ラベル

    if dir == "apple":
        label = 0          # りんごの場合は、0
    elif dir == "orange":
        label = 1          # オレンジの場合は、1

    for file in os.listdir(traindir):
        if file != ".DS_Store":

            label_list.append(label)            # 正解ラベルを配列に入れる

            filepath = traindir + "/" + file  # ファイルパス

            resized_img = Image.open(filepath).resize((25, 25))                                                    # 画像を25x25にリサイズする
            image = np.array(resized_img)                                                                       # 25x25の2次元配列にする→[[R,G,B], [R,G,B]...]
            image = image.transpose(2, 0, 1)                                                                 # 配列を次元を変換する→[[R,R,R,...], [G,G,G,...], [B,B,B,...]]
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]     # 1次元配列に変換→[R,R,R,...,G,G,G,...,B,B,B]
            image_list.append(image / 255.)                                                            # 0.0〜1.0までの値にして配列に入れる

image_list = np.array(image_list)       # 画像リストをnumpy配列に変換

Y = to_categorical(label_list)          # 正解ラベルを配列にする(0→[1,0], 1→[0,1])

# 層を構築
model = Sequential()
# 入力層
model.add(Dense(200, input_dim=1875))
model.add(Activation("relu"))
model.add(Dropout(0.2))

# 隠れ層
model.add(Dense(200))
model.add(Activation("relu"))
model.add(Dropout(0.2))

# 出力層
model.add(Dense(2))
model.add(Activation("softmax"))

# オプティマイザにAdamを使用
opt = Adam(lr=0.001)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
# nb_epoch: 学習回数
# batch_size: 1度に処理する分量(GPUモードの際は、メモリ制限がある場合がある)
model.fit(image_list, Y, nb_epoch=1500, batch_size=100, validation_split=0.1)
# model.fit(image_list, Y, nb_epoch=10, batch_size=100, validation_split=0.1)

total = 0.
ok_count = 0.

for dir in os.listdir("data/test"):
    
    testdir = "data/test/" + dir
    if os.path.isdir(testdir) == False:
        continue

    label = 0

    if dir == "apple":
        label = 0          # りんごの場合は、0
    elif dir == "orange":
        label = 1          # オレンジの場合は、1

    for file in os.listdir(testdir):
        if file != ".DS_Store":
            label_list.append(label)
            filepath = testdir + "/" + file

            resized_img = Image.open(filepath).resize((25, 25))    
            image = np.array(resized_img)
            image = image.transpose(2, 0, 1)
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]

            # 予測する
            print(filepath)
            result = model.predict_classes(np.array([image / 255.]))
            print("label:", label, "result:", result[0])

            total += 1.

            if label == result[0]:
                ok_count += 1.

print(ok_count / total * 100, "%")

【実行結果】

$ python3 07tf.py
Using TensorFlow backend.
/usr/local/lib/python3.5/dist-packages/keras/models.py:944: UserWarning: The `nb_epoch` argument in `fit` has been renamed `epochs`.
  warnings.warn('The `nb_epoch` argument in `fit` '
Train on 35 samples, validate on 4 samples
Epoch 1/1500
35/35 [==============================] - 3s 93ms/step - loss: 0.9337 - acc: 0.5429 - val_loss: 0.0025 - val_acc: 1.0000
Epoch 2/1500
35/35 [==============================] - 0s 2ms/step - loss: 2.7587 - acc: 0.4571 - val_loss: 0.3437 - val_acc: 1.0000

(中略)

Epoch 1498/1500
35/35 [==============================] - 0s 2ms/step - loss: 8.9737e-06 - acc: 1.0000 - val_loss: 3.8743e-07 - val_acc: 1.0000
Epoch 1499/1500
35/35 [==============================] - 0s 2ms/step - loss: 3.3191e-05 - acc: 1.0000 - val_loss: 3.5763e-07 - val_acc: 1.0000
Epoch 1500/1500
35/35 [==============================] - 0s 2ms/step - loss: 6.7218e-06 - acc: 1.0000 - val_loss: 3.5763e-07 - val_acc: 1.0000
data/test/apple/1212043top.jpg
label: 0 result: 0
data/test/apple/81306024002554.jpg
label: 0 result: 0
data/test/apple/81306024002557.jpg
label: 0 result: 0
data/test/apple/1757f226647a6f1.jpg
label: 0 result: 0
data/test/orange/images (1).jpeg
label: 1 result: 1
data/test/orange/8242_1.jpg
label: 1 result: 1
data/test/orange/1152194_orange_isolated_on_white_background.jpg
label: 1 result: 1
data/test/orange/DKCcTzZXUAA0ckm.jpg
label: 1 result: 1
100.0 %
Exception ignored in: <bound method BaseSession.__del__ of <tensorflow.python.client.session.Session object at 0x6f3a1430>>
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py", line 696, in __del__
TypeError: 'NoneType' object is not callable

若干のWarningとExceptionがでているんですが、概ね実行はできているっぽいようです。このサンプルの内容はりんごとオレンジを画像を学習して分類するというものです。

最初と最後にでているExceptionに関してはググってみると解決方法はあるようです。 Keras2系のバージョンによるものの模様です。

qiita.com

keras+tensorflowで終了処理でエラーが発生する | CodeLab技術ブログ

おわりに

前回はスッキリしない終わり方でしたが、今回はimport時のWarningなどは発生せずにうまくロードできました。

node.jsでPromiseを使って同期的に処理を行う

node.jsでPromiseを使って同期的に処理を行う

前回の補足的なエントリーです。

uepon.hatenadiary.com

以前のgoogle-home-notifierを使用した処理では以下のようなコードを書いてみました。 いちいちプログラムを起動するのも面倒だなと思ったので、常に入力を受け付けるような処理にしたいと思っていました。 ただ、キーボード入力と非同期実行のお陰で単純に書いても予想通りには動きません。(node.jsではこの部分が個人的には一番ハードルが高いと思う)

var googlehome = require('google-home-notifier');
var language = 'ja'; // if not set 'us' language will be used

googlehome.device('テスト', language);
googlehome.ip('xxx.xxx.xxx.xxx');  //IPアドレスは自分も持っているデバイスを調べて入力してください

var text = 'こんにちは';

if(process.argv.length == 3){
        text = process.argv[2];
}

try {
        googlehome.notify(text, function(notifyRes) {
                console.log(notifyRes);
        });
} catch(err) {
        console.log(err);
}

このコードではコマンド引数に与えた文字列を単純にgoogle-home-notifierモジュールのnotifierメソッドに渡しています。 この処理を無限ループにすることでいいはずなのですが、文字入力が完了する前に次の処理がおこなわれるので文字には空文字列が入り、 目的とは違った空振りのコードで動いてしまします。node.jsではI/O処理も非同期的に動作するため、時間のかかる処理では データが格納される前に次の処理が行われてしまいます。ネットワークからのデータ取得やファイルからの読み込み・書き込み、キーボード入力などが該当します。

そういう場合にはpromiseを使うようです。くっそ適当に言ってます。 C#みたいなasync/awaitの方が理解しやすいんですが、node.jsのasync/awaitは少し違っているみたいに思ったので、 今回はpromiseを理解することにしました。

const readlineSync = require('readline-sync');

var googlehome = require('google-home-notifier');
var language = 'ja';
googlehome.device('テスト', language);
googlehome.ip('xxx.xxx.xxx.xxx');

console.log('話させたい文字列を入力してください>');
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (data) {
        googleHomeNotify(data).then(()=> {
                return PromiseSleep(2000);
        }).then(() => {
                console.log('話させたい文字列を入力してください>');
        });
});

function googleHomeNotify(data) {
        return new Promise((resolve, reject) => {
                try {
                        googlehome.notify(data, function(notifyRes) {
                                console.log(notifyRes);
                        });
                } catch(err) {
                        console.log(err);
                }
                resolve();
        });
}

function PromiseSleep(time) {
        return new Promise((resolve, reject) => {
                setTimeout(() => {
                        resolve();
                }, time);
        });
}

基本的な流れとしては

  1. 常時入力待機(イベント)
  2. 入力が行われたらPromiseを生成して、その中でgoogle-home-notifierのnotifyメソッドに入力された文字列を引数に実行
  3. google-home-notifierのnotify処理を行っている間にwait処理を行う(setTimeoutメソッド)
  4. wait時間が過ぎたら次の文字列入力を促す表示を行う
  5. 入力待機状態に戻る

こんな感じです。process.stdin.on(...)の部分をwhileやforなどで無限ループにするとうまく動きません。Promiseと同期型のループ処理で散々悩みました…(単体では動くがループでは動かない)

もっと、多分良い書き方があるのかなと思いますが、こんな感じでかけました。もう少しスッキリかけるといいなあというところです。

関数の処理を見直して以下のように書き直してみました。

const readlineSync = require('readline-sync');

var googlehome = require('google-home-notifier');
var language = 'ja';
googlehome.device('テスト', language);
googlehome.ip('xxx.xxx.xxx.xxx');

console.log('話させたい文字列を入力してください>');
process.stdin.setEncoding('utf-8');
process.stdin.on('data', function (data) {
        googleHomeNotify(data).then(()=> {
                return PromiseSleep(2000);
        }).then(() => {
                console.log('話させたい文字列を入力してください>');
        });
});

function googleHomeNotify(data) {
        return new Promise((resolve, reject) => {
                try {
                        googlehome.notify(data, function(notifyRes) {
                                console.log(notifyRes);
                        });
                } catch(err) {
                        console.log(err);
                }
                resolve();
        });
}

function PromiseSleep(time) {
        return new Promise((resolve, reject) => {
                setTimeout(() => {
                        resolve();
                }, time);
        });
}

まとめ

いままでjavascriptの非同期的な部分は避けて通ってきましたが、やっと重い腰を上げられたかなと思います。

さらにPromiseとasync/awaitを使ってわかりやすくしてみました。 ネストが少し浅くなったのでわかりやすくなった気がします。

const readlineSync = require('readline-sync');

var googlehome = require('google-home-notifier');
var language = 'ja';
googlehome.device('テスト', language);
googlehome.ip('xxx.xxx.xxx.xxx');

console.log('話させたい文字列を入力してください>');
process.stdin.setEncoding('utf-8');
process.stdin.on('data', async function (data) {
        googleHomeNotify(data);
        await PromiseSleep(3000);
        console.log('話させたい文字列を入力してください>');
});

function googleHomeNotify(data) {
        try {
                googlehome.notify(data, function(notifyRes) {
                        console.log(notifyRes);
                });
        } catch(err) {
                console.log(err);
        }

function PromiseSleep(time) {
        return new Promise((resolve, reject) => {
                setTimeout(() => {
                        resolve();
                }, time);
        });
}

これで非同期的に動作する機能をわかりやすく記述することができたような気がしますね。

/* -----codeの行番号----- */