2019年12月アーカイブ

【ネタバレあり】


皆さん今年のM-1グランプリご覧になりましたか? 今年はミルクボーイさんが見事歴代最高得点で優勝しました。本当におめでとうございます!


ミルクボーイさんといえば

「それコーンフレークやないかい!」

「いやほなコーンフレークちゃうやないかい!」

と一方の文章に対してもう一方がコーンフレークかどうかをつっこむ、というネタですよね。


そこでテレビを見ながら思ったわけです。

「これって、機械学習でできるんじゃね?」

与えられた文章に対して、それがコーンフレークについての文章かどうかを判別する、というのはいかにも機械学習ができそうなことです。

ということで作ってみました!

スクリーンショット 2019-12-27 16.40.46.png @1_milkboy
このツイッターアカウントは入力した文章に対して「それコーンフレークやないかい!」/「いやほなコーンフレークちゃうやないかい!」と突っ込んでくれます。


BERTを使ってみた

まずは、元データを用意します。
これはミルクボーイさんの漫才の中の言葉の他に、ウィキペディアの記事などから集めてきました。
コーンフレークなら1、そうでないなら0という風に値を指定していきます。
時間もなかったので、全部で70件ほどしか集まりませんでした...
image1.png
次に、学習機です。
文章の学習、ということで用いたのはBERTです。(BERTについてはこちらを参照
Googleさんが104ヶ国語に対応しているmultilingual BERTを公開しているので、改めて日本語を学習していく必要はありません。
日本語を学習済みのBERTを使って、元データを読み込ませてどういう分がコーンフレークなのか学習していきます。

train = pd.read_csv(text, header=0)
BERT_MODEL_HUB = "https://tfhub.dev/google/bert_multi_cased_L-12_H-768_A-12/1"

[途中略]

print(f'Beginning Training!')
start_time = datetime.now()
model.train(input_fn=train_input_fn, max_steps=num_train_steps)
print("Training took time ", datetime.now() - start_time)


学習が終わったところで、実際の文で試していきましょう!

今回は
「朝ごはんに食べると目が覚めるやつ」

「ごはんと食べるとおいしいやつ」
の2文で試していきます。

前者が「ほなそれコーンフレークやないか」、後者が「ほなそれコーンフレークちゃうやないか」の予定なのですが、元データが少ないので、「朝ごはん」と「ごはん」の違いに引っかからないかがポイントですね。


labels = ["ほなそれコーンフレークやないか", "ほなそれコーンフレークちゃうやないか"]

[途中略]

predict_text = ["朝ごはんに食べると目が覚めるやつ", "ごはんと食べるとおいしいやつ"]
predictions = getPrediction(model, predict_text)
predictions


結果がこちら!
image2.png
残念ながらどちらも「ほなそれコーンフレークやないか!」とつっこまれています...
やはり元データを増やさないと精度はまだまだ出ませんね。


Word2Vecを使ってみた

ということで今度はWord2Vecを使ってみました!(切り替えが早い...)
BERTはWord2Vecより性能が高いのですが、元データが小さい場合、Word2Vecでキーワードだけ拾ったほうがよいのではと思い試してみることにしました。
まずは日本語のWord2Vecを(http://www.cl.ecei.tohoku.ac.jp/~m-suzuki/jawiki_vector/)から引っ張ってきました。
BERTもそうでしたが、日本語に対して学習済みの機械が出ているのは非常に助かりますね!

次に、入力を処理していきます。与えられた文章の中から名詞と動詞のみを抽出し、単語のlistにします。

def counter(texts):
            t = Tokenizer()
            word_count = defaultdict(int)
            words = []
            for text in texts:
                tokens = t.tokenize(text)
                for token in tokens:
                    pos = token.part_of_speech.split(',')[0]
                    if pos in ['名詞', '動詞']:
                        # 必要ない単語を省く(実際の結果を見て調整)
                        if token.base_form not in ["こと", "よう", "そう", "これ", "それ", "もの", "ため", "ある", "いる", "する"]:
                            word_count[token.base_form] += 1
                            words.append(token.base_form)
            return word_count, words


続いて、この単語のlistとキーワード(今回はコーンフレーク)との相関度を計算します。
Gensimのライブラリは充実しているので一行ですみます。

score = self.model.n_similarity([keyword], words)


最後にこの数字が0.5以上なら「ほなそれコーンフレークやないか」、0.5以下なら「ほなそれコーンフレークちゃうやないか」を出力する、という形にしました。

if (score > 0.5):
          return("ほなそれコーンフレークやないか")
        else:
          return("ほなそれコーンフレークちゃうやないか")


これをキーワードは「コーンフレーク」、文章は「アフリカの名産品って言ってた」で試してみましょう。
文章の中から「アフリカ」「名産品」「言って」を抜き出し、それぞれと「コーンフレーク」との相関の平均を取ります。
するとなんということでしょう、「ほなそれコーンフレークちゃうやないか」と出力されるではありませんか!

さきほど失敗した「ごはんとめっちゃ合うって言ってた」でも試してみると、
今度は大成功です!


まとめ

今回Word2VecとBERTの両方を試してみて、ぱっと見たところWord2Vecのほうが結果が良いことがわかりました。
しかしこれは本当は苦肉の策です。単語だけを抜き出すより、文脈全体を考慮したほうがよいですし、0.5という敷居値も独断っと偏見でしかありません。それぞれの単語とキーワードの相関も足し合わせただけではホントは良くないかもしれません。そういった点で、最初にBERTを使ってみたのですが、いかんせん元データの数が少なすぎました。いずれ十分なデータ量がたまれば、BERTが活躍できる日が来るかもしれません。
最終的にはミルクボーイさんとコラボなんてしてみたいですね。
それではまた!皆様よい新年をお迎えください!


Twitter・Facebookで定期的に情報発信しています!

最近、機械学習のビジネスへの活用がさかんになっています。
その中でこういったお困り事はありませんか?

  • トップダウンで「機械学習を導入してほしい」と言われたけど何から始めればいいか分からない
  • 機械学習の導入を検討したけど計画で頓挫した
  • 社内に機械学習の導入を提案したいが、提案がまとまらない


本記事ではそんな方向けに機械学習の検討ステップと導入効果をご紹介します。


機械学習の検討ステップ

Process

1.AI・機械学習で出来ることを知る
2.対象の業務・目的を決める
3.学習データを用意する
4.モデルを開発する
5.評価をする
6.運用する


もちろんお客様ごとに異なりますが、概ねこのような流れで検討〜導入することが多いです。
次に各検討ステップの詳細をご紹介します。


機械学習で出来ること

機械学習はこれらのことを得意としています。
スクリーンショット 2019-12-26 16.36.30.png


いくつかサービスの具体例を挙げてみます。

チャットボット

お客様や社内の問い合わせへチャットボットが回答します。
業務効率化だけではなく、お客様をお待たせしない仕組みを作ることができます。

不良品検知

製造過程での不良品をシステムが発見します。
人がおこなっていた長時間の業務をシステムが代替し、人はさらに生産性の高い業務に当たることができます。

手書き帳票処理

手書きの書類を読み取り、データ化します。
コストのかかっている人力での転記作業をシステムがおこないコストを削減し、紙の紛失リスクを低減します。

食べごろ野菜検知

収穫前の野菜にカメラをかざすと、食べごろの野菜に反応します。
人依存作業による、収穫時の人手不足を解消します。

離職危険性アラート

アンケート結果と部署異動等の変化等の現職者の状況から、離職可能性を可視化します。
早期に退職希望者やストレスを感じている人に気付き、フォローをおこなえます。

価格査定

過去の査定情報から、『もし、いま、これを売るとしたらいくら?』と査定します。
売り手への意思決定の補助、早期の情報提供ができます。


対象の業務と目的を決める

ずばり、AIを導入してどんな業務で何をしたいのかです。

「それを考えるのに困ってるんだよ〜。」

ってお思いですよね...。ただ、ここが定まらないとAI(機械)も困ってしまいます。 なぜなら、課題と指標の設定は人間にしかできません。

  • 上記に挙げた活用例の使えそうな業務を見つける
  • 困っていること(課題)に対して、解決の方法を見つける

の2パターンでどんな業務で何をするのかを決めていきます。


その後は、ゴール(目標)の設定が必要です。


問い合わせ業務の効率化のためにチャットボットの検討をしたとします。
現状、ありがたいことにお客様の問い合わせが多く、対応が追いついていません。
業務が膨大になるだけではなく、このままではお客様に迷惑をかけてしまいかねません。
ーーーーーーー...

この時に考えられるゴールは複数ありますよね。
問い合わせ業務にかかっている時間を◯%削減する、誰でも問い合わせ業務をできるようにして一人あたりの負担を軽減する、お客様満足度を向上させる...。

このゴールによって、AIでおこなうことや学習させるデータ、出力するものが変わってきます。
そのため、まずは活用例を参考に解決したい課題と対象業務を設定することが必要です。


学習させるデータを用意する

機械学習はその名の通り、機械に学習させて、結果を出力させます。

どんなに優秀な人でも、学習(勉強した答え)を誤ると正しい答えを導けません。
また、学習時間を多く取れば、正解の精度も上がりますが、同じ学習だけを長い時間し続けてしまうと、幅広い問題には対応できません。


機械も同じです。


そのため、目的に応じて正しいデータをたくさん学習させる必要があります。


導入効果

それではAI・機械学習の導入効果についてご紹介します。
これらが実現できます。

  • 業務効率化
  • コスト削減
  • 顧客満足度向上
  • リスク管理
  • 社内環境改善


機械学習は大量のデータを活用/同じ作業を早くくり返す/過去から未来を予測することが得意です。


そのため、 書類の転記作業や同じような問い合わせへの対応等、定期的に人手でおこなっている作業をシステムが代わることで、業務効率化をしコストの削減に繋がります。
そうすることで昨今叫ばれている生産性の向上の手助けにもなるでしょう。

作業時間が短縮することで、お客様対応へより多くの時間を充てることができます。接客はもちろんですが、お客様に喜ばれる企画をする時間も増えるということです。
お客様に充てる時間だけではなく、社内向けの時間も同様です。後回しになりがちなより働きやすい環境へ整備する時間を確保することができます。


おわりに

機械学習 検討ステップと導入効果をお伝えできたでしょうか。

当社のバリュー(行動規範)のひとつに『お客様と一蓮托生でプロジェクトを進める』を掲げています。
みなさまの検討へも早期段階からお手伝いいたします。

スクリーンショット 2019-12-26 19.16.02.png

機械学習は幅広い活用方法があり、お客様ごとの活用方法が必要です。
当社ではみなさまの課題やお困り事を一緒に解決していきます。
下記にご興味がありましたら、問い合わせ口からご連絡ください。

  • 機械学習の導入を考えている
  • 活用事例についてもっと詳しく知りたい
  • 他になにが出来るのか興味がある


当社、アクセルユニバースの紹介

私達はビジョンに『社会生活を豊かにさせるサービスを提供する。』ことを掲げ、このビジョンを通して世界を笑顔にしようと機械学習・深層学習を提案しています。
例えば、現在人がおこなっている作業をシステムが代わることで、人はより生産性の高い業務に携わることができます。


acceluniverse


Twitter・Facebookで定期的に情報発信しています!

はじめに

まずは下の動画をご覧ください。
スクリーンショット 2019-12-27 17.18.19.png
スパイダーマン2の主役はトビー・マグワイアですが、この動画ではトム・クルーズがスパイダーマンを演じています。
これは実際にトム・クルーズが演じているのではなく、トム・クルーズの顔画像を用いて合成したもので、機械学習の技術を用いて実現できます。
機械学習は画像に何が写っているか判別したり、株価の予測に使われていましたが、今回ご紹介するGANではdeep learningの技術を用いて「人間を騙す自然なもの」を生成することができます。
「人間を騙す自然なもの」を作るには本物のデータがどのように生成されるのか理解しなければなりません。
例えば、猫の画像を生成するときに耳を3つ書いたりしません。なので猫の耳は2つであるということを学習しなければなりません。


GANによる成果物をもう1つ紹介します。こちらのサイトにアクセスすると人物の顔が出てきます。
ですが出てくる人物はこの世に存在せず、GANが生成した人物です。
ご覧になっていただいてわかるように不自然なところは何もありません。


1. GANとは

GANとはGenerative Adversarial Networksの略で敵対的生成ネットワークとも呼ばれ、2014年に発表されました。
特徴は2つのネットワークを戦わせることにあります。

Gをデータを生成するネットワーク(Generator)、DをデータがGから生成されたものか実際のデータかを判別するネットワーク(Discriminator)とします。
GはDを騙すような画像を生成できるように、DはGから生成されたデータを見抜けるように、競いながら学習をします。
論文では例として、Gを偽札を作る悪人、Dを偽札を見抜く警察としています。


全体のネットワーク構造は以下のようになっています。
deep-learning-for-computer-vision-generative-models-and-adversarial-training-upc-2016-5-638.jpg

Gを図中のGenerator、Dを図中のDiscriminatorとします。
Gは何かしらの確率分布 P_z (一様分布など)から生成されたデータ z (図中 Latent random variable)から、通常のネットワークのように重みをかけて本物と同じサイズの画像を生成します。
Dは入力として画像(図中 sample)を受け取り、本物か偽物かを判定します。

D(・) を「・が本物のデータと判断する確率」としたとき、GAN は以下のような評価関数を最小化するようにGとDのパラメータ θ_g と θ_d を更新していきます。
\begin{eqnarray*} \min _{G} \max _{D} V(D, G)=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}(\boldsymbol{x})}[\log D(\boldsymbol{x})]+\mathbb{E}_{\boldsymbol{z} \sim p_{\boldsymbol{z}}(\boldsymbol{z})}[\log (1-D(G(\boldsymbol{z})))] \end{eqnarray*}

2. アルゴリズム

パラメータθgとθdを更新する手順を以下に示します。
kはハイパーパラメータです。


1. 2~4をイテレーションの数だけ繰り返す
  2. (2-1)~(2-4)をkの数だけ繰り返す
    2-1. pzからm個のサンプル{z1,z2...zm}を取得
    2-2. 実際のデータからm個のサンプル{x1,x2,...xm}を取得
    2-3. θ
dを以下のように更新<

更新式1

  3. pzに従うm個のサンプル{z1,z2...zm}を取得
  4. θ_gを以下のように更新

\begin{eqnarray*} \nabla_{\theta_{d}} \frac{1}{m} \sum_{i=1}^{m}\left[\log D\left(\boldsymbol{x}^{(i)}\right)+\log \left(1-D\left(G\left(\boldsymbol{z}^{(i)}\right)\right)\right)\right] \end{eqnarray*}

更新式2

\begin{eqnarray*} \nabla_{\theta_{g}} \frac{1}{m} \sum_{i=1}^{m} \log \left(1-D\left(G\left(\boldsymbol{z}^{(i)}\right)\right)\right) \end{eqnarray*}


3. おまけ 学習の様子

zがpzに従うときG(z)がpgに従うとします。
概略は以下のようになります。

スクリーンショット 2019-12-26 13.58.30.png


GANの目的はpg = pdataとなることです。
つまり本物のデータが生成される分布pdataとpgが等しいため、理論上は見分けがつかないことです。
これを言い換えるとD(x) = 1/2 が成り立つことだとも言えます。
これは、Dが本物のデータxをを本物だと見分ける確率が1/2で、当てずっぽうで判断していることになります。


論文にはpgがpdataに近づいていく様子が載せられています。
スクリーンショット 2019-12-26 12.28.58-1.png


青のドット線 : D(x)
緑の線 : pg
黒のドット線 : 実際のデータxが従う確率分布p
data

  • a
    学習前の様子です。

  • b Dが上のアルゴリズム(2-3)の手順で更新された後です。

  • c Gを上のアルゴリズム4の手順で更新された後です。
    bの状態と比べて、pgがpdataに近づいた事がわかります。

  • d pg = pdataとなった様子です。

4.まとめ

GANは人を自然な画像を生成するだけでなく、最初に挙げたスパイダーマンのように2つの画像を自然に合成することもできるようです。
買おうか迷っている服を自分の体に合成して、擬似的に試着することもできます。
服の試着だけでなく、化粧品のお試しや、髪型が自分に合うのかなどを事前にわかっていると便利そうです。
今後はGANについて最新の傾向をつかめるようにしていきたいと思います。


Twitter・Facebookで定期的に情報発信しています!


"TASK2VEC"は2019年2月にsubmitされた論文の中で提唱された手法であり、「タスクをベクトル化する」手法です。 この手法が提唱されるまでは、"タスク間の関係を表現する"フレームワークが存在しなかったため、一部界隈で脚光を浴びました。
しかし、このTASK2VECの言わんとすることが理解出来ても、どういうものなのか正直ちょっとわかりにくいです。そこで今回は出来るだけ論文の内容をかみ砕いて解説したいと思います。

目次

1.TASK2VEC 概要
2.事前知識
3.TASK2VEC 手順
4.論文の実験結果
5.まとめ
6.参考文献


1.TASK2VEC 概要

上でも述べたように、"TASK2VEC"は「タスク」または対応するデータセットをベクトル化します。ここで言う「タスク」とはデータセットとラベルの組み合わせのことです。
この論文によると、事前学習によって得られたモデルを利用し、タスクの埋め込み表現、つまりベクトル表現を獲得します。 これによってタスクの間の類似度を算出でき、適切な事前学習済みモデルを選択できるようになるということです。
例えば、「犬」の画像認識を行うことを考える時、「人」の画像認識で用いたパラメータより「猫」の画像認識で用いたパラメータを使って画像分類した方が良さそうですよね。直感的にはこういうことです。

さらに具体的に見ていきましょう。ここでは次の節の事前知識を見ておくと良いです(特にFIMのところ)。
本論文では「probe network」と呼ばれるネットワーク(特徴抽出器)を用います。 この「probe network」はImageNetで事前学習させた唯一の特徴抽出器であり、この事前学習ののち、特定のタスクの分類レイヤでのみ再学習させます。 学習が完了したら、特徴抽出器で得られたパラメータのFIMを計算します。このFIMはタスクに関する重要な情報を含んでいるのですが、これについては後述します。
ここでは「probe network」という単一の特徴抽出器を用いているため、各タスクを固定長の、つまりクラス数などの詳細に依存しないベクトルとして埋め込むことができるということです。


活用事例

このTASK2VECですが、実際に社会に活用できることとして考えられるのが、

画像を分類したい→データ数が少ないので転移学習を使おう
→良い学習済みモデルないかな?TASK2VECで探してみよう!


などの一連の流れが考えられます。ちょっと抽象的かもしれませんが、つまりはサンプル数が少なすぎても諦めないで!という感じです。 (強化学習の考え方に近いかもしれません)

2.事前知識

転移学習(Transfer Learning)

この記事をご覧になっている方は、この「転移学習」について既にご存知かと思いますが簡単に説明します。 転移学習とは、ある領域(ドメイン)で学習させたことを、他の領域に役立たせ効率よく学習させることです。事前に学習させたモデルを学習済みモデルと言ったりします。 「データの時代」と言われるこの世の中ですが、データの入手が困難なケースもあります。そういった少ないデータ量で学習を進めていく際、この転移学習は真価を発揮します。 また数万〜数十万もの教師データを集め、各々にラベル付けを行うにはかなりの人件コストがかかります。このように、既にある(精度のいい)モデルを、別のものに使いまわす転移学習はこれからの時代必要不可欠でしょう。

転移学習のステップとして、
1.与えられたデータから、特徴量を抽出:
学習済みモデルの最終層以外の部分を利用して、データ内の特徴量を抽出します。ここで得られた学習済みモデルを「特徴抽出器」と言います。

2.抽出した特徴量を用いて、クラス分類する:
1.で実際に求めた特徴量を用いて、新しく追加したモデルに学習を施します。特に画像や音声を識別するモデルは他の分野に比べて流用しやすいため、転移学習が役立ちます。

この転移学習の可能性について、かの有名なcourseraの講師であるAndrew Ngは「機械学習の成功を今後推進するのは、教師なし学習、そして最近目覚ましい進化を遂げている強化学習でもなく、転移学習である」と言及しています。


メタ学習(Meta Learning)

メタ学習とは、「学習方法を学習する(learning to learn)」ことに他なりません。
人間で例えるなら、勉強をする際に教科書や参考書を読み込んだりしますが、ただ読むとしてもその方法は人によって様々です。 そこで、効率が良く理解しやすい読み方が分かれば勉強しやすいですよね。その方法を学習するのがここで言うメタ学習です。

ディープラーニングにおけるメタ学習は次の3つのステップに分けられます。

1.どのタスクでも、同じディープラーニングモデルを使う
2. 各タスク自体がどのようなものかを表す特徴ベクトルをディープラーニングによって抽出し、このベクトルと1.のモデルを使ってタスクを行う
3.各タスクで少数のデータを使って再学習させ、満遍なく良い結果が得られる1.のモデルを見つける

結果として、様々なタスクに特化したモデルが得られると言うわけです。


フィッシャー情報行列(FIM)

