Taste of Tech Topics

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

キーフレーズを自動推定するPositionRankの収束性について解説

こんにちは。
学生時代に信号処理で使っていた数学の知識を生かして、機械学習関連の仕事をしている3年目の@maron8676です。

本記事はAdvent Calendar 機械学習の数理の21日目の記事となります。

0. はじめに

本記事では、文書からキーフレーズを抽出するアルゴリズムであるPositionRankの収束性について解説します。
原論文[1]には収束について書かれていませんが、アルゴリズムを使うにあたり収束性があるかどうかは気になるところだと思います。
機械学習では初期点によって結果が変わるなんてことはよくある話ですよね
そこで、今回はPositionRankの収束性について関数解析の視点から検証してみます。
結果として、PositionRankのアルゴリズムが作る点列は、任意の初期点に対し唯一つの点に収束するという、とてもよい性質を持っていることが分かります。

1. PositionRankについて

1.1. 目的

PositionRankは文書からキーフレーズ(文書のトピックを表すフレーズ)を抽出するためのアルゴリズムです。
キーフレーズが文書から自動で得られれば、文書を読まなくてもトピックが分かるため便利です。
また、自動で得られたキーフレーズをクラスタリングや、レコメンデーションへ応用することも考えられます。

1.2. 特徴

PositionRankはPageRankから影響を受けているアルゴリズムです。PageRankのページリンクに相当するものが、単語の共起情報となっています。
つまり、重要な単語と共起する単語は重要な単語であるという考えでキーフレーズの抽出を行っていると言えます。
PositionRankでは、単語の共起情報に加え、単語の出現位置を重要度に反映させることによって、共起情報だけを使った場合より精度を高めています。
それでは、PositionRankではどのようにしてキーフレーズを求めているのか見ていきましょう。

1.3. アルゴリズム

PositionRankでは、図1のような流れで各単語の重要度を算出し、高い重要度の単語を使ってキーフレーズを構築します。
単語の重要度が出てしまえば、高い重要度の単語を使ってキーフレーズを構成できます。
そのため以下では、各単語の重要度を導出するアルゴリズムについて注目していきましょう。

f:id:acro-engineer:20181217200127p:plain
図1. PositionRankで単語の重要度を導出するアルゴリズムの流れ

1.3.1. 文書から単語の共起情報を表すグラフを作る

ある単語とある単語が同じ場所で使われることを共起と言います。この共起情報を使って文書からグラフを作ることができます。
例えば、2つ隣までの単語は共起していることにすると、「PositionRankは文書からキーフレーズを抽出するためのアルゴリズムです」という文から
以下のグラフを作れます。

f:id:acro-engineer:20181217194955p:plain
図2. 例文から構成されるグラフ

図2では辺の重みが全て1のため省略しますが、辺の重みは共起回数となります。
実際にはいくつかの文があるため、さらに大きなグラフとなります。

1.3.2. グラフから各単語の重要度を計算する

作ったグラフの隣接行列M \in \mathbb{R}^{|V| \times |V|}と、単語の出現位置から構成したベクトルp \in \mathbb{R}^{|V|}を使い、初期点x_0 \in \mathbb{R}^{|V|}|V|はグラフの頂点数=単語の種類)から以下の計算を繰り返し行うことで各単語の重要度を求めます。
x_{n+1} = \alpha \widetilde{M} x_n + (1-\alpha)p \qquad (0 < \alpha <1)
\widetilde{M}はグラフの隣接行列Mの各行を和が1になるようにl_1ノルムで正規化した行列であり、
p=\Bigl [ \frac{p_1}{p_1+p_2+ \ldots +p_{|V|}},\frac{p_2}{p_1+p_2+ \ldots +p_{|V|}}, \ldots ,\frac{p_{|V|}}{p_1+p_2+ \ldots +p_{|V|}} \Bigr ]^T.
ここで、p_iは単語の出現位置から計算される値です。例えば、i番目の単語が文書の5番目と7番目に出現した場合はp_i=\frac{1}{5}+\frac{1}{7}=\frac{12}{35}となります。

2. アルゴリズムの収束性

それでは、1.3.のアルゴリズムによって作られる点列が唯一つの点に収束することを確認していきましょう。
図3の流れで確認を進めていきます。

f:id:acro-engineer:20181218073153p:plain
図3. 収束を証明する流れ

2.1. 行列\widetilde{M}による線形写像は非拡大写像である

行列\widetilde{M}の各行がl_1ノルムで正規化されていることを使って、非拡大写像であることを示します。
ここでの非拡大写像とは、完備なノルム空間における任意の  x, y \in \mathbb{R}^{|V|} に対し、以下が成り立つ写像A : \mathbb{R}^{|V|} \rightarrow \mathbb{R}^{|V|}のことです。
\|x-y\| \geq \|Ax-Ay\|
\|\cdot\|はノルムです。これ以降はノルムをl_{\infty}ノルム*1として進めていきます。最終的な収束の結果については、ノルムの等価性*2を使うと\mathbb{R}^{|V|}上の全てのノルムについて成り立つことが確認できます。
\widetilde{M}が線形写像であることに注意すると、
\begin{align}\|\widetilde{M}x-\widetilde{M}y\|_{\infty}&=\|\widetilde{M}(x-y)\|_{\infty}\\
&\leq\|\widetilde{M}\|_T\|x-y\|_{\infty}\end{align}
となります。\|\cdot\|_T作用素ノルム
\|A\|_T:=\max_{\|x\|_{\infty}=1}\|Ax\|_{\infty}
です。行列\widetilde{M}の各行はl_1ノルムで正規化されているため、\|\widetilde{M}\|_T = 1となります*3。したがって、行列\widetilde{M}による線形写像は非拡大写像です。

2.2. 実数 0 < \alpha < 1 によるpとの凸結合は縮小写像である

