室村日記

日々、試行錯誤したことを備忘録的にまとめていきます

python3での標準入出力

pythonでの標準入出力を求められる機会って稀によくありますよね.
pythonって2だったり3だったり紛らわしいし,調べてみてもコピペで動いてくれるスクリプトが載っている記事を見つけられなかったので,自分の検索能力のなさを嘆きつつ苦しんだ結果をメモ書き程度に残しておきます.
(環境はpython3.6.0を想定しています)

1 1行に1つの入力を受け取る

28

とか

test

とかを受け取りたい時です.
たぶんどの言語でもそうなんでしょうけど,pythonでも入力関数を一回呼び出すごとに1行分の入力を読み込んでくれるようです.
というわけで,

a = input()

とすればaに入力を代入できます.

ただしこれだとintだろうとfloatだろうとstrだろうと何でもstr型で読み込んでしまうので,型変換までついでにやっちゃうなら

a = int(input())

のように書けます.

2 1行に複数の入力を受け取る

a b c d e

1 2 3 4 5

とかを受け取りたい時です.

文字列で受け取るなら

a = input().split()

とすると,半角スペースのところで区切ってリストにして受け取ってくれます.

型を指定して読み込むときには,変数が1つのときの場合のようには書けず,map関数を使う必要があります.
intで受け取りたいなら

a = list(map(int, input().split()))

となります.
注意点としては,python3ではmap()を使って読み込むとmap objectというものになってしまうので,list()で変換する必要があります.

また,受け取りたい入力が数個程度で,その数がわかっているならこんな風にもできました
たとえば

3 4

に対して

a,b = list(map(int, input().split()))

とすればaに3が,bに4がint型で代入されます.


3 複数行に複数の入力を受け取る

3
1 2 3
4 5 6
7 8 9

みたいなのを受け取りたい時です.

input()を一回呼び出すごとに1行分の入力を受け取ってくれるので,for文などで行数分呼び出すか,whileなどを使えば良いようです.
というわけで,

N = int(input())
H = []
for i in range(N):
    H.append(list(map(int, input().split())))

最初に空のリストを用意して,appendでどんどん突っ込んでいくという方針です.
numpyを使うならここでついでにnp.arrayにしておいても良いかもしれません.

プログラミング初心者がUnityチュートリアルで苦しんだポイント

1 はじめに

プログラミング初心者がUnityのチュートリアルに挑戦してつまづいた部分をメモしていきます.
(逐次更新する予定です)

「初心者ゆうてもピンキリやがな」という話ですが,私がどのくらい初心者かというと,for文やif文くらいは自信を持ってわかると言えるレベルです.
クラス?宣言?オブジェクト指向
なんとなく説明できないこともないこともないこともないよ!ってなものです.

さて.
現在のUnityのバージョン(5.6.1)に対してチュートリアルで使われているバージョンが若干古く,UIなどの仕様変更が多々あっていちいちつまづきました.
プログラミングを勉強し始めてから,
あっこれ死にゲーってやつだ
と割と早く悟ることができたのですが,盲点なのは自分の記憶力が恐ろしく悪かったことです.
いつまでも序盤の罠でしに続けているわけにはいかないので,主に自分の為にそれらをメモしていこうと思います.

実際に作ったものや感想についてはまた別の記事で.


2 つまづいた部分

2.1 Assetについて

Asset Tabを開き,導入したいassetを検索してインポートします.
今回はspace shooterで検索するとチュートリアル用の一式を手に入れることができます.
Asset storeのタブは,ショートカット⌘9か,Window > Asset Storeで開くことができます.


2.2 Unity Web PlayerとWebGL

チュートリアルの動画ではWeb Playerが紹介されていますが,現在のUnityでは非推奨としており,どうもそのうち使えなくなるようです.
そこで代わりにWebGLを使用することにしました.
作業環境や対象とする環境は,⌘+Shift+Bでビルトの設定画面を開き,そこで設定することができます.


2.3 Gameウィンドウの設定

