かざんぶろぐ

だらだらと備忘録とってます

Unityでオブジェクトをスクリプトで動かし視界に映る画像をファイル保存

Unity上でエージェントをキー入力で動かし、エージェントの視界に映る映像をPNGファイルとして保存できるようにした。

環境

Mac Book Air : OS X EI Caption(version 10.11.6)
Unity : 2017.1.0f3

流れ

  • エージェントと、エージェントを動かす環境を作成
  • エージェントの視界に映った画像をPNGファイルとして保存するスクリプトの作成

シーンの環境構築

早速Unityを起動し、新しくプロジェクトを作成し、シーン上に環境を作っていく。
まずHierarchy上にて[Create Empty]で空オブジェクトを作成し、名前を「Agent」とする。
f:id:okuya-KAZAN:20170830174158p:plain
そして、Hierarchy上に最初からある「Main Camera」を「Agent」の子オブジェクトにし、名前を「Eye Camera」に変更。
f:id:okuya-KAZAN:20170830174214p:plain
続いて、エージェントが移動する地面を構築する。
シーン上にTerrainを配置し、名前を「Ground」に変更し、適当に隆起させたりテクスチャをつけた。
f:id:okuya-KAZAN:20170830174335p:plain
※地面のテクスチャはProjectビュー上のAssetsフォルダ内で右クリック->[Import Package]->[Environment]と進みインポートした。
f:id:okuya-KAZAN:20170830174355p:plain


Agentを動かす

キー入力を受けつけ、上向き矢印キーでエージェントが前に進み、左右の矢印キーで方向転換、スペースキーでエージェントがジャンプするようなスクリプトを作成する。
スクリプトを作成する前にまず、
「Character Controllerコンポーネント」をAgentオブジェクトに追加しておく。
f:id:okuya-KAZAN:20170830174454p:plain


