オッサンはDesktopが好き

自作PCや機械学習、自転車のことを脈絡無く書きます

トラックボール Perixx PERIMICE-720

 こんにちは.changです.

 トラックボール Perixx PERIMICE-720を購入しました. 1ヵ月程使ったので,今までに使ったトラックボールとの比較を含めて感想を書こう思います.

0. トラックボールとは?

 一度はご覧になったことがあるでしょうか. ボールを指で転がして使う変則型のマウスです. 正直,普通のマウスよりも不便ですし,手も疲れるんですが,,,昔から愛用しています.

 トラックボールの特徴は↓↓↓だと思っています.

  • マウスパッドが要らないので,机を広く使える
  • 細かい作業,特にドラッグ & ドロップが疲れる
  • ひねくれ者の心を惹きつける

1. トラックボール遍歴

 思い出せる範囲で回顧してみました.

(1) Elecom 人刺し指式

 大昔に使っていたので現物は存在せず,写真も残っていません. 型式もうる覚えなのですが,多分これ*1の旧版だと思います. 人刺し指式というと,当時はMicrosoftが主流だった様に思います. 僕はひねくれ者なので,敢えてこっちを買ったのです.

 人刺し指と中指でボールを転がし,親指で左右のボタンを操作するのが一般的かと思います. 僕の場合は,薬指で左ボタン,親指 or 小指で右ボタンを操作していました. ボタンの割り当てには専用のソフトが要るので,Windowsで使っていたのでしょう(記憶が曖昧).

 Windows限定ということは,修士論文をこれで書いたのかな??? 細かい絵を描いていると腕が痛くなってしまって普通のマウスに替えるという,間抜けなことをしていました. でも,薬指でクリック出来るので,ドラッグ & ドロップの安定感は高かったです.

(2) Logicool M570

f:id:changlikesdesktop:20210213064102p:plain:w400
Logicool M570.洗練されたデザインで愛着がわきます.

 博士課程に進学した頃に使い始めました. ワイヤレスマウスが珍しかった(?)当時,5000円程で手に入るこいつに飛びついた記憶があります. 初代は壊れてしまい,2代目を会社で使っています. 壊れやすいという意味ではありません. それぐらいにヘビーに使ったということです.

 正直,親指型ならこれ一択で良いと思います. それ位に使い易く,安定感があります. 軽くて持ち運びがし易いので,ノートパソコンとの組み合わせで仕事に使うのに適しています. 何といっても恰好良く,高級感があります.

 人刺し指から親指に替えた頃は,ドラッグ & ドロップに苦労しました. 人刺し指をロックした状態で親指を動かすというのが,身体の構造的に疲れるのでしょうね.

(3) Kensington Orbit Trackball with Scroll Ring

f:id:changlikesdesktop:20210213065202p:plain:w400
Kensington.左手で使える希少モデル

 現在のお気に入りで,自宅のWindowsマシンで使っています.

 魅力は左手で使える事です. 僕は右利きですが,マウスを左手で使うことが多いです. 右手で使う筆記用具などが,机の右側に溜まるするからだと思います. 人差し指と中指でボールを,薬指で左ボタンを,親指で右ボタンを操作しています.

 手首を使ってボールを大きく動かせるので,大きなモニタとの相性がとても良いです. 昔の金庫のダイヤルみたいなスクロールリングも,以外に使い易いです. 薬指でクリックできるので,ドラッグ & ドロップも楽.

 欠点は有線しか無いことですね.

(4) Digio2 Q MUS-TBLF132R

f:id:changlikesdesktop:20210213065921p:plain:w400
Digio2.蛙みたいなデザインが可愛い.

 M570の2個目を買う前に一時期使用していました. コンパクトで可愛らしいデザインなんですが,,,手にフィットしませんでした. 手のひらの支えが無く,親指の姿勢もキツいです. ヘビーユースには向かないかもですね.

1. Perixx PREMICE-720

f:id:changlikesdesktop:20210213070309p:plain:w400
Perixx PREMICE-720.紫のボールが斬新.

 やっと本題です(笑). ドイツ製という事もあってか,全体的に武骨な印象です. 作りというか,素材感というかが若干安っぽい感じがします.

 Linux(Ubuntu 18.04LTS)で動くのかが不安でしたが,全く問題ありませんでした. 子機をUSB端子に刺すだけで動作します.

 ボールが紫というのが斬新ですね. Amazonを見ると,交換用のボールが色々なカラーで買えるみたいです. M570に比べるとデカいので,持ち運びには向かないですかね. その意味で,在宅作業用への導入は成功かも知れません.

f:id:changlikesdesktop:20210213070844p:plain:w400
中央のボタンでアプリを切り替えられます.Alt + tabよりも良いかも.Linuxでもちゃんと動きます.

 使い勝手は良くも悪くも無いといった感じです. アプリの切り替えボタンは便利ですが,慣れてない(Alt + tabが沁みついている)のであまり使っていません.

f:id:changlikesdesktop:20210213071145p:plain:w400
角度変更用の付属パーツ

 角度を変えられる下駄みたいなのが付いてきますが,デカい図体が更にデカくなって邪魔なので使っていません.

