Galapagos Tech Blog

株式会社ガラパゴスのメンバーによる技術ブログです。

PhoenixでSwaggerする

御機嫌よう。ガラパゴスのおとめです。

今日は、「次に来る大物Web言語」と前評判の高いElixirと、そのRailsぽいフレームワークPhoenixに触りつつ、RESTful APIを作るには欠かせないSwaggerドキュメントを生成してみようと思います。

あなたは、誰あれ?

はじめに登場人物の簡単な紹介をしておきましょう。

ElixirとPhoenix

ガラパゴススマートフォンアプリの開発を主な事業としている会社です。一口にスマートフォンアプリと言っても、そこは端末だけで完結する世界ではなく、サーバとのやりとりが必要になります。

もちろんサーバーサイドはどんな言語で実装しても構わないのですが、パフォーマンスが気になったり、ホットデプロイしたかったり、煩わしい設定ファイルを書きたくなかったり、JEEが嫌いだったり、ActiveRecordが遅かったり、しますよね?

そんな時、あなたの傍には、ElixirPhoenixが佇んでいます。

簡単に言うと、関数型ぽい言語にRailsぽいフレームワーク、速くてホットデプロイ上等でめっちゃRails、それがElixir + Phoenixです。Elixirが言語でPhoenixフレームワーク。と言われても「?」ですね。百聞は一見にしかず、とも申しますゆえ、実際に導入してみましょう。

Swagger?

ガラパゴススマートフォンアプリの開発を主な事業としている会社です。一口にスマートフォンアプリと言っても、そこは端末だけで完結する世界ではなく、サーバとのやりとりが必要になります。

でもでも、もしもあなたがサーバーサイドのエンジニアなら、どうやってAPIの仕様を公開するの? どうしたらちゃんとドキュメントのメンテナンスできるの? みたいなことを思いますよね? それに、もしもあなたがアプリのエンジニアなら、サーバにはどんなAPIがあるの? どんな仕様なの? ちょっと試してみたいんだけど? と思いますよね?

そう思った時、あなたの傍にはSwaggerが佇んでいます。

簡単に言うと、RESTful APIの仕様を記述するための仕様、それがSwaggerです。と言われても「?」ですね。百聞は一見にしかず、とも申しますゆえ、実際に導入してみましょう。

環境作成

まだElixirの環境をお持ちでない方も、公式の手順で簡単に用意することができます。Elixirの環境ができたらおもむろにPhonenixもインストールしましょう。

また、Phoenixでは、デフォルトのデータベースがPostgreSQLになっていますので、必要ならインストールします。もしも事情があってPostgreSQLをインストールできない場合、この先の手順が多少変わったり、設定ファイルを変更したりする必要があります。この記事では、PostgreSQLを使えるものとして書いていきます。

なお、PostgreSQLはインストール直後のデフォルト設定ではpostgresユーザーでしか接続できませんので、pg_hba.confを適切に変更しておきます。

次にSwagger UIをインストールします。これは、生成したSwaggerドキュメントを表示するために使います。

Swagger UIにはWebサーバが必要ですのでお好みで。ここではnginxを使います。nginxをインストールしたら必要なファイルをコピーします。

$ git clone https://github.com/swagger-api/swagger-ui
$ sudo cp -R swagger-ui/dist /var/www/html/

アクセスしたら次のような画面が出てきましたね?

f:id:glpgsinc:20160825194932p:plain

プロジェクトの作成

必要な環境ができたら、いよいよプロジェクトを作ります。もちろん皆様はこの記事をここまで読む間に公式のガイドをチラ見くらいはしていて、すでにRailsをお使いの方なら、めっちゃRailsだとお分かりだと思います。rails newくらいに気軽に行ってみましょう(ここではswagger_sample_appとしていますが、もちろんお好みの名前をつけることができます)。

$ mix phoenix.new swagger_sample_app

実行すると、Railsをお使いの方にはどこかしら見覚えのある世界が出現すると思います。こんな風に。御機嫌よう世界。

swagger_sample_app
 +- config
 +- web
 |   +- controllers
 |   +- models
 |   +- views
 |   +- router.ex
 +- test
 +- mix.exs
 +- mix.lock