単語の出現位置から構成したベクトルpとの凸結合が縮小写像であることを示します。
ここでの凸結合とは、実ベクトル空間の任意の点x,y \in \mathbb{R}^{|V|}と実数0 < \alpha < 1から得られる
\alpha x + (1-\alpha)y
のことを言います。また、写像fが縮小写像であるとは、完備なノルム空間における任意の点x,y \in \mathbb{R}^{|V|}に対しあるk \in [0,1)が存在して、
\|f(x)-f(y)\|\leq k\|x-y\|
が成り立つことです。
fpとの凸結合
f(x)= \alpha x+(1-\alpha)p \quad (0 < \alpha < 1)
とすると、
\begin{align}\|f(x)-f(y)\|_{\infty}&=\|\alpha x+(1-a)p-\alpha y-(1-\alpha)p\|_{\infty}\\
&=\|\alpha x-\alpha y\|_{\infty}\\
&\leq|\alpha|\|x-y\|_{\infty}\end{align}
となるため縮小写像となります。

2.3. バナッハの不動点定理[2]を適用する

バナッハの不動点定理[2]は以下が成り立つことを示しています。
Tを完備距離空間における縮小写像とすると、Tは唯一つの不動点を持つ
点列x_n=T(x_{n-1})は唯一つの不動点に収束する。
2.1.と2.2.より、PositionRankの1イテレーション
g(x_{n}) = \alpha \widetilde{M} x_n + (1-\alpha)p \qquad (0 < \alpha <1)
は縮小写像であるため、バナッハの不動点定理[2]より唯一つの不動点が存在し、イテレーションを繰り返すことでその不動点に収束します。
   

3. アルゴリズムの収束速度

バナッハの不動点定理[2]により、収束速度も以下のように得ることができます。
\|x^* - x_k\| \leq \frac{\alpha^k}{1-\alpha}\|x_1 - x_0\|
ここで、x^*不動点k \in \mathbb{N}イテレーション回数であり、\alphaは2.2.の凸結合に登場する\alphaです。
実際の例として、距離がl_{\infty}ノルムで\alpha = 0.85*4とした場合を見てみましょう。
ランダムに決める初期値としては、原論文[1]で使われている、全ての成分が\frac{1}{|V|}のベクトルを使うことにしましょう。すると、 \|x_1 - x_0\|_{\infty} < 1となるため、100回イテレーションを回して得られるベクトルx_{100}
\begin{align}\|x^* - x_{100}\|_{\infty} &\leq \frac{\alpha^{100}}{1-\alpha}\|x_1 - x_0\|_{\infty}\\
&=\frac{0.85^{100}}{0.15}\|x_1 - x_0\|_{\infty}\\
&< 5.8 \times 10^{-7}\end{align}
を満たします。100回のイテレーションで十分収束していると言えますね。

4. まとめ

キーフレーズを抽出するアルゴリズムであるPositionRankの1イテレーションが縮小写像であることを示して、唯一つの点に収束することと収束の速度を確認しました。任意の初期点から始めて収束する+指数オーダで収束速度もいいという結果で、安心して使えます。実用上いい結果が出ることも大事なのですが、今回のように収束を検証できることも重要なことなので、何となく使っていたアルゴリズムを見直してみるのもよいのではないでしょうか。

5. 参考文献

[1] Corina Florescu and Cornelia Caragea, PositionRank: An Unsupervised Approach to Keyphrase Extraction from Scholarly Documents
[2] S. Banach, Sur les operations dans les ensembles abstraits et leur application aux equations integrales, Fund. Math. 3(1922), 133–181
[3] 山田 功, 工学のための関数解析, 数理工学社
[4] Sergey Brin and Lawrence Page, The anatomy of a large-scale hypertextual Web search engine

*1 ベクトルx \in \mathbb{R}^{|V|}l_{\infty}ノルムは\|x\|_{\infty} := \max_{1 \leq i \leq |V|}|x_i|です。

*2 ベクトル空間Xに定義されたノルム\|\cdot\|_Aとノルム\|\cdot\|_Bが等価であるとは、ある実数M_2 \geq M_1 > 0が存在して、すべてのx \in Xに対し、M_1 \|x\|_A \leq \|x\|_B \leq M_2 \|x\|_A
が成り立つことを言います。有限次元ベクトル空間に定義可能なノルムはすべて等価であることが知られています([3]を参照されたい)。

*3 証明は省略しますが([3]を参照されたい)、行列とベクトルの積を成分で展開し、
1. (積の絶対値)\leq(絶対値の積)から上界
2. 単位ベクトルとの積から下界
を作って挟み込むことで、l_{\infty}ノルムに対応する行列A \in \mathbb{R}^{|V|}作用素ノルムは
\|A\|_T:=\max_{1 \leq j \leq |V|} \Sigma_{k=1}^{|V|}|a_{jk}|
となることを示せます。

*4 PageRankのdamping factor[4]にならって0.85を設定することが多いようです。

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com

Elastic Stackで簡単!Dockerコンテナ監視ダッシュボード作成

こんにちは、CI/CDツールなどを活用し、DevOps推進活動などに携わっている横山です。
本記事は、Docker Advent Calendar 2018 - Qiita20日目です。

はじめに

Elastic Stackを使うと、簡単にDockerコンテナの監視ダッシュボードが作成できるので、今回はその紹介をしたいと思います。

きっかけとしては、「開発環境で立ち上げている複数コンテナの問題調査を楽にしたい」というのがあります。最近、開発環境に複数のコンテナを立ち上げて開発メンバーに提供していますが、開発メンバーから「重たいので環境を確認してほしい」といった声が上がってきます。その際、どのサーバのどのコンテナに問題が発生しているのか確認したいですが、その都度サーバに入って、docker statsなどのコマンドで確認するのはやや面倒です。

そこで、コンテナの監視ダッシュボードを作って、一か所で確認できるようにしようと考え、Elastic Stackを使うことにしました。

今回の記事では、以下のダッシュボードを作ります。
f:id:acro-engineer:20181220080124p:plain:w700

概要

内容としては、以下の流れで説明します。

  1. 監視対象のコンテナを起動
  2. Elasticsearch、Kibanaをコンテナで起動
  3. Metricbeatのインストール、設定
  4. Kibanaダッシュボードの確認

