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

adish intelligence

アディッシュ株式会社のエンジニアブログです。

私が感じるフィリピンという国 (前編)

adish のフィリピンオフィス(adish international)に勤務する岡安と申します。
開発チームのマネージャーとして2014年より現地で業務を行っております。

フィリピンシリーズ初回の今回は、海外で仕事出来るの?生活できるの?帰りたくなくならないの?英語は?など、フィリピンという国と仕事と私生活で感じている事を今日はご案内させていただきます。
これから海外での仕事にチャレンジをお考えの方の少しでもお役に少しでも立てれば幸いです。

f:id:adinoue37:20160823175753j:plain
  1. 私が感じるフィリピンという国。 <- 今日はここの前編
  2. 皆が嫌いな英語。
  3. マニラで進めているプロジェクトや、現地のIT開発会社について。

海外で仕事のイメージとは、カッコイイ!英語が心配。文化は?治安大丈夫?物価は?
私は赴任前は大方このような想像をしていました。半分正解です。

フィリピンって?

日本から約5時間。日本のバナナの9割はフィリピンから輸入されています。
熱帯地域に属する島の集合体の国です。雨季と乾季に分かれてますが、一年中日本の夏の服装で過ごせます。
私の住む、仕事をするマニラではタガログ語と英語を標準語としています。
通貨はフィリピンペソ。1万円は4,500フィリピンペソになります。(執筆時点のレート)
アジアでも経済発展に勢いがある国です。

海外で仕事ってカッコイイ!

海外で仕事するってカッコイイ。と来る前は私は思ってました。
現実は普通の仕事です。私の行うシステム開発の業務に関してはルールは同じです。
作るべきものの仕様要件が決まり、設計を行い、開発を行い、製品テストを行ってリリースする。なんら変わりません。
違うのは言葉だけです。現地のメンバーとの会話は基本英語と最近少し覚えたタガログ語です。
英語については重要な要素ではありますが、今後の記事で触れる予定です。

会議も含め仕事の流れで大きく異なることはありません。仲間と信頼関係を構築する事も同じですし、言語意外で特に大きく変わったことはしていません。
クラウド環境が整った今、インターネットとPCがあればどこでも仕事ができるという実感は私も身をもって感じています。(ただやはり英語は必要)
なお、日本のメンバーとの会議はchatもしくはテレビ会議で行ってます。

ですのでちっともカッコよくないですしごく普通です。夢やあこがれを持ってる方ごめんなさい。
しかし人は世界中どこでも一緒で、会話が出来ればお互い人です。そんなに違いません。

プログラムを書く事やシステムを作ること。これ自体に国境も無いですし世界標準ルールだし、ロケーションは関係ありません。
そういう意味でとても素敵な仕事を選べてとても幸運です。

フィリピンの文化って?

フィリピンは9割がカトリック教徒の国です。
とても信心深い人もいればそうでもない人もいます。人それぞれです。おおらかな性格の方が多いのですが意外とストレスに強くないです。
私がフィリピン人の一番好きな所は、彼らはとても家族を大事にする点。何があっても家族が一番。私を含め日本人がどこか忘れてしまったものを彼らは今も大事にしています。

フィリピンと一言で言っても多民族国家だというのは意外と知られていないかもしれません。元々国じゃなかった島々を国にする!と決められてしまったので国になってますが島毎に国家のような概念があったそうで、それが統合されてしまったので、多くの種族が1つにまとめられています。その中でもマニラ付近ではタガログ族が多数。その他 イロカノ族、ビコラノ族、カパンパンガ族などなど。その他華僑の人々、アメリカ系、ヒスパニック系などなど。
なので、フィリピン人と一言でくくるのは結構無理がありそうです。
あまりにもいろんな言葉があるので公用語はフィリピン語と英語にしてしまったというのは納得です。
地域が異なればフィリピン人同士でも英語で会話するというので日本の地方の方言という感覚ではなく別の言語のようです。

明るく笑顔が絶えず明るい国民です。よく歌います。
給料が月に2回あるのも特徴です。月に1回だと全部使ってしまうからだそうです。
そのためか、いたるところに巨大ショッピングモールがあり、週末は人で賑わってます。
また、GDPの1割が海外の出稼ぎ労働によるものだそうです。持ち帰ったお金を家族に使ったりこういったモールで消費をするそうです。
あと、皆異常に寒いところに憧れを持っています。
第2次大戦の不幸な歴史があったにも関わらず、現地の人々は日本人に比較的好意的に接してくれます。日本人だからという事で不遇な対応を受けたことはありません。