この他にもいろいろなファイルやらディレクトリやらができますが、とりあえずここでは、次のような対応が分かればよいでしょう。

Rails Phoenix
Gemfile mix.exs
Gemfile.lock mix.lock
config/routes.rb web/router.ex
appディレクト webディレクト

なお、Railsdatabase.ymlなどに相当するものはなく、Phoenixでは、config/dev.exsなどの、config配下の各ステージのファイルに記述していくことになります。

APIの作成

Phoenixでは、scaffoldどーん♪ みたいな感じで、APIも作れます。

$ mix phoenix.gen.json Sample samples foo:integer bar:string

実行したらルーティングとマイグレーションに関するメッセージが表示されたと思います。

まずルーティングの設定をします。メッセージはweb/router.exに書いて、みたいなあっさりしたものだったと思いますが、scopeや、今回作成するのはAPIですので、pipe_through :apiなども書く必要があります。

scope "/", SwaggerSampleApp do
  pipe_through :api
  resources "/samples", SampleController, except: [:new, :edit]
end

次にマイグレーションしましょう。まだデータベースを作っていないので、まずはデータベースの作成から。といってもrake db:create && rake db:migrateと同じくらいの感じでいけます。

$ mix ecto.create
$ mix ecto.migrate

これでCRUDを備えたAPI一式ができてしまいます。さらにはRailsにGrapeを導入した時のようにroutes.rakeをちょっと書くなどというひと手間もなく、作ったAPIのルーティングを確認することができます。

$ mix phoenix.routes
  page_path  GET     /             SwaggerSampleApp.PageController :index
sample_path  GET     /samples      SwaggerSampleApp.SampleController :index
sample_path  GET     /samples/:id  SwaggerSampleApp.SampleController :show
sample_path  POST    /samples      SwaggerSampleApp.SampleController :create
sample_path  PATCH   /samples/:id  SwaggerSampleApp.SampleController :update
             PUT     /samples/:id  SwaggerSampleApp.SampleController :update
sample_path  DELETE  /samples/:id  SwaggerSampleApp.SampleController :delete

デフォルトのインデックスページのほかに、sample_pathに今回作ったAPIができていますね。早速試してみましょう。

まず、phoenixを起動します。デフォルトでは4000番ポートで起動します。

$ mix phoenix.server

APIをコールしてみます。

$ curl http://127.0.0.1:4000/samples
{"data":[]}

まだ何も登録していないので空配列が帰ってきました。

Swaggerドキュメントの作成

さて、APIができたのでドキュメントを公開しましょう。

まず、プロジェクトにSwaggerを導入します。今回はphoenix_swaggerを使ってみます。

  # ...
  defp deps do
    [# ...,
     {:phoenix_swagger, path: "~> 0.1.4"}]
  end
  # ...
  def swagger_info do
    [version: "0.0.0", title: "Swagger sample"]
  end
  # ...

依存関係をインストールします。

$ mix deps.get

では、先ほど作成したAPIにドキュメントを書いていきましょう。ここではさわりとして、GET /sampesのドキュメントを書いてみます。

swagger_modelAPIの説明を書きます。また、レスポンスの型を明示したい時には、responsesの3番目のパラメタを指定します。このパラメタはfunction/0ですので、Swaggerに対応したレスポンスを返す関数を書きます。今回の例ではindex_schemaになります。

defmodule SwaggerSampleApp.SampleController do
  use SwaggerSampleApp.Web, :controller
  use PhoenixSwagger

  alias SwaggerSampleApp.Sample

  plug :scrub_params, "sample" when action in [:create, :update]

  swagger_model :index do
    description "登録したレコードの一覧を取得します"
    responses 200, "一覧", index_schema
  end

  def index_schema do
    %{type: :array, title: "一覧データ",
      items:
        %{title: "レコード",
          type: :object,
          properties: %{foo: %{type: :integer},
                        bar: %{type: :string}}}
     }
  end

  def index(conn, _params) do
  # ...

では、Swaggerで読み込むためのjsonを生成しましょう(プロジェクトのルートディレクトリで実行します)。

$ mix phoenix.swagger.generate

実行すると、swagger.jsonが出力されましたね? nginxで表示するために、/var/www/htmlあたりにコピーしまして、先ほどブラウザで表示したSwaggerで、おもむろにswagger.jsonのURLを入力してExploreしてみましょう。

