ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

なぜ違う値を見たのかーーパソコンとRaspberry Piで実験

こんにちは、プロダクト開発部のちょうです。

結構前に、この記事で、C++11で実験しようと書いていたので、先週ちょっとやってきました。

tech.unifa-e.com

まずコード(C++11)を出します。

#include <iostream>
#include <thread>

int data = 0;
bool flag = false;

void thread1() {
    data = 100;
    flag = true;
}

void thread2() {
    while(!flag) {
    }
    std::cout << data << std::endl;
}

int main() {
    std::thread t2(thread2);
    std::thread t1(thread1);
    t1.join();
    t2.join();
    return 0;
}

C++のコードを読めない方もいると思いますが、ここですこしコードを説明します。

グローバル変数dataとflagがあります。関数thread1で、dataを100にセットして、flagをtrueにします。関数thread2で、まずflagをチェックして、もしtrueでしたら、dataの内容を標準出力に出します。

関数mainの中では、関数thread1とthread2の内容を別々のスレッドで実行させます(thread2をthread1の前に書くのは問題発生しやすいようにするためです)。

つまりやりたいことはこれです。

f:id:unifa_tech:20190225144312p:plain

コード自体は問題なさそうですが、実際実行したら、どうですか。

実験では

  • 普通のパソコン(x86_64)
  • Raspberry Pi 3 b+(ARMv7)

を使います。x86_64とARMv7はCPUのアーキテクチャです。ちなみに、ARMプラットホームをテストしたいなら、スマートフォンよりRaspberry Piでやるほうがやすい、安心と思いますのでおすすめです。

パソコンで結果は

100

予想通り。ではRaspberry Piではどうですか。

(プログラムが帰ってこない)

!!! ちょっと予想外のことが起きたかもしれません。ただプログラムがとてもシンプルで、怪しいところがなさそうですが。

ここで悩むより、もう一つのプログラムを動かしましょう。

#include <iostream>
#include <thread>
#include <atomic>

std::atomic_int data{0};
std::atomic_bool flag{false};

void thread1() {
    data.store(100, std::memory_order_relaxed);
    flag.store(true, std::memory_order_release);
}

void thread2() {
    while(!flag.load(std::memory_order_acquire)) {
    }
    std::cout << data.load(std::memory_order_relaxed) << std::endl;
}

int main() {
    std::thread t2(thread2);
    std::thread t1(thread1);
    t1.join();
    t2.join();
    return 0;
}

コードの内容を気にしないでください。おおまかの内容はプログラム1と同じです。

パソコンで実行すると

100

問題ない、今度Raspberry Piはどうですか。

100

ようやくRaspberry Piでも正しく実行できました。

2つのプログラムで何が違うというと、二番目のプログラムは並列処理を考慮してちゃんと対応していたということです。プログラム1で帰ってこないというのは、thread2でflagをセットしたのを見えなくて無限ループしてしまったようです。まさにタイトル通り「違う値を見た」ということです。このシリーズの最初でCPUの設計はプログラムに影響があると説明したのですが、実験で実際を見れたんです。

ここでもっと原因追及をしたいですが、正直エンジニアとして複数のCPUアーキテクチャを理解する必要がありません。プログラム言語が提供する統一されたMemory Modelを学ぶほうがいいと思います。

最後に

いかがでしょうか。CPUのアーキテクチャが並列処理のプログラムに影響があるかのを体験できましたでしょうか。これからも、並列処理のプログラムを書くとき注意をしましょう。

Railsで更新系のSQLだけログに出力する方法

最近、鼻づまりがひどいWebエンジニアのほんまです。 今までだましだましやってきましたが、ついに花粉症になってしまったかもしれません😷🌸

さて、今回も前回に引き続き、ログ出力で工夫していることを紹介しようと思います。

背景

  1. 調査依頼などでログを追っていると、いつ、どういった流れでデータが追加/変更/削除されたのか、知りたい時がある。
  2. そのような時、SQLがログに出力されていると調査がはかどる。
  3. Railsではログレベルを DEBUG にすることで、SQLがログに記録されるようになる。
  4. しかし、SELECTのログも記録されるため、大量のログで見づらくなったり、ディスクを圧迫する問題がある。
  5. 更新系のSQLだけログに出力する方法はないものだろうか?