構成は以下のようになります。
f:id:acro-engineer:20181220103953p:plain:w500

環境、バージョン情報

※今回は、全て同一サーバ上で動かします。

OS CentOS 7.5
Elastic Stack 6.5.3
Docker 18.09
Docker Compose 1.23.1

Docker、Docker Composeはインストールしている前提とします。
Dockerのインストール方法については、
Docker入門(第二回)~Dockerセットアップ、コンテナ起動~ | さくらのナレッジ
を参照してください。

手順

監視対象コンテナを起動

今回は、監視対象として、RedmineのDockerコンテナを起動します。

任意のディレクトリにdocker-compose_redmine.ymlを作成します。

version: '3.1'

services:

  redmine:
    image: redmine
    restart: always
    ports:
      - 3000:3000
    environment:
      REDMINE_DB_MYSQL: db
      REDMINE_DB_PASSWORD: example

  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: example
      MYSQL_DATABASE: redmine

Redmineのコンテナを起動します。

$ docker-compose -f docker-compose_redmine.yml up -d
Creating network "redmine_default" with the default driver
Creating redmine_db_1_79a9b94cfd42      ... done
Creating redmine_redmine_1_195c7e2fd8c7 ... done

$ docker-compose -f docker-compose_redmine.yml ps
             Name                           Command               State           Ports         
------------------------------------------------------------------------------------------------
redmine_db_1_36b65927def4        docker-entrypoint.sh mysqld      Up      3306/tcp, 33060/tcp   
redmine_redmine_1_870966ab277a   /docker-entrypoint.sh rail ...   Up      0.0.0.0:3000->3000/tcp
$

Elasticsearch、Kibanaのコンテナを起動

Elasticsearch、Kibanaについても公式のDockerイメージを利用して起動します。

任意のディレクトリに以下のようなdocker-compose_elk.ymlを作成します。

version: '2.2'

services:

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.5.3
    container_name: elasticsearch
    environment:
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    ports:
      - 9200:9200
    networks:
      - esnet

  kibana:
    image: docker.elastic.co/kibana/kibana:6.5.3
    ports:
      - 5601:5601
    networks:
      - esnet

networks:
  esnet:

Elasticsearch起動のために「vm.max_map_count」を確認し、設定します。
まず、現状の値を確認します。

$ sudo sysctl -a | grep vm.max_map_count
・・・
vm.max_map_count = 65530

値が「262144」になっていない場合は、以下のコマンドで変更します。
(永続化する場合は、/etc/sysctl.confに設定します。)

$ sysctl -w vm.max_map_count=262144
vm.max_map_count = 262144

Elasticsearch、Kibanaのコンテナを起動します。

$ docker-compose -f docker-compose_elk.yml up -d
Creating network "elk_esnet" with the default driver
Creating elk_kibana_1_730c2b554976 ... done
Creating elasticsearch             ... done
 
$ docker-compose -f docker-compose_elk.yml ps
          Name                         Command               State                Ports              
-----------------------------------------------------------------------------------------------------
elasticsearch               /usr/local/bin/docker-entr ...   Up      0.0.0.0:9200->9200/tcp, 9300/tcp
elk_kibana_1_d87486e7e64b   /usr/local/bin/kibana-docker     Up      0.0.0.0:5601->5601/tcp          
$ 

Metricbeatのインストール、設定

監視対象のコンテナ情報を取得するために、Metricbeatをインストールします。

$ curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.5.3-x86_64.rpm
$ sudo rpm -vi metricbeat-6.5.3-x86_64.rpm

補足になりますが、Elasticsearch、Kibanaと、
Metricbeatを別サーバで動かすときは、以下の接続先(localhostの部分)の設定を変更します。

$ sudo vi /etc/metricbeat/metricbeat.yml

setup.kibana:
  host: "localhost:5601"

output.elasticsearch:
  hosts: ["localhost:9200"]

MetricbeatのDockerコンテナ用のモジュールを有効にします。

$ sudo metricbeat modules enable docker
Enabled docker
$ sudo vi /etc/metricbeat/modules.d/docker.yml

コメントアウトを外し、以下のように設定します。
(記述していない部分はデフォルトのままです。)
- module: docker
  metricsets:
    - container
    - cpu
    - diskio
    - healthcheck
    - info
    - memory
    - network

ElasticsearchのIndex Templateを適用します。

$ sudo metricbeat setup --template -E output.logstash.enabled=false -E 'output.elasticsearch.hosts=["localhost:9200"]'
Loaded index template

Kibanaのダッシュボード定義を作成します。
(テンプレートがあるのはうれしいですね。)

$ sudo metricbeat setup --dashboards
Loading dashboards (Kibana must be running and reachable)
Loaded dashboards

Metricbeatを起動し、確認します。

$ sudo systemctl start metricbeat
$ sudo systemctl status metricbeat

これで、一通りの設定が完了です。
それでは、Kibanaの画面で確認してみましょう。

Kibanaダッシュボード画面

ブラウザで以下のURLにアクセスし、Kibanaの画面を開きます。
 http://<KibanaサーバのIPアドレス>:5601

Kibanaのトップページ左のメニューから「Dashboard」をクリックします。
f:id:acro-engineer:20181220075940p:plain:w750

ダッシュボード一覧が表示されるので「[Metricbeat Docker] Overview」をクリックします。
f:id:acro-engineer:20181220080026p:plain:w500

以下のようなダッシュボードが表示されます。
f:id:acro-engineer:20181220080042p:plain:w750

監視対象用に起動しているRedmineコンテナ(redmine_redmine_1_dda7715e7770)やMySQLコンテナ(redmine_db_1_e54eba45c028)の監視ができています。
また、今回はElasticsearch、Kibanaもコンテナで起動しているので、それらも合わせて監視されています。

上図の赤丸部分で、MySQLコンテナのメモリ使用量が増えていることがパッと見てわかります。

