stMind

You'll never blog alone

CNNによるテキスト分類で学習済みword2vec(fastText)を使う

CNNとテキスト分類で検索すると、一番最初に出てくるWildMLのチュートリアルチュートリアルではembedding layerも含めて学習するようになっていますが、embeddingのところはFacebookが公開しているfastTextの学習済みword2vecで置き換えてやってみました。

github.com

変えたところは大きく3つの部分。

1. vocabとword_vecをfastTextからロード

英語の学習済みword2vecは6GBあって、gensimを使ってロードするのですが、毎回ロードしてると時間がかかってしまうので(20分近くかかる)、事前にvocabularyとword vectorsを別々のファイルに保存しておいて、そのファイルから読み込むようにしました。 後は、オリジナルと同じようにvocabulary indexの作成と、入力文中の単語をindexに変換しました。

print('Load pre-trained word vectors')
with open('fasttext_vocab_en.dat', 'rb') as fr:
    vocab = pickle.load(fr)
embedding = np.load('fasttext_embedding_en.npy')

pretrain = vocab_processor.fit(vocab.keys())
x = np.array(list(vocab_processor.transform(x_text)))

2. Embedding layerはfastTextをfeed

オリジナルでは、ランダムな値で初期化したWをembedding_lookupのparamとして、feedされる入力テキストの単語をベクトルにしていくのですが、Wは0で初期化して、placeholderとして受け取ったfastTextのword vectorsを代入して単語ベクトルに置き換えていきました。

W_ = tf.Variable(
    tf.constant(0.0, shape=[vocab_size, embedding_size]),
                trainable=False,
                name='W')
self.embedding_placeholder = tf.placeholder(
    tf.float32, [vocab_size, embedding_size],
    name='pre_trained')
W = tf.assign(W_, self.embedding_placeholder)

3. 学習

最後は、embeddingをfeed_dictで与えて学習をしました。

feed_dict = {
    cnn.input_x: x_batch,
    cnn.input_y: y_batch,
    cnn.dropout_keep_prob: FLAGS.dropout_keep_prob,
    cnn.embedding_placeholder: embedding
}

ロスとAccはこんな感じで進みました。

# The logs around step 1000 are as follows.
...
2017-07-08T13:12:27.329179: step 990, loss 0.178512, acc 0.953125
2017-07-08T13:12:28.902815: step 991, loss 0.133091, acc 0.984375
2017-07-08T13:12:30.473521: step 992, loss 0.148561, acc 0.984375
2017-07-08T13:12:32.041047: step 993, loss 0.21213, acc 0.90625
2017-07-08T13:12:33.617257: step 994, loss 0.230192, acc 0.9375
2017-07-08T13:12:35.223648: step 995, loss 0.222954, acc 0.9375
2017-07-08T13:12:36.822623: step 996, loss 0.161116, acc 0.96875
2017-07-08T13:12:38.437168: step 997, loss 0.224385, acc 0.921875
2017-07-08T13:12:40.073519: step 998, loss 0.258734, acc 0.921875
2017-07-08T13:12:41.649018: step 999, loss 0.207504, acc 0.953125
2017-07-08T13:12:43.215527: step 1000, loss 0.211571, acc 0.921875

Evaluation:
2017-07-08T13:12:44.823491: step 1000, loss 0.647888, acc 0.681

学習のロスは十分下がっていますが、Testのロスはそれに比べるとあまり下がっていきませんでした。過学習している様子なので、チューニングが必要そうです。

まとめ

CNNでテキスト分類するチュートリアルを、fastTextでやってみました。まとめてしまえばそれほど多くの変更はなかったのですが、いろいろ調べるのに1週間強はかかっていると思います。何かを身につけるのに簡単な道はないですね。

Try word2vec in 5 minutes

Task

word2vecを使ったテキスト分類の問題(質問文?回答文?)をやってみます。

Steps

  1. datasetの入手
  2. pre-trained modelの入手
  3. datasetとpre-trained modelのロード
  4. 学習
  5. 評価

1. datasetの入手

まずはデータセットが必要ですが、UCSD(カリフォルニア大学サンディエゴ校)の研究グループが、WWW2016で公開したAmazon question/answer dataを使います。

Amazon question/answer data

jsonの中身は、質問文、回答文の他に質問のタイプ(yes/noかopen-endedか)などが含まれていますが、ここでは質問文(questin)、回答文(answer)を使っていきます。

