Galapagos Tech Blog

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

第9回Quesに参加してみた話

はじめまして、テストチームの とののです!

テストについては勉強したてほやほやのテストエンジニアです!
今回、エンジニアブログ初参加ということで、先月わたしが生まれて初めて参加したイベントについて 張り切って書きたいと思います!!



ではさっそく… 「会場が渋谷でね、駅にはついたんだけどね、たてものの(泣)」
みたいのはおいといて

第9回Quesに参加してみたよ

「QA」と何かでググっていたところ、このイベントにぶつかった気がします。
他の会社ではどんなテストをしているのか興味があったので、いいタイミングでいいテーマの回に出会えたと思います。

公式サイト quesqa.com

イベントの内容はこんな感じでした

ーー

2016/11/18 19:00〜 @サイバーエージェント

第1部

「Amebaアプリ QAの歴史」

 株式会社サイバーエージェント Ameba統括本部

 Amebaメディア本部メディアQC室 関根 雄飛さん

第2部

「QAエンジニアのキャリアパスについて考える

〜いろいろな組織でリーダー/マネージャとしてやってきたこと〜」

 株式会社mediba 品質管理グループ 山本 久仁朗さん

ーー

質疑応答含めざっくり1時間弱ずつくらいで、時間いっぱいお話してくれた印象でした。

Amebaアプリ QAの歴史 について

公式サイトのレポートに資料があるので、細かい内容は書かずに思ったことだけ書きます。

テストフローや、利用しているツールなどかなり具体的に公開されていたので、
自分の仕事内容と比較しながら聞いていました。
テストフローはほぼ同じで、すべて人の手で最後までやっているところなど、親近感がありました。

似てる
・項目書とその中身:Googleスプレッドシート、区分の分け方、見た目など
・テスト実施前にテスト内容の共有
・夕会で仕様変更などの情報共有:弊社は朝会です

似てない
・項目書が機能ごとのシート分け
→エンハンスかそうでないかの違いだと思いますが、わたしは画面ごとで分けた項目書を見ることが多いので新鮮でした。

KPT法で項目の振り返り会
ガラパゴスでは、反省会は取り組み始めたばかりです。(いままではっきりとした形ではやっていませんでしたが、形になってきた)
方法論を参考にするのも、わかりやすくてよいかもしれないですね。

・開発チームとの距離
→人数の規模が違うので当然といえば当然なのですが、それを置いてもガラパゴスは開発チームとテストチームの距離は近いと思います!
OSでの挙動の違いを確認したり、どんな実装になっているか質問したり、
直接確認できるので物理的にも、雰囲気的にもコミュニケーションは取りやすい環境だと思います。

QAエンジニアのキャリアパスについて

山本さんがいままでどんなことをしてきたかについてのお話でした。
経験談おもしろかったです。

途中で知らない単語がたくさんあったので復習します

「シックス・シグマ」、「TPI/TPI NEXT」:プロセス改善方法
「Test.ssf」:テスト技術のものさし
「GQM」:モデル化の手法、ゴール・クエスチョン・メトリクス
「FiveCore」:会社名か英語ばっかり引っかかって…(泣)このリンクと関係ありそう?と思うのですがなにかまだわかってません
ISO/TS16949 コアツールとは|ISO9001 ISO14001コンサルティング 大阪 福岡 東京 400社を超える支援実績があります

「改善しよう」というところまでまだ意識がいっていないので、手法についてはさっぱりだったんですね!笑


全体的にとっても前向きな話でした。
冒頭にテスト工数についての話があって、

テスト工数は開発のだいたい40%の割合を占めている、大きければ60%以上!(そうなんだ、そんなに〜!)
→高品質にするには、テストに時間がかかる(わかる)
→テスト工数を効率化すれば、利益率も上がる?!(!!!!!!)

たしかに!!!同じ時間とお金をとってもらえるとしたら、中身濃いほうがいいに決まってますよね!
この話すごくしっくりきました。

他にびびっときたところは、

  • 社会人の勉強には答えがないよ。
  • いましかないから勉強しよ
  • 楽したかったら勉強しよ
  • テストは知恵と経験

勉強して、技術力をつけていきましょう!という内容にやる気が高まりました!!!
がんばります。

さいごに

生まれて初めてのイベントは、とっても刺激を受けました!
当日は平日でしたが、ガラパゴスフレックスタイム制、かつ「勉強?!どんどんしてこーい!」と送り出してくれる会社なので、 イベントや勉強会にも参加しやすい環境なんだと思います。
さらにいま、ガラパゴスでは刺激を求める貪欲エンジニアを大募集中です!!
ご興味ある方ご応募お待ちしております。

www.glpgs.com

AWS Step FunctionsのWorkerをRubyで実装してみた

こんにちは。細羽(@hosopy)です。