このように、複数のコンテナが動いている環境で問題調査をする際に、このようなダッシュボードで切り分けが楽になりました。


余談ですが、ダッシュボードの一覧で、「[Metricbeat System] Host overview」を選択すると、ホスト側の状態も確認できます。
f:id:acro-engineer:20181220080057p:plain

まとめ

今回は、Elasticsearch、 Kibana、MetricbeatでDockerコンテナの監視を行ってみました。
MetricbeatのモジュールやKibanaダッシュボードのテンプレートなどが用意されているので、簡単に監視ダッシュボードが作れるのは良いですね。
ダッシュボードのカスタマイズなどもして、より使い勝手の良いダッシュボードにもしてみたいと思います。

それでは。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
顧客のビジネスをインフラから加速するエンジニア募集! - Acroquest Technology株式会社のインフラエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com

JShellでフォルダ出力するワンライナーを書いてみた。

概要

こんにちは、最近JShellに嵌っているuedaです。
この記事はワンライナー自慢大会 Advent Calendar 2018 - Qiitaの19日目です。
JavaのJShellを使ってテキストに書かれたとおりの構成のフォルダを出力する
ワンライナーを紹介します。

昔はJavaワンライナーなんて考えもしませんでしたが、
Java9から使えるようになったJShell(REPL)で対話的に実行する事で、
ワンライナーっぽく書ける様になりました。
中々使う機会が無いので、勉強がてらワンライナーを作成しました。

f:id:acro-engineer:20181218044827j:plain:w200
duke

環境

Java: openjdk version "11.0.1" 2018-10-16
OS: Windows10 Home

※本稿のワンライナーはJava11のAPIを使用しているため、Java10以前では動きません。

JShellを使ったJavaコマンド実行への道

Java11のダウンロード

JDKはいくつか種類がありますが、今回はAdoptOpenJDKを使いました。
adoptopenjdk.net

パスを通す

展開したAdoptOpenJDKのbinにパスを通し、「JShell」をコマンドプロンプトから実行できるようにします。

コマンドプロンプトからJShellを経由してのJava実行

> echo System.out.plintln("Hello.") | JShell -

このように「echo」+「生のJavaコード」+パイプ~JShell~ハイフンでJavaコードの単発実行ができます。
クラスやmainメソッドを書かなくて済む事を考えると、大分楽です。

注意点として、コマンドプロンプト実行ではechoコマンドが「>」を評価してしまうため、「^」(キャレット)でエスケープする必要があります。
例えば以下のような記述になります。

例 (i -> i * 10) → (i -^^^> i * 10)

本題(フォルダ出力ワンライナー)

さて、本題のワンライナーについて。
大量のディレクトリを作んなきゃ、て事はたまにありますよね。
そんな時、さくっと生成したいと考え、
テキストに書いたとおりにフォルダを出力するワンライナーを書きました。

input.txt

Excelからぺっと貼り付けたタブインデント付のテキストです。
このテキストの構成どおりのフォルダ作成を考えます。

test
	test1
		1-1
		1-2
	test2
		1-1
		1-2
		2-1
		3-1
			images
			procedure
		3-2
			images
			procedure
		3-3
			images
			procedure

本稿の主役のワンライナー

上記input.txtと同じフォルダで下記ワンライナーを実行すると、テキストに記載された構成通りのフォルダが生成されます。

>echo Stream.of(new LinkedHashMap^^^<Integer, String^^^>()).forEach(map -^^^> {try { Files.readAllLines(Path.of("input.txt")).stream().peek(k -^^^> map.put(k.length() - k.stripLeading().length(), k.strip())).map(s -^^^> s.length() - s.stripLeading().length() == 0 ? s : String.join("/", map.values().stream().limit(s.length() - s.stripLeading().length()).collect(Collectors.toList())) + "/" + s.strip()).peek(System.out::println).forEach(r -^^^> {try {Files.createDirectory(Path.of(r));} catch (IOException e) {throw new UncheckedIOException(e);}});} catch (IOException e) {throw new UncheckedIOException(e);}}); | JShell -

解説

ワンライナーのままだと読みづらいので、展開してみます。

