工作と競馬

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

カテゴリ:4.ソフトウェア > 4.6 Python

概要

kerasで、MNISTのサンプルを実装し、動かしてみた。


背景と目的

kerasを初めて使ってみる。手始めに、MNISTの手書き文字判別のNNを実装してみる。


詳細

0.環境

  • Jupyter
  • Python3

1.インポート

import os
import keras
from keras.models import Sequential
from keras.layers import Dense

2.ネットワーク

784ニューロンの入力層、中間層は128, 64, 出力層は10の全結合NNを定義

model = Sequential()

model.add(Dense(units=128, activation='relu', input_dim=784))
model.add(Dense(units=64, activation='relu', input_dim=128))
model.add(Dense(units=10, activation='softmax', input_dim=64))

model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True),
             metrics=['accuracy'])

3.データの取得

from keras.datasets import mnist
(xtrain, y_train), (x_test, y_test) = mnist.load_data()

4.学習

batch_size = 100
epochs = 10 # 終了エポック
initial_epoch = 0 # 開始エポック
x_train = x_train.reshape(60000, 784) # 2次元配列を1次元に変換
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')   # int型をfloat32型に変換
x_test = x_test.astype('float32')
x_train /= 255                        # [0-255]の値を[0.0-1.0]に変換
x_test /= 255
y_train = keras.utils.to_categorical(y_train, 10)
y_test = keras.utils.to_categorical(y_test, 10)

from keras.callbacks import ModelCheckpoint
fpath = 'weights.{epoch:04d}.hdf5'
cb = ModelCheckpoint(
    fpath, # 保存先ファイルパス
    verbose=1, # コマンドライン出力
    savebestonly=True) # 精度が良くなった時だけ保存

weighthdf5 = "weights." + str(initialepoch).zfill(4) + '.hdf5'
if os.path.exists(weighthdf5):
    model.loadweights(weighthdf5)
    print(weighthdf5 + " loaded.")
else:
    print(weight_hdf5 + " not found.")

model.fit(xtrain, ytrain,
          epochs=epochs,
          initialepoch=initialepoch,
          batchsize=batchsize,
          validationdata=(xtest, y_test),
          callbacks=[cb, tsb])

classes = model.predict(xtest, batchsize=batch_size)