{'questionType': 'yes/no', 'asin': 'B00004U9JP', 'answerTime': 'Jun 27, 2014', 'unixTime': 1403852400, 'question': 'I have a 9 year old Badger 1 that needs replacing, will this Badger 1 install just like the original one?', 'answerType': '?', 'answer': 'I replaced my old one with this without a hitch.'}

2. pre-trained modelの入手

Google Newsのデータセットで学習されたmodelを使います。300万語の300次元ベクトルが含まれたmodelになっていて、3.4GBほどありますのでダウンロードには少し時間がかかります。下のページに、GoogleNews-vectors-negative300.bin.gzのリンクがあります。

https://code.google.com/archive/p/word2vec/

3. datasetとpre-trained modelのロード

pre-trained modelのロードはgensimを使えば簡単です。

# load pre-trained word2vec model
googlenews_w2v = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)

datasetの方はjson.loadを使いたい所ですが、シングルクォーテーションが使われていて、JSONDecodeErrorになってしまいます。仕方がないので、ast.literal_evalを使います。

# load datasets
# you cannot use json.load because this json file is invalid format...(using single quote)

questions = []
answers = []
with open('qa_Appliances.json', 'r') as f:
    for line in f:
        js = ast.literal_eval(line)
        questions.append(js['question'])
        answers.append(js['answer'])

参考。 stackoverflow.com

4. 学習

次は、学習です。 最初に、データセットを学習用とテスト用に分割します。

# Split dataset into train and test set
qa_texts = np.array(questions + answers)
qa_labels = np.zeros(len(qa_texts), dtype=np.int)
qa_labels[len(questions):] = 1  # question: 0, answer: 1

qa_idx = np.random.permutation(len(qa_texts))
qa_texts = qa_texts[qa_idx]
qa_labels = qa_labels[qa_idx]

X_train, X_test, y_train, y_test = train_test_split(qa_texts, qa_labels)

特徴量ベクトルの抽出は、シンプルに分類したいテキストに含まれる全単語のベクトルの平均を使うことにします。このアイデアは、下記のブログを参考にしました。また、識別器はRandomForestを使います。

nadbordrozd.github.io

# Simple word embedding feature by averaging word vectors for all words in a text
# ref: http://nadbordrozd.github.io/blog/2016/05/20/text-classification-with-word2vec/#comment-3233012354
class MeanEmbeddingVectorizer(object):
    def __init__(self, word2vec, dim):
        self.word2vec = word2vec
        self.dim = dim
        
    def fit(self, X, y):
        return self
    
    def transform(self, X):
        return np.array([
            np.mean([self.word2vec[w] for w in words if w in self.word2vec] or [np.zeros(self.dim)], axis=0)
            for words in X
        ])
# Train a model
rf_amazon_qa = Pipeline([
    ('word2vec', MeanEmbeddingVectorizer(googlenews_w2v, googlenews_w2v.vector_size)), 
    ('randomforest', RandomForestClassifier(n_estimators=200))])
rf_amazon_qa.fit(X_train, y_train)

5. 評価

最後に、テストデータに関して評価をします。結果は、8割弱の精度となりました。

# Evaluation
y_pred = rf_amazon_qa.predict(X_test)
print(classification_report(y_test, y_pred))
            precision    recall  f1-score   support

          0       0.77      0.81      0.79      2263
          1       0.80      0.76      0.78      2243

avg / total       0.78      0.78      0.78      4506

まとめ

色々調べてたら、とても5分では終わりませんでしたが、一旦わかってしまえば、再現するには5分程度で簡単に出来るのではないかと思います!全体のipynbは、下記にありますので参考まで。

github.com

アーセンベンゲル補完計画、始めました

先月末、ベンゲル監督が二年間の契約延長を発表しました。

www.arsenal.com

今シーズンは、就任以来初じゃないかと思うほどの不振が続いて、契約満了を迎える今シーズンで本当に辞めてしまうかもしれないと思いました。 とりあえず、契約更新は喜ばしいです。 契約更新したとはいえ、いつまでも続けられるわけではない。システム重視で、選手個人の創造性が制約される現代サッカーにおいて、 アタッキングサードでの攻撃には最大限の自由を与えるベンゲルスタイルと、選手は買うものではなく育てるものという哲学は ぜひとも後世に残したい。というわけで「補完計画」始めることにしました。

記者会見のQAからchar-rnnでベンゲル語録を学習

まずは、char-rnnを使って、単語レベルでベンゲル語録を学習し、ベンゲル監督らしい表現の生成に挑戦することとしました。

github.com

データセットは、アーセナル公式のニュースページから取得しました。char-rnnのREADMEによると、2MB以上のテキストデータを 推奨していたので、過去3年くらいのニュースを対象に学習データを作りましたが、結果をみると2MBだと、再現しているとは言い難いですね…