するとこのように、先ほど書いたドキュメントが出てきましたね?

f:id:glpgsinc:20160825195006p:plain

でも、諸兄諸姉は、RDocやJavadoc、もしかしたら万が一、Excel仕様書などと比べて何がいいのか、などとお思いでありましょう?

CORSの設定

先に進む前に、CORSの設定を行います。SwaggerとPhoexnixは別のサイトになりますので、アクセスを許可する必要があります。

Phoenixの設定

今回はcors_plugを使ってみます。

  defp deps do
    [# ...,
     {:phoenix_swagger, path: "/path/to/phoenix_swagger"},
     {:cors_plug, "~> 1.1"}]
  end

依存関係のインストールはもうお分かりですね?

$ mix deps.get

READMEにしたがってweb/router.exを編集しましょう。

  pipeline :api do
    # originの値は実行するサーバのIPアドレスやDNS名など適切に設定します
    plug CORSPlug, [origin: "http://example.com"]
    plug :accepts, ["json"]
  end
  # ...
  scope "/", SwaggerSampleApp do
    pipe_through :api
    resources "/samples", SampleController, except: [:new, :edit]
    options "/samples", SampleController, :options
    options "/samples/:id", SampleController, :options
  end

編集したらPhoenixを再起動します。

Swaggerの設定

次に、先ほど生成したswagger.jsonを編集しましょう。最後の方に、"host":"localhost:4000"と書かれていますので、この部分を"host":"example.com:4000"のように、Phoenixを実行しているサーバのDNS名またはIPアドレスに変更します。

SwaggerからAPIを実行する

さて、皆様はすでにSwaggerの画面上にある「Try it out!」というボタンにお気づきでしょう。ようやくこのボタンを押す時がやってきました。

f:id:glpgsinc:20160825195212p:plain

まあ、まだデータを登録していないので空の配列が帰ってくるのですが、つまりこうやって実際にAPIを叩くこともできるのですね。もちろんパラメタのあるAPIならパラメタを指定することもできるのですが、今回はここまでにしましょう。

さいごに

ガラパゴスでは、業務で使う技術だけでなく、いろいろな言語やツールを試してみちゃう仲間を大絶賛超募集しています。皆様の応募をお待ちしています。

RECRUIT | 株式会社ガラパゴス iPhone/iPad/Androidのスマートフォンアプリ開発

では、御機嫌よう。

TensorFlowでXOR問題を考察してみた

はじめに

こんにちは、Webチームの@vanhuyzです。

最近、暇なときにディープラーニングを勉強したり、TensorFlowをいじったりしています。 今回はまず簡単な問題を例に、理論と実際を比較しようと思います。

TensorFlowでXOR問題を解く

目的

XORをフィットさせたい

理論

XORとは

  • XOR(排他的論理和)とは、与えられた2つの命題のいずれかただ1つのみが真であるときに真となる論理演算です。
  • 真理値表:
 x_1  x_2   y
0 0 0
0 1 1
1 0 1
1 1 0

数式*1

  • フィットしたい関数 (正解関数):  y = f(\mathbf{x})
  • データから予測する仮説関数:  y = \hat{f}(\mathbf{x};\mathbf{\theta})
    今回の目的は  \hat{f} fに近づけるようなパラメータ \mathbf{\theta}を求めることです。正解関数  fが未知ですが、点数が4つしかないので、とりあえず \hat{f}が全部の点を通ったら良いです。これが回帰問題ですね。

  • 入力と出力を行列・ベクトルでまとめると、

$$ \mathbf{X} = \begin{bmatrix} 0 & 0 \\ 0 & 1 \\ 1 & 0 \\ 1 & 1
\end{bmatrix}, \mathbf{y} = \begin{bmatrix} 0 \\ 1 \\ 1 \\ 0
\end{bmatrix} $$

になります。

  • ロス関数:できるだけシンプルにしたいため、平均二乗誤差(mean squared error - MSE)を採用します。

$$ J(\mathbf{\theta}) = \frac{1}{4} \sum_{\mathbf{x} \in \mathbf{X}} (f(\mathbf{x}) - \hat{f}(\mathbf{x};\mathbf{\theta}))^2 $$

  • ネットワークの構成

