PythonでTripletな組み合わせを作る
ラベルに基づいて,ある基準となるデータと,そのデータと同じクラスのデータ,異なるクラスのデータの3つのデータの組み合わせを作るPythonプログラムについて書きます.
何かしらのデータdataとdataのクラスに基づいたラベルtargetが付与されたデータセットdatasetが手元にあったとき,ラベル情報に基づいてdataの組み合わせを作りたいと思いました.
その組み合わせは,ある1つのx_anchorに対して,そのx_anchorと同じクラスのデータx_positive,x_anchorと異なるクラスのデータx_negativeで構成される3つのデータの(tripletな)組み合わせです.そしてその組み合わせをbatch_size個だけ用意するプログラムを書くときに少し苦しんだので以下で説明していきます.
この組み合わせの問題ですが,例えば箱の中にリンゴとナシが4個ずつ入っていて,リンゴとナシにはそれぞれマジックで番号が書いてあります.箱の中を見て1つ取り出してメモって,戻して...の動作を3回繰り返します.そのうち2回は同じ果物でもう1つは異なる果物じゃなきゃダメ.そしてこの動作をてきとーに10回繰り返してください.という問題.さぁ,どうしましょう?
こんな問題をプログラムで記述したいと思いました.
まず思いついたのは,条件を満たす組み合わせを書き出して,その中から10回分だったらその数だけてきとーに選ぶという手法.For文で組み合わせをかたっぱしから総当たりしてリストを作って,リストから任意の数だけ取るだけ.とても簡単.
リンゴとナシが4個ずつ存在する問題なら問題ないですが,杏子,ライチ,スイカ,メロン,イチゴ,モモ,ミカン,ブドウ,イチジク,ザクロの10種類の果物がそれぞれ1000個ずつ箱に入っている問題だと9000万通りも作れてしまい普通のパソコンではメモリに載らない.
問題によって計算時間が異なったり,プログラムを実行する環境を選ぶコードを作るのはマズいということになりました.
次に考えたのは,10回分の組み合わせがほしい場合は,10回てきとーに果物を取り出して種類と番号をメモっておく.次にすべての種類の果物を10個ずつてきとーに小箱に移しておいて,小箱から同じ種類と異なる種類のものを1つずつてきとーに取り出す作業を10回行うという作戦.これだと1つの果物が何個ずつ用意されていても小箱の中しか見ないためてきとーに選ぶ労力が減る(メモリの使用量は1つのクラスごとのデータ数に依存しない)と考えたので実装しました.
小箱に移す作業はnumpy.where()で,小箱から取り出すのはpythonの組み込みのリストのメソッドpop()を使います.同じクラスじゃない果物をランダムに1つ取り出すという作業の「じゃないものをランダムに」というコードを書くのが難しくて,二度と読みたくないようなコードを書いてしまいました.
numpy.random.choice(果物の種類, 取り出す数, それぞれの種類からどれくらいの確率で取り出すか決める確率のリスト)で3つ目の引数の確率のリストのうち,取り出したくないものだけ確率を0にしてあげると,「じゃないものを(等しい確率で)ランダムに取り出す」ことができます.
もっとスマートなtripletな組み合わせを作る方法がありそうですが,これが限界でした.
今回の実験に使ったコードと解説は一番下に載せてあります.
今回の実装でよく使ったコードを最後にまとめておきます.
import numpy
matsuge = [1, 2, 3, 4, 5]
# 使える
hoge = numpy.random.permutation(matsuge)
# 使えない
hoge = numpy.random.shuffle(matsuge)
# 使える.matsugeのリストそのものがシャッフルされる
numpy.random.shuffle(matsuge)
# matsugeの要素の値がnのインデックス群が返ってくる.タプルで返ってくる.
n=1
matsuge = [i for i in range(10) for j in range(10)]
indexes = numpy.where(matsuge == n)[0]
# リストの最後の要素をリストから削除して返す
matsuge = [1, 2, 3, 4, 5].pop()
# pのリストの確率で0~2までの値のうち1つを返す(pは省略可).今回だと0が返ってくる確率が80%,2は0%.タプルで返ってくる.
matsuge = numpy.random.choice(3, 1, p=[0.8, 0.2, 0.])[0]
最後に今回のソースコード
Make a batch size of shuffle and combination array ...
Numpyで作るXOR, Irisデータセットを分類するニューラルネットワーク
前回作成したニューラルネットワーク(Neural Network)はNumpyを使わず、行列積のような数学的演算はすべて深くネストされたfor文で処理していましたが、今回はその部分をNumpyで書き換えたコードを載せます。
今回よく使ったメソッドを以下にまとめておきます。
- numpy.zeros(): ゼロで満たしたベクトルの作成タプルを引数に渡せば行列を作成
- numpy.ones(): zeros()と似てて1で満たされた行列を作成。任意の値で満たした行列を作りたい場合は、np.ones() * hoge(なんかの値)でいける
- numpy.dot(): 行列積を計算。np.dot(a, b)やa.dot(b)として使える
- numpy.random.permutation():引数をN(int)とすると0からN-1までのランダムな順列を作成して返す。
- ndarray[index1, index2, index3]: 取ってきたいインデックスを入れるとndarrayの要素を返す。
python script version former numpy neural network
せっかく、Numpyを使ってコードを書いたので、
前回のfor文をぐるぐる回したコードと今回載せたNumpyを使ったコードの処理時間を比較してみました。(10回試行した平均値を算出)
- for文を深くネストさせたコードの処理時間は8.51秒
- Numpyを使ったコードは4.38秒
という感じでNumpyを使って書き換えたほうが約2倍の速度で処理を行うことができることがわかりました。
さすがNumpy!はやいっすね!
必ずしもNumpyが速いわけではなく、append()など既存のリストを作り直す処理は遅い傾向があります。append()をfor文で回すくらいなら、
まずはfor文を使わなくて済むNumpyのメソッドを使うべきで、それが無理なら、最初にリストを作っておいて、値を代入していくだけの処理や、リスト内包表記を使ったほうが確実に処理速度が上がります。
今回は以上になります。
次回は今回のコードを変更して、Auto Encoderを作りたいと思います。
最後にJupyterで書いたときのコードを載せておきます。
PythonでXORとUCI_Iris_datasetを解くニューラルネットワーク
前回は線形の問題を解くニューラルネットワーク(Neural Net)のコードを書きましたが、今回は、非線形のXOR、巷で有名?なIrisデータセットを識別するNeural Netのコードを書きました。
前回のコードは2入力3出力の2層のニューラルネットワークをコーディングしましたが、平たく書いてしまったために層を増やすことも層の次元数(ユニット数)も変更することが簡単ではないものとなっていました。
今回は、できるだけ汎化的に使える構造でニューラルネットワークを書いてみました。もともと、1年以上前にPythonを初めて使った時にPythonの練習として
- Hello worldの出力
- Fizz Buzz問題
- とりあえず学習するNeural Network
- クラスや継承を使った3.よりスマートなNeural Network
を順にやっていました。
今回書いたプログラムは4.をちょこちょこ修正して(過去のコードのヒドさ、読みにくさは凄まじいもの...)バッチ学習などに対応したものにしました。
次回は、今回のコードをNumpyで書き換えて、より簡素に、より処理速度が速いPythonコードを書いて紹介したいと思います。
今回のコードは以下のものになります。
ちなみに記事の最後に最後にJupyter(最近ハマってる笑)をつかったコードも載せておきます。
Python script version. It's same content as the fo ...
今回のコードで使用したデータセットは、XOR(排他的論理和)とIris datasetです。XOR問題は、論理演算の1つのアレです。
詳しくは以下のWebページを参照してください。
XORとは|排他的論理和|EOR|EX-OR|eXclusive OR - 意味/定義 : IT用語辞典
XORを識別する関数を学習することは、2入力1出力の2クラス分類問題ですが、それぞれのクラスを1本の直線でわけることができない非線形の問題となっています。
http://www.gifu-nct.ac.jp/elec/deguchi/sotsuron/niwa/node11.html
もう1つのIris(アヤメ)データセットはカリフォルニア大学アーバイン校が提供するデータセットです。
アイドルユニットの画像が詰まったデータセットではありません。
http://manasite.net/animemanga/1661/
150個のデータが入っており、4入力の3クラス分類問題となっています。配布時には教師ラベルがIrisの花の種類の名前?となっているため、機械学習の手法をこのデータセットに適用する場合は教師ラベルを設計することも最初の問題となってきます。
UCI Machine Learning Repository: Iris Data Set
(以前、Python&機械学習デビューした時にはこの教師ラベル設計で苦しめられました...)
今回は、One-hot vectorで教師ラベルを振りました。
3クラスなら[[1, 0, 0], [0, 1, 0], [0, 0, 1]]などでしょうか。
最近は、自然言語処理の方で、1つの言語で使われる記号-character(アルファベット、数字など)をone-hot vectorで表して、文章をとてもスパースな行列にembeddingして入力データを表現してCNNに突っ込んで、文章分類を行うことがホットになってますね。
今回のコードでつまったのが、CSVの読み込みに以前はPythonの組み込みのCSVをimportして使っていたのが、Pandasを使ってみたところ、Data frame型で読み込まれて、組み込みのリスト型に直して整形する部分です。
pandasのdataframe型をnumpyのarray型に変換する - 新kensuke-miの日記
標準python、numpy、pandasを行ったり来たりするために① - Qiita
今回はNumpyを使わない縛りをしていたので、「Numpyないとこれできないんだー、不便すぎる(T_T)」みたいな気持ちに何度かなっていました。
結果的に、それぞれのデータセットで学習誤差(loss)の値は下がっていき、ほぼ100%の正解率を出すことができたので、こちらのコードで正しくNeural Netを学習して、非線形問題を解くことができる識別関数を獲得できたと思います。
排他的論理和の問題の学習時の誤差の推移は以下になりました。
縦軸が誤差の値(1epoch中のそれぞれのデータのlossの合計値を出して、1つのデータの平均のlossを出していなかったことがここで発覚、ミスってすんません)。横軸が学習epochになります。
Iris data settにおける学習時の誤差の推移は以下のようになりました。
データセットは、hold outで150個中、30個のデータ(全体の20%)をランダムにテストデータとして作成して、正解率(accuracy)を出しました。コードのハイパーパラメータではこの精度が最大っぽかったです。
以上になります。
次回はこのコードをNumpyを使ってよりスマートなコードに直したものを載せたいと思います。
Pythonでニューラルネットワークを実装してみた。
Python(一部Numpy)を使って2層ニューラルネットのスクリプトを作成しました。
ディープラーニング(Deep Learning)は魅力的ですが、ディープラーニングの基礎となる
ニューラルネット(CNNだとfull connected layerと呼ばれますね)の実装および、
誤差逆伝播法(Back propagation)に使う微分の更新式をチェインルールを使って手で求めて、
ゴリゴリと実装していくのも良い練習となるので、Netflixで'24'を24時間で見ながら
だらだらコーディングしていました。
ニューラルネットは、回帰または分類を行うことができる機械学習手法の1つ、
Back propagationは、ニューラルネットのパラメータを学習させるために使う最適化手法
簡単に言うと、こんな感じだと思います。
ニューラルネットといっても、脳のニューロンをモデルに提案された機械学習手法の1つであって、"脳を模倣しているから、ニューラルネットが人工知能hogehoge..."とは考えるべきではないです。
「人工知能」「ディープラーニング」という言葉がポピュラーになってきていますが、ニューラルネット自体ははるか昔に提案されているものという認識は持っておくべきであると思います。
「ニューラルネットが脳のニューロンを模している」のは面白いことで、複数の入力の線形和が閾値を超えたときにのみスパイクとなって次のニューロンに入力が伝播していく点は再現できており、閾値をシグモイド関数のような活性関数を定義して表現しているのは面白いです。
一方で、脳のニューロンは、神経細胞によってそれぞれ閾値が異なっている点、誤差逆伝播法のような学習を行わない点が機械学習におけるニューラルネットと異なる点だと思います。
神経細胞がSTDPなどの可塑性を用いて学習する一方で、ニューラルネットは誤差があるときだけ逆伝播して学習をしているので、正しい出力が行われた場合にも学習が進むような最適化手法が考えられると、脳科学の知見ともさらに親和性がとれて面白くなるのではないかと思います。
「Pythonによるニューラルネットの実装」からどんどん話がずれていますが、
機械学習のニューラルネット、full connected layer、全結合層、back propagationなどのキーワードについてはググればたくさん資料が出てきますので、ここではいくつか参考になりそうなサイト載せて理論の話は省きます。
ニューラルネットによるパターン認識 - 人工知能に関する断創録
Pythonによる実装について、基本的にはPythonの組み込み関数を使って実装を行い、Numpyはリストのシャッフルと、バッチサイズにスライスするために一部使用しました。
そのほかはNumpyの内積計算dot()などニューラルネットワークの実装に便利なメソッドも使っていません。
コードに関しては基本的にコメントを残しておきました。実装してみたい方は参考にしてください。
今回のニューラルネットは、線形分離可能な2入力3出力のToyデータセットを解くニューラルネットになっています。活性関数には、シグモイド関数など使わず、y = xの単純な実装になっています。3クラスの分類問題なので教師データはone-hot vectorを使用しました。
CUIのIrisデータを解くときは、活性関数の部分を非線形にして、
forward()、backward()の部分を少し書き直す必要があります。
層を増やす場合は、train_nn()も1行ほど書き直す必要があると思います。
次回はもうちょっと、汎用的に使えるニューラルネットのコードを書いてみたいと思います。そのときは、シグモイド関数も実装して、オブジェクト指向で書きたいと思います。
前置きが長くなりましたが、以下がJupyterで書いたニューラルネットのコードになります。誤差を可視化してみたところ、きれいに下がっていっているようなので、正しく学習できているのかなーと思っています。
Python(Numpy)で大津の2値化(Otsu method)を実装してみた。
前回の予告通り、Python(Numpy)で大津の2値化を実装してみました。
大津の2値化は2値化手法の1つで、統計的な観点から2値化を行うための
閾値を決定しているため、
直感的でわかりやすいうえに、けっこうきれいに
2値化できる手法らしいです。
OpenCVにも実装されていている手法です。
opencv.jp - OpenCV: フィルタと色変換(Filters and Color Conversion)サンプルコード -
OpenCVで1行で実装できますが、
今回は1年ほど前にNumpyを使わずに実装していたコードがあったので、
Numpyとリスト内包表記をつかって書き換えてみました。
大津の2値化の閾値を算出する過程、式は以下のサイトを参考にしました。
せっかく今回もJupyterを使って実装したので、
計算式をJupyterに書いておけばよかったかなと少し後悔していますが、
以下にソースコードを載せました。
前半で関数を定義していって、後半で4枚ほど画像を処理しています。
それぞれの出力画像において、
元画像、グレースケール画像、大津の手法、輝度値128の閾値で2値化
で4枚横に並べて出力しています。
輝度値128の閾値を緑色で縦に線を引いています。
大津の2値化(Otsu method)をPython(Numpy)を使って実装
結果的に、正しく実装できていない気がします。
処理する前の画像、実装した手法で2値化した画像、
OpenCVによって2値化された画像(cv2.threshold(img,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU) : opencv.jp - OpenCV: フィルタと色変換(Filters and Color Conversion)サンプルコード -)
を並べてみると、
こんな感じになります。
今回作成した2値画像のレナさんは、なんか怖そうな顔になってます...
コーディングが下手でこんな顔にしちゃってゴメンね。
いつかまた、コードを修正してきれいに2値化するよ...!
Jupyterで作った画像処理コードをGistを使ってブログにのせる
便利だから使っちゃいなよー!と進められるJupyterを使ってみました.
今回載せるのは以下の内容になります.
- Pythonで書いた画像処理に関する処理
- Jupyterのインストール
- JupyterのコードをGistを用いてブログに貼る方法
練習として画像処理に関するPythonプログラムを書きました.
使ったライブラリは,numpy, matplotlib, pillowがメインです.
今回解いた問題はこちらからお借りしました.
Jupyterは,Anacondaをインストールしてコンソールで"jupyter notebook"を入力したら
ブラウザが立ち上がって使用可能.お手軽ですね.
こちらのサイトを参考に勉強しました.
Jupyter Notebook を使ってみよう – Python でデータサイエンス
遠隔のサーバーにSSHでつないで,Jupyterを起動してみたいが
それはまた別の機会に!
Jupyterで書きたいだけコードを書いて...
以下のサイトを参考にGistに.ipynbファイルをアップロードして
こちらにポッチっとコードを載せました.
GitHub Gistを使ってブログにJupyter(IPython) Notebookを表示する方法
さて,コードは正しく表示されているのでしょうか...?
今回書いたコードは以下の内容になります.
- 画像の読み込みと出力
- 画像の拡大縮小
- 画像の回転
- ガンマ補正
- 2値化(後日,大津の2値化のコードを揚げる予定です)
- ヒストグラム均等化(ライブラリのメソッド使っただけです...)
- 疑似カラー
3.の回転処理で画像の端っこが切れないために,
画像の対角線の長さを三平方の定理で求めて,
その長さの「土台」となる画像を新規で作り,その上に回転させたい画像を張り付けて
回転させれば端が切れずに回転することができました.
5.は後日,大津の2値化(otsu threshold)を実装して載せたいと思います.
7.の疑似カラーは正しく実装できているか怪しいところですが,
こんなもんでしょうか.
Jupyterで画像を表示させながら処理を行えるのは便利ですね!
次回は,大津の2値化のコードを書きたいと思います!
Pythonのリスト内包表記と三項演算子を使ったFizzBuzz
Pythonのリスト内包表記と三項演算子を使ったFizzBuzzを書いてみました.
日々のアイディアと学びを文章として残しておくために,ブログをはじめました.
まとめておいたことが,自分だけでなく,いつか誰かのためになったらいいですね.
せっかくなので,今回はfizz buzzのコードを載せます.
fizz buzzといえば,プログラミング言語の練習でよく使われるアレです.
3の倍数になるとアホになるのではなくて
1から100まで標準出力するプログラムを少し変えて,
3の倍数のときに'fizz',5の倍数のときに'buzz',15の倍数のときに'fizzbuzz'が
表示されるようにしたプログラムです.
コードを載せるために,Gistを使いました.
使い方はいたって簡単でした.Githubのアカウントを持っていたら,
ファイル名とコードを入力して'Create public gist'を押して終わり!
こちらのサイトを参考にさせていただきました.
blogにソースコードを載せる(Gist編) - 林檎と珈琲
ソースコードは以下になります.
ちゃんとコードが貼れてるかな??
gist41fab93b22ea544b3aa5746b4a04674c