タスクを解くためには、入力\(x\)とそれに対応するラベル\(y\)の関係\(p_w (y|x)\)を学習する必要があります。 \(p_w (y|x)\)は重み\(w\)において入力\(x\)(例えば画像データ)が与えられた時、クラス(ラベル)が\(y\)である確率を示しています。
ここで、パラメータ\(w\)がこのタスクを解く際にどれほど重要な情報を含んでいるのか知るために、その摂動\(w'=w+\delta w\)について考えます。 \(p_w (y|x)\)と\(p_{w'} (y|x)\)が大きく異なるようであれば、そのタスクはパラメータに大きく依存していることがわかります。
これを計算する指標としてKLダイバージェンスと呼ばれるものがあります。その二次近似は、 \(\hat{p}\)を訓練データによって定義される経験分布とすると、 $$ \mathbb{E}_{x\sim \hat{p}}KL(p_{w'} (y|x) || p_w (y|x)) = \delta w \cdot F\delta w + \mathcal{o}(\delta w^2) $$ ここで出てきた右辺のFが、フィッシャー情報行列(FIM)と呼ばれるもので、FIMは以下の式で定義されます。 $$ F = \mathbb{E}_{x,y \sim \hat{p}(x)p_w (y|x)} [\nabla _w \log p_w (y|x)\nabla _w \log p_w (y|x)^T] $$ 数式がややこしくなってきましたが、FIMは確率分布の各パラメータ毎の勾配を計算し、その転置行列との積をとっています。また、FIMは確率分布空間上のリーマン距離(計量)であり、 結合分布\(\hat{p}(x)p_w (y|x)\)についてのある特定のパラメータ(重み、特徴量)を含んでいます。 確率分布はベクトルで、それをパラメータベクトルで(偏)微分すると行列となります。この時、行列のi行目は確率分布の全ての値をi番目のパラメータで微分したものとなります。 つまり、FIMの対角成分のみが、同じパラメータで微分したものの二乗が並びます。
これより、FIMにおける対応するエントリ(成分)が小さい値なら、そのタスクはパラメータに強く依拠していないことが分かります。また、FIMはタスクの(コルモゴロフ)複雑性にも関係していると言われています。
以上のことよりFIMをタスクを表す指標としているわけです。
しかし、異なるネットワークでFIMを計算すると比較ができないため、本論文では前述した「probe network」を用います。 また上の数式からも分かるように、全てのパラメータに対する勾配を計算しなくてはならないため、計算量が多すぎるという問題点があります。 これに対応するために、本論文では2つの仮定を置いています。

1.「probe network」内の異なるフィルター間には相関がないと(暗黙的に)仮定→FIMの対角成分についてのみ考える
2.それぞれのフィルターの重みパラメータは基本独立していない→同じフィルターの全ての重みに対し平均化を行う

これによって結果的に得られたものは、「probe network」内のフィルター数と一致した固定長ベクトルであり、 このベクトルを使ってタスク間の距離を計算していくのです。 本論文11ページ目の「Analytic FIM for two-layer model」に簡単な二層モデルでのFIM導出を紹介しているので、 興味のある方は是非参照してみて下さい。


3.TASK2VEC 手順

さてやっと本題に入れるわけですが、まずTASK2VECは大きく次のステップに分けられます

1.事前学習済みモデルによって得られるFIMを計算【2.事前知識参照】
2.FIMの対角成分のみを取り出し、固定長のベクトルを出力

行なっている作業は基本これだけで他のモデルと比較する際に、この出力されたベクトル間の距離を計算します。

ベクトル間の距離計算

このベクトル間距離をユークリッド距離で計量しようとすると、1.ネットワークのパラメータは異なるスケールを持つ、 2.埋め込まれたベクトルのノルムは、タスクの複雑性や計算に使われたサンプル数の影響を受けるといった問題が発生するため、 本論文では距離計算の手法として次の場合分けを行なっています。

◎Symmetric TASK2VEC distance【対称性あり】:
$$ d_{sym}(F_a, F_b) = d_{cos}\left(\frac{F_a}{F_a +F_b}, \frac{F_b}{F_a +F_b}\right). $$ ここで対称性ありの場合について、距離計算の際の頑健性を保つためコサイン距離を導入します。
\(d_{cos}\)はコサイン距離(類似度)、\(F_a\), \(F_b\)は2つのタスク\(a, b\)をベクトル化したもの(計算によって得られたFIMの対角成分)を表しています。 それぞれ\(F_a +F_b\)で割って標準化していることに注意してください。 例えば、iNaturalistにおける種間の分類学上距離(Tax. distance)とコサイン距離について、 以下の図右より、完璧にはマッチしてないですがタスクサイズ\(k\)が大きいほど良い相関(関係)があることが分かります。
自然言語処理
◎Asymmetric TASK2VEC distance【対称性なし】:
$$ d_{asym}(t_a \rightarrow t_b) = d_{sym}(t_a, t_b) - \alpha d_{sym}(t_a, t_0) $$ 次に非対称の場合についてですが、モデル選択の際は上記の転送距離(Transfer distance)が有効です。
一般的にImageNetのように複雑なタスク(データセット)から得られた学習済みモデルは良い性能を持っています。 本論文では、「タスクの複雑性は、"簡単なタスク\(t_0\)"の埋め込み表現によって得られるベクトルの大きさによって計算できる」 という考えをもとに、上記の計算式を導入しています。
この式が言わんとすることは、タスク\(a\)を用いてタスク\(b\)に転移学習させる際に、タスク\(a\)が複雑であればあるほど 良い結果がでるということです。つまり、タスク\(a\)とタスク\(b\)の類似性が高く、かつタスク\(a\)とタスク\(t_0\)の類似性が低いほど この距離は大きくなります。
ここで、\(t_a, t_b\)は2つのタスク、先ほど述べた\(t_0\)は簡単なタスク、\(\alpha\)はハイパーパラメータを表しています。 この\(\alpha\)はメタタスクをもとに選定されます。本論文の実験では、最適な\(\alpha\)がメタタスクの選定に関して 頑健性を持っていることが分かっています。(実験によるとprobe networkとして、ImageNetで事前学習させたResNet-34を使った時の\(\alpha\)は0.15)
【詳しくは本論文"3.1. Symmetric and asymmetric TASK2VEC metrics"参照】


MODEL2VEC【参考】

さて、上記のTASK2VECではタスク間の距離を算出することに成功しています。 しかしここまで読んでくださった読者様の中には、既にお気付きの方もいるでしょうがこのTASK2VEC、完全にモデルの詳細を無視してしまっています。
そこで、モデルが訓練されたタスクの埋め込みができるなら、そのモデルもベクトル化できるのではないか?と考えるわけです。
しかし、一般にそういったモデルの情報はブラックボックス化されてたりして分かりません。 そこで、同じタスクで異なる精度のモデルがあったら、タスクとモデル(アーキテクチャとトレーニングアルゴリズム)間の相互作用をモデル化するために、 2つの相互埋め込みを学習することを目標とすれば良いわけです。
具体的に本論文では、タスク空間に(似たタスク上でも良い精度を出すような)モデルを埋め込むために、メタ学習問題に関する次の公式を提唱しています。
\(k\)個のモデルが与えられた時、MODEL2VECによる埋め込み表現は、
$$ m_i = F_i + b_i $$ で与えられ、ここで\(F_i\)はモデル\(m_i\)をトレーニングするために使ったタスクをベクトル化したもので、\(b_i\)は学習済みの「モデルのバイアス」を表しています。
\(b_i\)は、タスク間距離によって与えられる最適なモデルを予測するための、k交差エントロピー誤差を最適化することによって得られます。 ここでタスク間距離は以下で定義されます。
$$ \mathcal{L} = \mathbb{E}[-\log p(m | d_{asym}(t, m_0), \cdots , d_{asym}(t, m_k))]. $$ トレーニング後、新しいタスクtが与えられた時、\(d_{asym}(t, m_i)\)を最大にするような\(m_i\)が最適なモデルというわけですが。。
あくまで参考程度に考えていただければ結構です。


4.論文の実験結果

使用データセット

まず本論文で使用するデータセットについて、簡単にまとめておきます。【右の数字はタスク数】

iNaturalist:生物を撮影した画像からなるデータセット。それぞれのタスクは種の分類タスクとなっている。【207】
CUB-200:鳥を撮影した画像からなるデータセット。それぞれのタスクは鳥類の分類タスクとなっている。【25】
iMaterialist:ファッション(服飾)画像のデータセット。服の分類に関するタスク(色、素材など)。【228】
DeepFashion:ファッション(服飾)画像のデータセット。iMaterialistと同じ、服の分類に関するタスク。【1000】

4つのデータ合わせて全1460のタスクで実験を行なっています。 これらのタスクで良い学習済みモデルを選択することに成功したTASK2VECですが、早速その実験結果を見ていきましょう。


学習済みモデル

先ほども述べましたが、本論文ではprobe networkとして、ImageNetで事前学習させたResNet-34を、 その後ファインチューニングしたものと、しないものとの2つで実験しています。

実験結果

本論文では4,100の分類器、156の特徴抽出機を使い、そして1,460の埋め込みを実験で行ったそうです(かかった時間は1,300GPU時間)。
再褐しますが、下図がiNaturalist, CUB-200, iMaterialistの3つのデータセットにおける各タスクを、t-SNEによって二次元のベクトルに落とし込み可視化したものです。(左図) 自然言語処理
データセットと相関のあるタスク(例えばiMaterialistだったらパンツの分類タスクなど)は、そのタスク間がうまく分離できており、きちんとグルーピングできています。 しかし右図のように入力ベクトルのみによって埋め込まれたものは、iMaterialistのタスクは1つのクラスタとしてまとまっています。 つまり、データセット内のタスク間を分離できていないということです。 やはりTASK2VECの方がよくタスク間の分離ができていますね。


誤差分布

次にiNaturalistとCUB-200の156の学習済みモデルから特徴量を抽出し、そのタスクの分類誤差を示したものを見ていきましょう。 自然言語処理
このヴァイオリン図において、縦軸はTest Error(テスト誤差)、横軸はそれぞれタスクを示しています。 赤の×がTASK2VECによってモデル選択を行なった場合の分類誤差、 青の△がファインチューニングしてないImageNetで事前学習させたものから抽出した分類誤差を表します。(赤×が低い値の方がいい)
まずこれらのタスクは、左から右へとベクトルのノルム(大きさ)が小さいものから順に並んでいます。 また右に行くに従って分類誤差が大きくなっていることから、TASK2VECによって得られるベクトルの大きさは、タスクの複雑性と正の相関があることが分かります。


サンプルサイズに対する依存性

私たちの興味のあるタスクが比較的少ないサンプル数しかない時、最適な特徴抽出器を見つけ出すことは特に重要になってきます。 次のグラフは、モデル選択においてサンプル数が変化した時、TASK2VECのパフォーマンスがどのように変わっていくかを表したものです。 自然言語処理 グラフはそれぞれ、
オレンジの点線:最も最適なモデルを選択した時
赤の実線:TASK2VECによって選択した特徴抽出器
赤の点線:ImageNetのみで事前学習させた特徴抽出器
青の実線:TASK2VECによって選択した特徴抽出器をファインチューニングしたもの
青の点線:ImageNetのみで事前学習させた特徴抽出器をファインチューニングしたもの
です。これより全てのサンプルサイズに対して、TASK2VECはImageNetのみで事前学習させたものより良い精度を出していることが分かります。
最後に、以下の最適誤差に関する表を見て下さい。 自然言語処理 まずMeta-taskとして、iNaturalistとCUB-200の二つのタスクを合わせた「iNat + CUB」、全てのタスクを混合させた「Mixed」について考えています。 ここで「Chance」は適当なモデルを選択した場合に得られた特徴抽出器で、この表よりChanceのように安価な(何も工夫してない)特徴抽出器よりも、 ImageNetのみで事前学習させた特徴抽出器の方が良い精度であることが分かります。 しかし、そのImageNetのみで事前学習させた特徴抽出器よりも、TASK2VECを用いてタスク間距離を測定し、 モデル選択を行なった方が更に良い最適なモデルが得られることが分かります。

5.まとめ

TASK2VECはタスク、もしくは対応するデータセットを固定長のベクトルとして表現する有用な方法です。
性質として、
1.そのベクトルのノルムは、タスクのテスト誤差やタスクの複雑性と相関がある。
2.埋め込みによるベクトル間のコサイン距離は、タスクの自然距離(例えば種間の分類学上距離、転移学習におけるファインチューニング距離)と相関がある。
などが挙げられます。
総括すると、TASK2VECに基づくモデル選択のアルゴリズムは、学習なしに最適な(もしくは最適に近い)モデルを提唱するため、 自分で一から力尽くで(brute force)最適な学習済みモデルを探す必要がない!ということです。とても画期的な考えですね。 さて本論文にもありましたが、タスク空間上のメタ学習はこれからの人工知能に向けての重要なステップとなるでしょう。
実際、このTASK2VECは完璧にタスクの複雑性や多様性を捉えているわけではないので、実社会で見ることは少ないかと思います。 しかし、このTASK2VECの考え方を基に更なる発展のため、私も勉強していきます。


6.参考文献

TASK2VEC: Task Embedding for Meta-Learning
転移学習とは?ディープラーニングで期待の「転移学習」はどうやる?
データがないのに学習可能? 最先端AI「メタ学習」がスゴい


Twitter・Facebookで定期的に情報発信しています!

最近は今までに増して機械学習を検討する企業が増えています。 しかし、導入検討にあたり、機械学習でなにを改善するのか/なにを解決するのかという、導入目的を決めあぐねているケースも多いと耳にします。

本記事では機械学習はどのような業種で、どんな目的で導入されようとしているのかを紹介し、機械学習の活用例と解決できる課題(導入効果)をお伝えします。

『こんな使い方が自社に役立つな』や『機械学習でこんなことをやりたい』を発見してもらえると嬉しいです。

検討段階のシフト

2018年にIBMがおこなった調査によると、企業の82%はAIの導入を検討しています。
また、AIの運用と最適化を開始している企業は、2016年と比較すると、33%増加しています。導入に向けて成果を上げている企業は概念検証のテストや実験の域を超えて進んでいます。

経営層の目の向く先は、

『機械学習を導入すべきか』
から
『どのようにして機械学習を導入するか』
へと移行しているようです。

機械学習は技術的な進歩がめざましいと紹介されていますが、それに沿うようにして機械学習への期待も膨らんでいます。

機械学習の導入・活用実績

機械学習の導入率はテジタル化が進んでいる業界で高く、そのスピードも今後上がっていくと見込まれます。

例えば金融サービス業界では既に16%の企業が 機械学習システムを運用または最適化し、次いで製造業が追いかけています。

しかし自動車やヘルスケア業界にもこの傾向は見られており、機械学習は今後も大きな期待をされていることがわかります。

機械学習に期待すること

情報技術 (IT) と情報セキュリティー (IS)が、最も優先すべき業務として挙げられています。

例えば 機械学習を利用したヘルプデスク仮想アシスタント、 プロセス自動化、脅威検知アルゴリズムの活用があります。
その他に、イノベーション、顧客サービス、リスクの領域があります。
イノベーション領域には戦略的な機会が不可欠ですし、顧客サービスの領域は多くの企業が仮想アシスタント機能を試験的に導入しています。 そしてリスクの領域では不正の防止と検知が重要です。

幅広い領域で機械学習は活用できますが、効果的な導入にするためにはどの領域で/何を達成するのかという設定が必須です。

コスト削減より、顧客満足度を重視

企業として売上増加はこの 2 年間で一層重視されるようになりました。

調査によると、経営層は顧客満足度と顧客維持率をAI投資の主な目標に掲げており、これはコストに対する考慮を大きく上回りました。
もちろんこれはコストが重要ではないという意味ではあり ません。

多くの 機械学習プロジェクトの中を見てみると、目的のひとつにはやはりコスト削減の要素が含まれているので、『コスト削減は当然で、次の視点として顧客満足度を重視している』とも言えます。
また、運用コストの削減も 3 番目に重要なものとされています。しかし、このランキングでの順位をを押し上げたのは最高財務責任者 (CFO) と財務部門かもしれません。

加えて、多くの企業は製品やサービス よりも顧客体験を重視しています。
何を売るかよりも、その製品やサービスを通じてどんな体験ができるか、ということです。体験は購入後だけではなく、購入前の検討時もその対象です。 

顧客体験の向上は、顧客に対面するその企業の担当者次第であることが少なくありませんが、
そのような場面で AIを的確に利用した仮想アシスタントを使用すれば、既存の専門知識を拡大して、顧客の質問により速く、正確に、しかも経済的に答えられるようになります。

機械学習活用例

ここまで、機械学習が活用されている業種と目的をお伝えしました。
次に業種ごとにお客様満足に繋がる機械学習の活用例をご紹介します。

製造業

『製造ラインでの不良品検知』


課題
食品製造ラインで不良品は手作業で弾いている。

  • 個人ごとに判断がばらつく場合がある
  • 同じ体勢で長時間いるため作業者への負荷が大きい


導入後
製造ラインにカメラを設置し、良品/不良品を自動で仕分けする。

  • 作業者は別の業務にあたることができ、より利益追求や改善が進んだ。
  • 生産性の向上も実現し、労働時間を短縮できた。


飲食業

『顧客の消費予測で廃棄、機会損失の減少』


課題
いつ、どれくらい売れるかを肌で感じ、仕入れをしている。

  • 店長のスキルで売上、利益が大きく異なる。
  • 仕入れを絞ると機会損失が生じ、仕入れを増やすと廃棄が多くなる。


導入後
過去の売上から未来の売上を予測する。

  • 廃棄や機会損失の減少
  • 常に欲しい物が適切量ある店舗で、顧客満足度も向上


小売業

『チャットボットでユーザー好みの商品をオススメ』


課題
ECサイトでの売上が伸び悩んでいる。

  • 試着ができないため、ユーザーは意思決定できない。
  • 電話での個別問い合わせはハードルが高く、利用されない。


導入後
ユーザーが質問に答えると似合う商品が分かるチャットボットを利用。

  • 廃棄や機会損失の減少
  • 常に欲しい物が適切量ある店舗で、顧客満足度の向上


『SNSから商品評価分析』


課題
商品のリアルな評価が不透明で新商品開発で悩んでいる。

  • どのようなシーンで、誰が商品が使われているか知りたい。
  • 口コミサイトや直接の評価は極端なものが多い。


導入後
SNSから自社商品が写っている写真を分析する。

  • 利用されている季節や雰囲気が分かり、より顧客に寄り添った商品開発が出来る。


おわりに

機械学習は幅広い活用方法があり、お客様ごとの活用方法の提案が必要です。
運用までこのような流れで進めることが多いです。
Process


アクセルユニバースではみなさまの課題やお困り事を一緒に解決していきます。
下記にご興味がありましたら、問い合わせ口からご連絡ください。

  • 活用事例についてもっと詳しく知りたい
  • 機械学習・深層学習は他になにが出来るのか興味がある


アクセルユニバースのご紹介

私達はビジョンに『社会生活を豊かにさせるサービスを提供する。』ことを掲げ、このビジョンを通して世界を笑顔にしようと機械学習・深層学習を提案しています。
例えば、現在人がおこなっている作業をシステムが代わることで、人はより生産性の高い業務に携わることができます。


acceluniverse


Twitter・Facebookで定期的に情報発信しています!

はじめに

2019年秋の応用情報技術者試験に無事合格することができたので、勉強法などを何番煎じかわかりませんが書いていこうと思います。 私は現在文系学部の2年生で、趣味でプログラムとかを5年ほどやっているのでパソコン系の知識はそこそこありますが、数学や理論といったことははめっきり。 今春に基本情報技術者試験を通ったのでその流れで受けました。

試験の概要

IPA(独立行政法人 情報処理推進機構)によって実施されており、士業の独占などはありませんが、国家資格のひとつです。

対象者像
高度IT人材となるために必要な応用的知識・技能をもち、高度IT人材としての方向性を確立した者


試験形態として、午前・午後試験があり、両方で60点以上で合格です。 午前試験が全て4択問題に対して、午後試験は記述式です。とはいえ、問題のほとんどが選択肢問題だったりします。
こちらから試験の過去問が公表されています。

問題の傾向や勉強法

午前試験

テクノロジ系 50問

マネジメント系 10問

ストラテジ系 20問

の3つ全80問から構成されています 。

基本情報と難易度はさして変わらないと言われますが、覚えないといけない量が少し増えます。特にテクノロジ系は計算問題が基本情報に比べ遥かに難しいと感じました。

ただ、午前試験の対策を万全にしていたら午後試験もその知識で解ける問題がほとんどだと思いました。

勉強法

ひたすら過去問を繰り返す。

私は ここを愛用していました。

会員登録はすると間違えた問題でソートしたりできるようになります。必須です。これだけでいいです。

参考書を基本情報試験のときに1冊購入しましたが、結局1回流し読みしてあとは辞書変わりに見る程度の使い方しかしていなかったので、応用情報用の参考書は買いませんでした。参考書通りにじっくり時間をかけるのはコスパ悪いと思います。 基本情報のときにはこちらを使っていました。

textbook

紙の本を買っても、電子版のpdfを無料で使えたので重宝しました。

わからなかったところは、ググって出てきた中で一番易しそうなのを見ました。文が多いと嫌になるので。
自分はここでさっと見てわからなかったら深く調べていました。 だいたいここで完結できます。

ある程度の知識があれば、通勤、通学時間だけで事足ります。

私は、通学に片道1時間程かかるので、その時間にせこせこやっていました。

電車の中では計算問題を出さないモードがあるので選択肢だけで完結している問題に終始するといいと思います。

本番はいくらでもメモが取れるのでそういった問題は別個で机に向かって解くと時短になります。

当日

試験時間が全部で150分もありますが、40分経つと退出可能になります。午後のことも考えて十分見直しが終わったら長めの昼を取るといいと思います。
私は30分で1週解ききって追加で30分ほどかけて見直しをして退出して隣の駅まで悠長に食事をしに行きました。

午後試験

試験の1ヶ月ほど前に気合を入れて過去問をコピーして3週間かばんにいれ読むことはなくお守りに。1週間前に焦って解き始めました。 分野として、

  1. 情報セキュリティ※必須
  2. 経営戦略
  3. プログラミング
  4. システムアーキテクチャ
  5. ネットワーク
  6. データベース
  7. 組込みシステム開発
  8. 情報システム開発
  9. プロジェクトマネジメント
  10. サービスマネジメント
  11. 11システム監査

の全11問の中から1以外から4問選択できます。

前までは経営戦略かプログラミングのどちらか1つが必須だったみたいですが、その縛りはなくなったようです。

勉強の時点で全てをやる必要はなく、ある程度自分が本番で解きそうな問題に絞ってやるようにしました。

選び方ですが、2, 6, 9, 10, 11が文系の人が取りやすいとよく言われています。 とはいえ、結局自分の得意なものがなにかをわかるのが一番重要です。

自分の知識がなにかを知る

私の場合事前に2, 3, 4, 5, 7にしぼりました。

  1. (必須), 前にctfに友人に誘われて参加したことがあり、そのときに学んだ知識と、午前の勉強でほぼ何にでも対応できそうだったので特段の勉強はしませんでした。ctfは分野が広いのでもしやっていれば非常に有効だと思います。私が受かったのはほぼこれのおかげだと思います。


  2. 簿記2級レベルならできるのでその範囲内に収まるなら選択するようにしました。その範囲から外れてたら過去問でも捨ててました。結局見極めが大事です。


  3. ある程度のプログラム経験があれば前準備なしでもできます。細かい仕様などは問題文に書いてくれているのでそこさえ気をつければあとは慣れです。


  4. システムアーキテクチャなぞ当然構築経験はありませんが、9, 10, 11に比べ文が少なくその代わりにちょっとした計算が絡むから楽だと思って選んでいました。本番は初っ端から掛け算間違えて途中まで解いて別に鞍替えしました。


  5. ctfと午前の知識でどうにでもと思い選択。本番では選択肢わからないのがあって途中まで解いて他で点が取れそうなのがあればそっちにいって無理そうだったら戻ってくることにしました。


  6. dbは1回覚えれば簡単!文系でもできる!と喧伝されてますが、sqlを触ったことがないのでパスしました。


  7. 3と4を足したような感じで読めばなんとでもなりそうだったので選択。毎年同じパターンの問題が1つは出るので過去問を多めに対策しました。


  8. 勉強のときには目もくれていなかった分野です。本番では惰性で5が自信ないからと見たら案外行けそうだったので選択。4との違いがいまいちわかりません。


  9. ここは基本情報のときに一番嫌いな分野だったので目もくれずスルー。計画性のあるかたは是非。


  10. ぶっちぎりの文字数。読みたくないです。


  11. 午前の監査系がこまごましてて嫌いだったので見ることもなく。

実際に本番で選択したのは、2, 3, 7, 8でした。事前にある程度範囲を絞るのも大事ですが、不足の事態に対応できるようにするべきだなぁと後悔しながら解きました。

極端な話3問完答すれば、残り2問は0点でも受かるので自分の得意な分野を探すことが本当に大事です。 これを書きながら再度感じました。

勉強法

上述の通り勉強自体は午前の用意の段階で終わっているので、あとはどの分野を選ぶかを決めるだけで十分だと思います。


過去問用の参考書も売っていますが、答えも公開されているのでそれをみてわからなかったら自分で調べてそれでもわからなかったら捨てる。という戦法で問題ないと思います。

ある程度絞ったら私は、傾向を掴むために過去問数回分やりました。数回分とはいえ、本番が1回2時間半で、本番より多い範囲やっているので結構な時間がとられます。


できなかった問題について用語など知っていればできた問題を落としたのか、それとも読めばわかる問題を落としたかの分類をして、知識が足りなければひたすら午前の問題を、論理問題を落としてしまったら集中力が切れているので寝てリフレッシュしましょう。


公式HP過去問
過去問道場

気をつけるべきこと

応募の締切が早い
2ヶ月前に締めきられてしまいます。私はこれで1回分逃しました。

試験当日への身構え
お金を払ったのに4割の受験者が会場に来ないようです。ハンター試験と揶揄されています。前もって予定の都合をつけたり当日面倒にならないようにメンタルケアをしておきましょう。

長期戦である
時間通りに受けていたら休憩時間が1時間しかありません。回収時間なども考えるともっと短くなってしまいます。早めに出て気晴らしに美味しいものでも食べましょう。食べすぎると午後眠くなります(戒め)。

思ったらまとめるほど勉強してなかったし運がよかったのかもしれません。

総勉強時間は、電車で毎日見ていたので往復2時間を20日ほど、午後の過去問を4回分解いたので約10時間。あわせて50時間。

机に向かっていた時間は10時間ほどなのでこうしてみるとそれでも結構やっていたなと思うくらいの時間感でした。

Twitter・Facebookで定期的に情報発信しています!

AIの進歩が目覚ましい近年、文章の文脈まで読み取ってくれるAIがあれば嬉しいですよね。
ビジネスの場面でも、クレームとお褒めの言葉を分類したり、議事録のアジェンダと中身が合っているかなど、文脈を判断できればAIの使える場はぐんと広がります。

文脈を機械が読み取るのは難しく、研究者も今まで苦労してきました。
しかし近年、ELMoとBERTという2つのAIが現れたことで、今機械学習は目覚ましい進歩を遂げています。
多くの解説記事は専門家向けに書いてあることが多く、要点だけをまとめたり、仕組みをわかりやすく説明した記事は多くありません。
今回の記事は、そういった「よくわからないけどもっと知りたい」「本質をつかみたい」という要望に答えるべく、最新のAIをできるだけわかりやすく説明することを目的としています。


目次

  • 今までのAI
  • ELMoとBERT ~新星の誕生~
  • 弱点と新たな新星


今までのAI

今までのAIは、単語一つ一つに対して数字を当てはめる方式をとってきました。
有名な例がWord2Vecの
「王」+「男」ー「女」=「女王」
という式です。上手に学習ができれば、このように単語ごとに数字を当てはめても他の単語との相関関係を表す事ができました。

しかし、大きな問題があります。
例えば、次の文を御覧ください。

「昨日から雨が降っている」

この文は、砂漠地域についての文章と熱帯地域の雨季についての文章では意味合いが違ってきます。
他にも、

「コストは2倍だが、利益は3倍だ。」

「利益は3倍だが、コストは2倍だ。」

の2つでは重点が違いますし、順番が違うだけで文章全体の意味合いが変わってきてしまいます。
つまり、単語ごとの数値だけではなく、文脈を考慮した数値を使わないとAIは文章を正確に判断できないのです。

ELMoとBERT ~新星の誕生~

そこで2018年に生まれたのがELMo、そしてその後継者のBERTです。
ELMoは文の単語を1語ずつ読み取り、単語の情報を徐々に蓄積させていく手法を使います。
以下の図を御覧ください。

image1.png

緑の箱はLSTMで、前の単語の情報を引き継いで新しい単語の情報を学習させています。(LSTMについてはこちらを参照
つまり「利益は3倍」というフレーズが、前に「コストは2倍だが」と書いてあるという情報をふまえた上でAIに学習されることになります。
当然、後ろ側が前を参照できるだけでは、前の単語にしっかりと文脈を反映させることはできません。そこで文章の単語を逆向きにして同じような学習をさせます。
image2.png シンプルなように見えますが、ELMoはこの手法を使うことで様々な記録を塗り替え、AIの進歩に大きく貢献しました。

その僅か数カ月後、2018年10月に今度はGoogleがELMoを改良したBERTを発表し、時代はBERTのものになりつつあります。
BERTが改良した点は、主な計算にLSTM(上図で緑の箱)の代わりにTransformer(下図でオレンジの箱)を採用したことです。

image3.png

Transformerとは強そう名前ですが、論文"All you need is Attention"において注目を浴びたAIの学習機で、Attentionと呼ばれるその名の通り「注意度」を考慮に入れた非常に強力なAIのモデルとされています。(Attention モデルについてはこちらを参照
LSTMより強力な部品を使っている分、BERTのほうがELMoよりも性能が高いというわけです。

BERTはコードが公開されているだけでなく、日本語を含めた104ヶ国語に対応しており、まさにGoogleの力を存分に発揮したAIとなっています。

文脈を読み取るとどのような課題に対応できるのでしょうか。
実際にBERTは

  • MNLI:含意関係の分類
  • QQP:質問内容が同じであるかを当てる
  • QNLI:質問と文が与えられ、文が質問の答えになるかを当てる
  • SST-2:映画のレビューに対する感情分析
  • CoLA:文の文法性判断
  • STS-B:2文の類似度を5段階で評価する
  • MRPC:ニュースに含まれる2文の意味が等しいかを当てる
  • RTE:小規模な含意関係の分類


で最高スコアを叩き出しています。


弱点と注意点

ここまで解説したように、BERTは文章の文脈を考慮に入れて文章を分類・生成できる強力なAIで、今後の応用が期待されています。
しかし弱点があるわけではなく、正しく理解しないとお金と時間の無駄になってしまいます。
今回の記事では要点のみを説明したので、細かい部分や数学は省略してあります。
また、BERTは文章をインプットし、文章を出力するAIです。なので実際の個別の課題を解く際はBERTの出力を更に分類したりするモデルが必要になってきます。
しかし言語全体の文脈感を反映させるにあたってやはり強力なAIと言えるでしょう。

また、BERTには弱点があります。
実際の学習では、文章の単語をランダムに隠し、そこを学習させることで精度を向上さています。
しかし実際の文章では文中の単語が隠されていることはなく、また複数の単語が隠されていた場合、それらの間の関係性を学習することができません。

例えば、
[単語]は2倍だが、[単語]は3倍だ。

というふうに隠されていた場合、文が正解として認識される単語の組み合わせは多数あります。なので本来の「コスト」「利益」という単語を学ぶよりも精度が下がってしまいます。
この弱点を克服したのが、今年(2019年)6月に発表されたXLNetなのですが、それはまた次回のブログ記事でご紹介致します。

以上、文脈を読み取ることができる最新のAIについて解説してきました。細かい部分は省略していますが、本質だけを突き詰めると意外とシンプルだという事がわかります。
文脈を読み取ることができれば、社会の様々な場面でAIがますます活躍することになるでしょう。
そのために複雑な数学を理解する必要はありませんが、AIの本質をしっかり把握しておくことで、変化の波に遅れることなく、AI活用の場を見出す事ができるのです。


アクセルユニバースの紹介

私達はビジョンに『社会生活を豊かにさせるサービスを提供する。』ことを掲げ、このビジョンを通して世界を笑顔にしようと機械学習・深層学習を提案しています。
例えば、現在人がおこなっている作業をシステムが代わることで、人はより生産性の高い業務に携わることができます。


当社ではみなさまの課題やお困り事を一緒に解決していきます。
下記にご興味がありましたら、問い合わせ口からご連絡ください。

  • 問い合わせ業務を自動化したい
  • 入力された文章を振り分けたい
  • 機械学習・深層学習は他になにが出来るのか興味ある


Twitter・Facebookで定期的に情報発信しています!

今回はデータを分析するという点で共通する経済学やデータサイエンスについて 概要を説明し、
経済学において機械学習を使うことの難しさについて説明します。
難しさと言っても、『活用できない』と否定をするわけではありません。
あくまでも活用していくことを前提に、気をつけたい点を紹介します。


目次

  • 経済学とデータサイエンスについて
  • なぜ経済学と機械学習が議論されるのか
  • 経済学で機械学習を利用する際の注意点
  • まとめ


経済学とデータサイエンスについて

そもそも経済学は、人がどんなものを買ったり、いくら貯金をするのかといった人の意思決定について仮説を立てて考えていく学問分野です。
分析する対象が人から、人が集まった国になれば、ニュースなどで見るGDPや為替といったテーマを扱います。
このように経済学が扱うテーマは幅広く、企業の経営課題や医療など多岐に渡ります。


近年はコンピューターが進化し、以前よりも多くのデータを扱えるようになったため、経済学的な仮説のモデルを実証分析するために統計的なアプローチからパラメータの推定、検証を行う計量経済学という分野が発展していきました。
このような流れから、経済学の中にデータドリブンな考えが入ってくることで、データから未来の行動を連想することが必要とされています。

Marketing


ここで機械学習を使ったデータサイエンスが想起されます。
集めたデータに対して、機械学習という統計的な手法を使い、未来の予測や分類を行います。
扱うテーマは広く、例えば、企業が持っているデータから課題を解決したり、医療データを使ったデータ分析も行われています。

若干の違いはあるものの経済学とデータサイエンスは、扱うテーマが近しく、経済学の分野でも機械学習を取り入れることが議論されています。


なぜ経済学と機械学習が議論されるのか

経済学は前述のように、人が関わるテーマを幅広く扱います。


機械学習では、今まで手に入らなかったビッグデータのような大量のデータを分析に使えるようになり、経済学でもそれまで使っていなかったデータで実証分析しようという動きがあります。
機械学習での予測や分類が、マーケティングや株価トレードなど経済学の分野の実用に結びつきやすかったためです。
そのため経済学的な仮説をビッグデータで実証分析する際に、計量経済学ではなく機械学習を使うアプローチが議論されました。
実際に『経済セミナー』(日本評論社、2019)でも「機械学習は経済学を変えるのか?」という特集が組まれています。


経済学で機械学習を利用する際の問題点

しかし、こうしたアプローチには問題点も存在します。


経済学は前述のように仮説ベース、モデルベースで考えることが多く、実証分析の前にあらかじめ説明変数等を理論から考えることが主流です。
しかし、機械学習は特徴量(説明変数)を自由に決め、精度の高さを追求するアプローチが主なものになっています。


両者の考え方がかなり異なっており、機械学習で発見した結果を経済理論で解釈する時点で、機械学習特有の、新たな発見は失われてしまう可能性があります。
また経済学の多くの目的が、予測ではなく「なぜそうなるのか」といった因果推論をおこなってきたため、結果の解釈を説明するのが苦手な機械学習とは、もともと相性が悪い点も無視できません。

Marketing

まとめ

以上のように、仮説ベースの経済学において機械学習を使う際は、両者の考え方の違いから機械学習のもつ利点を無くしてしまったり、経済学的に解釈できないといった問題が考えられます。
そのため経済学の分析で機械学習を分析につかう際は、こうした側面があることをしっかり認識したほうが良いでしょう。
※今回は広く経済学と機械学習の考え方を説明し、応用の難しさを説明しましたが、その上で機械学習を経済学分野で応用する例は近年増加しています。


参考文献

与田高典「経済分析ツールとしての機械学習」『経済セミナー』、
2019年2020年12月号1月号、23〜27ページ
今泉允聡「機械学習はデータを解釈できるのか?」『経済セミナー』、
2019年2020年12月号1月号、34〜39ページ


アクセルユニバースのご紹介

私達はビジョンに『社会生活を豊かにさせるサービスを提供する。』ことを掲げ、このビジョンを通して世界を笑顔にしようと機械学習・深層学習を提案しています。
例えば、現在人がおこなっている作業をシステムが代わることで、人はより生産性の高い業務に携わることができます。


当社ではみなさまの課題やお困り事を一緒に解決していきます。
下記にご興味がありましたら、問い合わせ口からご連絡ください。

  • 大量にデータはあるが活用方法に困っている
  • 機械学習・深層学習は他になにが出来るのか興味ある


Twitter・Facebookで定期的に情報発信しています!

〜普及に向けた課題と解決策〜に続き

 私が前回作成した記事である「海洋エネルギー × 機械学習 〜普及に向けた課題と解決策〜」では、海洋エネルギー発電の長所と課題とその解決策について触れた。今回はそこで取り上げた、

課題①「電力需要量とのバランスが取りにくい」、課題③「無駄な待機運転の時間がある」への解決策

~発電量・電力需要量予測~ ~機械学習を用いた制御~

を実装してみる。

 繰り返しにはなるが、この「発電量・電力需要量予測」と「機械学習を用いた制御」とは、まずカレンダー条件(時間帯、曜日など)と気温や日射強度などの気象条件を説明変数として電力需要量を予測し、電力需要量の送電にかかる時間分先の予測値と同量を発電するように発電装置を制御するという、極めて単純な制御である。前提として、電力は常に需要量と供給量(発電量)が同量でなければ、停電を起こしてしまうのであるが、この制御を行えば、需要と供給のバランスを常に保ちながら発電することができる。また発電装置の無駄な待機時間も削減され、設備稼働率も向上する。このようにして、課題①「電力需要量とのバランスが取りにくい」、課題③「無駄な待機運転の時間がある」を解決するのである。今回は発電方法を波力発電としてこれを実装する。


目次

  1. 今回の実装の設定
  2. 機械学習による電力需要量の予測
  3. 並進動揺型の波力発電装置の運動制御
  4. 考察
  5. 参考文献


1. 今回の実装の設定

1-1. どんな波力発電装置で実装するか

 まず波力発電装置の種類は表1、図1のように分類できる。

表1 波力発電装置の種類

スクリーンショット 2019-12-26 12.44.18.png スクリーンショット 2019-12-26 14.54.18.png

 図1波力発電装置の種類


 今回の実装では、垂直(上下)にのみ変位する並進動揺型の波力発電装置を考える。並進運動型の波力発電装置の特徴は、

  • 波に対する指向性がなく、他の発電装置に対して波向きの影響の点から優位である
  • 構造がシンプルで発電機構(PTO: power take off)も含め大部分が水中に没しているので、浮体本体の構造安全性や信頼性の点で有利である
  • 波浪荷重、発電荷重を下端で支えているため繰り返しの係留力が作用することになり、下部の構造強度に配慮する必要性がある
  • 発電装置が水面より下側にあることから、上部からの浸水に対しては十分注意が必要

 また、現在最も実用化に近いものと見られる波力発電装置は、米国のOcean Power Technologies社が開発した並進動揺型の波力発電装置「PowerBuoy」(図2)である。 スクリーンショット 2019-12-26 14.54.47.png

図2Ocean Power Technologies社の「PowerBuoy」


1-2. 実装の設定

 今回の設定は、1機の上下揺のみする並進動揺型の波力発電装置から、ある海沿いの一軒家に直接電力を供給するという状況を考える。波力発電装置から一軒家への送電時間は10分かかるとする。つまり上記の制御をこの波力発電装置に行うには、現在(時刻\(t\))の発電量\(P(t)\)が10分後(\(\tau=10~[分]\))の電力需要量の予測値\(P_{ML}(t+\tau)\)と等しくなる必要がある。


2. 機械学習による電力需要量の予測

 ある一軒家の電力需要量(電力使用量)を予測する時、本来ならば上記に示したようにカレンダー条件と気象条件を説明変数とするべきだが、カレンダー条件と気象条件、電力消費量のデータセットを手に入れることができなかったので、今回は特徴量を表3のようにしたデータセット(表4)を用いる。これは2016年1月11日17時00分から2016年5月11日16時50分までのデータセットである。

表3 データセットの特徴量

スクリーンショット 2019-12-26 16.11.02.png


表4 データセットの一部

スクリーンショット 2019-12-26 15.03.37.png


 初めに示しておくが、電力需要量は暑い夏や寒い冬など季節によって変動する。であるから正確に予測するためには少なくとも1年以上のデータ数が必要となるが今回はデータ数が1年に満たない。なので当然予測精度は悪くなると考えられるが、今回は実装手順を紹介したいので、精度については目を瞑る。
 予測モデルは時系列データに強いと言われているLSTMを用いた。すると予測結果は図3のようになる。破線はデータ値で、赤線が予測値である。但し値は0から1になるように正規化されているため、正規化された赤線の値を元に戻した値が\(P_{ML}(t+\tau)\)である。図3を見て分かるように電力需要量が多い時の予測値は実際の値とかなりずれているが、それは想定内で、それ以外の時は良く予測されている。この予測値を波力発電装置の制御に用いる。

スクリーンショット 2019-12-26 13.41.48.png

図3 予測結果


3. 並進動揺型の波力発電装置の制御

 まず、波浪中を自由に動揺する浮体(水面に浮く物体の総称)の問題は船舶流体力学の知識から、以下の二つの問題に分別することができる。

表5 波浪中を自由に動揺する浮体の問題の分類

スクリーンショット 2019-12-26 14.49.14.png


 ディフラクション問題において浮体に働く力には以下のようなものがある。但し、重力と浮力は釣り合っているとして除外する。

表6 ディフラクション問題において浮体に働く力

スクリーンショット 2019-12-26 14.49.41.png


 ラディエイション問題において浮体に働く力には以下のようなものがある。

表7 ラディエイション問題において浮体に働く力

スクリーンショット 2019-12-26 14.50.02.png


 当然ではあるが上下揺のみする並進動揺型の波力発電装置も浮体の一つであるから、ここでは浮体と呼ぶことにする。
 まず浮体の動揺を制御していない場合、波浪中の浮体の運動方程式は、浮体の上下方向の運動変位を\(z\)、上記で示したラディエイション力、復原力、波浪強制力の上下方向成分をそれぞれ\(F_{3}^{R}(\ddot{z},\dot{z})\)、\(F_{3}^{S}(\ddot{z},\dot{z})\)、\(F_{3}^{W}(\ddot{z},\dot{z})\)とすると、 \[ m\ddot{z}(t)=F_{3}^{R}(\ddot{z},\dot{z})+F_{3}^{S}(z)+F_{3}^{W}(t)\tag{1} \] と表せる。ここで\(F_{3}^{R}(\ddot{z},\dot{z})\)、\(F_{3}^{S}(z)\)、\(F_{3}^{W}(t)\)は数値計算または計測されていて既知であるとする。
 次に浮体の動揺を制御する場合、浮体の運動方程式は式に制御力\(G_{3}(t)\)を加えて、 \[ m\ddot{z}(t)=F_{3}^{R}(\ddot{z},\dot{z})+F_{3}^{S}(z)+F_{3}^{W}(t)+G_{3}(t)\tag{2} \] と表せる。この制御力\(G_{3}(t)\)を調節することで、望んでいる量を発電するように浮体を動揺させることができる。
 発電量\(P(t)~[W]\)は、発電機構の巻線抵抗\(R_{s}~[\Omega]\)、推力係数\(K_{t}~[N/A]\)を用いると、機械入力\(-G_{3}(t)\dot{z}(t)\)から発電ロス\(\cfrac{ R_{s} }{ K_{t}^{2} }G_{3}^{2}(t)\)の差として表せるので、 \[ P(t)=-G_{3}(t)\dot{z}(t)-\cfrac{ R_{s} }{ K_{t}^{2} }G_{3}^{2}(t)\tag{3} \] となる。
 この発電量\(P(t)\)を、機械学習(machine learning)で予測した時刻\(t+\tau\)の電力需要量\(P_{ML}(t+\tau)\)となるように制御したいので、 \[ P(t)=P_{ML}(t+\tau)\tag{4} \] とする。すると式\((3)\)は\(G_{3}(t)\)の二次方程式 \[ \cfrac{ R_{s} }{ K_{t}^{2} }G_{3}^{2}(t)+\dot{z}(t)G_{3}(t)+P_{ML}(t+\tau)=0\tag{5} \] となる。二次方程式の解の公式より、制御力\(G_{3}(t)\)は \[ G_{3}(t)=G_{3}(\dot{z})=\frac{ -\dot{z}(t)\pm\sqrt{ \dot{z}^2(t)-4\cfrac{ R_{s} }{ K_{t}^{2} }P_{ML}(t+\tau) } }{ 2\cfrac{ R_{s} }{ K_{t}^{2} }}\tag{6} \] と求まり、浮体の上下揺の速度\(\dot{z}\)の関数となる。この制御力\(G_{3}(\dot{z})\)を式\((2)\)に代入することで、浮体の動揺は\(P_{ML}(t+\tau)\)だけ発電するように制御される。


4. 考察

 上記で示したように制御することで、確かに需要と供給のバランスを常に保ちながら発電することができる。ただ、今回は実装の手順を示すことを目的として電力需要量の予測の精度については気にしなかったが、本来はここが極めて重要である。2章でも示したが、今回使用したデータは1年に満たないデータ数である。これでは電力需要量の朝、昼、夜の変化は捉えることができたとしても、季節の変化は捉えることができない。これが予測精度を落としている明らかな理由である。
 また、今回は10分間で送電が完了するという状況を考えたためそう遠くない未来の電力需要量を予測するだけであったが、実際波力発電装置が陸地からかなり遠い沖合に設置された場合、10分で送電できるとは考えられない。つまりもっと先の未来の電力需要量を予測できなければならない。実際に今回のデータセットで1時間先の電力需要量を予測してみたところ、かなり酷い精度になり、これでは波力発電装置の制御には使えないという結果となった。しかしこの原因もデータ数が足りないことであることは分かっている。季節の影響を捉えていないモデルで、未来を上手く予測できないのは当然である。
 上記二つの問題はデータ数を増やすことで改善できるので、長い期間の電力需要量のデータを入手次第、改めてやり直そうと考えている。
 問題はまだある。それは発電量が多い時、精度が悪くなることである。これは今のところ原因が分かっていないので、引き続き研究していく。
 また、今回は求めている発電量を発電できるような制御を行ったが、安全性については全く考慮していない。たとえ電力需要量と同量発電できたとしても、事故を起こしてしまっては元も子もない。なので、今回の制御に安全性の配慮を加えた研究も今後行っていく。


5.参考文献


Twitter・Facebookで定期的に情報発信しています!

もくじ

  1. 因果推論とは何か
  2. 交絡とは何か
  3. 交絡の調整法
  4. 因果推論の例
  5. まとめ
  6. 参考文献

1. 因果推論とは何か

因果推論とはある要因の間に因果関係があることを推論することである。因果推論をするための手法は多岐に渡るが、その前に因果関係について説明をしておく。

因果関係とは2つの要因が原因と結果という関係性で結びついていることである。これは原因となる要因が変化することで結果もまた変化する関係性にあることを示している。原因がA、結果がBであるとき、以下のような因果関係となる。

Alt text

ここで注意しておきたいのが、相関関係と因果関係の違いである。相関関係は2つの変数がどちらか一方が大きければ、もう一方もまた大きい(または小さい)かどうかというデータの「あり方」について述べているのに対し、因果関係には原因となる変数と結果となる変数があり、それは「原因に対し介入を加えることで結果が変わる」ものを指す。

つまり、相関関係があるということだけでは因果関係があるとは言えず、またどちらの変数が原因なのか、あるいは結果なのかを説明する根拠にはなりえないのである。因果推論は先に述べた相関と因果の取り違いのような因果関係の誤謬に注意して原因と結果を推論する行いだと言える。

2. 交絡とは何か

因果推論を説明する上で重要になってくるのが交絡である。交絡とは以下のような因果の構造を指す。

Alt text

上のような2つの要因の両方に影響するものを交絡因子と呼ぶ。この交絡因子Cが要因AとBの両方に影響することで、あたかもAとBが因果関係を持っているように思えるのである。

因果関係を正しく推論するためには、この交絡に対処する必要がある。観察されたデータに相関関係がみられるとき、それが因果関係によるものなのかあるいは交絡因子の影響によるものなのかを何かしらの方法によって区別しなければならない。

そのために交絡因子の調整を行い、その影響によって差が生じることを防ぐ必要がある。これは交絡因子の影響を固定し変化しないようにすることで、原因と結果だと思われる要因の関係を検討することである。3節では実際に行われる交絡の調整法について述べる。

3. 交絡の調整法

交絡の調整には様々な手法が存在するが、ここではその中でも広く使われているランダム化比較試験と層別解析の2つの手法について説明する。

ランダム化比較試験(RCT)

ランダム化比較実験(randomized controlled trial, 以下RCTと呼ぶ)とは原因と考えられる要因を変化させながらその結果の変化を見ていく中で、その結果の差を見るために介入を行うかどうかを無作為に行うことである。

具体例を挙げて示そう。ある医薬品Aを服用するかどうかによってその対象者の血糖値Bが下がるかどうかを考える。このとき、医薬品Aを服用するかどうかは無作為に決められなければならない。より詳しく言うならば、医薬品Aを服用するグループと服用しないグループで、その他の要因の偏りが生じてしまってはいけないのである。このグループ間の偏りが交絡因子の影響によるものだと考えられ、交絡因子として実験を行うときに知っている要因もあれば、全く知らない未知の要因も存在する。

しかし、このRCTを行い無作為に対象者を選ぶことで、既知の要因も未知の要因も2つのグループ間で平均的に同じぐらいにすることができる。つまり、交絡因子による影響を同程度にしながら医薬品Aの効果、つまり血糖値Bが低下するかどうかという因果関係を知ることができるのである。

層別解析

実験研究においてはRCTは可能であったが、介入の無作為化が困難な場合がある。例えば喫煙者と非喫煙者との間で喫煙の有無による影響を測定するときに、対象者ごとに喫煙するかどうかを実験をする者が決めることはできない。そうした際に、喫煙者と非喫煙者のそれぞれの特徴には偏りが生じると考えられる。

このようなデータに対する手法として層別解析が挙げられる。層別解析とは交絡因子として考えられる特徴について、その特徴が同じ水準のサンプルごとに層として分けて解析を行うことである。これにより、分けられた層の中では特徴が似通っている、つまり交絡因子の影響が同程度になるので正しい推論が可能となる。

この他にもマッチングや回帰モデルを用いた分析、傾向スコアを用いた分析などが存在し、それぞれ交絡因子の影響を調整する手法として広く使われている。

4. 因果推論の例

現在、因果推論は経済学や社会科学、心理学や臨床医学など多岐に渡る分野で応用されている。この節ではそんな因果推論の利用を身近な例からいくつか紹介したいと思う。

偶然の関係

「ニコラス・ケイジの1年間の映画出演回数」と「同年の水泳プールでの溺死者数」には相関関係があるという結果が示されている。しかし、常識的に考えればこの2つの要因には因果関係はない。このようなあからさまな例では誰しもが因果関係は無いことに気づくが、相関関係のあるデータには常にこの問題がつきまとう。

擬似的な相関関係を見て要因間のありもしない因果関係を「でっちあげ」ていることが、我々の生活の中で行われていることが意外にも多いのである。このような間違いを回避するためにも、因果推論の考え方は重要である。

交絡因子による影響

「子供の体力」と「子供の学力」に相関があるというとき、どのように結論づけるべきだろうか?体力と学力の間には因果関係はあるといえるのか?

前の節でも述べたように、2つの要因の相関性が別の交絡因子によるものであることがある。今回の場合、「親の教育への関心の高さ」を考えるとどうだろうか。教育に関心の高い親は、子供の体力作りに熱心であると同時に、学力向上にも力を入れていることが多いであろう。

つまり、「親の教育への関心の高さ」が交絡因子となり「子供の体力」と「子供の学力」に影響して相関関係が現れていると考えられる。因果推論する上で交絡を見つけ出すことも重要となる。

因果の向き

因果推論をする際には、因果関係があるということが言うだけでなく、その向きについても正しく推論するべきである。「アイスクリームが売れる」から「気温が高い」のではなく、「気温が高い」から「アイスクリームが売れる」と考えるのが自然である。

ただデータを見るだけではどちらの要因が原因で、どちらの要因が結果になるのかという「因果の向き」の推論を行うことは難しい。要因間の関係を慎重に解析し、因果の向きを取り違えないように推論する必要がある。

5. まとめ

今回の記事では、以下の事柄について述べた。

  • 因果推論について
  • 交絡とその調整法について
  • 実生活の中での因果推論の例

因果推論の手法には、まだまだ紹介しきれていないものもある。また、データから正しい推論をするデータサイエンスの領域では、今後も因果推論の考え方は重宝されていき、より発展していく分野だといえる。

6. 参考文献

  • 中室 牧子, 津川 友介『「原因と結果」の経済学―――データから真実を見抜く思考法』, ダイヤモンド社, 2017
  • 岡本 厚『岩波データサイエンス Vol.3 特集 因果推論― 実世界のデータから因果を読む』, 岩波書店, 2017
  • 星野 崇宏『確率と情報の科学 調査観察データの統計科学 因果推論・選択バイアス・データ融合』, 岩波書店, 2017


Twitter・Facebookで定期的に情報発信しています!

本記事では以下のような使い方ができるチャットボットを紹介します。
スクリーンショット 2019-12-24 16.53.21.png


2019年に施行された働き方改革関連法により、残業時間の削減や多様な働き方が認められました。
今後も多くの人にとって働きやすい環境に変化し、さらに多様な働き方になることが予想されます。
そのため、現在は『業務時間を減少させつつ、利益は向上させる』生産性向上が求められています。
生産性向上のために業務を効率化し、無駄な業務時間をなくし、より利益を創出する活動に業務時間を充てることが重要です。


機械学習・深層学習はこのような作業を得意とします。

スクリーンショット 2019-12-24 16.34.51.png


チャットボットでできる業務効率化

今回は特に業務効率化に役に立つ『チャットボットでお客様問い合わせ対応事例』をご紹介します。
チャットボットは同じ作業を早くくり返すことを得意としていて、質問内容とその回答を事前に学習させておくことで、まるで人と会話しているかのように、ユーザーが入力した文言に対して適切な回答を返します。
スクリーンショット 2019-12-24 16.38.07.png


例えば、こんなシーンはありませんか?
ーーーーーーーー
お客様から電話やメールで問い合わせが多く、対応が追いついていない。
膨大な問い合わせ対応業務により本来おこなうべきのコアな業務にも影響が出ている。業務時間が増えるだけではなく、回答が滞ることによりお客様にも迷惑がかかり、機会損失にもなりかねない状態。
ーーーーーーーー


特定の製品についての金額の質問など、問い合わせの多くを似た内容が占めていませんか?そうであれば、機械学習・深層学習の得意な業務です。


お客様問い合わせ業務をチャットボットで実現!

お客様への回答を自動返答することができれば、問い合わせ担当者の業務負担は大きく軽減します。それだけではなく、お客様をお待たせすることなく、必要なタイミングで情報を伝えることができます。
そうすることで、担当者はよりコアな業務に集中できるようになり、売上向上や社内環境改善などのさらなる利益にもつながるでしょう。


また、チャットボットへの問い合わせは、資料請求やメールでの問い合わせのように、個人情報を入力する必要はありません。
そのため、ユーザーは気軽に質問ができ、さらには営業時間外でもチャットボットが質問に答えてくれます。
このようなユーザーにとって快適な体験で購入前にも顧客の満足度が高まるだけではなく、顧客との接点が増えることで信頼性の向上や商品・サービスの認知度アップにもつながります。


さらに、多くのチャットボットはコミュニケーション内容をデータとして残すことができますので、幅広いユーザーのニーズを把握することも可能です。
このようなユーザーのニーズからチャットボットの回答やサイトの情報を改善したり、商品・サービスの改善につなげたりすることも可能になるのです。

その他活用例

問い合わせ対応だけではなく、このような使い方もできます。

スクリーンショット 2019-12-24 16.45.06.png

社内ヘルプデスク

問い合わせがあるのは、お客様からだけではありません。社内から情報システム部や総務部への問い合わせは毎日発生しています。
日々の問い合わせは軽微で同じようなものが多く、チャットボットで問い合わせから回答までを完結させてしまうことができます。
問い合わせをする側も気軽に利用でき、確認不足によるミスを減らすことも出来るでしょう。


「なぜ?」原因の特定

上記2つの事例からチャットボットは、相手と会話するようにシステムとやり取りできるサービスです。ここの『会話』に着目し、原因の特定に活用することもできます。
例えば、ミスが発生した際の原因特定。
報告書を提出し、上司との会話で原因を発見し、今後の対策を決めていきます。
この会話をチャットボットでおこなうことで、スピード感のある原因の特定がおこなえます。また、(本来あってはならないのですが、)当事者は上司ではなく、システムと会話するわけですから、より素直な回答をすることができ、正確な特定がおこなえるでしょう。


ユーザーへのレコメンド

ユーザーが好みや生活習慣を入力することで、ユーザーに適切な商品をおすすめすることができます。
例えば、化粧品は人の好みが大きく分かれるだけではなく、流行りの移り変わりも大きく、化粧品購入の際は、ユーザーは悩み、今までと同じものを購入することで妥協することも少なくはありません。
チャットボットを活用すると簡単に、どこでも、自分にピッタリの化粧品を見つけることができます。


おわりに

チャットボットは業務効率化を進めることはもちろん、幅広い活用方法があり、お客様ごとにぴったりな活用方法をご提案いたします。


当社ではみなさまの課題やお困り事を一緒に解決していきます。
下記にご興味がありましたら、問い合わせ口からご連絡ください。

  • チャットボットについてもっと詳しく知りたい
  • 困っていることを相談したい
  • 機械学習・深層学習は他になにが出来るのか興味ある


アクセルユニバースのご紹介

私達はビジョンに『社会生活を豊かにさせるサービスを提供する。』ことを掲げ、このビジョンを通して世界を笑顔にしようと機械学習・深層学習を提案しています。
例えば、現在人がおこなっている作業をシステムが代わることで、人はより生産性の高い業務に携わることができます。


Twitter・Facebookで定期的に情報発信しています!

アクセルユニバースの栗木です。
今回実務の練習として、同じインターン生の入江君と共に模擬案件に取り組みました。
模擬案件とは、機械学習を用いることで実生活におけるどのような問題を解決できるかを考え、課題設定からデータの準備、前処理、学習、検証までの一連の作業を経験するための問題です。
今回の取り組みの中で僕達が取り組んた内容や理論を各ステップ毎に紹介していきたいと思います。記事は2部構成として、本記事では前半部分を記します。


Step. 1 課題設定

今回は無人レジの実現に挑戦します。
僕は並ぶことが苦手なので、どうにかレジの混雑を解消できないか考えました。
レジ処理は「いらっしゃいませー」「レジに商品を登録」「支払い」「ありがとうございましたー」の流れですよね。ここの「レジに商品を登録」をどうにかして効率化できれば、レジの混雑を解消でき、普及すれば、僕みたいな並びたくない族もたくさん買い物に行くはずです。


今回の目標は画像検出を用いてレジを打つことなくカメラに映すだけで合計金額を計算することとします。
画像を利用する価値を高めるためにタグが付けられない商品(食べ物)で、誰もが大好きなドーナツを対象とします。特にまだ無人化が進んでいないドーナツ屋さんの商品をトレーに載せた状態を想定しました。検出が成功すれば金額だけでなくカロリーなど+αの情報も表示が可能になりますね。


まずはじめにPart1ではひとつひとつのドーナツが何であるかを判定しました。先に結果だけお伝えすると、現時点で正答率が99%を超えていて今後の検討も楽しみです。


実際にシステムを作っていきます。まずは学習データを用意していきましょう。

Step. 2 学習データの準備

早速7種類のドーナツを購入し撮影を行いました。
角度や裏表を変えつつそれぞれ100枚の写真を撮りました。以下に写真の一例を示しておきます。
ちなみに撮影後においしく頂きました。^^

Donuts また、それぞれのドーナツの名前(ラベル)は以下とします。
8枚目の写真は物体検出する際にアノテーションされたデータが必要となるのでそのためのものです。アノテーションに関する詳細は後半の記事で説明したいと思います。


1.エンゼルフレンチ
2.ダブルチョコレート
3.エンゼルクリーム
4.ポン・デ・リング
5.オールドファッションハニー
6.ストロベリーカスタードフレンチ
7.チョコファッション
8.複数ドーナツ(物体検出用)

Step. 3 画像データの読み込み

学習のための画像データが準備出来たので、学習に備えてデータの読み込みとサイズを統一するためリサイズを行います。
今回はGoogle Colabratoryを使用するため、Google Driveに先程の画像をアップロードし以下のようにマウントすることでGoogle Colabratoryからデータにアクセス出来るようにします。
Google DriveにはDonuts Filesディレクトリを作成し、そこに先程の画像のうち複数ドーナツ以外の7種類の画像をディレクトリ毎にまとめてあります。(複数ドーナツの画像は後半で扱います)


from google.colab import drive  
drive.mount('/content/drive')

次にDonuts Files内の画像をディレクトリごとに読み込みリサイズを行います。

#必要なライブラリのimport
import os
import numpy as np
import glob
import cv2
from keras.utils import np_utils

IMAGE_SIZE = 100 #リサイズ後のサイズ指定

# Donuts Filesディレクトリから画像の読み込み及びリサイズ
def get_data_and_label(path):
  X = []
  Y = []
  label = {}
  i = 0
  for dir_name in os.listdir(path):
      label[dir_name] = i
      for file_path in glob.glob(path+'/'+dir_name+'/'+'*'):
        img = cv2.imread(file_path)
        img = cv2.resize(img,(IMAGE_SIZE,IMAGE_SIZE)) #100*100にリサイズ
        X.append(img)
        Y.append(i)
      i += 1
  X = np.array(X)
  Y = np.array(Y)
  X = X.astype('float32')
  X = X / 255.0 #RGBの値を正規化
  Y = np_utils.to_categorical(Y, len(label)) #One-hotベクトルの作成
  return X, Y, label

X, Y, label = get_data_and_label('drive/My Drive/Donuts Files')

これでデータの読み込みとリサイズは完了です。最後に精度の評価用にデータの分割を行います。

from sklearn.model_selection import train_test_split

# 学習データのうち20%を検証データにする
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.20)

データの準備が出来たので、実際にモデルの学習と精度の検証を行いましょう。
最終目標は物体検出を行うことですが、前半である本記事ではまず1枚の画像に対して1つのラベルの予測を行う画像認識を実装しどの程度分類出来るか確認します。

Step. 4 モデル構築&学習

今回の問題は画像認識なので、モデルはCNN(畳み込みニューラルネットワーク)を使用します。
CNNの詳細については多くの方が記事にされているのでそちらを参考にして下さい。
簡単に説明すると、CNNは畳み込み層、プーリング層、全結合層から成り立っており、まず畳み込み層では複数のフィルタを使用して画像を畳み込むことでエッジやパターンなどの特徴を抽出します。
次にプーリング層でデータサイズを減らしつつ平行移動などの処理に対するロバスト性を獲得し、最後に通常のディープラーニングと同様の全結合層でこれまでに抽出した特徴とラベルの関係性を学習します。
CNNは2012年の画像認識コンペ(ILSVRC)で圧倒的な成績を残して注目されて以来、急速に発展を続けているので是非色々と調べてみて下さい。
それではKerasを用いてモデル構築を行いましょう。KerasはバックエンドでTensorFlowを利用するディープラーニングライブラリラッパーです。

#必要なライブラリのimport
import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.pooling import MaxPool2D
from keras.optimizers import RMSprop  #最適化手法としてRMSpropを使用

# モデルの定義
model = Sequential()

model.add(Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)))
model.add(MaxPool2D((2, 2), padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D((2, 2), padding='same'))
model.add(Conv2D(128, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D((2, 2), padding='same'))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dense(len(label), activation='softmax', name='predicton'))

Kerasではモデルの構造をmodel.summary()から以下のように簡単に確認できるので非常に便利ですね。

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 100, 100, 32)      896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 50, 50, 32)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 50, 50, 64)        18496     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 25, 25, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 25, 25, 128)       73856     
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 13, 13, 128)       0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 21632)             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                1384512   
_________________________________________________________________
predicton (Dense)            (None, 7)                 455       
=================================================================
Total params: 1,478,215
Trainable params: 1,478,215
Non-trainable params: 0
_________________________________________________________________

