python_tips_備忘録

はじめに

コードを書いていると、「あ、これ前もググって実装したやつだ」ということがよくある。 しかもどうやったか全く覚えていない。悲しい。 あげく検索ワードすらわからないときが頻繁にある。悲しい。

これ以上、そのような悲しいことを起こさないために、たまに使う細かい処理について書いていく。 間違っている部分や黒魔術的な部分もあるかもしれないので注意。 随時追加するつもり。 番号は参考サイトを示している。サンプルコードについて非常に参考にした。

for文が正しく終了したときに、処理を実行する。(for...else) [1]

正常終了するとelseというのが、なんとなく慣れない。いちいちフラグとかたてなくてもいい。

for i in range(10):
    print i
else:
    print "hoge"
0
1
2
3
4
5
6
7
8
9
hoge
for i in range(10):
    print i
    break
else:
    print "hoge"
0

コマンドライン引数を処理する。(argparser, click) [2]、[3]

コマンドライン引数を処理するモジュールとして、argparserとclickがある。まずはargpaeser。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-n', '--n_iter', default=1000, type=int, help="num_iteration")
parser.add_argument('-g', '--gpu', action='store_true')
args = parser.parse_args()

print args
print args.n_iter
print args.gpu
$ python temp.py -n 10 --gpu
Namespace(gpu=True, n_iter=10)
10
True

続いてclick。最近知ったので、使い方がまだ良くわかっていないが、こちらのほうがコンパクトに書けて便利な気がする。

import click

@click.command()
@click.argument("name")
@click.option("-n", "--num_iter", type=int, help="num iteration")
def main(name, num_iter):
    for i in range(num_iter):
        msg = "hello %s %d" % (name, i)
        print msg

if __name__ == '__main__':
    main()
$ python temp2.py world -n 10
hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
hello world 5
hello world 6
hello world 7
hello world 8
hello world 9

ファイル名に時間を埋め込む 。(datetime)[4]

結果の出力など、うっかり上書きしないようにするときによく使う。

from datetime import datetime
filename = "results/" + datetime.now().strftime("%Y%m%d_%H%M%S") + ".csv"
print filename
results/20151130_225340.csv

ファイルを開く。(with open... as f)

普通にopenだけで開くと後でcloseしないといけない。忘れるので、withを使う。 自作のクラスもこういうものに対応させておくと後で便利かも。

with open("temp.csv") as f:
    for i in f:
        print i

プログラムの進行状況などをprintするときに改行せず上書きする。 (sys.stdout.write)

ループの中で、print i などと書くと、いちいち改行されて鬱陶しい。 以下のコードでいい感じに進行状況を表示できる。

import sys
import time
for i in range(100):
    sys.stdout.write("\riter=%d" % i)
    sys.stdout.flush()
    time.sleep(0.1)
iter=99

ファイル名で検索してリストを取得する。(glob)

paramsディレクトリ内に存在する全てのパラメータファイルを読んでいって、順番に実行するとか、resultsディレクトリ以下の全てのファイルの平均値をとるとか、やりたいときに使う。

import glob

print glob.glob("./*")
print glob.glob("./*.py")
['./tips.org', './temp.csv', './Untitled.ipynb', './temp1.py', './temp2.py', './temp10.py']
['./temp1.py', './temp2.py', './temp10.py']

文字中に含まれる数字でソート。[5]

ファイル名などに数字が付いている時、普通にsortメソッドを使うと、2の前に10が来たりしてしまう。はじめから%05dとかで書いておけば不要。

import re
files = glob.glob("./*.py")
temp = [(re.search("[0-9]+", x).group(), x) for x in files]
temp.sort(cmp = lambda x, y: cmp(int(x[0]), int(y[0])))
print  [x[1] for x in temp]
['./temp1.py', './temp2.py', './temp10.py']

別のディレクトリのファイルをインポートする。(sys.path.append)

昔作ったコードを再利用したいとき、コピーしてきてもいいけど、これでもOK。

import sys
sys.path.append("../kaggle/mnist/")
import nn
net = nn.Perceptron(10,2)

ある条件を満たす数列を生成する。(リスト内包表記、ジェネレータ)

リスト内包表記をごちゃごちゃすると色々できる。 あまり使いすぎるとわかりにくいので、ほどほどに使う。

x = [i**2 for i in range(10) if i%2==0]
print x
[0, 4, 16, 36, 64]

ジェネレータでもいける。yieldは遅延評価なので、1つ値を生成するコストが大きいかつ、いくつ必要かわからないときとかいいかもしれない。

def generator():
    counter = 0
    while(True):
        yield counter**2
        counter += 2
for i in generator():
    print i
    if i>=64:
        break
0
4
16
36
64

学習データからランダムなミニバッチを作る。( np.random.permutation(N) ) [6]、[7]

SGDとかやるときに使う。

    def train(self, inputs, labels, tests):
        N = len(inputs)
        inputs = np.array(inputs).astype(np.float32)
        labels = np.array(labels).astype(np.int32)
        tests = np.array(tests).astype(np.float32)

        for epoch in range(self.num_epoch):
            print "epoch: %d" % epoch
            perm = np.random.permutation(N)
            for i in range(0, N, self.batchsize):
                x_batch = cuda.to_gpu(inputs[perm[i:i + self.batchsize]])
                y_batch = cuda.to_gpu(labels[perm[i:i + self.batchsize]])

KFold Cross Validation(sklearn.cross_validation.KFold) [8]

一瞬自分でつくろうかと思ったけど、探してみたらやっぱりあったscikit_learn。 自分で書くコードは最小限にする方針からこれを使う。

kf = cross_validation.KFold(len(all_vec), n_folds=10, shuffle=True)
for train_index, test_index in kf:
    test_vec = all_vec[test_index]
    test_label = labels[test_index]
    train_vec = all_vec[train_index]
    train_label = labels[train_index]

特徴ベクトル(の一部)を正規化する。(sklearn.preprocessing.MinMaxScaler) [9]

sklearnに正規化の関数があるので、それを使う。1ofKの変数とかは正規化しなくてもいいと思うので、一部だけやる例。

min_max_scaler = sklearn.preprocessing.MinMaxScaler()
no_normalize_index = 8
min_max_scaler.fit(train_vec[:, no_normalize_index:])
test_vec[:, no_normalize_index:] = min_max_scaler.transform(test_vec[:, no_normalize_index:])
train_vec[:, no_normalize_index:] = min_max_scaler.transform(train_vec[:, no_normalize_index:])

bool型の掛け算順

以前、一度バグを産んだ原因コード。演算子の優先度は、(符号としての) - > * > (引き算としての)- なので、こういうことをすると、一見引き算の順序を変えただけなのに、結果がかわる。これからはおとなしくastypeでキャストしようと心に誓った。

import numpy as np
bool_list = np.array([True, False])

print [0,0] - bool_list * 0.5
print -bool_list * (0.5) + [0,0]

print - bool_list.astype(int) * 0.5 + [0,0]
[-0.5  0. ]
[ 0.   0.5]
[-0.5  0. ]

クラスタリングなどの結果をscatterでプロットする(plt.scatter)

だいたい特徴量的にx, yを管理しているので、特徴ベクトル行列のrow, colを入れ替えてプロットする。 colorの変数に適当に突っ込むとよしなにやってくれる。 ついでにPCAもする。

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pcaed_data = pca.fit_transform(data[:,1:]/255.0)
pcaed_data = zip(*pcaed_data)
plt.scatter(pcaed_data[0], pcaed_data[1], marker=".",c=data[:,0], linewidths=0, alpha=0.7)

csvファイルからデータを読み込む。(pandas)

普通にopenでファイルを開いてもいいけど、pandasでやると欠損値とかの扱いがかなり楽。

import pandas as pd
data = pd.read_csv("train.csv")

data = data[~data.Cabin.isnull()] #"Cabin"がNanでないものだけを抽出する。
print data
     PassengerId  Survived  Pclass  \
1              2         1       1
3              4         1       1
6              7         0       1
10            11         1       3
..           ...       ...     ...
879          880         1       1
887          888         1       1
889          890         1       1

                                                  Name     Sex   Age  SibSp  \
1    Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1
3         Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1
6                              McCarthy, Mr. Timothy J    male  54.0      0
10                     Sandstrom, Miss. Marguerite Rut  female   4.0      1
..                                                 ...     ...   ...    ...
879      Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)  female  56.0      0
887                       Graham, Miss. Margaret Edith  female  19.0      0
889                              Behr, Mr. Karl Howell    male  26.0      0

     Parch       Ticket      Fare        Cabin Embarked
