読者です 読者をやめる 読者になる 読者になる

ytbilly3636’s 研究備忘録

機械学習,Python,ガンダムなど

Caffe2を使ってみたその2「MNISTの学習」

こんにちは.

今回の記事ではCaffe2を使ってLeNetを実装し,MNISTを学習させてみます. 方法については公式のチュートリアルを参照(というよりそのままです)しました.

MNIST - Create a CNN from Scratch | Caffe2

環境

項目 詳細
OS Ubuntu 14.04
GPU GTX970

データセットの用意

Caffe2ではCaffeの時と同様, 学習画像のデータベースを用意しておき, データベースから読み込んでいきます.

下記サイトから4つのファイルをダウンロードします.

http://yann.lucan.com/exdb/mnist/

  • train-images-idx3-ubyte.gz
  • train-labels-idx1-ubyte.gz
  • t10k-images-idx3-ubyte.gz
  • t10k-labels-idx1-ubyte.gz

ダウンロードしたファイルを解凍し,Caffe2のコマンドによってデータベース化していきます.

$ gunzip train-images-idx3-ubyte.gz
$ gunzip train-labels-idx1-ubyte.gz
$ gunzip t10k-images-idx3-ubyte.gz
$ gunzip t10k-labels-idx1-ubyte.gz

# 解凍したファイルはホームフォルダに置いたとする.
$ cd caffe2
$ mkdir -p caffe2/python/tutorials/tutorial_data/mnist/
$ ./build/caffe2/binaries/make_mnist_db --channel_first --db leveldb --image_file ~/train-images-idx3-ubyte --label_file ~/train-labels-idx1-ubyte --output_file caffe2/python/tutorials/tutorial_data/mnist/mnist-train-nchw-lebeldb
$ ./build/caffe2/binaries/make_mnist_db --channel_first --db leveldb --image_file ~/t10k-images-idx3-ubyte --label_file ~/t10k-labels-idx1-ubyte --output_file caffe2/python/tutorials/tutorial_data/mnist/mnist-test-nchw-lebeldb

スクリプトの用意

Caffe2ではCaffeとは異なりprototxtを用意せずとも, Pythonスクリプト1つで学習の記述ができるようです.

# -*- coding: utf-8 -*-

# 読み込むモジュール
import numpy as np, os, shutil
from   matplotlib import pyplot
from   caffe2.python import core, cnn, net_drawer, workspace, visualize


# 初期化
core.GlobalInit(['caffe2', '--caffe2_log_level=0'])
caffe2_root = '~/caffe2'


# パスのオプション
# 適宜書き換えてください!
data_folder = '/home/username/caffe2/caffe2/python/tutorials/tutorial_data/mnist'


# ネットワークの入力
def AddInput(model, batch_size, db, db_type):
    # DBからデータ(画像、ラベル)の読み込み
    data_uint8, label = model.TensorProtosDBInput(
        [], ['data_uint8', 'label'], batch_size=batch_size, db=db, db_type=db_type
    )
    
    # データ型の変換: uint8 -> float
    data = model.Cast(data_uint8, "data", to=core.DataType.FLOAT)
    
    # スケールの変更: [0 255] -> [0 1]
    data = model.Scale(data, data, scale=float(1./256))
    
    # 逆伝播時に微分の計算をしない(入力だから)
    data = model.StopGradient(data, data)
    
    return data, label
    
    
# ネットワークの本体
def AddLeNetModel(model, data):
    # conv -> pool -> conv -> pool -> Relu(fc) -> SoftMax(fc)
    
    # NxCxHxW: 1x1x28x28 -> 20x1x24x24 -> 20x1x12x12
    conv1 = model.Conv(data, 'conv1', 1, 20, 5)
    pool1 = model.MaxPool(conv1, 'pool1', kernel=2, stride=2)
    
    # NxCxHxW: 20x1x12x12 -> 50x1x8x8 -> 50x1x4x4
    conv2 = model.Conv(pool1, 'conv2', 20, 50, 5)
    pool2 = model.MaxPool(conv2, 'pool2', kernel=2, stride=2)
    
    # NxCxHxW: 800x1x1x1 -> 500x1x1x1
    fc3   = model.FC(pool2, 'fc3', 50 * 4 * 4, 500)
    fc3   = model.Relu(fc3, fc3)
    
    # NxCxHxW: 500x1x1x1 -> 10x1x1x1
    pred  = model.FC(fc3, 'pred', 500, 10)
    softmax = model.Softmax(pred, 'softmax')
    
    return softmax
    
    