地元の文化や国民性を知ることは仕事を進める上でとても重要です。
なぜ一緒に仕事をする彼らがそのように考えるかは文化の違いにより理解が難しい場合もあります。
でもここはフィリピンです。自分たちの文化を押し付けるのではなく、なぜそう彼らが考えるかを理解する努力が我々外国人には必要だと考えます。

実はここを理解することが海外で仕事をする上で一番重要だと私は信じてます。

今回はここまで。

f:id:adinoue37:20160823175811j:plain

自己紹介

岡安聡明 39歳。初めての海外赴任でフィリピンの自社オフィスにて2年。 32歳で英語を習得し始める。2014年より現地で開発組織を立ち上げ開始。 現在開発メンバーと共に、社内各拠点で利用する業務システムの構築に従事。 趣味はダイビングと世界をふらふらすること。

adishの技術開発部とは

どうもこんにちは。グループ本体であるGaiax社の頃から数えてそろそろ10年目になる井上です。
今回はadishの技術開発部がどういった開発をしているのか、お伝えしたいと思います。

技術開発部そのもの

現在adishには複数の事業が存在します。
それらはすべて、adishのミッションに沿って運用されています。

我々技術開発部のメンバは、この 運用を手助けするための開発 をメインとして日々業務を行っております。

技術開発部内のチーム

現在、技術開発部には大きく4つのチームがあります。

  • 雪風
  • ALICE
  • karen
  • BANANA

この4つです。
それぞれをさっと紹介致しましょう。

雪風チーム

事業向けに開発した各種アプリケーションの運用開発を行うチームです。
新規開発を行うこともありますが、基本的には「事業運用の効率化のための開発」です。

開発メンバ側で運用対応が必要な場合はそのオペレーションも行うのですが、そのオペレーションを効率化するための開発を新規で行ったりもします。
adishの事業を裏で支えているチームです。

ALICEチーム

機械学習について研究開発を行うチームです。
この分野は既存事業に対しての貢献度合いが非常に大きいと明確に見込まれており、期待も大きいチームです。

karenチーム

現在新しく立ち上がってきた事業のサポートツールを開発中のチームです。
市場のニーズも相俟って、かつて無いスピード感と圧倒的な開発力で突き進んでいるチームです。

チームBANANA

事業を効率化するための少し規模の大きいツールを、オフショアで開発するチームです。
拠点はフィリピンにあります。

技術開発部で使用している言語

古くからはGaiax社の系譜としてPerlが使われてきましたが、最近はそこに限ることはありません。

Perl

既存事業の大きめなシステムは基本Perlで組まれています。フレームワークはそれぞれで異なっていて、CatalystやAmon2を利用しています。

Ruby

簡易なWebアプリを用意する際は、Ruby on Railsでスパっと作ってしまうのもアリです。 その後の運用を行う上でも必要なモジュールは揃えやすいですし、そもそも開発に着手しやすいメリットも大きいです。 RoRを使った例としては、「普段の工数の10%を自由に使って良い」とする10%ルールで作ったプロダクトなどがあります。

Python

やはり機械学習に対してはモジュールがそろっているPythonが非常に便利です。 また、教師データを効率的に作成するためのツールをPythonのフレームワークDjangoで作成しています。

JavaScript(Node.js,React.js)

運用を行う上で効率的な画面操作を提供できるのであればSPAをReact.jsで構築するのも選択肢として充分に有り得る話。 実際、画面の一部を表示させたまま、操作を継続する場合の実装として利用しました。

Swift

iOSアプリを作成することだってあります。

Google Apps Script

運用を効率化する目的であればGASだって。

技術開発部で使用しているサービス

開発手法や利便性、あるいは運用の観点から判断してメリデメなどを部全体で話し合った上で決定するフローを取っています。

ソース管理

GitHubを利用しています。 ちなみに社内Subversion、社内git、bitbucket、と移り変わってきた歴史があります。

サーバ監視

サーバ監視には Mackerelを利用しています。 EC2への導入が簡単であり、機能・通知系も揃っていることから採用しました。 Gaiax社謹製のインシデント管理ツールReactioとの連携も強力で、24時間365日稼動する弊社事業のツールを運用していく上で欠かせないものとなっています。

コミュニケーション

開発メンバ間の連絡は Slackを基本としています。 ただ事業側はSkypeを利用しているため、全てSlackに移行できた訳ではありませんが、いつか全面Slack利用にしたいですね。

情報共有

Qiita:Teamでノウハウ共有しています。日報もこちら。 今まで各プロダクトの情報はそれぞれ違う場所に散らばっておりましたが、今後ドキュメント蓄積には esa.ioを使っていこうとしているところです。

