2018年2月23日金曜日

tf.scatter_ndでゼロ埋め式テンソルを一発で作る

 (10, 4) の形のテンソルを作る際に、10個のうち4個だけ該当するインデックスに対して値を埋めて、あとはゼロにしたいみたいなことがある。
numpy を使ってまっすぐやると


tensor = np.zeros((10, 4))
indices = [2, 5, 6, 9]
tensor[indices] = [1, 1, 1, 1]

# array([[ 0.,  0.,  0.,  0.],
#        [ 0.,  0.,  0.,  0.],
#        [ 1.,  1.,  1.,  1.],
#        [ 0.,  0.,  0.,  0.],
#        [ 0.,  0.,  0.,  0.],
#        [ 1.,  1.,  1.,  1.],
#        [ 1.,  1.,  1.,  1.],
#        [ 0.,  0.,  0.,  0.],
#        [ 0.,  0.,  0.,  0.],
#        [ 1.,  1.,  1.,  1.]])


tf.scatter_ndを使うと

indices = [[2], [5], [6], [9]]
updates = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
shape = [10, 4]
tensor = tf.scatter_nd(indices, updates, shape)

with tf.Session() as sess:
    print(sess.run(tensor))

# [[0 0 0 0]
# [0 0 0 0]
# [1 1 1 1]
# [0 0 0 0]
# [0 0 0 0]
# [1 1 1 1]
# [1 1 1 1]
# [0 0 0 0]
# [0 0 0 0]
# [1 1 1 1]]


一発でとか言ったんですが大概でした

2018年2月22日木曜日

np.eyeで簡単にone_hot_categoryベクトルを作る

https://docs.scipy.org/doc/numpy/reference/generated/numpy.eye.html



categories = ['background', 'Car', 'Truck', 'Pedestrian']
one_hot_category = np.eye(len(categories))

one_hot_category
# array([[ 1.,  0.,  0.,  0.],
#        [ 0.,  1.,  0.,  0.],
#        [ 0.,  0.,  1.,  0.],
#        [ 0.,  0.,  0.,  1.]])

2018年2月21日水曜日

MySQLで縦持ちテーブルを横持ちテーブルにする(サブクエリを使わない)

pythonの記事がどうしても今日かけなかったのでSQLのハックで好きなものを書きます(2017年3月の私の別の日記の記事)


日付 売上
2017-03-10 10,500
2017-03-12 5,470
2017-03-18 20,100


このようなorder_tableというテーブルがあったとして、これを


日付 2017-03-10 2017-03-12 2017-03-18
売上 10,500 5,470 20,100


こうする方法を考えます。
SQLでは縦並びの情報が日付と数字と違うとエラーなので、売上額だけ横に並べてみます。
カラム名もダイナミックに変える方法があれば教えてください。

SQLでは縦並びのデータを横並びにする方法はいくつかあるけど、

SELECT
  SUBSTRING_INDEX(GROUP_CONCAT(売上 ORDER BY 日付), ',', 1),
  SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(売上 ORDER BY 日付), ',', 2), ',', -1),
  SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(売上 ORDER BY 日付), ',', 3), ',', -1)
 FROM
  order_table


各カラム内でデータを縦持ちテーブル内の任意のデータに沿って並べ、その中のn番目のデータ(nは自由に指定できる)を表示させることが出来ます。

イメージ的にカラムがスロットルになったような感じです。
これでSQLが2次元から3次元になります。

python を始めてからインデックスの考え方に慣れたけどこれを書いた一年前は初めてで新鮮でした

2018年2月20日火曜日

学習済みのモデルの重みから一部だけ取り出す

例えばkerasを使えば、VGG16のアーキテクチャで、imagenetをベースに訓練された1000種類の物体を判別できるモデルを即使える。
https://keras.io/ja/applications/#vgg16

同じように、物体検知でも、SSDなどのアーキテクチャで、MSCOCOとかPASCAL VOCとかの物体を検知できるモデルが発表されている。
https://github.com/pierluigiferrari/ssd_keras#download-the-original-trained-model-weights
このレポジトリの方はcaffeのモデルをkerasで使えるようh5ファイルに変換して提供しているので、これをベースにお話しする。

このh5ファイルに入っているのは、ちょっと特殊なアクセス方法を取らなければいけない多次元配列であり、辞書のようにデータに名前がついていて、アクセスするには名前で呼び出さなければいけない。


import h5py

WEIGHT_PATH = 'weights.h5'
weights_file = h5py.File(WEIGHT_PATH, 'r')

weight = weights_file['conv6_3']['conv6_3']['kernel:0'].value


このようにh5pyでファイルを読みだして、それに対して名前を鍵に中身を出力していく。
このh5ファイルは、SSDのアーキテクチャで kernel:0 という鍵を最後にネットワークの重みを格納していて、バイアスに関しては同じ位置から bias:0 で呼び出せる。
この出力は普通に多次元配列で、かつこのモデルが作られた際の設定を確認すると、背景を含め81種類のMSCOCOの画像を認識検知できるモデルということがわかり、さらにMSCOCOの画像の種類順にcx, cy, w, hの四値の予測を格納していることがわかるので、
それを切り出して一種類の重みのみとして扱うことができる。
MSCOCOの画像の種類に対応するIDは、例えば止まれ標識は12番であり、
http://cocodataset.org/#explore
こちらで並んでいる順である。
なので12番を指定して重みを切り出すことができる。
また、全部で81種類のため、四値それぞれ切り出すには81個間を開けながら切りださなければいけない。