f:id:changlikesdesktop:20210213071624p:plain:w400
背面.周波数帯の切り替えスイッチがある.DPIおそらくボールの感度.過剰な滑り止めが気に入らない(笑)

 周波数帯を変えることで,2つのパソコンに繋げる様です. 現在の僕のデスクは左にサブ(Windows)用のKensington,右にメイン用のPerixxという状態なので,一つにまとめられるのは良いですね(^^). 未だ試していませんが,スイッチが背面にあるのが少し不便かな...

 しっくり来てないのが吸着力が強すぎる滑り止めです. 僕はパソコンデスクで食事を取ることが多いので,トラックボールと言えど,机に固定したくは無いのです.

 比較レビューを書こうと思ったのですが,人差し指式のElecomとKensingtonとは比べようがありません. 親指式ということではLogicoolが対抗馬ですが,,,既に書いてしまった通りです(笑). 今回の趣旨とは異なりますが,人差し指式か親指式かを問うならば,僕は人差し指派です.

2. むすび

 道具を変えて気分を変えるのは好きです. 王道のLogicoolから離れてみるのも楽しいですね.

Q Learning for bike road race

This article is a translation of Japanese ver.
Original ver. is here*1.

 Hi, this is chang. Today, I tried to make artificial intelligence learn bike-road race.

f:id:changlikesdesktop:20210125191439g:plain:w500
Win or...???

0. Bike road race

 Not so many people have interests in bike road race in Japan, but it is very popular in Europe, next to football and F1. About 10 years ago, I was attracted to the sports.

 Although I cannot write the whole attraction of the sports here, I have to explain a topic: slipstream.

 In short, this is the phenomena that you can easily go fast by following others. Is is also called as drafting. If a road bike is well greased up, friction generated with wheel rotations is very small. It means that you ride against air resistance. When you ride at 40 km/h or faster, you can feel that strong wind pushes your body back. If you run behind another person, the other takes the resistance from the wind instead. So you can run easily.

 According to the paper introduces in the article*2, group ride with two persons and three persons reduce 35.6% and 47.8% of power consumption, respectively. This one introduces the simulation of fluid mechanics*3.

 Because of the slipstream, you have to be clever if you want to win bike races. You have to make use of rivals' power to save your energy. This video*4 is good example. The eventual winners always hide behind others until the very last of the race and rush full gas at the goal sprint.

 In this article, I tested if reinforcement learning accomplishes the goal sprint.

1. Program

 I modified the sample that I had written before*5 for bike road race.

(1) Field

 Race is competed on the image with 32 × 8. It starts from the upper edge. If an agent reaches the bottom edge before rivals, it is win. The bar shown at the right side of the image shows the remaining energy.

f:id:changlikesdesktop:20210123062625p:plain:w300
Field. Although it looks like color image with "jet" color map, it's single a scale between 0.0 to 1.0.

(2) Player

 The player learns goal sprint.

f:id:changlikesdesktop:20210123064303p:plain:w300
Player

  • actions of 4 patterns
  • 1 sprint consumes 1 energy
  • If remaining energy is 0, sprint(0) is the same to go straight(2)
  • Initial energy is 5
  • Big movement with 2 pixels to the lateral directions are set for generating the tactics of chasing the back of rivals.

(3) Competitor

 The competitor moves randomly.

f:id:changlikesdesktop:20210123064650p:plain:w300
Competitor

  • actions of 4 patterns(randomly selected)
  • 1 sprint consumes the 1 energy
  • If remaining energy is 0, sprint(0) is the same to go straight(2)
  • Initial energy is 6

Note: I considered that strong competitor encouraged the accomplishment of clever strategy, so I tried high initial energy like 10. But I realized that too strong competitor prevented learning because the player seldom won. Thus I used 6 as initial energy of the competitor, that is close to the one of player. As I explained below, the slipstream does not work on the competitor. That means the energy of competitor never recovers. So I guess the potential strength of the two is very close.

(5) Slipstream

 I imitated slipstream using simple rules because calculating fluid mechanics is very very hard.

f:id:changlikesdesktop:20210123065708p:plain:w300
Imitated slipstream

  • If the player is 1 pixel behind of the competitor, the player's energy recover 2
  • If the player is 2 pixel behind of the competitor, the player's energy recover 1
  • Slipstream works only on the player.

(6) Expression of race progress

 This was the hardest point in this article. Many of image processing using neural network, including deep learning, is a reaction from a image. It is not good at learning long-term tactics like saving energy at the beginning of the race for preparing to goal sprints.

 Recurrent neural network(RNN) is a popular way for make neural network learn time series forecasting. For example, long short term memory(LSTM) is often used in natural language processing. I heard some studies tried the recurrent neural network in image processing. This time I selected the simple way: arranging the images with time series at the channel direction. Reinforcement learning is a very new field for me, so I preferred to a classical way.

f:id:changlikesdesktop:20210126055822p:plain:w300
Expression of race progress. Images with time series were arranged at the channel direction.

  • Initialize 32 images(channel) as zero
  • In every step, the new channel is updated. So the past remains in old channels.
  • When the race is finished, images are fixed and inputted to neural network. Thus, several channels are remains as 0.

(7) Reward

 Bike race is all or nothing as riders often say "second places is the same to 10th." So I set the reward in a simple way.

  • Win: 1.0
  • Lose: -1.0

 There was s problem. In this way, we can give reward only when the result is decided. To learn race progress, we need to give reward to the past. It is important to let the neural network know the actions that are highly possible to grab wins in the end. Thus, I assigned reward on the past in the linear way relative to the finish step.

dqn_agent.py

def experience_replay(self):
    ...(omittion)...
    size = len(self.D)
    tmp_state, tmp_action, tmp_reward, tmp_state_1, tmp_terminal = self.D[size - 1]
    for j in range(minibatch_size): # minibatch_indexes:
        ...(omittion)...
        reward_minibatch.append(tmp_reward * j/(minibatch_size - 1))
        ...(omittion)...

 Because the rewards for past are not decided until the race is finished, I used experiences only at the loop with race finish.