クラウド

AWSが基本ですが、Googleの各種APIを利用するにあたって GCP利用の例もあります。

技術開発部って結局

各チームで方向性や観点は異なっているように見えますが、一番の旗印は 『運用を手助けするための開発』 です。
もっと厳密に言いますと『「つながりを常によろこびに」をミッションとした運用を手助けするための開発』であるため、エンドユーザと運用を行うスタッフとのつながりや、エンドユーザとクライアントとのつながり、果てはエンドユーザ同士のつながりを手助けするための開発を行うことに繋がるものです。
karenチームで新規開発する際にもそれぞれの観点を重視した上で行いますし、雪風チームで既に稼働しているツールを改修したり機能追加する上でもやはり外せない観点です。

そういった意味では一番身近なユーザという事業運用メンバに「よろこび」をストレートに届けることが出来る業務と言えます。
そこに対して取るべき手法も、そのニーズに合わせて様々取ってきました。karenチームは1週間スプリントのスクラムにて開発を行っています。
また運用を手助けするための開発だけでなく、開発そのものから事業化を行うパターンも存在します。

ようはadishのミッションを基調とした上で、割となんでもありで開発を行っているのが技術開発部と言えるのではないでしょうか。


今回は本当に技術開発部のごく一部を紹介したに過ぎないですが、いかがでしたでしょう。

普段私が書く文章とテイストが異なっている気がしなくもないのですが(以降の記事テイスト変更のハードルを下げる仕込み)、技術開発部のことを誤解なきようお伝えするにはちょっとは真面目に書かなければ、と意気込んでしまった結果だと思って頂ければと思います。

次回はチームBANANAのリーダーを務める岡安さんから、フィリピン現地の模様をお伝え頂く予定です。
どうぞお楽しみに!

adishにインターン生としてjoinしました!

f:id:iwabuchi02:20160719173341j:plain

【自己紹介】

2016/7/12よりadishの技術開発部にjoinしました。岩渕です。 現在大学4年生で機械学習、特に強化学習の高速化を研究しています。 しかし、勉強内容のアウトプットが少ないことや、機械学習技術の実社会への応用力が足りないと実感しており、技術応用の視野を広げ、実務としての経験を積むために、入社しました。立ち位置はインターン生ですが、新卒に負けない気持ちでやっています。

現場では想像以上にフランクで話やすい方が多く、基本的に分からないところは丁寧に教えていただけています。そのうえ、両隣には上長と取締役が座っているという、他では味わえない恵まれた環境に来ることができたと感じています。

【所属紹介】

自分は技術開発部の、機械学習を用いた開発を行うチームに所属しています。担当は画像認識の精度向上ということで、より人の作業負担量を削減できるようなツール、システム開発に携わっています。深層学習を学んでいる背景もあり、知識の理解を深めると同時に、アウトプットができる良い機会となっているため、深い学びの場にもなっています。

【インターン募集】

もし、自分と同じように、応用の視野も広げ、経験を更に積みたいという方がいれば、インターン生募集は継続されているみたいなので、以下のURLから応募してみてください! http://wbawakate.jp/posts/adish_2016intern/

ITエンジニアのためのDeepLearning #6 レポート

こんにちは。ここ最近先輩にボウリングで大敗し、ボウリング熱が再燃しているshirakiyaです。

今回は2016年7月7日に弊社セミナールームで開催されたITエンジニアのためのDeepLearning #6 - connpassのレポートです。

当日は約50人の方に参加いただきました。こういった勉強会ってあまりQ&Aの時間で質問が少ないことも多いという印象を持っていますが、今回のイベントでは多くの質問が飛び出し、参加者のみなさんの関心の度合いが高いことを感じました。

何のイベント?

イベント告知ページにあるように

深層学習やパターン認識系の本の罠と、本には載っていない貴重な情報や、数式を全く使わない考え方を初学者向けに提供しています。

としたセミナー形式のイベントです。今回は6回目ということもあり、一目にはかなりディープな内容でしたが、特に畳み込みやプーリング等、主に画像認識の畳み込みニューラルネットワークで使われる処理について、かなりわかりやすい説明がなされました。

講師紹介

f:id:tech-adish:20160707215255p:plain

Pegara, Inc. Chief Technology Officer and Co-Founder 中塚氏

オープニングで「あっきーと呼んでください」とのことなので、以下、あっきーさんでw

f:id:tech-adish:20160707214412p:plain

アジェンダ

  • DeepLearningセッション (最新の事情編)
  • アディッシュの紹介
  • ライトニングトーク
  • DeepLearningセッション (技術背景、実装、Demo編)
  • 懇親会

