スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Pythonで機械学習の進捗 §3-5

3.5では「出力層の設計」の説明があります。
私は始め、出力層は自身の値を返す(つまり、恒等関数)のみだと考えていました。

つまるところ、隠れ層(中間層)で行う操作で出したい結果をまとめられると考えていたのです。
ソフトマックス関数を隠れ層として追加する考えです。しかし、これは非効率的でわざわざ中間層を1つ増やしていました。
このことから、わざわざ層を増やすよりも、出力層を使い分けることの方がきれいにまとまっているのです。

恒等関数の紹介は本に書かれている通りで、入ってきた値をそのまま返します。
数学でも恒等的と表現して使うことがあり、その場合も同様の使い方です。

ではソフトマックス関数とはどのようなものなのでしょうか。

式で表すと上のようになります。
良く分からない exp という文字を無視して考えます。
分母にあるMを横に倒した文字はシグマ和(と私は勝手に呼んでます)といって、繰り返される足し算を省略して表す記号です。
つまり、この式の意味は各成分を全ての成分を足した数で割っているということ。

で、結局何してるの? って話ですが・・・

ようは各成分が占める割合を%で表示したいということです。
%で表せたので、分類問題での各比率を出し比べられます。

では無視していた exp について考えましょう。
グラフにするとわかりやすい法則があるので、以下2つをグラフで表します。
・y=exp(x)
・y=x



曲線で上がっているのが y=exp(x) です。この曲線の特徴は2つあります。

・ y=x に比べて急激に増える。
・yの値が0より小さくならない。

この特徴は非常に重要なものです。

1つ目は各成分の違いがより明確になることです。差が広がれば、その分割合も変わってきます。
2つ目は負の値がなくなることです。実は%で表示する時に各成分が全て正の数(負の値ではない)である必要があります。

このような特徴をもつ簡単な関数の1つが exp なんですね。
スポンサーサイト

Pythonで機械学習Extra 1-1

こちらでは機械学習を学ぶ上で私が行っている、本では紹介されていないことを載せてます。

1つ目に行ったことはブラックジャックを機械学習で考えてみようと言うことです。

理由は2つ
・ブラックジャックは数学的に解析されているので、結果の比較がしやすいと考えたこと
・取り扱う情報量が少なく。ランダムを使用することでパターンの収集が楽に行えること

早速、機械学習を行う為にブラックジャックのプログラムを作成してみます。
# -*- coding: utf-8 -*-
#import numpy as np
import random

picture=["heart","spade","diamond","club"]
number=["A",2,3,4,5,6,7,8,9,10,"J","Q","K"]
deck=[True]*52#トランプの残り枚数 こいつはローカル変数
player=[]#「マーク、数」の順に2つでセット
dealer=[]
n=2#枚数

def score(players):
x=0#ループのカウント数
point=0#得点
a=0#エースの枚数
while x < len(players):
if players[x+1]=="A":
a+=1
point+=1
elif players[x+1]=="J":
point+=10
elif players[x+1]=="Q":
point+=10
elif players[x+1]=="K":
point+=10
else:
point+=number.index(players[x+1])+1
x+=2
while a > 0:
if point+10 < 22:#エースの調整
point+=10
a-=1
return point

def shuffle(decks):
decks=[True]*52
x=0
while x < len(dealer):
decks[picture.index(dealer[x])*13+number.index(dealer[x+1])]=False
x+=2
y=0
while y < len(player):
decks[picture.index(player[y])*13+number.index(player[y+1])]=False
y+=2
return decks

def hit(players):
global deck #グローバル変数化させる
x=0#ループのカウント数
while x < 2:
b=deck.count(True)
if b == 0:
deck=shuffle(deck)
R = random.choice(number)#ランダムに数を抽出
r = random.choice(picture)# ランダムにマークを抽出
if deck[picture.index(r)*13+number.index(R)]:#デッキの残りにあればTrue一致で処理する
# print ("drow->",r , R)
deck[picture.index(r)*13+number.index(R)]=False#デッキ情報の更新
players.append(r)
players.append(R)
x+=2

def display():
print ("dealer hands->",dealer[0],dealer[1])
print ("your hands->",player)
print ("your score->",score(player))
print ("deck->",deck.count(True))

def choose(player):
x=0#ループのカウント数
while x < 2:
if score(player) > 21:
break
print ("if you pull is your input [hit].")
print ("if you stop is your input [stand].")
input_line = input(">>> ")#入力
if input_line == 'hit':
hit(player)
display()
elif input_line == 'stand':
break
elif input_line == 'deck':
print (deck)
else:
print ("code error. select again your choose")