# ネットワークの正解率
def AddAccuracy(model, softmax, label):
    accuracy = model.Accuracy([softmax, label], 'accuracy')
    return accuracy
    
    
# 学習
def AddTrainingOperators(model, softmax, label):
    # クロスエントロピーの計算
    xent = model.LabelCrossEntropy([softmax, label], 'xent')
    
    # クロスエントロピーの平均損失の計算
    loss = model.AveragedLoss(xent, 'loss')
    
    # 正解率の計算
    AddAccuracy(model, softmax, label)
    
    # 損失関数の勾配を計算
    model.AddGradientOperators([loss])
    
    # 学習率の設定(lr = base_lr * (t ^ gamma))
    ITER = model.Iter('iter')
    LR = model.LearningRate(ITER, 'LR', base_lr=-0.1, policy='step', stepsize=1, gamma=0.999)
    
    # 更新に使う定数
    ONE = model.param_init_net.ConstantFill([], 'ONE', shape=[1], value=1.0)
    
    # 全パラメータにおいて更新
    # param = param + param_grad * LR(このような更新式の定義なので,学習率は負値になっている.)
    for param in model.params:
        param_grad = model.param_to_grad[param]
        model.WeightedSum([param, ONE, param_grad, LR], param)
        
    # 20イテレーション毎にチェックポイントを作成
    model.Checkpoint([ITER] + model.params, [], db='mnist_lenet_checkpoint_%05d.leveldb', db_type='leveldb', every=20)
    
    
# ログなどの出力用
def AddBookkeepingOperators(model):
    model.Print('accuracy', [], to_file=1)
    model.Print('loss', [], to_file=1)
    for param in model.params:
        model.Summarize(param, [], to_file=1)
        model.Summarize(model.param_to_grad[param], [], to_file=1)
        
        
# ここからネットワークの準備

# CNNのモデル型を学習用として用意
train_model = cnn.CNNModelHelper(order='NCHW', name='mnist_train')
# データセットの読み込み
data, label = AddInput(train_model, batch_size=64, db=os.path.join(data_folder, 'mnist-train-nchw-leveldb'), db_type='leveldb')
# ネットワークの設定
softmax = AddLeNetModel(train_model, data)
# 学習の設定
AddTrainingOperators(train_model, softmax, label)
# ログの設定
AddBookkeepingOperators(train_model)

# CNNのモデル型をテスト用として用意
test_model = cnn.CNNModelHelper(order='NCHW', name='mnist_test', init_params=False)
# データセットの読み込み
data, label = AddInput(test_model, batch_size=100, db=os.path.join(data_folder, 'mnist-test-nchw-leveldb'), db_type='leveldb')
# ネットワークの設定
softmax = AddLeNetModel(test_model, data)
# 正解率の設定
AddAccuracy(test_model, softmax, label)

# デプロイを用意(何に使うのだろう)
deploy_model = cnn.CNNModelHelper(order='NCHW', name='mnist_deploy', init_params=False)
AddLeNetModel(deploy_model, 'data')


# ここから学習の処理

# ネットワークの初期化
workspace.RunNetOnce(train_model.param_init_net)
workspace.CreateNet(train_model.net)

# pyplot用
total_iters = 200
accuracy = np.zeros(total_iters)
loss     = np.zeros(total_iters)

# 学習
for i in xrange(total_iters):
    workspace.RunNet(train_model.net.Proto().name)
    
    # グラフ描画
    accuracy[i] = workspace.FetchBlob('accuracy')
    loss[i]     = workspace.FetchBlob('loss')
    pyplot.clf()
    pyplot.plot(accuracy, 'r')
    pyplot.plot(loss, 'b')
    pyplot.legend(('loss', 'accuracy'), loc='upper right')
    pyplot.pause(.01)

    
# ここからテストの処理

# ネットワークの初期化
workspace.RunNetOnce(test_model.param_init_net)
workspace.CreateNet(test_model.net)

# pyplot用
test_accuracy = np.zeros(100)

# テスト
for i in range(100):
    workspace.RunNet(test_model.net.Proto().name)
    test_accuracy[i] = workspace.FetchBlob('accuracy')

# グラフ描画
pyplot.plot(test_accuracy, 'r')
pyplot.title('Acuracy over test batches.')
print('test_accuracy: %f' % test_accuracy.mean())