score = model.evaluate(xtest, ytest, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

5.結果

10エポックだけやってみた。ちゃんと学習できていそう。

weights.0000.hdf5 not found.
Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 2s 35us/step - loss: 0.4187 - acc: 0.8800 - valloss: 0.2162 - valacc: 0.9380

Epoch 00001: valloss improved from inf to 0.21623, saving model to weights.0001.hdf5
Epoch 2/10
60000/60000 [==============================] - ETA: 0s - loss: 0.1933 - acc: 0.944 - 2s 35us/step - loss: 0.1931 - acc: 0.9440 - valloss: 0.1542 - val_acc: 0.9546

Epoch 00002: valloss improved from 0.21623 to 0.15421, saving model to weights.0002.hdf5
Epoch 3/10
60000/60000 [==============================] - 2s 33us/step - loss: 0.1426 - acc: 0.9578 - valloss: 0.1216 - val_acc: 0.9628

Epoch 00003: valloss improved from 0.15421 to 0.12159, saving model to weights.0003.hdf5
Epoch 4/10
60000/60000 [==============================] - 2s 26us/step - loss: 0.1128 - acc: 0.9667 - valloss: 0.1126 - val_acc: 0.9657

Epoch 00004: valloss improved from 0.12159 to 0.11258, saving model to weights.0004.hdf5
Epoch 5/10
60000/60000 [==============================] - ETA: 0s - loss: 0.0939 - acc: 0.972 - 2s 30us/step - loss: 0.0938 - acc: 0.9725 - valloss: 0.0992 - val_acc: 0.9697

Epoch 00005: valloss improved from 0.11258 to 0.09921, saving model to weights.0005.hdf5
Epoch 6/10
60000/60000 [==============================] - 2s 34us/step - loss: 0.0794 - acc: 0.9771 - valloss: 0.0882 - val_acc: 0.9737

Epoch 00006: valloss improved from 0.09921 to 0.08821, saving model to weights.0006.hdf5
Epoch 7/10
60000/60000 [==============================] - 2s 33us/step - loss: 0.0684 - acc: 0.9799 - valloss: 0.0880 - val_acc: 0.9726

Epoch 00007: valloss improved from 0.08821 to 0.08802, saving model to weights.0007.hdf5
Epoch 8/10
60000/60000 [==============================] - 2s 31us/step - loss: 0.0595 - acc: 0.9827 - valloss: 0.0803 - val_acc: 0.9741

Epoch 00008: valloss improved from 0.08802 to 0.08034, saving model to weights.0008.hdf5
Epoch 9/10
60000/60000 [==============================] - 2s 33us/step - loss: 0.0530 - acc: 0.9844 - valloss: 0.0779 - val_acc: 0.9751

Epoch 00009: valloss improved from 0.08034 to 0.07787, saving model to weights.0009.hdf5
Epoch 10/10
60000/60000 [==============================] - 2s 30us/step - loss: 0.0461 - acc: 0.9867 - valloss: 0.0826 - val_acc: 0.9749

Epoch 00010: val_loss did not improve from 0.07787
Test loss: 0.08257695595924743
Test accuracy: 0.9749

まとめ

kerasで、MNISTのサンプルを実装し、動かしてみた。

概要

Pythonで.vntファイルの変換をした。


背景と目的

家族が機種変更のため、旧機種のandroidスマホのメモアプリからメモをエクスポートしたところ、vntというファイルでエクスポートされ、新機種側で読めなかった。変換アプリ等を探したところうまく見つけられたなかったので、PC上で無理やり変換する。


詳細

1.方法の検討

.vntファイルは、ちゃんと仕様があるだろうが、"="の後ろの数値がある文字コードに対応した16進文字列

=80=33blog...

(blogはasciiにあるのでそのまま)という感じなので、これを変換すればよさそうだ。


2.スクリプト作成

Pythonで出来そうなのでいろいろ調べた結果、

"="の後ろの2文字を16進文字列からバイナリに変換

していけばいけそうなので、

import binascii

x = binascii.unhexlify('16進文字列')

という感じの処理をすればよいことがわかった。


3.結果

というわけで、.vntファイルを読みこみ、"="の後ろ2文字にbinascii.unhexlifyをに適用してバイナリに変換し、それをすべて連結させてファイルに書き出したところ、無事変換ができた。

※出力ファイルの文字コードは適宜変換すること。今回、私の場合は.vntのヘッダらしき部分にShift-JISとの記述があったので、バイナリに変換後、shift-jisと解釈して扱ったところうまくいった。


まとめ

.vntファイルの変換をPythonでできた。雑多な処理テクニックがまた1つ増えた。もう使うことなさそうだけど。

概要

ニューラルネットワークライブラリKerasを導入して動く環境を整えた。


背景と目的

ニューラルネットワークのプログラミングを素早く行える環境を整えたくなったので、やってみる。


詳細

0.準備

ニューラルネットワークのプログラミングを素早く行える環境としては、いろいろあるようだが今回はKerasを使うことにした。PCは以下。

  • Lenovo Idea Pad Yoga 13
  • Windows10


1.各種インストール

1.1 Anacondaのインストール

まず、https://www.anaconda.com/download/から、64ビット版、バージョン3.6をダウンロードしてインストールした。裸のPython環境に入れてもいいが、Web上で紹介されていることが多いのがAnacondaなのでそれに従った。

また、これに伴いpipが古いだの、msgpackがないと言われたのでAnaconda Promptを立ち上げて、以下のコマンドを実行してアップデート。

python -m pip install --upgrade pip
pip install msgpack


1.2 Tensorflowのインストール

Kerasでは、Tensorflowをテンソル計算ライブラリに使用しているため、Anaconda Promptで以下を実行し、インストール。

pip install tensorflow


1.3 Kerasのインストール

いよいよKeras。

pip install keras


2.autoencoderのプログラムを動かす

とりあえず、MNISTの学習と判別をしてみるということで、こちらそのままをコーディングし、実行した。

そのままなので、コードは省略。


3.結果

実行結果は以下。所要時間は5分くらいだろうか。とりあえず、環境は整った。

Epoch 50/50
60000/60000 [==============================] - 5s 88us/step - loss: 0.1059 - val_loss: 0.1041

10個だけ、入力と出力の比較をしてみる。上が入力、下が出力。

20180822231358


まとめ

Kerasを導入して動く環境を整えられた。ちょっとずつプログラミングに慣れようと思う。

概要

Pythonのloggingモジュールを使った際のメモ。

背景と目的

Pythonのloggingモジュールを使った際のメモ。


詳細

1.基本

import logging
# ルートオブジェクトを取得
logger = logging.getLogger()
# 名前を指定してロガーオブジェクトを取得
logger = logging.getLogger("名前")

2.モジュール間で参照

まず、参照される側のモジュール(test.pyとする)では、__name__としてオブジェクトを作成してしておく。

import logging

# __name__としてオブジェクトを作成して
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) # レベル設定を忘れない(呼び出し側でやってもいいけどとにかく忘れないこと)

