IT + X

ITと何かのコラボレーションは時代を変えるかも

(おまけ)ディープラーニングを学んで思ったこと

前のエントリーでディープラーニングについて書きましたが、学ぶ中で思ったことや考えたことが色々あったので書いておこうと思います。

ニューラルネットはソフトウェアのイノベーション

今までのソフトウェアは、人間がいちからプログラムを書かないと正しく機能しませんでした。
しかし、ニューラルネットではデータを与えることによって自ら改善していくようになったことはエポックメイキングなことだと思いました。
ある意味、いままで実現できなかった自動プログラミングが可能になったということだと思います。
これが進化すれば、プログラミングさえもコンピューター自身が行うこともいずれ可能になるでしょう。
ソフトウェア開発だけでなく、あらゆるものの製造やデザイン、研究などもできるようになるかもしれません。
もちろん、全てをコンピューターに代替するのは難しくて、物事の善悪を判断したり文化的背景を考えたりすることは難しいと思います。
ただ、コンピューターに依存する場面はこれからますます増えていきますので、すべての人がコンピューターについて正しい知識を持つ必要がある時代になっていくのではないでしょうか。

バイスの進化がイノベーションを可能にする

ディープラーニングの技術は最近出てきたように思われているかもしれませんが、元々のアイデアは40年前に考えられていたのですが(しかも日本人!)いろいろな意味で早すぎたため注目されませんでした。
それが、半導体チップの集積率が上がって実現可能な技術となったため一気に知られるようになりました。
一見華やかなイノベーションも地道な努力があったから可能になったということなのでしょうね。
昔、私がPCをはじめて買った頃はゲームしかできない高いおもちゃで役に立たないとさんざん言われましたが、ここまで来るとは想像もできなかったです。

学習が一番負荷がかかる

機械学習で一番負荷のかかるのは学習です。
大量のデータを用意するのも大変ですが、それをニューラルネットに入力して学習させるのにかなりのCPUパワーが必要です。
そこで機械学習に特化したチップやGPUなどを使って高速化する技術も出てきました。
アプリケーションによってデバイスにも技術革新が起こるという点で興味深い現象です。
そんな機械学習を学んでいると、ふと人間の学習についても考えるようになりました。
人間でも小中高大と16年もかけて学習していますが、そんなに長期間かけてるのにちゃんと学習できているのかと思いました。
テストで点数を取ることばかり気にして本当の理解ができていないのではないか、それは機械学習で言えばテストが通るようなモデルを作っているのと同じだと思います。
機械学習には過学習という問題があって、特定のデータに最適化された学習が行われると正しい判断ができないモデルになってしまうことがあります。
人間の教育もあまりにもテストを重視するために過学習状態になっている人が多いのではないでしょうか。
最近の日本のエリートの劣化を見ると、そんなことも考えてしまいました。
技術から人間のあり方が洞察できるというのもおもしろいですね。

多くの試行錯誤がいいモデルを作る

機械学習では、バッチでひとまとまりのデータを複数回与えてニューラルネットを改善していくことが基本動作になります。
なので、最初は赤ちゃんと同じで何もできない状態ですが、試行錯誤しながら学習することによってうまく処理できるようになっていきます。
ここで思ったのが、日本社会はこの対局にあるなということでした。
学校でも減点主義でミスするとバッテンをくらいますし、企業ではミスすると左遷されたり給料査定を下げられたりします。
こういうことをすると学習する機会を奪われることになります。
一般にはディープラーニングは話題になっていますが、今の日本社会では受け入れられない技術ではないかと思います。
特に、日本企業は終身雇用で長く働いている人が多いので、自分が用済みになりそうな技術を積極的に入れようとは思わないでしょう。
そういう意味で私は日本社会には絶望していますので、機械学習でビジネスするのなら海外がいいのではないかと思っています。

ブラックボックスではない

AIはブラックボックスで中身がわからないとよく言われますが、これは正しくありません。
前のエントリーでも説明したとおり、ニューラルネットのキモはニューロン間の接続の重みデータです。
これは見ることはできますが、ただの数値の羅列なので人間がその意味を理解できないだけです。
しかし、それをわかりやすく表示するツールや挙動を分析するツールなども出てきてますのでこれからだんだんと可視化されるようになるのではないかと思います。
なので、怖がるのではなくちゃんと理解して付き合うことが大事だと思います。

ちょっとエッセイ風に書いてみましたが、いかがだったでしょうか?
私はディープラーニングをインターネット以来のイノベーションだと思っています。
そして、単に技術的な進歩というだけでなく、仕事がなくなったり誤った使われ方をするなどの負の側面を考えることによって、これから人はどうあるべきなのかを考える機会にもなるのではないかと思います。
いずれにしても機械学習によってよりよい社会になってほしいなと思います。

ディープラーニングは難しくない

ディープラーニングという言葉は今では専門家だけでなく多くの人が知るようになりました。
自動運転車や顔認識などニュースや身の回りで使われるようになっているから広く知られるようになったのでしょうが、ディープラーニングがどのようにして動いているのかエンジニアでも知らない人も結構います。
今回、集中的にディープラーニングについて学んだのでそのまとめと学んでいて感じたことを書いておこうと思います。

まず、ディープラーニングを学ぼうと思った時に本屋さんで色々見てみたのですが、その多くが数学の教科書みたいなものばかりでした。
大学で発展してきた技術なので仕方ないのでしょう。
しかし、実際に学んでみて思ったのが、線形代数や確率などの数学は使う分にはそこまで数学は必要ではなく、実際のプログラムではライブラリが難しい部分をやってくれるのでがちで数学が必要なところはそんなにありません。
後、ほとんどの本が詳細なところにフォーカスしていて全体的にイメージしにくいと思いました。
著者が学者かエンジニアの人ばかりなので細かな所に気が行ってしまうのでしょうが、私のような初心者には全体をイメージできるほうが理解がしやすいなと感じました。
そんな中で比較的理解できたのが次の2冊でした。