スクリプトの前半部分で関数を定義して, 後半部分でそれらの関数を使ってるイメージです. スクリプトを「train_mnist.py」という名前で保存して,下記コマンドで実行できます.

$ python train_mnist.py

使ってみて思ったこと

Caffeのprototxtよりもネットワークの記述がスッキリしたように思えます. Caffe2独自の書き方に慣れさえすれば,Pythonユーザーにとってはそれほど難しいものではないように思えます. ネットワークが小さいせいか,Caffe2の新機能のおかげなのかはわかりませんが学習はサクサク動きます.

NumPyを使うと幸せになれるかも2: CuPyを使う

こんにちは.

今回の記事はシリーズ第2段です. 厳密に言えばNumPyの話ではなく, Chainerに含まれている行列演算モジュールCuPyの話です.

CuPy解説

CuPyとはNumPyの機能をCUDA上で動かせるというもの. 使い方はnumpyをcupyで置き換えれば良いとのこと. そんなに簡単に使えるのなら使わない手はないと思い, NumPyで実装したあるコードをCuPyで置き換えてみましたが……

遅い……

ということで検証してみました.

環境

項目 詳細
OS Ubuntu 14.04
CPU i5-3570
GPU GTX970

コード

numpyとcuda.cupyの速度を行列積で比較してみました. cuda.cupyは何もしないバージョンとto_gpu()をするバージョンの2つを比較しました.

# -*- coding: utf-8 -*-

import numpy as np
import chainer
import chainer.cuda.cupy as cp
import time

# MNISTを読み込み
# 784次元のベクトル
train, test = chainer.datasets.get_mnist()

s1 = time.time()
np_ar1 = np.array(train[0][0])
np_ar2 = np.array(train[1][0])
a1 = np_ar1.dot(np_ar2.T)

s2 = time.time()
cp_ar1 = cp.array(train[0][0])
cp_ar2 = cp.array(train[1][0])
a2 = cp_ar1.dot(cp_ar2.T)

s3 = time.time()
cp2_ar1 = chainer.cuda.to_gpu(cp.array(train[0][0]))
cp2_ar2 = chainer.cuda.to_gpu(cp.array(train[1][0]))
a3 = cp2_ar1.dot(cp2_ar2.T)

s4 = time.time()
print 'np:',s2-s1
print 'cp:',s3-s2
print 'cp2:',s4-s3
np: 5.57899475098e-05
cp: 0.182370185852
cp2: 0.000236988067627

やはりNumPyが一番速かったです. CuPyが負けてしまったのはGPUへのメモリ転送がネックになっているのだと思います.

あと,numpyをcupyにただ置き換えるだけではなく, to_gpu()を書いてあげたほうがいいみたいです.

ちなみに大きめの行列だとCuPyに軍配が上がります.

s1 = time.time()
a = np.ones((1000, 1000),dtype=np.float32)
a.dot(a.T)

s2 = time.time()
b = cp.ones((1000, 1000),dtype=cp.float32)
b.dot(b.T)

s3 = time.time()
c = chainer.cuda.to_gpu(cp.ones((1000, 1000),dtype=cp.float32))
c.dot(c.T)

s4 = time.time()

print 'np:',s2-s1
print 'cp:',s3-s2
print 'cp2:',s4-s3
np: 0.0469000339508
cp: 0.281046152115
cp2: 0.000430822372437

※とある事情でコードを手打ちで書いたのでミスがあるかもしれません,悪しからず.

負の数の引き算ってなんだっけ

こんにちは.

先日研究室で数学ってよくわからないという話をしていまして, その中に負の数の引き算ってなんだっけという話題になりました.

おそらく大多数の人が以下のように頭の中で変換して計算していると思います.

{ \displaystyle
a - (-b) = a + b
}

この変換式を使えば高校入試や大学入試の数学の問題は解けるので, これまで生きてきて問題はなかったのですが, もし負の数に初めて出会った小学生や中学生から

「負の数の引き算ってなに?」

と聞かれてもうまく答えられる自信がありません.

今回の記事では, 小学生や中学生に負の数の引き算を説明できる大人になれることを目指して, 自分なりの解釈を考えてみようと思います.

正の数の加減算の例え話

幼稚園児や小学生に加減算の説明をする場合, 実際のモノを使って例え話をするのがオーソドックスな教え方だと思います.

足し算の例

