External Memory

プログラミング周辺知識の備忘録メイン

Gradient descent optimizerとAdagrad optimizer

先日のTensorFlowで最適化法について知識があまりなかったので少しだけ調べた。

Gradient descent optimizerとAdagrad optimizer

先日使ったoptimaizerはGradient descentとAdagrad。
これらは次元ごとに分離して最小値が得られる。

最急降下法は以下の数列が収束することにより、\displaystyle f(x)の最小値を求めるアルゴリズム

\displaystyle \mathrm{x_{n+1}} = {\mathrm{x_n}} - \eta\,{\mathrm{grad}}\,f{(\mathrm{x_n})}
\displaystyle \eta : learning rate(学習率)


Adagradの場合は以下のようになる。

\displaystyle \mathrm{x_{n+1}} = \mathrm{x_n} - \frac{\eta}{\sqrt{G_n + \epsilon}} \,\mathrm{grad}\,f(\mathrm{x_n})

\displaystyle G_n : 0からnまでのgrad[f(x)]の二乗和、\displaystyle \epsilon : ゼロ割防止のための項


Adagradに関する文献
http://www.jmlr.org/papers/volume12/duchi11a/duchi11a.pdf

元文献だが、かなり内容が専門的なので読み通すことすら困難だった。

https://arxiv.org/pdf/1212.5701v1.pdf

こちらのほうが概要的で易しい。
AdadeltaメインでAdagradの初期学習率の選択に関する問題と学習率の低下の問題を回避しようとしている。


Gradient descent とAdagradの数式を比較すると
Adagradの方は学習率が動的ループが進めば進むほど学習率は低下するので
うまくいけば大域では幅広く最小値を探し出し、
最小値付近で素早く収束すると予想できる。

また初期の勾配が大きいと学習率の低下が大きいので、
繊細な学習率の初期設定が必要。

Pythonで検証

import numpy as np

div = 1000000
R = np.pi

def f(x):
    return (x-0.7)**2

def g(x):
    return np.sin(x-np.pi/2-0.7)+1

def gradientdecent(init,rate):
    eps = 1e-5
    N = 1000
    v = np.linspace(0,R,div)
    arrf = f(v)
    arrg = g(v)
    diff = np.gradient(arrf,R/div)
    difg = np.gradient(arrg,R/div)
    x1 = x2 = round(div/R * init) 
    
    for i in range(N):
            
        n_x1 = x1 - rate * div/R * diff[np.int(x1)]
        
        if abs(n_x1 - x1)*R/div < eps:
            print("{0}\ngradientdicent {1}".format(rate,i+1),end="  ")
            break
        x1 = n_x1
        if i==N-1 :print("{0}\ngradientdicent {1}".format(rate,N),end=" ")
    
        
    for i in range(N):
            
        n_x2 = x2 - rate * div/R * difg[np.int(x2)]
        
        if abs(n_x2 - x2)*R/div < eps:
            print(i+1,end="  ")
            break
        x2 = n_x2
        if i==N-1 :print(N,end="  ")
        
    return f(x1/div*R) + g(x2/div*R),x1/div*R,x2/div*R

def adagrad(init,rate):
    eps = 1e-5
    e = 1e-8
    N = 1000
    v = np.linspace(0,R,div)
    arrf = f(v)
    arrg = g(v)
    diff = np.gradient(arrf,R/div)
    difg = np.gradient(arrg,R/div)
    x1 = x2 = round(div/R * init) 
    sum_x1 = sum_x2 = 0
    
    for i in range(N):
        
        sum_x1 += diff[np.int(x1)]**2
        n_x1 = x1 - (rate * div/R * diff[np.int(x1)]/np.sqrt(sum_x1+e))
        
        if abs(n_x1 - x1)*R/div < eps:
            print("adagrad       ",i+1,end="  ")
            break
        
        x1 = n_x1
        if i==N-1 :print("adagrad       ",N,end="  ")

    
        
    for i in range(N):
        
        sum_x2 += diff[np.int(x2)]**2
        n_x2 = x2 - (rate * div/R * difg[np.int(x2)]/np.sqrt(sum_x2+e))
        
        if abs(n_x2 - x2)*R/div < eps:
            print(i+1,end="  ")
            break
        
        x2 = n_x2
        if i==N-1 :print(N,end="  ")

        
    return f(x1/div*R) + g(x2/div*R),x1/div*R,x2/div*R

ratelist = [0.001,0.01,0.1,0.2,0.3,0.4,0.5,0.7,1]