train.py

if terminal:
    # experience replay
    agent.experience_replay()

2. Result

 Because of random values included in the program, the learning results were not always the same. There were both the case with successful and failed learning.

f:id:changlikesdesktop:20210125191404g:plain:w500
Case of failed learning(20000 epoch). Self-destruction with kamikaze-attack

f:id:changlikesdesktop:20210125193506g:plain:w500
Successful learning(20000 epoch). Goal sprinting near the finish

f:id:changlikesdesktop:20210125195354p:plain:w400
Winning rate during learning. You can see that successful learning (=orange, goal sprint) acquired the superior tactics with high winning rate

 I compared the tactics and winning rate during learning of the two cases. In the case of failed learning, the player took sprint from the begging and run out energies, so to called "kamikaze-attack." The winning rate (blue in the graph) rapidly rose soon after the learning start but stagnated. On the other side, in the case of successful learning the player saved (recovered) the energies using slipstream and goal sprinted. Its winning rate(orange in the graph) continued to rise slowly and accomplished the high value in the end. The result shows that you need to learn from defeats to be strong. It is possible that a rider who learn a lot from defeats becomes stronger than a rider who wins a lot soon after the start of carrier.

 I have no confidence that the player used lateral movement to catch the back of the competitor. The player tended to dash when the competitor occasionally came in the front, and surpassed before the finish. I think It is a little different phenomena of the real.

3. Afterward

 It was a interesting experiment. I want to make the neural network generate the new tactics that has not yet been used in the real.

 The source code is here*6.

Debug the cpp library built with Boost.numpy

This is personal note.

  • Debugging boost.numpy libraries for python using gdb.
  • The article is continued from *1.

1. Write cpp to build libraries as executable

test_boost.cpp

#include "./sample.cpp"

#include <iostream>
#include <fstream>
#include <random>
#include <sys/stat.h>
using namespace std;

double rand_uniform(double min, double max)
{
    double width = max - min;
    std::random_device rnd;
    std::mt19937 mt(rnd());
    std::uniform_int_distribution<> rand10000(0, 10000*width);
    int randi = rand10000(mt);
    double val = min + (float)randi/10000.0;
    return val;
}

int main(int argc, char* argv[])
{
    cout << "Hellow" << endl;
    Py_Initialize();
    np::initialize();
    
    double a[5*5];
    double b[5*5];
    int m_size = 5*5;
    for(int i = 0; i < 5; i++)
    {
        for(int j = 0; j < 5; j++)
        {
            a[i*5 + j] = rand_uniform(-1.0, 1.0); //random.uniform(-10.0, 10.0)
            b[i*5 + j] = rand_uniform(-1.0, 1.0); //random.uniform(-10.0, 10.0)
        }
    }

    cout << "input a:" << endl;
    cout << a[0] << "," << a[1] << "," << a[2] << "," << a[3] << "," << a[4] << endl;
    cout << a[5] << "," << a[6] << "," << a[7] << "," << a[8] << "," << a[9] << endl;
    cout << a[10] << "," << a[11] << "," << a[12] << "," << a[13] << "," << a[14] << endl;
    cout << a[15] << "," << a[16] << "," << a[17] << "," << a[18] << "," << a[19] << endl;
    cout << a[20] << "," << a[21] << "," << a[22] << "," << a[23] << "," << a[24] << endl;

    cout << "input b:" << endl;
    cout << b[0] << "," << b[1] << "," << b[2] << "," << b[3] << "," << b[4] << endl;
    cout << b[5] << "," << b[6] << "," << b[7] << "," << b[8] << "," << b[9] << endl;
    cout << b[10] << "," << b[11] << "," << b[12] << "," << b[13] << "," << b[14] << endl;
    cout << b[15] << "," << b[16] << "," << b[17] << "," << b[18] << "," << b[19] << endl;
    cout << b[20] << "," << b[21] << "," << b[22] << "," << b[23] << "," << b[24] << endl;

    p::tuple shape = p::make_tuple(m_size);
    p::tuple stride = p::make_tuple(sizeof(double));
    np::dtype dt = np::dtype::get_builtin<double>();
    np::ndarray a_np = np::from_data(&a[0], dt, shape, stride, p::object());
    np::ndarray b_np = np::from_data(&b[0], dt, shape, stride, p::object());

    np::ndarray result_np = multiply_matrix(a_np, b_np, 5);

    double *result = reinterpret_cast<double *>(result_np.get_data());
    cout << "result:" << endl;
    cout << result[0] << "," << result[1] << "," << result[2] << "," << result[3] << "," << result[4] << endl;
    cout << result[5] << "," << result[6] << "," << result[7] << "," << result[8] << "," << result[9] << endl;
    cout << result[10] << "," << result[11] << "," << result[12] << "," << result[13] << "," << result[14] << endl;
    cout << result[15] << "," << result[16] << "," << result[17] << "," << result[18] << "," << result[19] << endl;
    cout << result[20] << "," << result[21] << "," << result[22] << "," << result[23] << "," << result[24] << endl;

    return 0;
}

A Little worry about casting 2 dimensional array to double*....

2. Modify task.json

{
    "tasks": [
        {
            "type": "shell",
            "label": "g++-7 build active file",
            "command": "/usr/bin/g++-7",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}",
                "-std=c++11",
                "-I/opt/boost_1_67_0/include",
                "-I/usr/include/python3.6",
                "-I/usr/include/x86_64-linux-gnu/python3.6",
                "-L/usr/bin",
                "-lpython3.6m",
                "-L/opt/boost_1_67_0/lib",
                "-lboost_numpy36",
                "-lboost_python36"
            ],
            "options": {
                "cwd": "/usr/bin"
            }
        }
    ],
    "version": "2.0.0"
}

