Huawei製のスマホでAndroidの実機デバッグ

windows環境で、Huawei製のスマホAndroidの実機デバックをしようとしたときに少し詰まったのでまとめておきます。

PC(Windows)側の設定

Hi suiteの導入

Huawei製品で実機デバックをするときにはHi Suiteというアプリをwindowsにインストールする必要があるようです。これをインストールしないとAndroid Studioで正しく認識してくれませんでした。

スマホ側の操作

開発者モードを有効にする

システム設定 ➡【端末情報】➡ 【ビルド番号】に進み、ビルド番号のところを数回押し続けるとデベロッパーモードになります。

USBデバックをOnにする

上の設定でシステム設定の画面に【開発者向けオプション】が表示されるので、その中の【USBデバッグ】を有効にします。

USB接続時の挙動を切り替える

【開発者オプション】➡【USB設定を選択】の画面で【ファイル(MTP)】を選択します。



以上でAndroid Studioで認識してくれるようになりました。

多層パーセプトロンの実装

Network in network の論文を読んで自分が多層パーセプトロンをまともに実装したことがないということに気づきました。この記事ではKerasのリファレンスを参考に実装してみたいと思います。
keras.io

多層パーセプトロンとは

パーセプトロンを何層か重ねたものを多層パーセプトロンと呼びます。パーセプトロンは入力となる信号と、それぞれの入力に対する重みの演算を出力します。単独のパーセプトロンでは非常に限られた表現力しか持てませんが、層を重ねることで表現力を強化することができます。

Kerasを使った多層パーセプトロンの実装

import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import SGD
from keras.utils import plot_model

# ダミーデータ生成
import numpy as np
x_train = np.random.random((1000, 20))
y_train = keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)
x_test = np.random.random((100, 20))
y_test = keras.utils.to_categorical(np.random.randint(10, size=(100, 1)), num_classes=10)

model = Sequential()
# Dense(64) は,64個のhidden unitを持つ全結合層です.
# 最初のlayerでは,想定する入力データshapeを指定する必要があり,ここでは20次元としてます.
model.add(Dense(64, activation='relu', input_dim=20))
model.add(Dropout(0.5))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])

model.fit(x_train, y_train,
          epochs=20,
          batch_size=128)
score = model.evaluate(x_test, y_test, batch_size=128)

データの詳細

学習データは、20次元の特徴量を持ったものが1000個用意されています。その一つ一つが、1から10の値をとるクラスのいずれかに属しています。テストデータとして、同じフォーマットのデータが100個用意されています。

モデルの詳細

入力層、中間層、出力の3層構造になっています。入力層には特徴量の次元と同じ数を input_dim 引数にしています。入力層と中間層のユニット数はともに64で活性化関数はreluを使用します。最後の出力層にはクラスの数と同じだけのユニット数を指定します。今回は10クラスの分類なので、出力層のユニット数は10になります。

最適化アルゴリズムには確率的勾配降下法(Stochastic Gradient Descent)を用います。SGDの引数を以下にまとめます。
lr:学習率(learning rate)
momentum:前回の更新量に対してα倍して加算する慣性項の係数。
decay:学習率を徐々に減衰させていくときの値。
nesterov:nesterov momentumを使うかどうかを指定する。

モデルを学習する前にcompileをします。後に訓練データをfitメソッドに渡して学習させます。

まとめ

今回のデータは完全にランダムなデータだったので結果に関しては省略します。
本当はモデルを可視化することも載せたかったのでうまく動かなかったのでまたやり直してみようと思います。

Kotlinのlet関数

例えば、String?型の変数を作成した場合メソッドやプロパティを呼び出すには、その都度?.をつけなくてはいけない。

    var name:String? = "Peter"
    var upperCase = name?.toUpperCase()
    var len = name?.length
    print("$upperCase $len")

この操作をlet関数を使うと以下のように書き換えることができる。

    var output = name?.let {
    	var upperCase = it.toUpperCase()
    	var len = it.length
    	"$upperCase $len"
    }
    print(output)

let関数を使うと、いちいち?.を書かなく済む。letのラムダ式内ではnameはnullでないことが保証されていて、itを用いることで参照することができる。

猫の写真をGANで学習させてみる

実家でノルウェージャン・フォレストキャットを飼っていました。かなりおっとりした性格の子で、めちゃくちゃかわいかったんですが、ある嵐の夜に外に出てしましそれ以来帰ってくることがありませんでした。そんな愛しのにゃんこをどうにか再現できないかと思い、GANに頼ることにしました。そこで今回の記事はこちらの記事を参考にGANの実装をします。先に結果を言ってしまいますが、結果は失敗でした。理由は圧倒的なデータ不足かなと思います。

GANとは

GANの詳細はコードの参照先でもあるこの記事をご覧ください。