def hoge():
    logger.debug("hogehoge")

次に、呼び出し側のスクリプトで、testをインポートし、"test"という名前のロガーを取得

import logging
import test # 参照モジュール

# testモジュールのオブジェクトを取得
testLogger = logging.getLogger("test")
logger = logging.getLogger(__name__)

def hoge2():
    logger.debug("hoge2hoge2")

if __name__ == "__main__":

    test.hoge()
    
    hoge2()

こうすれば、以下のような出力になる。

hogehoge
hoge2hoge2


3.コンソールとファイルに同時に出力、フォーマット、レベル設定など


# フォーマット
format_string = "%(asctime)s %(levelname)s %(message)s"
formatter = logging.Formatter(format_string)

# コンソール
hConsole = logging.StreamHandler()
hConsole.setFormatter(formatter)
hConsole.setLevel(logging.DEBUG)
logger.addHandler(hConsole)

# ファイル
hFile = logging.FileHandler(ファイルパス)
hFile.setFormatter(formatter)
hFile.setLevel(logging.DEBUG)
logger.addHandler(hFile)


まとめ

ざっくりこんな感じ。

概要

ここに、概要を2行くらいで書きます。


背景と目的

先日、Raspberry Piで、Webアクセスをするプログラムを動かしたところ、レスポンスが遅かったので、解決策を調べる。


詳細

1.症状

  • Pythonのurllib2を使ってプログラムを書いていたときに起こった
  • 試しにcurlでアクセスを試したところ、同様に遅い(約5秒)
  • pingも遅い(1発目のレスポンスが来るまで、2発目以降は1秒ごと)

という状況だった。

2.調査

そこで、Web上でいろいろ調べた結果わかったのは、

 ipv6のDNSに接続しに行ってタイムアウトし、ipv4のDNSに接続しなおしている

ということのようだった。

3.対策

こちらなどを見ると、curlの引数に、-4という、ipv4だけを使うようにするオプションがある。そこで、-4という引数を付けたところ、素早くレスポンスが帰ってきた。

というわけで、ipv6のDNSに接続しに行ってタイムアウトするのを防ぐためには、

 curlで-4という引数を付ける

でよい。

ただし、この対策は根本治癒ではないので、curlが使えない状況などがあるとまずい。(私の場合Pythonでプログラムを作ってきてしまっていたのでsubprocessで、curlを実行しレスポンスをもらうという方法でどうにか乗り切れた。)