単純なニューラルネットワークを構成しましょう。以下の図のように、入力層・2つユニットの隠れ層・出力層があります。XORは非線形のため、入力層から隠れ層まで活性化関数としてReLU関数を適応します。昔、線形から非線形に変換するのはよくシグモイド関数を使われていましたが、最近ほとんどの場合ReLUを使うようになりました。ReLU関数は単純に  \textrm{relu}(t) = \max(0,t) です。

f:id:glpgsinc:20160727225009p:plain

(各層のユニットと重みのイメージです。バイアス項を省略しています)

まず、隠れ層の式は:

$$ \mathbf{h} = \begin{bmatrix} h_1 \\ h_2 \end{bmatrix} = \textrm{relu}\Big( \begin{bmatrix} w_1^{11}x_1 + w_1^{21}x_2 \\ w_1^{12}x_1 + w_1^{22}x_2 \end{bmatrix} + \begin{bmatrix} b_1^1 \\ b_1^2 \end{bmatrix}\Big) = \textrm{relu}(\mathbf{W_1}^\top \mathbf{x} + \mathbf{b_1}) $$

ここで、 \mathbf{W_1}, \mathbf{b_1} は入力層から隠れ層までの重み、バイアスです。

最後に、出力  y は:

$$ y = w_2^1 h_1 + w_2^2 h_2 + b_2 = \mathbf{W_2}^\top \mathbf{h}+ b_2 $$

になります。ここで、 \mathbf{W_2} = [w_2^1, w_2^2]^\top です。

全部まとめますと、関数 \hat{f}は以下のようになります。

$$ \hat{f}(\mathbf{x};\mathbf{W_1},\mathbf{b_1},\mathbf{W_2},b_2) = \mathbf{W_2}^\top \textrm{relu}(\mathbf{W_1}^\top\mathbf{x} + \mathbf{b_1}) + b_2 $$

  • では、このニューラルネットワークが本当にXORにフィットできるかどうかかを確認しましょう。そこで、1つの解を示します。 例えば、

$$ \mathbf{W_1} = \begin{bmatrix} 1 & 1 \\ 1 & 1 \end{bmatrix}, \mathbf{b_1} = \begin{bmatrix} 0 \\ -1 \end{bmatrix}, \mathbf{W_2} = \begin{bmatrix} 1 \\ -2 \end{bmatrix}, b_2 = 0 $$

全部の入力 \mathbf{X}をまとめて計算します。まず、

$$ \mathbf{X}\mathbf{W_1} = \begin{bmatrix} 0 & 0 \\ 0 & 1 \\ 1 & 0 \\ 1 & 1
\end{bmatrix} \times \begin{bmatrix} 1 & 1 \\ 1 & 1 \end{bmatrix} = \begin{bmatrix} 0 & 0 \\ 1 & 1 \\ 1 & 1 \\ 2 & 2
\end{bmatrix} $$

バイアス \mathbf{b_1}を足すと、

$$ \begin{bmatrix} 0 & -1 \\ 1 & 0 \\ 1 & 0 \\ 2 & 1
\end{bmatrix} $$

になります(ここで全部入力をまとめて計算したため、プラス演算子はブロードキャストです)。そして、ReLUを適応すると、

$$ \begin{bmatrix} 0 & 0 \\ 1 & 0 \\ 1 & 0 \\ 2 & 1
\end{bmatrix} $$

になります。最後に \mathbf{W_2}をかけると、

\begin{bmatrix} 0 \\ 1 \\ 1 \\ 0
\end{bmatrix}

の結果が得られます。これは \mathbf{y}と一緒ですね。

以上の構成したニューラルネットワークがXORにフィットできることを確認できました。次に、TensorFlowでそれぞれのパラメータ(重みとバイアス)を求めていきたいと思います。

実装

import numpy as np
import tensorflow as tf

# 学習データセットとラベルを定義
train_dataset = np.array([[0,0],[0,1],[1,0],[1,1]]).astype(np.float32)
train_labels = np.array([[0],[1],[1],[0]]).astype(np.float32)

# フィードフォワードにて予測値を計算
def tf_prediction(X,W1,b1,W2,b2):
    h = tf.nn.relu(tf.matmul(X,W1) + b1)
    return tf.matmul(h,W2) + b2