ビルトの設定画面で左下のplayer settingsをクリックすると,Inspectorタブで設定ができるようになります.
ここではResolution and Presantationのところでサイズを変更し,そのあとGameタブの左上のFree Aspectとなっているところをクリックして+を選択し,新しく600×900に設定し直しました.


2.4 スクリプトエディターについて

Unityをインストールすると勝手についてくるMonoDevelopを使えばとりあえずスクリプトは書けます.
サジェスト機能など,使い心地などがちょっとアレな場合はVisual Studioなどを使うこともできます.
www.visualstudio.com


2.5 RIgidbodyの取得

Unity公式のチュートリアル動画では

rigidbody.velocity = movement

のように,rigidbodyのvelocityメソッドに直接movementの情報を与えられると書いていますが,これはUnity4のための書き方であって,現在のUnity5ではエラーとなってしまいます.
Unity5では,まずvoid FixedUpdate()の前にvoid Start()を宣言し,その中でGetComponent()を使ってrigidbodyを取得してやる必要があるようです.

つまりスクリプト

void Start()
{
	rb = GetComponent<Rigidbody> ();
}

void FixedUpdate()
{
	float moveHorizontal = Input.GetAxis ("Horizontal");
	float moveVertical = Input.GetAxis ("Vertical");

	Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
	rb.velocity = movement;
}

と書き換えれば大丈夫です.


2.6 Mathf

チュートリアルの中で,公式にリファレンスがあるので逐一チェックすると良いよと言われました.
試しにMathfで検索してみたら公式の(しかも日本語の!)リファレンスがヒットしました.
docs.unity3d.com
これによるとMathfはUnityEngineの中に入っている,標準的な数学関数を扱うためのモジュールのようです.


2.7 public float speedなど

public class PlayerController : MonoBehaviour
{
	public float speed;
	public float xMin, xMax, zMin, zMax;
}

のようにクラスの先頭で[public+型+変数名]で宣言した変数は,UnityのInspectorで後から与えることができます.
こんな感じに新しい入力窓が勝手にできます.
f:id:muromura:20170612194751p:plain


また詳しいことはわかりませんが,class宣言時に[System.Serializable]を使うと,以下のように折り畳んだ形で変数を与えられるようです.
f:id:muromura:20170612195538p:plain





3 まとめ

そもそもCとC++C#の違いすらわかっていない人間なので,コードの意味もなんとなくしか追えず,ひたすら辛い時間が続いています.
しかしその辛さを乗り越えて余りあるのが,ゲームを作る楽しさです.

とりあえずチュートリアルの範疇では面倒なキャラクターの3Dモデリングやテクスチャー作成をする必要がないのも,素晴らしい点です.
システムとしてはそんな複雑なことをやっていなくても,とりあえずモデリングとテクスチャーがしっかりしていたら見れるものになりますもんね.

それでは.




4 この記事を書くにあたって参考にさせていただいた記事

tech-camp.in
idol-ch.net
docs.unity3d.com
brian.hatenablog.jp
curious-boys.hatenablog.com

大数の法則

1 はじめに

中心極限定理について直感ではいまいち納得できなかったのでちょっとまとめようとおもったのですが,その前にまず大数の法則についてまとめてみます.

wikipedia先生に中心極限定理について聞いてみます.

大数の法則によると、ある母集団から無作為抽出された標本平均はサンプルのサイズを大きくすると真の平均に近づく。これに対し中心極限定理は標本平均と真の平均との誤差を論ずるものである。多くの場合、母集団の分布がどんな分布であっても、その誤差はサンプルのサイズを大きくしたとき近似的に正規分布に従う。

wikipedia:中心極限定理
さて,中心極限定理について聞いたのに大数の法則というのが出てきました.

2 大数の法則とは?