まとめ

Webアクセスが妙に遅い場合、ipv6のDNSに接続しに行ってタイムアウトを疑ってみて、curlの-4オプションをつけて早くなればそれが原因と推定できる。

概要

Pythonで、SFTPでリモートマシンにファイルを送るスクリプトを作成した。


背景と目的

Pythonで、SFTPでリモートマシンにファイルを送る必要が出たので、スクリプトを作成する。


詳細

1.準備

以下が参考になった。

paramikoをインストール。

pip install paramiko


2.コーディング

まず、モジュールのインポートと送信元と送信先情報を定義。

import paramiko
import os

# 設定
HOST = 'ホスト名'
USER = 'ユーザー名'
PSWD = 'パスワード'
LOCAL_DIR = "送信元フォルダ"
REMOTE_DIR = "送信先フォルダ"

次に、あるディレクトリ以下にある全ファイルをあるディレクトリに送信する関数として以下を定義。

def put_files(sftp, localdir, remotedir):
    for name in os.listdir(localdir):
        localpath = os.path.join(localdir, name).replace("\\", "/")
        remotepath = os.path.join(remotedir, name).replace("\\", "/")
        if os.path.isdir(localpath):
            # フォルダのとき
            if not name in sftp.listdir(remotedir):
                # リモートに当該ディレクトリがないときは作成
                sftp.mkdir(remotepath)
            # 再帰的に実行
            put_files(sftp, localpath, remotepath)
        else:
            # ファイルの時
            print os.path.join(remotedir, name).replace("\\", "/")
            sftp.put(localpath, remotepath)

上記関数を呼び出して実行するスクリプト部分。

if __name__ == "__main__":

    # SSHクライアントを作成
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(HOST, username=USER, password=PSWD)

    # SFTPクライアント作成
    sftp = ssh.open_sftp()

    # 送信
    put_files(sftp, LOCAL_DIR, REMOTE_DIR)

    # 終了
    sftp.close()

    # close
    ssh.close()


まとめ

上記スクリプトを実行したところ、ちゃんと送信できた。

概要

MicroPythonの開発環境を整え、ESP-WROOM-32で動くか試した。無事Lチカが動いた。


背景と目的

トランジスタ技術2018年5月号に、MicroPythonなるものが紹介されている。マイコンプログラムをPythonで組めるようだ。私は普段Pythonを使う機会が多く、慣れているので、マイコンプログラミングもPythonでできるとよいと思う。なので、とりあえずLチカを試しにやってみる。


詳細

1.環境整備

詳しい方法は、トランジスタ技術2018年5月号など各種書籍を参考にするとして、私がやった手順をメモっておく。今回は、

  • Windows10 PC上に環境構築
  • ESP-WROOM-32 Dev Kit Cを使用

という条件だ。

1.1 MicroPythonのダウンロード

githubのリポジトリからクローニング。

cd C:/Users/MyName/Documents
git clone https: //github.com/micropython/micropython.git MicroPython
1.2 ESP32用MicroPythonファームウェアのダウンロード

以下から、最新版をダウンロード。保存先はどこでもいいが、わかりやすいように1.3のToolと同じ場所に置いた。

https://micropython.org/download#esp32

1.3 Flash Download Toolのダウンロード

EspressifのWebサイトより、ツールをダウンロード。

https://www.espressif.com/sites/default/files/tools/flash_download_tools_v3.6.4_0.rar

RAR形式なので、解凍注意。


1.4 ampyのインストール

ampyは、ESP32のFlashメモリに書き込みを行うためのツール。

PCにPythonが既にインストールされている前提で、pipで以下を実行。

pip install adafruit-ampy

2.コンソールモードで実行

どうやら、シリアルコンソールで対話的にプログラムを実行できるようなので、やってみる。