1        0     PC 17599   71.2833          C85        C
3        0       113803   53.1000         C123        S
6        0        17463   51.8625          E46        S
10       1      PP 9549   16.7000           G6        S
..     ...          ...       ...          ...      ...
879      1        11767   83.1583          C50        C
887      0       112053   30.0000          B42        S
889      0       111369   30.0000         C148        C

[204 rows x 12 columns]

リストで重複した要素を削除する。(pandas)

例えばIDが重複していて、どちらかの要素がいらないとき、これを見つけて削除する。 オプションで上にあるものを残すか、下にあるものを残すかを選べる。

print data[~data.Cabin.duplicated()]
     PassengerId  Survived  Pclass  \
1              2         1       1
3              4         1       1
6              7         0       1
10            11         1       3
..           ...       ...     ...
879          880         1       1
887          888         1       1
889          890         1       1

                                                  Name     Sex   Age  SibSp  \
1    Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1
3         Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  35.0      1
6                              McCarthy, Mr. Timothy J    male  54.0      0
10                     Sandstrom, Miss. Marguerite Rut  female   4.0      1
..                                                 ...     ...   ...    ...
879      Potter, Mrs. Thomas Jr (Lily Alexenia Wilson)  female  56.0      0
887                       Graham, Miss. Margaret Edith  female  19.0      0
889                              Behr, Mr. Karl Howell    male  26.0      0

     Parch       Ticket      Fare        Cabin Embarked
1        0     PC 17599   71.2833          C85        C
3        0       113803   53.1000         C123        S
6        0        17463   51.8625          E46        S
10       1      PP 9549   16.7000           G6        S
..     ...          ...       ...          ...      ...
879      1        11767   83.1583          C50        C
887      0       112053   30.0000          B42        S
889      0       111369   30.0000         C148        C

[147 rows x 12 columns]

特定の文字を含むものを取り出す(pandas)

数字は簡単だけど、文字の一部に含まれているというのがよくわからなかった。これもpandasを使えば簡単。

print data[data.Cabin.str.contains("A")]
     PassengerId  Survived  Pclass  \
23            24         1       1
96            97         0       1
174          175         0       1
185          186         0       1
209          210         1       1
284          285         0       1
445          446         1       1
475          476         0       1
556          557         1       1
583          584         0       1
599          600         1       1
630          631         1       1
647          648         1       1
806          807         0       1
867          868         0       1

                                                  Name     Sex  Age  SibSp  \