用意したデータ

f:id:memo_dl:20191020135503p:plain
猫ちゃんの写真
実家でノルウェージャン・フォレストキャットを飼っていました。かなりおっとりした性格の子で、めちゃくちゃかわいかったんですが、ある嵐の夜に外に出てしましそれ以来帰ってくることがありませんでした。そんな愛しのにゃんこをどうにか再現できないかと思い、GANに頼ることにしました。そこで今回の記事はこちらの記事を参考にGANの実装をします。先に結果を言ってしまいますが、結果は失敗でした。理由は圧倒的なデータ不足かなと思います。

GANの実装

参考にさせていただいた記事のコードに以下の2つの関数を追加しました。

  • get_image_from_directory()
  • change_img_size()

コードの全体像はこちらです。

### -*-coding:utf-8-*-
from __future__ import print_function, division

from keras.datasets import mnist
from keras.layers import Input, Dense, Reshape, Flatten, Dropout
from keras.layers import BatchNormalization, Activation, ZeroPadding2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.models import Sequential, Model
from keras.optimizers import Adam

import matplotlib.pyplot as plt

import sys

import numpy as np

class GAN():
    def __init__(self):
        #mnistデータ用の入力データサイズ
        self.img_rows = 128 
        self.img_cols = 128
        self.channels = 3
        self.img_shape = (self.img_rows, self.img_cols, self.channels)
        
        self.img_path = "hogehoe"

        # 潜在変数の次元数 
        self.z_dim = 100

        optimizer = Adam(0.0002, 0.5)

        # discriminatorモデル
        self.discriminator = self.build_discriminator()
        self.discriminator.compile(loss='binary_crossentropy', 
            optimizer=optimizer,
            metrics=['accuracy'])

        # Generatorモデル
        self.generator = self.build_generator()
        # generatorは単体で学習しないのでコンパイルは必要ない
        #self.generator.compile(loss='binary_crossentropy', optimizer=optimizer)

        self.combined = self.build_combined1()
        #self.combined = self.build_combined2()
        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

    def build_generator(self):

        noise_shape = (self.z_dim,)
        model = Sequential()

        model.add(Dense(256, input_shape=noise_shape))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(1024))
        model.add(LeakyReLU(alpha=0.2))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(np.prod(self.img_shape), activation='tanh'))
        model.add(Reshape(self.img_shape))

        model.summary()

        return model

    def build_discriminator(self):

        img_shape = (self.img_rows, self.img_cols, self.channels)
        
        model = Sequential()

        model.add(Flatten(input_shape=img_shape))
        model.add(Dense(512))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dense(256))
        model.add(LeakyReLU(alpha=0.2))
        model.add(Dense(1, activation='sigmoid'))
        model.summary()

        return model
    
    def build_combined1(self):
        self.discriminator.trainable = False
        model = Sequential([self.generator, self.discriminator])
        return model

    def build_combined2(self):
        z = Input(shape=(self.z_dim,))
        img = self.generator(z)
        self.discriminator.trainable = False
        valid = self.discriminator(img)
        model = Model(z, valid)
        model.summary()
        return model


    def train(self, epochs, batch_size=128, save_interval=50):

        # mnistデータの読み込み
        #(X_train, _), (_, _) = mnist.load_data()
        X_train = self.change_img_size(picture_size=(128, 128))

        # 値を-1 to 1に規格化
        X_train = (X_train.astype(np.float32) - 127.5) / 127.5
        # X_train = np.expand_dims(X_train, axis=3)

        half_batch = int(batch_size / 2)

        num_batches = int(X_train.shape[0] / half_batch)
        print('Number of batches:', num_batches)
        
        for epoch in range(epochs):

            for iteration in range(num_batches):

                # ---------------------
                #  Discriminatorの学習
                # ---------------------

                # バッチサイズの半数をGeneratorから生成
                noise = np.random.normal(0, 1, (half_batch, self.z_dim))
                gen_imgs = self.generator.predict(noise)


                # バッチサイズの半数を教師データからピックアップ
                idx = np.random.randint(0, X_train.shape[0], half_batch)
                imgs = X_train[idx]

                # discriminatorを学習
                # 本物データと偽物データは別々に学習させる
                d_loss_real = self.discriminator.train_on_batch(imgs, np.ones((half_batch, 1)))
                d_loss_fake = self.discriminator.train_on_batch(gen_imgs, np.zeros((half_batch, 1)))
                # それぞれの損失関数を平均
                d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)


                # ---------------------
                #  Generatorの学習
                # ---------------------

                noise = np.random.normal(0, 1, (batch_size, self.z_dim))

                # 生成データの正解ラベルは本物(1) 
                valid_y = np.array([1] * batch_size)

                # Train the generator
                g_loss = self.combined.train_on_batch(noise, valid_y)

                # 進捗の表示
                print ("epoch:%d, iter:%d,  [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, iteration, d_loss[0], 100*d_loss[1], g_loss))

                # 指定した間隔で生成画像を保存
                if epoch % save_interval == 0:
                    self.save_imgs(epoch)

    def save_imgs(self, epoch):
        # 生成画像を敷き詰めるときの行数、列数
        r, c = 5, 5

        noise = np.random.normal(0, 1, (r * c, self.z_dim))
        gen_imgs = self.generator.predict(noise)

        # 生成画像を0-1に再スケール
        gen_imgs = 0.5 * gen_imgs + 0.5

        fig, axs = plt.subplots(r, c)
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i,j].imshow(gen_imgs[cnt, :,:,0])
                axs[i,j].axis('off')
                cnt += 1
        fig.savefig("foo/_%d.png" % epoch)
        plt.close()


    def change_img_size(self, picture_size=(128, 128)):

        from PIL import Image
        
        image_list = self.get_image_from_directory()
        resized_img_list = []

        for image in image_list:
            img = Image.open(image)
            img_resize = img.resize(picture_size)
            img_resize = np.array(img_resize, np.float)
            resized_img_list.append(img_resize)

        return np.array(resized_img_list)


    def get_image_from_directory(self):
        """
        path:画像ファイルの親ディレクトリまでを指定
        デフォルトでjpgのファイルを取得する
        """
        import glob

        path = self.img_path
        image_list = glob.glob(path + "/*.jpg")
        return image_list