大数の法則が言っているのはあたりまえのことでした.
世の中のたいていの事象は無限に存在するので,その全てを調べ尽くすことは不可能に等しい行為です.
例えば卵にはたまに黄味が2つ入っているのがあります.
しかし黄味が2つ入っている確率を求めたいとして,そのためにこの世の全ての卵を調べようなんてのは無理な相談です.(こうしている間にも鶏は卵を産み続けます)
そこでほとんどの人が思いつく方法が,だいたい1,000個とか頑張って10,000個とかの卵を調べて,その中の黄味が2つある卵を数え,それでとりあえず確率を出して満足しておこうというものでしょう.
これがまさに大数の法則の考え方となります.
自分が調べることのできる量(=1000個)なんて全体の量(=世界中の卵の数)からみたら本当に微々たるものにすぎませんが,それでも結構信頼の高い確率を得ることができるということです.
これを指して,大数の法則は「経験的確率と理論的確率が一致する」ことを示すものだと言ったりもします.
また,ここで信頼性という面で大事なのは自分が”何個”調べたかであって,”何%”調べたかではないというところに注意が必要です.

大数の法則が現実に使われている例としては,選挙速報やTVの視聴率調査があります.
これらはほとんど現実の値から外れることはありませんが,現実にすべての投票者.すべてのTVのある家庭に対して調査を行っているわけではありません.
一部の家庭や有権者を調べるだけで,全体の動向もだいたいわかってしまうものなんですねえ.

Pythonで麻雀の戦績を集計してみた

1 初めに

前回の投稿に引き続き,記録をとっていた最近の戦績を集計してみました.
基本的にはpythonのpandasしか使ってないのですが,集計してみたり,for文回してみたり,DataFrame 結合してみたり,ブロードキャストについて思い出したりと結構よい勉強になりました.
それではいよいよ実際に行った作業に移ります.


2 やったこと

こんな感じのデータを読み込みます.
f:id:muromura:20170510223606p:plain
列が各対局者,行が半荘ごとの成績(得点)となっています.
対局に参加していない人の欄は空白になっており,pythonではNaNとして読み込まれます.

3行目から6行目はエクセルで順位つけようとして途中で面倒になってやめた名残です.
不要行削除の例示に都合良いのでそのままにしておきます.



1行を削除するときには,配列のスライスと同じように指定すれば良いです.

#不要部分削除
hoge[:5]

列を削除するときにはdrop()を使います.

#不要部分削除
data = data.drop("削除したい列名", axis=1)

対局数をカウントするのには,NaNの数を利用することにしました.
各列ごとにis.null()でNaNの数を調べるとTrue/Falseで結果が帰ってきます.
pythonにおいてはTrue/Falseは数値計算すると1/0に等しくなるので,結果を1から引いてやることで各列で対局に参加した部分(=得点が入力されている部分)を1,それ以外を0とすることができました.
あとはそれを合計するだけです.

#対局数
(1-data.isnull()).sum()

順位の集計にはちょっと手間取りました.
rank()を使えば良いのですが,このとき行ごとに順位を出すには,引数でaxis=1を指定します.
ちなみにこのときNaNはNaNのまま無視してくれました.便利!

rank()を使うと数が少ない順に1から数字が振られるので,本来の順位とは逆になってしまいます.
それをひっくり返すためには5から順位を引けば良いことになります.
これはpythonのブロードキャストを使って簡単に書けました.

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

次に,1位から4位までそれぞれの順位を何回とったか数えます.
たぶん他にもっと良い方法があると思いますが,思いつかなかったので,各列ごとに抜き出してvalue_counts()で集計し,後でそれを全部くっつけるという方法をとることにしました,
最初に空のDataFrameを用意し,colmuns()でとってきた列名でfor文を回して集計結果をくっつけています.
DataFrameの結合にはconcat()を使い,列方向にくっつけたいときにはaxis=1を指定します.

#各順位を何回とったかの集計
#列名を取得
players = rank.columns
#空のデータフレームを用意
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
#for文で回してranksに突っ込んでいく.
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

3 サンプルコード

というわけで長くなってしまいましたが,サンプルコードは以下.

import numpy as np
import pandas as pd

#データ読み込み
raw_data = pd.read_csv("sample.csv", encoding="sjis")