f:id:changlikesdesktop:20210128170240p:plain:w400
task.json. . Modify the green-surrounded part according to your environment

3. Register boost libraries

.bashrc

export LD_LIBRARY_PATH="/opt/boost_1_67_0/lib:$PATH"

4. Degug from VSCode

Good luck!!!

強化学習に自転車ロードレースをやらせてみる

 こんにちは,changです. 今回は強化学習に自動車ロードレース(っぽいこと)をさせてみます.

f:id:changlikesdesktop:20210125191439g:plain:w500
勝利の行方は???

0. 自転車ロードレース

 日本では馴染みが薄い自転車ロードレースですが,ヨーロッパではサッカーやF1に次ぐ程の人気スポーツです. およそ10年前,僕はその魅力に取りつかれました.

 自転車レースの魅力についてはここでは語り切れません. でも一つだけ,今回の記事を書くに当たって説明する必要があります. スリップストリームです.

 簡単に言うと「他人の背後を走ると楽に走れる」現象です. ドラフティングと呼んだりもします. グリスアップされたロードバイクでは,進行に際して生じる抵抗の大部分が空気抵抗になります. 時速40 kmやそれを超える速度で走っていると,自分の身体が風を押し広げながら進んでいるのを感じることが出来ます. 他人の後ろを走ると,風の抵抗をその他人が代わりに受けてくれる為,楽に走れるのです.

 例えばこの記事*1で紹介されている海外の研究論文によると,2人で走って後ろに付いた場合には35.6%,3人で走って後ろに付いた場合には47.8%のパワー削減になるそうです. チーム練習やレースにおいて,実際にその位の効果を感じます. 流体力学のシミュレーションをしてくれている方もいますね*2

 スリップストリームが在ることで,自転車レースは頭脳戦・心理戦になります. 他人の後ろに隠れて体力を温存することが,勝機に繋がるからです. これ*3をみると良くわかりますが,最後に処理をつかむ選手は大抵,ゴール直前まで他の選手の後ろに潜んでいて,最後の最後に力を爆発させる(=ゴールスプリント)のです.

 今回はこのゴールスプリントを強化学習にやらせて見ようと思います.

1. プログラム

 前回書いたサンプル*4を,自転車ロードレースに合わせて書き直して行きます.

(1) フィールド

 32 × 8画像の上から下方向にレースをさせます. 速く最下行に到達した方が勝ちです. 右端にあるバーは,後述するエネルギーの残量を表しています.

f:id:changlikesdesktop:20210123062625p:plain:w300
今回使ったフィールド.Jetで表示しているのでカラーに見えるが,実際には0.0-1.0のシングルスケール

(2) プレイヤー

 こいつにゴールスプリントをさせます.

f:id:changlikesdesktop:20210123064303p:plain:w300
プレイヤー

  • アクションは4パターン
  • 1回のスプリントで,エネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは5
  • コンペティターの背後に入る動きを発生させる為,横方向の動きを大きくしている

(3) コンペティター

 こいつにランダムに動いてもらい,競争相手になってもらいます.

f:id:changlikesdesktop:20210123064650p:plain:w300
コンペティター

  • アクションは4パターン(ランダム)
  • 1回のスプリントで,エネルギー1を消費する
  • エネルギーが0になると,スプリント(0)は前進(2)と同じ動きになる
  • 初期エネルギーは6

Note: コンペティターが強い(初期エネルギーが大きい)方がより狡猾に(?)戦略を学習すると思ったのですが,コンペティターが強すぎると勝てるケースが少なすぎて学習が進みませんでした. このため,今回はコンペティターの初期エネルギーを(プレイヤーに対して1多いだけの)6にしました. 後述するようにコンペティターにはスリップストリームが効かない(エネルギーが回復しない)ので,プレイヤーとコンペティターの戦力は切迫していたと思います.

(5) スリップストリーム

 流体力学を計算するのは大変なので,簡単なルールで擬似することにしました.

f:id:changlikesdesktop:20210123065708p:plain:w300
疑似スリップストリーム.リアルでは,エネルギーの回復と言うよりも,温存なのだが...

(6) レース展開の表現

 今回,一番苦労したところです. ディープ・ラーニングもそうですが,基本的には画像に対しての一問一答の構造です. レース前半にエネルギーを温存し,後半にゴールスプリントをしかえるといった「展開」を学習することには不向きです.

 時系列や文脈をニューラルネットワークに学習させる方法の代表例が,再帰ニューラルネットワーク(RNN)です. 代表例のLSTM(Long Short Term Memory)が,近年の自然言語処理でよく使われています. 画像処理に再帰型を導入した実例もある様ですが,強化学習という慣れない分野で策を凝らすのは疲れると思いました. このため,今回はシンプルにチャンネル方向に画像を並べることにしました.

f:id:changlikesdesktop:20210126055822p:plain:w300
レース展開の表現.時系列毎の画像をチャンネル方向に配置

  • 32枚(チャンネル)の画像を0初期化する
  • 1ステップ毎にチャンネルをずらしながら,画像を更新する
  • 勝敗が決まった時点で画像を確定し,ネットワークに入力する(数チャンネルは初期(0)画像のまま)