この本はPythonを使ったディープラーニングの本ですが、極力ライブラリを使わず底辺から作っていくスタイルなのでディープラーニングのしくみを学ぶにはとてもいいと思います。
数学の記述も多少ありますが、高校レベル+線形代数の基礎くらいで読みとけるのでそんなに難しくないと思います。

PythonとKerasによるディープラーニング

PythonとKerasによるディープラーニング

この本もPythonを使っていますが、TensorFlowやKerasなどのライブラリを使っています。
前の本はゼロから作っていましたが、こちらの本ではライブラリを使うので実践で使うにはこちらのほうがいいかもしれません。

後、ネットの情報などでも色々学んでみて思ったのはディープラーニングの基本的なアイデアは単純だということでした。
ディープラーニングの元となるニューラルネットのアイデアは20年以上前からあったらしいのですが、最近ブレイクしたのはコンピュータの処理能力が上がったからでした。
なので、色々学ぶことは多いですが基本はシンプルですので理解するのはそんなに難しくないと思います。
では、ディープラーニングについて詳しく説明していきましょう。

ニューロン

ディープラーニングの元々のアイデアは生物の脳の研究が元になっています。
未だに生物の脳がどう動いているかは解明されていませんが、その基本要素である神経細胞ニューロン)はどう動いているかはわかっています。
f:id:anthony-g:20190927123555p:plain この図のようにニューロンは電気信号を伝える細胞で、複数の入力と一つの出力を持つ素子になっています。
そして、入力信号がある閾値を越えるとニューロンが発火し信号を出力します。
ディープラーニングはこのアイデアを元にコンピューター上で実現された技術です。

ニューラルネット

では、このアイデアディープラーニングではどう使われているのでしょうか?
ここでニューラルネットというものが出てきます。
ニューラルネットとは、ニューロンを複数束ねてそれぞれを信号線で接続した形のものを言います。
下図のようなものが一例です。

f:id:anthony-g:20190927124302p:plain

ニューロンを複数持つグループが層になっていてそれぞれ接続されているものをニューラルネットといいます。
図のように入力層、隠れ層、出力層と呼ばれる層で構成されていてこの順番で信号が流れます。
この隠れ層が100以上持つようなニューラルネットディープラーニングでは使われます。
当たり前ですが、この層が増えるに従ってマシンパワーが必要になります。
ディープラーニングがコンピューターの能力が上がらないと実現できなかった技術であることがこれで理解できると思います。

ディープラーニングワークフロー

ディープラーニングでは、データを読み込んで学習し、学習結果が正しいかをテストした後にAIとして使えるようになります。
ディープラーニング入門でよく使われる手書き文字認識の例で具体的に説明します。 まず、コンピューターに学習させるために教師データというものを用意します。
この例では、教師データは手書き文字画像とそれがなんの文字であるかを表すラベルがペアになっています。
その教師データをニューラルネットに大量に入力します。

f:id:anthony-g:20190927131627p:plain

この学習の処理がディープラーニングで一番負荷のかかる処理です。
学習の結果、ニューラルネットが手書き文字認識のために最適化されますので、それがちゃんと正しいかをテストします。
テストデータは学習したときと同じ形式のデータを使いますが、データ内容は別のものを用意します。
なぜなら、学習データに都合のいいニューラルネットになっていないかをチェックするためです。
ニューラルネットには過学習という問題があり、特定のデータに過度に最適化されると違うタイプのデータをうまく処理できなくなるからです。

f:id:anthony-g:20190927132458p:plain

何を処理するかにもよりますが、正解率99%以上が現実に使えるニューラルネットのようです。
そして、テストで問題なければ実際のデータで画像認識を行います。

f:id:anthony-g:20190927133107p:plain

これが一般的な機械学習処理のワークフローになります。
では、ニューラルネット上で学習処理はどう行われているのかを次に説明します。

学習とはニューラルネットの重みデータの最適化

前にニューラルネットは層で別れていてニューロン同士が接続されている構成をしていると説明しました。

f:id:anthony-g:20190927124302p:plain

この図ではニューロン同士がフルメッシュで接続されていますが、特定のニューロンの接続だけを取り出したのが次の図になります。

f:id:anthony-g:20190927133906p:plain

b, x1, x2のニューロンとyのニューロンが接続されていて、b, x1, x2から入力されたデータがyで出力されるということを表しています。
ここで大事なのがw1, w2という値で「重み」と呼ばれている値です。
これはニューロン間の接続に付随する値で、w1はx1にかけられw2はx2にかけられます。
ちなみにbはバイアスと呼ばれていてニューロンの発火のしやすさを制御します。
この例では右にある方程式の計算が行われて計算結果が0より大きければyは1、0以下ではyは0となるニューラルネットを表しています。
先程の手書き認識の例でいうとx1,x2は入力画像データです。
そして、w1, w2は学習処理のときに設定されます。
つまり、機械学習とはこの重みデータを最適な値に設定する処理のことをいいます。
これはすごくシンプルな考え方だと思いませんか?
私が最初にこのことを理解したとき、機械学習ってこういうことなのかと目からウロコでした。
つまり、重みの値を変えることによって手書き文字が読めるようになったり翻訳できるようになったりするのです。
実際は、後で説明するように特殊なニューラルネットを使ったり速度を上げるためのテクニックとかあるのですが、基本はこれです。
これを抑えておけばあとはTipsみたいなものと考えていいと思います。