それでは最後にモデルの学習と評価を行います。

# モデルのコンパイル
model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])

#パラメータの指定
batch_size = 64
epochs = 20

# 学習
history = model.fit(X_train, y_train, epochs=epochs, batch_size=batch_size)

# 検証データによるモデルの評価
print(model.evaluate(X_test, y_test))

最終的なlossと正解率は以下のようになりました。

loss = 0.02750563397655182
accuracy = 0.9927007299270073

正答率が99%を超えており非常に高いスコアとなりました。
今回はかなり問題設定が単純だったことと、本来は同じ種類であっても多くのドーナツを用意すべきところを1つのドーナツから学習データを作成したことがこのスコアにつながったと考えられます。

終わりに

ドーナツ検出に向けて前半である本記事では、目的を設定し画像を収集するところから単純な画像認識を行うところまでご紹介しました。
最近は画像認識を簡単に実装出来るので、是非身近なものなどの分類に挑戦してみて下さい。


さてドーナツの分類は高精度で行えることが確認できたので、後半はいよいよレジの無人化という課題に向けて検出に挑戦しようと思います。
後半では物体検出のアルゴリズムの概要や検出の様子を紹介したいと考えているので、後半もよろしくお願いします!


アクセルユニバース株式会社(以下当社)では、人が対応している業務を画像認識、音声認識、文字認識等を活用して効率化する機械学習ソリューション開発をおこなっています。
今回挑戦している無人レジだけではなく、人と会話するかのようにシステムに問合せができるチャットボットや、カメラの前の人が誰であるか判別できる認証システムも検討しております。

  • 機械学習でどんな事ができるのか興味がある
  • 課題と感じている業務がある
  • 効率化したい業務がある