DeepLearning 概要編

あっきーさんの自己紹介から始まります。 Caffe等のライブラリなしで、C/C++とOpenCLを使って1からDeepLearningを実装したとのこと。すごすぎる。

  • DeepLearningあらゆる分野に広がっている
    • インターネットや開発ツールだけでなく、教育機関や防衛などにも
    • 例えば、シェールガスの埋蔵調査で、調査結果の波形出力から地下に存在するかどうか判定するなど
  • 画像認識が専攻分野
    • Googleフォトやドローンの自動航行
  • マーケット分析や音声認識

のようなお話がありました。印象的だったのは、教師データの収集が困難とのことで、ちゃんとしたものを作ろうと思ったら5,000万円位かかるし、Googleレベルは数億~数十億かかけているのでは、とのこと。

また、画像認識の例として「コンピュータが写真を理解するようになるまで」の動画が紹介されました。この動画は2015年3月のものです。

www.ted.com

この動画では、画像認識よりも1つ上のタスクである画像キャプションの自動生成について、著名なDeepLearning研究者の一人であるフェイフェイ・リーさんがTEDでお話しているものです。

今回のセミナーでは画像キャプションの自動生成の話題まで触れなかったため、参考までにこちらの東京大学の牛久先生の記事が良くまとまっていますので載せておきます。

www.slideshare.net

アディッシュ紹介

会場提供させてもらったということもあり、軽く弊社の会社紹介をさせてもらいました。 (公式Webサイトはこちら→ adish.co.jp)

f:id:tech-adish:20160707220622p:plain

↑事業紹介に続いて、

f:id:tech-adish:20160707220723p:plain

↑ 私が取り組んでいる、Web技術を用いた教師データ作成について簡単に紹介しました。
(簡単なアプリケーションを作ってデモすることを折り込んでいたのですが、開始30分前までできていなくてとても焦っていたのですが、間に合って良かったです。)
この発表の中で話した、Bing Search APIを用いた画像の収集についての詳細は 当ブログ7月7日の記事 に書きましたので、ご参考まで。

アディッシュでは、DeepLearningによる画像認識で判断できることは任せてしまい、人じゃないと判断が難しいところに集中できるようにする、という方針でDeepLearningを活用できないか、にチャレンジしています。

ライトニングトーク

f:id:tech-adish:20160708154158p:plain シリコンバレー在住の山中さんに「論文をAIに査読させる」試みについてLTしていただきました。

f:id:tech-adish:20160708154433p:plain

論文内に含まれる語の出現数からクラスタリングし、近年の論文の傾向を見るということをされていました。

個人的には、t-SNE(多次元のベクトルを二次元ベクトルに次元圧縮し、可視化するライブラリ)が便利そう(だし、社内外でプレゼンをするときにオッ、っと思わせることがでそう)なので、一度試しに使ってみようと思いました。

また、「シリコンバレーに住むメリットは?」という問いに対して「Meet up がたくさん行われている。そこで優れたエンジニアにいっぱい会えるのがいい」との質疑応答がありました。

DeepLearning 技術編

f:id:tech-adish:20160708161432p:plain

初心者がDeepLearningを始めるには、まず用語と用途を知るところから始めるのが良いと、あっきーさんは仰っていました。

今回の資料の中にも、一例ですが

  • Perceptron(パーセプトロン)
  • MLP(MultiLayer Perceptron、多層パーセプトロン)
  • DCNN(Deep Convolutional Neural Network、深層畳み込みニューラルネットワーク)
  • RNN(Recurrent Nueral Network、再帰型ニューラルネットワーク)
  • Activation Function(活性化関数)

...などの用語が出てきました。それぞれどういう意味で、何をするためのものなのかを理解していくことが大事ですね。

また、高校レベルの数学(線形代数)の知識は必須とのこと。私も最低限のところで、行列の内積計算はできるようになっていないといけないと思っています。

今回、「ハンバーガー」「ライス」「スパゲティ」「ステーキ」の画像を判別するデモを行いました。

やはり、用語と用途を知るのはとても重要だと思います。

画像認識タスクで今やデファクトスタンダードになっているDCNNでは、出現する技術要素の出現もある程度落ち着いてきたかなところで、こちらの書籍などで出現するワードを抑えられていると、かなりのレベルアップだと思います。ただ、若干難しいので、ネットの記事やスライドなどを数見ていくと、おおよそ理解が進んでいくと思います。

Q&A

Q1: DeepLearningは、AI(人工知能)の閃きなどにつながるのか?

