Masaponto's Blog

お勉強メモ

TeratermでDvorak配列

はじめに

TeratermWindowsSSHクライアントとして有名なソフトウェアです。 本記事ではTeraterm上でDvorak配列を実現する設定方法を紹介します。 Teratermにはキーボードショートカット編集機能があります。 設定ファイルを記述するこで、キーボードの機能を自由にカスタマイズが可能です。
https://ttssh2.osdn.jp/manual/ja/setup/keyboard.html

今回は上記のページを参考にQwerty配列Dvorak配列にkeymapする設定を書きました。
開発環境が、レジストリ変更権限がなくDvorakJのインストールができないWindows、かつSSH先がsudo権限のないLinux、である場合に有効です(涙)。需要あるかな? しかし、keymapできる数は99個だったり、装飾キーとの組み合わせも一つのkeymapとして設定する必要があったりするため限界があります。

KEYBOARD.CNF

下記を既存のKEYBORD.CNFに追記してご利用ください。

[User keys]
User1 = 16,0,$27
; q to '
User2 = 17,0,$2c
; w to ,
User3 = 18,0,$2e
; e to .
User4 = 19,0,$70
; r to p
User5 = 20,0,$79
; t to y
User6 = 21,0,$66
; y to f
User7 = 22,0,$67
; u to g
User8 = 23,0,$63
; i to c
User9 = 24,0,$72
; o to r
User10 = 25,0,$6C
; p to l
User11 = 26,0,$2F
; @ to /
User12 = 27,0,$3D
; [ to =
User13 = 31,0,$6F
; s to o
User14 = 32,0,$65
; d to e
User15 = 33,0,$75
; f to u
User16 = 34,0,$69
; g to i
User17 = 35,0,$64
; h to d
User18 = 36,0,$68
; j to h
User19 = 37,0,$74
; k to t
User20 = 38,0,$6E
; l to n
User21 = 39,0,$73
; ; to s
User22 = 40,0,$2D
; : to -
User23 = 43,0,$5C
; ] to \
User24 = 44,0,$3B
; z to ;
User25 = 45,0,$71
; x to q
User26 = 46,0,$6A
; c to j
User27 = 47,0,$6B
; v to k
User28 = 48,0,$78
; b to x
User29 = 49,0,$62
; n to b
User30 = 51,0,$77
; , to w
User31 = 52,0,$76
; . to v
User32 = 53,0,$7A
; / to z
User33 = 515,0,$40
; " to @
User34 = 517,0,$5E
; & to ^
User35 = 520,0,$26
; ' to &
User36 = 521,0,$2A
; ( to *
User37 = 522,0,$28
; ) to (
User38 = 523,0,$29
;   to )
User39 = 524,0,$7D
; = to {
User40 = 525,0,$7B
; ~ to }
User41 = 125,0,$60
;  to `
User42 = 553,0,$7E
; to ~
User43 = 528,0,$22
; Q to "
User44 = 529,0,$3C
; W to <
User45 = 530,0,$3E
; E to >
User46 = 531,0,$50
; R to P
User47 = 532,0,$59
; T to Y
User48 = 533,0,$46
; Y to F
User49 = 534,0,$47
; U to G
User50 = 535,0,$43
; I to C
User51 = 536,0,$52
; O to R
User52 = 537,0,$4C
; P to L
User53 = 538,0,$3F
; ` to ?
User54 = 539,0,$2B
; { to +
User55 = 543,0,$4F
; S to O
User56 = 544,0,$45
; D to E
User57 = 545,0,$55
; F to U
User58 = 546,0,$49
; G to I
User59 = 547,0,$44
; H to D
User60 = 548,0,$48
; J to H
User61 = 549,0,$54
; K to T
User62 = 550,0,$4E
; L to N
User63 = 551,0,$53
; + to S
User64 = 552,0,$5F
; * to _
User65 = 555,0,$7C
; } to |
User66 = 556,0,$3A
; Z to :
User67 = 557,0,$51
; X to Q
User68 = 558,0,$4A
; C to J
User69 = 559,0,$4B
; V to K
User70 = 560,0,$58
; B to X
User71 = 561,0,$42
; N to B
User72 = 563,0,$57
; < to W
User73 = 564,0,$56
; > to V
User74 = 565,0,$5A
; ? to Z
User75 = 1043,0,$10
; C-r to C-p
User76 = 1044,0,$19
; C-t to C-y
User77 = 1045,0,$06
; C-y to C-f
User78 = 1046,0,$07
; C-u to C-g
User79 = 1047,0,$03
; C-i to C-c
User80 = 1048,0,$12
; C-o to C-r
User81 = 1049,0,$0C
; C-p to C-l
User82 = 1055,0,$0F
; C-s to C-o
User83 = 1056,0,$05
; C-d to C-e
User84 = 1057,0,$15
; c-f to c-u
User85 = 1058,0,$09
; c-g to c-i
User86 = 1059,0,$04
; c-h to c-d
User87 = 1060,0,$08
; c-j to c-h
User88 = 1061,0,$14
; c-k to c-t
User89 = 1062,0,$0E
; c-l to c-n
User90 = 1063,0,$13
; c-; to c-s
User91 = 1069,0,$11
; c-x to c-q
User92 = 1070,0,$0A
; C-c to C-j
User93 = 1071,0,$OB
; c-v to c-k
User94 = 1072,0,$18
; c-b to c-x
User95 = 1073,0,$02
; c-n to c-b
User96 = 1075,0,$17
; c-, to c-w
User97 = 1076,0,$16
; c-. to C-v
User98 = 1077,0,$1A
; c-/ to c-z