(7) 報酬

 自転車レースは「2位はビリと同じ」と言われる位にall or nothingの世界です. ですので,報酬はシンプルに設定しました.

  • 勝利: 1.0
  • 負け: -1.0

 単純にこうしてしまうと,勝敗が決した時点での画像にしか報酬を与えることが出来ません. レース展開を学習させるためには,レースが決着する前の画像にも報酬を与え,勝利をつかむ可能性の高いアクションを学ばせる必要があります. このため,勝利が決した時点から遡って線形に,過去の画像に報酬を割り振ることにしました.

dqn_agent.py

def experience_replay(self):
    ...(中略)...
    size = len(self.D)
    tmp_state, tmp_action, tmp_reward, tmp_state_1, tmp_terminal = self.D[size - 1]
    for j in range(minibatch_size): # minibatch_indexes:
        ...(中略)...
        reward_minibatch.append(tmp_reward * j/(minibatch_size - 1))
        ...(中略)...

 このやり方だと,勝敗が決まる前の画像であっても,勝敗が付くまで報酬が確定しません. このため,勝敗が決したループのみで経験を積ませることにしました.

train.py

if terminal:
    # experience replay
    agent.experience_replay()

2. 結果

 乱数を使っている為,同じプログラムで計算を回しても学習が進む場合と進まない場合がありました.

f:id:changlikesdesktop:20210125191404g:plain:w500
学習が進まなかったケース(20000 epoch).先行逃げ切りで自滅する

f:id:changlikesdesktop:20210125193506g:plain:w500
学習が進んだケース(20000 epoch).後半にゴールスプリントする

f:id:changlikesdesktop:20210125195354p:plain:w400
学習中の勝率の推移.学習が進んだケース(ゴールスプリント型)では,勝率の高い(=優れた)戦略を獲得できたことがわかる

 学習中の勝率の推移を比較しました. 学習が進まなかった先行逃げ切り型(青)では,学習開始直後から一気に勝率が上がり,その後横ばいになりました. 一方,学習が進んだゴールスプリント型(橙)では,学習開始直後から継続的に学習が進んだ結果,高い勝率を獲得しました. このことは,強くなる為には「失敗から学ぶ」必要があることを示しているでしょう. キャリアの序盤に大量の勝利を積んで戦略を学ぶのを怠った選手よりも,多くの敗北から経験を積んだ選手のほうが最終的には強くなるのかも知れないですね.

 学習が進んだケースを繰り返し見てみましたが,横の動きでコンペティターの背後に付く動きをしたかというと少し怪しいです. どちらかと言うと,コンペティター側が偶然自分の前に入って来た時に加速してエネルギー回復をさせ,ゴール手前で一気に抜き去っていました. 現実とは少し異なる動きなので,改良しようと思います.

3. むすび

 面白い実験ができました. 現実では起きていない戦略を,強化学習に創造させる事が出来たら最高ですね(^^). 試してみたい事が色々あるので,順次,発表して行くつもりです.

 今回書いたソースはここ*5です.

雑記: 今年を振り返って

 タイトル通り,大晦日の雑記になります.

0. COVID 19

 コロナに始まり,コロナに終わる年でしたね. 今,世界は恐慌の真っ只中なのかも知れません.

 世界経済を揺るがす大きな出来事というと,直近ではリーマンショックでした. 当時僕は学生だったので,事の重大さをいまいち実感していませんでした. 今,(一応)社会人になったんですが,,,やはりそれ程の実感がないです... 飲食業界や観光業界にいないということはあると思います(他人事の様な物言いになってすみません). 勤め先も一定程度の影響を受けている筈なんです. 今期の賞与は過去最低でしたしね. それでも,何というか,,,「危機感」を感じないのです. その理由を考察してみようと思います.

 パンデミックによる渡航 & 外出禁止は急速かつ大きな変化でした. その変化に瞬時に対応できない会社や個人がいるのは当然だと思います. コロナを天災と呼ぶのか人災と呼ぶのかは難しいですが,生態学的には,種の存続を脅かすような事件と言うのはある程度の頻度で起きるものです. もし,今回の経済危機の所為で人類の人工が飛躍的に減ったりするのならば,それはある種の自然調整がかかった結果なのかも知れないです.

 「生態学」という言葉を使いましたが,コロナの難しいところは,その感染拡大に人類自らが寄与してしまっているところだと思います. COVID 19は中国で発生し,その後,瞬く間に世界中に広がりました. 航空機によって大陸間の移動が容易だからです. 太古であれば,村一つが全滅するだけで済んでいた可能性があります.

 これに充て付けて,「グローバライゼーションの限界」みたいな事を言う人がいます. 確かに,現代社会は国家間のヒト・モノ・カネの流れの上に成り立っており,一部が綻ぶと全世界が共倒れになる構造になっています. 共倒れを避けようとすれば,戦争が起きます. 米中間の対立ばかりがクローズアップされますが,貧しい国などでは今まで無い程にクーデーターが起きやすい状況になっているのでは無いでしょうか.

 では,また鎖国すれば良いのか? 僕はそうでは無いと思います. そもそも出来ない. 資源に乏しい日本は特にそうですが,多くの先進国も同じでしょう. トランプさんがどれだけ中国に喧嘩を売ったとしても,米国の経済は中国抜きでは成り立たないのです.

 そうした逃げられない感というか,どうしようもない感が,冒頭に行った危機感の乏しさの源泉の様に思います. そもそも,去年も一昨年も,TVや新聞は視聴者の不安を煽るようなニュースをわんさかと流していました. 放射性物質,核爆弾,環境問題,国際紛争,,,キリがありません. 僕達は,常に毒壺の中に生きているのです. くだらないニュースに一喜一憂していても仕方がありません.

 こうしたダルな態度をとると,非難されるかも知れません. 不謹慎ですよね. でも,少なくとも現状はこれで良いと思っています. 人間が抱えられる不安の大きさには一定の限界があります. 不安の量が限界を超えると壊れたり,冷静さを失ったりします. その方が余程危険です.

 一方,ただただ能天気にしていようとも思いません. 自分を取り巻く環境に対して,取るべき行動があると感じているからです.

 その一つが,やっぱりというか仕事. 勤め先の詳細は書けませんが,コロナショックの前から業績は悪かったです. コロナショックが無くても赤字転落だったと思います. それが,コロナで有耶無耶になった感じです. 業績が悪い=コロナの影響という言い訳が効きますからね. これは,勤め先に限った事では無いんじゃないかな?

 内閣の振る舞いとかを見ても,国の将来よりも,組織内の体面を気にしているように見えますよね. 協調派(?)の菅さんは,特にそういうタイプなのでしょう. トランプさんの様に突進力があって,かつ最大数の人間を満足させるようなリーダーは居ないという事ですね. またまた,どうしようもない感...

 個人的には,どうしようもない感は組織が大きくなるほど強くなると思います. 一人であれば,自由度が広がります. ただ正直,今年のパンデミックが独立にビビりを入れたことは間違いないです(汗). 良い意味で冷静になれたとも言えます. ありきたりですが,諦めず,細々とでも,どうしようもない感に抗う活動をしていこうと思います.

 コロナの影響で苦しんでいる方々には心からお悔み申し上げます. 僕にできることはあまり無いんですが,無駄遣いにはならない程度にお金を使う様にしています. 後述する新車も買ったしね(笑). 来年か,もっと先になるかも知れないし,今迄とは違った形になるかも知れませんが,必ず平穏は訪れる,そう信じています.