2.1 ファームウェアの書き込み

ESP32開発ボードをUSBでPCに接続する。

Flash Download Toolを起動し、ESP32 Download Toolというボタンをクリックする。次に、立ち上がったツールで、1.2でダウンロードしたファームウェアを選択し、書き込み先先頭アドレスを0x1000と指定。シリアルポートとボーレートを115200bpsに設定して、STARTボタンを押す。プログレスバーが伸びきったら、書き込み完了。

20180417215812

2.2 コンソールモードで実行

Tera Termなどのシリアルコンソールアプリで、接続先COMポートをボーレートを115200bpsで開くと、以下のような表示となる。お馴染みのPythonコンソールだ。

20180417220148

ここで、以下のように入力。

from machine import Pin
io12 = Pin(12, Pin.OUT)
io12.value(1)

すると、GPIO12に接続されたLEDが点灯!まずは、コンソールモードでGPIOが制御できた。


3.プログラム作成

Lチカのコードを作成してみる。ファイル名は、main.pyとする。どうやら、ESP32起動後boot.py、main.pyの順に実行される仕組みのようだ。

GPIO12に、LEDを接続し、HIGHで点灯。1秒周期で点滅させる。

# coding: utf-8
from machine import Pin
import time

# ピンの設定
io12 = Pin(12, Pin.OUT)

# 1秒周期で点滅
while True:
    io12.value(1)
    time.sleep(0.5)
    io12.value(0)
    time.sleep(0.5)


4.書き込み

先ほど作成したmain.pyをampyを使って書き込む。コマンドプロンプトで、以下を実行。シリアルポート名とボーレート、ファイル名を指定。

ampy -p COM3 -b 115200 put main.py

ちなみに、ampyで書き込みを行うと当り前だがコンソールモードが実行できなくなる。またコンソールモードが使いたい場合、

ampy -p COM3 -b 115200 rm main.py

で、main.pyを削除すればよいようだ。


5.動作確認

リセットボタンを押したところ、プログラムが動き始め、LEDが点滅した。どうやらちゃんと書き込めたようだ。というわけで、とりあえずMicroPython環境が整った。


まとめ

MicroPythonの開発環境を整え、ESP-WROOM-32で無事Lチカできた。次は、Wi-Fiなどを使えるようにしてみたい。

概要

LambdaからAWS KMSの暗号化・復号化を試し、うまくいった。


背景と目的

DynamoDBに、暗号化した情報を保存する必要がでた。Lambdaで、暗号化/復号化処理をやって保存できるようにしてみる。


詳細

0.方法

の2つがあるが、1つ目は、SDK一式をまとめてLambdaで実行しようとしたらインポートエラー。ローカルで試すことはできるんだけど、SDKに含まれるcryptographyというモジュールが、環境ごとにコンパイルされたものが必要らしく、うまくいかなかった。(私の場合、Windowsでテスト、LambdaはLinux?)

なので、boto3でやることにした。(冷静に考えると2つ目でやるのが当たり前だが、Webで調べたら1つ目が先に出てきてしまったせいで、引っ張られてしまった。。。)


1.コーディング

こちらこちらを参考とした。

1.1 暗号化
import boto3
import base64

# KEYID
KEYID = "arn:aws:kms:{region}:{account-id}:key/{keyid}"

kms = boto3.client("kms")

# 暗号化したいテキスト
my_plaintext = u'こんにちは'
    
# 暗号化
resp = kms.encrypt(
    KeyId = KEYID,
    Plaintext = my_plaintext
)
my_ciphertext = resp["CiphertextBlob"]

# base64エンコードする
txt = base64.b64encode(my_ciphertext)

base64エンコードしているのは、DynamoDBに保存する際に、文字列型として保存するため。

1.2 復号化
# まず、base64でコードする(DynamoDB保存用にエンコードされている前提)
my_ciphertext = base64.b64decode(item["test"])