あとがき

やはり自作キーボードが最適解ですかね。

Thinkpad X1 carbonとXFCEで4Kディスプレイを利用する

4Kディスプレイを買いました。

持っているもの

買ったもの

  • https://www.amazon.co.jp/gp/product/B07LC3L6JG
    DELLかLGかで迷ったのですが、足がしっかりしているのでDELLにしました。また、ドット欠けの保証があったのも選定理由です。
  • https://www.amazon.co.jp/gp/product/B0778D9MZ3
    Mini DisplayPort to Thunderbolt3ケーブルです。60Hzで出力するために購入しました。HDMIでの接続では30Hzでの出力になってしまい60Hzで出力できませんでした。30Hzだとyoutubeの動画がカクついて見えるのでかなり厳しかったです。また、本ケーブルで接続することで、XFCEが自動で4Kを検出してくれて大変便利です。HDMIで接続した場合は、WQHDで出力されてしまうため、手動でから4Kへ変更する必要がありました。

Hidpi

XFCEの設定でDPIをポチポチすれば4Kでも快適に利用可能なサイズを設定できます。ちなみに私は160に設定しております。自分の場合はもともとWQHDのthinkpadなので普段使いのために設定していましたが、今回の4K出力にあたってはそのままの設定で問題ありませんでした。
詳しくはArchWikiを参照しましょう。
https://wiki.archlinux.jp/index.php/HiDPI

感想

ChromeでのブラウジングemacsやPycharmでのコーディング等を行っておりますが、今の所問題なく快適に使えております。thunderboltケーブルをぶっ刺すだけで出力されます。新年度に向けてぜひ!

TensorflowLiteでAndroidでCNN

この記事はAizu Adc 2018 20日目にかかれた4日目の記事です。
前の人は@xatu0202氏, 次の人@ywkw氏です。

こんにちは。@masapontoです。最近は東京でデータ分析太郎として暮らしております。

気がづくと本ブログはアドベントカレンダーでしか書かなくなってしまいました。 今回はTensorflow Lite を使って Android上で画像分類(CNN)をする話をしようと思います。

TonsorFlow Liteを紹介する多くの日本語記事は、サンプルとして用意されている学習済みモデルを動かしてみたっていう話が多い気がします(主観)。 それに対し本記事では、自身で定義して学習を行ったCNNを動かしてみようと思います。 データはCIFAR10っていう画像データセットを使います。
CIFAR10はairplane, automobile, bird, cat, deer, dog, frog, horse, ship, truckの10クラスの画像データセットです。 https://www.cs.toronto.edu/~kriz/cifar.html