上記のような方はぜひ問い合わせページにご連絡ください。


Twitter・Facebookで定期的に情報発信しています!

本記事では、スタッキングの仕組みについて解説します。

この記事を読めば、スタッキングの理解、実装が円滑になると思います。

この記事の対象者

スタッキングの仕組みが分からないけど実装してみたい人

記事の目的

スタッキングのシンプルな仕組みを知り、実装しやすくする。
どういうときにスタッキングが有効なのか、どのようなモデルを組み合わせればよいのかを知る。

※コード実装部分は書いていません!

スタッキングとは

スタッキングとはアンサンブルの手法の一つであり、モデルを積み上げていく方法です。

・クロスバリデーションを用いる
・目的変数の予測結果を特徴量として用いる
・アンサンブル手法でもあり特徴エンジニアリング手法でもある

といった特徴があり、trainデータとtestデータの分布が似ているとき、特に良い精度が出せます。

スタッキングの手順解説

以下がスタッキングの手順です。

  1. クロスバリデーションでtrainデータとtestデータの目的変数を予測する
  2. 1の予測をそれぞれの特徴量に追加する
  3. 1,2を色々なモデルで行う
  4. 1~2で追加した特徴量も含めて、testデータの目的変数の予測を行う

それでは手順について細かく見ていきましょう。

1. クロスバリデーションでtrainデータとtestデータの目的変数を予測する

クロスバリデーションでtrainデータとtestデータの目的変数をそれぞれ予測します。

①,trainデータを分割(fold1~4)し、分割の内の1つ(青の丸部分)を、それ以外の残りのデータ(オレンジの丸部分)を用いて予測する
②,オレンジ丸部分を用いてtestデータの目的変数(青の長丸)を予測する。
③,①と②を全ての組み合わせで行う

下の図は、①〜③を図にしたもので、クロスバリデーションのやり方です。
※trainデータの方ではtrainデータの目的変数の一部(分割の少数側)を予測するのに対し、testデータの方ではtestデータの目的変数の全体を予測していることに注意してください。
※trainデータの説明変数をtrainX、目的変数をtrainY。testデータの説明変数をtestX、目的変数をtestYとしています。

1枚目:fold2~4を用いて学習させたモデルでfold1のtrainYとtestデータ全体の目的変数を予測
スクリーンショット 2019-12-23 9.50.00.png

2枚目:fold1,3,4を用いて学習させたモデルでfold2のtrainYとtestデータ全体の目的変数を予測
スクリーンショット 2019-12-23 9.53.12.png

3枚目:fold1,2,4を用いて学習させたモデルでfold3のtrainYとtestデータ全体の目的変数を予測
スクリーンショット 2019-12-23 9.54.28.png

4枚目:fold1~3を用いて学習させたモデルでfold4のtrainYとtestデータ全体の目的変数を予測
スクリーンショット 2019-12-23 9.55.24.png

以上が全ての分割の組み合わせで各目的変数を予測するイメージです。
これでtrainデータとtestデータの目的変数の全体を予測することができました。(下図)

1枚目:クロスバリデーションでtrainデータの目的変数を予測したもの
スクリーンショット 2019-12-23 10.15.10.png2枚目:クロスバリデーションでtestデータの目的変数を予測し、4つの予測結果を平均します。
スクリーンショット 2019-12-23 10.15.19.png

2. 1で行った目的変数の予測結果をそれぞれの特徴量に追加する

見出しの通りですが、下図のように追加します。
スクリーンショット 2019-12-23 11.02.05.png

3. 1,2を色々なモデルで行う

1,2の作業、つまり、「クロスバリデーション→trainデータ、testデータの目的変数の予測→特徴量に追加」を色々なモデルで行いましょう。
複数のモデル(今回は3つ)で行ってみました。その結果、このような感じで特徴量が増えていきます。

スクリーンショット 2019-12-23 11.50.42.png

4. 1~3で追加した特徴量を含めて、testデータの目的変数の予測を行う

スクリーンショット 2019-12-23 11.50.03.png

スタッキングのコツ

ところで、
「どのようなモデルを組み合わせれば良いのだろう?」とか「最後の予測では全ての特徴量を含めたほうがいいの?」とか、疑問に思いませんでしたか?自分が社内のインターン生で勉強会を行った時、これらのような質問が飛んできました。

Q,どのモデルを組み合わせれば良いのですか?

A,ごめんなさいわかりません!後日調べます!

ということで、Kaggle本や様々なネット記事を検索して調べてみました。
結局、確立した方法はみつかりませんでした。色々な組み合わせを試してみて、精度の上がったものを選択するようです。
ただ、スタッキングはアンサンブル手法ですので、汎化性能があがるようにモデルを組み合わせるのが良いです。
例えば、相関の近いモデルどうしを組み合わせても汎化性能は上がりません。モデル同士の相関を調べてみて、組み合わせて実験するのが良いのではないでしょうか。

次の質問です。

Q,最後の予測では元々合った特徴量含めるべき?

A,場合によるのではないでしょうか...?(わかってない)

ということで、同じように調べて考えてみました。

・元々の特徴量を加えた場合
過学習しやすい。同じデータの使われる回数が増え過学習しやすくなります。
その代わり、元々合った特徴量と予測値の関係性を分析することができます。

・加えなかった場合
過学習しづらい。学習時間が短く済む。アンサンブルの目的として、汎化性能を高めることがあるので過学習しづらい点は評価できます。

加えた場合も加えなかった場合も一長一短あるようなので、時間があればどちらも試すのが良いのではないでしょうか。

最後の質問です。

Q,どういうときにスタッキングは使えるの?

A, trainデータとtestデータの分布が似ていれば精度が上がりやすいです。

なので、時系列データの場合は分布が異なる場合が多いので、注意が必要です。
分布が似ているかどうかは、"Adversarial Validation"を使えば判断できます。

まとめ

本記事では、スタッキングの仕組みを図を用いて簡潔に解説しました。

  1. クロスバリデーションでtrainデータとtestデータの目的変数を予測する
  2. 1の予測をそれぞれの特徴量に追加する
  3. 1,2を色々なモデルで行う
  4. 1~2で追加した特徴量も含めて、testデータの目的変数の予測を行う

以上の手順で実装することができました。

スタッキングの実装は、仕組みを知ってしまえば難しいことではないと思います。
ただし、スタッキングが良い影響をでるかどうか、どのモデルを混ぜるか、など扱うのが難しい手法です。

テクニカルな利用方法はKaggleのnotebookや技術本などで研究する必要がありそうです。

参考文献

門脇大輔・阪田隆司・保坂桂佑・平松雄司(2019)『Kaggleで勝つデータ分析の技術』 技術評論社

はじめに

 最近のAIは音声認識や翻訳においては高い精度を出すことができます。
例えばAIが英語から日本語に翻訳する時、私達日本人が読んで多少の違和感があったりもしますが英文がどんなことを言っているのか理解することはできます。  
もしも機械が言語を理解して、人間と違和感なく会話できたらどうでしょう。そのようなことが可能になると、機械がどのようにして言語を理解したかを見ることによって、人間には気づくことができなかった新たな言語に関する発見ができるかもしれません。
さらには私達が子供の頃、お母さんなどの周りの言葉を聞いて徐々に話すようになり言葉を理解し始めました。これがどのようなメカニズムで理解されているか発見できるかもしれません。また、極論を言えば人間が機械に言語理解を任せれば良いので人間は時間をかけて言語を勉強する必要もなくなるかもしれません。このブログでは機械が言語を理解するための学問に関して簡単に書きます。

目次

  • はじめに

  • 機械が言語を理解するためには

  • 自然言語処理とは

  • 自然言語処理でできること

  • まとめ

機械が言語を理解するためには

 機械が言語を理解するということは機械がただ翻訳できればよいというわけでわありません。機械が文から人間がどのようなことを伝えたいか理解するためにはどのような壁があるでしょう。

  • 人間が話したり書いたりした文章から意味を理解する

  • 意味を理解したら人間が必要としてる回答を見つける

  • 回答を見つけたら人間にわかるように伝える

などなど...

 上記で挙げたものは全て難しいです。このような壁を乗り越えるためにどうすればよいでしょう。そこで登場するのが自然言語処理です。

自然言語処理とは

 自然言語処理とは、先程箇条書きであげた壁を乗り越えるための学問で機械に言語を理解させることを目標にしています。自然言語というのは人工言語(人工的に作られた言語、プログラミング言語)と対比させた言い方で人間が普段使っている言語のことです。

Alt text

 簡単に自然言語処理の歴史を説明します。

 1940〜1960年ごろは黎明期と呼ばれ1946年に初めてコンピュータが誕生しました。当初の目的は、弾道計算や暗号解読などの軍事目的でした。このコンピュータが翻訳にも使えるのではないかとロックフェーラー財団のウィーバーが考えました。この考えをきっかけに米国内で機械翻訳への関心が高まり1952年ジョージタウン大学とIBMが共同で翻訳プロジェクトを立ち上げロシア語から英語への小規模な翻訳実験が始まりました。これが自然言語処理の始まりです。 

その後、米国はソ連の科学技術の実態を知るべくロシア語から英語の翻訳に関する研究に関して多額の研究予算を投入しました。これにより機械翻訳は進展していきました。この時代は、コンピュータの処理能力が十分でなかったことも関係し研究者が自由な発想で夢を膨らましていたそうです。
スクリーンショット 2019-12-20 18.35.10.png

 1960〜1990年頃は忍耐期と呼ばれ莫大な研究費をかけましたが、研究が進展するに連れ、問題の難しさが認識されるような状況になりました。
1966年には機械翻訳の現状と将来に関する調査結果をALPAC報告書と呼ばれる報告書にまとめ、そこには近い将来に機械翻訳をすることは困難であり、言語の理解を目指す基礎的な研究を行うべきである、という内容のものでした。この報告書を機に、米国では機械翻訳においてほとんど研究費が出なくなりました。
更にこの頃は、自然言語処理の研究の難しさが明確になり、チェスのような明確に定義された問題における探索などに興味の中心が移っていきました。

この頃の自然言語処理に関する重要な出来事といえば1967年のBrown Corpusの発表です。
これは米国の言語の使用を調査する目的で、電子化された文書として初の100万語規模のコーパス(簡単に言えばテキスト文書の集合にある種の情報を付与したものです)で、新聞、雑誌、書籍など様々なジャンルのテキストをバランスよく収集したものを発表しました。1970年頃からコンピュータの処理能力が向上し言語やテキストを扱う基本的な環境が整い出しました。しかし、機械翻訳に代表される知的処理についてはまだ実用には精度が足りない状況でした。
スクリーンショット 2019-12-20 18.54.35.png

 1990年頃から現在までは発展期と呼ばれ、この頃はインターネットが世界的に普及し、社会基盤になった時期です。Brown Corpusの経験に学び各地で数億語規模のコーパスが作られました。1993年にはBrown Corpusのテキストに解釈を与えたPenn Treebankが最初の論文として発表されました。これは今後の機械学習に基づく自然言語処理研究を牽引したデータになりました。機械翻訳においても1980年代後半から統計的機械翻訳の研究を行いました。しかし、当時のコンピュータの処理能力や対訳コーパスの不足により十分に研究は発展しませんでした。
ところが、1990年代後半あたりから計算環境や対訳コーパス環境が整い、米国が機械翻訳によって紛争地域の素早い情報収集をするなどの目的から再度、多額の研究費を出すようになったこともあり、2000年以降、機械翻訳研究の大きな進展が見られました。またこの頃は、音声自動翻訳システムいわゆる通訳システムの研究も始められました。日本では東京オリンピック・パラリンピックが開催される2020年を目標に実用システムの開発を目指しています。このような自然言語処理の発展を強く印象づける出来事として2011年、Watsonが米国の人気クイズ番組「Jeopardy!」で人間チャンピオンに勝利しました。Watsonは約3000個のCPUからなる並列コンピュータによって、自然言語による質問を理解し、ウィキペディアなどの大量の情報の中から適切な回答を選択するシステムでした。更に、自然言語処理に大きな技術革新として2015年頃から本格化し始めたニューラルネットワークです。ニューラルネットワーク自体は1940年代に提案されていましたが当時のマシンパワーでは計算力が足りず難しい状況でした。


2000年代に入り、マシンパワーの増大、ビックデータの利用、アルゴリズムの改良などから再び注目されました。2010年代に入り画像認識、音声認識などの様々なタスクで大きな精度向上が見られるようになりました。特に、ニューラルネットワークを用いた翻訳手法、ニューラル機械翻訳は大きな精度向上をもたらし、機械翻訳を一気に実用レベルの技術に押し上げました。
fabio-oyXis2kALVg-unsplash.jpg

 自然言語処理の現状としてDeepLearningの登場もあり、文章を単語単位に分割し単語がどのように並んでいるかパターンを捉えることや、似ている単語を把握する事ができます。これに関しては実用化もされていて有名なものだと新聞記事の単語の関係性などを機械が学習し記事の重要な部分を抜き出してまとめてくれる要約サービスがあります。しかし、機械が言語を理解することはDeepLearningを用いた最新モデルとされているBERT(バート)でさえ実現することはできませんでした。機械が言語を理解するにはまだまだ研究段階であり時間を必要としているのが現状です。

自然言語処理でできること

自然言語処理でできることを今回は3つ紹介します。

コールセンターの対応を自動化

コールセンターの業務はマニュアル通りに電話応対をし人間がマニュアルを参照し適切な回答を選択しています。マニュアルで対応できない質問に対しては後日回答や担当の者に電話をつなげるなどの対処が必要です。自然言語処理を用いれば今まで人がマニュアル通りに応対していたところを、機械で実現できます。音声認識により機械が質問を理解し、自然言語処理を用いて質問にあった適切な回答を選択します。

教育現場において生徒のSNSを分析しいじめ発見

SNS分析をする際も自然言語処理は有効です。自然言語処理の中の文書分類を用い、SNSに書き込まれた文章に対してこの文章はいじめに関係しそうな単語がどの程度含まれているか確率的に分析し、その確率が高いときはいじめに関係している文章とし、生徒のいじめ早期発見につなげます。

コンビニやスーパーでアンケートを自動で分類するシステム

コンビニやスーパー等においてアンケートを自動で分類する際もクラスタリングを用いることで可能です。大量のアンケートから項目ごとに似た回答をしているものをグループ化し、アンケートの分類を自動化する事が可能です。具体的な手法としてはK-means方というのがあり詳しくはこちらを参考にして下さい。

まとめ

最近の話題だと、東ロボくんというロボットが東大に入れるかというプロジェクトがあります。これは、機械が言語を理解できたら確実に合格できると言われています。あくまで想像ですが、入学後も常に成績トップで人間には到底かなわない存在になってしまうかもしれません。
それほど自然言語処理という学問はかなり難しいとされています。難しい分、研究が進み機械が言語を理解したら私達の常識が変わったり、人間の言語に対する理解が深まり様々な場面で応用できることが期待されており、私達はその難問に挑戦します。

参考文献

コンピュータで言葉を理解する言葉の意味を処理するとは?

人間と人工知能の違い

チャットボットAIの現状を解説!自然言語処理は今なにができるの?

自然言語処理〔改訂版〕 (放送大学教材) 黒橋禎夫著


Twitter・Facebookで定期的に情報発信しています!

AUCのAWS環境構築はEBがメイン

 こんにちは。社員の大田です。この秋就任したCTOに、うちの会社の諸々のAWS環境について少しだけ話を聞く機会がありました。

 僕は若手なので日々勉強をしながら、プロジェクトの隅っこで小さな仕事をしています。AWSに関してはちょうどこの冬あたりからSAAの勉強を始めました。大体のサービスのかんたんなことは知っているつもりですが、何しろ、インフラ周りは全体に影響がでる大切なところなので、これまであまり実環境に触れることはありませんでした。

 CTO曰く、うちの開発はEB(ElasticBeanstalk)をよく使うということでした。GIthubでコミット&マージしたものを、CircleCIを通して、EBで組んだ環境にデプロイするパターンが多いようです。

EB_diagram.png

 EB(ElasticBeanstalk)とはAWS上のインフラ構築を自動化してくれるサービスです。

Elastic Beanstalk では、アプリケーションを実行しているインフラストラクチャについて学習することなく、AWS クラウドでアプリケーションをすばやくデプロイし、管理できます。Elastic Beanstalk は、選択肢を狭めたり制御を制限したりすることなく、管理の複雑さを軽減します。アプリケーションをアップロードするだけで、Elastic Beanstalk が自動的に容量のプロビジョニング、負荷分散、拡張、およびアプリケーションの状態のモニタリングといった詳細を処理します。

https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/Welcome.html

 たとえば僕のような初心者でも、EBを使えばかんたんにアプリケーションのインフラ構築ができます。ALBを立てて、TargetGroupを作って、AutoScalingの設定をして、、といったことにいちいち頭を悩ます手間を省くことができます。

 また、デプロイも容易に行えるため、コミットから反映までのリードタイムをできるかぎり小さくできるという利点もあります。これは、スピーディにプロジェクトを進めるためには、とても大事なことです。

 もちろん、相当大規模で複数なシステムの場合は、細かな部分に慎重に配慮する必要があります。けれども、一人ひとりが複数のプロジェクトに関わりながら、スピード感をもって仕事をしていかなければならない以上、EBを用いたインフラ構築&管理がメインになるのは、当然のことなのだなと思いました。

 ただし、EBも万能なわけではありません。煩雑な作業を自動化してくれるとはいえ、一つ一つのリソースが裏で絡み合っている以上、何か問題があったときには、それらの細かなところに踏み入って対処しなければなりません。

 そんなときに無駄にハマってしまうことがないように、僕ももっと勉強をしなくちゃならないなと思います。年末年始はいつものように寝ていないで、ちょっとがんばりたいですね。

はじめに

 今回は世の中のトレンドを用いた株価予測をしてみたいと思います。今までに得た機械学習などの知識を実生活に用いてみたいと思っており、お金を稼ぐという意味で株価予測が候補にありました。以前、個人的にFXの値の上下予測を5分単位、1時間単位などで、テクニカル指標を用いて試してみたことがあったのですが、テクニカル指標だけでは期待した精度は出ませんでした。

 ということで、世の中のトレンドも特徴量として取り入れてみようと思い立ち、トレンドに左右されそうな株価で予測してみることにしました。

 よって、トレンドの影響具合を確認することをゴールとします。銘柄も絞ったほうがトレンドに対する影響が出やすいと考え、今回は任天堂を採用しました。任天堂を選んだ理由は特にないです。

結果

2015〜2019までの株価データを用い、過去6週のデータで次週の株価が上がるか下がるかの予測をし、

2015〜2018のデータで2019を予測:正解率60.4%

となりました!

取り組みの過程

以下で、取り組みの流れを説明します。

データを持ってくる

 皆さんは、Google Trendsというサービスをご存知でしょうか?トレンドを定量化する方法に悩んだ末たどり着いた方法がこれです。

 あるワードを入力すると、過去にGoogleでそのワードが検索された頻度(正規化済み)を示してくれます。CSVファイルとしてダウンロードもできるため、今回はこのサービスを活用しました。手軽さがこのサービスの良い点ですね。 カテゴリごとに分けることもできる(金融、ショッピングなど)ため、いくつかに分けてみました。

 最大で5年間のデータが持ってこれるようですが、1週間ごとになってしまいます。データ量が減ってしまうため、ここは少し残念です...

date

前処理など

以下の写真のように複数のカテゴリのデータも用います。 まずライブラリをインポートし、

 import pandas as pd
 import numpy as np
 import matplotlib.pyplot as plt
 import seaborn as sns
 %matplotlib inline

トレンドと、任天堂の株価のデータを一つにまとめます。

