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