What is gradient in Gradient Descent? / [勾配降下法]関数の勾配の定義を振り返る

勾配とは?

そもそもの勾配の定義から振り返ろう。以下のような関数fについて考える。

[mathjax]

$$f(x) = 3x_1^2 – 2x_1x_2 + 3x_2^2 -4x_1 -4x_2$$

このときベクトル$latex \nabla f(x)$を点xにおける関数fの勾配(gradient)という。

$$ \nabla f(x) = \left( \frac{\partial f(x)}{\partial x_1}, \frac{\partial f(x)}{\partial x_2}, \cdots, \frac{\partial f(x)}{\partial x_n} \right) \in \mathbb R^n $$

※ただし以下が成り立つ場合(これは2点を結ぶ線分の傾きから求められますよね?)

$$ f(x+d) = f(x) + \nabla f(x)^Td + o(\Vert{d}\Vert), d \in \mathbb R^n$$

この定義に基づくと上式の勾配は以下で表すことができる。

$$\nabla f(x) = \begin{pmatrix}
6x_1-2x_2-4 \\
-2x_1 + 6×2 -4 \\
\end{pmatrix}
$$

ここで勾配の定義をより直感的にするために以下の2点における勾配を考えてみる。

$$a = (0, 0)^T, \nabla f(a) = (-4, -4)^T$$
$$b = (2, 0)^T, \nabla f(b) = (8, -8)^T$$

このようにfにおける各点の勾配は接線に対して垂直なベクトルとして表される。
そして上図の各点において傾きが最大となる方向を表し、勾配と反対になる方向が降下方向となる。

つまり探索方向を勾配$latex \nabla f(x)$の逆(降下)方向に定めて探索することから「勾配降下法」と言われる所以である(と思う。)

勾配降下法の探索イメージ

初期点を(0, 0)とするとたとえばこんな感じ?

Change default file name of screenshot in macOS / macOS スクリーンショットのデフォルトファイル名を変更する

#SCREEN CAPTURE
# スクリーンショットのファイル名に日付を表示する/表示しない
defaults write com.apple.screencapture include-date -bool false

# スクリーンショットをPNG形式で保存(その他のオプション:BMP、GIF、JPG、PDF、TIFF)
defaults write com.apple.screencapture type -string "png"

# スクリーンショットの保存場所
defaults write com.apple.screencapture location ~/Desktop/Screenshots

# スクリーンキャプチャのシャドウを無効にする
defaults write com.apple.screencapture disable-shadow -bool true

# ファイル名を変更する
defaults write com.apple.screencapture name [yourFilename]

# 設定変更を反映
killall SystemUIServer  

参考リンク

https://apple.stackexchange.com/questions/294548/macos-sierra-10-12-screen-captures-how-to-edit-system-files

How to implement Genetic Algorithm with python / 遺伝的アルゴリズムをPythonで実装する

モチベーション

以前子供たちと一緒にめんぼうで遺伝的アルゴリズムを実装しました。その内容をPythonプログラムとして実装することでより一層理解を深めたいと思い始めました。

https://loochs.org/session36-asobi-workshop/

そもそも遺伝的アルゴリズムとは?

以下の書籍が非常に参考となります。今回の実装もこちらに基づいております。

ソースコード

実際の実装は以下リンク先から参照してください。

https://github.com/kichinosukey/GA-Question

サンプルコード

3つの3択問題についての解答を試行するGAになります。

import numpy as np

from lib import crossover, evaluate, gen_population, select, mutation