trend = pd.read_csv("multiTimeline_all.csv", header=1)
trend.columns = ["date", "value_all"]
trend["date"] = pd.to_datetime(trend["date"])

for cat in ["art_entertainment", "beauty", "business", "finance", "game", "law", "news", "shopping"]:
    cat_trend = pd.read_csv(f"multiTimeline_{cat}.csv", header=1)
    cat_trend.columns = ["date", f"value_{cat}"]
    cat_trend["date"] = pd.to_datetime(cat_trend["date"])
    trend = pd.merge(trend, cat_trend, on=["date"], how="left")

import pandas.tseries.offsets
trend["date"] = trend["date"] + offsets.Day(2)

import codecs
stock_price = pd.DataFrame()
for i in range(5):
    year = i + 2015
    with codecs.open(f"7974_{year}.csv", "r", "Shift-JIS", "ignore") as file:
        stock_price_each_year = pd.read_table(file, delimiter=",", header=1)
        stock_price = pd.concat([stock_price, stock_price_each_year], axis=0)

stock_price = stock_price[["日付", "出来高" , "終値調整値"]]
stock_price.columns = ["date", "volume", "close_price"]
stock_price["date"] = pd.to_datetime(stock_price["date"])

matrix = pd.merge(trend, stock_price, on=["date"], how="left")
matrix = matrix.dropna(subset=["volume"])

トレンドのデータは毎週日曜日の日付になっていたので、2日遅らせて火曜日としました(月曜日は休日で株価のデータがない場合が多いため)。 ここから1〜6週間前のトレンドを特徴量として作ります。

new_fe = []
for i in range(6):
    for col in ['value_all', 'value_art_entertainment', 'value_beauty',
       'value_business', 'value_finance', 'value_game', 'value_law',
       'value_news', 'value_shopping', 'volume', 'close_price']:
        matrix[f"{col}_{i+1}_weeks_ago"] = matrix[f"{col}"].shift(i+1)
        new_fe.append(f"{col}_{i+1}_weeks_ago")
matrix = matrix.drop([3, 4, 5, 6, 7])

そして、このnew_feの特徴量を予測に使います。

matrix["close_price_diff"] = matrix["close_price"] - matrix["close_price"].shift()
matrix["binary"] = matrix["close_price_diff"].apply(lambda x: 1 if x >= 0 else 0)

目的変数も作りました。

ちなみに、目的変数と特徴量(使わないもの含む)の相関の上位です。

date

モデルのトレーニングと予測

 モデルはLGBMを用いました。時系列データであるためLSTMも使ってみたかったのですが、データ量が少ないため断念しました。今回のLGBMのmetricはAUCです。

import lightgbm as lgb
from sklearn.metrics import accuracy_score

params = {'objective': 'binary',
          'max_depth': -1,
          'learning_rate': 0.01,
          "boosting_type": "gbdt",
          "bagging_seed": 44,
          "metric": "auc",
          "verbosity": -1,
          "random_state": 17
}
X = matrix[new_fe]
y = matrix["binary"]

columns = X.columns
y_oof = np.zeros(X.shape[0])
score = 0

feature_importances = pd.DataFrame()
feature_importances['feature'] = columns

X_train, X_valid = X[:X.shape[0]//5*4], X[X.shape[0]//5*4:]
y_train, y_valid = y[:X.shape[0]//5*4], y[X.shape[0]//5*4:]

dtrain = lgb.Dataset(X_train, label=y_train)
dvalid = lgb.Dataset(X_valid, label=y_valid)

clf = lgb.train(params, dtrain, 3000, valid_sets = [dtrain, dvalid], verbose_eval=100, early_stopping_rounds=400)

feature_importances[f'fold_{fold_n + 1}'] = clf.feature_importance(importance_type="gain")

def my_binary(x):
    if x >= 0:
        return 1
    else:
        return 0

y_pred_valid = clf.predict(X_valid)
y_oof[valid_index] = list(map(my_binary, y_pred_valid))


print(f"AC: {(accuracy_score(y_valid, list(map(my_binary, y_pred_valid))))}")

del X_train, X_valid, y_train, y_valid

AUCと正解率は以下です。

date

特徴量重要度の上位は以下のようになりました。

スクリーンショット 2019-12-13 15.35.23.png

また、この予測した通りに取引した場合の結果も計算しました。

def my_profit(diff, ans, pred):
    if ans == pred:
        return abs(diff)
    else:
        return abs(diff) * -1
sum(matrix.iloc[int(X.shape[0]/20):, :][["close_price_diff", "binary", "pred"]].apply(lambda x: my_profit(x[0], x[1], x[2]), axis=1).values)

結果は以下です。

date

つまり、毎回100株の取引をすると30万円の儲けです!これはなかなか嬉しいですね

結果の考察

 データが240と少ないため、断定はできませんが、トレンドと株価には何かしらの関係があるのではないかと考えられます。

 特徴量重要度をみると、「取引量」のほか、「アート、エンターテイメント」、「金融」、「美容、フィットネス」などのカテゴリのトレンドが重要であったことがわかります。また、6週間前の値が特徴量として効いていたりもするようで、非常に興味深いです。少し追加で調べていきます。

 まず、Google Trendsから得られるのはあくまで検索数の上下であり、それは任天堂のプラスなトレンドもマイナスなトレンドも同時に反映しています。にも関わらず、なぜ上下を60%で予測できたのかについて考えました。僕はこれについて、カテゴリごとにトレンドを分けた特徴量を加えたからではないかという考えに至りました。上に記載した特徴量重要度をみてみると、value_all、すなわち全体の検索数の特徴量があまり予測に寄与していないことがわかります。

date

これはカテゴリごとの時間の変化での検索数(正規化済み)の上下の1部のグラフですが、カテゴリによって動きが大きく違います。

 つまり、任天堂にとってプラスなトレンドを表すカテゴリ、逆にマイナスを表すカテゴリがあるのではないでしょうか。例えば、アート、エンターテイメントの検索はマイナスよりプラスな意味での検索をする人が多く、法律の検索は告訴した、告訴されたなどのマイナスな検索が多そうです。

date

上の写真は法律カテゴリの検索数ですが、2018年1月付近に急増加しています。1月10日はコロプラが、12月22日付で特許権侵害に関する訴訟を任天堂より提起され、1月9日に訴状内容を確認したことを発表した日です。

まとめ

 トレンドの特徴量だけでここまでの精度が出るとは思っていませんでした。トレンドの測り方はGoogle Trends 以外にもツイッターなどのSNSがあるため、これらを増やしていくことで長期間での株価の予測は可能なのではないでしょうか。株価は様々なものに影響を受けるものであるため、どんな特徴量を足していくか?が重要だと思われます。


Twitter・Facebookで定期的に情報発信しています!

はじめに

今回はU-netの番外編でskip-conectionについてまとめようと思います。 前回のtensorflowで学習をさせてみた際に、skip-conectionがない場合、学習が遅いだけでなく精度が悪いことがわかりました。 skip-conectionが(おそらく)初めて使われたのが2015年12月に発表されたResNetで、ImageNet2015の分類コンペ(ILSVRC 2015)で1位になりました。


目次

  • ResNet以前
  • skip-conection
  • skip-conectionの効果
  • なぜskip-conectionが効くのか


1.ResNet以前

深層学習において層の数はとても重要な要素であり、ImageNetにおいてResNetが発表される以前は16~30層ほどの比較的深いモデルが主流でした。 そこで「層を深くすればするほど、精度は良くなるのか」という疑問が生まれますが、そう上手くはいきません。 なぜなら層を深くすることにより勾配消失問題が起きてしまうからです。 この勾配消失問題に対してBatch Normalizationが考案され、勾配消失が起きづらくなりました。

しかし、層を深くしていく中である深さで精度が頭打ちになり、更に層を増やすことで精度が著しく悪くなってしまいました。

Alt text

上の図を見ると18層のモデルに層を追加し34層にしても精度がほとんど変わらないどころか、むしろ悪くなっていることがわかります。


2.skip-conection

ResNetの著者は、上の図に現れている劣化現象は勾配消失によるものではなく重みの最適化に問題があると考えました。つまり層を深くしても最適化できず、層を深くした恩恵を受けられないと考えました。 そこで考案されたのがskip-conectionです。

Alt text

xを入力として、xに重みをかける(weighted layerを通る)関数をF(x)とします。 F(x) + xを最終的な出力としています。

skip-conectionを34層のモデルで行った様子は以下のようになります。 左が通常のモデル、右がskip-conectionありのモデル

Alt text


3.skip-conectionの効果

通常のモデルにskip-conectionを用いたものをResNetと呼ぶことにします。 上と同様にImageNetで精度を計算すると以下のようになりました。

Alt text 層が深いモデルの方が精度が良いことがわかります。 さらに重要なことに34層のモデルの学習時の精度(赤の細線)が、18層のモデルの学習時の精度(青の細線)よりもかなり良くなっていることがわかります。 これは劣化現象を上手く解決し、より深く学習できるようになったことを示しています。 (層を深くすることによる恩恵を受けられた。)


4.なぜskip-conectionが効くのか

Alt text xを入力としてH(x)を目標とする関数とします。 xに重みをかける(weighted layerを通る)関数をF(x)とします。 F(x) + xを最終的な出力とし、これを目標の関数H(x)に近づけます。

F(x) + x = H(x) の時(目標の関数に等しくなれた時) F(x) = H(x) - xとなることから、Fは入力xと目標とする関数H(x)の残差(residual)と等しいことがわかります。

つまりFそのものをHに近くなるように学習するのではなく、Hとxの残差を正確に予測できるように学習しています。

このようにするとHが恒等関数に近い時、FそのものをHに近づけるのは難しいですが、F+xを近づけることは簡単になります。 論文の筆者も劣化問題は目標の関数Hが恒等関数の時、重みに対して非線形である関数FでHに近づくことが難しいために起きていると主張しています。


まとめ

U-netでskip-conectionは畳み込みにより失われる物体の位置情報を保持する役目があり、skip-conectionが使われ始めた当初とは少しだけニュアンスが違うように感じました。 残差を予測するという考えは以前からあるようで、代表的なものだとGBDTが挙げられます。 こちらの記事も是非ご覧ください。

GBDTを小学生レベルの数学で直感的に理解する


参考

https://arxiv.org/abs/1512.03385


Twitter・Facebookで定期的に情報発信しています!

2019年11月24日に行われた統計検定1級で、統計数理と統計応用(理工学)の両分野で合格をいただけました。このブログには僕がその合格までに行ったこととその振り返りを書き連ねておこうと思います。

admission_card

suri

ouyo

もくじ


統計検定1級を受けようと思った理由

僕がこの統計検定1級を受けようと思った理由は僕の興味対象となる研究分野が確率統計だったからです。現在僕は大学の学部3年で、4年で配属される研究室が主に統計の分野を研究しており一度基礎固めをしておきたかったと考えました。また、大学の授業で学んだ確率統計の知識を何かしらの形でアウトプットしておきたいと考え、その1つとしてこの資格を選びました。

申し込む前の学習状況

申込みを行う前に、既に大学で確率統計の授業を履修していました。レベルとしては統計検定2級ぐらいの内容+αを学んでいました。しかし、「この確率分布の名前は聞いたことあるけど、どんな形の密度関数だっけ」とか「この統計量に対しては何の検定をすればいいんだっけ」とか、かなり知識としては曖昧でした。実際の学習も基礎の復習からはじめました。
確率統計以外の分野では、線形代数や微積分の基礎数学についても一通り大学で学んでいました。特に積分計算は試験に必要だと思います。その他の基礎数学は学習して詰まったときに、その都度調べて思い出せるぐらいで大丈夫だと思います。(ある程度慣れておく必要はあると思います)

申し込み

統計検定を受けようと思ったのは8月頃で、実際に申し込んだのは9月の中旬です。申し込み時点では本格的に学習はしておらず、「そろそろ始めなきゃな...」ぐらいの気持ちでした。
統計検定1級は他の級とは少し異なり、「統計数理」と「統計応用」の2つの試験に分かれています。基本的には午前に統計数理、午後に統計応用(片方だけ受験することも可能)を受験することになるのですが、統計応用は人文科学・社会科学・理工学・医薬生物学の4分野から1分野を申し込みの時点で選択して受験します。僕は統計数理で学習した内容がそのまま応用できる問題が多いとされる理工学の分野を選択しました。このときは「勉強する量が少なく済んでラッキー」ぐらいの気持ちでしたが、この選択によって後で痛い目に遭います...

本番までに行った学習

申し込んでから試験日までは日本統計学会が出版している公式のテキストと過去問題集を使って学習を進めていきました。また、公式のテキストは出題される内容が網羅されていますが、詳細な証明や練習問題があまりなかったので、それを補う形で数理統計の教科書を一緒に読み進めました。基本的には公式のテキストを1から読み進めていき、必要に応じて教科書で証明や練習問題を補っていきました。学習がある程度進んだ段階で過去問題を大問ごとに解きました。過去問題でわからなかった部分をもう一度公式のテキストや教科書に戻って復習するというサイクルで勉強を進めました。
統計数理の具体的な学習内容としては、それぞれの確率分布に対して期待値・分散・モーメント母関数はすべて一度自分の手で導出しました。また、確率分布同士の関係性(極限をとることで二項分布→ポアソン分布、など)や確率変数の変換なども一通り目を通しました。これらは統計数理の問題を解く上でかなり重要だと思います。僕は学習のはじめにこれらを行い、ある程度分かった時点ですぐに過去問題に取り組んだことが理解を進めるのに役立ったと感じています。
統計応用の対策としてはあまり時間は取れませんでしたが、自分の得意なもの・興味のあるものを中心に学習しました。応用では様々な内容が出題されるのですべてを学習するのは難しいと思います。なのである程度自分の得意な分野を作って、それを得点源にしようと考えました。僕の場合は生存時間解析についてある程度知見があったので、その復習を行いました。時間が合えば他の分野も出来たと思いますが...
学習の総時間としてはだいたい40~50時間程度でした。過去問題も2018年~2016年までは統計数理と統計応用の両方を全問解きました。学習の進度としてはあまり学習時間が取れず、応用の内容すべてまでは手が回りませんでした。ある程度数理の内容でカバーできるとはいえ、これはかなりの悪手だったと今では反省しています...

試験本番

あまり準備ができていない状態で本番に臨みました...が、やるしかありません。統計数理と統計応用はそれぞれ5題出題されその中から3題を選ぶので、問題選択が重要になってきます。

統計数理

本番では統計的仮説検定はあまり自信がなかったので大問1,2,3を選びました。例年と比べて誘導が丁寧だったので解きやすかったと感じました。自分がした統計数理の対策がうまく効いたと思います。3の順序統計量の扱いには苦戦しましたが、どうにか7割ぐらいは取れたと確信しました。

統計応用(理工学)

問題の統計応用です。「なるべく数理の問題を解こう」......
ない。
いつもはある数理の問題が1問も見当たらない。例年と比べて大きく傾向が変わっています。いや、あるにはあるけど分散分析や適合度検定まで学習の手が回っていない!!死を確信しました。
とりあえずできそうなものから手をつけよう。1,2,5を選択しました。幸いにも生存時間解析はある程度やっていたので1で7割ぐらいは出来たと思います。しかし2,5は見たことのないトピック&苦手な分野なので、おそらく半分もできていないと思います...
そんな感じで午後にメンタルをやられた1日でした。

試験までを振り返って

そんな感じでドキドキな結果発表でしたが、驚いたことに数理と応用の2つで合格を頂けました。おそらく理工学は傾向が大きく変わり、全体の平均点が下がったからだと思います。僕の場合は得意な分野で得点を稼ぐことができたのでなんとか合格にこぎつけることができたのだと思います。
やはり試験合格までにはもう少し時間をかけて、応用的な分野まで一通り目を通して、傾向が変わっても得点を稼ぐ力を身につけることが重要だと感じました。合格したことにはしたのですが、逆にまだまだ勉強不足だと感じた試験でもありました。
一方で、統計数理の分野の基礎固めは試験合格のための必要条件だと思いました。これを突破しないことには数理と応用のどちらの分野でも合格は厳しいと思います。学習のはじめにここに注力したのは正解だったなと感じます。
以上が僕の統計検定1級の合格記です。正直運もありましたが、自分の確率統計の基礎固めとして良い経験となったかなと思います。この記事がこれから統計検定を受ける方々の参考になればと思います。


Twitter・Facebookで定期的に情報発信しています!

概要

DeepArtのようなアーティスティックな画像を作れるサービスをご存知でしょうか?
こういったサービスではディープラーニングが使われており、コンテンツ画像とスタイル画像を元に次のような画像の画風変換を行うことができます。この記事では画風変換の基礎となるGatysらの論文「Image Style Transfer Using Convolutional Neural Networks」[1]の解説と実装を行っていきます。

examples_of_style_transfer

引用元: Gatys et al. (2016)[1]


手法

モデルにはCNN(Convolutional Neural Network) が用いられており、VGG[2]という物体認識のための事前学習済みのモデルをベースとしています。


こちらの図はCNNが各層においてどのようにコンテンツ画像とスタイル画像を表現するか示しています。

image_representaion_on_each_layers_in_CNN

引用元: Gatys et al. (2016)[1]


Content Reconstructions (下段) のa, b, c を見ると入力画像がほぼ完璧に復元されていることがわかります。一方で d, e を見ると詳細な情報は失われているものの、物体認識をする際に重要な情報が抽出されていることがわかります。これは画像からコンテンツが抽出され、画風を表す情報が落とされていると考えられます。よって画風変換のモデルでは画像からコンテンツを捉えるためにCNNの深い層を利用します。
次にStyle Reconstructions (上段) を見てみましょう。a からe はそれまでの各層の特徴マップの相関をもとに復元されています。例えば c は第1層から第3層までの各層における特徴マップの相関から生成されています。こうすることで画像内のコンテンツの配置などによらない画風を抽出することができます[3]。


画風変換のモデルではコンテンツ画像とスタイル画像を入力として受け取り、上記のことを利用してスタイル画像の画風をコンテンツ画像に反映した画像を新たに生成します。


では具体的にモデルの中身を見ていきましょう。


モデルのアーキテクチャ

algorithm_of_style_transfer_model

引用元: Gatys et al. (2016)[1]


まずコンテンツ画像 (\(\vec{p}\)) とスタイル画像 (\(\vec{a}\)) から特徴マップが抽出されます。次にこれらの特徴マップと生成画像 (\(\vec{x}\))の特徴マップとの損失が計算されます。この計算で求められる損失をそれぞれコンテンツ損失、スタイル損失と呼ぶことにします。そしてコンテンツ損失とスタイル損失の合計が最終的な損失となり、これを最小化していきます。
ここで注意しなければならないことが1つあります。通常、ディープラーニングでは重みが最適化の対象になりますが、今回は重みは固定して生成画像のピクセルを最適化します。


コンテンツ損失

コンテンツ損失はコンテンツ画像と生成画像のVGGのある1層から出力された特徴マップの平均二乗誤差によって計算されます。

$$ \mathcal{L}_{content} (\vec{p}, \vec{x}, l) = \frac{1}{2} \sum_{i, j}^{} (F_{ij}^{l} - P_{ij}^{l})^2 $$ ここで\(F_{ij}^{l}\) は生成画像の\(l\)層における\(i\) 番目のフィルターの位置\(j\) でのアクティベーションを表しています。\(P_{ij}^{l}\)についても同様ですが、こちらはコンテンツ画像についてのアクティベーションを表しています。


スタイル損失

スタイル画像から画風を捉えるためにまず各層における特徴マップの相関を計算します。

$$ G_{ij}^{l} = \sum_{k}^{} F_{ik}^{l}F_{jk}^{l} $$ これはグラム行列と呼ばれ、\(l\) 層におけるフィルター間の特徴マップの相関をとっています。このグラム行列を用いることで画風を表現することができます[3]。 そして生成画像のグラム行列とスタイル画像のグラム行列の平均二乗誤差を求めます。 $$ E_{l} = \frac{1}{4N_{l}^{2}M_{l}^{2}} \sum_{i,j}^{} (G_{ij}^{l} - A_{ij}^{l})^2 $$ ここで\(N_{l}\)は特徴マップの数、\(M_{l}\)は特徴マップのサイズを表します。また\(G_{ij}^{l}\)、\(A_{ij}^{l}\) はそれぞれ\(l\) 層における生成画像のグラム行列とスタイル画像のグラム行列を表します。  最後に各層の損失の線形和をとってスタイル損失とします。 $$ \mathcal{L}_{style} (\vec{a}, \vec{x}) = \sum_{l=0}^{L} w_{l}E_{l} $$ このとき\(w_{l}\) は\(l\) 層の損失の重みを表します。


最適化

コンテンツ損失とスタイル損失から合計の損失を求めます。合計損失は\(\alpha\) と\(\beta\) をそれぞれコンテンツ損失とスタイル損失の重みとして $$ \mathcal{L}_{total}(\vec{p}, \vec{a}, \vec{x}) = \alpha\mathcal{L}_{content}(\vec{p}, \vec{x}) + \beta\mathcal{L}_{style}(\vec{a}, \vec{x}) $$ この損失を最小化する形で生成画像の最適化を行っていきます。ですので最適化には\(\frac{\partial L_{totla}}{\partial \vec{x}}\) を用いることになります。 論文中ではL-BFGSで最も良い結果になったと記述されていましたが、今回の実装ではAdam を使って最適化を行いました。 この最適化によって、コンテンツ画像をスタイル画像に合わせて画風変換した新たな画像が生成されます。


実装

実装にはTensorFlowを用いました。また実装に際してTensorFlowのチュートリアル[4]を参考にしました。


まずは必要なライブラリをインストールします。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
%matplotlib inline
mpl.rcParams['figure.figsize'] = (12,12)
mpl.rcParams['axes.grid'] = False
import time
import IPython.display as display
import PIL.Image

import tensorflow as tf
from keras.preprocessing import image


画像を読み込んで配列に変換する関数、テンソルを画像に変換する関数を定義します。

# 画像を読み込み配列に変換し正規化する
def load_image(input_path, size):
    image = tf.keras.preprocessing.image.load_img(input_path, target_size=size)
    image = tf.keras.preprocessing.image.img_to_array(image)
    image = np.expand_dims(image, axis=0)
    image /= 255
    return image


# テンソルを画像に戻す
def tensor_to_image(tensor):
    tensor = tensor.numpy()
    tensor *= 255
    tensor = np.array(tensor, dtype=np.uint8)
    if np.ndim(tensor)>3:
        assert tensor.shape[0] == 1
        tensor = tensor[0]
    return PIL.Image.fromarray(tensor)


このクラスは配列に変換された画像を受け取り、VGG19の各層からの出力を返します。この時点でスタイルの表現に使われる特徴マップはグラム行列に変換されます。

class StyleContentModel():
    def __init__(self):
        # VGG19のどの層の出力を使うか指定する
        self.style_layers = ['block1_conv1', 'block2_conv1', 'block3_conv1', 'block4_conv1', 'block5_conv1']
        self.content_layers = ['block5_conv2']

        self.num_style_layers = len(self.style_layers)        
        self.vgg = self.get_vgg_model()
        self.vgg.trainable = False

    def __call__(self, inputs):
        preprocessed_input = tf.keras.applications.vgg19.preprocess_input(inputs * 255)
        vgg_outputs = self.vgg(preprocessed_input)

        style_outputs, content_outputs = (vgg_outputs[:self.num_style_layers], vgg_outputs[self.num_style_layers:])
        style_outputs = [self.gram_matrix(style_output) for style_output in style_outputs]

        style_dict = {style_name:value for style_name, value in zip(self.style_layers, style_outputs)}
        content_dict = {content_name:value  for content_name, value in zip(self.content_layers, content_outputs)}

        return {'style':style_dict, 'content':content_dict}

    # Keras API を利用してVGG19を取得する  
    def get_vgg_model(self):
        vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet')
        vgg.trainable = False

        outputs = [vgg.get_layer(name).output for name in (self.style_layers + self.content_layers)]
        model = tf.keras.Model(vgg.input, outputs)

        return model

    # グラム行列を計算する
    def gram_matrix(self, input_tensor):
        result = tf.linalg.einsum('bijc,bijd->bcd', input_tensor, input_tensor)
        input_shape = tf.shape(input_tensor)
        num_locations = tf.cast(input_shape[1] * input_shape[2], tf.float32)
        return result / num_locations


そして合計損失を計算する関数、計算した損失から勾配を計算する関数を定義します。

# 合計損失を計算する
def compute_loss(model, base_image, style_targets, content_targets, style_weight, content_weight):
    model_outputs = model(base_image)
    style_outputs = model_outputs['style']
    content_outputs = model_outputs['content']

    style_loss = tf.add_n([tf.reduce_mean((style_outputs[name]-style_targets[name])**2) for name in style_outputs.keys()])
    style_loss *= style_weight / len(style_outputs)

    content_loss = tf.add_n([tf.reduce_mean((content_outputs[name]-content_targets[name])**2) for name in content_outputs.keys()])
    content_loss *= content_weight / len(content_outputs)

    loss = style_loss + content_loss
    return loss, style_loss, content_loss

# 損失を元に勾配を計算する
@tf.function()
def compute_grads(params):
    with tf.GradientTape() as tape:
        all_loss = compute_loss(**params)

    grads = tape.gradient(all_loss[0], params['base_image'])
    return grads, all_loss


最後に画像の生成を行う関数を定義していきます。
生成画像のベースとなる画像にはノイズ画像を指定しています。ベース画像にコンテンツ画像やスタイル画像を指定するとまた違った結果が得られます。

def run_style_transfer(style_path, content_path, num_iteration, style_weight, content_weight, display_interval):
    size = image.load_img(content_path).size[::-1]
    noise_image = np.random.uniform(-20, 20, (1, size[0], size[1], 3)).astype(np.float32) / 255
    content_image = load_image(content_path, size)
    style_image = load_image(style_path, size)

    model = StyleContentModel()
    style_targets = model(style_image)['style']
    content_targets = model(content_image)['content']

    # 生成画像のベースとしてノイズ画像を使う
    # ベースにはコンテンツ画像またはスタイル画像を用いることもできる
    base_image = tf.Variable(noise_image)

    opt = tf.optimizers.Adam(learning_rate=0.02, beta_1=0.99, epsilon=1e-1)

    params = {
        'model': model,
        'base_image': base_image,
        'style_targets': style_targets,
        'content_targets': content_targets,
        'style_weight': style_weight,
        'content_weight': content_weight
    }

    best_loss = float('inf')
    best_image = None

    start = time.time()
    for i in range(num_iteration):
        grads, all_loss = compute_grads(params)
        loss, style_loss, content_loss = all_loss

        opt.apply_gradients([(grads, base_image)])
        clipped_image = tf.clip_by_value(base_image, clip_value_min=0., clip_value_max=255.0)
        base_image.assign(clipped_image)

        # 損失が減らなくなったら最適化を終了する        
        if loss < best_loss:
            best_loss = loss
            best_image = base_image
        elif loss > best_loss:
            tensor_to_image(base_image).save('output_' + str(i+1) + '.jpg')
            break

        if (i + 1) % display_interval == 0:
            display.clear_output(wait=True)
            display.display(tensor_to_image(base_image))
            tensor_to_image(base_image).save('output_' + str(i+1) + '.jpg')
            print(f'Train step: {i+1}')
            print('Total loss: {:.4e}, Style loss: {:.4e}, Content loss: {:.4e}'.format(loss, style_loss, content_loss))

    print('Total time: {:.4f}s'.format(time.time() - start))
    display.clear_output(wait=True)
    display.display(tensor_to_image(base_image))

    return best_image


では実際に画風変換を行ってみましょう。styleweightとcontentweightはそれぞれスタイル損失とコンテンツ損失の重みを表します。

style_path = '../input/neural-image-transfer/StarryNight.jpg'
content_path = '../input/neural-image-transfer/FlindersStStation.jpg'
num_iteration = 5000
style_weight = 1e-2
content_weight = 1e4
display_interval = 100

best_image = run_style_transfer(style_path, content_path, num_iteration, style_weight, content_weight, display_interval)


結果

コンテンツ画像とスタイル画像はこれらの画像を使いました。


コンテンツ画像

Flinders_Street_Station_in_Melbourne

引用元: https://commons.wikimedia.org/wiki/File:Flinders_Street_Station_3.jpg


スタイル画像

The_Starry_Night_art_of_Gogh

引用元: https://commons.wikimedia.org/wiki/File:Van_Gogh_-_Starry_Night_-_Google_Art_Project.jpg


上記のコードを走らせて生成した画像がこちらになります。 スタイル損失とコンテンツ損失の重みを変えて4種類の画像を生成しました。

  • \(\alpha = 10, \beta = 10^{-2}\) generated_image_with_content_loss_weight_1e1_style_loss_weight_1e-2



  • \(\alpha = 10^{2}, \beta = 10^{-2}\) generated_image_with_content_loss_weight_1e2_style_loss_weight_1e-2



  • \(\alpha = 10^{3}, \beta = 10^{-2}\) generated_image_with_content_loss_weight_1e3_style_loss_weight_1e-2



  • \(\alpha = 10^{4}, \beta = 10^{-2}\) generated_image_with_content_loss_weight_1e4_style_loss_weight_1e-2



結果からわかるように \(\frac{\alpha}{\beta}\) が大きくなればなるほどコンテンツ画像がはっきりと生成画像に反映されていることがわかります。これは\(\frac{\alpha}{\beta}\)が大きいとコンテンツ損失に対してモデルが敏感になるからです。 逆に\(\alpha = 10, \beta = 10^{-2}\) の場合はスタイル損失に敏感になりすぎて、生成画像からコンテンツを見つけることができなくなっています。


まとめ

今回は画風変換の基礎となる論文の解説と実装を行いました。ベース画像やパラメーター、VGGのどの層の出力を使うかなどによって違った結果が得られるので色々といじってみるのも面白いかもしれません。またこの論文以降にも画風変換の研究が進められていますので、それらを試す助けになればと思います。


参考文献

[1] Image Style Transfer Using Convolutional Neural Networks
[2] Very Deep Convolutional Networks For Large-scale Image Recognition
[3] Texture Synthesis Using Convolutional Neural Networks
[4] TensorFlow Core: Neural style transfer


Twitter・Facebookで定期的に情報発信しています!

目次

  1. 海洋エネルギーとは?
  2. 海洋エネルギー発電の普及に向けた課題とその解決策
  3. 機械学習を用いた海洋エネルギー発電の課題への解決策
  4. これから私が取り組むこと
  5. 参考文献


1. 海洋エネルギーとは?

 海洋再生可能エネルギー(略して、海洋エネルギー)とは、洋上風力(図1)、波力(図2、3)、海流(図4)、潮流(図5)、潮汐、海洋温度差、塩分濃度差といった再生可能エネルギーを指す。地球表面の約70%は海で覆われているため、海洋エネルギーは非常に膨大で、決して枯渇することはないエネルギーである。我が国の200海里経済水域の面積は世界第6位であり、国民一人当たりの海域面積は主要先進国では最も大きい。このことからして、我が国が強力に開発すべき再生可能エネルギーは、海洋エネルギーである。

Ocean energy

図1 長崎県五島市の洋上風力発電装置「はえんかぜ」



Ocean energy

図2 Ocean Power Technologies社の波力発電装置



Ocean energy

図3 Wello Oy社の波力発電装置



Ocean energy

図4 IHIの海流発電装置



Ocean energy

図5 SIMEC Atlantis Energy社の潮流発電装置(これが海中に設置される)


 そもそも再生可能エネルギーとは、地球温暖化の原因である温室効果ガスを排出せず、環境にやさしく、枯渇する心配がないエネルギーのことである。地球温暖化が進行すると、

  • 高潮や沿岸部の洪水、海面上昇による被害
  • 大都市部への内水氾濫による被害
  • 極端な気象現象によるインフラ機能停止
  • 熱波による死亡や疾病
  • 気温上昇や干ばつによる食料不足や食料安全保障の欠如
  • 水資源不足と農業生産減少
  • 陸域や淡水の生態系、生物多様性がもたらす様々なサービスの損失
  • 海域の生態系、生物多様性への影響
    上記のような多くの問題が生じる。


 化石燃料(石油、天然ガス、石炭)による発電は、温室効果ガス(二酸化炭素など)を排出し、地球温暖化を進める発電方法である。また、資源に乏しい我が国は、エネルギー供給のうち、化石燃料が8割以上を占めており、そのほとんどを海外からの輸入に頼っている。特に東日本大震災後、エネルギー自給率は10%を下回っており、エネルギー安定供給の観点から、この改善を図っていく必要がある。


 再生可能エネルギーは国産のエネルギー源であるため、エネルギー自給率の改善にも寄与することができる。更に、東日本大震災による原発事故を受け、原子力発電所が東日本大震災並の地震に耐え得るには、設計の見直しが必要であり、今後安全対策費が高くなることが予想されるため、発電コストは今後も高くなる。また、放射性廃棄物の処理についてもまだ未解決の問題である。このことから再生可能エネルギーは今非常に注目を集めている。


 では、その再生可能エネルギー発電と言えば、太陽光発電や風力発電が現在主流であるが、これらに対抗できるほどのメリットが海洋エネルギー発電にあるのかと疑問を抱く方は多いかもしれない。太陽光発電は、夜間や雨・曇りの日などには発電できない。即ち、設備利用率(発電設備の実際の発電量が仮にフル稼働していた際の発電量の何パーセントほどであるのかを示す数値)が低い。それに対し、海洋エネルギー発電は、夜間でも、天候が多少悪くても稼働できるため、設備利用率は太陽光発電に比べて高い。


 また陸上の風力発電は土地や道路の制約があり、特に国土が狭く地形も複雑な日本では、風の条件が良い場所はどうしても限られ、景観や騒音の問題によって大型化できないのに対し、同じ風力でも洋上風力発電ではその心配はない。表1は再生可能エネルギーの設備利用率の比較である。海洋エネルギー発電には他の再生可能エネルギーより優れた点がある。

スクリーンショット 2019-12-12 12.32.39.png


2. 海洋エネルギー発電の普及に向けた課題とその解決策

 現在、海洋エネルギー発電は、まだ世界的にも研究開発もしくは実証研究の段階にあり、本格的な事業化に向けて、以下の課題を克服しなければならない。

スクリーンショット 2019-12-10 14.55.02.png
 まず、機械学習を活用しない解決策を課題毎に述べる。


<課題①「電力需要量とのバランスが取りにくい」>

  • 蓄電装置や電力の水素への転換などの蓄電方法の開発・改良を行う。



<課題②「発電効率が火力発電や原子力発電などに比べて低い」、課題⑥「発電コストが高い」>

  • 単機では発電効率が低く発電コストは高いが、数を増やすことで発電効率を向上させ、発電コストも下げる。(再生可能エネルギーは数が増えることで発電コストが急減する)

Ocean energy

図6 ドイツのブーテンディーク洋上風力発電所



<課題⑤「他の再生可能エネルギーと比べて実績が足りない」>

  • 水槽試験だけでなく長期的な実海域試験を単機だけでなく複数機で行う。
  • EMEC(欧州海洋エネルギーセンター)のような海洋エネルギーの実海域試験を行う研究所を我が国にも造る。

スクリーンショット 2019-12-10 17.26.12.png

図7 EMECの景観を配慮した波力発電テストサイトの変電施設(長崎海洋大使・海外先進地 派遣事業報告書)



<課題⑦「試験運転であっても国や地方自治体への計画・建設の許可が必要」>

  • 技術的課題、経済的課題、環境的課題をできる限り解決し、国や地方自治体の認可をもらえるようにする。



<課題⑧「航行や漁業などの海域利用者や地域の方々の理解が必要」>

  • 海域利用者と十分に協議し、周辺住民への情報の開示する。
  • 地域の活性化に繋がることを周辺住民へ伝える。
  • 発電機周辺には魚が集まってくるなど、漁業にもメリットがあることを伝える。

Ocean energy

図8 発電装置に付着した海藻に集まる魚



<課題⑨「辺環境や生物生態系への懸念」>

  • 実海域試験を行い、周辺環境や生物生態系への影響を調査し評価する。


3. 機械学習を用いた海洋エネルギー発電の課題への解決策

 海洋エネルギーの技術的課題を解決するためには当然、発電装置や蓄電装置、海底ケーブルなどの開発・改良を行う必要があるが、ここでは、太陽光発電や陸上風力発電で取り組まれている解決策を参考に私が考えた、機械学習(人工知能)を用いた解決策を紹介する。


<課題①「電力需要量とのバランスが取りにくい」、課題③「無駄な待機運転の時間がある」への解決策>

~発電量・電力需要量予測と制御~

 発電装置周辺の気象・海象データを説明変数として発電量を予測し、カレンダー条件(時間帯、曜日など)と気温や日射強度などの気象条件を説明変数として電力需要量を予測することで、需要と供給の差が確認できる。そして電力需要量の送電にかかる時間分先の予測値と同量を発電するように発電装置を制御すれば、需要と供給のバランスを保ちながら発電することができる。発電機の無駄な待機時間も削減され、設備稼働率も向上する。



<課題②「発電効率が火力発電や原子力発電などに比べて低い」への解決策>

~機械学習を用いた制御~

 例えば洋上風力発電や海流発電、潮流発電ならば、効率よく電力を取り出せる羽根の回転数が風速または流速によって異なるので、回転数を機械学習によって制御することで、発電効率を最大化することができる。
 また、これは私のアイデアではないが、波力発電装置の発電量の平均値が最大になるように機械学習(強化学習)を用いて制御するといった研究も行われている。



<課題③「無駄な待機運転の時間がある」、課題④「台風など局所的に厳しい海象条件下での安全性の懸念」への解決策>

~発電装置が遭遇する気象・海象を予測~

 発電装置が遭遇する気象や海象を事前に予測しておくことで、台風や大波が来る前に発電をやめ、安全を確保する状態に切り替えることができる。また台風や大波といった本当に稼働停止しなくては損傷してしまう場合以外は稼働することができ、無駄な待機時間が減少する。



<課題③「長いメンテナンスにより稼働時間が減る」、課題⑥「海上工事や潜水作業のコストが高い」への解決策>

~海中ロボットによるメンテナンス~

 メンテナンス時の潜水作業を人が行うのではなく、機械学習を搭載した海中ロボットが行う。 水中を自動運転したり、画像診断などによって腐食や付着物を検知するなど、メンテナンス作業を効率よく行い、発電装置の稼働時間を増やす。



<課題④「台風など局所的に厳しい海象条件下での安全性の懸念」への解決策>

~異常検知~

 発電装置に速度センサや加速度センサを設置して、発電機の運動を測定し、応力(単位面積当たりの力)センサを設置して、発電機の応力を測定することで、運動や応力のデータが得られ、それを説明変数として、発電機の異常検知をする。
異常を確認すると稼働停止するようにしておくことで、安全性が向上する。


4. これから私が取り組むこと

 私は何としてでも、海洋エネルギーを我が国で普及させたいと考えている。そのために今回海洋エネルギー発電の課題について取り上げてきた。そして3章で示したように、海洋エネルギーを普及させる方法の一つとして機械学習を用いる方法があり、私はこれに非常に興味を持ち、可能性を感じている。
 今後は、「機械学習を用いた海洋エネルギー発電の効率向上と制御」「機械学習を用いた発電装置などの運動する海洋構造物の安全性確保と評価」をテーマに研究を行っていく。具体的には3章で紹介した「機械学習を用いた制御」や「異常検知」について着目しようと考えている。また安全性の評価として機械学習を用いることができないかなど、海洋エネルギーに対する機械学習の新たな用途の探求も行っていく。
 このような私の研究によって、海洋エネルギー発電の課題が一つずつ解決して行き、海洋エネルギー発電が事業化することで、我が国だけでなく世界中のエネルギー問題が解決することを私は期待している。しかし、この研究を進めるためには海洋エネルギーに関するデータが必要であることは言うまでもない。


5. 参考文献


Twitter・Facebookで定期的に情報発信しています!

はじめに

前回の「画像セグメンテーションのためのU-net概要紹介」では画像のクラス分類のタスクを、画像のSegmentationのタスクにどう発展させるかを解説し、SegmentationのネットワークであるU-netの理論ついて簡単に解説しました。
今回はTensorFlowのSegmentationのチュートリアルを行いながら、実際にU-netを学習させてみたいと思います。
尚、本記事ではTensorflowの詳しい解説は行いません。
参考 : https://www.tensorflow.org/tutorials/images/segmentation

Segmentationとは

ある物体が画像内に含まれている時、画像のどこにあるのかを推定するタスクのことです。
言い換えると「画像のピクセルがそれぞれ何かを推定する」タスクのことです。
今回はOxford-IIIT Pet Datasetというデータセットを用いて学習を行い、ピクセルごとに以下のようなクラス分けを行います。

  • Class 0 : 動物のピクセル
  • Class 1 : 動物とその他の境界線のピクセル
  • Class 2 : その他のピクセル


目標は以下のような出力を得ることです。(左:入力画像 右:出力画像)
Alt text


ライブラリのインポート

import tensorflow as tf
import sys
from IPython.display import display
from IPython.display import HTML
from PIL import Image
# sys.modules['Image'] = Image 
from __future__ import absolute_import , division, print_function, unicode_literals
from tensorflow_examples.models.pix2pix import pix2pix

import tensorflow_datasets as tfds
tfds.disable_progress_bar()

from IPython.display import clear_output
import matplotlib.pyplot as plt


データの読み込み&可視化

dataset, info = tfds.load('oxford_iiit_pet:3.0.0', with_info=True)

def normalize(input_image,input_mask):
    input_image = tf.cast(input_image, tf.float32) / 255
    input_mask -= 1
    return input_image,input_mask

@tf.function
def load_image_train(datapoint):
    input_image = tf.image.resize(datapoint['image'],(128,128))
    input_mask = tf.image.resize(datapoint['segmentation_mask'],(128,128))

    if tf.random.uniform(()) > 0.5:
        input_image = tf.image.flip_left_right(input_image)
        input_mask = tf.image.flip_left_right(input_mask)
    input_image,input_mask = normalize(input_image, input_mask)
    return input_image, input_mask

def load_image_test(datapoint):
    input_image = tf.image.resize(datapoint['image'], (128, 128))
    input_mask = tf.image.resize(datapoint['segmentation_mask'], (128, 128))
    input_image, input_mask = normalize(input_image, input_mask)
    return input_image, input_mask

TRAIN_LENGTH = info.splits['train'].num_examples
BATCH_SIZE = 64
BUFFER_SIZE = 1000
STEPS_PER_EPOCH = TRAIN_LENGTH // BATCH_SIZE

train = dataset['train'].map(load_image_train, num_parallel_calls=tf.data.experimental.AUTOTUNE)
test = dataset['test'].map(load_image_test)

train_dataset = train.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
train_dataset = train_dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)
test_dataset = test.batch(BATCH_SIZE)

def display(display_list):
    plt.figure(figsize=(15, 15))

    title = ['Input Image', 'True Mask', 'Predicted Mask']

    for i in range(len(display_list)):
        plt.subplot(1, len(display_list), i+1)
        plt.title(title[i])
        plt.imshow(tf.keras.preprocessing.image.array_to_img(display_list[i]))
        plt.axis('off')
    plt.show()

for image, mask in train.take(88):
    sample_image, sample_mask = image, mask
display([sample_image, sample_mask])

Alt text


U-netの構築

U-netのencorder部分には今回のデータセットとは別のデータセットを学習したのMovileNetを用います。
MovileNetの重みは学習で更新しないように更新しておきます。
このようにすることU-netの精度を上げることができます。(転移学習)
decorder部分には未学習のpix2pixを用います。
pix2pixの重みは学習により更新されます。

OUTPUT_CHANNELS = 3

# encorder部分には学習済みのMovileNet
base_model = tf.keras.applications.MobileNetV2(input_shape=[128,128,3],include_top=False)
layer_names = [
    'block_1_expand_relu',
    'block_3_expand_relu',
    'block_6_expand_relu',
    'block_13_expand_relu',
    'block_16_project'
]
layers = [base_model.get_layer(name).output for name in layer_names]
down_stack = tf.keras.Model(inputs=base_model.input, outputs=layers)
# MovileNetの重みは固定
down_stack.trainable = False

# decorder部分にはpix2pixを用いる
up_stack = [
    pix2pix.upsample(512, 3),  # 4x4 -> 8x8
    pix2pix.upsample(256, 3),  # 8x8 -> 16x16
    pix2pix.upsample(128, 3),  # 16x16 -> 32x32
    pix2pix.upsample(64, 3),   # 32x32 -> 64x64
]

def unet_model(output_channels):

    # This is the last layer of the model
    last = tf.keras.layers.Conv2DTranspose(
        output_channels, 3, strides=2,
        padding='same', activation='softmax')  #64x64 -> 128x128

    inputs = tf.keras.layers.Input(shape=[128, 128, 3])
    x = inputs

    # Downsampling through the model
    skips = down_stack(x)
    x = skips[-1]
    skips = reversed(skips[:-1])

    # Upsampling and establishing the skip connections
    for up, skip in zip(up_stack, skips):
        x = up(x)
        concat = tf.keras.layers.Concatenate()
        x = concat([x, skip])

    x = last(x)

    return tf.keras.Model(inputs=inputs, outputs=x)


U-netの学習

実際にU-netが学習していく様子を眺めてみましょう。
epochが進むにつれ、正しく予測できているのがわかります。

class DisplayCallback(tf.keras.callbacks.Callback):
    def on_epoch_end(self, epoch,logs=None):
        clear_output(wait=True)
        ims.append([sample_image, sample_mask,
                 create_mask(model.predict(sample_image[tf.newaxis, ...]))])
        print ('\nSample Prediction after epoch {}\n'.format(epoch+1))

def create_mask(pred_mask):
    pred_mask = tf.argmax(pred_mask, axis=-1)
    pred_mask = pred_mask[..., tf.newaxis]
    return pred_mask[0]

def show_predictions(dataset=None, num=1):
    if dataset:
        for image, mask in dataset.take(num):
            pred_mask = model.predict(image)
            display([image[0], mask[0], create_mask(pred_mask)])
            ims.append([image[0], mask[0], create_mask(pred_mask)])
    else:
        display([sample_image, sample_mask,
                 create_mask(model.predict(sample_image[tf.newaxis, ...]))])
        ims.append([sample_image, sample_mask,
                 create_mask(model.predict(sample_image[tf.newaxis, ...]))])

ims = []
EPOCHS = 100
VAL_SUBSPLITS = 5
VALIDATION_STEPS = info.splits['test'].num_examples//BATCH_SIZE//VAL_SUBSPLITS

model = unet_model(OUTPUT_CHANNELS)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model_history = model.fit(train_dataset, epochs=EPOCHS,
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_steps=VALIDATION_STEPS,
                          validation_data=test_dataset,
                          callbacks=[DisplayCallback()])

import matplotlib.animation as animation

def make_animation(ims):
    %matplotlib nbagg
    fig, (ax0, ax1, ax2) = plt.subplots(1,3,figsize=(14.0, 8.0))
    ax0.axis('off')
    ax1.axis('off')
    ax2.axis('off')
    ax0.set_title('Input Image')
    ax1.set_title('True Mask')
    ax2.set_title('Predicted Mask')

    ims2 = []
    for epoch,im in enumerate(ims):   

        im0, = [ax0.imshow(tf.keras.preprocessing.image.array_to_img(im[0]))]
        im1, = [ax1.imshow(tf.keras.preprocessing.image.array_to_img(im[1]))]
        im2, = [ax2.imshow(tf.keras.preprocessing.image.array_to_img(im[2]))]
        ims2.append([im0,im1,im2])
    ani = animation.ArtistAnimation(fig, ims2, interval=50, repeat_delay=1000)
    return ani

学習の様子

学習の様子を見てみます。epochが進むごとに精度が増していることがわかります。

Alt text

ani1 = make_animation(ims)
HTML(ani1.to_jshtml())


おまけ

U-netにはskip-conectionという手法が使われています。
encorder部分で畳み込みをして失ってしまった画像内の位置情報を保持する役割を持ちます。
U-netにskip-conectionが無い場合も比較してみましょう。

up_stack = [
    pix2pix.upsample(512, 3),  # 4x4 -> 8x8
    pix2pix.upsample(256, 3),  # 8x8 -> 16x16
    pix2pix.upsample(128, 3),  # 16x16 -> 32x32
    pix2pix.upsample(64, 3),   # 32x32 -> 64x64
]

def unet_model_no_sc(output_channels):

    # This is the last layer of the model
    last = tf.keras.layers.Conv2DTranspose(
        output_channels, 3, strides=2,
        padding='same', activation='softmax')  #64x64 -> 128x128

    inputs = tf.keras.layers.Input(shape=[128, 128, 3])
    x = inputs
    x = down_stack(x)
    x = x[-1]
    for up in up_stack:
        x = up(x)
    x = last(x)

    return tf.keras.Model(inputs=inputs, outputs=x)

ims = []
EPOCHS = 100
VAL_SUBSPLITS = 5
VALIDATION_STEPS = info.splits['test'].num_examples//BATCH_SIZE//VAL_SUBSPLITS

model = unet_model_no_sc(OUTPUT_CHANNELS)
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
model_history = model.fit(train_dataset, epochs=EPOCHS,
                          steps_per_epoch=STEPS_PER_EPOCH,
                          validation_steps=VALIDATION_STEPS,
                          validation_data=test_dataset,
                          callbacks=[DisplayCallback()])


学習の様子

こちらも学習の経過を眺めてみます。
skip-conectionがある場合と比べ、学習が遅いばかりか精度が悪いことがわかります。

Alt text


Twitter・Facebookで定期的に情報発信しています!

はじめに

こんにちは、システム部の譚です。
Google Colablatoryで、手書きの数字(0, 1, 2 など)から構成されているMNISTデータセットを使い、分類問題のニューラルネットワークを構築してみました。

目次

はじめに
モデルを構築する
おわりに

モデルを構築する

手順

1.TensorFlowのモデルを構築し訓練するためのハイレベルのAPIである tf.kerasを使用する。

from __future__ import absolute_import, division, print_function, unicode_literals

# TensorFlow and tf.keras
import tensorflow as tf
from tensorflow import keras

# Helper libraries
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)