続きを読む

インターネット?ネットワークのおはなし

ごきげんようユニファのインフラ見てますすずきです。

最近弊社にイングランドからエンジニアが入ってきて、
社内に(エンジニア近辺)英会話の機運の高まりを感じています。
(年末に勉強しなきゃといって全くできていない私の話はまた今度)

さて普段ブログは仕事で使ってる技術の話してるんですが、 今回は簡単にインターネットというか、ネットワークのお話を【超噛み砕いて】できればなと思います。

続きを読む

顔認証におけるいろいろな損失関数(Loss function)

こんにちは、iOSエンジニアのしだです。いつの間にかR&Dチームとして1年経ってしまいました。R&Dというと社内でも何をやっているか理解しづらい部署であり、いよいよ社内でもぼっち感がでてきました。 ここ最近は、顔認証(Face Recognition)ばかりやっていて、iOSからも同僚からもだいぶ離れてしまいさびしさも感じられる今日このごろです。 今回は、顔認証におけるディープラーニングで学習する際に用いられる損失関数についてどのような違いがあるか見てみたいと思います。

続きを読む

深層学習で馬を見分ける(その1)

f:id:unifa_tech:20190124213022j:plain:w200:right R&Dエンジニアの浅野です。息子が乗馬を習っていてレッスンや試合の様子を見る機会があります。馬にはそれぞれクセや個性があり、それを理解しながら見ると「あ、気持ちが高ぶっている馬をよくコントロールしてるな」とか「あそこで左に逃げないように工夫してるな」など奥深さを感じて楽しみが倍増します。そのため誰がどの馬に乗っているのかをその場で知りたいことがよくあるのですが、素人には馬を見分けるのが非常に難しいです。そこで今回は画像分類で大きな力を発揮する深層学習を使って馬を識別してみたいと思います。

指導者や経験豊富な人が見分けるときは全身をみて判断しているのですが、全身を対象にすると鞍やゼッケン、騎乗者などの影響を取り除くためにセグメンテーションを行ったり、様々な姿勢の影響を考慮したりする必要があり難易度が上がるため、よりシンプルな顔認証で識別に挑戦していきます。馬の顔認証というと難しそうなイメージがありますが、サケ(!)で顔認証をやっている人もいるくらいなのできっと大丈夫なはず。

1回の記事に収めると長くなるので3回に分けて書いていきたいと思います。

  1. 顔の検出器の作成 ← 今ここ
  2. 分類器の作成
  3. スマホで動くようにする

顔検出

人間の顔検出器に関しては学習済みのモデルがいろいろと公開されていますが、馬の顔検出器は見つからなさそうなので自分で作ることにします。下記の図のように、学習の前に正解ラベル付きデータを準備する必要があります。馬の顔検出モデル作成に必要なデータは、馬が写っている画像とそのどこに顔があるかという情報(いわゆるバウンディングボックスの位置)です。とりあえずこれを1,000データほど集めることを目指します。

f:id:unifa_tech:20190124212728j:plain
物体検出モデル作成の流れ

まず、ChromeDriverを使うことで一度にたくさんの画像をスクレイピングできるGoogle Images Downloadを用いて馬が写っている画像を収集します。キーワードをいくつか変えてスクリプトを走らせることで延べ1,500枚ほどの画像が集まりました。

f:id:unifa_tech:20190124224516j:plain:w300:left 次にクレンジングとして、md5の値をチェックして重複画像を省くことで1,250枚に、続いてざっと画像を閲覧しながら馬の顔が写っていないものを削除することで750枚が残りました。これらの画像にLabelImgを使って馬の顔の部分を囲っていきます(左図)。一つの画像に複数の顔が写っていることもあるため、合計で1,193個のラベル付きデータが得られました。

RetinaNetの学習

物体検出の学習のベースとなるモデルとしてRetinaNetを使用します。物体検出の方法は大きく分けてスピード重視のOne Stage系と精度重視のTwo Stage系に分けられます。RetinaNetはOne Stage系でありながらTwo Stage系の良いところを取り入れてスピードと精度の両立を実現したモデルです。実装はKeras RetinaNetを参考に行います。1,193個のデータのうち、約10%の120個をバリデーションデータとし、残りの1073個を学習データとします。RetinaNetのバックボーンはCOCOデータセットで学習済みのResNet50を使用します。バックボーンは重みを固定し、それ以外の部分の重みを学習によって更新させていきました。