if __name__ == '__main__':

    answer = [1, 2, 3]
    num_question = len(answer)
    num_survive = 3
    num_population = 5

    population = gen_population(num_population, num_question, (min(answer), max(answer)))
    score_dict = {idx:sum(evaluate(answer, result)) for idx, result in population.items()}
    score_list_sorted = sorted(score_dict.items(), reverse=True, key=lambda x: x[1])
    
    print(score_list_sorted[0])
    cnt = 0
    while score_list_sorted[0][1] != 3:
        

        score_list_selected = select(score_list_sorted, num_survive)
        idx_selected = [t[0] for t in score_list_selected]
        population_selected = [population[idx] for idx in idx_selected]

        pop01 = population[score_list_sorted[0][0]]
        pop02 = population[score_list_sorted[1][0]]
        crv_point = np.random.randint(1, num_question-1)
        new01, new02 = crossover(pop01, pop02, crv_point)
        new01 = mutation(new01, (1, 4))
        new02 = mutation(new02, (1, 4))

        population_selected.append(new01)
        population_selected.append(new02)

        population = population_selected[:]
        population = {i:pop for i, pop in enumerate(population)}
        score_dict = {idx:sum(evaluate(answer, result)) for idx, result in population.items()}
        score_list_sorted = sorted(score_dict.items(), reverse=True, key=lambda x: x[1])       

        print(score_list_sorted[0])

        cnt += 1

        if cnt > 100:
            print(population)
            break

設計コンセプト

遺伝的アルゴリズムは大まかに以下の4つの処理を繰り返していきます。

  • 解候補の生成
  • 各解候補の評価
  • 優秀な解候補の選別
  • 新規解候補の生成(交叉・変異)

以下に各処理に関連するメソッドを載せておきます。

解候補の生成

generateでは単一の候補が決められた範囲内(range_ans)からint型の解を生成します。次にgen_populationでは解候補を指定数生成して各候補ごとにgenerateで解を生成します。

def generate(number_question, range_ans):
    """Generate answer array for questions
    
    Args:
        number_question(int):
        range_ans(tuple):
    
    Returns:
        (np.array):
    """
    return np.random.randint(range_ans[0], range_ans[1], number_question)

def gen_population(num_population, num_question, range_ans):
    """Generate population
    
    Args:
        num_population(int): number of population
        num_question(int): number of question
        range_ans(tuple): range of answers, only supported int
    
    Returns:
        (dict):{index: answer_array}
    """
    return {i:list(generate(num_question, range_ans)) for i in range(num_population)}

各解候補の評価

各候補の解と生成した解を比較して候補ごとの評価を下します。

def evaluate(answer, generated):
    """Evaluate answer array
    
    Args:
        answer(list): answer array
        generated(list): generated array
    """
    result = []
    for a, b in zip(answer, generated):
        if a == b:
            match = 1
        else:
            match = 0
        result.append(match)
    return result

優秀な解候補の選別

evaluateを使って候補ごとにスコアが出たので次の世代に残したい候補数(num_survive)を上位から順に確保しておきます。

def select(score_list, num_survive):
    """Selection method
    
    Args:
        score_list(list):
        num_survive(int): number of survival candidate
    
    Returns:
        (list)
    """
    return score_list[:num_survive]

新規解候補の生成(交叉・変異)

交叉と変異の実装です。簡易的なものなのでここは工夫のしがいがあります。今後の伸び代としてとっておきます。

def mutation(arr, range_mut):
    """Mutation
    Args:
        arr(list): array for mutation
        range_mut(tuple/list): range value for mutation
    """
    idx_max_mut = len(arr)
    idx_mut = np.random.randint(0, idx_max_mut)
    arr[idx_mut] = np.random.randint(range_mut[0], range_mut[1])
    return arr

def crossover(arr_01, arr_02, crv_point):
    """Cross over
    Args:
        arr(list):
        crv_point(int): cross over point
    Returns:
        (tuple)
    
    """
    arr_01_left = arr_01[:crv_point]
    arr_01_right = arr_01[crv_point:]
    arr_02_left = arr_02[:crv_point]
    arr_02_right = arr_02[crv_point:]

    arr_01_new = arr_01_left + arr_02_right
    arr_02_new = arr_02_left + arr_01_right

    return arr_01_new, arr_02_new

最後に

実は以前にの簡単なGAの実装に試みたことがあったのですがうまくいきませんでした。前回との違いはアソビワークショップで綿棒実装したことでかなり理解が深まっていたものと思います。今後の課題としては適用可能な問題が整数型の選択問題に限ったものなのでその適用範囲を広げるといった試みは必要かと思います。

そういえば変異を実装せずに一度サンプルスクリプトを動かしてみるとなかなか解が収束しない状況に陥りました。やはり候補の多様性というのが遺伝子の生き残りにおいては重要な要素なのだと改めて実感した次第です。