2.訓練データとテストデータをダウンロードする。

fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

3.後ほど画像を出力するときのために、クラス名を保存しておく。

mnist = keras.datasets.mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

4.label nameをつける。

class_names = ['0', '1', '2', '3', '4','5', '6', '7', '8', '9']

5.データの前処理をする。
最初の画像を調べてみればわかるように、ピクセルの値は0から255の間の数値です。
ニューラルネットワークにデータを投入する前に、これらの値を0から1までの範囲にスケールするので、画素の値を255で割ります。

train_images = train_images / 255.0

test_images = test_images / 255.0

6.訓練用データセットの最初の25枚の画像を、クラス名付きで表示する。

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

7.モデルを構築する。 

model = keras.Sequential([
    # データのフォーマット変換
    keras.layers.Flatten(input_shape=(28, 28)),
    # 128個のノードのDense層
    keras.layers.Dense(128, activation=tf.nn.relu),
    # 10ノードのsoftmax層
    keras.layers.Dense(10, activation=tf.nn.softmax)
])

8.モデルのコンパイルをする。

model.compile(
# モデルが見ているデータと、損失関数の値から、どのようにモデルを更新するかを決定
optimizer=tf.keras.optimizers.Adam(),

# 訓練中にモデルがどれくらい正確かを測定します
loss='sparse_categorical_crossentropy',

# 訓練とテストのステップを監視するのに使用します
metrics=['accuracy'])

9.モデルの訓練

モデルは、画像とラベルの対応関係を学習します
model.fit(train_images, train_labels, epochs=5)
Epoch 1/5
60000/60000 [==============================] - 7s 111us/sample - loss: 0.2607 - acc: 0.9261
Epoch 2/5
60000/60000 [==============================] - 6s 105us/sample - loss: 0.1127 - acc: 0.9668
Epoch 3/5
60000/60000 [==============================] - 6s 103us/sample - loss: 0.0763 - acc: 0.9769
Epoch 4/5
60000/60000 [==============================] - 6s 103us/sample - loss: 0.0557 - acc: 0.9832
Epoch 5/5
60000/60000 [==============================] - 6s 104us/sample - loss: 0.0448 - acc: 0.9864

モデルの訓練の進行とともに、損失値と正解率が表示されます。このモデルの場合、訓練用データでは0.98(すなわち98%)の正解率に達します。

10.正解率を評価する。 テスト用データセットに対するモデルの性能を比較します。

test_loss, test_acc = model.evaluate(test_images, test_labels)

print('Test accuracy:', test_acc)
10000/10000 [==============================] - 0s 41us/sample - loss: 0.0717 - acc: 0.9777
Test accuracy: 0.9777

ご覧の通り、テスト用データの正解率は訓練用より少し低い結果でした。これは過学習(over fitting)の一例です。

11.予測する。

predictions = model.predict(test_images)
np.argmax(predictions[0])

今回の予測は合っていることが確認できました。

12.10チャンネル全てをグラフ化する。

def plot_image(i, predictions_array, true_label, img):
  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])

  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'

  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  predictions_array, true_label = predictions_array[i], true_label[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1])
  predicted_label = np.argmax(predictions_array)

thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

0番目の画像と、予測、予測配列を見てみましょう。

i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()

予測確率は100%なので、すべて青で表示されました。
やはり数字に自信満々ですね。(チュートリアルのほうは、Fashion MNISTを使って若干赤かグレーも出てきました。)

13.予測の中のいくつかの画像を、予測値と共に表示する。

# X個のテスト画像、予測されたラベル、正解ラベルを表示します。
# 正しい予測は青で、間違った予測は赤で表示しています。