先日開催されたAWS re:Inventですが、新しいサービスがドカドカと発表されてお腹いっぱいになり、良い年末を迎えられそうです。

そんな中、個人的に気になったサービスの一つに、AWS Step Functionsがあります。

今回は、AWS Step FunctionsのWorkerをRubyで実装してみたので、その内容を共有したいと思います。

なお、AWS Step Functionsについては、既に大変分かりやすい記事(ありがたい!)があるため、基本的な説明はここでは割愛させて頂きます。

qiita.com

TaskとActivity

多くのチュートリアルがそうなっていますが、Taskに割り当てられる代表的なリソースは、Lambda Functionです。

おそらく、最もAWSらしく、かつ、Serverlessのコンセプトに沿った使い方だと思います。

一方で、TaskにはLambda Function以外のリソースも割り当てることが出来ます。 EC2やECS、時にオンプレで動くバッチ処理等が、それに相当します。

でもどうやって?

AWS Step Functionsには、Lambda Function以外のリソースとTaskとを繋ぐために、Activityという概念が定義されています。

Lambda Functionが割り当てられたTaskの定義では、StateのResourceにはLambda FunctionのARNが指定されています。

{
  "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function",
  "StartAt": "HelloWorld",
  "States": {
    "HelloWorld": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function: HelloWorldFunction",
      "End": true
    }
  }
}

一方で、Activityが割り当てられたTaskの定義では、StateのResourceにはActivityのARNが指定されます。

{
  "Comment": "A Hello World example of the Amazon States Language using an Activity",
  "StartAt": "HelloWorld",
  "States": {
    "HelloWorld": {
      "Type": "Task",
      "Resource": "arn:aws:states:REGION:ACCOUNT_ID:activity:HelloWorldActivity",
      "End": true
    }
  }
}

定義としてはシンプルですが、AWS Step Functions上では、Activityはただの識別子でしかありません。

実際のリソースを繋ぐためには、ActivityをキーにしてAWS Step Functionsと対話するWorkerを実装して動かす必要があります。

Workerを実装して動かすまでの流れ

JavaによるWorker実装のチュートリアルの内容を参考に、RubyによるWorker実装にトライしてみます。

1. Activityの定義

AWS Step Functionsのコンソールで、Tasksメニューを開き、Create new activityを選択します。

f:id:glpgsinc:20161209135654p:plain

Activity登録フォームが表示されるので、適当な名前(ここではHelloWorldActivity )を入力してActivityを作成します。

f:id:glpgsinc:20161209135941p:plain

作成後、 Activitiesの中に作成されたActivityのARNが表示されていると思います。 このARNは、State Machieの定義で使うのでメモしておきます。

arn:aws:states:ap-northeast-1:ACCOUNT_ID:activity:HelloWorldActivity

2. State Machineの定義

State Machineを定義します。 今回は、Hello Worldというblueprintのサンプルをベースにして定義してみます。

{
  "Comment": "A Hello World example of the Amazon States Language using an Activity",
  "StartAt": "HelloWorld",
  "States": {
    "HelloWorld": {
      "Type": "Task",
      "Resource": "arn:aws:states:ap-northeast-1:xxxxxxxxxxxx:activity:HelloWorldActivity",
      "End": true
    }
  }
}

f:id:glpgsinc:20161209142010p:plain

IAM Roleを選択後、State Machineの作成が完了します。

3. State Machineの実行1

まだWorkerを動かしていないので失敗するだけですが、試しにState Machineを実行してみます。

State Machine作成完了後の画面に表示されたNew executionを押してみます。

f:id:glpgsinc:20161209142137p:plain

すると、InputのJSONを編集する画面が表示されるので、適当に編集してStart Executionを押して処理を開始します。

f:id:glpgsinc:20161209151723p:plain

HelloWorldがIn progressに、Execution StatusがRunningな状態になり、処理が開始して実行中になっているのが分かります。

f:id:glpgsinc:20161209170306p:plain

ですが、、、Activityを実装しておらず、また、Workerも動かしていない状態であるため、何分待っても処理が終わらないと思います。

それでは、いったんStop Executionで処理を止めて終了し、Activityの実装に移ります。

4. Activityを実装

GitHubにソースを置きました。

github.com

main.rbが中心ですが、要素だけ抜粋します。

client = Aws::States::Client.new

loop do
  # Taskを受信するまでブロックされるが、
  # 内部でAWS Step FunctionsにPollingしているものと思われる
  resp = client.get_activity_task({ activity_arn: activity_arn })

  # Task識別子とInputを取得
  task_token = resp.task_token
  input      = resp.input

  begin
    input = JSON.parse(resp.input)

    # Inputが不正であれば、AWS Step FunctionsにFailureを通知する
    unless input.has_key?('who')
      client.send_task_failure({ task_token: task_token, cause: 'Input error' })
    end

    # AWS Step FunctionsにSuccessを通知する
    client.send_task_success({
      task_token: resp.task_token,
      output:     JSON.generate({ message: "Hello #{input['who'].to_s}!" })
    })
  rescue => err
    # AWS Step FunctionsにFailureを通知する
    client.send_task_failure({ task_token: task_token, cause: 'Unexpected error' })
  end