# 復号化
resp = kms.decrypt(
    CiphertextBlob = my_ciphertext
)
decrypted_plaintext = resp["Plaintext"]

# 復号化されたものは、unicode文字列なので、適宜変換
decrypted_plaintext = decrypted_plaintext.decode("utf-8")

2.テスト

上記の暗号化、復号化を

print "decrypted_plaintext = " + decrypted_plaintext

として実行したところ、正しく動作した。

decrypted_plaintext = こんにちは

DynamoDBには、暗号化&base64エンコードされた文字列が保存された。


まとめ

LambdaからAWS KMSの暗号化・復号化を試し、うまくいった。

概要

Sphinxで、コードサンプルのシンタックスハイライトをさせる方法を知らべ、動作を確認した。


背景と目的

Sphinxで、コードサンプルのシンタックスハイライトがしたくなった。


詳細

0.方法の調査

こちらによれば、Pygmentsというものをインストールし、RestructuredTextのソース内にディレクティブを記述すればよいようだ。


1.Pygmentsのインストール

Pygmentsに従いインストールのつもりだったが、私の環境にはすでに入っていた。インストールする場合は、以下。

pip install Pygments


2.ディレクティブの記述

以下のように、highlightディレクティブをコードブロック(2連コロン)の行の前に書いておけばよい。言語は、ディレクティブの後ろに、"c"などと書けばOK。以下は、Arduinoの例。

.. highlight:: c

::

    // 定数
    const int GPIO_LED = 12; // LEDを接続するGPIOポート番号
    const int CYCLE_IN_MSEC = 1000; // 点滅周期

    void setup() {
        // GPIOポートの設定
        pinMode(GPIO_LED, OUTPUT); // GPIO_LED番ポートを出力に設定
    }

    void loop() {
        // 点滅
        digitalWrite(GPIO_LED, HIGH) // HIGHで点灯
        delay(CYCLE_IN_MSEC / 2); // 半周期待つ
        digitalWrite(GPIO_LED, LOW); // LOWで消灯
        delay(CYCLE_IN_MSEC / 2); // 半周期待つ
    }

なお、サポートされている言語は、こちらを参照。(HTML、javascript、C/C++、MATLAB、javaあたりがあるので個人的には十分。)


3.動作確認

実際に、記述してコンパイルしてみた結果が以下。ちゃんとハイライトされている。

  • ハイライトがある場合

20171101003749

  • ハイライトがない場合

20171101003812

まとめ

Sphinxで、コードサンプルのシンタックスハイライトをさせる方法を知らべ、動作を確認できた。

PythonのIDE Sypderで、ブレイクポイントを張ってデバッグをしようと思ったら、いきなり以下のエラー。

  • Python 2.7.10
  • Spyder 2.3.5.0
  • Window7 32bit
Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 704, in debugfile
    debugger.run("runfile(%r, args=%r, wdir=%r)" % (filename, args, wdir))
  File "C:\Python27\lib\bdb.py", line 400, in run
    exec cmd in globals, locals
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 684, in runfile
    execfile(filename, namespace)
  File "C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.py", line 63, in execfile
    scripttext = builtins.open(fname).read()+ '\n'
IOError: [Errno 2] No such file or directory: 'スクリプトのフルパス'

原因は、 C:\Python27\lib\site-packages\spyderlib\widgets\externalshell\sitecustomize.pyの509~510行目

# Breakpoints don't work for files with non-ascii chars in Python 2
# Fixes Issue 1484

を見る限り、non-ascii chars(要するに日本語)がデバッグ対象の.pyファイルのパスに含まれているせいのようだ。510行目に、この不具合を直したのか直す意志があるだけなのかよくわからない記述があるが、実際直っていない。日本語の含まれないパスに置いて再度実行したところ、ちゃんと動いた。

というわけで、Spyderでデバッグするときは、日本語が含まれないパスに置くこと。

このページのトップヘ