num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images): 
    plt.subplot(num_rows, 2*num_cols, 2*i+1)
    plot_image(i, predictions, test_labels, test_images)
    plt.subplot(num_rows, 2*num_cols, 2*i+2)
    plot_value_array(i, predictions, test_labels)
plt.show()

図のように、三行目の5の予測率が82%だった以外は、100%です。(これは手書きが悪かったですね)

おわりに

今回は試すことが多かったですが、これから学ぶべきことに気づけました。
例えば...

  • モデルのレイヤーとは。
  • モデルをコンパイルする際に、損失関数、オプティマイザ、メトリクスなどはモデルにどのような影響するのか。
  • ヘルパーライブラリnumpy、matplotlib.pyplotなどの具体的な使い方など。
    実際に扱ってみることで、機械学習に対する怖さはなくなり、今後も様々なモデルで実装していきたいと思います。

参考

Google Colablatory link 
TensorFlow はじめてのニューラルネットワーク:分類問題の初歩


目次

        

はじめに:Fairness in Machine Learningとは

皆さんに質問です。"Fairness(公平性)"とは何でしょう?


では、次の質問です。機械学習や使われるデータは"Fairness(公平性)"が担保されているでしょうか?


題名のFairness in Machine Learningとは『機械学習における公平性』を表しています。


機械学習や統計データと聞くと世間的には『データを使った結果(相関・因果関係、予測モデル) だから信頼できる!』と感じている方も多くいると思います。残念ながら、その考えは正確ではありません。なぜなら、データは『嘘を付くから』です。


加えて、機械学習や統計は完全に"objective(客観的)"だと考えられているかもしれませんが数字で表しても、数学を駆使しても、故意であろうとなかろうと、客観的とは限りません。


2018年アマゾン社(以下、アマゾン)のAI採用を例に見てみましょう(※同社は既にこの採用システムを打ち切っています)。アマゾンはコンピューターに過去の履歴書を学習させ、優秀な人材を採用していました。具体的には点数をつけどの人材がアマゾンにとって最適かの判断を基に採用活動を行いました。


しかしながら、技術関係の職種においてそれまでの多くの応募者(採用者)は男性だったため、AIが女性、もしくは、女性という単語が履歴書に入っているだけで減点の対象になっていました。その結果、数名の女性応募者は選考に落ちてしまいました。


もちろん、アマゾンはこれだけを採用の判断材料とはしていなかったでしょうが、結果としてAIが女性に対して不利な判断を導き出す結果になりました。


以上の例は機械学習における『バイアス』の一部です。データが既にバイアスを含んでいれば、相関関係や予測モデル構築の際にもその影響が出てきます。またデータに含まれるバイアスだけでなく、差別や機械学習モデルの公平性なども予測モデル構築、及び、その解釈に影響します。


この"Fairness in Machine Learning"に関する議論は機械学習技術が注目される前から存在していました。近年、その技術の発展とともに今機械学習界で最もホットなトピックの1つです。今回はそんな注目の話題を、Mehrabi氏達による論文"A Survey on Bias and Fairness in Machine Learning"(2019年9月公表)に基づき説明します。


まず、データにはどのような『バイアス』が含まれている可能性があるのか見ていきましょう。


データとバイアス

bias          

Mehrabi氏達はデータ変数特有の不均一性がバイアスを生じさせる、と述べています。御存知の通り、各データは多種多様な要素・手法を使って集められています。データの多様さに伴い、バイアスにも多くの種類が存在します。


Mehrabi氏達は同論文中にその他の研究を引用し23個ものバイアスの種類を挙げています。全部列挙し、説明すると膨大な量になるためいくつかに絞って紹介します


バイアスの種類

  • Historical bias(歴史的バイアス)

  • この歴史的バイアスはSuresh氏とGuttag氏による論文の中に登場しました。同氏達が最初に挙げた歴史的バイアス、簡潔に言うと、これまでに起こった出来事(歴史)が欲していない結果を生じさせることを意味しています。


    同氏達が例に挙げたのは"crime(犯罪)"です。欠点のない完璧な定義を『犯罪』に与えたとしましょう。そして犯罪率などのデータを分析した際、より貧しい住民ほど犯罪に手を染める、と結果が出たとします。


    結果を視た人は潜在的に『貧しい人は犯罪に手を染める』と考えてしまうようになります。これが歴史的バイアスです。


  • Population bias(人口バイアス)
  •          

    人口バイアスとは対象データが各グループ内においてデータ分布や数などが異なっている状態です。例えば、Hargittai氏は、どんな要素が人々の使用するSNSの種類決定に影響を及ぼしているかについて研究を行いました。


    その結果、人種、民族、若者に至っては両親の教育レベルなどによってSNSのユーザー層が異なると結論付けました(例:若者ユーザーの両親が経済的に豊かであればFacebookを使う傾向がある 等)。


    例えば、絶対的貧困層の需要(社会保障、教育 等)をFacebookを使い調査するとします。しかし、Facebookユーザーにはそれほど貧困に苦しんでいるユーザーはいません。もちろん、相対的貧困に苦しんでいる人々はFacebook上でも存在するでしょう。しかしこの例の調査は"絶対的"貧困層であり、相対的貧困層ではありません。


    したがって、上記のようなFacebook調査でもし仮に十分な数の意見が集まったとしても、それは絶対的な貧困層に対する間違った分析結果を導きかねません。


    このようにデータ分布に偏りがあれば、もちろん、それを使った分析は間違った予測を導き出してしまいます。


  • Cause-effect bias(因果バイアス)
  •          

    因果バイアスとは相関関係と因果関係を一緒だと考えることです。Mester氏はある記事に説明しているように、2つ(ないしはそれ以上)の要素が相関関係にあったとしてもそれらがいつも因果関係には至るとは限りません。


    同氏はECサイトのloyaltyメンバーを例に出しています。Loyaltyメンバーの顧客はLoyaltyメンバーでない顧客の5倍、購買しています。この結果Loyaltyメンバープログラムが成功したと結論づけても問題ないでしょうか?


    Loyaltyメンバーの顧客が同メンバーシップに興味があったのは間違いありませんが、そもそもLoyaltyメンバーになるような顧客はメンバーでなくてもその他の顧客に比べて5倍の金額の消費をしていたかもしれません。


    これ以外にもSocial bias(社会的バイアス)やPopularlity bias(人気バイアス)などその種類は様々です。参考文献からいくつか選んで読んでみてください。


      "Discrimination"(差別)とは?

      discrimination          

      この記事のタイトルは"Fairness in Machine Learning"なので、機械学習における"Fairness"(公平性)とは何なのかを述べなければなりません。


      しかし、当記事の基になっているMehrabi氏達の論文では、先にその対義語である"Discrimination"(差別)について論じています。そのためそれに習いまずはどのような種類の差別があるか見ていきましょう。

      差別の種類

      • Direct discrimination(直接的差別)

      • 直接的差別とは当人がどうしようもない要素によって望ましくない結果(面接や履歴書での不合格など)を受け取る状況です。


        例えば、とても優秀な1人の女性がある会社に従業員として働くために応募するとします。しかしその会社は彼女が女性であるため雇いませんでした。


        このように明らかにその人物には落ち度がない(能力や対人面接能力などに問題がない)にも関わらず、女性であるだけで雇われないのは明確な直接的差別です。


      • Indirect discrimination(非直接的差別)

      • 非直接的差別とは公平性が担保されているような要因が結果として差別を引き起こしている状況です。


        例えば、郵便番号。郵便番号は住所さえあれば誰でも平等に手に入ります。しかしそれは差別を引き起こす可能性を含んでいます。なぜでしょうか?住所によって振り分けられる郵便番号は、言い換えれば、人々が住んでいる地域を判別します。


        周りから疎まれているある特定の人種の人たちがどの地域に住んでいるか判別可能なため、ローンを組む際にその人種には許可を出さないなどもできてしまいます。このように一見、公平に見えるものからの間接的な差別の誘発を非直接的差別といいます。


        他にもSystemic discrimination(構造的差別)、Statistical discrimination(統計的差別)等が存在します。


      "Fairness"(公平性)とは?

      fairness          

      バイアスとは何か、差別とは何かの説明後、Mehrabi氏達はFairness(公平性)について様々な論文を基にまとめています。全部で10個も存在するためいくつか筆者が選んで紹介します。


      Fairness(公平性)の定義

      • Equalized odds(等しい確率)

      • 機械学習モデルによって導き出される答えが、センシティブ属性(人種、国籍、性別 等)による影響を受けない状態です。全員が等しくTrue positiveとFalse positiveの確率を受け取ることを指します。センシティブ属性について当記事では割愛していますが、参考文献からご覧いただけます。


      • Equal opportunity(機会均等)
      •  

        これも1つ目と類似しており、機械学習モデルによって導き出される答えがセンシティブ属性による影響を受けない状態です。全員が等しくTrue positiveの確率を受け取ることを指します。


      • Demographic parity
      •                                           

        センシティブ属性に関わらず、予測される結果(positive outcome)の確率は同じでなければなりません。

              

      この他にも様々な定義があります。ただこれもバイアスの種類と同様、膨大な数が紹介されているので全部は紹介しませんでした。

                             
                             

      まとめると、筆者の解釈は、機械学習モデルによって導き出す答えが個人のステータスやバックグラウンドに左右されない状態をFairness(公平性)だと考えています。


              

      Fairness in Machine Learningがなぜ重要なのか、Mehrabi氏達は論文内で議論しています。Fair(公平)でないデータやモデルを利用すれば、潜在的にある特定の人々が被害を受けるかもしれません。例えば、ある町で貧困に喘ぐ人と窃盗犯罪率が相関関係にあるとします。ここで機械学習において貧困者だから窃盗犯に違いない、もしくは、窃盗犯の確率が高いと結論付けられしてしまえば、そこにはFairness(公平性)は存在しません。


              

      このような問題は人々の潜在的差別意識にも働きかけてしまいます。貧困者=犯罪者と誤認してしまい貧困者に対する差別がより強まるかもしれません。短格的な考えに至ればホームレスに対する殺人を正当化するなど、間違った解釈をしてしまう可能性も否めません。


              

      私たち人が偏見や差別意識を持っているように、機械学習においても同じ問題が起こります。それらを極力防ぐために研究者や機械学習に精通する人以外もデータや予測モデルにおける差別やFairness(公平性)を認識することが重要ではないでしょうか?

      機械学習モデルをどのようにFair(公平)にするか

      fair         

      では、バイアスや差別をなくし、機械学習モデルにおいてFairness(公平性)を実現するにはどうすればよいでしょうか?


      同論文内では3つの方法(段階)が示されています。またこれに伴いMehrabi氏達は基本的な機械学習技術を例示しています。

      Fairness(公平性)のためのデータ処理方法

      • Pre-processing(前処理)

      • 差別がなくなるようにデータを編集します。前処理テクニックの特徴量作成、データ分類、次元削減(PCA)などが当てはまります。下記論文にデータの"Discrimination(差別)"を無くすための方法、その基となるアルゴリズムが書かれています。オープンソースなので興味のある方はぜひ一読してはいかがでしょうか?

        Kamiran and Calders, T. (2012) "Data preprocessing techniques for classification without discrimination"


      • In-processing(処理中)

      • データ処理中において差別を無くすような関数などをモデルに組み込みます。また関数だけでなくデータ分類や適した回帰モデルの使用によってFairness(公平性)の実現に近づきます。


      • Post-processing(後処理)

      • トレーニングデータに関与しないデータセットにのみ行う処理です。最後のpost-processingに関してはあまり意味を理解できませんでした。下記の論文が例のようですが、pre-processingやin-processingとあまり変わらない気も...。

        Hardt, M. *et al*. (2016) "Equality of opportunity in supervised learning"

        Pleiss, G. et al. (2017) "On fairness and calibration"


              

      恐らく、機械学習モデルが割り当てたラベルが適切でない場合の(例えば、カテゴリカル変数であるべきデータが連続変数になってしまっているので適切な変数を割り当てる)処理の仕方だと思います。加えてモデルが出した結果の可視化(visualization)なども含まれるそうで、幅広い意味でモデルが適した結果を出しているか確認する方法のようです。


              

      Mehrabi氏達はこれらの説明をした後、詳細な手法やどのモデルにどの操作を加えるといいかを提案しています。当記事では文字量や複雑さの関係から扱いませんが、興味のある方は同論文に目を通してみてください。25ページほどの論文でかなり読み応えがあります。


      おわりに

              

      "Fairness in Machine Learning"についての議論は機械学習やAIが浸透するに伴い、避けては通れません。


      『機械学習』と聞くと技術の問題やセキュリティの問題などがよく目に付きます。筆者の個人的意見ですが、そこにはデータや機械学習の判断が完璧である、という奢りが存在しているのではないでしょうか?


      データや機械学習を批判しているのではなく、それらはありとあらゆる問題点(注意すべき点)を含んでいると人々が自覚すべきだと考えています。なぜなら、冒頭でも述べたように全てのデータや予測モデルが客観性を担保しているわけではないからです。


      皆さんもこの機会に機械学習とFairness(公平性)について考えてみてはいかがでしょうか?


      参考文献、リンク

      Hardt, M. *et al*. (2016) "Equality of opportunity in supervised learning"

                             

      Kamiran, F. and Calders, T. (2012) "Data preprocessing techniques for classification without discrimination"


      Mehrabi, N. *et al*. (2019) "A survey on bias and fairness in machine learning "


      Pleiss, G. *et al*. (2017) "On fairness and calibration"


      Suresh, H. and Guttag, J. V. (2019) "A framework for understanding unintended consequences of machine learning"


      Statistical bias types explained - part2 (with examples)


      焦点:アマゾンがAI採用打ち切り、「女性差別」の欠陥露呈で


はじめに

iOSネイティブアプリと機械学習を組み合わせたいと思い、Appleが提供してくれているフレームワークCore MLとVision、事前に訓練したInception v3モデルを使用し、ドラマのSilicon Valleyに出たthis is not hot dogアプリを作ってみました。 
this is not hot dog とは、撮影された画像がホットドッグかどうかを認識するアプリです。YouTube動画がありますのでご覧ください。
YouTubeより Silicon Valley: Season 4 Episode 4: Not Hotdog (HBO)

目次

はじめに
作成手順
まとめ
ソースコード


環境

  • プロセッサ Intel Core i7-5557U 3.1 GHz
  • メモリ 16GB 1867 MHz DDR3
  • Apple Swift version 4.2.1
  • Xcode Version 10.1 (10B61)


Core MLとは

Core ML is the foundation for domain-specific frameworks and functionality
日本語に訳すと学習モデル等をiOS / macOS上で利用する際に、開発者が専門的な知識を必要とせずに扱えるように補助するフレームワークです。

主に下記の2つ機能があります。
1.Load a Pre-Trained Model
 事前学習済みModelをロードでする
2.Make Predictions
 予測する

Visionとは

コンピュータビジョンの機械学習機能を容易にAppに組み込むことができます。
フェイストラッキング、顔検出、ランドマーク、テキスト検出、矩形検出、バーコード検出、オブジェクトトラッキング、画像レジストレーションなどの機能に対応しています。

Inception v3はCore MLモデルのひとつです。 
木、動物、食べ物、乗り物、人々など、1000のカテゴリセットから画像に存在する主なオブジェクトを検出することができます。

作成手順

1.Xcodeにてプロジェクトを作成する
2.下の画像のようなUIをつくる
Xcode作成画面 3.こちらのリンク からInception v3をダウンロード
4.ダウンロードしたものをプロジェクトに入れる
5.モデルの作成

guard let model = try? VNCoreMLModel(for: Inceptionv3().model) else {
  fatalError("Loading Core ML Model Failed.")
}

6.リクエスト(VNCoreMLRequest)の生成とハンドラ処理

 let request = VNCoreMLRequest(model: model){( request,error) in
             guard let results = request.results as? [VNClassificationObservation] else{
                 fatalError("Model failrd to process image")
             }

7.受け取ったrequestのfirstでhotdogかどうかを判断し、結果はnavigationItemに渡す(一個目のrequestは精度が一番高いため、下記のように94%ぐらいで判断された) 

6A29C419-EB06-4321-BC25-8A7220368DC8, revision 1, 0.942064 "hotdog, hot dog, red hot",
if let firstResult = results.first {
   if firstResult.identifier.contains("hotdog") {
       self.navigationItem.title = "Hotdog!"
   } else {
       self.navigationItem.title = "not Hotdog!"
   }
}

8.CIImageへの変換

guard let ciimage = CIImage(image: userPickedImage) else {
    fatalError("Could not convert UIImage into CIImage")
}

9.ハンドラの生成と実行

let handler = VNImageRequestHandler(ciImage: image)
do {
   try! handler.perform([request])
}
catch{
   print(error)
}

10.画像のようなアプリが完成。
hotdogだと...。 Core MLとVision、Inception v3モデルを使ったiOSアプリ

画面上にhotdogの判定があります! 

hotdog以外だと...・ Core MLとVision、Inception v3モデルを使ったiOSアプリ

きちんとNot hotdogの判定がされました。

まとめ

いかがでしょうか?
数行のソースコードでこのように作成できて楽しかったです。
Appleさんは他のモデルも用意してくれているので、ぜひ使ってみてください!

アクセルユニバース株式会社 (以下当社)では、人が対応している業務を画像認識、音声認識、文字認識等を活用して効率化する機械学習ソリューション開発をおこなっています。

当社のインターン生は機械学習論文解説を書いたり、実際に実装したりしています。
随時紹介していくので是非ご覧ください!

定期的にメルマガでも情報配信予定なので、問い合わせページに「メルマガ登録希望」とご連絡ください。


ソースコード

class ViewController: UIViewController, UIImagePickerControllerDelegate,
UINavigationControllerDelegate {


    @IBOutlet weak var imageView: UIImageView!
    let imagePicker = UIImagePickerController()

    var classificationResults : [VNClassificationObservation] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        imagePicker.delegate = self
        imagePicker.sourceType = .camera
        imagePicker.allowsEditing = true

    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

        if let userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            imageView.image = userPickedImage

            guard let ciimage = CIImage(image: userPickedImage) else {
                fatalError("Could not convert UIImage into CIImage")

            }
            detect(image: ciimage)
        }

         imagePicker.dismiss(animated: true, completion: nil)
    }

    func detect(image: CIImage){

        guard let model = try? VNCoreMLModel(for: Inceptionv3().model) else {
            fatalError("Loading Core ML Model Failed.")
        }

        let request = VNCoreMLRequest(model: model){( request,error) in
            guard let results = request.results as? [VNClassificationObservation] else{
                fatalError("Model failed to process image")
            }

            print(results)

            if let firstResult = results.first {
                if firstResult.identifier.contains("hotdog") {
                    self.navigationItem.title = "Hotdog!"
                } else {
                    self.navigationItem.title = "not Hotdog!"
                }
            }
        }

        let handler = VNImageRequestHandler(ciImage: image)

        do {
            try! handler.perform([request])
        }
        catch{
            print(error)
        }
    }

    @IBAction func cameraTapped(_ sender: UIBarButtonItem) {
        imagePicker.sourceType = .camera
        imagePicker.allowsEditing = false

        present(imagePicker, animated: true, completion: nil)
    }
}

LightgbmやXgboostを利用する際に知っておくべき基本的なアルゴリズム「GBDT」を直感的に理解できるように数式を控えた説明をしています。

対象者

  • GBDTを理解してLightgbmやXgboostを活用したい人
  • GBDTやXgboostの解説記事の数式が難しく感じる人

※GBDTを直感的に理解してもらうために、簡略化された説明をしています。

GBDTのメリット・良さ

  • 精度が比較的高い
  • 欠損値を扱える
  • 不要な特徴量を追加しても精度が落ちにくい
  • 汎用性が高い(下図を参照)
  • LightgbmやXgboostの理解に役立つ

GBDT説明図
引用元:門脇大輔、阪田隆司、保坂佳祐、平松雄司(2019)『Kaggleで勝つデータ分析の技術』技術評論社(230)

GBDTとは

G... Gradient(勾配)
B... Boosting(ブースティング) = アンサンブル手法の1つ
D... Decision(決定)
T... Tree(木)

つまり、GBDTとは「勾配降下法(Gradient)」と「Boosting(アンサンブル)」、「決定木(Decision Tree)」を組み合わせた手法です。
まずは、GBDTの基本となる上3つの概念について直感的に理解していきましょう。

勾配降下法

機械学習アルゴリズムの主な目的は、正確な予測を行うことです。予測が正確かどうかは「誤差の大きさ」で判断することができます。

「誤差が小さい = 予測が正確」

と言うことができますよね。


その誤差を小さくする方法の1つが、「勾配降下法」です。

勾配降下法は、名前の通り「勾配」(傾斜)を降下する最適化の手法で、精度の誤差を最適化する役割をもっているわけです。

誤差関数グラフ

これは誤差関数のグラフで、「誤差の大きさ」を表しています。

このグラフの場合、真ん中に近づくに連れて誤差が小さくなっていますね。

誤差関数の勾配(傾斜)を下れば下るほど、「誤差が小さくなる=予測が正確になる」わけです。

Boosting(アンサンブルの1種)

アンサンブルとは、精度の低い学習器を複数組み合わせて、精度を高くする手法です。アンサンブルを表現することわざで、

「三人寄れば文殊の知恵」

というものがあります。

凡人でも三人で集まって相談すれば文殊に劣らぬほどよい知恵が出るものだ。」

つまり、弱学習器(予測の精度があまり高くないもの)を複数組み合わせることで強学習機(精度の高いもの)を作ることができるのです。

アンサンブルの手法には、さまざまな手法がありますが、その中でもGBDTに利用されているのが「Boosting」です。

決定木(Decision Tree)

決定木は、データから決定木を作る機械学習の手法のことです。

例えば、ECサイトで「商品Xを購入するかどうか」を予測したいとしましょう。

ECサイトに訪れた人が商品Xを購入するかどうかは、そのページに訪れた回数や、関連する商品Yを調べた回数などさまざまな要因から予測することができるかもしれません。このときに、

「商品Xのページに訪れた回数が5回以上」 → 商品Xを購入するだろう

「商品Xのページに訪れた回数が5回未満」 → 商品Xを購入しないだろう

このように条件を用意し予測を行う手法が決定木です。

GBDTの手順

  1. 目的変数の平均を計算する
  2. 誤差を計算する
  3. 決定木を構築する
  4. アンサンブルを用いて新たな予測値を求める
  5. 再び誤差を計算する
  6. 3~5を繰り返す
  7. 最終予測を行う

具体的に解説

こちらのデータを使って解説していきます。

スクリーンショット 2019-11-28 11.11.36.png

労働時間、好きな果物、性別のデータを用いて給料を求めます。

ステップ1.給料の平均を求める

スクリーンショット 2019-11-28 11.17.00.png
計算結果を予測1とします。
これをベースにして予測を行います。

ステップ2.誤差を計算する

「誤差1」=「給料の値」ー「予測1」で誤差を求めています。

例えば・・・

誤差1 = 900 - 650 = 250

カラム名は「誤差1」とします。
スクリーンショット 2019-11-28 13.48.38.png

ステップ3.誤差を予測する目的で決定木を構築する


スクリーンショット 2020-02-18 17.22.45.png
茶色の部分にはデータを分ける条件が入り、緑色の部分(葉)には各データごとの誤差の値が入ります。
葉の数よりも多く誤差の値がある場合は、1つの葉に複数の誤差の値が入り、平均します。

ステップ4.アンサンブルを用いて新たな予測値を求める

ここでは、決定木の構築で求めた誤差を用いて、給料の予測値を計算します。

スクリーンショット 2020-02-18 11.18.40.png

予測2 = 予測1(ステップ1) + 学習率 * 誤差

これを各データに対して計算を行います。

例えば・・・

予測2 = 650 + 0.1 * 200 = 670

このような計算を行って予測値を求めます。

ここで、予測2と予測1の値を比べてみてください。

スクリーンショット 2019-11-28 16.48.20.png

若干ではありますが、実際の値に予測2の方が近づいていて、誤差が少しだけ修正されています。
この「誤差を求めて学習率を掛けて足す」という作業を何度も繰り返し行うことで、精度が少しずつ改善されていきます。

※学習率を乗算する意味

学習率を挟むことで、予測を行うときに各誤差に対して学習率が乗算され、
何度もアンサンブルをしなければ予測値が実際の値に近づくことができなくなります。その結果過学習が起こりづらくなります。

学習率を挟まなかった場合と比べてみてください!

ステップ5.再び誤差を計算する

ここでは、予測2と給料の値の誤差を計算します。ステップ3と同じように、誤差の値を決定木の葉に使用します。

「誤差」=「給料の値」ー「予測2」

例えば・・・

誤差 = 900 - 670 = 230

このような計算をすべてのデータに対して行います。

ステップ6.ステップ3~5を繰り返す

つまり、

・誤差を用いた決定木を構築

・アンサンブルを用いて新たな予測値を求める

・誤差を計算する

これらを繰り返します。

ステップ7.最終予測を行う

アンサンブル内のすべての決定木を使用して、給料の最終的な予測を行います。
最終的な予測は、最初に計算した平均に、学習率を掛けた決定木をすべて足した値になります。

スクリーンショット 2020-02-14 18.15.33.png

スクリーンショット 2020-02-14 18.15.33.png

GBDTのまとめ

GBDTは、

-予測値と実際の値の誤差を計算

-求めた誤差を利用して決定木を構築

-造った決定木をそれ以前の予測結果とアンサンブルして誤差を小さくする→精度があがる

これらを繰り返すことで精度を改善する機械学習アルゴリズムです。この記事を理解した上で、GBDTの派生であるLightgbmやXgboostの解説記事を見てみてみると、なんとなくでも理解しやすくなっていると思いますし、Kaggleでパラメータチューニングを行うのにも役に立つと思いますので、ぜひ挑戦してみてください。


Twitter・Facebookで定期的に情報発信しています!

アクセルユニバースの根岸です。

突然ですが、お寿司は食べますか?日本に住んでいて食べたことない人はほとんどいないと思います。ということは何を食べているのか判断できるのは当然の教養ですよね。一方で私のようなお寿司に疎い人もいるわけで、なんとなく美味しいで終わってしまうのはもったいない。そんな時にこそ機械学習を使って解決してみましょう。

ということで、本記事ではネットからお寿司の写真を入手して機械学習(画像認識)を用いてお寿司を判別するためのモデル構築を行うまでの一連の流れを記していこうと思います。コードも合わせて載せていくのでpythonの基本文法とAPIについて少し知っておくと理解しやすいかもしれません。

目次

  • 1.お寿司の画像データをネットから収集する
  • 2.画像を振り分ける
  • 3.画像処理と機械学習で判別する
  • 終わりに一連の流れ

1.お寿司の画像データをネットから収集する