※本記事の内容は、単に動かしてみたよって感じの記事なので、詳細な説明を求める方にはおすすめできません。 まぁ何いっても公式ドキュメントを読めばいい話です(完)。 https://www.tensorflow.org/lite/devguide

Tensorflow Liteとは

モバイル端末や組み込み端末で機械学習の推論をするぞいっていうライブラリです。
https://www.tensorflow.org/lite/

基本的な使い方としては、以下の3ステップです。

  1. PC上のTensorFlowでNNを学習させてモデルMを作る。 (on Python)
  2. MをAndroid向けに変換し、 M'を作る。 (on Python)
  3. M'をAndroidアプリのassets/フォルダとかにいれて、呼び出す。 (on Android)

手順 on Python

私の開発環境は、ArchLinux (x64) / Python 3.6.1/ tensorflow 0.12.0 です。

で、下記のようにCNNを学習させるコードを書きます。 いつのまにかTensorFlowにKerasインターフェースが入っていたので、それを使って書いてみました。

# !/usr/bin/env python

import tensorflow as tf


def convolutional():
    model = tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(filters=32,
                               kernel_size=[5, 5],
                               padding='same',
                               activation='relu',
                               input_shape=(32, 32, 3),
                               name='input'),
        tf.keras.layers.MaxPooling2D(pool_size=[2, 2], strides=2),
        tf.keras.layers.Conv2D(filters=32,
                               kernel_size=[5, 5],
                               padding='same',
                               activation='relu'),
        tf.keras.layers.MaxPooling2D(pool_size=[2, 2], strides=2),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(units=1024, activation='relu'),
        tf.keras.layers.Dropout(rate=0.4, trainable=True),
        tf.keras.layers.Dense(units=10, activation='softmax', name='output')
    ])

    return model


def main():

    (X_train, Y_train), (X_test, Y_test) = tf.keras.datasets.cifar10.load_data()

    X_train = X_train/255
    X_test = X_test/255


    print(X_train.shape)

    Y_train = tf.keras.utils.to_categorical(Y_train, 10)
    Y_test = tf.keras.utils.to_categorical(Y_test, 10)

    model = convolutional()
    model.compile(optimizer=tf.keras.optimizers.Adam(0.0001),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])

    model.fit(X_train,
              Y_train,
              epochs=100,
              batch_size=50,
              verbose=1)


    # モデルの図示 (任意)
    tf.keras.utils.plot_model(model, to_file='model.png', show_shapes=True)

    # モデルをh5で保存
    keras_file = "model_keras/cnn_model.h5"
    model.save(keras_file)

    # TFLiteでコンバータを用意
    converter = tf.contrib.lite.TFLiteConverter.from_keras_model_file(keras_file,
                                                                      input_arrays=['input_input'],
                                                                      output_arrays=['output/Softmax'],
                                                                      input_shapes={'input_input': [None, 32, 32, 3]})
    # コンバート
    tflite_model = converter.convert()
    open("model_keras/converted_model.tflite", "wb").write(tflite_model)


if __name__ == '__main__':
    main()

ドキュメントにあまり詳しくない部分があって、ググって試行錯誤してなんとか動きました。
converterを作るときに、input_arraysとかoutput_arraysで名前を指定する必要があるっぽいです。 あと入力層のinput_shapesも指定する必要があるみたいです。(このへんよくわかってないので教えてください)

手順 on Android

Kotlinコードを下記においておきます。
動作確認環境はAndroidStudio20172.3/Kotlin 1.2.71/Zenfone 5/Android 8.0.0です。

準備 (TensorFlowLite)