for i in ratelist:
    print("({0[0]:.4f},{0[1]:.4f},{0[2]:.4f)}".format(gradientdecent(0.5,i)))
    print("({0[0]:.4f},{0[1]:.4f},{0[2]:.4f)}\n\n".format(adagrad(0.5,i)))

結果

学習率
最適化法 更新回数(f, g)(最小値, f収束値x, g収束値x)

0.001
gradientdicent  1000  1000  (0.0034, 0.6730, 0.6262)
adagrad     1000   1000  (0.0346, 0.5581, 0.5299)

0.01
gradientdicent  298   529  (0.0000, 0.6995, 0.6990)
adagrad    537   1000   (0.0001, 0.6989, 0.6878)

0.1
gradientdicent  39  74   (0.0000, 0.7000, 0.6999)
adagrad     17   49  (0.0000, 0.7000, 0.6999)

0.2
gradientdicent  19   39   (0.0000, 0.7000, 0.7000)
adagrad    2   17   (0.0000, 0.7000, 0.7000)

0.3
gradientdicent  12   26  (0.0000, 0.7000, 0.7000)
adagrad    11   9   (0.0000, 0.7000, 0.7000)

0.4
gradientdicent  8   19   (0.0000, 0.7000, 0.7000)
adagrad    12   3  (0.0000, 0.7000, 0.7000)

0.5
gradientdicent  2  15  (0.0000, 0.7000, 0.7000)
adagrad    12   8   (0.0000, 0.7000, 0.7000)

0.7
gradientdicent  13   10  (0.0000, 0.7000, 0.7000)
adagrad    11   12  (0.0000, 0.7000, 0.7000)

1
gradientdicent  1000  3   (0.0403, 0.4992, 0.7000)
adagrad    10   12   (0.0000, 0.7000, 0.7000)


パラメータをコントロールして目当ての値で最小に収束するようにしたが、
sin関数は別の凹へ移動してしまうことがあったので、
局所的な最小値(極小)で収束してしまう特徴はどちらでもありそうです。

またgradientdecentでは学習率を上げると発散しそうな特徴も見られた。

adagradの場合は徐々に学習率が減少するので、発散を防ぎ、
より確実に速く収束する可能性はあるが、
収束が遅くなることもあるのがデータから見て取れる。


先日のTensorFlow機械学習結果で正答率が低かったのは
対数関数をフィット出来なかったからだろうか??

TensorFlowの機械学習を利用した擬似星の等級の計算

Tensorflowの使い方と深層学習(というより機械学習)の勉強の導入として、
単純モデルを利用したプログラムを作った。
参考にしたのは以下TensorFlowホームページのGET STARTEDページ
Getting Started  |  TensorFlow



今回題材として用いたのは星の等級の計算。
星の明るさはよく一等星、二等星など等級で表されるが、
絶対等級Mと観測者からの距離d(パーセク)から
以下の式で見かけの等級mを見積もることができる。

M = m + 5 - 5 * log10(d) (wikipediaより)

十分なサンプルデータを見つけることができなかったので、
ランダムにデータを生成して、TensorFlowに学習させた。

単純モデルの学習過程は大体以下の通りである。

  • データ値にテンソルで重みをつけ、バイアスと組み合わせて特徴量とする。
  • softmax関数などで特徴量を際立たせる。
  • 正答と予測回答との誤差関数を設定する。(クロスエントロピーなど)
  • 誤差関数を最小にするようにオプティマイズする。(訓練)

プログラム

# -*- coding: utf-8 -*-

import tensorflow as tf
import math
import random

N = 5000
R = 9 #label Range
times = 1000 # times of training         
                 
#absolute magnitude
abmlist = [random.uniform(0,1) for i in range(N)] 

#distance
#d_list = [random.uniform(0.001,1) for i in range(N)] 
d_list = [random.uniform(0.1,1) for i in range(N)]

#factor = [0.1 for i in range(N)] 

#apparent magnitude(答え合わせ用リスト)
apmlist = [ [0 for i in range(R)] for j in range(N)]

for i in range(N):
    #星の等級式 apm = abm + 5 * log(distance)- 5 
    #リストインデクス値を非負整数にするため小数点を丸めて定数を加えてlabel作成
    #apmlist[i][round(abmlist[i] * 15 - 5 + 5 * math.log10(d_list[i] * 100) - 5) + 15] = 1 #R = 31
    apmlist[i][round(abmlist[i] * 3 + 5 * math.log10(d_list[i] * 10))] = 1 #R = 9
    