Stream.of(new LinkedHashMap<Integer, String>()).forEach(map -> {
	try {
		Files.readAllLines(Path.of("input.txt")).stream()
				.peek(k -> map.put(k.length() - k.stripLeading().length(), k.strip()))
				.map(s -> s.length() - s.stripLeading().length() == 0 ? s
						: String.join("/", map.values().stream().limit(s.length() - s.stripLeading().length())
								.collect(Collectors.toList())) + "/" + s.strip())
				.peek(System.out::println)
				.forEach(r -> {
					try {
						Files.createDirectory(Path.of(r));
					} catch (IOException e) {
						throw new UncheckedIOException(e);
					}
				});
	} catch (IOException e) {
		throw new UncheckedIOException(e);
	}
});
最初のStreamは、処理中で使用するLinkedHashMap生成のためだけに行います。
Stream.of(new LinkedHashMap<Integer, String>()).forEach(map -> 以降実際の出力処理
ファイルを読み込み、ファイル1行ごとの文字列に対しストリーム処理を行います。
Files.readAllLines(Path.of("input.txt")).stream()
peekはストリームに影響を与えない処理を行うため、ここでMapにファイル内容を格納しています。

 Mapはキーがタブインデントの数、バリューがフォルダ名になるよう加工しています。

.peek(k -> map.put(k.length() - k.stripLeading().length(), k.strip()))
相対パスの生成を行っています。

 タブ数が0ならそのまま、0以外なら全親フォルダのパス+自分のフォルダ名がストリームに流れます。

.map(s -> s.length() - s.stripLeading().length() == 0 ? s : String.join("/", map.values().stream().limit(s.length() - s.stripLeading().length()).collect(Collectors.toList())) + "/" + s.strip()).
生成された相対パスのDEBUG出力です。
.peek(System.out::println)
生成された相対パスに対し順次フォルダ生成を行います。
forEach(r -> {try {Files.createDirectory(Path.of(r));}


書いてみて、今回の処理は流れをさかのぼる処理があったことから、
最初のmap生成など強引な処理になっているところがあります。
この辺りワンライナーに向いていなかったのではないかと後になって気がつきました。

ちなみに

Streamを使わずに、展開して記述すると以下のようになります。

	var map = new LinkedHashMap<Integer, String>();
	var resultPathList = new ArrayList<String>();
	List<String> lines = null;
	lines = Files.readAllLines(Path.of("input.txt"));

	for (String line : lines) {
		String pathName = "";
		int tabCount = line.length() - line.stripLeading().length();
		line = line.strip();
		map.put(tabCount, line);
		if (tabCount == 0) {
			pathName = line;
		} else {
			for (int index = 0; index < tabCount; index++) {
				pathName += map.get(index) + "/";
			}
			pathName += line;
		}
		resultPathList.add(pathName);
	}

	for (String resultPath : resultPathList) {
		System.out.println(resultPath);
		Files.createDirectory(Path.of(resultPath));
	}

24Lineぐらいありますね。是に比べるとStreamを使ったコードはスッキリしています。
こちらのほうが読みやすいですが。。

まとめ

以上、JShellで書いたワンライナー、いかがでしたでしょうか。
Javaはあまり向いていないとは思いますが、
ストリームを書く練習にはちょうどよいのではないかと思います。

ではでは。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

世界初のElastic認定エンジニアと一緒に働きたい人Wanted! - Acroquest Technology株式会社のエンジニア中途・インターンシップの求人 - Wantedlywww.wantedly.com

Re:VIEW 3.0でレイアウトをカスタマイズしたい!

こんにちは、@snuffkinです。
このエントリは技術同人誌 その2 Advent Calendar 2018の18日目の記事です。

みなさん、技術同人誌、書いてますか~
私は技術書典5で初めて技術同人誌を作成しました。
acro-engineer.hatenablog.com
acro-engineer.hatenablog.com


このとき初めてRe:VIEWに触れたのですが、とても便利ですね。
Markdownライクな独自フォーマットで記述した原稿から、書籍の形式のPDFを簡単に生成できます。

ただ、技術書典5では原稿を書くためにパワーの大半を使ってしまい、書籍のレイアウトにはあまり時間を割けませんでした。
その後、Re:VIEWが3.0にバージョンアップしたのを機に、レイアウトをカスタマイズするために必要な知識を学びました。
この記事では、Re:VIEWでPDFを生成したことがあり、TeXの知識が多少ある方に向けて、Re:VIEWのレイアウト修正方法についてご紹介します。

Re:VIEWの情報源

Re:VIEWのサイトはこちらになります。
Re:VIEW - Digital Publishing System for Books and eBooks(https://reviewml.org/ja/)

カスタマイズして利用したい方は、こちらのナレッジベースが参考になります。
Re:VIEW ナレッジベース — Re:VIEW knowledge ドキュメント(https://review-knowledge-ja.readthedocs.io/ja/latest/)

デフォルト設定でPDFを出力

PDFを生成するには、config.ymlのtexdocumentclassにmedia=ebookを指定し、

texdocumentclass: ["review-jsbook", "media=ebook,paper=a5"]

次のコマンドを実行します。

rake pdf

生成されたPDFは次のようなレイアウトになっています。
f:id:acro-engineer:20181216132648p:plain

このレイアウトには気になる点があったため、赤枠で囲いました。
デフォルトの設定では、各ページのヘッダに章名と節名が表示されています。
私の感覚としては、

  • 偶数ページの左側に章名
  • 奇数ページの右側に章名

とし、赤枠の部分には何も出力されないようにしたいです。

レイアウトを変更

そこで、レイアウトを変更してみましょう。
Re:VIEWのレイアウト情報はstyディレクトリにTeXで記載されています。
そのため、レイアウトをカスタマイズするには、TeXの知識が必要になります。

ページヘッダはsty/review-style.styの次の部分になります。

\RequirePackage{fancyhdr}
\pagestyle{fancy}
\lhead{\gtfamily\sffamily\bfseries\upshape \leftmark}
\chead{}
\rhead{\gtfamily\sffamily\bfseries\upshape \rightmark}

TeXのfancyhdrパッケージを使っています。
lhead、rheadの仕様は

  • \lhead[偶数ページのヘッダの左側]{奇数ページのヘッダの左側}
  • \rhead[偶数ページのヘッダの右側]{奇数ページのヘッダの右側}

であり、[]を省略すると偶数ページ・奇数ページ共に同じヘッダになります。

ヘッダのレイアウトを偶数ページと奇数ページで分ければいいんですね。

Re:VIEWでスタイルを修正したい場合は、sty/review-custom.styに記載します。
review-custom.styは他のstyより後に読み込まれるため、ここに記載した内容でデフォルト設定を上書きすることができます。

\lhead[\gtfamily\sffamily\bfseries\upshape \leftmark]{}
\rhead[]{\gtfamily\sffamily\bfseries\upshape \rightmark}

さて、これでrake pdfしてみると、、、
期待通りのヘッダになりました!
f:id:acro-engineer:20181216132704p:plain

このように、TeXの知識があればレイアウトを変更することができます。
ちょっとレイアウトを変更したいのであれば、このような流れで対応できますね。

おまけ: @<m>でTeXを使うときに「}」はエスケープしなくて良いです!

TeXで「\sum_{x=1}^n x」と出力するとき、次のように書きます。

\sum_{x=1}^n x

Re:VIEWにインライン命令でTeX記法を使うときは「@<m>」を使いますが、多くのサイトで

@<m>{\sum_{x=1\}^n x}

と書くよう説明されています。
「}」をエスケープする必要があるため、「x=1」の後ろの「}」を「\}」と書く必要があります。
TeXは大量の「}」を使うので、いちいちエスケープするのは非常につらいです。

これは最近のRe:VIEWでは改善されていて、「@<m>」の開始と終了に「$」を指定できるようになっています。
「$」を使えば「}」はエスケープしなくて良いです!