f:id:unifa_tech:20190124212823j:plainf:id:unifa_tech:20190124212813j:plain
学習の様子。左:学習時の損失(≒誤差)、右:バリデーションデータでの精度

50エポック学習をまわして損失は順調に下がっていますが、右側のグラフを見ると10エポック以降は過学習になっている感じです。最も汎化性能が良さそうな10エポック終了時点でのモデルを使い、学習に使用していないテストデータに対して顔検出を行った結果の例が下記です。ちゃんと馬の顔検出ができているようですね。データ数が少ないこともあり、時々検出できなかったりしっぽの部分を顔と誤認識したりすることもありますが、趣味用途なので目をつぶりましょう。

f:id:unifa_tech:20190125085435p:plainf:id:unifa_tech:20190125085430p:plain

次回は検出した顔の部分を切り出して識別を行うための分類器を作ります。

保育の世界を変えるエンジニア募集中

(今回のブログは保育とは全く関係ありませんでしたが)ユニファではテクノロジーで保育を変えていく仲間を大募集中です。少しでも興味を持たれた方はぜひ声をかけてください!

【AI系技術サーバサイドエンジニア】R&Dチームにて機械学習やAIを用いたサービス開発を担う(東京)の採用情報 | ユニファ株式会社

Datadog Meetupに参加してきました!けど記事の内容はTerraform v0.12絡み!

おはこんばんちは!

インフラ担当のすずきです!