たかしくんの家の冷蔵庫にじゃがいもが2個入っています. たかしくんは学校の帰り道にじゃがいも畑に寄り道をして, そこから3個じゃがいもをくすねて家の冷蔵庫に入れました. 冷蔵庫に入っているじゃがいもの数はいくつでしょう.

{ \displaystyle
2 + 3 = 5
}

引き算の例

たかしくんの家の冷蔵庫にじゃがいもが5個入っています. たかしくんは友達の家に遊びに行くとき, 手土産としてじゃがいもを4個持っていきました. 冷蔵庫に残っているじゃがいもの数はいくつでしょう.

{ \displaystyle
5 - 4 = 1
}

たかしくんのクレイジーな行動が気になりますが, このような例題を使って教えることができます.

しかし,負の数が絡んでくると例えるのが難しくなります. じゃがいもを1個2個と数えることができても,-1個-2個と数えることはできないからです.

提案手法

モノを負の数で表現するのは難しいので, モノに対する行動を正の数・負の数で表現して, 行動したことによる結果を足し算や引き算で表現します.

じゃがいもの例だと以下のように定義します.

  • 正の数:じゃがいもを冷蔵庫に入れるという行動
  • 負の数:じゃがいもを冷蔵庫から出すという行動
  • 足し算:行動した後の状態を求める演算子
  • 引き算:行動する前の状態を求める演算子

負の数の足し算の例

たかしくんの家の冷蔵庫にじゃがいもが5個入っています. たかしくんは友達の家に遊びに行くとき, 手土産としてじゃがいもを4個持っていきました. 冷蔵庫に残っているじゃがいもの数はいくつでしょう.

  • じゃがいもを冷蔵庫から4個出すという行動:-4
  • 行動した後の状態を求めたい:+

{ \displaystyle
5 + (- 4) = 1
}

負の数の引き算の例

たかしくんは友達の家に遊びに行くとき, 手土産としてじゃがいもを4個持っていきました. 冷蔵庫に残っているじゃがいもは1個でした. 元々冷蔵庫にじゃがいもはいくつだったでしょうか.

  • じゃがいもを冷蔵庫から4個出すという行動:-4
  • 行動する前の状態を求めたい:-

{ \displaystyle
1 - (- 4) = 5
}

Caffe2を使ってみたその1「インストール」

こんにちは.

Caffe2というDeep Learning用のフレームワークが出たようです. まだドキュメントをちゃんと読んでいないのでその良し悪しはわかりませんがとりあえず試してみることとします.

caffe2.ai

環境

項目 詳細
OS Ubuntu 14.04
GPU GTX970

ちなみに2じゃないほうのCaffeをインストール済みです.

手順

手順といっても,Caffe2の公式ドキュメントの方法に従うだけです. ソースからのビルドを試してみます.

Install | Caffe2

依存パッケージのインストール

# 私の場合Caffeを入れたときに以下のパッケージは導入済みなので飛ばしました
$ sudo apt-get update
$ sudo apt-get install -y --no-install-recommends build-essential cmake git libgoogle-glog-dev libprotobuf-dev protobuf-compiler python-dev python-pip
$ sudo pip install numpy protobuf

CUDAとcuDNNのインストール

# 公式ドキュメントにはグラフィックドライバーを更新してねと書いていました.
# そのあたりは各自で適宜行って下さい.

# 当初CUDA7.5を導入済みだったのでここを飛ばしましたが,後述のエラーが出ました.8.0が良いのでしょうか.
$ sudo apt-get update && sudo apt-get install wget -y --no-install-recommends
$ wget "http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1404/x86_64/cuda-repo-ubuntu1404_8.0.61-1_amd64.deb"
$ sudo dpkg -i cuda-repo-ubuntu1404_8.0.61-1_amd64.deb
$ sudo apt-get update
$ sudo apt-get install cuda

$ CUDNN_URL="http://developer.download.nvidia.com/compute/redist/cudnn/v5.1/cudnn-8.0-linux-x64-v5.1.tgz"
$ wget ${CUDNN_URL}
$ sudo tar -xzf cudnn-8.0-linux-x64-v5.1.tgz -C /usr/local
$ rm cudnn-8.0-linux-x64-v5.1.tgz && sudo ldconfig

残りの依存パッケージのインストール