@<m>$\sum_{x=1}^n x$

これで、Re:VIEWの原稿にTeXを書きやすくなりますね。

最後に

Re:VIEWはとても素晴らしいツールで、手軽に技術書を書くことができます。
是非皆さんも技術書を書いてみませんか。

では、次回の技術書典で(?)お会いしましょう~

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
モノリシックなアプリケーションをマイクロサービス化したいエンジニア募集! - Acroquest Technology株式会社のWeb エンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com

JJUG CCC 2018 Fall で発表しました

皆さんこんにちは、しんどーです。

この記事はJava Advent Calendar 2018の17日目のエントリーです。
16日目はdo-m-gatoruさんの「JJUG CCC 2018 Fall」に参加してきた。でした。

はじめに

先日の12/15(土)に行われたJJUG CCC 2018 Fallに参加し、登壇させていただきました。
JJUG CCCは日本Javaユーザグループ主催のコミュニティイベントです。
参加者は登録数で1000人超と、ユーザカンファレンスとしては最大級の規模です。
Javaのイベントではありますが、ほとんどJavaに関係ないセッションもあったりでJavaユーザ以外でも十分に楽しめる内容だと思います。

発表について

朝イチで『ふつうのJavaアプリ開発のための自動テスト戦略』というタイトルで発表しました。

ふつうのJavaアプリ開発のための自動テスト戦略 / JJUG CCC 2018 Fall - Speaker Deck



前回のJJUG CCC 2018 SpringではJUnit 5について発表しており、2回連続でテストネタと、特に意識はしてなかったのですが続編的なものになってしまいました。
まだまだ間に合う!JUnit 5入門 / JJUG CCC 2018 Spring - Speaker Deck

ありがたいことに、登壇初心者枠のセッションにも関わらず多くの方にご参加いただき満席御礼となりました。僕に何をそこまで期待していたのでしょうか!?

参加してくださった方々、あとから資料を見てくださった方々には大変感謝感激です。
はてブもたくさんついてるようで、何やら恥ずかしさがこみ上げてきました。

f:id:acro-engineer:20181217003124j:plain
発表の様子

裏話的なこと

テストネタで発表しておきながら、実は僕はあまりテストが好きではありませんw
ただ嫌いなりにテスト(特にユニットテスト)のことに向き合って、いかに楽にできるか、いかに楽しく開発できるかを考えながら発表させていただきました。

CFPでは「実際のコードも交えながらお話します」とか書きましたが、結果コードは1行も出てきていませんww
「話が違うじゃねーか!」と発表中に靴を投げ込まれるのではないかと内心ビクビクしていました。

ノウハウや知識よりも、考え方や自分の想いを伝えるセッションにしたつもりですので、パッション的なものを感じていただければ何よりです。

さいごに

1年前はJJUG CCCに登壇するなんて考えもしなかったのですが、なんと今年は2回もしてしまいました。裏で多くの方に背中を押していただいて、こういう経験をできたのはエンジニアとしてとても貴重なことだと思います。

ちなみにですが、こんな超個人的な内容を会社ブログに上げさせてくれる弊社は自由だなーと思いました。


明日18日目のJava Advent Calendarの担当はnabedgeさんです。
登壇上級者からのJJUG CCCの感想が楽しみです!



Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 

少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

モノリシックなアプリケーションをマイクロサービス化したいエンジニア募集! - Acroquest Technology株式会社のWeb エンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com



ElasticsearchのRanking Evaluation APIについて整理してみた

概要

こんにちは、shin0higuchiです😊
この記事はElastic stack (Elasticsearch) Advent Calendar 2018 - Qiitaの17日目です。
ElasticsearchのRanking Evaluation APIについて整理してみました 。

バージョン情報など

Elasticsearch : 6.5.1
Kibana : 6.5.1

Ranking Evaluation APIとは

Ranking Evaluation APIは、検索クエリに対する検索結果の妥当性を評価するためのAPIです。
Elasticsearchのバージョン6.2以降で利用することができます。


突然ですが、「良い検索」とはどういったものでしょうか?
一般的に、指定した検索条件に対して、より関連度の高いドキュメントが検索結果の上位に来るほど、良い検索システムであると言えます。
このAPIでは、「関連度の高いドキュメント」を事前にテストデータセットとして定義することで、実際の検索結果を評価します。

使いどころとしては、「index時に利用しているanalyzerの設定が適切か?」「アプリケーションがElasticsearchに対して発行するクエリは適切か?」などを定量的に評価したい場合に利用するのが良いと思います。

使い方

データ登録

今回は、日本語のWikipediaデータをElasticsearchに登録しておきます。
https://dumps.wikimedia.org/jawiki/20181201/jawiki-20181201-pages-articles6.xml-p2534193p3873913.bz2
からデータをダウンロードし、Logstashを使って取り込みました。

Kibanaから確認してみたところ、wikiというindexが作成されています。

f:id:acro-engineer:20181216212547p:plain:w700
title一覧

約60万件の記事が登録されていることがわかります。

また、タイトル一覧を取得してみると、非常に多岐にわたる記事が登録されていますね。

f:id:acro-engineer:20181216213806p:plain:w700
title一覧

リクエストの形式

Ranking Evaluation APIを利用する際のリクエスト形式は次のようになります。

GET /wiki/_rank_eval
{
    "requests": [ ... ], 
    "metric": { ... }
}

詳細については後述しますが、リクエストボディは大まかに2つの要素からなります。

  • requests : 検索クエリおよび、そのクエリで検索結果の上位に来て欲しいドキュメントのリスト
  • metrics : 検索結果の評価方法の定義

では、それぞれの定義内容について説明します。

requests

requestsの形式は次の通り。

{
            "id": " ... ",
            "request": {
                "query": { ... }
            },
            "ratings": [
                { "_index": "wiki", "_id": "doc1", "rating": 0 }.
                { "_index": "wiki", "_id": "doc2", "rating": 1 },
                { "_index": "wiki", "_id": "doc3", "rating": 2 }
            ]
        }