#不要な部分を切り捨てる
data = pd.DataFrame(raw_data[5:])
data = data.drop("day", axis=1)
#型をチェック
#data.dtypes

#順位づけ
rank = data.rank(axis=1)
rank= 5 - rank

#対局数
(1-data.isnull()).sum()

#平均順位算
rank.mean()

#合計計算
data.sum()

#平均得点計算
data.mean()

#順位合計
players = rank.columns
players
ranks = pd.DataFrame(index=[1,2,3,4], columns=[])
for player in players:
    r = rank[player].value_counts()
    ranks = pd.concat([ranks, r], axis=1)

#集計
result = pd.concat([(1-data.isnull()).sum(),
                    ranks.T,
                    rank.mean(),
                    data.sum(), 
                    data.mean()],
                   axis=1, join='inner').T

result.index=['対局数',"1位","2位","3位","4位",'平均順位','合計得点','平均得点']
result = result.round(2)

結果はこんな感じになります.
f:id:muromura:20170510224213p:plain


また時間のあるときに,次は各日ごとの集計をしたり,player同士の相性を計算したりなどやっていきたいと思います.
適当ですが,得点の相関係数とかでも相性の指標になりうるかもしれませんね.

それにしてもplayer1勝ちすぎです.
それでは.

pythonでcsvファイルを読み込む

1 はじめに

いつか集計しようと思って,趣味の麻雀の記録をexcelでとっていたのですが,この度ついに実行に移すことになりました.
とりあえず最初はRでごちゃごちゃとやっていたのですが,せっかくなら勉強中のpythonでやったほうがいいのではないかと思いたち,まずはデータを読み込むところから始めようと思ったわけです.

2 やったこと

exelで集計していたデータをpythonで読み込みます.
使用環境は3系(python3.6.0)でJupyter Notebookを利用しています.

というわけでpandasのread_csv()を利用します.
名前的にもcsvしか読み込んでくれなさそなので,エクセルで作成したファイルを[cmd+shift+s]でファイル形式をcsvに変えて別名保存します.
早速以下のスクリプトを動かすと

pd.read_csv("../Data/mahjong_log.csv")

怒られました.

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8c in position 1: invalid start byte

初めから蹴つまずきますね.
google恩師に尋ねながら,read_table()を試してみたりしたけど同じエラーを吐きよります.

Rのときと同じように,エディタにコピペしてencoding変えて保存したらうまくいくんでしょうけど,この先データを更新するたびにその作業をするのも面倒ですよね.
そもそもエクセルをcsvに保存し直すのすらめんどいのに.
と考えたところでRのように読み込み時にencoding="sjis"で指定してやったらうまくいくんじゃないかと思ったら一発で読み込んでくれました.

3 サンプルコード

というわけでサンプルコードは以下.

import pandas as pd
pd.read_csv("hoge.csv", encoding="sjis")

次はとりあえず点数を順位に変換して平均順位などを出したいと思います.

それでは.

ファイルを一括で検索して移動

1.はじめに

 ダウンロードしてきたフリーペーパーがPDFなのはいいんだけど,各章ごとに別フォルダに分割されちゃっていました.
f:id:muromura:20170426013039p:plain
 この中に1つずつバラバラに各章のPDFが入っている感じです.

 印刷したり読んだりするのに,いちいちフォルダを開いたり閉じたりして一箇所にまとめなくちゃいけないのが面倒だなーと思っていたところ,むかしbashでそういうファイル管理を触ったことがあるのを思い出したのでメモ.

*ちなみにMacならディレクトリだろ?とかbashLinux?とかよくわかんないのでスルー


2.相対パス

 相対パスの書き方って言語ごとに微妙に違うようですね.困りますね.
 とりあえず,bash書くときは ../ で親ディレクトリが取れます.
 さらに遡りたいときは ../../ のように続けていけば大丈夫そうです.


3.mv

mv [現在のpath] [移動したいpath]

ディレクトリやファイルが移動できます.このときに例えば