下記を追記 (src: https://codelabs.developers.google.com/codelabs/tensorflow-for-poets-2-tflite/#6)
- app/build.gradle

android {
    // Add
    aaptOptions {
        noCompress "tflite"
    }
}

dependencies {
    // Add
    implementation 'org.tensorflow:tensorflow-lite:1.12.0'
}

また、作成したモデル(.tfliteファイル)は、AppName/app/src/main/assets/に入れます。

コード

下記のようなコードを書いて、MainActivityから呼びます。   classifyImageFromPath(file) でファイルパスを指定してやって分類する感じです。

package io.github.masaponto.tflitecifarten

import android.app.Activity
import android.graphics.Bitmap
import org.tensorflow.lite.Interpreter
import java.io.FileInputStream
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
import android.graphics.BitmapFactory
import java.io.File
import java.nio.ByteOrder


class Classifier(activity: Activity) {
    private val MODEL_NAME = "converted_model.tflite"

    private val IMAGE_SIZE = 32
    private val IMAGE_MEAN = 128
    private val IMAGE_STD = 128.0f

    private var tffile: Interpreter
    private var labelProbArray: Array<FloatArray>

    init {
        tffile = Interpreter(loadModelFile(activity)) // deprecated
        labelProbArray = Array(1){FloatArray(10)}
    }


    @Throws(IOException::class)
    private fun loadModelFile(activity: Activity): MappedByteBuffer {
        val fileDescriptor = activity.assets.openFd(MODEL_NAME)
        val inputStream = FileInputStream(fileDescriptor.fileDescriptor)
        val fileChannel = inputStream.channel
        val startOffset = fileDescriptor.startOffset
        val declaredLength = fileDescriptor.declaredLength
        return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
    }

    fun classifyImageFromPath(path: String): Int {
        val file = File(path)

        if (!file.exists()) {
            throw Exception("Fail to load image")
        }

        // load image
        val bitmap = BitmapFactory.decodeFile(file.path)
        val scaledBitmap = Bitmap.createScaledBitmap(bitmap, IMAGE_SIZE, IMAGE_SIZE,true)

        // convert bitmap to bytebuffer
        val byteBuffer = convertBitmapToByteBuffer(scaledBitmap)

        // classification with TF Lite
        val pred = classifyImage(byteBuffer)

        return onehotToLabel(pred[0])
    }

    private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
        val byteBuffer = ByteBuffer.allocateDirect( IMAGE_SIZE * IMAGE_SIZE * 3 * 4)
        byteBuffer.order(ByteOrder.nativeOrder())
        val intValues = IntArray(IMAGE_SIZE * IMAGE_SIZE)

        bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
        var pixel = 0
        for (i in 0 until IMAGE_SIZE) {
            for (j in 0 until IMAGE_SIZE) {
                val v = intValues[pixel++]

                byteBuffer.putFloat((((v.shr(16) and 0xFF) - IMAGE_MEAN) / IMAGE_STD))
                byteBuffer.putFloat((((v.shr(8) and 0xFF) - IMAGE_MEAN) / IMAGE_STD))
                byteBuffer.putFloat((((v and 0xFF) - IMAGE_MEAN) / IMAGE_STD))
            }
        }
        return byteBuffer
    }

    fun classifyImage(bytebuffer: ByteBuffer): Array<FloatArray> {
        tffile.run(bytebuffer, labelProbArray)
        return labelProbArray
    }

    private fun onehotToLabel(floatArray: FloatArray): Int {
        val tmp = floatArray.indices.maxBy { floatArray[it] } ?: -1
        return tmp + 1
    }
}

全体のリポジトリ

github.com

参考: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/examples/android/app/src/main/java/org/tensorflow/demo/TFLiteImageClassifier.java

convertBitmapToByteBufferもよくわかってないです。(つらい..)

動かす

適当に拾った猫ちゃん画像を分類してみましょう。
f:id:masaponto:20181219234316j:plain:w200

f:id:masaponto:20181219234322j:plain:w200
クラスラベルは1:airplane, 2:automobile, 3:bird, 4:cat, 5:deer, 6:dog, 7:frog, 8:horse, 9:ship, 10:truckの順ぽいのでたぶん動いてますね(雑)。

まとめ。

Kerasインターフェースを使って簡単にCNNの学習コードかいて、携帯端末用に変換する事ができました。 ちなみに、tfliteに変換するときにNoneでなくintegerで指定すればバッチサイズを変更指定できます。 Android端末で機械学習ができるので、わりと作れるアプリの幅が広がった気がします。 こんな調子でお仕事がんばるぞい。

参考