A1: 残念ながらないです。人間が課題を与えることが必要なので、プログラムによるひらめきではない。

Q2: 初心者が始めるには何から始めたらいいか?

A2:

  1. DeepLearning周りで使われている単語の意味を知る
  2. プログラムを書いて、学習させてみる
  3. チューニングするときに試行錯誤する

という順番だったかなと思います。あっきーさんはその中で、DeepLearningライブラリを使わずに実装するという変な方向に進んでしまった、とのことですがw

まとめ

講義後の懇親会でも、あっきーさんはおそらく全員とお話されていたと思います。参加者のみなさんも水曜夜にもかかわらず遅くまで盛り上がっていました。

f:id:tech-adish:20160708160825p:plain

普段私が参加するようなWebエンジニアが集まる勉強会と違って、年齢層が広く、聞いたところによると定年退職された方も勉強会に来ていらっしゃったとか。それだけこの領域の技術に魅力を感じているひとが多いのだなと感じました。

今回は第6回でしたが、7回、8回と予定されているようですので、ご興味のある方は追ってみてはいかがでしょうか。
主催者の田中さんのイベント情報→tanaka_j_tomohiroさんのプロフィール(主催イベント一覧) - connpass

PythonでBing Search APIを使って画像データを集める方法

こんにちは、エンジニアの白木(shirakiya)です。
近年、人工知能がすごく話題になっていますが、adishでも画像認識タスクなどで、機械学習を利用しています。

画像認識では、DeepLearningを使うのがデファクトスタンダードな状況になっていて、その中でどういったモデルを使うかの議論が行われていますが、どのモデルを使うにせよ必要なのは 教師データ です。
画像認識タスクでの教師データの中身は、認識対象としたい「画像」とその画像の「分類情報」が対になったもので、それが大量にあればあるほど認識対象においての汎化性能*1が高くなります。(例えば、スイカを認識させたい場合の教師データは多数の「スイカの画像」と「スイカの分類ID(整数値)」の対です。※分類IDは個人個人が設定するもの)

f:id:shirakiya:20160707112425p:plain

つまり、画像認識タスクが行える分類器を作るためには大量の画像が必要になってきます。
(弊社もDeepLearning技術の検証目的で画像が必要でした。)

画像を集める方法は色々あると思います。ざっと思いつく方法を列挙していくと、

  • 人力で
    • ググッて、色んなサイトから画像を「名前を付けて保存」で保存する
    • クラウドソーシングを使って画像を集めてきてもらう
  • プログラムで
    • 画像検索系APIを使って、一気に画像を検索・保存する
    • 特定のサイトをスクレイピングして、画像を集める
  • サービスで
    • (規約等で画像の利用に同意してもらっているという条件下で)Webサービスを通して画像を集める

といったところでしょうか。
やはり大量の画像を集めないといけないので、そこはプログラムに任せるのが得策だと思います。
スクレイピングは例えばこちらの記事のように、欲しい画像が多くある・ありそうな特定のブログ等から画像を収集する方法のことです。スクレイピングはサイトへのアクセスをプログラムで実行させるため、DDoS攻撃のように対象サイトのサーバーに負荷をかけてしまう可能性があり、その対策として間隔を空けてアクセスするようにプログラムしなければなりません。
このため、大量に画像を集めたいという今回のケースでは、取得に要する時間の観点から、より目的にマッチしている「画像検索系APIを使って、一気に画像を検索・保存する」方法を、Pythonを使って入門的にご紹介します。

画像検索API

画像検索系APIは

等他にもいくつかありますが、 欲しい画像予算 に合わせてAPIを選定するのが良いと思います。
例えば、Flickrは「風景や絵になる写真」が多いですが、一方でGoogleだとアニメ画像等も含めて結果が出てくる印象です。また、日本への対応とかも観点であったりして、例えばFlickrとGoogleで「太宰府」で画像検索してみると、イメージしている画像とその違いがわかるかなと思います。(左がFlickrで、右がGoogleです)

f:id:shirakiya:20160706172234p:plain

今回はMicrosoftの画像検索サービス「Bing」の検索API「Bing Search API」を使ってみることとします。

Bing Search API

Bing Search APIはBingの検索とその結果の取得をプログラム上で行うためのAPIです。
https://datamarket.azure.com/dataset/5BA839F1-12CE-4CCE-BF57-A49D98D29A44

アカウント作成

まずAPIを利用できるようにするために、Microsoft Azure Marketplaceにアカウントを作成するところから始めます。

1. Microsoftアカウントを作る