if __name__ == '__main__':
    gan = GAN()
    gan.train(epochs=1000, batch_size=16, save_interval=1)

生成画像

f:id:memo_dl:20191020140655p:plain
うーん。全然だめですね笑
ちなみに、1500epochあたりから学習が全く進まなくなってノイズみたいな画像しか出力しなくなってしまいました。一定間隔で保存した写真の中で、それなりに猫っぽい姿が見えたものが上の写真です。

次の課題

なんといっても次はデータ不足の問題をどう克服すればいいのかを考えることだと思います。猫ちゃんはもういないので新たに写真を撮ることができません。アイディアとしては他の猫の写真を大量に用意して、そこから猫の特徴を抽出した後に、我が猫ちゃんの特徴を張っつけるみたいな感じなのかなと考えています。多分論文探せば同じようなことやっている人が見つかるでしょう。

計量経済学の使いかた[基礎編]を読んで(第2章:最小二乗法)

大学の図書館に計量経済学の使いかたという本がおいてあり、気になったので読んでみることにしました。上[基礎編]と下[応用編]の2シリーズで構成されています。内容は回帰モデルを使って分析を進めていく際の疑問点についてやさしくまとめたものになっています。「なぜこうなるのか」という理論的な話題よりも、「どうやって分析を進めるのか」という実践的な内容になっています。理論な話題を取り上げる本は多く存在すると思いますが、こうした実践的な内容がまとめられているものは多くなく、本当にありがたいです。
 この記事では、上[基礎編]の最小二乗法について簡単にまとめていきたいと思います。

なぜ最小二乗法(OLS)を使うのか

OLSを使う理由は以下のものがあげられています

1. 比較的扱いやすい
2. 残差二乗和を最小化することは理論的にかなり妥当なこと
3. OLSは数多くの有用な性質を持っている

1と2については単純で、OLSは紙と鉛筆を使えば(頑張れば)計算できてしまうことと、残差に関する正、負を気にしなくてOKということです。3については、残差の和がちょうど0になることと、一連の仮定の下で最良推定量となることを挙げています。

独立変数が1つの場合の最小二乗法

まずは、独立変数が1つという、以下の式のようなとてもシンプルなモデルを考えます。

Y_i = \beta_0 + \beta_1 X_i + \varepsilon_i

この式のβ₁は以下の式で求まります。

 \displaystyle \hat{\beta}_1 = \frac{\sum_{i=1}^N [(X_i-\overline{X})(Y_i-\overline{Y})] } {\sum_{i=1}^N (X_i - \overline{X})^2 }

さらにこのβ₁の値を用いてβ₀の値を求めます。

 \hat{\beta_0} = \overline{Y} - \hat{\beta_0}\overline{X}

この様な手順で最小二乗法は実行されます。

全変動、回帰変動、残差変動

観測値と観測値の平均との差を二乗した値を全変動と呼びます。つまり、分散です。そのうち推定された式でもって説明できるものを回帰変動と呼びます。そして、回帰変動では表現することのできない残りの変動を残差変動と呼びます。
以下の図を見ていただければ何となく関係性がつかめると思います。
f:id:memo_dl:20191020134021p:plain