$ sudo apt-get install -y --no-install-recommends libgflags2
$ sudo apt-get install -y --no-install-recommends libgtest-dev libiomp-dev libleveldb-dev liblmdb-dev libopencv-dev libopenmpi-dev libsnappy-dev openmpi-bin openmpi-doc python-pydot
$ sudo pip install flask graphviz hypothesis jupyter matplotlib pydot python-nvd3 pyyaml requests scikit-image scipy setuptools tornado

ソースのクローンとビルド

# ホームディレクトリにクローンしました.場所については適宜読み替えて行って下さい.
# makeでエラーが出たらmake cleanしてやり直し
$ git clone --recursive https://github.com/caffe2/caffe2.git && cd caffe2
$ make && cd build && sudo make install
$ python -c 'from caffe2.python import core' 2>/dev/null && echo "Success" || echo "Failure"

環境変数の設定

「.bashrc」の末尾にパスを追記します.

$ gedit ~/.bashrc &
# .bashrcの末尾に追記
# 2行目のパスは各自読み替えて下さい.
export PYTHONPATH=/usr/local:$PYTHONPATH
export PYTHONPATH=$PYTHONPATH:/home/username/caffe2/build
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

動作確認

動くかな? ワクワク……

$ python -m caffe2.python.operator_test.relu_op_test
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([[ 1.        ,  0.58414382, -0.07458938],
       [-0.92077535,  1.        , -0.31444678]], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'CUDNN')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([-0.        , -0.77440119], dtype=float32), gc=, dc=[, device_type: 1], engine=u'')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([ 0.,  0.], dtype=float32), gc=, dc=[, device_type: 1], engine=u'')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([[[ 0.12101772,  1.        ,  0.98191798, -0.75570065],
        [ 0.        ,  1.        , -0.66422468, -0.87821025],
        [-0.76647252,  0.19047344, -0.32112685, -0.        ],
        [ 0.6859479 , -0.47230539, -0.79940522, -0.24955569]],

       [[ 1.        , -0.36730164, -0.78970796,  0.46469435],
        [-0.95587206, -0.44132483,  0.        , -0.28097823],
        [-0.08904378,  0.38005501, -0.61275059,  0.79579282],
        [-0.02774031,  0.50639385, -0.91766995, -0.33312017]]], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'CUDNN')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([[-0.56960249, -0.84849566, -0.        ]], dtype=float32), gc=, dc=[, device_type: 1], engine=u'CUDNN')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([ -0.00000000e+00,  -6.69241369e-01,   3.31036976e-28], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'CUDNN')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([ 0.97428375, -0.07135547], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([-0., -0.], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([-0.        , -0.94964397], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'CUDNN')
Trying example: test_relu(self=<__main__.TestRelu testMethod=test_relu>, X=array([ 0.41938838,  0.64380383, -0.30215767], dtype=float32), gc=device_type: 1, dc=[, device_type: 1], engine=u'')
.
----------------------------------------------------------------------
Ran 1 test in 0.946s

OK

OKらしいです.

エラーの対処

make中に以下のエラーに遭遇しました

[ 59%] Built target Caffe2_CPU
make[3]: ディレクトリ `/home/username/caffe2/build' に入ります
[ 59%] Building NVCC (Device) object caffe2/CMakeFiles/Caffe2_GPU.dir/utils/./Caffe2_GPU_generated_math_gpu.cu.o
/home/username/caffe2/caffe2/utils/conversions.h(137): warning: variable "r" was declared but never referenced

/home/username/caffe2/caffe2/utils/math_gpu.cu(136): error: identifier "CUDA_R_16F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(312): error: identifier "CUDA_R_16F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(496): error: identifier "CUDA_R_16F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(496): error: identifier "CUDA_R_32F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(496): error: identifier "cublasDotEx" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(740): error: identifier "CUDA_R_16F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(740): error: identifier "CUDA_R_32F" is undefined

/home/username/caffe2/caffe2/utils/math_gpu.cu(740): error: identifier "cublasAxpyEx" is undefined

8 errors detected in the compilation of "/tmp/tmpxft_000079b6_00000000-7_math_gpu.cpp1.ii".
CMake Error at Caffe2_GPU_generated_math_gpu.cu.o.cmake:260 (message):
  Error generating file
  /home/username/caffe2/build/caffe2/CMakeFiles/Caffe2_GPU.dir/utils/./Caffe2_GPU_generated_math_gpu.cu.o


make[3]: *** [caffe2/CMakeFiles/Caffe2_GPU.dir/utils/./Caffe2_GPU_generated_math_gpu.cu.o] エラー 1
make[3]: ディレクトリ `/home/username/caffe2/build' から出ます
make[2]: *** [caffe2/CMakeFiles/Caffe2_GPU.dir/all] エラー 2
make[2]: ディレクトリ `/home/username/caffe2/build' から出ます
make[1]: *** [all] エラー 2
make[1]: ディレクトリ `/home/username/caffe2/build' から出ます
make: *** [all] エラー 2

CUDA周りのエラーですかね? CUDAを入れ直してから「.bashrc」の末尾にパスを追加してやりました.

$ gedit ~/.bashrc &
# .bashrcの末尾に追記
export PATH=/usr/local/cuda-8.0/bin:${PATH}
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64:${LD_LIBRARY_PATH}

再起動してから再びmakeをするとうまく行きました.

ガンダムエクシアダークマター3 つや消し

こんにちは.

すっかり報告を忘れていたエクシアダークマターの組み立て記録です. 今回は初めてつや消しに挑戦をしてみました. つや消しには「Mr.トップコートつや消し」を使いました.

Amazon | Mr.トップコートスプレー つや消し B503 | 溶剤・うすめ液 通販

パーツのマスキング

スプレーの粒子がかからない部分が出ないようにパーツをある程度分解します. また,パーツの接合部やクリアパーツなどつや消しをしたくない部分にはマスキングテープを張っておきます.

f:id:ytbilly3636:20170409125306j:plain

エクシアの関節部分のマスキングがめんどくさかったです.

f:id:ytbilly3636:20170409125400j:plain

ちなみにに明るい色のパーツにはスミ入れもしています.

f:id:ytbilly3636:20170409125722j:plain

スプレー吹きかけ

各パーツをクリップで留めてムラ無くスプレーを吹きかけます. ベースはダンボールを切リ貼りして手作りしたものです.

f:id:ytbilly3636:20170409125846j:plain

1日乾かしたらツヤが消えてマット感が出ていました. 写真ではわかりづらいと思いますが, スプレーを吹きかけた部分はすりガラスのような質感になり, マスキングをしていたクリアパーツはツルツルのままで仕上げることが出来ました.

f:id:ytbilly3636:20170409130130j:plain

またクリップのツヤも消えていました. 写真の右側がつや消しスプレーを吹きかけたクリップです. メタリックなパーツにはつや消しスプレーを吹きかけないほうが良いかもしれません.

f:id:ytbilly3636:20170409130459j:plain

CIFAR-100のスーパークラスを調べた

こんにちは.
このブログを始めた当初は週一くらいのペースで更新するつもりでしたが, 先週はうっかり更新を忘れていました. まぁ気楽に行きましょう.

ChainerからCIFAR-100を読み込む

ChainerにはMNISTやCIFAR-10,CIFAR-100などといったよく使われているデータセットを読み込む機能が実装されています.

import chainer
from chainer import datasets

train, test = datasets.get_cifar100()

中身を覗いてみます.画像の出力にはOpenCVを使いました.

#さっきの続き

import cv2
import numpy as np

image = train[0][0]    # trainの0番目の画像
label = train[0][1]    # trainの0番目のラベル

print label

# 画素が0~1に正規化されているので255倍
cv2.imwrite("image.jpg", np.rollaxis(image, 0, 3) * 255)

以下の画像が出力されました.
f:id:ytbilly3636:20170408223036j:plain
なんでしょうか.牛ですかね.ラベルは19番でした.

CIFAR-100のスーパークラス

CIFAR-100は100クラスの画像データセットになっていますが, これは20のスーパークラスに分類されており, 各スーパークラスは5クラスが内包されています.

CIFAR-10 and CIFAR-100 datasets

ところがどっこい, Chainerのget_cifar100()から取得したデータにはこのスーパークラスの情報が(多分)含まれていません. 私はデータがスーパークラスに分かれた状態のデータセットが欲しかったので, なんとかスーパークラスにに振り分ける方法はないものか,と調べてみたものの結局見つからず…….

手動でクラスを調べてやりましたよ

OpenCVで画像を可視化して,0番がリンゴで1番が観賞魚で……と一枚一枚確認していきました. 以下に私の努力の成果(笑)を示します.間違いがあったらごめんなさい.

Superclass Classes
aquatic mammals beaver(4), dolphin(30), otter(55), seal(72), whale(95)
fish aquarium fish(1), flatfish(32), ray(67), shark(73), trout(91)
flowers orchids(54), poppies(62), roses(70), sunflowers(82), tulips(92)
food containers bottles(9), bowls(10), cans(16), cups(28), plates(61)
fruit and vegetables apples(0), mushrooms(51), oranges(53), pears(57), sweet peppers(83)
household electrical devices clock(22), computer keyboard(39), lamp(40), telephone(86), television(97)
household furniture bed(5), chair(20), couch(25), table(84), wardrobe(94)
insects bee(6), beetle(7), butterfly(14), caterpillar(18), cockroach(24)
large carnivores bear(3), leopard(42), lion(43), tiger(88), wolf(97)
large man-made outdoor things bridge(12), castle(17), house(37), road(68), skyscraper(76)
large natural outdoor scenes cloud(23), forest(33), mountain(49), plain(60), sea(71)
large omnivores and herbivores camel(15), cattle(19), chimpanzee(21), elephant(31), kangaroo(38)
medium-sized mammals fox(34), porcupine(63), possum(64), raccoon(66), skunk(75)
non-insect invertebrates crab(26), lobster(45), snail(77), spider(79), worm(99)
people baby(2), boy(11), girl(35), man(46), woman(98)
reptiles crocodile(27), dinosaur(29), lizard(44), snake(78), turtle(93)
small mammals hamster(36), mouse(50), rabbit(65), shrew(74), squirrel(80)
trees maple(47), oak(52), palm(56), pine(59), willow(96)
vehicles 1 bicycle(8), bus(13), motorcycle(48), pickup truck(58), train(90)
vehicles 2 lawn-mower(41), rocket(69), streetcar(81), tank(85), tractor(89)

※かっこ内が0~99のラベル番号になります.

Pythonによる画像の二値化

こんにちは.久しぶりの技術系ネタです.

先日研究室でこのような会話がありました.

メンバ「Pythonで画像の二値化ってどうやってる?」
わたし「OpenCVのメソッドを使ってます」
メンバ「そっか……OpenCV入れないといけないのか」
わたし「あ,ちょっと待ってください!」
(しばらく経って)
わたし「OpenCV無しでもできました」
メンバ「ありがとう,お礼にお菓子をあげよう」

ということで,お菓子をもらえたのでここで共有しておきます.

実装したコード

実行にはNumpyとOpenCVの導入が必要です.

$ sudo apt-get install python-numpy
$ sudo apt-get install libopencv-dev python-opencv

下記のコードでは, 画像(image_example.jpg)をグレースケールモードで読み込み, OpenCVの二値化処理とOpenCVを使わない二値化処理を行っています. 閾値は適当に127としました.

#!/usr/bin/python
#-*- coding:utf-8 -*-

import numpy as np
import cv2

# original image (gray scale image)
org_img = cv2.imread('image_example.jpg', 0)

# preference
THRESHOLD = 127
MAXVALUE  = 255

# binarization using opencv
_, bin_cv2 = cv2.threshold(org_img, THRESHOLD, MAXVALUE, cv2.THRESH_BINARY)

# binarization not using opencv
bin_npy = np.zeros(org_img.shape, org_img.dtype)
bin_npy[np.where(org_img > THRESHOLD)] = MAXVALUE

# check
cv2.imwrite('original.png',   org_img)
cv2.imwrite('binary_cv2.png', bin_cv2)
cv2.imwrite('binary_npy.png', bin_npy)

結果

入力画像(image_example.jpg)には昨年タイに行ったときに撮影した遺跡の写真を使いました. f:id:ytbilly3636:20170325234024j:plain

original.png f:id:ytbilly3636:20170325234130p:plain

binary_cv2.png f:id:ytbilly3636:20170325234158p:plain

binary_npy.png f:id:ytbilly3636:20170325234216p:plain

簡単な解説

OpenCVの二値化はthresholdというメソッドに画像を渡すだけです. このメソッドの返り値は2つありますが,処理後の画像は2番目になるようです. 引数の詳細については下記リンクを参照してください.

Miscellaneous Image Transformations — OpenCV 2.4.13.2 documentation

OpenCVを使わない場合は,numpy.whereというメソッドを活用します. このメソッドは引数の条件を満たすインデックスを返却します. つまり上記の例ではorg_imgの中から閾値よりも大きい要素のインデックスを返却しています. そのインデックスの部分を255,それ以外を0にすることで二値化画像を生成しています.