mv [現在のpath] ~/hoge/piyo.pdf

とすれば,hogeというディレクトリの下のpiyo.pdfと名前を変更しつつ保存できるし,

mv [現在のpath] ~/hoge/

とすれば,hogeというディレクトリの下に名前はそのままで保存してくれます.

基本的にbashのmv(移動)やcp(コピー)は,移動先に指定された名前のディレクトリが存在すればそこに,なければ勝手に作ってそこに移動してくれるようです.


4.正規表現

~/hoge/*

のように * を使うと,hogeディレクトリにある全てのファイル,ディレクトリのことを指してくれます.これはちなみに,

~/hoge/*.pdf

のように部分指定をかけることも可能です.

5.スクリプトbashとして動かす

 vimでもsublimeでもatomでもいいのですが,sample.shのように.shという拡張子のファイルを作ります.拡張子もべつに何でもいいのですが,シェルスクリプトであるということを表すために.shを用いるのが普通です.bashというのはシェルスクリプトの中の一つです.

 中身は以下のようにします.

#!/bin/sh

[ここに命令を書く]

 スクリプトが完成したら,ターミナルに

bash [.shファイルのパス]

 と打ち込むと実行できます.
 例えばカレントディレクトリにsample.shがあれば以下です.

bash sample.sh

6.サンプルスクリプト

ということで,以上全ての知識を生かして今回使ったスクリプトが下です.

#!/bin/sh

pdfs="../paper/*/*"
for pdf in $pdfs; do
    echo "Moving: " $pdf
    mv $pdf ../pdf/
done

ここでは,paperというディレクトリの下に
f:id:muromura:20170426013039p:plain
このようにディレクトリが並んでいて,その中にさらにpdfファイルが入っている状態から,paperのとなりのpdfというディレクトリに,一括でpdfファイルを移動する操作を行いました.

ちなみにbashでは,変数は頭に$をつけないと呼び出せません.
echoは標準出力してくれる関数です.他の言語のprintのようなものですかね.


5.まとめ

何を簡単なことをだらだら書いてんねんという話ですが,こういう簡単なことも,やらないと半年ぐらいですぐに忘れるのでメモしておきました.
初心者はありとあらゆるところにつまづきます.

それでは.

ニューラルネットワークにおける勾配法と学習率

1.はじめに

 初めに注意.
 この文章は「ゼロからつくるDeep Learning」の第5章「誤差逆伝搬法」までを学習した者により,備忘録の意を込めて書かれています.しかもそいつはプログラミングすらほとんど真面目に勉強したことがありません.よって,抜け漏れや勘違いは非常に多いと思われます.

 さて.1週間で終わらせると息巻いていたものの,予定は未定とはよく言ったもので,結局1週間経ったというのにまだ終わる見込みがありません.
 とりあえず現在初めて,ニューラルネットによる文字認識の学習を回しているのですが,それがあまりにも遅いので,その間にいくつかまとめておこうと思います.例のごとく間違った認識かもしれませんが,あくまで自分用ということで……
 
 今回は勾配法と学習率について簡単にまとめようと思います.
 文字(手書き数字)認識というのは,要はコンピュータに人間が書いた手書きの,つまり時として汚かったり癖があったりする数字を認識してもらい,果たしてその文字が1~9のどれなのかを判別してもらうというものです.
f:id:muromura:20170424230236j:plain
 そのときにコンピュータの内部では様々なパラメータが動いていて,例えば
「これは丸があって上に棒が飛び出ているから”6”かな」
「これは丸が二つあるから8かな」
「これは二回折れ曲がっている棒だから7かな」
のような判断をしているようです.
例えばこの「二回折れ曲がっている」などを判別しするために大量のパラメータが用意されています.
このパラメータをいい感じに調整して,1は1,7は7として認識してくれるようにするのが,学習ということになります.
 今回はその学習の方法として勾配法を用いるようです.
 