# 学習グラフを構成
graph = tf.Graph()
with graph.as_default():
    # 入力と出力を宣伝
    X = tf.placeholder(tf.float32, shape=[4,2])
    y = tf.placeholder(tf.float32, shape=[4,1])

    # パラメータを初期化:重みが[-1.0,1.0]の間のランダムな値、バイアスが全て0.0
    W1 = tf.Variable(tf.random_uniform([2,2], -1.0, 1.0))
    b1 = tf.Variable(tf.zeros([2]))

    W2 = tf.Variable(tf.random_uniform([2,1], -1.0, 1.0))
    b2 = tf.Variable(tf.zeros([1]))

    # 予測値を計算
    y_predict = tf_prediction(X,W1,b1,W2,b2)
    
    # 平均2乗誤差を計算
    loss = tf.reduce_mean(tf.square(y_predict - y))
    
    # 平均2乗誤差を最小化
    optimizer = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

# 学習させる
with tf.Session(graph=graph) as sess:
    sess.run(tf.initialize_all_variables())
    feed_dict = {X : train_dataset, y : train_labels}

    for step in range(1001):
        _, l, predictions = sess.run([optimizer, loss, y_predict], feed_dict=feed_dict)

        if step % 200 == 0:
            print('Loss at step %d: %f ' % (step,l))

# 最後の予測出力をプリント
print("Final predictions:")
print(predictions)

結果

パラメータの初期値がランダムなのでロスがいろいろな値に収束しました。

うまくいく例

Loss at step 0: 0.500307 
Loss at step 200: 0.232446 
Loss at step 400: 0.157888 
Loss at step 600: 0.004861 
Loss at step 800: 0.000007 
Loss at step 1000: 0.000000 
Final predictions:
[[  1.38186124e-06]
 [  9.99999106e-01]
 [  9.99999106e-01]
 [  4.06278104e-07]]

最後の予測値は [0,1,1,0]と非常に近いですね。

求めた関数  \hat{f}を図形してみます。

f:id:glpgsinc:20160728173727p:plain

ちゃんと全ての入力点を通っているようにみえますね!

うまくいかない例

一番確率高いのはロスが0.25に収束しました。

Loss at step 0: 0.500088 
Loss at step 200: 0.250000 
Loss at step 400: 0.250000 
Loss at step 600: 0.250000 
Loss at step 800: 0.250000 
Loss at step 1000: 0.250000 
Final predictions:
[[ 0.49999994]
 [ 0.49999994]
 [ 0.49999994]
 [ 0.49999994]]

予測結果が全て0.5になりますね。求めた関数  \hat{f}を図形すると、こうなります。

f:id:glpgsinc:20160728174457p:plain

入力依存せず全て0.5を出力する関数ですね。

他の収束値もいろいろありました。

Loss at step 0: 0.499994 
Loss at step 200: 0.183986 
Loss at step 400: 0.125485 
Loss at step 600: 0.125000 
Loss at step 800: 0.125000 
Loss at step 1000: 0.125000 

Final predictions:
[[  4.99999911e-01]
 [  4.99999911e-01]
 [  9.99999881e-01]
 [  1.78813934e-07]]
Loss at step 0: 0.500204 
Loss at step 200: 0.249998 
Loss at step 400: 0.249468 
Loss at step 600: 0.188922 
Loss at step 800: 0.166667 
Loss at step 1000: 0.166667 
Final predictions:
[[  6.66666329e-01]
 [  6.66666329e-01]
 [  6.66666329e-01]
 [  5.36441803e-07]]
Loss at step 0: 0.499941 
Loss at step 200: 0.188342 
Loss at step 400: 0.166667 
Loss at step 600: 0.166667 
Loss at step 800: 0.166667 
Loss at step 1000: 0.166667 
Final predictions:
[[ 0.33333343]
 [ 1.        ]
 [ 0.33333343]
 [ 0.33333343]]

考察

いろいろ試した結果、ロスの最小値は0.0だとわかりました。但し、最小点以外に停留点がたくさんありますね。 では、ロス関数を振り返しますと、

$$ J(\mathbf{\theta}) = \frac{1}{4} \sum_{\mathbf{x} \in \mathbf{X}} (f(\mathbf{x}) - \hat{f}(\mathbf{x};\mathbf{\theta}))^2 $$