rnn_sizeを128で実行した結果。前後の文脈を考慮するにはサイズが小さいかもしれません。選手名やクラブ名は再現出来てたりはします。

pleing or any other game . is not a great situation . I could get on Saturday . That is what more than that a decision for the lead but as indipphit is that we didn’t stop . I haven got past and let. Nach there are not quickly as the 250 per cent it didn’t take this alent win , to perform , there were worked when he will play a talent achievement and I think overall they made a positive player , I hunnled a humatict inflacke in the game . The players are going to be played as well . You never know whether watching you old and he seemed the ball and when I started very well . We would have Ozil all his game . Once I think , we had quite about twick the keeper we should have not easy at the time in mone big aspects of our game , you could have right until the end of the season . I go for Sagna and Ozil beat Welbeck , we do not live that training after five , I just sait that someone . Cotgion was able to keep toam . We played the five years because we are in a player was ‘ win the league in their good squad well and we played with Olivier is always the day , I also lose us at home and overall I played well . We saw that he was his little to find it , I am surprising is something tellveted to see the new bysical is training . Giroud just very good confirmally . It was a responsiby in the club but let’s just not inflam knowred . On the sched is to the bench comfortably . Sometimes I wanted to do that . We have a big respect for us , he played at half-time and showed that they are disappointed and power or to do . I was success away to Emirates in the way it is a great game and who is great , but it’s a desperate things respects at Tottenham , to go out everybody and it was a very strong test for us and when you don’t improve our young players who is back there happy quich. Hents in the Premier League came on and Man City and we are going . He issunds it finished because we got back to his feet played a little bad . now he had chances away from home they do , t

次は、rnn_sizeを256にして実行した結果。多少のフレーズは生成されるようになった気がします。が、ベンゲル監督の言葉というよりは単にニュースっぽい。というのは、学習データセットで、ベンゲル監督の言葉以外の部分を含んでしまったかも?