1. 研究

(1) ディープ・ラーニング

 ディープ・ラーニングを作り始めたのが2019年の始めだったと思います. 当時は,ディープ・ラーニングという未知の素材を理解するだけで精一杯でした. 環境構築の煩雑さや,言語の不便さに大分苦しめられましたね(笑).

 粘り強く取り組んだ甲斐もあったのか,2020年には,僕独自の考えを盛り込んだ,所謂,論文に近い記事を何本か書くことができました. 勿論,未だ未だ未熟です. 記事も殆ど読まれていないので,自己満足に近いと言えます. この後に学会の話を書きましたが,査読を受けずに発表するということは,世の中からの反応が全てです. 継続することは勿論大切ですが,加えて,表現方法を工夫していくことが必要だと思っています.

 所謂,ブロガーとかYouTuberみたいな事で副業を始めようと思っていたんですが,2020年内には残念ながら出来ませんでした. 正直なところを言うと,職場でも思い通りの事が出来る様になって研究意欲が満たされているのが,年の瀬の失速の原因ではあります. とは言え,組織内での活動には前述したどうしようもない感が必ず伴います. 個人としての活動は,これまで以上に重視していくつもりです.

 以前の記事でも述べましたが*1*2,ディープ・ラーニングはある程度頭打ちになると感じています. 応用先が非常に限定的で,実質的には画像の外観検査にしか使えません. 「ビッグデータを解析してイノベーション」みたいな事は起こらないと思っています. 年末の土壇場で強化学習*3をやったのも,ディープ・ラーニングに限界を感じている為です. その辺の戦略(?)もまた考えていくつもりです.

(2) 学会

 菅内閣の学術振興会騒ぎがありましたね. 結局何だったのか判りませんでした.

 この騒ぎには興味も関心も無いのですが,学会という組織を改めて考えるきっかけにはなりました. 以前,「今は研究成果をSNSで発表すれば良いから学会に所属する必要など無い」という様な事を書きました*4. 大学から離れて何年も経つので実際のところはわかりませんが,日本のアカデミーは古い体制を引きずり続けるのでしょうね. 研究者というと,協調性が低くてコミュニケーション能力が低い,というイメージを持たれがちです. 僕も含めて,実際そうです. 言い訳がましいですが,体制上そうなりがちなのです. 大学の研究って,個々の学会に合わせた論文の体裁作りとか,役人を相手にした予算取りの書類作成とかに費やす時間が膨大なんです. 世の中に対峙した仕事をしていないのね. 「役立たずだね」と捉えられるのは真っ当と言えます. 個人的には,学会とか,政界みたいなところを根城にする研究者は,世界の競争の中ではどんどん衰退していくと思います.

 かといって,「自分は世の中から望まれる研究者になろう!」とは思わないんです. ひねくれものなんですね(笑).

 世の中で望まれる研究者というのは,専門的な知識を分かりやすく説明してくれて,経営判断とかを手伝ってくれる知識人だと思います. シンクタンクみたく広く浅い知識に精通していて,どちらかというと役人が得意とするような仕事ね. こういう人達はとても優秀でお行儀が良いです. だから,皆して同じ事を言います.

 一方,例えばディープ・ラーニングなどは狭く深い追及の上に成り立っています. この手の作業が出来る人は,自分の世界に入って驚異的な集中力を発揮します. 謂わば,ヲタクですね. イノベーションの源泉となるのはこの手の人材だと思うんですが,残念ながら評価されません. 高飛車な言い方をしますが,僕もそうでした.

 嬉しいことに,ヲタクにも開かれた世の中になりました. ブログやYouTubeで誰でも垣根の無い自己表現が出来ます. 勿論,世の中から評価されるのは学会の中で評価されるよりも遥かに大変です. そこをやりがいと感じられるような意識改革が必要です. 実質的に,今の大学は職業斡旋所になっています. 研究機関に属するよりも,個人で活動する方が自由な研究を展開できる可能性があると感じます. それこそ,シンクタンクの人達が,個人ブログやYouTubeで情報収集をする時代が来るんじゃないかな.

 ただね,YouTubeで研究を展開するのはなかなか厳しいとも感じています. 優秀な研究者でも,論文発表は一年に数本のペースです. 対して,YouTuberは毎週動画を公開しています. そのペースで研究成果を出すのは不可能です. それっぽい事をしている人もいるけど,所謂「やってみた」系です. 研究と言うのはその先に在ります. やり方を工夫する必要があると感じています.

