飲酒プログラミング Advent Calendar 2019

はじめに

この記事は、「飲酒プログラミング Advent Calendar 2019」の12/22の回のログです。

f:id:ryook:20191222212822p:plain

と書いたのに、21時くらいに気づきました。 すでにワインが一本空きそうなタイミングでしたので、ストロングゼロロング缶追加のため寒空の下コンビニにダッシュします。 なぜストロングゼロロング缶なのかというと、今日有馬記念中山競馬場に初めていったのですが、勝手なイメージ競馬場におじさんが持ってるストロングゼロを買っていくのを忘れたからです。

なにやるか

前々からくそほど酔っぱらた状態でも何の日本酒を飲んだかメモできるアプリがほしいと思っていて、知人ともそういう話をしていました。 とりあえずは画像検索かな?という話をしていたので、学習データになる画像を集めることからはじめます。 数年前にスクレイピングやったことがあったのですがhtml構造が変わっていたりしてうまくできなかったのでそこの修正をします。

学習データを集めて、画像検索までやってみるみたいなことをアドベントカレンダー登録時に考えていましたが、普通に無理なのでスクレイピングinstagramで画像を集めるということに挑戦します

instagramスクレイピング

実はinstagramスクレイピングは結構面倒です。 楽をするにはinstagramAPIを使ったviewerサービスを使うといいです。 ここでは具体的なサービス名を書きませんが、ググるとでてくるので見つかったやつを使うといいと思います。 いくつか確認していますが、割とシンプルなhtml構造なので本家を対象とするより圧倒的に楽ができます。

スクレイピングについて

言語はpythonを使います。 pythonスクレイピングをするには

  1. scrapyを使う
  2. requests & beautifulsoupを使う
  3. selenium使う
  4. その他

等いくつかありますが、今回は2のrequests & beautifulsoupを使う方法でやります。

ログ

スクリプト修正

数年前に書いていたやつをhtml修正します。 1hちょっと格闘して完了。

リファクタ

だらだらと書いていたスクリプトをimportで関数呼び出せるように修正。

memo

なんか有意義なことめもしようと思ったけど、無理。 スクレイピングネタは書かないほうがいいだろうなということが多いのでブログには向かない気がする。

検索UXを考える

この記事は検索・検索エンジンアドベントカレンダー6日目の記事です。

qiita.com

はじめに

最近検索画面の改善を検討して、いろいろ調べてみた際に重要かなと思った考え方をすこしまとめたものです。 扱っているサービスがニュースサイトなので、文字列の検索で文章を探すことを想定しています。

具体的にUIをどうするといいという話ではなく、どちらかというとそのベースとなるユーザーの行動についての研究とかで重要だなと思ったことをメモしたような記事です。 デザイナーではないのでUI/UXもちゃんと学んだわけではないので間違ったことを書いているかもしれませんので、おかしなところがあればご指摘ください。

情報探索プロセス

インターフェイスを考えるまえにユーザーの情報検索プロセスについて理解しておくのは重要です。 情報検索プロセスについては多くのモデルが提唱されているのでいくつか紹介します。

行為遂行のサイクル

D.ノーマンは、人の行為を、遂行すべきゴールを達成するための「実行」と達成できたかの「評価」に分割しています。

情報検索では、目的の文章(ゴール)を見つけるために、クエリを入力(実行)して、結果が目的のものか確認(評価)に相当します。

標準的な情報探索モデル

情報検索のためのユーザインタフェイス(共立出版)では、情報探索の標準的なモデルとして以下の4つの行為のサイクルとして形式化したものを紹介しています。

  1. 情報要求の特定
  2. 情報要求の明確化
  3. クエリ作成
  4. 検索結果の評価

情報探索ではこのサイクルを何度も繰り返すプロセスとしています。 また、先に紹介したD.ノーマンの行為遂行のサイクのモデルを標準的な情報探索モデルの認知的裏付けとしています。 さらに、評価の結果と意図した要求との乖離が小さいほどユーザーインターフェイスにおいてシステム自体の有用性が高いとしています。

ダイナミックモデル

標準的モデルは1.の情報要求が変わらないことが仮定としておかれています。しかし検索者が検索システムとインタラクションを行うことで情報要求自体が変わることもあります。 実際に、最初はなんとなく検索はじめたけど、途中からより最適なクエリを見つけて目的の文章を見つけたということは誰もがあるんじゃないでしょうか。

