ユニファ開発者ブログ

ユニファ株式会社システム開発部メンバーによるブログです。

そのコマンドにロマンはあるか

こんにちは。ICT開発の柿本です。

全てのエンジニアに声を大にして問いたいのですが、 コマンドを覚えるのってツラくないですか?

自分の記憶力の問題なのか、この世にコマンドが多すぎるのかわかりませんが、『あのコマンドってどうやって使うんだっけ?』と思ってググると、検索結果のほとんどがアクセス済みだったりして、『そういえば最近調べたなぁ』となります。

しかも、Googleはご丁寧に『〇〇/〇〇/〇〇 にこのページにアクセスしました。』と表示してくれますが、『あれ?これ今日じゃない?』となるとパソコンを閉じたくなります。(そもそもあの表示に何の意味があるんだろう。。)

そんなこんなで私の作業メモにはつらつらと色々なコマンドが書いてあるわけですが、今日はその中の一部を恥を忍んで紹介します。

環境

OS: Mac OS 10.14  
シェル: zsh

『シェルは何を使ってますか?』
『zshです。』
『すごいですね!!』

はエンジニアトークあるあるですが、zshは補完機能が優れているのでどちらかというと すごくない人 こそ使うべきだと思います。

そして 適度にすごい人.zshrc をGitHubから拝借すれば、労せずに楽できます。
超絶すごい人.zshrc は逆に混乱するので避けたほうが良いです。

ただ、ネットで見つけたコマンドがなぜか動かなくて4時間くらい潰した結果『パラメータ文字列の をエスケープしないとダメだった〜!!』みたいなことはままあるので、その辺は気をつけたほうがいいです。

まあその4時間の学びが大事だったりしますが。

それではさっそく紹介していきます!!

curlしたい

% curl -X POST -H 'Content-Type:application/json' http://localhost:3000/api/v1/users -d '{"name":"Taro"}'

APIサーバーの動作確認をする時によく使います。

『これを覚えるのがツラいの?』てレベルですが、 Content-Type だったか ContentType だったかいつも間違えるので、メモしておくことにしました。

認証を通しつつcurlしたい

% curl http://localhost:3000/api/v1/users/1 -H "Authorization:Bearer token_string"

これもAPIサーバーの動作確認をする時によく使います。

これは Authorization だったか Authenticate だったか Authentication だったか混乱するので、メモしておくことにしました。

『AuthorizationとAuthenticationの違いを学び直してこいよ!』というツッコミもありそうですが、そもそもそれが頭に入るのであればこのコマンドはとっくに覚えられています。

データをたくさん作りたい

% i=0; while [ $i -le 9 ]; do curl -X POST -H 'Content-Type:application/json' http://localhost:3000/api/v1/users -d '{"name":"Taro_0'"$i"'"}'; i=$(expr $i + 1);done

API経由でデータをたくさん作る時のワンライナーです。

単純にダミーデータが欲しかったり、負荷をかけてみたかったり、データをたくさん欲しいことがあったので、メモしました。

大量に投入したい時はDBに直接入れたほうがパフォーマンスはいいですが、リレーションなども気にしながら入れるのであれば、API経由が一番気が楽だったりします。

nginxのaccess.logを解析したい

% find . -name 'access.log.201901011[0-3]_0.log' | xargs grep -h -o '\turi:/\S*' | sed -e 's/[0-9]\{1,\}/*/g' | sort | uniq -c | sort -r

ログを1時間でローテーションしていて『4時間分のログを解析したい!』といった時に、複数ファイルをまたがってややこしくなるので、メモしました。

やっていることは、

  1. 対象のログファイルをfind(ここでは10時から13時まで)
  2. uri: xxx の部分だけgrepで抽出
  3. /users/1 /users/2 は同じuriとして扱いたいので数字を * にsedで置換
  4. uniq -c するために sort
  5. 最後にもう1回降順ソートしてアクセスランキング的な並びにする

これはエンドポイントごとのアクセス数を見たい時でしたが、 grep の対象を変えればいろいろな切り口の解析ができます。

ログファイルを解析するツールもあるのでしょうが、大事なのはロマンなのです。

RailsアプリをDockerでサクッと動かしたい

% docker run --name ruby-tmp -it -v `pwd`:/var/ruby -w /var/ruby -p 3000:3000 ruby:2.6.1 /bin/bash
# bundle install
# apt-get update && apt-get install -y nodejs
# rails db:migrate
# rails s