2. 自転車

(1) CTL90の壁

 去年の8月位から,トレーニングにCTLを取り入れました. 3月に出場を予定していたレースに向けたピーキングをして,レース直前にはCTL 90弱に到達しました. この時はメチャクチャ調子が良かったです. どんなに走っても疲れないというか. レースに向けて高揚していたんですが,残念ながらレースは中止になりました. レース予定日の数日前の発表だったので,結構なショックを受けました. 気を紛らわす様に,チームメートと走りまくってしまいました. 結果,CTLが一気に上がって 90を超えました.

 当時,自分の身体が今までに無い状態にあるのを感じました. 兎に角,四六時中お腹が減っているんです. 仕事中にボリボリやっている訳にも行かないので我慢するんですが,イライラして集中できませんでした. 元々小食というか,生き物っぽい自分が嫌いな質なのですよ(笑). 結局,微熱が続いて,身体が動かなくなってしまってしまいました. 自転車には1~2週間程度乗れませんでしたし,会社も1~2日休みました. 社会人として反省しました.

 CTL100に壁がある,という話をよく聞きます. その手前で躓いた感じでしょうか? 自分に甘い発言になってしまいますが,僕の体格(体重52 kg)ではここ(CTL90)に一つの壁があるように感じています.

 この後,トレーニング量を徐々に戻して行ったのですが,これまでに無く慎重になりました. オーバートレーニングにならない様,その日に獲得するTSSを決めて練習しています. 外出禁止でトレーニングに制限があったのが,逆に良かったのかも知れないです. 今現在,再びCTL90に来ていますが,問題無い感じです.

 実感している事ですが,CTLを上げていく,或いは高い水準で保てばPWRは必ず上がります. 5倍までは今のやり方でいけるイメージを持っています. このオフで到達するのは少し難しそうですが(現在,約4.5倍),身体の反応を見ながらチャレンジしてみるつもりです.

(2) ディスクロード

 買いました. 組み上がってから既に数か月経っています. インプレを書きたくてウズウズしているんですが,注文したホイールが届かないのです(泣). 新年に持ち越しにします.

3. 2021年の目標

研究に関しては:

  • 個人研究の副業化(取り敢えず収入は問わず)

自転車に関しては:

  • 富士チャレGold,富士ヒルGold
  • PWR 5倍到達

4. むすび

 長々と脈絡無く書きました. 来年も宜しくお願い申し上げます.

tensorflow + kerasで強化学習してみる

 こんにちは.changです.

 今回は強化学習をやってみます. ディープ・ラーニングにも少し飽きてきたので...(笑).

 作ったのはよくあるインベーダー的なやつです. やってみると意外と大変でした(汗). kerasの環境が整っていなくて,tensorflow単独に先祖返りする感じです.

0. 今回の目的

 冒頭にも触れたように,ディープ・ラーニングにも大分飽きて底が見えてきました. 原理的な探求には区切りをつけ,実用性を重視したアプリケーション開発に移行するタイミングだと思っています. 本来はここからが仕事(=営利活動)なのですが,こればかりをやっていると直ぐに時代遅れになってしまいます. 次の素材に手を出し始めようと考えました. で,AIつながりで浅はかですが,強化学習をやってみます.

 情報の少ないアルゴリズムを動かすときに大切なのが,ベースとなり,かつ整然と書かれたサンプルを持つことだと考えています. ディープ・ラーニングではmnist*1とU-Net*2がそれでした. 僕の書くソースのほとんどはこの2つのソースの派生になっています. 単に裾野が狭いとも言える...(汗).

 ベースが出来てしまうと,そこからの派生や応用は案外簡単にできるものです. また,立ち返る原点が定まっていれば,迷っても初めからやり直しできます. 「やり直し」を「出戻り」と読んで避難するヒトもいますが,,,同じ条件でもう一度やり直せば,必ず一度目よりも速く,賢くゴールに辿り着く筈です.

 今回の目的は,このベースを強化学習について構築することです.

1. 強化学習

 あちこちで紹介されているので,詳しい説明は省きます. というより,僕自身が勉強不足で詳しい説明が出来ません(笑).

 イメージだけで話しますが,強化学習は所謂「教師無し学習」です. Alpha GOがプロ棋士を負かしたという話はあまりにも有名ですが,Alpha GOの凄いところはヒトの対戦を真似るのではなく,自身で対局を繰り返して勝手に強くなることです.

 少し前に,藤井聡太君が50万円の自作PCで将棋ソフトを使っているという記事が話題になりました. 将棋ソフトについて,彼が「予想のしない手を打ってくる」と発言していた記憶があります(羽生さんだったかも). これは,ヒト(=教師)の模倣ではない強化学習ならでは特徴だと言えます.

 ヒトが発想出来なかったアイデア創発することが,僕が強化学習に期待することです.

2. keras

 一年位前からkerasを使い始めました. 「モデルを書くのが楽」という声をよく聞きますが,最大の恩恵はデバッグのしやすさです. tensorflow単独でのプログラミングはPlaceholderの中身を追えない為,非常に厄介です. kerasを使うと,C言語的というか,ひとつながりのデータの流の中で自分の書いたソースを追いかけることが出来ます. 強化学習でも,このメリットを生かしたいと考えました.

