stMind

about Tech, Computer vision and Machine learning

Tensorflowでロゴ画像を分類する

UdacityのDeep LearningコースでTensorflowを試してみたものの、いまいちしっくりこない感じがありました。こういうのは、自分で何かしら作って試してみるのが一番ということで、ブランドロゴ画像の分類をしてみました。

なお、ここで書いたコードは

GitHub - satojkovic/DeepLogo: A brand logo recognition system using deep convolutional neural networks.

にあります。python3系ならば動くと思います。scipy、scikit-learn、Pillow(PIL)、numpyあたりを使ってますので、必要に応じてインストールしてください。

タスクとデータセット

データセットflikcr_logos_27_datasetを使います。AdidasAppleBMWなどなどの27種類のロゴ分類タスクです。

f:id:satojkovic:20160821151857p:plain

ロゴ識別CNN

今回作るCNNは、ナンバープレート認識をTensorflowでやってるブログを大いに参考にしました。発想としては単純で、ロゴ画像って文字や数字、模様から出来ていて、一方でナンバープレートは数字と文字で構成されていて、特徴として非常に似ているので、CNNのネットワーク構成がそのまま使えるかなと思った、とそれだけです。 違うのは、ナンバープレートはグレースケールなのに対して、ロゴ画像は色も特徴の一つになりそうなので、カラー画像で3チャンネルの入力になるところと、画像サイズが64 x 32になっているところです。 オリジナルのネットワーク図を書き換えると、次のような構成。

f:id:satojkovic:20160821161807p:plain

コードとしては、train_deep_logo_cnn.pyとtest_deep_logo_cnn.pyにあるmodelが該当するところです。重みとバイアス項は、mainの所で記述してmodelメソッドに渡しています。

def model(data, w_conv1, b_conv1, w_conv2, b_conv2, w_conv3, b_conv3, w_fc1,
          b_fc1, w_fc2, b_fc2):
    # First layer
    h_conv1 = tf.nn.relu(
        tf.nn.conv2d(
            data, w_conv1, [1, 1, 1, 1], padding='SAME') + b_conv1)
    h_pool1 = tf.nn.max_pool(
        h_conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # Second layer
    h_conv2 = tf.nn.relu(
        tf.nn.conv2d(
            h_pool1, w_conv2, [1, 1, 1, 1], padding='SAME') + b_conv2)
    h_pool2 = tf.nn.max_pool(
        h_conv2, ksize=[1, 1, 2, 1], strides=[1, 1, 2, 1], padding='SAME')

    # Third layer
    h_conv3 = tf.nn.relu(
        tf.nn.conv2d(
            h_pool2, w_conv3, [1, 1, 1, 1], padding='SAME') + b_conv3)
    h_pool3 = tf.nn.max_pool(
        h_conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

    # Fully connected layer
    conv_layer_flat = tf.reshape(h_pool3, [-1, 16 * 4 * 128])
    h_fc1 = tf.nn.relu(tf.matmul(conv_layer_flat, w_fc1) + b_fc1)

    # Output layer
    out = tf.matmul(h_fc1, w_fc2) + b_fc2

    return out

学習画像の生成

学習画像は、データセットアノテーションファイルを使ってロゴ部分だけを切り出し(crop)、切り出した画像についてデータオーグメンテーション(シフト、回転、拡大縮小)を適用して、全体で21万枚程度に増やしました。27クラスあるので、クラス毎に8000枚くらいになる計算ですね。実際には、結構偏りがありますが。

その後、train75%、test25%の割合にして、フォルダに画像を分けるようにしています。ここまでを、crop_and_aug.pyで実行します。

def main():
    annot_train = np.loadtxt(os.path.join(TRAIN_DIR, ANNOT_FILE), dtype='a')
    print('train_annotation: %d, %d ' % (annot_train.shape))

    # cropping and data augmentation
    crop_and_aug(annot_train)

    # train_test_split
    do_train_test_split()

さらに加えて、Tensorflowを使ってCNNを学習するためのデータ形式にするのに、gen_train_valid_test.pyを使ってpickleファイルをクラス毎に生成しています。ここまでやって、CNNを学習する準備が完了になります。学習画像の例(回転、シフトしているのが分かりにくいな...)。

f:id:satojkovic:20160821172356j:plain

学習とロゴ分類テスト

学習はtrain_deep_logo_cnn.pyで実行します。大体、3000ステップくらいでロスが小さくなって、学習が収束しているようです。学習データに対するAccuracyは90%、テストデータに対するAccuracyもあまり変わらず89%程度になっていました。また、学習が終わったモデルでテストしてみると、だいたい正しく分類してくれているようです。

$ python test_deep_logo_cnn.py 
Test image: flickr_logos_27_dataset/flickr_logos_27_dataset_cropped_augmented_images/Google/test/272645705_Google_6_2_r-13.jpg
Model restored
Class name: Google

Test image: flickr_logos_27_dataset/flickr_logos_27_dataset_cropped_augmented_images/Nbc/test/3570522135_Nbc_6_2_r0.jpg
Model restored
Class name: Nbc

Test image: flickr_logos_27_dataset/flickr_logos_27_dataset_cropped_augmented_images/Mini/test/2792546855_Mini_2_2_p-1-1.jpg
Model restored
Class name: Mini

まとめ

独自データセットで実際にTensorflowを使ってみることで、かなり理解が進みました。Tensorflow自体は使い方を知ってみれば難しくはなく、それよりもデータをどのように準備すればいいのかを決めて、データを準備するところがあまり面白くはないのですが、頑張る必要がありますね。

また、今回は参考になりそうなブログを見つけて、ほぼそのままのネットワーク構成を使いましたが、最初はあまり複雑なネットワーク構成を独自に作ったりせずに、シンプルな既存モデルから始めるのがアプローチとしては良いのかなと思いました。