あともう一つ説明しなければいけないのが活性化関数についてです。
前に、ニューロンは入力によってしきい値を越えると発火すると言いました。
したがって、どのような値で発火するか判定する関数を定義する必要があります。
それが活性化関数です。
上のニューラルネットの図のyのニューロンで重みをかけて計算した結果を活性化関数に渡しています。

f:id:anthony-g:20190927154640p:plain

上図の右の方程式は、重みによる計算結果を活性化関数に与えてニューロンが発火するかどうかを判定しています。
活性化関数としてはシグモイドとReLUが代表的ですが、グラフにあるようにある閾値を越えるとyの値が増えるのがわかると思います。
活性化関数もライブラリで用意されてますので引数を渡して呼べばいいだけです。

では、重みデータの最適化はどのように行われるのでしょうか?
最初は、重みデータは0で初期化されていたりランダムな数値が設定されていたりします。
それを、教師データを読み込んで正解と予測値を比べて差異がなくなるまで繰り返し試行錯誤を行います。

f:id:anthony-g:20190927153013p:plain

上の図で損失関数というのが正解と予測値の差を求める関数で、オプティマイザーは差の値を元にニューラルネットの重みデータを更新します。
損失関数には二乗和誤差とか交差エントロピー誤差などがあり、オプティマイザーのアルゴリズムもRMSPropやAdamといったものがありますが、扱うケースによってライブラリのパラメータで指定することができますので内部の詳細なアルゴリズムまで知らなくても使うことができます。
(もちろん、興味のある方はぜひ勉強してみてください。)