3. プログラム

(1) 参考にしたソース

 いくつかの簡易版がシンプルに動作しますが,tensorflow単独で書かれていました. この方*3はkerasを使われていますが,ご本人もおっしゃっているようにkerasを使うことによって逆にソースが煩雑になっています. この方*4のソースは,モデルの記述にのみkerasを使っています. また,簡易版ではしばしば省略されているtarget network等も丁寧に書かれていて,発展性が高いと思いました. ただ,表示部が無い(公開されていない?)のです. 理想を満たすサンプルはなかなか見つかりません...(>.<). 結局,これらのソースを自分で統合することにしました.

(2) ルール

 参考にしたソースそのままです.

  • 爆弾(?)をキャッチしたら報酬1
  • 爆弾を落としたら報酬-1 & ゲーム終了
  • 右に動く,左に動く,動かないの3パターンの中からアクションを選択
  • フィールドは8 × 8または16 × 16から選んで設定

(3) ネットワーク

 シンプルな全結合版と,少し複雑な畳み込み版の2パターンを書きました.

シンプルな全結合版

def build_model(input_shape, nb_output):
    model = Sequential()
    inputs = tf.placeholder(dtype=tf.float32, shape=[None,input_shape[0]*input_shape[1]], name="input")
    model.add(InputLayer(input_shape=(input_shape[0]*input_shape[1],)))
    model.add(Dense(64, activation="relu"))
    model.add(Dense(nb_output))
    outputs = model(inputs)
    return inputs, outputs, model

少し複雑な畳み込み版

def build_model_cnn(input_shape, nb_output):
    model = Sequential()
    inputs = tf.placeholder(dtype=tf.float32, shape=[None,input_shape[0]*input_shape[1]], name="input")
    model.add(InputLayer(input_shape=(input_shape[0], input_shape[1], 1)))
    model.add(Convolution2D(16, 4, 4, border_mode='same', activation='relu', subsample=(2, 2)))
    model.add(Convolution2D(32, 2, 2, border_mode='same', activation='relu', subsample=(1, 1)))
    model.add(Convolution2D(32, 2, 2, border_mode='same', activation='relu', subsample=(1, 1)))
    model.add(Flatten())
    model.add(Dense(128, activation='relu'))
    model.add(Dense(nb_output, activation='linear'))
    outputs = model(tf.reshape(inputs, shape=[-1, input_shape[0], input_shape[1], 1]))
    return inputs, outputs, model

(4) 最適化関数

 最適化には,tensorflow標準のRMSPropを使いました. ここ*5で紹介されているのように,論文発表者の改変版を使のがベターの様です.

optimizer = tf.train.RMSPropOptimizer(learning_rate=self.learning_rate)

4. 学習結果

(1) 8×8フィールド

 2000スペック&全結合版でもそこそこの学習効果がありました.

f:id:changlikesdesktop:20201218052206g:plain:w400
8 × 8, 全結合版での学習結果

f:id:changlikesdesktop:20201218052747p:plain:w400
縦軸: 1ゲームで得られた報酬.横軸: エポック数

 ネットワークを畳み込み版に変えると,更に良くなります.

f:id:changlikesdesktop:20201218054054g:plain:w400
8 × 8, 畳み込み版での学習結果

f:id:changlikesdesktop:20201218054134p:plain:w400
縦軸: 1ゲームで得られた報酬.横軸: エポック数

(2) 16×16フィールド

 2000スペック & 全結合版では,爆弾をほとんどキャッチしませんでした.

f:id:changlikesdesktop:20201218055527g:plain:w400
16×16, 全結合版での学習結果

f:id:changlikesdesktop:20201218055629p:plain:w400
縦軸: 1ゲームで得られた報酬.横軸: エポック数

 2000スペック & 畳み込み版でそれなりな動きになりました. 未だ未だ,工夫が必要な感じがしますね.

f:id:changlikesdesktop:20201216145405g:plain:w400
16×16, 畳み込み版での学習結果

f:id:changlikesdesktop:20201218055735p:plain:w400
縦軸: 1ゲームで得られた報酬.横軸: エポック数

5. 結び

 新しい素材に触れるのは楽しいですね.

 今回残念だったのが,kerasのメリットをほとんど活かせ無かった事です. モデルの記述にはkerasを使いましたが,ネットワークへの入力にはPlaceholderを使いました(使わざるを得ませんでした). これでは,tensorflow単独でのプログラミングとほぼ変わりません. 新しいkerasやtensorflow ver. 2では改善されているのかも...?

 今回のソースが,強化学習を作っていく上でのベースとして機能するか未だ判りませんが,取り敢えずの起点は作れたと思います. 自分なりの工夫を加えて遊んでみるつもりです.

 今回書いたソースはここ*6です.

kerasで保存したh5ファイルが読めなくなった件

自分用のメモ

 新しくパソコンを作った際,kerasで保存したh5ファイルが読めなくて困ったときの対処を記録.

 先ず,環境を確認.

pip3 list

f:id:changlikesdesktop:20201216113227p:plain:w400
pip3 listで環境をチェック

 kerasのver. 違いかと思ったので,実績のある環境と比べてみるが違いは無い.

 更に良く見てみると,h5pyのver. が違う(新しく作った環境にはver. 3が入っていた). 実績のあるver. に変えてやる.

$ pip3 uninstall h5py
$ pip3 install h5py==2.10.0

解決!