oints then you have to keep some demans and they are in where . I had some uncertainties about how can we gain them losing that in the decisive those clubs where we may streak about [ the Liverpool team, I don’t see what the [ampers if you don’t want to just explating , you do not take our characters away . Ospina , the ] hardest physically straight away . has a strange game for the nervous guy ! is not the only thing we can doctict at all that in every day threat . We need to keep this resulting . In the second half the way we want to get a little bit with Chelsea and or the efurition to be a part of it, From South Anegation in the end it was important to him something that makes it on the way that every game and at the end we gave the penalty desire to do it . leader in training , he really not managed to go into the future supporters . , I don’t know whatos.. Alexis Sanchez has been [ Danny Welbeck ] , no matter how export about it . is a fantastic exappoint on six of our defensive players and you see some teams you all are on a visit one and I am confident that Stoke harm for you that about it in the squad last year but I don’t think I cannot completely ready many teams because it was good , and wherea…Buchmestants and finally played well , they were sort of guys like Stoke, we are confident that we ’ll be enough to bring . They were one of the better side today , that’s what we want to do . Szczesny , December 13Gerbracyna Believis , Alex Oxlade-Chamberlain 2-1 Republic or PolandWojciech Szczesny played the full 90 minutes and calm helped Craik Premier League attacled at Rocalem . Consick or £24 ( or 18 ) to know ( will period because he ’s the right movement and into the top close this matchesterscinal because when he hasn’t enjoyed it . ..That ’s a really good cost or not . But after that Theo’s passigh , there was now behind many times . We won all player oncesively fans pressuress .

補完計画の今後

もう少し学習データセットを増やして、rnn_sizeも大きくして試してみたい。 ベンゲル監督の哲学をいつまでも残すために…

MacとLinuxでディレクトリ内ファイルリストを取得する時の挙動が違う?

以前作ったDeep Learningを使ったブランドロゴ認識だけど、ありがたいことに使ってくれる方がいて、githubにissueが来ました。

github.com

27クラスのロゴ画像をそれぞれ学習用、テスト用に分けてpickle化するスクリプトでエラーになるということらしい。Issueにあがってるエラーログを見ると、Macで実行したときと、ファイルリストを取得した結果が違っている。OSXで実行した時は、各ロゴのtestとtrainが交互で、かつソートされているので、パスのリストであるtrain_test_dirsっていう変数から、train_test_dirs[0::2]でtestのリストだけをpickleにして、train_test_dirs[1::2]でtrainのリストだけをpickleにするような処理をしていた。要は各フォルダがtest→trainという順番になっていることを想定していた。(フォルダパスをリストにするだけなのに、実際のフォルダ構成を調べて処理しなくてもよいのだけど)

train_datasets = maybe_pickle(train_test_dirs[1::2])  # trainをpickle化
test_datasets = maybe_pickle(train_test_dirs[0::2])  # testをpickle化

OSXでのリストはこんな感じ。

Adidas/test
Adidas/train
Apple/test
Apple/train
BMW/test
BMW/train
(以下略)

一方で、どのDistributionかはわからないけど、Linuxで実行したらしき結果はこんな感じ。

Texaco/train
Texaco/test
Starbucks/train
Starbucks/test
Mini/train
Mini/test
(以下略)

ソートされてないし、trainとtestの順序が逆になっている。とりあえず、リストをスライスするときに、最初がtrainで次がtestになるように、

train_datasets = maybe_pickle(train_test_dirs[0::2])  # 最初(0)がtrain
test_datasets = maybe_pickle(train_test_dirs[1::2])  # 次(1)がtest

と変更してもらったら、エラーは出なくなったようだ。ちなみに、centosだとエラーだったけど、debianにしたら動いた、という方もいた。

結局

StackOverflowに回答あるみたいだけど、MacLinuxというか、FileSystemによって順番は違っていて、順番を固定した処理がしたければos.listdirでリストを取得した後に明示的にソートしておくということか。stackoverflow.com

研究を売れ

研究を売れ!―ソニーコンピュータサイエンス研究所のしたたかな技術経営

研究を売れ!―ソニーコンピュータサイエンス研究所のしたたかな技術経営

ソニーコンピュータサイエンス研究所(CSL)の研究成果を実用化する研究営業組織、テクノロジープロモーションオフィス(TPO)の設立、活動内容とその手法に関する本です。

企業における研究開発の理想

企業に所属する研究者、開発者の立場から見て、研究開発の理想形とは何かと考えると、一つには次のような流れではないだろうか。

  1. 研究者の思いに基づいて研究が始まる
  2. トップカンファレンスに採択されるなど、研究が外部で認められる
  3. 社内の事業部に移管され、製品やサービスに実装される
  4. 製品やサービスが公開され、広く世の中で使われる

例えば、Jamie Shottonさん達のRandom Forestを使った姿勢推定の研究が、CVPR2010のベストペーパーになって、ゲームデバイスとして発売、2013年のデータですが全世界で2400万台も出荷されたKinect。改めて思うに、こういう研究が出来れば言うことないですね!

現実、そして研究営業を行うTPO

しかし、現実にはこんな風にうまくいく研究開発はなかなかない。書籍によると、研究から具体的な商品に結びついた成功事例は1万件に1件くらいじゃないかということなんで、ほとんどがうまくいかない!というのは実感としてわかるなぁと思うところです。実際、研究を事業にする際には、研究をしている部隊ごと事業部門に移管したり、研究している技術だけを移管したりすることが一般的に行われますが、いずれの場合でも移管をいかに効果的に行うかが課題となっていて、そこで、CSLの研究成果を実用に結びつけるための研究営業をメインに行う組織であるTPOが設立されたそうです。

試行錯誤と教訓

書籍では、実際の研究営業のやり方について、様々な事例を通して語られています。

  • 他の企業とのコラボ
  • 外部の人に使ってもらう
  • 社内の他部門との繋がりを大事にする
  • スピンアウトして事業をする会社を立ち上げる
  • 出張デモ
  • デモを実際に見てくれた人にだけ送るメルマガ

これらを読んでみると、よく言われている技術移管のための取り組みを愚直に実践しているのだなというのがわかりましたし、王道はないというのもその通りだなと思いました。結局、地道に続けていくしかないんですよね。こういった取り組みを繰り返し行なって環境など様々な条件が揃った時、書籍内の言葉で言うと時間と空間を超えて実用化に適したタイミングにハマった時に、CybercodeやPOBoxなどの成功事例が出て来るのだと思います。著者の方が、この研究営業を10年続けて得られた教訓、ぜひ書籍を見て内容を把握してほしいですが、項目だけ引用しておきます。

  • 全ては研究を出すタイミングにかかっている
  • 必要なコネはなんでも使え
  • 組織に対する先入観をもってはいけない
  • 顧客に対しては、ほぼ伝らない、という前提で努力すべし
  • メンタルを鍛えろ
  • 前例のないやり方を試せ
  • 一分一秒でも早く動くべし
  • 技術を移管した後も追い続けるべし
  • 単なる研究営業ではなく、研究プロデュースを目指すべし
  • 常に研究所のための営業であることを自覚せよ

さいごに

CSLほどの基礎研究ではないですが、応用研究に近い立場で長く仕事をしている身として、どういう考えで、どのフェーズに貢献すべきかというのは常に思っているところだったので、そのものズバリな書籍で非常に参考になりました。繰り返しになりますが、自分の思いとしては、KinectのJamie Shottonのように研究を立ち上げて、それを論文にして、さらに製品化にも関わって、という最初から最後まで中心で関わるというのが理想ではあるんですけどね。自分一人ではなく、チームでこれを目指せばいいよなと、読み終えた後に感じることが出来たので、これから少しずつ行動していきたいですね。

Habits change into character

休暇中ということもあり、2017年がふわっとスタートしたわけですが、一年の計は元旦にありの教えに従って今年の抱負を記しておきたいと思います。(既に元旦ではなくなってますが、それはおいておきます)

「身につけた習慣が個性になる」

タイトルを意訳すると、このような意味になるでしょうか。ローマの詩人Ovidの言葉だそうです。iOSアプリのDayoneを立ち上げた時に起動画面上に表示されていて、良いフレーズだなと思ったのでメモっておいたのでした。新年の抱負といえば今年にやりたいことを書くことが多いですが、少し抽象化して身につけたい習慣について書いてみようと思います。

ネイティブ英語を毎日聞く

英語論文を読んだり、英語ブログを読むことは苦もなく出来るようになりましたが、リスニングに関してはまだまだです。話している単語は所々で聞き取れるものの、内容が即座に理解できないことが多いです。ただ、英語字幕を見ながらであれば理解できるので、目から入力された英語に対する神経回路網は十分な量のデータで学習されていると思われます。また、字幕ありで約2ヶ月ほど毎日英語を聞いていた状態では、字幕なしの英語が理解できるようになっていたので、耳から入力された英語に対する脳の学習はできつつあったのだと思います。休暇に入って聞かなくなったら、元に戻ってしまいましたが...

英語字幕ありでの学習を習慣化して、リスニング脳を十分に学習させ、今年は英語リスニングを身につけたいと思います。Courseraでコースを取って聞くのが一石二鳥でいいかな。ビデオに字幕もついてるし(所々間違っているけど)、1つが10分前後で長さ的にもちょうどいい感じだし。

※英語ライティングも合わせてやっていきたいが、目と耳が鍛えられたら比較的楽に出来るのでは?とも思っていたり。

運動後のストレッチを怠らない

Runkeeperで記録をつけ始めてから約3年、合計の走破距離が1500キロ(1年で500キロ、1月で40キロちょっと、毎週10キロ程度)を超えるところまで来て、主にランニングですが運動をする習慣はほぼ身についていると思います。結果として、フルコートでサッカーをした時に、20代の若者についていけるくらいの体力は維持できているかなという実感があります。一方で、今まで経験したことのないような体の痛みを感じて、しかもそれが長引くということがあって、特に膝や肩などの関節で多く発生して、それが原因でランニングがしたくても出来ない状態がしばらく続くことになってしまいました。

今までは運動をする方ばかりを意識していましたが、今年は運動と運動後のケアはセットであるという意識付けをして、運動後にはストレッチを怠らないようにしたいと思います。運動を生涯継続するためには、ケアが大事!(それを実感した2016年の膝の痛みでした)

Deep Learningを自分の中に体系化する

https://www.quora.com/Is-deep-learning-overhypedwww.quora.com

Deep Learningや人工知能に関する話題は、毎日twitterに何十件も流れてきて、特にここ最近はarXivに論文が発表されると同時にgithubに実装が公開されるなど、進化のスピードがめちゃくちゃ速くて、ついていくのも大変です。とはいえ、Deep Learningが出来ていないタスクはたくさんあって、今後何年かかけて更に進化していくことになると思いますし、Computer Visionを仕事にしている以上、避けては通れないことも確か。そのためにも、情報に触れた時に、自分の中で整理・体系化していくことを習慣づけしていきたいと思います。単なる趣味として、githubにあるコードを動かしてみたり、Tensorflowなどのフレームワークを使って自分で作ってみたりなども、引き続きやっていこうとも思っています。ゼロから作るディープラーニングは、体系化するための基礎を、手を動かしながら理解するのに良さそうです。

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自体は使い方を知ってみれば難しくはなく、それよりもデータをどのように準備すればいいのかを決めて、データを準備するところがあまり面白くはないのですが、頑張る必要がありますね。

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