ちょっとこのRailsアプリを動かしてみたいな、という時に使います。
PostgreSQLなど使っているともう少し工夫が必要ですが、SQLiteなら大丈夫です。

普段の開発でdocker-composeを使っていると、 docker-compose updocker-compose down くらいしか使わず、 docker コマンドは忘れ去ってしまうのでメモしました。

弊社ではサーバーサイドエンジニアの採用時に簡単なRailsアプリを課題(これが結構面白い)として作っていただくのですが、それを動かしてみる時に便利です。

終わりに

私のメモにはもっとたくさんコマンドが書いてあって、

『それくらい覚えろよ』

もしくは

『わざわざコマンドでやる必要ないよね』

のどちらかに分類できるのですが、コマンドにはロマンがあると信じてこれからもどんどんメモしていこうと思います。

おしまい

DataBindingでLoading

こんにちは、スマホチームのあいばです。
最近思い立ってオンライン英会話に入会しました。リモートの時には自宅でせっせとランチ英会話しているのですがまだまだ効果は見えません。。
最近お仕事の方はiOS中心だったのですが、Android案件も盛り上がって来たのでおさらいしています。
そこで今回はGithubBrowserSample を読み、処理中のProgressBarの表示についてどんなことしてるか見てみようと思います。

GithubBrowserSample概要

リポジトリ検索、リポジトリ詳細、ユーザ情報の3画面からなるアプリです。
今回はリポジトリ詳細のコードから引用して説明したいと思います。(この機能を選んだ理由は特にないです)
図は各モジュールが相互にどのようなやり取りをしているかを示しています。公式より f:id:unifa_tech:20190307100655p:plain

build.gradle

app/build.gradle
DataBindingを利用してProgressBarの表示を更新しているため設定を有効にする必要があります。

dataBinding {  
    enabled = true  
}

レイアウト

loading_state.xml
ProgressBarとリトライボタン、エラーテキストを持つレイアウトです。
変数resourceの値を参照し各Viewのvisibilityを切り替えています。
ほとんどJavaと同じようにコードを書くこともできます。詳細は公式のドキュメント でどうぞ。

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
            <import type="com.android.example.github.vo.Resource" />
            <import type="com.android.example.github.vo.Status" />
            <!-- 変数を宣言しレイアウトファイル内のバインディング式で使えるようにする -->
            <variable
                name="resource"
                type="Resource" />
            <variable
                name="callback"
                type="com.android.example.github.ui.common.RetryCallback" />
        </data>

        <LinearLayout
            android:orientation="vertical"
            <!-- カスタムセッターでvisibilityを設定 -->
            <!-- データがセットされたら非表示になる -->
            app:visibleGone="@{resource.data == null}"
            android:layout_width="wrap_content"
            android:gravity="center"
            android:padding="@dimen/default_margin"
            android:layout_height="wrap_content">
            <ProgressBar
                <!-- ローディング中は表示する -->
                app:visibleGone="@{resource.status == Status.LOADING}"
                style="?android:attr/progressBarStyle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/progress_bar"
                android:layout_margin="8dp" />
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/retry"
                android:id="@+id/retry"
                <!-- リスナーバインディングを利用してリトライを実行する -->
                android:onClick="@{() -> callback.retry()}"
                <!-- エラー時にリトライボタンを表示する -->
                app:visibleGone="@{resource.status == Status.ERROR}" />
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/error_msg"
                android:text="@{resource.message ?? @string/unknown_error}"
                <!-- エラー時にTextViewを表示する -->
                app:visibleGone="@{resource.status == Status.ERROR}" />
        </LinearLayout>
    </layout>

BindingAdapters.kt
BindingAdapterを利用してカスタム セッターを作成しています。
xmlに直接バインディング式を書いても実現できますが、レイアウトがややこしくなるのは辛いですよね。

    object BindingAdapters {
        @JvmStatic
        @BindingAdapter("visibleGone")
        fun showHide(view: View, show: Boolean) {
            view.visibility = if (show) View.VISIBLE else View.GONE
        }
    }

ちなみにカスタムセッターを使わない場合の実装はこんな感じでしょうか。 loading_state.xmlより

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <data>
            <!-- Viewクラス追加 -->
            <import type="android.view.View"/>
             ...
                <LinearLayout
                android:orientation="vertical"
                <!-- 書き換え -->
                <!-- app:visibleGone="@{resource.data == null}" -->
                android:visibility="@{resource.data == null ? View.VISIBLE : View.GONE"

                android:layout_width="wrap_content"
                android:gravity="center"
                android:padding="@dimen/default_margin"
                android:layout_height="wrap_content">
                ...