まずは機械学習に必要なデータの収集から始めていきます。判別対象はお寿司なのでお寿司の画像データを収集する必要があります。方法としては

  • 画像検索エンジン(googleなど)から一つずつ引っ張る
  • 画像共有サイト(Instagram,フォト蔵など)で公開されているWeb APIから抽出する
  • kaggle等からデータセットを取得する
  • 自分で作る

があります。今回の画像はWebAPIが充実している「フォト蔵」から抽出してくることにしましょう。

そもそもWebAPIとは、あるサイトが備えている機能を外部から利用できるように公開しているものです。WebAPIでは基本的にHTTP通信が利用されており、下の図のようにユーザがAPIを提供しているサーバに対して、任意のHTTPリクエストを送信することでサーバーからXMLやJSON形式のファイルが返ってくるという仕組みになっています。こうしてAPIから任意のデータを取得する行為を俗に「APIを叩く(物理的にでは有りません)」と言います。それぞれの形式の中身もフォーマットが決まっているので、ファイルのどの部分から情報を取ってくればよいか分かりやすくなっています。(下記JSONレスポンス参照)

WebAPI
HTTPリクエストは基本的に提供元が併せて公開してくれているのでそれに従って作りましょう。
それでは早速今回利用する「フォト蔵」のWebAPIを確認していきましょう。フォト蔵の検索APIは以下のようになっています。
http://photozou.jp/basic/apimethodsearch_public
フォト蔵検索API.png

加えて以下のパラメータを指定することで、柔軟な検索が可能になっています。keywordをクエリとして設定することで任意のキーワードに沿ったデータのみ取得が可能になりそうですね。

フォト蔵検索API_パラメータ

試しにkeywordを"中トロ"としてクエリを設定してブラウザのURL欄に入力してみると以下のような表示がされます。
https://api.photozou.jp/rest/search_public.xml?keyword=中トロ

中トロxml
XMLファイルがブラウザ上で表示されました。

中身を詳しく見ていくとoriginal_widthやoriginal_heightがありますよね。これは元画像の解像度なので、写真の投稿者によって異なります。画像認識する上で画像サイズが違うと前処理が大変なので、サムネイルに使用している画像サイズ(120×120)の画像URLであるthumbnail_image_urlを使うことにしましょう。

このURLをファイルの中から全て抜き出してそれぞれダウンロードしてくれば欲しい画像が抜き出せるはずです。これらをすべて入手するのにurllibライブラリを使って画像を収集していきます。

それでは画像取得までの手順を確認してみましょう。

  1. フォト蔵のAPIを利用して(今回は)JSONファイルを取得する。
  2. JSONファイル内のthumbnail_image_urlから画像のURLをすべて取得する。
  3. 取得したURLの画像を一つずつダウンロードして保存する

JSONファイルの構成は以下のようになっています。

JSONレスポンス
{
"info": {
"photo_num": ###,
"photo": [
{
"photo_id": ###,   ←画像のナンバリングとして利用
"user_id": ###,
"album_id": ###,
"photo_title": "タイトル",
"favorite_num": ###,
"comment_num": ###,
"view_num": ###,
"copyright": "normal/creativecommons",
"copyright_commercial": "yes/no"
"copyright_modifications": "yes/no/share"
"original_height": ##,
"original_width": ###,
"geo": {
"latitude": ###,
"longitude": ###
},
"date": "YYYY-MM-DD",
"regist_time": "YYYY-MM-DDThh:mm:ss+09:00",
"url": "URL",
"image_url": "URL",
"original_image_url": "URL",
"thumbnail_image_url": "URL",   ←これを抜き出す
"large_tag": "<a href=\"...\">...</a>",
"medium_tag": "<a href=\"...\">...</a>"
},
...
]
}
}

それでは実際にコードを書いていきます。
まずは必要なモジュールをimportしてきます。

import urllib.request as req 
import urllib.parse as parse
import os, re, time
import json
#APIのメソッドを指定
photozou_api='https://api.photozou.jp/rest/search_public.json'
sushi_dir='./image/sushi'
  • urllib.request:URLの読み込み、ダウンロード
  • urllib.parse:相対URLから絶対URLを取得
  • os, re:ディレクトリー作成
  • time:ソースコードの一時停止(リソースを逼迫させないためにAPI叩く間隔を空ける)
  • json:JSONファイル読み込み

API用のHTTPリクエストとダウンロードした画像を保存する自身のディレクトリーを指定しておきます。
次に画像を検索するための関数search_photoを作っていきます。上記APIのパラメータ表記を参考にkeyword, offset,limitを指定することでAPIのクエリを組み立てて、それに応じた指定のJSONファイルをダウンロードして返す関数になっています。

この時に画像保存のためのフォルダを一緒に作っています。limitの値を変更すれば最大で引っ張ってこれる画像URLの数を調整できます。ここでは上記WebAPIのパラメータに書いてある初期値の100を使います。

def search_photo(keyword, offset=0, limit=100): #画像の検索及びJSONファイルを返す関数
#APIのクエリ組み立て
keyword_encoding=parse.quote_plus(keyword) #HTMLフォーム値の空白をプラス記号に置き換え
query="keyword={0}&offset={1}&limit={2}".format(keyword_encoding,offset,limit)
url=photozou_api+'?'+query
#クエリとキャッシュを保存しておく
if not os.path.exists(sushi_dir):
os.makedirs(sushi_dir)
sushi=sushi_dir+"/"+re.sub(r'[^a-zA-Z0-9\%\#]','_',url)
if os.path.exists(sushi):
return json.load(open(sushi, "r", encoding='utf-8'))
print(url)
req.urlretrieve(url,sushi)
time.sleep(1) #逼迫させないために間隔を空ける
return json.load(open(sushi, "r", encoding='utf-8'))

JSONファイルが取得できたらその中からthumbnail_image_urlの部分を抜き出してダウンロードしてみましょう。中にはthumbnail_image_urlを持たないものが混ざっているので、例外として排除しておきます。ファイル名はphoto_idと'_thumb'を用いてjpgで保存します。画像をダウンロードするときにもリソースの逼迫につながらないように配慮して間隔を空けるようにしておきましょう。

def download_single(info, save_dir): #画像のダウンロード 
#画像保存のためのフォルダを作成
if not os.path.exists(save_dir):
os.makedirs(save_dir)
if info is None:
return
if not 'photo' in info['info']:
return
photo_list=info['info']['photo']
for photo in photo_list:
photo_title=photo['photo_title']
photo_id=photo['photo_id']
url=photo['thumbnail_image_url']
path=save_dir+'/'+str(photo_id)+'_thumb.jpg'
if os.path.exists(path):
continue
try:
print('download',photo_id,photo_title)
req.urlretrieve(url,path)
time.sleep(1)#間隔を空ける
except Exception as e:
print('ダウンロードできませんでした url=',url)

それでは上記2つを実行する関数を作って行きます。

実際に何個の画像を取得できるかわからないのでwhileとmax_photoで上限内でありったけの画像をダウンロードしてくることにします。いくつか条件分岐させていますがニッチなカテゴリーを検索するわけじゃないので、記述しなくても問題ないですが念のため。

def download_all(keyword, save_dir, max_photo=1000):
offset=0
limit=100
while True:
info=search_photo(keyword, offset=offset, limit=limit)
#情報が欠損している場合(例外処理)
if (info is None) or (not 'info' in info) or (not 'photo_num' in info['info']):
print('情報が欠損しています')
return
photo_num=info['info']['photo_num']
if photo_num==0:
print('photo_num=0,offset=',offset)
return
#画像が含まれている場合
print('download offset=', offset)
download_single(info, save_dir)
offset+=limit
if offset >= max_photo:
break

モジュールとして使わずに単独で使用する用に以下を記述しておきます。download_allのkeywordを'中トロ'にしていますがここのkeywordを変えることでそれに応じた検索画像をダウンロードしてきます。

if __name__=='__main__':
download_all('中トロ','./image/sushi')

ということでプログラムが完成した(downloader-sushi.pyで保存)のでターミナルから実行してみましょう。

$ python3 downloader-sushi.py

するとフォト蔵にある中トロの画像を最大で1000件ダウンロードします。
中トロ画像たち.png
中トロ以外にも鉄火巻、こはだについて同じ処理をした後、新しいフォルダ"tyutoro","tekkamaki","kohada"を作成します。

中トロ, 鉄火巻, こはだ
これにて学習に必要な材料は揃いましたが・・・

2.画像を振り分ける

画像を振り分ける

ここからが泥臭い作業になります。

中トロと検索すれば全て中トロのお寿司を1貫を画面いっぱいに載せてくれている画像なら良いのですが、残念ながら中には複数の別のお寿司が写ってる、中トロが写っていない、もはや人しかいない、なんて画像がかなり混在しています。ここで手作業で学習に使えそうな画像を抽出するという泥作業が発生します(画像を部分的にアノテーションすれば使えるものもありますが、サイズの調整が大変なので割愛します)。正にデータサイエンティストの醍醐味ですね!

そうして抽出した画像数は以下の通りです。

  • 中トロ 111点
  • 鉄火巻 135点
  • こはだ 66点

計311点
中トロ、鉄火巻、こはだ

圧倒的こはだ不足です。そもそも全体的にサンプルが足りていないですが、見逃してください...。
これらの画像のファイルは"./image"上にそれぞれ作成しました。後は画像処理を行って画像認識のモデルに突っ込んでいきましょう。

3.画像処理と機械学習で判別する

画像処理と機械学習

ここで行うことは3点です

  • 画像を数値データに変換と水増し(画像処理)
  • 画像認識モデルの構築と学習
  • 画像の判別

3-1 画像を数値データに変換と水増し

画像データを学習させるためには数値データへの変換が必須です。変換後はデータサイエンティスト御用達のNumpyの形式に変換させましょう。まずは必要なモジュールをimportしてきます。

from PIL import Image
import os, glob
import numpy as np
  • PIL:画像処理(エンコードに利用)
  • glob:画像のディレクトリパス一覧を取得

分類対象は中トロ、鉄火巻、こはだの3種類なので下記で指定しておきます。

root_dir='./image/'
categories=['tyutoro','tekkamaki','kohada']
nb_classes=len(categories)
image_size=100

次にフォルダ(カテゴリー)ごとに画像データを読み込んでRGB変換、Numpy形式への変換を行う関数を宣言していきます。

この際に画像の水増しも一緒に行っていきます。画像の角度を変えたり反転させることで画像数を増やしていきます。注意すべきことは水増ししたデータが学習データとテストデータの両方に混在してしまうとリークが発生してしまい、精度が異常に高くなってしまう現象が起きるので、水増しは学習データのみ行うようにしましょう。is_trainの真偽で水増しをするかどうかを設定しています。

#画像処理と水増しを行う
X=[]
Y=[]
def padding(cat, fname, is_train):
img=Image.open(fname)
img=img.convert("RGB") #RGB変換
img=img.resize((image_size,image_size)) #画像サイズ変更(100×100)
data=np.array(img) #numpy形式に変換
X.append(data)
Y.append(cat)
#学習データのみ水増しするので学習データではない場合は以下のfor文を実行しない
if not is_train:
return
for angle in range(-20, 20, 10):
#画像の回転
img2=img.rotate(angle)
data=np.asarray(img2)
X.append(data)
Y.append(cat)
#画像の左右反転
img3=img.transpose(Image.FLIP_LEFT_RIGHT)
data=np.asarray(img3)
X.append(data)
Y.append(cat)

#カテゴリーごとの処理
def make_train(files, is_train):
global X,Y
X=[]
Y=[]
for cat, fname in files:
padding(cat, fname, is_train)
return np.array(X), np.array(Y)

加えてディレクトリーごとに分けられているファイルを収集してallfilesに統合しています。

#ディレクトリーごとにファイルを収集する
files_all=[]
for idx, cat in enumerate(categories):
image_dir=root_dir+'/'+cat
files=glob.glob(image_dir+'/*.jpg')
for f in files:
files_all.append((idx, f))

加えてディレクトリーごとに分けられているファイルを収集してallfilesに統合しています。

最後に学習データとテストデータに分けてモデルに入れるデータが整います。

import random, math
#シャッフル
random.shuffle(files_all)
MATH=math.floor(len(files_all)*0.6)
train=files_all[0:MATH]
test=files_all[MATH:]

後はモデルを作って学習させて完成ですね。

3-2 画像認識モデルの構築と学習

それでは機械学習のフェーズに入っていきましょう。

使用するモデルはCNN(畳み込みニューラルネットワーク)です。ここではTensorFlowとKerasを組み合わせてCNNを組み立てて行きます。モデルの概要は以下のようにしています。
CNN概要
CNNの具体的な中身が以下の通り

_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 100, 100, 64) 1792
_________________________________________________________________
activation_1 (Activation) (None, 100, 100, 64) 0
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 50, 50, 64) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 50, 50, 64) 0
_________________________________________________________________
conv2d_2 (Conv2D) (None, 50, 50, 128) 73856
_________________________________________________________________
activation_2 (Activation) (None, 50, 50, 128) 0
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 25, 25, 128) 0
_________________________________________________________________
dropout_2 (Dropout) (None, 25, 25, 128) 0
_________________________________________________________________
conv2d_3 (Conv2D) (None, 23, 23, 128) 147584
_________________________________________________________________
activation_3 (Activation) (None, 23, 23, 128) 0
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 11, 11, 128) 0
_________________________________________________________________
dropout_3 (Dropout) (None, 11, 11, 128) 0
_________________________________________________________________
flatten_1 (Flatten) (None, 15488) 0
_________________________________________________________________
dense_1 (Dense) (None, 1028) 15922692
_________________________________________________________________
activation_4 (Activation) (None, 1028) 0
_________________________________________________________________
dropout_4 (Dropout) (None, 1028) 0
_________________________________________________________________
dense_2 (Dense) (None, 3) 3087
_________________________________________________________________
activation_5 (Activation) (None, 3) 0
=================================================================
Total params: 16,149,011
Trainable params: 16,149,011
Non-trainable params: 0
_________________________________________________________________

加えてディレクトリーごとに分けられているファイルを収集してallfilesに統合しています。

それでは上記を元にコードを書いていきます。

# データをロード
def main():
X_train, y_train=make_train(train,True)
X_test, y_test=make_train(test,False)
# データを正規化する
X_train=X_train.astype("float") / 256
X_test=X_test.astype("float") / 256
y_train=np_utils.to_categorical(y_train, nb_classes)
y_test=np_utils.to_categorical(y_test, nb_classes)
# モデルを学習し評価する
model=model_train(X_train, y_train)
model_eval(model, X_test, y_test)
# モデルを構築
def build_model(in_shape):
model=Sequential()
model.add(Convolution2D(64 , 3, 3, border_mode='same',input_shape=in_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Convolution2D(128, 3, 3, border_mode='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Convolution2D(128, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(1028))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))
model.compile(loss='binary_crossentropy',optimizer='rmsprop',metrics=['accuracy'])
model.summary()
return model
# 学習
def model_train(X, y):
model=build_model(X.shape[1:])
model.fit(X, y, batch_size=30, nb_epoch=30)
return model
# 評価
def model_eval(model, X, y):
score=model.evaluate(X, y)
print('loss=', score[0])
print('accuracy=', score[1])
if __name__=="__main__":
main()

3-3 画像を判別する

ということでプログラムが完成した(sushi_cnn.pyで保存)のでターミナルから実行してみましょう。

$ python3 sushi_cnn.py

計算に結構な時間かかりますが、最終的に以下のような正答率が出てきました。

loss= 1.2733278312683105
accuracy= 0.8826666955947876

およそ88%といったところでしょうか。

他にも画像サイズを変えてみたり、層を変えてみたりしましたが概ね90%前後を漂う感じでした。3種類と比較的少ない種類を判定しましたが、ほどほどに上手くできてるかと思います。

終わりに

本記事では、Web APIによる画像入手に始まり、前処理(データの振り分けや選別)、CNNの学習による画像認識まで行ってみました。もちろん学習させたお寿司の種類が少なかったので実際に使えるわけではないですが、"データが集まって前処理をしっかりすれば"実用的なものになっていくのではないでしょうか。

○環境
OS:Mac Catalina 10.15.1
CPU:3.1 GHz デュアルコアIntel Core i7
RAM:16 GB 1867MHz DDR3
python:3.7.5
各種ライブラリは投稿時点で最新のものを使用しています


Twitter・Facebookで定期的に情報発信しています!


GBDTのひとつ、Catboost

GBDTという手法がKaggleなどでもメジャーなものになってきました。
今回はそんなGBDTライブラリの一つで比較的新しいCatboostについて見ていきます。
Catboostは、ロシア最大手検索エンジンサイトであるYandex社から2017年にリリースされ、11月29日現在v0.20まで公開されています。その革新さをいくつかあげられていたのでそれらをこちらの論文に沿いながら確認していきます。

目次

カテゴリ変数の扱い

勾配バイアスの扱い

決定木選定アルゴリズム

実際に動かしてみた


カテゴリ変数の扱い

前処理

機械学習にあたってカテゴリ変数を扱うときにはそれらを学習させるためにデータの前処理が必要です。
しかし、Catboostではそれらを内部で吸収し、自分で処理してくれます。
たとえば、低カーディナリティ(性別などのように該当する項目が少ない)カテゴリ変数に対してはOne-hotエンコーディングがなされることが一般的です。これは前処理時か、学習を始める際に変換されるものですが、Catboostでは学習時に後述するカテゴリ変数のコンビネーションでも使いうるので前処理をせずにそのまま渡すのが望ましいとされています。
また、ユーザIDのようにカーディナリティの高い変数を扱う際にはそれらの統計量(Target Statistics)を使いますが、これは訓練データの中だけで完結したものであるため、ターゲットリーケージ(target leakage)という過学習の危険性が非常に高いものになります。
そこで、Catboostではランダムにデータを選び、その中から統計量を取るという作業を繰り返し代用値を計算するという手法で対処されています。 具体的に以下の式で代用値が算出されています。
\(x_{\sigma_{p}, k} =\displaystyle\frac{\sum_{j=1}^{p-1}[x_{\sigma {_j}, k}= x_{\sigma _{p} , k}] Y_{\sigma j} + a \cdot P}{\sum_{j=1}^{p-1} [x_{\sigma _{j }, k} = x_{\sigma _{p} , k}] + a}\)

ここで、データセット{\({X_i, Y_i}\)}とし, \(X_i\)は\((x_{i, 1},...x_{i, m}\))のm次元ベクトルで表され、σは順列(どのデータを取るかの集合)、Pを重要度、aをPの重みとして計算されています。
重要度を足すのは頻度の低いカテゴリから生じるノイズを減らすことを目的としていて、回帰問題ではデータセットのラベル値の平均、二値クラス分類問題では真の先験的遭遇確率(True a priori distribution)がよく用いられます。
ここでの統計量の算定の仕方にもランダム性を用いることは、過学習回避に有効です。

カテゴリ変数のコンビネーション

Catboostでは、統計量を使った過学習回避としてもう1つカテゴリ変数を組み合わせる、新奇な手法で対処しています。
たとえば、音楽のレコメンド機能で、ユーザIDと、そのユーザが好きなジャンルという2つのカテゴリ変数があるとします。あるユーザーがロックを好んでいるというデータがあるときに、この2つを上述の代用値に置き換えると、この新しい代用値群は、新たな変数として過学習を気にすることなく使うことができるようになります。
しかしこのカテゴリ変数の組み合わせは、カテゴリ変数が多ければ多いほど指数関数的にその数を増します。
全てを考慮することはまだできないため、新たな木構造を生成する際に最初の枝に対しては組み合わせを考えず、それ以降について組み合わせを適用することである程度貪欲にそして正確性を保った結果が保証されています。
CatBoost

勾配バイアスの扱い

GBDTライブラリでは、新しい木を生成する際に、現在の木から、そして同じデータセットから勾配近似値を求めるため、真の確率分布と予測勾配値の確率分布の差異でを生むPrediction shiftという危険性を抱えています。
もう少し詳しくこの木を作る過程を見ていきます。
まず、どんな構造の木を使うかを選定し、それが決まったら葉に値を入れていきます。どの構造を使うかは、アルゴリズムが様々な種類を列挙し、それらに点数付けをして最良のものを選びます。ここで葉の値は勾配の近似値あるいはニュートン法によって一般的に計算されています。
この木の構造選定に際して起こるprediction shiftに対処するためにCatboostではOrdered Boostingという手法を取っています。Ordered Boostingは前回のブースティング段階で毎回新たなデータセットを個別にサンプリングして木を作っていきます。
毎回新たなデータセットを使うというのは限られたデータの中では不可能に思われますが、上述のカテゴリ変数のコンビネーションによってその数を確保することに成功しています。

決定木の選定アルゴリズム

Catboostでは、予測をOblivious Decision Treesを用いて行われています。
ここでObliviousは、全ての木で同様の分岐基準を使われていることを示しています。この木構造は均衡が取れていて過学習をしづらく、また後述する性質上実行速度が高速という特徴を持っています。
Oblivious Treesではそれぞれの葉のインデックスは深さと一致する2値ベクトルで表されます。モデル予測の際には、様々な数値データをバイナリ特徴量に変換して計算をします。
全てのベクトル特徴量は連続ベクトルBに保管され、葉の値は大きさ\(2^d\)(dは葉の深さ)のベクトルで保管されます。

実際に動かしてみた

実際のデータを用いたベンチマークが、公式のGithubにあります。
実行時のスコアから見ていきます。

Score


Default CatBoost Tuned CatBoost Default LightGBM Tuned LightGBM Default XGBoost Tuned XGBoost Default H2O Tuned H2O
Adult 0.272978 (±0.0004) (+1.20%) 0.269741 (±0.0001) 0.287165 (±0.0000) (+6.46%) 0.276018 (±0.0003) (+2.33%) 0.280087 (±0.0000) (+3.84%) 0.275423 (±0.0002) (+2.11%) 0.276066 (±0.0000) (+2.35%) 0.275104 (±0.0003) (+1.99%)
Amazon 0.138114 (±0.0004) (+0.29%) 0.137720 (±0.0005) 0.167159 (±0.0000) (+21.38%) 0.163600 (±0.0002) (+18.79%) 0.165365 (±0.0000) (+20.07%) 0.163271 (±0.0001) (+18.55%) 0.169497 (±0.0000) (+23.07%) 0.162641 (±0.0001) (+18.09%)
Appet 0.071382 (±0.0002) (-0.18%) 0.071511 (±0.0001) 0.074823 (±0.0000) (+4.63%) 0.071795 (±0.0001) (+0.40%) 0.074659 (±0.0000) (+4.40%) 0.071760 (±0.0000) (+0.35%) 0.073554 (±0.0000) (+2.86%) 0.072457 (±0.0002) (+1.32%)
Click 0.391116 (±0.0001) (+0.05%) 0.390902 (±0.0001) 0.397491 (±0.0000) (+1.69%) 0.396328 (±0.0001) (+1.39%) 0.397638 (±0.0000) (+1.72%) 0.396242 (±0.0000) (+1.37%) 0.397853 (±0.0000) (+1.78%) 0.397595 (±0.0001) (+1.71%)
Internet 0.220206 (±0.0005) (+5.49%) 0.208748 (±0.0011) 0.236269 (±0.0000) (+13.18%) 0.223154 (±0.0005) (+6.90%) 0.234678 (±0.0000) (+12.42%) 0.225323 (±0.0002) (+7.94%) 0.240228 (±0.0000) (+15.08%) 0.222091 (±0.0005) (+6.39%)
Kdd98 0.194794 (±0.0001) (+0.06%) 0.194668 (±0.0001) 0.198369 (±0.0000) (+1.90%) 0.195759 (±0.0001) (+0.56%) 0.197949 (±0.0000) (+1.69%) 0.195677 (±0.0000) (+0.52%) 0.196075 (±0.0000) (+0.72%) 0.195395 (±0.0000) (+0.37%)
Kddchurn 0.231935 (±0.0004) (+0.28%) 0.231289 (±0.0002) 0.235649 (±0.0000) (+1.88%) 0.232049 (±0.0001) (+0.33%) 0.233693 (±0.0000) (+1.04%) 0.233123 (±0.0001) (+0.79%) 0.232874 (±0.0000) (+0.68%) 0.232752 (±0.0000) (+0.63%)
Kick 0.284912 (±0.0003) (+0.04%) 0.284793 (±0.0002) 0.298774 (±0.0000) (+4.91%) 0.295660 (±0.0000) (+3.82%) 0.298161 (±0.0000) (+4.69%) 0.294647 (±0.0000) (+3.46%) 0.296355 (±0.0000) (+4.06%) 0.294814 (±0.0003) (+3.52%)
Upsel 0.166742 (±0.0002) (+0.37%) 0.166128 (±0.0002) 0.171071 (±0.0000) (+2.98%) 0.166818 (±0.0000) (+0.42%) 0.168732 (±0.0000) (+1.57%) 0.166322 (±0.0001) (+0.12%) 0.169807 (±0.0000) (+2.21%) 0.168241 (±0.0001) (+1.27%)

全ての結果でcatboostが1番良いスコアをマークしています(Log lossのため低いほどよい)。
また、ほぼ全ての結果においてcatboostのデフォルト値を用いた結果が、チューニング後の他ライブラリより高い結果となっています。

モデル評価時間

ライブラリ 所要時間
XGBoost 32Thread 4.89 s ± 223 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)
LightGBM 32Thread 11.1 s ± 402 ms per loop (mean ± std. dev. of 5 runs, 1 loop each)
Catboost 32Thread 184 ms ± 806 µs per loop (mean ± std. dev. of 5 runs, 1 loop each)

こちらも他を圧倒しています。

モデル作成時間

test size catboost lightgbm xgboost
1000 0.069 0.002 0.011
5000 0.349 0.001 0.047
10000 0.770 0.001 0.089

初めてcatboostが遅れをとる結果になりました。 しかも相当のビハインドがあります。
これらのベンチマークは2年前のもので、Python2で書かれたものなので一概にこの結果が正しいと言えるかはわかりません。
そこでAmazonデータセットに対してデフォルト値で比較してみました。詳しいコードは以下を参照してください。


ライブラリ モデル作成時間時間 スコア
XGBoost 1.805s 0.7452
LightGBM 0.6343s 0.8326
Catboost 149.301s 0.8845

Catboostが圧倒的に時間がかかっているのがわかります。

まとめ

特段のチューニングなしでもかなり良い結果を得られるライブラリでした。
しかし、Catboostを使う際には適切なパラメータチューニングの上で相応のマシンスペックと時間の確保が必要になりそうです。
12/8から行われるneurIPSにも論文が出されるそうなので、そこでの発表も楽しみです。

参考文献

CatBoost: unbiased boosting with categorical features

CatBoost: gradient boosting with categorical features support

Mastering The New Generation of Gradient Boosting

CatBoost Document

LightGBM Document

XGBoost Document

Catboost Benchmarks


Twitter・Facebookで定期的に情報発信しています!

このアーカイブについて

このページには、2019年12月に書かれた記事が新しい順に公開されています。

前のアーカイブは2019年11月です。

次のアーカイブは2020年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。