request内にクエリを記述し、そのクエリで検索結果の上位に来て欲しいドキュメントのリストはratingsの中に記述します。indexとdocument idを指定し、ratingに関連度を記述します。ratingの数値は整数で、0であれば検索にヒットして欲しくないドキュメント(クエリに関連のないドキュメント)、数値が大きいほど関連のある(検索上位に来て欲しい)ドキュメントとなります。

上記の例だと、_idがdoc3とdoc2のものが検索結果上位に来て、doc1はヒットしないのが理想ということです。
Ranking Evaluation APIでの評価は、実際にクエリを実行した時に、ratingに定義したリストにどれだけ近いかを数値化するものです。
厳密にはこの後に説明するmetricで評価方法を決定します。

metric

評価指標としては次の4つがサポートされています。

  • Precision at K
  • Mean Reciprocal Rank (MRR)
  • Discounted Cumulative Gain (DCG)
  • Expected Reciprocal Rank (ERR)

詳細については、本記事では割愛します。
もし評価方法論について興味があれば、個人的には下記の書籍がオススメです。
情報アクセス評価方法論-酒井哲也-

この記事ではもっともシンプルなPrecision at K(以下、P@K)を用います。
これは実際の検索結果の上位Kドキュメントの適合率を表します。

metricにP@Kを設定するには下記のように記述します。

"metric": {
      "precision": {
        "k" : 5,
        "relevant_rating_threshold": 1
      }
   }

この例では、検索結果の上位5件に、rank1以上のドキュメントが何割あるかを評価します。
5件全てが、関連のあるドキュメント(rank1以上)であれば適合率は100%、つまりP@Kは1となります。

実行してみる

では、実際に今回のケースに適用してみます。

例えば検索ユーザーが、「Javascriptフレームワークに関連するドキュメントが欲しい」と考えたとしましょう。ユーザーは「Javascript Web フレームワーク」などと検索すると仮定します。このクエリで検索した時の結果を評価してみましょう。実際のリクエストは下記のようになります。

GET wiki/_rank_eval
{
  "requests": [
    {
      "id": "javascriptのフレームワークを探すクエリ例",
      "request": {
        "query": {
          "query_string": {
            "query": "Javascript Web フレームワーク"
          }
        }
      },
      "ratings": [
        {
          "_index": "wiki",
          "_id": "3440787",
          "rating": 1
        },
        {
          "_index": "wiki",
          "_id": "3387491",
          "rating": 1
        },
        {
          "_index": "wiki",
          "_id": "2611804",
          "rating": 1
        },
        {
          "_index": "wiki",
          "_id": "2922947",
          "rating": 1
        },
        {
          "_index": "wiki",
          "_id": "3672684",
          "rating": 1
        }
      ]
    }
  ],
  "metric": {
      "precision": {
        "k" : 5,
        "relevant_rating_threshold": 1
      }
   }
}

ここでは関連のあるドキュメント上位5件のIDをratingsに登録しています。
実際に実行してみると次のようなレスポンスが返ってきます。

{
  "metric_score" : 0.4,
  "details" : {
    "javascriptのフレームワークを探すクエリ例" : {
      "metric_score" : 0.4,
      "unrated_docs" : [
        {
          "_index" : "wiki",
          "_id" : "2828794"
        },
        {
          "_index" : "wiki",
          "_id" : "3447215"
        },
        {
          "_index" : "wiki",
          "_id" : "3609062"
        }
      ],
      "hits" : [
        {
          "hit" : {
            "_index" : "wiki",
            "_type" : "page",
            "_id" : "2828794",
            "_score" : 32.780136
          },
          "rating" : null
        },
        {
          "hit" : {
            "_index" : "wiki",
            "_type" : "page",
            "_id" : "3440787",
            "_score" : 32.139748
          },
          "rating" : 1
        },
        {
          "hit" : {
            "_index" : "wiki",
            "_type" : "page",
            "_id" : "3447215",
            "_score" : 31.709887
          },
          "rating" : null
        },
        {
          "hit" : {
            "_index" : "wiki",
            "_type" : "page",
            "_id" : "3609062",
            "_score" : 28.539764
          },
          "rating" : null
        },
        {
          "hit" : {
            "_index" : "wiki",
            "_type" : "page",
            "_id" : "3387491",
            "_score" : 28.265165
          },
          "rating" : 1
        }
      ],
      "metric_details" : {
        "precision" : {
          "relevant_docs_retrieved" : 2,
          "docs_retrieved" : 5
        }
      }
    }
  },
  "failures" : { }
}

"metric_score" : 0.4という結果から分かるように、適合率は40%、つまり5件中の2件のみが関連するドキュメントで、残りの3件は関連がない若しくはrateingされていないドキュメントということになります。

実際、unrated_docsというブロックを見ると、3件のドキュメントが入っています。
これは、想定していなかった(リクエスト時にrating指定していなかった)ドキュメントが、検索結果の上位5件に入ってきたことを意味します。
ドキュメントを確認してみたところ、「Vue.js」「Angular.js」などの他に、「シングルページアプリケーション」などがヒットしていました。

今回のケースでは、「シングルページアプリケーション」よりも上位に来て欲しい他のフレームワークなどもありました。
実際には、ここからanalyzerやscoringなどを調整して理想の検索結果に近づくようチューニングをしてゆくことになるでしょう。
その時に再びRanking Evaluation APIを利用して精度が上がったかどうかを客観的数値で測ることができます。
今回の記事の中では、長くなってしまうためチューニングについては割愛します。

さて、ここまでRanking Evaluation APIの使い方を簡単に説明してきました。
真面目に検索システムとして評価するとなると、通常は1つのクエリだけでなく、複数のクエリを用いて総合的に評価するでしょうし、ratingに関してもより網羅性を上げる必要があると思います。ただ、全ドキュメントにレートをつけるのは現実的ではないので、通常はどの程度までratingするものなのか、詳しい人がいたら是非話してみたいものです。