ユーザーは最初の情報要求に基づいて検索した結果から学び、新しい疑問をいだくことで情報要求が変わっていくというプロセスをモデルにしたのがベリー採集モデル(Beats)で、2つの特徴があります。

  1. 検索プロセスで得られる情報を読んで、学習が進むに連れ、ユーザーの情報要求とクエリが連続的に変化する
  2. 検索者の情報要求は、一度の検索で得られた結果で満足されるわけではなく、連続した選択とその過程で得られる断片的な情報によって満たされる

これは、先に紹介した標準的な情報探索モデルと対象的なモデルです。

情報検索でのユーザーの情報読み取りパターン

少し話は変わって、興味深い論文があったので紹介しておきます。

Teach Machine How to Read: Reading Behavior Inspired Relevance Estimation

http://www.thuir.cn/group/~YQLiu/publications/SIGIR2019Li.pdf

IR reading2019autoumで紹介されていた論文で、ユーザーの検索行動をまとめてそれをモデルに組み込もうというものです。 中でも興味深かったのは、ユーザーが文章の関連性を読み取るときの行動パターンの話で、以下のパターンに分類されていました。

Heuristic Description
1 Sequential reading ユーザーは上から下に読む
2 Vertical decaying attention 注意力は垂直に減衰する
3 Query centric guidance 検索クエリのまわりほど注目される
4 Context-aware reading 読書行動は以前に読んだテキストの関連性に影響をうける
5 Selective attention ユーザーは一見して無関係な部分はスキップする
6 Early stop reading 関連性しているかわかった場合はすぐに読むのをやめる

もとになった同筆者による実験の論文 Understanding Reading Attention Distribution during RelevanceJudgement

http://www.thuir.cn/group/~mzhang/publications/CIKM18Li.pdf

一般的に検索画面でクエリを入力して結果を確認するときも同じ行動をとるんじゃないかと思っています。

実際に自分の行動を鑑みても、検索結果は

  • 上から順にみていく(1)
  • 途中で全部確認する気もうせる(2)
  • タイトルやハイライトされた一致部分をまずは中心に確認する(3)
  • 表示された結果の1ドキュメントにたいしては少ない情報量で判断する(6)
  • 無関係なものはスキップする(5)
  • そもそもその判断は、これまでの自分の知識が前提となっているので、知っていることと全く知らないことを検索するには答えにたとどりつくまで時間が違う(4)

ので、検索結果をどう返すか、どういう情報を表示するか、考える上で重要なポイントだと思いました。

具体的な事例

ここで少し具体的な話にすすめていきたいと思います。 例えばニュースサイトのユーザーで検索を使う用途としては以下の2つが考えられます。

  1. 特定のニュースが知りたい
  2. 特定のトピックについて知りたい

1は、具体的に「あの記事」とユーザーの中に明確な答えがあるパターンと、「あのことについて書かれた記事」というものがあります。

2は、特定の記事が読みたいわけではなく、「自動運転」に関するニュースを知りたい、今おきているトピックについて知りたい、などがあります。

それぞれの目的を達成するための検索体験としては、最初に紹介した情報探索モデルにあてはめると、1は標準的モデル、2はダイナミックモデルになります。 つまり、同じサービスの検索でもユーザーの目的によって、提供すべき体験が代わり必要な機能も異なります。

特定のニュースが知りたいユーザーに重要なこと

1のタイプのユーザーには、目的の記事に早くたどりつける体験を提供することが重要だと思っています。 なので、以下のような情報探索プロセスのモデルの4つの行為のサイクルをフォローする機能を提供したほうがよく

ユーザーの情報読み取りパターンを考えると、

  • 目的のドキュメントが上に来るようにソートは関連度順にする。
  • ハイライト機能をつける

ということが重要になります。 また、検索精度として適合率をより重視した結果を返すほうが適切そうです。

特定のトピックについて知りたいユーザーに重要なこと

一方で2のタイプのユーザーには、検索行動自体をよりフォローする機能を提供したほうがよさそうです。 たとえば、

  • 検索履歴
  • 検索結果の一時的な保存

などの機能がよさそうですし、そもそもの最初のクエリを提供するため

  • 急上昇検索ランキング
  • タグなど特定のトピック一覧

をつくるというのも手だと思っています。

検索結果に表示する内容について

最後に話はそれますが、個人的に重要だなと思っていることがあるのでメモとして残しておきます。

情報探索プロセスの4. 検索結果の評価後目的が達成されなければ、再度2. 情報要求の明確化 or 3. クエリ作成となります。これはダイナミックモデルに沿った検索行動の場合でも情報要求が変化するだけで同様で再度クエリ作成となります。