2019年初ブログでしょうか?(記憶が曖昧

正月明け頃から咳がではじめて、今週頭くらいまでずっと悩まされていました。
後述するイベントまでにはなんとか収まってホッとしております。

昨晩DatadogのMeetupイベント「Datadogはじめました! - connpass」に参加してLTもしてまいりました。
咳が収まってなかったらただでさえ拙い発表が、ひどいものになっていたのではないか…!!(人生2回目のLT)

さて今回のブログの内容ですが、参加した内容は割愛して私がLTで発表したTerraform v0.12 とそれに対応したdatadog-providerを使った話をもう少し詳しく書こうかと思います。

発表資料は末尾に記載しておくのでご興味あるかたは御覧ください。

続きを読む

JavaScriptに数独のヒントをもらおう その1

新年明けましておめでとうございます。今年もよろしくお願いいたします。

先週、新年早々ハトにフンを落とされました。ウン(運)のついている柿本です。
※フンはきちんと拭き取って洗濯したのでご安心を!

2018年最後のエントリーで赤沼が発表している通りこの開発ブログでは年末にアクセスランキングが発表されるようです!つまり、年始のこのエントリーはかなり有利なはず!

もうランキングにノミネートされた気分でおりますが、冒頭にくだらないことを書いていると離反されてしまうので、本題に入ります。

今回のエントリーではJavaScriptを使って、数独のヒントを教えてもらうことにしました。

タイトルに「その1」とあることから察していただきたいのですが、今回のエントリーでは完成まで行き着いていないので悪しからず。「その2」以後があるかもわからないので悪しからず。

夫の威厳と数独

昨年の終わりにひょんなことから私の妻が数独にはまりました。

私はもともと数独が好きで、週末の新聞に載っているものを必ず解いているのですが(それ以外で私が新聞を開くことはない)、文系人間の妻が好きになるとは意外でした。

とはいえ、妻はそれほどスラスラ解けないので、少し詰まると「ヒントちょうだい」と言ってきます。もちろん私は上から目線で「これはね、、」と教えるわけですが、もっと上から目線の “僕が教えるまでもないよね” スタンスでいたいので、代わりにJavaScriptにヒントを教えてもらうことにしました。

今ここ

とりあえず今時点のものをこちらに上げました。
https://unifa-inc.github.io/sudoku_solver/

「ヒントちょうだい」ボタンを押すと、数字が特定できるマスをピンクにして教えてくれます。難しくてヒントが思いつかないと、開き直って5歳児になります。

f:id:unifa_tech:20190110222021g:plain
実際に動かしたGIF。途中一度だけ叱られます。

数独の解き方

数独ってそもそも何?という方はWikipediaあたりをご覧ください。

ja.wikipedia.org

簡単に説明すると、3×3のブロックで区切られた9×9のマス目に、それぞれの行・列・ブロックに1〜9が1つずつ入るように数字を埋めていくゲームです。

数独にはいくつかの解き方がありますが、一般的に大きく以下のステップに分かれます。
①ルールをそのまま適用する
②2国同盟(n国同盟)
③仮定法(背理法)

それぞれの説明はgoogle先生にお願いするとして、私は①でさらに以下の2つの解き方で進めます。

①-1:1つのブロックの1つの数字に注目して、以下を全ブロック×全数字(1〜9)に対して繰り返し行います。

  • 水平方向の2つのブロック両方にその数字がある場合、ブロック内の行が特定できる
  • 垂直方向の2つのブロック両方にその数字がある場合、ブロック内の列が特定できる

 ===> 行と列が交差しているマスにはその数字が入る

f:id:unifa_tech:20190110180059p:plain:w300
例(①-1)左上のブロックに注目した時に、赤枠と青枠が交差している黄枠のマスに1が入ることがわかる

①ー2:1つの行(もしくは列)の空マスに注目して、以下を全行(列)に対して繰り返し行います。

  • とあるマスに入れられる数字が1つのみである
  • とある数字を入れられるマスが1つのみである

 ===> どちらかの場合、そのマスにはその数字が入る

f:id:unifa_tech:20190110180150p:plain:w300
例(①-2)赤枠の行に注目した時、青枠と紫枠には1を入れられないため、黄枠のマスに1が入ることがわかる

完成形は③の解法まで適用されたヒントを出すことなのですが、このエントリーでは①ー1で解ける範囲内でヒントを出すことをゴールとします。

処理の流れ

左上のブロックから順番に1から9までの各数字について、以下の検証を行うことにします。

ターゲットとする数字(n)について、

  1. ブロック内に既にnが存在するかを確認
    • 存在する場合 → 次のブロックへ
    • 存在しない場合 → 処理を続行
  2. ブロック内の空きマスを確認
    • 空いていないマス → そのマスにはnは入らない、という制約を保持
    • 空いているマス → 処理を続行
  3. 水平方向(左右)のブロックにnが存在するかを確認
    • 存在する場合 → 同じ行にはnは入らない、という制約を保持
    • 存在しない場合 → 処理を続行
  4. 垂直方向(上下)のブロックにnが存在するかを確認
    • 存在する場合 → 同じ列にはnは入らない、という制約を保持
    • 存在しない場合 → 処理を続行
  5. 制約のかかっていないマスの数を確認
    • 1つのみの場合 → そのマスを色付け表示して処理を終える(そのマスに nが入ると断定できる ため、ヒントとして色付け)
    • 2つ以上の場合 → 次のブロックへ(このブロックでは nが入るマスを断定できない

ロジック

ソースコードをGitHubで公開しておりますのでそちらをご覧ください。

github.com

工夫と課題

工夫したところ: 制約の持ち方

『処理の流れ』で書いている「制約」をどうデータとして保持するか、に少し悩みました。

結果、3×3のブロックのマスを表す2次元配列に0と1を保持し(0:制約あり/1:制約なし)、最終的に「1」が1つだけの場合はnがそのマスに入ると断定できる、と判断する方法をとりました。

課題なところ: 公然の多重ループ

パズル系に関わらず、ゲーム要素のあるものを作ろうと思うと必ず多重ループや多重分岐の罠にハマる気がします。世にある多くのゲームたちもやはり多重ループが量産されているのだろうか?と疑問に思いました。

知見のある方、ぜひ教えてください。

終わりに

サーバーサイドエンジニアの仕事は、DBのデータを出したり入れたり、の繰り返しです。実際には検証したりくっつけてみたり離してみたりもあるのですが、意外とロジカルなロジックを書くことは少ないです。

なので、たまにこういうロジックロジックしたものを書くとなんだか心が温まりますので、皆様もぜひ!

それではみなさま、良い2019年をお過ごしください!

保育をハックするエンジニア募集中

ユニファでは一緒に働く仲間を募集中です。DBのデータをただ出し入れすることに飽き飽きしている方、もっともっと出し入れしまくりたいぜ!という方、ぜひご応募ください!

www.green-japan.com