Projectビュー上にて[Create]->[C# Script]で新規スクリプトを作成、名前をPlayerController.csとし、中身は以下のようにした。


PlayerController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {
  CharacterController controller;
  Vector3 moveDirection;

  public float gravity;
  public float speed;
  public float amountRotate;
  public float speedJump;

  void Start () {
    controller = GetComponent<CharacterController>();
    moveDirection = Vector3.zero;
  }

  void Update () {
    //地上にいる場合のみ操作を行う
    if(controller.isGrounded){
      //Inputの検知->0未満となってバックすることを避ける
      if(Input.GetAxis("Vertical") > 0.0f){
        moveDirection.z = Input.GetAxis("Vertical")*speed;
      }
      else{
        moveDirection.z = 0;
      }

      //方向転換
      transform.Rotate(0,Input.GetAxis("Horizontal")*amountRotate ,0);

      //ジャンプ
      if(Input.GetButton("Jump")){
        moveDirection.y = speedJump;
      }
    }
    //毎フレーム重力を加算
    moveDirection.y -= gravity * Time.deltaTime;

    //移動の実行
    Vector3 globalDirection = transform.TransformDirection(moveDirection);
    controller.Move(globalDirection * Time.deltaTime);

    //移動後地面についてるなら重力をなくす
    if(controller.isGrounded){
      moveDirection.y = 0;
    }
  }
}



スクリプトの詳しい解説は割愛する(疲れてしまったので...)
作成したPlayerController.csをAgentオブジェクトに追加し、Inspectorでpublic変数の値を設定。
f:id:okuya-KAZAN:20170830174552p:plain


プレビューを開始するとAgentを動かすことができた。
f:id:okuya-KAZAN:20170830174611p:plain


エージェントの視界に映った画像をPNGファイルとして保存

というわけでこの記事のメインに入っていく。
流れとしてはこんな感じ。
1.ProjectViewでRenderTextureを作成
2.EyeCameraにRenderTextureを設定
3.RenderTextureをTexture2D変換し、さらにPNG画像として保存するスクリプトを作成

RenderTextureを作成し、TargetTextureに設定

RenderTextureとは、カメラで撮影した映像をTextureとして使える機能である。
まず、Projectビュー上にて、[Create]->[Render Texture]でRenderTextureを作成、名前をMyRenderTextureに変更。
続いて、EyeCameraオブジェクトのInspector上にある[Target Texture]に作成したMyRenderTextureを設定。
これでEyeCameraで描画したものはMyRenderTextureに描画されるようになる。
f:id:okuya-KAZAN:20170830174750p:plain


f:id:okuya-KAZAN:20170830174859p:plain


視界を保存するためのスクリプトの作成

Projectビュー上にて[Create]->[C# Script]で新規スクリプトを作成し、名前をSaveImage.csとした。
実際のスクリプトは以下。


SaveImage.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//File.~を使うため
using System.IO;

public class SaveImage : MonoBehaviour {
  public Camera eyeCamera;
  private Texture2D texture;

  private int photoNumber = 1;


// Use this for initialization
void Start () {
    texture = new Texture2D (eyeCamera.targetTexture.width, eyeCamera.targetTexture.height,
                            TextureFormat.RGB24, false);
                          }
// Update is called once per frame
void Update () {
    //キーボードの「s」を押したら画像を保存
    if(Input.GetKeyDown("s")){
      SaveCameraImage();
    }
  }

  public void SaveCameraImage() {
    // Remember currently active render textureture
    RenderTexture currentRT = RenderTexture.active;
    // Set the supplied RenderTexture as the active one
    RenderTexture.active = eyeCamera.targetTexture;
    eyeCamera.Render();
    // Create a new Texture2D and read the RenderTexture texture into it
    texture.ReadPixels(new Rect(0, 0, eyeCamera.targetTexture.width, eyeCamera.targetTexture.height), 0, 0);
    //転送処理の適用
    texture.Apply();
    // Restorie previously active render texture to avoid errors
    RenderTexture.active = currentRT;
    //PNGに変換
    byte[] bytes =  texture.EncodeToPNG ();
    //保存
    File.WriteAllBytes("/Users/okuyamatakashi/Desktop/Unity/photo/Hoge" + photoNumber + ".png", bytes);
    photoNumber++;
  }
}



まず、視界が描画されたRenderTextureの転送先であるTexture2Dを作成している。

texture = new Texture2D (eyeCamera.targetTexture.width, eyeCamera.targetTexture.height,
                        TextureFormat.RGB24, false);


キーボードの「S」キーを押したら、画像が保存されるようにした。

void Update () {
  //キーボードの「s」を押したら画像を保存
  if(Input.GetKeyDown("s")){
    SaveCameraImage();
  }
}



RenderTextureからピクセル値を取得するには、RenderTexture.activeに取得元のRenderTextureを指定する必要があるらしい。
そして、RenderTexture.activeに指定された映像をキャプチャするReadPixels()で、ピクセルデータを転送。
しかし、このままではピクセル値をスクリプトから参照できないので、targetTexture.Apply()で転送処理を適用している。

// Set the supplied RenderTexture as the active one
RenderTexture.active = eyeCamera.targetTexture;
eyeCamera.Render();
// Create a new Texture2D and read the RenderTexture texture into it
texture.ReadPixels(new Rect(0, 0, eyeCamera.targetTexture.width, eyeCamera.targetTexture.height), 0, 0);
//転送処理の適用
texture.Apply();



Texture2DをPNGに変換し。
フォルダを指定し保存。

//PNGに変換
byte[] bytes =  texture.EncodeToPNG ();
//保存
File.WriteAllBytes("/Users/okuyamatakashi/Desktop/Unity/photo/Hoge" + photoNumber + ".png", bytes);
photoNumber++;

※File.○○を使用するにはスクリプト冒頭に

using System.IO;

と記述する必要がある。


作成したPlayerController.csをAgentオブジェクトに追加し、eyeCamera変数にEyeCameraオブジェクトを設定。
プレビューを開始し、「S」キーを押すと指定したフォルダにエージェントの視界に映る画像が保存されていることが確認できる。
f:id:okuya-KAZAN:20170830175303p:plain


f:id:okuya-KAZAN:20170830175310p:plain

Life in Silicoのサンプルを動かした

最近Unityをハマりだしているのだが、Unity上で作成した環境内でAIを学習させる方法はないかと思い検索してみると、
ドワンゴさんの人工知能研究所が開発した超人工生命Life in Sillico(以下LIS)という強化学習を体験できるアプリケーションを見つけた。
今回はそのLISをインストールし、サンプルを動かすまでしたので、その備忘録。

環境

OS : Windows10 Enterprise
Python : Python 2.7.12 |Anaconda custom (x86_64)|
Unity : 2017.1.0f3

LISについて

LISの原理を簡単にメモしておく。
まずUnity内に作成した環境内に存在するエージェントが見た視界をPythonプログラムに送信。
Python側では、
•視界をCNN(Convolutional Neural Network)を通して抽象化したもの
•障害物などの深度情報
この2つの情報を特徴量として、Q-Learningのネットワークに入力。
出力として行動を取得しUnity側に返信する。
そしてUnity側はPython側から受け取った行動を実際に行う。


f:id:okuya-KAZAN:20170816182311p:plain
図:LISの構造(サイト[1]より)

導入

実際にLISのサンプルを動かすまでをやってみる。

Unityのインストール

Unityのホームページから最新版をインストールしておく。

pipを使えるようにする

1番手っ取り早いのはAnacondaをインストールすることなんだろうけど、今回は詳しい説明は省略。

Gitをインストール

こちらのページからインストール

LISのダウンロード

コマンドプロンプトを開いて、
以下のコマンドを実行。

$ git clone https://github.com/wbap/lis.git

必要なPythonパッケージをインストール

lis/python-agent/requirements.txtを開くと、
LISを動かすのに必要なPythonのパッケージが書いてある。


f:id:okuya-KAZAN:20170816182345p:plain


これを全てpipでインストールしておく。
一気にインストールしたい場合は、

$ pip install -r python-agent/requirements.txt

このコマンドでOK。

必要なデータをダウンロード

コマンドプロンプトにて、

$ cd lis

でダウンロードしたlisディレクトリに移動し、

$ fetch.sh

でデータをダウンロード(結構時間かかる...)

Python側のサーバーを起動

$ cd python-agent

ディレクトリを移動し、

$ python server.py

でサーバーを起動。

Unity側の準備

Unityを起動し、「Open」を選び、lisディレクトリ内にある「unity-sample-environment」を開く。
この時、"元のunity-sample-environmentを作成時とUnityのバージョンが違うぞ"的なアラートが出てくるが、無視してContinue。
ProjectビューのScenesの中のsampleシーンを選択し、


f:id:okuya-KAZAN:20170816182405p:plain


プレビューをスタート。
数分待つと、サンプルが動き出した。

Pythonで作成した自作モジュールを様々な階層からimport

作成したPythonファイルをモジュールとして、他のPythonファイルから呼び出し(import)、1つのファイルを複数のファイルから再利用することができる。
今回はそのモジュールのimportに関する備忘録。

パッケージとモジュールと__init__.py

一応メモしておくと、モジュールが.pyファイルなのに対し、パッケージは複数のモジュールがまとまったディレクトリのことを指すらしい。
ここで注意したいのは、パッケージとなるディレクトリには__init__.pyというファイルを置かなくてはならないこと。
__init__.pyとは、モジュールをimportする時の初期化を行ってくれるファイルであり、このファイルが置いていないパッケージのモジュールをimportしようとしても、import errorとなってしまう。
あくまで、「このファイルにはモジュールが存在する」ということを表すだけなので、今回はこのファイルの中身は空である。

環境

Mac Book Air : OS X EI Caption(version 10.11.6)
Python 2.7.12 |Anaconda custom (x86_64)|

前準備

Parentsディレクトリ内に、
__init__.py , main.py , parent_main.py , parent_module.pyの4つのファイルと、
Childrenディレクトリを作成し、
Childrenディレクトリ内に、
__init__.py , child_main.py , child_module.pyの3つのファイルを作成した。

Parentsディレクトリ
      |
      ---__init__.py
      |
      ---main.py
      |
      ---parent_main.py
      |
      ---parent_module.py
      |
      ---Childrenディレクトリ
                  |
                  ---__init__.py
                  |
                  ---child_main.py
                  |
                  ---child_module.py



parent_module.py

def goal():
    print "I am parent."



child_module.py

def goal():
    print "I am child."

やったこと

  1. main.pyからparant_moduleをimport(同じ階層からの呼び出し)
  2. parent_main.pyからchild_moduleをimport(上の階層からの呼び出し)
  3. child_main.pyからparent_moduleをimport(下の階層からの呼び出し)

1.同じ階層からの呼び出し

Parentsディレクトリ
      |
      ---main.py
      |
      ---parent_module.py

同じディレクトリに存在するモジュールをimportする


main.py

import parent_module
parent_module.goal()



結果

$ python -B main.py
I am parent

※オプションで-Bをつけることで、モジュールの.pycファイルができなくなる。


同じ階層の場合は上記の通り、importしたいファイルのファイル名から.pyを抜いたものを記入すればOK。

2.上の階層からの呼び出し

Parentsディレクトリ
      |
      ---parent_main.py
      |
      ---Childrenディレクトリ
                  |
                  ---child_module.py

1つ下のディレクトリに存在するモジュールをimportする


parent_main.py

import Children.child_module
Children.child_module.goal()

または

from Children.child_module import goal
goal()



結果

$ python -B parent_main.py
I am child



fromを使う場合と使わない場合の違いについては、この記事では省略する。
注意すべき点としては、fromを使った場合は、importの後にaaa.bbbのように「.」を使うことはできない。
そして以下のようなコードはエラーが出る

import Children.child_module.goal
Children.child_module.goal()

3.下の階層からの呼び出し

Parentsディレクトリ
      |
      ---parent_module.py
      |
      ---Childrenディレクトリ
                  |
                  ---child_main.py

1つ上のディレクトリに存在するモジュールをimportする。
まずはエラーが出る例について示す。


child_main.py

from .. import parent_module
parent_module.goal()

このようにコードを書くと、以下のようなエラーメッセージが出ていてしまう。

ValueError: Attempted relative import in non-package



ここで、child_main.pyに以下のようなコードを書いて実行してみる。

import sys
print sys.path



結果

$ python Children/child_main.py
['/Users/okuyamatakashi/Parents/Children', 環境変数で設定しているpath達]

sys.pathは絶対パスの文字列リストであり、リスト内には、実行スクリプトが存在するディレクトリの、絶対パスが加わっている。
ポイントは、「sys.pathより上はパッケージの検索対象ではない」という点だ。
なので解決方法としては、
「sys.pathに上の階層にあるパッケージ(Parents)の絶対パスを追加して検索対象にする」といった感じ。

import sys
import os

print "os.getcwd() -> ",os.getcwd()

sys.path.append(os.getcwd())

import parent_module
parent_module.goal()



結果

$ python Children/child_main.py
os.getcwd() ->  /Users/okuyamatakashi/Parents
I am parent!!



os.getcwd()で実行パス(Parentsディレクトリ)を取得し、それをsys.pathに追加することでParentsディレクトリを検索対象にしている。
注意すべき点としては、今回の実行パスがParentsディレクトリであるということ。
これがもしChildrenディレクトリならまた違った方法でParentsディレクトリのパスをsys.pathに加える必要がある。


ここまで書いておいてあれなんだが、sys.pathにパスを追加して無理矢理別階層のモジュールをインポートしたりすることは、インポートされる側が把握しきれなくなったりのであまり良くない。
sys.pathは全てのモジュールが参照するため、他のモジュールに影響を及ぼす可能性があるらしい。
(例えば、あるモジュールを外すと今まで動いていたモジュールが、途端にimport Errorが発生する、挙動が変わってしまうなどの不具合)
解決法としては、
環境変数PYTHONPATHを設定する方法がある。
以下のように環境変数を設定しすると、sys.pathには常にPatentsへのパスが入る。

f:id:okuya-KAZAN:20170624013450p:plain

よってchild_mainの中身は、

import parent_module
parent_module.goal()

これだけで良い。

まとめ

色々なところで使いたいモジュールを置いているパッケージが固定で、そのモジュールを様々なところからimportしたいのであれば、PYTHONPATHを設定すると楽かも。

pipのコマンドのメモ

Pythonのパッケージ管理の際に使いまくるpipだが、コマンドをすぐ忘れてしまうのでここにメモしておく。

pipのバージョンを確認

pip -V



パッケージの検索

pip search <パッケージ名>



インストー

pip install <パッケージ名>


バージョンを指定してインストー

pip install <パッケージ名> == 1.2.3.
pip install <パッケージ名> >= 1.2.3
pip install <パッケージ名> < 1.2.3
||< 
<br>

アンインストール
>||
pip uninstall <パッケージ名>



再インストー

まずはパッケージをアンインストール。その後再インストールということをしたい場合、
前回インストール(ビルド)した時のキャッシュが使われてしまうため、それを無効にするため、--no-cache-dirオプションをつける

pip --no-cache-dir install -I pillow <パッケージ名>


パッケージのアップデート

pip install -U <パッケージ名>


インストールされているパーケージを表示(-oまたは--outdatedオプションをつけるとアップデート可能なパッケージを表示してくれる)

pip list



インストールされているパッケージの情報を表示

pip show <パッケージ名>


Pythonでフレームワークを使わずにニューラルネットワークを実装

Chainerや、TensorFlowといった、深層学習のフレームワークを使用せず、NumpyとMatplotlibのみでニューラルネットワークを実装した。

全てのコードなどが公開されたリポジトリはこちら

やったこと

3クラス分類を行うニューラルネットワークPythonで実装した。
(ついでに2クラス分類を行ったり、XORを解いたりしたのでリポジトリに公開しておく)

データの準備

先ほどドヤ顔で深層学習のフレームワークを使用しないと宣言した手前情けないのだが、データの作成に関しては、自分で1から作成せず、scikit-learnmake_classificationという機能を使って作成した。

import sklearn.datasets
x , label = sklearn.datasets.make_classification(
n_features=2, n_samples=300, n_redundant=0, 
n_informative=2,n_clusters_per_class=1, n_classes=3)

これでxに入力データが、labelに教師データが格納される。
make_classification関数の引数は(3)の方の記事に詳しく書いてある。

import matplotlib.pyplot as plt
plt.scatter(x[:, 0], x[:, 1],c=label,cmap=plt.cm.jet)
plt.show()

f:id:okuya-KAZAN:20170523164550p:plain
このような感じでデータが生成された。
xは2つの入力値(座標)を持つサンプルを300個含んだ配列であり、
labelは各サンプルのクラス(0,1,2)を含んだ配列である。

print x[:10]
array([[ 0.52358787, -1.15423424],
       [-0.52633513, -1.76794057],
       [ 0.55418671, -0.46817551],
       [-1.17471257, -0.44251872],
       [-1.34866206, -0.54916791],
       [ 0.67537883, -0.53149583],
       [ 1.61055345,  0.0481599 ],
       [-0.09616488, -1.51676462],
       [ 0.22409607,  1.40511859],
       [ 2.24986887,  1.18862095]])
print label[:10]
array([2, 0, 0, 0, 0, 2, 1, 0, 1, 1])

ニューラルネットワークの構造

以下のようにニューラルネットワークを設計した。


f:id:okuya-KAZAN:20170523173356p:plain


層は入力層、隠れ層、出力層の3つで、各層のユニット数は(2,3,3)とし、
隠れ層の活性化関数にはTanh関数、
出力層の活性化関数にはSoftmax関数を使用した。

class Function(object):
    def tanh(self, x):
        return np.tanh(x)

    def dtanh(self, x):
        return 1. - x * x

    def softmax(self, x):
        e = np.exp(x - np.max(x))  # オーバーフローを防ぐ
        if e.ndim == 1:
            return e / np.sum(e, axis=0)
        else:
            return e / np.array([np.sum(e, axis=1)]).T # サンプル数が1の時

実装

※これ以降定義する関数は全てNewral_Networkクラスの中のメソッドである。

コンストラクタ

コンストラクタとは、新たにオブジェクトが生成される際に呼び出されて内容の初期化などを行なうメソッドのことであり、メッソド名は__init__とする。
ここでニューラルネットワークの構築を行う。

class Newral_Network(object):
    def __init__(self, unit):
        print "Number of layer = ",len(unit)
        print unit
        print "-------------------------"
        self.F = Function()
        self.unit = unit
        self.W = []
        self.B = []
        self.dW = []
        for i in range(len(self.unit) - 1):
            w = np.random.rand(self.unit[i], self.unit[i + 1])
            self.W.append(w)
            dw = np.random.rand(self.unit[i], self.unit[i + 1])
            self.dW.append(dw)
            b = np.random.rand(self.unit[i + 1])
            self.B.append(b)

unitは各層のユニット数が格納された配列である(今回の場合、unit=[2,3,3])。
unitの情報をもとに、学習するパラメータ(W, B)をランダムに初期化している。
dwは重みの修正量を保持する配列であり、モメンタムでの計算に使う。

順伝搬

構築したネットワークに入力値を与え、出力を得るforwardメソッドを定義する。
配列Zを用意し、そこに各層の出力値を加えていく。

# 順伝搬
def forward(self, _inputs):
    self.Z = []
    self.Z.append(_inputs)
    for i in range(len(self.unit) - 1):
        u = self.U(self.Z[i], self.W[i], self.B[i])
        if(i != len(self.unit) - 2):
            z = np.tanh(u)
        else:
            z = self.F.softmax(u)
        self.Z.append(z)
    return np.argmax(z, axis=1)

# ユニットへの総入力を返す関数
def U(self, x, w, b):
        return np.dot(x, w) + b

forwardメソッドの返り値だが、
出力層の各ユニットの出力値の中で最も高い値を出力したユニットの番号、つまり入力値の属するクラスの予測値である。
f:id:okuya-KAZAN:20170523184335p:plain

誤差の算出

ニューラルネットワークの出力値と目標出力値(label)から誤差を算出する。
誤差関数には交差エントロピーを用いている。
※出力値はforwardメソッド内で算出された配列Zの中の1番末尾にある配列であり、forwardメソッドの返り値ではない。

# 誤差の計算
def calc_loss(self, label):
    error = np.sum(label * np.log(self.Z[-1]), axis=1)
    return -np.mean(error)

誤差逆伝搬

今回のプログラムの肝となる誤差逆伝搬法によるパラメータの学習を行う部分。

# 誤差逆伝搬
def backPropagate(self, _label, eta, M):
    # calculate output_delta and error terms
    W_grad = []
    B_grad = []
    for i in range(len(self.W)):
        w_grad = np.zeros_like(self.W[i])
        W_grad.append(w_grad)
        b_grad = np.zeros_like(self.W[i])
        B_grad.append(b_grad)

    output = True

    delta = np.zeros_like(self.Z[-1])
    for i in range(len(self.W)):
        delta = self.calc_delta(
            delta, self.W[-(i)], self.Z[-(i + 1)], _label, output)
        W_grad[-(i + 1)], B_grad[-(i + 1)] = self.calc_grad(self.W[-(i + 1)], self.B[-(i + 1)], self.Z[-(i + 2)], delta)

        output = False

    # パラメータのチューニング
    for i in range(len(self.W)):
        self.W[i] = self.W[i] - eta * W_grad[i] + M * self.dW[i]
        self.B[i] = self.B[i] - eta * B_grad[i]
        # モメンタムの計算
        self.dW[i] = -eta * W_grad[i] + M * self.dW[i]

# デルタの計算
def calc_delta(self, delta_dash, w, z, label, output):
    # delta_dash : 1つ先の層のデルタ
    # w : pre_deltaとdeltaを繋ぐネットの重み
    # z : wへ向かう出力
    if(output):
        delta = z - label
    else:
        delta = np.dot(delta_dash, w.T) * self.F.dtanh(z)
    return delta

デルタの計算方法は出力層とそれ以外の層では異なるため、bool変数であるoutputを定義し、その値によってデルタの算出方法を変えている。
変数Mはモメンタムでの加算の割合を制御するハイパーパラメータ。

学習

これまでに定義したメソッドを駆使し、ニューラルネットワークの出力が目標出力(label)に近づいていくようパラメータをチューニングしていく。

def train(self, dataset, N, iterations=1000, minibatch=4, eta=0.5, M=0.1):
    print "-----Train-----"
    # 入力データ
    inputs = dataset[:, :self.unit[0]]
    # 訓練データ
    label = dataset[:, self.unit[0]:]

    errors = []

    for val in range(iterations):
        minibatch_errors = []
        for index in range(0, N, minibatch):
            _inputs = inputs[index: index + minibatch]
            _label = label[index: index + minibatch]
            self.forward(_inputs)
            self.backPropagate(_label, eta, M)

            loss = self.calc_loss(_label)
            minibatch_errors.append(loss)
        En = sum(minibatch_errors) / len(minibatch_errors)
        print "epoch", val + 1, " : Loss = ", En
        errors.append(En)
    print "\n"
    errors = np.asarray(errors)
    plt.plot(errors)

うーん...なんかコードが汚い...


実際に3000回学習させた時の誤差の変動を示したグラフがこち
f:id:okuya-KAZAN:20170523190031p:plain

結果

学習させたパラメータを引数に取るdraw_testメソッドを定義

def draw_test(self, x, label, W, B):
    self.W = W
    self.B = B
    x1_max = max(x[:, 0]) + 0.5
    x2_max = max(x[:, 1]) + 0.5
    x1_min = min(x[:, 0]) - 0.5
    x2_min = min(x[:, 1]) - 0.5
    xx, yy = np.meshgrid(np.arange(x1_min, x1_max, 0.01),
                         np.arange(x2_min, x2_max, 0.01))
    Z = self.forward(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    plt.contourf(xx, yy, Z, cmap=plt.cm.jet)
    plt.scatter(x[:, 0], x[:, 1], c=label, cmap=plt.cm.jet)
    plt.show()

f:id:okuya-KAZAN:20170523164550p:plain
f:id:okuya-KAZAN:20170523190637p:plain
認識できてる...!!

2クラス分類

scikit-learnmake_moonsという機能を使って2クラスのデータも作成し、
クラスの分類を行うニューラルネットワークを構築し学習させた。
層は入力層、隠れ層、出力層の3つで、各層のユニット数は(2,3,2)とした。
f:id:okuya-KAZAN:20170523191231p:plain
f:id:okuya-KAZAN:20170523191243p:plain


ちなみに、隠れ層のユニット数を20にした場合
f:id:okuya-KAZAN:20170523191511p:plain
多少オーバーフィットを起こしている恐れがある。

Gitのコマンドのメモ

ずいぶん更新が遅れてしまった...
GitをCUIで操作する際のコマンドの備忘録をとっておく。

設定系

名前の設定

git config --global user.name <名前>


メールアドレスの設定

git config --global user.email <メアド>


設定の確認

git config -l 




Gitを使うという宣言

git init 




コマンドの省略名(エイリアスの指定)

git config --global alias.<省略名> <コマンド> 


共同リポジトリでのGit宣言

git init --bare 


設定情報一覧

git config -l 



※ .ignoreファイルに書き込まれたファイルはGitにあがらない


Upload系

ワークディレクトリからステージエリアへ

git add  <ファイル名>




カレントディレクトリのファイル全てをadd

git add . 




ステージエリアからリポリジへ

git commit <ファイル名> 




vimを立ち上げずにコミットメッセージを書ける

git commit -m <コミットメッセージ>


確認

差分があるか確認

git status 




差分を確認(赤字)

git diff 


差分を確認(緑字)

git diff --cached 




リポリジのlogを見る

git log 


簡潔にリポリジのlogを見る

git log --oneline 


詳しくリポリジのlogを見る

git log -p 


コミットによってどのファイルが何箇所変わったか確認

git log --stat 


commitの内容を知ることができる

git show <commitID> 


管理から削除

※間違えてコミットしてしまった時などに使う


管理対象から外す(ファイル自体はディレクトリに残る)

git rm —-cached <ファイル名> 




ファイルをディレクトリからも削除

git rm <ファイル名> 


ディレクトリを削除

git rm -r <ディレクトリ名> 




gitの管理自体をやめる。

rm -rf .git/

これをすると、git initで作成された.git/ がなくなりgit管理されていない単なるディレクトリになる。


修正

直前のコミットの変更

git commit --amend -m<修正したコミットメッセージ>


IDのバージョンに戻る( logの1番上のcommitのIDはHEAD上から2番目はHEAD^)

git reset --hard <commitID> 



ブランチ

現在いるブランチを確認

git branch 


新しいブランチを作成

git branch <ブランチ名> 




ブランチを変える

git checkout <ブランチ名> 




ブランチを作成した上で作成したブランチに変える

git branch -d <ブランチ名> 


ブランチを混ぜる

git marge <ブランチ名> 


タグ

commitIDに名前をつける(commitIDを書かなかった場合HEADのcommitにタグが付く)

git tag <tagname> <commitID> 


使用してるtagの確認

git tag 


タグの削除

git tag -d <tagname> 


Remote

※共同リポジトリのフォルダは.gitをつける

共同リポジトリへの道を作成(アクセス名の通例はorigin)

git remote add <アクセス名> <共同リポジトリのパス> 


共同リポジトリにpush

git push origin <ブランチ名>


Aの内容をBにコピー

git clone <フォルダA> <フォルダB> 


共同リポジトリのブランチをいただく

git pull origin <ブランチ名>


Atomの設定をMacからWindowsへ共有

macOSで構築したAtomの環境設定をWindowsOSでも使えるようにしたのでその備忘録。

MacBook Air(バージョン10.11.6)で作成した環境設定を、
Windows10 Exterpriseに引き継ぐ。

なお、GItHubアカウントは作成している前提で話を進める。

やったこと

共有の方法としては、シンボリックリンクや「apt star」コマンドを用いる方法などもあるらしいが、
今回は、Atomプラグインである、「sync-settings」をインストールした。
このプラグインは、Atomの設定をGitHub経由で他のコンピュータと同期させることができるらしい。

環境構築

sync-settingsインストールからWindowsOSへの同期までの流れを書いていく。

インストール

Atomの設定画面を開き、「Install」からsync-settingsをインストール。

f:id:okuya-KAZAN:20170428174543p:plain


Gist IDとGitHub personal access tokenを設定しろと怒られたので、早速設定する。

f:id:okuya-KAZAN:20170428174843p:plain

GitHubの設定

GitHubにログイン -> 「settings」 -> 「Personal access tokens」 -> 「Generate new token」
という感じで進む。
この、Personal access tokensというのは、Atom側がGistへアクセスする権限のようなものらしい。

f:id:okuya-KAZAN:20170428180741p:plain
f:id:okuya-KAZAN:20170428180750p:plain
f:id:okuya-KAZAN:20170428184144p:plain

「Token description」に、適当な文章を入れ、
「Select scopes」は「gist」のみチェックを入れ、
「Generate token」を選択。
作成されると、英語で「パーソナルアクセストークンをすぐにコピーしろ!これは2度と表示されないぞ!」的な警告が出てきたので、急いでトークンをコピー。
そして、先ほどインストールしたsync-settingsの設定画面の「Personal Access Token」にペーストする。
f:id:okuya-KAZAN:20170428184552p:plain

続いて、GistIDの設定。
GitHubにログイン -> 「Gist」
と進む。
Gist descriptionには何も入力せず、
「Name this file」とテキストには適当な文字を入れ、Create secret Gistボタンを押す。

f:id:okuya-KAZAN:20170428185109p:plain


遷移した画面のURLからgistのID("ユーザー名/"の後に続く英数字部分)をコピーし、
Atomのsync-settings設定画面のGist idにペースト。
f:id:okuya-KAZAN:20170428185200p:plain

同期

共有される側の準備

共有先のOSのAtomでもsync-settingsをインストールし、settingsを開き、先ほど手に入れた、パーソナルアクセストークンとGistIDをペーストする。

設定のアップロード
  • [Ctrl]+[Alt]+[u]
  • コマンドパレットにて「Sync Settings: Backup」を実行
  • 上のメニュバーから、[Packages] -> [Synchronize Settings] -> [Backup]

上記のいずれかをすると、設定をアップロードすることができる。
アップロード完了後、
このようなメッセージが出てきた。

f:id:okuya-KAZAN:20170428191023p:plain


Gistを開き、アップロードされた設定を確認
f:id:okuya-KAZAN:20170428191201p:plain

設定のダウンロード

Gistにバックアップしてる環境設定を、別環境に共有する。
※ダウンロード中はAtomが固まるので他のプロセスが行われていない時にダウンロードすると良いとのこと

  • [Ctrl]+[Alt]+[d]
  • コマンドパレットにて「Sync Settings: Restore」を実行
  • 上のメニュバーから、[Packages] -> [Synchronize Settings] -> [Restore]

上記のいずれかをすると、設定をダウンロードすることができる。

しばらくするとAtom上に通知が来た。
f:id:okuya-KAZAN:20170428191656p:plain

通知が来てからAtomを再起動すると、設定やパッケージが同期されている!素晴らしい!!