*正確には丸とか棒ではなくもっと抽象的に捉えているのだと思いますが,わかりやすい例としてあげました.さらに正確に言えば,今回の手法では文字の画像を個別のピクセルレベルに分解して捉えています.



2.勾配法

 学習のためには,どちらに向かって進めばいいのか,つまり道しるべが必要になってきます.
 その道しるべとして,訓練用データと正解データの損失関数(ここでは交差エントロピー誤差)を用いるのが勾配法です.

 なぜ,正解/不正解の数という認識精度をそのまま道しるべとして用いてはいけないのかという疑問が生じます.こちらの方が筋道が通っているようにみえます.
 
 しかし,これだと学習がうまくいかないのです.なぜなら認識精度は非連続だからです.
 つまり,100枚中90枚正解したとするとその値は90%となります.90%の次は91%です.この場合には90.7%正解しました,などということはありません.

 その場合,どのような問題が起きるか?
 例えば,少し精度がよくなる方にパラメータが調整できたとします.しかしそれが,確かに向上はしているのですが,1枚分正解を増やすほどの向上ではなかったとします.そのとき,認識精度を目的とした学習だと,今回の調整はうまくいかなかったとして破棄してしまうのです.
 つまり,実際には少しずつでももっと精度を上げていくことができるのに,精度を評価する道しるべが荒いために,そこで立ち止まるしかなくなってしまうのです.

 ここで登場するのが交差エントロピー誤差などの損失関数です.これらの損失関数は連続です.
 連続ということは,パラメータ調整によるわずかな向上も勘定に入れることができるということです.
 さっき0.98424...だった値が0.98512...になっていれば,それは今回の調整がうまくいったということを表しているので,それを元に次なる調整に進むことができます.

*交差エントロピー誤差とは結局のところどうやって生まれた何なのかとか,なぜそれを用いるのかとかについては詳しくわかっていないので,別の機会に調べます.ごめんなさい.


3.学習率

 勾配法では,勾配,すなわち微分の情報を用いて進むべき方向を決定します.
 この場合,損失関数を最小化するのが目的なので,道しるべの例になぞらえれば,高低差のある地形を探索してとにかく窪地=一番低い場所を探して進んでいくのです.
 その進むべき方向の決定は,一歩ごとに行われます.すなわち,一歩分進んだら周りを見渡して一番低い場所に続きそうな方向に足を向け,また一歩進んで周りを見渡してということを繰り返すわけです.

 このときの一歩の大きさにあたるのが学習率です.
 容易に想像できることですが,一歩の幅があまりに小さければ,遅々として進まず,東京から大阪を目指す蟻のように,いつまでたっても目的地に辿りつけなさそうです.逆に一歩の幅があまりに大きければ,巨人のように,目的地を飛び越えてしまうことになって,これまたうまくいかなさそうです.
 
 というわけで,この学習率の大きさを適切に定めることも重要な仕事となってきます.
 (この学習率のような,パラメータを決める学習のためのパラメータのことをハイパーパラメータというようです)
 じゃあ学習率を決めるのにもうまい方法があるのかと勢い込んで読んだのですが,どうも人の手によってあれこれ試しながら探すしかないとのことです.
 ちょっとがっくりきましたが,ざっと調べてみたところ,よくわからないながらも,これを自動的に決める学習方法もあるようです.しかし何せ本元のニューラルネットワークについてもよくわかっていない身で手を出すのはあまりにもアレなので今回はスルーしました.


4.まとめ

 学問的にあっているんだか間違っているんだか自信がありませんが,少なくとも将来自分が見返したときに役立つ資料とはなりました.
 ひとまず満足です.

 問題点があるとすれば,これを書いたり夜ご飯の二郎系ラーメンを食べに行ったりしていてなおニューラルネットワークによる学習が終わらないということです.Rのパッケージで試した時から線形回帰に比べ大幅に遅いことには気づいていましたが,まさかこれほどとは.どうなることやら.

*追記:あまりに遅いので中身のぞいてみたらエラー吐いてました.こういうのに気づくのも経験ですかね……

 それでは.