まとめ

  • Ranking Evaluation APIを使って、検索システムとしての定量的評価ができる
  • 事前のRatingなど、テストデータセットの準備が手間

以上です。ツッコミ等あれば是非お願いいたします。
お読みいただきありがとうございました。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

世界初のElastic認定エンジニアと一緒に働きたい人Wanted! - Acroquest Technology株式会社のエンジニア中途・インターンシップの求人 - Wantedlywww.wantedly.com

開発者が見落としがちなユーザビリティアンチパターン

こんにちは。QAエンジニアの @yuki_shiro_823 です。

このエントリはソフトウェアテスト Advent Calendar 2018の15日目の記事です。

きっかけ

私は社内で色々なプロジェクトを渡り歩き、横断的にテストを行っています。
テストをしていると、

  • 機能的には問題ないけれども、なんとなく使いづらい
  • お客様の業務に照らし合わせると、もっとこうした方がいいのでは?

という事例を時々見かけます。
「開発者にもユーザ視点で、開発時から基本的なユーザビリティを向上させられるようになってほしい!」
という思いから、いくつかアンチパターンをまとめてみました。
その一部を紹介します。

ユーザビリティの定義と本エントリで扱う範囲

ユーザビリティの定義は、ヤコブ・ニールセンによる定義やISO25010などいくつかあります。
本エントリではISO-25010の「システム/ソフトウェア製品品質」で定義されている「使用性」の一部「適切度認識性」にまつわるアンチパターンをとりあげます。
f:id:acro-engineer:20181214014259p:plain:w450

適切度認識性のアンチパターン

適切度認識性について

ISO25010では、「ニーズに適した利用かどうか認識できる度合」と定義されています。

損なわれるとどうなるか

ユーザがシステムを利用して、明示された作業及び目的の達成を実行できなくなる、と考えられます。

アンチパターン1:探し物はどこですか?

f:id:acro-engineer:20181214020750p:plain:w200

内容

表やドロップダウンリストの並びが何の順番になっているのか不明で、自分の見たいものがどこにあるのか分からない、というケースです。
業務上、最新の結果から順番に表示してほしいのに、件名の昇順になっていて使いにくいということがありました。
また、サイトを訪れる人のほとんどが日本国籍と思われるサイトで、国籍を選択するドロップダウンリストになかなか「日本」が出てこないというのもよく見ます。
f:id:acro-engineer:20181214235724p:plain

対処策

一覧であれば、業務での使用順に合わせて並んでいる、ドロップダウンリストであれば、選択肢が使用頻度の高いものから並んでいるという構成にしてほしいです。上記の例では「JP 日本」がドロップダウンリストの上位に表示されていると、ユーザにとっては使いやすいですね。
要件をヒアリングする段階で、ユーザが一覧にした内容で何をするのか、が明確にできていると後々助かります。(コーディングの段階で気づくと、修正が厄介な場合が多い問題です)

アンチパターン2:反応がない…

f:id:acro-engineer:20181214021830p:plain:w200

内容

「登録」や「検索」などのボタンをクリックしたものの、画面に何も反応がなく、処理が行われているか分からないというケースです。ユーザがボタンを連打してしまい、二重登録などが発生してしまう恐れがあります。

対処策

処理中であることが、ユーザに明確に分かるようにしてください。
こちらは、はてなブログの編集画面にある「公開する」ボタンです。
f:id:acro-engineer:20181215002713p:plain
一度「公開する」ボタンを押下すると、次の通りボタンの色が変わります。
f:id:acro-engineer:20181215004535p:plain
いかにもクリックできない様子になりました。
このように、

  • ボタンをクリックできなくする/ボタンの色を変える
  • プログレスバーなどを表示し、処理中であること示す

などがあると分かりやすくなります。
登録画面などの場合は、「登録ボタンは一度だけクリックしてください」などの注意書きがあるだけでも、連打の防止に効果があるそうです。

アンチパターン3:キャンセルのキャンセル?

f:id:acro-engineer:20181214030515p:plain:w200

内容

ユーザの操作に対して、何らかのメッセージが出るものの、次に何をしてよいか明確でない、すぐに操作が行えない、というケースです。
f:id:acro-engineer:20181215000125p:plain
パッと見ただけでは、「キャンセル」ボタンをクリックしたときに、どちらになるのか分かりません。
キャンセルをキャンセルするので、結果は「キャンセルされない」になるのですが、キャンセルがゲシュタルト崩壊しそうですね。
こちらは、キャンセルのキャンセル問題という有名なアンチパターンです。詳しく知りたい方はぜひ、以下のサイトをどうぞ。
キャンセルのキャンセル問題から考えるダイアログデザイン

対処策

メッセージの場合は、「xxxに誤りがあります。ご確認ください」など次に何をすればよいか明示するようにしてください。
また、メッセージ中の動詞とボタンの動詞をそろえるのも効果的です。

アンチパターン4:どこへも行けない

f:id:acro-engineer:20181214025231p:plain:w200

内容

ユーザがある画面から、他の画面に戻りたいと思ったときに移動する手段がないケースです。例えば、次のような画面がありました。

  • 「戻る」ボタンがない
  • ダイアログ表示で「戻る」または「閉じる」ボタンがない

f:id:acro-engineer:20181215000220p:plain:w300
こちらのキャプチャの例は、半分ネタですが、ダイアログのエリア外を押すとダイアログが消えることを知らないと、ユーザは何もできなくなってしまいます。

対処策

明確に「戻る」や「閉じる」のボタンを設けてください。
ボタンを設けない場合は、「ブラウザのバックボタンでお戻りください」など、ユーザが次の操作を行えるような指示を記載してください。

最後に

4点のアンチパターンを紹介しましたが、気を抜くと誰しもやってしまいがちなものだと思います。
何かの折にふと思い出してもらい、ユーザビリティの高いシステムを作るヒントにしてもらえると幸いです。

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
モノリシックなアプリケーションをマイクロサービス化したいエンジニア募集! - Acroquest Technology株式会社のWeb エンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com