end

5. ActivityのWorkerを起動

実装したActivityをWorkerとして起動してみます。${ACTIVITY_ARN}は、自身で定義したActivityのARNを指定します。

$ git clone https://github.com/hosopy/aws-step-functions-hello-world
$ cd aws-step-functions-hello-world
$ bundle install
$ bundle exec ruby main.rb ${ACTIVITY_ARN}

WorkerはTask受信待ちになります。

I, [2016-12-09T16:02:05.691154 #51083]  INFO -- : Waiting for a task to start. Press Ctrl-C to interrupt.

6. State Machineの実行2

ActivityのWorkerが起動したところで、State Machineを実行してみます。

f:id:glpgsinc:20161209164842p:plain

今回実装したActivityは、Input JSONwhoというフィールドがあることを前提とするので、Input JSONを変更して実行してみます。

f:id:glpgsinc:20161209165110p:plain

すると、Worker側で受信ログが確認出来ます。

I, [2016-12-09T16:02:53.924983 #51083]  INFO -- : Get activity task : #<struct Aws::States::Types::GetActivityTaskOutput task_token="xxxxxx", input="{\n    \"who\": \"hoge\"\n}">
I, [2016-12-09T16:02:54.196109 #51083]  INFO -- : Success
I, [2016-12-09T16:02:54.196184 #51083]  INFO -- : Waiting for a task to start. Press Ctrl-C to interrupt.

そして、AWS Step Functions側でもHelloWorldが Successに、Execution Statusが Succeededな状態になり、見事にState Machineが正常終了しました!

f:id:glpgsinc:20161209165809p:plain

Outputも意図したJSONになっているようです。

f:id:glpgsinc:20161209165623p:plain

考察

Lambda vs Activity

Activityを定義してWorkerを実装することで、Lambda Function以外のリソースを、AWS Step Functionsに統合できることが分かりました。

たとえば、以下のようなLambdaに載せられない処理を組込みたい場合は、Activityとして定義すると良さ気です。

  • バッチ処理
  • GPUを必要とする処理(深層学習など)
  • どうしてもオンプレ環境でないと実行できない処理

一方で、

  • Workerの実装自体が面倒
  • Workerの可用性やスケーラビリティを考える必要がある

という点を考えると、全てのTaskがAWS Managedなコンポーネントで構成されている方が、 Serverlessとしての究極的な理想形なのかなと思います。

そのあたりは、先日のre:Inventで同時に発表された、AWS Batchあたりに期待したいところです。

最後に

株式会社ガラパゴスでは、AWSを使ったより良いアーキテクチャを追求したいエンジニアを絶賛大募集中です!

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

TensorFlowで単純なseq2seqモデルとattention seq2seqモデルを比較してみた

こんにちは、
11月8日にAWSxBot勉強会*1で無事に発表してきた@vanhuyzです。 発表資料も上げましたので、来られなかった方は是非チェックしてください!

さて、今回の内容です。 最近、Google翻訳先生がすごくなるよという話題がありますね。 手法はこの論文*2に詳しく書いてありますが、基本的にSequence to Sequenceモデル (seq2seq) を使っているそうです。

背景

まず、seq2seqモデルを少しまとめてみます。

単純なseq2seqモデル

f:id:glpgsinc:20161128113836p:plain

seq2seqモデルは名前の通り、入力シーケンスから出力シーケンスに変換するモデルです。シーケンスはテキストでも良し、画像や音声でも構いません。そのため、seq2seqは機械翻訳、会話生成、画像キャプション、音声認識などに広く応用されています。

上の図にはABCというシーケンスからWXYZというシーケンスに変換しますね。 基本的にseq2seqは2つのRNNから構成されています。1つ目のRNNはencoderと呼ばれていて、入力シーケンスから固定サイズのベクトルに変換します。次に、その固定サイズベクトルを2目のRNN(decoderと呼ばれる)に入れて、出力シーケンスを生成します。

Attention Seq2seqモデル

f:id:glpgsinc:20161128113852p:plain

単純なseq2seqモデルは固定サイズベクトルを使用するため、複雑なタスクに対して表現できないことがあります。そのため、入力シーケンスの各ステップの出力(または状態)の情報を利用すると、より良い出力シーケンスを生成できるという考えです。これは注目(attention)メカニズムと呼ばれています。

今回は単純なseq2seqモデルとattention seq2seqモデルを比較しようと思います。

実験

問題設定

今回は機械翻訳タスクを検証してみたいです。 実際の言語を試したいですが、今回はデモという目的なので、1つの言語を作ります。 例えば、ある国の言語は数字だけで表記します。Numberishを呼びましょう。

この国の言葉は英語から翻訳すると、こういうルールとなります:

i → 1
you → 3
english → 7
Pneumonoultramicroscopicsilicovolcanoconiosis → 45

見たら分かる通り単純に文字数です。

次は文法です。文法は英語とちょっと逆で、例えば

i love you (→ love i you) → 413
this is a long sentence (→ is this a sentence long) → 24184

となっています。大体って言えば3,6,9…番目の単語をそのままにして、残りはとなりの単語と取り替えるということです。もし最終の単語が取替相手がない場合、そのままにします。コードで表すと、

def grammar(length):
  mygrammar = [1, 0, 2]
  if length <= 0:
    raise ValueError('Length should be >= 1') 
  if length == 1:
    return [0]
  if length == 2:
    return [1,0]
  for i in range(3,length):
    if length % 3 == 1 and i == length - 1:
      next_pos = length - 1
    else:
      next_pos = mygrammar[i-3] + 3
    mygrammar.append(next_pos)
  return mygrammar

となります。

>>> print(grammar(1))
[0]
>>> print(grammar(2))
[1, 0]
>>> print(grammar(10))
[1, 0, 2, 4, 3, 5, 7, 6, 8, 9]
>>> print(grammar(11))
[1, 0, 2, 4, 3, 5, 7, 6, 8, 10, 9]

Numberishの特徴:

  • 曖昧さがある
  • 単語間の区切りなし(日本語と同じ)
  • もともとの英語文の長さと違うことがある
    • 例えば: neural machine translation(長さ3)→ 7611(長さ4)

こういう特徴で本来の機械翻訳タスクの難しさとちょうど良いと思います。

実装

教師データ生成

教師データは英文とNumberishに訳した文のペアが必要です。 例えば、「i have a pen」→「4113」というペアです。

Numberishの先生はルールがわかりましたので、大量のデータを作ることができます。 データだけモデルに与えて、もちろん文法のルールを一切教えないのです。

def encode(text):
  """ Numberize a sequence """
  words = text.split()
  new_text = ''
  for i in grammar(len(words)):
    new_text += str(len(words[i]))
  return new_text
>>> test = 'i have a pen'
>>> print(encode(test))
4113

学習モデルの定義

単純なseq2seqモデルとattention seq2seqモデルはTensorFlowが提供するのでそれらを使います。

  • 単純なseq2seq:tf.nn.seq2seq.embedding_rnn_seq2seq
  • Attention seq2seq:tf.nn.seq2seq.embedding_attention_seq2seq

ソースコードGitHubに上げましたので、興味ある方は是非チェックしてください。 github.com

結果

まずはloss値です。

f:id:glpgsinc:20161124211010p:plain

f:id:glpgsinc:20161124211023p:plain

グラフを見たらわかるように、単純seq2seqのlossは0.3に収束しますが、attention seq2seqは0.2までいけました。 0.1の差は小さいように見えますが、実はかなり大きいです。 この結果を見たら、機械翻訳タスクに対しては確かにattention seq2seqの方が優勝だとわかりました。

では、実際のテスト文を入れてみましょう。この文は学習データに含まらないのです。

Input                   :  neural machine translation by jointly learning to align and translate
Correct output          :  76117285239
Simple seq2seq output   :  126112853298
Attention seq2seq output:  76117285236

この例を見たら、単純seq2seqは明らかにダメのに対して、attention seq2seqは90%当たりましたね。最後のtranslateは9文字なのに6に訳してしまうのがちょっと残念です。

Attention Seq2seqの面白いことは可視化することができます。以下はattention matrixの可視化です。attention matrixは元の英文と訳した文の間、どういう関係があるのかを表す行列です。 x軸は元の英文で、y軸はNumberishに訳した文です。英語の単語がNumberishのどの単語に相当するのかモデルがよく判断できましたね。 例えば、「neural」→「6」、「translation」→ 「11」となっています。

文法が違って、単語の位置がずれてもちゃんと訳できるのですごく感動しました。

f:id:glpgsinc:20161124193902p:plain

ちなみに、attention matrixの値はTensorFlowのAPIから取れず、元の論文*3を参考しながらTensorFlowソースコードをいじりました。

最後に

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

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

*1:AWS×BOT】TechTalk #3 https://lig.connpass.com/event/41826/

*2:Neural Machine Translation by Jointly Learning to Align and Translate https://arxiv.org/abs/1409.0473

*3:Grammar as a Foreign Language https://arxiv.org/abs/1412.7449