CNN(畳み込みニューラルネット

ディープラーニングが一番脚光を浴びたのは画像認識です。
動物や人の顔を見分けたり地形を検知したりいろんなところで応用されています。
画像認識でよく使わるのが畳み込みニューラルネット(Convolutional Neural Net)と呼ばれるものです。

f:id:anthony-g:20190927155613p:plain

上図に畳み込み層とプーリング層というのがあると思いますが、これは画像の特徴あるところを抽出する層です。
詳細は本を読んでいただいたほうがいいと思いますが、例えば人が猫の写真を見た場合、顔の形とか目などの特徴を抽出して判断しているらしいのですがそれと似たことをCNNでも数値処理として行っています。
後、ソフトマックスというのは複数の値を出力するための層で、例えば猫である確率と犬である確率と人である確率を出したい場合は3つの出力を持つソフトマックスということになります。
そして、いまやディープラーニングは人間の認識能力を上回るようになりました。
人間でも気づかないような特徴をコンピューターが認識できるようになったということなのでしょうね。

RNN(リカレントニューラルネット

ただ、CNNには過去のデータを覚えられないという弱点があります。
例えば、文脈で判断する必要のある文章や過去の動作情報が必要な対物検知など時系列のデータを扱うものには使えません。
そこで考えられたのがリカレントニューラルネット(Recurrent Neural Net)です。

f:id:anthony-g:20190927160611p:plain

上図左のRNNが一つのニューラルネットを表していて、自分の出力を入力に再投入します。
右図は詳細の図で、前のニューラルネットの結果を次のニューラルネットに入力することによって前のデータの反映した出力を行うことができます。

かなりざっとした説明になってしまいましたが、ディープラーニングを学ぶ上でこれだけ前知識があれば本を読む時にかなり理解しやすいと思います。
特にご紹介した2冊は私が見た中では一番わかりやすいのでぜひ読んでみてください。
また、ネタが溜まってきたらブログでまとめたいと思います。

Elixirを使ってみたらとても楽しかった

Elixirはマルチプロセスを前提とした関数型プログラミング言語でエリクソンが電話交換機プログラムのために開発したErlangという仮想マシンシステム上で動作します。
Elixirが流行ってきた背景として、CPUが複数コアで構成されるようになり個人のパソコンでも8コアや16コアのCPUが使われるようになってきたのがあると思います。
しかし、CやJava,Pythonなどの現在主流の言語はシングルコアのCPUを前提にした言語なのでマルチコアをうまく扱えません。
そこでGoやRustなど新しい言語が登場してきたのですが、Elixirはその中でも比較的とっつきやすく関数型プログラミングのよさもうまく取り込んだ言語です。
ただ、古いタイプの言語に慣れている人にはそこがとっつきにくい部分かもしれません。

最新の情報を得たかったのでこちらの本で学びました。

Programming Elixir ? 1.6: Functional |> Concurrent |> Pragmatic |> Fun

Programming Elixir ? 1.6: Functional |> Concurrent |> Pragmatic |> Fun

Elixirを使ってみて私がいいなと思ったのはこんなところでした。

  • Lispっぽいリスト
  • mapや再帰を使ってループを使わない
  • 関数シグネチャーを使って条件分岐を行う
  • 関数を複数つなぐパイプオペレーター
  • 複数プロセス間のメッセージ通信が簡単

Lispっぽいリスト

昔、EmacsというエディタでLispという言語を使ったことがありますが、Elixirの配列はLispによく似てると思いました。
例えば、Lispはリスト構造が基本でcarやcdrというリストの先頭やそれ以外を取得する関数があります。
それと同じような機能がElixirにもあります。
例えば、配列の先頭を取得するには以下のように書きます。

iex > [ head | tail ] = [ 1 , 2 ,3 ,4 ,5 ]
iex > head
1
iex> tail
[ 2, 3, 4, 5 ]

ちなみにiexはElixirのシェルみたいなものでRubyでいうirbと同じものです。 この言語を作った人は明らかにLisperですね。

また、mapというデータタイプを使うことができます。

iex> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}
iex> map[:a]
1

mapとはキーと値がペアになっているデータで、他の言語では連想配列と呼ばれているものです。
ちなみに配列の添字に使われている:a や:bはアトムと呼ばれている定数データで見た感じ文字列のように見えますが スキャン処理などを高速に行えるようにハッシュ値のようなデータです。
Rubyのシンボルを同じですね。

再帰を使ってループを使わない

Lispでは再帰呼び出しというテクニックをよく使いますが、Elixirでもよく使われます。 再帰とは関数の中で自分自身を呼ぶことをいいます。
例えば、フィボナッチ数を計算する関数はこんな感じです。

def fib_calc(n) do
   result = fib_calc(n - 1) + fib_calc(n - 2)
end

フィボナッチ数は最初0と1から始まるので停止条件として書いておくべきなのですが上の例では省略しました。
CやJavaで書く場合はforループなどで集計していくことが多いですが、再帰で書くことによってシンプルな記述になるところがいいですね。
ただ、再帰は慣れていないと訳がわからないコードになりがちなのでプログラマーの習熟が必要です。
また、再帰は普通スタック領域をたくさん消費したり実行速度が遅かったりするのですが、Elixirではうまく最適化してくれているようです。

関数シグネチャーを使って条件分岐を行う

例えば、C言語でエラー処理のコードはこんな感じになります。

func1(int val , int error_code){
   if (error == 0){
          do_something(val);
   }else{
         do_error(error_code);
   }
}

Cでコード書いていると条件分岐でエラー処理だらけになることが多いです。
一方、Elixirではこういう書き方ができます。

defmodule m1 do
    def func1( {:ok, val}) do
           do_something(val)
    end
    def func1({:error, error_code}) do
            do_error(error_code)
     end
end

波括弧の引数はタプルと呼ばれているデータ型で変更できない配列です。
上の例では同じ名前で引数の違う関数func1が定義されます。
Elixirでは同じ関数名でも引数を変えることにより別の関数として定義することができます。
したがって、この例では:okを引数に与えると通常の処理をして:errorを与えるとエラー処理が行われます。
このように、Elixirではifなどの条件分岐をできるだけ使わないようにしてコードの見通しをよくすることができます。

関数を複数つなぐパイプオペレーター

シェルをご存知の方はコマンド同士をつなぐパイプという機能があるのをご存知だと思います。

# cat abc.txt|grep "ABC" | wc -l

Unixのコマンドは一つ一つは単純な処理しかできませんが、パイプで組み合わせることによって様々な処理を行わせることができます。
Elixirにもパイプオペレーターというものがあり、関数をつないで複雑な処理を行わせることができます。

iex> "Elixir rocks" |> String.upcase() |> String.split()
["ELIXIR", "ROCKS"]

上の例では|>がパイプオペレーターで"Elixir rocks"という文字列がString.upcase()関数の第一引数として与えられます。
つまり、String.upcase("Elixir rocks")を呼んでるのと同じことになります。
次のString.split()も同様にデータが流れます。
Unixエンジニアにはあたかもシェルスクリプトを書いているみたいでたまらない機能ですね。

大量プロセスを起動してもオーバーヘッドが少ない

Elixirでは簡単に複数のプロセスを立ち上げて並列で処理させることができます。
ここでいうプロセスとはOSネイティブのプロセスではなくElixirのVM上で実行するプロセスなのでとても軽いものになっています。
プロセスの生成は以下のように行います。

spawn(module_name, function_name, [function_params])

例えば、複数のプロセスで合計するプログラムはこんな感じになります。

defmodule ParalellSum do
    def psum(n) do
        result = Enum.reduce(n, fn(x, acc) -> x + acc end)
        result
    end
    def do_process(n) do
        num_processes = n
        calc_val = [1,2,3,4,5]
        Enum.each 1..num_processes, fn(_) -> spawn(ParalellSum, :psum, [calc_val]) end
    end
    def run(n) do
        :timer.tc(ParalellSum, :do_process, [n])
        |> IO.inspect
    end
end

大まかに説明すると、spawn(..)でプロセスを生成してpsum()という関数を別プロセスで並列に動かしています。
psum()関数はcalc_valの要素を合計します。
このプログラムをプロセス数1000,10000,100000で動かした結果がこちらです。

$ elixir -r myprocess.ex -e "ParalellSum.run(1000)"
{1756, :ok}
$ elixir -r myprocess.ex -e "ParalellSum.run(10000)"
{18173, :ok}
$ elixir -r myprocess.ex -e "ParalellSum.run(100000)"
{156408, :ok}

run関数のパラメータがプロセス数で結果の数値は実行時間をマイクロ秒で出力します。
ご覧の通り、10万プロセスで0.1秒ほどしかかかっていません。
ちなみに実行環境はMacBookPro Corei5 2.3GHzでした。
プロセスによるオーバーヘッドはほぼないと考えて開発できますね。

複数プロセス間のメッセージ通信が簡単

Elixirの一番の売りはプロセス間通信が簡単なことです。
次の例はエコーサーバーを生成してクライアントのメッセージをそのまま返すプログラムです。

defmodule Echo1 do
    def echo_back do
        receive do
            {sender, msg} ->
                send sender, {:ok, "#{msg}" }
        end
    end
end

# client
pid = spawn(Echo1, :echo_back, [])
send pid, {self(), "Hello World !"}

receive do
    {:ok, message} ->
        IO.puts message
end

spawn()関数でエコーサーバーを生成してクライアントから"Hello World!"というメッセージを送信するとサーバーがそのまま返します。
send pid,...がサーバーにメッセージを送信しているところで、echo_back関数のreceive do...のところで受信しています。
{send, msg}->のところは送られてきたメッセージのパターンを記述するところでマッチすればそれ以下の処理が実行されます。
したがって、このパターンを複数持たせればメッセージパターンによって違う処理をさせることができます。 LinuxC言語でデーモンを書くよりはるかに簡単ですね。

ざっと触ったところではこんな感じでした。
また、ElixirにはRailsに似たウェブフレームワークPhenixや組み込み系フレームワークのNervesなどがあっておもしろそうです。
Elixirはマルチプロセスで通信しながら処理を行うのが当たり前なのでRubyPythonとは違った作りにできると思います。
次回ウェブアプリ開発を行うときはElixirを使ってみたいなと思いました。

Vue.jsを使ってみてよかったところ、イマイチなところ

Vue.jsはReact,Angularに並ぶJavascriptフレームワークです。
しばらくフロントエンド開発にはかかわらなかったのでいろいろハマりましたが、備忘録も兼ねてまとめてみました。

Vue.jsのいいところ

リアクティブシステム

Vue.jsはリアクティブシステムになっています。
リアクティブシステムとは、ある変数の値を変更するとブラウザ上の表示も自動でアップデートしてくれるしくみをいいます。
例えば、以下のようなJavascriptのコードの場合、

let price = 1000;
let quantity = 10;

total = price*quantity;

totalに計算結果が出力されますが、後でpriceの値を変更(例えばprice = 200)にしてもtotalの値は変わりません。
しかし、リアクティブシステムではpriceの変更にともなってtotalが更新されます。
Vue.jsでは以下のように書きます。

var vm = new Vue({
   el: '#app',
   data: {
     price: 1000,
     quantity: 10
   },
   computed: {
    totalPrice(){
       return this.price * this.quantity
     }
  }
})

<div id="app">
  <div>Price: ${{ price }}</div>
  <div>Total: ${{ totalPrice}}</div>
</div>

ブラウザのデベロッパーツールのコンソールを開いて以上のコードを入力したあと、vm.price = 2000と入力するとTotalが自動で20000に更新されます。 これによりネイティブアプリのようなダイナミックなウェブアプリを作ることができます。
もしリアクティブでないJavascriptで同じようなことをやろうとすると、自分で変数の更新をチェックし変更されたらターゲットフィールドの値を更新するという処理を自分で書かなければいけません。
ダイナミックなウェブアプリを開発するときにとても便利ですね。

UIを部品化できる

Vue.jsは、コンポーネントと呼ばれるしくみによってソフトウェアを部品化して再利用できるようになっています。
例えば、ボタンの押した回数を表示する部品はこんな感じに書けます。

Vue.component('counter', {
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

コンポーネントをVue.jsで利用するためには以下のように書きます。

<div id="app">
  <counter></counter>
</div>
new Vue({ el: '#app' })

counterというタグでクリックすると数えてくれるパーツとして扱えます。

フレームワークがある幸せ

複数のメンバーでチームで開発する場合、フレームワークがあると規約に則って開発することができるのでメンバー間で話し合いながら作るときにはとても役に立ちます。
Vue.jsでは単一ファイルコンポーネントという形式があります。

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports ={
  data: functon(){
     return {
        greeting: 'Hello'
     }
 }
}
</script>

<style scoped>
p {
  font-size: 2em;
  test-align: center;
}
</style>

templateタグのところが表示するHTMLで、scriptタグがJavascriptコード、styleタグがCSSコードを記述するところです。
通常、それぞれのタグ部分はHTMLファイル、JSファイル、CSSファイルに分けて記述されますが、一緒に書けるようにすることでモジュール性を強化しています。

さらに、vue-cliを使えば自動的にディレクトリ、ファイルを生成して整えてくれます。
vue-cliパッケージをnpmでインストールしてプロジェクトの雛形を作成してみましょう。

# npm install vue-cli
# npm install -g @vue/cli
# vue create <ディレクトリ名>

vue createでは以下のようなファイルが作成されます。

f:id:anthony-g:20190430123205p:plain

Vue.jsプログラムファイルはsrcディレクトリ下にあり、以下のようなファイル構成になっています。

f:id:anthony-g:20190501154027p:plain

App.vueとmain.jsがプログラムメインファイルで、componentsディレクトリ以下のファイルがコンポーネントのプログラムファイルです。
このようにvue-cliでプロジェクトの雛形が作成されます。

Vue.jsのイマイチなところ

コンポーネント間のデータやりとりが面倒

コンポーネント構造になっていることによってプログラムの規模が大きくなってもプログラムがスパゲッティー化しないのでいいのですが、コンポーネント間で情報をやり取りする必要が出てきて結構面倒くさいです。
コンポーネント間のデータのやり取りは2つの方法があります。

1. propによるデータのやりとり
親のコンポーネントから子のコンポーネントへデータを渡すには子コンポーネントにpropsを定義します。

main.js

// 子コンポーネント
const childComponent = {
   props: ['myMessage'],
   template: '<span>{{ myMessage }}</span>
}

// 親コンポーネント
new Vue({
    el: '#app',
    components: {
         'childComponent': child-component
    }
});

html

...
<child-component v-bind:my-message="こんにちは"></child-component>
...

HTML上では属性名は大文字小文字を区別しないので、タグ名はchild-component、属性名はmy-messageという感じでキャメル形式をケバブ形式に変換しています。 しかし、propsを使う方法は親から子への一方向でしか情報をやりとりできません。
子から親に情報を伝えるためにはemitを使って可能です。 情報の伝達方向でやり方が違うというのもいまいちな感じです。

2. Vuexによるデータのやりとり
しかし、コンポーネント間の関係が親子関係であるとはかぎりません。
そういうときはVuexを使います。
Vuexとは、Vue.jsアプリケーションローカルに持つことのできるDBのようなデータ格納領域を作成するライブラリで、全てのコンポーネントからアクセスすることができます。
まず、Vuexのストアを定義します。
store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(vuex)

const store = new Vuex.Store({
  state: {
         myMessage: ""
  },
  mutations: {
   setMyMessage(state,{msg}){
      state.myMessage = msg
   }
})

stateに格納するデータを、myMessageと定義して文字列を持つようにします。 Vuexでデータを更新する場合は、commit()を呼ばなければいけないお約束になっていて、mutationsはその関数を定義するところです。 次に、main.jsでstoreをVue.jsに読み込みます。

src/main.js

...
import store from './store'
...
new Vue({
    render: h => h(App),
    store: store
}).$mount('#app')

次に、コンポーネントでstoreにアクセスしてみます。

src/components/xxx.vue

<template>
<div>
  <span>{this.$store.state</span>
   <input type="text" />
   <button @click="ok()">OK</button>
</div>
</template>
<script>
export default {
  name: "xxx",
  methods: {
     ok(){
         this.$store.commit('setMyMessage",{myMessage: "Hello World"})
     }
   }
}
</script>

これはテキストボックスに入力してボタンを押すとVuexの情報が更新される例です。 これでVuexを介してコンポーネント同士のデータのやり取りができるようになりますが結構面倒ですね。 Vuexについての詳しい解説は以下のURLを参照してください。

vuex.vuejs.org

リアクティブシステムがわかりにくい

リアクティブシステムはリッチなUIを作るには便利なのですが、どのタイミングで更新されるのかがわかりにくいというのがあります。
例えば以下の例の場合、

src/components/xxx.vue

<template>
<div v-if="showComponent">
...
</div
</template>
<script>
export default {
   name: "xxx",
   computed: {
      showComponent(){
         return this.$store.state.showFlag
      }
   }
}
</script>

VuexデータのshowFlagでコンポーネントを表示したり消したりしたいのですがうまく動きません。 (これで結構長い間悩みました。)
VuexストアのshowFlagが更新されたらshowComponentが呼ばれてほしいのですがそうはなりません。
v-ifのほうでリアクティブな変数をモニターしているみたいなのですが、computedメソッドの中までは見てないみたいです。
こういうところが最初とまどうところです。

学習コストがやや高め

通常プログラムは記述した順番に実行されるものですが、リアクティブシステムは何かを変更したらキックされるイベントドリブンなシステムなので慣れるまで少しかかります。
また、モジュール化でソースが複数ファイルに分かれてしまうので、なれないと全体が見えづらいというのもあります。

結論

ちょっと使った感じでは、Vue.jsは規模の大きなフロントエンド開発向けなのかなと感じました。
ただ、うまく動かないときに調べるのが結構大変だったのでデバッグがもう少しやりやすくなるといいと思いました。
Reactも少し触ってみましたが、なんでもかんでもJavascriptのコードにしてしまうReactよりVue.jsのほうがわかりやすい印象でした。
あと、VuexとVue-Routerはほぼ必須で使うコンポーネントだと思います。
最初は少しとっつきにくいですが、慣れれば使って損はないのではないでしょうか。

参照URL:
www.vuemastery.com

CSSグリッドを試してみる

ウェブページでレイアウトするときは、いままではdivでボックス作ってスタイルシートのfloatで並べるというのが一般的な方法でした。 (ちなみに、ウェブ黎明期はtableで全てレイアウトしていました。) しかし、最近はCSSグリッドなるものがあるというので使ってみたら、とても便利だったのでブログ記事にしました。

例として、以下のようなレイアウトをCSSグリッドで作ってみましょう。

f:id:anthony-g:20190308230422p:plain

HTMLは以下のようになります。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Grid Test</title>

<meta name="viewport" content="width=device-width">
<link href="https://fonts.googleapis.com/css?family=Raleway" rel="stylesheet">

<link rel="stylesheet" href="style.css">
</head>

<body>
  <header>トップページ</header>
  <section>
    <div id="box1">ボックス1</div>
    <div id="box2">ボックス2</div>
    <div id="box3">ボックス3</div>
    <div id="box4">ボックス4</div>
  </section>
</body>
</html>

divボックスにそれぞれbox1,box2,box3とIDをつけておきます。

次に,CSSファイルは以下のようになります。

style.css

@charset "UTF-8";

/* 基本設定 */

* {
    margin: 0;
}

/* ページ全体の設定 */
body {
    display: grid;
    grid-template-columns: 20px 200px 20px;
    column-gap: 10px;
    grid-template-rows: 
        [head] 50px
        [r1] 200px;
    grid-row-gap: 20px;
    row-gap: 20px;
    font-family: sans-serif;
}

header {
    grid-row: head;
    grid-column: 2;
    justify-self: center;
    align-self: center;
    width: 100px;
    border: solid 1px black;
}

section {
    grid-row: r1;
    grid-column: 2;
    display: grid;
    grid-template-columns: 100px 100px;
    grid-template-rows:
       [sr1] 100px
       [sr2] 100px;
    row-gap: 10px;
    column-gap: 5px;
}

div {
    border: solid 1px black;
}

div#box1 {
    grid-row: sr1;
}

div#box2 {
    grid-row: sr1;
}
div#box3 {
    grid-row: sr2;
}
div#box4 {
    grid-row: sr2;
}

まず、bodyのところでdisplay: gridとしてCSSグリッドを使う指定をします。 次に、行方向のグリッドのレイアウトを以下のように定義します。

grid-template-columns: 20px 200px 20px;

これは以下のように両側に20pxの余白をつけて中央にコンテンツを200pxの幅で配置するレイアウトになります。

f:id:anthony-g:20190309113349p:plain

カラムの区切りは左から1,2,3...と番号がふられますので、これでコンテンツを表示する位置を指定します。 このページはsection部分が表示されるコンテンツですので、sectionのgrid-columnを2に指定します。

次に、列方向のレイアウトの指定を行います。 こちらも行と同じように以下のように設定します。

grid-template-rows: 
    [head] 50px
    [r1] 200px;
f:id:anthony-g:20190309114946p:plain

[head]とか[r1]というのが追加されていますが、これは列を番号ではなく名前で指定できるようにするための設定です。 headerで以下のように設定します。

header {
   grid-row: head;
...

表示コンテンツを含むsectionは、以下のように設定します。

section {
   grid-row: r1;
   ...

これで、大枠のレイアウトが完了です。 4つのdivはさらにsectionの中でグリッドを設定してレイアウトしています。

このようにグリッドを使えば、方眼紙の上でデザインするようにレイアウトすることができます。 floatを使うより直感的なので慣れればこちらのほうがデザインしやすいのはないでしょうか。

参考にした本

CSSグリッドで作る HTML5&CSS3 レッスンブック

CSSグリッドで作る HTML5&CSS3 レッスンブック

Firebase使ってみた

FirebaseはGoogleが提供するBaaSと呼ばれるクラウドサービスです。

firebase.google.com

これからは自分でサーバーを構築する時代でもないと思ったので、Firebaseを試してみました。

まずは、firebaseに登録してウェブアプリをデプロイするところまでやってみたのですが、ちょっとハマったところもあったのでまとめておきました。

 

まず、Google Cloud Platformに登録して 新しいプロジェクトを作成します。
作成方法についてはこちらを参照してください。

cloud.google.com

次に、GCP Cloud Shellを開きます。
これはブラウザからクラウドインスタンス上でシェルを使うためのものです。
GCPダッシュボードの右上のプロンプトマークをクリックします。

 

f:id:anthony-g:20190228202922p:plain

すると、下にシェルのターミナルが開きます。

f:id:anthony-g:20190228204726p:plain

サンプルウェブアプリのソースを以下のコマンドでダウンロードしておきます。


# git clone https://github.com/firebase/friendlychat-web

 

次にFirebase(https://firebase.google.com)のサイトにブラウザでアクセスして、プロジェクトを作成します。

f:id:anthony-g:20190301123150p:plain

 

プロジェクトを作成すると以下のようなページが表示されます。

f:id:anthony-g:20190301123331p:plain

 

Firebaseは以下のようなサービスで構成されています。

  • Authentication - 認証機能
  • Database - データベース機能
  • Storage - 静的コンテンツやファイルを保存する機能
  • Hosting - コンテンツの配信やデプロイ
  • Functions - クラウド関数

サンプルアプリを動かすためにAuthentication,Database,Storageを設定します。

左サイドメニューの「Database」をクリックしてRealtime Databaseの「データベースの作成」をクリックします。

f:id:anthony-g:20190301131242p:plain

 

「テストモード」を選択し、「データベースを作成」をクリックします。

f:id:anthony-g:20190301131512p:plain

 

データベース作成完了です。

f:id:anthony-g:20190301132719p:plain

 

次に、認証機能を設定します。
左メニューのAuthenticationをクリックし、「ログイン設定ボタン」をクリックします。

f:id:anthony-g:20190301162816p:plain

 

「ログイン方法」タブを選択した後、ログインプロバイダーとしてGoogleを選択し「有効にする」スライダーをオンにします。「プロジェクトの公開名」はFriendly Chatと入力します。

f:id:anthony-g:20190301165235p:plain

 

Google認証が有効になりました。

f:id:anthony-g:20190301171933p:plain

 

次に、Storageの設定を行います。

f:id:anthony-g:20190301172103p:plain

 

「スタートガイド」ボタンをクリックします。

f:id:anthony-g:20190301172227p:plain

 

「セキュリティルール」ダイアログのOKボタンをクリックします。

f:id:anthony-g:20190301173237p:plain

 

これでFirebase側の設定は完了です。

次に、サービスアカウントを作成します。
サービスアカウントとは、アプリケーションや仮想マシンに属する特別なGoogleアカウントです。GCPからFirebaseにアクセスするには事前に作成しておく必要があります。

GCPダッシュボードのメニュー「APIとサービス」- 「認証情報」をクリックします。

f:id:anthony-g:20190302181612p:plain

「認証情報」の「認証情報の作成」-「サービスアカウントキー」を選びます。

f:id:anthony-g:20190302184907p:plain

 

「サービスアカウント」メニューで「新しいサービスアカウント」を選択し、「役割」メニューは「Project」-「オーナー」を選択します。

f:id:anthony-g:20190302185440p:plain

アカウントが作成された後、暗号鍵が含まれたJSONファイルがダウンロードされますので、PCの適当なところに保存してください。

PC上のエディタでダウンロードしたJSONファイルを開いてテキストをコピーします。
次に、GCPコンソール上でvi等のエディターを開いてペーストしてファイル保存してください。(ファイルパス $HOME/friendly_chat_web/web/fbtest.json)
そして、GCPシェル上で保存したJSONファイルのパスを環境変数GOOGLE_APPLICATION_CREDENTIALSに以下のように設定します。

# export GOOGLE_APPLICATION_CREDENTIALS=$HOME/friendly_chat_web/web/fbtest.json

次に、アプリのディレクトリで以下のコマンドを実行します。

# cd friendly_chat_web/web
# firebase login --no-localhost

ブラウザでアクセスして認証するためのURLが表示されるので
表示されたURLにアクセスしてサービスアカウントのアクセスを許可してください。

そして、以下のコマンドをGCPシェルで実行してください。
# firebase init

f:id:anthony-g:20190302200746p:plain

ラジオボタンになっているところの「Database」と「Storage」に上下カーソルキーで移動しスペースキーでチェックを入れてリターンします。

f:id:anthony-g:20190302201152p:plain

Select a default Firebase project for this directory:で(don't setup a default project)を選びます。

f:id:anthony-g:20190302202129p:plain

その後、何回か聞かれますがリターンで問題ないです。

そして、アプリケーションのデプロイを行います。
# firebase deploy --project プロジェクトID

プロジェクトIDは、Firebaseのプロジェクトの設定で確認できます。

f:id:anthony-g:20190303211521p:plain

 

Hosting URLをブラウザでアクセスするとウェブアプリが動作していることを確認できます。

f:id:anthony-g:20190302204647p:plain

 

GCPとFirebaseの接続設定のところで結構はまりましたが、認証やデータベースをFirebaseに任せられるのはいいですね。
さらに色々いじってみたいと思います。

 

参考URL: 

www.topgate.co.jp

ジョブ理論 ー イノベーションを予測可能にする消費のメカニズム

 

ジョブ理論 イノベーションを予測可能にする消費のメカニズム (ビジネスリーダー1万人が選ぶベストビジネス書トップポイント大賞第2位!  ハーパーコリンズ・ノンフィクション)

ジョブ理論 イノベーションを予測可能にする消費のメカニズム (ビジネスリーダー1万人が選ぶベストビジネス書トップポイント大賞第2位! ハーパーコリンズ・ノンフィクション)

  • 作者: クレイトン M クリステンセン,タディホール,カレンディロン,デイビッド S ダンカン,依田光江
  • 出版社/メーカー: ハーパーコリンズ・ ジャパン
  • 発売日: 2017/08/01
  • メディア: 単行本
  • この商品を含むブログ (6件) を見る
 

 

今はものが売れない時代と言われています。

たしかに、生活の中でものはあふれていて、顧客が何を欲しがっているか見つけるのが難しくなっているのは事実です。

しかし、売れている商品やサービスは今でも存在しています。

trend.nikkeibp.co.jp

では、なぜこれらの商品は売れたのでしょうか?

 

「ジョブ理論」によれば、顧客がものを買うのは自分のジョブを片付けたいからといいます。
ジョブとは、特定の状況で人あるいは人の集まりが追求する進歩のことをいいます。

 

例として、朝のミルクシェイクのエピソードがあげられていました。
あるお店で朝にミルクシェイクがよく売れるようになったそうです。
そこで、お客さんに聞いたところ、朝に仕事先まで1~2時間ドライブするときに空腹と退屈な時間を解消するためだったということがわかりました。
でも、ドーナツだとボロボロこぼれて運転しながらだと食べにくいし、バナナだとすぐ食べ終わってしまうからだめだったのです。
ミルクシェイクを買うことによって、顧客の朝の生活に進歩をもたらしたのです。

 

顧客の本当のジョブを見つけるのは結構難しいと思います。
ミルクシェイクのケースも、まず朝に売れていることに関心をもつ人なんてめったにいないでしょう。
気がついたとしても、おいしいから売れてるんだろうくらいにしか普通は思えないでしょう。
大事なのは人に関心をもって、なぜあんな行動をするのかと疑問を解明しようとする姿勢なのだと思います

 

ミルクシェイクのケースは顧客がいたからわかりましたが、顧客がいない場合もあります。
サザンニューハンプシャー大学にはオンラインコースがあったのですが、リソースもあまり投入していない状況だったので業績はいまいちでした。
そこで、ジョブベースで考えてみたところ志望者のジョブを解決するようなサービスを提供していないことに気がつきます。
例えば、オンラインコースに申し込む人は社会人が多かったのに高校生を対象にした内容になっていたり、奨学金の申し込みも手続きが面倒だったりしてあきらめている人が多かったのです。
それを社会人向けの内容に変え、奨学金も簡単に申請できるようにしました。
入学後も学習のフォローをきめ細かくして、全米で有名な大学になっていきました。
このケースでは、表には現れていない生徒のジョブにフォーカスして解決するサービスを提供することが成功した理由だったのです。

 

ただ、ジョブを機能や利便性の面だけから見るのはまちがいです。
なぜなら、人は感情で判断する生き物だからです。

GMは、自社の車に搭載する車載システム向けにオンスターという情報サービスを提供しています。
オンスターはナビゲーションやリモート診断、ハンズフリー通話など様々な情報サービスから構成されています。
GMは、最初このサービスを高級車オーナーをターゲットにしましたが、エントリーレベルの車種でも売れているのを見て不思議に思いました。
顧客を調査したところ、ドライバーがエンジンランプがついているのを見て不安になったり、道に迷って困ったときの頼れるサービスを求めていたのです。
つまり、心の不安を取り除いてくれるサービスにニーズがあったのです
それを理解したGMは高機能を目指すのではなく、ドライバーの不安に対処するサービスを提供することに方向転換して成功しました。

いくら技術が発達しても相手にするのは人間です。
人間は合理的に判断することは意外と少なく、感情で判断することがとても多い生き物なのです。

今は何でもデータ化して定量的に分析することが正しいとされますが、定性的な情報のほうが重要なことが多いのです。
また、データに頼りすぎると相関関係があるというだけで因果関係に結びつけてしまうまちがいが起こりやすいのです。

昔、ソニーの社長の盛田さんがウォークマンを売り出す時、再生だけしかできない音楽プレーヤーなんか売れないと社内中から反対されたそうです。
マーケティングリサーチでもそういう結果が出てたかららしいですが、盛田さんは必ず売れるという確信を持っていたそうです。
私も昔ウォークマンを持ってましたが、実際に使ってる人でないと本当のよさは感じられない商品だなと思いました。
ウォークマンは、外で歩きながら音楽を楽しみたいというジョブを解消してくれる素晴らしい進歩だったのです。

どんな時代になっても人が片づけたいジョブがなくなることはありません。今の日本だと高齢化や少子化、産業の空洞化などでジョブが山積しています。それらのジョブを見つけて解決しようとすれば今でも売れる商品を開発することができるのではないでしょうか。
 

ソニー 盛田昭夫

ソニー 盛田昭夫