つまり、新しいクエリというのは検索した結果を見て考えるわけなので、検索できない内容を結果に出すのはよくないのではないかといか考えています。

例えば、検索した結果にタグなどの情報があるのに検索対象となっていない場合、ユーザーがタグをクエリにすればより最適な結果が得られると思って検索しても適切な文章を返せないということになります。

そもそも、サービスでどのような種類の情報が利用可能でそのうち何が検索できるのか?何を検索対象にするといいのか?ということから考えるべきかなと思っています。

まとめ

雑多になってしまいましたが、結局大事なのは以下の2つにつきるのかなと思います。

  • ユーザーの情報ニーズが何か
  • それを解決するためにはどうすればいいか

そのためには検索させないというの答えにいきつくかもしれないですけどね。

ECS Canary deoloyをterraformで作成

TL;DR

ecsで構築したアプリケーションをCanary deployする構成を作ってterraformで構築します。

目的

ecsで構築するEC2起動タイプのアプリケーションをaws-sampleを参考にCanary deployできるよう設計を試みました。 それをterraformで構築したのでメモを兼ねて紹介します。 もっとよい方法があったら教えてほしいです。

github.com

前提

  • ecsはEC2起動タイプを使う
  • ECSについては用語等最低限知っているものとする
  • route53の設定は済んでいるものとする
  • vpc, subnet, security group, はすでにあるものを使う(ここで作成しない)

構成

概略

f:id:ryook:20191027110544p:plain

説明

ecsのcluster内に2つのserviceを作成し、Route53の荷重ルーティングを使ってCanary deployを実現します。

各serviceの前段にLoadBalancerをおいてRoute53はそこを指すようにします。

コード

sample

sampleコードはgithubにあります。

github.com

service2つとそれぞれのloadbalancerを作ります。

clusterとtaskは同じresourceを使えるので、service_resourceだけ利用します。 デプロイの際はtaskを切り替えるなど適宜に修正が必要です。

# cluster
resource "aws_ecs_cluster" "sample_app_canary_deploy" {
  name = "sample-app_canary-deploy"
}

# task
resource "aws_ecs_task_definition" "task_example" {
  family                   = "sample_app"
  cpu                      = "256"
  memory                   = "512"
  requires_compatibilities = ["EC2"]
  container_definitions    = "${file("./task_definition.json")}"
}

# servie_blue
resource "aws_ecs_service" "service_blue" {
  name                              = "example"
  cluster                           = "${aws_ecs_cluster.sample_app_canary_deploy.arn}"
  task_definition                   = "${aws_ecs_task_definition.task_example.arn}"
  desired_count                     = 1
  launch_type                       = "EC2" // default: EC2 
  health_check_grace_period_seconds = 1     // defautl:0 task起動に時間がかかると引っかるので0以上にしておく

  load_balancer {
    target_group_arn = "${aws_lb_target_group.green_target}"
    container_name   = "example"
    container_port   = 80
  }

  lifecycle {
    ignore_changes = []
  }
}


# serivice_green
# servie_blueと同様

serviceごとにtarget groupを作成し、向き先をaws_ecs_serviceで指定します。 listener groupとtarget groupだけblue, green環境用に2つ必要です。

resource "aws_lb" "alb_sample" {
  name                       = "ecs-sample-alb"
  load_balancer_type         = "application"
  internal                   = false
  idle_timeout               = 60
  enable_deletion_protection = false
  subnets = [
    "subnet-a",
    "subnet-b",
    "subnet-c"
  ]
  security_groups = [
    "sg-1"
  ]
}

resource "aws_lb_listener" "http" {
  load_balancer_arn = "${aws_lb.alb_sample.arn}"
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "text/plain"
      message_body = "test"
      status_code  = "200"
    }
  }
}

resource "aws_lb_target_group" "blue_target" {
  name        = "ecs-sample_blue-lb-target"
  vpc_id      = "vpc-a"
  target_type = "instance"
  port        = 80
  protocol    = "HTTP"
  depends_on  = ["${aws_lb.example}"]
}

# green用も同様に作る


resource "aws_lb_listener_rule" "listener_blue" {
  listener_arn = "${aws_lb_listener.http.arn}"
  action {
    type             = "forward"
    target_group_arn = "${aws_lb_target_group.blue_target.arn}"
  }
  condition {
    field  = "path-pattern"
    values = ["/*"]
  }
}


# green用も同様に作る

参考

【ダウンロード版】Pragmatic Terraform on AWS - KOS-MOS - BOOTH