def setup(Dealer,players):#始まりの処理
while len(Dealer) < n*2:
hit(Dealer)
while len(players) < n*2:
hit(players)
display()

def finish():#終わりの処理
x=0#ループのカウント数
player_score = score(player)#確定してるのでループ外で変数化
while x < 2:
dealer_score = score(dealer)#更新が必要なのでループ内で変数化
print ("dealer hands->",dealer)
print ("dealer score->",score(dealer))
print ("your hands->",player)
print ("your score->",score(player))
print ("deck->",deck.count(True))
if dealer_score > 21:#ディーラーがバスト
print ("dealer is bust!")
print ("you win!")
x+=2
elif dealer_score > player_score:#ディーラーの勝利
print ("dealer choose")
print (">>> stand")
print ("you lose")
x+=2
elif dealer_score == player_score:#引き分け判定
print ("dealer choose")
print (">>> stand")
print ("draw")
x+=2
elif dealer_score > 16:#ディーラーの投了
print ("dealer choose")
print (">>> stand")
print ("you win!")
x+=2
else:
print ("dealer choose")
print(">>> hit")
hit(dealer)

#処理開始
setup(dealer,player)
choose(player)
print("finish")
if score(player) > 21:
print ("you are bust!")
print ("you lose")
else:
finish()
z=0
while z < 2:
print ("continue? y/n")
input_line = input(">>> ")#入力
if input_line == 'y':
del player[:]
del dealer[:]
print ("<>")
setup(dealer,player)
choose(player)
print("finish")
if score(player) > 21:
print ("you are bust!")
print ("you lose")
else:
finish()
elif input_line == 'n':
break
else:
print ("code error. select again your choose")

print("end")

覚え立てのptyhonで四苦八苦しながら組んだプログラムなので、細かい粗には目をつぶって頂いて・・・
困った箇所はいっぱいありますが、しいていくつか挙げるなら、
・勝敗条件の決定
・デッキのリシャッフル
・ブラックジャックの判定

プレイヤーの行動がhit/standの2つしかありませんが、これでブラックジャックの仕組みを再現できました。
今現在ではdouble down , surrender , insuranceも実装できましたがsplitはまだできていません。

これを使って簡単な機械学習を行いたいと思います。
細かい内容は次回に回します。
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。

Pythonで機械学習の進捗 §3-4

前回の記事§3-1でさらっと流したシグモイド関数について触れていました。紹介の順番があべこべになってしまいましたが気にせずいきます。

3.4では「ニューラルネットワークの実装」を紹介しています。
本では一番隠れ層が少ない3層の場合を取り上げています。

一番単純な機構という前提で考えると、私はこの”ニューラルネットワークの仕組み”を”行列をたくさん掛けるもの”だと思いました。
そりゃ行列を使うと言っているのだから当たり前のことなのですが。
言葉のイメージとして、

ベクトル →  行列  → … → ベクトル
(入力値) × (重み) ×   = (出力値)

このようなことを考えました。
ここには簡略化のためにバイアスを含めていないです。

本では計算を効率的に行う為に行列で実装しています。
ですが、私はむしろニューラルネットワークの仕組みが行列でも表せることを示したことが素晴らしいと思います。
それはなぜか。機械学習を数学的に解析することができるかもしれないからです。
機械学習を解析? と思われるかもしれません。なので、機械学習について考えてみます。

機械学習はここ数年で飛躍的な技術の向上があり、何かと話題になりますが、先行研究は1900年代に既に存在していたそうです。その頃のCPUの性能は現在に比べて著しく低いものでした。なので、機械学習の仕組みそのものを手計算で簡単に再現でき、その良し悪しを解析できました。
しかし、ここ数年の技術向上により機械学習がブラックボックス化してしまい、使えばうまくいくがなぜなのか分からなくなってしまいました。なので、当然解析もできなくなっているのです。
ブラックボックスとは過程が見えない・分からないことです。

このブラックボックス化してしまった機械学習の仕組みを解析することは数学者の中で当面の課題にもなっています。

私は行列の部分に注目することで多少の解析を行えるのではないかと考えています。
個人的な課題としては、行列が時々刻々と更新されて変化していく部分があります。決まってしまえば観察のしようがあるのですが、変わっていく様子となると複雑な計算が増えそうな気がします。

Pythonで機械学習の進捗 §3-3

3.2では活性化関数の紹介がされています。
そして「シグモイド関数」の節タイトルと共に物々しい式が紹介されています。



高校生の時に数学が苦手だった方は、この数式を見て諦めようと思ったのではないでしょうか?