少しややこしいのですが、本来作りたいのはMicrosoft Azure Marketplaceのアカウントですが、そのためにはMicrosoftアカウントを作成しないといけません。
以下のページから、Microsoftアカウントを作成します。
https://signup.live.com/signup

2. Microsoft Azure Marketplaceに専用のアカウント情報を入力

1でアカウントを作成してログインしている状態で、Microsoft Azure Marketplaceにアクセスし、Marketplace専用のアカウント情報を入力します。

アカウント情報のページで「プライマリアカウントキー」の文字列がゲットできればOKです。

Bing Search API を有効にする

Bing Search API へアクセスします。
「アクティブなサブスクリプション」から任意のプランを選んでください。今回は無料の5000トランザクション/月のプランを選ぶこととします。
※ ちなみに1トランザクションにつき50枚の画像が取得可能なので、5000トランザクションだと月に25万枚取得することができる計算です。

f:id:shirakiya:20160706172421p:plain

同意画面が出てチェックしてOKすると、購入完了画面が出ます。

f:id:shirakiya:20160706172614p:plain

購入すると(実際は無料ですが)、マイアカウントのマイデータのページにBing Search APIが登録されています。

f:id:shirakiya:20160706172604p:plain

これでBing Search APIを利用する準備が整いました。

検索条件の品定め

https://datamarket.azure.com/dataset/explore/bing/search
から、簡単に検索に必要な条件を入力してAPIのレスポンスを表示することができます。
また、その条件をクエリパラメータとしたリクエストURLを自動的に作成してくれるため、これを見れば作成すべきクエリの形がわかり、最初は重宝します。
ここで、適切な検索条件を見つけてからスクリプト書くと、コードが書きやすいかなと思われます。

※1. 1回の検索(画面内では"適用"ボタン)でクエリを1個消費するので注意。
※2. https://datamarket.azure.com/dataset/bing/search#schema に簡単なAPIの仕様が掲載されています。
※3. より詳細なドキュメントはこちらのリンクからwordファイルで確認できます。

スクリプトを書く

仕様として、

  • Python3系
  • コマンドラインからスクリプトを実行すれば imagesディレクトリに画像を格納する
  • 引数で「検索ワード」と「取得画像枚数」を指定することができる
  • 収集するのはjpegかpngの画像に限る

というスクリプトを作ることを考えます。
以下、サンプルコードです。

# -*- coding: utf-8 -*-
# Usage:
#    $ python collection/scripts/collect.py <検索文字列> <取得画像数>

import argparse
import urllib
import requests
import json
import io
import imghdr
import uuid
import os

# Bing Search APIの仕様で決まっている1リクエストあたりの画像取得最大枚数
ONE_SEARCH_LIMIT = 50
# Bing Search API のURL
ROOT_URL = 'https://api.datamarket.azure.com/Bing/Search/v1/Image?'
# APIキー
API_KEY = '<プライマリアカウントキー>'
# 画像のダウンロードのタイムアウト(秒)
TIMEOUT = 5
# ダウンロードした画像を格納するディレクトリ名
SAVE_DIR = 'images'