なので、偏微分

$$ \frac{\partial J(\mathbf{\theta})}{\partial \theta_i} = \frac{1}{2} \sum_{\mathbf{x} \in \mathbf{X}} (\hat{f}(\mathbf{x};\mathbf{\theta} - f(\mathbf{x}))) \frac{\partial \hat{f}(\mathbf{x};\mathbf{\theta})}{\partial \theta_i} $$

になります。 \hat{f}(\mathbf{x};\mathbf{\theta}) = 0.5 \forall (\mathbf{x},\mathbf{\theta}) なら、 \frac{\partial f}{\partial \theta_i} = 0 \forall i になり、 \frac{\partial J(\mathbf{\theta})}{\partial \theta_i} が0になることがわかります。勾配が0になると、最急降下法でパラメータの更新ができなくなりますね。その結果、停留点に止まります。運がよければ最小点に止まるし、運が悪ければ他の点に止まります。

最初に停留点は極小点だと思いましたが、この論文*2によると、XOR問題のロス関数には最小点以外の停留点は全て鞍点だそうです。

局所的な極小点・鞍点に収束するのは最急降下法の古典な問題で、回避するために、複数の初期値から探索を行うなどの対策が必要です。例えば、

 \mathbf{W_1} = [[0.1,0.0],[0.0,0.1]], \mathbf{W_1} = [[0.1],[0.0]]、バイアスが全て0に初期化したら、

Loss at step 0: 0.495050 
Loss at step 100: 0.250069 
Loss at step 200: 0.250051 
Loss at step 300: 0.250039 
Loss at step 400: 0.250031 
Loss at step 500: 0.250025 
Loss at step 600: 0.249973 
Loss at step 700: 0.245165 
Loss at step 800: 0.185728 
Loss at step 900: 0.080708 
Loss at step 1000: 0.000572 
Loss at step 1100: 0.000001 
Loss at step 1200: 0.000000 
Loss at step 1300: 0.000000 
Loss at step 1400: 0.000000 
Loss at step 1500: 0.000000 

ちゃんと最小点までに行けました。

ところで、この結果のステップ100~700を注目してください。ロスがずっと0.25周辺に、下がる速度が非常に遅いです。上の失敗結果を参考に、この0.25は恐らく鞍点だと思われます。今の初期値で、鞍点から脱出できましたが、非常に時間がかかるのがわかりました。

局所的な極小点・鞍点は小次元パラメータ空間では難問ですが、幸いにも高次元パラメータ空間では問題にならないようです。まぁ、学習速度が落ちるだけです。 この論文*3によると、大きいネットワークだと、ほとんどの局所的な極小点はテストデータセットに対して大体同じ性能がでます。さらに、"悪い"極小点に収束確率はネットワークサイズと逆比例だそうです。さらに、面白いことに、最小点に収束すると過学習になるケースが多いようです。

そこで、ネットワークサイズを上げてみます。隠れ層を1つ増やして2層になり、ユニット数がどちらも128にしました。パラメータの初期値を変えずに (重みは[-0.1, 0.1]の間のランダム、バイアスは全て0) 毎回ロスが0.0に行けました。最小値に行きやすくなるのが実感できました。もちろん、局所的な極小点・鞍点問題は完全に解決できないです。例えば、重みが全部0より大きい値に初期化すると、また0.25に収束します。初期値によって収束先が全然違いますので、やはり初期値の選び方が大事ですね!

他の局所的な極小点・鞍点問題対策として、(バッチ)勾配降下法の代わりに確率的勾配降下法がります。それを使うと、計算量が減る、かつ、局所的な極小点・鞍点から脱出できるという説があります。但し、今回の学習データは非常に少なく、あまり役に立ちませんでした。

まとめ

今回はXOR問題をTensorFlowで考察しました。XOR問題は単純ですが、小さいニューラルネットワークだと鞍点に収束しやすいのがわかりました。また、ディープラーニングの実装で、初期値は凄く大事だと実感できました。

最近、ガラパゴス社内では

All you need is a good init!*4

という句が流行っています。初期値は重要なハイパーパラメータだと考えられます。

以上となります。