数学好きな私から言わせてもらえば、この式はe(ネイピア数、オイラー数)が持つ魅力の1つの側面だと感じられます。
数学が好きな人にとってはこのように感じ取っているのだという一例と考えてください。

数式の解説より先にグラフでイメージすると理解し易いので、図示したものを載せます。



少しは式のことが理解できたのではないでしょうか?
式の意味を翻訳すると次の主張となります。
・-6から6までの範囲で徐々に0から1に上昇していく関数である。

式の意味を理解すると、次にこのような疑問が浮かぶと思います。
「y=xのような斜めの直線ではダメなのだろうか?」
本のなかではこの疑問にしっかりと触れて、回答を載せています。ですが、比喩表現が多く出てきており、私自身も一読した限りでは分かった気になる程度でした。

数学的な回答だとどうなるのか。それは、
「始めの数式は”非線形”であり、y=xは”線型”という違いがある」
実は善し悪しの問題では無かったのですね。

明確なダメな理由。それは、
「機械学習では非線形関数を重ねることが大切なこと」
なので、y=xのような斜めの直線ではダメ。

数学的な問題と機械学習などの技術的な問題をしっかり区別して考えないと本質は見えてきません。

シグモイド関数の紹介はここで切り上げますが、機械学習を行う上で必要な認識は「シグモイド関数は便利だから使う」ことです。
もし機械学習を進めていき、関数の部分を変更したいと考えたとき、この場所に立ち戻って非線形関数とは何ぞやと勉強すると良いと思われます。

Pythonで機械学習の進捗 §3-2

3.3では「多次元配列の計算」が紹介されています。
行列を知っている人からすれば、多次元配列=行列と紹介できます。
高校数学を覚えている人に紹介するなら、ベクトルの要素にベクトルをもつ形です。
ベクトルも何なのか覚えてない、知らない人はぜひ行列を知るところから始めてみてください。

これもそのうち、行列を知らない人向けに紹介の記事をまとめたいですね。

ここで行列をざっくり紹介すると、複数のデータをまとめて計算できるすごい手法のことです。
他のプログラミング言語だと行列を直接計算できるものがいくつかあって代表的なものは、mathmaticaやscilabなどがあります。
逆に行列計算が標準で含まれていないプログラミング言語はjavaやC言語があり、これらはfor文を使って繰り返し処理で行列の計算をしなければいけないので面倒ですね。

ではpythonはどちらなのでしょうか。答えは後者であり前者です。
まぁ、思い出してみれば第1章のnumpyの紹介の部分で行列を計算していました。
つまり、python単体には行列計算する機構はないですが、numpyに含まれているということですね。
このおかげで行列計算を知らなくても機械学習を学ぶことができるのですね。

Pythonで機械学習の進捗 §3-1

3章からいよいよ機械学習の基礎を成すニューラルネットワークの紹介になります。
ニューラルネットワークとは何でしょうか。
簡単に言えば、2章で学んだパーセプトロンを組み合わせたものです。
3.1ではこのことが丁寧に紹介されています。

私は始めて図3-1を見たとき、「なぜ中間層があるのだろうか。入力から直接出力へ向かう構造の方が単純で良いではないか」と考えました。
ニューラルネットワークがパーセプトロンの組み合わせだと考えれば、図3-1を見て納得することができます。
中間層がある理由、それは2.4節で見たXORゲートのように単純ではないパーセプトロンを組み込むからです。
図2-13を見れば分かります。この図が「パーセプトロンの組み合わせ」つまり、「ニューラルネットワーク」だと考えれば、第0層が入力層、第1層が中間層、第2層が出力層に対応しています。

次の節3.2では活性化関数が登場します。
活性化関数として、ステップ関数とシグモイド関数の2つが紹介されています。
活性化関数はこの2つ以外にもいくつかありますが、機械学習を行う上ではこの2つを知り、区別して使えば実装できるということです。

では、活性化関数とは何をしているのか。それは必要ない情報を捨てていることです。
ステップ関数は0を境にいるいらないを区別しており、シグモイド関数は0に近い情報はいらず1に近い情報が重要であると区別しているのです。
そもそも、私たち人間がものを見て判断する際に、目に見える情報をすべてを認識していません。
例えば、目の前にリンゴが置いてあります。「目の前にある果物はなんですか?」と問われたら、目で見てリンゴだと認識し「リンゴです」と答えます。この時にリンゴが皿の上に置いてあることや背景に見える物は認識していないことが言えます。
つまり、「必要な情報だけを取り出す」ということは「必要ない情報を捨てる」ということです。
なので、活性化関数が行っていることは、人間が無意識に行える「必要ない情報を無視する」処理なのです。