全変動、回帰変動、誤差変動は以下の式のような関係があります。

 全変動(TSS)= 回帰変動(ESS)+ 残差変動(RSS)

このような関係を分散分解と呼びます。

モデルの当てはまりについて

回帰結果が算出されたら次はモデルについてよく考察する手順に移ります。具体的には以下の様な事柄について考える必要があります。

  • 回帰式は信頼できる理論によって支持されているか
  • 推定された回帰式はデータにどれほどうまく当てはまっているか
  • データセットは十分に豊富で正確であるか
  • OLSは、この回帰式に使用されるべき最良推定量であるか
  • 推定された係数は、データを収集する前に研究者が作り上げた予想にどれほどうまく一致しているか
  • 明らかに重要な変数がすべて、回帰式の中に含まれているか
  • 理論的に最も道理に適った関数型が使用されているか
  • 回帰分析は、主要な計量経済学の問題を回避できているか

<参考文献> 計量経済学の使いかた上[基礎編] 66頁

上記のリストについて検証していくには背景となる理論について熟知していることも必要ですが、モデルの当てはまりについて定量的に判断できる材料があると便利そうです。そこで使われるのが決定係数と自由度修正済み決定係数です。

決定係数R²

決定係数R²は一般的に用いられている当てはまりの尺度です。回帰変動(ESS)の全変動(TSS)に対する比率で表され、以下の式で表すことができます。

 R^2 = \frac{ESS}{TSS} = 1 - \frac{RSS}{TSS} = 1 - \frac{\sum{e_i^2}}{\sum{(Y_i - \overline{Y})^2}}

R²の値は0から1の値をとり、値が大きいほどモデルの当てはまりが良いといえます。直感的にはR²の値だけを見ていればモデルの当てはまりについては評価できるような気がしますが、実は決定係数には、その値が減少しないという問題点があります。つまり独立変数を際限なく増やしていけばR²の値は増加していってしまうのです。一般に、目的変数の変動をあまり説明できない独立変数まで回帰式に取り込むことは推奨されません。その理由としては、自由度が低下するためです。自由度が低下すると推定された式の信頼度が低下してしまいます。

自由度

自由度とは、観測値Nと推定される値(推定する係数の数+1(切片))の個数の差を表します。

自由度(degree \: of \: freedom)=N-(K+1)

N:観測数、K+1:推定する係数の個数(1は切片の分)

自由度修正済み決定係数

自由度修正済み決定係数を調べることで、回帰式に独立変数を追加する際の当てはまりの増加分と、自由度の減少分を比較することができます。つまり、独立変数をあらたに加えるかどうかの指標として使うことができるのです。これは、自由度修正済み決定係数がある独立変数が追加されたときに(増加・減少・不変)のいずれの変化をとりうることによります。
以下に自由度修正済み決定係数の式を示します。

 \overline{R^2} = 1 - \frac{\sum{e_i^2}/(N-K-1)}{\sum{(Y_i-\overline{Y})^2/(N-1)}}

決定係数R²と比較して自由度による修正が入っていることがわかると思います。この修正により、従属変数が同じで、独立変数の個数が異なる式に対して、式の当てはまりの良さを比較することができます。

バブルソートの実装をしてみる

テストコードの雰囲気をつかむためにも、Pythonバブルソートを実装してみました。
以下がバブルソートのコードになります。

def bubble_sort(arr):
 for i in range(len(arr)):
  for j in range(len(arr)-1, i, -1):
   if arr[j] < arr[j-1]:
   arr[j], arr[j-1] = arr[j-1], arr[j]
 return arr

そして、以下のものがテスト用のコードになります。テストの書き方はまだ勉強始めたばかりなのでその質はご容赦を、、

import unittest
from bubble_sort import bubble_sort

l1 = [3, 6, 5, 10, 4, 8]
l2 = [9, 8, 10, 5, 1, 4]
l3 = [5, 9, 7, 1, 12, 3]

class Test_bubble_sort(unittest.TestCase):

  def setUp(self):
    l1_out = bubble_sort(l1)
    l2_out = bubble_sort(l2)
    l3_out =bubble_sort(l3)
    list_out = [l1_out, l2_out, l3_out]

    l1_expect = [3, 4, 5, 6, 8, 10]
    l2_expect = [1, 4, 5, 8, 9, 10]
    l3_expect = [1, 3, 5, 7, 9, 12]
    list_expect = [l1_expect, l2_expect, l3_expect]

    for i in range(0, 3):
      self.assertEqual(list_out[i], list_expect[i])

if __name__ == '__main__':
  unittest.main()

実行した結果はこんな感じ。

Ran 0 tests in 0.000s

OK

プロセスは終了コード 0 で完了しました

Empty suite

多分OKでしょう。
以上適当バブルソート実装でした。