今回のソースコードは全てGitHubに公開しました。

最後に、株式会社ガラパゴスでは、新しい技術が好きな方を絶賛大募集中です!

RECRUIT | 株式会社ガラパゴス iPhone/iPad/Androidのスマートフォンアプリ開発

参考文献

*1:Goodfellow et al. Deep Learning Book p170-176, 2016. http://www.deeplearningbook.org/

*2:Ida Sprinkhuizen-Kuyper et al. The error surface of the 2-2-1 XOR network: The finite stationary points. Neural networks: the official journal of the International Neural Network Society 11(4) p683-690, July 1998. https://www.researchgate.net/publication/10832860_The_error_surface_of_the_2-2-1_XOR_network_The_finite_stationary_points

*3:Anna Choromanska et al. The Loss Surfaces of Multilayer Networks, 2014. https://arxiv.org/abs/1412.0233

*4:Dmytro Mishkin et al. All you need is a good init, 2015. http://arxiv.org/abs/1511.06422

祝!エンジニアブログ再開 ガラパゴスの開発チームを紹介します

こんにちは。株式会社ガラパゴスの細羽(@hosopy)です。

このたび、実に3-4年ぶりになりますが、エンジニアブログを再開することになりました!

どんな会社?

スマートフォンアプリの開発を得意とする会社です。

www.glpgs.com

iOSアプリやAndroidアプリの開発はもちろんのこと、サーバサイド(バックエンド)も開発領域としています。

自社アプリもいくつかリリースしており、運動習慣支援アプリStart fitは、30万人を超える多くのユーザ様にご利用頂いております。

itunes.apple.com

最先端の技術に寄り添いながら人類の進化に貢献するというミッションを掲げ、日々、最新技術のチェックに余念がありません。

ブログへの思い

弊社はこれまで、対外的な情報発信にはあまり積極的ではありませんでした。

しかし最近、エンジニアチームの人数も増え、

  • 今までお世話になった分、OSSやコミュニティに貢献していきたい
  • もっと自分たちのことを知ってもらって、一緒に働く仲間を増やしたい

という思いがチーム内で高まってきました。

そこで、まずはブログGitHubを活用して積極的に情報発信していこう!ということになりました。

ガラパゴスの開発チームを紹介

記念すべき初回エントリーということで、会社とエンジニアチームの紹介をしたいと思います。

コミュニティ

実務的にはプロジェクト毎にチームが組まれるのですが、技術的な情報共有を目的として、開発分野ごとに3つのコミュニティがあります。

  • iOSチーム
  • Androidチーム
  • Webチーム(主にサーバサイド)

各チームで毎週30分程度集まって、ネタを持ち寄ってLTなどを開催しています。 普段は緩くLTや課題の共有をしているのですが、WWDCGoogle I/OAWS re:Inventの開催前後には活気が溢れます。

また、特定の技術に興味のある有志で構成される、非公式?な技術コミュニティも存在しており、

  • Machine Learning
  • IoT

などがあります。

例えばMachine Learningチームは、毎週1時間程度集まって、CourseraのMachine LearningUdacityのDeep Learningの動画を視聴し、ディープラーニングの勉強をしながら活用方法を日々模索しています。

技術とツール

分野ごとに利用している技術、活用しているツール群をリストアップしてみました。

特にガラパゴスで特徴的だと思う点は、

  • GitLabとGitLab CIをフル活用していること
  • Zeplinを活用して、アプリエンジニアとデザイナの仕事を効率化していること

あたりです。

GitLabやGitLab CIの活用状況については、またブログに書きたいと思います。

文化

開発チーム全体に根付いている文化や空気です。

  • コードに対する責任感
    • 開発チーム全体でコードレビューが定着しています。
  • 変化を恐れない
    • 積極的に新しい技術を試したり、未知なる要件へのチャレンジを楽しんだり、変化を恐れない空気があります。
  • 遊びが好き

最後に

初回エントリーということで、弊社の開発チームを簡単に紹介してみました。 今後は技術中心の記事で更新していく予定なので、ご期待ください!

株式会社ガラパゴスでは、一緒に働く仲間を絶賛大募集中です!

RECRUIT | 株式会社ガラパゴス iPhone/iPad/Androidのスマートフォンアプリ開発