23                        Sloper, Mr. William Thompson    male   28      0
96                           Goldschmidt, Mr. George B    male   71      0
174                            Smith, Mr. James Clinch    male   56      0
185                              Rood, Mr. Hugh Roscoe    male  NaN      0
209                                   Blank, Mr. Henry    male   40      0
284                         Smith, Mr. Richard William    male  NaN      0
445                          Dodge, Master. Washington    male    4      0
475                        Clifford, Mr. George Quincy    male  NaN      0
556  Duff Gordon, Lady. (Lucille Christiana Sutherl...  female   48      1
583                                Ross, Mr. John Hugo    male   36      0
599       Duff Gordon, Sir. Cosmo Edmund ("Mr Morgan")    male   49      1
630               Barkworth, Mr. Algernon Henry Wilson    male   80      0
647                Simonius-Blumer, Col. Oberst Alfons    male   56      0
806                             Andrews, Mr. Thomas Jr    male   39      0
867               Roebling, Mr. Washington Augustus II    male   31      0

     Parch    Ticket     Fare Cabin Embarked
23       0    113788  35.5000    A6        S
96       0  PC 17754  34.6542    A5        C
174      0     17764  30.6958    A7        C
185      0    113767  50.0000   A32        S
209      0    112277  31.0000   A31        C
284      0    113056  26.0000   A19        S
445      2     33638  81.8583   A34        S
475      0    110465  52.0000   A14        S
556      0     11755  39.6000   A16        C
583      0     13049  40.1250   A10        C
599      0  PC 17485  56.9292   A20        C
630      0     27042  30.0000   A23        S
647      0     13213  35.5000   A26        C
806      0    112050   0.0000   A36        S
867      0  PC 17590  50.4958   A24        S

おわりに

書いたら覚えるだろう、そう思っていた。

参考

  1. for文の終了時の処理(for...else) - 繰り返し - Python入門
  2. argparseを使ってみた - そこはかとなく書くよ。
  3. Python: コマンドラインパーサの Click が便利すぎた - CUBE SUGAR CONTAINER
  4. 現在時刻を取得・ファイル名用に整形 - 週末京都
  5. スーパーIT戦士になりたい!: Python: ファイル名の番号でソートする
  6. Chainerによる多層パーセプトロンの実装 - 人工知能に関する断創録
  7. numpy.random.permutation — NumPy v1.10 Manual
  8. sklearn.cross_validation.KFold — scikit-learn 0.17 documentation
  9. API Reference — scikit-learn 0.17 documentation

kaggle2回目 タイタニック号の生存者予測

概要

kaggle2回目。タイタニック号の乗員乗客の生存者予測。 データを眺めて思いついたことをやってみると、ベースラインは超えられたけれど、今ひとつうまくいかなかった。 今後、続きをやるか、別の課題をやるか、全然違うことをやるかは考え中。

はじめに

kaggle初挑戦ということで、前回手書き文字認識をやった。 課題自体やり尽くされている感じであまりおもしろくないので、今回はタイタニック乗客の生存予測に挑戦することにする。 手法は諸々の事情からニューラルネットワークにした。

どういう課題か

タイタニック号の沈没事故によって、多くの人が亡くなっている。 今回の課題は、この沈没事故によって死亡した人と生き残った人とを分類する問題である。 kaggleから与えられる情報は以下。

  • PassengerId: kaggleが振ったただの連番。役に立たない気がする。
  • PassengerClass: 乗客の等級。1から3まで。金持ちが生き残りそう。
  • Name: 名前。"Mr."とか"Don."とか色々情報がある。うまくやれば家族の情報も抽出できるかも。
  • Sex: 性別。映画では女性、子どもが先に救命ボートに乗ったらしい。関係ありそう。
  • Age: 年齢。同上。
  • Sibsp: 海外に住んでいる兄弟、配偶者の数。なんやこれ
  • Parch: 海外に住んでいる両親、子どもの数。なんやこれ
  • Ticket: チケットの番号。"113803"とか"A/5 21171"とか規則がよくわからない。
  • Fare: 運賃。金持ちが生き残りそう。
  • Cabin: 部屋番号。"C85"みたいな感じ。ぱっと見た感じほとんど欠損している。
  • Embarked: 乗船した港。Cherbourg、Queenstown、Southamptonの3種類。

ぱっとみると、関係ありそうなデータからそうでもないデータまで色々ある。 課題用のものというより、事後処理のときの名簿か何かなのかもしれない。

kaggleから提示されるベースラインは4つ。

  1. Assume All Perished [0.62679] 全員死んだとしたときの精度。
  2. Gender Based Model [0.76555] 女性が全員助かって、男性が全員死んだとしたときの精度。
  3. My First Random Forest [0.77512] Name、Ticket、Cabinのデータ以外を使ってランダムフォレスト。コードも公開。
  4. Gender, Price and Class Based Model [0.77990] 性別、チケットのクラス、チケットの値段の割合に基づいて予測(?)

とりあえず、これら全てのベースラインを超えることを目標とする。

どういうデータか

とりあえず、生データを眺めて以下のことに気づいた。

  • 欠損値が多い。: Ageは結構抜けていて、Cabinにいたっては欠損している方が多い。
  • Fareが0の人がいる。: 欠損しているのか、来賓的なやつなのか。どう扱っていいものか迷う。
  • 同じ名字、部屋番号の人がそれなりにいる。: 家族なら生死を共にしている可能性が高い気がする。

欠損値の補完の方法によって精度が結構変わりそう。 kaggleのコードでは文字列的な情報は使っていないので、欠損値の補完は、単に中央値を用いていた。 補完せずにデータを捨てると、いくつぐらいになるのかを計算してみるとデータ数が891→183になった。 もともと多くはないデータ数をかなり削ることになってしまうので、この案はなし。

とりあえず、色々ヒストグラムを書きながら考えることにする。 まず、データを生存か死亡かで分割する。

import pandas as pd
import pylab as plt

data = pd.read_csv("train.csv").replace("male",0).replace("female",1)
data = data.replace("C",0).replace("Q",1).replace("S",2)
split_data = []
for did_survive in [0,1]:
    split_data.append(data[data.Survived==did_survive])

以下のヒストグラムは全て、青色が死亡した人、緑が生き残った人。 まずはAge。

temp = [i["Age"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=16)

思ったよりも5歳以下の子どもが助かる確率が高い。優先的に逃げれたのだろうか。 一方で、5歳から10歳になると突然低くなる。

60歳超えるとだんだん厳しくなると思いきや、75歳以上の人が1人助かっている。 少ない訓練データにこういうのがあると、すごい高齢の人は助かるみたいな謎の学習しそうで怖い。 なんにせよ年齢はかなり使えそう。

f:id:ksknw:20151129151059p:plain

次にPassenger Class。

temp = [i["Pclass"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=3)

やはり一等客の生存率が高い。お金の力か。 f:id:ksknw:20151129151101p:plain

続いて性別。

temp = [i["Sex"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=3)

男(左)はだいたい死んでいて、女性(右)はかなり助かっている。 救命ボートの数が足りなかったらしいタイタニックにおいて、レディファーストとかやってたんだろうか。 この値もかなり使えそう。 f:id:ksknw:20151129151103p:plain

次に運賃。

temp = [i["Fare"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=10)

これもある程度出していたほうが、生存率高くなりそう。 f:id:ksknw:20151129151105p:plain

ここから、役に立つかよくわからないデータ。 一つ目、国外に住む兄弟または配偶者の数

temp = [i["SibSp"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=8)

グラフを見る限り、0人よりも1人のほうが生存率高そうなんだけど、この値が何と関係しているのかわからない。 国外に家族がいたほうが、収入が高くなって、高い運賃を払いやすくなるんだろうか。 f:id:ksknw:20151129151107p:plain

次、国外に住む親または子どもの数 一方で、こちらはパッとしない。

temp = [i["Parch"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=8)

f:id:ksknw:20151129151109p:plain

乗船した港。 これも意外と役に立ちそうな値。 地域による所得差とか、売られていた部屋の分布が偏っているとかそういうのだろうか。

temp = [i["Embarked"].dropna() for i in split_data]
plt.hist(temp, histtype="barstacked", bins=3)

f:id:ksknw:20151129151112p:plain

My First Neural Network

自分的ベースラインとして、とりあえず、ニューラルネットに突っ込んでみる。 使った特徴量はPassengerClass,Sex,Age,Sibsp,Parch,Fare,Embarked。 欠損値はなんとなく平均値で補完した。 PassengerClassとEmbarkedは1ofKにした。 結果60%ぐらいで、これは先の男女だけで判別したモデルよりも低い。雑魚すぎる。 (実は正規化のところでやらかしていたので普通にミスだった)

欠損値の補完とはなにか

欠損していた場合、モデルにとってニュートラルな信号を入力すべきと思うんだけど、 例えば年齢を欠損していた時、これを平均値もしくは中央値で補完すると、死亡と判定する率が上がりそうな気がする。

調べてみると、欠損値を補完する手法が色々あるらしいが、面倒なので欠損の場所によってモデルを分けることにした。 上記の特徴量に関して、Testデータで欠損している部分を調べると、Ageが欠損しているパターンとFareが欠損しているパターンがあった。 よって3つのモデルを学習させることにする。

  1. Age、Fareが欠損していないデータを学習データにして、同様のデータを予測。
  2. Fareが欠損していないデータを学習データにして、Ageが欠損しているデータを予測。
  3. Ageが欠損していないデータを学習データにして、Fareが欠損しているデータを予測。

3つのネットワークを学習させて結果をsubmitすると、 0.77990でベースラインに並んだ。

f:id:ksknw:20151129150618p:plain

ここまで適当にしていたパラメータ調整をちゃんとやればベースラインを超えられる気がしたので、 パラメータ色々いじってsubmitしまくると、 0.78469になり、無事ベースラインを超えることができた。

f:id:ksknw:20151129150625p:plain

試行回数をふやす

何度かパラメータ調整をしてsubmitを繰り返していると、スコアが結構ブレることに気づく。 また、先ほどのモデルの1.に使う学習データは結構少ないので、精度が本当にでるのかやや不安でもある。 そこで、学習を複数回行い、また、1.のモデルで判別していたデータを2.のモデルでも判別した。 それらの結果の平均を最終的な判別結果とする。

submitすると、0.77990で、うーんという感じ。 結果の分散が減ったぶん、たまたまうまくいくのもなくなっているという印象。

別の特徴量を考える。

このままモデルをいじっていてもジリ貧な気がしたので、別な特徴量を考えることにした。

  • PassengerId: なんとなく削っていたけれども、もしかすると何かあるかもしれないので足す。
  • 欠損値の有無: そもそも欠損しているのはなぜか。死んだからではないのか。
  • Nameの敬称: Mr.とかMs.とかだけだと思っていたら、こちらによるともっと色々あるらしいので、突っ込んでみることにした。

以上を加えてモデルを学習させると 0.76077でいまいちだった。ぐぬぬ

ちなみに現在のコードのだいたい。

#-*- coding: utf-8 -*-
import pandas as pd
import numpy as np
from nn import Perceptron
from chainer import cuda
import sklearn.preprocessing


def to_feature(data):

    emberked = []
    for i in data["Embarked"]:
        temp = np.zeros(3)
        if not np.isnan(i):
            temp.put(i, 1)
        emberked.append(temp)
    feature = emberked

    pclass = []
    for i in data["Pclass"]:
        temp = np.zeros(3)
        if not np.isnan(i):
            temp.put(i - 1, 1)
        pclass.append(temp)
    feature = np.c_[feature, pclass]

    feature = np.c_[feature, data["Age_na"]]
    feature = np.c_[feature, data["Cabin_na"]]

    for keyword in ["Master.", "Col.", "Mrs.",
                    "Ms.", "Miss.", "Rev.",
                    "Mr.",  "Dr.", "Major."]:

        feature = np.c_[feature, data.Name.str.contains(keyword).astype(int)]

    try:
        feature = np.c_[feature, data["Age"]]
    except:
        pass
    try:
        feature = np.c_[feature, data["Fare"]]
    except:
        pass
    try:
        feature = np.c_[feature, data["SibSp"]]
    except:
        pass
    try:
        feature = np.c_[feature, data["Parch"]]
    except:
        pass
    feature = np.c_[feature, data["Sex"]]
    feature = np.c_[feature, data["PassengerId"]]

    return feature


def read_data(data_filename):
    data = pd.read_csv(data_filename).replace("male", 0).replace("female", 1)
    data = data.replace("C", 0).replace("Q", 1).replace("S", 2)
    return data


def add_is_na_columns(data):
    for label in ["Age",  "Cabin"]:
        temp = pd.DataFrame(pd.isnull(data[label]).astype(int))
        temp.columns = [label + "_na"]
        data = pd.concat([data, temp], axis=1)
    return data

if __name__ == '__main__':
    test_data = add_is_na_columns(read_data("test.csv"))
    train_data = add_is_na_columns(read_data("train.csv"))

    label_categories = [['PassengerId', 'Name', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare',  'Embarked', 'Cabin_na', 'Age_na'],
                        ['PassengerId', 'Name', 'Pclass', 'Sex', 'SibSp', 'Parch', 'Fare',  'Embarked', 'Cabin_na', 'Age_na']]

    results = []

    for i in range(1):
        print i
        for label in label_categories:
            temp_train_data = train_data.ix[:, train_data.columns.isin(label + ["Survived"])].dropna()
            temp_test_data = test_data.ix[:, test_data.columns.isin(label)].dropna()

            train_vec = to_feature(temp_train_data)
            test_vec = to_feature(temp_test_data)

            min_max_scaler = sklearn.preprocessing.MinMaxScaler()

            no_normalize_index = 8 + 9
            min_max_scaler.fit(train_vec[:, no_normalize_index:])

            test_vec[:, no_normalize_index:] = min_max_scaler.transform(test_vec[:, no_normalize_index:])
            train_vec[:, no_normalize_index:] = min_max_scaler.transform(train_vec[:, no_normalize_index:])

            nn = Perceptron(len(train_vec[0]), 2, 700, 45, 45)
            results.append([temp_test_data.PassengerId,
                            nn.train(train_vec,
                                     temp_train_data["Survived"],
                                     test_vec)])
            # from sklearn.ensemble import RandomForestClassifier
            # random_forest = RandomForestClassifier()
            # random_forest.fit(train_vec, temp_train_data["Survived"])
            # results.append([temp_test_data.PassengerId,
            #                 random_forest.predict(test_vec)])

    results.append([[1044], [1, 0]])  # Fareが欠損している一人は死んだことにする
    # results.append([[1044],  0])  # Fareが欠損している一人は死んだことにする

    print results
#    f = open("result_random_forest.csv", "w")
    f = open("result_nn_10.csv", "w")
    f.write("PassengerId,Survived\n")

    for result in results:
        for i in zip(*result):
            print i
            f.write("%d,%d\n" % (i[0], np.argmax(i[1])))
#            f.write("%d,%d\n" % (i[0], i[1]))
    f.close()

終わりに

なんか適当にパラメータいじっていたら0.78947になった。 たまたま感ある。 f:id:ksknw:20151129150629p:plain

色々やってみたけれども、なかなか難しい。 特徴量を増やしてみても、よくなるわけでもなく。 というかそもそも、どれぐらいいけるものなのかもわからない。 ランキングの一番上は1.000なので、萎える。

もうちょっとアイデイアがあるので、もしかすると続くかもしれないし、 飽きてきている感があるので、別の課題をやるかもしれない。

参考

kaggleで予測モデルを構築してみた (1) - kaggleって何? - About connecting the dots.

kaggle初挑戦 手書き文字認識

概要

kaggleコンテスト(チュートリアル)に初参加した。 手書きの文字認識を4層のNeural Networkでやってみると、ベンチマーク(ランダムフォレスト)は超えられた。 これからちょくちょくやっていきたい。

はじめに

研修で別の業務をやっているので、退屈している、プログラムが書きたい、機械学習したい。 ということで、データサイエンス的なコンテストであるkaggleに参加することにした。 kaggleは初挑戦かつ色々にわかなので、適当ですが、許してほしい。

kaggleとは

公式サイトはこちら

Wikipediaによると

Kaggleは企業や研究者がデータを投稿し、世界中の統計家やデータ分析家がその最適モデルを競い合う、予測モデリング及び分析手法関連プラットフォーム及びその運営会社である。

要するに、企業とかがデータを提供して、世界中の人でそれを解くコンテストである。 今やってるコンテストを見ると、例えば、降水量の予測とか売上の予想とか色々あって面白そう。 本番のコンテストは賞金がついてるものもあって、出題者が本当に課題を解決したいんだなぁという感じがある。

いきなり本番のコンテストに参加するのは色々ハードルが高いので、とりあえず、チュートリアル的なコンテストをやることにする。 チュートリアル的なコンテストは4つ。

とりあえず、一番簡単そうな手書き数字認識をやることにした。

手書き数字認識(mnist)

今回認識するデータはmnistという大規模手書き数字データ。 画像の大きさは28x28ピクセルで白黒画像である。 今回のコンテストでは42000枚のラベル付きの画像で学習を行い、28000枚のラベルなしの画像を認識させる。 このコンテストはチュートリアルなので、ベンチマークアルゴリズム(ランダムフォレスト)、コード、結果(正答率93.514%)が公開されていた。 とりあえずこれを超える結果を出すこと今回の目標とした。

データ

とりあえず、生データをみる。 たまに人間でもなかなか厳しそうなやつがある。もうちょっと丁寧に書いてほしい。 f:id:ksknw:20151108163057p:plain

まずは何も考えずに、画素データを単にベクトルと思って、PCAしてプロットしてみる。

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pcaed_data = pca.fit_transform(data[:,1:]/255.0)
pcaed_data = zip(*pcaed_data)

plt.scatter(pcaed_data[0], pcaed_data[1], marker=".", c=data[:,0], linewidths=0, alpha=0.7)
f=plt.gcf()
f.set_size_inches(20.0,20.0)
plt.savefig("pca.png")

f:id:ksknw:20151108163045p:plain この時点ですでに、いくつか分かれているものもあるけど、まあ正直よくわからない。 真面目にやるならもうちょっとデータを眺めているのもいいと思うけど、そこまでやる気はないので適当にささっと判別する。

アルゴリズム選択

教師あり学習、画像判別ということで、パッと思いつくアルゴリズムは以下。

  • ランダムフォレスト
  • SIFTやHOG、HLACなどを計算した後、SVMとか
  • Convolutional Neural Network
  • 多層パーセプトロン

最近CNNを使った際、かなり性能が良かったこともあり、これらの中で、直感的に性能が出そうなものはCNNと思う。 ただ、数字だけの認識なのでCNNまでいかなくても、多層パーセプトロンで何とかなるだろうと思った。 そこで、今回はまず多層パーセプトロンで実装してみて、だめなら、もうちょっと強そうなアルゴリズムを使うことにした。

mnistは有名なデータセットなので、少し調べるだけで、多層パーセプトロンで分類をやっている人はたくさんいた。 これらの記事をありがたく参考にさせてもらいつつ、実装を行う。

こちらのブログ で4層のパーセプトロンで認識を行っていたので、とりあえず、4層のネットワークを組むことにした。

実装

今後の勉強も兼ねて、chainerで実装を行う。 もし4層でイマイチな性能だったときに簡単に層数を増やせそうというのもある。

中間層の数は1000ずつ、活性化関数はReLUにして、Dropoutもすることにした。 chainer使うとこのへんの処理がとても簡単に書けるので良い。

学習方法について、最近こういうの を見て、確率的勾配法いまいちなのではと思っている。 今回は、詳細を知らないけど強そうなAdamを使うことにした。

エポック数は20に設定した。 本当は汎化誤差とか見ながら打ち切りするといいと思う。 4層程度ならgpuはいらないかなと思ってcpuで動くコードにした(あとで後悔した)。

import chainer
import chainer.functions as F
from chainer import optimizers
import numpy as np


class Perceptron(object):

    def __init__(self):
        self.batchsize = 100
        self.num_epoch = 20
        self.num_hiddens = 1000
        self.model = chainer.FunctionSet(
            l1=F.Linear(784, self.num_hiddens),
            l2=F.Linear(self.num_hiddens, self.num_hiddens),
            l3=F.Linear(self.num_hiddens, 10)
        )
        self.optimizer = optimizers.Adam()
        self.optimizer.setup(self.model)

    def forward(self, x_data, y_data=None, train=True):
        x = chainer.Variable(x_data)
        if train == True:
            t = chainer.Variable(y_data)
        h1 = F.dropout(F.relu(self.model.l1(x)), train=train)
        h2 = F.dropout(F.relu(self.model.l2(h1)), train=train)
        y = self.model.l3(h2)
        if train:
            loss = F.softmax_cross_entropy(y, t)
            return loss
        else:
            return y.data

    def train(self, inputs, labels, tests):
        N = len(inputs)
        inputs = np.array(inputs).astype(np.float32)
        labels = np.array(labels).astype(np.int32)
        tests = np.array(tests).astype(np.float32)

        for epoch in range(self.num_epoch):
            print "epoch: %d" % epoch

            perm = np.random.permutation(N)
            sum_loss = 0
            for i in range(0, N, self.batchsize):
                x_batch = np.asarray(inputs[perm[i:i + self.batchsize]])
                y_batch = np.asarray(labels[perm[i:i + self.batchsize]])

                self.optimizer.zero_grads()
                loss = self.forward(x_batch, y_batch)
                loss.backward()
                self.optimizer.update()
                sum_loss += float(loss.data) * len(y_batch)
            print "train mean loss: %f" % (sum_loss / N)
        np.asarray(tests)
        y = self.forward(tests, train=False)
        return y

これをipython notebookから読んで使う。

import pandas as pd
import numpy as np

data = pd.read_csv("train.csv")
data = data.as_matrix()
test_data = pd.read_csv("test.csv")

import nn
perceptron = nn.Perceptron()
test_results = perceptron.train(data[:,1:]/255.0, data[:,0], test_data[:]/255.0)
epoch: 0
train mean loss: 0.314591
epoch: 1
train mean loss: 0.149317
epoch: 2
train mean loss: 0.115880
epoch: 3
train mean loss: 0.101776
epoch: 4
train mean loss: 0.082577
epoch: 5
train mean loss: 0.082045
epoch: 6
train mean loss: 0.074332
epoch: 7
train mean loss: 0.064091
epoch: 8
train mean loss: 0.059199
epoch: 9
train mean loss: 0.061192
epoch: 10
train mean loss: 0.053155
epoch: 11
train mean loss: 0.054124
epoch: 12
train mean loss: 0.050729
epoch: 13
train mean loss: 0.050139
epoch: 14
train mean loss: 0.048307
epoch: 15
train mean loss: 0.045948
epoch: 16
train mean loss: 0.044958
epoch: 17
train mean loss: 0.046211
epoch: 18
train mean loss: 0.040627
epoch: 19
train mean loss: 0.041885

ちゃんと損失が下がっている。 あとは提出用のフォーマットを作成する。

results =  np.argmax(test_results, axis = 1)
import csv
f = open("results.csv", "w")
f.write(('"ImageId","Label"\n'))
for i, result in enumerate(results):
    f.write(('%d,"%d"\n'%(i+1,result)))
f.close()

結果

結果をkaggleのサイトに送信すると、正答率とランキングを教えてくれる。 今回の正答率は98%で、順位は205/830位でした。 f:id:ksknw:20151108162603p:plain

間違ったデータを確認してアルゴリズムを改良したいところだけど、テストデータはラベルがないのでどれ間違ったのかわからない。 ちゃんとやるなら、学習データ分割してvalidationしないとだめそう。

今後

パラメータもデータの正規化も、アルゴリズムもかなり適当だったので、色々もうちょっとちゃんとすれば性能上がると思う。 とはいえ、mnist自体はやり尽くされている感じで、あまりおもしろい感じではないので、次はタイタニックのやつをやってみようと思う。

参考

Kaggle: The Home of Data Science

MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges

Deniz Yuret's Homepage: Alec Radford's animations for optimization algorithms

Chainerによる多層パーセプトロンの実装 - 人工知能に関する断創録

iPython notebookを使おうかと思ったけどやめた話(最近むちゃくちゃ使ってるのでこれは嘘)

概要

iPython notebookというPythonコードと数式、グラフなどを統合的に扱えるものを知ったので、 使ってみたけど、Emacsからは微妙だったので使うのやめようかなと思ってる。 あくまでも小一時時間さわってみた感想なので、もう少し使っていれば別の感想になると思う。 タスクごとにエディタを変えることに抵抗が少ない人にはおすすめ。

iPython

iPythonはPythonの対話型インタプリタの拡張。 普通のPythonの対話型インタプリタbashとするなら、iPythonはzshみたいな感じ。 変数名、オブジェクトのメソッド名など、タブで色々補完してくれるので、便利。 他にもシェルのコマンドが使えたり、実行時間が簡単に測れたり色々ある。 こちら が詳しい。

(え、対話型インタプリタって使うの?って正直思ってるんですが、みんな使ってるんですか?)

iPython notebook

iPython notebookではPythonMarkdown、普通のテキストを書くことができる。 これらはそれぞれ、セルごとにどれを使って記述するか指定することができる。 詳しくはこちら を参考。

実際に使ってみたときの画像を以下に示す。 f:id:ksknw:20151010195152p:plain

Markdownで数式を書いて、その下にPythonのコード、さらに結果のグラフを一つのファイルの中で完結させることができている。

また、画面上にツールバーがあり、記述したPythonのコードをその場で実行し、結果をすぐ下に表示させることができる。

今まで、パラメータを変更した際の結果を管理する方法として、ファイル名やフォルダ名にパラメータを記述するという手段をとっていた。 iPython notebookを使うことで、Markdownでパラメータ(値だけでなく式でもいい)を記述し、グラフを管理することができるかもしれない。

Emacsから使う

確かに一つのファイルの中で、Python、その出力結果、Markdownを管理できるのは魅力的な機能である。 一方で、その機能を使うために、別のエディタ(しかもブラウザ上で動作する)を使うというのは、 Emacs教徒である僕には我慢ならない。 ということで、EmacsからiPython notebookを使う。

EmacsにおけるiPython notebookはein(Emacs iPython notebook)から利用できる。 こちら を参考にした。

実際に使ってみた画像は以下。 f:id:ksknw:20151010194432p:plain

Markdownの数式が表示させることができなかった。

結局使うのをやめた理由

以下の理由から、真面目に使うのはもうちょっと後かなぁと思っている。 とはいえ、数式とプログラムと実行結果を一つにまとめられるというのはとても魅力的なので、 そのうち、もう一度トライしてみようと思う。

また、自分の結果をまとめるというよりは、教育や知識の共有に向いているような印象を持った。 理論的な部分の説明から実際のコードの実装、結果を見ることができて、なおかつ、自分でコードを色々いじって結果をすぐに見れるというのは、理解を助けてくれると思う。

Emacsから扱うためのモード"ein"が使いにくい

einはまだ開発途中なのか、動作が不安定な場面が多い気がした。 また、普段Emacspythonを使うときはautopep8やwhite spaceでコードの体裁を整えている。 これらの機能が正常に動いてくれなかったので、コードがぐちゃぐちゃになってしまった。 演算子の両側のスペースとか手動で打つ気にはまったくならない。 設定をいじればどうにかなるのかもしれない。

そもそもMarkdown書けなかった

実はMarkdown書けなかった。 できればorg-modeの中にコードと実行結果を埋めこみたい。

追記(2016/4/23)

Markdown書けるようになったので最近むっちゃ使ってる。 einは今のところ使っていない。

参考

IPythonの使い方

はじめるiPython notebook

EmacsでiPython notebookを使う(EIN / Emacs iPython notebook)

Ubuntu14.04 のクリーンインストールからchainer-goghを実行して、芸術的な猫を描くまで

概要

Ubuntu14.04をクリーンインストールするところから、CUDAやChainerをインストールした。 chainer-goghが正しく動作することを確認し、芸術的な猫を描いた。

以下のものをインストールする。

  • Ubuntu 14.04
  • cuda-7.5
  • chainer(-cuda-deps)

(2015/12/26 追記)

あまりに適当だったので環境の構築を書き直した。 Ubuntu14.04での自分的環境構築をちゃんと書く - やったことの説明


CUDAのインストール

インストールに関して、主にこちらの記事を参考にした。 クリーンインストールした Ubuntu14.04 に環境構築 (2)

とりあえず、今後のことを考えてディレクトリを英語にする。

LANG=C xdg-user-dirs-gtk-update

色々インストールする。

$ sudo apt-get  update
$ sudo apt-get install freeglut3-dev build-essential libx11-dev libxmu-dev  libxi-dev libgl1-mesa-glx libglu1-mesa libglu1-mesa-dev linux-headers-`uname -r`
$ sudo lshw -short -class display
$ sudo apt-get remove --purge nvidia*
$ mkdir cuda
$ cd cuda/

NVIDIAのサイトからCUDAの最新版とグラボのドライバをダウンロードして色々やる。

$ chmod +x cuda_7.0.28_linux.run
$ chmod +x NVIDIA-Linux-x86_64-352.41.run
$ sudo nano /etc/modprobe.d/blacklist-nouveau.conf
$ echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf
$ sudo update-initramfs -u

再起動して、Ctrl+Alt+F1を押してCUIに入る。 そのままではグラボのドライバをインストールできないので、以下をする。

$ sudo service lightdm stop

グラボのドライバをインストールする。

$ chmod +x NVIDIA-Linux-x86_64-352.41.run

そのままCUDAをインストールする。

$ chmod +x cuda_7.0.28_linux.run

.bashrcなり.zshrcなりに環境変数を書いて、CUDAのインストールは終わり。

chainerのインストール

ささっとchainerをインストールする。Caffeと違って非常に簡単にインストールできた。

$ sudo apt-get install python-pip
$ sudo apt-get install python-dev
$ sudo pip install chainer
$ sudo visudo -> secure pathをコメントアウト
$ sudo pip install chainer-cuda-deps

ここまでで、すでにchainerでプログラムが動く。すごい。 chainerのテストのために、こちら をダウンロードする。解説はこちら。 中身は一般物体認識で学習させたCNNを使って、絵の画風を変換するというもの。 詳細は解説記事の方を見てほしい。すごい。

$ sudo apt-get install git
$ git clone https://github.com/mattya/chainer-gogh.git
$ cd chainer-gogh

vgg model fileもダウンロードしておく。

実行

手元のマシン(GTX-750Ti)では、メモリが足らず、256x256の画像を生成することができなかったので、画像サイズは128x128にした。 入力画像として可愛い猫の画像を使う。 f:id:ksknw:20150920205050p:plain かわいい。

スタイル画像はもともとのものを使わせてもらう。

実行結果

f:id:ksknw:20150920205043p:plain f:id:ksknw:20150920205103p:plain f:id:ksknw:20150920205113p:plain f:id:ksknw:20150920205116p:plain

左がスタイル画像、右が生成画像。 簡単に芸術的な猫を描くことが出来ました。 かわいいね!

参考

git + latexdiff でTeXの原稿を添削してもらいやすくする。

(latexdiffのバージョンが古かったりで微妙だったので書き直した 2018年3月)

はじめに

論文や報告書で,TeXの文章を人に添削してもらう機会は多い. それなりに長い文章を添削してもらうとき,前回の添削からどこをどう訂正したのかわからないと, 何度も読まないといけないだろうし,申し訳ない.

自分が訂正した部分をわかりやすくするために,

などがあると思うが,どれも手動でやらなければならないので,めんどくさい. 画期的なソリューションが必要である.

探してみるとlatexdiffというのがあり,gitから呼ぶと便利らしいので,使ってみる.

latexdiff

latexdiffは2つのTeXファイルの差分をTeXファイルで出力してくれるアプリケーション.

サイトはこちら . 普通にapt-getからインストールできた.

$ sudo apt-get install latexdiff
$ latexdiff --version
Latexmk, John Collins, 1 January 2015. Version 4.41

とりあえず,以下のTeXファイルを適当に作る.

before.tex

\documentclass{jarticle}
\begin{document}
消したところは赤で表示される.

\end{document}

after.tex

\documentclass{jarticle}
\begin{document}
追加したところは青で表示される.

\end{document}

あとは以下のコマンドを叩く.

$ latexdiff -e utf8 -t CFONT before.tex after.tex > diff.tex

文字コードnkf –guess とかで調べる. ちなみに指定しないと,日本語が文字化けしてコンパイルできなかった.

コンパイルするとこんな感じのPDFファイルができる.便利.

f:id:ksknw:20180314221745p:plain

latexdiff-git

このままでも使えなくはないけど,変更前のファイルを残しておかないといけない. めんどくさいし,なによりださい. もともとgit使って管理しているなら,前のコミットをlatexdiffの入力として使える.

$ latexdiff-git -r -t CFONT -e utf8 test.tex

とすれば,差分ファイルが出力される.

直前以外のコミットと現在のファイルの差分を出力したいときは

$ latexdiff-git -r HEAD~1 -e utf8 -t CFONT main.tex

などでリビジョンを指定することもできる.

複数ファイルにまたがるとき

ここまでのやり方だと,inputのようなコマンドを使用しているときに,うまく差分ファイルを生成することができない. こちらによると,flattenというオプションを指定すればいいらしい.

以下のようなファイルで実際にやってみると,ちゃんとdiffファイルが生成された.

$ latexdiff -e utf8 --flatten main.tex main2.tex > diff.tex
\documentclass{jarticle}
\begin{document}
\input{before.tex}

\end{document}

参考にしたサイト

Emacsの上にも3年

はじめに

研究室に配属されて以来Emacsを使い続けて,そろそろ3年になる.研究室のPCもそろそろ返却しないといけないので,自宅のPCにEmacsをインストールしつつ,今やってる設定をまとめてみようと思う.3年使っているけれど,lispはさっぱりなので,色々な方のブログ等を頼りに.だいたいはコピペしているだけなので,参考元のページを参照してほしい.使っている言語はpythonC++TeXが主なので,そのあたりの設定だけしかしていない.

環境

Emacs24.4は普通にやると入らなかった覚えがある.特にMacは,こちらをみてパッチを当てるなどした.

[追記]

1年たって、helmを導入したり色々変わった。 特にpythonの設定について、こっちに書きなおした。 ksknw.hatenablog.com

init-loader

最近まで,init.elに全部書いて,適当に管理していたので,設定がぐっちゃぐちゃになっていた.init-loaderは特定のディレクトリ以下のすべてのファイルを自動的に読み込んでくれる.設定ファイルを簡単に分割できるので便利.

こちらのサイトから設定,その他をありがたくコピペさせていただいた.

;;init-loaderで残りの設定を読み込む
(require 'init-loader)

;;initファイルを読み込むときのエラーを表示
(defun init-loader-re-load (re dir &optional sort)
  (let ((load-path (cons dir load-path)))
    (dolist (el (init-loader--re-load-files re dir sort))
      (condition-case e
      (let ((time (car (benchmark-run (load (file-name-sans-extension el))))))
        (init-loader-log (format "loaded %s. %s" (locate-library el) time)))
    (error
     (init-loader-error-log (format "%s. %s" (locate-library el) (error-message-string e))) ;追加
     )))))

;;~/.emacs.d/inits/ 以下を読み込み
(init-loader-load "~/.emacs.d/inits")

分割は今のところこんな感じ.

anything

研究室にあったinit.elを捨てて,自分で最初に入れた拡張がこれだった.すべてのファイル,コマンド,クリップボードなんかを統合的に扱ってくれるインターフェイスといえばいいのか.とにかく便利だった.今では依存しきっていて,anythingがないと,ちょっと前に作ったファイルは開けないだろうし,コマンドもほぼ打てないだろうと思う.

こちらはるびきちさんのサイトを参考に.

(require 'anything-startup)

(setq anything-c-filelist-file-name "~/.emacs.d/all.filelist")
(setq anything-grep-candidates-fast-directory-regexp "^~/.emacs.d/")

;;anythingでファイルリストを検索
(define-key global-map (kbd "C-:") 'anything-filelist+)
;;クリップボードの履歴をanythingで検索
(global-set-key "\M-y" 'anything-show-kill-ring)
(global-set-key (kbd "C-.") 'anything-do-grep)

ファイルとコマンドだけでなく,クリップボードに溜め込んだものを扱える,anything-kill-ringも便利.anything-grepは未だに使い方がよくわかってなくて,たまにうまく検索できない.

undo histを使ってるので,filelistを作るスクリプトにそのディレクトリを見ないように設定した以外は,ほぼデフォルト.最近はhelmのほうがいい(?)らしいけど,初めに入れたのがanythingだったので,なんとなく使っている.

migemo

migemoは日本語をローマ字のまま検索してくれる機能.コード書いてて,コメントを検索したいときに,いちいち変換する必要がなくなる.

このあたりを参考にさせていただいて,インストールして

sudo apt-get install cmigemo

適当に設定を書く.

(setq migemo-dictionary "~/cmigemo/dict/utf-8.d/migemo-dict")
(setq migemo-command "cmigemo")
(setq migemo-options '("-q" "--emacs"))
(setq migemo-user-dictionary nil)
(setq migemo-coding-system 'utf-8)
(setq migemo-regex-dictionary nil)
(load-library "migemo")
(migemo-init)

そんなに賢くはないので,大量の日本語から英単語を見つけるときにはむしろ邪魔かもしれない.特にTeX書いてるときとか.

white space

white spaceはスペースやタブなどを可視化してくれる機能.ついでに行末のスペースとか文末の改行とか消すこともできる.

僕は諸悪の根源たる全角スペースを可視化している.ついでに,行末のスペースとかも不要なので消すようにした.

こちらこちらを参考にした.

;;保存時に行末のスペースを削除.文末の改行は削除しない
(require 'whitespace)
(set-face-foreground 'whitespace-space "DarkGoldenrod1")
(set-face-background 'whitespace-space nil)
(set-face-bold-p 'whitespace-space t)
(setq whitespace-style '(tabs           ; タブ
             trailing       ; 行末
             spaces         ; スペース
             space-mark     ; 表示のマッピング
;;                         tab-mark
             ))
(setq whitespace-action '(auto-cleanup))
(global-whitespace-mode 1)

;;全角のスペースを目立たせる   <-こんな感じ
(setq whitespace-space-regexp "\\(\x3000+\\)")
(setq whitespace-display-mappings
      '((space-mark ?\x3000 [?\□])
    (tab-mark   ?\t   [?\xBB ?\t])
    ))

undo tree and hist

undo histはファイルを閉じてもundoの履歴を保存してくれる機能.undo tree は今までのundoの履歴を樹形図みたいに表示してくれる機能.

こちらこちらを参考にした.

;; undoの履歴をウィンドウを閉じても保持する
(when (require 'undohist nil t)
  (undohist-initialize))

;; undoの樹形図を表示する C-x u
(when (require 'undo-tree nil t)
  (global-undo-tree-mode))

関係ないけど,EmacsのundoはWindowsとかの場合と挙動が違っていて,最初に躓いた記憶がある.

その他の細かい設定

基本は研究室にもとからあったものを参考.

自分で入れたのはpopwinとieditとflycheck.それぞれ,コンパイルとかanythingとかでウィンドウ構成を変えないようにする機能.複数箇所の同時編集.文法チェック.

を参考にした.

;;改行時に自動でインデントする
;;C-mとEnterは同等なので,これでOK
(global-set-key "\C-m" 'newline-and-indent)

;;ファイルを開くときに大文字小文字の違いを無視
(setq read-buffer-completion-ignore-case t)    ;; バッファ名
(setq read-file-name-completion-ignore-case t) ;; ファイル名

;; 問い合わせを簡略化 yes/no を y/n
(fset 'yes-or-no-p 'y-or-n-p)

;; C-k で行末の改行も消去
(setq kill-whole-line t)

;;バッファ自動読み込み
(global-auto-revert-mode 1)

;;同名のファイルのバッファ名を変更
(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward-angle-brackets)

;;compileとかでウィンドウ構成がこわれないようにする
(require 'popwin)
(setq display-buffer-function 'popwin:display-buffer)

;;コンパイル画面でスクロールする
(setq compilation-scroll-output t)

;; 行番号を表示する
(require 'linum)
(global-linum-mode t)      ; デフォルトで linum-mode を有効にする
(setq linum-format "%5d ") ; 5 桁分の領域を確保して行番号のあとにスペースを入れる

;; スタートアップページを表示しない
(setq inhibit-startup-message t)
(server-start)

;タイトルバーにファイル名を表示
(setq frame-title-format (format "%%f - Emacs@%s" (system-name)))

;; ツールバーを消す
(tool-bar-mode 0)

;; バックアップファイルを作らない
(setq backup-inhibited t)
(setq make-backup-files nil)
;; 終了時にオートセーブファイルを消す
(setq delete-auto-save-files t)

;;音を鳴らさない
(setq visible-bell t)
(setq ring-bell-function 'ignore)

;;対応する括弧を挿入
(defun electric-pair ()
  "Insert character pair without sournding spaces"
  (interactive)
  (let (parens-require-spaces)
    (insert-pair)))

;;複数箇所の同時編集 sublime text的なやつ
(require 'iedit)

;; syntaxチェック
(require 'flycheck)

python-mode

追記:結局jediが最強だったので、以下のpython設定はもう使っていない。

最近は以下。 PythonをEmacsで書く+α - やったことの説明

elpy

僕のPythonの設定はほぼこれで終わりといってもいいかもしれない.どこまでがelpyなのかよくわからないが,package-list-packagesで入れると,色々くっついてインストールされた.変数名の補完から,文法チェックまでいろいろしてくれる."."を打つだけで,メソッド名を補完してくれるのが嬉しい.

英語で説明してくれるビデオを参考にして入れた.この動画のシリーズが結構いいかもしれない.

設定は1行.

(elpy-enable)

elpyをインストールする前に入れていた,yasnippetとかauto-completeとかflycheckとかと干渉してよくわからないことになったことがあった.結局Python時にACを切って,snippetのフォルダからpython-modeを削除して,flycheckも言語ごとに設定するようにして,なんとかなった.

その他

色々書いているけど,なくてもelpyで動くとこもありそう.

(add-hook 'python-mode-hook
           '(lambda ()
              (setq indent-tabs-mode nil);;tabの幅を変える
              (setq indent-level 4)
              (setq python-indent 4)
              (setq tab-width 4)
              (define-key python-mode-map "\"" 'electric-pair) ;;括弧の補完
              (define-key python-mode-map "\'" 'electric-pair)
              (define-key python-mode-map "(" 'electric-pair)
              (define-key python-mode-map "[" 'electric-pair)
              (define-key python-mode-map "{" 'electric-pair)
              (define-key company-active-map (kbd "\C-n") 'company-select-next)
              (define-key company-active-map (kbd "\C-p") 'company-select-previous)
              (define-key company-active-map (kbd "\C-d") 'company-show-doc-buffer)
              (define-key company-active-map (kbd "<tab>") 'company-complete)
              (auto-complete-mode -1)
              ))

;;elpy 色の設定 デフォルトは黄色でださい
(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(company-minimum-prefix-length 1)
 '(company-selection-wrap-around t))
(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(default ((t (:background "#131313" :foreground "white"))))
 '(company-scrollbar-bg ((t (:inherit company-tooltip :background "dim gray"))))
 '(company-scrollbar-fg ((t (:background "blue"))))
 '(company-tooltip ((t (:background "dim gray" :foreground "white"))))
 '(company-tooltip-annotation ((t (:inherit company-tooltip :foreground "white"))))
 '(company-tooltip-common ((t (:inherit company-tooltip :foreground "white"))))
 '(company-tooltip-common-selection ((t (:inherit company-tooltip-selection :foreground "white"))))
 '(company-tooltip-selection ((t (:inherit company-tooltip :background "blue"))))
 '(mode-line ((t (:foreground "#F8F8F2" :background "#303030" :box (:line-width 1 :color "#000000" :style released-button)))))
 '(mode-line-buffer-id ((t (:foreground nil :background nil))))
 '(mode-line-inactive ((t (:foreground "#BCBCBC" :background "#101010" :box (:line-width 1 :color "#333333"))))))

ネットで見ていると,どうもEmacspython書くには微妙という記述をちょくちょく見かける.どうなんでしょうか.

YaTex-mode

修士論文書くのにも大活躍中のYaTeX.数式のショートカット覚えてから,数式を打つのがそんなに苦ではなくなった.

auto-complete入ってるけど,使うコマンドはだいたいショートカットかsnippetがあるので,あまり必要ないかもしれない.RefTeXの使い方を全く知らないので,設定も研究室にあったものをそのまま.

;; YaTeX
(autoload 'yatex-mode "yatex" "Yet Another LaTeX mode" t)
(setq auto-mode-alist (append
  '(("\\.tex$" . yatex-mode)
    ("\\.ltx$" . yatex-mode)
    ("\\.cls$" . yatex-mode)
    ("\\.sty$" . yatex-mode)
    ("\\.clo$" . yatex-mode)
    ("\\.bbl$" . yatex-mode)) auto-mode-alist))

;;自動改行を抑制 これがないと長い文章のよくわからないところで改行される
(add-hook ' yatex-mode-hook
'(lambda () (auto-fill-mode -1)))

;; RefTexという参考文献とかを補完するモードをONにする 使い方がよくわかってないので調べる
(add-hook 'yatex-mode-hook
      #'(lambda ()
          (reftex-mode 1)
          (define-key reftex-mode-map
        (concat YaTeX-prefix ">") 'YaTeX-comment-region)
          (define-key reftex-mode-map
        (concat YaTeX-prefix "<") 'YaTeX-uncomment-region)
          ))

;;YaTeXのいろいろをAutoCompleteの補完リストに追加する
;;snippetsは"\"いらないけど,ACは"\" も打たないと補完されない 変えたほうがいい?
;; for YaTeX
(require 'auto-complete-latex)
(setq ac-l-dict-directory "~/.emacs.d/ac-l-dict/")
(add-to-list 'ac-modes 'yatex-mode)
(add-hook 'yatex-mode-hook 'ac-l-setup)
(add-to-list 'ac-modes 'foo-mode)
(add-hook 'foo-mode-hook 'ac-l-setup)

(add-hook 'yatex-mode-hook
      #'(lambda ()
          (define-key YaTeX-mode-map (kbd "C-c c") 'compile) ;;C-c c でmake できるようにする
          (auto-complete-mode t)
          ))

c++-mode

auto-complete,yasnippet,gtags,flycheckぐらいしか使っていない.昔はauto-complete-clangとか使っていたような気もする.

それぞれ以下のサイトを参考にした.

cc-mode いらないのではないかという気はしている.

(setq auto-mode-alist (append
  '(("\\.cpp$" . c++-mode)
    ("\\.hpp$" . c++-mode)
    ("\\.h$"   . c++-mode) )auto-mode-alist))


(setq gtags-prefix-key "\C-c")
(require 'gtags)
(require 'anything-gtags)
;; キーバインド
(setq gtags-mode-hook
      '(lambda ()
     (define-key gtags-mode-map "\C-cs" 'gtags-find-symbol)
     (define-key gtags-mode-map "\C-cr" 'gtags-find-rtag)
     (define-key gtags-mode-map "\C-ct" 'gtags-find-tag)
     (define-key gtags-mode-map "\C-cf" 'gtags-parse-file)))


(defvar c++-ac-sources
          '(ac-source-yasnippet
        ac-source-abbrev
        ac-source-dictionary
        ac-source-words-in-same-mode-buffers
        ;;ac-source-clang-async
        ))


;; cc-modeの自前スタイル設定
(add-hook 'c-mode-common-hook
      (lambda()
         (setq completion-mode t)
         (setq compilation-window-height 10)
         (setq c-basic-offset 2)
         ;; cc-mode内で定義されるキーバインド
         (define-key c-mode-base-map "\C-cc"      'compile)
         (define-key c-mode-base-map "\C-cg"      'gdb)
         (define-key c-mode-base-map "\C-ck" 'kill-compilation);やめる
         (define-key c-mode-base-map "\C-ce" 'next-error)   ;エラー検索
         (define-key c-mode-base-map "\"" 'electric-pair)
         (define-key c-mode-base-map "\'" 'electric-pair)
         (define-key c-mode-base-map "(" 'electric-pair)
         (define-key c-mode-base-map "[" 'electric-pair)
         (define-key c-mode-base-map "{" 'electric-pair)
         ;; 括弧や;を入力すると自動で改行
         ;(setq c-auto-newline t)
         ;; TAB はスペース 2 個ぶんで
         (setq-default tab-width 2)
         (setq indent-tabs-mode nil)
         ;; # をインデントしない
         ;;(setq c-electric-pound-behavior '(alignleft))
         (c-set-offset 'cpp-macro 0)
         (c-set-offset 'cpp-macro-cont '+)
         (setq-default ac-sources c++-ac-sources)
         (flycheck-mode t)
         (gtags-mode 1)
         ))


(add-hook 'c++-mode-common-hook
      (lambda()
        (setq completion-mode t)
        (setq compilation-window-height 10)
        (setq c-basic-offset 2)
        ;; cc-mode内で定義されるキーバインド
        (define-key c++-mode-base-map "\C-cc"      'compile)
        (define-key c++-mode-base-map "\C-cg"      'gdb)
        (define-key c++-mode-base-map "\C-ck" 'kill-compilation);やめる
        (define-key c++-mode-base-map "\C-ce" 'next-error)  ;エラー検索
        (define-key c++-mode-base-map "\"" 'electric-pair)
        (define-key c++-mode-base-map "\'" 'electric-pair)
        (define-key c++-mode-base-map "(" 'electric-pair)
        (define-key c++-mode-base-map "[" 'electric-pair)
        (define-key c++-mode-base-map "{" 'electric-pair)
        ;; TAB はスペース 2 個ぶんで
        (setq-default tab-width 2)
        (setq indent-tabs-mode nil)
        ;; # をインデントしない
        (setq c-electric-pound-behavior '(alignleft))
        (c-set-offset 'cpp-macro 0)
        (c-set-offset 'cpp-macro-cont '+)
        (setq-default ac-sources c++-ac-sources)
        (flycheck-mode t)
        (gtags-mode 1)
        ))

;; update GTAGS
(defun update-gtags (&optional prefix)
  (interactive "P")
  (let ((rootdir (gtags-get-rootpath))
    (args (if prefix "-v" "-iv")))
    (when rootdir
      (let* ((default-directory rootdir)
         (buffer (get-buffer-create "*update GTAGS*")))
    (save-excursion
      (set-buffer buffer)
      (erase-buffer)
      (let ((result (process-file "gtags" nil buffer nil args)))
        (if (= 0 result)
        (message "GTAGS successfully updated.")
          (message "update GTAGS error with exit status %d" result))))))))
(add-hook 'after-save-hook 'update-gtags)

org-mode

TODOリストとかメモとかを管理できるモード.最近入れたので,まだ使い方はよくわかっていない.この記事もこのモードで書いて,Markdownで出力している.このモードがなければ,この記事の長さは10分の1以下になっていたであろうと思う.

こちらを参考にした.

画面の切り替えをC-Tabに設定しているので,そこだけ動くようにした.

(require 'ox-qmd)

;; capture templates
(setq org-capture-templates
      '(("p" "Project Task" entry (file+headline (expand-file-name "~/project/project.org") "Inbox")
         "** TODO %?\n    %i\n    %a\n    %T")
    ("m" "memo" entry (file (expand-file-name "~/memo.org"))
         "* %?\n    %i\n    %a\n    %T")))

;; agenda
(setq org-agenda-files (list (expand-file-name "~/project")))

;; C-tab はウインドウの移動に使いたいのではずす
(add-hook 'org-mode-hook
      '(lambda ()
         (define-key org-mode-map [(control tab)] nil)))

見た目

基本的に研究室にあったinit.elからそのまま.mode-lineの色とかはいじった気がする.

;;文字サイズ
(set-face-attribute 'default nil
       :height 110)    ;; font siez
;; 日本語
(set-fontset-font
 nil 'japanese-jisx0208
;; (font-spec :family "Hiragino Mincho Pro")) ;; font
  (font-spec :family "Hiragino Kaku Gothic ProN")) ;; font

;; 半角と全角の比を1:2に
(setq face-font-rescale-alist
      '((".*Hiragino_Mincho_pro.*" . 1.2)))

;;折り返し記号の色
(set-face-foreground 'fringe "#7f7f7f")
;;折り返し記号のとこの背景色
(set-face-background 'fringe "gray12")

;; region の色
(custom-set-faces
 '(default ((t
         (:background "#131313" :foreground "white")
         )))
 '(mode-line ((t (:foreground "#F8F8F2" :background "#303030" :box (:line-width 1 :color "#000000" :style released-button)))))
 '(mode-line-inactive ((t (:foreground "#BCBCBC" :background "#101010" :box (:line-width 1 :color "#333333")))))
 '(mode-line-buffer-id ((t (:foreground nil :background nil))))
 )


;;カーソル行のハイライト
(defface hlline-face
  '((((class color)
      (background dark))
     (:background "gray13" :underline nil))
    (((class color)
      (background light))
     (:background "#00AA00":underline nil))
    (t ()))
  "*Face used by hl-line.")

(setq hl-line-face 'hlline-face)
(global-hl-line-mode)
;;; カーソルの点滅を止める
(blink-cursor-mode 0)


;;透過の設定
(when window-system
  (progn
    (setq default-frame-alist
      (append
       (list
        '(vertical-scroll-bars . nil) ;;スクロールバーはいらない
        '(alpha  . 93))
       default-frame-alist))))

;;起動時にウィンドウを最大化する
(set-frame-parameter nil 'fullscreen 'maximized)

;; リージョン指定,括弧対応に色をつける
(setq transient-mark-mode t)
(show-paren-mode)
(setq show-paren-style 'parenthesis)
(setq show-paren-delay 0.05) ;; default: 0.125

;;行,桁番号,バッファサイズの表示
(line-number-mode t)
(column-number-mode t)

;; 行末のスペースを表示
(setq-default show-trailing-whitespace t)

そのうち入れようと思っているもの

helm

anythingとなにが違うのか,ちゃんと調べてないので保留.

jedi

pythonのオムニ補完.最初入れてたけど,elpyを設定しなおしたときに消してしまった.elpyの設定,しばらくは触りたくない.

stan-mode

pythonとかRからも触れて,簡単にMCMCできるstanという言語のモード.stanについてほぼ知らないので保留.

git関連

バージョン管理の大事さは最近とても感じる.git-gutterとかその辺りをいれるつもり.

おわりに

入れたほうがいいものがあったら教えてください.

参考にさせていただいたサイト

Yosemite で Emacs-24.4 をビルドして動かす

年末emacs設定大掃除をして、これは捨てられないと思った設定書いてく - $shibayu36->blog;

anything導入のeverything ~3分で使えるanything.el~

migemoを使ってEmacsライフを快適に

whitespace-modeを使って、ファイルの保存時に行末のスペースや末尾の改行を削除する

emacsで全角スペースを見やすくする。 - プログラミングのメモ

Emacs24.3の導入とundo-tree.elの紹介

ヘルプバッファや補完バッファをポップアップで表示してくれるpopwin.elをリリースしました - Functional Emacser

Emacs as a C/C++ Editor/IDE (Part I): auto-complete, yasnippet, and auto-complete-c-headers

flymakeの設定する前に flycheckを確認しよう - Life is very short

Emacs as a Python editor and IDE (Part 1): Installing elpy mode

「Emacsのトラノマキ」連載第09回「auto-completeを使おう」(松山智大) | ありえるえりあ

Emacs - コードリーディングにとっても便利な GNU GLOBAL と gtags.el (と anything-gtags.el) をつかおう

Emacs - org-modeの使い方例