x = tf.placeholder(tf.float32,[None,2])
W = tf.Variable(tf.zeros([2,R]))

"""
x = tf.placeholder(tf.float32,[None,3])
W = tf.Variable(tf.zeros([3,R]))
"""
b = tf.Variable(tf.zeros([R]))
y = tf.matmul(x,W) + b

y_ = tf.placeholder(tf.float32,[None,R])


cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y_,logits = y))
train_step = tf.train.GradientDescentOptimizer(7.0).minimize(cross_entropy) 
#train_step = tf.train.AdagradOptimizer(150).minimize(cross_entropy)

sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

for i in range(times):
    sess.run(train_step,feed_dict={x:[[i,j] for (i,j) in zip(abmlist[:-500],d_list[:-500])],y_:apmlist[:-500]})
    
correct_prediction = tf.equal(tf.arg_max(y,1),tf.arg_max(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
testlist = [[i,j] for (i,j) in zip(abmlist[-500:],d_list[-500:])]
labellist = apmlist[-500:]

"""
for i in range(times):
    sess.run(train_step,feed_dict={x:[[i,j,k] for (i,j,k) in zip(abmlist[:-500],d_list[:-500],factor[:-500])],y_:apmlist[:-500]})
    
correct_prediction = tf.equal(tf.arg_max(y,1),tf.arg_max(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
testlist = [[i,j,k] for (i,j,k) in zip(abmlist[-500:],d_list[-500:],factor[-500:])]
labellist = apmlist[-500:]
"""

print(sess.run(accuracy,feed_dict={x:testlist,y_:labellist}))

結果

大まかな正答率は以下である。

・0.33 ± 0.03  (R=31
・0.75 ± 0.03  (R=9
・0.76 ± 0.01  (R=9 データ種類増 補正値導入
・0.885 ± 0.005 (R=9 optimaizer引数 0.5→7.0
・0.885 ± 0.010  (R=9 optimaizer adamgrad利用 引数150
・0.83 ± 0.02  (R=9 train回数1000→5000変更

単純モデルからか、正答率は最良でも89%程度であり良い結果ではなかったが、
一応学習はされているようではある。

labelの次元数(値域)で正答率がかなり違ってくるようで、
正確さを求めるなら定義域と値域には気を使わないといけない。

単に確率として考えても選択肢の数から
正答率が上がるのは当然ともいえるかもしれない。

いろいろいじってみて、良好な結果を得るには

  • データ数
  • 訓練回数
  • label数

辺りのパラメータををうまくバランスとれるように設定しないといけない。
単に訓練回数やデータ数を多くしてもうまくいかない。

また、今回はランダムデータを用いて学習させたが
この学習が実際のデータではうまくいかない可能性もある。


アルゴリズムやモデルについての知識がまだまだ甘い。
機械学習ならSVCのほうが正答率が良かったかもしれない。
CNNに関しても試してみる。


プログラムを書くにあたってエディタはspyderを利用した。

>conda install -n tensorflow spyder
>activate tensorflow
>spyder

開発環境の構築(2) C++

C++の勉強用環境構築。
息抜きのオンラインジャッジ用途としても使いたいので、
windowsgccが使えるようにする。
editorとしてVisual Studio Codeを使い、
コンパイル用途以外でも使えそうなbash on Ubuntu on windowsを組み合わせた。

bash on Ubuntu on windowsインストー

・スタートボタン右クリック
・「アプリと機能」をクリック
・右上の「プログラムと機能」をクリック
・「Windowsの機能の有効化または無効化」をクリック
・「Windows Subsystem for Linux (Beta)」にチェック→インストー

bash.exeを起動させる。

-- ベータ機能 --
これにより Windows に Ubuntu がインストールされます。Ubuntu は Canonical によって配布される製品であり、
次のサイトに示される条件に基づいてライセンスされています。
https://aka.ms/uowterms

続行するには、"y" を入力してください: y
Windows ストアからダウンロードしています... 100%
ファイル システムを展開しています。この処理には数分かかります...
Ubuntu のロケールを Windows のロケール (ja-JP) と一致するように設定しますか?
既定のロケールは en_US です。
続行するには、"y" を入力してください: y
既定の UNIX ユーザー アカウントを作成してください。ユーザー名は、Windows のユーザー名と一致する必要はありません。
詳細: https://aka.ms/wslusers を参照してください
新しい UNIX ユーザー名を入力してください: [username]を入力
新しい UNIX パスワードを入力してください:
新しい UNIX パスワードを再入力してください:
passwd: password updated successfully
インストールが正常に終了しました
環境が間もなく開始されます...
ドキュメントを参照できる場所: https://aka.ms/wsldocs
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

$ ls -al
合計 8
drwxr-xr-x 0 user user  512  7月 23 09:24 .
drwxr-xr-x 0 root root  512  7月 23 09:24 ..
-rw-r--r-- 1 user user  220  7月 23 09:24 .bash_logout
-rw-r--r-- 1 user user 3771  7月 23 09:24 .bashrc
-rw-r--r-- 1 user user  655  7月 23 09:24 .profile

$ sudo apt update
$ sudo apt upgrade

$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

VScodeをインストー

Visual Studio Code - Code Editing. Redefined

からダウンロード後、指示に従ってインストール。

統合ターミナルをbash on Ubuntu on Windowsとするため、
VScode 基本設定→設定から
右パレット(ユーザー設定)に下記を書きこむ。

{
  "terminal.integrated.shell.windows": "C:\\WINDOWS\\sysnative\\bash.exe"
}

ctrl+@ で統合ターミナルとしてbashが開く。

#include <iostream>
using namespace std;

int main(){
    cout<<"test"<< endl;
    return 0;
}
$ g++ -Wall -O2 test.cpp
$ ./a.out
test


C/C++拡張機能をインストールしたが
VScodeからはコンパイルデバッグができなかった。
c_cpp_properties.jsonにincludeパスを追加しても通らない。

とりあえずデバッグgdbありゃいいかな。

$sudo apt install gdb 

$ gdb ./a.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
---Type <return> to continue, or q <return> to quit---
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...(no debugging symbols found)...done.
(gdb) b main
Breakpoint 1 at 0x40084a
(gdb) run
Starting program: /mnt/c/Users/[username]/vsbin/a.out

Breakpoint 1, 0x000000000040084a in main ()
(gdb) i r
rax            0x400846 4196422
rbx            0x0      0
rcx            0xc0     192
rdx            0x7ffffffde4b8   140737488217272
rsi            0x7ffffffde4a8   140737488217256
rdi            0x1      1
rbp            0x7ffffffde3c0   0x7ffffffde3c0
rsp            0x7ffffffde3c0   0x7ffffffde3c0
r8             0x7fffff3efac0   140737475705536
r9             0x7fffff3e4780   140737475659648
r10            0x32f    815
r11            0x7ffffecda280   140737468277376
r12            0x400750 4196176
---Type <return> to continue, or q <return> to quit---
r13            0x7ffffffde4a0   140737488217248
r14            0x0      0
r15            0x0      0
rip            0x40084a 0x40084a <main+4>
eflags         0x246    [ PF ZF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0
(gdb) x/16 $rsp
0x7ffffffde3c0: 4196544 0       -20182992       32767
0x7ffffffde3d0: 0       0       -138072 32767
0x7ffffffde3e0: -138056 1       4196422 0
0x7ffffffde3f0: 0       0       -1844874634     1522134322
(gdb) x/16x $rsp
0x7ffffffde3c0: 0x004008c0      0x00000000      0xfecc0830      0x00007fff
0x7ffffffde3d0: 0x00000000      0x00000000      0xfffde4a8      0x00007fff
0x7ffffffde3e0: 0xfffde4b8      0x00000001      0x00400846      0x00000000
0x7ffffffde3f0: 0x00000000      0x00000000      0x92097276      0x5ab9ed32
(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400846 <+0>:     push   %rbp
   0x0000000000400847 <+1>:     mov    %rsp,%rbp
=> 0x000000000040084a <+4>:     mov    $0x400944,%esi
   0x000000000040084f <+9>:     mov    $0x601060,%edi
   0x0000000000400854 <+14>:    callq  0x400710 <_ZStlsISt11char_traitsIcEERSt13basic_ostream
IcT_ES5_PKc@plt>
   0x0000000000400859 <+19>:    mov    $0x400730,%esi
   0x000000000040085e <+24>:    mov    %rax,%rdi
   0x0000000000400861 <+27>:    callq  0x400720 <_ZNSolsEPFRSoS_E@plt>
   0x0000000000400866 <+32>:    mov    $0x0,%eax
   0x000000000040086b <+37>:    pop    %rbp
   0x000000000040086c <+38>:    retq
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) c
Continuing.
test
[Inferior 1 (process 14) exited normally]
(gdb) exit
Undefined command: "exit".  Try "help".
(gdb) q