classes_of_interest = [0, 12] # background, stop sign
n_classes_source = 81
subsampling_indices = []
out_channels = weight.shape[3]
for i in range(int(out_channels/n_classes_source)):
    indices = np.array(classes_of_interest) + i * n_classes_source
    subsampling_indices.append(indices)
subsampling_indices = list(np.concatenate(subsampling_indices))

new_weight = weight[:,:,:,subsampling_indices]


そしてh5ファイルはデータに対して消去処理や作成処理、さらにflush()など特殊な処理をしないといけない。


DESTINATION_WEIGHT_PATH = 'subsampled.h5'
weights_destination_file = h5py.File(DESTINATION_WEIGHT_PATH)

del weights_destination_file['conv6_3']['conv6_3']['kernel:0']

weights_destination_file['conv6_3']['conv6_3'].create_dataset(name='kernel:0', data=new_weight)

weights_destination_file.flush()



これでこのSSDのconv6_3の層のカテゴリー判別ようの重みに関しては、背景と止まれ標識しか認知しなくなった。

2018年2月17日土曜日

多次元配列で最大値とそのインデックスをとる


li = np.random.rand(1,20).reshape(4,5)

# array([[ 0.08902982, 0.07044699, 0.80305672, 0.16506277, 0.48436258],
#        [ 0.34410367, 0.52830111, 0.29357735, 0.52468119, 0.55911187],
#        [ 0.98363013, 0.63727013, 0.3395793 , 0.94851833, 0.42974549],
#        [ 0.48017984, 0.50335503, 0.03280679, 0.82860064, 0.77796785]])


mx = li.argmax(axis=1)
l = []
for i in range(len(li)):
    l.append([[position, value] for position, value in enumerate(li[i])][mx[i]])

# l ->
# [[2, 0.80305672332582601],
# [4, 0.55911186642729027],
# [0, 0.98363012662030236],
# [3, 0.82860063949987028]]

処理のしやすさから l = np.array(l) としてここから使っていく。
物体検知のカテゴリ推論で使いました

https://docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html

2018年2月16日金曜日

Jupyter notebook で virtualenv を使う


workon my-virtualenv-name
pip install ipykernel
python -m ipykernel install --user --name=my-virtualenv-name

jupyter notebook を再起動したらメニューのKernel → change kernels で仮想環境の名前が出るのでスイッチ

https://help.pythonanywhere.com/pages/IPythonNotebookVirtualenvs/

2018年2月15日木曜日

pandas.DataFrameの中の値をfloatからintにする


In [1]: import pandas as pd

In [2]: csv = pd.read_csv('sample.csv',index_col=0)

In [3]: csv
Out[3]:
         class_id         image_name   xmax   xmin    ymax   ymin
0             Cat       IMG_0001.jpg  850.0  752.0   531.0  459.0
1             Cat       IMG_0002.jpg  508.0  193.0   481.0  260.0
2             Cat       IMG_0003.jpg  542.0  358.0   381.0  266.0
...           ...                ...    ...    ...     ...    ...
145597        Dog       IMG_9998.jpg  569.0  237.0   754.0  423.0
145598        Dog       IMG_9999.jpg  582.0  267.0   831.0  495.0

[145740 rows x 6 columns]

In [4]: csv[['xmin','ymin','xmax','ymax']] = csv[['xmin','ymin','xmax','ymax']].values.astype(int)

In [5]: csv
Out[5]:
         class_id         image_name  xmax  xmin  ymax  ymin
0             Cat       IMG_0001.jpg   850   752   531   459
1             Cat       IMG_0002.jpg   508   193   481   260
2             Cat       IMG_0003.jpg   542   358   381   266
...           ...                ...   ...   ...   ...   ...
145597        Dog       IMG_9998.jpg   569   237   754   423
145598        Dog       IMG_9999.jpg   582   267   831   495

[145740 rows x 6 columns]



・Loopするときとかfloatだと回せなかったりする

2018年2月13日火曜日

JPG画像のexif形式対応

画像を処理する前に、iPhoneなどで撮影した画像はExifというデータを元に上下左右のオリエンテーションを直しておかないと、PILとかで処理するときに勝手にひっくり返ったりしてしまう。
学習中に間違ったデータを与えかねないので、一括で直しておく。

from PIL import Image
import os

img_path = 'img/'

def rota(i):
    image = Image.open(i)
    if hasattr(image, '_getexif'):
        orientation = 0x0112
        exif = image._getexif()
        if exif is not None:
            orientation = exif[orientation]
            rotations = {
                3: Image.ROTATE_180,
                6: Image.ROTATE_270,
                8: Image.ROTATE_90
            }
            if orientation in rotations:
                image = image.transpose(rotations[orientation])
    image.save(i)

for i in os.listdir(img_path):
    rota(img_path+i)

4万枚画像があるので結構時間がかかる