def main():
    parser = argparse.ArgumentParser(description='Collect image via Bing Search API')
    parser.add_argument('search_word', type=str, help='Search word')
    parser.add_argument('count', type=int, help='Number of collected images counts')
    options = parser.parse_args()

    if options.count % ONE_SEARCH_LIMIT != 0:
        raise Exception('number must be divisible by {0}!!'.format(ONE_SEARCH_LIMIT))

    for i in range(options.count // ONE_SEARCH_LIMIT):
        offset = i * ONE_SEARCH_LIMIT
        print(offset)
        params= {
            'Query': "'{}'".format(options.search_word),
            'Market': "'{}'".format('ja-JP'),
            '$format': 'json',
            '$top': '{0:d}'.format(ONE_SEARCH_LIMIT),
            '$skip': '{0:d}'.format(offset),
        }
        url = ROOT_URL + urllib.parse.urlencode(params)

        response_json = requests.get(url, auth=('', API_KEY))
        response = json.loads(response_json.text)

        for result in response['d']['results']:
            image_url = result['MediaUrl']
            try:
                response_image = requests.get(image_url, timeout=TIMEOUT)
                image_binary = response_image.content
            except:
                continue

            with io.BytesIO(image_binary) as fh:
                image_type = imghdr.what(fh)

            if image_type == 'jpeg':
                extension = '.jpg'
            elif image_type == 'png':
                extension = '.png'
            else:
                continue

            filename = str(uuid.uuid4()) + extension
            
            if not os.path.isdir(SAVE_DIR):
                os.mkdir(SAVE_DIR)
            with open(os.path.join(SAVE_DIR, filename), 'wb') as f:
                f.write(image_binary)


if __name__ == '__main__':
    main()

解説

import argparse

parser = argparse.ArgumentParser(description='Collect image via Bing Search API')
parser.add_argument('search_word', type=str, help='Search word')
parser.add_argument('count', type=int, help='Number of collected images counts')
options = parser.parse_args()

if options.count % ONE_SEARCH_LIMIT != 0:
    raise Exception('number must be divisible by {0}!!'.format(ONE_SEARCH_LIMIT))

ここでコマンドラインの引数を定義し、受け取っています。
Pythonでは sys.argv で引数をリストで受け取ることも可能ですが、argparseライブラリを使うとより高機能にコマンドライン引数の定義を行うことができるので、重宝します。
今回の場合では、Bing Search APIは1リクエストに付き50件まで画像を取得することができるので、取得枚数は50で割り切れる数字のみ受け付け、それ以外は例外を投げるようにしています。

import urllib
import requests
import json

# Bing Search API のURL
ROOT_URL = 'https://api.datamarket.azure.com/Bing/Search/v1/Image?'
# APIキー
API_KEY = '<プライマリアカウントキー>'

for i in range(options.count // ONE_SEARCH_LIMIT):
    offset = i * ONE_SEARCH_LIMIT
    params= {
        'Query': "'{}'".format(options.search_word),
        'Market': "'{}'".format('ja-JP'),
        '$format': 'json',
        '$top': '{0:d}'.format(ONE_SEARCH_LIMIT),
        '$skip': '{0:d}'.format(offset),
    }
    url = ROOT_URL + urllib.parse.urlencode(params)
    response_json = requests.get(url, auth=('', API_KEY))
    response = json.loads(response_json.text)

ここで指定された取得枚数に応じて、APIを数度に渡ってリクエストするようにしています。(仮に100件欲しい場合は2回ループする)
使っているパラメータの意味は以下の通りです。

  • Query: 検索ワード
  • Market: 検索対象とする国、地域の指定
  • $format: レスポンスフォーマット、デフォルトはAtom。他にjsonとxmlが選べる
  • $top: 取得画像数
  • $skip: オフセット。SQLで言うところのoffsetで、100〜149件目が得たい場合は$skip=100とする

URLエンコードをしてからGETでAPIを叩きます。引数authとAPIキーを入れるのを忘れないように。
レスポンスをjson.loadsでデコードして辞書型にしています。

# 画像のダウンロードのタイムアウト(秒)
TIMEOUT = 5

for result in response['d']['results']:
    image_url = result['MediaUrl']
   try:
       response_image = requests.get(image_url, timeout=TIMEOUT)
       image_binary = response_image.content
    except:
        continue

Bing Search APIのAPIレスポンスの中には、画像のバイナリ情報は入っていません。
なので、MediaUrlに画像のURLが文字列で入っているので、そのURLを使って再度画像をダウンロードしています。
(ここでタイムアウトは設定しておいた方が良いです。たまに無効なサイトであったりして、そのサイトのせいでプログラムの実行時間が長くなってしまいます。大量の画像を得るので、1枚2枚の画像は捨てて、プログラム全体の実行時間を短くしたほうが効率的です。)

import io
import imghdr
import uuid

with io.BytesIO(image_binary) as fh:
    image_type = imghdr.what(fh)

    if image_type == 'jpeg':
        extension = '.jpg'
    elif image_type == 'png':
        extension = '.png'
    else:
        continue

    filename = str(uuid.uuid4()) + extension

ここではダウンロードした画像のフォーマットを標準ライブラリの imghdr ライブラリを使って特定し、jpeg|png 以外の画像を捨てています。
レスポンス内のContentTypeで見る方法もあるかもですが、それだと、ContentTypeと実際の画像のフォーマットが異なっていたりしていて、整合性が取れない時があります。
そして、ファイル名にはUUIDを付けることで、ファイル名の重複による上書きを防止してます。

import os

# ダウンロードした画像を格納するディレクトリ名
SAVE_DIR = 'images'

if not os.path.isdir(SAVE_DIR):
    os.mkdir(SAVE_DIR)
with open(os.path.join(SAVE_DIR, filename), 'wb') as f:
    f.write(image_binary)

最後にimagesディレクトリに保存しています。

あとは、コマンドライン上でスクリプトを実行すれば、画像をどかっと保存することができると思います。

Python初挑戦な人やBing Search API初挑戦な人向けに少し詳しめに説明してみました。
これで画像を収集するのはプログラムに任せてしまえますね!

補足

実はMicrosoft Azure MarketplaceにあるBing Search API2016年12月15日を持って終了するとのアナウンスがあります。(書いてて気付きました…)
今はMicrosoft Azure Cognitive ServiceBing API v5というAPIが既にリリースされており、いずれこちらのAPIに切り替える必要があります。
その切替の方法は追ってこの場で報告しようと思います。

それでは!

*1: 汎化性能とは「未知データに対する分類精度」です。ざっくり言うと、まだ学習させたことがない画像を入力して出力された分類が意図通りなのかどうかの精度のことを言います。

エンジニアブログ始めました

ごあいさつ

今日からアディッシュ株式会社のエンジニアブログを始めます!

当ブログの推進を担当いたします@sakaimoと申します。よろしくお願いします。

いきなりですが、世の中の「エンジニアブログ」って何のためにやってるんだろうと考えることがあります。

例えば...

  • 技術的に困ってる人の問題解決をしたい! ← すてき!
  • 自分たちの技術力を自慢したい! ← 大事なモチベーション
  • 発信を通して逆に情報を集めたい! ← このループは確かに存在する

などがあると思います。

このブログの目的

ブログを始めるにあたって「ブログの目的」をエンジニアで議論したところ、下記のような結論になりました。

ブログで発信していくことで私たちのことを知ってもらい、興味を持ってもらい、「共感してもらえるエンジニアの採用につなげる」

です!

...いやいや、私もブログが採用に直接結びつくわけないと思っています。

しかしながら

  • ブログ記事を発信していく
  • アディッシュおよびそのエンジニアのことを知ってもらえる
  • 技術的な取り組みを知ってもらえる
  • その中にみなさんの気になる記事がある(かもしれない)
  • そこから新たな出会いがうまれる(かもしれない)

のようにまずは「認知してもらう」ためのブログと考えています。

ということで、アディッシュのサービスを支えている技術のことや、エンジニアの取り組みを発信していき、エンジニアの皆さんに「おもしろいことやってるな」とか「楽しそうなチームだな」と思っていただけるようなブログにしていきます。

(もしこの時点で「話を聞いてみたい!」と思っていただけた方は → お問合わせはこちら)

会社の紹介

私たちのサービスはBtoBサービスがほとんどですので、社名、サービス名ともにあまり知られていないのではないかと思います。少し紹介させてください。

f:id:tech-adish:20160628193937p:plain

アディッシュは株式会社ガイアックスのグループ会社です。 (ガイアックスのエンジニアブログはこちら → Gaiax Engineers' Blog)

アディッシュでは下記のようなサービスを提供しています。

ソーシャルアプリサポート事業

f:id:tech-adish:20160629113537p:plain:w280

サービスのサイトはこちら

ソーシャルアプリ(スマホゲームなど)を利用しているユーザーからのお問い合わせを、アプリ制作会社に代わって対応するカスタマーサポートサービス。

→お問い合わせを一元管理できるシステムを開発しています。

スクールガーディアン事業

f:id:tech-adish:20160629113453g:plain:w280

サービスのサイトはこちら

  • 学校非公式サイト(学校裏サイト)のパトロール
  • 学校生活上の課題となりうるインターネットでの個人情報流出やネットいじめへの対策
  • ソーシャルメディアの活用について学校関係者をトータルにサポートするコンサルティング

を提供しています。

→効率的にパトロールや対策を行うためのツールを開発・運用しています。

モニタリング事業

f:id:tech-adish:20160629113520g:plain

f:id:tech-adish:20160629113524g:plain

コミュニティパトロール サービスサイトはこちら
ソーシャルリスニング サービスサイトはこちら

24時間365日体制の自社運用センターにて、企業を中心とするお客様がお持ちのSNSやコミュニティを目視チェックしています。

→目視でチェックする際に効率的に作業がおこなえるためのツールを開発・運用しています。

Medio

Medio AppStore

スマホ1つで、友達同士の会話を収録して、ラジオ番組として配信できるiOSアプリです。

→iOSネイティブアプリを開発しています。

フロントサポート事業

サービスのサイトはこちら

ソーシャルを活用したアクティブサポートからサイト内のWeb接客につなげ、カスタマーサクセスを実現していく、という新しいコンセプトの攻めのCS。

まとめ

上記サービスを支える技術のことやエンジニアのこと。あるいはこれから始まる取り組みなどなど、アディッシュのエンジニアのことがわかるような記事を発信していきます。

これからよろしくお願いします!


アディッシュのエンジニアに興味がある方はこちらまでお問合わせください!