repo_fragment.xml
loading_state.xmlをインクルードしています。
アプリの名前空間と変数名を使用するとインクルードされたレイアウトに変数をセットできます。

    <layout xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            ...
            <variable
                name="retryCallback"
                type="com.android.example.github.ui.common.RetryCallback" />
        </data>
        <androidx.constraintlayout.widget.ConstraintLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <include
                layout="@layout/loading_state"
                <!-- loading_state.xmlに宣言したresource, callbackに値をセット -->
                app:resource="@{(Resource) repo}"
                app:callback="@{() -> retryCallback.retry()}"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="8dp"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginEnd="8dp"
                app:layout_constraintBottom_toBottomOf="parent"
                android:layout_marginBottom="8dp"
                android:layout_marginTop="8dp"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>

Repository

NetworkBoundResource.kt
sqlite DBとネットワーク共通でリソースを提供するクラスです。 このクラスでResourceの状態を管理しています。

    init {  
        // 生成時に処理中ステータスをセット
        result.value = Resource.loading(null)  
        @Suppress("LeakingThis")  
        // ローカルDBから読み込み
        val dbSource = loadFromDb()  
        result.addSource(dbSource) { data ->  
            result.removeSource(dbSource)  
            if (shouldFetch(data)) {  
                // DBから取得できなかった場合ネットワークから取得する
                fetchFromNetwork(dbSource)  
            } else {  
                result.addSource(dbSource) { newData ->  
                //DBから読み込めた場合はステータスをSuccessへ変更
                    setValue(Resource.success(newData))  
            }  
        }  
    }

コードは省略しますが、ネットワークからの取得成功/失敗のタイミングでもステータスを変更しています。 またNetworkBoundResourceはabstractになっていて、各機能のRepositoryクラスで匿名クラスを実装しカスタマイズしています。

RepoFragment.kt
FragmentではBindingクラスを生成し、値を適用しています。 RepoFragmentBindingクラスは自動生成されるクラスなのですが、名前はレイアウトファイル名から決まるようです。

    var binding by autoCleared<RepoFragmentBinding>()
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {  
        val dataBinding = DataBindingUtil.inflate<RepoFragmentBinding>(  
            inflater,
            R.layout.repo_fragment,
            container,
            false
        )
        // リトライボタンのイベント処理を実装
        dataBinding.retryCallback = object : RetryCallback {  
            override fun retry() {  
                repoViewModel.retry()  
            }  
        }  
        binding = dataBinding  
        return dataBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {  
        repoViewModel = ViewModelProviders.of(this, viewModelFactory).get(RepoViewModel::class.java)  
        repoViewModel.setId(params.owner, params.name)
        
        // LiveDataの変更を監視するためのLifecycleOwnerをセットします
        binding.setLifecycleOwner(viewLifecycleOwner)  
        binding.repo = repoViewModel.repo  
        val adapter = ContributorAdapter(dataBindingComponent, appExecutors) { contributor ->
            navController().navigate(
                RepoFragmentDirections.showUser(contributor.login) 
            )  
        } 
        this.adapter = adapter  
        binding.contributorList.adapter = adapter  
        initContributorList(repoViewModel)  
    }

最後に

以前からLoadingの表示ってストレスフルだなぁと感じていたこともあり今回調べてみました。
"リクエスト投げる前にProgressBar/Dialogを表示してコールバック受けたら消す"みたいな実装をよくみる(やってた)と思いますが、バグの宝庫なんですよね…
成功、失敗、キャンセル、画面回転…さらにリクエスト投げるまでのシーケンスも次第に複雑になっていくのでとても面倒見きれない。
自分のプロダクトに適用できるかはまだわかりませんが、このサンプルではステータスが変わればDatabindingで表示が切り替わるので余計なことを考えなくて済むのではないでしょうか。
また、LiveDataを使えばライフサイクルとも連動してくれるので組み合わせて使えば何かと便利そうです。
DataBindingでできることを色々調査できてよかったです!

なぜ違う値を見たのかーーパソコンと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を用いたサービス開発を担う(東京)の採用情報 | ユニファ株式会社