Amazon Fire HD タブレットをサブディスプレイ化する方法

やりたいこと(やったこと)

  • 興味本位で3年前に買った Fire HD 8 タブレット
  • 今ではその「操作もっさり感」からまるで使わない
  • せめてPCのサブディスプレイとして運用したい
  • とはいえ,1280×800 と解像度的にはきつい...
  • CPU負荷率グラフとかをモニタリングはどうか

1. Fire HD と PC を接続する

Spacedesk というフリーソフトを使って,

PCの画面をローカルネットワーク経由でFireHDに飛ばして表示させます.

PC 側 の準備

https://www.spacedesk.net/ より,サーバーソフトをダウンロード.

インストール後,サーバーアプリを立ち上げます

これでPC側の準備は終わりです.

Fire HD 側の準備と接続

amazon apps アプリストア を起動

検索ボタンを押す

「spacedesk」で検索してインストール

アプリを起動します

中央右の「+」ボタンを押します

自分のPCのIPアドレスを入力します.

PCのIPアドレスが分からなければ,

PC側でコマンドプロンプトを起動して,

ipconfig と打って,IPv4アドレスの項目を参照してください.

以上で,PC側に接続されるはずです.

2. CPUモニタリングソフトの選択と起動

CPU使用率モニタとして使用するにあたり,

タスクマネージャーを使うのもよいのですが,

単一項目のグラフしか表示できません.

CPU・メモリ・ネットワーク使用量のグラフは見たいので,

フリーソフトを導入します.

候補1. KMeter

こんな感じで,各種項目の分だけアナログメータを追加できます.

また,大きさの調整もでき,Fire HD の画面サイズいっぱいに表示もOK.

ただ,惜しむらくはグラフが表示できないところ.

なので,候補2 の紹介となります

候補2. myResources 1.5.0.12

https://softaro.net/myresources/

ダウンロードに起動したら,DCMという小さいウィンドウが出てきます.

DCMはそれぞれDisk, CPU, Memory の略で,負荷率でアイコンの色が変動します.

右クリックをしてグラフを配していきます.

おススメ1. CPU, メモリ, ディスク, ネット各々個別のグラフ表示

CPU, RAM(メモリ), Disk のグラフを表示

ネットワーク(アップロード,ダウンロード)を表示

適当にサイズも調整しながら画面いっぱいに並べる.

グラフの色の変更は後述します.

おススメ2. 全部まとめてグラフ表示

ネットワークを表示.

Max lines のチェックを外して,非表示に.

Legend を押して,凡例を表示

グラフの色の変更

好みにもよりますが,寒色系を中心に設定するとクールです.

個別グラフの方はこんな↓感じになります.

まとめ

候補1. KMeter, 候補2.myResources とある中,

私は候補2で全部まとめて表示にして使うことにしました.

理由は,

  • 時系列でモニタリングできること
  • 負荷率の関連性(CPUが上がると,メモリも上がるのか)も見たい

の2点になります.

最後に余談ですが,現在の私の画面構成は5枚になりました.

悪ノリで iPhone や iPad も繋げると,夢(?) の7枚構成も可能です(笑)

物理上の置き場所という壁があるため,実現には至りませんが.

WindowsユーザのためのMacbookのUS版キーボードの設定

何がしたいのか

  • 日本語入力モードでも半角でスペースを入れたい, 句読点は「,.」を使いたい
  • HHKB US版となるべく同じキー配置にしたい
    ex. caps lock → Ctrl, 右Command → 言語切替
  • スクショを手軽に取りたい

1. Google Input mac版をインストール

半角スペース強制,句読点「,.」へ変更のために,

Google Input をインストールする.

インストール後に右上のアイコンを右クリックして,環境設定より,

句読点とスペースの設定を行う.

2. Karabiner をインストールする.

caps_lock に 左command を,

左command に print_screen(F13) を,

右command に F16 を割り当てる.

3. システム環境設定で,ショートカットの設定を行う

アップルメニュー → システム環境設定 → キーボード → ショートカット内で,

入力ソースを選択し,「前の入力ソースを選択」を右command(F16) にする.

さらに,スクリーンショットのショートカットを左command を中心に,設定していく.この辺のキーの組み合わせはお好みで.