この調整を行えばより精度の高い学習結果が得られると期待できます。そして、この調整の部分はすでに他の人が作った良いものを流用して機械学習させる「Fine tuning」という手法もあります。
私の余裕があれば「Fine tuning」の紹介もしたいですね。

Pythonで機械学習の進捗 §2-3

2.4では「パーセプトロンの限界」という題がついています。
2章で見てきた論理回路のうち3つをpythonでも実装することができると。ここで見ていくのはそのどれとも違うXORゲートというものです。
これは排他的論理和とも呼ばれます。その仕組みとしては、同じ値が入力されれば0を、異なる値が入力されれば1を出力するものです。
ここだけ聞けば、なんの変哲もない論理回路の1つとしか感じません。実際に、パーセプトロンの作り方を無視して実装しようとするならばifを用いて簡単に作ることができます。

では、この章で一所懸命に説明していることは何か。
それは”パーセプトロン1つで実装できない論理回路をいくつか使用すれば実装できる”ということです。
結論を先に述べると、XORゲートはANDゲートとNANDゲートとORゲートの3つを組み合わせればパーセプトロンの作り方で作れます。

本の中ではなぜパーセプトロンの作り方でXORゲートが実装できないのかを説明しています。
図や線型/非線形といった単語が出てきて説明をされて、だた読んでいくと、すごく分かりづらい箇所だと感じました。
かみ砕いて説明するならば、
パーセプトロンの作り方とは実は直線で0と1の部分に分けていると言うこと。
そして、XORでは直線1本では、うまく分けることが出来ないこと。

線型/非線形とは何なのか。一言で示すなら「線型とは直線のこと、非線型とは直線以外のこと」です。
数学的にはもっと厳密な定義があるのですが、ここでは必要ないでしょう。

2.5の部分でXORゲートが組み合わせで作れることが示されます。
どのように実装するのかは本に載っているので省略します。
私がここでもったいないと思うことは図2-6や図2-8でわざわざ視覚的にわかりやすく場合分け出来ること(また、場合分けしたいこと)を示しているのに、組み合わせてXORゲートを実装した場合にどのような図になるのかが乗っていないのです。
図で表したものが下になります。



当たり前ですが、直線を2本使えば分けられるということです。

この章で本が伝えたかったことは何だったのでしょうか。
それは、パーセプトロンの考え方と「バイアス」「重み」といった今後使う用語の説明をしたかったことです。
それ以外はありません。
なぜならここまで一所懸命に説明した論理回路はこの後使われなくなるからです。
もちろん本当に基礎的な部分を支える考え方として論理回路は重要なのですが、pythonで機械学習を実装する上では知らなくてもできるということです。

Pythonで機械学習の進捗 §2-2

2.2では「論理回路」の紹介がされています。

ここで紹介されるものは、もっとも単純な論理回路となっています。何が単純なのか。それは入力の数が2つで出力が1つだからです。
論理回路を考える際に入力される値は「0」か「1」のいずれか(論理学などでは「True」or「False」)です。
入力が1つの時は出力も1つしかなく、入力された値がそのまま出力される値となる為、回路と呼ぶにはあまりにもなさ過ぎます。
なので、もっとも単純な論理の回路は入力が2つある場合なのです。

本では真理値表を使用してANDゲート(またはAND回路と呼ぶ)とNANDゲート、ORゲートが紹介されています。
一般的に論理回路を考える際に用いられるのが真理値表であり、用いるメリットとして入力数が少ない内はコンパクトに収まることです。
他の表現方法として(私が知る限りでは)二分木で表現する方法があります。下の図が二分木の形となります

こちらのメリットは入力数が増えた際にも見やすく、表の省略や圧縮の方法が理論的に確立しているので考えやすいことです。

話は逸れましたが、ANDゲートとNANDゲート、ORゲートをパーセプトロンで表せることはすぐに分かります。
2.3ではその実装ということで、pythonを用いてそれぞれを実装します。
プログラムは単にifで分岐させています。当然4つの場合分けするのではなく、2つの場合分けを行います。結果は2通りなので、自然な発想です。
また、ここで「重み」と「バイアス」の導入があります。実装をする上でバイアスをつける必要はないのですが、3章で使用する為にここでわざわざ紹介しています。

私が2章で一番面白く感じた箇所は「XORゲート」の紹介部分ですが、その感想については次の記事で。
プロフィール

yamoto.tvp

Author:yamoto.tvp
FC2ブログへようこそ!
大学の数学科に通うほど、数学が好きな私です。
趣味で勉強している”機械学習”などの進捗を載せています。

最新記事
最新コメント
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。